5 * by JH <jheinonen@bigfoot.com>
7 * Copyright (C) Jaakko Heinonen
54 while( ( *str = toupper( *str ) ) )
65 while( ( *str = tolower ( *str ) ) )
78 for(t = s; ISSPACE(*t); t++);
80 memmove(s, t, strlen(t)+1);
82 for (tt = t = s; *t != '\0'; t++)
96 /* varargs declarations: */
99 # define MY_VA_LOCAL_DECL va_list ap
100 # define MY_VA_START(f) va_start(ap, f)
101 # define MY_VA_SHIFT(v,t) v = va_arg(ap, t)
102 # define MY_VA_END va_end(ap)
104 # error HAVE_STDARG_H not defined
108 mkstr (const char *format, ... )
114 (char *) abook_malloc (size);
116 (char *) malloc (size);
122 n = vsnprintf (buffer, size,
133 (char *) abook_realloc (buffer, size);
135 (char *) realloc (buffer, size);
142 strconcat (const char *str, ...)
151 l = 1 + strlen (str);
153 MY_VA_SHIFT(s, char*);
156 MY_VA_SHIFT(s, char*);
167 strcpy (concat, str);
169 MY_VA_SHIFT(s, char*);
172 MY_VA_SHIFT(s, char*);
181 safe_strcmp(const char *s1, const char * s2)
183 if (s1 == NULL && s2 == NULL) return 0;
184 if (s1 == NULL) return -1;
185 if (s2 == NULL) return 1;
187 return strcmp(s1, s2);
198 while( getcwd(dir, size) == NULL && errno == ERANGE )
199 dir = realloc(dir, size *=2);
204 #define INITIAL_SIZE 128
206 # define abook_malloc(X) malloc(X)
207 # define abook_realloc(X, XX) realloc(X, XX)
213 char *buf; /* buffer for line */
214 size_t size; /* size of buffer */
215 size_t inc; /* how much to enlarge buffer */
216 size_t len; /* # of chars stored into buf before '\0' */
218 const size_t thres = 128; /* initial buffer size (most lines should
219 fit into this size, so think of this as
220 the "long line threshold"). */
221 const size_t mucho = 128; /* if there is at least this much wasted
222 space when the whole buffer has been
223 read, try to reclaim it. Don't make
224 this too small, else there is too much
225 time wasted trying to reclaim a couple
227 const size_t mininc = 64; /* minimum number of bytes by which
228 to increase the allocated memory */
232 buf = abook_malloc(size);
234 while (fgets(buf+len, size-len, f) != NULL) {
235 len += strlen(buf+len);
236 if (len > 0 && buf[len-1] == '\n')
237 break; /* the whole line has been read */
239 for (inc = size, p = NULL; inc > mininc; inc /= 2)
240 if ((p = abook_realloc(buf, size + inc)) != NULL)
249 return NULL; /* nothing read (eof or error) */
252 if (buf[len-1] == '\n') /* remove newline, if there */
255 if (size - len > mucho) { /* a plenitude of unused memory? */
256 p = abook_realloc(buf, len+1);
266 /**************************************************************
268 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
269 * A bombproof version of doprnt (dopr) included.
270 * Sigh. This sort of thing is always nasty do deal with. Note that
271 * the version here does not include floating point...
273 * snprintf() is used instead of sprintf() as it does limit checks
274 * for string length. This covers a nasty loophole.
276 * The other functions are there to prevent NULL pointers from
277 * causing nast effects.
280 * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
281 * This was ugly. It is still ugly. I opted out of floating point
282 * numbers, but the formatter understands just about everything
283 * from the normal C string format, at least as far as I can tell from
284 * the Solaris 2.5 printf(3S) man page.
286 * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
287 * Ok, added some minimal floating point support, which means this
288 * probably requires libm on most operating systems. Don't yet
289 * support the exponent (e,E) and sigfig (g,G). Also, fmtint()
290 * was pretty badly broken, it just wasn't being exercised in ways
291 * which showed it, so that's been fixed. Also, formated the code
292 * to mutt conventions, and removed dead code left over from the
293 * original. Also, there is now a builtin-test, just compile with:
294 * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
295 * and run snprintf for results.
297 * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
298 * The PGP code was using unsigned hexadecimal formats.
299 * Unfortunately, unsigned formats simply didn't work.
301 * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
302 * The original code assumed that both snprintf() and vsnprintf() were
303 * missing. Some systems only have snprintf() but not vsnprintf(), so
304 * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
306 **************************************************************/
310 #if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF)
314 #include <sys/types.h>
316 /* Define this as a fall through, HAVE_STDARG_H is probably already set */
318 #if !defined(HAVE_STDARG_H) && !defined(HAVE_VARARGS_H)
319 # define HAVE_VARARGS_H 1
322 /* varargs declarations: */
324 #if defined(HAVE_STDARG_H)
325 /*# include <stdarg.h>*/
326 # define HAVE_STDARGS /* let's hope that works everywhere (mj) */
327 # define VA_LOCAL_DECL va_list ap
328 # define VA_START(f) va_start(ap, f)
329 # define VA_SHIFT(v,t) ; /* no-op for ANSI */
330 # define VA_END va_end(ap)
332 # if defined(HAVE_VARARGS_H)
333 # include <varargs.h>
335 # define VA_LOCAL_DECL va_list ap
336 # define VA_START(f) va_start(ap) /* f is ignored! */
337 # define VA_SHIFT(v,t) v = va_arg(ap,t)
338 # define VA_END va_end(ap)
340 /*XX ** NO VARARGS ** XX*/
344 /*int snprintf (char *str, size_t count, const char *fmt, ...);*/
345 /*int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);*/
347 static void dopr (char *buffer, size_t maxlen, const char *format,
349 static void fmtstr (char *buffer, size_t *currlen, size_t maxlen,
350 char *value, int flags, int min, int max);
351 static void fmtint (char *buffer, size_t *currlen, size_t maxlen,
352 long value, int base, int min, int max, int flags);
353 static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
354 long double fvalue, int min, int max, int flags);
355 static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c );
358 * dopr(): poor man's version of doprintf
361 /* format read states */
362 #define DP_S_DEFAULT 0
371 /* format flags - Bits */
372 #define DP_F_MINUS (1 << 0)
373 #define DP_F_PLUS (1 << 1)
374 #define DP_F_SPACE (1 << 2)
375 #define DP_F_NUM (1 << 3)
376 #define DP_F_ZERO (1 << 4)
377 #define DP_F_UP (1 << 5)
378 #define DP_F_UNSIGNED (1 << 6)
380 /* Conversion Flags */
383 #define DP_C_LDOUBLE 3
385 #define char_to_int(p) (p - '0')
386 #define MAX(p,q) ((p >= q) ? p : q)
388 static void dopr (char *buffer, size_t maxlen, const char *format, va_list args)
401 state = DP_S_DEFAULT;
402 currlen = flags = cflags = min = 0;
406 while (state != DP_S_DONE)
408 if ((ch == '\0') || (currlen >= maxlen))
417 dopr_outch (buffer, &currlen, maxlen, ch);
449 if (isdigit((unsigned char)ch))
451 min = 10*min + char_to_int (ch);
456 min = va_arg (args, int);
473 if (isdigit((unsigned char)ch))
477 max = 10*max + char_to_int (ch);
482 max = va_arg (args, int);
490 /* Currently, we don't support Long Long, bummer */
502 cflags = DP_C_LDOUBLE;
515 if (cflags == DP_C_SHORT)
516 value = va_arg (args, short int);
517 else if (cflags == DP_C_LONG)
518 value = va_arg (args, long int);
520 value = va_arg (args, int);
521 fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
524 flags |= DP_F_UNSIGNED;
525 if (cflags == DP_C_SHORT)
526 value = va_arg (args, unsigned short int);
527 else if (cflags == DP_C_LONG)
528 value = va_arg (args, unsigned long int);
530 value = va_arg (args, unsigned int);
531 fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
534 flags |= DP_F_UNSIGNED;
535 if (cflags == DP_C_SHORT)
536 value = va_arg (args, unsigned short int);
537 else if (cflags == DP_C_LONG)
538 value = va_arg (args, unsigned long int);
540 value = va_arg (args, unsigned int);
541 fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
546 flags |= DP_F_UNSIGNED;
547 if (cflags == DP_C_SHORT)
548 value = va_arg (args, unsigned short int);
549 else if (cflags == DP_C_LONG)
550 value = va_arg (args, unsigned long int);
552 value = va_arg (args, unsigned int);
553 fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
556 if (cflags == DP_C_LDOUBLE)
557 fvalue = va_arg (args, long double);
559 fvalue = va_arg (args, double);
560 /* um, floating point? */
561 fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
566 if (cflags == DP_C_LDOUBLE)
567 fvalue = va_arg (args, long double);
569 fvalue = va_arg (args, double);
574 if (cflags == DP_C_LDOUBLE)
575 fvalue = va_arg (args, long double);
577 fvalue = va_arg (args, double);
580 dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
583 strvalue = va_arg (args, char *);
585 max = maxlen; /* ie, no max */
586 fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
589 strvalue = va_arg (args, void *);
590 fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
593 if (cflags == DP_C_SHORT)
596 num = va_arg (args, short int *);
599 else if (cflags == DP_C_LONG)
602 num = va_arg (args, long int *);
608 num = va_arg (args, int *);
613 dopr_outch (buffer, &currlen, maxlen, ch);
616 /* not supported yet, treat as next char */
624 state = DP_S_DEFAULT;
625 flags = cflags = min = 0;
632 break; /* some picky compilers need this */
635 if (currlen < maxlen - 1)
636 buffer[currlen] = '\0';
638 buffer[maxlen - 1] = '\0';
641 static void fmtstr (char *buffer, size_t *currlen, size_t maxlen,
642 char *value, int flags, int min, int max)
644 int padlen, strln; /* amount to pad */
652 for (strln = 0; value[strln]; ++strln); /* strlen */
653 padlen = min - strln;
656 if (flags & DP_F_MINUS)
657 padlen = -padlen; /* Left Justify */
659 while ((padlen > 0) && (cnt < max))
661 dopr_outch (buffer, currlen, maxlen, ' ');
665 while (*value && (cnt < max))
667 dopr_outch (buffer, currlen, maxlen, *value++);
670 while ((padlen < 0) && (cnt < max))
672 dopr_outch (buffer, currlen, maxlen, ' ');
678 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
680 static void fmtint (char *buffer, size_t *currlen, size_t maxlen,
681 long value, int base, int min, int max, int flags)
684 unsigned long uvalue;
687 int spadlen = 0; /* amount to space pad */
688 int zpadlen = 0; /* amount to zero pad */
696 if(!(flags & DP_F_UNSIGNED))
703 if (flags & DP_F_PLUS) /* Do a sign (+/i) */
706 if (flags & DP_F_SPACE)
710 if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
714 (caps? "0123456789ABCDEF":"0123456789abcdef")
715 [uvalue % (unsigned)base ];
716 uvalue = (uvalue / (unsigned)base );
717 } while(uvalue && (place < 20));
718 if (place == 20) place--;
721 zpadlen = max - place;
722 spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
723 if (zpadlen < 0) zpadlen = 0;
724 if (spadlen < 0) spadlen = 0;
725 if (flags & DP_F_ZERO)
727 zpadlen = MAX(zpadlen, spadlen);
730 if (flags & DP_F_MINUS)
731 spadlen = -spadlen; /* Left Justifty */
733 #ifdef DEBUG_SNPRINTF
734 dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
735 zpadlen, spadlen, min, max, place));
741 dopr_outch (buffer, currlen, maxlen, ' ');
747 dopr_outch (buffer, currlen, maxlen, signvalue);
754 dopr_outch (buffer, currlen, maxlen, '0');
761 dopr_outch (buffer, currlen, maxlen, convert[--place]);
763 /* Left Justified spaces */
764 while (spadlen < 0) {
765 dopr_outch (buffer, currlen, maxlen, ' ');
770 static long double abs_val (long double value)
772 long double result = value;
780 static long double pow10 (int exp)
782 long double result = 1;
793 static long round (long double value)
798 value = value - intpart;
805 static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
806 long double fvalue, int min, int max, int flags)
814 int padlen = 0; /* amount to pad */
821 * AIX manpage says the default is 0, but Solaris says the default
822 * is 6, and sprintf on AIX defaults to 6
827 ufvalue = abs_val (fvalue);
832 if (flags & DP_F_PLUS) /* Do a sign (+/i) */
835 if (flags & DP_F_SPACE)
839 if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
845 * Sorry, we only support 9 digits past the decimal because of our
851 /* We "cheat" by converting the fractional part to integer by
852 * multiplying by a factor of 10
854 fracpart = round ((pow10 (max)) * (ufvalue - intpart));
856 if (fracpart >= pow10 (max))
859 fracpart -= pow10 (max);
862 #ifdef DEBUG_SNPRINTF
863 dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart));
866 /* Convert integer part */
869 (caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10];
870 intpart = (intpart / 10);
871 } while(intpart && (iplace < 20));
872 if (iplace == 20) iplace--;
873 iconvert[iplace] = 0;
875 /* Convert fractional part */
878 (caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10];
879 fracpart = (fracpart / 10);
880 } while(fracpart && (fplace < 20));
881 if (fplace == 20) fplace--;
882 fconvert[fplace] = 0;
884 /* -1 for decimal point, another -1 if we are printing a sign */
885 padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
886 zpadlen = max - fplace;
891 if (flags & DP_F_MINUS)
892 padlen = -padlen; /* Left Justifty */
894 if ((flags & DP_F_ZERO) && (padlen > 0))
898 dopr_outch (buffer, currlen, maxlen, signvalue);
904 dopr_outch (buffer, currlen, maxlen, '0');
910 dopr_outch (buffer, currlen, maxlen, ' ');
914 dopr_outch (buffer, currlen, maxlen, signvalue);
917 dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
920 * Decimal point. This should probably use locale to find the correct
923 dopr_outch (buffer, currlen, maxlen, '.');
926 dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
930 dopr_outch (buffer, currlen, maxlen, '0');
936 dopr_outch (buffer, currlen, maxlen, ' ');
941 static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c)
943 if (*currlen < maxlen)
944 buffer[(*currlen)++] = c;
946 #endif /* !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) */
948 #ifndef HAVE_VSNPRINTF
949 int vsnprintf (char *str, size_t count, const char *fmt, va_list args)
952 dopr(str, count, fmt, args);
955 #endif /* !HAVE_VSNPRINTF */
957 #ifndef HAVE_SNPRINTF
960 int snprintf (char *str,size_t count,const char *fmt,...)
962 int snprintf (va_alist) va_dcl
973 VA_SHIFT (str, char *);
974 VA_SHIFT (count, size_t );
975 VA_SHIFT (fmt, char *);
976 (void) vsnprintf(str, count, fmt, ap);
983 #define LONG_STRING 1024
987 char buf1[LONG_STRING];
988 char buf2[LONG_STRING];
1003 double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996,
1004 0.9996, 1.996, 4.136, 0};
1017 long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
1022 printf ("Testing snprintf format codes against system sprintf...\n");
1024 for (x = 0; fp_fmt[x] != NULL ; x++)
1025 for (y = 0; fp_nums[y] != 0 ; y++)
1027 snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]);
1028 sprintf (buf2, fp_fmt[x], fp_nums[y]);
1029 if (strcmp (buf1, buf2))
1031 printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n",
1032 fp_fmt[x], buf1, buf2);
1038 for (x = 0; int_fmt[x] != NULL ; x++)
1039 for (y = 0; int_nums[y] != 0 ; y++)
1041 snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]);
1042 sprintf (buf2, int_fmt[x], int_nums[y]);
1043 if (strcmp (buf1, buf2))
1045 printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n",
1046 int_fmt[x], buf1, buf2);
1051 printf ("%d tests failed out of %d.\n", fail, num);
1053 #endif /* SNPRINTF_TEST */
1055 #endif /* !HAVE_SNPRINTF */