{"id":15049307,"url":"https://github.com/moehriegitt/vastringify","last_synced_at":"2025-08-05T11:19:41.791Z","repository":{"id":43777343,"uuid":"436744477","full_name":"moehriegitt/vastringify","owner":"moehriegitt","description":"Type-safe Printf in C","archived":false,"fork":false,"pushed_at":"2025-01-15T19:29:18.000Z","size":244,"stargazers_count":72,"open_issues_count":0,"forks_count":5,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-02T21:56:28.488Z","etag":null,"topics":["c","c11","c99","macro","printf","stack-usage","string-escape","typesafe","unicode","utf16","utf32","utf8"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/moehriegitt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-12-09T19:56:59.000Z","updated_at":"2025-03-12T06:50:27.000Z","dependencies_parsed_at":"2022-09-06T21:11:24.223Z","dependency_job_id":null,"html_url":"https://github.com/moehriegitt/vastringify","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moehriegitt%2Fvastringify","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moehriegitt%2Fvastringify/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moehriegitt%2Fvastringify/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moehriegitt%2Fvastringify/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/moehriegitt","download_url":"https://codeload.github.com/moehriegitt/vastringify/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248144008,"owners_count":21054864,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c","c11","c99","macro","printf","stack-usage","string-escape","typesafe","unicode","utf16","utf32","utf8"],"created_at":"2024-09-24T21:19:40.103Z","updated_at":"2025-04-10T02:29:21.113Z","avatar_url":"https://github.com/moehriegitt.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Type-Safe Printf For C\n\nThis uses macro magic, compound literals, and _Generic to take\nprintf() to the next level: type-safe printing, printing into compound\nliteral char arrays, easy UTF-8, -16, and -32, with good error\nhandling.\n\nThe goal is to be safe by removing the need for function varargs.\n\nThe usual C printf formatting syntax is used, with some restrictions\nand quite a few extensions.\n\nThis let's you mix UTF-8, -16, -32 strings seamlessly in input and\noutput strings, without manual string format conversions, and without\nusing different format specifiers or print function names.\n\nThis liberates you from thinking about `%u` vs. `%lu` vs. `%llu`\nvs. `%zu`, even in portable code with different integer types. .  With\nthis library, the compiler chooses the right function for your\nparameter and they all print fine with `~s`, like also strings and\npointers do.\n\n'Type-safe' in this context does not mean that you get more compile\nerrors or warnings, but that you cannot make a mistake, and you do not\nneed the format string to specify the argument type.  Format strings\nwith this library do not need compile-time checking to be safe,\nbecause the compiler chooses the right formatting function for each\nparameter.  You cannot pass the wrong size parameter and crash,\nbecause `...` is avoided.\n\n## Examples\n\n - `va_printf(\"~s ~s ~s\", 65, (long long)65, \"65\")` prints `65 65 65`\n - `va_printf(\"~c ~c ~c\", 65, (long long)65, \"65\")` prints `A A 65`\n - `va_printf(\"~x ~x ~x\", 65, (long long)65, \"65\")` prints `41 41 65`\n - `va_printf(\"~p ~p ~p\", 65, (long long)65, \"65\")` prints\n   `0x41 0x41 0x8838abc3932` (the pointer value of the string)\n - `va_lprintf(\"~p\", 65)` returns `4`, the length of `0x41`\n - `va_nprintf(10, \"~p\", 65)` returns `\"0x41\"`, the pointer to a compound\n   literal `(char[10]){}` that was printed into\n - `va_printf(\"~t x = ~=qzs\", u\"fo\\020o\")` prints `char16_t* x = u\"fo\\no\"`\n\n## Compatibility\n\nThis library requires at least a C11 compiler (for `_Generic`,\n`char16_t`, `char32_t`), and it uses a few gcc extensions that are\nalso understood by Clang and a few other compilers (`({...})`,\n`,##__VA_ARGS__`, `__typeof__`, `__attribute__`).\n\n## Synopsis\n\nIn the following, `Char` may be `char`, `char16_t`, or `char32_t`:\n\n```c\n#include \u003cva_print/file.h\u003e\n\nvoid\nva_fprintf(FILE *f, Char const *format, ...);\n\nvoid\nva_ufprintf(FILE *f, Char const *format, ...);\n\nvoid\nva_Ufprintf(FILE *f, Char const *format, ...);\n\nvoid\nva_printf(Char const *format, ...);\n\nvoid\nva_eprintf(Char const *format, ...);\n\nva_stream_file_t\nVA_STREAM_FILE(FILE *f);\n\n\n#include \u003cva_print/char.h\u003e\n\nChar *\nva_snprintf(Char *s, size_t n, Char const *format, ...);\n\nChar *\nva_sprintf(Char s[], Char const *format, ...);\n\nchar *\nva_nprintf(size_t n, Char const *format, ...);\n\nchar16_t *\nva_unprintf(size_t n, Char const *format, ...);\n\nchar32_t *\nva_Unprintf(size_t n, Char const *format, ...);\n\nCharType *\nva_gnprintf(CharType, size_t n, Char const *format, ...);\n\nsize_t\nva_zprintf(Char const *format, ...);\n\nsize_t\nva_uzprintf(Char const *format, ...);\n\nsize_t\nva_Uzprintf(Char const *format, ...);\n\nsize_t\nva_gzprintf(CharType, Char const *format, ...);\n\nva_stream_charp_t\nVA_STREAM_CHAR_P(Char *s, size_t n);\n\n\n#include \u003cva_print/alloc.h\u003e\n\nchar *\nva_axprintf(void *(*alloc)(void *, size_t, size_t), Char const *, ...);\n\nchar16_t *\nva_uaxprintf(void *(*alloc)(void *, size_t, size_t), Char const *, ...);\n\nchar32_t *\nva_Uaxprintf(void *(*alloc)(void *, size_t, size_t), Char const *, ...);\n\nchar *\nva_asprintf(Char const *, ...);\n\nchar16_t *\nva_uasprintf(Char const *, ...);\n\nchar32_t *\nva_Uasprintf(Char const *, ...);\n\nva_stream_vec_t\nVA_STREAM_VEC(void *(*alloc)(void *, size_t, size_t));\n\nva_stream_vec16_t\nVA_STREAM_VEC16(void *(*alloc)(void *, size_t, size_t));\n\nva_stream_vec32_t\nVA_STREAM_VEC32(void *(*alloc)(void *, size_t, size_t));\n\nvoid *\nva_alloc(void *data, size_t nmemb, size_t size);\n\n\n#include \u003cva_print/fd.h\u003e\n\nvoid\nva_dprintf(int fd, Char const *format, ...);\n\nvoid\nva_udprintf(int fd, Char const *format, ...);\n\nvoid\nva_Udprintf(int fd, Char const *format, ...);\n\nva_stream_file_t\nVA_STREAM_FD(int fd);\n\n\n#include \u003cva_print/len.h\u003e\n\nsize_t\nva_lprintf(Char const *format, ...);\n\nva_stream_len_t\nVA_STREAM_LEN();\n\n\n#include \u003cva_print/core.h\u003e\n\nva_stream_...t *\nva_xprintf(va_stream_...t *s, Char const *format, ...);\n\nvoid\nva_iprintf(va_stream_...t *s, Char const *format, ...);\n\nvoid\nva_pprintf(va_stream_vtab_t *v, Char const *format, ...);\n\nunsigned\nva_stream_get_error(va_stream_...t const *s);\n\nextern\nchar const *va_strerror(unsigned error_code);\n\n\n#include \u003cva_print/base.h\u003e\n\ntypedef struct { ... } va_stream_t;\n\ntypedef struct { ... } va_stream_vtab_t;\n\ntypedef struct { unsigned code; } va_error_t;\n#define VA_E_OK     ...\n#define VA_E_NULL   ...\n#define VA_E_DECODE ...\n#define VA_E_ENCODE ...\n#define VA_E_TRUNC  ...\n#define VA_E_FORMAT ...\n#define VA_E_ARGC   ...\n\nva_stream_t\nVA_STREAM(va_stream_vtab_t const *vtab);\n\n#define VA_U_REPLACEMENT 0xfffd\n#define VA_U_BOM         0xfeff\n#define VA_U_SURR_MIN    0xd800\n#define VA_U_SURR_MAX    0xdfff\n#define VA_U_MAX         0x0010ffff\n#define VA_U_MAXMAX      0x00ffffff\n\n#define va_countof(A)    (sizeof(A)/sizeof((A)[0]))\n```\n\n## Description\n\nThis library provides a type-safe printing mechanism to print\nany kind of string of base type `char`, `char16_t`, or `char32_t`,\nor any integer or pointer into a new string, an array, or a file.\n\nThe library also provides functions for user-defined output streams\nthat can print into any other kind of stream.\n\nThe arguments to the formatted print are passed into a `_Generic()`\nmacro instead of '...' and the resulting function call is thus\ntype-safe based on the actual argument type, and cannot crash due to a\nwrong format specifier.\n\nThe format specifiers in this printing mechanism serve to define which\noutput format should be used, as they are not needed for type\ninformation.  The format specifier \"~s\" can be used as a generic\n'default' output format.\n\n### Format Specifiers\n\nThe format is string is decoded and then output as is into the output\nstream where it is encoded, except for sequences of `~`, which are\ninterpreted as a format specifier.\n\nFor each format specifier in the format string, 0, 1, or more\narguments are read (how many is specified below).  If there are more\narguments than what is needed for the format specifiers, the rest of\nthe argumnets are ignored and the `VA_E_ARGC` stream error is set.\n\nIf fewer arguments are given than needed for the format string, the\nrest of the format specifiers print empty and the `VA_E_ARGC` stream\nerror is set.\n\nA format specifier begins with `~`, and what follows is similar to C:\n\n - a list of flag characters\n - a width specifier\n - a precision specifier\n - a list of integer mask and quotation specifiers\n - a conversion letter\n\n`~` is used instead of `%` to avoid confusion in source code that uses\nboth this library and the standard C `printf`.\n\n#### Flags\n\nGenerally, specifying multiple identical flags like `~008s` is\nreserved for future use and should be avoided. It is unspecified how\nthe current library handles such format strings.\n\n - `#` print in alternative form.  For numeric format, a prefix to\n   designate the base is prefixed to the value except to 0:\n     - for `o` and base 8, `0` is prefixed\n     - for `b` and base 2, `0b` is prefixed,\n     - for `B` and base 2, `0B` is prefixed,\n     - for `x` and base 16, `0x` is prefixed,\n     - for `X` and base 16, `0X` is prefixed,\n     - for `e` and base 32, `0e` is prefixed,\n     - for `E` and base 32, `0E` is prefixed.\n\n   In `~#p`, the `#` flag switches off the implicit `#` that is\n   contained in `p`, e.g., does not print the base prefix.\n\n   For quoted strings, `#` inhibits printing of delimiting quotes.\n\n - `0` pads numerics with zero `0` on the left rather than\n   with a space character ` `.  If a precision is given, this is\n   ignored.\n\n   For C and JSON quotation, this selects to quote non-US-ASCII\n   characters using `\\u` and `\\U` instead of printing them in\n   output encoding.\n\n - `-` selects to left flush instead of the default right flush.\n\n - ` ` (a space character U+0020) selects that a space is printed in\n   front of positive signed integers. Nothing is printed if the\n   precision is 0 and the value is 0 (this is different compared to\n   the behaviour of C's printf).\n\n - `+` selects that a `+` is printed in front of positive signed\n   integers and zero.  Nothing is printed if the precision is 0 and\n   the value is 0 (this is different compared to the behaviour of C's\n   printf).\n\n - `=` specifies that the last value is printed again using this\n   new format specifier.  This is meager replacement for the `$`\n   position specifiers that are not implemented in this library.\n\nA width is either a decimal integer, or a `*`.  The `*` selects\nthat the width is taken from the next function parameter.  If fewer\ncode points result from the conversion, the output is padded with\nwhite space up the width.  A negative width is intepreted as\na `-` flag followed by a positive width.\n\nA precision is specified by a `.` (period) followed by either a\ndecimal integer or a `*`.  The `*` selects that the width is taken\nfrom the next function parameter.  If the precision is just `.`,\nit is interpreted as zero.  The precision defines the minimum number\nof digits in numeric conversions. For strings, this is the maximum\nnumber of raw code units read from the input string (not the number\nof converted code points, but the low-level number of elements\nin the string, so that non-NUL terminated arrays can be printed\nwith their size passed as precision, even with multi-byte/multi-word\nencodings stored inside.  Alternatively, there is `va_span_t` for a\nstring prefix parameter type.\n\n#### End of String and Encoding Errors\n\nThe input decoder (e.g. for UTF-8) has two modes of operation,\ndistinguished by how incomplete sequences at the end of the input\nstream are handled.\n\nIn normal mode, the input decoder reads every byte up until the last\none from the input stream.  Incomplete sequences at the end will be\nreported as decoding errors.\n\nIn chunk mode, the input decoder stops before an incomplete sequence\nat the end, simply reporting the end of text.  Incomplete sequences\nwill not be iterated into, so that the stream can be printed\nchunk-wise even if encoding sequences cross the boundary between two\nchunks.\n\nThe mode is selected by the input type that is printed.  Normal mode\nis used by default.  To select chunk mode, an iterator\n(`va_read_iter_t`) or a pointer to a string (`char const **`) needs to\nbe printed.  This is because only for iterators, the printer can\nreport back how much data was read, which is required for chunk mode\nanyway, to know where to start to print the next chunk (which is where\nthe decoder stopped for the previous chunk).  The caller can read the\ndecoder stop position from the iterator.\n\nSetting up the `va_read_iter_t` manually is a bit of a mouthful, but\nit is not needed frequently, I presume, so there is no syntactic sugar\nfor it.  E.g. to print a chunk of bytes from a `char* s` in UTF-8\nformat in chunk mode, i.e., stopping before an incomplete sequence at the end,\nthe following code can be used:\n\n```c\nva_read_iter_t iter = VA_READ_ITER(\u0026va_char_p_read_vtab_utf8, s);\nva_eprintf(\"~s\", \u0026iter);\n```\nAfter that, `iter.cur` can be checked to see how many bytes were read.\n\n#### Integer Mask and Quotation Specifiers\n\nGenerally, specifying multiple identical mask and quotation specifiers\nor more than listed in the following list, like `~zzu` or `~hhhx`, is\nreserved for future use and should be avoided.  It is unspecified how\nthe current library handles such format strings.\n\n - `h` applies the mask `0xffff` to an integer, then zero extends unsigned\n   values, or sign extends signed values.\n   E.g., `va_printf(\"~#hx\",0xabcdef)` prints `-0x3211`.\n\n - `hh` applies the mask `0xff` to an integer, then zero extends unsigned\n   values, or sign extends signed values.\n   E.g., `va_printf(\"~hhX\",0xabcdU)` prints `CD`.\n\n - `z` reinterprets a signed integer as unsigned (mnemonic: zero\n   extension).  `z` is implicit in formats `u` (and `U`).\n   E.g., `va_printf(\"~hhu\", -1)` prints `255`.\n\n - `q` selects C quotation for strings and char format.  There is a\n   separate section below to explain this.\n\n - `Q` selects JSON quotation for strings and char format.  There is a\n   separate section below to explain this.\n\n - `k` selects Bourne or Korn shell quotation.  There is a\n   separate section below to explain this.\n\n - `K` additional custom quotation\n\n - `qq`, `QQ`, `kk` more additional custom quotations\n\nNote that most of the usual length specifiers (`l`, `ll`, etc.) known\nfrom C make no sense and are not recognised (nor ignored), because\ntype casting control in varargs is not needed here due to the\ntype-safety.\n\n#### Conversions\n\nThe format specifiers is terminated by a single conversion character\nfrom the following list.\n\n - `s` prints anything in default notation (mnemonic: 'standard').\n   `s` is used by standard C `printf` for strings, and CommonLisp\n   uses `~s` for 'standard' format.\n\n - `o` selects octal integer notation for numeric printing (including\n   pointers).\n\n - `d` or `i` selects decimal integer notation for numeric\n   printing (including pointers).\n\n - `u` is equivalent to `zd`, i.e., prints a signed integer as\n   unsigned in decimal notation.  This implicitly sets the `z`\n   option, which also affects quoted string printing, so `~qu`\n   prints strings like `~qzs`.\n\n - `x` or `X` selects hexadecimal integer notation for numeric\n   printing (including pointers).  `x` uses lower case digits,\n   `X` upper case.  Note that this also prints signed numbers with\n   a `-` if appropriate: `va_printf(\"~#x\", -5)` prints `-0x5`.\n   There's the `z` flag to print signed integers as unsigned.\n\n - `b` or `B` selects binary integer notation for numeric\n   printing (including pointers).  `b` uses lower case prefix,\n   `B` uses upper case.  The difference is only visible\n   with the `#` flag.\n\n - `e` or `E` selects Base32 notation using the digits\n   'a'..'z','2'..'7'.  `e` uses lower case digits and prefix,\n   `E` uses upper case.\n\n - `p` prints like `x`, toggles the `#` flag, and for any strings,\n   prints the pointer value instead of the contents.  Note that\n   it also prints signed numbers: `va_print(\"~p\",-5)` prints `-0x5`.\n\n   Note that `~#p` prints pointers like `~x` and `~p` prints like\n   `~#x`, i.e., the `#` flag is toggled.\n\n - `c` prints integers (but not pointers) as characters, like a\n   one-element string.  Note that the NUL character is not printed,\n   but behaves like an empty string, unless quotation is used.\n   For string quotation where hexadecimals are printed, this uses\n   lower case characters.\n\n - `a`, `f`, and `g` print like `s`, but will print differently when\n   floating point support is added.\n\n - `t` prints the argument type in C syntax:\n   `int8_t`..`int64_t`, `uint8_t`..`uint64_t`, `char*`,\n   `char16_t*`, `char32_t*`, `void*`.  Note that `va_error_t*`\n   arguments never print, and never consume a `~` format, but\n   always just return the stream error.\n\n - `m` prints the (error) status of the referenced item.  This is\n   for custom printers, and it is encoded as `VA_MODE_STAT`.\n   The pre-defined data types have no error values and thus no\n   error is printed.  Retrieving the stream error using `va_error_t`\n   is another topic, as it does not print anything.\n   The letter `m` is inspired by the GNU extension `%m`, which prints\n   `strerror(errno)`, which this library does not support natively\n   (to avoid depending on `\u003cerrno.h\u003e`).\n\n - `~` prints `~` characters.  By default, one is printed.  The\n   width gives the number of tildes, e.g. `~5~` prints `~~~~~`,\n   and `~0~` prints nothing.  `~*~` reads the width from an\n   argument.  The use of precision and justification flags is\n   reserved for future use, and it is unspecified how the library\n   handles them.\n\n - any letter mentioned above in lowercase only also exists in\n   uppercase, and then prints whatever is usually printed in lowercase\n   in uppercase, like like hexadecimal digits or numeric base prefixes\n   like `0B` or `0X`.\n\n - any format character not mentioned above is reserved for future\n   use.  If used, the argument is skipped, and the `VA_E_FORMAT` error\n   is set in the stream.\n\n - any combination of format character and type not mentioned above\n   prints in default notation.\n\n## Parameter Types\n\nThe following function parameter types are recognised.  Note that\nenums are not listed here, because of the weak type system of C, where\nenum constants have type 'int' and enum types match type 'int' in\n_Generic.\n\n - `int`, `unsigned`, `char`, `signed char`, `unsigned char`, `short`,\n   `unsigned short`, `long`, `unsigned long`, `long long`,\n   `unsigned long long`: these are integer and are printed\n   in unsigned or signed decimal integer notation by default.\n\n   This means that `char`, `char16_t`, and `char32_t` all print in\n   numeric format by default, not in character format, as they are not\n   distinct types.  For interpreting them as a 1-element Unicode\n   codepoint string, `c` format should be used.\n\n   Also note that character constants like `'a'` have type `int` in C\n   and print numerically by default.\n\n - `_Bool` (or `bool` with `\u003cstdbool.h\u003e`): prints a boolean type.\n   This is the only enum in C that does not match an `int` type in\n   `_Generic`, so it is supported.  Note that `true` and `false` still\n   have type `int` and not `bool`, so only variables of type `bool`\n   will print in boolean mode.  This prints `true` or `false` by\n   default or `0` and `1` if a numeric format is used: `d`, `u`,\n   `x`, `o`, `b`.\n\n - `char *`, `char const *`: 8-bit character strings or\n   arrays.  They print as is by default.\n\n   The default string encoding is UTF-8,  It can be reset to\n   a user encoding by #defining `va_char_p_decode`.  Also see\n   the section on encoding below.\n\n   Unquoted, `NULL` prints empty and sets the `VA_E_NULL` error.\n   Also see the section on quotation below.\n\n   If the input decoder encounters an incomplete UTF-8 sequence right\n   in front of the terminating `NUL` character, it will return the\n   bytes of the incomplete sequence as decoding errors.  However, if a\n   precision, i.e., maximum string size is specified, it can be made\n   to stop decoding before the incomplete sequence without a decoding\n   error.  This way, strings can be printed in chunks without errors.\n   To trigger this behaviour, a pointer to a string or an iterator can\n   be used, so the final string position, i.e., the first byte of the\n   incomplete sequence at the end, can be queried in order to start a\n   new string chunk with the incomplete sequence at the beginning,\n   followed by, hopefully, the missing bytes of the UTF-8 sequence\n   from the next chunk.\n\n - `char16_t *`, `char16_t const *`: 16-bit character strings or\n   arrays.  The default encoding is UTF-16, which can be\n   switched using `va_char16_p_decode`.\n\n   Unquoted, `NULL` prints empty and sets the `VA_E_NULL` error.\n\n   If the input decoder encounters an high UTF-16 surrogate right in front\n   of the terminating `NUL` character, it will return the high surrogate\n   as a decoding error.  However, if a precision, i.e., maximum string\n   size is specified, it will stop decoding before the high surrogate.\n   This can be used for chunked printing like with UTF-8.\n\n - `char32_t *`, `char32_t const *`: 32-bit character strings or\n   arrays.  The default encoding is UTF-32, which can be\n   switched using `va_char32_p_decode`.\n\n   Unquoted, `NULL` prints empty and sets the `VA_E_NULL` error.\n\n - `Char **`, `Char const **`: pointers to pointers to\n   characters, i.e., pointers to string, will print the string\n   and then update the pointer to point to the code\n   unit just behind the last one that was read from the\n   string.  With no precision given in the format, they will\n   point to the terminating NUL character.  When these\n   parameters are printed multiple times using the `=` flag,\n   the string will be reset each time and the updated value\n   will correspond to the end position during the last print\n   of the string.\n\n - `va_error_t*`: this retrieves the error code from the\n   stream and writes it into the passed struct.  This can\n   be used to check for encoding or decoding errors, out\n   of memory conditions, or hitting the end of the output\n   array.  `NULL` must not be passed as a pointer.\n\n - `va_read_iter_t*`: this is an internal type to read from\n   strings.  There are quite a few constraints on how to\n   define a proper `va_read_iter_t`, which are not all\n   documented here.\n\n - `va_span_t*`: this is a length delimited string for printing\n   non-NUL terminated strings or prefixes of strings.  It is\n   an alternative way to specify the string size in the argument\n   directly instead of using the precision in the format specifier.\n   When strings are specified this way, embedded U+0000 (NUL)\n   characters are passed down, so quotation may print them as\n   \\u0000 or \\000 or similar.  NUL characters are never printed\n   verbatim into the output stream, however, because the output\n   stream is assumed to be text.\n\n - `va_span16_t*`: the same as `va_span_t`, but for `char16_t` strings.\n\n - `va_span32_t*`: the same as `va_span_t`, but for `char32_t` strings.\n\n - `va_print_t*`: user-defined printer for a value of an arbitrary\n   type (there is a separate chapter on this, below).\n\n - anything else: is tried to be converted to a pointer and\n   printed in hexadecimal encoding by default, i.e., in `~x`\n   format.\n\n## Library Modules\n\n### Printing Into Fixed Size Arrays\n\n```c\n#include \u003cva_print/char.h\u003e\n```\n\nTo print into an string of characters up to a given number of elements\nin the array, the following function can be used, and it returns the\npointer to the string.  The resulting string is always NUL terminated,\ni.e., the maximum string length is one less than the passed element\ncount.\n\n```c\nchar s[20];\nchar *t = va_snprintf(s, sizeof(s), \"foo~s\", 5);\nassert(s == t);\n```\n\nThe target buffer's type may be 8, 16, or 32 bit characters -- no need\nto use a different function.  For anything but `char` buffers, use\n`va_countof()` for the array size, so that the number of elements, not\nthe number of bytes, is used as the string size.\n\n\n```c\nchar16_t s1[20];\nchar16_t *t1 = va_snprintf(s1, va_countof(s1), \"foo~s\", 5);\n\nchar32_t s2[20];\nchar32_t *t2 = va_snprintf(s2, va_countof(s2), \"foo~s\", 5);\n```\n\nThe `va_countof()` values is inferred when using `va_sprintf`. This\nis different from the standard C `sprintf` function, which unsafely\nassumes a sufficiently large string -- you cannot express that with\nthis library, but you'd have to use `snprintf` with a large size\ninstead.\n\n```c\nchar s[20];\nchar *t = va_sprintf(s, \"foo~s\", 5);\n```\n\nIt is possible to print into a compound literal of a given\nsize and return the pointer to that string.  In this case,\nno character array can be used to infer the string type,\nso it is encoded in the function name.  There are functions\nfor `char`, `char16_t`, and `char32_t` strings, as well as\na generic version that takes as first argument the string\ncharacter type.\n```c\nchar     *t1a = va_nprintf (20, \"foo~s\", 5);\nchar16_t *t2a = va_unprintf(20, \"foo~s\", 5);\nchar32_t *t3a = va_Unprintf(20, \"foo~s\", 5);\nchar     *t1b = va_gnprintf(char, 20, \"foo~s\", 5);\nchar16_t *t2b = va_gnprintf(char16_t, 20, \"foo~s\", 5);\nchar32_t *t3b = va_gnprintf(char32_t, 20, \"foo~s\", 5);\n```\n\nThe stream for printing can also be generated separately and then used\nfor iterative printing using `va_iprintf`.  The stream makes sure not\nto print past the end of the char array.\n\n```c\nchar buff[20];\nva_stream_char_p_t stream = VA_STREAM_CHAR_P(buff, va_countof(buff));\nva_iprintf(\u0026stream, \"foo\");\nva_iprintf(\u0026stream, \"bar ~u\", 55);\nva_iprintf(\u0026stream, \"longer than the string, will be cropped\");\n...\n```\n\nThere is a `stream.pos` counter for the current write index in the\narray, i.e., the string length of the encoded byte sequence.  `pos`\nincrements up to `stream.size-1`, but no further (note that `size==0`\nis an illegal configuration, because then there is no space for NUL\nterminating the string).\n\nCreating a `va_stream_char_p_t` with the buffer equal to `NULL` is\nexplicitly allowed.  Putting the bytes into a char array will then be\ninhibited.  The printer still increments `stream.pos` up to\n`stream.size-1`, so by this, `strnlen()` functionality can be\nimplemented on the resulting string.  This is exactly how `va_zprintf`\n(mnemonic: `siZe`) works.\n\nTo determine whether the stream was truncated, i.e., whether the\nbuffer was too small for the print result, the stream's error code can\nbe checked for the `VA_E_TRUNC` error code value after printing is\ndone.\n\n```c\n...\nva_error_t e;\nva_iprintf(\u0026stream, \"\", \u0026e);\nif (e.code != VA_E_OK) {\n    /* ... some stream error occurred ... */\n}\n...\n```\n\nAlternatively, if you have a stream anyway, there is\n`va_stream_get_error()` that returns the stream's error code.\nThere is also `va_strerror()` to get the enum value as a string.\n\n```c\n...\nunsigned ec = va_stream_get_error(\u0026stream);\nif (ec != VA_E_OK) {\n    va_fprintf(stderr, \"ERROR: found ec=~s\\n\", va_strerror(ec));\n    /* ... more error handling */\n}\n```\n\nNote that there is no `%n` equivalent format specifier for reading the\nprinted length; use `stream.pos` instead.  Or use `va_zprintf` to\ncompute the needed array size.\n\nThere is also `va_lprintf` to count the length of the string, i.e.,\nthe number of codepoints written, instead of the number of encoded\nbytes.\n\n### Printing Into Growing Vectors\n\n```c\n#include \u003cva_print/alloc.h\u003e\n```\n\nIt is possible to print into a string that is allocated grows using\n`malloc()`:\n\n```c\nchar *c = va_asprintf(\"foo~s\", msg);\n...\nfree(c);\n```\n\nHere, the `va_alloc()` function is implicitly used to allocate,\npossibly reallocate while printing, and possibly freeing the string in\ncase of an out-of-memory error.\n\nFor `char16_t*` and `char32_t*` target strings, there are\n`va_uasprintf` and `va_Uasprintf`, resp.\n\n`va_alloc` is a wrapper around `realloc` and `free`.  Any compatible\nfunction with the same prototype can be used instead.\n\nA user defined allocation function can be supplied by using the\n`va_axprintf` function, which is just like `va_asprintf`, but takes\nthe allocator function of type `void *(void *, size_t nmemb, size_t\nsize)` as parameter, which is used for allocation, reallocation, and\nfreeing (with `nmemb==0`).  For `char` strings, the function is\ninvoked with `size==1`.\n\n```c\nchar *c = va_axprintf(va_alloc, \"foo~s\", msg);\n...\nfree(c);\n```\n\nFor `char16_t` and `char32_t` output strings, there is `va_uaxprintf`\nand `va_Uaxprintf`, resp.  The allocator function will then be invoked\nwith a `size==2` for `char16_t` and `size==4` for `char32_t`.\n\nIt is also possible to create a stream for iterative printing.\n\n```c\nva_stream_vec_t stream = VA_STREAM_VEC(va_alloc);\nva_iprintf(\u0026stream, \"foo\");\nva_iprintf(\u0026stream, \"bar ~u\", 55);\n```\n\nFor 16 and 32 bit chars, there is `va_stream_vec16_t` plus\n`VA_STREAM_VEC16` and `va_stream_vec32_t` plus `VA_STREAM_VEC32`.\n\n### Printing Into Files\n\n```c\n#include \u003cva_print/file.h\u003e\n```\n\nTo print into `FILE*` files, there is `va_fprintf`, which returns\nnothing.\n\n```c\nva_fprintf(stderr, \"foo~s\", msg);\n```\n\nThere is also `va_printf` that prints into `stdout` and `va_eprintf`\nthat prints into `stderr`.  They also both return nothing.\n\n```c\nva_printf(\"foo~s\", msg);\nva_eprintf(\"foo~s\", msg);\n```\n\nTo write `char16_t` or `char32_t` streams into files, the encodings\n`UTF-16BE` and `UTF-32BE` are used by default.  Functions for this\nare called `va_ufprintf` and `va_Ufprintf`, resp.\n\nFor files, there is a stream type `va_stream_file_t` that can be\nconstructed using `VA_STREAM_FILE`, e.g., to iteratively print.\n\n```c\nva_stream_file_t stream = VA_STREAM_FILE(stderr);\nva_iprintf(\u0026stream, \"foo\");\nva_iprintf(\u0026stream, \"bar ~u\", 55);\nva_iprintf(\u0026stream, \"longer than the string, will be cropped\");\n...\n```\n\nThe 16-bit and 32-bit versions use the same stream type, and the\nconstructors are called `VA_STREAM_FILE16` and `VA_STREAM_FILE32`,\nresp.  `\n\n\n### Printing Into Raw File Descriptors\n\n```c\n#include \u003cva_print/fd.h\u003e\n```\n\nTo print into `int` typed file descriptors, there is `va_dprintf`, which\nreturns nothing.\n\n```c\nva_dprintf(2, \"foo~s\", msg);\n```\n\nTo write `char16_t` or `char32_t` streams into files, the encodings\n`UTF-16BE` and `UTF-32BE` are used by default.  Functions for this\nare called `va_udprintf` and `va_Udprintf`, resp.\n\nFor file descriptors, there is a stream type `va_stream_fd_t` that\ncan be constructed using `VA_STREAM_FD`, e.g., to iteratively print.\n\n```c\nva_stream_fd_t stream = VA_STREAM_FD(2);\nva_iprintf(\u0026stream, \"foo\");\nva_iprintf(\u0026stream, \"bar ~u\", 55);\nva_iprintf(\u0026stream, \"longer than the string, will be cropped\");\n...\n```\n\nThe 16-bit and 32-bit versions use the same stream type, and the\nconstructors are called `VA_STREAM_FD16` and `VA_STREAM_FD32`, resp.\n`\n\n### Printing non-NUL Terminated Strings\n\nOne way to print non-NUL terminated strings or prefixes of strings\nis by specifying the 'precision' in the format.  It is the length\nin `char`, `char16_t`, or `char32_t` elements and not the number of\nextracted codepoints, exactly for this purpose.\n\n```c\nchar const *data = \"abcdef\";\nsize_t size = 3;\nva_fprintf(stderr, \"token=~.*qs\", size, data);\n```\nThis prints `token=\"abc\"`.\n\nAn alternative way of controlling this is to pass a pointer to\n`va_span_t` to the printer, which contains the data and size:\n\n```c\nchar const *data = \"abcdef\";\nsize_t size = 3;\nva_fprintf(stderr, \"token=~qs\", (\u0026(va_span_t){ size, data }));\n```\n\nThis also prints `token=\"abc\"`.\n\nNote that strings specified by their size may contain U+0000 (NUL)\ncharacters, and they are quoted accordingly, if requested:\n\n```c\nva_fprintf(stderr, \"token=~qs\", (\u0026(va_span_t){ 1, \"\" }));\n```\n\nThis also prints `token=\"\\000\"`.\n\nThere are similar types `va_span16_t` and `va_span32_t` for wide\ncharacter strings.\n\n### Computing String Lengths\n\n```c\n#include \u003cva_print/len.h\u003e\n```\n\nThe function `va_lprintf` returns the number of codepoints printed\ninto an output stream.  This is the string length regardless of output\nencoding.\n\n```c\nsize_t cp_count = va_lprintf(\"foo~s\", msg);\n```\n\nThis is not a good function for computing array sizes -- use the\n`va_zprint` family instead.\n\n\n### Computing String Array Sizes\n\n```c\n#include \u003cva_print/char.h\u003e\n```\n\nTo compute the size of the array needed to store a given printed\nstring, there is `va_zprintf`.\n\n```c\nsize_t n = va_zprintf(\"foo~s\", msg);\nchar *s = malloc(n);\nva_error_t e;\nva_snprintf(s, n, \"foo~s\", msg, \u0026e);\nassert(e.code == VA_E_OK);\n```\n\nThis function counts the encoded size of the needed array, i.e., it\nalso includes the `NUL` character in the count, and it counts for each\ncodepoint, how many UTF-8 (or whatever encoding is used) bytes are\nused for each codepoint.  This function is, therefore, useful for\ncomputing array sizes that fit the printed string exactly.\n\nFor `char16_t` and `char32_t` based strings, the function is called\n`va_uzprintf` and `va_Uzprintf`, resp.\n\nThere is a generic version that can be passed the array element\ntype as the first parameter.\n\n```c\ntypedef SomeCharacterType MyChar;\n...\nsize_t n = va_gzprintf(MyChar, \"foo~s\", msg);\nMyChar *s = malloc(sizeof(MyChar) * n);\nva_snprintf(s, n, \"foo~s\", msg);\n```\n\n## Unicode\n\nInternally, this library uses 32-bit codepoints with 24-bit payload\nand 8-bit tags for processing strings, and by default, the payload\nrepresentation is Unicode.  The library tries not to interpret the\npayload data unless necessary, so that other encodings could in\nprinciple be used and passed through the library.\n\nThe only place the core library uses Unicode interpretation is when\nquoting C or JSON strings for codepoints \u003e0x80 (e.g., when formatting\nwith `~0qs`), and if a decoding error is encountered or if the value\nis not valid Unicode, then it uses \\ufffd to show this, because the\nquotation using \\u or \\U would otherwise be a lie.\n\nThe internal representation allows any value within 24 bits to be used\nfor codepoints.  0 is interpreted as 'end of string' and is never\nprinted into the output stream.\n\nUTF-8, -16, and -32 encoders and decoders check that the Unicode\nconstraints are met, like excluding anything above 0x10FFFF and high\nand low UTF-16 surrogates, and detecting decoding errors according to\nthe Unicode recommendations and best practices.  The encoder/decoder\npairs usually try to pass through faulty sequences as is, if possible,\ne.g., reading ISO-8859-1 data from an UTF-8 `~s` and printing it into\nan UTF-8 output stream preserves the original ISO-8859-1 byte\nsequence, although the intermediate steps do raise 'illegal sequence'\nerrors.\n\nIntegers print without Unicode checks, i.e., if an integer is printed\nas a character using `~c`, then the lower 24 bits is passed down to\nthe output stream encoder as is.  If integers larger than 0xffffff are\ntried to be printed with `~c`, this results in a decoding error, and\nonly the lower 24 bits are used.\n\n## Encodings\n\nThe library supports different string encodings for the format string,\nfor input strings, and for output streams.  The defaults are UTF-8,\nUTF-16, or UTF-32.  This can be switched by setting the following\n#defines before including headers of this library, i.e., it cannot be\nswitched dynamically out of the box, because this would mean that all\nthe encoding modules would always be linked.  Dynamic switching can\nbe added by defining a new encoding that internally switches dynamically.\n\nThe following #defines switch function names:\n\n### Format String Encoding\n\nThe default is UTF-8, -16, or -32 encoding, and it can be changed\nby #defining before `#include \u003cva_print/...\u003e`:\n\n    #define va_char_p_format utf8\n    #define va_char16_p_format utf16\n    #define va_char32_p_format utf32\n\nThese macros are appended to an identifier to find the appropriate\nreader for the format string as follows:\n\n    va_char_p_read_vtab ## va_char_p_format\n    va_char16_p_read_vtab ## va_char16_p_format\n    va_char32_p_read_vtab ## va_char32_p_format\n\nWhen using a different encoding than the default, it must be ensured\nthat the corresponding vtab declarations are visible.\n\n### String Value Encoding\n\nThe default for reading string values is UTF-8, -16, or -32 encoding,\nfor `\"...\"`, `u\"...\"`,and `U\"...\"` strings,resp.  The default can be\nchanged by defining one of the following macros before `#include \u003cva_print/...\u003e`:\n\n    #define va_char_p_decode utf8\n    #define va_char16_p_decode utf16\n    #define va_char32_p_decode utf32\n\nThese macros are appended to an identifier to find the appropriate\nreader for the string value as follows:\n\n    va_xprintf_char_p_ ## va_char_p_decode\n    va_xprintf_char_pp_ ## va_char_p_decode\n    va_xprintf_char_const_pp_ ## va_char_p_decode\n    va_xprintf_char16_p_ ## va_char16_p_decode\n    va_xprintf_char16_pp_ ## va_char16_p_decode\n    va_xprintf_char16_const_pp_ ## va_char16_p_decode\n    va_xprintf_char32_p_ ## va_char32_p_decode\n    va_xprintf_char32_pp_ ## va_char32_p_decode\n    va_xprintf_char32_const_pp_ ## va_char32_p_decode\n\nNote that for each parameter type, a different printer function is used,\nso for a different encoding, three functions need to be provided.  A\ntypical such function implementation looks as follows:\n\n    va_stream_t *va_xprintf_char_p_utf8(\n        va_stream_t *s,\n        char const *x)\n    {\n        va_read_iter_t iter = VA_READ_ITER(\u0026va_char_p_read_vtab_utf8, x);\n        return va_xprintf_iter(s, \u0026iter);\n    }\n\n### Output Stream Encoding\n\nFor encoding strings into character arrays, the default encoding is\nUTF-8, UTF-16, or UTF-32, depending on the string type.  To override\nthe default, the following #defines can be set\nbefore `#include \u003cva_print/...\u003e`.\n\n    #define va_char_p_encode utf8\n    #define va_char16_p_encode utf16\n    #define va_char32_p_encode utf32\n\nThese are suffixed to find the vtab object for writing:\n\n    va_char_p_vtab_ ## va_char_p_encode\n    va_char16_p_vtab_ ## va_char16_p_encode\n    va_char32_p_vtab_ ## va_char32_p_encode\n\nFor dynamically allocated arrays, there are separate #definitions:\n\n    #define va_vec8_encode utf8\n    #define va_vec16_encode utf16\n    #define va_vec32_encode utf32\n\nThese are suffixed to find the vtab object for writing:\n\n    va_vec_vtab_ ## va_vec_encode\n    va_vec16_vtab_ ## va_vec16_encode\n    va_vec32_vtab_ ## va_vec32_encode\n\nFor `FILE*` output, the default encoding is UTF-8, UTF-16BE,\nand UTF-32BE, depending on output character width.  The\nfollowing #defines correspond to the encoding:\n\n    #define va_file8_encode utf8\n    #define va_file16_encode utf16be\n    #define va_file32_encode utf32be\n\nThese are suffixed to find the vtab object for writing:\n\n    va_file_vtab_ ## va_file_encode\n    va_file16_vtab_ ## va_file16_encode\n    va_file32_vtab_ ## va_file32_encode\n\nFor `int` file descriptor output, the default encoding is UTF-8,\nUTF-16BE, and UTF-32BE, depending on output character width.  The\nfollowing #defines correspond to the encoding:\n\n    #define va_fd8_encode utf8\n    #define va_fd16_encode utf16be\n    #define va_fd32_encode utf32be\n\nThese are suffixed to find the vtab object for writing:\n\n    va_fd_vtab_ ## va_fd_encode\n    va_fd16_vtab_ ## va_fd16_encode\n    va_fd32_vtab_ ## va_fd32_encode\n\n## Quotation\n\n### C/C++ quotation\n\n- `q` quotation option in format specifier\n- when printing integers, this is ignored\n- when printing pointers, this adds the `#` flag, i.e., the\n  `0x` prefix is printed\n- when printing strings, this selects C format quoted output\n- `NULL` strings print as `NULL`, and do not set the\n  `VA_E_NULL` error, in contrast to unquoted printing.\n- without `#`, prints quotation marks, single for `c` and `C`,\n  conversion, otherwise double.\n- with `z` prints the string size indicator based on the input\n  string: empty for `char`, `u` for `char16_t`, and `U` for\n  `char32_t` (and also `U` for 64-bit ints).\n- quotation of unprintable characters \u003cU+0080 is done using\n  octal quotation.\n- quotation of some characters in special notation:\n  `\\t`, `\\r`, `\\n`, `\\'`, `\\\"`, `\\\\`.\n- `0` flag quotes all non-ASCII using `\\u` or `\\U`.  Note\n  that `\\x` is not used, because it may not terminate, so\n  quoting `\\x1` plus `1` is more complicated.\n- with `0` flag, chars that are marked as decoding errors are\n  quoted as `\\ufffd`, the replacement character, to avoid\n  printing encoding errors with `\\u` quotation, which would\n  make the resulting string more wrong than with only the\n  encoding errors.  Without `0` flag, encoding errors are\n  passed through if the input encoding equals output\n  encoding, otherwise `U+FFFD` is encoded.\n- upper case formats use upper case letters in hexadecimals\n\nExamples:\n\n- `va_printf(\"~qs\", \"foo'bar\")` prints `\"foo\\'bar\"`.\n- `va_printf(\"~qs\", \"foo'bar\")` prints `\"foo\\'bar\"`.\n- `va_printf(\"~qzs\", u\"foo'bar\")` prints `u\"foo\\'bar\"`.\n- `va_printf(\"~qc\", 10)` prints `'\\n'`.\n- `va_printf(\"~qzc\", 10)` prints `U'\\n'`\n- `va_printf(\"~#qc\", 16)` prints `\\020`.\n- `va_printf(\"~#0qc\", 0x201c)` prints `\\u201c`.\n- `va_printf(\"~#0qC\", 0x201c)` prints `\\u201C`.\n- `va_printf(\"~qa\", (void*)18)` prints `0x12` (on normal machines)\n- `va_printf(\"~qa\", 18)` prints `18`\n- `va_printf(\"~0qa\", u\"\\xd801\")` prints `\"\\xfffd\"`\n\n### Java/JSON quotation\n\n- `Q` quotation option in format specifier\n- Like C, but always uses `\\u` or `\\U` and never octal\n- `NULL` strings print as `null`, and do not set the\n  `VA_E_NULL` error, in contrast to unquoted printing.\n- the `z` flag is ignored (no `u` or `U` prefixes are printed).\n\nExamples:\n\n- `va_printf(\"~Qs\", \"foo'bar\")` prints `\"foo\\'bar\"`.\n- `va_printf(\"~Qc\", 10)` prints `'\\n'`.\n- `va_printf(\"~#Qc\", 16)` prints `\\u0010`.\n- `va_printf(\"~#0Qc\", 0x201c)` prints `\\u201c`.\n- `va_printf(\"~#0QC\", 0x201c)` prints `\\u201C`.\n- `va_printf(\"~Qa\", (void*)18)` prints `0x12` (on normal machines)\n- `va_printf(\"~Qa\", 18)` prints `18`\n\n### Bourne Shell quotation\n\n- `k` quotation option in format specifier (mnemonic: Korn\n  Shell quotation)\n- when printing integers, this is ignored\n- when printing pointers, this adds the `#` flag, i.e.,\n  the `0x` prefix is printed\n- when printing strings, this selects Shell quoted format\n- `NULL` strings print as empty string, and set the\n  `VA_E_NULL` error, just like unquoted printing.\n- the empty string is printed as `''`\n- uses single quotes if necessary\n- without `#`, prints quotation marks if necessary\n- others print no quotation marks for in-string printing\n- this actually quotes nothing except the single quotation\n  mark.\n- chars marked as decoding errors are not quoted, but passed\n  through.\n\nExamples:\n\n- `va_printf(\"~ks\", \"ab\")` prints `ab`.\n- `va_printf(\"~ks\", \"\")` prints `''`.\n- `va_printf(\"~#ks\", \"\")` prints ``.\n- `va_printf(\"~ks\", \"a b\")` prints `'a b'`.\n- `va_printf(\"~ks\", \"a'b\")` prints `'a'\\''b'`.\n- `va_printf(\"~#ks\", \"a'b\")` prints `a'\\''b`.\n- `va_printf(\"~ka\", (void*)18)` prints `0x12` (on normal machines)\n- `va_printf(\"~ka\", 18)` prints `18`\n\n### User-Defined Quotation\n\nThe quotation mechanism of the library can be extended by own\nquotation techniques.  The API for this is currently preliminary\nand may change.\n\nFor implementing custom quotation, the `impl.h` header file needs to\nbe included to get access to the internal programming API:\n\n```c\n#include \u003cva_print/impl.h\u003e\n```\n\nThere is a definition of a struct `va_quotation_t` which has three\nentries to be defined for a quotation mechanism:\n\n- `unsigned delim[2]`: the delimiter with which to quote. array index [0]\n  is used for characters and index [1] is used for string\n  quotation.  The `VA_DELIM(prefix, frontquote, backquote)` macro constructs\n  an entry.  The quotation characters `frontquote` and `backquot` must be\n  in the BMP, i.e., smaller than or equal to `U+FFFF`.  The `prefix` character\n  must be smaller than or equal to U+00FF.  If it is 0xff, then the\n  prefix is selected if the `z` modifier is used based on the string character\n  type: empty for `char`, `u` for `char16_t` and `U` for `char32_t`.\n\n- `bool (*check_quote)(va_stream_t *s, unsigned c)`: if NULL, quotation is\n  always used. If non-NULL, this function is used on each character of the\n  string to check whether quotation is needed.  If the function returns\n  non-false for any of the characters, then quotation is needed.\n\n- `bool (*check_flush)(va_stream_t *s)`: if non-NULL, will be invoked at the\n  end of the string quotation check (only if check_quote is non-NULL) to\n  check again whether quotation is needed.  `check_quote` and `check_flush`\n  may use `s-\u003eqctxt` for storing some state, e.g., for detecting and empty\n  string (which needs quotation in Shell quotation).\n\n- `void (*render_quote)(va_stream_t *s, unsigned ch)`: for the actual\n  quotation of a character.  This must invoke one of the `va_stream_render*()`\n  functions for writing the quoted representation of the character into\n  the output stream.  The renderer can store context in `s-\u003eqctxt`, an\n  `unsigned`, during quotation rendering.  It is initialised to `0`\n  when rendering starts.\n\n- `void (*render_flush)(va_stream_t *s)`: callback for the quotation to\n  signal the end of the quoted string.  Maybe NULL if not needed.\n\nThere are the following rendering functions for the `render_quote` method:\n\n- `va_stream_render(s,c)`: prints the character verbatim into the output\n  stream.  Note that this cannot be done manually by a different function,\n  because this function also contains the logic for counting string\n  widths, etc.\n- `va_stream_render_quote_u(s,c)`: prints as `\\u0123` or `\\U01234567` in\n  hexadecimal notation\n- `va_stream_render_quote_oct(s,c)`: prints as `\\012` in octal notation.\n\nFor setting a quotation technique, a `va_quotation_t` needs to be\ninitialised and set using `va_quotation_set()`.\n\n```c\nstatic va_quotation_t const my_quotation = {\n    .delim = { VA_DELIM(0, '\u003c', '\u003e'), VA_DELIM(0, '|', '|') },\n    .render_quote = my_render_quote,\n};\nvoid my_init(void)\n{\n    va_quotation_t const *old = va_quotation_set(VA_QUOTE_qq, \u0026my_quotation);\n}\n```\n\nThis sets the `qq` prefix to use `my_quotation` as a quotation method.\nThere are currently 8 different quotation method slots:\n\n- `0`: the default if no `q`, `Q`, `k`, or `K` modifier is specified\n- `VA_QUOTE_q`: used if the modifier `q` is given\n- `VA_QUOTE_Q`: used if the modifier `Q` is given\n- `VA_QUOTE_k`: used if the modifier `k` is given\n- `VA_QUOTE_K`: used if the modifier `K` is given\n- `VA_QUOTE_qq`: used if the modifier `qq` is given\n- `VA_QUOTE_QQ`: used if the modifier `QQ` is given\n- `VA_QUOTE_kk`: used if the modifier `kk` is given\n\nThe prefixes `q`, `k`, and `Q` are predefined as described in the\nprevious sections.  The others do not quote by default and are free\nfor adding user quotation methods.  Note that is possible to set\nquotation 0 so that it is used when no quotation modifier is given.\n\n## User Defined Printers\n\nFor user types, printers can be defined so that you do not need to\nprint into an intermediate string buffer, but you can directly print\ninto the output stream.  This saves space for the temporary string\nbuffer and avoids thinking about buffer sizes.\n\nThe library provides the type `va_print_t*` that can be passed as an\nargument to the printing functions instead of the value itself.  This\nprovides the library with a user-defined callback for printing, and\nalso encapsulates the value to be printed.\n\n`va_print_t` has a callback function `void (*print)(va_stream_t *,\nva_print_t*)` that the user can fill in to print the user value.  The\nfunction can make use of `va_iprintf` to stringify the value.  The\noriginal stream's format is passed via `width`, `prec`, and `opt`\nvalue in the `va_print_t` struct so that the printer can query them.\n\nTo define a printer for a custom type, a derived struct of\n`va_print_t` can be used to encapsulate the value, or the provided\n`value` can be used to store store a pointer you your value into\n`va_print_t::value` if that is sufficient information.  E.g., for a\nsimple pair of integers:\n\n```C\ntypedef struct {\n    unsigned a,b;\n} my_pair_t;\n```\n\na printing function is defined for values of this user type:\n\n```C\nstatic void my_print_pair(va_stream_t *s, va_print_t *p)\n{\n    my_pair_t const *v = p-\u003evalue;\n    va_iprintf(s, \"(.a=~s .b=~s)\", v-\u003ea, v-\u003eb);\n}\n```\n\nWhen this is in place, to make the invocation easy, it is a good idea\nto encapsulate the creation of a temporary `va_print_t` into a macro.\nThe following macro uses an `({...})` block to implement a type check,\nbecause `va_print_t::value` is a void pointer.\n\n```C\n#define P_PAIR(_v) (\u0026VA_PRINT(\u0026my_print_pair,({my_pair_t const *v_=(v); v_;}))\n```\n\nWith these definitions, values of the user type can be printed:\n\n```C\nmy_pair_t pair = { 1, 2 };\nva_fprintf(stderr, \"pair=~s\\n\", P_PAIR(\u0026pair));\n```\n\nThis prints `pair=(.a=1 .b=2)` with the above definitions.\n\nThe custom printer framework handles width and quotation just like with\nnormal string types.\n\n```C\nva_fprintf(stderr, \"quote(pair)=~-10qs\\n\", P_PAIR(\u0026pair));\n```\n\nIn contrast to the width, alignment, and quotation, The print\nprecision is not handled by the framework -- the print function needs\nto handle it, because the semantics of a precision depends on the\ntype.  Also, `NULL` values, are not handled specially, but such values\nare printed via the normal mechanism -- the framework does not examine\nthe `va_print_t::value` at all.\n\nDifferent print formats must be handled by the user print function,\ne.g., for printing the type or a pointer values -- nothing of this is\ndone by the framework.  E.g., with `VA_BGET(p-\u003eopt, VA_OPT_MODE)`, the\nmode can be queries and one of the `VA_MODE_*` constants can be\nchecked.  See `va_print/impl.h` for the implementation API to access\nthe format options.\n\nIn the `P_PAIR` macro above, one could apply some `_Generic` magic to\nselect the right printer for a given object type, maybe even to\nimprove on the `({...})` type checking.\n\n## Extensions\n\n- This is type-safe, i.e., printing an int using \"~s\" will not\n  crash, but just print the integer.\n\n- `~b` and `~B` print binary, with optional `0b` or `0B` prefix.\n\n- `~e` and `~E` print integers in Base32, with optional `0e` or `0E`\n  prefix.  This could be handy for writing error codes: 0EINVAL,\n  0EAGAIN, 0EIO, ...\n\n- any meaningless format specifier (=letter) defaults to 'print in\n  natural default form'.  It is recommended to use `~s` for default\n  format printing of anything.\n\n- The `=` modifier prints the last value again, possibly with a\n  different format.  Note that the format containing `=` should not\n  contain any `*`, because then the width/precision will be\n  printed, not the last value, which is probably not what you want.\n\n- The `q`, `Q`, `k`, and `K` modifiers mark different kinds of quotation.\n  `q` is for C, `Q` is for Java/JSON, and `k` for Bourne/Korn Shells.\n\n- The `t` format prints the input value type in C syntax.\n\n- The `m` format is a custom format to print the status of an object,\n  usually for the error status.\n\n## Differences\n\n- The `~x` specifier also prints negative signed numbers, again, due\n  to type-safety.  Reinterpreting them as unsigned can be done with\n  the `z` flag.\n\n- The format specifiers are not needed to prevent the program from\n  crashing, because the information about the type that is passed is\n  not needed.  The format really only specifies 'print like ...', so\n  by default it is recommended to just print with `~s`.\n\n- Due to the type-safety, most length modifiers are not supported nor\n  needed.  See `h`, `hh`, and `z` modifiers.\n\n- for strings, the precision counts the number of output bytes in the\n  standard, but in this library, it is the number of input elements in\n  the array, i.e., the precision specifies the array size the string\n  points to.  It is felt that input count is more useful, because\n  it allows to print non-NUL terminated strings, while the output\n  width can be controlled by a delimited printer.  Also, different\n  glyph widths in Unicode means that the visual width cannot really\n  be controlled by the output count, either.\n\n- for strings, the width counts the number of characters that are\n  printed, before encoding them in the output encoding.  This\n  includes all characters needed for quotation.\n\n- This library assumes that text is printed, not binary, so it will\n  not output plain `\\0`.\n\n## Restrictions\n\n- `%n` is not implemented, because pointers to integers are already\n  used for strings, and the ambiguity between `size_t*` and\n  `char32_t*` is common on many 32-bit systems, where both are\n  `unsigned*` in C.  Distinguishing whether to read or to write based\n  on the format string alone is also the opposite of what this library\n  tries to do, and accidentally writing the print size into an\n  `char32_t*` string is a weird bug I'd rather not make possible.\n\n- `m$` syntax for reordering format strings is not supported, because\n  it would require storing the parameters in an array and would\n  counteract all the magic of the recursive expressions.  This would\n  make the code much more complex and stack usage infeasible.  In\n  fact, it would probably make the whole point of this library\n  infeasible.  There is the extended `=` option for at least printing\n  the same value multiple times, so `~d ~=#x` prints the same value\n  decimal and hexadecimal, and `~qs ~=p` prints a string in C quotation\n  and its pointer value.\n\n- no floats, because support would be too large for a small library.\n  Maybe it is added later -- it could be in a separate .o file that\n  is only used if float arguments are actually used (the magic of\n  _Generic: you would not pay for floats unless you use them).\n\n- Of the size flags, `hh`, `h`, `l`, `ll`, `L`, `q`, `j`, `z`, `Z`,\n  `t`, only `h`, `hh`, and `z` are implemented, with slightly\n  different semantics to print unsigned integers: `h` applies a mask\n  0xffff, `hh` applies a mask `0xff` and `z` reinterprets the given\n  number as unsigned.  Due to the type-safety, the other flags are not\n  needed as the library just prints whatever is thrown at it.\n\n- `'...'` literals have type `int` in C, so values \u003e0x7f, with its\n  highest bit set, will be misinterpreted as illegal Unicode on\n  compilers where `char` is signed.  On my compiler, printing\n  `\"~c\",'\\xfe'` prints a replacement characters, because `\\xfe` equals\n  `(int)0xfffffffe`, which is not valid Unicode, and this library\n  has no chance to find out that this is in fact `(char)0xfe`.  So\n  printing `'...'` literals is unfortunately broken, without a fix.\n  Printing with `~hhc` works as expected (but `~zc` does not,\n  because, `\\xfe` is an `int`).  Printing `(char)'\\xfe'` also works,\n  but is more ugly in my opinion (I do not like casts much).\n\n- Compiling `printf` with any modern compiler gives you compile time\n  warnings about the argument type vs. format string consistency.  If\n  these warnings are gone, there are usually no typing problems left\n  for the target architecture (but compiling for other architectures\n  may still have warnings and produce crashing code).\n\n  For this library, no such warnings are be issued, because the\n  argument passing is type safe and you cannot crash it with a wrong\n  format specifier.  However, if the argument count and format\n  specifier count do not match, then the output is likely wrong, and\n  the function will have undesirable behaviour.  There is no compile\n  time warning for this.\n\n- gcc 6: The library itself uses relatively little stack.  But gcc\n  (and also clang 3.8) accumulates the temporary stack objects in each\n  function without reusing the stack space, i.e., each call to some\n  print function builds up more stack at the call site. The temporary\n  objects are clearly dead, but gcc keeps them. It does not help to add\n  `({...})` or `do{...}while(0)` to formally restrict the official\n  lifetime of the object to a block -- the compilers keep the object\n  around.  This is highly undesirable here, but I have no idea how to\n  prevent this.  -fconserve-stack and any other optimisations I tried\n  don't change anything.\n\n  gcc 11 fixes this (or maybe some earlier version), but it requires a\n  block to limit the lifetime, even if the object is clearly dead.  I\n  added `({...})` to the macros so that newer compilers produce much\n  less stack usage at call sites.\n\n## Q\u0026A\n\n- Q: Why formatted printing?\n\n  A: Because it is nicer, and also it is feasible for Gnu gettext,\n  which e.g. C++'s `cout\u003c\u003c` is not.  A: Because I like the string\n  template based approach and find it more concise and can read it\n  with less effort.\n\n- Q: Is this perfect?\n\n  A: Well, no.  It is hard to extend for other types to print.  The\n  macro mechanisms used are near impossible to understand and causse\n  weird error messages and wrong error positions (in my gcc).  The\n  _Generic mechanism causes a ton of C code to be emitted for each\n  print call -- the compiler throws almost all of it away, based on\n  argument type, but looking at the pre-processed code is\n  interesting. The number of arguments may be inconsistent with the\n  format string without compile time warning.\n\n- Q: I stack usage really low?\n\n  A: Kind of, but not as much as I'd like.  It's around 250 bytes\n  worst case on my x86-64.\n\n- Q: What about code size?\n\n  A: This generates more code at the call site, because each argument\n  is translated to another function call.  Also, the temporary objects\n  cause more stack to be used at the call site.  Interally, the library\n  is OK wrt. code size, I think.\n\n- Q: What about speed?\n\n  A: Really?  This is about printing messages -- probably short ones\n  (less than a few kB, I'd guess).  So while I did try not to mess it up,\n  this is not optimised for speed.\n\n- Q: Is this safer than `printf`?\n\n  A: Definitely, I think.  There is absolutely no chance to give a\n  wrong format specifier and access the stack (like `printf` does via\n  `stdarg.h`) in undefined ways.  This is particularly true for\n  multi-arch development where with `printf` you need to be careful\n  about length specifiers, and you might not get a warning on your\n  machine, but the next person will and it will crash there.  I\n  usually need to compile a few times on multiple architectures to\n  get the integer length right, e.g., `%u` vs `%lu` vs. `%llu`\n  vs. `%zu`.\n\n  This library's mechanism is also more convenient, because you do not\n  need to think much about what you're printing with what format\n  specifier, and there are no `PRId16` macros that obfuscate your\n  portable code.  And you can use UTF-8, -16, -32 strings seamlessly\n  and mix them freely.  You can print into a alloced or stack\n  allocated compound literal safely, with error checking (end of\n  string, out of memory, etc.) and guaranteed NUL termination.\n\n- Q: Why do you use `~s` and not `%s`?\n\n  A1: This did use `%s` at the beginning.  But the format strings must\n  not be be confused with the standard C `printf`.  The format is not\n  compatible with a `printf` call, and this is not a drop-in\n  replacement.  E.g., in a larger code base, both this and old\n  `printf` might be mixed, maybe during a transition period, or just\n  because.  So programmers may see both styles.  With the different\n  sigil, it is immediately clear which format is used in a print call\n  when editing code, and confusion can hopefully be avoided.\n\n## TODO\n\n- ISO-8859-1 (because why not)\n\n## Examples\n\nOpen a file with computed name, up to a fixed path length:\n\n```c\n#include \u003cva_print/char.h\u003e\n\nFILE *open_text_rd(char const *dir, char const *file, unsigned suffix)\n{\n    return fopen(va_nprintf(80, \"~s/~s~.s\", dir, file, suffix), \"rt\");\n}\n```\n\nThe same with error checking about truncated string or en- or decoding\nerrors:\n\n```c\nFILE *open_text_rd(\n    char const *dir, char const *file, unsigned suffix)\n{\n    va_error_t e;\n    char *fn = va_nprintf(80, \"~s/~s~.s\", dir, file, suffix, \u0026e);\n    if (e.code != VA_E_OK) {\n        return NULL;\n    }\n    return fopen(fn, \"rt\");\n}\n```\n\nYou can use 8-bit, 16-bit, or 32-bit characters seamlessly.  The\nfollowing uses UTF-16 as a parameter, but calls fopen() with an UTF-8\nstring.  The only change is the parameter type.  Just for fun, let's\nuse an UTF-32 format string:\n\n```c\nFILE *open_text_rd(\n    char16_t const *dir, char16_t const *file, unsigned suffix)\n{\n    va_error_t e;\n    char *fn = va_nprintf(80, U\"~s/~s~.s\", dir, file, suffix, \u0026e);\n    if (e.code != VA_E_OK) {\n        return NULL;\n    }\n    return fopen(fn, \"rt\");\n}\n```\n\nThis can also be done by creating a dynamically allocated string.  The\n`va_error_t` mechanism then protects against out-of-memory situation\n(in which case the function deallocates what it allocated before and\nreturns NULL), and also against Unicode decoding/encoding errors, and\nother traps.\n\n```c\n#include \u003cva_print/alloc.h\u003e\n\nFILE *open_text_rd(char const *dir, char const *file, unsigned suffix)\n{\n    FILE *f = NULL;\n    va_error_t e;\n    char *fn = va_asprintf(\"~s/~s~.s\", dir, file, suffix, \u0026e);\n    if (e.code == VA_E_OK) {\n        f = fopen(fn, \"rt\");\n    }\n    free(fn);\n    return f;\n}\n```\n\nUsing VLA, do the same with arbitrary length by pre-computing the length\nusing va_lprintf():\n\n```c\n#include \u003cva_print/len.h\u003e\n\nFILE *open_text_rd(char const *dir, char const *file, unsigned suffix)\n{\n    char s[va_zprintf(\"~s/~s~.s\", dir, file, suffix)];\n    return fopen(va_sprintf(s, \"~s/~s~.s\", dir, file, suffix), \"rt\");\n\n}\n```\n\n## How Does This Work?\n\nThe main idea is to use macro magic (both standard C99 and some extensions\nfrom gcc, like allowing `__VA_ARGS__` to be empty etc.) to convert the\nprintf calls:\n\n```c\nx_printf(format);\nx_printf(format, arg1);\nx_printf(format, arg1, arg2);\n...\n```\n\nInto a recursive call sequence:\n\n```c\ninit(\u0026STREAM(...), format);\nrender(init(\u0026STREAM(...), format), arg1);\nrender(render(init(\u0026STREAM(...), format), arg1), arg2);\n...\n```\n\nThe `STREAM()` is a temporary stream object, a compound literal, that\nis used for state information when parsing the format string, and for\nstoring the output printer.  The pointer to this temporary object is\nreturned by all of the functions to the next layer of recursion.  The\n`init()` initialises the format parser and the output stream (e.g. for\nNUL termination and initial `alloc()`), and each `render()` consumes\none argument by printing it (once or more times) or using it as a\nwidth or precision.\n\nThe macro magic is called `VA_REC()`.  Additional to what is described\nabove, it passes a first parameter to the `init()` and `render()`\ncalls to show whether the call is the last one of the expression. This\nis done to be able to optimise the call site code generation, and to\nallow sane error handling if the number of format specifiers and\narguments do not match.  You can try it with `gcc -E`:\n\n```c\nVA_REC(render, init, stream);\nVA_REC(render, init, stream, a);\nVA_REC(render, init, stream, a, b);\n...\n```\n\nThis becomes:\n\n```c\ninit(0,stream);\nrender(0, init(1,stream), a);\nrender(0, render(1, init(1,stream), a), b);\n```\n\nThe `init(0,...)` macro call is an extern function call that\ninitialises the stream, initialises the output stream (e.g., NUL\nterminates a char array and/or allocs initial memory), and parses the\nformat string, so that even with no arguments, the expression behaves\nin a sane way.\n\nThe `init(1,...)` macro call instead resolves to a fast inline\nfunction that initialises the stream by just setting all the slots.\nThe format and output stream initialisation is then done by the first\n`render(...)` invocation.  This way, there is the minimal number of\nextern calls to keep the call site code small.\n\nThe `render()` resolves to a `_Generic()` call that selects the\nappropriate printer based on the type of the argument, and based on\nwhether it's the last call of the expression, so that for each\nargument, a different C functions may be invoked.  E.g.:\n\n    int i;\n    render(1,s,i)    -\u003e print_int(s,i)\n    render(0,s,i)    -\u003e print_last_int(s,i)\n\n    char const *x;\n    render(1,s,x)    -\u003e print_string(s,x)\n    render(0,s,x)    -\u003e print_last_string(s,x)\n\nThe `_last` variant finishes printing the format string even if no\nmore argument is given -- this is used to make error handling sane\nand not just stop in the middle of the format string.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoehriegitt%2Fvastringify","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmoehriegitt%2Fvastringify","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoehriegitt%2Fvastringify/lists"}