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