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