1 /****************************************************************************
5 *************************************************************************
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
12 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
13 * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
14 * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
17 * A merge of Bjorn Reese's format() function and Daniel's dsprintf()
18 * 1.0. A full blooded printf() clone with full support for <num>$
19 * everywhere (parameters, widths and precisions) including variabled
20 * sized parameters (like doubles, long longs, long doubles and even
21 * void * in 64-bit architectures).
23 * Current restrictions:
24 * - Max 128 parameters
25 * - No 'long double' support.
27 * If you ever want truly portable and good *printf() clones, the project that
28 * took on from here is named 'Trio' and you find more details on the trio web
29 * page at http://daniel.haxx.se/trio/
34 #include <sys/types.h>
41 #if defined(DJGPP) && (DJGPP_MINOR < 4)
42 #undef CURLDEBUG /* don't use x_was_used() here */
45 #include <curl/mprintf.h>
47 #ifndef SIZEOF_LONG_DOUBLE
48 #define SIZEOF_LONG_DOUBLE 0
52 /* default to 4 bytes for size_t unless defined in the config.h */
53 #define SIZEOF_SIZE_T 4
58 #define LONG_LONG long long
63 /* The last #include file should be: */
66 #define BUFFSIZE 256 /* buffer for long-to-str and float-to-str calcs */
67 #define MAX_PARAMETERS 128 /* lame static limit */
77 # define TRUE ((char)(1 == 1))
78 # define FALSE ((char)(0 == 1))
86 /* Lower-case digits. */
87 static const char lower_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
89 /* Upper-case digits. */
90 static const char upper_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
94 if(stream((unsigned char)(x), (FILE *)data) != -1) \
97 return done; /* return immediately on failure */ \
100 /* Data type to read from the arglist */
111 FORMAT_WIDTH /* For internal use */
114 /* convertion and display flags */
118 FLAGS_SHOWSIGN = 1<<1,
123 FLAGS_LONGLONG = 1<<6,
124 FLAGS_LONGDOUBLE = 1<<7,
125 FLAGS_PAD_NIL = 1<<8,
126 FLAGS_UNSIGNED = 1<<9,
130 FLAGS_WIDTH = 1<<13, /* '*' or '*<num>$' used */
131 FLAGS_WIDTHPARAM = 1<<14, /* width PARAMETER was specified */
132 FLAGS_PREC = 1<<15, /* precision was specified */
133 FLAGS_PRECPARAM = 1<<16, /* precision PARAMETER was specified */
134 FLAGS_CHAR = 1<<17, /* %c story */
135 FLAGS_FLOATE = 1<<18, /* %e or %E */
136 FLAGS_FLOATG = 1<<19 /* %g or %G */
142 long width; /* width OR width parameter number */
143 long precision; /* precision OR precision parameter number */
162 char *buffer; /* allocated buffer */
163 size_t len; /* length of string */
164 size_t alloc; /* length of alloc */
165 bool fail; /* TRUE if an alloc has failed and thus the output is not
169 int curl_msprintf(char *buffer, const char *format, ...);
171 static long dprintf_DollarString(char *input, char **end)
174 while(isdigit((int)*input)) {
176 number += *input-'0';
179 if(number && ('$'==*input++)) {
186 static BOOL dprintf_IsQualifierNoDollar(char c)
189 case '-': case '+': case ' ': case '#': case '.':
190 case '0': case '1': case '2': case '3': case '4':
191 case '5': case '6': case '7': case '8': case '9':
192 case 'h': case 'l': case 'L': case 'z': case 'q':
200 #ifdef DPRINTF_DEBUG2
201 int dprintf_Pass1Report(va_stack_t *vto, int max)
208 for(i=0; i<max; i++) {
210 switch(vto[i].type) {
226 case FORMAT_LONGLONG:
232 case FORMAT_LONGDOUBLE:
233 type = "long double";
240 for(bit=0; bit<31; bit++) {
241 flags = vto[i].flags & (1<<bit);
243 if(flags & FLAGS_SPACE)
244 strcat(buffer, "space ");
245 else if(flags & FLAGS_SHOWSIGN)
246 strcat(buffer, "plus ");
247 else if(flags & FLAGS_LEFT)
248 strcat(buffer, "left ");
249 else if(flags & FLAGS_ALT)
250 strcat(buffer, "alt ");
251 else if(flags & FLAGS_SHORT)
252 strcat(buffer, "short ");
253 else if(flags & FLAGS_LONG)
254 strcat(buffer, "long ");
255 else if(flags & FLAGS_LONGLONG)
256 strcat(buffer, "longlong ");
257 else if(flags & FLAGS_LONGDOUBLE)
258 strcat(buffer, "longdouble ");
259 else if(flags & FLAGS_PAD_NIL)
260 strcat(buffer, "padnil ");
261 else if(flags & FLAGS_UNSIGNED)
262 strcat(buffer, "unsigned ");
263 else if(flags & FLAGS_OCTAL)
264 strcat(buffer, "octal ");
265 else if(flags & FLAGS_HEX)
266 strcat(buffer, "hex ");
267 else if(flags & FLAGS_UPPER)
268 strcat(buffer, "upper ");
269 else if(flags & FLAGS_WIDTH)
270 strcat(buffer, "width ");
271 else if(flags & FLAGS_WIDTHPARAM)
272 strcat(buffer, "widthparam ");
273 else if(flags & FLAGS_PREC)
274 strcat(buffer, "precision ");
275 else if(flags & FLAGS_PRECPARAM)
276 strcat(buffer, "precparam ");
277 else if(flags & FLAGS_CHAR)
278 strcat(buffer, "char ");
279 else if(flags & FLAGS_FLOATE)
280 strcat(buffer, "floate ");
281 else if(flags & FLAGS_FLOATG)
282 strcat(buffer, "floatg ");
284 printf("REPORT: %d. %s [%s]\n", i, type, buffer);
292 /******************************************************************
295 * Create an index with the type of each parameter entry and its
296 * value (may vary in size)
298 ******************************************************************/
300 static long dprintf_Pass1(char *format, va_stack_t *vto, char **endpos,
316 continue; /* while */
321 /* Handle the positional case (N$) */
325 this_param = dprintf_DollarString(fmt, &fmt);
327 /* we got no positional, get the next counter */
328 this_param = param_num;
330 if (this_param > max_param)
331 max_param = this_param;
334 * The parameter with number 'i' should be used. Next, we need
335 * to get SIZE and TYPE of the parameter. Add the information
342 /* Handle the flags */
344 while (dprintf_IsQualifierNoDollar(*fmt)) {
347 flags |= FLAGS_SPACE;
350 flags |= FLAGS_SHOWSIGN;
354 flags &= ~FLAGS_PAD_NIL;
362 /* The precision is picked from a specified parameter */
364 flags |= FLAGS_PRECPARAM;
368 i = dprintf_DollarString(fmt, &fmt);
372 precision = param_num;
374 if (precision > max_param)
375 max_param = precision;
379 precision = strtol(fmt, &fmt, 10);
383 flags |= FLAGS_SHORT;
386 if (flags & FLAGS_LONG)
387 flags |= FLAGS_LONGLONG;
392 flags |= FLAGS_LONGDOUBLE;
395 flags |= FLAGS_LONGLONG;
398 /* the code below generates a warning if -Wunreachable-code is
401 flags |= FLAGS_LONGLONG;
407 #if SIZEOF_CURL_OFF_T > 4
408 flags |= FLAGS_LONGLONG;
414 if (!(flags & FLAGS_LEFT))
415 flags |= FLAGS_PAD_NIL;
417 case '1': case '2': case '3': case '4':
418 case '5': case '6': case '7': case '8': case '9':
419 flags |= FLAGS_WIDTH;
420 width = strtol(fmt-1, &fmt, 10);
422 case '*': /* Special case */
423 flags |= FLAGS_WIDTHPARAM;
426 i = dprintf_DollarString(fmt, &fmt);
431 if(width > max_param)
439 /* Handle the specifier */
448 vto[i].type = FORMAT_STRING;
451 vto[i].type = FORMAT_INTPTR;
454 vto[i].type = FORMAT_PTR;
457 vto[i].type = FORMAT_INT;
460 vto[i].type = FORMAT_INT;
461 flags |= FLAGS_UNSIGNED;
464 vto[i].type = FORMAT_INT;
465 flags |= FLAGS_OCTAL;
468 vto[i].type = FORMAT_INT;
472 vto[i].type = FORMAT_INT;
473 flags |= FLAGS_HEX|FLAGS_UPPER;
476 vto[i].type = FORMAT_INT;
480 vto[i].type = FORMAT_DOUBLE;
483 vto[i].type = FORMAT_DOUBLE;
484 flags |= FLAGS_FLOATE;
487 vto[i].type = FORMAT_DOUBLE;
488 flags |= FLAGS_FLOATE|FLAGS_UPPER;
491 vto[i].type = FORMAT_DOUBLE;
492 flags |= FLAGS_FLOATG;
495 vto[i].type = FORMAT_DOUBLE;
496 flags |= FLAGS_FLOATG|FLAGS_UPPER;
499 vto[i].type = FORMAT_UNKNOWN;
503 vto[i].flags = flags;
504 vto[i].width = width;
505 vto[i].precision = precision;
507 if (flags & FLAGS_WIDTHPARAM) {
508 /* we have the width specified from a parameter, so we make that
509 parameter's info setup properly */
510 vto[i].width = width - 1;
512 vto[i].type = FORMAT_WIDTH;
513 vto[i].flags = FLAGS_NEW;
514 vto[i].precision = vto[i].width = 0; /* can't use width or precision
517 if (flags & FLAGS_PRECPARAM) {
518 /* we have the precision specified from a parameter, so we make that
519 parameter's info setup properly */
520 vto[i].precision = precision - 1;
522 vto[i].type = FORMAT_WIDTH;
523 vto[i].flags = FLAGS_NEW;
524 vto[i].precision = vto[i].width = 0; /* can't use width or precision
527 *endpos++ = fmt + 1; /* end of this sequence */
531 #ifdef DPRINTF_DEBUG2
532 dprintf_Pass1Report(vto, max_param);
535 /* Read the arg list parameters into our data list */
536 for (i=0; i<max_param; i++) {
537 if ((i + 1 < max_param) && (vto[i + 1].type == FORMAT_WIDTH))
539 /* Width/precision arguments must be read before the main argument
540 * they are attached to
542 vto[i + 1].data.num = va_arg(arglist, int);
548 vto[i].data.str = va_arg(arglist, char *);
554 vto[i].data.ptr = va_arg(arglist, void *);
559 if(vto[i].flags & FLAGS_LONGLONG)
560 vto[i].data.lnum = va_arg(arglist, LONG_LONG);
563 if(vto[i].flags & FLAGS_LONG)
564 vto[i].data.num = va_arg(arglist, long);
566 vto[i].data.num = va_arg(arglist, int);
570 vto[i].data.dnum = va_arg(arglist, double);
574 /* Argument has been read. Silently convert it into an integer
577 vto[i].type = FORMAT_INT;
589 static int dprintf_formatf(
590 void *data, /* untouched by format(), just sent to the stream() function in
591 the second argument */
592 /* function pointer called for each output character */
593 int (*stream)(int, FILE *),
594 const char *format, /* %-formatted string */
595 va_list ap_save) /* list of parameters */
597 /* Base-36 digits for numbers. */
598 const char *digits = lower_digits;
600 /* Pointer into the format string. */
603 /* Number of characters written. */
606 long param; /* current parameter to read */
607 long param_num=0; /* parameter counter */
609 va_stack_t vto[MAX_PARAMETERS];
610 char *endpos[MAX_PARAMETERS];
617 /* Do the actual %-code parsing */
618 dprintf_Pass1((char *)format, vto, endpos, ap_save);
620 end = &endpos[0]; /* the initial end-position from the list dprintf_Pass1()
625 /* Format spec modifiers. */
628 /* Width of a field. */
631 /* Precision of a field. */
634 /* Decimal integer is negative. */
637 /* Base of a number to be written. */
640 /* Integral values to be written. */
642 unsigned LONG_LONG num;
649 /* This isn't a format spec, so write everything out until the next one
650 OR end of string is reached. */
653 } while(*++f && ('%' != *f));
659 /* Check for "%%". Note that although the ANSI standard lists
660 '%' as a conversion specifier, it says "The complete format
661 specification shall be `%%'," so we can avoid all the width
662 and precision processing. */
669 /* If this is a positional parameter, the position must follow imediately
670 after the %, thus create a %<num>$ sequence */
671 param=dprintf_DollarString(f, &f);
678 param_num++; /* increase this always to allow "%2$s %1$s %s" and then the
679 third %s will pick the 3rd argument */
683 /* pick up the specified width */
684 if(p->flags & FLAGS_WIDTHPARAM)
685 width = vto[p->width].data.num;
689 /* pick up the specified precision */
690 if(p->flags & FLAGS_PRECPARAM)
691 prec = vto[p->precision].data.num;
692 else if(p->flags & FLAGS_PREC)
697 alt = (p->flags & FLAGS_ALT)?TRUE:FALSE;
702 if(p->flags & FLAGS_CHAR) {
704 if (!(p->flags & FLAGS_LEFT))
708 if (p->flags & FLAGS_LEFT)
713 if(p->flags & FLAGS_UNSIGNED) {
714 /* Decimal unsigned integer. */
716 goto unsigned_number;
718 if(p->flags & FLAGS_OCTAL) {
719 /* Octal unsigned integer. */
721 goto unsigned_number;
723 if(p->flags & FLAGS_HEX) {
724 /* Hexadecimal unsigned integer. */
726 digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits;
728 goto unsigned_number;
731 /* Decimal integer. */
735 if(p->flags & FLAGS_LONGLONG) {
737 is_neg = p->data.lnum < 0;
738 num = is_neg ? (- p->data.lnum) : p->data.lnum;
743 signed_num = (long) num;
744 is_neg = signed_num < 0;
745 num = is_neg ? (- signed_num) : signed_num;
750 /* Unsigned number of base BASE. */
754 /* Number of base BASE. */
756 char *workend = &work[sizeof(work) - 1];
759 /* Supply a default precision if none was given. */
763 /* Put the number in WORK. */
766 *w-- = digits[num % base];
769 width -= (long)(workend - w);
770 prec -= (long)(workend - w);
772 if (alt && base == 8 && prec <= 0) {
783 if (alt && base == 16)
786 if (is_neg || (p->flags & FLAGS_SHOWSIGN) || (p->flags & FLAGS_SPACE))
789 if (!(p->flags & FLAGS_LEFT) && !(p->flags & FLAGS_PAD_NIL))
795 else if (p->flags & FLAGS_SHOWSIGN)
797 else if (p->flags & FLAGS_SPACE)
800 if (alt && base == 16) {
802 if(p->flags & FLAGS_UPPER)
808 if (!(p->flags & FLAGS_LEFT) && (p->flags & FLAGS_PAD_NIL))
812 /* Write the number. */
813 while (++w <= workend) {
817 if (p->flags & FLAGS_LEFT)
826 static const char null[] = "(nil)";
830 str = (char *) p->data.str;
832 /* Write null[] if there's space. */
833 if (prec == -1 || prec >= (long) sizeof(null) - 1) {
835 len = sizeof(null) - 1;
836 /* Disable quotes around (nil) */
837 p->flags &= (~FLAGS_ALT);
847 if (prec != -1 && (size_t) prec < len)
851 if (p->flags & FLAGS_ALT)
854 if (!(p->flags&FLAGS_LEFT))
860 if (p->flags&FLAGS_LEFT)
864 if (p->flags & FLAGS_ALT)
870 /* Generic pointer. */
873 ptr = (void *) p->data.ptr;
875 /* If the pointer is not NULL, write it as a %#x spec. */
877 digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits;
884 /* Write "(nil)" for a nil pointer. */
885 static const char strnil[] = "(nil)";
888 width -= sizeof(strnil) - 1;
889 if (p->flags & FLAGS_LEFT)
892 for (point = strnil; *point != '\0'; ++point)
894 if (! (p->flags & FLAGS_LEFT))
903 char formatbuf[32]="%";
905 size_t left = sizeof(formatbuf)-strlen(formatbuf);
909 if (p->flags & FLAGS_WIDTH)
911 else if (p->flags & FLAGS_WIDTHPARAM)
912 width = vto[p->width].data.num;
915 if (p->flags & FLAGS_PREC)
917 else if (p->flags & FLAGS_PRECPARAM)
918 prec = vto[p->precision].data.num;
920 if (p->flags & FLAGS_LEFT)
921 strcat(formatbuf, "-");
922 if (p->flags & FLAGS_SHOWSIGN)
923 strcat(formatbuf, "+");
924 if (p->flags & FLAGS_SPACE)
925 strcat(formatbuf, " ");
926 if (p->flags & FLAGS_ALT)
927 strcat(formatbuf, "#");
929 fptr=&formatbuf[strlen(formatbuf)];
932 /* RECURSIVE USAGE */
933 len = curl_msnprintf(fptr, left, "%ld", width);
938 /* RECURSIVE USAGE */
939 len = curl_msnprintf(fptr, left, ".%ld", prec);
943 if (p->flags & FLAGS_LONG)
946 if (p->flags & FLAGS_FLOATE)
947 *fptr++ = p->flags&FLAGS_UPPER ? 'E':'e';
948 else if (p->flags & FLAGS_FLOATG)
949 *fptr++ = p->flags & FLAGS_UPPER ? 'G' : 'g';
953 *fptr = 0; /* and a final zero termination */
955 /* NOTE NOTE NOTE!! Not all sprintf() implementations returns number
956 of output characters */
957 (sprintf)(work, formatbuf, p->data.dnum);
959 for(fptr=work; *fptr; fptr++)
965 /* Answer the count of characters written. */
967 if (p->flags & FLAGS_LONGLONG)
968 *(LONG_LONG *) p->data.ptr = (LONG_LONG)done;
971 if (p->flags & FLAGS_LONG)
972 *(long *) p->data.ptr = (long)done;
973 else if (!(p->flags & FLAGS_SHORT))
974 *(int *) p->data.ptr = (int)done;
976 *(short *) p->data.ptr = (short)done;
982 f = *end++; /* goto end of %-code */
988 /* fputc() look-alike */
989 static int addbyter(int output, FILE *data)
991 struct nsprintf *infop=(struct nsprintf *)data;
992 unsigned char outc = (unsigned char)output;
994 if(infop->length < infop->max) {
995 /* only do this if we haven't reached max length yet */
996 infop->buffer[0] = outc; /* store */
997 infop->buffer++; /* increase pointer */
998 infop->length++; /* we are now one byte larger */
999 return outc; /* fputc() returns like this on success */
1004 int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format,
1008 struct nsprintf info;
1010 info.buffer = buffer;
1012 info.max = maxlength;
1014 retcode = dprintf_formatf(&info, addbyter, format, ap_save);
1016 /* we terminate this with a zero byte */
1017 if(info.max == info.length)
1018 /* we're at maximum, scrap the last letter */
1019 info.buffer[-1] = 0;
1026 int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...)
1029 va_list ap_save; /* argument pointer */
1030 va_start(ap_save, format);
1031 retcode = curl_mvsnprintf(buffer, maxlength, format, ap_save);
1036 /* fputc() look-alike */
1037 static int alloc_addbyter(int output, FILE *data)
1039 struct asprintf *infop=(struct asprintf *)data;
1040 unsigned char outc = (unsigned char)output;
1042 if(!infop->buffer) {
1043 infop->buffer=(char *)malloc(32);
1044 if(!infop->buffer) {
1046 return -1; /* fail */
1051 else if(infop->len+1 >= infop->alloc) {
1054 newptr = (char *)realloc(infop->buffer, infop->alloc*2);
1060 infop->buffer = newptr;
1064 infop->buffer[ infop->len ] = outc;
1068 return outc; /* fputc() returns like this on success */
1071 char *curl_maprintf(const char *format, ...)
1073 va_list ap_save; /* argument pointer */
1075 struct asprintf info;
1082 va_start(ap_save, format);
1083 retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save);
1085 if((-1 == retcode) || info.fail) {
1091 info.buffer[info.len] = 0; /* we terminate this with a zero byte */
1098 char *curl_mvaprintf(const char *format, va_list ap_save)
1101 struct asprintf info;
1108 retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save);
1109 if((-1 == retcode) || info.fail) {
1116 info.buffer[info.len] = 0; /* we terminate this with a zero byte */
1123 static int storebuffer(int output, FILE *data)
1125 char **buffer = (char **)data;
1126 unsigned char outc = (unsigned char)output;
1129 return outc; /* act like fputc() ! */
1132 int curl_msprintf(char *buffer, const char *format, ...)
1134 va_list ap_save; /* argument pointer */
1136 va_start(ap_save, format);
1137 retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save);
1139 *buffer=0; /* we terminate this with a zero byte */
1143 int curl_mprintf(const char *format, ...)
1146 va_list ap_save; /* argument pointer */
1147 va_start(ap_save, format);
1149 retcode = dprintf_formatf(stdout, fputc, format, ap_save);
1154 int curl_mfprintf(FILE *whereto, const char *format, ...)
1157 va_list ap_save; /* argument pointer */
1158 va_start(ap_save, format);
1159 retcode = dprintf_formatf(whereto, fputc, format, ap_save);
1164 int curl_mvsprintf(char *buffer, const char *format, va_list ap_save)
1167 retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save);
1168 *buffer=0; /* we terminate this with a zero byte */
1172 int curl_mvprintf(const char *format, va_list ap_save)
1174 return dprintf_formatf(stdout, fputc, format, ap_save);
1177 int curl_mvfprintf(FILE *whereto, const char *format, va_list ap_save)
1179 return dprintf_formatf(whereto, fputc, format, ap_save);
1182 #ifdef DPRINTF_DEBUG
1190 long long test = 0x1000000000LL;
1191 curl_mprintf("%lld %lld %lld\n", one, two, test);
1194 curl_mprintf("%3d %5d\n", 10, 1998);
1196 ptr=curl_maprintf("test this then baby %s%s%s%s%s%s %d %d %d loser baby get a hit in yer face now!", "", "pretty long string pretty long string pretty long string pretty long string pretty long string", "/", "/", "/", "pretty long string", 1998, 1999, 2001);
1200 memset(ptr, 55, strlen(ptr)+1);
1205 curl_mprintf(buffer, "%s %s %d", "daniel", "stenberg", 19988);
1208 curl_mfprintf(stderr, "%s %#08x\n", "dummy", 65);
1210 printf("%s %#08x\n", "dummy", 65);
1212 double tryout = 3.14156592;
1213 curl_mprintf(buffer, "%.2g %G %f %e %E", tryout, tryout, tryout, tryout, tryout);
1215 printf("%.2g %G %f %e %E\n", tryout, tryout, tryout, tryout, tryout);