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