1 /*
2 * Copyright Patrick Powell 1995
3 * This code is based on code written by Patrick Powell (papowell@astart.com)
4 * It may be used for any purpose as long as this notice remains intact
5 * on all source code distributions
6 */
7 /**************************************************************
8 * Original:
9 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
10 * A bombproof version of doprnt (dopr) included.
11 * Sigh. This sort of thing is always nasty do deal with. Note that
12 * the version here does not include floating point...
13 *
14 * snprintf() is used instead of sprintf() as it does limit checks
15 * for string length. This covers a nasty loophole.
16 *
17 * The other functions are there to prevent NULL pointers from
18 * causing nast effects.
19 *
20 * More Recently:
21 * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
22 * This was ugly. It is still ugly. I opted out of floating point
23 * numbers, but the formatter understands just about everything
24 * from the normal C string format, at least as far as I can tell from
25 * the Solaris 2.5 printf(3S) man page.
26 *
27 * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
28 * Ok, added some minimal floating point support, which means this
29 * probably requires libm on most operating systems. Don't yet
30 * support the exponent (e,E) and sigfig (g,G). Also, fmtint()
31 * was pretty badly broken, it just wasn't being exercised in ways
32 * which showed it, so that's been fixed. Also, formated the code
33 * to mutt conventions, and removed dead code left over from the
34 * original. Also, there is now a builtin-test, just compile with:
35 * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
36 * and run snprintf for results.
37 *
38 * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
39 * The PGP code was using unsigned hexadecimal formats.
40 * Unfortunately, unsigned formats simply didn't work.
41 *
42 * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
43 * The original code assumed that both snprintf() and vsnprintf() were
44 * missing. Some systems only have snprintf() but not vsnprintf(), so
45 * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
46 *
47 **************************************************************/
48
49 #include "config.h"
50
51 #include <string.h>
52 #include <ctype.h>
53 #include <sys/types.h>
54 #ifndef _WIN32
55 #include <sys/stat.h>
56 #include <fcntl.h>
57 #endif
58
59 #ifndef FLT_MAX
60 # include <float.h>
61 #endif
62 #include <math.h>
63 #include <stdlib.h>
64
65 #ifdef _WIN32
66 #include <io.h>
67 #include <stdio.h>
68 #include <windows.h>
69 #else
70 #include <unistd.h>
71 #endif
72
73 /* varargs declarations: */
74
75 #if defined(HAVE_STDARG_H)
76 # include <stdarg.h>
77 # define HAVE_STDARGS /* let's hope that works everywhere (mj) */
78 # define VA_LOCAL_DECL va_list ap;
79 # define VA_START(f) va_start(ap, f)
80 # define VA_SHIFT(v,t) ; /* no-op for ANSI */
81 # define VA_END va_end(ap)
82 #else
83 # if defined(HAVE_VARARGS_H)
84 # include <varargs.h>
85 # undef HAVE_STDARGS
86 # define VA_LOCAL_DECL va_list ap;
87 # define VA_START(f) va_start(ap) /* f is ignored! */
88 # define VA_SHIFT(v,t) v = va_arg(ap,t)
89 # define VA_END va_end(ap)
90 # else
91 /*XX ** NO VARARGS ** XX*/
92 # error no either stdarg or varargs needed
93 # endif
94 #endif
95
96 static void dopr (char *buffer, size_t maxlen, const char *format,
97 va_list args);
98 static void fmtstr (char *buffer, size_t *currlen, size_t maxlen,
99 const char *value, int flags, int min, int max);
100 static void fmtint (char *buffer, size_t *currlen, size_t maxlen,
101 long value, int base, int min, int max, int flags);
102 static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
103 long double fvalue, int min, int max, int flags);
104 static void fmtefp (char *buffer, size_t *currlen, size_t maxlen,
105 long double fvalue, int min, int max, int flags);
106 static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c );
107
108 /*
109 * myisdigit(): signalhandlerparanoia isdigit version independend of C-Library
110 */
111
myisdigit(int c)112 static int myisdigit(int c)
113 {
114 switch(c)
115 {
116 case '0':
117 return 1;
118 case '1':
119 return 1;
120 case '2':
121 return 1;
122 case '3':
123 return 1;
124 case '4':
125 return 1;
126 case '5':
127 return 1;
128 case '6':
129 return 1;
130 case '7':
131 return 1;
132 case '8':
133 return 1;
134 case '9':
135 return 1;
136 default:
137 return 0;
138 }
139 }
140
141 /*
142 * mystrlen(): signalhandlerparanoia strlen version independend of C-Library
143 */
144
mystrlen(const char * s)145 size_t mystrlen (const char *s)
146 {
147 int strln;
148 for (strln = 0; s[strln]; ++strln);
149 return strln;
150 }
151
152 /*
153 * mystrcpy(): signalhandlerparanoia strcpy version independend of C-Library
154 */
155
mystrcpy(char * s1,const char * s2)156 char *mystrcpy(char *s1, const char *s2)
157 {
158 int strln;
159 for (strln = 0; s2[strln]; ++strln)
160 s1[strln]=s2[strln];
161 s1[strln]=0;
162 return s1;
163 }
164
165 /*
166 * mystrcat(): signalhandlerparanoia strcpy version independend of C-Library
167 */
168
mystrcat(char * s1,const char * s2)169 char *mystrcat(char *s1, const char *s2)
170 {
171 int strln;
172 int length=mystrlen(s1);
173 for (strln = 0; s2[strln]; ++strln)
174 s1[length+strln]=s2[strln];
175 s1[length+strln]=0;
176 return s1;
177 }
178
179
180 #ifdef HAVE_XOPEN_SOURCE_4_FPCLASSIFY
181 # define _XOPEN_SOURCE 600
182 # define HAVE_FPCLASSIFY 1
183 #endif
184
185 #ifdef HAVE_FPCLASSIFY
186
187 #define MAX_VALUE FLT_MAX
188
handle_nan(int fpclass,long double fvalue)189 static long double handle_nan(int fpclass, long double fvalue)
190 {
191 const char *error = NULL;
192 long double ret = fvalue;
193 switch (fpclass) {
194 case FP_INFINITE:
195 // error = "dune internal error: tried to write +/- INFINITE floating point value !\n";
196 ret = 0;
197 break;
198 case FP_NAN:
199 // error = "dune internal error: tried to write NAN floating point value !\n";
200 case FP_SUBNORMAL:
201 // error = "dune internal error: tried to write SUBNORMAL floating point value !\n";
202 ret = 0;
203 break;
204 case FP_ZERO:
205 case FP_NORMAL:
206 break;
207 // default:
208 // error = "dune internal error: tried to write unclassified floatingpoint !\n";
209 }
210 if (error != NULL)
211 while (write(2, error, strlen(error)) == -1);
212 return ret;
213 }
214
check_nan(long double fvalue)215 static long double check_nan(long double fvalue)
216 {
217 return handle_nan(fpclassify(fvalue),fvalue);
218 }
219
220 /*
221 static long double check_dnan(double fvalue)
222 {
223 return handle_nan(fpclassify(fvalue),fvalue);
224 }
225
226 static long double check_fnan(float fvalue)
227 {
228 return handle_nan(fpclassify(fvalue),fvalue);
229 }
230 */
231
232 #endif
233
234 /*
235 * dopr(): poor man's version of doprintf
236 */
237
238 /* format read states */
239 #define DP_S_DEFAULT 0
240 #define DP_S_FLAGS 1
241 #define DP_S_MIN 2
242 #define DP_S_DOT 3
243 #define DP_S_MAX 4
244 #define DP_S_MOD 5
245 #define DP_S_CONV 6
246 #define DP_S_DONE 7
247
248 /* format flags - Bits */
249 #define DP_F_MINUS (1 << 0)
250 #define DP_F_PLUS (1 << 1)
251 #define DP_F_SPACE (1 << 2)
252 #define DP_F_NUM (1 << 3)
253 #define DP_F_ZERO (1 << 4)
254 #define DP_F_UP (1 << 5)
255 #define DP_F_UNSIGNED (1 << 6)
256
257 /* Conversion Flags */
258 #define DP_C_SHORT 1
259 #define DP_C_LONG 2
260 #define DP_C_LDOUBLE 3
261
262 #define char_to_int(p) (p - '0')
263 #ifndef MAX
264 # define MAX(p,q) ((p >= q) ? p : q)
265 #endif
266
dopr(char * buffer,size_t maxlen,const char * format,va_list args)267 static void dopr (char *buffer, size_t maxlen, const char *format, va_list args)
268 {
269 char ch = 0;
270 long value = 0;
271 long double fvalue = 0;
272 char *strvalue = NULL;
273 int min = 0;
274 int max = -1;
275 int state = 0;
276 int flags = 0;
277 int cflags = 0;
278 size_t currlen = 0;
279
280 state = DP_S_DEFAULT;
281 currlen = flags = cflags = min = 0;
282 ch = *format++;
283
284 while (state != DP_S_DONE)
285 {
286 if ((ch == '\0') || (currlen >= maxlen))
287 state = DP_S_DONE;
288
289 switch(state)
290 {
291 case DP_S_DEFAULT:
292 if (ch == '%')
293 state = DP_S_FLAGS;
294 else
295 dopr_outch (buffer, &currlen, maxlen, ch);
296 ch = *format++;
297 break;
298 case DP_S_FLAGS:
299 switch (ch)
300 {
301 case '-':
302 flags |= DP_F_MINUS;
303 ch = *format++;
304 break;
305 case '+':
306 flags |= DP_F_PLUS;
307 ch = *format++;
308 break;
309 case ' ':
310 flags |= DP_F_SPACE;
311 ch = *format++;
312 break;
313 case '#':
314 flags |= DP_F_NUM;
315 ch = *format++;
316 break;
317 case '0':
318 flags |= DP_F_ZERO;
319 ch = *format++;
320 break;
321 default:
322 state = DP_S_MIN;
323 break;
324 }
325 break;
326 case DP_S_MIN:
327 if (myisdigit((unsigned char)ch))
328 {
329 min = 10 * min + char_to_int (ch);
330 ch = *format++;
331 }
332 else if (ch == '*')
333 {
334 min = va_arg (args, int);
335 ch = *format++;
336 state = DP_S_DOT;
337 }
338 else
339 state = DP_S_DOT;
340 break;
341 case DP_S_DOT:
342 if (ch == '.')
343 {
344 state = DP_S_MAX;
345 ch = *format++;
346 }
347 else
348 state = DP_S_MOD;
349 break;
350 case DP_S_MAX:
351 if (myisdigit((unsigned char)ch))
352 {
353 if (max < 0)
354 max = 0;
355 max = 10 * max + char_to_int (ch);
356 ch = *format++;
357 }
358 else if (ch == '*')
359 {
360 max = va_arg (args, int);
361 ch = *format++;
362 state = DP_S_MOD;
363 }
364 else
365 state = DP_S_MOD;
366 break;
367 case DP_S_MOD:
368 /* Currently, we don't support Long Long, bummer */
369 switch (ch)
370 {
371 case 'h':
372 cflags = DP_C_SHORT;
373 ch = *format++;
374 break;
375 case 'l':
376 cflags = DP_C_LONG;
377 ch = *format++;
378 break;
379 case 'L':
380 cflags = DP_C_LDOUBLE;
381 ch = *format++;
382 break;
383 default:
384 break;
385 }
386 state = DP_S_CONV;
387 break;
388 case DP_S_CONV:
389 switch (ch)
390 {
391 case 'd':
392 case 'i':
393 if (cflags == DP_C_SHORT)
394 value =(short int) va_arg (args, int);
395 else if (cflags == DP_C_LONG)
396 value = va_arg (args, long int);
397 else
398 value = va_arg (args, int);
399 fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
400 break;
401 case 'o':
402 flags |= DP_F_UNSIGNED;
403 if (cflags == DP_C_SHORT)
404 value = (unsigned short int) va_arg (args, int);
405 else if (cflags == DP_C_LONG)
406 value = va_arg (args, unsigned long int);
407 else
408 value = va_arg (args, unsigned int);
409 fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
410 break;
411 case 'u':
412 flags |= DP_F_UNSIGNED;
413 if (cflags == DP_C_SHORT)
414 value = (unsigned short int) va_arg (args, int);
415 else if (cflags == DP_C_LONG)
416 value = va_arg (args, unsigned long int);
417 else
418 value = va_arg (args, unsigned int);
419 fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
420 break;
421 case 'X':
422 flags |= DP_F_UP;
423 case 'x':
424 flags |= DP_F_UNSIGNED;
425 if (cflags == DP_C_SHORT)
426 value = (unsigned short int) va_arg (args, int);
427 else if (cflags == DP_C_LONG)
428 value = va_arg (args, unsigned long int);
429 else
430 value = va_arg (args, unsigned int);
431 fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
432 break;
433 case 'f':
434 if (cflags == DP_C_LDOUBLE)
435 fvalue = va_arg (args, long double);
436 else
437 fvalue = va_arg (args, double);
438 /* um, floating point? */
439 fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
440 break;
441 case 'E':
442 flags |= DP_F_UP;
443 case 'e':
444 if (cflags == DP_C_LDOUBLE)
445 fvalue = va_arg (args, long double);
446 else
447 fvalue = va_arg (args, double);
448 /* um, floating point? */
449 fmtefp (buffer, &currlen, maxlen, fvalue, min, max, flags);
450 break;
451 case 'G':
452 flags |= DP_F_UP;
453 case 'g':
454 if (cflags == DP_C_LDOUBLE)
455 fvalue = va_arg (args, long double);
456 else
457 fvalue = va_arg (args, double);
458 /* um, floating point? */
459 fmtefp (buffer, &currlen, maxlen, fvalue, min, max, flags);
460 break;
461 case 'c':
462 dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
463 break;
464 case 's':
465 strvalue = va_arg (args, char *);
466 if (max < 0)
467 max = maxlen; /* ie, no max */
468 fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
469 break;
470 case 'p':
471 strvalue = va_arg (args, void *);
472 fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
473 break;
474 case 'n':
475 if (cflags == DP_C_SHORT)
476 {
477 short int *num;
478 num = va_arg (args, short int *);
479 *num = currlen;
480 }
481 else if (cflags == DP_C_LONG)
482 {
483 long int *num;
484 num = va_arg (args, long int *);
485 *num = currlen;
486 }
487 else
488 {
489 int *num;
490 num = va_arg (args, int *);
491 *num = currlen;
492 }
493 break;
494 case '%':
495 dopr_outch (buffer, &currlen, maxlen, ch);
496 break;
497 case 'w':
498 /* not supported yet, treat as next char */
499 ch = *format++;
500 break;
501 default:
502 /* Unknown, skip */
503 break;
504 }
505 ch = *format++;
506 state = DP_S_DEFAULT;
507 flags = cflags = min = 0;
508 max = -1;
509 break;
510 case DP_S_DONE:
511 break;
512 default:
513 /* hmm? */
514 break; /* some picky compilers need this */
515 }
516 }
517 if (currlen < maxlen - 1)
518 buffer[currlen] = '\0';
519 else
520 buffer[maxlen - 1] = '\0';
521 }
522
fmtstr(char * buffer,size_t * currlen,size_t maxlen,const char * value,int flags,int min,int max)523 static void fmtstr (char *buffer, size_t *currlen, size_t maxlen,
524 const char *value, int flags, int min, int max)
525 {
526 int padlen, strln; /* amount to pad */
527 int cnt = 0;
528
529 if (value == 0)
530 {
531 value = "<NULL>";
532 }
533
534 for (strln = 0; value[strln]; ++strln); /* strlen */
535 padlen = min - strln;
536 if (padlen < 0)
537 padlen = 0;
538 if (flags & DP_F_MINUS)
539 padlen = -padlen; /* Left Justify */
540
541 while ((padlen > 0) && (cnt < max))
542 {
543 dopr_outch (buffer, currlen, maxlen, ' ');
544 --padlen;
545 ++cnt;
546 }
547 while (*value && (cnt < max))
548 {
549 dopr_outch (buffer, currlen, maxlen, *value++);
550 ++cnt;
551 }
552 while ((padlen < 0) && (cnt < max))
553 {
554 dopr_outch (buffer, currlen, maxlen, ' ');
555 ++padlen;
556 ++cnt;
557 }
558 }
559
560 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
561
fmtint(char * buffer,size_t * currlen,size_t maxlen,long value,int base,int min,int max,int flags)562 static void fmtint (char *buffer, size_t *currlen, size_t maxlen,
563 long value, int base, int min, int max, int flags)
564 {
565 int signvalue = 0;
566 unsigned long uvalue;
567 char convert[20];
568 int place = 0;
569 int spadlen = 0; /* amount to space pad */
570 int zpadlen = 0; /* amount to zero pad */
571 int caps = 0;
572
573 if (max < 0)
574 max = 0;
575
576 uvalue = value;
577
578 if(!(flags & DP_F_UNSIGNED))
579 {
580 if( value < 0 ) {
581 signvalue = '-';
582 uvalue = -value;
583 }
584 else
585 if (flags & DP_F_PLUS) /* Do a sign (+/i) */
586 signvalue = '+';
587 else
588 if (flags & DP_F_SPACE)
589 signvalue = ' ';
590 }
591
592 if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
593
594 do {
595 convert[place++] =
596 (caps ? "0123456789ABCDEF0" : "0123456789abcdef0")
597 [uvalue % (unsigned)base];
598 uvalue = (uvalue / (unsigned)base );
599 } while(uvalue && (place < 20));
600 if (place == 20) place--;
601 convert[place] = 0;
602
603 zpadlen = max - place;
604 spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
605 if (zpadlen < 0) zpadlen = 0;
606 if (spadlen < 0) spadlen = 0;
607 if (flags & DP_F_ZERO)
608 {
609 zpadlen = MAX(zpadlen, spadlen);
610 spadlen = 0;
611 }
612 if (flags & DP_F_MINUS)
613 spadlen = -spadlen; /* Left Justifty */
614
615 #ifdef DEBUG_SNPRINTF
616 dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
617 zpadlen, spadlen, min, max, place));
618 #endif
619
620 /* Spaces */
621 while (spadlen > 0)
622 {
623 dopr_outch (buffer, currlen, maxlen, ' ');
624 --spadlen;
625 }
626
627 /* Sign */
628 if (signvalue)
629 dopr_outch (buffer, currlen, maxlen, signvalue);
630
631 /* Zeros */
632 if (zpadlen > 0)
633 {
634 while (zpadlen > 0)
635 {
636 dopr_outch (buffer, currlen, maxlen, '0');
637 --zpadlen;
638 }
639 }
640
641 /* Digits */
642 while (place > 0)
643 dopr_outch (buffer, currlen, maxlen, convert[--place]);
644
645 /* Left Justified spaces */
646 while (spadlen < 0) {
647 dopr_outch (buffer, currlen, maxlen, ' ');
648 ++spadlen;
649 }
650 }
651
abs_val(long double value)652 static long double abs_val (long double value)
653 {
654 long double result = value;
655
656 if (value < 0)
657 result = -value;
658
659 return result;
660 }
661
mypow10(int exp)662 static long double mypow10 (int exp)
663 {
664 long double result = 1;
665
666 while (exp)
667 {
668 result *= 10;
669 exp--;
670 }
671
672 return result;
673 }
674
675 static int default_max = 6;
676
set_number_of_digits(int digits)677 void set_number_of_digits(int digits)
678 {
679 default_max = digits;
680 }
681
682 static int use_Efloat_format = 1;
683
set_Efloat_format(int flag)684 void set_Efloat_format(int flag)
685 {
686 use_Efloat_format = flag;
687 }
688
fmtfp(char * buffer,size_t * currlen,size_t maxlen,long double value,int min,int max,int flags)689 static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
690 long double value, int min, int max, int flags)
691 {
692 int signvalue = 0;
693 long double ufvalue = 0;
694 char iconvert[20];
695 char fconvert[20];
696 int iplace = 0;
697 int fplace = 0;
698 int padlen = 0; /* amount to pad */
699 int zpadlen = 0;
700 int caps = 0;
701 long intpart = 0;
702 long fracpart = 0;
703 long old_fracpart = 0;
704 int zeros = 0;
705 int i = 0;
706 long double fvalue = 0;
707
708 fvalue = value;
709
710 /*
711 * AIX manpage says the default is 0, but Solaris says the default
712 * is 6, and sprintf on AIX defaults to 6
713 */
714 if (max < 0) {
715 // fprintf(stderr, "setting max to %d\n", default_max);
716 max = default_max;
717 }
718
719 #ifdef HAVE_FPCLASSIFY
720 fvalue = check_nan(fvalue);
721 #endif
722
723 ufvalue = abs_val(fvalue);
724
725 if (fvalue < 0)
726 signvalue = '-';
727 else
728 if (flags & DP_F_PLUS) /* Do a sign (+/i) */
729 signvalue = '+';
730 else
731 if (flags & DP_F_SPACE)
732 signvalue = ' ';
733
734 #if 0
735 if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
736 #endif
737
738 intpart = lrint(ufvalue);
739
740 /*
741 * Sorry, we only support 9 digits past the decimal because of our
742 * conversion method
743 */
744 if (max > 9)
745 max = 9;
746
747 /* We "cheat" by converting the fractional part to integer by
748 * multiplying by a factor of 10
749 */
750
751 fracpart = rint((mypow10(max)) * (ufvalue - intpart));
752
753 if (fracpart>0)
754 {
755 zeros=fabs(floor(log(fracpart / mypow10(max))/log(10))+1);
756 if (zeros>=20)
757 zeros=20;
758 }
759 else
760 {
761 fracpart=0;
762 zeros=0;
763 }
764 old_fracpart=fracpart;
765
766 if (fracpart >= mypow10 (max))
767 {
768 intpart++;
769 fracpart -= mypow10 (max);
770 }
771
772 #ifdef DEBUG_SNPRINTF
773 dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart));
774 #endif
775
776 /* Convert integer part */
777 do {
778 iconvert[iplace++] = intpart < 0 ? '0' :
779 (caps ? "0123456789ABCDEF0" : "0123456789abcdef0")[(intpart % 10)];
780 intpart = (intpart / 10);
781 } while(intpart && (iplace < 20));
782 if (iplace == 20) iplace--;
783 iconvert[iplace] = 0;
784
785 /* Convert fractional part */
786 do {
787 fconvert[fplace++] =
788 (caps ? "0123456789ABCDEF0" : "0123456789abcdef0")[fracpart % 10];
789 fracpart = (fracpart / 10);
790 } while(fracpart && (fplace-zeros < 20));
791 for (i=0;(i<zeros);i++)
792 fconvert[fplace++]='0';
793 if (fplace == 20) fplace--;
794 fconvert[fplace] = 0;
795
796 /* -1 for decimal point, another -1 if we are printing a sign */
797 padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
798 zpadlen = max - fplace;
799 if (zpadlen < 0)
800 zpadlen = 0;
801 if (padlen < 0)
802 padlen = 0;
803 if (flags & DP_F_MINUS)
804 padlen = -padlen; /* Left Justifty */
805
806 if ((flags & DP_F_ZERO) && (padlen > 0))
807 {
808 if (signvalue)
809 {
810 dopr_outch (buffer, currlen, maxlen, signvalue);
811 --padlen;
812 signvalue = 0;
813 }
814 while (padlen > 0)
815 {
816 dopr_outch (buffer, currlen, maxlen, '0');
817 --padlen;
818 }
819 }
820 while (padlen > 0)
821 {
822 dopr_outch (buffer, currlen, maxlen, ' ');
823 --padlen;
824 }
825 if (signvalue)
826 dopr_outch (buffer, currlen, maxlen, signvalue);
827
828 while (iplace > 0)
829 dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
830
831 if (old_fracpart!=0) {
832 /*
833 * Decimal point. This should probably use locale to find the correct
834 * char to print out.
835 */
836 dopr_outch (buffer, currlen, maxlen, '.');
837
838 while (fplace > 0)
839 dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
840
841 while (zpadlen > 0)
842 {
843 dopr_outch (buffer, currlen, maxlen, '0');
844 --zpadlen;
845 }
846
847 while (padlen < 0)
848 {
849 dopr_outch (buffer, currlen, maxlen, ' ');
850 ++padlen;
851 }
852 }
853 }
854
855
dopr_outch(char * buffer,size_t * currlen,size_t maxlen,char c)856 static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c)
857 {
858 if (*currlen < maxlen)
859 buffer[(*currlen)++] = c;
860 }
861
myvsnprintf(char * str,size_t count,const char * fmt,va_list args)862 int myvsnprintf (char *str, size_t count, const char *fmt, va_list args)
863 {
864 str[0] = 0;
865 dopr(str, count, fmt, args);
866 return(mystrlen(str));
867 }
868
869 /* VARARGS3 */
870 #ifdef HAVE_STDARGS
mysnprintf(char * str,size_t count,const char * fmt,...)871 int mysnprintf (char *str,size_t count,const char *fmt,...)
872 #else
873 int mysnprintf (va_alist) va_dcl
874 #endif
875 {
876 #ifndef HAVE_STDARGS
877 char *str = NULL;
878 size_t count = 0;
879 char *fmt = NULL;
880 #endif
881 VA_LOCAL_DECL;
882
883 VA_START (fmt);
884 VA_SHIFT (str, char *);
885 VA_SHIFT (count, size_t );
886 VA_SHIFT (fmt, char *);
887 (void) myvsnprintf(str, count, fmt, ap);
888 VA_END;
889 return(mystrlen(str));
890 }
891
892 #ifndef HAVE_POWL
mypowl(long double ten,int exp)893 long double mypowl(long double ten,int exp)
894 {
895 // poor mans powl, only useful if second argument is a integer
896 int i;
897 long double result = 1;
898 for (i = 0; i < exp; i++)
899 result *= ten;
900 for (i = 0; i > exp; i--)
901 result /= ten;
902 return result;
903 }
904 #endif
905
906 #ifdef _WIN32
907 /* VARARGS3 */
908 #ifdef HAVE_STDARGS
msgboxprintf(FILE * f,const char * fmt,...)909 int __cdecl msgboxprintf (FILE * f,const char *fmt,...)
910 #else
911 int __cdecl msgboxprintf (va_alist) va_dcl
912 #endif
913 {
914 #ifndef HAVE_STDARGS
915 FILE* f;
916 char *fmt;
917 #endif
918 char text[1024];
919 VA_LOCAL_DECL;
920
921 VA_START (fmt);
922 VA_SHIFT (text, char *);
923 VA_SHIFT (1024, size_t );
924 VA_SHIFT (fmt, char *);
925 (void) myvsnprintf(text, 1024, fmt, ap);
926 VA_END;
927 MessageBox(NULL,text,"white_dune",MB_ICONINFORMATION);
928 return(mystrlen(text));
929 }
930 #endif
931
fmtefp(char * buffer,size_t * currlen,size_t maxlen,long double evalue,int min,int max,int flags)932 static void fmtefp (char *buffer, size_t *currlen, size_t maxlen,
933 long double evalue, int min, int max, int flags)
934 {
935 long double fvalue = 0;
936 int exp = 0;
937
938 #ifdef HAVE_FPCLASSIFY
939 evalue = check_nan(evalue);
940 #endif
941
942 if (evalue==0)
943 dopr_outch(buffer, currlen, maxlen, '0');
944 else
945 {
946 exp=floor(log(fabs(evalue))/log(10.0));
947 if (use_Efloat_format && ((exp>2) || (exp<-1)))
948 {
949 #ifdef HAVE_POWL
950 fvalue=evalue*powl(10.0,-(long double)exp);
951 #else
952 fvalue=evalue*mypowl(10.0,-exp);
953 #endif
954 fmtfp (buffer, currlen, maxlen, fvalue, min, max, flags);
955 dopr_outch(buffer, currlen, maxlen, 'e');
956 fmtint (buffer,currlen, maxlen,exp,10,min,max,flags);
957 }
958 else
959 fmtfp (buffer, currlen, maxlen, evalue, min, max, flags);
960 }
961
962 }
963
964 # define MY_MAX_BUFFER_SIZE 65536
965
966 static char buffer[MY_MAX_BUFFER_SIZE];
967 static int buffersize = 0;
968 static int current_filedes = -1;
969
mywrite(int filedes,const char * str,int size)970 int mywrite(int filedes,const char *str,int size)
971 {
972 int restlength=size;
973 int position_written=0;
974
975 while (restlength>0)
976 {
977 int writereturn;
978
979 if ( (writereturn = write(filedes,str+position_written,restlength)) < 0 )
980 return(-1);
981 restlength=restlength-writereturn;
982 position_written=position_written+writereturn;
983 }
984 return(0);
985 }
986
myflushbuffer(void)987 static int myflushbuffer(void)
988 {
989 int ret = 0;
990 if (buffersize > 0)
991 ret=mywrite(current_filedes,buffer,buffersize);
992 buffersize=0;
993 current_filedes=-1;
994 return ret;
995 }
996
myflushall(void)997 int myflushall(void)
998 {
999 return myflushbuffer();
1000 }
1001
myclose(int filedes)1002 int myclose(int filedes)
1003 {
1004 int error=0;
1005 if (myflushbuffer() < 0) {
1006 error=-1;
1007 }
1008 buffersize=0;
1009 current_filedes=-1;
1010 close(filedes);
1011 return error;
1012 }
1013
1014 #ifdef DEBUG
1015 /* write unbuffered */
1016
mywritestr(int filedes,const char * str)1017 int mywritestr(int filedes,const char *str)
1018 {
1019 if (mywrite(filedes,str,strlen(str)) < 0)
1020 return -1;
1021 return 0;
1022 }
1023 #else
1024 /* write via a buffer to improve performance */
1025
mywritestr(int filedes,const char * str)1026 int mywritestr(int filedes,const char *str)
1027 {
1028 int len=strlen(str);
1029 if (len==0)
1030 return 0;
1031 if (len > MY_MAX_BUFFER_SIZE)
1032 {
1033 if (myflushbuffer() < 0)
1034 return -1;
1035 if (mywrite(filedes,str,len) < 0)
1036 return -1;
1037 return 0;
1038 }
1039 if (((current_filedes != -1) && (current_filedes != filedes)) ||
1040 ((buffersize+len) >= MY_MAX_BUFFER_SIZE))
1041 {
1042 if (myflushbuffer() < 0)
1043 return -1;
1044 }
1045 current_filedes = filedes;
1046 memcpy(buffer+buffersize, str, len);
1047 buffersize+=len;
1048 return 0;
1049 }
1050 #endif
1051
1052
1053 /* VARARGS3 */
1054 #ifdef HAVE_STDARGS
mywritef(int filedes,const char * fmt,...)1055 int mywritef (int filedes,const char *fmt,...)
1056 #else
1057 int mywritef (va_alist) va_dcl
1058 #endif
1059 {
1060 char str[8000];
1061 #ifndef HAVE_STDARGS
1062 int filedes;
1063 char *fmt;
1064 #endif
1065 VA_LOCAL_DECL;
1066
1067 VA_START (fmt);
1068 VA_SHIFT (filedes, int);
1069 VA_SHIFT (fmt, char *);
1070 (void) myvsnprintf(str, 8000, fmt, ap);
1071 VA_END;
1072 /* error, myvsnprintf possibly truncated data */
1073 if (mystrlen(str)>=7999)
1074 return(-2);
1075 return mywritestr(filedes,str);
1076 }
1077
1078 /*
1079 * strncpy that copies n bytes and can write zero to n+1-th byte
1080 * use this when you want to copy exact n bytes (or less)
1081 */
mystrncpy_danger(char * str1,const char * str2,int n)1082 char* mystrncpy_danger(char* str1,const char *str2,int n)
1083 {
1084 char* ret;
1085 ret = strncpy(str1,str2,n);
1086 ret[n] = 0;
1087 return ret;
1088 }
1089
1090 /*
1091 * strncpy that copies n bytes and then write zero to n-th byte
1092 * use this when you want to copy less than n bytes
1093 */
mystrncpy_secure(char * str1,const char * str2,int n)1094 char* mystrncpy_secure(char* str1,const char *str2,int n)
1095 {
1096 char* ret;
1097 ret = strncpy(str1,str2,n);
1098 ret[n-1] = 0;
1099 return ret;
1100 }
1101
1102 /*
1103 * special version of strdup, that explicitly calls malloc
1104 * this is needed to allow ld -r linking of the efence library
1105 */
mystrdup(const char * s)1106 char *mystrdup(const char *s)
1107 {
1108 char *ret = NULL;
1109 if (s == NULL) {
1110 ret = malloc(1);
1111 ret[0] = 0;
1112 return ret;
1113 }
1114 ret = malloc(strlen(s) + 1);
1115 strcpy(ret, s);
1116 return ret;
1117 }
1118
1119 #ifdef TEST_SNPRINTF
1120 #ifndef LONG_STRING
1121 #define LONG_STRING 1024
1122 #endif
1123 #include <stdio.h>
main(void)1124 int main (void)
1125 {
1126 char buf1[LONG_STRING];
1127 char buf2[LONG_STRING];
1128 char *fp_fmt[] = {
1129 "%-1.5f",
1130 "%g",
1131 "%f",
1132 "%-1.5f",
1133 "%1.5f",
1134 "%123.9f",
1135 "%10.5f",
1136 "% 10.5f",
1137 "%+22.9f",
1138 "%+4.9f",
1139 "%01.3f",
1140 "%4f",
1141 "%3.1f",
1142 "%3.2f",
1143 NULL
1144 };
1145 float test_fp_nums[] = { -1, 1, 0.5, 0102.1, 1.0032, 203.9,-1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996,
1146 0.9996, 1.996, 1.003, 4.136, 3e-2, 0};
1147 char *int_fmt[] = {
1148 "%-1.5d",
1149 "%1.5d",
1150 "%123.9d",
1151 "%5.5d",
1152 "%10.5d",
1153 "% 10.5d",
1154 "%+22.33d",
1155 "%01.3d",
1156 "%4d",
1157 NULL
1158 };
1159 long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
1160 int x, y;
1161 int fail = 0;
1162 int num = 0;
1163
1164 printf ("Testing snprintf format codes against system sprintf...\n");
1165
1166 for (x = 0; fp_fmt[x] != NULL ; x++)
1167 {
1168 for (y = 0; test_fp_nums[y] != 0 ; y++)
1169 {
1170 mysnprintf (buf1, sizeof (buf1), fp_fmt[x], test_fp_nums[y]);
1171 sprintf (buf2, fp_fmt[x], test_fp_nums[y]);
1172 if (strcmp (buf1, buf2))
1173 {
1174 printf("snprintf %lf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n",
1175 test_fp_nums[y],fp_fmt[x], buf1, buf2);
1176 fail++;
1177 }
1178 }
1179 mysnprintf (buf1, sizeof (buf1), fp_fmt[x],0);
1180 sprintf (buf2, fp_fmt[x], 0);
1181 if (strcmp (buf1, buf2))
1182 {
1183 printf("snprintf %lf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n",
1184 test_fp_nums[y],fp_fmt[x], buf1, buf2);
1185 fail++;
1186 }
1187 num++;
1188 }
1189
1190 for (x = 0; int_fmt[x] != NULL ; x++)
1191 for (y = 0; int_nums[y] != 0 ; y++)
1192 {
1193 mysnprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]);
1194 sprintf (buf2, int_fmt[x], int_nums[y]);
1195 if (strcmp (buf1, buf2))
1196 {
1197 printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n",
1198 int_fmt[x], buf1, buf2);
1199 fail++;
1200 }
1201 num++;
1202 }
1203 printf ("%d tests failed out of %d.\n", fail, num);
1204 }
1205 #endif /* SNPRINTF_TEST */
1206
1207