fxlibc/include/fxlibc/printf.h

227 lines
7.5 KiB
C

#ifndef __FXLIBC_PRINTF_H__
#define __FXLIBC_PRINTF_H__
/* This headers covers fxlibc-specific extensions to the *printf API. */
#include <bits/types/FILE.h>
#include <stdint.h>
#include <stdarg.h>
/*
** 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__ */