diff mbox series

[RFC,v3] printf.3head: Document functions for customizing printf(3)-like functions

Message ID 20220919165456.28289-2-alx.manpages@gmail.com
State New
Headers show
Series [RFC,v3] printf.3head: Document functions for customizing printf(3)-like functions | expand

Commit Message

Alejandro Colomar Sept. 19, 2022, 4:54 p.m. UTC
Suggested-by: Walter Harms <wharms@bfs.de>
Cc: <Radisson97@gmx.de>
Cc: "G. Branden Robinson" <g.branden.robinson@gmail.com>
Cc: <libc-alpha@sourceware.org>
Signed-off-by: Alex Colomar <alx.manpages@gmail.com>
---
 man3head/printf.h.3head | 561 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 561 insertions(+)
 create mode 100644 man3head/printf.h.3head
diff mbox series

Patch

diff --git a/man3head/printf.h.3head b/man3head/printf.h.3head
new file mode 100644
index 000000000..cd396c872
--- /dev/null
+++ b/man3head/printf.h.3head
@@ -0,0 +1,561 @@ 
+.\" Copyright (C) 2022 Alejandro Colomar <alx.manpages@gmail.com>
+.\"
+.\" SPDX-License-Identifier: Linux-man-pages-copyleft
+.\"
+.TH printf.h 3head 2022-09-18 "Linux man-pages (unreleased)"
+.SH NAME
+printf.h,
+\%register_printf_specifier,
+\%register_printf_modifier,
+\%register_printf_type,
+\%printf_function,
+\%printf_arginfo_size_function,
+\%printf_va_arg_function,
+\%printf_info,
+\%PA_INT,
+\%PA_CHAR,
+\%PA_WCHAR,
+\%PA_STRING,
+\%PA_WSTRING,
+\%PA_POINTER,
+\%PA_FLOAT,
+\%PA_DOUBLE,
+\%PA_LAST,
+\%PA_FLAG_LONG_LONG,
+\%PA_FLAG_LONG_DOUBLE,
+\%PA_FLAG_LONG,
+\%PA_FLAG_SHORT,
+\%PA_FLAG_PTR
+\- define custom behavior for printf-like functions
+.SH LIBRARY
+Standard C library
+.RI ( libc ", " \-lc )
+.SH SYNOPSIS
+.nf
+.B #include <printf.h>
+.PP
+.BI "int register_printf_specifier(int " spec ", printf_function " func ,
+.BI "                              printf_arginfo_size_function " arginfo );
+.BI "int register_printf_modifier(const wchar_t *" str );
+.BI "int register_printf_type(printf_va_arg_function " fct );
+.fi
+.SS Callbacks
+.nf
+.BI "typedef int printf_function(FILE *" stream ", const struct printf_info *" info ,
+.BI "                              const void *const " args []);
+.BI "typedef int printf_arginfo_size_function(const struct printf_info *" info ,
+.BI "                              size_t " n ", int " argtypes [ n "], int " size [ n ]);
+.BI "typedef void printf_va_arg_function(void *" mem ", va_list *" ap );
+.fi
+.SS Types
+.EX
+.B struct printf_info
+.B {
+.BR "    int            prec;            " "// Precision"
+.BR "    int            width;           " "// Width"
+.BR "    wchar_t        spec;            " "// Format letter"
+.BR "    unsigned int   is_long_double:1;" "// " L " or " ll " flag"
+.BR "    unsigned int   is_short:1;      " "// " h " flag"
+.BR "    unsigned int   is_long:1;       " "// " l " flag"
+.BR "    unsigned int   alt:1;           " "// " # " flag"
+.BR "    unsigned int   space:1;         " "// Space flag"
+.BR "    unsigned int   left:1;          " "// " - " flag"
+.BR "    unsigned int   showsign:1;      " "// " + " flag"
+.BR "    unsigned int   group:1;         " "// " ' " flag"
+.BR "    unsigned int   extra:1;         " "// For special use"
+.BR "    unsigned int   is_char:1;       " "// " hh " flag"
+.BR "    unsigned int   wide:1;          " "// True for wide character streams"
+.BR "    unsigned int   i18n:1;          " "// " I " flag"
+.BR "    unsigned int   is_binary128:1;  " "/* Floating-point argument is"
+.BR "                                    " "   ABI-compatible with"
+.BR "                                    " "   IEC 60559 binary128 */"
+.BR "    unsigned short user;            " "// Bits for user-installed modifiers"
+.BR "    wchar_t        pad;             " "// Padding character"
+.B };
+.EE
+.SS Constants
+.EX
+.B enum
+.BR "{              " "// C type:"
+.BR "    PA_INT,    " "// " int
+.BR "    PA_CHAR,   " "// " int ", cast to " char
+.BR "    PA_WCHAR,  " "// " wchar_t
+.BR "    PA_STRING, " "// " "const char *" ", a " '\e0' "-terminated string"
+.BR "    PA_WSTRING," "// " "const wchar_t *" ", wide character " L'\e0' "-terminated string"
+.BR "    PA_POINTER," "// " "void *"
+.BR "    PA_FLOAT,  " "// " float
+.BR "    PA_DOUBLE, " "// " double
+.BR "    PA_LAST
+.B };
+.PP
+.BR "#define PA_FLAG_LONG_LONG    " "/* ... */"
+.BR "#define PA_FLAG_LONG_DOUBLE  " "/* ... */"
+.BR "#define PA_FLAG_LONG         " "/* ... */"
+.BR "#define PA_FLAG_SHORT        " "/* ... */"
+.BR "#define PA_FLAG_PTR          " "/* ... */"
+.EE
+.SH DESCRIPTION
+These functions serve to extend and/or modify the behavior of the
+.BR printf (3)
+family of functions.
+.SS register_printf_specifier()
+This function registers a custom conversion specifier for the
+.BR printf (3)
+family of functions.
+.TP
+.I spec
+The character which will be used as a conversion specifier in the format string.
+.TP
+.I func
+Callback function that will be executed by the
+.BR printf (3)
+family of functions
+to format the input arguments into the output
+.IR stream .
+.RS
+.TP
+.I stream
+Output stream where the formatted output should be printed.
+This stream transparently represents the output,
+even in the case of functions that write to a string.
+.TP
+.I info
+Structure that holds context information,
+including the modifiers specified in the format string.
+This holds the same contents as in
+.IR arginfo .
+.TP
+.I args
+Array of pointers to the arguments to the
+.BR printf (3)\c
+-like function.
+.RE
+.TP
+.I arginfo
+Callback function that will be executed by the
+.BR printf (3)
+family of functions
+to know how many arguments should be parsed for the custom specifier
+and also their types.
+.RS
+.TP
+.I info
+Structure that holds context information,
+including the modifiers specified in the format string.
+This holds the same contents as in
+.IR func .
+.TP
+.I n
+Number of arguments remaining to be parsed.
+.TP
+.I argtypes
+This array should be set to
+define the type of each of the arguments that will be parsed.
+Each element in the array represents one of the arguments to be parsed,
+in the same order that they are passed to the
+.BR printf (3)\c
+-like function.
+Each element should be set to a base type
+.RB ( PA_ *)
+from the enum above,
+or a custom one,
+and optionally ORed with an appropriate length modifier
+.RB ( PA_FLAG_ *).
+.TP
+.I size
+For user-defined types,
+the size of the type (in bytes) should also be specified through this array.
+Otherwise, leave it unused.
+.RE
+.PP
+.I arginfo
+is called before
+.IR func ,
+and prepares some information needed to call
+.IR func .
+.SS register_printf_modifier()
+TODO
+.SS register_printf_type()
+TODO
+.SH RETURN VALUE
+.BR \%register_printf_specifier (),
+.BR \%register_printf_modifier (),
+and
+.BR \%register_printf_type ()
+return zero on success, or \-1 on error.
+.SS Callbacks
+The callback of type
+.I printf_function
+should return the number of characters written,
+or \-1 on error.
+.PP
+The callback of type
+.I \%printf_arginfo_size_function
+should return the number of arguments to be parsed by this specifier.
+It also passes information about the type of those arguments
+to the caller through
+.IR argtypes .
+On error, it should return \-1.
+.SH ERRORS
+.TP
+.B EINVAL
+The specifier was not a valid character.
+.SH VERSIONS
+.BR \%register_printf_function (3)
+is an older function similar to
+.BR \%register_printf_specifier,
+and is now deprecated.
+That function can't handle user-defined types.
+.PP
+.BR \%register_printf_specifier ()
+superseeds
+.BR \%register_printf_function (3).
+.SH STANDARDS
+These nonstandard functions are present in glibc.
+.SH EXAMPLES
+The following example program registers the 'b' and 'B' specifiers
+to print integers in binary format,
+mirroring rules for other unsigned conversion specifiers like 'x' and 'u'.
+This can be used to print in binary prior to C23.
+.PP
+.\" SRC BEGIN (register_printf_specifier.c)
+.EX
+/* This code is in the public domain */
+
+#include <err.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+
+#include <printf.h>
+
+#define GROUP_SEP  '_'
+
+struct Printf_Pad {
+    char    ch;
+    size_t  len;
+};
+
+static int b_printf(FILE *stream, const struct printf_info *info,
+                    const void *const args[]);
+static int b_arginf_sz(const struct printf_info *info,
+                    size_t n, int argtypes[n], int size[n]);
+
+static uintmax_t b_value(const struct printf_info *info,
+                    const void *arg);
+static size_t b_bin_repr(char bin[UINTMAX_WIDTH],
+                    const struct printf_info *info, const void *arg);
+static size_t b_bin_len(const struct printf_info *info,
+                    ptrdiff_t min_len);
+static size_t b_pad_len(const struct printf_info *info,
+                    ptrdiff_t bin_len);
+static ssize_t b_print_prefix(FILE *stream,
+                    const struct printf_info *info);
+static ssize_t b_pad_zeros(FILE *stream, const struct printf_info *info,
+                    ptrdiff_t min_len);
+static ssize_t b_print_number(FILE *stream,
+                    const struct printf_info *info,
+                    const char bin[UINTMAX_WIDTH],
+                    size_t min_len, size_t bin_len);
+static char pad_ch(const struct printf_info *info);
+static ssize_t pad_spaces(FILE *stream, size_t pad_len);
+
+int
+main(void)
+{
+    if (register_printf_specifier('b', b_printf, b_arginf_sz) == -1)
+        err(EXIT_FAILURE, "register_printf_specifier('b', ...)");
+    if (register_printf_specifier('B', b_printf, b_arginf_sz) == -1)
+        err(EXIT_FAILURE, "register_printf_specifier('B', ...)");
+
+    printf("....----....----....----....----\en");
+    printf("%llb;\en", 0x5Ellu);
+    printf("%lB;\en", 0x5Elu);
+    printf("%b;\en", 0x5Eu);
+    printf("%hB;\en", 0x5Eu);
+    printf("%hhb;\en", 0x5Eu);
+    printf("%jb;\en", (uintmax_t)0x5E);
+    printf("%zb;\en", (size_t)0x5E);
+    printf("....----....----....----....----\en");
+    printf("%#b;\en", 0x5Eu);
+    printf("%#B;\en", 0x5Eu);
+    printf("....----....----....----....----\en");
+    printf("%10b;\en", 0x5Eu);
+    printf("%010b;\en", 0x5Eu);
+    printf("%.10b;\en", 0x5Eu);
+    printf("....----....----....----....----\en");
+    printf("%\-10B;\en", 0x5Eu);
+    printf("....----....----....----....----\en");
+    printf("%'B;\en", 0x5Eu);
+    printf("....----....----....----....----\en");   
+    printf("....----....----....----....----\en");
+    printf("%#16.12b;\en", 0xAB);
+    printf("%\-#'20.12b;\en", 0xAB);
+    printf("%#'020B;\en", 0xAB);
+    printf("....----....----....----....----\en");
+    printf("%#020B;\en", 0xAB);
+    printf("%'020B;\en", 0xAB);
+    printf("%020B;\en", 0xAB);
+    printf("....----....----....----....----\en");
+    printf("%#021B;\en", 0xAB);
+    printf("%'021B;\en", 0xAB);
+    printf("%021B;\en", 0xAB);
+    printf("....----....----....----....----\en");
+    printf("%#022B;\en", 0xAB);
+    printf("%'022B;\en", 0xAB);
+    printf("%022B;\en", 0xAB);
+    printf("....----....----....----....----\en");
+    printf("%#023B;\en", 0xAB);
+    printf("%'023B;\en", 0xAB);
+    printf("%023B;\en", 0xAB);
+    printf("....----....----....----....----\en");
+    printf("%\-#'19.11b;\en", 0xAB);
+    printf("%#'019B;\en", 0xAB);
+    printf("%#019B;\en", 0xAB);
+    printf("....----....----....----....----\en");
+    printf("%'019B;\en", 0xAB);
+    printf("%019B;\en", 0xAB);
+    printf("%#016b;\en", 0xAB);
+    printf("....----....----....----....----\en");
+
+    return 0;
+}
+
+static int
+b_printf(FILE *stream, const struct printf_info *info,
+         const void *const args[])
+{
+    char               bin[UINTMAX_WIDTH];
+    size_t             min_len, bin_len;
+    ssize_t            len, tmp;
+    struct Printf_Pad  pad = {0};
+
+    len = 0;
+
+    min_len = b_bin_repr(bin, info, args[0]);
+    bin_len = b_bin_len(info, min_len);
+
+    pad.ch = pad_ch(info);
+    if (pad.ch == ' ')
+        pad.len = b_pad_len(info, bin_len);
+
+    /* Padding with ' ' (right aligned) */
+    if ((pad.ch == ' ') && !info->left) {
+        tmp = pad_spaces(stream, pad.len);
+        if (tmp == EOF)
+            return EOF;
+        len += tmp;
+    }
+
+    /* "0b"/"0B" prefix */
+    if (info->alt) {
+        tmp = b_print_prefix(stream, info);
+        if (tmp == EOF)
+            return EOF;
+        len += tmp;
+    }
+
+    /* Padding with '0' */
+    if (pad.ch == '0') {
+        tmp = b_pad_zeros(stream, info, min_len);
+        if (tmp == EOF)
+            return EOF;
+        len += tmp;
+    }
+
+    /* Print number (including leading 0s to fill precision) */
+    tmp = b_print_number(stream, info, bin, min_len, bin_len);
+    if (tmp == EOF)
+        return EOF;
+    len += tmp;
+
+    /* Padding with ' ' (left aligned) */
+    if (info\->left) {
+        tmp = pad_spaces(stream, pad.len);
+        if (tmp == EOF)
+            return EOF;
+        len += tmp;
+    }
+
+    return len;
+}
+
+static int
+b_arginf_sz(const struct printf_info *info, size_t n, int argtypes[n],
+            [[maybe_unused]] int size[n])
+{
+    if (n < 1)
+        return -1;
+
+    if (info\->is_long_double)
+        argtypes[0] = PA_INT | PA_FLAG_LONG_LONG;
+    else if (info\->is_long)
+        argtypes[0] = PA_INT | PA_FLAG_LONG;
+    else
+        argtypes[0] = PA_INT;
+
+    return 1;
+}
+
+static uintmax_t
+b_value(const struct printf_info *info, const void *arg)
+{
+    if (info\->is_long_double)
+        return *(const unsigned long long *)arg;
+    if (info\->is_long)
+        return *(const unsigned long *)arg;
+
+    /* short and char are both promoted to int */
+    return *(const unsigned int *)arg;
+}
+
+static size_t
+b_bin_repr(char bin[UINTMAX_WIDTH],
+           const struct printf_info *info, const void *arg)
+{
+    size_t     min_len;
+    uintmax_t  val;
+
+    val = b_value(info, arg);
+
+    bin[0] = '0';
+    for (min_len = 0; val; min_len++) {
+        bin[min_len] = '0' + (val % 2);
+        val >>= 1;
+    }
+
+    return MAX(min_len, 1);
+}
+
+static size_t
+b_bin_len(const struct printf_info *info, ptrdiff_t min_len)
+{
+    return MAX(info\->prec, min_len);
+}
+
+static size_t
+b_pad_len(const struct printf_info *info, ptrdiff_t bin_len)
+{
+    ptrdiff_t  pad_len;
+
+    pad_len = info\->width \- bin_len;
+    if (info\->alt)
+        pad_len \-= 2;
+    if (info\->group)
+        pad_len \-= (bin_len \- 1) / 4;
+
+    return MAX(pad_len, 0);
+}
+
+static ssize_t
+b_print_prefix(FILE *stream, const struct printf_info *info)
+{
+    ssize_t len;
+
+    len = 0;
+    if (fputc('0', stream) == EOF)
+        return EOF;
+    len++;
+    if (fputc(info\->spec, stream) == EOF)
+        return EOF;
+    len++;
+
+    return len;
+}
+
+static ssize_t
+b_pad_zeros(FILE *stream, const struct printf_info *info,
+            ptrdiff_t min_len)
+{
+    ssize_t    len;
+    ptrdiff_t  tmp;
+
+    len = 0;
+    tmp = info\->width \- (info\->alt * 2);
+    if (info\->group)
+        tmp \-= tmp / 5 \- !(tmp % 5);
+    for (ptrdiff_t i = tmp \- 1; i > min_len \- 1; i\-\-) {
+        if (fputc('0', stream) == EOF)
+            return EOF;
+        len++;
+
+        if (!info\->group || (i % 4))
+            continue;
+        if (fputc(GROUP_SEP, stream) == EOF)
+            return EOF;
+        len++;
+    }
+
+    return len;
+}
+
+static ssize_t
+b_print_number(FILE *stream, const struct printf_info *info,
+               const char bin[UINTMAX_WIDTH],
+               size_t min_len, size_t bin_len)
+{
+    ssize_t  len;
+
+    len = 0;
+
+    /* Print leading zeros to fill precision */
+    for (size_t i = bin_len \- 1; i > min_len \- 1; i\-\-) {
+        if (fputc('0', stream) == EOF)
+            return EOF;
+        len++;
+
+        if (!info\->group || (i % 4))
+            continue;
+        if (fputc(GROUP_SEP, stream) == EOF)
+            return EOF;
+        len++;
+    }
+
+    /* Print number */
+    for (size_t i = min_len \- 1; i < min_len; i\-\-) {
+        if (fputc(bin[i], stream) == EOF)
+            return EOF;
+        len++;
+
+        if (!info\->group || (i % 4) || !i)
+            continue;
+        if (fputc(GROUP_SEP, stream) == EOF)
+            return EOF;
+        len++;
+    }
+
+    return len;
+}
+
+static char
+pad_ch(const struct printf_info *info)
+{
+    if ((info\->prec != \-1) || (info\->pad == ' ') || info\->left)
+        return ' ';
+    return '0';
+}
+
+static ssize_t
+pad_spaces(FILE *stream, size_t pad_len)
+{
+    ssize_t  len;
+
+    len = 0;
+    for (size_t i = pad_len - 1; i < pad_len; i\-\-) {
+        if (fputc(' ', stream) == EOF)
+            return EOF;
+        len++;
+    }
+
+    return len;
+}
+.EE
+.\" SRC END
+.SH SEE ALSO
+.BR asprintf (3),
+.BR printf (3),
+.BR wprintf (3)