gint/include/gint/kprint.h

156 lines
5.5 KiB
C

//---
// gint:kprint - gint's printf(3)-like formatted printer
//---
#ifndef GINT_KPRINT
#define GINT_KPRINT
#include <gint/defs/types.h>
#include <gint/defs/attributes.h>
#include <stdarg.h>
//---
// Formatted printing
//---
/* kvsprint(): Formatted print to string
This function is similar to vsnprintf(3), except that variadic arguments are
passed as a pointer to va_list. For standard functions like vsnprintf(3),
see <stdio.h>. */
size_t kvsprint(char *output, size_t len, char const *format, va_list *args);
/* kprint_enable_fp(): Enable and load floating-point formats
Loads floating-point formats %e, %E, %f, %F, %g and %G. By default these
formats are not enabled because the formatting code takes a large amount of
space. Calling this function pulls the floating-point formatter from the
gint library at link time. */
void kprint_enable_fp(void);
//---
// Printer extensions
//---
/* Arguments passed to formatters */
#define KFORMAT_ARGS \
GUNUSED int spec, /* Specifier letter */ \
GUNUSED kprint_options_t *opt, /* Other options in format */ \
va_list *args /* Vararg list to read from */
/* kprint_options_t: Standard format options */
typedef struct {
/* 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 */
uint alternative :1;
/* ( ) Add a blank sign before nonnegative numbers */
uint blank_sign :1;
/* (+) Always add a sign before a number (overrides ' ') */
uint force_sign :1;
/* Alignment options: each option overrides all others before itself
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;
} GPACKED(2) kprint_options_t;
/* kprint_formatter_t: Type of format function */
typedef void (*kprint_formatter_t)(KFORMAT_ARGS);
/* kprint_register(): Register a formatter
The formatter designated by the specified lowercase letter (eg 'q') is
registered. It will handle formats for both the lowercase and uppercase
formats (eg "%q" and "%Q").
Default formatters can be overridden (although not advised), but formatters
for "h" and "l" and "z" cannot be set because these letters are used as size
specifiers (eg in "%hd" or "%zu").
A formatter can be removed or disabled by registering NULL.
@spec Specifier to use, should be a lowercase letter
@kformat Format function */
void kprint_register(int spec, kprint_formatter_t kformat);
/* kprint_out(): Output a single character to the kprint buffer */
void kprint_out(int byte);
/* kprint_outn(): Output the same character <n> times */
void kprint_outn(int byte, int n);
/* kprint_outstr(): Output a string to the kprint buffer */
void kprint_outstr(char const *str, int n);
//---
// Helper functions for formatters
//---
/* kprint_geometry_t: General geometry for a format
This helper defines the structure of a format 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. */
typedef struct
{
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:
KPRINT_GENERIC Sign ignored, 0-padding ignored
KPRINT_INTEGER .precision causes 0-padding
KPRINT_NUMERIC No effect */
enum { KPRINT_GENERIC = 0, KPRINT_INTEGER, KPRINT_NUMERIC } style;
} GPACKED(2) kprint_geometry_t;
/* kformat_geometry(): Calculate the geometry of a format
The caller provides as input:
* opt, as passed by the main kprint() routine
* g->prefix, the length of the desired prefix (if unused, 0)
* g->content, the natural content length for the provided data
* g->sign, the sign of the input ("+" or "-"); for KPRINT_GENERIC, 0
* g->style, which affects the meaning of options
This function outputs:
* g->sign, which will be changed to " " or NUL (0) depending on options
* All fields of g 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 == '-').
@opt Options provided by the main kprint() routine
@g Format geometry (input/output) */
void kformat_geometry(kprint_options_t *opt, kprint_geometry_t *g);
#endif /* GINT_KPRINT */