From 0a1ea4d57e9679a49acc74182b38d234a8db014d Mon Sep 17 00:00:00 2001 From: GrayJack Date: Thu, 20 Mar 2025 19:46:46 -0300 Subject: [PATCH] docs: Add documentation of the printf format --- src/header/stdio/printf.rs | 269 +++++++++++++++++++++++++++++++++++++ 1 file changed, 269 insertions(+) diff --git a/src/header/stdio/printf.rs b/src/header/stdio/printf.rs index 0f4abe16f5..324be2cfcf 100644 --- a/src/header/stdio/printf.rs +++ b/src/header/stdio/printf.rs @@ -981,6 +981,275 @@ unsafe fn inner_printf(w: W, format: *const c_char, mut ap: VaList) -> Ok(w.written as c_int) } +/// Implementation of `printf` formatting function, generic over a `writer` +/// +/// This implementation in currently compliant over C17 specification (lacking a few one from C23) +/// and contains extensions as well. +/// +/// # The Format Specification +/// ```text +/// %[conversion-flags][field-width][precision][length-modifier] +/// ``` +/// +///
+/// ※ : This symbol means it is not implemented yet, but it is defined in the C standard +///
+/// +/// ## Conversion Flags +/// Conversion flags are flags that modify the behavior of the [conversion +/// format]. Each one can happen only once per format specifier. They are: +/// +/// - `-`: The result of the conversion is left-justified within the field (by default it is +/// right-justified). +/// - `+`: The sign of signed conversions is always prepended to the result of the conversion (by +/// default the result is preceded by minus **only** when it is negative). +/// - ` `(space): If the result of a signed conversion does not start with a sign character, or is +/// empty, space is prepended to the result. +/// - It is ignored if `+` flag is present. +/// - `#`: Alternative form of the conversion is performed. See the documentation for each +/// [conversion format] for details. +/// - `0`: For integer and floating-point number conversions, leading zeros are used to pad the +/// field instead of space characters. +/// - For integer numbers it is ignored if the precision is explicitly specified. +/// - For other conversions using this flag results in undefined behavior. +/// - It is ignored if `-` flag is present. +/// +/// ## Field Width +/// Specifies minimum field width. This makes the result to be padded (with spaces by default, with +/// zeroes if `0` conversion flag is specified) if the converted value has fewer characters than the +/// specified width. It can take three forms: +/// +/// - `N` where N is a positive integer: Specifies the field width value of `N`. +/// - `*`: The width is specified by an extra argument of type [`int`], which has to appear before +/// the argument to be converted and the [precision] (if specified with `.*`). +/// - If the value of e extra argument is negative, it is interpreted as with `-` [conversion +/// flag], i.e. left-justified result. +/// - `*P$` where P is a positive integer: The width is specified by an extra argument of type +/// [`int`], which has to appear exactly at the position specified by `P`. +/// - This is a popular extension of the C and POSIX standards. +/// - If the value of e extra argument is negative, it is interpreted as with `-` [conversion +/// flag], i.e. left-justified result. +/// +/// ## Precision +/// Specifies the precision of the conversion. +/// +/// For integer [conversion formats], this specifies the number of digits to appear in the result. +/// +/// For float point [conversion formats], this specifies the number of digits to appear after the +/// decimal-point character. +/// +/// It can take three forms: +/// +/// - `.N` where N is a positive integer: Specifies the precision value of `N`. +/// - `.*`: The precision is specified by an extra argument of type [`int`], which has to appear +/// before the argument to be converted and after the the [field width] (if specified with `*`). +/// - If the value of e extra argument is negative, it is interpreted as if the precision were +/// omitted. +/// - `.*P$` where P is a positive integer: The precision is specified by an extra argument of type +/// [`int`], which has to appear exactly at the position specified by `P`. +/// - This is an popular extension of the C and POSIX standards. +/// - If the value of e extra argument is negative, it is interpreted as if the precision were +/// omitted. +/// +/// ## Length Modifier +/// Specifies the size of the argument. In combination with the [conversion format], it specifies +/// the type of the corresponding argument. +/// +/// - `hh`: Byte size +/// - Works with integer conversion formats (`d`, `i`, `o`, `x`, `X`, `b`, `B`) +/// - Works with written number conversion format (`n`) +/// - `h`: Short size +/// - Works with integer conversion formats (`d`, `i`, `o`, `x`, `X`, `b`, `B`) +/// - Works with written number conversion format (`n`) +/// - `l`: Long size +/// - Works with integer conversion formats (`d`, `i`, `o`, `x`, `X`, `b`, `B`) +/// - Works with character conversion format (`c`) +/// - Works with string conversion format (`s`) +/// - Works with written number conversion format (`n`) +/// - Works with float conversion formats (`f`, `F`, `e`, `E`, `a`, `A`, `g`, `G`) (C99) +/// - `ll`: Long long size +/// - Works with integer conversion formats (`d`, `i`, `o`, `x`, `X`, `b`, `B`) +/// - Works with written number conversion format (`n`) +/// - `j`: Maximum width +/// - Works with integer conversion formats (`d`, `i`, `o`, `x`, `X`, `b`, `B`) +/// - Works with written number conversion format (`n`) +/// - `z`: Pointer width size +/// - Works with integer conversion formats (`d`, `i`, `o`, `x`, `X`, `b`, `B`) +/// - Works with written number conversion format (`n`) +/// - `t`: Pointer diff width +/// - Works with integer conversion formats (`d`, `i`, `o`, `x`, `X`, `b`, `B`) +/// - Works with written number conversion format (`n`) +/// - `wN` (C23 ※): Specifies that the size should be N bits width version of the supported +/// conversion format. +/// - Works with integer conversion formats (`d`, `i`, `o`, `x`, `X`, `b`, `B`) +/// - The supported values of `N` must be the same as the widths specified in `stdint.h` +/// - `wfN` (C23 ※): Specifies that the size should be the fast N bits width version of the +/// supported conversion format. +/// - Works with integer conversion formats (`d`, `i`, `o`, `x`, `X`, `b`, `B`) +/// - The supported values of `N` must be the same as the widths specified in `stdint.h` +/// - `L`: Long double size +/// - Works with float conversion formats (`f`, `F`, `e`, `E`, `a`, `A`, `g`, `G`) +/// - `H` (C23 ※): _Decimal32 size +/// - Works with float conversion formats (`f`, `F`, `e`, `E`, `a`, `A`, `g`, `G`) +/// - `D` (C23 ※): _Decimal64 size +/// - Works with float conversion formats (`f`, `F`, `e`, `E`, `a`, `A`, `g`, `G`) +/// - `DD` (C23 ※): _Decimal128 size +/// - Works with float conversion formats (`f`, `F`, `e`, `E`, `a`, `A`, `g`, `G`) +/// +/// ## Conversion Format +/// Specifies the conversion format as one of the following: +/// +/// - `%`: Writes a percent symbol. The full conversion format must be `%%`. +/// - `c`: Writes as single character +/// - Without length modifier: +/// - The argument is first converted to [`unsigned char`] +/// - With `l` length modifier: +/// - The argument is first converted to a character string as if by `%ls` with a array of 2 +/// [`wchar_t`] argument. +/// - `s`: Writes a character string +/// - The argument is a pointer to the first character +/// - The [precision] specifies the maximum number of bytes to be written. If not specified, +/// writes up to the first null character found. +/// - `d` and `i`: Writes a decimal representation of a signed integer +/// - The [precision] specifies the minimal number to appear (defaults to `1`). +/// - If the precision is zero and the value to be written is also zero, the result is no +/// characters written. +/// - `u`: Writes the decimal representation of a unsigned integer +/// - The [precision] specifies the minimal number to appear (defaults to `1`). +/// - If the precision is zero and the value to be written is also zero, the result is no +/// characters written. +/// - `o`: Writes the octal representation of a unsigned integer. +/// - The [precision] specifies the minimal number to appear (defaults to `1`). +/// - If the precision is zero and the value to be written is also zero, the result is no +/// characters written. +/// - The alternative representation includes a leading `0`. +/// - The types are the same as `u` +/// - `x`: Writes the hexadecimal representation of a unsigned integer with lowercase characters. +/// - The [precision] specifies the minimal number to appear (defaults to `1`). +/// - If the precision is zero and the value to be written is also zero, the result is no +/// characters written. +/// - The alternative representation includes a leading `0x`. +/// - The types are the same as `u` +/// - `X`: Writes the hexadecimal representation of a unsigned integer with uppercase characters. +/// - The [precision] specifies the minimal number to appear (defaults to `1`). +/// - If the precision is zero and the value to be written is also zero, the result is no +/// characters written. +/// - The alternative representation includes a leading `0X`. +/// - The types are the same as `u`. +/// - `b` | `B` (C23 ※): Writes the binary representation of a unsigned integer. +/// - The [precision] specifies the minimal number to appear (defaults to `1`). +/// - If the precision is zero and the value to be written is also zero, the result is no +/// characters written. +/// - The alternative representation includes a leading `0b` and `0B`, respectively. +/// - The types are the same as `u`. +/// - `f` | `F`: Writes the decimal representation of a float point number. +/// - The [precision] specifies the exact number of digits to appear after the decimal point +/// character (defaults to `6`). +/// - The alternative representation, the decimal point character is written even if no digits +/// follow it. +/// - `e` | `E`: Writes the float point number with the decimal exponential notation (\[-\]d.ddd +/// **e**±dd | \[-\]d.ddd **E**±dd) +/// - The [precision] specifies the exact number of digits to appear after the decimal point +/// character (defaults to `6`). +/// - The exponent contains at least two digits, more digits are used only if necessary. +/// - If the value is ​zero, the exponent is also ​zero​. +/// - The alternative representation: decimal point character is written even if no digits follow +/// it. +/// - `a` | `A`: Writes the float point number with the hexadecimal exponential notation (\[-\] +/// **0x**h.hhh **p**±d | \[-\] **0X**h.hhh **P**±d) +/// - The [precision] specifies the exact number of digits to appear after the hexadecimal point +/// character (defaults to `6`). +/// - If the value is ​zero, the exponent is also ​zero​. +/// - The alternative representation: decimal point character is written even if no digits follow +/// it. +/// - `g` | `G`: Writes the float point number to decimal or decimal exponent notation depending on +/// the value and the [precision]. +/// - Let `P` equal the precision if nonzero, `6` if the precision is not specified, or `1` if the +/// precision is `​0`​. Then, if a conversion with style `E` would have an exponent of `X`: +/// - If `P > X ≥ −4`, the conversion is with the format `f` and precision `P − 1 − X`. +/// - Otherwise, the conversion is with the format `e` or `E` and precision `P − 1`. +/// - Unless alternative representation is requested, the trailing zeros are removed. Also the +/// decimal point character is removed if no fractional part is left. +/// - `n`: Writes the number of characters written in the call into the argument pointer +/// - It can not contain any [conversion flag], [field width], or [precision]. +/// - `p`: Writes an implementation defined character sequence defining a pointer. +/// +/// ### Types +/// The types expected by the format string can change with the [length modifier]. +/// +/// For the `c`: +/// - Without length modifier: [`int`] +/// - With `l` length modifier: [`wint_t`] +/// +/// For the `s`: +/// - Without length modifier: pointer to [`char`] (`char*`, `const char*`) +/// - With `l` length modifier: pointer to [`wchar_t`] (`wchar_t*`, `const wchar_t*`) +/// +/// For the `d` and `i`: +/// - Without length modifier: [`int`] +/// - With `hh` length modifier: [`signed char`] +/// - With `h` length modifier: [`short`] +/// - With `l` length modifier: [`long`] +/// - With `ll` length modifier: [`long long`] +/// - With `j` length modifier: [`intmax_t`] +/// - With `z` length modifier: [`ssize_t`] +/// - With `t` length modifier: [`ptrdiff_t`] +/// +/// For the `u`, `o`, `x`, `X`, `b`, `B`: +/// - Without length modifier: [`unsigned int`] +/// - With `hh` length modifier: [`unsigned char`] +/// - With `h` length modifier: [`unsigned short`] +/// - With `l` length modifier: [`unsigned long`] +/// - With `ll` length modifier: [`unsigned long long`] +/// - With `j` length modifier: [`uintmax_t`] +/// - With `z` length modifier: [`size_t`] +/// - With `t` length modifier: [`unsigned ptrdiff_t`] +/// +/// For the `f`, `F`, `e`, `E`, `a`, `A`, `g`, `G`: +/// - Without length modifier: [`double`] +/// - With `l` length modifier: [`double`] +/// - With `L` length modifier: `long double` +/// - With `H` length modifier (C23 ※): `_Decimal32` +/// - With `D` length modifier (C23 ※): `_Decimal64` +/// - With `DD` length modifier (C23 ※): `_Decimal128` +/// +/// For the `n` +/// - Without length modifier: pointer to [`int`] (`int*`) +/// - With `hh` length modifier: pointer to [`signed char`] (`signed char*`) +/// - With `h` length modifier: pointer to [`short`] (`short*`) +/// - With `l` length modifier: pointer to [`long`] (`long*`) +/// - With `ll` length modifier: pointer to [`long long`] (`long long*`) +/// - With `j` length modifier: pointer to [`intmax_t`] (`intmax_t*`) +/// - With `z` length modifier: pointer to [`ssize_t`] (`ssize_t*`) +/// - With `t` length modifier: pointer to [`ptrdiff_t`] (`ptrdiff_t*`) +/// +/// For the `p`, it must always be a pointer to [`void`] (`void*` | `const void*`) +/// +/// [precision]: #precision +/// [field width]: #field-width +/// [length modifier]: #length-modifier +/// [conversion format]: #conversion-format +/// [`int`]: c_int +/// [`unsigned char`]: c_uchar +/// [`unsigned short`]: c_ushort +/// [`unsigned int`]: c_uint +/// [`unsigned long`]: c_ulong +/// [`unsigned long long`]: c_ulonglong +/// [`unsigned ptrdiff_t`]: ptrdiff_t +/// [`wchar_t`]: wchar_t +/// [`char`]: c_char +/// [`signed char`]: c_schar +/// [`short`]: c_short +/// [`long`]: c_long +/// [`long long`]: c_longlong +/// [`double`]: c_double +/// [`long double`]: c_longdouble +/// [`void`]: c_void +/// +/// # Safety +/// Behavior is undefined if any of the following conditions are violated: +/// - `format` must point to valid null-terminated string. +/// - `ap` must follow the safety contract of variable arguments of C. pub unsafe fn printf(w: W, format: *const c_char, ap: VaList) -> c_int { inner_printf(w, format, ap).unwrap_or(-1) }