diff options
Diffstat (limited to 'bsd-snprintf.c')
-rw-r--r-- | bsd-snprintf.c | 895 |
1 files changed, 751 insertions, 144 deletions
diff --git a/bsd-snprintf.c b/bsd-snprintf.c index ff26a1048..4716ee291 100644 --- a/bsd-snprintf.c +++ b/bsd-snprintf.c | |||
@@ -1,181 +1,788 @@ | |||
1 | /* | 1 | /************************************************************** |
2 | * Revision 12: http://theos.com/~deraadt/snprintf.c | 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... | ||
3 | * | 7 | * |
4 | * Copyright (c) 1997 Theo de Raadt | 8 | * snprintf() is used instead of sprintf() as it does limit checks |
9 | * for string length. This covers a nasty loophole. | ||
5 | * | 10 | * |
6 | * Redistribution and use in source and binary forms, with or without | 11 | * The other functions are there to prevent NULL pointers from |
7 | * modification, are permitted provided that the following conditions | 12 | * causing nast effects. |
8 | * are met: | ||
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 | * | 13 | * |
15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | 14 | * More Recently: |
16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | 15 | * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43 |
17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | 16 | * This was ugly. It is still ugly. I opted out of floating point |
18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | 17 | * numbers, but the formatter understands just about everything |
19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | 18 | * from the normal C string format, at least as far as I can tell from |
20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 19 | * the Solaris 2.5 printf(3S) man page. |
21 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 20 | * |
22 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 21 | * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1 |
23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 22 | * Ok, added some minimal floating point support, which means this |
24 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 23 | * probably requires libm on most operating systems. Don't yet |
25 | */ | 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 | * | ||
41 | **************************************************************/ | ||
26 | 42 | ||
27 | #include "config.h" | 43 | #include "config.h" |
28 | 44 | ||
29 | #if defined(BROKEN_SNPRINTF) || !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) | 45 | #if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) |
30 | 46 | ||
31 | #include <sys/param.h> | ||
32 | #include <sys/types.h> | ||
33 | #include <sys/mman.h> | ||
34 | #include <signal.h> | ||
35 | #include <stdio.h> | ||
36 | #include <unistd.h> | ||
37 | #include <string.h> | 47 | #include <string.h> |
38 | #if __STDC__ | 48 | # include <ctype.h> |
39 | #include <stdarg.h> | 49 | #include <sys/types.h> |
40 | #include <stdlib.h> | 50 | |
51 | /* Define this as a fall through, HAVE_STDARG_H is probably already set */ | ||
52 | |||
53 | #define HAVE_VARARGS_H | ||
54 | |||
55 | /* varargs declarations: */ | ||
56 | |||
57 | #if defined(HAVE_STDARG_H) | ||
58 | # include <stdarg.h> | ||
59 | # define HAVE_STDARGS /* let's hope that works everywhere (mj) */ | ||
60 | # define VA_LOCAL_DECL va_list ap | ||
61 | # define VA_START(f) va_start(ap, f) | ||
62 | # define VA_SHIFT(v,t) ; /* no-op for ANSI */ | ||
63 | # define VA_END va_end(ap) | ||
41 | #else | 64 | #else |
42 | #include <varargs.h> | 65 | # if defined(HAVE_VARARGS_H) |
66 | # include <varargs.h> | ||
67 | # undef HAVE_STDARGS | ||
68 | # define VA_LOCAL_DECL va_list ap | ||
69 | # define VA_START(f) va_start(ap) /* f is ignored! */ | ||
70 | # define VA_SHIFT(v,t) v = va_arg(ap,t) | ||
71 | # define VA_END va_end(ap) | ||
72 | # else | ||
73 | /*XX ** NO VARARGS ** XX*/ | ||
74 | # endif | ||
43 | #endif | 75 | #endif |
44 | #include <setjmp.h> | ||
45 | 76 | ||
46 | #ifndef roundup | 77 | /*int snprintf (char *str, size_t count, const char *fmt, ...);*/ |
47 | #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) | 78 | /*int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);*/ |
79 | |||
80 | static void dopr (char *buffer, size_t maxlen, const char *format, | ||
81 | va_list args); | ||
82 | static void fmtstr (char *buffer, size_t *currlen, size_t maxlen, | ||
83 | char *value, int flags, int min, int max); | ||
84 | static void fmtint (char *buffer, size_t *currlen, size_t maxlen, | ||
85 | long value, int base, int min, int max, int flags); | ||
86 | static void fmtfp (char *buffer, size_t *currlen, size_t maxlen, | ||
87 | long double fvalue, int min, int max, int flags); | ||
88 | static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c ); | ||
89 | |||
90 | /* | ||
91 | * dopr(): poor man's version of doprintf | ||
92 | */ | ||
93 | |||
94 | /* format read states */ | ||
95 | #define DP_S_DEFAULT 0 | ||
96 | #define DP_S_FLAGS 1 | ||
97 | #define DP_S_MIN 2 | ||
98 | #define DP_S_DOT 3 | ||
99 | #define DP_S_MAX 4 | ||
100 | #define DP_S_MOD 5 | ||
101 | #define DP_S_CONV 6 | ||
102 | #define DP_S_DONE 7 | ||
103 | |||
104 | /* format flags - Bits */ | ||
105 | #define DP_F_MINUS (1 << 0) | ||
106 | #define DP_F_PLUS (1 << 1) | ||
107 | #define DP_F_SPACE (1 << 2) | ||
108 | #define DP_F_NUM (1 << 3) | ||
109 | #define DP_F_ZERO (1 << 4) | ||
110 | #define DP_F_UP (1 << 5) | ||
111 | #define DP_F_UNSIGNED (1 << 6) | ||
112 | |||
113 | /* Conversion Flags */ | ||
114 | #define DP_C_SHORT 1 | ||
115 | #define DP_C_LONG 2 | ||
116 | #define DP_C_LDOUBLE 3 | ||
117 | |||
118 | #define char_to_int(p) (p - '0') | ||
119 | #define MAX(p,q) ((p >= q) ? p : q) | ||
120 | |||
121 | static void dopr (char *buffer, size_t maxlen, const char *format, va_list args) | ||
122 | { | ||
123 | char ch; | ||
124 | long value; | ||
125 | long double fvalue; | ||
126 | char *strvalue; | ||
127 | int min; | ||
128 | int max; | ||
129 | int state; | ||
130 | int flags; | ||
131 | int cflags; | ||
132 | size_t currlen; | ||
133 | |||
134 | state = DP_S_DEFAULT; | ||
135 | currlen = flags = cflags = min = 0; | ||
136 | max = -1; | ||
137 | ch = *format++; | ||
138 | |||
139 | while (state != DP_S_DONE) | ||
140 | { | ||
141 | if ((ch == '\0') || (currlen >= maxlen)) | ||
142 | state = DP_S_DONE; | ||
143 | |||
144 | switch(state) | ||
145 | { | ||
146 | case DP_S_DEFAULT: | ||
147 | if (ch == '%') | ||
148 | state = DP_S_FLAGS; | ||
149 | else | ||
150 | dopr_outch (buffer, &currlen, maxlen, ch); | ||
151 | ch = *format++; | ||
152 | break; | ||
153 | case DP_S_FLAGS: | ||
154 | switch (ch) | ||
155 | { | ||
156 | case '-': | ||
157 | flags |= DP_F_MINUS; | ||
158 | ch = *format++; | ||
159 | break; | ||
160 | case '+': | ||
161 | flags |= DP_F_PLUS; | ||
162 | ch = *format++; | ||
163 | break; | ||
164 | case ' ': | ||
165 | flags |= DP_F_SPACE; | ||
166 | ch = *format++; | ||
167 | break; | ||
168 | case '#': | ||
169 | flags |= DP_F_NUM; | ||
170 | ch = *format++; | ||
171 | break; | ||
172 | case '0': | ||
173 | flags |= DP_F_ZERO; | ||
174 | ch = *format++; | ||
175 | break; | ||
176 | default: | ||
177 | state = DP_S_MIN; | ||
178 | break; | ||
179 | } | ||
180 | break; | ||
181 | case DP_S_MIN: | ||
182 | if (isdigit((unsigned char)ch)) | ||
183 | { | ||
184 | min = 10*min + char_to_int (ch); | ||
185 | ch = *format++; | ||
186 | } | ||
187 | else if (ch == '*') | ||
188 | { | ||
189 | min = va_arg (args, int); | ||
190 | ch = *format++; | ||
191 | state = DP_S_DOT; | ||
192 | } | ||
193 | else | ||
194 | state = DP_S_DOT; | ||
195 | break; | ||
196 | case DP_S_DOT: | ||
197 | if (ch == '.') | ||
198 | { | ||
199 | state = DP_S_MAX; | ||
200 | ch = *format++; | ||
201 | } | ||
202 | else | ||
203 | state = DP_S_MOD; | ||
204 | break; | ||
205 | case DP_S_MAX: | ||
206 | if (isdigit((unsigned char)ch)) | ||
207 | { | ||
208 | if (max < 0) | ||
209 | max = 0; | ||
210 | max = 10*max + char_to_int (ch); | ||
211 | ch = *format++; | ||
212 | } | ||
213 | else if (ch == '*') | ||
214 | { | ||
215 | max = va_arg (args, int); | ||
216 | ch = *format++; | ||
217 | state = DP_S_MOD; | ||
218 | } | ||
219 | else | ||
220 | state = DP_S_MOD; | ||
221 | break; | ||
222 | case DP_S_MOD: | ||
223 | /* Currently, we don't support Long Long, bummer */ | ||
224 | switch (ch) | ||
225 | { | ||
226 | case 'h': | ||
227 | cflags = DP_C_SHORT; | ||
228 | ch = *format++; | ||
229 | break; | ||
230 | case 'l': | ||
231 | cflags = DP_C_LONG; | ||
232 | ch = *format++; | ||
233 | break; | ||
234 | case 'L': | ||
235 | cflags = DP_C_LDOUBLE; | ||
236 | ch = *format++; | ||
237 | break; | ||
238 | default: | ||
239 | break; | ||
240 | } | ||
241 | state = DP_S_CONV; | ||
242 | break; | ||
243 | case DP_S_CONV: | ||
244 | switch (ch) | ||
245 | { | ||
246 | case 'd': | ||
247 | case 'i': | ||
248 | if (cflags == DP_C_SHORT) | ||
249 | value = va_arg (args, short int); | ||
250 | else if (cflags == DP_C_LONG) | ||
251 | value = va_arg (args, long int); | ||
252 | else | ||
253 | value = va_arg (args, int); | ||
254 | fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); | ||
255 | break; | ||
256 | case 'o': | ||
257 | flags |= DP_F_UNSIGNED; | ||
258 | if (cflags == DP_C_SHORT) | ||
259 | value = va_arg (args, unsigned short int); | ||
260 | else if (cflags == DP_C_LONG) | ||
261 | value = va_arg (args, unsigned long int); | ||
262 | else | ||
263 | value = va_arg (args, unsigned int); | ||
264 | fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags); | ||
265 | break; | ||
266 | case 'u': | ||
267 | flags |= DP_F_UNSIGNED; | ||
268 | if (cflags == DP_C_SHORT) | ||
269 | value = va_arg (args, unsigned short int); | ||
270 | else if (cflags == DP_C_LONG) | ||
271 | value = va_arg (args, unsigned long int); | ||
272 | else | ||
273 | value = va_arg (args, unsigned int); | ||
274 | fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); | ||
275 | break; | ||
276 | case 'X': | ||
277 | flags |= DP_F_UP; | ||
278 | case 'x': | ||
279 | flags |= DP_F_UNSIGNED; | ||
280 | if (cflags == DP_C_SHORT) | ||
281 | value = va_arg (args, unsigned short int); | ||
282 | else if (cflags == DP_C_LONG) | ||
283 | value = va_arg (args, unsigned long int); | ||
284 | else | ||
285 | value = va_arg (args, unsigned int); | ||
286 | fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags); | ||
287 | break; | ||
288 | case 'f': | ||
289 | if (cflags == DP_C_LDOUBLE) | ||
290 | fvalue = va_arg (args, long double); | ||
291 | else | ||
292 | fvalue = va_arg (args, double); | ||
293 | /* um, floating point? */ | ||
294 | fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); | ||
295 | break; | ||
296 | case 'E': | ||
297 | flags |= DP_F_UP; | ||
298 | case 'e': | ||
299 | if (cflags == DP_C_LDOUBLE) | ||
300 | fvalue = va_arg (args, long double); | ||
301 | else | ||
302 | fvalue = va_arg (args, double); | ||
303 | break; | ||
304 | case 'G': | ||
305 | flags |= DP_F_UP; | ||
306 | case 'g': | ||
307 | if (cflags == DP_C_LDOUBLE) | ||
308 | fvalue = va_arg (args, long double); | ||
309 | else | ||
310 | fvalue = va_arg (args, double); | ||
311 | break; | ||
312 | case 'c': | ||
313 | dopr_outch (buffer, &currlen, maxlen, va_arg (args, int)); | ||
314 | break; | ||
315 | case 's': | ||
316 | strvalue = va_arg (args, char *); | ||
317 | if (max < 0) | ||
318 | max = maxlen; /* ie, no max */ | ||
319 | fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max); | ||
320 | break; | ||
321 | case 'p': | ||
322 | strvalue = va_arg (args, void *); | ||
323 | fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags); | ||
324 | break; | ||
325 | case 'n': | ||
326 | if (cflags == DP_C_SHORT) | ||
327 | { | ||
328 | short int *num; | ||
329 | num = va_arg (args, short int *); | ||
330 | *num = currlen; | ||
331 | } | ||
332 | else if (cflags == DP_C_LONG) | ||
333 | { | ||
334 | long int *num; | ||
335 | num = va_arg (args, long int *); | ||
336 | *num = currlen; | ||
337 | } | ||
338 | else | ||
339 | { | ||
340 | int *num; | ||
341 | num = va_arg (args, int *); | ||
342 | *num = currlen; | ||
343 | } | ||
344 | break; | ||
345 | case '%': | ||
346 | dopr_outch (buffer, &currlen, maxlen, ch); | ||
347 | break; | ||
348 | case 'w': | ||
349 | /* not supported yet, treat as next char */ | ||
350 | ch = *format++; | ||
351 | break; | ||
352 | default: | ||
353 | /* Unknown, skip */ | ||
354 | break; | ||
355 | } | ||
356 | ch = *format++; | ||
357 | state = DP_S_DEFAULT; | ||
358 | flags = cflags = min = 0; | ||
359 | max = -1; | ||
360 | break; | ||
361 | case DP_S_DONE: | ||
362 | break; | ||
363 | default: | ||
364 | /* hmm? */ | ||
365 | break; /* some picky compilers need this */ | ||
366 | } | ||
367 | } | ||
368 | if (currlen < maxlen - 1) | ||
369 | buffer[currlen] = '\0'; | ||
370 | else | ||
371 | buffer[maxlen - 1] = '\0'; | ||
372 | } | ||
373 | |||
374 | static void fmtstr (char *buffer, size_t *currlen, size_t maxlen, | ||
375 | char *value, int flags, int min, int max) | ||
376 | { | ||
377 | int padlen, strln; /* amount to pad */ | ||
378 | int cnt = 0; | ||
379 | |||
380 | if (value == 0) | ||
381 | { | ||
382 | value = "<NULL>"; | ||
383 | } | ||
384 | |||
385 | for (strln = 0; value[strln]; ++strln); /* strlen */ | ||
386 | padlen = min - strln; | ||
387 | if (padlen < 0) | ||
388 | padlen = 0; | ||
389 | if (flags & DP_F_MINUS) | ||
390 | padlen = -padlen; /* Left Justify */ | ||
391 | |||
392 | while ((padlen > 0) && (cnt < max)) | ||
393 | { | ||
394 | dopr_outch (buffer, currlen, maxlen, ' '); | ||
395 | --padlen; | ||
396 | ++cnt; | ||
397 | } | ||
398 | while (*value && (cnt < max)) | ||
399 | { | ||
400 | dopr_outch (buffer, currlen, maxlen, *value++); | ||
401 | ++cnt; | ||
402 | } | ||
403 | while ((padlen < 0) && (cnt < max)) | ||
404 | { | ||
405 | dopr_outch (buffer, currlen, maxlen, ' '); | ||
406 | ++padlen; | ||
407 | ++cnt; | ||
408 | } | ||
409 | } | ||
410 | |||
411 | /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */ | ||
412 | |||
413 | static void fmtint (char *buffer, size_t *currlen, size_t maxlen, | ||
414 | long value, int base, int min, int max, int flags) | ||
415 | { | ||
416 | int signvalue = 0; | ||
417 | unsigned long uvalue; | ||
418 | char convert[20]; | ||
419 | int place = 0; | ||
420 | int spadlen = 0; /* amount to space pad */ | ||
421 | int zpadlen = 0; /* amount to zero pad */ | ||
422 | int caps = 0; | ||
423 | |||
424 | if (max < 0) | ||
425 | max = 0; | ||
426 | |||
427 | uvalue = value; | ||
428 | |||
429 | if(!(flags & DP_F_UNSIGNED)) | ||
430 | { | ||
431 | if( value < 0 ) { | ||
432 | signvalue = '-'; | ||
433 | uvalue = -value; | ||
434 | } | ||
435 | else | ||
436 | if (flags & DP_F_PLUS) /* Do a sign (+/i) */ | ||
437 | signvalue = '+'; | ||
438 | else | ||
439 | if (flags & DP_F_SPACE) | ||
440 | signvalue = ' '; | ||
441 | } | ||
442 | |||
443 | if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ | ||
444 | |||
445 | do { | ||
446 | convert[place++] = | ||
447 | (caps? "0123456789ABCDEF":"0123456789abcdef") | ||
448 | [uvalue % (unsigned)base ]; | ||
449 | uvalue = (uvalue / (unsigned)base ); | ||
450 | } while(uvalue && (place < 20)); | ||
451 | if (place == 20) place--; | ||
452 | convert[place] = 0; | ||
453 | |||
454 | zpadlen = max - place; | ||
455 | spadlen = min - MAX (max, place) - (signvalue ? 1 : 0); | ||
456 | if (zpadlen < 0) zpadlen = 0; | ||
457 | if (spadlen < 0) spadlen = 0; | ||
458 | if (flags & DP_F_ZERO) | ||
459 | { | ||
460 | zpadlen = MAX(zpadlen, spadlen); | ||
461 | spadlen = 0; | ||
462 | } | ||
463 | if (flags & DP_F_MINUS) | ||
464 | spadlen = -spadlen; /* Left Justifty */ | ||
465 | |||
466 | #ifdef DEBUG_SNPRINTF | ||
467 | dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n", | ||
468 | zpadlen, spadlen, min, max, place)); | ||
48 | #endif | 469 | #endif |
49 | 470 | ||
50 | static int pgsize; | 471 | /* Spaces */ |
51 | static char *curobj; | 472 | while (spadlen > 0) |
52 | static int caught; | 473 | { |
53 | static sigjmp_buf bail; | 474 | dopr_outch (buffer, currlen, maxlen, ' '); |
475 | --spadlen; | ||
476 | } | ||
54 | 477 | ||
55 | #define EXTRABYTES 2 /* XXX: why 2? you don't want to know */ | 478 | /* Sign */ |
479 | if (signvalue) | ||
480 | dopr_outch (buffer, currlen, maxlen, signvalue); | ||
56 | 481 | ||
57 | #ifndef HAVE_GETPAGESIZE | 482 | /* Zeros */ |
58 | int | 483 | if (zpadlen > 0) |
59 | getpagesize() | 484 | { |
485 | while (zpadlen > 0) | ||
486 | { | ||
487 | dopr_outch (buffer, currlen, maxlen, '0'); | ||
488 | --zpadlen; | ||
489 | } | ||
490 | } | ||
491 | |||
492 | /* Digits */ | ||
493 | while (place > 0) | ||
494 | dopr_outch (buffer, currlen, maxlen, convert[--place]); | ||
495 | |||
496 | /* Left Justified spaces */ | ||
497 | while (spadlen < 0) { | ||
498 | dopr_outch (buffer, currlen, maxlen, ' '); | ||
499 | ++spadlen; | ||
500 | } | ||
501 | } | ||
502 | |||
503 | static long double abs_val (long double value) | ||
60 | { | 504 | { |
61 | #ifdef EXEC_PAGESIZE | 505 | long double result = value; |
62 | return EXEC_PAGESIZE; | 506 | |
63 | #else /* !EXEC_PAGESIZE */ | 507 | if (value < 0) |
64 | # ifdef NBPG | 508 | result = -value; |
65 | # ifndef CLSIZE | 509 | |
66 | # define CLSIZE 1 | 510 | return result; |
67 | # endif /* No CLSIZE */ | ||
68 | return NBPG * CLSIZE; | ||
69 | # else /* !NBPG */ | ||
70 | return NBPC; | ||
71 | # endif /* NBPG */ | ||
72 | #endif /* EXEC_PAGESIZE */ | ||
73 | } | 511 | } |
74 | #endif /* HAVE_GETPAGESIZE */ | ||
75 | 512 | ||
76 | static char * | 513 | static long double pow10 (int exp) |
77 | msetup(str, n) | ||
78 | char *str; | ||
79 | size_t n; | ||
80 | { | 514 | { |
81 | char *e; | 515 | long double result = 1; |
82 | 516 | ||
83 | if (n == 0) | 517 | while (exp) |
84 | return NULL; | 518 | { |
85 | if (pgsize == 0) | 519 | result *= 10; |
86 | pgsize = getpagesize(); | 520 | exp--; |
87 | curobj = (char *)malloc(n + EXTRABYTES + pgsize * 2); | 521 | } |
88 | if (curobj == NULL) | 522 | |
89 | return NULL; | 523 | return result; |
90 | e = curobj + n + EXTRABYTES; | ||
91 | e = (char *)roundup((unsigned long)e, pgsize); | ||
92 | if (mprotect(e, pgsize, PROT_NONE) == -1) { | ||
93 | free(curobj); | ||
94 | curobj = NULL; | ||
95 | return NULL; | ||
96 | } | ||
97 | e = e - n - EXTRABYTES; | ||
98 | *e = '\0'; | ||
99 | return (e); | ||
100 | } | 524 | } |
101 | 525 | ||
102 | static void | 526 | static long round (long double value) |
103 | mcatch() | ||
104 | { | 527 | { |
105 | siglongjmp(bail, 1); | 528 | long intpart; |
529 | |||
530 | intpart = value; | ||
531 | value = value - intpart; | ||
532 | if (value >= 0.5) | ||
533 | intpart++; | ||
534 | |||
535 | return intpart; | ||
106 | } | 536 | } |
107 | 537 | ||
108 | static void | 538 | static void fmtfp (char *buffer, size_t *currlen, size_t maxlen, |
109 | mcleanup(str, n, p) | 539 | long double fvalue, int min, int max, int flags) |
110 | char *str; | ||
111 | size_t n; | ||
112 | char *p; | ||
113 | { | 540 | { |
114 | strncpy(str, p, n-1); | 541 | int signvalue = 0; |
115 | str[n-1] = '\0'; | 542 | long double ufvalue; |
116 | if (mprotect((caddr_t)(p + n + EXTRABYTES), pgsize, | 543 | char iconvert[20]; |
117 | PROT_READ|PROT_WRITE|PROT_EXEC) == -1) | 544 | char fconvert[20]; |
118 | mprotect((caddr_t)(p + n + EXTRABYTES), pgsize, | 545 | int iplace = 0; |
119 | PROT_READ|PROT_WRITE); | 546 | int fplace = 0; |
120 | free(curobj); | 547 | int padlen = 0; /* amount to pad */ |
548 | int zpadlen = 0; | ||
549 | int caps = 0; | ||
550 | long intpart; | ||
551 | long fracpart; | ||
552 | |||
553 | /* | ||
554 | * AIX manpage says the default is 0, but Solaris says the default | ||
555 | * is 6, and sprintf on AIX defaults to 6 | ||
556 | */ | ||
557 | if (max < 0) | ||
558 | max = 6; | ||
559 | |||
560 | ufvalue = abs_val (fvalue); | ||
561 | |||
562 | if (fvalue < 0) | ||
563 | signvalue = '-'; | ||
564 | else | ||
565 | if (flags & DP_F_PLUS) /* Do a sign (+/i) */ | ||
566 | signvalue = '+'; | ||
567 | else | ||
568 | if (flags & DP_F_SPACE) | ||
569 | signvalue = ' '; | ||
570 | |||
571 | #if 0 | ||
572 | if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ | ||
573 | #endif | ||
574 | |||
575 | intpart = ufvalue; | ||
576 | |||
577 | /* | ||
578 | * Sorry, we only support 9 digits past the decimal because of our | ||
579 | * conversion method | ||
580 | */ | ||
581 | if (max > 9) | ||
582 | max = 9; | ||
583 | |||
584 | /* We "cheat" by converting the fractional part to integer by | ||
585 | * multiplying by a factor of 10 | ||
586 | */ | ||
587 | fracpart = round ((pow10 (max)) * (ufvalue - intpart)); | ||
588 | |||
589 | if (fracpart >= pow10 (max)) | ||
590 | { | ||
591 | intpart++; | ||
592 | fracpart -= pow10 (max); | ||
593 | } | ||
594 | |||
595 | #ifdef DEBUG_SNPRINTF | ||
596 | dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart)); | ||
597 | #endif | ||
598 | |||
599 | /* Convert integer part */ | ||
600 | do { | ||
601 | iconvert[iplace++] = | ||
602 | (caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10]; | ||
603 | intpart = (intpart / 10); | ||
604 | } while(intpart && (iplace < 20)); | ||
605 | if (iplace == 20) iplace--; | ||
606 | iconvert[iplace] = 0; | ||
607 | |||
608 | /* Convert fractional part */ | ||
609 | do { | ||
610 | fconvert[fplace++] = | ||
611 | (caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10]; | ||
612 | fracpart = (fracpart / 10); | ||
613 | } while(fracpart && (fplace < 20)); | ||
614 | if (fplace == 20) fplace--; | ||
615 | fconvert[fplace] = 0; | ||
616 | |||
617 | /* -1 for decimal point, another -1 if we are printing a sign */ | ||
618 | padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); | ||
619 | zpadlen = max - fplace; | ||
620 | if (zpadlen < 0) | ||
621 | zpadlen = 0; | ||
622 | if (padlen < 0) | ||
623 | padlen = 0; | ||
624 | if (flags & DP_F_MINUS) | ||
625 | padlen = -padlen; /* Left Justifty */ | ||
626 | |||
627 | if ((flags & DP_F_ZERO) && (padlen > 0)) | ||
628 | { | ||
629 | if (signvalue) | ||
630 | { | ||
631 | dopr_outch (buffer, currlen, maxlen, signvalue); | ||
632 | --padlen; | ||
633 | signvalue = 0; | ||
634 | } | ||
635 | while (padlen > 0) | ||
636 | { | ||
637 | dopr_outch (buffer, currlen, maxlen, '0'); | ||
638 | --padlen; | ||
639 | } | ||
640 | } | ||
641 | while (padlen > 0) | ||
642 | { | ||
643 | dopr_outch (buffer, currlen, maxlen, ' '); | ||
644 | --padlen; | ||
645 | } | ||
646 | if (signvalue) | ||
647 | dopr_outch (buffer, currlen, maxlen, signvalue); | ||
648 | |||
649 | while (iplace > 0) | ||
650 | dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]); | ||
651 | |||
652 | /* | ||
653 | * Decimal point. This should probably use locale to find the correct | ||
654 | * char to print out. | ||
655 | */ | ||
656 | dopr_outch (buffer, currlen, maxlen, '.'); | ||
657 | |||
658 | while (fplace > 0) | ||
659 | dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]); | ||
660 | |||
661 | while (zpadlen > 0) | ||
662 | { | ||
663 | dopr_outch (buffer, currlen, maxlen, '0'); | ||
664 | --zpadlen; | ||
665 | } | ||
666 | |||
667 | while (padlen < 0) | ||
668 | { | ||
669 | dopr_outch (buffer, currlen, maxlen, ' '); | ||
670 | ++padlen; | ||
671 | } | ||
672 | } | ||
673 | |||
674 | static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c) | ||
675 | { | ||
676 | if (*currlen < maxlen) | ||
677 | buffer[(*currlen)++] = c; | ||
121 | } | 678 | } |
679 | #endif /* !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) */ | ||
122 | 680 | ||
123 | #if !defined(HAVE_VSNPRINTF) || defined(BROKEN_SNPRINTF) | 681 | #ifndef HAVE_VSNPRINTF |
124 | int | 682 | int vsnprintf (char *str, size_t count, const char *fmt, va_list args) |
125 | vsnprintf(str, n, fmt, ap) | ||
126 | char *str; | ||
127 | size_t n; | ||
128 | char *fmt; | ||
129 | va_list ap; | ||
130 | { | 683 | { |
131 | struct sigaction osa, nsa; | 684 | str[0] = 0; |
132 | char *p; | 685 | dopr(str, count, fmt, args); |
133 | int ret = n + 1; /* if we bail, indicated we overflowed */ | 686 | return(strlen(str)); |
134 | |||
135 | memset(&nsa, 0, sizeof nsa); | ||
136 | nsa.sa_handler = mcatch; | ||
137 | sigemptyset(&nsa.sa_mask); | ||
138 | |||
139 | p = msetup(str, n); | ||
140 | if (p == NULL) { | ||
141 | *str = '\0'; | ||
142 | return 0; | ||
143 | } | ||
144 | if (sigsetjmp(bail, 1) == 0) { | ||
145 | if (sigaction(SIGSEGV, &nsa, &osa) == -1) { | ||
146 | mcleanup(str, n, p); | ||
147 | return (0); | ||
148 | } | ||
149 | ret = vsprintf(p, fmt, ap); | ||
150 | } | ||
151 | mcleanup(str, n, p); | ||
152 | (void) sigaction(SIGSEGV, &osa, NULL); | ||
153 | return (ret); | ||
154 | } | 687 | } |
155 | #endif /* !defined(HAVE_VSNPRINTF) || defined(BROKEN_SNPRINTF) */ | 688 | #endif /* !HAVE_VSNPRINTF */ |
156 | 689 | ||
157 | #if !defined(HAVE_SNPRINTF) || defined(BROKEN_SNPRINTF) | 690 | #ifndef HAVE_SNPRINTF |
158 | int | 691 | /* VARARGS3 */ |
159 | #if __STDC__ | 692 | #ifdef HAVE_STDARGS |
160 | snprintf(char *str, size_t n, char const *fmt, ...) | 693 | int snprintf (char *str,size_t count,const char *fmt,...) |
161 | #else | 694 | #else |
162 | snprintf(str, n, fmt, va_alist) | 695 | int snprintf (va_alist) va_dcl |
163 | char *str; | ||
164 | size_t n; | ||
165 | char *fmt; | ||
166 | va_dcl | ||
167 | #endif | 696 | #endif |
168 | { | 697 | { |
169 | va_list ap; | 698 | #ifndef HAVE_STDARGS |
170 | #if __STDC__ | 699 | char *str; |
171 | va_start(ap, fmt); | 700 | size_t count; |
172 | #else | 701 | char *fmt; |
173 | va_start(ap); | ||
174 | #endif | 702 | #endif |
703 | VA_LOCAL_DECL; | ||
704 | |||
705 | VA_START (fmt); | ||
706 | VA_SHIFT (str, char *); | ||
707 | VA_SHIFT (count, size_t ); | ||
708 | VA_SHIFT (fmt, char *); | ||
709 | (void) vsnprintf(str, count, fmt, ap); | ||
710 | VA_END; | ||
711 | return(strlen(str)); | ||
712 | } | ||
713 | |||
714 | #ifdef TEST_SNPRINTF | ||
715 | #ifndef LONG_STRING | ||
716 | #define LONG_STRING 1024 | ||
717 | #endif | ||
718 | int main (void) | ||
719 | { | ||
720 | char buf1[LONG_STRING]; | ||
721 | char buf2[LONG_STRING]; | ||
722 | char *fp_fmt[] = { | ||
723 | "%-1.5f", | ||
724 | "%1.5f", | ||
725 | "%123.9f", | ||
726 | "%10.5f", | ||
727 | "% 10.5f", | ||
728 | "%+22.9f", | ||
729 | "%+4.9f", | ||
730 | "%01.3f", | ||
731 | "%4f", | ||
732 | "%3.1f", | ||
733 | "%3.2f", | ||
734 | NULL | ||
735 | }; | ||
736 | double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996, | ||
737 | 0.9996, 1.996, 4.136, 0}; | ||
738 | char *int_fmt[] = { | ||
739 | "%-1.5d", | ||
740 | "%1.5d", | ||
741 | "%123.9d", | ||
742 | "%5.5d", | ||
743 | "%10.5d", | ||
744 | "% 10.5d", | ||
745 | "%+22.33d", | ||
746 | "%01.3d", | ||
747 | "%4d", | ||
748 | NULL | ||
749 | }; | ||
750 | long int_nums[] = { -1, 134, 91340, 341, 0203, 0}; | ||
751 | int x, y; | ||
752 | int fail = 0; | ||
753 | int num = 0; | ||
754 | |||
755 | printf ("Testing snprintf format codes against system sprintf...\n"); | ||
756 | |||
757 | for (x = 0; fp_fmt[x] != NULL ; x++) | ||
758 | for (y = 0; fp_nums[y] != 0 ; y++) | ||
759 | { | ||
760 | snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]); | ||
761 | sprintf (buf2, fp_fmt[x], fp_nums[y]); | ||
762 | if (strcmp (buf1, buf2)) | ||
763 | { | ||
764 | printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n", | ||
765 | fp_fmt[x], buf1, buf2); | ||
766 | fail++; | ||
767 | } | ||
768 | num++; | ||
769 | } | ||
175 | 770 | ||
176 | return (vsnprintf(str, n, fmt, ap)); | 771 | for (x = 0; int_fmt[x] != NULL ; x++) |
177 | va_end(ap); | 772 | for (y = 0; int_nums[y] != 0 ; y++) |
773 | { | ||
774 | snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]); | ||
775 | sprintf (buf2, int_fmt[x], int_nums[y]); | ||
776 | if (strcmp (buf1, buf2)) | ||
777 | { | ||
778 | printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n", | ||
779 | int_fmt[x], buf1, buf2); | ||
780 | fail++; | ||
781 | } | ||
782 | num++; | ||
783 | } | ||
784 | printf ("%d tests failed out of %d.\n", fail, num); | ||
178 | } | 785 | } |
179 | #endif /* !defined(HAVE_SNPRINTF) || defined(BROKEN_SNPRINTF) */ | 786 | #endif /* SNPRINTF_TEST */ |
180 | 787 | ||
181 | #endif /* defined(BROKEN_SNPRINTF) || !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) */ | 788 | #endif /* !HAVE_SNPRINTF */ |