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