5 * by JH <jheinonen@users.sourceforge.net>
7 * Copyright (C) Jaakko Heinonen
19 #ifdef HANDLE_MULTIBYTE
20 # include <mbswidth.h>
40 while( ( *str = tolower ( *str ) ) )
53 for(t = s; isspace(*t); t++);
55 memmove(s, t, strlen(t)+1);
57 for (tt = t = s; *t != '\0'; t++)
71 /* varargs declarations: */
74 # define MY_VA_LOCAL_DECL va_list ap
75 # define MY_VA_START(f) va_start(ap, f)
76 # define MY_VA_SHIFT(v,t) v = va_arg(ap, t)
77 # define MY_VA_END va_end(ap)
79 # error HAVE_STDARG_H not defined
83 mkstr (const char *format, ... )
87 char *buffer = xmalloc (size);
89 assert(format != NULL);
94 n = vsnprintf (buffer, size,
98 if (n > -1 && n < size)
106 buffer = xrealloc(buffer, size);
112 strconcat (const char *str, ...)
120 l = 1 + strlen (str);
122 MY_VA_SHIFT(s, char*);
125 MY_VA_SHIFT(s, char*);
134 strcpy (concat, str);
136 MY_VA_SHIFT(s, char*);
139 MY_VA_SHIFT(s, char*);
148 safe_strcmp(const char *s1, const char *s2)
150 if (s1 == NULL && s2 == NULL) return 0;
151 if (s1 == NULL) return -1;
152 if (s2 == NULL) return 1;
154 return strcmp(s1, s2);
158 safe_strcoll(const char *s1, const char *s2)
161 if (s1 == NULL && s2 == NULL) return 0;
162 if (s1 == NULL) return -1;
163 if (s2 == NULL) return 1;
165 return strcoll(s1, s2);
166 #else /* fall back to strcmp */
167 return safe_strcmp(s1, s2);
177 if( (dir = xmalloc(size)) == NULL)
182 while( getcwd(dir, size) == NULL && errno == ERANGE )
183 if( (dir = xrealloc(dir, size *=2)) == NULL)
189 #define INITIAL_SIZE 128
194 char *buf; /* buffer for line */
195 size_t size; /* size of buffer */
196 size_t inc; /* how much to enlarge buffer */
197 size_t len; /* # of chars stored into buf before '\0' */
199 const size_t thres = 128; /* initial buffer size (most lines should
200 fit into this size, so think of this as
201 the "long line threshold"). */
202 const size_t mucho = 128; /* if there is at least this much wasted
203 space when the whole buffer has been
204 read, try to reclaim it. Don't make
205 this too small, else there is too much
206 time wasted trying to reclaim a couple
208 const size_t mininc = 64; /* minimum number of bytes by which
209 to increase the allocated memory */
215 while (fgets(buf+len, size-len, f) != NULL) {
216 len += strlen(buf+len);
217 if (len > 0 && buf[len-1] == '\n')
218 break; /* the whole line has been read */
220 for (inc = size, p = NULL; inc > mininc; inc /= 2)
221 if ((p = xrealloc_inc(buf, size, inc)) !=
231 return NULL; /* nothing read (eof or error) */
234 if (buf[len-1] == '\n') /* remove newline, if there */
237 if (size - len > mucho) { /* a plenitude of unused memory? */
238 p = xrealloc_inc(buf, len, 1);
249 strwidth(const char *s)
252 #ifdef HANDLE_MULTIBYTE
253 return (int)mbswidth(s, 0);
260 bytes2width(const char *s, int width)
263 #ifdef HANDLE_MULTIBYTE
264 return mbsnbytes(s, strlen(s), width, 0);
270 /**************************************************************
272 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
273 * A bombproof version of doprnt (dopr) included.
274 * Sigh. This sort of thing is always nasty do deal with. Note that
275 * the version here does not include floating point...
277 * snprintf() is used instead of sprintf() as it does limit checks
278 * for string length. This covers a nasty loophole.
280 * The other functions are there to prevent NULL pointers from
281 * causing nast effects.
284 * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
285 * This was ugly. It is still ugly. I opted out of floating point
286 * numbers, but the formatter understands just about everything
287 * from the normal C string format, at least as far as I can tell from
288 * the Solaris 2.5 printf(3S) man page.
290 * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
291 * Ok, added some minimal floating point support, which means this
292 * probably requires libm on most operating systems. Don't yet
293 * support the exponent (e,E) and sigfig (g,G). Also, fmtint()
294 * was pretty badly broken, it just wasn't being exercised in ways
295 * which showed it, so that's been fixed. Also, formated the code
296 * to mutt conventions, and removed dead code left over from the
297 * original. Also, there is now a builtin-test, just compile with:
298 * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
299 * and run snprintf for results.
301 * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
302 * The PGP code was using unsigned hexadecimal formats.
303 * Unfortunately, unsigned formats simply didn't work.
305 * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
306 * The original code assumed that both snprintf() and vsnprintf() were
307 * missing. Some systems only have snprintf() but not vsnprintf(), so
308 * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
310 **************************************************************/
314 #if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF)
318 #include <sys/types.h>
320 /* Define this as a fall through, HAVE_STDARG_H is probably already set */
322 #if !defined(HAVE_STDARG_H) && !defined(HAVE_VARARGS_H)
323 # define HAVE_VARARGS_H 1
326 /* varargs declarations: */
328 #if defined(HAVE_STDARG_H)
329 /*# include <stdarg.h>*/
330 # define HAVE_STDARGS /* let's hope that works everywhere (mj) */
331 # define VA_LOCAL_DECL va_list ap
332 # define VA_START(f) va_start(ap, f)
333 # define VA_SHIFT(v,t) ; /* no-op for ANSI */
334 # define VA_END va_end(ap)
336 # if defined(HAVE_VARARGS_H)
337 # include <varargs.h>
339 # define VA_LOCAL_DECL va_list ap
340 # define VA_START(f) va_start(ap) /* f is ignored! */
341 # define VA_SHIFT(v,t) v = va_arg(ap,t)
342 # define VA_END va_end(ap)
344 /*XX ** NO VARARGS ** XX*/
348 /*int snprintf (char *str, size_t count, const char *fmt, ...);*/
349 /*int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);*/
351 static void dopr (char *buffer, size_t maxlen, const char *format,
353 static void fmtstr (char *buffer, size_t *currlen, size_t maxlen,
354 char *value, int flags, int min, int max);
355 static void fmtint (char *buffer, size_t *currlen, size_t maxlen,
356 long value, int base, int min, int max, int flags);
357 static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
358 long double fvalue, int min, int max, int flags);
359 static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c );
362 * dopr(): poor man's version of doprintf
365 /* format read states */
366 #define DP_S_DEFAULT 0
375 /* format flags - Bits */
376 #define DP_F_MINUS (1 << 0)
377 #define DP_F_PLUS (1 << 1)
378 #define DP_F_SPACE (1 << 2)
379 #define DP_F_NUM (1 << 3)
380 #define DP_F_ZERO (1 << 4)
381 #define DP_F_UP (1 << 5)
382 #define DP_F_UNSIGNED (1 << 6)
384 /* Conversion Flags */
387 #define DP_C_LDOUBLE 3
389 #define char_to_int(p) (p - '0')
390 #define MAX(p,q) ((p >= q) ? p : q)
392 static void dopr (char *buffer, size_t maxlen, const char *format, va_list args)
405 state = DP_S_DEFAULT;
406 currlen = flags = cflags = min = 0;
410 while (state != DP_S_DONE)
412 if ((ch == '\0') || (currlen >= maxlen))
421 dopr_outch (buffer, &currlen, maxlen, ch);
453 if (isdigit((unsigned char)ch))
455 min = 10*min + char_to_int (ch);
460 min = va_arg (args, int);
477 if (isdigit((unsigned char)ch))
481 max = 10*max + char_to_int (ch);
486 max = va_arg (args, int);
494 /* Currently, we don't support Long Long, bummer */
506 cflags = DP_C_LDOUBLE;
519 if (cflags == DP_C_SHORT)
520 value = va_arg (args, short int);
521 else if (cflags == DP_C_LONG)
522 value = va_arg (args, long int);
524 value = va_arg (args, int);
525 fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
528 flags |= DP_F_UNSIGNED;
529 if (cflags == DP_C_SHORT)
530 value = va_arg (args, unsigned short int);
531 else if (cflags == DP_C_LONG)
532 value = va_arg (args, unsigned long int);
534 value = va_arg (args, unsigned int);
535 fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
538 flags |= DP_F_UNSIGNED;
539 if (cflags == DP_C_SHORT)
540 value = va_arg (args, unsigned short int);
541 else if (cflags == DP_C_LONG)
542 value = va_arg (args, unsigned long int);
544 value = va_arg (args, unsigned int);
545 fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
550 flags |= DP_F_UNSIGNED;
551 if (cflags == DP_C_SHORT)
552 value = va_arg (args, unsigned short int);
553 else if (cflags == DP_C_LONG)
554 value = va_arg (args, unsigned long int);
556 value = va_arg (args, unsigned int);
557 fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
560 if (cflags == DP_C_LDOUBLE)
561 fvalue = va_arg (args, long double);
563 fvalue = va_arg (args, double);
564 /* um, floating point? */
565 fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
570 if (cflags == DP_C_LDOUBLE)
571 fvalue = va_arg (args, long double);
573 fvalue = va_arg (args, double);
578 if (cflags == DP_C_LDOUBLE)
579 fvalue = va_arg (args, long double);
581 fvalue = va_arg (args, double);
584 dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
587 strvalue = va_arg (args, char *);
589 max = maxlen; /* ie, no max */
590 fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
593 strvalue = va_arg (args, void *);
594 fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
597 if (cflags == DP_C_SHORT)
600 num = va_arg (args, short int *);
603 else if (cflags == DP_C_LONG)
606 num = va_arg (args, long int *);
612 num = va_arg (args, int *);
617 dopr_outch (buffer, &currlen, maxlen, ch);
620 /* not supported yet, treat as next char */
628 state = DP_S_DEFAULT;
629 flags = cflags = min = 0;
636 break; /* some picky compilers need this */
639 if (currlen < maxlen - 1)
640 buffer[currlen] = '\0';
642 buffer[maxlen - 1] = '\0';
645 static void fmtstr (char *buffer, size_t *currlen, size_t maxlen,
646 char *value, int flags, int min, int max)
648 int padlen, strln; /* amount to pad */
656 for (strln = 0; value[strln]; ++strln); /* strlen */
657 padlen = min - strln;
660 if (flags & DP_F_MINUS)
661 padlen = -padlen; /* Left Justify */
663 while ((padlen > 0) && (cnt < max))
665 dopr_outch (buffer, currlen, maxlen, ' ');
669 while (*value && (cnt < max))
671 dopr_outch (buffer, currlen, maxlen, *value++);
674 while ((padlen < 0) && (cnt < max))
676 dopr_outch (buffer, currlen, maxlen, ' ');
682 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
684 static void fmtint (char *buffer, size_t *currlen, size_t maxlen,
685 long value, int base, int min, int max, int flags)
688 unsigned long uvalue;
691 int spadlen = 0; /* amount to space pad */
692 int zpadlen = 0; /* amount to zero pad */
700 if(!(flags & DP_F_UNSIGNED))
707 if (flags & DP_F_PLUS) /* Do a sign (+/i) */
710 if (flags & DP_F_SPACE)
714 if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
718 (caps? "0123456789ABCDEF":"0123456789abcdef")
719 [uvalue % (unsigned)base ];
720 uvalue = (uvalue / (unsigned)base );
721 } while(uvalue && (place < 20));
722 if (place == 20) place--;
725 zpadlen = max - place;
726 spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
727 if (zpadlen < 0) zpadlen = 0;
728 if (spadlen < 0) spadlen = 0;
729 if (flags & DP_F_ZERO)
731 zpadlen = MAX(zpadlen, spadlen);
734 if (flags & DP_F_MINUS)
735 spadlen = -spadlen; /* Left Justifty */
737 #ifdef DEBUG_SNPRINTF
738 dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
739 zpadlen, spadlen, min, max, place));
745 dopr_outch (buffer, currlen, maxlen, ' ');
751 dopr_outch (buffer, currlen, maxlen, signvalue);
758 dopr_outch (buffer, currlen, maxlen, '0');
765 dopr_outch (buffer, currlen, maxlen, convert[--place]);
767 /* Left Justified spaces */
768 while (spadlen < 0) {
769 dopr_outch (buffer, currlen, maxlen, ' ');
774 static long double abs_val (long double value)
776 long double result = value;
784 static long double pow10 (int exp)
786 long double result = 1;
797 static long round (long double value)
802 value = value - intpart;
809 static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
810 long double fvalue, int min, int max, int flags)
818 int padlen = 0; /* amount to pad */
825 * AIX manpage says the default is 0, but Solaris says the default
826 * is 6, and sprintf on AIX defaults to 6
831 ufvalue = abs_val (fvalue);
836 if (flags & DP_F_PLUS) /* Do a sign (+/i) */
839 if (flags & DP_F_SPACE)
843 if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
849 * Sorry, we only support 9 digits past the decimal because of our
855 /* We "cheat" by converting the fractional part to integer by
856 * multiplying by a factor of 10
858 fracpart = round ((pow10 (max)) * (ufvalue - intpart));
860 if (fracpart >= pow10 (max))
863 fracpart -= pow10 (max);
866 #ifdef DEBUG_SNPRINTF
867 dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart));
870 /* Convert integer part */
873 (caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10];
874 intpart = (intpart / 10);
875 } while(intpart && (iplace < 20));
876 if (iplace == 20) iplace--;
877 iconvert[iplace] = 0;
879 /* Convert fractional part */
882 (caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10];
883 fracpart = (fracpart / 10);
884 } while(fracpart && (fplace < 20));
885 if (fplace == 20) fplace--;
886 fconvert[fplace] = 0;
888 /* -1 for decimal point, another -1 if we are printing a sign */
889 padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
890 zpadlen = max - fplace;
895 if (flags & DP_F_MINUS)
896 padlen = -padlen; /* Left Justifty */
898 if ((flags & DP_F_ZERO) && (padlen > 0))
902 dopr_outch (buffer, currlen, maxlen, signvalue);
908 dopr_outch (buffer, currlen, maxlen, '0');
914 dopr_outch (buffer, currlen, maxlen, ' ');
918 dopr_outch (buffer, currlen, maxlen, signvalue);
921 dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
924 * Decimal point. This should probably use locale to find the correct
927 dopr_outch (buffer, currlen, maxlen, '.');
930 dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
934 dopr_outch (buffer, currlen, maxlen, '0');
940 dopr_outch (buffer, currlen, maxlen, ' ');
945 static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c)
947 if (*currlen < maxlen)
948 buffer[(*currlen)++] = c;
950 #endif /* !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) */
952 #ifndef HAVE_VSNPRINTF
953 int vsnprintf (char *str, size_t count, const char *fmt, va_list args)
956 dopr(str, count, fmt, args);
959 #endif /* !HAVE_VSNPRINTF */
961 #ifndef HAVE_SNPRINTF
964 int snprintf (char *str,size_t count,const char *fmt,...)
966 int snprintf (va_alist) va_dcl
977 VA_SHIFT (str, char *);
978 VA_SHIFT (count, size_t );
979 VA_SHIFT (fmt, char *);
980 (void) vsnprintf(str, count, fmt, ap);
987 #define LONG_STRING 1024
991 char buf1[LONG_STRING];
992 char buf2[LONG_STRING];
1007 double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996,
1008 0.9996, 1.996, 4.136, 0};
1021 long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
1026 printf ("Testing snprintf format codes against system sprintf...\n");
1028 for (x = 0; fp_fmt[x] != NULL ; x++)
1029 for (y = 0; fp_nums[y] != 0 ; y++)
1031 snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]);
1032 sprintf (buf2, fp_fmt[x], fp_nums[y]);
1033 if (strcmp (buf1, buf2))
1035 printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n",
1036 fp_fmt[x], buf1, buf2);
1042 for (x = 0; int_fmt[x] != NULL ; x++)
1043 for (y = 0; int_nums[y] != 0 ; y++)
1045 snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]);
1046 sprintf (buf2, int_fmt[x], int_nums[y]);
1047 if (strcmp (buf1, buf2))
1049 printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n",
1050 int_fmt[x], buf1, buf2);
1055 printf ("%d tests failed out of %d.\n", fail, num);
1057 #endif /* SNPRINTF_TEST */
1059 #endif /* !HAVE_SNPRINTF */