1.Dd Aug 19, 2012
2.Dt XPRINTF 5
3.Os Darwin
4.Sh NAME
5.Nm xprintf
6.Nd extensible printf
7.Sh SYNOPSIS
8.In printf.h
9.Ft "typedef int"
10.Fn printf_arginfo_function "const struct printf_info *info" "size_t n" "int *argtypes"
11.Ft "typedef int"
12.Fn printf_function "FILE *stream" "const struct printf_info *info" "const void *const *args"
13.Sh DESCRIPTION
14The standard
15.Xr printf 3
16family of routines provides a convenient way to convert one or more arguments
17to various forms for output, under the control of a format string.
18The format string may contain any number of conversion specifications, which
19start with the
20.Sq Li %
21character and end with a conversion specifier character (like
22.Sq Li d
23or
24.Sq Li f ) ,
25with conversion flag characters in-between.
26.Pp
27Extensible printf is an enhancement that allows adding new (user-defined)
28conversion specifiers, or modifying/removing existing ones.
29The implementation of extensible printf in Mac OS X is derived from the
30FreeBSD version, which is based on the one in GNU libc (GLIBC).
31Documentation for the GLIBC version is available at:
32.Pp
33.Li http://www.gnu.org/software/libc/manual/html_node/Customizing-Printf.html
34.Pp
35The main problem with the usual forms of extensible printf is that
36changes to
37.Xr printf 3
38are program-wide.
39But this is unsafe, since frameworks,
40libraries or some other thread could change printf behavior in ways
41unexpected by the main program, or the latter could unexpectedly affect the
42former.
43.Pp
44So instead, the implementation used in Mac OS X makes
45changes to conversion specifiers within printf domains,
46which are independent structures containing the specifier definitions.
47These domains are created as described in
48.Xr xprintf_domain 3 ,
49and once set up, it can be passed to a
50.Xr xprintf 3
51variant along with the format string and arguments to generate output.
52The standard
53.Xr printf 3
54behavior is never affected.
55.Pp
56To define a new conversion specifier, two function typedefs are defined, and
57the user must provide two functions based on these typedefs.
58These functions will get called from extensible printf while processing
59the corresponding conversion specification.
60.Pp
61During the first of three phases of extensible printf processing, the format
62string is parsed, and for each conversion specification, a
63.Vt struct printf_info
64is created, containing the option flags specified in the
65conversion specification as well as other settings.
66Important fields in
67.Vt struct printf_info
68are:
69.Bl -tag -width ".Va is_long_double"
70.It Va alt
71Boolean value whether the
72.Sq Li #
73flag was specified.
74.It Va context
75A
76.Vt void *
77pointer to arbitrary data specified in the original call to
78.Xr register_printf_domain_function 3 .
79.It Va group
80Boolean value whether the
81.Sq Li '
82flag was specified.
83.It Va is_char
84Boolean value whether the
85.Sq Li hh
86flag was specified.
87.It Va is_intmax
88Boolean value whether the
89.Sq Li j
90flag was specified.
91.It Va is_long
92Boolean value whether the
93.Sq Li l
94flag was specified.
95.It Va is_long_double
96Boolean value whether the
97.Sq Li L
98or
99.Sq Li ll
100flags were specified.
101.It Va is_ptrdiff
102Boolean value whether the
103.Sq Li t
104flag was specified.
105.It Va is_quad
106Boolean value whether the
107.Sq Li q
108flag was specified.
109.It Va is_short
110Boolean value whether the
111.Sq Li h
112flag was specified.
113.It Va is_size
114Boolean value whether the
115.Sq Li z
116flag was specified.
117.It Va is_vec
118Boolean value whether the
119.Sq Li v
120flag was specified.
121.It Va left
122Boolean value whether the
123.Sq Li -
124flag was specified.
125.It Va loc
126The extended locale (see
127.Xr xlocale 3 )
128specified by the extensible printf caller (never
129.Dv NULL ) .
130.It Va pad
131The padding character; either
132.Sq Li 0
133or space.
134.It Va prec
135The value of the optional precision.
136-1 means the precision was unspecified.
137.It Va showsign
138Boolean value whether the
139.Sq Li +
140flag was specified.
141.It Va signchar
142The sign character, either
143.Sq Li + ,
144space or zero if none.
145.It Va space
146Boolean value whether the space flag was specified.
147.It Va spec
148The specifier character itself.
149.It Va vsep
150The separator character between vector items (using the
151.Sq Li v
152flag).
153Can be any one of the four characters
154.Dq Li ,:;_
155or
156.Sq Li X
157if no separator character was specified (meaning that a space is used as the
158separator, unless the specifier is
159.Sq Li c ,
160in which case no separator is used).
161.It Va width
162The value of the minimum field width (defaults to zero).
163.El
164.Pp
165All other structure fields are either unused or private (and shouldn't be
166used).
167.Pp
168This
169.Vt struct printf_info
170structure is then passed to the corresponding
171.Nm printf_arginfo_function
172callback function.
173The callback function should return the number of consecutive arguments the
174specifier handles, including zero (the maximum number of consecutive arguments
175a single specifier can handle is
176.Dv __PRINTFMAXARG ,
177which is currently set to 2, but could be increased in the future if there is
178need).
179.Pp
180The callback function is also passed an integer array and the length of that
181array; the length will typically be
182.Dv __PRINTFMAXARG .
183The function should fill out the array up to the number of arguments it expects,
184using the following values:
185.Bl -tag -width ".Dv PA_POINTER"
186.It Dv PA_CHAR
187The argument type is an
188.Vt int
189cast to a
190.Vt char .
191.It Dv PA_DOUBLE
192The argument type is a
193.Vt double .
194OR-ing
195.Dv PA_DOUBLE
196with
197.Dv PA_FLAG_LONG_DOUBLE
198specifies a
199.Vt "long double"
200type.
201.It Dv PA_FLOAT
202(Defined but unused; best to avoid, since
203.Vt float
204is automatically promoted to
205.Vt double
206anyways.)
207.It Dv PA_INT
208The argument type is
209.Vt int
210(either signed or unsigned).
211The size can be adjusted by OR-ing the following values to
212.Dv PA_INT :
213.Bl -tag -width ".Dv PA_FLAG_LONG_LONG"
214.It Dv PA_FLAG_INTMAX
215The integer is the size of a
216.Vt intmax_t .
217.It Dv PA_FLAG_LONG
218The integer is the size of a
219.Vt long .
220.It Dv PA_FLAG_LONG_LONG
221The integer is the size of a
222.Vt "long long" .
223.It Dv PA_FLAG_PTRDIFF
224The integer is the size of a
225.Vt ptrdiff_t .
226.It Dv PA_FLAG_QUAD
227The integer is the size of a
228.Vt quad_t
229(deprecated).
230.It Dv PA_FLAG_SHORT
231The integer is the size of a
232.Vt short .
233.It Dv PA_FLAG_SIZE
234The integer is the size of a
235.Vt size_t .
236.El
237.It Dv PA_POINTER
238The argument type is a pointer type, cast to a
239.Vt "void *" .
240.It Dv PA_STRING
241The argument type is a null-terminated character string
242.Vt ( "char *" ) .
243.It Dv PA_VECTOR
244The argument type is an AltiVec or SSE vector (16 bytes).
245.It Dv PA_WCHAR
246The argument type is a
247.Vt wchar_t .
248.It Dv PA_WSTRING
249The argument type is a null-terminated wide character string
250.Vt ( "wchar_t *" ) .
251.El
252.Pp
253After the
254.Nm printf_arginfo_function
255returns, phase 2 of extensible printf processing involves converting the
256argument according to the types specified by the returned type array.
257Note that positional arguments are dealt with here as well.
258.Pp
259Then in phase 3, output is generated, either from the text in-between the
260conversion specifications, or by calling the so-called rendering functions
261associated with each conversion specifier (with typedef
262.Nm printf_function ) .
263The rendering function is passed the same
264.Vt struct printf_info
265structure, as well as an array of pointers to each of the arguments converted
266in phase 2 that it is responsible for.
267The callback should write its output to the provided output
268stdio stream, and then return the number of characters written.
269.Sh EXAMPLE
270Here is an example that demonstrates many of the features of extensible printf:
271.Bd -literal
272#include <stdio.h>
273#include <stdlib.h>
274#include <printf.h>
275#include <locale.h>
276#include <xlocale.h>
277#include <err.h>
278
279/* The Coordinate type */
280typedef struct {
281    double x;
282    double y;
283} Coordinate;
284
285#define L	(1 << 0)
286#define P	(1 << 1)
287
288/* The renderer callback for Coordinate */
289static int
290print_coordinate (FILE *stream, const struct printf_info *info,
291    const void *const *args)
292{
293    const Coordinate *c;
294    int width, ret, which = 0;
295    char fmt[32];
296    char *bp, *cp, *ep;
297    /* The optional coordinate labels */
298    const char **labels = (const char **)info->context;
299
300    /* Get the argument pointer to a Coordinate */
301    c = *((const Coordinate **) (args[0]));
302
303    /* Set up the format string */
304    cp = fmt;
305    if(info->alt) *cp++ = '(';
306    bp = cp;
307    if(labels) {
308	which |= L;
309	*cp++ = '%';
310	*cp++ = 's';
311    }
312    *cp++ = '%';
313    if(info->group) *cp++ = '\e'';
314    *cp++ = '*';
315    if(info->prec >= 0) {
316	which |= P;
317	*cp++ = '.';
318	*cp++ = '*';
319    }
320    *cp++ = 'l';
321    *cp++ = 'f';
322    ep = cp;
323    if(info->alt) *cp++ = ',';
324    *cp++ = ' ';
325    while(bp < ep) *cp++ = *bp++;
326    if(info->alt) *cp++ = ')';
327    *cp = 0;
328
329    width = info->left ? -info->width : info->width;
330
331    /* Output to the given stream */
332    switch(which) {
333    case 0:
334	ret = fprintf_l(stream, info->loc, fmt, width, c->x, width, c->y);
335	break;
336    case L:
337	ret = fprintf_l(stream, info->loc, fmt, labels[0], width, c->x,
338			labels[1], width, c->y);
339	break;
340    case P:
341	ret = fprintf_l(stream, info->loc, fmt, width, info->prec, c->x,
342			width, info->prec, c->y);
343	break;
344    case (L | P):
345	ret = fprintf_l(stream, info->loc, fmt, labels[0], width,
346			info->prec, c->x, labels[1], width, info->prec,
347			c->y);
348	break;
349    }
350
351    return ret;
352}
353
354/* The arginfo callback for Coordinate */
355static int
356coordinate_arginfo (const struct printf_info *info, size_t n,
357		    int *argtypes)
358{
359  /* We always take exactly one argument and this is a pointer to the
360     structure.. */
361  if (n > 0)
362    argtypes[0] = PA_POINTER;
363  return 1;
364}
365
366int
367main (void)
368{
369    Coordinate mycoordinate = {12345.6789, 3.141593};
370    printf_domain_t domain;
371    locale_t loc;
372    const char *labels[] = {"x=", "y="};
373
374    /* Set up a domain to add support for Coordinate conversion */
375    domain = new_printf_domain();
376    if(!domain)
377	err(1, "new_printf_domain");
378    /* Set up an extended locale to test locale support */
379    loc = newlocale(LC_ALL_MASK, "uk_UA.UTF-8", NULL);
380    if(!loc)
381	err(1, "newlocale");
382
383    /* Register the callbacks for Coordinates in the domain */
384    register_printf_domain_function (domain, 'C', print_coordinate,
385				     coordinate_arginfo, NULL);
386
387    /* Print the coordinate using the current locale (C). */
388    xprintf(domain, NULL, "|%'C|\en", &mycoordinate);
389    xprintf(domain, NULL, "|%'14C|\en", &mycoordinate);
390    xprintf(domain, NULL, "|%'-14.2C|\en", &mycoordinate);
391    xprintf(domain, NULL, "|%'#C|\en", &mycoordinate);
392    xprintf(domain, NULL, "|%'#14C|\en", &mycoordinate);
393    xprintf(domain, NULL, "|%'#-14.2C|\en", &mycoordinate);
394
395    printf("-------------\en");
396    /* Reregister the callbacks, specifying coordinate labels
397     * and setting the global locale (notice thousands separator) */
398    register_printf_domain_function (domain, 'C', print_coordinate,
399				     coordinate_arginfo, labels);
400    if(setlocale(LC_ALL, "en_US.UTF-8") == NULL)
401	errx(1, "setlocale");
402
403    /* Reprint with labels */
404    xprintf(domain, NULL, "|%'C|\en", &mycoordinate);
405    xprintf(domain, NULL, "|%'14C|\en", &mycoordinate);
406    xprintf(domain, NULL, "|%'-14.2C|\en", &mycoordinate);
407    xprintf(domain, NULL, "|%'#C|\en", &mycoordinate);
408    xprintf(domain, NULL, "|%'#14C|\en", &mycoordinate);
409    xprintf(domain, NULL, "|%'#-14.2C|\en", &mycoordinate);
410
411    printf("-------------\en");
412    /* Now print with the test locale (notice decimal point and
413     * thousands separator) */
414    xprintf(domain, loc, "|%'C|\en", &mycoordinate);
415    xprintf(domain, loc, "|%'14C|\en", &mycoordinate);
416    xprintf(domain, loc, "|%'-14.2C|\en", &mycoordinate);
417    xprintf(domain, loc, "|%'#C|\en", &mycoordinate);
418    xprintf(domain, loc, "|%'#14C|\en", &mycoordinate);
419    xprintf(domain, loc, "|%'#-14.2C|\en", &mycoordinate);
420
421    return 0;
422}
423.Ed
424.Pp
425This example defines a Coordinate type, that consists of a pair of doubles.
426We create a conversion specifier that displays a Coordinate type, either just
427as two floating point numbers, or with the
428.Sq Li #
429(alternate form) flag, as parenthesized numbers separated by a comma.
430Note the use of
431.Nm printf_l
432to do the actual output; this is using regular printf from within an extensible
433printf renderer callback.
434The use of
435.Nm printf_l
436also insures correct handling of extended locales.
437.Pp
438The output of the programs looks like:
439.Bd -literal
440|12345.678900 3.141593|
441|  12345.678900       3.141593|
442|12345.68       3.14          |
443|(12345.678900, 3.141593)|
444|(  12345.678900,       3.141593)|
445|(12345.68      , 3.14          )|
446-------------
447|x=12,345.678900 y=3.141593|
448|x= 12,345.678900 y=      3.141593|
449|x=12,345.68      y=3.14          |
450|(x=12,345.678900, y=3.141593)|
451|(x= 12,345.678900, y=      3.141593)|
452|(x=12,345.68     , y=3.14          )|
453-------------
454|x=12 345,678900 y=3,141593|
455|x= 12 345,678900 y=      3,141593|
456|x=12 345,68      y=3,14          |
457|(x=12 345,678900, y=3,141593)|
458|(x= 12 345,678900, y=      3,141593)|
459|(x=12 345,68     , y=3,14          )|
460.Ed
461.Pp
462Notice:
463.Bl -bullet
464.It
465Field width, precision and left adjustment are applied to each of the numbers.
466.It
467The alternate form, using parenthesized numbers separated by a comma.
468.It
469In the second group of six, the thousands separator corresponds to the
470global locale setting
471.Pq Li en_US.UTF-8 .
472.It
473The second and third group have a label for each number, provide through
474the user-defined context argument.
475.It
476The third group has the decimal point and thousands separator of the extended
477locale argument
478.Pq Li uk_UA.UTF-8 .
479.El
480.Sh PERFORMANCE
481Because of the three phase processing of extensible printf, as well as the
482use of two callbacks for each conversion specifier, performance is
483considerably slower than the one pass, highly optimized regular
484.Xr printf 3 .
485Recursive use of
486.Xr printf 3
487from within an extensible printf renderer callback
488(as in the
489.Sx EXAMPLE
490above) adds additional overhead.
491.Pp
492To ameliorate some of this slowness, the concept of separate compilation
493and execution phases has be added to extensible printf.
494The functions in
495.Xr xprintf_comp 3
496allow the creation of pre-compiled extensible printf structures (performing
497phase one of extensible printf processing).
498These pre-compiled structures can then be passed to the printf variants in
499.Xr xprintf_exec 3
500to produce the actual output (performing phases 2 and 3).
501The compilation phase need only be done once, while execution can be performed
502any number of times.
503.Pp
504A simple example of use is:
505.Bd -literal
506    printf_comp_t pc = new_printf_comp(domain, loc, "%d: %C\en");
507    for(i = 0; i = sizeof(coords) / sizeof(*coords); i++) {
508	xprintf_exec(pc, i, &coords[i]);
509    }
510    free_printf_comp(pc);
511.Ed
512.Pp
513Here,
514.Va coords
515is a array containing
516.Vt Coordinate
517structures that are to be printed and the
518.Va domain
519and
520.Va loc
521variables are as from
522.Sx EXAMPLE
523above.
524(Error checking on the return value from
525.Fn new_printf_comp
526is not shown).
527.Sh SEE ALSO
528.Xr printf 3 ,
529.Xr xlocale 3 ,
530.Xr xprintf 3 ,
531.Xr xprintf_comp 3 ,
532.Xr xprintf_domain 3 ,
533.Xr xprintf_exec 3
534