#ifndef __FXLIBC_PRINTF_H__ #define __FXLIBC_PRINTF_H__ /* This headers covers fxlibc-specific extensions to the *printf API. */ #include #include #include /* ** Output specification; only one of the targets may be non-trivial. ** -> str != NULL ** -> fp != NULL ** -> fd >= 0 ** The size field can be set independently. */ struct __printf_output { /* Final output, after buffering */ char * restrict str; FILE *fp; int fd; /* Size of the final output */ size_t size; /* The following members are set by __printf; do not write to them. */ /* Current output buffer, output position, and buffer limit */ char *buffer, *ptr, *limit; /* Number of characters written so far */ size_t count; }; /* Generic formatted printing. */ int __printf( struct __printf_output * restrict __out, char const * restrict __format, va_list *__args); /* ** Enable floating-point formatters %e, %E, %f, %F, %g, and %G. The formats are ** disabled by default because the representation algorithm has tables of ** powers of 10 and quite a bit of code, resulting in 10-15 kiB of additional ** size in every binary. ** ** Calling this functions pulls object files with floating-point representation ** code from the fxlibc library and registers formatters for all 6 ** floating-point formats. */ void __printf_enable_fp(void); /* Format extension API. */ /* Standard format. */ struct __printf_format { /* Minimal length of formatted string (padding can be added). */ uint16_t length; /* How much significant characters of data, meaning varies. */ int16_t precision; /* ** Size specifier for integers (%o, %x, %i, %d, %u), may be one of: ** (0) char (8-bit) ** (1) short (16-bit) ** (2) int (32-bit) ** (3) long (32-bit) ** (4) long long (64-bit) */ uint8_t size; /* (#) Alternative form: base prefixes, decimal point. */ uint8_t alternative :1; /* ( ) Add a blank sign before nonnegative numbers. */ uint8_t blank_sign :1; /* (+) Always add a sign before a number (overrides ' '). */ uint8_t force_sign :1; /* ** Alignment options, from lowest priority to highest priority: ** NUL By default, align right ** (0) Left-pad numerical values with zeros ** (-) Align left by adding space on the right, dropping zeros */ uint8_t alignment; /* Format specifier. */ char spec; }; /* ** Type of format functions. ** -> __spec is the specifier letter (eg. "d" in "%d") ** -> __opts are the length, precision, sign, alignment, etc. options ** -> __args is a pointer to the variable list of arguments to read from */ typedef void __printf_formatter_t( struct __printf_output * restrict __out, struct __printf_format * restrict __opts, va_list * restrict __args ); /* ** Register a new format. ** ** The formatter designated by the specified lowercase or uppercase letter ** (eg 'p' or 'P') is registered. This functions allows overriding default ** formatters, but this is very much discouraged. Letters with special meaning ** in the standard cannot be changed. A formatted can be removed of disabled by ** registering NULL. ** ** Here are used characters in the C standard: ** ** a: Hexadecimal floating-point A: Hexadecimal floating-point ** b: _ B: _ ** c: Character C: Deprecated synonym for lc ** d: Decimal integer D: _ ** e: Exponent floating-point E: Exponent floating-point ** f: Floating-point F: Floating-point ** g: General floating-point G: General: floating-point ** h: short or char size H: _ ** i: Integer I: Locale-aware digits ** j: intmax_t size J: _ ** k: _ K: _ ** l: long or long long size L: long double size ** m: Error message from errno M: _ ** n: Number of characters written N: _ ** o: Octal integer O: _ ** p: Pointer P: _ ** q: Nonstandard synonym for ll Q: _ ** r: _ R: _ ** s: String S: Deprecated synonym for ls ** t: ptrdiff_t size T: _ ** u: Unsigned decimal integer U: _ ** v: _ V: _ ** w: _ W: _ ** x: Hexadecimal integer X: Hexadecimal integer ** y: _ Y: _ ** z: size_t size Z: Old synonym for z */ void __printf_register(int __spec, __printf_formatter_t *__format); /* Functions for formatters to output characters. */ /* Flush the buffer. (Don't call this directly.) */ void __printf_flush(struct __printf_output *__out); /* Output a single character in the buffer (and possibly flush it). */ static inline void __printf_out(struct __printf_output *__out, int __c) { if(__out->ptr >= __out->limit) __printf_flush(__out); *(__out->ptr++) = __c; } /* Output the same character __n times. */ static inline void __printf_outn(struct __printf_output *__out, int __c, int __n) { while(__n-- > 0) __printf_out(__out, __c); } /* Output a string. */ static inline void __printf_outstr(struct __printf_output *__out, char const *__str, int __n) { for(int i = 0; i < __n; i++) __printf_out(__out, __str[i]); } /* Helper functions for formatters. */ /* ** Format geometry helper. The structure of a format is as follows: ** ** sign v |< zeros >| |< content >| ** _ _ _ _ _ _ _ _ _ + 0 x 0 0 0 0 0 0 8 a c 7 . 3 c _ _ _ _ _ _ _ _ _ _ ** |< left_spaces >| ^^^ prefix |< right_spaces >| ** ** The sign character is absent if sign=0, the prefix is specified by length ** and is also absent if prefix=0. */ struct __printf_geometry { uint16_t left_spaces; /* Spaces before content */ uint8_t sign; /* Sign character (NUL, ' ', '+' or '-') */ uint8_t prefix; /* Base prefix ('0', '0x', etc) length */ uint16_t zeros; /* For integer displays, number of zeros */ uint16_t content; /* Content length in bytes */ uint16_t right_spaces; /* Spaces after content */ /* Style of display: _PRINTF_GENERIC Sign ignored, 0-padding ignored _PRINTF_INTEGER .precision causes 0-padding _PRINTF_NUMERIC No effect */ enum { _PRINTF_GENERIC = 0, _PRINTF_INTEGER, _PRINTF_NUMERIC } style; }; /* ** Calculate the geometry of a format. ** ** The caller provides as input __opt (as it received in the formatter ** function), and the following attributes of __geometry: ** ** - prefix: the length of the desired prefix (if unused, 0) ** - content: the natural content length for the provided data ** - sign: the sign of the input ('+' or '-'); for _PRINTF_GENERIC, 0 ** - style, which affects the meaning of options ** ** This function outputs: ** - sign: will be changed to ' ' or NUL (0) depending on options ** - All fields of __geometry that are not part of the input ** ** The algorithm for laying out the format is as follows. ** 1. For numerical and integer formats, turn a "+" sign into " " if ** __opt->blank_sign is set, "+" if __opt->force_sign is set, NUL otherwise. ** 2. Compute the total amount of padding needed to reach __opt->length. ** 3. In integer style, if a precision is specified and more than content ** length plus sign and prefix, turn some padding into zeros. ** 4. If numerical and integer styles, if __opt->alignment == '0' turn all the ** padding into zeros. ** 5. Turn remaining padding into spaces at the left (if opt->alignment == NUL) ** or right (if opt->alignment == '-'). */ void __printf_compute_geometry( struct __printf_format *__opt, struct __printf_geometry *__geometry); #endif /* __FXLIBC_PRINTF_H__ */