home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 2 / 2172 < prev    next >
Encoding:
Internet Message Format  |  1990-12-28  |  40.3 KB

  1. From: drh@duke.cs.duke.edu (D. Richard Hipp)
  2. Newsgroups: alt.sources
  3. Subject: Source code for enhanced printf.
  4. Message-ID: <659913362@romeo.cs.duke.edu>
  5. Date: 29 Nov 90 21:16:04 GMT
  6.  
  7. Source code for "printf()" and its cousins is attached.  There are some
  8. enhancements.  Mostly, there are some new variations on "printf()" such
  9. as:
  10.     mprintf(),  snprintf(),   nprintf(),   and   xprintf().
  11.  
  12. "mprintf()" goes to malloc for space to put its output, and returns a
  13. pointer to that space.  (Returns NULL if malloc fails, of course.)  This
  14. I have found to be the most useful.  "snprintf()" is to "sprintf()" as
  15. "strncpy()" is to "strcpy()".  (Not quite.  The string returned by snprintf
  16. is always zero-terminated -- not so for strncpy.)  "xprintf()" calls a
  17. function to dispose of its output.  "nprintf()" has no output -- it just
  18. returns the number of characters that would have been output by "printf".
  19. All the varargs variations on these are included, of course.
  20.  
  21. The second big enhancement is that new conversion format letters can
  22. be added at run-time.  Now you can easily add in customized conversion
  23. letters for currency, dates, complex numbers, boolean values, Roman
  24. numerals, or whatever you want.
  25.  
  26. Oh yea, this version of printf is also FASTER than the library code in
  27. Sun OS 4.1 (Sparc).
  28.  
  29. There are also several other minor improvements.  (Well, I call them
  30. improvements.  You might have other opinions...)
  31.  
  32. Send comments, bug reports, suggestions, and so forth to drh@cs.duke.edu.
  33.  
  34. -------------------------------- cut here -------------------------------
  35. #!/bin/sh
  36. echo extracting archive.sh
  37. cat <<\!end-of-archive.sh! >archive.sh
  38. #!/bin/sh
  39. echo '#!/bin/sh' >xprintf.sh;
  40. cat <<\! | ( while read filename; do\
  41.     echo 'archiving' $filename '...' 1>&2; \
  42.     echo 'echo extracting '$filename ;\
  43.     echo 'cat <<\!end-of-'$filename'! >'$filename ;\
  44.     cat $filename ;\
  45.     echo '!end-of-'$filename'!' ;\
  46.     done; ) >>xprintf.sh;
  47. archive.sh
  48. xprintf.c
  49. xprintf.h
  50. !
  51. !end-of-archive.sh!
  52. echo extracting xprintf.c
  53. cat <<\!end-of-xprintf.c! >xprintf.c
  54. /*
  55. ** NAME:    $Source: /home/grad1/drh/res/srclib/xprintf.sh,v $
  56. ** VERSION: $Revision: 1.1 $
  57. ** DATE:    $Date: 90/11/29 13:12:44 $
  58. **
  59. ** ONELINER:   A replacement for formatted printing programs.
  60. **
  61. ** COPYRIGHT:
  62. **   Copyright (c) 1990 by D. R. Hipp.  This code is an original work
  63. **   and has been prepared without reference to any prior
  64. **   implementations of similar functions.  No part of this code is
  65. **   subject to licensing restrictions of any telephone company or
  66. **   university.
  67. **
  68. **   Permission is hereby granted for this code to be used by anyone
  69. **   for any purpose under the following restrictions:
  70. **     1.  No attempt shall be made to prevent others (especially the
  71. **         author) from using this code.
  72. **     2.  Changes to this code are to be clearly marked.
  73. **     3.  The origins of this code are not to be misrepresented.
  74. **     4.  The user agrees that the author is in no way responsible for
  75. **         the correctness or usefulness of this program.
  76. **
  77. ** DESCRIPTION:
  78. **   This program is an enhanced replacement for the "printf" programs
  79. **   found in the standard library.  The following enhancements are
  80. **   supported:
  81. **
  82. **      +  Additional functions.  The standard set of "printf" functions
  83. **         includes printf, fprintf, sprintf, vprintf, vfprintf, and
  84. **         vsprintf.  This module adds the following:
  85. **
  86. **           *  snprintf -- Works like sprintf, but has an extra argument
  87. **                          which is the size of the buffer written to.
  88. **
  89. **           *  mprintf --  Similar to sprintf.  Writes output to memory
  90. **                          obtained from malloc.
  91. **
  92. **           *  xprintf --  Calls a function to dispose of output.
  93. **
  94. **           *  nprintf --  No output, but returns the number of characters
  95. **                          that would have been output by printf.
  96. **
  97. **           *  A v- version (ex: vsnprintf) of every function is also
  98. **              supplied.
  99. **
  100. **      +  A few extensions to the formatting notation are supported:
  101. **
  102. **           *  The %b field outputs an integer in binary notation.
  103. **
  104. **           *  The %c field now accepts a precision.  The character output
  105. **              is repeated by the number of times the precision specifies.
  106. **
  107. **           *  The %' field works like %c, but takes as its character the
  108. **              next character of the format string, instead of the next
  109. **              argument.  For example,  printf("%.78'-")  prints 78 minus
  110. **              signs, the same as  printf("%.78c",'-').
  111. **
  112. **      +  The user can add a limited number (20) of new conversion fields
  113. **         at run-time using the "converter" function.  Test case 5 below
  114. **         shows an example of how this is done.
  115. **
  116. **      +  When compiled using GCC on a SPARC, this version of printf is
  117. **         faster than the library printf for SUN OS 4.1.
  118. **
  119. **      +  All functions (except converter) are fully reentrant.
  120. **
  121. ** TESTING
  122. **   Use the following command sequence to run 5 tests on this code:
  123. **
  124. **     gcc -DTEST1 xprintf.c         Test integer conversion
  125. **     a.out
  126. **     gcc -DTEST2 xprintf.c         Test string and character output
  127. **     a.out
  128. **     gcc -DTEST3 xprintf.c         Test floating point conversions
  129. **     a.out
  130. **     gcc -DTEST4 xprintf.c         Test the library
  131. **     a.out
  132. **     gcc -DTEST5 xprintf.c         Test the "converter" function
  133. **     a.out
  134. **
  135. */
  136.  
  137. /*
  138. ** Undefine COMPATIBILITY to make some slight changes in the way things
  139. ** work.  I think the changes are an improvement, but they are not
  140. ** backwards compatible.
  141. */
  142. #define COMPATIBILITY       /* Compatible with SUN OS 4.1 */
  143.  
  144. #ifndef NOINTERFACE
  145. #  if defined(TEST1) || defined(TEST2) || defined(TEST3)
  146. #    define NOINTERFACE
  147. #  else
  148. #    define printf    DUMMY_FUNCTION_1    /* I don't want prototypes */
  149. #    define fprintf   DUMMY_FUNCTION_2    /*    for any of the standard */    
  150. #    define sprintf   DUMMY_FUNCTION_3    /*    functions, because such */
  151. #    define vprintf   DUMMY_FUNCTION_4    /*    prototypes could clash with */
  152. #    define vfprintf  DUMMY_FUNCTION_5    /*    my redefinitions */
  153. #    define vsprintf  DUMMY_FUNCTION_6
  154. #  endif
  155. #endif
  156.  
  157. #include <ctype.h>
  158. #include <stdio.h>
  159. #include <stdarg.h>
  160. #include <stdlib.h>
  161. #include <string.h>
  162. #include "xprintf.h"
  163.  
  164. #ifndef NOINTERFACE
  165. #  undef printf
  166. #  undef fprintf
  167. #  undef sprintf
  168. #  undef vprintf
  169. #  undef vfprintf
  170. #  undef vsprintf
  171. #endif
  172.  
  173. /*
  174. ** Conversion types fall into various categories as defined by the
  175. ** following enumeration.
  176. */
  177. enum e_type {    /* The type of the format field */
  178.    RADIX,            /* Integer types.  %d, %x, %o, and so forth */
  179.    FLOAT,            /* Floating point.  %f */
  180.    EXP,              /* Exponentional notation. %e and %E */
  181.    GENERIC,          /* Floating or exponential, depending on exponent. %g */
  182.    SIZE,             /* Return number of characters processed so far. %n */
  183.    STRING,           /* Strings. %s */
  184.    PERCENT,          /* Percent symbol. %% */
  185.    CHAR,             /* Characters. %c */
  186.    CHARLIT,          /* Literal characters.  %' */
  187.    USER,             /* User specified conversion */
  188.    ERROR,            /* Used to indicate no such conversion type */
  189. };
  190.  
  191. /*
  192. ** Each builtin conversion character (ex: the 'd' in "%d") is described
  193. ** by an instance of the following structure
  194. */
  195. typedef struct s_info {   /* Information about each format field */
  196.   int  fmttype;              /* The format field code letter */
  197.   int  base;                 /* The base for radix conversion */
  198.   char *charset;             /* The character set for conversion */
  199.   int  flag_signed;          /* Is the quantity signed? */
  200.   char *prefix;              /* Prefix on non-zero values in alt format */
  201.   enum e_type type;          /* Conversion paradigm */
  202. } info;
  203.  
  204. /*
  205. ** The following table is searched linearly, so it is good to put the
  206. ** most frequently used conversion types first.
  207. */
  208. static const info fmtinfo[] = {
  209.   'd',  10,  "0123456789",       1,    0, RADIX,
  210.   's',   0,  0,                  0,    0, STRING,
  211.   'c',   0,  0,                  0,    0, CHAR,
  212.   'o',   8,  "01234567",         0,  "0", RADIX,
  213.   'u',  10,  "0123456789",       0,    0, RADIX,
  214.   'x',  16,  "0123456789abcdef", 0, "x0", RADIX,
  215.   'X',  16,  "0123456789ABCDEF", 0, "X0", RADIX,
  216.   'f',   0,  0,                  1,    0, FLOAT,
  217.   'e',   0,  "e",                1,    0, EXP,
  218.   'E',   0,  "E",                1,    0, EXP,
  219.   'g',   0,  "e",                1,    0, GENERIC,
  220.   'G',   0,  "E",                1,    0, GENERIC,
  221.   'i',  10,  "0123456789",       1,    0, RADIX,
  222.   'n',   0,  0,                  0,    0, SIZE,
  223.   '%',   0,  0,                  0,    0, PERCENT,
  224.   'b',   2,  "01",               0, "b0", RADIX,    /* Binary notation */
  225.   'p',  10,  "0123456789",       0,    0, RADIX,    /* Pointers */
  226.   '\'',  0,  0,                  0,    0, CHARLIT,  /* Literal character */
  227. };
  228. #define NINFO  (sizeof(fmtinfo)/sizeof(info))  /* Size of the fmtinfo table */
  229.  
  230. /*
  231. ** If NOFLOATINGPOINT is defined, then none of the floating point
  232. ** conversions will work.
  233. */
  234. #ifndef NOFLOATINGPOINT
  235. /*
  236. ** "*val" is a double such that 0.1 <= *val < 10.0
  237. ** Return the ascii code for the leading digit of *val, then
  238. ** multiply "*val" by 10.0 to renormalize.
  239. **
  240. ** Example:
  241. **     input:     *val = 3.14159
  242. **     output:    *val = 1.4159    function return = '3'
  243. */
  244. static int getdigit(double *val){
  245.   int digit;
  246.   double d;
  247.   digit = (int)*val;
  248.   d = digit;
  249.   digit += '0';
  250.   *val = (*val - d)*10.0;
  251.   return digit;
  252. }
  253. #endif
  254.  
  255. /*
  256. ** User specified conversion fields.
  257. */
  258.  
  259. /*
  260. ** For internal use only:  This structure remembers the details of a
  261. ** user defined conversion.
  262. */    
  263. typedef struct s_userinfo {
  264.   char format;                 /* The conversion format character */
  265.   int (*func)(convertctrl*);   /* The function to call to do the convert */
  266.   void *arg;                   /* A generic argument to (*func)(...) */
  267. } userinfo;
  268.  
  269. /*
  270. ** Here is an array of the user defined conversions.  There are a fixed
  271. ** number.
  272. */
  273. #define NUSERFMT 20                 /* Max no. of user-defined conversions */
  274. static int nuserfmt = 0;            /* No. of previously defined */
  275. static userinfo userfmt[NUSERFMT];  /* Array of user-defined conversions */
  276.  
  277. /*
  278. ** Call this function in order to install a new user-specified conversion
  279. ** field.
  280. **
  281. ** The function returns TRUE if the conversion is successfully installed.
  282. */
  283. int converter(char format, int (*func)(convertctrl*), void *arg){
  284.   int rc;
  285.   if( nuserfmt<NUSERFMT ){
  286.     userfmt[nuserfmt].format = format;
  287.     userfmt[nuserfmt].func = func;
  288.     userfmt[nuserfmt].arg = arg;
  289.     nuserfmt++;
  290.     rc = 1;
  291.   }else{
  292.     rc = 0;
  293.   }
  294.   return rc;
  295. }
  296.  
  297. #ifndef TEST4
  298. # define BUFSIZE 1000  /* Size of the output buffer */
  299.     /*  NOTE:  No field can be longer than BUFSIZE characters! */
  300. #else
  301. # define BUFSIZE 7     /* Set low to exercise code during TEST4 */
  302. #endif
  303.  
  304. /*
  305. ** The root program.  All variations call this core.
  306. **
  307. ** INPUTS:
  308. **   func   This is a pointer to a function taking three arguments
  309. **            1. A pointer to the list of characters to be output
  310. **               (Note, this list is NOT null terminated.)
  311. **            2. An integer number of characters to be output.
  312. **               (Note: This number might be zero.)
  313. **            3. A pointer to anything.  Same as the "arg" parameter.
  314. **
  315. **   arg    This is the pointer to anything which will be passed as the
  316. **          third argument to "func".  Use it for whatever you like.
  317. **
  318. **   fmt    This is the format string, as in the usual printf.
  319. **
  320. **   ap     This is a pointer to a list of arguments.  Same as in
  321. **          vfprintf.
  322. **
  323. ** OUTPUTS:
  324. **          The return value is the total number of characters sent to
  325. **          the function "func".  Returns EOF on a error.
  326. **
  327. ** Note that the order in which automatic variables are declared below
  328. ** seems to make a big difference in determining how fast this beast
  329. ** will run.
  330. */
  331. int vxprintf(void (*func)(char*,int,void*),
  332. void *arg, const char *format, va_list ap){
  333.   register const char *fmt; /* The format string. */
  334.   register int c;           /* Next character in the format string */
  335.   register char *bufpt;     /* Pointer to the conversion buffer */
  336.   register int  precision;  /* Precision of the current field */
  337.   register int  length;     /* Length of the field */
  338.   register int  idx;        /* A general purpose loop counter */
  339.   int count;                /* Total number of characters output */
  340.   int width;                /* Width of the current field */
  341.   int flag_leftjustify;     /* True if "-" flag is present */
  342.   int flag_plussign;        /* True if "+" flag is present */
  343.   int flag_blanksign;       /* True if " " flag is present */
  344.   int flag_alternateform;   /* True if "#" flag is present */
  345.   int flag_zeropad;         /* True if field width constant starts with zero */
  346.   int flag_long;            /* True if "l" flag is present */
  347.   unsigned long longvalue;  /* Value for integer types */
  348.   double realvalue;         /* Value for real types */
  349.   const info *infop;        /* Pointer to the appropriate info structure */
  350.   char buf[BUFSIZE];        /* Conversion buffer */
  351.   char prefix;              /* Prefix character.  "+" or "-" or " " or 0. */
  352.   int  errorflag = 0;       /* True if an error is encountered */
  353.   enum e_type xtype;        /* Which of 3 different FP formats */
  354.   convertctrl v;            /* Used for calling user conversion routines */
  355.   userinfo *ufp = 0;        /* Pointer to info about a user conversion */
  356.   static char spaces[]="                                                    ";
  357. #define SPACESIZE (sizeof(spaces)-1)
  358. #ifndef NOFLOATINGPOINT
  359.   int  exp;                 /* exponent of real numbers */
  360.   double rounder;           /* Used for rounding floating point values */
  361.   int flag_dp;              /* True if decimal point should be shown */
  362.   int flag_rtz;             /* True if trailing zeros should be removed */
  363. #endif
  364.  
  365.   fmt = format;                     /* Put in a register for speed */
  366.   count = length = 0;
  367.   bufpt = 0;
  368.   for(; (c=(*fmt))!=0; ++fmt){
  369.     if( c!='%' ){
  370.       register int amt;
  371.       bufpt = (char *)fmt;
  372.       amt = 1;
  373.       while( (c=*++fmt)!='%' && c!=0 ) amt++;
  374.       (*func)(bufpt,amt,arg);
  375.       count += amt;
  376.       if( c==0 ) break;
  377.     }
  378.     if( (c=(*++fmt))==0 ){
  379.       errorflag = 1;
  380.       (*func)("%",1,arg);
  381.       count++;
  382.       break;
  383.     }
  384.     /* Find out what flags are present */
  385.     flag_leftjustify = flag_plussign = flag_blanksign = 
  386.      flag_alternateform = flag_zeropad = 0;
  387.     do{
  388.       switch( c ){
  389.         case '-':   flag_leftjustify = 1;     c = 0;   break;
  390.         case '+':   flag_plussign = 1;        c = 0;   break;
  391.         case ' ':   flag_blanksign = 1;       c = 0;   break;
  392.         case '#':   flag_alternateform = 1;   c = 0;   break;
  393.         case '0':   flag_zeropad = 1;         c = 0;   break;
  394.         default:                                       break;
  395.       }
  396.     }while( c==0 && (c=*++fmt)!=0 );
  397.     /* Get the field width */
  398.     width = 0;
  399.     if( c=='*' ){
  400.       width = va_arg(ap,int);
  401.       if( width<0 ){
  402.         flag_leftjustify = 1;
  403.         width = -width;
  404.       }
  405.       c = *++fmt;
  406.     }else{
  407.       while( isdigit(c) ){
  408.         width = width*10 + c - '0';
  409.         c = *++fmt;
  410.       }
  411.     }
  412.     /* Get the precision */
  413.     if( c=='.' ){
  414.       precision = 0;
  415.       c = *++fmt;
  416.       if( c=='*' ){
  417.         precision = va_arg(ap,int);
  418. #ifndef COMPATIBILITY
  419.         /* This is sensible, but SUN OS 4.1 doesn't do it. */
  420.         if( precision<0 ) precision = -precision;
  421. #endif
  422.         c = *++fmt;
  423.       }else{
  424.         while( isdigit(c) ){
  425.           precision = precision*10 + c - '0';
  426.           c = *++fmt;
  427.         }
  428.       }
  429.       /* Limit the precision to prevent overflowing buf[] during conversion */
  430.       if( precision>BUFSIZE-40 ) precision = BUFSIZE-40;
  431.     }else{
  432.       precision = -1;
  433.     }
  434.     /* Get the conversion type modifier */
  435.     if( c=='l' ){
  436.       flag_long = 1;
  437.       c = *++fmt;
  438.     }else{
  439.       flag_long = 0;
  440.     }
  441.     /* Fetch the info entry for the field */
  442.     infop = 0;
  443.     for(idx=0; idx<NINFO; idx++){
  444.       if( c==fmtinfo[idx].fmttype ){
  445.         infop = &fmtinfo[idx];
  446.         break;
  447.       }
  448.     }
  449.     /* No info entry found.  Perhaps this is a user-specified conversion.
  450.     ** Otherwise, it must be an error.  Check it out. */
  451.     if( infop==0 ){
  452.       int i;
  453.       xtype = ERROR;
  454.       for(i=0; i<nuserfmt; i++){
  455.         if( userfmt[i].format==c ){
  456.           ufp = &userfmt[i];
  457.           xtype = USER;
  458.           break;
  459.     }
  460.       }
  461.     }else{
  462.       xtype = infop->type;
  463.     }
  464.     /*
  465.     ** At this point, variables are initialized as follows:
  466.     **
  467.     **   flag_alternateform          TRUE if a '#' is present.
  468.     **   flag_plussign               TRUE if a '+' is present.
  469.     **   flag_leftjustify            TRUE if a '-' is present or if the
  470.     **                               field width was negative.
  471.     **   flag_zeropad                TRUE if the width began with 0.
  472.     **   flag_long                   TRUE if the letter 'l' (ell) prefixed
  473.     **                               the conversion character.
  474.     **   flag_blanksign              TRUE if a ' ' is present.
  475.     **   width                       The specified field width.  This is
  476.     **                               always non-negative.  Zero is the default.
  477.     **   precision                   The specified precision.  The default
  478.     **                               is -1.
  479.     **   xtype                       The class of the conversion.
  480.     **   infop                       Pointer to the appropriate info struct.
  481.     **   ufp                         Pointer to an approariate userinfo struct.
  482.     */
  483.     switch( xtype ){
  484.       case RADIX:
  485.         if( flag_long )  longvalue = va_arg(ap,long);
  486.     else             longvalue = va_arg(ap,int);
  487. #ifdef COMPATIBILITY
  488.         /* For the format %#x, the value zero is printed "0" not "0x0".
  489.         ** I think this is stupid. */
  490.         if( longvalue==0 ) flag_alternateform = 0;
  491. #else
  492.         /* More sensible: turn off the prefix for octal (to prevent "00"),
  493.         ** but leave the prefix for hex. */
  494.         if( longvalue==0 && infop->base==8 ) flag_alternateform = 0;
  495. #endif
  496.         if( infop->flag_signed ){
  497.           if( *(long*)&longvalue<0 ){
  498.             longvalue = -*(long*)&longvalue;
  499.             prefix = '-';
  500.           }else if( flag_plussign )  prefix = '+';
  501.           else if( flag_blanksign )  prefix = ' ';
  502.           else                       prefix = 0;
  503.         }else                        prefix = 0;
  504.         if( flag_zeropad && precision<width-(prefix!=0) ){
  505.           precision = width-(prefix!=0);
  506.     }
  507.         {
  508.           register char *cset;      /* Use registers for speed */
  509.           register int base;
  510.           cset = infop->charset;
  511.           base = infop->base;
  512.           bufpt = &buf[BUFSIZE];
  513.           do{                                           /* Convert to ascii */
  514.             *(--bufpt) = cset[longvalue%base];
  515.             longvalue = longvalue/base;
  516.           }while( longvalue>0 );
  517.     }
  518.         length = (int)&buf[BUFSIZE]-(int)bufpt;
  519.         for(idx=precision-length; idx>0; idx--){
  520.           *(--bufpt) = '0';                             /* Zero pad */
  521.     }
  522.         if( prefix ) *(--bufpt) = prefix;               /* Add sign */
  523.         if( flag_alternateform && infop->prefix ){      /* Add "0" or "0x" */
  524.           char *pre, x;
  525.           pre = infop->prefix;
  526.           if( *bufpt!=pre[0] ){
  527.             for(pre=infop->prefix; (x=*pre)!=0; pre++) *(--bufpt) = x;
  528.       }
  529.         }
  530.         length = (int)&buf[BUFSIZE]-(int)bufpt;
  531.         break;
  532.       case FLOAT:
  533.       case EXP:
  534.       case GENERIC:
  535.         realvalue = va_arg(ap,double);
  536. #ifndef NOFLOATINGPOINT
  537.         if( precision<0 ) precision = 6;         /* Set default precision */
  538.         if( precision>BUFSIZE-10 ) precision = BUFSIZE-10;
  539.         if( realvalue<0.0 ){
  540.           realvalue = -realvalue;
  541.           prefix = '-';
  542.     }else{
  543.           if( flag_plussign )          prefix = '+';
  544.           else if( flag_blanksign )    prefix = ' ';
  545.           else                         prefix = 0;
  546.     }
  547.         if( infop->type==GENERIC && precision>0 ) precision--;
  548.         for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1);
  549.         if( infop->type==FLOAT ) realvalue += rounder;
  550.         /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
  551.         exp = 0;
  552.         while( realvalue>=1e8 ){ realvalue *= 1e-8; exp+=8; }
  553.         while( realvalue>=10.0 ){ realvalue *= 0.1; exp++; }
  554.         while( realvalue<1e-8 ){ realvalue *= 1e8; exp-=8; }
  555.         while( realvalue<1.0 ){ realvalue *= 10.0; exp--; }
  556.         bufpt = buf;
  557.         /*
  558.         ** If the field type is GENERIC, then convert to either EXP
  559.         ** or FLOAT, as appropriate.
  560.         */
  561.         if( xtype==GENERIC ){
  562.           flag_rtz = !flag_alternateform;
  563.             if( exp<-4 || exp>precision ){
  564.             xtype = EXP;
  565.           }else{
  566.             precision = precision - exp;
  567.             realvalue += rounder;
  568.             xtype = FLOAT;
  569.           }
  570.     }
  571.         /*
  572.         ** The "exp+precision" test causes output to be of type EXP if
  573.         ** the precision is too large to fit in buf[].
  574.         */
  575.         if( xtype==FLOAT && exp+precision<BUFSIZE-30 ){
  576.           flag_rtz = 0;
  577.           flag_dp = (precision>0 || flag_alternateform);
  578.           if( prefix ) *(bufpt++) = prefix;         /* Sign */
  579.           if( exp<0 )  *(bufpt++) = '0';            /* Digits before "." */
  580.           else for(; exp>=0; exp--) *(bufpt++) = getdigit(&realvalue);
  581.           if( flag_dp ) *(bufpt++) = '.';           /* The decimal point */
  582.           for(exp++; exp<0 && precision>0; precision--, exp++){
  583.             *(bufpt++) = '0';
  584.           }
  585.           while( (precision--)>0 ) *(bufpt++) = getdigit(&realvalue);
  586.           *(bufpt--) = 0;                           /* Null terminate */
  587.           if( flag_rtz && flag_dp ){     /* Remove trailing zeros and "." */
  588.             while( bufpt>=buf && *bufpt=='0' ) *(bufpt--) = 0;
  589.             if( bufpt>=buf && *bufpt=='.' ) *(bufpt--) = 0;
  590.           }
  591.           bufpt++;                            /* point to next free slot */
  592.     }else{    /* EXP */
  593.           flag_rtz = 0;
  594.           flag_dp = (precision>0 || flag_alternateform);
  595.           realvalue += rounder;
  596.           if( prefix ) *(bufpt++) = prefix;   /* Sign */
  597.           *(bufpt++) = getdigit(&realvalue);  /* First digit */
  598.           if( flag_dp ) *(bufpt++) = '.';     /* Decimal point */
  599.           while( (precision--)>0 ) *(bufpt++) = getdigit(&realvalue);
  600.           bufpt--;                            /* point to last digit */
  601.           if( flag_rtz && flag_dp ){          /* Remove tail zeros */
  602.             while( bufpt>=buf && *bufpt=='0' ) *(bufpt--) = 0;
  603.             if( bufpt>=buf && *bufpt=='.' ) *(bufpt--) = 0;
  604.           }
  605.           bufpt++;                            /* point to next free slot */
  606.           *(bufpt++) = infop->charset[0];
  607.           if( exp<0 ){ *(bufpt++) = '-'; exp = -exp; } /* sign of exp */
  608.           else       { *(bufpt++) = '+'; }
  609.           if( exp>=100 ) *(bufpt++) = (exp/100)+'0';   /* 100's digit */
  610.           *(bufpt++) = exp/10+'0';                     /* 10's digit */
  611.           *(bufpt++) = exp%10+'0';                     /* 1's digit */
  612.     }
  613.         /* The converted number is in buf[] and zero terminated. Output it.
  614.         ** Note that the number is in the usual order, not reversed as with
  615.         ** integer conversions. */
  616.         length = (int)bufpt-(int)buf;
  617.         bufpt = buf;
  618. #endif
  619.         break;
  620.       case SIZE:
  621.         *(va_arg(ap,int*)) = count;
  622.         length = width = 0;
  623.         break;
  624.       case PERCENT:
  625.         buf[0] = '%';
  626.         bufpt = buf;
  627.         length = 1;
  628.         break;
  629.       case CHARLIT:
  630.       case CHAR:
  631.         c = buf[0] = (xtype==CHAR ? va_arg(ap,int) : *++fmt);
  632.         if( precision>=0 ){
  633.           for(idx=1; idx<precision; idx++) buf[idx] = c;
  634.           length = precision;
  635.     }else{
  636.           length =1;
  637.     }
  638.         bufpt = buf;
  639.         break;
  640.       case STRING:
  641.         bufpt = va_arg(ap,char*);
  642.         if( bufpt==0 ) bufpt = "(null)";
  643.         length = strlen(bufpt);
  644.         if( precision>=0 && precision<length ) length = precision;
  645.         break;
  646.       case USER:
  647.         v.bufsize = BUFSIZE;
  648.         v.buf = buf;
  649.         v.precision = precision;
  650.         v.flag_sharp = flag_alternateform;
  651.         v.flag_plus = flag_plussign;
  652.         v.flag_blank = flag_blanksign;
  653.         v.ap = ap;
  654.         v.arg = ufp->arg;
  655.         length = (*(ufp->func))(&v);
  656.         ap = v.ap;
  657.         bufpt = buf;
  658.         break; 
  659.       case ERROR:
  660.         buf[0] = '%';
  661.         buf[1] = c;
  662.         errorflag = 0;
  663.         idx = 1+(c!=0);
  664.         (*func)("%",idx,arg);
  665.         count += idx;
  666.         if( c==0 ) fmt--;
  667.         break;
  668.     }/* End switch over the format type */
  669.     /*
  670.     ** The text of the conversion is pointed to by "bufpt" and is
  671.     ** "length" characters long.  The field width is "width".  Do
  672.     ** the output.
  673.     */
  674.     if( !flag_leftjustify ){
  675.       register int nspace;
  676.       nspace = width-length;
  677.       if( nspace>0 ){
  678.         count += nspace;
  679.         while( nspace>=SPACESIZE ){
  680.           (*func)(spaces,SPACESIZE,arg);
  681.           nspace -= SPACESIZE;
  682.         }
  683.         if( nspace>0 ) (*func)(spaces,nspace,arg);
  684.       }
  685.     }
  686.     if( length>0 ){
  687.       (*func)(bufpt,length,arg);
  688.       count += length;
  689.     }
  690.     if( flag_leftjustify ){
  691.       register int nspace;
  692.       nspace = width-length;
  693.       if( nspace>0 ){
  694.         count += nspace;
  695.         while( nspace>=SPACESIZE ){
  696.           (*func)(spaces,SPACESIZE,arg);
  697.           nspace -= SPACESIZE;
  698.         }
  699.         if( nspace>0 ) (*func)(spaces,nspace,arg);
  700.       }
  701.     }
  702.   }/* End for loop over the format string */
  703.   return errorflag ? EOF : count;
  704. } /* End of function */
  705.  
  706. int xprintf(void (*func)(char*,int,void*), void *arg, const char *fmt, ...){
  707.   va_list ap;
  708.   va_start(ap,fmt);
  709.   return vxprintf(func,arg,fmt,ap);
  710. }
  711.  
  712. #ifndef NOINTERFACE
  713. /*
  714. ** The following set of routines implement convenient interfaces to the
  715. ** xprintf functions.  These are as follows:
  716. **
  717. **     [v][f]printf          The usual library routines.
  718. **     [v]sprintf            For generating strings.
  719. **     [v]snprintf           Like sprintf, but the strings are limited in
  720. **                           length to avoid overflowing buffers.
  721. **     [v]mprintf            Gets space to hold the string from malloc
  722. **                           and returns a pointer to that space.
  723. **     [v]nprintf            Do no printing.  Measure the size of the
  724. **                           output string if it were to be printed.
  725. **
  726. ** First, the regular print, and its variants, as found in any standard
  727. ** library.
  728. */
  729. static void fout(register char *txt, register int amt, register void *arg){
  730.   register int c;
  731.   while( amt-->0 ){
  732.     c = *txt++;
  733.     putc(c,(FILE *)arg);
  734.   }
  735. }
  736. int printf(const char *fmt, ...){
  737.   va_list ap;
  738.   va_start(ap,fmt);
  739.   return vxprintf(fout,(void*)stdout,fmt,ap);
  740. }
  741. int vprintf(const char *fmt, va_list ap){
  742.   return vxprintf(fout,(void*)stdout,fmt,ap);
  743. }
  744. int fprintf(FILE *fp, const char *fmt, ...){
  745.   va_list ap;
  746.   va_start(ap,fmt);
  747.   return vxprintf(fout,(void*)fp,fmt,ap);
  748. }
  749. int vfprintf(FILE *fp, const char *fmt, va_list ap){
  750.   return vxprintf(fout,(void*)fp,fmt,ap);
  751. }
  752.  
  753. /*
  754. ** Now for string-printf, also as found in any standard library.
  755. ** Add to this the snprintf function which stops added characters
  756. ** to the string at a given length.
  757. **
  758. ** Note that snprintf returns the length of the string as it would
  759. ** be if there were no limit on the output.
  760. */
  761. typedef struct s_strargument {    /* Describes the string being written to */
  762.   char *next;                        /* Next free slot in the string */
  763.   char *last;                        /* Last available slot in the string */
  764. } sarg;
  765.  
  766. static void sout(char *txt, int amt, void *arg){
  767.   register char *head;
  768.   register char *t;  
  769.   register int a;
  770.   register char *tail;
  771.   a = amt;
  772.   t = txt;
  773.   head = ((sarg*)arg)->next;
  774.   tail = ((sarg*)arg)->last;
  775.   if( tail ){
  776.     while( a-->0 && head<tail ) *(head++) = *(t++);
  777.   }else{
  778.     while( a-->0 ) *(head++) = *(t++);
  779.   }
  780.   *head = 0;
  781.   ((sarg*)arg)->next = head;
  782. }
  783. int sprintf(char *buf, const char *fmt, ...){
  784.   sarg arg;
  785.   va_list ap;
  786.   va_start(ap,fmt);
  787.   arg.next = buf;
  788.   arg.last = 0;
  789.   return vxprintf(sout,(void*)&arg,fmt,ap);
  790. }
  791. int vsprintf(char *buf, const char *fmt, va_list ap){
  792.   sarg arg;
  793.   arg.next = buf;
  794.   arg.last = 0;
  795.   return vxprintf(sout,(void*)&arg,fmt,ap);
  796. }
  797. int snprintf(char *buf, int n, const char *fmt, ...){
  798.   va_list ap;
  799.   sarg arg;
  800.   va_start(ap,fmt);
  801.   arg.next = buf;
  802.   arg.last = &buf[n-1];
  803.   return vxprintf(sout,(void*)&arg,fmt,ap);
  804. }
  805. int vsnprintf(char *buf, int n, const char *fmt, va_list ap){
  806.   sarg arg;
  807.   arg.next = buf;
  808.   arg.last = &buf[n-1];
  809.   return vxprintf(sout,(void*)&arg,fmt,ap);
  810. }
  811.  
  812. /*
  813. ** The following are for malloc-printf.  Space is obtained from malloc
  814. ** to hold the string which results, and a pointer to this space is
  815. ** returned.  NULL is returned if we run out of space.
  816. */
  817. typedef struct s_mprintfarg {
  818.   char *buf;
  819.   int  size;
  820.   char *next;
  821. } marg;
  822. static void mout(char *txt, int amt, void *arg){
  823.   register char *head = 0;
  824.   register char *t;
  825.   register int a;
  826.   marg *mp;
  827.   mp = (marg*)arg;
  828.   a = amt;
  829.   t = txt;
  830.   if( mp->size==0 ){
  831.     mp->size = amt+1;
  832.     head = mp->buf = malloc( mp->size );
  833.     if( head ){
  834.       while( a-->0 ) *(head++) = *(t++);
  835.       *head = 0;
  836.     }
  837.   }else{
  838.     if( mp->buf!=0 ){
  839.       mp->buf = realloc(mp->buf,mp->size+amt);
  840.       if( mp->buf ){
  841.         head = &(mp->buf[mp->size-1]);
  842.         while( a-->0 ) *(head++) = *(t++);
  843.         *head = 0;
  844.         mp->size += amt;
  845.       }
  846.     }
  847.   }
  848.   mp->next = head;
  849. }
  850. char *mprintf(const char *fmt, ...){
  851.   va_list ap;
  852.   marg mp;
  853.   va_start(ap,fmt);
  854.   mp.size = 0;
  855.   mp.buf = mp.next = 0;
  856.   vxprintf(mout,(void*)&mp,fmt,ap);
  857.   return mp.buf;
  858. }
  859. char *vmprintf(const char *fmt, va_list ap){
  860.   marg mp;
  861.   mp.size = 0;
  862.   mp.buf = mp.next = 0;
  863.   vxprintf(mout,(void*)&mp,fmt,ap);
  864.   return mp.buf;
  865. }
  866.  
  867. /*
  868. ** Null-printf does nothing but measure the size of the output
  869. ** string.
  870. */
  871. static void nout(void){
  872.   return;
  873. }
  874. int nprintf(const char *fmt, ...){
  875.   va_list ap;
  876.   va_start(ap,fmt);
  877.   return vxprintf((void(*)(char*,int,void*))nout,(void*)0,fmt,ap);
  878. }
  879. int vnprintf(const char *fmt, va_list ap){
  880.   return vxprintf((void(*)(char*,int,void*))nout,(void*)0,fmt,ap);
  881. }
  882. #endif
  883.  
  884. #ifdef TEST1
  885. /****************************************************************************
  886. *                         Test integer conversions                          *
  887. ****************************************************************************/
  888.  
  889. #ifndef NOINTERFACE
  890.    Sorry....  This test will not work if the interface is
  891.    turned on.
  892. #endif
  893.  
  894. char outbuf[1000];
  895. int  outidx = 0;
  896.  
  897. int fout(char *str, int amt){
  898.   strncpy(&outbuf[outidx],str,amt);
  899.   outidx += amt;
  900.   return 0;
  901. }
  902.  
  903. int testprintf(char *fmt, ...){
  904.   va_list ap;
  905.   va_start(ap,fmt);
  906.   return vxprintf((void(*)(char*,int,void*))fout,0,fmt,ap);
  907. }
  908. #include <a.out.h>
  909. void moncontrol(int);
  910.  
  911. void main(){
  912.   static char letters[] = "dixXou%";
  913.   static char *flags[] = { "", "-", " ", "+", "#", "-#", "+-", "+#", "# ",
  914.                            "-+#", "# -+", 0 };
  915.   static char *widths[] = { "", "0", "05", "1", "3", "10", "*", 0 };
  916.   static char *precisions[] = { "", ".1", ".3", ".10", ".*", 0 };
  917.   int values[] = { 0, 1, 10, 9999, -1, -10, -9999 };
  918.   char format[100];
  919.   char sysout[1000];
  920.   int longflag;
  921.   int sysrc;
  922.   int vdrc;
  923.   int lcnt;
  924.   int fcnt;
  925.   int wcnt;
  926.   int pcnt;
  927.   int vcnt;
  928.   int v1, v2, v3;
  929.   int count = 0;
  930.   moncontrol(0);
  931.   lcnt = fcnt = vcnt = longflag = wcnt = pcnt = v1 = v2 = v3 = 0;
  932.   do{
  933.     sprintf(format,"Ho ho ho %%%s%s%s%s%c hi hi hi",
  934.        flags[fcnt],widths[wcnt],precisions[pcnt],
  935.        longflag?"l":"",letters[lcnt]);
  936.     v1 = values[vcnt];
  937.     if( widths[wcnt][0]=='*' ){
  938.       v2 = v1;
  939.       v1 = 8;
  940.     }
  941.     if( precisions[pcnt][1]=='*' ){
  942.       v3 = v2;
  943.       v2 = v1;
  944.       v1 = 7;
  945.     }
  946.     outidx = 0;
  947.     moncontrol(1);
  948.     if( longflag ){
  949.       sprintf(sysout,format,(long)v1,v2,v3);
  950.       vdrc = testprintf(format,(long)v1,v2,v3);
  951.     }else{
  952.       sprintf(sysout,format,v1,v2,v3);
  953.       vdrc = testprintf(format,v1,v2,v3);
  954.     }
  955.     moncontrol(0);
  956.     sysrc = strlen(sysout);
  957.     outbuf[outidx] = 0;
  958.     count++;
  959.     if( count%1000==0 ){
  960.       printf("\rChecked thru %d...   ",count);
  961.       fflush(stdout);
  962.     }
  963.     if( vdrc!=sysrc || strcmp(outbuf,sysout)!=0 ){
  964.       printf("Format: [%s]  Arguments: %d, %d, %d\n",format,v1,v2,v3);
  965.       printf("  system:   [%s] returned %d\n",sysout,sysrc);
  966.       printf("  vxprintf: [%s] returned %d\n",outbuf,vdrc);
  967.     }
  968.     vcnt++;
  969.     if( vcnt>=sizeof(values)/sizeof(int) ){ vcnt = 0; lcnt++; }
  970.     if( letters[lcnt]==0 ){ lcnt = 0; longflag++; }
  971.     if( longflag==2 ){ longflag = 0; fcnt++; }
  972.     if( flags[fcnt]==0 ){ fcnt = 0; wcnt++; }
  973.     if( widths[wcnt]==0 ){ wcnt = 0; pcnt++; }
  974.   }while( precisions[pcnt] );
  975.   printf("\nChecked %d combinations.\n",count);
  976. }
  977. #endif
  978. #ifdef TEST2
  979. /****************************************************************************
  980. *                         Test string conversions                           *
  981. ****************************************************************************/
  982.  
  983. #ifndef NOINTERFACE
  984.    Sorry....  This test will not work if the interface is
  985.    turned on.
  986. #endif
  987.  
  988. char outbuf[1000];
  989. int  outidx = 0;
  990.  
  991. int fout(char *str, int amt){
  992.   strncpy(&outbuf[outidx],str,amt);
  993.   outidx += amt;
  994.   return 0;
  995. }
  996.  
  997. int testprintf(char *fmt, ...){
  998.   va_list ap;
  999.   va_start(ap,fmt);
  1000.   return vxprintf((void(*)(char*,int,void*))fout,0,fmt,ap);
  1001. }
  1002.  
  1003. void main(){
  1004.   int count = 0;
  1005.   char sysout[1000];
  1006.   int sysrc;
  1007.   int vdrc;
  1008.   int i;
  1009.   int sysx;
  1010.   int vdx;
  1011.   for(i=0; i<50; i++){
  1012.     sprintf(sysout,"%*s%n--%c ho ho ho",i,(i&1)?"xxx":"yyyyyyyy",&sysx,i+'a');
  1013.     sysrc = strlen(sysout);
  1014.     outidx = 0;
  1015.     vdrc = testprintf("%*s%n--%c ho ho ho",i,(i&1)?"xxx":"yyyyyyyy",&vdx,i+'a');
  1016.     outbuf[outidx] = 0;
  1017.     count++;
  1018.     if( vdrc!=sysrc || strcmp(outbuf,sysout)!=0 || sysx!=vdx ){
  1019.       printf("  system:   [%s] returned %d with x=%d\n",sysout,sysrc,sysx);
  1020.       printf("  vxprintf: [%s] returned %d with x=%d\n",outbuf,vdrc,vdx);
  1021.     }
  1022.   }
  1023.   printf("Checked %d combinations.\n",count);
  1024. }
  1025. #endif
  1026. #ifdef TEST3
  1027. /****************************************************************************
  1028. *                      Test floating-point conversions                      *
  1029. ****************************************************************************/
  1030.  
  1031. #ifndef NOINTERFACE
  1032.    Sorry....  This test will not work if the interface is
  1033.    turned on.
  1034. #endif
  1035.  
  1036. char outbuf[1000];
  1037. int  outidx = 0;
  1038.  
  1039. int fout(char *str, int amt){
  1040.   strncpy(&outbuf[outidx],str,amt);
  1041.   outidx += amt;
  1042.   return 0;
  1043. }
  1044.  
  1045. int testprintf(char *fmt, ...){
  1046.   va_list ap;
  1047.   va_start(ap,fmt);
  1048.   return vxprintf((void(*)(char*,int,void*))fout,0,fmt,ap);
  1049. }
  1050. #include <a.out.h>
  1051. void moncontrol(int);
  1052.  
  1053. /*
  1054. ** compare number strings.  Ignore errors past the first 15 significant
  1055. ** digits.  Return TRUE for a match, and FALSE for a mismatch.
  1056. */
  1057. int rcmp(char *a, char *b){
  1058.   int digitcnt = 0;
  1059.   while( *a && *b ){
  1060.     if( (!isdigit(*a) || ++digitcnt<15) && *a!=*b ){
  1061.       return 0;
  1062.     }
  1063.     if( !isdigit(*a) && *a!='.' ) digitcnt = 0;
  1064.     a++;
  1065.     b++;
  1066.   }
  1067.   return *a==*b;
  1068. }
  1069.  
  1070. void main(){
  1071.   int count = 0;
  1072.   char sysout[1000];
  1073.   int sysrc;
  1074.   int vdrc;
  1075.   double value;
  1076.   int w,p;
  1077.   static char *format[] = { "%*.*g","%-*.*g","%#*.*g","%+*.*g", "% *.*G",
  1078.                             "%*.*e", "%#*.*E", "%*.*f", "%#*.*f", "%*%%*g"};
  1079.   int fcnt;
  1080.   moncontrol(0);
  1081.   for(fcnt=0; fcnt<sizeof(format)/sizeof(char*); fcnt++){
  1082.    for(w=0; w<=12; w += 3){
  1083.     for(p=0; p<=12; p += 3){
  1084.       for(value=1.234567890123456789e-32; value<1.3e+32; value *= 1e8 ){
  1085.         outidx = 0;
  1086.         moncontrol(1);
  1087.         sprintf(sysout,format[fcnt],w,p,value);
  1088.         vdrc = testprintf(format[fcnt],w,p,value);
  1089.         moncontrol(0);
  1090.         sysrc = strlen(sysout);
  1091.         outbuf[outidx] = 0;
  1092.         count++;
  1093.         if( vdrc!=sysrc || !rcmp(outbuf,sysout) ){
  1094.           printf("Count=%d  Format=%s  Width=%d  Precision=%d  Value=%.16g\n",
  1095.             count,format[fcnt],w,p,value);
  1096.           printf("  system:   [%s] returned %d\n",sysout,sysrc);
  1097.           printf("  vxprintf: [%s] returned %d\n",outbuf,vdrc);
  1098.         }
  1099.       }
  1100.     }
  1101.    }
  1102.   }
  1103.   printf("Checked %d combinations.\n",count);
  1104. }
  1105. #endif
  1106. #ifdef TEST4
  1107. /****************************************************************************
  1108. *                        Test the interface routines                        *
  1109. ****************************************************************************/
  1110.  
  1111. #define BSIZE 71
  1112.  
  1113. void main(void){
  1114.   char buf[BSIZE];
  1115.   char buf2[BSIZE];
  1116.   char *cp;
  1117.   int i;
  1118.   for(i=0; i<BSIZE; i++) buf[i] = (i%10)+'0';
  1119.   buf[BSIZE-1] = 0;
  1120.   printf("This is a test of all the interface routines:\n");
  1121.   printf("vprintf:\n");
  1122.   vprintf(buf,0);
  1123.   printf("\nfprintf:\n");
  1124.   fprintf(stdout,buf);
  1125.   printf("\nvfprintf:\n");
  1126.   vfprintf(stdout,buf,0);
  1127.   printf("\nsprintf:\n");
  1128.   sprintf(buf2,buf);
  1129.   printf(buf2);
  1130.   printf("\nvsprintf:\n");
  1131.   vsprintf(buf2,buf,0);
  1132.   printf(buf2);
  1133.   printf("\nsnprintf:  (n=52)\n");
  1134.   snprintf(buf2,52,buf);
  1135.   printf(buf2);
  1136.   printf("\nvsnprintf:  (n=17)\n");
  1137.   vsnprintf(buf2,17,buf,0);
  1138.   printf(buf2);
  1139.   printf("\nmprintf:\n");
  1140.   cp = mprintf(buf);
  1141.   printf(cp);
  1142.   free(cp);
  1143.   printf("\nvmprintf:\n");
  1144.   cp = vmprintf(buf,0);
  1145.   printf(cp);
  1146.   free(cp);
  1147.   printf("\nnprintf:\n");
  1148.   i = nprintf(buf);
  1149.   printf("(returns %d)\n",i);
  1150.   printf("vnprintf:\n");
  1151.   i = vnprintf(buf,0);
  1152.   printf("(returns %d)\n",i);
  1153. }
  1154. #endif
  1155. #ifdef TEST5
  1156. /****************************************************************************
  1157. *                  Test the user-specified conversion logic                 *
  1158. ****************************************************************************/
  1159.  
  1160. /*
  1161. ** A routine to print integer number of cents as a dollar amount.
  1162. */
  1163. int dollar(convertctrl *vp){
  1164.   int d,neg;
  1165.   char *front, *back;
  1166.   int size, fbsize;
  1167.   int prec;
  1168.  
  1169.   d = va_arg(vp->ap,int);
  1170.   if( d<0 ){
  1171.     d = -d;
  1172.     neg = 1;
  1173.   }else{
  1174.     neg = 0;
  1175.   }
  1176.   prec = vp->precision;
  1177.   back = front = "";
  1178.   if( neg ){
  1179.     if( vp->flag_sharp ){
  1180.       front = "(";
  1181.       back = ")";
  1182.     }else{
  1183.       front = "-";
  1184.     }
  1185.   }else{
  1186.     if( vp->flag_sharp ) back = " ";
  1187.     if( vp->flag_plus ) front = "+";
  1188.     if( vp->flag_blank ) front = " ";
  1189.   }
  1190.   fbsize = (*front!=0) + (*back!=0);
  1191.   size = sprintf(vp->buf,"%s%d.%.2d%s",front,d/100,d%100,back);
  1192.   if( prec>0 && size>prec ){
  1193.     size = sprintf(vp->buf,"%s%.*'*.**%s",front,prec-fbsize-3,back);
  1194.   }
  1195.   return size;
  1196. }
  1197.  
  1198. char *fmt[] = { "%$", "%#$", "%#10.7$", "%-10.5$", "%#.8$",
  1199.                 "%+.10$", "% #8.8$" };
  1200. #define nfmt (sizeof(fmt)/sizeof(char*))
  1201.  
  1202. int value[] = { 0, 1, 100, 123456789, -1, -100, -123456789 };
  1203. #define nvalue (sizeof(value)/sizeof(int))
  1204.  
  1205. void main(void){
  1206.   if( !converter('$',dollar,0) ){
  1207.     printf("Call to converter() failed...\n");
  1208.   }else{
  1209.     int i,j;
  1210.     for(i=0; i<nfmt; i++){
  1211.       for(j=0; j<nvalue; j++){
  1212.         int x;
  1213.         x = printf("format=\"%s\"  value=%d",fmt[i],value[j]);
  1214.         printf("%.*' result=\"",40-x);
  1215.         printf(fmt[i],value[j]);
  1216.         printf("\"\n");
  1217.       }
  1218.     }
  1219.   }
  1220. }
  1221. #endif
  1222. !end-of-xprintf.c!
  1223. echo extracting xprintf.h
  1224. cat <<\!end-of-xprintf.h! >xprintf.h
  1225. /*
  1226. ** Extra printf routines implemented by xprintf.c which are not found
  1227. ** in standard libraries are declared here.
  1228. */
  1229. #ifndef XPRINTFH
  1230.  
  1231. #define XPRINTFH
  1232. #include <stdarg.h>
  1233. int xprintf(void (*)(char*,int,void*), void*, const char*, ...);
  1234. int vxprintf(void (*)(char*,int,void*), void*, const char*, va_list);
  1235. int snprintf(char*, int, const char*, ...);
  1236. int vsnprintf(char*, int, const char*, va_list);
  1237. char *mprintf(const char*, ...);
  1238. char *vmprintf(const char*, va_list);
  1239. int nprintf(const char*, ...);
  1240. int vnprintf(const char*, va_list);
  1241.  
  1242. /*
  1243. ** The following structure is used to pass information from vxprintf to
  1244. ** the user specified conversion routine for user specified conversions.
  1245. **
  1246. ** The user conversion routine should read the next argument using
  1247. ** the "ap" field of this structure, convert this argument into text
  1248. ** of "bufsize" or fewer characters, place this text in "*buf", then
  1249. ** return the length of the text.  Information about the precision and
  1250. ** values of flags are supplied.  (If no precision is specified, then
  1251. ** a value of -1 is put in the "precision" field.)
  1252. */
  1253. typedef struct s_cvertctrl {
  1254.   int bufsize;         /* Size of buf[] */
  1255.   char *buf;           /* Start of buffer into which conversion is written */
  1256.   int precision;       /* Precision.  -1 if none specified */
  1257.   int flag_sharp;      /* True if the "#" flag is present */
  1258.   int flag_plus;       /* True if the "+" flag is present */
  1259.   int flag_blank;      /* True if the " " flag is present */
  1260.   va_list ap;          /* Pointer to the argument to be converted */
  1261.   void *arg;           /* Same as 3rd argument to converter() */
  1262. } convertctrl;
  1263. int converter(char, int (*)(convertctrl*), void *);
  1264.  
  1265. #endif
  1266. !end-of-xprintf.h!
  1267.