]>
Commit | Line | Data |
---|---|---|
6465356a A |
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 | |
14 | The standard | |
15 | .Xr printf 3 | |
16 | family of routines provides a convenient way to convert one or more arguments | |
17 | to various forms for output, under the control of a format string. | |
18 | The format string may contain any number of conversion specifications, which | |
19 | start with the | |
20 | .Sq Li % | |
21 | character and end with a conversion specifier character (like | |
22 | .Sq Li d | |
23 | or | |
24 | .Sq Li f ) , | |
25 | with conversion flag characters in-between. | |
26 | .Pp | |
27 | Extensible printf is an enhancement that allows adding new (user-defined) | |
28 | conversion specifiers, or modifying/removing existing ones. | |
29 | The implementation of extensible printf in Mac OS X is derived from the | |
30 | FreeBSD version, which is based on the one in GNU libc (GLIBC). | |
31 | Documentation 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 | |
35 | The main problem with the usual forms of extensible printf is that | |
36 | changes to | |
37 | .Xr printf 3 | |
38 | are program-wide. | |
39 | But this is unsafe, since frameworks, | |
40 | libraries or some other thread could change printf behavior in ways | |
41 | unexpected by the main program, or the latter could unexpectedly affect the | |
42 | former. | |
43 | .Pp | |
44 | So instead, the implementation used in Mac OS X makes | |
45 | changes to conversion specifiers within printf domains, | |
46 | which are independent structures containing the specifier definitions. | |
47 | These domains are created as described in | |
48 | .Xr xprintf_domain 3 , | |
49 | and once set up, it can be passed to a | |
50 | .Xr xprintf 3 | |
51 | variant along with the format string and arguments to generate output. | |
52 | The standard | |
53 | .Xr printf 3 | |
54 | behavior is never affected. | |
55 | .Pp | |
56 | To define a new conversion specifier, two function typedefs are defined, and | |
57 | the user must provide two functions based on these typedefs. | |
58 | These functions will get called from extensible printf while processing | |
59 | the corresponding conversion specification. | |
60 | .Pp | |
61 | During the first of three phases of extensible printf processing, the format | |
62 | string is parsed, and for each conversion specification, a | |
63 | .Vt struct printf_info | |
64 | is created, containing the option flags specified in the | |
65 | conversion specification as well as other settings. | |
66 | Important fields in | |
67 | .Vt struct printf_info | |
68 | are: | |
69 | .Bl -tag -width ".Va is_long_double" | |
70 | .It Va alt | |
71 | Boolean value whether the | |
72 | .Sq Li # | |
73 | flag was specified. | |
74 | .It Va context | |
75 | A | |
76 | .Vt void * | |
77 | pointer to arbitrary data specified in the original call to | |
78 | .Xr register_printf_domain_function 3 . | |
79 | .It Va group | |
80 | Boolean value whether the | |
81 | .Sq Li ' | |
82 | flag was specified. | |
83 | .It Va is_char | |
84 | Boolean value whether the | |
85 | .Sq Li hh | |
86 | flag was specified. | |
87 | .It Va is_intmax | |
88 | Boolean value whether the | |
89 | .Sq Li j | |
90 | flag was specified. | |
91 | .It Va is_long | |
92 | Boolean value whether the | |
93 | .Sq Li l | |
94 | flag was specified. | |
95 | .It Va is_long_double | |
96 | Boolean value whether the | |
97 | .Sq Li L | |
98 | or | |
99 | .Sq Li ll | |
100 | flags were specified. | |
101 | .It Va is_ptrdiff | |
102 | Boolean value whether the | |
103 | .Sq Li t | |
104 | flag was specified. | |
105 | .It Va is_quad | |
106 | Boolean value whether the | |
107 | .Sq Li q | |
108 | flag was specified. | |
109 | .It Va is_short | |
110 | Boolean value whether the | |
111 | .Sq Li h | |
112 | flag was specified. | |
113 | .It Va is_size | |
114 | Boolean value whether the | |
115 | .Sq Li z | |
116 | flag was specified. | |
117 | .It Va is_vec | |
118 | Boolean value whether the | |
119 | .Sq Li v | |
120 | flag was specified. | |
121 | .It Va left | |
122 | Boolean value whether the | |
123 | .Sq Li - | |
124 | flag was specified. | |
125 | .It Va loc | |
126 | The extended locale (see | |
127 | .Xr xlocale 3 ) | |
128 | specified by the extensible printf caller (never | |
129 | .Dv NULL ) . | |
130 | .It Va pad | |
131 | The padding character; either | |
132 | .Sq Li 0 | |
133 | or space. | |
134 | .It Va prec | |
135 | The value of the optional precision. | |
136 | -1 means the precision was unspecified. | |
137 | .It Va showsign | |
138 | Boolean value whether the | |
139 | .Sq Li + | |
140 | flag was specified. | |
141 | .It Va signchar | |
142 | The sign character, either | |
143 | .Sq Li + , | |
144 | space or zero if none. | |
145 | .It Va space | |
146 | Boolean value whether the space flag was specified. | |
147 | .It Va spec | |
148 | The specifier character itself. | |
149 | .It Va vsep | |
150 | The separator character between vector items (using the | |
151 | .Sq Li v | |
152 | flag). | |
153 | Can be any one of the four characters | |
154 | .Dq Li ,:;_ | |
155 | or | |
156 | .Sq Li X | |
157 | if no separator character was specified (meaning that a space is used as the | |
158 | separator, unless the specifier is | |
159 | .Sq Li c , | |
160 | in which case no separator is used). | |
161 | .It Va width | |
162 | The value of the minimum field width (defaults to zero). | |
163 | .El | |
164 | .Pp | |
165 | All other structure fields are either unused or private (and shouldn't be | |
166 | used). | |
167 | .Pp | |
168 | This | |
169 | .Vt struct printf_info | |
170 | structure is then passed to the corresponding | |
171 | .Nm printf_arginfo_function | |
172 | callback function. | |
173 | The callback function should return the number of consecutive arguments the | |
174 | specifier handles, including zero (the maximum number of consecutive arguments | |
175 | a single specifier can handle is | |
176 | .Dv __PRINTFMAXARG , | |
177 | which is currently set to 2, but could be increased in the future if there is | |
178 | need). | |
179 | .Pp | |
180 | The callback function is also passed an integer array and the length of that | |
181 | array; the length will typically be | |
182 | .Dv __PRINTFMAXARG . | |
183 | The function should fill out the array up to the number of arguments it expects, | |
184 | using the following values: | |
185 | .Bl -tag -width ".Dv PA_POINTER" | |
186 | .It Dv PA_CHAR | |
187 | The argument type is an | |
188 | .Vt int | |
189 | cast to a | |
190 | .Vt char . | |
191 | .It Dv PA_DOUBLE | |
192 | The argument type is a | |
193 | .Vt double . | |
194 | OR-ing | |
195 | .Dv PA_DOUBLE | |
196 | with | |
197 | .Dv PA_FLAG_LONG_DOUBLE | |
198 | specifies a | |
199 | .Vt "long double" | |
200 | type. | |
201 | .It Dv PA_FLOAT | |
202 | (Defined but unused; best to avoid, since | |
203 | .Vt float | |
204 | is automatically promoted to | |
205 | .Vt double | |
206 | anyways.) | |
207 | .It Dv PA_INT | |
208 | The argument type is | |
209 | .Vt int | |
210 | (either signed or unsigned). | |
211 | The 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 | |
215 | The integer is the size of a | |
216 | .Vt intmax_t . | |
217 | .It Dv PA_FLAG_LONG | |
218 | The integer is the size of a | |
219 | .Vt long . | |
220 | .It Dv PA_FLAG_LONG_LONG | |
221 | The integer is the size of a | |
222 | .Vt "long long" . | |
223 | .It Dv PA_FLAG_PTRDIFF | |
224 | The integer is the size of a | |
225 | .Vt ptrdiff_t . | |
226 | .It Dv PA_FLAG_QUAD | |
227 | The integer is the size of a | |
228 | .Vt quad_t | |
229 | (deprecated). | |
230 | .It Dv PA_FLAG_SHORT | |
231 | The integer is the size of a | |
232 | .Vt short . | |
233 | .It Dv PA_FLAG_SIZE | |
234 | The integer is the size of a | |
235 | .Vt size_t . | |
236 | .El | |
237 | .It Dv PA_POINTER | |
238 | The argument type is a pointer type, cast to a | |
239 | .Vt "void *" . | |
240 | .It Dv PA_STRING | |
241 | The argument type is a null-terminated character string | |
242 | .Vt ( "char *" ) . | |
243 | .It Dv PA_VECTOR | |
244 | The argument type is an AltiVec or SSE vector (16 bytes). | |
245 | .It Dv PA_WCHAR | |
246 | The argument type is a | |
247 | .Vt wchar_t . | |
248 | .It Dv PA_WSTRING | |
249 | The argument type is a null-terminated wide character string | |
250 | .Vt ( "wchar_t *" ) . | |
251 | .El | |
252 | .Pp | |
253 | After the | |
254 | .Nm printf_arginfo_function | |
255 | returns, phase 2 of extensible printf processing involves converting the | |
256 | argument according to the types specified by the returned type array. | |
257 | Note that positional arguments are dealt with here as well. | |
258 | .Pp | |
259 | Then in phase 3, output is generated, either from the text in-between the | |
260 | conversion specifications, or by calling the so-called rendering functions | |
261 | associated with each conversion specifier (with typedef | |
262 | .Nm printf_function ) . | |
263 | The rendering function is passed the same | |
264 | .Vt struct printf_info | |
265 | structure, as well as an array of pointers to each of the arguments converted | |
266 | in phase 2 that it is responsible for. | |
267 | The callback should write its output to the provided output | |
268 | stdio stream, and then return the number of characters written. | |
269 | .Sh EXAMPLE | |
270 | Here 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 */ | |
280 | typedef 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 */ | |
289 | static int | |
290 | print_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 */ | |
355 | static int | |
356 | coordinate_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 | ||
366 | int | |
367 | main (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 | |
425 | This example defines a Coordinate type, that consists of a pair of doubles. | |
426 | We create a conversion specifier that displays a Coordinate type, either just | |
427 | as two floating point numbers, or with the | |
428 | .Sq Li # | |
429 | (alternate form) flag, as parenthesized numbers separated by a comma. | |
430 | Note the use of | |
431 | .Nm printf_l | |
432 | to do the actual output; this is using regular printf from within an extensible | |
433 | printf renderer callback. | |
434 | The use of | |
435 | .Nm printf_l | |
436 | also insures correct handling of extended locales. | |
437 | .Pp | |
438 | The 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 | |
462 | Notice: | |
463 | .Bl -bullet | |
464 | .It | |
465 | Field width, precision and left adjustment are applied to each of the numbers. | |
466 | .It | |
467 | The alternate form, using parenthesized numbers separated by a comma. | |
468 | .It | |
469 | In the second group of six, the thousands separator corresponds to the | |
470 | global locale setting | |
471 | .Pq Li en_US.UTF-8 . | |
472 | .It | |
473 | The second and third group have a label for each number, provide through | |
474 | the user-defined context argument. | |
475 | .It | |
476 | The third group has the decimal point and thousands separator of the extended | |
477 | locale argument | |
478 | .Pq Li uk_UA.UTF-8 . | |
479 | .El | |
480 | .Sh PERFORMANCE | |
481 | Because of the three phase processing of extensible printf, as well as the | |
482 | use of two callbacks for each conversion specifier, performance is | |
483 | considerably slower than the one pass, highly optimized regular | |
484 | .Xr printf 3 . | |
485 | Recursive use of | |
486 | .Xr printf 3 | |
487 | from within an extensible printf renderer callback | |
488 | (as in the | |
489 | .Sx EXAMPLE | |
490 | above) adds additional overhead. | |
491 | .Pp | |
492 | To ameliorate some of this slowness, the concept of separate compilation | |
493 | and execution phases has be added to extensible printf. | |
494 | The functions in | |
495 | .Xr xprintf_comp 3 | |
496 | allow the creation of pre-compiled extensible printf structures (performing | |
497 | phase one of extensible printf processing). | |
498 | These pre-compiled structures can then be passed to the printf variants in | |
499 | .Xr xprintf_exec 3 | |
500 | to produce the actual output (performing phases 2 and 3). | |
501 | The compilation phase need only be done once, while execution can be performed | |
502 | any number of times. | |
503 | .Pp | |
504 | A 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 | |
513 | Here, | |
514 | .Va coords | |
515 | is a array containing | |
516 | .Vt Coordinate | |
517 | structures that are to be printed and the | |
518 | .Va domain | |
519 | and | |
520 | .Va loc | |
521 | variables are as from | |
522 | .Sx EXAMPLE | |
523 | above. | |
524 | (Error checking on the return value from | |
525 | .Fn new_printf_comp | |
526 | is 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 |