5 * by JH <jheinonen@users.sourceforge.net>
7 * Copyright (C) Jaakko Heinonen
22 #ifdef HANDLE_MULTIBYTE
23 # include <mbswidth.h>
45 while( ( *str = tolower ( *str ) ) )
58 for(t = s; ISSPACE(*t); t++);
60 memmove(s, t, strlen(t)+1);
62 for (tt = t = s; *t != '\0'; t++)
76 /* varargs declarations: */
79 # define MY_VA_LOCAL_DECL va_list ap
80 # define MY_VA_START(f) va_start(ap, f)
81 # define MY_VA_SHIFT(v,t) v = va_arg(ap, t)
82 # define MY_VA_END va_end(ap)
84 # error HAVE_STDARG_H not defined
88 mkstr (const char *format, ... )
94 (char *) abook_malloc (size);
96 (char *) xmalloc (size);
99 assert(format != NULL);
104 n = vsnprintf (buffer, size,
108 if (n > -1 && n < size)
118 (char *) abook_realloc (buffer, size);
120 (char *) xrealloc (buffer, size);
127 strconcat (const char *str, ...)
135 l = 1 + strlen (str);
137 MY_VA_SHIFT(s, char*);
140 MY_VA_SHIFT(s, char*);
153 strcpy (concat, str);
155 MY_VA_SHIFT(s, char*);
158 MY_VA_SHIFT(s, char*);
167 safe_strcmp(const char *s1, const char *s2)
169 if (s1 == NULL && s2 == NULL) return 0;
170 if (s1 == NULL) return -1;
171 if (s2 == NULL) return 1;
173 return strcmp(s1, s2);
177 safe_strcoll(const char *s1, const char *s2)
180 if (s1 == NULL && s2 == NULL) return 0;
181 if (s1 == NULL) return -1;
182 if (s2 == NULL) return 1;
184 return strcoll(s1, s2);
185 #else /* fall back to strcmp */
186 return safe_strcmp(s1, s2);
196 if( (dir = (char *)malloc(size)) == NULL)
201 while( getcwd(dir, size) == NULL && errno == ERANGE )
202 if( (dir = (char *)realloc(dir, size *=2)) == NULL)
208 #define INITIAL_SIZE 128
210 # define abook_malloc(X) xmalloc(X)
211 # define abook_realloc(X, XX) xrealloc(X, XX)
217 char *buf; /* buffer for line */
218 size_t size; /* size of buffer */
219 size_t inc; /* how much to enlarge buffer */
220 size_t len; /* # of chars stored into buf before '\0' */
222 const size_t thres = 128; /* initial buffer size (most lines should
223 fit into this size, so think of this as
224 the "long line threshold"). */
225 const size_t mucho = 128; /* if there is at least this much wasted
226 space when the whole buffer has been
227 read, try to reclaim it. Don't make
228 this too small, else there is too much
229 time wasted trying to reclaim a couple
231 const size_t mininc = 64; /* minimum number of bytes by which
232 to increase the allocated memory */
236 buf = (char *)abook_malloc(size);
238 while (fgets(buf+len, size-len, f) != NULL) {
239 len += strlen(buf+len);
240 if (len > 0 && buf[len-1] == '\n')
241 break; /* the whole line has been read */
243 for (inc = size, p = NULL; inc > mininc; inc /= 2)
244 if ((p = (char *)abook_realloc(buf, size + inc)) !=
254 return NULL; /* nothing read (eof or error) */
257 if (buf[len-1] == '\n') /* remove newline, if there */
260 if (size - len > mucho) { /* a plenitude of unused memory? */
261 p = (char *)abook_realloc(buf, len+1);
272 strwidth(const char *s)
275 #ifdef HANDLE_MULTIBYTE
276 return (int)mbswidth(s, 0);
283 bytes2width(const char *s, int width)
286 #ifdef HANDLE_MULTIBYTE
287 return mbsnbytes(s, strlen(s), width, 0);
293 /**************************************************************
295 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
296 * A bombproof version of doprnt (dopr) included.
297 * Sigh. This sort of thing is always nasty do deal with. Note that
298 * the version here does not include floating point...
300 * snprintf() is used instead of sprintf() as it does limit checks
301 * for string length. This covers a nasty loophole.
303 * The other functions are there to prevent NULL pointers from
304 * causing nast effects.
307 * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
308 * This was ugly. It is still ugly. I opted out of floating point
309 * numbers, but the formatter understands just about everything
310 * from the normal C string format, at least as far as I can tell from
311 * the Solaris 2.5 printf(3S) man page.
313 * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
314 * Ok, added some minimal floating point support, which means this
315 * probably requires libm on most operating systems. Don't yet
316 * support the exponent (e,E) and sigfig (g,G). Also, fmtint()
317 * was pretty badly broken, it just wasn't being exercised in ways
318 * which showed it, so that's been fixed. Also, formated the code
319 * to mutt conventions, and removed dead code left over from the
320 * original. Also, there is now a builtin-test, just compile with:
321 * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
322 * and run snprintf for results.
324 * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
325 * The PGP code was using unsigned hexadecimal formats.
326 * Unfortunately, unsigned formats simply didn't work.
328 * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
329 * The original code assumed that both snprintf() and vsnprintf() were
330 * missing. Some systems only have snprintf() but not vsnprintf(), so
331 * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
333 **************************************************************/
337 #if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF)
341 #include <sys/types.h>
343 /* Define this as a fall through, HAVE_STDARG_H is probably already set */
345 #if !defined(HAVE_STDARG_H) && !defined(HAVE_VARARGS_H)
346 # define HAVE_VARARGS_H 1
349 /* varargs declarations: */
351 #if defined(HAVE_STDARG_H)
352 /*# include <stdarg.h>*/
353 # define HAVE_STDARGS /* let's hope that works everywhere (mj) */
354 # define VA_LOCAL_DECL va_list ap
355 # define VA_START(f) va_start(ap, f)
356 # define VA_SHIFT(v,t) ; /* no-op for ANSI */
357 # define VA_END va_end(ap)
359 # if defined(HAVE_VARARGS_H)
360 # include <varargs.h>
362 # define VA_LOCAL_DECL va_list ap
363 # define VA_START(f) va_start(ap) /* f is ignored! */
364 # define VA_SHIFT(v,t) v = va_arg(ap,t)
365 # define VA_END va_end(ap)
367 /*XX ** NO VARARGS ** XX*/
371 /*int snprintf (char *str, size_t count, const char *fmt, ...);*/
372 /*int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);*/
374 static void dopr (char *buffer, size_t maxlen, const char *format,
376 static void fmtstr (char *buffer, size_t *currlen, size_t maxlen,
377 char *value, int flags, int min, int max);
378 static void fmtint (char *buffer, size_t *currlen, size_t maxlen,
379 long value, int base, int min, int max, int flags);
380 static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
381 long double fvalue, int min, int max, int flags);
382 static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c );
385 * dopr(): poor man's version of doprintf
388 /* format read states */
389 #define DP_S_DEFAULT 0
398 /* format flags - Bits */
399 #define DP_F_MINUS (1 << 0)
400 #define DP_F_PLUS (1 << 1)
401 #define DP_F_SPACE (1 << 2)
402 #define DP_F_NUM (1 << 3)
403 #define DP_F_ZERO (1 << 4)
404 #define DP_F_UP (1 << 5)
405 #define DP_F_UNSIGNED (1 << 6)
407 /* Conversion Flags */
410 #define DP_C_LDOUBLE 3
412 #define char_to_int(p) (p - '0')
413 #define MAX(p,q) ((p >= q) ? p : q)
415 static void dopr (char *buffer, size_t maxlen, const char *format, va_list args)
428 state = DP_S_DEFAULT;
429 currlen = flags = cflags = min = 0;
433 while (state != DP_S_DONE)
435 if ((ch == '\0') || (currlen >= maxlen))
444 dopr_outch (buffer, &currlen, maxlen, ch);
476 if (isdigit((unsigned char)ch))
478 min = 10*min + char_to_int (ch);
483 min = va_arg (args, int);
500 if (isdigit((unsigned char)ch))
504 max = 10*max + char_to_int (ch);
509 max = va_arg (args, int);
517 /* Currently, we don't support Long Long, bummer */
529 cflags = DP_C_LDOUBLE;
542 if (cflags == DP_C_SHORT)
543 value = va_arg (args, short int);
544 else if (cflags == DP_C_LONG)
545 value = va_arg (args, long int);
547 value = va_arg (args, int);
548 fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
551 flags |= DP_F_UNSIGNED;
552 if (cflags == DP_C_SHORT)
553 value = va_arg (args, unsigned short int);
554 else if (cflags == DP_C_LONG)
555 value = va_arg (args, unsigned long int);
557 value = va_arg (args, unsigned int);
558 fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
561 flags |= DP_F_UNSIGNED;
562 if (cflags == DP_C_SHORT)
563 value = va_arg (args, unsigned short int);
564 else if (cflags == DP_C_LONG)
565 value = va_arg (args, unsigned long int);
567 value = va_arg (args, unsigned int);
568 fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
573 flags |= DP_F_UNSIGNED;
574 if (cflags == DP_C_SHORT)
575 value = va_arg (args, unsigned short int);
576 else if (cflags == DP_C_LONG)
577 value = va_arg (args, unsigned long int);
579 value = va_arg (args, unsigned int);
580 fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
583 if (cflags == DP_C_LDOUBLE)
584 fvalue = va_arg (args, long double);
586 fvalue = va_arg (args, double);
587 /* um, floating point? */
588 fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
593 if (cflags == DP_C_LDOUBLE)
594 fvalue = va_arg (args, long double);
596 fvalue = va_arg (args, double);
601 if (cflags == DP_C_LDOUBLE)
602 fvalue = va_arg (args, long double);
604 fvalue = va_arg (args, double);
607 dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
610 strvalue = va_arg (args, char *);
612 max = maxlen; /* ie, no max */
613 fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
616 strvalue = va_arg (args, void *);
617 fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
620 if (cflags == DP_C_SHORT)
623 num = va_arg (args, short int *);
626 else if (cflags == DP_C_LONG)
629 num = va_arg (args, long int *);
635 num = va_arg (args, int *);
640 dopr_outch (buffer, &currlen, maxlen, ch);
643 /* not supported yet, treat as next char */
651 state = DP_S_DEFAULT;
652 flags = cflags = min = 0;
659 break; /* some picky compilers need this */
662 if (currlen < maxlen - 1)
663 buffer[currlen] = '\0';
665 buffer[maxlen - 1] = '\0';
668 static void fmtstr (char *buffer, size_t *currlen, size_t maxlen,
669 char *value, int flags, int min, int max)
671 int padlen, strln; /* amount to pad */
679 for (strln = 0; value[strln]; ++strln); /* strlen */
680 padlen = min - strln;
683 if (flags & DP_F_MINUS)
684 padlen = -padlen; /* Left Justify */
686 while ((padlen > 0) && (cnt < max))
688 dopr_outch (buffer, currlen, maxlen, ' ');
692 while (*value && (cnt < max))
694 dopr_outch (buffer, currlen, maxlen, *value++);
697 while ((padlen < 0) && (cnt < max))
699 dopr_outch (buffer, currlen, maxlen, ' ');
705 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
707 static void fmtint (char *buffer, size_t *currlen, size_t maxlen,
708 long value, int base, int min, int max, int flags)
711 unsigned long uvalue;
714 int spadlen = 0; /* amount to space pad */
715 int zpadlen = 0; /* amount to zero pad */
723 if(!(flags & DP_F_UNSIGNED))
730 if (flags & DP_F_PLUS) /* Do a sign (+/i) */
733 if (flags & DP_F_SPACE)
737 if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
741 (caps? "0123456789ABCDEF":"0123456789abcdef")
742 [uvalue % (unsigned)base ];
743 uvalue = (uvalue / (unsigned)base );
744 } while(uvalue && (place < 20));
745 if (place == 20) place--;
748 zpadlen = max - place;
749 spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
750 if (zpadlen < 0) zpadlen = 0;
751 if (spadlen < 0) spadlen = 0;
752 if (flags & DP_F_ZERO)
754 zpadlen = MAX(zpadlen, spadlen);
757 if (flags & DP_F_MINUS)
758 spadlen = -spadlen; /* Left Justifty */
760 #ifdef DEBUG_SNPRINTF
761 dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
762 zpadlen, spadlen, min, max, place));
768 dopr_outch (buffer, currlen, maxlen, ' ');
774 dopr_outch (buffer, currlen, maxlen, signvalue);
781 dopr_outch (buffer, currlen, maxlen, '0');
788 dopr_outch (buffer, currlen, maxlen, convert[--place]);
790 /* Left Justified spaces */
791 while (spadlen < 0) {
792 dopr_outch (buffer, currlen, maxlen, ' ');
797 static long double abs_val (long double value)
799 long double result = value;
807 static long double pow10 (int exp)
809 long double result = 1;
820 static long round (long double value)
825 value = value - intpart;
832 static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
833 long double fvalue, int min, int max, int flags)
841 int padlen = 0; /* amount to pad */
848 * AIX manpage says the default is 0, but Solaris says the default
849 * is 6, and sprintf on AIX defaults to 6
854 ufvalue = abs_val (fvalue);
859 if (flags & DP_F_PLUS) /* Do a sign (+/i) */
862 if (flags & DP_F_SPACE)
866 if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
872 * Sorry, we only support 9 digits past the decimal because of our
878 /* We "cheat" by converting the fractional part to integer by
879 * multiplying by a factor of 10
881 fracpart = round ((pow10 (max)) * (ufvalue - intpart));
883 if (fracpart >= pow10 (max))
886 fracpart -= pow10 (max);
889 #ifdef DEBUG_SNPRINTF
890 dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart));
893 /* Convert integer part */
896 (caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10];
897 intpart = (intpart / 10);
898 } while(intpart && (iplace < 20));
899 if (iplace == 20) iplace--;
900 iconvert[iplace] = 0;
902 /* Convert fractional part */
905 (caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10];
906 fracpart = (fracpart / 10);
907 } while(fracpart && (fplace < 20));
908 if (fplace == 20) fplace--;
909 fconvert[fplace] = 0;
911 /* -1 for decimal point, another -1 if we are printing a sign */
912 padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
913 zpadlen = max - fplace;
918 if (flags & DP_F_MINUS)
919 padlen = -padlen; /* Left Justifty */
921 if ((flags & DP_F_ZERO) && (padlen > 0))
925 dopr_outch (buffer, currlen, maxlen, signvalue);
931 dopr_outch (buffer, currlen, maxlen, '0');
937 dopr_outch (buffer, currlen, maxlen, ' ');
941 dopr_outch (buffer, currlen, maxlen, signvalue);
944 dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
947 * Decimal point. This should probably use locale to find the correct
950 dopr_outch (buffer, currlen, maxlen, '.');
953 dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
957 dopr_outch (buffer, currlen, maxlen, '0');
963 dopr_outch (buffer, currlen, maxlen, ' ');
968 static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c)
970 if (*currlen < maxlen)
971 buffer[(*currlen)++] = c;
973 #endif /* !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) */
975 #ifndef HAVE_VSNPRINTF
976 int vsnprintf (char *str, size_t count, const char *fmt, va_list args)
979 dopr(str, count, fmt, args);
982 #endif /* !HAVE_VSNPRINTF */
984 #ifndef HAVE_SNPRINTF
987 int snprintf (char *str,size_t count,const char *fmt,...)
989 int snprintf (va_alist) va_dcl
1000 VA_SHIFT (str, char *);
1001 VA_SHIFT (count, size_t );
1002 VA_SHIFT (fmt, char *);
1003 (void) vsnprintf(str, count, fmt, ap);
1005 return(strlen(str));
1008 #ifdef TEST_SNPRINTF
1010 #define LONG_STRING 1024
1014 char buf1[LONG_STRING];
1015 char buf2[LONG_STRING];
1030 double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996,
1031 0.9996, 1.996, 4.136, 0};
1044 long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
1049 printf ("Testing snprintf format codes against system sprintf...\n");
1051 for (x = 0; fp_fmt[x] != NULL ; x++)
1052 for (y = 0; fp_nums[y] != 0 ; y++)
1054 snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]);
1055 sprintf (buf2, fp_fmt[x], fp_nums[y]);
1056 if (strcmp (buf1, buf2))
1058 printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n",
1059 fp_fmt[x], buf1, buf2);
1065 for (x = 0; int_fmt[x] != NULL ; x++)
1066 for (y = 0; int_nums[y] != 0 ; y++)
1068 snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]);
1069 sprintf (buf2, int_fmt[x], int_nums[y]);
1070 if (strcmp (buf1, buf2))
1072 printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n",
1073 int_fmt[x], buf1, buf2);
1078 printf ("%d tests failed out of %d.\n", fail, num);
1080 #endif /* SNPRINTF_TEST */
1082 #endif /* !HAVE_SNPRINTF */