5 * by JH <jheinonen@users.sourceforge.net>
7 * Copyright (C) Jaakko Heinonen
22 #ifdef HANDLE_MULTIBYTE
24 # include "mbswidth.h"
46 while( ( *str = tolower ( *str ) ) )
59 for(t = s; ISSPACE(*t); t++);
61 memmove(s, t, strlen(t)+1);
63 for (tt = t = s; *t != '\0'; t++)
77 /* varargs declarations: */
80 # define MY_VA_LOCAL_DECL va_list ap
81 # define MY_VA_START(f) va_start(ap, f)
82 # define MY_VA_SHIFT(v,t) v = va_arg(ap, t)
83 # define MY_VA_END va_end(ap)
85 # error HAVE_STDARG_H not defined
89 mkstr (const char *format, ... )
95 (char *) abook_malloc (size);
97 (char *) xmalloc (size);
100 assert(format != NULL);
105 n = vsnprintf (buffer, size,
109 if (n > -1 && n < size)
119 (char *) abook_realloc (buffer, size);
121 (char *) xrealloc (buffer, size);
128 strconcat (const char *str, ...)
136 l = 1 + strlen (str);
138 MY_VA_SHIFT(s, char*);
141 MY_VA_SHIFT(s, char*);
154 strcpy (concat, str);
156 MY_VA_SHIFT(s, char*);
159 MY_VA_SHIFT(s, char*);
168 safe_strcmp(const char *s1, const char *s2)
170 if (s1 == NULL && s2 == NULL) return 0;
171 if (s1 == NULL) return -1;
172 if (s2 == NULL) return 1;
174 return strcmp(s1, s2);
178 safe_strcoll(const char *s1, const char *s2)
181 if (s1 == NULL && s2 == NULL) return 0;
182 if (s1 == NULL) return -1;
183 if (s2 == NULL) return 1;
185 return strcoll(s1, s2);
186 #else /* fall back to strcmp */
187 return safe_strcmp(s1, s2);
197 if( (dir = (char *)malloc(size)) == NULL)
202 while( getcwd(dir, size) == NULL && errno == ERANGE )
203 if( (dir = (char *)realloc(dir, size *=2)) == NULL)
209 #define INITIAL_SIZE 128
211 # define abook_malloc(X) xmalloc(X)
212 # define abook_realloc(X, XX) xrealloc(X, XX)
218 char *buf; /* buffer for line */
219 size_t size; /* size of buffer */
220 size_t inc; /* how much to enlarge buffer */
221 size_t len; /* # of chars stored into buf before '\0' */
223 const size_t thres = 128; /* initial buffer size (most lines should
224 fit into this size, so think of this as
225 the "long line threshold"). */
226 const size_t mucho = 128; /* if there is at least this much wasted
227 space when the whole buffer has been
228 read, try to reclaim it. Don't make
229 this too small, else there is too much
230 time wasted trying to reclaim a couple
232 const size_t mininc = 64; /* minimum number of bytes by which
233 to increase the allocated memory */
237 buf = (char *)abook_malloc(size);
239 while (fgets(buf+len, size-len, f) != NULL) {
240 len += strlen(buf+len);
241 if (len > 0 && buf[len-1] == '\n')
242 break; /* the whole line has been read */
244 for (inc = size, p = NULL; inc > mininc; inc /= 2)
245 if ((p = (char *)abook_realloc(buf, size + inc)) !=
255 return NULL; /* nothing read (eof or error) */
258 if (buf[len-1] == '\n') /* remove newline, if there */
261 if (size - len > mucho) { /* a plenitude of unused memory? */
262 p = (char *)abook_realloc(buf, len+1);
273 strwidth(const char *s)
276 #ifdef HANDLE_MULTIBYTE
277 return (int)mbswidth(s, 0);
284 bytes2width(const char *s, int width)
287 #ifdef HANDLE_MULTIBYTE
288 return mbsnbytes(s, strlen(s), width, 0);
294 /**************************************************************
296 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
297 * A bombproof version of doprnt (dopr) included.
298 * Sigh. This sort of thing is always nasty do deal with. Note that
299 * the version here does not include floating point...
301 * snprintf() is used instead of sprintf() as it does limit checks
302 * for string length. This covers a nasty loophole.
304 * The other functions are there to prevent NULL pointers from
305 * causing nast effects.
308 * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
309 * This was ugly. It is still ugly. I opted out of floating point
310 * numbers, but the formatter understands just about everything
311 * from the normal C string format, at least as far as I can tell from
312 * the Solaris 2.5 printf(3S) man page.
314 * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
315 * Ok, added some minimal floating point support, which means this
316 * probably requires libm on most operating systems. Don't yet
317 * support the exponent (e,E) and sigfig (g,G). Also, fmtint()
318 * was pretty badly broken, it just wasn't being exercised in ways
319 * which showed it, so that's been fixed. Also, formated the code
320 * to mutt conventions, and removed dead code left over from the
321 * original. Also, there is now a builtin-test, just compile with:
322 * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
323 * and run snprintf for results.
325 * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
326 * The PGP code was using unsigned hexadecimal formats.
327 * Unfortunately, unsigned formats simply didn't work.
329 * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
330 * The original code assumed that both snprintf() and vsnprintf() were
331 * missing. Some systems only have snprintf() but not vsnprintf(), so
332 * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
334 **************************************************************/
338 #if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF)
342 #include <sys/types.h>
344 /* Define this as a fall through, HAVE_STDARG_H is probably already set */
346 #if !defined(HAVE_STDARG_H) && !defined(HAVE_VARARGS_H)
347 # define HAVE_VARARGS_H 1
350 /* varargs declarations: */
352 #if defined(HAVE_STDARG_H)
353 /*# include <stdarg.h>*/
354 # define HAVE_STDARGS /* let's hope that works everywhere (mj) */
355 # define VA_LOCAL_DECL va_list ap
356 # define VA_START(f) va_start(ap, f)
357 # define VA_SHIFT(v,t) ; /* no-op for ANSI */
358 # define VA_END va_end(ap)
360 # if defined(HAVE_VARARGS_H)
361 # include <varargs.h>
363 # define VA_LOCAL_DECL va_list ap
364 # define VA_START(f) va_start(ap) /* f is ignored! */
365 # define VA_SHIFT(v,t) v = va_arg(ap,t)
366 # define VA_END va_end(ap)
368 /*XX ** NO VARARGS ** XX*/
372 /*int snprintf (char *str, size_t count, const char *fmt, ...);*/
373 /*int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);*/
375 static void dopr (char *buffer, size_t maxlen, const char *format,
377 static void fmtstr (char *buffer, size_t *currlen, size_t maxlen,
378 char *value, int flags, int min, int max);
379 static void fmtint (char *buffer, size_t *currlen, size_t maxlen,
380 long value, int base, int min, int max, int flags);
381 static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
382 long double fvalue, int min, int max, int flags);
383 static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c );
386 * dopr(): poor man's version of doprintf
389 /* format read states */
390 #define DP_S_DEFAULT 0
399 /* format flags - Bits */
400 #define DP_F_MINUS (1 << 0)
401 #define DP_F_PLUS (1 << 1)
402 #define DP_F_SPACE (1 << 2)
403 #define DP_F_NUM (1 << 3)
404 #define DP_F_ZERO (1 << 4)
405 #define DP_F_UP (1 << 5)
406 #define DP_F_UNSIGNED (1 << 6)
408 /* Conversion Flags */
411 #define DP_C_LDOUBLE 3
413 #define char_to_int(p) (p - '0')
414 #define MAX(p,q) ((p >= q) ? p : q)
416 static void dopr (char *buffer, size_t maxlen, const char *format, va_list args)
429 state = DP_S_DEFAULT;
430 currlen = flags = cflags = min = 0;
434 while (state != DP_S_DONE)
436 if ((ch == '\0') || (currlen >= maxlen))
445 dopr_outch (buffer, &currlen, maxlen, ch);
477 if (isdigit((unsigned char)ch))
479 min = 10*min + char_to_int (ch);
484 min = va_arg (args, int);
501 if (isdigit((unsigned char)ch))
505 max = 10*max + char_to_int (ch);
510 max = va_arg (args, int);
518 /* Currently, we don't support Long Long, bummer */
530 cflags = DP_C_LDOUBLE;
543 if (cflags == DP_C_SHORT)
544 value = va_arg (args, short int);
545 else if (cflags == DP_C_LONG)
546 value = va_arg (args, long int);
548 value = va_arg (args, int);
549 fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
552 flags |= DP_F_UNSIGNED;
553 if (cflags == DP_C_SHORT)
554 value = va_arg (args, unsigned short int);
555 else if (cflags == DP_C_LONG)
556 value = va_arg (args, unsigned long int);
558 value = va_arg (args, unsigned int);
559 fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
562 flags |= DP_F_UNSIGNED;
563 if (cflags == DP_C_SHORT)
564 value = va_arg (args, unsigned short int);
565 else if (cflags == DP_C_LONG)
566 value = va_arg (args, unsigned long int);
568 value = va_arg (args, unsigned int);
569 fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
574 flags |= DP_F_UNSIGNED;
575 if (cflags == DP_C_SHORT)
576 value = va_arg (args, unsigned short int);
577 else if (cflags == DP_C_LONG)
578 value = va_arg (args, unsigned long int);
580 value = va_arg (args, unsigned int);
581 fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
584 if (cflags == DP_C_LDOUBLE)
585 fvalue = va_arg (args, long double);
587 fvalue = va_arg (args, double);
588 /* um, floating point? */
589 fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
594 if (cflags == DP_C_LDOUBLE)
595 fvalue = va_arg (args, long double);
597 fvalue = va_arg (args, double);
602 if (cflags == DP_C_LDOUBLE)
603 fvalue = va_arg (args, long double);
605 fvalue = va_arg (args, double);
608 dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
611 strvalue = va_arg (args, char *);
613 max = maxlen; /* ie, no max */
614 fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
617 strvalue = va_arg (args, void *);
618 fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
621 if (cflags == DP_C_SHORT)
624 num = va_arg (args, short int *);
627 else if (cflags == DP_C_LONG)
630 num = va_arg (args, long int *);
636 num = va_arg (args, int *);
641 dopr_outch (buffer, &currlen, maxlen, ch);
644 /* not supported yet, treat as next char */
652 state = DP_S_DEFAULT;
653 flags = cflags = min = 0;
660 break; /* some picky compilers need this */
663 if (currlen < maxlen - 1)
664 buffer[currlen] = '\0';
666 buffer[maxlen - 1] = '\0';
669 static void fmtstr (char *buffer, size_t *currlen, size_t maxlen,
670 char *value, int flags, int min, int max)
672 int padlen, strln; /* amount to pad */
680 for (strln = 0; value[strln]; ++strln); /* strlen */
681 padlen = min - strln;
684 if (flags & DP_F_MINUS)
685 padlen = -padlen; /* Left Justify */
687 while ((padlen > 0) && (cnt < max))
689 dopr_outch (buffer, currlen, maxlen, ' ');
693 while (*value && (cnt < max))
695 dopr_outch (buffer, currlen, maxlen, *value++);
698 while ((padlen < 0) && (cnt < max))
700 dopr_outch (buffer, currlen, maxlen, ' ');
706 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
708 static void fmtint (char *buffer, size_t *currlen, size_t maxlen,
709 long value, int base, int min, int max, int flags)
712 unsigned long uvalue;
715 int spadlen = 0; /* amount to space pad */
716 int zpadlen = 0; /* amount to zero pad */
724 if(!(flags & DP_F_UNSIGNED))
731 if (flags & DP_F_PLUS) /* Do a sign (+/i) */
734 if (flags & DP_F_SPACE)
738 if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
742 (caps? "0123456789ABCDEF":"0123456789abcdef")
743 [uvalue % (unsigned)base ];
744 uvalue = (uvalue / (unsigned)base );
745 } while(uvalue && (place < 20));
746 if (place == 20) place--;
749 zpadlen = max - place;
750 spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
751 if (zpadlen < 0) zpadlen = 0;
752 if (spadlen < 0) spadlen = 0;
753 if (flags & DP_F_ZERO)
755 zpadlen = MAX(zpadlen, spadlen);
758 if (flags & DP_F_MINUS)
759 spadlen = -spadlen; /* Left Justifty */
761 #ifdef DEBUG_SNPRINTF
762 dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
763 zpadlen, spadlen, min, max, place));
769 dopr_outch (buffer, currlen, maxlen, ' ');
775 dopr_outch (buffer, currlen, maxlen, signvalue);
782 dopr_outch (buffer, currlen, maxlen, '0');
789 dopr_outch (buffer, currlen, maxlen, convert[--place]);
791 /* Left Justified spaces */
792 while (spadlen < 0) {
793 dopr_outch (buffer, currlen, maxlen, ' ');
798 static long double abs_val (long double value)
800 long double result = value;
808 static long double pow10 (int exp)
810 long double result = 1;
821 static long round (long double value)
826 value = value - intpart;
833 static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
834 long double fvalue, int min, int max, int flags)
842 int padlen = 0; /* amount to pad */
849 * AIX manpage says the default is 0, but Solaris says the default
850 * is 6, and sprintf on AIX defaults to 6
855 ufvalue = abs_val (fvalue);
860 if (flags & DP_F_PLUS) /* Do a sign (+/i) */
863 if (flags & DP_F_SPACE)
867 if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
873 * Sorry, we only support 9 digits past the decimal because of our
879 /* We "cheat" by converting the fractional part to integer by
880 * multiplying by a factor of 10
882 fracpart = round ((pow10 (max)) * (ufvalue - intpart));
884 if (fracpart >= pow10 (max))
887 fracpart -= pow10 (max);
890 #ifdef DEBUG_SNPRINTF
891 dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart));
894 /* Convert integer part */
897 (caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10];
898 intpart = (intpart / 10);
899 } while(intpart && (iplace < 20));
900 if (iplace == 20) iplace--;
901 iconvert[iplace] = 0;
903 /* Convert fractional part */
906 (caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10];
907 fracpart = (fracpart / 10);
908 } while(fracpart && (fplace < 20));
909 if (fplace == 20) fplace--;
910 fconvert[fplace] = 0;
912 /* -1 for decimal point, another -1 if we are printing a sign */
913 padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
914 zpadlen = max - fplace;
919 if (flags & DP_F_MINUS)
920 padlen = -padlen; /* Left Justifty */
922 if ((flags & DP_F_ZERO) && (padlen > 0))
926 dopr_outch (buffer, currlen, maxlen, signvalue);
932 dopr_outch (buffer, currlen, maxlen, '0');
938 dopr_outch (buffer, currlen, maxlen, ' ');
942 dopr_outch (buffer, currlen, maxlen, signvalue);
945 dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
948 * Decimal point. This should probably use locale to find the correct
951 dopr_outch (buffer, currlen, maxlen, '.');
954 dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
958 dopr_outch (buffer, currlen, maxlen, '0');
964 dopr_outch (buffer, currlen, maxlen, ' ');
969 static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c)
971 if (*currlen < maxlen)
972 buffer[(*currlen)++] = c;
974 #endif /* !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) */
976 #ifndef HAVE_VSNPRINTF
977 int vsnprintf (char *str, size_t count, const char *fmt, va_list args)
980 dopr(str, count, fmt, args);
983 #endif /* !HAVE_VSNPRINTF */
985 #ifndef HAVE_SNPRINTF
988 int snprintf (char *str,size_t count,const char *fmt,...)
990 int snprintf (va_alist) va_dcl
1001 VA_SHIFT (str, char *);
1002 VA_SHIFT (count, size_t );
1003 VA_SHIFT (fmt, char *);
1004 (void) vsnprintf(str, count, fmt, ap);
1006 return(strlen(str));
1009 #ifdef TEST_SNPRINTF
1011 #define LONG_STRING 1024
1015 char buf1[LONG_STRING];
1016 char buf2[LONG_STRING];
1031 double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996,
1032 0.9996, 1.996, 4.136, 0};
1045 long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
1050 printf ("Testing snprintf format codes against system sprintf...\n");
1052 for (x = 0; fp_fmt[x] != NULL ; x++)
1053 for (y = 0; fp_nums[y] != 0 ; y++)
1055 snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]);
1056 sprintf (buf2, fp_fmt[x], fp_nums[y]);
1057 if (strcmp (buf1, buf2))
1059 printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n",
1060 fp_fmt[x], buf1, buf2);
1066 for (x = 0; int_fmt[x] != NULL ; x++)
1067 for (y = 0; int_nums[y] != 0 ; y++)
1069 snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]);
1070 sprintf (buf2, int_fmt[x], int_nums[y]);
1071 if (strcmp (buf1, buf2))
1073 printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n",
1074 int_fmt[x], buf1, buf2);
1079 printf ("%d tests failed out of %d.\n", fail, num);
1081 #endif /* SNPRINTF_TEST */
1083 #endif /* !HAVE_SNPRINTF */