blob: 1c72ea61daa5053a7bf74356060d56db48b6f814 [file] [log] [blame]
Damien Miller168e6ac2000-07-11 12:23:01 +10001/**************************************************************
2 * Original:
3 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
4 * A bombproof version of doprnt (dopr) included.
5 * Sigh. This sort of thing is always nasty do deal with. Note that
6 * the version here does not include floating point...
Damien Miller42b81ff1999-11-26 12:21:24 +11007 *
Damien Miller168e6ac2000-07-11 12:23:01 +10008 * snprintf() is used instead of sprintf() as it does limit checks
9 * for string length. This covers a nasty loophole.
Damien Miller42b81ff1999-11-26 12:21:24 +110010 *
Damien Miller168e6ac2000-07-11 12:23:01 +100011 * The other functions are there to prevent NULL pointers from
12 * causing nast effects.
Damien Miller42b81ff1999-11-26 12:21:24 +110013 *
Damien Miller168e6ac2000-07-11 12:23:01 +100014 * More Recently:
15 * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
16 * This was ugly. It is still ugly. I opted out of floating point
17 * numbers, but the formatter understands just about everything
18 * from the normal C string format, at least as far as I can tell from
19 * the Solaris 2.5 printf(3S) man page.
20 *
21 * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
22 * Ok, added some minimal floating point support, which means this
23 * probably requires libm on most operating systems. Don't yet
24 * support the exponent (e,E) and sigfig (g,G). Also, fmtint()
25 * was pretty badly broken, it just wasn't being exercised in ways
26 * which showed it, so that's been fixed. Also, formated the code
27 * to mutt conventions, and removed dead code left over from the
28 * original. Also, there is now a builtin-test, just compile with:
29 * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
30 * and run snprintf for results.
31 *
32 * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
33 * The PGP code was using unsigned hexadecimal formats.
34 * Unfortunately, unsigned formats simply didn't work.
35 *
36 * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
37 * The original code assumed that both snprintf() and vsnprintf() were
38 * missing. Some systems only have snprintf() but not vsnprintf(), so
39 * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
40 *
Ben Lindstrom6c92dab2001-02-13 02:18:50 +000041 * Ben Lindstrom <mouring@eviladmin.org> 09/27/00 for OpenSSH
Damien Miller3dfb0dd2000-09-30 09:49:08 +110042 * Welcome to the world of %lld and %qd support. With other
43 * long long support. This is needed for sftp-server to work
44 * right.
Ben Lindstrom6c92dab2001-02-13 02:18:50 +000045 *
46 * Ben Lindstrom <mouring@eviladmin.org> 02/12/01 for OpenSSH
47 * Removed all hint of VARARGS stuff and banished it to the void,
48 * and did a bit of KNF style work to make things a bit more
49 * acceptable. Consider stealing from mutt or enlightenment.
Damien Miller168e6ac2000-07-11 12:23:01 +100050 **************************************************************/
Damien Miller42b81ff1999-11-26 12:21:24 +110051
Damien Millere9cf3572001-02-09 12:55:35 +110052#include "includes.h"
53
Ben Lindstrom63941f92001-02-25 23:20:40 +000054RCSID("$Id: bsd-snprintf.c,v 1.5 2001/02/25 23:20:41 mouring Exp $");
55
56#if defined(BROKEN_SNPRINTF) /* For those with broken snprintf() */
57# undef HAVE_SNPRINTF
58# undef HAVE_VSNPRINTF
59#endif
Damien Miller42b81ff1999-11-26 12:21:24 +110060
Damien Miller168e6ac2000-07-11 12:23:01 +100061#if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF)
Damien Miller42b81ff1999-11-26 12:21:24 +110062
Ben Lindstrom6c92dab2001-02-13 02:18:50 +000063static void
64dopr(char *buffer, size_t maxlen, const char *format, va_list args);
Damien Miller168e6ac2000-07-11 12:23:01 +100065
Ben Lindstrom6c92dab2001-02-13 02:18:50 +000066static void
67fmtstr(char *buffer, size_t *currlen, size_t maxlen, char *value, int flags,
68 int min, int max);
Damien Miller168e6ac2000-07-11 12:23:01 +100069
Ben Lindstrom6c92dab2001-02-13 02:18:50 +000070static void
71fmtint(char *buffer, size_t *currlen, size_t maxlen, long value, int base,
72 int min, int max, int flags);
Damien Miller168e6ac2000-07-11 12:23:01 +100073
Ben Lindstrom6c92dab2001-02-13 02:18:50 +000074static void
75fmtfp(char *buffer, size_t *currlen, size_t maxlen, long double fvalue,
76 int min, int max, int flags);
Damien Miller42b81ff1999-11-26 12:21:24 +110077
Ben Lindstrom116b6bd2001-02-13 14:05:59 +000078static void
79dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c);
80
Damien Miller168e6ac2000-07-11 12:23:01 +100081/*
82 * dopr(): poor man's version of doprintf
83 */
84
85/* format read states */
86#define DP_S_DEFAULT 0
87#define DP_S_FLAGS 1
88#define DP_S_MIN 2
89#define DP_S_DOT 3
90#define DP_S_MAX 4
91#define DP_S_MOD 5
92#define DP_S_CONV 6
93#define DP_S_DONE 7
94
95/* format flags - Bits */
96#define DP_F_MINUS (1 << 0)
97#define DP_F_PLUS (1 << 1)
98#define DP_F_SPACE (1 << 2)
99#define DP_F_NUM (1 << 3)
100#define DP_F_ZERO (1 << 4)
101#define DP_F_UP (1 << 5)
102#define DP_F_UNSIGNED (1 << 6)
103
104/* Conversion Flags */
Damien Miller3dfb0dd2000-09-30 09:49:08 +1100105#define DP_C_SHORT 1
106#define DP_C_LONG 2
107#define DP_C_LDOUBLE 3
108#define DP_C_LONG_LONG 4
Damien Miller168e6ac2000-07-11 12:23:01 +1000109
110#define char_to_int(p) (p - '0')
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000111#define abs_val(p) (p < 0 ? -p : p)
Damien Miller168e6ac2000-07-11 12:23:01 +1000112
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000113
114static void
115dopr(char *buffer, size_t maxlen, const char *format, va_list args)
Damien Miller13bc0be1999-12-28 10:19:16 +1100116{
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000117 char *strvalue;
118 char ch;
119 long value;
120 long double fvalue;
121 int min = 0;
122 int max = -1;
123 int state = DP_S_DEFAULT;
124 int flags = 0;
125 int cflags = 0;
126 size_t currlen = 0;
Damien Miller168e6ac2000-07-11 12:23:01 +1000127
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000128 ch = *format++;
Damien Miller13bc0be1999-12-28 10:19:16 +1100129
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000130 while (state != DP_S_DONE) {
131 if ((ch == '\0') || (currlen >= maxlen))
132 state = DP_S_DONE;
Damien Miller42b81ff1999-11-26 12:21:24 +1100133
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000134 switch(state) {
135 case DP_S_DEFAULT:
136 if (ch == '%')
137 state = DP_S_FLAGS;
138 else
139 dopr_outch(buffer, &currlen, maxlen, ch);
140 ch = *format++;
141 break;
142 case DP_S_FLAGS:
143 switch (ch) {
144 case '-':
145 flags |= DP_F_MINUS;
146 ch = *format++;
147 break;
148 case '+':
149 flags |= DP_F_PLUS;
150 ch = *format++;
151 break;
152 case ' ':
153 flags |= DP_F_SPACE;
154 ch = *format++;
155 break;
156 case '#':
157 flags |= DP_F_NUM;
158 ch = *format++;
159 break;
160 case '0':
161 flags |= DP_F_ZERO;
162 ch = *format++;
163 break;
164 default:
165 state = DP_S_MIN;
166 break;
167 }
168 break;
169 case DP_S_MIN:
170 if (isdigit((unsigned char)ch)) {
171 min = 10*min + char_to_int (ch);
172 ch = *format++;
173 } else if (ch == '*') {
174 min = va_arg (args, int);
175 ch = *format++;
176 state = DP_S_DOT;
177 } else
178 state = DP_S_DOT;
179 break;
180 case DP_S_DOT:
181 if (ch == '.') {
182 state = DP_S_MAX;
183 ch = *format++;
184 } else
185 state = DP_S_MOD;
186 break;
187 case DP_S_MAX:
188 if (isdigit((unsigned char)ch)) {
189 if (max < 0)
190 max = 0;
191 max = 10*max + char_to_int(ch);
192 ch = *format++;
193 } else if (ch == '*') {
194 max = va_arg (args, int);
195 ch = *format++;
196 state = DP_S_MOD;
197 } else
198 state = DP_S_MOD;
199 break;
200 case DP_S_MOD:
201 switch (ch) {
202 case 'h':
203 cflags = DP_C_SHORT;
204 ch = *format++;
205 break;
206 case 'l':
207 cflags = DP_C_LONG;
208 ch = *format++;
209 if (ch == 'l') {
210 cflags = DP_C_LONG_LONG;
211 ch = *format++;
212 }
213 break;
214 case 'q':
215 cflags = DP_C_LONG_LONG;
216 ch = *format++;
217 break;
218 case 'L':
219 cflags = DP_C_LDOUBLE;
220 ch = *format++;
221 break;
222 default:
223 break;
224 }
225 state = DP_S_CONV;
226 break;
227 case DP_S_CONV:
228 switch (ch) {
229 case 'd':
230 case 'i':
231 if (cflags == DP_C_SHORT)
232 value = va_arg(args, int);
233 else if (cflags == DP_C_LONG)
234 value = va_arg(args, long int);
235 else if (cflags == DP_C_LONG_LONG)
236 value = va_arg (args, long long);
237 else
238 value = va_arg (args, int);
239 fmtint(buffer, &currlen, maxlen, value, 10, min, max, flags);
240 break;
241 case 'o':
242 flags |= DP_F_UNSIGNED;
243 if (cflags == DP_C_SHORT)
244 value = va_arg(args, unsigned int);
245 else if (cflags == DP_C_LONG)
246 value = va_arg(args, unsigned long int);
247 else if (cflags == DP_C_LONG_LONG)
248 value = va_arg(args, unsigned long long);
249 else
250 value = va_arg(args, unsigned int);
251 fmtint(buffer, &currlen, maxlen, value, 8, min, max, flags);
252 break;
253 case 'u':
254 flags |= DP_F_UNSIGNED;
255 if (cflags == DP_C_SHORT)
256 value = va_arg(args, unsigned int);
257 else if (cflags == DP_C_LONG)
258 value = va_arg(args, unsigned long int);
259 else if (cflags == DP_C_LONG_LONG)
260 value = va_arg(args, unsigned long long);
261 else
262 value = va_arg(args, unsigned int);
263 fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
264 break;
265 case 'X':
266 flags |= DP_F_UP;
267 case 'x':
268 flags |= DP_F_UNSIGNED;
269 if (cflags == DP_C_SHORT)
270 value = va_arg(args, unsigned int);
271 else if (cflags == DP_C_LONG)
272 value = va_arg(args, unsigned long int);
273 else if (cflags == DP_C_LONG_LONG)
274 value = va_arg(args, unsigned long long);
275 else
276 value = va_arg(args, unsigned int);
277 fmtint(buffer, &currlen, maxlen, value, 16, min, max, flags);
278 break;
279 case 'f':
280 if (cflags == DP_C_LDOUBLE)
281 fvalue = va_arg(args, long double);
282 else
283 fvalue = va_arg(args, double);
284 /* um, floating point? */
285 fmtfp(buffer, &currlen, maxlen, fvalue, min, max, flags);
286 break;
287 case 'E':
288 flags |= DP_F_UP;
289 case 'e':
290 if (cflags == DP_C_LDOUBLE)
291 fvalue = va_arg(args, long double);
292 else
293 fvalue = va_arg(args, double);
294 break;
295 case 'G':
296 flags |= DP_F_UP;
297 case 'g':
298 if (cflags == DP_C_LDOUBLE)
299 fvalue = va_arg(args, long double);
300 else
301 fvalue = va_arg(args, double);
302 break;
303 case 'c':
304 dopr_outch(buffer, &currlen, maxlen, va_arg(args, int));
305 break;
306 case 's':
307 strvalue = va_arg(args, char *);
308 if (max < 0)
309 max = maxlen; /* ie, no max */
310 fmtstr(buffer, &currlen, maxlen, strvalue, flags, min, max);
311 break;
312 case 'p':
313 strvalue = va_arg(args, void *);
314 fmtint(buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
315 break;
316 case 'n':
317 if (cflags == DP_C_SHORT) {
318 short int *num;
319 num = va_arg(args, short int *);
320 *num = currlen;
321 } else if (cflags == DP_C_LONG) {
322 long int *num;
323 num = va_arg(args, long int *);
324 *num = currlen;
325 } else if (cflags == DP_C_LONG_LONG) {
326 long long *num;
327 num = va_arg(args, long long *);
328 *num = currlen;
329 } else {
330 int *num;
331 num = va_arg(args, int *);
332 *num = currlen;
333 }
334 break;
335 case '%':
336 dopr_outch(buffer, &currlen, maxlen, ch);
337 break;
338 case 'w': /* not supported yet, treat as next char */
339 ch = *format++;
340 break;
341 default: /* Unknown, skip */
342 break;
343 }
344 ch = *format++;
345 state = DP_S_DEFAULT;
346 flags = cflags = min = 0;
347 max = -1;
348 break;
349 case DP_S_DONE:
350 break;
351 default: /* hmm? */
352 break; /* some picky compilers need this */
353 }
354 }
355 if (currlen < maxlen - 1)
356 buffer[currlen] = '\0';
Damien Miller168e6ac2000-07-11 12:23:01 +1000357 else
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000358 buffer[maxlen - 1] = '\0';
Damien Miller42b81ff1999-11-26 12:21:24 +1100359}
360
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000361static void
362fmtstr(char *buffer, size_t *currlen, size_t maxlen,
363 char *value, int flags, int min, int max)
Damien Miller42b81ff1999-11-26 12:21:24 +1100364{
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000365 int padlen, strln; /* amount to pad */
366 int cnt = 0;
Damien Miller168e6ac2000-07-11 12:23:01 +1000367
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000368 if (value == 0)
369 value = "<NULL>";
Damien Miller168e6ac2000-07-11 12:23:01 +1000370
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000371 for (strln = 0; value[strln]; ++strln); /* strlen */
372 padlen = min - strln;
373 if (padlen < 0)
374 padlen = 0;
375 if (flags & DP_F_MINUS)
376 padlen = -padlen; /* Left Justify */
Damien Miller168e6ac2000-07-11 12:23:01 +1000377
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000378 while ((padlen > 0) && (cnt < max)) {
379 dopr_outch(buffer, currlen, maxlen, ' ');
380 --padlen;
381 ++cnt;
382 }
383 while (*value && (cnt < max)) {
384 dopr_outch(buffer, currlen, maxlen, *value++);
385 ++cnt;
386 }
387 while ((padlen < 0) && (cnt < max)) {
388 dopr_outch(buffer, currlen, maxlen, ' ');
389 ++padlen;
390 ++cnt;
391 }
Damien Miller42b81ff1999-11-26 12:21:24 +1100392}
393
Damien Miller168e6ac2000-07-11 12:23:01 +1000394/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
395
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000396static void
397fmtint(char *buffer, size_t *currlen, size_t maxlen,
398 long value, int base, int min, int max, int flags)
Damien Miller42b81ff1999-11-26 12:21:24 +1100399{
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000400 unsigned long uvalue;
401 char convert[20];
402 int signvalue = 0;
403 int place = 0;
404 int spadlen = 0; /* amount to space pad */
405 int zpadlen = 0; /* amount to zero pad */
406 int caps = 0;
Damien Miller168e6ac2000-07-11 12:23:01 +1000407
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000408 if (max < 0)
409 max = 0;
Damien Miller168e6ac2000-07-11 12:23:01 +1000410
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000411 uvalue = value;
Damien Miller168e6ac2000-07-11 12:23:01 +1000412
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000413 if (!(flags & DP_F_UNSIGNED)) {
414 if (value < 0) {
415 signvalue = '-';
416 uvalue = -value;
417 } else if (flags & DP_F_PLUS) /* Do a sign (+/i) */
418 signvalue = '+';
419 else if (flags & DP_F_SPACE)
420 signvalue = ' ';
421 }
Damien Miller168e6ac2000-07-11 12:23:01 +1000422
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000423 if (flags & DP_F_UP)
424 caps = 1; /* Should characters be upper case? */
Damien Miller168e6ac2000-07-11 12:23:01 +1000425
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000426 do {
427 convert[place++] =
428 (caps? "0123456789ABCDEF":"0123456789abcdef")
429 [uvalue % (unsigned)base];
430 uvalue = (uvalue / (unsigned)base );
431 } while (uvalue && (place < 20));
432 if (place == 20)
433 place--;
434 convert[place] = 0;
Damien Miller168e6ac2000-07-11 12:23:01 +1000435
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000436 zpadlen = max - place;
437 spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
438 if (zpadlen < 0)
439 zpadlen = 0;
440 if (spadlen < 0)
441 spadlen = 0;
442 if (flags & DP_F_ZERO) {
443 zpadlen = MAX(zpadlen, spadlen);
444 spadlen = 0;
445 }
446 if (flags & DP_F_MINUS)
447 spadlen = -spadlen; /* Left Justifty */
Damien Miller168e6ac2000-07-11 12:23:01 +1000448
Damien Miller168e6ac2000-07-11 12:23:01 +1000449
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000450 /* Spaces */
451 while (spadlen > 0) {
452 dopr_outch(buffer, currlen, maxlen, ' ');
453 --spadlen;
454 }
Damien Miller168e6ac2000-07-11 12:23:01 +1000455
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000456 /* Sign */
457 if (signvalue)
458 dopr_outch(buffer, currlen, maxlen, signvalue);
Damien Miller168e6ac2000-07-11 12:23:01 +1000459
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000460 /* Zeros */
461 if (zpadlen > 0) {
462 while (zpadlen > 0) {
463 dopr_outch(buffer, currlen, maxlen, '0');
464 --zpadlen;
465 }
466 }
Damien Miller168e6ac2000-07-11 12:23:01 +1000467
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000468 /* Digits */
469 while (place > 0)
470 dopr_outch(buffer, currlen, maxlen, convert[--place]);
Damien Miller168e6ac2000-07-11 12:23:01 +1000471
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000472 /* Left Justified spaces */
473 while (spadlen < 0) {
474 dopr_outch (buffer, currlen, maxlen, ' ');
475 ++spadlen;
476 }
Damien Miller42b81ff1999-11-26 12:21:24 +1100477}
478
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000479static long double
480pow10(int exp)
Damien Miller42b81ff1999-11-26 12:21:24 +1100481{
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000482 long double result = 1;
Damien Miller42b81ff1999-11-26 12:21:24 +1100483
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000484 while (exp) {
485 result *= 10;
486 exp--;
487 }
Damien Miller168e6ac2000-07-11 12:23:01 +1000488
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000489 return result;
Damien Miller168e6ac2000-07-11 12:23:01 +1000490}
491
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000492static long
493round(long double value)
Damien Miller168e6ac2000-07-11 12:23:01 +1000494{
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000495 long intpart = value;
Damien Miller168e6ac2000-07-11 12:23:01 +1000496
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000497 value -= intpart;
498 if (value >= 0.5)
499 intpart++;
Damien Miller168e6ac2000-07-11 12:23:01 +1000500
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000501 return intpart;
Damien Miller168e6ac2000-07-11 12:23:01 +1000502}
503
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000504static void
505fmtfp(char *buffer, size_t *currlen, size_t maxlen, long double fvalue,
506 int min, int max, int flags)
Damien Miller168e6ac2000-07-11 12:23:01 +1000507{
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000508 char iconvert[20];
509 char fconvert[20];
510 int signvalue = 0;
511 int iplace = 0;
512 int fplace = 0;
513 int padlen = 0; /* amount to pad */
514 int zpadlen = 0;
515 int caps = 0;
516 long intpart;
517 long fracpart;
518 long double ufvalue;
Damien Miller168e6ac2000-07-11 12:23:01 +1000519
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000520 /*
521 * AIX manpage says the default is 0, but Solaris says the default
522 * is 6, and sprintf on AIX defaults to 6
523 */
524 if (max < 0)
525 max = 6;
Damien Miller168e6ac2000-07-11 12:23:01 +1000526
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000527 ufvalue = abs_val(fvalue);
Damien Miller168e6ac2000-07-11 12:23:01 +1000528
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000529 if (fvalue < 0)
530 signvalue = '-';
531 else if (flags & DP_F_PLUS) /* Do a sign (+/i) */
532 signvalue = '+';
533 else if (flags & DP_F_SPACE)
534 signvalue = ' ';
Damien Miller168e6ac2000-07-11 12:23:01 +1000535
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000536 intpart = ufvalue;
Damien Miller168e6ac2000-07-11 12:23:01 +1000537
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000538 /*
539 * Sorry, we only support 9 digits past the decimal because of our
540 * conversion method
541 */
542 if (max > 9)
543 max = 9;
Damien Miller168e6ac2000-07-11 12:23:01 +1000544
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000545 /* We "cheat" by converting the fractional part to integer by
546 * multiplying by a factor of 10
547 */
548 fracpart = round((pow10 (max)) * (ufvalue - intpart));
Damien Miller168e6ac2000-07-11 12:23:01 +1000549
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000550 if (fracpart >= pow10 (max)) {
551 intpart++;
552 fracpart -= pow10 (max);
553 }
Damien Miller168e6ac2000-07-11 12:23:01 +1000554
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000555 /* Convert integer part */
556 do {
557 iconvert[iplace++] =
558 (caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10];
559 intpart = (intpart / 10);
560 } while(intpart && (iplace < 20));
561 if (iplace == 20)
562 iplace--;
563 iconvert[iplace] = 0;
Damien Miller168e6ac2000-07-11 12:23:01 +1000564
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000565 /* Convert fractional part */
566 do {
567 fconvert[fplace++] =
568 (caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10];
569 fracpart = (fracpart / 10);
570 } while(fracpart && (fplace < 20));
571 if (fplace == 20)
572 fplace--;
573 fconvert[fplace] = 0;
Damien Miller168e6ac2000-07-11 12:23:01 +1000574
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000575 /* -1 for decimal point, another -1 if we are printing a sign */
576 padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
577 zpadlen = max - fplace;
578 if (zpadlen < 0)
579 zpadlen = 0;
580 if (padlen < 0)
581 padlen = 0;
582 if (flags & DP_F_MINUS)
583 padlen = -padlen; /* Left Justifty */
Damien Miller168e6ac2000-07-11 12:23:01 +1000584
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000585 if ((flags & DP_F_ZERO) && (padlen > 0)) {
586 if (signvalue) {
587 dopr_outch(buffer, currlen, maxlen, signvalue);
588 --padlen;
589 signvalue = 0;
590 }
591 while (padlen > 0) {
592 dopr_outch(buffer, currlen, maxlen, '0');
593 --padlen;
594 }
595 }
596 while (padlen > 0) {
597 dopr_outch(buffer, currlen, maxlen, ' ');
598 --padlen;
599 }
600 if (signvalue)
601 dopr_outch(buffer, currlen, maxlen, signvalue);
Damien Miller168e6ac2000-07-11 12:23:01 +1000602
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000603 while (iplace > 0)
604 dopr_outch(buffer, currlen, maxlen, iconvert[--iplace]);
Damien Miller168e6ac2000-07-11 12:23:01 +1000605
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000606 /*
607 * Decimal point. This should probably use locale to find the correct
608 * char to print out.
609 */
610 dopr_outch(buffer, currlen, maxlen, '.');
Damien Miller168e6ac2000-07-11 12:23:01 +1000611
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000612 while (fplace > 0)
613 dopr_outch(buffer, currlen, maxlen, fconvert[--fplace]);
Damien Miller168e6ac2000-07-11 12:23:01 +1000614
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000615 while (zpadlen > 0) {
616 dopr_outch(buffer, currlen, maxlen, '0');
617 --zpadlen;
618 }
Damien Miller168e6ac2000-07-11 12:23:01 +1000619
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000620 while (padlen < 0) {
621 dopr_outch(buffer, currlen, maxlen, ' ');
622 ++padlen;
623 }
Damien Miller168e6ac2000-07-11 12:23:01 +1000624}
625
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000626static void
627dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c)
Damien Miller168e6ac2000-07-11 12:23:01 +1000628{
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000629 if (*currlen < maxlen)
630 buffer[(*currlen)++] = c;
Damien Miller168e6ac2000-07-11 12:23:01 +1000631}
632#endif /* !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) */
633
634#ifndef HAVE_VSNPRINTF
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000635int
636vsnprintf(char *str, size_t count, const char *fmt, va_list args)
Damien Miller168e6ac2000-07-11 12:23:01 +1000637{
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000638 str[0] = 0;
639 dopr(str, count, fmt, args);
640
641 return(strlen(str));
Damien Miller168e6ac2000-07-11 12:23:01 +1000642}
643#endif /* !HAVE_VSNPRINTF */
644
645#ifndef HAVE_SNPRINTF
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000646int
647snprintf(char *str,size_t count,const char *fmt,...)
Damien Millerc6b3bbe1999-12-13 08:27:33 +1100648{
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000649 va_list ap;
650
651 va_start(ap, fmt);
652 (void) vsnprintf(str, count, fmt, ap);
653 va_end(ap);
654
655 return(strlen(str));
Damien Millerc6b3bbe1999-12-13 08:27:33 +1100656}
Damien Millerc6b3bbe1999-12-13 08:27:33 +1100657
Damien Miller168e6ac2000-07-11 12:23:01 +1000658#ifdef TEST_SNPRINTF
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000659int
660main(void)
Damien Miller168e6ac2000-07-11 12:23:01 +1000661{
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000662#define LONG_STRING 1024
663 char buf1[LONG_STRING];
664 char buf2[LONG_STRING];
665 char *fp_fmt[] = {
666 "%-1.5f",
667 "%1.5f",
668 "%123.9f",
669 "%10.5f",
670 "% 10.5f",
671 "%+22.9f",
672 "%+4.9f",
673 "%01.3f",
674 "%4f",
675 "%3.1f",
676 "%3.2f",
677 NULL
678 };
679 double fp_nums[] = {
680 -1.5,
681 134.21,
682 91340.2,
683 341.1234,
684 0203.9,
685 0.96,
686 0.996,
687 0.9996,
688 1.996,
689 4.136,
690 0
691 };
692 char *int_fmt[] = {
693 "%-1.5d",
694 "%1.5d",
695 "%123.9d",
696 "%5.5d",
697 "%10.5d",
698 "% 10.5d",
699 "%+22.33d",
700 "%01.3d",
701 "%4d",
702 "%lld",
703 "%qd",
704 NULL
705 };
706 long long int_nums[] = { -1, 134, 91340, 341, 0203, 0, 9999999 };
707 int x, y;
708 int fail = 0;
709 int num = 0;
Damien Miller168e6ac2000-07-11 12:23:01 +1000710
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000711 printf("Testing snprintf format codes against system sprintf...\n");
Damien Miller168e6ac2000-07-11 12:23:01 +1000712
Ben Lindstrom6c92dab2001-02-13 02:18:50 +0000713 for (x = 0; fp_fmt[x] != NULL ; x++) {
714 for (y = 0; fp_nums[y] != 0 ; y++) {
715 snprintf(buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]);
716 sprintf (buf2, fp_fmt[x], fp_nums[y]);
717 if (strcmp (buf1, buf2)) {
718 printf("snprintf doesn't match Format: %s\n\t"
719 "snprintf = %s\n\tsprintf = %s\n",
720 fp_fmt[x], buf1, buf2);
721 fail++;
722 }
723 num++;
724 }
725 }
726 for (x = 0; int_fmt[x] != NULL ; x++) {
727 for (y = 0; int_nums[y] != 0 ; y++) {
728 snprintf(buf1, sizeof (buf1), int_fmt[x], int_nums[y]);
729 sprintf(buf2, int_fmt[x], int_nums[y]);
730 if (strcmp (buf1, buf2)) {
731 printf("snprintf doesn't match Format: %s\n\t"
732 "snprintf = %s\n\tsprintf = %s\n",
733 int_fmt[x], buf1, buf2);
734 fail++;
735 }
736 num++;
737 }
738 }
739 printf("%d tests failed out of %d.\n", fail, num);
740 return(0);
Damien Miller168e6ac2000-07-11 12:23:01 +1000741}
742#endif /* SNPRINTF_TEST */
743
744#endif /* !HAVE_SNPRINTF */