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/
40 #if defined(DJGPP) && (DJGPP_MINOR < 4)
41 #undef _MPRINTF_REPLACE /* don't use x_was_used() here */
44 #include <curl/mprintf.h>
46 #ifndef SIZEOF_LONG_DOUBLE
47 #define SIZEOF_LONG_DOUBLE 0
51 /* default to 4 bytes for size_t unless defined in the config.h */
52 #define SIZEOF_SIZE_T 4
57 #define LONG_LONG long long
62 /* The last #include file should be: */
65 #define BUFFSIZE 256 /* buffer for long-to-str and float-to-str calcs */
66 #define MAX_PARAMETERS 128 /* lame static limit */
76 # define TRUE ((char)(1 == 1))
77 # define FALSE ((char)(0 == 1))
85 /* Lower-case digits. */
86 static const char lower_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
88 /* Upper-case digits. */
89 static const char upper_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
93 if(stream((unsigned char)(x), (FILE *)data) != -1) \
96 return done; /* return immediately on failure */ \
99 /* Data type to read from the arglist */
110 FORMAT_WIDTH /* For internal use */
113 /* convertion and display flags */
117 FLAGS_SHOWSIGN = 1<<1,
122 FLAGS_LONGLONG = 1<<6,
123 FLAGS_LONGDOUBLE = 1<<7,
124 FLAGS_PAD_NIL = 1<<8,
125 FLAGS_UNSIGNED = 1<<9,
129 FLAGS_WIDTH = 1<<13, /* '*' or '*<num>$' used */
130 FLAGS_WIDTHPARAM = 1<<14, /* width PARAMETER was specified */
131 FLAGS_PREC = 1<<15, /* precision was specified */
132 FLAGS_PRECPARAM = 1<<16, /* precision PARAMETER was specified */
133 FLAGS_CHAR = 1<<17, /* %c story */
134 FLAGS_FLOATE = 1<<18, /* %e or %E */
135 FLAGS_FLOATG = 1<<19 /* %g or %G */
141 long width; /* width OR width parameter number */
142 long precision; /* precision OR precision parameter number */
161 char *buffer; /* allocated buffer */
162 size_t len; /* length of string */
163 size_t alloc; /* length of alloc */
164 bool fail; /* TRUE if an alloc has failed and thus the output is not
168 int curl_msprintf(char *buffer, const char *format, ...);
170 static long dprintf_DollarString(char *input, char **end)
173 while(ISDIGIT(*input)) {
175 number += *input-'0';
178 if(number && ('$'==*input++)) {
185 static BOOL dprintf_IsQualifierNoDollar(char c)
188 case '-': case '+': case ' ': case '#': case '.':
189 case '0': case '1': case '2': case '3': case '4':
190 case '5': case '6': case '7': case '8': case '9':
191 case 'h': case 'l': case 'L': case 'z': case 'q':
199 #ifdef DPRINTF_DEBUG2
200 int dprintf_Pass1Report(va_stack_t *vto, int max)
207 for(i=0; i<max; i++) {
209 switch(vto[i].type) {
225 case FORMAT_LONGLONG:
231 case FORMAT_LONGDOUBLE:
232 type = "long double";
239 for(bit=0; bit<31; bit++) {
240 flags = vto[i].flags & (1<<bit);
242 if(flags & FLAGS_SPACE)
243 strcat(buffer, "space ");
244 else if(flags & FLAGS_SHOWSIGN)
245 strcat(buffer, "plus ");
246 else if(flags & FLAGS_LEFT)
247 strcat(buffer, "left ");
248 else if(flags & FLAGS_ALT)
249 strcat(buffer, "alt ");
250 else if(flags & FLAGS_SHORT)
251 strcat(buffer, "short ");
252 else if(flags & FLAGS_LONG)
253 strcat(buffer, "long ");
254 else if(flags & FLAGS_LONGLONG)
255 strcat(buffer, "longlong ");
256 else if(flags & FLAGS_LONGDOUBLE)
257 strcat(buffer, "longdouble ");
258 else if(flags & FLAGS_PAD_NIL)
259 strcat(buffer, "padnil ");
260 else if(flags & FLAGS_UNSIGNED)
261 strcat(buffer, "unsigned ");
262 else if(flags & FLAGS_OCTAL)
263 strcat(buffer, "octal ");
264 else if(flags & FLAGS_HEX)
265 strcat(buffer, "hex ");
266 else if(flags & FLAGS_UPPER)
267 strcat(buffer, "upper ");
268 else if(flags & FLAGS_WIDTH)
269 strcat(buffer, "width ");
270 else if(flags & FLAGS_WIDTHPARAM)
271 strcat(buffer, "widthparam ");
272 else if(flags & FLAGS_PREC)
273 strcat(buffer, "precision ");
274 else if(flags & FLAGS_PRECPARAM)
275 strcat(buffer, "precparam ");
276 else if(flags & FLAGS_CHAR)
277 strcat(buffer, "char ");
278 else if(flags & FLAGS_FLOATE)
279 strcat(buffer, "floate ");
280 else if(flags & FLAGS_FLOATG)
281 strcat(buffer, "floatg ");
283 printf("REPORT: %d. %s [%s]\n", i, type, buffer);
291 /******************************************************************
294 * Create an index with the type of each parameter entry and its
295 * value (may vary in size)
297 ******************************************************************/
299 static long dprintf_Pass1(char *format, va_stack_t *vto, char **endpos,
315 continue; /* while */
320 /* Handle the positional case (N$) */
324 this_param = dprintf_DollarString(fmt, &fmt);
326 /* we got no positional, get the next counter */
327 this_param = param_num;
329 if (this_param > max_param)
330 max_param = this_param;
333 * The parameter with number 'i' should be used. Next, we need
334 * to get SIZE and TYPE of the parameter. Add the information
341 /* Handle the flags */
343 while (dprintf_IsQualifierNoDollar(*fmt)) {
346 flags |= FLAGS_SPACE;
349 flags |= FLAGS_SHOWSIGN;
353 flags &= ~FLAGS_PAD_NIL;
361 /* The precision is picked from a specified parameter */
363 flags |= FLAGS_PRECPARAM;
367 i = dprintf_DollarString(fmt, &fmt);
371 precision = param_num;
373 if (precision > max_param)
374 max_param = precision;
378 precision = strtol(fmt, &fmt, 10);
382 flags |= FLAGS_SHORT;
385 if (flags & FLAGS_LONG)
386 flags |= FLAGS_LONGLONG;
391 flags |= FLAGS_LONGDOUBLE;
394 flags |= FLAGS_LONGLONG;
397 /* the code below generates a warning if -Wunreachable-code is
400 flags |= FLAGS_LONGLONG;
406 #if SIZEOF_CURL_OFF_T > 4
407 flags |= FLAGS_LONGLONG;
413 if (!(flags & FLAGS_LEFT))
414 flags |= FLAGS_PAD_NIL;
416 case '1': case '2': case '3': case '4':
417 case '5': case '6': case '7': case '8': case '9':
418 flags |= FLAGS_WIDTH;
419 width = strtol(fmt-1, &fmt, 10);
421 case '*': /* Special case */
422 flags |= FLAGS_WIDTHPARAM;
425 i = dprintf_DollarString(fmt, &fmt);
430 if(width > max_param)
438 /* Handle the specifier */
447 vto[i].type = FORMAT_STRING;
450 vto[i].type = FORMAT_INTPTR;
453 vto[i].type = FORMAT_PTR;
456 vto[i].type = FORMAT_INT;
459 vto[i].type = FORMAT_INT;
460 flags |= FLAGS_UNSIGNED;
463 vto[i].type = FORMAT_INT;
464 flags |= FLAGS_OCTAL;
467 vto[i].type = FORMAT_INT;
471 vto[i].type = FORMAT_INT;
472 flags |= FLAGS_HEX|FLAGS_UPPER;
475 vto[i].type = FORMAT_INT;
479 vto[i].type = FORMAT_DOUBLE;
482 vto[i].type = FORMAT_DOUBLE;
483 flags |= FLAGS_FLOATE;
486 vto[i].type = FORMAT_DOUBLE;
487 flags |= FLAGS_FLOATE|FLAGS_UPPER;
490 vto[i].type = FORMAT_DOUBLE;
491 flags |= FLAGS_FLOATG;
494 vto[i].type = FORMAT_DOUBLE;
495 flags |= FLAGS_FLOATG|FLAGS_UPPER;
498 vto[i].type = FORMAT_UNKNOWN;
502 vto[i].flags = flags;
503 vto[i].width = width;
504 vto[i].precision = precision;
506 if (flags & FLAGS_WIDTHPARAM) {
507 /* we have the width specified from a parameter, so we make that
508 parameter's info setup properly */
509 vto[i].width = width - 1;
511 vto[i].type = FORMAT_WIDTH;
512 vto[i].flags = FLAGS_NEW;
513 vto[i].precision = vto[i].width = 0; /* can't use width or precision
516 if (flags & FLAGS_PRECPARAM) {
517 /* we have the precision specified from a parameter, so we make that
518 parameter's info setup properly */
519 vto[i].precision = precision - 1;
521 vto[i].type = FORMAT_WIDTH;
522 vto[i].flags = FLAGS_NEW;
523 vto[i].precision = vto[i].width = 0; /* can't use width or precision
526 *endpos++ = fmt + 1; /* end of this sequence */
530 #ifdef DPRINTF_DEBUG2
531 dprintf_Pass1Report(vto, max_param);
534 /* Read the arg list parameters into our data list */
535 for (i=0; i<max_param; i++) {
536 if ((i + 1 < max_param) && (vto[i + 1].type == FORMAT_WIDTH))
538 /* Width/precision arguments must be read before the main argument
539 * they are attached to
541 vto[i + 1].data.num = va_arg(arglist, int);
547 vto[i].data.str = va_arg(arglist, char *);
553 vto[i].data.ptr = va_arg(arglist, void *);
558 if(vto[i].flags & FLAGS_LONGLONG)
559 vto[i].data.lnum = va_arg(arglist, LONG_LONG);
562 if(vto[i].flags & FLAGS_LONG)
563 vto[i].data.num = va_arg(arglist, long);
565 vto[i].data.num = va_arg(arglist, int);
569 vto[i].data.dnum = va_arg(arglist, double);
573 /* Argument has been read. Silently convert it into an integer
576 vto[i].type = FORMAT_INT;
588 static int dprintf_formatf(
589 void *data, /* untouched by format(), just sent to the stream() function in
590 the second argument */
591 /* function pointer called for each output character */
592 int (*stream)(int, FILE *),
593 const char *format, /* %-formatted string */
594 va_list ap_save) /* list of parameters */
596 /* Base-36 digits for numbers. */
597 const char *digits = lower_digits;
599 /* Pointer into the format string. */
602 /* Number of characters written. */
605 long param; /* current parameter to read */
606 long param_num=0; /* parameter counter */
608 va_stack_t vto[MAX_PARAMETERS];
609 char *endpos[MAX_PARAMETERS];
616 /* Do the actual %-code parsing */
617 dprintf_Pass1((char *)format, vto, endpos, ap_save);
619 end = &endpos[0]; /* the initial end-position from the list dprintf_Pass1()
624 /* Format spec modifiers. */
627 /* Width of a field. */
630 /* Precision of a field. */
633 /* Decimal integer is negative. */
636 /* Base of a number to be written. */
639 /* Integral values to be written. */
641 unsigned LONG_LONG num;
648 /* This isn't a format spec, so write everything out until the next one
649 OR end of string is reached. */
652 } while(*++f && ('%' != *f));
658 /* Check for "%%". Note that although the ANSI standard lists
659 '%' as a conversion specifier, it says "The complete format
660 specification shall be `%%'," so we can avoid all the width
661 and precision processing. */
668 /* If this is a positional parameter, the position must follow imediately
669 after the %, thus create a %<num>$ sequence */
670 param=dprintf_DollarString(f, &f);
677 param_num++; /* increase this always to allow "%2$s %1$s %s" and then the
678 third %s will pick the 3rd argument */
682 /* pick up the specified width */
683 if(p->flags & FLAGS_WIDTHPARAM)
684 width = vto[p->width].data.num;
688 /* pick up the specified precision */
689 if(p->flags & FLAGS_PRECPARAM)
690 prec = vto[p->precision].data.num;
691 else if(p->flags & FLAGS_PREC)
696 alt = (char)((p->flags & FLAGS_ALT)?TRUE:FALSE);
701 if(p->flags & FLAGS_CHAR) {
703 if (!(p->flags & FLAGS_LEFT))
707 if (p->flags & FLAGS_LEFT)
712 if(p->flags & FLAGS_UNSIGNED) {
713 /* Decimal unsigned integer. */
715 goto unsigned_number;
717 if(p->flags & FLAGS_OCTAL) {
718 /* Octal unsigned integer. */
720 goto unsigned_number;
722 if(p->flags & FLAGS_HEX) {
723 /* Hexadecimal unsigned integer. */
725 digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits;
727 goto unsigned_number;
730 /* Decimal integer. */
734 if(p->flags & FLAGS_LONGLONG) {
736 is_neg = (char)(p->data.lnum < 0);
737 num = is_neg ? (- p->data.lnum) : p->data.lnum;
742 signed_num = (long) num;
743 is_neg = (char)(signed_num < 0);
744 num = is_neg ? (- signed_num) : signed_num;
749 /* Unsigned number of base BASE. */
753 /* Number of base BASE. */
755 char *workend = &work[sizeof(work) - 1];
758 /* Supply a default precision if none was given. */
762 /* Put the number in WORK. */
765 *w-- = digits[num % base];
768 width -= (long)(workend - w);
769 prec -= (long)(workend - w);
771 if (alt && base == 8 && prec <= 0) {
782 if (alt && base == 16)
785 if (is_neg || (p->flags & FLAGS_SHOWSIGN) || (p->flags & FLAGS_SPACE))
788 if (!(p->flags & FLAGS_LEFT) && !(p->flags & FLAGS_PAD_NIL))
794 else if (p->flags & FLAGS_SHOWSIGN)
796 else if (p->flags & FLAGS_SPACE)
799 if (alt && base == 16) {
801 if(p->flags & FLAGS_UPPER)
807 if (!(p->flags & FLAGS_LEFT) && (p->flags & FLAGS_PAD_NIL))
811 /* Write the number. */
812 while (++w <= workend) {
816 if (p->flags & FLAGS_LEFT)
825 static const char null[] = "(nil)";
829 str = (char *) p->data.str;
831 /* Write null[] if there's space. */
832 if (prec == -1 || prec >= (long) sizeof(null) - 1) {
834 len = sizeof(null) - 1;
835 /* Disable quotes around (nil) */
836 p->flags &= (~FLAGS_ALT);
846 if (prec != -1 && (size_t) prec < len)
850 if (p->flags & FLAGS_ALT)
853 if (!(p->flags&FLAGS_LEFT))
859 if (p->flags&FLAGS_LEFT)
863 if (p->flags & FLAGS_ALT)
869 /* Generic pointer. */
872 ptr = (void *) p->data.ptr;
874 /* If the pointer is not NULL, write it as a %#x spec. */
876 digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits;
883 /* Write "(nil)" for a nil pointer. */
884 static const char strnil[] = "(nil)";
887 width -= sizeof(strnil) - 1;
888 if (p->flags & FLAGS_LEFT)
891 for (point = strnil; *point != '\0'; ++point)
893 if (! (p->flags & FLAGS_LEFT))
902 char formatbuf[32]="%";
904 size_t left = sizeof(formatbuf)-strlen(formatbuf);
908 if (p->flags & FLAGS_WIDTH)
910 else if (p->flags & FLAGS_WIDTHPARAM)
911 width = vto[p->width].data.num;
914 if (p->flags & FLAGS_PREC)
916 else if (p->flags & FLAGS_PRECPARAM)
917 prec = vto[p->precision].data.num;
919 if (p->flags & FLAGS_LEFT)
920 strcat(formatbuf, "-");
921 if (p->flags & FLAGS_SHOWSIGN)
922 strcat(formatbuf, "+");
923 if (p->flags & FLAGS_SPACE)
924 strcat(formatbuf, " ");
925 if (p->flags & FLAGS_ALT)
926 strcat(formatbuf, "#");
928 fptr=&formatbuf[strlen(formatbuf)];
931 /* RECURSIVE USAGE */
932 len = curl_msnprintf(fptr, left, "%ld", width);
937 /* RECURSIVE USAGE */
938 len = curl_msnprintf(fptr, left, ".%ld", prec);
942 if (p->flags & FLAGS_LONG)
945 if (p->flags & FLAGS_FLOATE)
946 *fptr++ = (char)((p->flags & FLAGS_UPPER) ? 'E':'e');
947 else if (p->flags & FLAGS_FLOATG)
948 *fptr++ = (char)((p->flags & FLAGS_UPPER) ? 'G' : 'g');
952 *fptr = 0; /* and a final zero termination */
954 /* NOTE NOTE NOTE!! Not all sprintf() implementations returns number
955 of output characters */
956 (sprintf)(work, formatbuf, p->data.dnum);
958 for(fptr=work; *fptr; fptr++)
964 /* Answer the count of characters written. */
966 if (p->flags & FLAGS_LONGLONG)
967 *(LONG_LONG *) p->data.ptr = (LONG_LONG)done;
970 if (p->flags & FLAGS_LONG)
971 *(long *) p->data.ptr = (long)done;
972 else if (!(p->flags & FLAGS_SHORT))
973 *(int *) p->data.ptr = (int)done;
975 *(short *) p->data.ptr = (short)done;
981 f = *end++; /* goto end of %-code */
987 /* fputc() look-alike */
988 static int addbyter(int output, FILE *data)
990 struct nsprintf *infop=(struct nsprintf *)data;
991 unsigned char outc = (unsigned char)output;
993 if(infop->length < infop->max) {
994 /* only do this if we haven't reached max length yet */
995 infop->buffer[0] = outc; /* store */
996 infop->buffer++; /* increase pointer */
997 infop->length++; /* we are now one byte larger */
998 return outc; /* fputc() returns like this on success */
1003 int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format,
1007 struct nsprintf info;
1009 info.buffer = buffer;
1011 info.max = maxlength;
1013 retcode = dprintf_formatf(&info, addbyter, format, ap_save);
1015 /* we terminate this with a zero byte */
1016 if(info.max == info.length)
1017 /* we're at maximum, scrap the last letter */
1018 info.buffer[-1] = 0;
1025 int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...)
1028 va_list ap_save; /* argument pointer */
1029 va_start(ap_save, format);
1030 retcode = curl_mvsnprintf(buffer, maxlength, format, ap_save);
1035 /* fputc() look-alike */
1036 static int alloc_addbyter(int output, FILE *data)
1038 struct asprintf *infop=(struct asprintf *)data;
1039 unsigned char outc = (unsigned char)output;
1041 if(!infop->buffer) {
1042 infop->buffer=(char *)malloc(32);
1043 if(!infop->buffer) {
1045 return -1; /* fail */
1050 else if(infop->len+1 >= infop->alloc) {
1053 newptr = (char *)realloc(infop->buffer, infop->alloc*2);
1059 infop->buffer = newptr;
1063 infop->buffer[ infop->len ] = outc;
1067 return outc; /* fputc() returns like this on success */
1070 char *curl_maprintf(const char *format, ...)
1072 va_list ap_save; /* argument pointer */
1074 struct asprintf info;
1081 va_start(ap_save, format);
1082 retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save);
1084 if((-1 == retcode) || info.fail) {
1090 info.buffer[info.len] = 0; /* we terminate this with a zero byte */
1097 char *curl_mvaprintf(const char *format, va_list ap_save)
1100 struct asprintf info;
1107 retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save);
1108 if((-1 == retcode) || info.fail) {
1115 info.buffer[info.len] = 0; /* we terminate this with a zero byte */
1122 static int storebuffer(int output, FILE *data)
1124 char **buffer = (char **)data;
1125 unsigned char outc = (unsigned char)output;
1128 return outc; /* act like fputc() ! */
1131 int curl_msprintf(char *buffer, const char *format, ...)
1133 va_list ap_save; /* argument pointer */
1135 va_start(ap_save, format);
1136 retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save);
1138 *buffer=0; /* we terminate this with a zero byte */
1142 int curl_mprintf(const char *format, ...)
1145 va_list ap_save; /* argument pointer */
1146 va_start(ap_save, format);
1148 retcode = dprintf_formatf(stdout, fputc, format, ap_save);
1153 int curl_mfprintf(FILE *whereto, const char *format, ...)
1156 va_list ap_save; /* argument pointer */
1157 va_start(ap_save, format);
1158 retcode = dprintf_formatf(whereto, fputc, format, ap_save);
1163 int curl_mvsprintf(char *buffer, const char *format, va_list ap_save)
1166 retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save);
1167 *buffer=0; /* we terminate this with a zero byte */
1171 int curl_mvprintf(const char *format, va_list ap_save)
1173 return dprintf_formatf(stdout, fputc, format, ap_save);
1176 int curl_mvfprintf(FILE *whereto, const char *format, va_list ap_save)
1178 return dprintf_formatf(whereto, fputc, format, ap_save);
1181 #ifdef DPRINTF_DEBUG
1189 long long test = 0x1000000000LL;
1190 curl_mprintf("%lld %lld %lld\n", one, two, test);
1193 curl_mprintf("%3d %5d\n", 10, 1998);
1195 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);
1199 memset(ptr, 55, strlen(ptr)+1);
1204 curl_mprintf(buffer, "%s %s %d", "daniel", "stenberg", 19988);
1207 curl_mfprintf(stderr, "%s %#08x\n", "dummy", 65);
1209 printf("%s %#08x\n", "dummy", 65);
1211 double tryout = 3.14156592;
1212 curl_mprintf(buffer, "%.2g %G %f %e %E", tryout, tryout, tryout, tryout, tryout);
1214 printf("%.2g %G %f %e %E\n", tryout, tryout, tryout, tryout, tryout);