4 * by JH <jheinonen@bigfoot.com>
6 * Copyright (C) Jaakko Heinonen
53 while( ( *str = toupper( *str ) ) )
64 while( ( *str = tolower ( *str ) ) )
77 for(t = s; ISSPACE(*t); t++);
79 memmove(s, t, strlen(t)+1);
81 for (tt = t = s; *t != '\0'; t++)
95 /* varargs declarations: */
98 # define MY_VA_LOCAL_DECL va_list ap
99 # define MY_VA_START(f) va_start(ap, f)
100 # define MY_VA_SHIFT(v,t) v = va_arg(ap, t)
101 # define MY_VA_END va_end(ap)
103 # error HAVE_STDARG_H not defined
107 mkstr (const char *format, ... )
113 (char *) abook_malloc (size);
115 (char *) malloc (size);
121 n = vsnprintf (buffer, size,
132 (char *) abook_realloc (buffer, size);
134 (char *) realloc (buffer, size);
141 strconcat (const char *str, ...)
150 l = 1 + strlen (str);
152 MY_VA_SHIFT(s, char*);
155 MY_VA_SHIFT(s, char*);
166 strcpy (concat, str);
168 MY_VA_SHIFT(s, char*);
171 MY_VA_SHIFT(s, char*);
180 safe_strcmp(const char *s1, const char * s2)
182 if (s1 == NULL && s2 == NULL) return 0;
183 if (s1 == NULL) return -1;
184 if (s2 == NULL) return 1;
186 return strcmp(s1, s2);
197 while( getcwd(dir, size) == NULL && errno == ERANGE )
198 dir = realloc(dir, size *=2);
203 #define INITIAL_SIZE 128
205 # define abook_malloc(X) malloc(X)
206 # define abook_realloc(X, XX) realloc(X, XX)
212 char *buf; /* buffer for line */
213 size_t size; /* size of buffer */
214 size_t inc; /* how much to enlarge buffer */
215 size_t len; /* # of chars stored into buf before '\0' */
217 const size_t thres = 128; /* initial buffer size (most lines should
218 fit into this size, so think of this as
219 the "long line threshold"). */
220 const size_t mucho = 128; /* if there is at least this much wasted
221 space when the whole buffer has been
222 read, try to reclaim it. Don't make
223 this too small, else there is too much
224 time wasted trying to reclaim a couple
226 const size_t mininc = 64; /* minimum number of bytes by which
227 to increase the allocated memory */
231 buf = abook_malloc(size);
233 while (fgets(buf+len, size-len, f) != NULL) {
234 len += strlen(buf+len);
235 if (len > 0 && buf[len-1] == '\n')
236 break; /* the whole line has been read */
238 for (inc = size, p = NULL; inc > mininc; inc /= 2)
239 if ((p = abook_realloc(buf, size + inc)) != NULL)
248 return NULL; /* nothing read (eof or error) */
251 if (buf[len-1] == '\n') /* remove newline, if there */
254 if (size - len > mucho) { /* a plenitude of unused memory? */
255 p = abook_realloc(buf, len+1);
265 /**************************************************************
267 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
268 * A bombproof version of doprnt (dopr) included.
269 * Sigh. This sort of thing is always nasty do deal with. Note that
270 * the version here does not include floating point...
272 * snprintf() is used instead of sprintf() as it does limit checks
273 * for string length. This covers a nasty loophole.
275 * The other functions are there to prevent NULL pointers from
276 * causing nast effects.
279 * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
280 * This was ugly. It is still ugly. I opted out of floating point
281 * numbers, but the formatter understands just about everything
282 * from the normal C string format, at least as far as I can tell from
283 * the Solaris 2.5 printf(3S) man page.
285 * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
286 * Ok, added some minimal floating point support, which means this
287 * probably requires libm on most operating systems. Don't yet
288 * support the exponent (e,E) and sigfig (g,G). Also, fmtint()
289 * was pretty badly broken, it just wasn't being exercised in ways
290 * which showed it, so that's been fixed. Also, formated the code
291 * to mutt conventions, and removed dead code left over from the
292 * original. Also, there is now a builtin-test, just compile with:
293 * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
294 * and run snprintf for results.
296 * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
297 * The PGP code was using unsigned hexadecimal formats.
298 * Unfortunately, unsigned formats simply didn't work.
300 * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
301 * The original code assumed that both snprintf() and vsnprintf() were
302 * missing. Some systems only have snprintf() but not vsnprintf(), so
303 * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
305 **************************************************************/
309 #if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF)
313 #include <sys/types.h>
315 /* Define this as a fall through, HAVE_STDARG_H is probably already set */
317 #if !defined(HAVE_STDARG_H) && !defined(HAVE_VARARGS_H)
318 # define HAVE_VARARGS_H 1
321 /* varargs declarations: */
323 #if defined(HAVE_STDARG_H)
324 /*# include <stdarg.h>*/
325 # define HAVE_STDARGS /* let's hope that works everywhere (mj) */
326 # define VA_LOCAL_DECL va_list ap
327 # define VA_START(f) va_start(ap, f)
328 # define VA_SHIFT(v,t) ; /* no-op for ANSI */
329 # define VA_END va_end(ap)
331 # if defined(HAVE_VARARGS_H)
332 # include <varargs.h>
334 # define VA_LOCAL_DECL va_list ap
335 # define VA_START(f) va_start(ap) /* f is ignored! */
336 # define VA_SHIFT(v,t) v = va_arg(ap,t)
337 # define VA_END va_end(ap)
339 /*XX ** NO VARARGS ** XX*/
343 /*int snprintf (char *str, size_t count, const char *fmt, ...);*/
344 /*int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);*/
346 static void dopr (char *buffer, size_t maxlen, const char *format,
348 static void fmtstr (char *buffer, size_t *currlen, size_t maxlen,
349 char *value, int flags, int min, int max);
350 static void fmtint (char *buffer, size_t *currlen, size_t maxlen,
351 long value, int base, int min, int max, int flags);
352 static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
353 long double fvalue, int min, int max, int flags);
354 static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c );
357 * dopr(): poor man's version of doprintf
360 /* format read states */
361 #define DP_S_DEFAULT 0
370 /* format flags - Bits */
371 #define DP_F_MINUS (1 << 0)
372 #define DP_F_PLUS (1 << 1)
373 #define DP_F_SPACE (1 << 2)
374 #define DP_F_NUM (1 << 3)
375 #define DP_F_ZERO (1 << 4)
376 #define DP_F_UP (1 << 5)
377 #define DP_F_UNSIGNED (1 << 6)
379 /* Conversion Flags */
382 #define DP_C_LDOUBLE 3
384 #define char_to_int(p) (p - '0')
385 #define MAX(p,q) ((p >= q) ? p : q)
387 static void dopr (char *buffer, size_t maxlen, const char *format, va_list args)
400 state = DP_S_DEFAULT;
401 currlen = flags = cflags = min = 0;
405 while (state != DP_S_DONE)
407 if ((ch == '\0') || (currlen >= maxlen))
416 dopr_outch (buffer, &currlen, maxlen, ch);
448 if (isdigit((unsigned char)ch))
450 min = 10*min + char_to_int (ch);
455 min = va_arg (args, int);
472 if (isdigit((unsigned char)ch))
476 max = 10*max + char_to_int (ch);
481 max = va_arg (args, int);
489 /* Currently, we don't support Long Long, bummer */
501 cflags = DP_C_LDOUBLE;
514 if (cflags == DP_C_SHORT)
515 value = va_arg (args, short int);
516 else if (cflags == DP_C_LONG)
517 value = va_arg (args, long int);
519 value = va_arg (args, int);
520 fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
523 flags |= DP_F_UNSIGNED;
524 if (cflags == DP_C_SHORT)
525 value = va_arg (args, unsigned short int);
526 else if (cflags == DP_C_LONG)
527 value = va_arg (args, unsigned long int);
529 value = va_arg (args, unsigned int);
530 fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
533 flags |= DP_F_UNSIGNED;
534 if (cflags == DP_C_SHORT)
535 value = va_arg (args, unsigned short int);
536 else if (cflags == DP_C_LONG)
537 value = va_arg (args, unsigned long int);
539 value = va_arg (args, unsigned int);
540 fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
545 flags |= DP_F_UNSIGNED;
546 if (cflags == DP_C_SHORT)
547 value = va_arg (args, unsigned short int);
548 else if (cflags == DP_C_LONG)
549 value = va_arg (args, unsigned long int);
551 value = va_arg (args, unsigned int);
552 fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
555 if (cflags == DP_C_LDOUBLE)
556 fvalue = va_arg (args, long double);
558 fvalue = va_arg (args, double);
559 /* um, floating point? */
560 fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
565 if (cflags == DP_C_LDOUBLE)
566 fvalue = va_arg (args, long double);
568 fvalue = va_arg (args, double);
573 if (cflags == DP_C_LDOUBLE)
574 fvalue = va_arg (args, long double);
576 fvalue = va_arg (args, double);
579 dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
582 strvalue = va_arg (args, char *);
584 max = maxlen; /* ie, no max */
585 fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
588 strvalue = va_arg (args, void *);
589 fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
592 if (cflags == DP_C_SHORT)
595 num = va_arg (args, short int *);
598 else if (cflags == DP_C_LONG)
601 num = va_arg (args, long int *);
607 num = va_arg (args, int *);
612 dopr_outch (buffer, &currlen, maxlen, ch);
615 /* not supported yet, treat as next char */
623 state = DP_S_DEFAULT;
624 flags = cflags = min = 0;
631 break; /* some picky compilers need this */
634 if (currlen < maxlen - 1)
635 buffer[currlen] = '\0';
637 buffer[maxlen - 1] = '\0';
640 static void fmtstr (char *buffer, size_t *currlen, size_t maxlen,
641 char *value, int flags, int min, int max)
643 int padlen, strln; /* amount to pad */
651 for (strln = 0; value[strln]; ++strln); /* strlen */
652 padlen = min - strln;
655 if (flags & DP_F_MINUS)
656 padlen = -padlen; /* Left Justify */
658 while ((padlen > 0) && (cnt < max))
660 dopr_outch (buffer, currlen, maxlen, ' ');
664 while (*value && (cnt < max))
666 dopr_outch (buffer, currlen, maxlen, *value++);
669 while ((padlen < 0) && (cnt < max))
671 dopr_outch (buffer, currlen, maxlen, ' ');
677 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
679 static void fmtint (char *buffer, size_t *currlen, size_t maxlen,
680 long value, int base, int min, int max, int flags)
683 unsigned long uvalue;
686 int spadlen = 0; /* amount to space pad */
687 int zpadlen = 0; /* amount to zero pad */
695 if(!(flags & DP_F_UNSIGNED))
702 if (flags & DP_F_PLUS) /* Do a sign (+/i) */
705 if (flags & DP_F_SPACE)
709 if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
713 (caps? "0123456789ABCDEF":"0123456789abcdef")
714 [uvalue % (unsigned)base ];
715 uvalue = (uvalue / (unsigned)base );
716 } while(uvalue && (place < 20));
717 if (place == 20) place--;
720 zpadlen = max - place;
721 spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
722 if (zpadlen < 0) zpadlen = 0;
723 if (spadlen < 0) spadlen = 0;
724 if (flags & DP_F_ZERO)
726 zpadlen = MAX(zpadlen, spadlen);
729 if (flags & DP_F_MINUS)
730 spadlen = -spadlen; /* Left Justifty */
732 #ifdef DEBUG_SNPRINTF
733 dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
734 zpadlen, spadlen, min, max, place));
740 dopr_outch (buffer, currlen, maxlen, ' ');
746 dopr_outch (buffer, currlen, maxlen, signvalue);
753 dopr_outch (buffer, currlen, maxlen, '0');
760 dopr_outch (buffer, currlen, maxlen, convert[--place]);
762 /* Left Justified spaces */
763 while (spadlen < 0) {
764 dopr_outch (buffer, currlen, maxlen, ' ');
769 static long double abs_val (long double value)
771 long double result = value;
779 static long double pow10 (int exp)
781 long double result = 1;
792 static long round (long double value)
797 value = value - intpart;
804 static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
805 long double fvalue, int min, int max, int flags)
813 int padlen = 0; /* amount to pad */
820 * AIX manpage says the default is 0, but Solaris says the default
821 * is 6, and sprintf on AIX defaults to 6
826 ufvalue = abs_val (fvalue);
831 if (flags & DP_F_PLUS) /* Do a sign (+/i) */
834 if (flags & DP_F_SPACE)
838 if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
844 * Sorry, we only support 9 digits past the decimal because of our
850 /* We "cheat" by converting the fractional part to integer by
851 * multiplying by a factor of 10
853 fracpart = round ((pow10 (max)) * (ufvalue - intpart));
855 if (fracpart >= pow10 (max))
858 fracpart -= pow10 (max);
861 #ifdef DEBUG_SNPRINTF
862 dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart));
865 /* Convert integer part */
868 (caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10];
869 intpart = (intpart / 10);
870 } while(intpart && (iplace < 20));
871 if (iplace == 20) iplace--;
872 iconvert[iplace] = 0;
874 /* Convert fractional part */
877 (caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10];
878 fracpart = (fracpart / 10);
879 } while(fracpart && (fplace < 20));
880 if (fplace == 20) fplace--;
881 fconvert[fplace] = 0;
883 /* -1 for decimal point, another -1 if we are printing a sign */
884 padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
885 zpadlen = max - fplace;
890 if (flags & DP_F_MINUS)
891 padlen = -padlen; /* Left Justifty */
893 if ((flags & DP_F_ZERO) && (padlen > 0))
897 dopr_outch (buffer, currlen, maxlen, signvalue);
903 dopr_outch (buffer, currlen, maxlen, '0');
909 dopr_outch (buffer, currlen, maxlen, ' ');
913 dopr_outch (buffer, currlen, maxlen, signvalue);
916 dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
919 * Decimal point. This should probably use locale to find the correct
922 dopr_outch (buffer, currlen, maxlen, '.');
925 dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
929 dopr_outch (buffer, currlen, maxlen, '0');
935 dopr_outch (buffer, currlen, maxlen, ' ');
940 static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c)
942 if (*currlen < maxlen)
943 buffer[(*currlen)++] = c;
945 #endif /* !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) */
947 #ifndef HAVE_VSNPRINTF
948 int vsnprintf (char *str, size_t count, const char *fmt, va_list args)
951 dopr(str, count, fmt, args);
954 #endif /* !HAVE_VSNPRINTF */
956 #ifndef HAVE_SNPRINTF
959 int snprintf (char *str,size_t count,const char *fmt,...)
961 int snprintf (va_alist) va_dcl
972 VA_SHIFT (str, char *);
973 VA_SHIFT (count, size_t );
974 VA_SHIFT (fmt, char *);
975 (void) vsnprintf(str, count, fmt, ap);
982 #define LONG_STRING 1024
986 char buf1[LONG_STRING];
987 char buf2[LONG_STRING];
1002 double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996,
1003 0.9996, 1.996, 4.136, 0};
1016 long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
1021 printf ("Testing snprintf format codes against system sprintf...\n");
1023 for (x = 0; fp_fmt[x] != NULL ; x++)
1024 for (y = 0; fp_nums[y] != 0 ; y++)
1026 snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]);
1027 sprintf (buf2, fp_fmt[x], fp_nums[y]);
1028 if (strcmp (buf1, buf2))
1030 printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n",
1031 fp_fmt[x], buf1, buf2);
1037 for (x = 0; int_fmt[x] != NULL ; x++)
1038 for (y = 0; int_nums[y] != 0 ; y++)
1040 snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]);
1041 sprintf (buf2, int_fmt[x], int_nums[y]);
1042 if (strcmp (buf1, buf2))
1044 printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n",
1045 int_fmt[x], buf1, buf2);
1050 printf ("%d tests failed out of %d.\n", fail, num);
1052 #endif /* SNPRINTF_TEST */
1054 #endif /* !HAVE_SNPRINTF */