]> arthur.barton.de Git - netatalk.git/blob - libatalk/compat/snprintf.c
7ee7a931837f572695a4d8d5a7d24ba6049d1fd8
[netatalk.git] / libatalk / compat / snprintf.c
1 /**************************************************************************
2  * Copyright 1994-2000 Patrick Powell, San Diego, CA <papowell@astart.com>
3  * 
4  * Modified for Netatalk 2002/02/12 Burkhard Schmidt <bs@cpfs.mpg.de>
5  **************************************************************************/
6
7 /*
8    Overview:
9
10    plp_snprintf( char *buffer, int len, const char *format,...)
11    plp_unsafe_snprintf( char *buffer, int len, const char *format,...)
12      its horribly unsafe companion that does NOT protect you from
13      the printing of evil control characters,  but may be necessary
14      See the man page documentation below
15    
16    This version of snprintf was developed originally for printing
17    on a motley collection of specialized hardware that had NO IO
18    library.  Due to contractual restrictions,  a clean room implementation
19    of the printf() code had to be developed.
20    
21    The method chosen for printf was to be as paranoid as possible,
22    as these platforms had NO memory protection,  and very small
23    address spaces.  This made it possible to try to print
24    very long strings, i.e. - all of memory, very easily.  To guard
25    against this,  all printing was done via a buffer, generous enough
26    to hold strings,  but small enough to protect against overruns,
27    etc.
28    
29    Strangely enough,  this proved to be of immense importance when
30    SPRINTFing to a buffer on a stack...  The rest,  of course,  is
31    well known,  as buffer overruns in the stack are a common way to
32    do horrible things to operating systems, security, etc etc.
33    
34    This version of snprintf is VERY limited by modern standards.
35
36    Revision History:
37    First Released Version - 1994.  This version had NO comments.
38    First Released Version - 1994.  This version had NO comments.
39    Second Major Released Version - Tue May 23 10:43:44 PDT 2000
40     Configuration and other items changed.  Read this doc.
41     Treat this as a new version.
42    
43    COPYRIGHT AND TERMS OF USE:
44    
45    You may use, copy, distribute, or otherwise incorporate this software
46    and documentation into any product or other item,  provided that
47    the copyright in the documentation and source code as well as the
48    source code generated constant strings in the object, executable
49    or other code remain in place and are present in executable modules
50    or objects.
51    
52    You may modify this code as appropriate to your usage; however the
53    modified version must be identified by changing the various source
54    and object code identification strings as is appropriately noted
55    in the source code.
56    
57    The next include line is expected to work in conjunction with the
58    GNU CONFIGURE utility.  You  should define the following macros
59    appropriately:
60    
61    HAVE_STDARG_H - if the <stdargs.h> include file is available
62    HAVE_VARARG_H - if the <varargs.h> include file is available
63    
64    HAVE_STRERROR - if the strerror() routine is available.
65      If it is not available, then examine the lines containing
66      the tests below.  You may need to fiddle with HAVE_SYS_NERR
67    HAVE_SYS_ERRLIST
68    HAVE_DECL_SYS_ERRLIST
69    HAVE_SYS_NERR
70    HAVE_DECL_SYS_NERR
71    
72    HAVE_QUAD_T      - if the quad_t type is defined
73    HAVE_LONG_LONG   - if the long long type is defined
74    HAVE_LONG_DOUBLE - if the long double type is defined
75    
76      If you are using the GNU configure (autoconf) facility, add the
77      following line to the configure.in file, to force checking for the
78      quad_t and long long  data types:
79
80
81         AC_CHECK_FUNCS(strerror);
82         AC_CACHE_CHECK(for errno,
83         ac_cv_errno,
84         [
85         AC_TRY_LINK(,[extern int errno; return (errno);],
86                 ac_cv_errno=yes, ac_cv_errno=no)
87         ])
88         if test "$ac_cv_errno" = yes; then
89                 AC_DEFINE(HAVE_ERRNO)
90                 AC_CACHE_CHECK(for errno declaration,
91                 ac_cv_decl_errno,
92                 [
93                 AC_TRY_COMPILE([
94                 #include <stdio.h>
95                 #ifdef HAVE_STDLIB_H
96                 #include <stdlib.h>
97                 #endif
98                 #ifdef HAVE_UNISTD_H
99                 #include <unistd.h>
100                 #endif
101                 #ifdef HAVE_ERRNO_H
102                 #include <errno.h>
103                 ],[return(sys_nerr);],
104                         ac_cv_decl_errno=yes, ac_cv_decl_errno=no)
105                 ])
106                 if test "$ac_cv_decl_errno" = yes; then
107                         AC_DEFINE(HAVE_DECL_ERRNO)
108                 fi;
109         fi
110
111         AC_CACHE_CHECK(for sys_nerr,
112         ac_cv_sys_nerr,
113         [
114         AC_TRY_LINK(,[extern int sys_nerr; return (sys_nerr);],
115                 ac_cv_sys_nerr=yes, ac_cv_sys_nerr=no)
116         ])
117         if test "$ac_cv_sys_nerr" = yes; then
118                 AC_DEFINE(HAVE_SYS_NERR)
119                 AC_CACHE_CHECK(for sys_nerr declaration,
120                 ac_cv_decl_sys_nerr,
121                 [
122                 AC_TRY_COMPILE([
123                 #include <stdio.h>
124                 #ifdef HAVE_STDLIB_H
125                 #include <stdlib.h>
126                 #endif
127                 #ifdef HAVE_UNISTD_H
128                 #include <unistd.h>
129                 #endif],[return(sys_nerr);],
130                 ac_cv_decl_sys_nerr_def=yes, ac_cv_decl_sys_nerr_def=no)
131                 ])
132                 if test "$ac_cv_decl_sys_nerr" = yes; then
133                         AC_DEFINE(HAVE_DECL_SYS_NERR)
134                 fi
135         fi
136
137
138         AC_CACHE_CHECK(for sys_errlist array,
139         ac_cv_sys_errlist,
140         [AC_TRY_LINK(,[extern char *sys_errlist[];
141                 sys_errlist[0];],
142                 ac_cv_sys_errlist=yes, ac_cv_sys_errlist=no)
143         ])
144         if test "$ac_cv_sys_errlist" = yes; then
145                 AC_DEFINE(HAVE_SYS_ERRLIST)
146                 AC_CACHE_CHECK(for sys_errlist declaration,
147                 ac_cv_sys_errlist_def,
148                 [AC_TRY_COMPILE([
149                 #include <stdio.h>
150                 #include <errno.h>
151                 #ifdef HAVE_STDLIB_H
152                 #include <stdlib.h>
153                 #endif
154                 #ifdef HAVE_UNISTD_H
155                 #include <unistd.h>
156                 #endif],[char *s = sys_errlist[0]; return(*s);],
157                 ac_cv_decl_sys_errlist=yes, ac_cv_decl_sys_errlist=no)
158                 ])
159                 if test "$ac_cv_decl_sys_errlist" = yes; then
160                         AC_DEFINE(HAVE_DECL_SYS_ERRLIST)
161                 fi
162         fi
163
164
165
166         AC_CACHE_CHECK(checking for long long,
167         ac_cv_long_long,
168         [
169         AC_TRY_COMPILE([
170         #include <stdio.h>
171         #include <sys/types.h>
172         ], [printf("%d",sizeof(long long));],
173         ac_cv_long_long=yes, ac_cv_long_long=no)
174         ])
175         if test $ac_cv_long_long = yes; then
176           AC_DEFINE(HAVE_LONG_LONG)
177         fi
178
179         AC_CACHE_CHECK(checking for long double,
180         ac_cv_long_double,
181         [
182         AC_TRY_COMPILE([
183         #include <stdio.h>
184         #include <sys/types.h>
185         ], [printf("%d",sizeof(long double));],
186         ac_cv_long_double=yes, ac_cv_long_double=no)
187         ])
188         if test $ac_cv_long_double = yes; then
189           AC_DEFINE(HAVE_LONG_DOUBLE)
190         fi
191
192         AC_CACHE_CHECK(checking for quad_t,
193         ac_cv_quad_t,
194         [
195         AC_TRY_COMPILE([
196         #include <stdio.h>
197         #include <sys/types.h>
198         ], [printf("%d",sizeof(quad_t));],
199         ac_cv_quad_t=yes, ac_cv_quad_t=no)
200         ])
201         if test $ac_cv_quad_t = yes; then
202           AC_DEFINE(HAVE_QUAD_T)
203         fi
204
205
206
207 NAME
208      plp_snprintf, plp_vsnprintf - formatted output conversion
209
210 SYNOPSIS
211      #include <stdio.h>
212      #include <stdarg.h>
213
214      int
215      plp_snprintf(const char *format, size_t size, va_list ap);
216      int
217      plp_unsafe_snprintf(const char *format, size_t size, va_list ap);
218
219      AKA snprintf and unsafe_snprintf in the documentation below
220
221      int
222      vsnprintf(char *str, size_t size, const char *format, va_list ap);
223      int
224      unsafe_vsnprintf(char *str, size_t size, const char *format, va_list ap);
225
226      AKA vsnprintf and unsafe_vsnprintf in the documentation below
227
228      (Multithreaded Safe)
229
230 DESCRIPTION
231      The printf() family of functions produces output according to
232      a format as described below.  Snprintf(), and vsnprintf()
233      write to the character string str. These functions write the
234      output under the control of a format string that specifies
235      how subsequent arguments (or arguments accessed via the
236      variable-length argument facilities of stdarg(3))  are converted
237      for output.  These functions return the number of characters
238      printed (not including the trailing `\0' used to end output
239      to strings).  Snprintf() and vsnprintf() will write at most
240      size-1 of the characters printed into the output string (the
241      size'th character then gets the terminating `\0'); if the
242      return value is greater than or equal to the size argument,
243      the string was too short and some of the printed characters
244      were discarded.  The size or str may be given as zero to find
245      out how many characters are needed; in this case, the str
246      argument is ignored.
247
248      By default, the snprintf function will not format control
249      characters (except new line and tab) in strings.  This is a
250      safety feature that has proven to be extremely critical when
251      using snprintf for secure applications and when debugging.
252      If you MUST have control characters formatted or printed,
253      then use the unsafe_snprintf() and unsafe_vsnprintf() and on
254      your own head be the consequences.  You have been warned.
255
256      There is one exception to the comments above, and that is
257      the "%c" (character) format.  It brutally assumes that the
258      user will have performed the necessary 'isprint()' or other
259      checks and uses the integer value as a character.
260
261      The format string is composed of zero or more directives:
262      ordinary characters (not %), which are copied unchanged to
263      the output stream; and conversion specifications, each
264      of which results in fetching zero or more subsequent arguments.
265      Each conversion specification is introduced by the character
266      %. The arguments must correspond properly (after type promotion)
267      with the conversion specifier.  After the %, the following
268      appear in sequence:
269
270      o   Zero or more of the following flags:
271
272            -   A zero `0' character specifying zero padding.  For
273              all conversions except n, the converted value is padded
274              on the left with zeros rather than blanks.  If a
275              precision is given with a numeric conversion (d, i,
276              o, u, i, x, and X), the `0' flag is ignored.
277
278        -   A negative field width flag `-' indicates the converted
279              value is to be left adjusted on the field boundary.  Except
280              for n conversions, the converted value is padded on
281              the right with blanks, rather than on the left with
282              blanks or zeros.  A `-' overrides a `0' if both are
283              given.
284
285            -   A space, specifying that a blank should be left before
286              a positive number produced by a signed conversion (d, e, E, f,
287              g, G, or i).
288
289            -   A `+' character specifying that a sign always be placed
290              before a number produced by a signed conversion.  A `+' overrides
291              a space if both are used.
292
293      o   An optional decimal digit string specifying a minimum
294                  field width.  If the converted value has fewer
295                  characters than the field width, it will be padded
296                  with spaces on the left (or right, if the
297                  left-adjustment flag has been given) to fill out
298                  the field width.
299
300      o   An optional precision, in the form of a period `.' followed
301                  by an optional digit string.  If the digit string
302                  is omitted, the precision is taken as zero.  This
303                  gives the minimum number of digits to appear for
304                  d, i, o, u, x, and X conversions, the number of
305                  digits to appear after the decimal-point for e,
306                  E, and f conversions, the maximum number of
307                  significant digits for g and G conversions, or
308                  the maximum number of characters to be printed
309                  from a string for s conversions.
310
311      o   The optional character h, specifying that a following d,
312                  i, o, u, x, or X conversion corresponds to a short
313                  int or unsigned short int argument, or that a
314                  following n conversion corresponds to a pointer
315                  to a short int argument.
316
317      o   The optional character l (ell) specifying that a following
318                  d, i, o, u, x, or X conversion applies to a pointer
319                  to a long int or unsigned long int argument, or
320                  that a following n conversion corresponds to a
321                  pointer to a long int argument.
322
323      o   The optional character q, specifying that a following d,
324                  i, o, u, x, or X conversion corresponds to a quad_t
325                  or u_quad_t argument, or that a following n
326                  conversion corresponds to a quad_t argument.
327          This value is always printed in HEX notation.  Tough.
328          quad_t's are an OS system implementation, and should
329          not be allowed.
330
331      o   The character L specifying that a following e, E, f, g,
332                  or G conversion corresponds to a long double
333                  argument.
334
335      o   A character that specifies the type of conversion to be applied.
336
337
338      A field width or precision, or both, may be indicated by an asterisk `*'
339      instead of a digit string.  In this case, an int argument supplies the
340      field width or precision.  A negative field width is treated as a left
341      adjustment flag followed by a positive field width; a negative precision
342      is treated as though it were missing.
343
344      The conversion specifiers and their meanings are:
345
346      diouxX  The int (or appropriate variant) argument is converted to signed
347                          decimal (d and i), unsigned octal (o), unsigned decimal
348                          (u), or unsigned hexadecimal (x and X) notation.  The
349                          letters abcdef are used for x conversions; the letters
350                          ABCDEF are used for X conversions.  The precision, if
351                          any, gives the minimum number of digits that must
352                          appear; if the converted value requires fewer digits,
353                          it is padded on the left with zeros.
354
355      eE      The double argument is rounded and converted in the style
356              [-]d.ddde+-dd where there is one digit before the decimal-point
357                          character and the number of digits after it is equal
358                          to the precision; if the precision is missing, it is
359                          taken as 6; if the precision is zero, no decimal-point
360                          character appears.  An E conversion uses the letter
361                          E (rather than e) to introduce the exponent.
362                          The exponent always contains at least two digits; if
363                          the value is zero, the exponent is 00.
364
365      f       The double argument is rounded and converted to decimal notation
366              in the style [-]ddd.ddd, where the number of digits after the
367              decimal-point character is equal to the precision specification.
368              If the precision is missing, it is taken as 6; if the precision
369              is explicitly zero, no decimal-point character appears.  If a
370              decimal point appears, at least one digit appears before it.
371
372      g       The double argument is converted in style f or e (or
373                          E for G conversions).  The precision specifies the
374                          number of significant digits.  If the precision is
375                          missing, 6 digits are given; if the precision is zero,
376                          it is treated as 1.  Style e is used if the exponent
377                          from its conversion is less than -4 or greater than
378                          or equal to the precision.  Trailing zeros are removed
379                          from the fractional part of the result; a decimal
380                          point appears only if it is followed by at least one
381                          digit.
382
383      c       The int argument is converted to an unsigned char,
384              and the resulting character is written.
385
386      s       The ``char *'' argument is expected to be a pointer to an array
387                          of character type (pointer to a string).  Characters
388                          from the array are written up to (but not including)
389                          a terminating NUL character; if a precision is
390                          specified, no more than the number specified are
391                          written.  If a precision is given, no null character
392                          need be present; if the precision is not specified,
393                          or is greater than the size of the array, the array
394                          must contain a terminating NUL character.
395
396      %       A `%' is written. No argument is converted. The complete
397              conversion specification is `%%'.
398
399      In no case does a non-existent or small field width cause truncation of a
400      field; if the result of a conversion is wider than the field width, the
401      field is expanded to contain the conversion result.
402
403 EXAMPLES
404      To print a date and time in the form `Sunday, July 3, 10:02', where
405      weekday and month are pointers to strings:
406
407            #include <stdio.h>
408            fprintf(stdout, "%s, %s %d, %.2d:%.2d\n",
409                    weekday, month, day, hour, min);
410
411      To print pi to five decimal places:
412
413            #include <math.h>
414            #include <stdio.h>
415            fprintf(stdout, "pi = %.5f\n", 4 * atan(1.0));
416
417      To allocate a 128 byte string and print into it:
418
419            #include <stdio.h>
420            #include <stdlib.h>
421            #include <stdarg.h>
422            char *newfmt(const char *fmt, ...)
423            {
424                            char *p;
425                            va_list ap;
426                            if ((p = malloc(128)) == NULL)
427                                    return (NULL);
428                            va_start(ap, fmt);
429                            (void) vsnprintf(p, 128, fmt, ap);
430                            va_end(ap);
431                            return (p);
432            }
433
434 SEE ALSO
435      printf(1),  scanf(3)
436
437 STANDARDS
438      Turkey C Standardization and wimpy POSIX folks did not define
439      snprintf or vsnprintf().
440
441 BUGS
442      The conversion formats %D, %O, and %U are not standard and are provided
443      only for backward compatibility.  The effect of padding the %p format
444      with zeros (either by the `0' flag or by specifying a precision), and the
445      benign effect (i.e., none) of the `#' flag on %n and %p conversions, as
446      well as other nonsensical combinations such as %Ld, are not standard;
447      such combinations should be avoided.
448
449      The typedef names quad_t and u_quad_t are infelicitous.
450
451 */
452
453
454 #include "config.h"
455
456 #if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF)
457
458 #include <sys/types.h>
459 #include <ctype.h>
460 #include <stdlib.h>
461 #if defined(HAVE_STRING_H)
462 # include <string.h>
463 #endif
464 #if defined(HAVE_STRINGS_H)
465 # include <strings.h>
466 #endif
467 #include <stdio.h>
468
469 /*
470  * For testing, define these values
471  */
472 #if 0
473 #define HAVE_STDARG_H 1
474 #define TEST 1
475 #define HAVE_QUAD_T 1
476 #endif
477
478 /**** ENDINCLUDE ****/
479
480 /*************************************************
481  * KEEP THIS STRING - MODIFY AT THE END WITH YOUR REVISIONS
482  * i.e. - the LOCAL REVISIONS part is for your use
483  *************************************************/
484  
485  
486  static char *const _id = "plp_snprintf V98.12.21 Copyright Patrick Powell 1988-2000 "
487  "$Id: snprintf.c,v 1.1 2002-08-30 16:56:50 jmarcus Exp $"
488  " LOCAL REVISIONS: Modified for Netatalk 2002/02/12 Burkhard Schmidt";
489
490 /* varargs declarations: */
491
492 # undef HAVE_STDARGS    /* let's hope that works everywhere (mj) */
493 # undef VA_LOCAL_DECL
494 # undef VA_START
495 # undef VA_SHIFT
496 # undef VA_END
497
498 #if defined(HAVE_STDARG_H)
499 # include <stdarg.h>
500 # define HAVE_STDARGS    /* let's hope that works everywhere (mj) */
501 # define VA_LOCAL_DECL   va_list ap;
502 # define VA_START(f)     va_start(ap, f)
503 # define VA_SHIFT(v,t)  ;       /* no-op for ANSI */
504 # define VA_END          va_end(ap)
505 #else
506 # if defined(HAVE_VARARGS_H)
507 #  include <varargs.h>
508 #  undef HAVE_STDARGS
509 #  define VA_LOCAL_DECL   va_list ap;
510 #  define VA_START(f)     va_start(ap)          /* f is ignored! */
511 #  define VA_SHIFT(v,t) v = va_arg(ap,t)
512 #  define VA_END                va_end(ap)
513 # else
514 XX ** NO VARARGS ** XX
515 # endif
516 #endif
517
518 union value {
519 #if defined(HAVE_QUAD_T)
520         quad_t qvalue;
521 #endif
522 #if defined(HAVE_LONG_LONG)
523         long long value;
524 #else
525         long value;
526 #endif
527         double dvalue;
528 };
529
530 #undef CVAL 
531 #define CVAL(s) (*((unsigned char *)s))
532
533 extern int errno;
534  static char * plp_Errormsg ( int err, char *buffer );
535  static void dopr( int visible_control, char **buffer, int *left,
536         const char *format, va_list args );
537  static void fmtstr( int visible_control, char **buffer, int *left,
538         char *value, int ljust, int len, int zpad, int precision );
539  static void fmtnum(  char **buffer, int *left,
540         union value *value, int base, int dosign,
541         int ljust, int len, int zpad, int precision );
542 #if defined(HAVE_QUAD_T)
543  static void fmtquad(  char **buffer, int *left,
544         union value *value, int base, int dosign,
545         int ljust, int len, int zpad, int precision );
546 #endif
547  static void fmtdouble( char **bufer, int *left,
548         int fmt, double value,
549         int ljust, int len, int zpad, int precision );
550  static void dostr(  char **buffer, int *left, char *str );
551  static void dopr_outch(  char **buffer, int *left, int c );
552
553 int plp_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
554 {
555         int left;
556         char *buffer;
557         if( count < 0 ) count = 0;
558         left = count;
559         if( count == 0 ) str = 0;
560         buffer = str;
561         dopr( 1, &buffer, &left, fmt, args );
562         /* fprintf(stderr,"str 0x%x, buffer 0x%x, count %d, left %d\n",
563                 (int)str, (int)buffer, count, left ); */
564         if( str && count > 0 ){
565                 if( left > 0 ){
566                         str[count-left] = 0;
567                 } else {
568                         str[count-1] = 0;
569                 }
570         }
571         return(count - left);
572 }
573
574 int plp_unsafe_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
575 {
576         int left;
577         char *buffer;
578         if( count < 0 ) count = 0;
579         left = count;
580         if( count == 0 ) str = 0;
581         buffer = str;
582         dopr( 0, &buffer, &left, fmt, args );
583         /* fprintf(stderr,"str 0x%x, buffer 0x%x, count %d, left %d\n",
584                 (int)str, (int)buffer, count, left ); */
585         if( str && count > 0 ){
586                 if( left > 0 ){
587                         str[count-left] = 0;
588                 } else {
589                         str[count-1] = 0;
590                 }
591         }
592         return(count - left);
593 }
594
595 /* VARARGS3 */
596 #ifdef HAVE_STDARGS
597 int plp_snprintf (char *str,size_t count,const char *fmt,...)
598 #else
599 int plp_snprintf (va_alist) va_dcl
600 #endif
601 {
602 #ifndef HAVE_STDARGS
603     char *str;
604         size_t count;
605     char *fmt;
606 #endif
607         int n = 0;
608     VA_LOCAL_DECL
609
610     VA_START (fmt);
611     VA_SHIFT (str, char *);
612     VA_SHIFT (count, size_t );
613     VA_SHIFT (fmt, char *);
614     n = plp_vsnprintf ( str, count, fmt, ap);
615     VA_END;
616         return( n );
617 }
618
619
620 /* VARARGS3 */
621 #ifdef HAVE_STDARGS
622 int plp_unsafe_snprintf (char *str,size_t count,const char *fmt,...)
623 #else
624 int plp_unsafe_snprintf (va_alist) va_dcl
625 #endif
626 {
627 #ifndef HAVE_STDARGS
628     char *str;
629         size_t count;
630     char *fmt;
631 #endif
632         int n = 0;
633     VA_LOCAL_DECL
634
635     VA_START (fmt);
636     VA_SHIFT (str, char *);
637     VA_SHIFT (count, size_t );
638     VA_SHIFT (fmt, char *);
639     n = plp_unsafe_vsnprintf ( str, count, fmt, ap);
640     VA_END;
641         return( n );
642 }
643  static void dopr( int visible_control, char **buffer, int *left, const char *format, va_list args )
644 {
645         int ch;
646         union value value;
647         int longflag = 0;
648         int quadflag = 0;
649         char *strvalue;
650         int ljust;
651         int len;
652         int zpad;
653         int precision;
654         int set_precision;
655         double dval;
656         int err = errno;
657         int base = 0;
658         int signed_val = 0;
659
660         while( (ch = *format++) ){
661                 switch( ch ){
662                 case '%':
663                         longflag = quadflag =
664                         ljust = len = zpad = base = signed_val = 0;
665                         precision = -1; set_precision = 0;
666                 nextch: 
667                         ch = *format++;
668                         switch( ch ){
669                         case 0:
670                                 dostr( buffer, left, "**end of format**" );
671                                 return;
672                         case '-': ljust = 1; goto nextch;
673                         case '.': set_precision = 1; precision = 0; goto nextch;
674                         case '*': len = va_arg( args, int ); goto nextch;
675                         case '0': /* set zero padding if len not set */
676                                 if(len==0 && set_precision == 0 ) zpad = '0';
677                         case '1': case '2': case '3':
678                         case '4': case '5': case '6':
679                         case '7': case '8': case '9':
680                                 if( set_precision ){
681                                         precision = precision*10 + ch - '0';
682                                 } else {
683                                         len = len*10 + ch - '0';
684                                 }
685                                 goto nextch;
686                         case 'l': ++longflag; goto nextch;
687                         case 'q':
688 #if !defined( HAVE_QUAD_T )
689                                         dostr( buffer, left, "*no quad_t support *");
690                                         return;
691 #endif
692                                         quadflag = 1;
693                                         goto nextch;
694                         case 'u': case 'U':
695                                 if( base == 0 ){ base = 10; signed_val = 0; }
696                         case 'o': case 'O':
697                                 if( base == 0 ){ base = 8; signed_val = 0; }
698                         case 'd': case 'D':
699                                 if( base == 0 ){ base = 10; signed_val = 1; }
700                         case 'x':
701                                 if( base == 0 ){ base = 16; signed_val = 0; }
702                         case 'X':
703                                 if( base == 0 ){ base = -16; signed_val = 0; }
704 #if defined( HAVE_QUAD_T )
705                                 if( quadflag ){
706                                         value.qvalue = va_arg( args, quad_t );
707                                         fmtquad( buffer, left,  &value,base,signed_val, ljust, len, zpad, precision );
708                                         break;
709                                 } else
710 #endif
711                                 if( longflag > 1 ){
712 #if defined(HAVE_LONG_LONG)
713                                         if( signed_val ){
714                                         value.value = va_arg( args, long long );
715                                         } else {
716                                         value.value = va_arg( args, unsigned long long );
717                                         }
718 #else
719                                         if( signed_val ){
720                                         value.value = va_arg( args, long );
721                                         } else {
722                                         value.value = va_arg( args, unsigned long );
723                                         }
724 #endif
725                                 } else if( longflag ){
726                                         if( signed_val ){
727                                                 value.value = va_arg( args, long );
728                                         } else {
729                                                 value.value = va_arg( args, unsigned long );
730                                         }
731                                 } else {
732                                         if( signed_val ){
733                                                 value.value = va_arg( args, int );
734                                         } else {
735                                                 value.value = va_arg( args, unsigned int );
736                                         }
737                                 }
738                                 fmtnum( buffer, left,  &value,base,signed_val, ljust, len, zpad, precision ); break;
739                         case 's':
740                                 strvalue = va_arg( args, char *);
741                                 fmtstr( visible_control, buffer, left, strvalue,ljust,len, zpad, precision );
742                                 break;
743                         case 'c':
744                                 ch = va_arg( args, int );
745                                 { char b[2];
746                                         b[0] = ch;
747                                         b[1] = 0;
748                                         fmtstr( 0, buffer, left, b,ljust,len, zpad, precision );
749                                 }
750                                 break;
751                         case 'f': case 'g': case 'e':
752                                 dval = va_arg( args, double );
753                                 fmtdouble( buffer, left, ch, dval,ljust,len, zpad, precision ); break;
754                         case 'm':
755                                 { char shortbuffer[32];
756                                 fmtstr( visible_control, buffer, left,
757                                         plp_Errormsg(err, shortbuffer),ljust,len, zpad, precision );
758                                 }
759                                 break;
760                         case '%': dopr_outch( buffer, left, ch ); continue;
761                         default:
762                                 dostr(  buffer, left, "???????" );
763                         }
764                         longflag = 0;
765                         break;
766                 default:
767                         dopr_outch( buffer, left, ch );
768                         break;
769                 }
770         }
771 }
772
773 /*
774  * Format '%[-]len[.precision]s'
775  * -   = left justify (ljust)
776  * len = minimum length
777  * precision = numbers of chars in string to use
778  */
779  static void
780 fmtstr( int visible_control, char **buffer, int *left,
781          char *value, int ljust, int len, int zpad, int precision )
782 {
783         int padlen, strlenv, i, c;      /* amount to pad */
784
785         if( value == 0 ){
786                 value = "<NULL>";
787         }
788         /* cheap strlen so you do not have library call */
789         for( strlenv = i = 0; (c=CVAL(value+i)); ++i ){
790                 if( visible_control && iscntrl( c ) && c != '\t' && c != '\n' ){
791                         ++strlenv;
792                 }
793                 ++strlenv;
794         }
795         if( precision > 0 && strlenv > precision ){
796                 strlenv = precision;
797         }
798         padlen = len - strlenv;
799         if( padlen < 0 ) padlen = 0;
800         if( ljust ) padlen = -padlen;
801         while( padlen > 0 ) {
802                 dopr_outch( buffer, left, ' ' );
803                 --padlen;
804         }
805         /* output characters */
806         for( i = 0; i < strlenv && (c = CVAL(value+i)); ++i ){
807                 if( visible_control && iscntrl( c ) && c != '\t' && c != '\n' ){
808                         dopr_outch(buffer, left, '^');
809                         c = ('@' | (c & 0x1F));
810                 }
811                 dopr_outch(buffer, left, c);
812         }
813         while( padlen < 0 ) {
814                 dopr_outch( buffer, left, ' ' );
815                 ++padlen;
816         }
817 }
818
819  static void
820 fmtnum( char **buffer, int *left,
821         union value *value, int base, int dosign, int ljust,
822         int len, int zpad, int precision )
823 {
824         int signvalue = 0;
825 #if defined(HAVE_LONG_LONG)
826         unsigned long long uvalue;
827 #else
828         unsigned long uvalue;
829 #endif
830         char convert[sizeof( union value) * 8 + 16];
831         int place = 0;
832         int padlen = 0; /* amount to pad */
833         int caps = 0;
834
835         /* fprintf(stderr,"value 0x%x, base %d, dosign %d, ljust %d, len %d, zpad %d\n",
836                 value, base, dosign, ljust, len, zpad );/ **/
837         uvalue = value->value;
838         if( dosign ){
839                 if( value->value < 0 ) {
840                         signvalue = '-';
841                         uvalue = -value->value; 
842                 }
843         }
844         if( base < 0 ){
845                 caps = 1;
846                 base = -base;
847         }
848         do{
849                 convert[place++] =
850                         (caps? "0123456789ABCDEF":"0123456789abcdef")
851                          [uvalue % (unsigned)base  ];
852                 uvalue = (uvalue / (unsigned)base );
853         }while(uvalue);
854         convert[place] = 0;
855         padlen = len - place;
856         if( padlen < 0 ) padlen = 0;
857         if( ljust ) padlen = -padlen;
858         /* fprintf( stderr, "str '%s', place %d, sign %c, padlen %d\n",
859                 convert,place,signvalue,padlen); / **/
860         if( zpad && padlen > 0 ){
861                 if( signvalue ){
862                         dopr_outch( buffer, left, signvalue );
863                         --padlen;
864                         signvalue = 0;
865                 }
866                 while( padlen > 0 ){
867                         dopr_outch( buffer, left, zpad );
868                         --padlen;
869                 }
870         }
871         while( padlen > 0 ) {
872                 dopr_outch( buffer, left, ' ' );
873                 --padlen;
874         }
875         if( signvalue ) dopr_outch( buffer, left, signvalue );
876         while( place > 0 ) dopr_outch( buffer, left, convert[--place] );
877         while( padlen < 0 ){
878                 dopr_outch( buffer, left, ' ' );
879                 ++padlen;
880         }
881 }
882
883 #if defined(HAVE_QUAD_T)
884
885  static void
886 fmtquad( char **buffer, int *left,
887         union value *value, int base, int dosign, int ljust,
888         int len, int zpad, int precision )
889 {
890         int signvalue = 0;
891         int place = 0;
892         int padlen = 0; /* amount to pad */
893         int caps = 0;
894         int i, c;
895         union {
896                 quad_t qvalue;
897                 unsigned char qconvert[sizeof(quad_t)];
898         } vvalue;
899         char convert[2*sizeof(quad_t)+1];
900
901         /* fprintf(stderr,"value 0x%x, base %d, dosign %d, ljust %d, len %d, zpad %d\n",
902                 value, base, dosign, ljust, len, zpad );/ **/
903         vvalue.qvalue = value->qvalue;
904
905         if( base < 0 ){
906                 caps = 1;
907         }
908
909         for( i = 0; i < sizeof(quad_t); ++i ){
910                 c = vvalue.qconvert[i];
911                 convert[2*i] = 
912                         (caps? "0123456789ABCDEF":"0123456789abcdef")[ (c >> 4) & 0xF];
913                 convert[2*i+1] = 
914                         (caps? "0123456789ABCDEF":"0123456789abcdef")[ c  & 0xF];
915         }
916         convert[2*i] = 0;
917
918         place = strlen(convert);
919         padlen = len - place;
920         if( padlen < 0 ) padlen = 0;
921         if( ljust ) padlen = -padlen;
922         /* fprintf( stderr, "str '%s', place %d, sign %c, padlen %d\n",
923                 convert,place,signvalue,padlen); / **/
924         if( zpad && padlen > 0 ){
925                 if( signvalue ){
926                         dopr_outch( buffer, left, signvalue );
927                         --padlen;
928                         signvalue = 0;
929                 }
930                 while( padlen > 0 ){
931                         dopr_outch( buffer, left, zpad );
932                         --padlen;
933                 }
934         }
935         while( padlen > 0 ) {
936                 dopr_outch( buffer, left, ' ' );
937                 --padlen;
938         }
939         if( signvalue ) dopr_outch( buffer, left, signvalue );
940         while( place > 0 ) dopr_outch( buffer, left, convert[--place] );
941         while( padlen < 0 ){
942                 dopr_outch( buffer, left, ' ' );
943                 ++padlen;
944         }
945 }
946
947 #endif
948
949  static void mystrcat(char *dest, char *src )
950 {
951         if( dest && src ){
952                 dest += strlen(dest);
953                 strcpy(dest,src);
954         }
955 }
956
957  static void
958 fmtdouble( char **buffer, int *left,
959         int fmt, double value, int ljust, int len, int zpad, int precision )
960 {
961         char convert[sizeof( union value) * 8 + 16];
962         char formatstr[128];
963
964         /* fprintf(stderr,"len %d, precision %d\n", len, precision ); */
965         if( len > (sizeof(convert) - 20) ){
966                 len = sizeof(convert) - 20;
967         }
968         if( precision >= 0 && precision > sizeof(convert) - 20 ){
969                 precision = sizeof(convert) - 20;
970         }
971         if( precision >= 0 && precision > len ) precision = len;
972         strcpy( formatstr, "%" );
973         if( ljust ) mystrcat(formatstr, "-" );
974         if( zpad ) mystrcat(formatstr, "0" );
975         if( len ){
976                 sprintf( formatstr+strlen(formatstr), "%d", len );
977         }
978         if( precision >= 0 ){
979                 sprintf( formatstr+strlen(formatstr), ".%d", precision );
980         }
981         sprintf( formatstr+strlen(formatstr), "%c", fmt );
982         /* this is easier than trying to do the portable dtostr */
983         /* fprintf(stderr,"format string '%s'\n", formatstr); */
984         sprintf( convert, formatstr, value );
985         dostr( buffer, left, convert );
986 }
987
988  static void dostr( char **buffer, int *left, char *str  )
989 {
990         if(str)while(*str) dopr_outch( buffer, left, *str++ );
991 }
992
993  static void dopr_outch( char **buffer, int *left, int c )
994 {
995         if( *left > 0 ){
996                 *(*buffer)++ = c;
997         }
998         *left -= 1;
999 }
1000
1001
1002 /****************************************************************************
1003  * static char *plp_errormsg( int err )
1004  *  returns a printable form of the
1005  *  errormessage corresponding to the valie of err.
1006  *  This is the poor man's version of sperror(), not available on all systems
1007  *  Patrick Powell Tue Apr 11 08:05:05 PDT 1995
1008  ****************************************************************************/
1009 /****************************************************************************/
1010
1011 #if !defined(HAVE_STRERROR)
1012 # undef  num_errors
1013 # if defined(HAVE_SYS_ERRLIST)
1014 #  if !defined(HAVE_DECL_SYS_ERRLIST)
1015      extern const char *const sys_errlist[];
1016 #  endif
1017 #  if defined(HAVE_SYS_NERR)
1018 #   if !defined(HAVE_DECL_SYS_NERR)
1019       extern int sys_nerr;
1020 #   endif
1021 #   define num_errors    (sys_nerr)
1022 #  endif
1023 # endif
1024 # if !defined(num_errors)
1025 #   define num_errors   (-1)            /* always use "errno=%d" */
1026 # endif
1027 #endif
1028
1029  static char * plp_Errormsg ( int err, char *buffer /* int maxlen = 32 */)
1030 {
1031     char *cp;
1032
1033 #if defined(HAVE_STRERROR)
1034         cp = (void *)strerror(err);
1035 #else
1036 # if defined(HAVE_SYS_ERRLIST)
1037     if (err >= 0 && err < num_errors) {
1038                 cp = (void *)sys_errlist[err];
1039     } else
1040 # endif
1041         {
1042                 (void) sprintf (buffer, "errno=%d", err);
1043                 cp = buffer;
1044     }
1045 #endif
1046     return (cp);
1047 }
1048
1049 #if defined(TEST)
1050 #include <stdio.h>
1051 int main( void )
1052 {
1053         char buffer[128];
1054         char *t;
1055         char *test1 = "01234";
1056         int n;
1057         errno = 1;
1058         buffer[0] = 0;
1059         n = plp_snprintf( buffer, 0, (t="test")); printf( "[%d] %s = '%s'\n", n, t, buffer );
1060         n = plp_snprintf( buffer, sizeof(buffer), (t="errno '%m'")); printf( "[%d] %s = '%s'\n", n, t, buffer );
1061         n = plp_snprintf( buffer, sizeof(buffer), (t = "%s"), test1 ); printf( "[%d] %s = '%s'\n", n, t, buffer );
1062         n = plp_snprintf( buffer, sizeof(buffer), (t = "%12s"), test1 ); printf( "[%d] %s = '%s'\n", n, t, buffer );
1063         n = plp_snprintf( buffer, sizeof(buffer), (t = "%-12s"), test1 ); printf( "[%d] %s = '%s'\n", n, t, buffer );
1064         n = plp_snprintf( buffer, sizeof(buffer), (t = "%12.2s"), test1 ); printf( "[%d] %s = '%s'\n", n, t, buffer );
1065         n = plp_snprintf( buffer, sizeof(buffer), (t = "%-12.2s"), test1 ); printf( "[%d] %s = '%s'\n", n, t, buffer );
1066         n = plp_snprintf( buffer, sizeof(buffer), (t = "%g"), 1.25 ); printf( "[%d] %s = '%s'\n", n, t, buffer );
1067         n = plp_snprintf( buffer, sizeof(buffer), (t = "%g"), 1.2345 ); printf( "[%d] %s = '%s'\n", n, t, buffer );
1068         n = plp_snprintf( buffer, sizeof(buffer), (t = "%12g"), 1.25 ); printf( "[%d] %s = '%s'\n", n, t, buffer );
1069         n = plp_snprintf( buffer, sizeof(buffer), (t = "%12.1g"), 1.25 ); printf( "[%d] %s = '%s'\n", n, t, buffer );
1070         n = plp_snprintf( buffer, sizeof(buffer), (t = "%12.2g"), 1.25 ); printf( "[%d] %s = '%s'\n", n, t, buffer );
1071         n = plp_snprintf( buffer, sizeof(buffer), (t = "%12.3g"), 1.25 ); printf( "[%d] %s = '%s'\n", n, t, buffer );
1072         n = plp_snprintf( buffer, sizeof(buffer), (t = "%0*d"), 6, 1 ); printf( "[%d] %s = '%s'\n", n, t, buffer );
1073 #if defined(HAVE_LONG_LONG)
1074         n = plp_snprintf( buffer, sizeof(buffer), (t = "%llx"), 1, 2, 3, 4 ); printf( "[%d] %s = '%s'\n", n, t, buffer );
1075         n = plp_snprintf( buffer, sizeof(buffer), (t = "%llx"), (long long)1, (long long)2 ); printf( "[%d] %s = '%s'\n", n, t, buffer );
1076         n = plp_snprintf( buffer, sizeof(buffer), (t = "%qx"), 1, 2, 3, 4 ); printf( "[%d] %s = '%s'\n", n, t, buffer );
1077         n = plp_snprintf( buffer, sizeof(buffer), (t = "%qx"), (quad_t)1, (quad_t)2 ); printf( "[%d] %s = '%s'\n", n, t, buffer );
1078 #endif
1079         n = plp_snprintf( buffer, sizeof(buffer), (t = "0%x, 0%x"), (char *)(0x01234567), (char *)0, 0, 0, 0); printf( "[%d] %s = '%s'\n", n, t, buffer );
1080         n = plp_snprintf( buffer, sizeof(buffer), (t = "0%x, 0%x"), (char *)(0x01234567), (char *)0x89ABCDEF, 0, 0, 0); printf( "[%d] %s = '%s'\n", n, t, buffer );
1081         n = plp_snprintf( buffer, sizeof(buffer), (t = "0%x, 0%x"), t, 0, 0, 0, 0); printf( "[%d] %s = '%s'\n", n, t, buffer );
1082         n = plp_snprintf( buffer, sizeof(buffer), (t = "%f"), 1.25 ); printf( "[%d] %s = '%s'\n", n, t, buffer );
1083         n = plp_snprintf( buffer, sizeof(buffer), (t = "%f"), 1.2345 ); printf( "[%d] %s = '%s'\n", n, t, buffer );
1084         n = plp_snprintf( buffer, sizeof(buffer), (t = "%12f"), 1.25 ); printf( "[%d] %s = '%s'\n", n, t, buffer );
1085         n = plp_snprintf( buffer, sizeof(buffer), (t = "%12.2f"), 1.25 ); printf( "[%d] %s = '%s'\n", n, t, buffer );
1086         n = plp_snprintf( buffer, sizeof(buffer), (t = "%f"), 1.0 ); printf( "[%d] %s = '%s'\n", n, t, buffer );
1087         n = plp_snprintf( buffer, sizeof(buffer), (t = "%.0f"), 1.0 ); printf( "[%d] %s = '%s'\n", n, t, buffer );
1088         n = plp_snprintf( buffer, sizeof(buffer), (t = "%0.0f"), 1.0 ); printf( "[%d] %s = '%s'\n", n, t, buffer );
1089         n = plp_snprintf( buffer, sizeof(buffer), (t = "%1.0f"), 1.0 ); printf( "[%d] %s = '%s'\n", n, t, buffer );
1090         n = plp_snprintf( buffer, sizeof(buffer), (t = "%1.5f"), 1.0 ); printf( "[%d] %s = '%s'\n", n, t, buffer );
1091         n = plp_snprintf( buffer, sizeof(buffer), (t = "%5.5f"), 1.0 ); printf( "[%d] %s = '%s'\n", n, t, buffer );
1092         return(0);
1093 }
1094 #endif
1095
1096 #endif /* !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) */
1097
1098 #ifndef HAVE_VSNPRINTF
1099 int vsnprintf(char *str, size_t count, const char *fmt, va_list args)
1100 {
1101     int n;
1102     
1103     n = plp_vsnprintf(str, count, fmt, args);
1104     
1105     return(n);
1106 }
1107 #endif /* ! HAVE_VSNPRINTF */
1108
1109 #ifndef HAVE_SNPRINTF
1110 #ifdef HAVE_STDARGS
1111 int snprintf (char *str,size_t count,const char *fmt,...)
1112 #else
1113 int snprintf (va_alist) va_dcl
1114 #endif
1115 {
1116 #ifndef HAVE_STDARGS
1117     char *str;
1118         size_t count;
1119     char *fmt;
1120 #endif
1121         int n = 0;
1122     VA_LOCAL_DECL
1123
1124     VA_START (fmt);
1125     VA_SHIFT (str, char *);
1126     VA_SHIFT (count, size_t );
1127     VA_SHIFT (fmt, char *);
1128     n = plp_vsnprintf ( str, count, fmt, ap);
1129     VA_END;
1130         return( n );
1131 }
1132 #endif /* ! HAVE_VNSPRINTF */