1 /*
2  * snprintf.c - a portable implementation of snprintf and vsnprintf
3  *
4  * $Id: snprintf.c,v 1.20 (0.9) 2004/02/06 23:34:02 [Xp-AvR] Exp $
5  */
6 
7 #include "main.h"
8 #include "snprintf.h"
9 
10 #include <string.h>
11 #include <ctype.h>
12 #include <sys/types.h>
13 
14 #ifndef HAVE_VSNPRINTF
15 
16 #if defined(__STDC__)
17 #  ifdef HAVE_STDARG_H
18 #    include <stdarg.h>
19 #  else
20 #    ifdef HAVE_STD_ARGS_H
21 #      include <std_args.h>
22 #    endif
23 #  endif
24 #  define HAVE_STDARGS
25 #  define VA_LOCAL_DECL va_list ap
26 #  define VA_START(f)   va_start(ap, f)
27 #  define VA_SHIFT(v,t) ;
28 #  define VA_END        va_end(ap)
29 #else
30 #  include <varargs.h>
31 #  undef HAVE_STDARGS
32 #  define VA_LOCAL_DECL va_list ap
33 #  define VA_START(f)   va_start(ap)
34 #  define VA_SHIFT(v,t) v = va_arg(ap,t)
35 #  define VA_END        va_end(ap)
36 #endif
37 
38 #ifdef HAVE_LONG_DOUBLE
39 #define LDOUBLE long double
40 #else
41 #define LDOUBLE double
42 #endif
43 
44 static void dopr(char *buffer, size_t maxlen, const char *format, va_list args);
45 static void fmtstr(char *buffer, size_t *currlen, size_t maxlen, char *value,
46                    int flags, int min, int max);
47 static void fmtint(char *buffer, size_t *currlen, size_t maxlen, long value,
48                    int base, int min, int max, int flags);
49 static void fmtfp(char *buffer, size_t *currlen, size_t maxlen, LDOUBLE fvalue,
50                   int min, int max, int flags);
51 static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c);
52 
53 /* format read states */
54 #define DP_S_DEFAULT 0
55 #define DP_S_FLAGS   1
56 #define DP_S_MIN     2
57 #define DP_S_DOT     3
58 #define DP_S_MAX     4
59 #define DP_S_MOD     5
60 #define DP_S_CONV    6
61 #define DP_S_DONE    7
62 
63 /* format flags - Bits */
64 #define DP_F_MINUS      (1 << 0)
65 #define DP_F_PLUS       (1 << 1)
66 #define DP_F_SPACE      (1 << 2)
67 #define DP_F_NUM        (1 << 3)
68 #define DP_F_ZERO       (1 << 4)
69 #define DP_F_UP         (1 << 5)
70 #define DP_F_UNSIGNED   (1 << 6)
71 
72 /* Conversion Flags */
73 #define DP_C_SHORT   1
74 #define DP_C_LONG    2
75 #define DP_C_LDOUBLE 3
76 
77 #define char_to_int(p) (p - '0')
78 
79 #ifdef MAX
80 #  undef MAX
81 #endif
82 #define MAX(p,q) ((p >= q) ? p : q)
83 
dopr(char * buffer,size_t maxlen,const char * format,va_list args)84 static void dopr(char *buffer, size_t maxlen, const char *format, va_list args)
85 {
86   char ch;
87   long value;
88   LDOUBLE fvalue;
89   char *strvalue;
90   int min;
91   int max;
92   int state;
93   int flags;
94   int cflags;
95   size_t currlen;
96 
97   state = DP_S_DEFAULT;
98   currlen = flags = cflags = min = 0;
99   max = -1;
100   ch = *format++;
101 
102   while (state != DP_S_DONE) {
103     if ((ch == '\0') || (currlen >= maxlen))
104       state = DP_S_DONE;
105 
106     switch (state) {
107     case DP_S_DEFAULT:
108       if (ch == '%')
109         state = DP_S_FLAGS;
110       else
111         dopr_outch(buffer, &currlen, maxlen, ch);
112       ch = *format++;
113       break;
114     case DP_S_FLAGS:
115       switch (ch) {
116       case '-':
117         flags |= DP_F_MINUS;
118         ch = *format++;
119         break;
120       case '+':
121         flags |= DP_F_PLUS;
122         ch = *format++;
123         break;
124       case ' ':
125         flags |= DP_F_SPACE;
126         ch = *format++;
127         break;
128       case '#':
129         flags |= DP_F_NUM;
130         ch = *format++;
131         break;
132       case '0':
133         flags |= DP_F_ZERO;
134         ch = *format++;
135         break;
136       default:
137         state = DP_S_MIN;
138         break;
139       }
140       break;
141     case DP_S_MIN:
142       if (EvangelineIsdigit(ch)) {
143         min = 10 * min + char_to_int(ch);
144         ch = *format++;
145       } else if (ch == '*') {
146         min = va_arg(args, int);
147 
148         ch = *format++;
149         state = DP_S_DOT;
150       } else
151         state = DP_S_DOT;
152       break;
153     case DP_S_DOT:
154       if (ch == '.') {
155         state = DP_S_MAX;
156         ch = *format++;
157       } else
158         state = DP_S_MOD;
159       break;
160     case DP_S_MAX:
161       if (EvangelineIsdigit(ch)) {
162         if (max < 0)
163           max = 0;
164         max = 10 * max + char_to_int(ch);
165         ch = *format++;
166       } else if (ch == '*') {
167         max = va_arg(args, int);
168 
169         ch = *format++;
170         state = DP_S_MOD;
171       } else
172         state = DP_S_MOD;
173       break;
174     case DP_S_MOD:
175       switch (ch) {
176       case 'h':
177         cflags = DP_C_SHORT;
178         ch = *format++;
179         break;
180       case 'l':
181         cflags = DP_C_LONG;
182         ch = *format++;
183         break;
184       case 'L':
185         cflags = DP_C_LDOUBLE;
186         ch = *format++;
187         break;
188       default:
189         break;
190       }
191       state = DP_S_CONV;
192       break;
193     case DP_S_CONV:
194       switch (ch) {
195       case 'd':
196       case 'i':
197         if (cflags == DP_C_SHORT)
198           value = va_arg(args, short int);
199 
200         else if (cflags == DP_C_LONG)
201           value = va_arg(args, long int);
202 
203         else
204           value = va_arg(args, int);
205 
206         fmtint(buffer, &currlen, maxlen, value, 10, min, max, flags);
207         break;
208       case 'o':
209         flags |= DP_F_UNSIGNED;
210         if (cflags == DP_C_SHORT)
211           value = va_arg(args, unsigned short int);
212 
213         else if (cflags == DP_C_LONG)
214           value = va_arg(args, unsigned long int);
215 
216         else
217           value = va_arg(args, unsigned int);
218 
219         fmtint(buffer, &currlen, maxlen, value, 8, min, max, flags);
220         break;
221       case 'u':
222         flags |= DP_F_UNSIGNED;
223         if (cflags == DP_C_SHORT)
224           value = va_arg(args, unsigned short int);
225 
226         else if (cflags == DP_C_LONG)
227           value = va_arg(args, unsigned long int);
228 
229         else
230           value = va_arg(args, unsigned int);
231 
232         fmtint(buffer, &currlen, maxlen, value, 10, min, max, flags);
233         break;
234       case 'X':
235         flags |= DP_F_UP;
236       case 'x':
237         flags |= DP_F_UNSIGNED;
238         if (cflags == DP_C_SHORT)
239           value = va_arg(args, unsigned short int);
240 
241         else if (cflags == DP_C_LONG)
242           value = va_arg(args, unsigned long int);
243 
244         else
245           value = va_arg(args, unsigned int);
246 
247         fmtint(buffer, &currlen, maxlen, value, 16, min, max, flags);
248         break;
249       case 'f':
250         if (cflags == DP_C_LDOUBLE)
251           fvalue = va_arg(args, LDOUBLE);
252         else
253           fvalue = va_arg(args, double);
254 
255         fmtfp(buffer, &currlen, maxlen, fvalue, min, max, flags);
256         break;
257       case 'E':
258         flags |= DP_F_UP;
259       case 'e':
260         if (cflags == DP_C_LDOUBLE)
261           fvalue = va_arg(args, LDOUBLE);
262         else
263           fvalue = va_arg(args, double);
264 
265         break;
266       case 'G':
267         flags |= DP_F_UP;
268       case 'g':
269         if (cflags == DP_C_LDOUBLE)
270           fvalue = va_arg(args, LDOUBLE);
271         else
272           fvalue = va_arg(args, double);
273 
274         break;
275       case 'c':
276         dopr_outch(buffer, &currlen, maxlen, va_arg(args, int));
277 
278         break;
279       case 's':
280         strvalue = va_arg(args, char *);
281 
282         if (max < 0)
283           max = maxlen;
284         fmtstr(buffer, &currlen, maxlen, strvalue, flags, min, max);
285         break;
286       case 'p':
287         strvalue = va_arg(args, void *);
288 
289         fmtint(buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
290         break;
291       case 'n':
292         if (cflags == DP_C_SHORT) {
293           short int *num;
294           num = va_arg(args, short int *);
295 
296           *num = currlen;
297         } else if (cflags == DP_C_LONG) {
298           long int *num;
299           num = va_arg(args, long int *);
300 
301           *num = currlen;
302         } else {
303           int *num;
304           num = va_arg(args, int *);
305 
306           *num = currlen;
307         }
308         break;
309       case '%':
310         dopr_outch(buffer, &currlen, maxlen, ch);
311         break;
312       case 'w':
313         ch = *format++;
314         break;
315       default:
316         break;
317       }
318       ch = *format++;
319       state = DP_S_DEFAULT;
320       flags = cflags = min = 0;
321       max = -1;
322       break;
323     case DP_S_DONE:
324       break;
325     default:
326       break;
327     }
328   }
329   if (currlen < maxlen - 1)
330     buffer[currlen] = '\0';
331   else
332     buffer[maxlen - 1] = '\0';
333 }
334 
fmtstr(char * buffer,size_t * currlen,size_t maxlen,char * value,int flags,int min,int max)335 static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
336                    char *value, int flags, int min, int max)
337 {
338   int padlen, strln;
339   int cnt = 0;
340 
341   if (value == 0) {
342     value = "<NULL>";
343   }
344 
345   for (strln = 0; value[strln]; ++strln);
346   padlen = min - strln;
347   if (padlen < 0)
348     padlen = 0;
349   if (flags & DP_F_MINUS)
350     padlen = -padlen;
351 
352   while ((padlen > 0) && (cnt < max)) {
353     dopr_outch(buffer, currlen, maxlen, ' ');
354     --padlen;
355     ++cnt;
356   }
357   while (*value && (cnt < max)) {
358     dopr_outch(buffer, currlen, maxlen, *value++);
359     ++cnt;
360   }
361   while ((padlen < 0) && (cnt < max)) {
362     dopr_outch(buffer, currlen, maxlen, ' ');
363     ++padlen;
364     ++cnt;
365   }
366 }
367 
fmtint(char * buffer,size_t * currlen,size_t maxlen,long value,int base,int min,int max,int flags)368 static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
369                    long value, int base, int min, int max, int flags)
370 {
371   int signvalue = 0;
372   unsigned long uvalue;
373   char convert[20];
374   int place = 0;
375   int spadlen = 0;
376   int zpadlen = 0;
377   int caps = 0;
378 
379   if (max < 0)
380     max = 0;
381 
382   uvalue = value;
383 
384   if (!(flags & DP_F_UNSIGNED)) {
385     if (value < 0) {
386       signvalue = '-';
387       uvalue = -value;
388     } else if (flags & DP_F_PLUS)
389       signvalue = '+';
390     else if (flags & DP_F_SPACE)
391       signvalue = ' ';
392   }
393 
394   if (flags & DP_F_UP)
395     caps = 1;
396 
397   do {
398     convert[place++] = (caps ? "0123456789ABCDEF" : "0123456789abcdef")
399       [uvalue % (unsigned) base];
400     uvalue = (uvalue / (unsigned) base);
401   }
402   while (uvalue && (place < 20));
403   if (place == 20)
404     place--;
405   convert[place] = 0;
406 
407   zpadlen = max - place;
408   spadlen = min - MAX(max, place) - (signvalue ? 1 : 0);
409   if (zpadlen < 0)
410     zpadlen = 0;
411   if (spadlen < 0)
412     spadlen = 0;
413   if (flags & DP_F_ZERO) {
414     zpadlen = MAX(zpadlen, spadlen);
415     spadlen = 0;
416   }
417   if (flags & DP_F_MINUS)
418     spadlen = -spadlen;
419 
420 #ifdef DEBUG_SNPRINTF
421   dprint(1,
422          (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
423           zpadlen, spadlen, min, max, place));
424 #endif
425 
426   while (spadlen > 0) {
427     dopr_outch(buffer, currlen, maxlen, ' ');
428     --spadlen;
429   }
430 
431   if (signvalue)
432     dopr_outch(buffer, currlen, maxlen, signvalue);
433 
434   if (zpadlen > 0) {
435     while (zpadlen > 0) {
436       dopr_outch(buffer, currlen, maxlen, '0');
437       --zpadlen;
438     }
439   }
440 
441   while (place > 0)
442     dopr_outch(buffer, currlen, maxlen, convert[--place]);
443 
444   while (spadlen < 0) {
445     dopr_outch(buffer, currlen, maxlen, ' ');
446     ++spadlen;
447   }
448 }
449 
abs_val(LDOUBLE value)450 static LDOUBLE abs_val(LDOUBLE value)
451 {
452   LDOUBLE result = value;
453 
454   if (value < 0)
455     result = -value;
456 
457   return result;
458 }
459 
pow10(int exp)460 static LDOUBLE pow10(int exp)
461 {
462   LDOUBLE result = 1;
463 
464   while (exp) {
465     result *= 10;
466     exp--;
467   }
468 
469   return result;
470 }
471 
round(LDOUBLE value)472 static long round(LDOUBLE value)
473 {
474   long intpart;
475 
476   intpart = value;
477   value = value - intpart;
478   if (value >= 0.5)
479     intpart++;
480 
481   return intpart;
482 }
483 
fmtfp(char * buffer,size_t * currlen,size_t maxlen,LDOUBLE fvalue,int min,int max,int flags)484 static void fmtfp(char *buffer, size_t *currlen, size_t maxlen,
485                   LDOUBLE fvalue, int min, int max, int flags)
486 {
487   int signvalue = 0;
488   LDOUBLE ufvalue;
489   char iconvert[20];
490   char fconvert[20];
491   int iplace = 0;
492   int fplace = 0;
493   int padlen = 0;
494   int zpadlen = 0;
495   int caps = 0;
496   long intpart;
497   long fracpart;
498 
499   if (max < 0)
500     max = 6;
501 
502   ufvalue = abs_val(fvalue);
503 
504   if (fvalue < 0)
505     signvalue = '-';
506   else if (flags & DP_F_PLUS)
507     signvalue = '+';
508   else if (flags & DP_F_SPACE)
509     signvalue = ' ';
510 
511 #if 0
512   if (flags & DP_F_UP)
513     caps = 1;
514 #endif
515 
516   intpart = ufvalue;
517 
518   if (max > 9)
519     max = 9;
520 
521   fracpart = round((pow10(max)) * (ufvalue - intpart));
522 
523   if (fracpart >= pow10(max)) {
524     intpart++;
525     fracpart -= pow10(max);
526   }
527 
528   do {
529     iconvert[iplace++] =
530       (caps ? "0123456789ABCDEF" : "0123456789abcdef")[intpart % 10];
531     intpart = (intpart / 10);
532   }
533   while (intpart && (iplace < 20));
534   if (iplace == 20)
535     iplace--;
536   iconvert[iplace] = 0;
537 
538   do {
539     fconvert[fplace++] =
540       (caps ? "0123456789ABCDEF" : "0123456789abcdef")[fracpart % 10];
541     fracpart = (fracpart / 10);
542   }
543   while (fracpart && (fplace < 20));
544   if (fplace == 20)
545     fplace--;
546   fconvert[fplace] = 0;
547 
548   padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
549   zpadlen = max - fplace;
550   if (zpadlen < 0)
551     zpadlen = 0;
552   if (padlen < 0)
553     padlen = 0;
554   if (flags & DP_F_MINUS)
555     padlen = -padlen;
556 
557   if ((flags & DP_F_ZERO) && (padlen > 0)) {
558     if (signvalue) {
559       dopr_outch(buffer, currlen, maxlen, signvalue);
560       --padlen;
561       signvalue = 0;
562     }
563     while (padlen > 0) {
564       dopr_outch(buffer, currlen, maxlen, '0');
565       --padlen;
566     }
567   }
568   while (padlen > 0) {
569     dopr_outch(buffer, currlen, maxlen, ' ');
570     --padlen;
571   }
572   if (signvalue)
573     dopr_outch(buffer, currlen, maxlen, signvalue);
574 
575   while (iplace > 0)
576     dopr_outch(buffer, currlen, maxlen, iconvert[--iplace]);
577 
578   if (max > 0) {
579     dopr_outch(buffer, currlen, maxlen, '.');
580 
581     while (fplace > 0)
582       dopr_outch(buffer, currlen, maxlen, fconvert[--fplace]);
583   }
584 
585   while (zpadlen > 0) {
586     dopr_outch(buffer, currlen, maxlen, '0');
587     --zpadlen;
588   }
589 
590   while (padlen < 0) {
591     dopr_outch(buffer, currlen, maxlen, ' ');
592     ++padlen;
593   }
594 }
595 
dopr_outch(char * buffer,size_t * currlen,size_t maxlen,char c)596 static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c)
597 {
598   if (*currlen < maxlen)
599     buffer[(*currlen)++] = c;
600 }
601 
EvangelineVsnprintf(char * str,size_t count,const char * fmt,va_list args)602 int EvangelineVsnprintf(char *str, size_t count, const char *fmt, va_list args)
603 {
604   str[0] = 0;
605   dopr(str, count, fmt, args);
606   return (strlen(str));
607 }
608 #endif /* !HAVE_VSNPRINTF */
609 
610 #ifndef HAVE_SNPRINTF
611 #  ifdef HAVE_STDARGS
Evangelinesnprintf(char * str,size_t count,const char * fmt,...)612 int Evangelinesnprintf(char *str, size_t count, const char *fmt, ...)
613 #  else
614 int EvangelineSnprintf(va_alist)
615 va_dcl
616 #  endif
617 {
618 #  ifndef HAVE_STDARGS
619   char *str;
620   size_t count;
621   char *fmt;
622 #  endif
623   VA_LOCAL_DECL;
624 
625   VA_START(fmt);
626   VA_SHIFT(str, char *);
627 
628   VA_SHIFT(count, size_t);
629   VA_SHIFT(fmt, char *);
630 
631   (void) EvangelineVsnprintf(str, count, fmt, ap);
632   VA_END;
633   return (strlen(str));
634 }
635 #endif /* !HAVE_SNPRINTF */
636