/[cvs]/eggdrop1.8/src/compat/snprintf.c
ViewVC logotype

Contents of /eggdrop1.8/src/compat/snprintf.c

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.1.1.1 - (show annotations) (download) (as text) (vendor branch)
Mon Jul 26 21:11:06 2010 UTC (9 years ago) by simple
Branch: eggheads, MAIN
CVS Tags: v1, HEAD
Branch point for: gettext
Changes since 1.1: +0 -0 lines
File MIME type: text/x-chdr
Imported Eggdrop 1.6.20

1 /*
2 * snprintf.c - a portable implementation of snprintf and vsnprintf
3 *
4 * $Id: snprintf.c,v 1.27 2010/01/03 13:27:40 pseudo Exp $
5 */
6 /*
7 * Portions Copyright (C) 2000 - 2010 Eggheads Development Team
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 */
23
24 #include "main.h"
25 #include "snprintf.h"
26
27
28 /*
29 * Copyright Patrick Powell 1995
30 * This code is based on code written by Patrick Powell (papowell@astart.com)
31 * It may be used for any purpose as long as this notice remains intact
32 * on all source code distributions
33 */
34
35 /**************************************************************
36 * Original:
37 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
38 * A bombproof version of doprnt (dopr) included.
39 * Sigh. This sort of thing is always nasty do deal with. Note that
40 * the version here does not include floating point...
41 *
42 * snprintf() is used instead of sprintf() as it does limit checks
43 * for string length. This covers a nasty loophole.
44 *
45 * The other functions are there to prevent NULL pointers from
46 * causing nast effects.
47 *
48 * More Recently:
49 * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
50 * This was ugly. It is still ugly. I opted out of floating point
51 * numbers, but the formatter understands just about everything
52 * from the normal C string format, at least as far as I can tell from
53 * the Solaris 2.5 printf(3S) man page.
54 *
55 * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
56 * Ok, added some minimal floating point support, which means this
57 * probably requires libm on most operating systems. Don't yet
58 * support the exponent (e,E) and sigfig (g,G). Also, fmtint()
59 * was pretty badly broken, it just wasn't being exercised in ways
60 * which showed it, so that's been fixed. Also, formated the code
61 * to mutt conventions, and removed dead code left over from the
62 * original. Also, there is now a builtin-test, just compile with:
63 * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
64 * and run snprintf for results.
65 *
66 * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
67 * The PGP code was using unsigned hexadecimal formats.
68 * Unfortunately, unsigned formats simply didn't work.
69 *
70 * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
71 * The original code assumed that both snprintf() and vsnprintf() were
72 * missing. Some systems only have snprintf() but not vsnprintf(), so
73 * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
74 *
75 * Andrew Tridgell (tridge@samba.org) Oct 1998
76 * fixed handling of %.0f
77 * added test for HAVE_LONG_DOUBLE
78 *
79 * Fabian Knittel <fknittel@gmx.de> Apr 2000 for eggdrop 1.5.3
80 * Indented code to match eggdrop style. Adjusted to fit into eggdrops
81 * build environment. Added `egg_' prefixes to snprintf and vsnprintf.
82 *
83 **************************************************************/
84
85 #include <string.h>
86 #include <ctype.h>
87 #include <sys/types.h>
88
89 #ifndef HAVE_VSNPRINTF
90
91 /* varargs declarations: */
92
93 #if defined(__STDC__)
94 # ifdef HAVE_STDARG_H
95 # include <stdarg.h>
96 # endif
97 # define HAVE_STDARGS /* let's hope that works everywhere (mj) */
98 # define VA_LOCAL_DECL va_list ap
99 # define VA_START(f) va_start(ap, f)
100 # define VA_SHIFT(v,t) ; /* no-op for ANSI */
101 # define VA_END va_end(ap)
102 #else
103 # include <varargs.h>
104 # undef HAVE_STDARGS
105 # define VA_LOCAL_DECL va_list ap
106 # define VA_START(f) va_start(ap) /* f is ignored! */
107 # define VA_SHIFT(v,t) v = va_arg(ap,t)
108 # define VA_END va_end(ap)
109 #endif
110
111 #ifdef HAVE_LONG_DOUBLE
112 #define LDOUBLE long double
113 #else
114 #define LDOUBLE double
115 #endif
116
117 static void dopr(char *buffer, size_t maxlen, const char *format, va_list args);
118 static void fmtstr(char *buffer, size_t *currlen, size_t maxlen, char *value,
119 int flags, int min, int max);
120 static void fmtint(char *buffer, size_t *currlen, size_t maxlen, long value,
121 int base, int min, int max, int flags);
122 static void fmtfp(char *buffer, size_t *currlen, size_t maxlen, LDOUBLE fvalue,
123 int min, int max, int flags);
124 static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c);
125
126 /*
127 * dopr(): poor man's version of doprintf
128 */
129
130 /* format read states */
131 #define DP_S_DEFAULT 0
132 #define DP_S_FLAGS 1
133 #define DP_S_MIN 2
134 #define DP_S_DOT 3
135 #define DP_S_MAX 4
136 #define DP_S_MOD 5
137 #define DP_S_CONV 6
138 #define DP_S_DONE 7
139
140 /* format flags - Bits */
141 #define DP_F_MINUS (1 << 0)
142 #define DP_F_PLUS (1 << 1)
143 #define DP_F_SPACE (1 << 2)
144 #define DP_F_NUM (1 << 3)
145 #define DP_F_ZERO (1 << 4)
146 #define DP_F_UP (1 << 5)
147 #define DP_F_UNSIGNED (1 << 6)
148
149 /* Conversion Flags */
150 #define DP_C_SHORT 1
151 #define DP_C_LONG 2
152 #define DP_C_LDOUBLE 3
153
154 #define char_to_int(p) (p - '0')
155
156 #ifdef MAX
157 # undef MAX
158 #endif
159 #define MAX(p,q) ((p >= q) ? p : q)
160
161 static void dopr(char *buffer, size_t maxlen, const char *format, va_list args)
162 {
163 char ch;
164 long value;
165 LDOUBLE fvalue;
166 char *strvalue;
167 int min;
168 int max;
169 int state;
170 int flags;
171 int cflags;
172 size_t currlen;
173
174 state = DP_S_DEFAULT;
175 currlen = flags = cflags = min = 0;
176 max = -1;
177 ch = *format++;
178
179 while (state != DP_S_DONE) {
180 if ((ch == '\0') || (currlen >= maxlen))
181 state = DP_S_DONE;
182
183 switch (state) {
184 case DP_S_DEFAULT:
185 if (ch == '%')
186 state = DP_S_FLAGS;
187 else
188 dopr_outch(buffer, &currlen, maxlen, ch);
189 ch = *format++;
190 break;
191 case DP_S_FLAGS:
192 switch (ch) {
193 case '-':
194 flags |= DP_F_MINUS;
195 ch = *format++;
196 break;
197 case '+':
198 flags |= DP_F_PLUS;
199 ch = *format++;
200 break;
201 case ' ':
202 flags |= DP_F_SPACE;
203 ch = *format++;
204 break;
205 case '#':
206 flags |= DP_F_NUM;
207 ch = *format++;
208 break;
209 case '0':
210 flags |= DP_F_ZERO;
211 ch = *format++;
212 break;
213 default:
214 state = DP_S_MIN;
215 break;
216 }
217 break;
218 case DP_S_MIN:
219 if (egg_isdigit(ch)) {
220 min = 10 * min + char_to_int(ch);
221 ch = *format++;
222 } else if (ch == '*') {
223 min = va_arg(args, int);
224
225 ch = *format++;
226 state = DP_S_DOT;
227 } else
228 state = DP_S_DOT;
229 break;
230 case DP_S_DOT:
231 if (ch == '.') {
232 state = DP_S_MAX;
233 ch = *format++;
234 } else
235 state = DP_S_MOD;
236 break;
237 case DP_S_MAX:
238 if (egg_isdigit(ch)) {
239 if (max < 0)
240 max = 0;
241 max = 10 * max + char_to_int(ch);
242 ch = *format++;
243 } else if (ch == '*') {
244 max = va_arg(args, int);
245
246 ch = *format++;
247 state = DP_S_MOD;
248 } else
249 state = DP_S_MOD;
250 break;
251 case DP_S_MOD:
252 /* Currently, we don't support Long Long, bummer */
253 switch (ch) {
254 case 'h':
255 cflags = DP_C_SHORT;
256 ch = *format++;
257 break;
258 case 'l':
259 cflags = DP_C_LONG;
260 ch = *format++;
261 break;
262 case 'L':
263 cflags = DP_C_LDOUBLE;
264 ch = *format++;
265 break;
266 default:
267 break;
268 }
269 state = DP_S_CONV;
270 break;
271 case DP_S_CONV:
272 switch (ch) {
273 case 'd':
274 case 'i':
275 if (cflags == DP_C_SHORT)
276 value = va_arg(args, int);
277
278 else if (cflags == DP_C_LONG)
279 value = va_arg(args, long int);
280
281 else
282 value = va_arg(args, int);
283
284 fmtint(buffer, &currlen, maxlen, value, 10, min, max, flags);
285 break;
286 case 'o':
287 flags |= DP_F_UNSIGNED;
288 if (cflags == DP_C_SHORT)
289 value = va_arg(args, unsigned int);
290
291 else if (cflags == DP_C_LONG)
292 value = va_arg(args, unsigned long int);
293
294 else
295 value = va_arg(args, unsigned int);
296
297 fmtint(buffer, &currlen, maxlen, value, 8, min, max, flags);
298 break;
299 case 'u':
300 flags |= DP_F_UNSIGNED;
301 if (cflags == DP_C_SHORT)
302 value = va_arg(args, unsigned int);
303
304 else if (cflags == DP_C_LONG)
305 value = va_arg(args, unsigned long int);
306
307 else
308 value = va_arg(args, unsigned int);
309
310 fmtint(buffer, &currlen, maxlen, value, 10, min, max, flags);
311 break;
312 case 'X':
313 flags |= DP_F_UP;
314 case 'x':
315 flags |= DP_F_UNSIGNED;
316 if (cflags == DP_C_SHORT)
317 value = va_arg(args, unsigned int);
318
319 else if (cflags == DP_C_LONG)
320 value = va_arg(args, unsigned long int);
321
322 else
323 value = va_arg(args, unsigned int);
324
325 fmtint(buffer, &currlen, maxlen, value, 16, min, max, flags);
326 break;
327 case 'f':
328 if (cflags == DP_C_LDOUBLE)
329 fvalue = va_arg(args, LDOUBLE);
330 else
331 fvalue = va_arg(args, double);
332
333 /* um, floating point? */
334 fmtfp(buffer, &currlen, maxlen, fvalue, min, max, flags);
335 break;
336 case 'E':
337 flags |= DP_F_UP;
338 case 'e':
339 if (cflags == DP_C_LDOUBLE)
340 fvalue = va_arg(args, LDOUBLE);
341 else
342 fvalue = va_arg(args, double);
343
344 break;
345 case 'G':
346 flags |= DP_F_UP;
347 case 'g':
348 if (cflags == DP_C_LDOUBLE)
349 fvalue = va_arg(args, LDOUBLE);
350 else
351 fvalue = va_arg(args, double);
352
353 break;
354 case 'c':
355 dopr_outch(buffer, &currlen, maxlen, va_arg(args, int));
356
357 break;
358 case 's':
359 strvalue = va_arg(args, char *);
360
361 if (max < 0)
362 max = maxlen; /* ie, no max */
363 fmtstr(buffer, &currlen, maxlen, strvalue, flags, min, max);
364 break;
365 case 'p':
366 strvalue = va_arg(args, void *);
367
368 fmtint(buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
369 break;
370 case 'n':
371 if (cflags == DP_C_SHORT) {
372 short int *num;
373 num = va_arg(args, short int *);
374
375 *num = currlen;
376 } else if (cflags == DP_C_LONG) {
377 long int *num;
378 num = va_arg(args, long int *);
379
380 *num = currlen;
381 } else {
382 int *num;
383 num = va_arg(args, int *);
384
385 *num = currlen;
386 }
387 break;
388 case '%':
389 dopr_outch(buffer, &currlen, maxlen, ch);
390 break;
391 case 'w':
392 /* not supported yet, treat as next char */
393 ch = *format++;
394 break;
395 default:
396 /* Unknown, skip */
397 break;
398 }
399 ch = *format++;
400 state = DP_S_DEFAULT;
401 flags = cflags = min = 0;
402 max = -1;
403 break;
404 case DP_S_DONE:
405 break;
406 default:
407 /* hmm? */
408 break; /* some picky compilers need this */
409 }
410 }
411 if (currlen < maxlen - 1)
412 buffer[currlen] = '\0';
413 else
414 buffer[maxlen - 1] = '\0';
415 }
416
417 static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
418 char *value, int flags, int min, int max)
419 {
420 int padlen, strln; /* amount to pad */
421 int cnt = 0;
422
423 if (value == 0) {
424 value = "<NULL>";
425 }
426
427 for (strln = 0; value[strln]; ++strln); /* strlen */
428 padlen = min - strln;
429 if (padlen < 0)
430 padlen = 0;
431 if (flags & DP_F_MINUS)
432 padlen = -padlen; /* Left Justify */
433
434 while ((padlen > 0) && (cnt < max)) {
435 dopr_outch(buffer, currlen, maxlen, ' ');
436 --padlen;
437 ++cnt;
438 }
439 while (*value && (cnt < max)) {
440 dopr_outch(buffer, currlen, maxlen, *value++);
441 ++cnt;
442 }
443 while ((padlen < 0) && (cnt < max)) {
444 dopr_outch(buffer, currlen, maxlen, ' ');
445 ++padlen;
446 ++cnt;
447 }
448 }
449
450 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
451
452 static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
453 long value, int base, int min, int max, int flags)
454 {
455 int signvalue = 0;
456 unsigned long uvalue;
457 char convert[20];
458 int place = 0;
459 int spadlen = 0; /* amount to space pad */
460 int zpadlen = 0; /* amount to zero pad */
461 int caps = 0;
462
463 if (max < 0)
464 max = 0;
465
466 uvalue = value;
467
468 if (!(flags & DP_F_UNSIGNED)) {
469 if (value < 0) {
470 signvalue = '-';
471 uvalue = -value;
472 } else if (flags & DP_F_PLUS) /* Do a sign (+/i) */
473 signvalue = '+';
474 else if (flags & DP_F_SPACE)
475 signvalue = ' ';
476 }
477
478 if (flags & DP_F_UP)
479 caps = 1; /* Should characters be upper case? */
480
481 do {
482 convert[place++] = (caps ? "0123456789ABCDEF" : "0123456789abcdef")
483 [uvalue % (unsigned) base];
484 uvalue = (uvalue / (unsigned) base);
485 }
486 while (uvalue && (place < 20));
487 if (place == 20)
488 place--;
489 convert[place] = 0;
490
491 zpadlen = max - place;
492 spadlen = min - MAX(max, place) - (signvalue ? 1 : 0);
493 if (zpadlen < 0)
494 zpadlen = 0;
495 if (spadlen < 0)
496 spadlen = 0;
497 if (flags & DP_F_ZERO) {
498 zpadlen = MAX(zpadlen, spadlen);
499 spadlen = 0;
500 }
501 if (flags & DP_F_MINUS)
502 spadlen = -spadlen; /* Left Justifty */
503
504 #ifdef DEBUG_SNPRINTF
505 dprint(1,
506 (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
507 zpadlen, spadlen, min, max, place));
508 #endif
509
510 /* Spaces */
511 while (spadlen > 0) {
512 dopr_outch(buffer, currlen, maxlen, ' ');
513 --spadlen;
514 }
515
516 /* Sign */
517 if (signvalue)
518 dopr_outch(buffer, currlen, maxlen, signvalue);
519
520 /* Zeros */
521 if (zpadlen > 0) {
522 while (zpadlen > 0) {
523 dopr_outch(buffer, currlen, maxlen, '0');
524 --zpadlen;
525 }
526 }
527
528 /* Digits */
529 while (place > 0)
530 dopr_outch(buffer, currlen, maxlen, convert[--place]);
531
532 /* Left Justified spaces */
533 while (spadlen < 0) {
534 dopr_outch(buffer, currlen, maxlen, ' ');
535 ++spadlen;
536 }
537 }
538
539 static LDOUBLE abs_val(LDOUBLE value)
540 {
541 LDOUBLE result = value;
542
543 if (value < 0)
544 result = -value;
545
546 return result;
547 }
548
549 static LDOUBLE pow10(int exp)
550 {
551 LDOUBLE result = 1;
552
553 while (exp) {
554 result *= 10;
555 exp--;
556 }
557
558 return result;
559 }
560
561 static long round(LDOUBLE value)
562 {
563 long intpart;
564
565 intpart = value;
566 value = value - intpart;
567 if (value >= 0.5)
568 intpart++;
569
570 return intpart;
571 }
572
573 static void fmtfp(char *buffer, size_t *currlen, size_t maxlen,
574 LDOUBLE fvalue, int min, int max, int flags)
575 {
576 int signvalue = 0;
577 LDOUBLE ufvalue;
578 char iconvert[20];
579 char fconvert[20];
580 int iplace = 0;
581 int fplace = 0;
582 int padlen = 0; /* amount to pad */
583 int zpadlen = 0;
584 int caps = 0;
585 long intpart;
586 long fracpart;
587
588 /*
589 * AIX manpage says the default is 0, but Solaris says the default
590 * is 6, and sprintf on AIX defaults to 6
591 */
592 if (max < 0)
593 max = 6;
594
595 ufvalue = abs_val(fvalue);
596
597 if (fvalue < 0)
598 signvalue = '-';
599 else if (flags & DP_F_PLUS) /* Do a sign (+/i) */
600 signvalue = '+';
601 else if (flags & DP_F_SPACE)
602 signvalue = ' ';
603
604 #if 0
605 if (flags & DP_F_UP)
606 caps = 1; /* Should characters be upper case? */
607 #endif
608
609 intpart = ufvalue;
610
611 /*
612 * Sorry, we only support 9 digits past the decimal because of our
613 * conversion method
614 */
615 if (max > 9)
616 max = 9;
617
618 /* We "cheat" by converting the fractional part to integer by
619 * multiplying by a factor of 10
620 */
621 fracpart = round((pow10(max)) * (ufvalue - intpart));
622
623 if (fracpart >= pow10(max)) {
624 intpart++;
625 fracpart -= pow10(max);
626 }
627
628 /* Convert integer part */
629 do {
630 iconvert[iplace++] =
631 (caps ? "0123456789ABCDEF" : "0123456789abcdef")[intpart % 10];
632 intpart = (intpart / 10);
633 }
634 while (intpart && (iplace < 20));
635 if (iplace == 20)
636 iplace--;
637 iconvert[iplace] = 0;
638
639 /* Convert fractional part */
640 do {
641 fconvert[fplace++] =
642 (caps ? "0123456789ABCDEF" : "0123456789abcdef")[fracpart % 10];
643 fracpart = (fracpart / 10);
644 }
645 while (fracpart && (fplace < 20));
646 if (fplace == 20)
647 fplace--;
648 fconvert[fplace] = 0;
649
650 /* -1 for decimal point, another -1 if we are printing a sign */
651 padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
652 zpadlen = max - fplace;
653 if (zpadlen < 0)
654 zpadlen = 0;
655 if (padlen < 0)
656 padlen = 0;
657 if (flags & DP_F_MINUS)
658 padlen = -padlen; /* Left Justifty */
659
660 if ((flags & DP_F_ZERO) && (padlen > 0)) {
661 if (signvalue) {
662 dopr_outch(buffer, currlen, maxlen, signvalue);
663 --padlen;
664 signvalue = 0;
665 }
666 while (padlen > 0) {
667 dopr_outch(buffer, currlen, maxlen, '0');
668 --padlen;
669 }
670 }
671 while (padlen > 0) {
672 dopr_outch(buffer, currlen, maxlen, ' ');
673 --padlen;
674 }
675 if (signvalue)
676 dopr_outch(buffer, currlen, maxlen, signvalue);
677
678 while (iplace > 0)
679 dopr_outch(buffer, currlen, maxlen, iconvert[--iplace]);
680
681 /*
682 * Decimal point. This should probably use locale to find the correct
683 * char to print out.
684 */
685 if (max > 0) {
686 dopr_outch(buffer, currlen, maxlen, '.');
687
688 while (fplace > 0)
689 dopr_outch(buffer, currlen, maxlen, fconvert[--fplace]);
690 }
691
692 while (zpadlen > 0) {
693 dopr_outch(buffer, currlen, maxlen, '0');
694 --zpadlen;
695 }
696
697 while (padlen < 0) {
698 dopr_outch(buffer, currlen, maxlen, ' ');
699 ++padlen;
700 }
701 }
702
703 static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c)
704 {
705 if (*currlen < maxlen)
706 buffer[(*currlen)++] = c;
707 }
708
709 int egg_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
710 {
711 str[0] = 0;
712 dopr(str, count, fmt, args);
713 return (strlen(str));
714 }
715 #endif /* !HAVE_VSNPRINTF */
716
717 #ifndef HAVE_SNPRINTF
718 # ifdef HAVE_STDARGS
719 int egg_snprintf(char *str, size_t count, const char *fmt, ...)
720 # else
721 int egg_snprintf(va_alist)
722 va_dcl
723 # endif
724 {
725 # ifndef HAVE_STDARGS
726 char *str;
727 size_t count;
728 char *fmt;
729 # endif
730 VA_LOCAL_DECL;
731
732 VA_START(fmt);
733 VA_SHIFT(str, char *);
734
735 VA_SHIFT(count, size_t);
736 VA_SHIFT(fmt, char *);
737
738 (void) egg_vsnprintf(str, count, fmt, ap);
739 VA_END;
740 return (strlen(str));
741 }
742 #endif /* !HAVE_SNPRINTF */

webmaster@eggheads.org
ViewVC Help
Powered by ViewVC 1.1.23