5 * by JH <jheinonen@users.sourceforge.net>
7 * Copyright (C) Jaakko Heinonen
42 while( ( *str = tolower ( *str ) ) )
55 for(t = s; ISSPACE(*t); t++);
57 memmove(s, t, strlen(t)+1);
59 for (tt = t = s; *t != '\0'; t++)
73 /* varargs declarations: */
76 # define MY_VA_LOCAL_DECL va_list ap
77 # define MY_VA_START(f) va_start(ap, f)
78 # define MY_VA_SHIFT(v,t) v = va_arg(ap, t)
79 # define MY_VA_END va_end(ap)
81 # error HAVE_STDARG_H not defined
85 mkstr (const char *format, ... )
91 (char *) abook_malloc (size);
93 (char *) malloc (size);
96 assert(format != NULL);
101 n = vsnprintf (buffer, size,
105 if (n > -1 && n < size)
115 (char *) abook_realloc (buffer, size);
117 (char *) realloc (buffer, size);
124 strconcat (const char *str, ...)
132 l = 1 + strlen (str);
134 MY_VA_SHIFT(s, char*);
137 MY_VA_SHIFT(s, char*);
148 strcpy (concat, str);
150 MY_VA_SHIFT(s, char*);
153 MY_VA_SHIFT(s, char*);
162 safe_strcmp(const char *s1, const char *s2)
164 if (s1 == NULL && s2 == NULL) return 0;
165 if (s1 == NULL) return -1;
166 if (s2 == NULL) return 1;
168 return strcmp(s1, s2);
172 safe_strcoll(const char *s1, const char *s2)
175 if (s1 == NULL && s2 == NULL) return 0;
176 if (s1 == NULL) return -1;
177 if (s2 == NULL) return 1;
179 return strcoll(s1, s2);
180 #else /* fall back to strcmp */
181 return safe_strcmp(s1, s2);
191 if( (dir = (char *)malloc(size)) == NULL)
194 while( getcwd(dir, size) == NULL && errno == ERANGE )
195 if( (dir = (char *)realloc(dir, size *=2)) == NULL)
201 #define INITIAL_SIZE 128
203 # define abook_malloc(X) malloc(X)
204 # define abook_realloc(X, XX) realloc(X, XX)
210 char *buf; /* buffer for line */
211 size_t size; /* size of buffer */
212 size_t inc; /* how much to enlarge buffer */
213 size_t len; /* # of chars stored into buf before '\0' */
215 const size_t thres = 128; /* initial buffer size (most lines should
216 fit into this size, so think of this as
217 the "long line threshold"). */
218 const size_t mucho = 128; /* if there is at least this much wasted
219 space when the whole buffer has been
220 read, try to reclaim it. Don't make
221 this too small, else there is too much
222 time wasted trying to reclaim a couple
224 const size_t mininc = 64; /* minimum number of bytes by which
225 to increase the allocated memory */
229 buf = (char *)abook_malloc(size);
231 while (fgets(buf+len, size-len, f) != NULL) {
232 len += strlen(buf+len);
233 if (len > 0 && buf[len-1] == '\n')
234 break; /* the whole line has been read */
236 for (inc = size, p = NULL; inc > mininc; inc /= 2)
237 if ((p = (char *)abook_realloc(buf, size + inc)) !=
247 return NULL; /* nothing read (eof or error) */
250 if (buf[len-1] == '\n') /* remove newline, if there */
253 if (size - len > mucho) { /* a plenitude of unused memory? */
254 p = (char *)abook_realloc(buf, len+1);
264 /**************************************************************
266 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
267 * A bombproof version of doprnt (dopr) included.
268 * Sigh. This sort of thing is always nasty do deal with. Note that
269 * the version here does not include floating point...
271 * snprintf() is used instead of sprintf() as it does limit checks
272 * for string length. This covers a nasty loophole.
274 * The other functions are there to prevent NULL pointers from
275 * causing nast effects.
278 * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
279 * This was ugly. It is still ugly. I opted out of floating point
280 * numbers, but the formatter understands just about everything
281 * from the normal C string format, at least as far as I can tell from
282 * the Solaris 2.5 printf(3S) man page.
284 * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
285 * Ok, added some minimal floating point support, which means this
286 * probably requires libm on most operating systems. Don't yet
287 * support the exponent (e,E) and sigfig (g,G). Also, fmtint()
288 * was pretty badly broken, it just wasn't being exercised in ways
289 * which showed it, so that's been fixed. Also, formated the code
290 * to mutt conventions, and removed dead code left over from the
291 * original. Also, there is now a builtin-test, just compile with:
292 * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
293 * and run snprintf for results.
295 * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
296 * The PGP code was using unsigned hexadecimal formats.
297 * Unfortunately, unsigned formats simply didn't work.
299 * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
300 * The original code assumed that both snprintf() and vsnprintf() were
301 * missing. Some systems only have snprintf() but not vsnprintf(), so
302 * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
304 **************************************************************/
308 #if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF)
312 #include <sys/types.h>
314 /* Define this as a fall through, HAVE_STDARG_H is probably already set */
316 #if !defined(HAVE_STDARG_H) && !defined(HAVE_VARARGS_H)
317 # define HAVE_VARARGS_H 1
320 /* varargs declarations: */
322 #if defined(HAVE_STDARG_H)
323 /*# include <stdarg.h>*/
324 # define HAVE_STDARGS /* let's hope that works everywhere (mj) */
325 # define VA_LOCAL_DECL va_list ap
326 # define VA_START(f) va_start(ap, f)
327 # define VA_SHIFT(v,t) ; /* no-op for ANSI */
328 # define VA_END va_end(ap)
330 # if defined(HAVE_VARARGS_H)
331 # include <varargs.h>
333 # define VA_LOCAL_DECL va_list ap
334 # define VA_START(f) va_start(ap) /* f is ignored! */
335 # define VA_SHIFT(v,t) v = va_arg(ap,t)
336 # define VA_END va_end(ap)
338 /*XX ** NO VARARGS ** XX*/
342 /*int snprintf (char *str, size_t count, const char *fmt, ...);*/
343 /*int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);*/
345 static void dopr (char *buffer, size_t maxlen, const char *format,
347 static void fmtstr (char *buffer, size_t *currlen, size_t maxlen,
348 char *value, int flags, int min, int max);
349 static void fmtint (char *buffer, size_t *currlen, size_t maxlen,
350 long value, int base, int min, int max, int flags);
351 static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
352 long double fvalue, int min, int max, int flags);
353 static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c );
356 * dopr(): poor man's version of doprintf
359 /* format read states */
360 #define DP_S_DEFAULT 0
369 /* format flags - Bits */
370 #define DP_F_MINUS (1 << 0)
371 #define DP_F_PLUS (1 << 1)
372 #define DP_F_SPACE (1 << 2)
373 #define DP_F_NUM (1 << 3)
374 #define DP_F_ZERO (1 << 4)
375 #define DP_F_UP (1 << 5)
376 #define DP_F_UNSIGNED (1 << 6)
378 /* Conversion Flags */
381 #define DP_C_LDOUBLE 3
383 #define char_to_int(p) (p - '0')
384 #define MAX(p,q) ((p >= q) ? p : q)
386 static void dopr (char *buffer, size_t maxlen, const char *format, va_list args)
399 state = DP_S_DEFAULT;
400 currlen = flags = cflags = min = 0;
404 while (state != DP_S_DONE)
406 if ((ch == '\0') || (currlen >= maxlen))
415 dopr_outch (buffer, &currlen, maxlen, ch);
447 if (isdigit((unsigned char)ch))
449 min = 10*min + char_to_int (ch);
454 min = va_arg (args, int);
471 if (isdigit((unsigned char)ch))
475 max = 10*max + char_to_int (ch);
480 max = va_arg (args, int);
488 /* Currently, we don't support Long Long, bummer */
500 cflags = DP_C_LDOUBLE;
513 if (cflags == DP_C_SHORT)
514 value = va_arg (args, short int);
515 else if (cflags == DP_C_LONG)
516 value = va_arg (args, long int);
518 value = va_arg (args, int);
519 fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
522 flags |= DP_F_UNSIGNED;
523 if (cflags == DP_C_SHORT)
524 value = va_arg (args, unsigned short int);
525 else if (cflags == DP_C_LONG)
526 value = va_arg (args, unsigned long int);
528 value = va_arg (args, unsigned int);
529 fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
532 flags |= DP_F_UNSIGNED;
533 if (cflags == DP_C_SHORT)
534 value = va_arg (args, unsigned short int);
535 else if (cflags == DP_C_LONG)
536 value = va_arg (args, unsigned long int);
538 value = va_arg (args, unsigned int);
539 fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
544 flags |= DP_F_UNSIGNED;
545 if (cflags == DP_C_SHORT)
546 value = va_arg (args, unsigned short int);
547 else if (cflags == DP_C_LONG)
548 value = va_arg (args, unsigned long int);
550 value = va_arg (args, unsigned int);
551 fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
554 if (cflags == DP_C_LDOUBLE)
555 fvalue = va_arg (args, long double);
557 fvalue = va_arg (args, double);
558 /* um, floating point? */
559 fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
564 if (cflags == DP_C_LDOUBLE)
565 fvalue = va_arg (args, long double);
567 fvalue = va_arg (args, double);
572 if (cflags == DP_C_LDOUBLE)
573 fvalue = va_arg (args, long double);
575 fvalue = va_arg (args, double);
578 dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
581 strvalue = va_arg (args, char *);
583 max = maxlen; /* ie, no max */
584 fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
587 strvalue = va_arg (args, void *);
588 fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
591 if (cflags == DP_C_SHORT)
594 num = va_arg (args, short int *);
597 else if (cflags == DP_C_LONG)
600 num = va_arg (args, long int *);
606 num = va_arg (args, int *);
611 dopr_outch (buffer, &currlen, maxlen, ch);
614 /* not supported yet, treat as next char */
622 state = DP_S_DEFAULT;
623 flags = cflags = min = 0;
630 break; /* some picky compilers need this */
633 if (currlen < maxlen - 1)
634 buffer[currlen] = '\0';
636 buffer[maxlen - 1] = '\0';
639 static void fmtstr (char *buffer, size_t *currlen, size_t maxlen,
640 char *value, int flags, int min, int max)
642 int padlen, strln; /* amount to pad */
650 for (strln = 0; value[strln]; ++strln); /* strlen */
651 padlen = min - strln;
654 if (flags & DP_F_MINUS)
655 padlen = -padlen; /* Left Justify */
657 while ((padlen > 0) && (cnt < max))
659 dopr_outch (buffer, currlen, maxlen, ' ');
663 while (*value && (cnt < max))
665 dopr_outch (buffer, currlen, maxlen, *value++);
668 while ((padlen < 0) && (cnt < max))
670 dopr_outch (buffer, currlen, maxlen, ' ');
676 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
678 static void fmtint (char *buffer, size_t *currlen, size_t maxlen,
679 long value, int base, int min, int max, int flags)
682 unsigned long uvalue;
685 int spadlen = 0; /* amount to space pad */
686 int zpadlen = 0; /* amount to zero pad */
694 if(!(flags & DP_F_UNSIGNED))
701 if (flags & DP_F_PLUS) /* Do a sign (+/i) */
704 if (flags & DP_F_SPACE)
708 if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
712 (caps? "0123456789ABCDEF":"0123456789abcdef")
713 [uvalue % (unsigned)base ];
714 uvalue = (uvalue / (unsigned)base );
715 } while(uvalue && (place < 20));
716 if (place == 20) place--;
719 zpadlen = max - place;
720 spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
721 if (zpadlen < 0) zpadlen = 0;
722 if (spadlen < 0) spadlen = 0;
723 if (flags & DP_F_ZERO)
725 zpadlen = MAX(zpadlen, spadlen);
728 if (flags & DP_F_MINUS)
729 spadlen = -spadlen; /* Left Justifty */
731 #ifdef DEBUG_SNPRINTF
732 dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
733 zpadlen, spadlen, min, max, place));
739 dopr_outch (buffer, currlen, maxlen, ' ');
745 dopr_outch (buffer, currlen, maxlen, signvalue);
752 dopr_outch (buffer, currlen, maxlen, '0');
759 dopr_outch (buffer, currlen, maxlen, convert[--place]);
761 /* Left Justified spaces */
762 while (spadlen < 0) {
763 dopr_outch (buffer, currlen, maxlen, ' ');
768 static long double abs_val (long double value)
770 long double result = value;
778 static long double pow10 (int exp)
780 long double result = 1;
791 static long round (long double value)
796 value = value - intpart;
803 static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
804 long double fvalue, int min, int max, int flags)
812 int padlen = 0; /* amount to pad */
819 * AIX manpage says the default is 0, but Solaris says the default
820 * is 6, and sprintf on AIX defaults to 6
825 ufvalue = abs_val (fvalue);
830 if (flags & DP_F_PLUS) /* Do a sign (+/i) */
833 if (flags & DP_F_SPACE)
837 if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
843 * Sorry, we only support 9 digits past the decimal because of our
849 /* We "cheat" by converting the fractional part to integer by
850 * multiplying by a factor of 10
852 fracpart = round ((pow10 (max)) * (ufvalue - intpart));
854 if (fracpart >= pow10 (max))
857 fracpart -= pow10 (max);
860 #ifdef DEBUG_SNPRINTF
861 dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart));
864 /* Convert integer part */
867 (caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10];
868 intpart = (intpart / 10);
869 } while(intpart && (iplace < 20));
870 if (iplace == 20) iplace--;
871 iconvert[iplace] = 0;
873 /* Convert fractional part */
876 (caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10];
877 fracpart = (fracpart / 10);
878 } while(fracpart && (fplace < 20));
879 if (fplace == 20) fplace--;
880 fconvert[fplace] = 0;
882 /* -1 for decimal point, another -1 if we are printing a sign */
883 padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
884 zpadlen = max - fplace;
889 if (flags & DP_F_MINUS)
890 padlen = -padlen; /* Left Justifty */
892 if ((flags & DP_F_ZERO) && (padlen > 0))
896 dopr_outch (buffer, currlen, maxlen, signvalue);
902 dopr_outch (buffer, currlen, maxlen, '0');
908 dopr_outch (buffer, currlen, maxlen, ' ');
912 dopr_outch (buffer, currlen, maxlen, signvalue);
915 dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
918 * Decimal point. This should probably use locale to find the correct
921 dopr_outch (buffer, currlen, maxlen, '.');
924 dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
928 dopr_outch (buffer, currlen, maxlen, '0');
934 dopr_outch (buffer, currlen, maxlen, ' ');
939 static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c)
941 if (*currlen < maxlen)
942 buffer[(*currlen)++] = c;
944 #endif /* !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) */
946 #ifndef HAVE_VSNPRINTF
947 int vsnprintf (char *str, size_t count, const char *fmt, va_list args)
950 dopr(str, count, fmt, args);
953 #endif /* !HAVE_VSNPRINTF */
955 #ifndef HAVE_SNPRINTF
958 int snprintf (char *str,size_t count,const char *fmt,...)
960 int snprintf (va_alist) va_dcl
971 VA_SHIFT (str, char *);
972 VA_SHIFT (count, size_t );
973 VA_SHIFT (fmt, char *);
974 (void) vsnprintf(str, count, fmt, ap);
981 #define LONG_STRING 1024
985 char buf1[LONG_STRING];
986 char buf2[LONG_STRING];
1001 double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996,
1002 0.9996, 1.996, 4.136, 0};
1015 long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
1020 printf ("Testing snprintf format codes against system sprintf...\n");
1022 for (x = 0; fp_fmt[x] != NULL ; x++)
1023 for (y = 0; fp_nums[y] != 0 ; y++)
1025 snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]);
1026 sprintf (buf2, fp_fmt[x], fp_nums[y]);
1027 if (strcmp (buf1, buf2))
1029 printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n",
1030 fp_fmt[x], buf1, buf2);
1036 for (x = 0; int_fmt[x] != NULL ; x++)
1037 for (y = 0; int_nums[y] != 0 ; y++)
1039 snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]);
1040 sprintf (buf2, int_fmt[x], int_nums[y]);
1041 if (strcmp (buf1, buf2))
1043 printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n",
1044 int_fmt[x], buf1, buf2);
1049 printf ("%d tests failed out of %d.\n", fail, num);
1051 #endif /* SNPRINTF_TEST */
1053 #endif /* !HAVE_SNPRINTF */