1899
|
1 /*
|
|
2 * Copyright Patrick Powell 1995
|
|
3 * This code is based on code written by Patrick Powell (papowell@astart.com)
|
|
4 * It may be used for any purpose as long as this notice remains intact
|
|
5 * on all source code distributions
|
|
6 */
|
|
7
|
|
8 /**************************************************************
|
|
9 * Original:
|
|
10 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
|
|
11 * A bombproof version of doprnt (dopr) included.
|
|
12 * Sigh. This sort of thing is always nasty do deal with. Note that
|
|
13 * the version here does not include floating point...
|
|
14 *
|
|
15 * snprintf() is used instead of sprintf() as it does limit checks
|
|
16 * for string length. This covers a nasty loophole.
|
|
17 *
|
|
18 * The other functions are there to prevent NULL pointers from
|
|
19 * causing nast effects.
|
|
20 *
|
|
21 * More Recently:
|
|
22 * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
|
|
23 * This was ugly. It is still ugly. I opted out of floating point
|
|
24 * numbers, but the formatter understands just about everything
|
|
25 * from the normal C string format, at least as far as I can tell from
|
|
26 * the Solaris 2.5 printf(3S) man page.
|
|
27 *
|
|
28 * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
|
|
29 * Ok, added some minimal floating point support, which means this
|
|
30 * probably requires libm on most operating systems. Don't yet
|
|
31 * support the exponent (e,E) and sigfig (g,G). Also, fmtint()
|
|
32 * was pretty badly broken, it just wasn't being exercised in ways
|
|
33 * which showed it, so that's been fixed. Also, formated the code
|
|
34 * to mutt conventions, and removed dead code left over from the
|
|
35 * original. Also, there is now a builtin-test, just compile with:
|
|
36 * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
|
|
37 * and run snprintf for results.
|
|
38 *
|
|
39 * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
|
|
40 * The PGP code was using unsigned hexadecimal formats.
|
|
41 * Unfortunately, unsigned formats simply didn't work.
|
|
42 *
|
|
43 * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
|
|
44 * The original code assumed that both snprintf() and vsnprintf() were
|
|
45 * missing. Some systems only have snprintf() but not vsnprintf(), so
|
|
46 * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
|
|
47 *
|
|
48 * Andrew Tridgell (tridge@samba.org) Oct 1998
|
|
49 * fixed handling of %.0f
|
|
50 * added test for HAVE_LONG_DOUBLE
|
|
51 *
|
|
52 * tridge@samba.org, idra@samba.org, April 2001
|
|
53 * got rid of fcvt code (twas buggy and made testing harder)
|
|
54 * added C99 semantics
|
|
55 *
|
|
56 **************************************************************/
|
|
57
|
|
58 #ifndef NO_CONFIG_H /* for some tests */
|
|
59 #include "config.h"
|
|
60 #endif
|
|
61
|
|
62 #ifdef HAVE_STRING_H
|
|
63 #include <string.h>
|
|
64 #endif
|
|
65
|
|
66 #ifdef HAVE_STRINGS_H
|
|
67 #include <strings.h>
|
|
68 #endif
|
|
69 #ifdef HAVE_CTYPE_H
|
|
70 #include <ctype.h>
|
|
71 #endif
|
|
72 #include <sys/types.h>
|
|
73 #include <stdarg.h>
|
|
74 #ifdef HAVE_STDLIB_H
|
|
75 #include <stdlib.h>
|
|
76 #endif
|
|
77
|
|
78 #if defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF) && defined(HAVE_C99_VSNPRINTF)
|
|
79 /* only include stdio.h if we are not re-defining snprintf or vsnprintf */
|
|
80 #include <stdio.h>
|
|
81 /* make the compiler happy with an empty file */
|
|
82 void dummy_snprintf(void) {}
|
|
83 #else
|
|
84
|
|
85 #ifdef HAVE_LONG_DOUBLE
|
|
86 #define LDOUBLE long double
|
|
87 #else
|
|
88 #define LDOUBLE double
|
|
89 #endif
|
|
90
|
|
91 #ifdef HAVE_LONG_LONG
|
|
92 #define LLONG long long
|
|
93 #else
|
|
94 #define LLONG long
|
|
95 #endif
|
|
96
|
|
97 static size_t dopr(char *buffer, size_t maxlen, const char *format,
|
|
98 va_list args);
|
|
99 static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
|
|
100 char *value, int flags, int min, int max);
|
|
101 static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
|
|
102 long value, int base, int min, int max, int flags);
|
|
103 static void fmtfp(char *buffer, size_t *currlen, size_t maxlen,
|
|
104 LDOUBLE fvalue, int min, int max, int flags);
|
|
105 static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c);
|
|
106
|
|
107 /*
|
|
108 * dopr(): poor man's version of doprintf
|
|
109 */
|
|
110
|
|
111 /* format read states */
|
|
112 #define DP_S_DEFAULT 0
|
|
113 #define DP_S_FLAGS 1
|
|
114 #define DP_S_MIN 2
|
|
115 #define DP_S_DOT 3
|
|
116 #define DP_S_MAX 4
|
|
117 #define DP_S_MOD 5
|
|
118 #define DP_S_CONV 6
|
|
119 #define DP_S_DONE 7
|
|
120
|
|
121 /* format flags - Bits */
|
|
122 #define DP_F_MINUS (1 << 0)
|
|
123 #define DP_F_PLUS (1 << 1)
|
|
124 #define DP_F_SPACE (1 << 2)
|
|
125 #define DP_F_NUM (1 << 3)
|
|
126 #define DP_F_ZERO (1 << 4)
|
|
127 #define DP_F_UP (1 << 5)
|
|
128 #define DP_F_UNSIGNED (1 << 6)
|
|
129
|
|
130 /* Conversion Flags */
|
|
131 #define DP_C_SHORT 1
|
|
132 #define DP_C_LONG 2
|
|
133 #define DP_C_LDOUBLE 3
|
|
134 #define DP_C_LLONG 4
|
|
135
|
|
136 #define char_to_int(p) ((p)- '0')
|
|
137 #ifndef MAX
|
|
138 #define MAX(p,q) (((p) >= (q)) ? (p) : (q))
|
|
139 #endif
|
|
140
|
|
141 static size_t dopr(char *buffer, size_t maxlen, const char *format, va_list args)
|
|
142 {
|
|
143 char ch;
|
|
144 LLONG value;
|
|
145 LDOUBLE fvalue;
|
|
146 char *strvalue;
|
|
147 int min;
|
|
148 int max;
|
|
149 int state;
|
|
150 int flags;
|
|
151 int cflags;
|
|
152 size_t currlen;
|
|
153
|
|
154 state = DP_S_DEFAULT;
|
|
155 currlen = flags = cflags = min = 0;
|
|
156 max = -1;
|
|
157 ch = *format++;
|
|
158
|
|
159 while (state != DP_S_DONE) {
|
|
160 if (ch == '\0')
|
|
161 state = DP_S_DONE;
|
|
162
|
|
163 switch(state) {
|
|
164 case DP_S_DEFAULT:
|
|
165 if (ch == '%')
|
|
166 state = DP_S_FLAGS;
|
|
167 else
|
|
168 dopr_outch (buffer, &currlen, maxlen, ch);
|
|
169 ch = *format++;
|
|
170 break;
|
|
171 case DP_S_FLAGS:
|
|
172 switch (ch) {
|
|
173 case '-':
|
|
174 flags |= DP_F_MINUS;
|
|
175 ch = *format++;
|
|
176 break;
|
|
177 case '+':
|
|
178 flags |= DP_F_PLUS;
|
|
179 ch = *format++;
|
|
180 break;
|
|
181 case ' ':
|
|
182 flags |= DP_F_SPACE;
|
|
183 ch = *format++;
|
|
184 break;
|
|
185 case '#':
|
|
186 flags |= DP_F_NUM;
|
|
187 ch = *format++;
|
|
188 break;
|
|
189 case '0':
|
|
190 flags |= DP_F_ZERO;
|
|
191 ch = *format++;
|
|
192 break;
|
|
193 default:
|
|
194 state = DP_S_MIN;
|
|
195 break;
|
|
196 }
|
|
197 break;
|
|
198 case DP_S_MIN:
|
|
199 if (isdigit((unsigned char)ch)) {
|
|
200 min = 10*min + char_to_int (ch);
|
|
201 ch = *format++;
|
|
202 } else if (ch == '*') {
|
|
203 min = va_arg (args, int);
|
|
204 ch = *format++;
|
|
205 state = DP_S_DOT;
|
|
206 } else {
|
|
207 state = DP_S_DOT;
|
|
208 }
|
|
209 break;
|
|
210 case DP_S_DOT:
|
|
211 if (ch == '.') {
|
|
212 state = DP_S_MAX;
|
|
213 ch = *format++;
|
|
214 } else {
|
|
215 state = DP_S_MOD;
|
|
216 }
|
|
217 break;
|
|
218 case DP_S_MAX:
|
|
219 if (isdigit((unsigned char)ch)) {
|
|
220 if (max < 0)
|
|
221 max = 0;
|
|
222 max = 10*max + char_to_int (ch);
|
|
223 ch = *format++;
|
|
224 } else if (ch == '*') {
|
|
225 max = va_arg (args, int);
|
|
226 ch = *format++;
|
|
227 state = DP_S_MOD;
|
|
228 } else {
|
|
229 state = DP_S_MOD;
|
|
230 }
|
|
231 break;
|
|
232 case DP_S_MOD:
|
|
233 switch (ch) {
|
|
234 case 'h':
|
|
235 cflags = DP_C_SHORT;
|
|
236 ch = *format++;
|
|
237 break;
|
|
238 case 'l':
|
|
239 cflags = DP_C_LONG;
|
|
240 ch = *format++;
|
|
241 if (ch == 'l') { /* It's a long long */
|
|
242 cflags = DP_C_LLONG;
|
|
243 ch = *format++;
|
|
244 }
|
|
245 break;
|
|
246 case 'L':
|
|
247 cflags = DP_C_LDOUBLE;
|
|
248 ch = *format++;
|
|
249 break;
|
|
250 default:
|
|
251 break;
|
|
252 }
|
|
253 state = DP_S_CONV;
|
|
254 break;
|
|
255 case DP_S_CONV:
|
|
256 switch (ch) {
|
|
257 case 'd':
|
|
258 case 'i':
|
|
259 if (cflags == DP_C_SHORT)
|
|
260 value = va_arg (args, int);
|
|
261 else if (cflags == DP_C_LONG)
|
|
262 value = va_arg (args, long int);
|
|
263 else if (cflags == DP_C_LLONG)
|
|
264 value = va_arg (args, LLONG);
|
|
265 else
|
|
266 value = va_arg (args, int);
|
|
267 fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
|
|
268 break;
|
|
269 case 'o':
|
|
270 flags |= DP_F_UNSIGNED;
|
|
271 if (cflags == DP_C_SHORT)
|
|
272 value = va_arg (args, unsigned int);
|
|
273 else if (cflags == DP_C_LONG)
|
|
274 value = (long)va_arg (args, unsigned long int);
|
|
275 else if (cflags == DP_C_LLONG)
|
|
276 value = (long)va_arg (args, unsigned LLONG);
|
|
277 else
|
|
278 value = (long)va_arg (args, unsigned int);
|
|
279 fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
|
|
280 break;
|
|
281 case 'u':
|
|
282 flags |= DP_F_UNSIGNED;
|
|
283 if (cflags == DP_C_SHORT)
|
|
284 value = va_arg (args, unsigned int);
|
|
285 else if (cflags == DP_C_LONG)
|
|
286 value = (long)va_arg (args, unsigned long int);
|
|
287 else if (cflags == DP_C_LLONG)
|
|
288 value = (LLONG)va_arg (args, unsigned LLONG);
|
|
289 else
|
|
290 value = (long)va_arg (args, unsigned int);
|
|
291 fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
|
|
292 break;
|
|
293 case 'X':
|
|
294 flags |= DP_F_UP;
|
|
295 case 'x':
|
|
296 flags |= DP_F_UNSIGNED;
|
|
297 if (cflags == DP_C_SHORT)
|
|
298 value = va_arg (args, unsigned int);
|
|
299 else if (cflags == DP_C_LONG)
|
|
300 value = (long)va_arg (args, unsigned long int);
|
|
301 else if (cflags == DP_C_LLONG)
|
|
302 value = (LLONG)va_arg (args, unsigned LLONG);
|
|
303 else
|
|
304 value = (long)va_arg (args, unsigned int);
|
|
305 fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
|
|
306 break;
|
|
307 case 'f':
|
|
308 if (cflags == DP_C_LDOUBLE)
|
|
309 fvalue = va_arg (args, LDOUBLE);
|
|
310 else
|
|
311 fvalue = va_arg (args, double);
|
|
312 /* um, floating point? */
|
|
313 fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
|
|
314 break;
|
|
315 case 'E':
|
|
316 flags |= DP_F_UP;
|
|
317 case 'e':
|
|
318 if (cflags == DP_C_LDOUBLE)
|
|
319 fvalue = va_arg (args, LDOUBLE);
|
|
320 else
|
|
321 fvalue = va_arg (args, double);
|
|
322 break;
|
|
323 case 'G':
|
|
324 flags |= DP_F_UP;
|
|
325 case 'g':
|
|
326 if (cflags == DP_C_LDOUBLE)
|
|
327 fvalue = va_arg (args, LDOUBLE);
|
|
328 else
|
|
329 fvalue = va_arg (args, double);
|
|
330 break;
|
|
331 case 'c':
|
|
332 dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
|
|
333 break;
|
|
334 case 's':
|
|
335 strvalue = va_arg (args, char *);
|
|
336 if (!strvalue) strvalue = "(NULL)";
|
|
337 if (max == -1) {
|
|
338 max = strlen(strvalue);
|
|
339 }
|
|
340 if (min > 0 && max >= 0 && min > max) max = min;
|
|
341 fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
|
|
342 break;
|
|
343 case 'p':
|
|
344 strvalue = (char *)va_arg(args, void *);
|
|
345 fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
|
|
346 break;
|
|
347 case 'n':
|
|
348 if (cflags == DP_C_SHORT) {
|
|
349 short int *num;
|
|
350 num = va_arg (args, short int *);
|
|
351 *num = currlen;
|
|
352 } else if (cflags == DP_C_LONG) {
|
|
353 long int *num;
|
|
354 num = va_arg (args, long int *);
|
|
355 *num = (long int)currlen;
|
|
356 } else if (cflags == DP_C_LLONG) {
|
|
357 LLONG *num;
|
|
358 num = va_arg (args, LLONG *);
|
|
359 *num = (LLONG)currlen;
|
|
360 } else {
|
|
361 int *num;
|
|
362 num = va_arg (args, int *);
|
|
363 *num = currlen;
|
|
364 }
|
|
365 break;
|
|
366 case '%':
|
|
367 dopr_outch (buffer, &currlen, maxlen, ch);
|
|
368 break;
|
|
369 case 'w':
|
|
370 /* not supported yet, treat as next char */
|
|
371 ch = *format++;
|
|
372 break;
|
|
373 default:
|
|
374 /* Unknown, skip */
|
|
375 break;
|
|
376 }
|
|
377 ch = *format++;
|
|
378 state = DP_S_DEFAULT;
|
|
379 flags = cflags = min = 0;
|
|
380 max = -1;
|
|
381 break;
|
|
382 case DP_S_DONE:
|
|
383 break;
|
|
384 default:
|
|
385 /* hmm? */
|
|
386 break; /* some picky compilers need this */
|
|
387 }
|
|
388 }
|
|
389 if (maxlen != 0) {
|
|
390 if (currlen < maxlen - 1)
|
|
391 buffer[currlen] = '\0';
|
|
392 else if (maxlen > 0)
|
|
393 buffer[maxlen - 1] = '\0';
|
|
394 }
|
|
395
|
|
396 return currlen;
|
|
397 }
|
|
398
|
|
399 static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
|
|
400 char *value, int flags, int min, int max)
|
|
401 {
|
|
402 int padlen, strln; /* amount to pad */
|
|
403 int cnt = 0;
|
|
404
|
|
405 #ifdef DEBUG_SNPRINTF
|
|
406 printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value);
|
|
407 #endif
|
|
408 if (value == 0) {
|
|
409 value = "<NULL>";
|
|
410 }
|
|
411
|
|
412 for (strln = 0; value[strln]; ++strln); /* strlen */
|
|
413 padlen = min - strln;
|
|
414 if (padlen < 0)
|
|
415 padlen = 0;
|
|
416 if (flags & DP_F_MINUS)
|
|
417 padlen = -padlen; /* Left Justify */
|
|
418
|
|
419 while ((padlen > 0) && (cnt < max)) {
|
|
420 dopr_outch (buffer, currlen, maxlen, ' ');
|
|
421 --padlen;
|
|
422 ++cnt;
|
|
423 }
|
|
424 while (*value && (cnt < max)) {
|
|
425 dopr_outch (buffer, currlen, maxlen, *value++);
|
|
426 ++cnt;
|
|
427 }
|
|
428 while ((padlen < 0) && (cnt < max)) {
|
|
429 dopr_outch (buffer, currlen, maxlen, ' ');
|
|
430 ++padlen;
|
|
431 ++cnt;
|
|
432 }
|
|
433 }
|
|
434
|
|
435 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
|
|
436
|
|
437 static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
|
|
438 long value, int base, int min, int max, int flags)
|
|
439 {
|
|
440 int signvalue = 0;
|
|
441 unsigned long uvalue;
|
|
442 char convert[20];
|
|
443 int place = 0;
|
|
444 int spadlen = 0; /* amount to space pad */
|
|
445 int zpadlen = 0; /* amount to zero pad */
|
|
446 int caps = 0;
|
|
447
|
|
448 if (max < 0)
|
|
449 max = 0;
|
|
450
|
|
451 uvalue = value;
|
|
452
|
|
453 if(!(flags & DP_F_UNSIGNED)) {
|
|
454 if( value < 0 ) {
|
|
455 signvalue = '-';
|
|
456 uvalue = -value;
|
|
457 } else {
|
|
458 if (flags & DP_F_PLUS) /* Do a sign (+/i) */
|
|
459 signvalue = '+';
|
|
460 else if (flags & DP_F_SPACE)
|
|
461 signvalue = ' ';
|
|
462 }
|
|
463 }
|
|
464
|
|
465 if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
|
|
466
|
|
467 do {
|
|
468 convert[place++] =
|
|
469 (caps? "0123456789ABCDEF":"0123456789abcdef")
|
|
470 [uvalue % (unsigned)base ];
|
|
471 uvalue = (uvalue / (unsigned)base );
|
|
472 } while(uvalue && (place < 20));
|
|
473 if (place == 20) place--;
|
|
474 convert[place] = 0;
|
|
475
|
|
476 zpadlen = max - place;
|
|
477 spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
|
|
478 if (zpadlen < 0) zpadlen = 0;
|
|
479 if (spadlen < 0) spadlen = 0;
|
|
480 if (flags & DP_F_ZERO) {
|
|
481 zpadlen = MAX(zpadlen, spadlen);
|
|
482 spadlen = 0;
|
|
483 }
|
|
484 if (flags & DP_F_MINUS)
|
|
485 spadlen = -spadlen; /* Left Justifty */
|
|
486
|
|
487 #ifdef DEBUG_SNPRINTF
|
|
488 printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
|
|
489 zpadlen, spadlen, min, max, place);
|
|
490 #endif
|
|
491
|
|
492 /* Spaces */
|
|
493 while (spadlen > 0) {
|
|
494 dopr_outch (buffer, currlen, maxlen, ' ');
|
|
495 --spadlen;
|
|
496 }
|
|
497
|
|
498 /* Sign */
|
|
499 if (signvalue)
|
|
500 dopr_outch (buffer, currlen, maxlen, signvalue);
|
|
501
|
|
502 /* Zeros */
|
|
503 if (zpadlen > 0) {
|
|
504 while (zpadlen > 0) {
|
|
505 dopr_outch (buffer, currlen, maxlen, '0');
|
|
506 --zpadlen;
|
|
507 }
|
|
508 }
|
|
509
|
|
510 /* Digits */
|
|
511 while (place > 0)
|
|
512 dopr_outch (buffer, currlen, maxlen, convert[--place]);
|
|
513
|
|
514 /* Left Justified spaces */
|
|
515 while (spadlen < 0) {
|
|
516 dopr_outch (buffer, currlen, maxlen, ' ');
|
|
517 ++spadlen;
|
|
518 }
|
|
519 }
|
|
520
|
|
521 static LDOUBLE abs_val(LDOUBLE value)
|
|
522 {
|
|
523 LDOUBLE result = value;
|
|
524
|
|
525 if (value < 0)
|
|
526 result = -value;
|
|
527
|
|
528 return result;
|
|
529 }
|
|
530
|
|
531 static LDOUBLE POW10(int exp)
|
|
532 {
|
|
533 LDOUBLE result = 1;
|
|
534
|
|
535 while (exp) {
|
|
536 result *= 10;
|
|
537 exp--;
|
|
538 }
|
|
539
|
|
540 return result;
|
|
541 }
|
|
542
|
|
543 static LLONG ROUND(LDOUBLE value)
|
|
544 {
|
|
545 LLONG intpart;
|
|
546
|
|
547 intpart = (LLONG)value;
|
|
548 value = value - intpart;
|
|
549 if (value >= 0.5) intpart++;
|
|
550
|
|
551 return intpart;
|
|
552 }
|
|
553
|
|
554 /* a replacement for modf that doesn't need the math library. Should
|
|
555 be portable, but slow */
|
|
556 static double my_modf(double x0, double *iptr)
|
|
557 {
|
|
558 int i;
|
|
559 long l;
|
|
560 double x = x0;
|
|
561 double f = 1.0;
|
|
562
|
|
563 for (i=0;i<100;i++) {
|
|
564 l = (long)x;
|
|
565 if (l <= (x+1) && l >= (x-1)) break;
|
|
566 x *= 0.1;
|
|
567 f *= 10.0;
|
|
568 }
|
|
569
|
|
570 if (i == 100) {
|
|
571 /* yikes! the number is beyond what we can handle. What do we do? */
|
|
572 (*iptr) = 0;
|
|
573 return 0;
|
|
574 }
|
|
575
|
|
576 if (i != 0) {
|
|
577 double i2;
|
|
578 double ret;
|
|
579
|
|
580 ret = my_modf(x0-l*f, &i2);
|
|
581 (*iptr) = l*f + i2;
|
|
582 return ret;
|
|
583 }
|
|
584
|
|
585 (*iptr) = l;
|
|
586 return x - (*iptr);
|
|
587 }
|
|
588
|
|
589
|
|
590 static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
|
|
591 LDOUBLE fvalue, int min, int max, int flags)
|
|
592 {
|
|
593 int signvalue = 0;
|
|
594 double ufvalue;
|
|
595 char iconvert[311];
|
|
596 char fconvert[311];
|
|
597 int iplace = 0;
|
|
598 int fplace = 0;
|
|
599 int padlen = 0; /* amount to pad */
|
|
600 int zpadlen = 0;
|
|
601 int caps = 0;
|
|
602 int index;
|
|
603 double intpart;
|
|
604 double fracpart;
|
|
605 double temp;
|
|
606
|
|
607 /*
|
|
608 * AIX manpage says the default is 0, but Solaris says the default
|
|
609 * is 6, and sprintf on AIX defaults to 6
|
|
610 */
|
|
611 if (max < 0)
|
|
612 max = 6;
|
|
613
|
|
614 ufvalue = abs_val (fvalue);
|
|
615
|
|
616 if (fvalue < 0) {
|
|
617 signvalue = '-';
|
|
618 } else {
|
|
619 if (flags & DP_F_PLUS) { /* Do a sign (+/i) */
|
|
620 signvalue = '+';
|
|
621 } else {
|
|
622 if (flags & DP_F_SPACE)
|
|
623 signvalue = ' ';
|
|
624 }
|
|
625 }
|
|
626
|
|
627 #if 0
|
|
628 if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
|
|
629 #endif
|
|
630
|
|
631 #if 0
|
|
632 if (max == 0) ufvalue += 0.5; /* if max = 0 we must round */
|
|
633 #endif
|
|
634
|
|
635 /*
|
|
636 * Sorry, we only support 16 digits past the decimal because of our
|
|
637 * conversion method
|
|
638 */
|
|
639 if (max > 16)
|
|
640 max = 16;
|
|
641
|
|
642 /* We "cheat" by converting the fractional part to integer by
|
|
643 * multiplying by a factor of 10
|
|
644 */
|
|
645
|
|
646 temp = ufvalue;
|
|
647 my_modf(temp, &intpart);
|
|
648
|
|
649 fracpart = ROUND((POW10(max)) * (ufvalue - intpart));
|
|
650
|
|
651 if (fracpart >= POW10(max)) {
|
|
652 intpart++;
|
|
653 fracpart -= POW10(max);
|
|
654 }
|
|
655
|
|
656
|
|
657 /* Convert integer part */
|
|
658 do {
|
|
659 temp = intpart;
|
|
660 my_modf(intpart*0.1, &intpart);
|
|
661 temp = temp*0.1;
|
|
662 index = (int) ((temp -intpart +0.05)* 10.0);
|
|
663 /* index = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */
|
|
664 /* printf ("%llf, %f, %x\n", temp, intpart, index); */
|
|
665 iconvert[iplace++] =
|
|
666 (caps? "0123456789ABCDEF":"0123456789abcdef")[index];
|
|
667 } while (intpart && (iplace < 311));
|
|
668 if (iplace == 311) iplace--;
|
|
669 iconvert[iplace] = 0;
|
|
670
|
|
671 /* Convert fractional part */
|
|
672 if (fracpart)
|
|
673 {
|
|
674 do {
|
|
675 temp = fracpart;
|
|
676 my_modf(fracpart*0.1, &fracpart);
|
|
677 temp = temp*0.1;
|
|
678 index = (int) ((temp -fracpart +0.05)* 10.0);
|
|
679 /* index = (int) ((((temp/10) -fracpart) +0.05) *10); */
|
|
680 /* printf ("%lf, %lf, %ld\n", temp, fracpart, index); */
|
|
681 fconvert[fplace++] =
|
|
682 (caps? "0123456789ABCDEF":"0123456789abcdef")[index];
|
|
683 } while(fracpart && (fplace < 311));
|
|
684 if (fplace == 311) fplace--;
|
|
685 }
|
|
686 fconvert[fplace] = 0;
|
|
687
|
|
688 /* -1 for decimal point, another -1 if we are printing a sign */
|
|
689 padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
|
|
690 zpadlen = max - fplace;
|
|
691 if (zpadlen < 0) zpadlen = 0;
|
|
692 if (padlen < 0)
|
|
693 padlen = 0;
|
|
694 if (flags & DP_F_MINUS)
|
|
695 padlen = -padlen; /* Left Justifty */
|
|
696
|
|
697 if ((flags & DP_F_ZERO) && (padlen > 0)) {
|
|
698 if (signvalue) {
|
|
699 dopr_outch (buffer, currlen, maxlen, signvalue);
|
|
700 --padlen;
|
|
701 signvalue = 0;
|
|
702 }
|
|
703 while (padlen > 0) {
|
|
704 dopr_outch (buffer, currlen, maxlen, '0');
|
|
705 --padlen;
|
|
706 }
|
|
707 }
|
|
708 while (padlen > 0) {
|
|
709 dopr_outch (buffer, currlen, maxlen, ' ');
|
|
710 --padlen;
|
|
711 }
|
|
712 if (signvalue)
|
|
713 dopr_outch (buffer, currlen, maxlen, signvalue);
|
|
714
|
|
715 while (iplace > 0)
|
|
716 dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
|
|
717
|
|
718 #ifdef DEBUG_SNPRINTF
|
|
719 printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen);
|
|
720 #endif
|
|
721
|
|
722 /*
|
|
723 * Decimal point. This should probably use locale to find the correct
|
|
724 * char to print out.
|
|
725 */
|
|
726 if (max > 0) {
|
|
727 dopr_outch (buffer, currlen, maxlen, '.');
|
|
728
|
|
729 while (fplace > 0)
|
|
730 dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
|
|
731 }
|
|
732
|
|
733 while (zpadlen > 0) {
|
|
734 dopr_outch (buffer, currlen, maxlen, '0');
|
|
735 --zpadlen;
|
|
736 }
|
|
737
|
|
738 while (padlen < 0) {
|
|
739 dopr_outch (buffer, currlen, maxlen, ' ');
|
|
740 ++padlen;
|
|
741 }
|
|
742 }
|
|
743
|
|
744 static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c)
|
|
745 {
|
|
746 if (*currlen < maxlen) {
|
|
747 buffer[(*currlen)] = c;
|
|
748 }
|
|
749 (*currlen)++;
|
|
750 }
|
|
751
|
|
752 /* yes this really must be a ||. Don't muck with this (tridge) */
|
|
753 #if !defined(HAVE_VSNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
|
|
754 int vsnprintf (char *str, size_t count, const char *fmt, va_list args)
|
|
755 {
|
|
756 return dopr(str, count, fmt, args);
|
|
757 }
|
|
758 #endif
|
|
759
|
|
760 /* yes this really must be a ||. Don't muck wiith this (tridge)
|
|
761 *
|
|
762 * The logic for these two is that we need our own definition if the
|
|
763 * OS *either* has no definition of *sprintf, or if it does have one
|
|
764 * that doesn't work properly according to the autoconf test. Perhaps
|
|
765 * these should really be smb_snprintf to avoid conflicts with buggy
|
|
766 * linkers? -- mbp
|
|
767 */
|
|
768 #if !defined(HAVE_SNPRINTF) || !defined(HAVE_C99_SNPRINTF)
|
|
769 int snprintf(char *str,size_t count,const char *fmt,...)
|
|
770 {
|
|
771 size_t ret;
|
|
772 va_list ap;
|
|
773
|
|
774 va_start(ap, fmt);
|
|
775 ret = vsnprintf(str, count, fmt, ap);
|
|
776 va_end(ap);
|
|
777 return ret;
|
|
778 }
|
|
779 #endif
|
|
780
|
|
781 #endif
|
|
782
|
|
783 #ifndef HAVE_VASPRINTF
|
|
784 int vasprintf(char **ptr, const char *format, va_list ap)
|
|
785 {
|
|
786 int ret;
|
|
787
|
|
788 ret = vsnprintf(0, 0, format, ap);
|
|
789 if (ret <= 0) return ret;
|
|
790
|
|
791 (*ptr) = (char *)malloc(ret+1);
|
|
792 if (!*ptr) return -1;
|
|
793 ret = vsnprintf(*ptr, ret+1, format, ap);
|
|
794
|
|
795 return ret;
|
|
796 }
|
|
797 #endif
|
|
798
|
|
799
|
|
800 #ifndef HAVE_ASPRINTF
|
|
801 int asprintf(char **ptr, const char *format, ...)
|
|
802 {
|
|
803 va_list ap;
|
|
804 int ret;
|
|
805
|
|
806 *ptr = 0;
|
|
807 va_start(ap, format);
|
|
808 ret = vasprintf(ptr, format, ap);
|
|
809 va_end(ap);
|
|
810
|
|
811 return ret;
|
|
812 }
|
|
813 #endif
|
|
814
|
|
815 #ifndef HAVE_VSYSLOG
|
|
816 #ifdef HAVE_SYSLOG
|
|
817 void vsyslog (int facility_priority, char *format, va_list arglist)
|
|
818 {
|
|
819 char *msg = 0;
|
|
820 vasprintf(&msg, format, arglist);
|
|
821 if (!msg)
|
|
822 return;
|
|
823 syslog(facility_priority, "%s", msg);
|
|
824 free(msg);
|
|
825 }
|
|
826 #endif /* HAVE_SYSLOG */
|
|
827 #endif /* HAVE_VSYSLOG */
|
|
828
|
|
829 #ifdef TEST_SNPRINTF
|
|
830
|
|
831 int sprintf(char *str,const char *fmt,...);
|
|
832
|
|
833 int main (void)
|
|
834 {
|
|
835 char buf1[1024];
|
|
836 char buf2[1024];
|
|
837 char *fp_fmt[] = {
|
|
838 "%1.1f",
|
|
839 "%-1.5f",
|
|
840 "%1.5f",
|
|
841 "%123.9f",
|
|
842 "%10.5f",
|
|
843 "% 10.5f",
|
|
844 "%+22.9f",
|
|
845 "%+4.9f",
|
|
846 "%01.3f",
|
|
847 "%4f",
|
|
848 "%3.1f",
|
|
849 "%3.2f",
|
|
850 "%.0f",
|
|
851 "%f",
|
|
852 "-16.16f",
|
|
853 0
|
|
854 };
|
|
855 double fp_nums[] = { 6442452944.1234, -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996,
|
|
856 0.9996, 1.996, 4.136, 0};
|
|
857 char *int_fmt[] = {
|
|
858 "%-1.5d",
|
|
859 "%1.5d",
|
|
860 "%123.9d",
|
|
861 "%5.5d",
|
|
862 "%10.5d",
|
|
863 "% 10.5d",
|
|
864 "%+22.33d",
|
|
865 "%01.3d",
|
|
866 "%4d",
|
|
867 "%d",
|
|
868 0
|
|
869 };
|
|
870 long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
|
|
871 char *str_fmt[] = {
|
|
872 "10.5s",
|
|
873 "5.10s",
|
|
874 "10.1s",
|
|
875 "0.10s",
|
|
876 "10.0s",
|
|
877 "1.10s",
|
|
878 "%s",
|
|
879 "%.1s",
|
|
880 "%.10s",
|
|
881 "%10s",
|
|
882 0
|
|
883 };
|
|
884 char *str_vals[] = {"hello", "a", "", "a longer string", 0};
|
|
885 int x, y;
|
|
886 int fail = 0;
|
|
887 int num = 0;
|
|
888
|
|
889 printf ("Testing snprintf format codes against system sprintf...\n");
|
|
890
|
|
891 for (x = 0; fp_fmt[x] ; x++) {
|
|
892 for (y = 0; fp_nums[y] != 0 ; y++) {
|
|
893 int l1 = snprintf(0, 0, fp_fmt[x], fp_nums[y]);
|
|
894 int l2 = snprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]);
|
|
895 sprintf (buf2, fp_fmt[x], fp_nums[y]);
|
|
896 if (strcmp (buf1, buf2)) {
|
|
897 printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n",
|
|
898 fp_fmt[x], buf1, buf2);
|
|
899 fail++;
|
|
900 }
|
|
901 if (l1 != l2) {
|
|
902 printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, fp_fmt[x]);
|
|
903 fail++;
|
|
904 }
|
|
905 num++;
|
|
906 }
|
|
907 }
|
|
908
|
|
909 for (x = 0; int_fmt[x] ; x++) {
|
|
910 for (y = 0; int_nums[y] != 0 ; y++) {
|
|
911 int l1 = snprintf(0, 0, int_fmt[x], int_nums[y]);
|
|
912 int l2 = snprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]);
|
|
913 sprintf (buf2, int_fmt[x], int_nums[y]);
|
|
914 if (strcmp (buf1, buf2)) {
|
|
915 printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n",
|
|
916 int_fmt[x], buf1, buf2);
|
|
917 fail++;
|
|
918 }
|
|
919 if (l1 != l2) {
|
|
920 printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, int_fmt[x]);
|
|
921 fail++;
|
|
922 }
|
|
923 num++;
|
|
924 }
|
|
925 }
|
|
926
|
|
927 for (x = 0; str_fmt[x] ; x++) {
|
|
928 for (y = 0; str_vals[y] != 0 ; y++) {
|
|
929 int l1 = snprintf(0, 0, str_fmt[x], str_vals[y]);
|
|
930 int l2 = snprintf(buf1, sizeof(buf1), str_fmt[x], str_vals[y]);
|
|
931 sprintf (buf2, str_fmt[x], str_vals[y]);
|
|
932 if (strcmp (buf1, buf2)) {
|
|
933 printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n",
|
|
934 str_fmt[x], buf1, buf2);
|
|
935 fail++;
|
|
936 }
|
|
937 if (l1 != l2) {
|
|
938 printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, str_fmt[x]);
|
|
939 fail++;
|
|
940 }
|
|
941 num++;
|
|
942 }
|
|
943 }
|
|
944
|
|
945 printf ("%d tests failed out of %d.\n", fail, num);
|
|
946
|
|
947 printf("seeing how many digits we support\n");
|
|
948 {
|
|
949 double v0 = 0.12345678901234567890123456789012345678901;
|
|
950 for (x=0; x<100; x++) {
|
|
951 snprintf(buf1, sizeof(buf1), "%1.1f", v0*pow(10, x));
|
|
952 sprintf(buf2, "%1.1f", v0*pow(10, x));
|
|
953 if (strcmp(buf1, buf2)) {
|
|
954 printf("we seem to support %d digits\n", x-1);
|
|
955 break;
|
|
956 }
|
|
957 }
|
|
958 }
|
|
959
|
|
960 return 0;
|
|
961 }
|
|
962 #endif /* SNPRINTF_TEST */
|