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