2 * Copyright (c) 1983, 1995, 1996 Eric P. Allman
3 * Copyright (c) 1988, 1993
4 * The Regents of the University of California. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the University nor the names of its contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 #include <sys/ioctl.h>
39 #include <sys/param.h>
47 * SNPRINTF, VSNPRINTF and friends
49 * These versions have been grabbed off the net. They have been
50 * cleaned up to compile properly and support for most of the Single Unix
51 * Specification has been added. Remaining unimplemented features are:
53 * 1. No locale support: the radix character is always '.' and the '
54 * (single quote) format flag is ignored.
56 * 2. No support for the "%n" format specification.
58 * 3. No support for wide characters ("lc" and "ls" formats).
60 * 4. No support for "long double" ("Lf" and related formats).
62 * 5. Space and '#' flags are not implemented.
65 * The result values of these functions are not the same across different
66 * platforms. This implementation is compatible with the Single Unix Spec:
68 * 1. -1 is returned only if processing is abandoned due to an invalid
69 * parameter, such as incorrect format string. (Although not required by
70 * the spec, this happens only when no characters have yet been transmitted
71 * to the destination.)
73 * 2. For snprintf and sprintf, 0 is returned if str == NULL or count == 0;
74 * no data has been stored.
76 * 3. Otherwise, the number of bytes actually transmitted to the destination
77 * is returned (excluding the trailing '\0' for snprintf and sprintf).
79 * For snprintf with nonzero count, the result cannot be more than count-1
80 * (a trailing '\0' is always stored); it is not possible to distinguish
81 * buffer overrun from exact fit. This is unlike some implementations that
82 * return the number of bytes that would have been needed for the complete
86 /**************************************************************
88 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
89 * A bombproof version of doprnt (dopr) included.
90 * Sigh. This sort of thing is always nasty do deal with. Note that
91 * the version here does not include floating point. (now it does ... tgl)
92 **************************************************************/
94 /* Prevent recursion */
102 /* Info about where the formatted output is going */
105 char *bufptr
; /* next buffer output position */
106 char *bufstart
; /* first buffer element */
107 char *bufend
; /* last buffer element, or NULL */
108 /* bufend == NULL is for sprintf, where we assume buf is big enough */
109 FILE *stream
; /* eventual output destination, or NULL */
110 int nchars
; /* # chars already sent to stream */
114 * Info about the type and value of a formatting parameter. Note that we
115 * don't currently support "long double", "wint_t", or "wchar_t *" data,
116 * nor the '%n' formatting code; else we'd need more types. Also, at this
117 * level we need not worry about signed vs unsigned values.
139 static void flushbuffer(PrintfTarget
*target
);
140 static int dopr(PrintfTarget
*target
, const char *format
, va_list args
);
144 pg_vsnprintf(char *str
, size_t count
, const char *fmt
, va_list args
)
148 if (str
== NULL
|| count
== 0)
150 target
.bufstart
= target
.bufptr
= str
;
151 target
.bufend
= str
+ count
- 1;
152 target
.stream
= NULL
;
153 /* target.nchars is unused in this case */
154 if (dopr(&target
, fmt
, args
))
156 *(target
.bufptr
) = '\0';
157 errno
= EINVAL
; /* bad format */
160 *(target
.bufptr
) = '\0';
161 return target
.bufptr
- target
.bufstart
;
165 pg_snprintf(char *str
, size_t count
, const char *fmt
,...)
171 len
= pg_vsnprintf(str
, count
, fmt
, args
);
177 pg_vsprintf(char *str
, const char *fmt
, va_list args
)
183 target
.bufstart
= target
.bufptr
= str
;
184 target
.bufend
= NULL
;
185 target
.stream
= NULL
;
186 /* target.nchars is unused in this case */
187 if (dopr(&target
, fmt
, args
))
189 *(target
.bufptr
) = '\0';
190 errno
= EINVAL
; /* bad format */
193 *(target
.bufptr
) = '\0';
194 return target
.bufptr
- target
.bufstart
;
198 pg_sprintf(char *str
, const char *fmt
,...)
204 len
= pg_vsprintf(str
, fmt
, args
);
210 pg_vfprintf(FILE *stream
, const char *fmt
, va_list args
)
213 char buffer
[1024]; /* size is arbitrary */
220 target
.bufstart
= target
.bufptr
= buffer
;
221 target
.bufend
= buffer
+ sizeof(buffer
) - 1;
222 target
.stream
= stream
;
224 if (dopr(&target
, fmt
, args
))
226 errno
= EINVAL
; /* bad format */
229 /* dump any remaining buffer contents */
230 flushbuffer(&target
);
231 return target
.nchars
;
235 pg_fprintf(FILE *stream
, const char *fmt
,...)
241 len
= pg_vfprintf(stream
, fmt
, args
);
247 pg_printf(const char *fmt
,...)
253 len
= pg_vfprintf(stdout
, fmt
, args
);
258 /* call this only when stream is defined */
260 flushbuffer(PrintfTarget
*target
)
262 size_t nc
= target
->bufptr
- target
->bufstart
;
265 target
->nchars
+= fwrite(target
->bufstart
, 1, nc
, target
->stream
);
266 target
->bufptr
= target
->bufstart
;
270 static void fmtstr(char *value
, int leftjust
, int minlen
, int maxwidth
,
271 int pointflag
, PrintfTarget
*target
);
272 static void fmtptr(void *value
, PrintfTarget
*target
);
273 static void fmtint(int64 value
, char type
, int forcesign
,
274 int leftjust
, int minlen
, int zpad
, int precision
, int pointflag
,
275 PrintfTarget
*target
);
276 static void fmtchar(int value
, int leftjust
, int minlen
, PrintfTarget
*target
);
277 static void fmtfloat(double value
, char type
, int forcesign
,
278 int leftjust
, int minlen
, int zpad
, int precision
, int pointflag
,
279 PrintfTarget
*target
);
280 static void dostr(const char *str
, int slen
, PrintfTarget
*target
);
281 static void dopr_outch(int c
, PrintfTarget
*target
);
282 static int adjust_sign(int is_negative
, int forcesign
, int *signvalue
);
283 static void adjust_padlen(int minlen
, int vallen
, int leftjust
, int *padlen
);
284 static void leading_pad(int zpad
, int *signvalue
, int *padlen
,
285 PrintfTarget
*target
);
286 static void trailing_pad(int *padlen
, PrintfTarget
*target
);
290 * dopr(): poor man's version of doprintf
293 dopr(PrintfTarget
*target
, const char *format
, va_list args
)
295 const char *format_start
= format
;
298 bool have_non_dollar
;
317 PrintfArgType argtypes
[NL_ARGMAX
+ 1];
318 PrintfArgValue argvalues
[NL_ARGMAX
+ 1];
321 * Parse the format string to determine whether there are %n$ format
322 * specs, and identify the types and order of the format parameters.
324 have_dollar
= have_non_dollar
= false;
326 MemSet(argtypes
, 0, sizeof(argtypes
));
328 while ((ch
= *format
++) != '\0')
332 longflag
= longlongflag
= pointflag
= 0;
338 break; /* illegal, but we don't complain */
354 accum
= accum
* 10 + (ch
- '0');
362 have_non_dollar
= true; /* multiple stars */
368 if (accum
<= 0 || accum
> NL_ARGMAX
)
372 if (argtypes
[accum
] &&
373 argtypes
[accum
] != ATYPE_INT
)
375 argtypes
[accum
] = ATYPE_INT
;
376 last_dollar
= Max(last_dollar
, accum
);
404 atype
= ATYPE_LONGLONG
;
409 if (argtypes
[fmtpos
] &&
410 argtypes
[fmtpos
] != atype
)
412 argtypes
[fmtpos
] = atype
;
413 last_dollar
= Max(last_dollar
, fmtpos
);
416 have_non_dollar
= true;
421 if (argtypes
[fmtpos
] &&
422 argtypes
[fmtpos
] != ATYPE_INT
)
424 argtypes
[fmtpos
] = ATYPE_INT
;
425 last_dollar
= Max(last_dollar
, fmtpos
);
428 have_non_dollar
= true;
434 if (argtypes
[fmtpos
] &&
435 argtypes
[fmtpos
] != ATYPE_CHARPTR
)
437 argtypes
[fmtpos
] = ATYPE_CHARPTR
;
438 last_dollar
= Max(last_dollar
, fmtpos
);
441 have_non_dollar
= true;
450 if (argtypes
[fmtpos
] &&
451 argtypes
[fmtpos
] != ATYPE_DOUBLE
)
453 argtypes
[fmtpos
] = ATYPE_DOUBLE
;
454 last_dollar
= Max(last_dollar
, fmtpos
);
457 have_non_dollar
= true;
464 * If we finish the spec with afterstar still set, there's a
465 * non-dollar star in there.
468 have_non_dollar
= true;
471 /* Per spec, you use either all dollar or all not. */
472 if (have_dollar
&& have_non_dollar
)
476 * In dollar mode, collect the arguments in physical order.
478 for (i
= 1; i
<= last_dollar
; i
++)
483 return -1; /* invalid format */
485 argvalues
[i
].i
= va_arg(args
, int);
488 argvalues
[i
].l
= va_arg(args
, long);
491 argvalues
[i
].ll
= va_arg(args
, int64
);
494 argvalues
[i
].d
= va_arg(args
, double);
497 argvalues
[i
].cptr
= va_arg(args
, char *);
503 * At last we can parse the format for real.
505 format
= format_start
;
506 while ((ch
= *format
++) != '\0')
510 dopr_outch(ch
, target
);
513 fieldwidth
= precision
= zpad
= leftjust
= forcesign
= 0;
514 longflag
= longlongflag
= pointflag
= 0;
516 have_star
= afterstar
= false;
520 break; /* illegal, but we don't complain */
530 /* set zero padding if no nonzero digits yet */
531 if (accum
== 0 && !pointflag
)
543 accum
= accum
* 10 + (ch
- '0');
556 /* process value after reading n$ */
561 /* fetch and process value now */
562 int starval
= va_arg(args
, int);
575 fieldwidth
= starval
;
579 fieldwidth
= -fieldwidth
;
589 /* fetch and process star value */
590 int starval
= argvalues
[accum
].i
;
603 fieldwidth
= starval
;
607 fieldwidth
= -fieldwidth
;
638 numvalue
= argvalues
[fmtpos
].ll
;
640 numvalue
= argvalues
[fmtpos
].l
;
642 numvalue
= argvalues
[fmtpos
].i
;
647 numvalue
= va_arg(args
, int64
);
649 numvalue
= va_arg(args
, long);
651 numvalue
= va_arg(args
, int);
653 fmtint(numvalue
, ch
, forcesign
, leftjust
, fieldwidth
, zpad
,
654 precision
, pointflag
, target
);
670 numvalue
= (uint64
) argvalues
[fmtpos
].ll
;
672 numvalue
= (unsigned long) argvalues
[fmtpos
].l
;
674 numvalue
= (unsigned int) argvalues
[fmtpos
].i
;
679 numvalue
= (uint64
) va_arg(args
, int64
);
681 numvalue
= (unsigned long) va_arg(args
, long);
683 numvalue
= (unsigned int) va_arg(args
, int);
685 fmtint(numvalue
, ch
, forcesign
, leftjust
, fieldwidth
, zpad
,
686 precision
, pointflag
, target
);
697 cvalue
= (unsigned char) argvalues
[fmtpos
].i
;
699 cvalue
= (unsigned char) va_arg(args
, int);
700 fmtchar(cvalue
, leftjust
, fieldwidth
, target
);
711 strvalue
= argvalues
[fmtpos
].cptr
;
713 strvalue
= va_arg(args
, char *);
714 fmtstr(strvalue
, leftjust
, fieldwidth
, precision
, pointflag
,
718 /* fieldwidth/leftjust are ignored ... */
720 strvalue
= argvalues
[fmtpos
].cptr
;
722 strvalue
= va_arg(args
, char *);
723 fmtptr((void *) strvalue
, target
);
738 fvalue
= argvalues
[fmtpos
].d
;
740 fvalue
= va_arg(args
, double);
741 fmtfloat(fvalue
, ch
, forcesign
, leftjust
,
743 precision
, pointflag
,
747 dopr_outch('%', target
);
756 pg_strnlen(const char *str
, size_t maxlen
)
760 while (maxlen
-- > 0 && *p
)
766 fmtstr(char *value
, int leftjust
, int minlen
, int maxwidth
,
767 int pointflag
, PrintfTarget
*target
)
770 vallen
; /* amount to pad */
773 * If a maxwidth (precision) is specified, we must not fetch more bytes
777 vallen
= pg_strnlen(value
, maxwidth
);
779 vallen
= strlen(value
);
781 adjust_padlen(minlen
, vallen
, leftjust
, &padlen
);
785 dopr_outch(' ', target
);
789 dostr(value
, vallen
, target
);
791 trailing_pad(&padlen
, target
);
795 fmtptr(void *value
, PrintfTarget
*target
)
800 /* we rely on regular C library's sprintf to do the basic conversion */
801 vallen
= sprintf(convert
, "%p", value
);
803 dostr(convert
, vallen
, target
);
807 fmtint(int64 value
, char type
, int forcesign
, int leftjust
,
808 int minlen
, int zpad
, int precision
, int pointflag
,
809 PrintfTarget
*target
)
813 const char *cvt
= "0123456789abcdef";
817 int padlen
= 0; /* amount to pad */
818 int zeropad
; /* extra leading zeroes */
840 cvt
= "0123456789ABCDEF";
845 return; /* keep compiler quiet */
849 if (dosign
&& adjust_sign((value
< 0), forcesign
, &signvalue
))
853 * SUS: the result of converting 0 with an explicit precision of 0 is no
856 if (value
== 0 && pointflag
&& precision
== 0)
860 /* make integer string */
861 uint64 uvalue
= (uint64
) value
;
865 convert
[vallen
++] = cvt
[uvalue
% base
];
866 uvalue
= uvalue
/ base
;
870 zeropad
= Max(0, precision
- vallen
);
872 adjust_padlen(minlen
, vallen
+ zeropad
, leftjust
, &padlen
);
874 leading_pad(zpad
, &signvalue
, &padlen
, target
);
876 while (zeropad
-- > 0)
877 dopr_outch('0', target
);
880 dopr_outch(convert
[--vallen
], target
);
882 trailing_pad(&padlen
, target
);
886 fmtchar(int value
, int leftjust
, int minlen
, PrintfTarget
*target
)
888 int padlen
= 0; /* amount to pad */
890 adjust_padlen(minlen
, 1, leftjust
, &padlen
);
894 dopr_outch(' ', target
);
898 dopr_outch(value
, target
);
900 trailing_pad(&padlen
, target
);
904 fmtfloat(double value
, char type
, int forcesign
, int leftjust
,
905 int minlen
, int zpad
, int precision
, int pointflag
,
906 PrintfTarget
*target
)
912 int padlen
= 0; /* amount to pad */
914 /* we rely on regular C library's sprintf to do the basic conversion */
916 sprintf(fmt
, "%%.%d%c", precision
, type
);
918 sprintf(fmt
, "%%%c", type
);
920 if (adjust_sign((value
< 0), forcesign
, &signvalue
))
923 vallen
= sprintf(convert
, fmt
, value
);
925 adjust_padlen(minlen
, vallen
, leftjust
, &padlen
);
927 leading_pad(zpad
, &signvalue
, &padlen
, target
);
929 dostr(convert
, vallen
, target
);
931 trailing_pad(&padlen
, target
);
935 dostr(const char *str
, int slen
, PrintfTarget
*target
)
941 if (target
->bufend
!= NULL
)
942 avail
= target
->bufend
- target
->bufptr
;
947 /* buffer full, can we dump to stream? */
948 if (target
->stream
== NULL
)
949 return; /* no, lose the data */
953 avail
= Min(avail
, slen
);
954 memmove(target
->bufptr
, str
, avail
);
955 target
->bufptr
+= avail
;
962 dopr_outch(int c
, PrintfTarget
*target
)
964 if (target
->bufend
!= NULL
&& target
->bufptr
>= target
->bufend
)
966 /* buffer full, can we dump to stream? */
967 if (target
->stream
== NULL
)
968 return; /* no, lose the data */
971 *(target
->bufptr
++) = c
;
976 adjust_sign(int is_negative
, int forcesign
, int *signvalue
)
990 adjust_padlen(int minlen
, int vallen
, int leftjust
, int *padlen
)
992 *padlen
= minlen
- vallen
;
996 *padlen
= -(*padlen
);
1001 leading_pad(int zpad
, int *signvalue
, int *padlen
, PrintfTarget
*target
)
1003 if (*padlen
> 0 && zpad
)
1007 dopr_outch(*signvalue
, target
);
1013 dopr_outch(zpad
, target
);
1017 while (*padlen
> (*signvalue
!= 0))
1019 dopr_outch(' ', target
);
1024 dopr_outch(*signvalue
, target
);
1027 else if (*padlen
< 0)
1034 trailing_pad(int *padlen
, PrintfTarget
*target
)
1038 dopr_outch(' ', target
);