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
207NAME
208     plp_snprintf, plp_vsnprintf - formatted output conversion
209
210SYNOPSIS
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
230DESCRIPTION
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
403EXAMPLES
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
434SEE ALSO
435     printf(1),  scanf(3)
436
437STANDARDS
438     Turkey C Standardization and wimpy POSIX folks did not define
439     snprintf or vsnprintf().
440
441BUGS
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.2 2008-11-14 10:29:08 didg 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
514XX ** NO VARARGS ** XX
515# endif
516#endif
517
518union 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
533extern 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
553int 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
574int 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
597int plp_snprintf (char *str,size_t count,const char *fmt,...)
598#else
599int 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
622int plp_unsafe_snprintf (char *str,size_t count,const char *fmt,...)
623#else
624int 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
780fmtstr( 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
820fmtnum( 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
886fmtquad( 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
958fmtdouble( 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>
1051int 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 '%s'")); printf( "[%d] %s = '%s'\n", n, t, buffer, strerror(errno) );
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
1099int 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
1111int snprintf (char *str,size_t count,const char *fmt,...)
1112#else
1113int 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 */
1133