new
[libcurl.git] / lib / mprintf.c
blob2aa3fc0fec740d93596a3f292887f7ea76320f3b
1 /****************************************************************************
3 * $Id: mprintf.c,v 1.1.1.1 1999-12-29 14:21:34 bagder Exp $
5 *************************************************************************
7 * Purpose:
8 * A merge of Bjorn Reese's format() function and Daniel's dsprintf()
9 * 1.0. A full blooded printf() clone with full support for <num>$
10 * everywhere (parameters, widths and precisions) including variabled
11 * sized parameters (like doubles, long longs, long doubles and even
12 * void * in 64-bit architectures).
14 * Current restrictions:
15 * - Max 128 parameters
16 * - No 'long double' support.
18 *************************************************************************
21 * 1998/01/10 (v2.8)
22 * Daniel
23 * - Updated version number.
24 * - Corrected a static non-zero prefixed width problem.
26 * 1998/11/17 - Daniel
27 * Added daprintf() and dvaprintf() for allocated printf() and vprintf().
28 * They return an allocated buffer with the result inside. The result must
29 * be free()ed!
31 * 1998/08/23 - breese
33 * Converted all non-printable (and non-whitespace) characters into
34 * their decimal ASCII value preceeded by a '\' character
35 * (this only applies to snprintf family so far)
37 * Added %S (which is the same as %#s)
39 * 1998/05/05 (v2.7)
41 * Fixed precision and width qualifiers (%.*s)
43 * Added support for snprintf()
45 * Quoting (%#s) is disabled for the (nil) pointer
47 * 1997/06/09 (v2.6)
49 * %#s means that the string will be quoted with "
50 * (I was getting tired of writing \"%s\" all the time)
52 * [ERR] for strings changed to (nil)
54 * v2.5
55 * - Added C++ support
56 * - Prepended all internal functions with dprintf_
57 * - Defined the booleans
59 * v2.4
60 * - Added dvsprintf(), dvfprintf() and dvprintf().
61 * - Made the formatting function available with the name _formatf() to enable
62 * other *printf()-inspired functions. (I considered adding a dmsprintf()
63 * that works like sprintf() but allocates the destination string and
64 * possibly enlarges it itself, but things like that should be done with the
65 * new _formatf() instead.)
67 * v2.3
68 * - Small modifications to make it compile nicely at both Daniel's and
69 * Bjorn's place.
71 * v2.2
72 * - Made it work with text to the right of the last %!
73 * - Introduced dprintf(), dsprintf() and dfprintf().
74 * - Float/double support enabled. This system is currently using the ordinary
75 * sprintf() function. NOTE that positional parameters, widths and precisions
76 * will still work like it should since the d-system takes care of that and
77 * passes that information re-formatted to the old sprintf().
79 * v2.1
80 * - Fixed space padding (i.e %d was extra padded previously)
81 * - long long output is supported
82 * - alternate output is done correct like in %#08x
84 ****************************************************************************/
86 static const char rcsid[] = "@(#)$Id: mprintf.c,v 1.1.1.1 1999-12-29 14:21:34 bagder Exp $";
89 * To test:
91 * Use WIDTH, PRECISION and NUMBERED ARGUMENT combined.
94 #include <sys/types.h>
95 #include <stdio.h>
96 #include <stdlib.h>
97 #include <stdarg.h>
98 #include <ctype.h>
99 #include <string.h>
102 #define BUFFSIZE 256 /* buffer for long-to-str and float-to-str calcs */
103 #define MAX_PARAMETERS 128 /* lame static limit */
105 #undef TRUE
106 #undef FALSE
107 #undef BOOL
108 #ifdef __cplusplus
109 # define TRUE true
110 # define FALSE false
111 # define BOOL bool
112 #else
113 # define TRUE ((char)(1 == 1))
114 # define FALSE ((char)(0 == 1))
115 # define BOOL char
116 #endif
119 /* Lower-case digits. */
120 static const char lower_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
122 /* Upper-case digits. */
123 static const char upper_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
125 #define OUTCHAR(x) done+=(stream(x, (FILE *)data)==-1?0:1)
127 /* Data type to read from the arglist */
128 typedef enum {
129 FORMAT_UNKNOWN = 0,
130 FORMAT_STRING,
131 FORMAT_PTR,
132 FORMAT_INT,
133 FORMAT_INTPTR,
134 FORMAT_LONG,
135 FORMAT_LONGLONG,
136 FORMAT_DOUBLE,
137 FORMAT_LONGDOUBLE,
138 FORMAT_WIDTH /* For internal use */
139 } FormatType;
141 /* convertion and display flags */
142 enum {
143 FLAGS_NEW = 0,
144 FLAGS_SPACE = 1<<0,
145 FLAGS_SHOWSIGN = 1<<1,
146 FLAGS_LEFT = 1<<2,
147 FLAGS_ALT = 1<<3,
148 FLAGS_SHORT = 1<<4,
149 FLAGS_LONG = 1<<5,
150 FLAGS_LONGLONG = 1<<6,
151 FLAGS_LONGDOUBLE = 1<<7,
152 FLAGS_PAD_NIL = 1<<8,
153 FLAGS_UNSIGNED = 1<<9,
154 FLAGS_OCTAL = 1<<10,
155 FLAGS_HEX = 1<<11,
156 FLAGS_UPPER = 1<<12,
157 FLAGS_WIDTH = 1<<13, /* '*' or '*<num>$' used */
158 FLAGS_WIDTHPARAM = 1<<14, /* width PARAMETER was specified */
159 FLAGS_PREC = 1<<15, /* precision was specified */
160 FLAGS_PRECPARAM = 1<<16, /* precision PARAMETER was specified */
161 FLAGS_CHAR = 1<<17, /* %c story */
162 FLAGS_FLOATE = 1<<18, /* %e or %E */
163 FLAGS_FLOATG = 1<<19 /* %g or %G */
166 typedef struct {
167 FormatType type;
168 int flags;
169 int width; /* width OR width parameter number */
170 int precision; /* precision OR precision parameter number */
171 union {
172 char *str;
173 void *ptr;
174 long num;
175 #if SIZEOF_LONG_LONG /* if this is non-zero */
176 long long lnum;
177 #endif
178 double dnum;
179 #if SIZEOF_LONG_DOUBLE
180 long double ldnum;
181 #endif
182 } data;
183 } va_stack_t;
185 struct nsprintf {
186 char *buffer;
187 size_t length;
188 size_t max;
191 struct asprintf {
192 char *buffer; /* allocated buffer */
193 size_t len; /* length of string */
194 size_t alloc; /* length of alloc */
197 int msprintf(char *buffer, const char *format, ...);
199 static int dprintf_DollarString(char *input, char **end)
201 int number=0;
202 while(isdigit((int)*input)) {
203 number *= 10;
204 number += *input-'0';
205 input++;
207 if(number && ('$'==*input++)) {
208 *end = input;
209 return number;
211 return 0;
214 static BOOL dprintf_IsQualifierNoDollar(char c)
216 switch (c) {
217 case '-': case '+': case ' ': case '#': case '.':
218 case '0': case '1': case '2': case '3': case '4':
219 case '5': case '6': case '7': case '8': case '9':
220 case 'h': case 'l': case 'L': case 'Z': case 'q':
221 return TRUE;
222 default:
223 return FALSE;
227 #ifdef DPRINTF_DEBUG2
228 int dprintf_Pass1Report(va_stack_t *vto, int max)
230 int i;
231 char buffer[128];
232 int bit;
233 int flags;
235 for(i=0; i<max; i++) {
236 char *type;
237 switch(vto[i].type) {
238 case FORMAT_UNKNOWN:
239 type = "unknown";
240 break;
241 case FORMAT_STRING:
242 type ="string";
243 break;
244 case FORMAT_PTR:
245 type ="pointer";
246 break;
247 case FORMAT_INT:
248 type = "int";
249 break;
250 case FORMAT_LONG:
251 type = "long";
252 break;
253 case FORMAT_LONGLONG:
254 type = "long long";
255 break;
256 case FORMAT_DOUBLE:
257 type = "double";
258 break;
259 case FORMAT_LONGDOUBLE:
260 type = "long double";
261 break;
265 buffer[0]=0;
267 for(bit=0; bit<31; bit++) {
268 flags = vto[i].flags & (1<<bit);
270 if(flags & FLAGS_SPACE)
271 strcat(buffer, "space ");
272 else if(flags & FLAGS_SHOWSIGN)
273 strcat(buffer, "plus ");
274 else if(flags & FLAGS_LEFT)
275 strcat(buffer, "left ");
276 else if(flags & FLAGS_ALT)
277 strcat(buffer, "alt ");
278 else if(flags & FLAGS_SHORT)
279 strcat(buffer, "short ");
280 else if(flags & FLAGS_LONG)
281 strcat(buffer, "long ");
282 else if(flags & FLAGS_LONGLONG)
283 strcat(buffer, "longlong ");
284 else if(flags & FLAGS_LONGDOUBLE)
285 strcat(buffer, "longdouble ");
286 else if(flags & FLAGS_PAD_NIL)
287 strcat(buffer, "padnil ");
288 else if(flags & FLAGS_UNSIGNED)
289 strcat(buffer, "unsigned ");
290 else if(flags & FLAGS_OCTAL)
291 strcat(buffer, "octal ");
292 else if(flags & FLAGS_HEX)
293 strcat(buffer, "hex ");
294 else if(flags & FLAGS_UPPER)
295 strcat(buffer, "upper ");
296 else if(flags & FLAGS_WIDTH)
297 strcat(buffer, "width ");
298 else if(flags & FLAGS_WIDTHPARAM)
299 strcat(buffer, "widthparam ");
300 else if(flags & FLAGS_PREC)
301 strcat(buffer, "precision ");
302 else if(flags & FLAGS_PRECPARAM)
303 strcat(buffer, "precparam ");
304 else if(flags & FLAGS_CHAR)
305 strcat(buffer, "char ");
306 else if(flags & FLAGS_FLOATE)
307 strcat(buffer, "floate ");
308 else if(flags & FLAGS_FLOATG)
309 strcat(buffer, "floatg ");
311 printf("REPORT: %d. %s [%s]\n", i, type, buffer);
317 #endif
319 /******************************************************************
321 * Pass 1:
322 * Create an index with the type of each parameter entry and its
323 * value (may vary in size)
325 ******************************************************************/
327 static int dprintf_Pass1(char *format, va_stack_t *vto, char **endpos, va_list arglist)
329 char *fmt = format;
330 int param_num = 0;
331 int this_param;
332 int width;
333 int precision;
334 int flags;
335 int max_param=0;
336 int i;
338 while (*fmt) {
339 if (*fmt++ == '%') {
340 if (*fmt == '%') {
341 fmt++;
342 continue; /* while */
345 flags = FLAGS_NEW;
347 /* Handle the positional case (N$) */
349 param_num++;
351 this_param = dprintf_DollarString(fmt, &fmt);
352 if (0 == this_param)
353 /* we got no positional, get the next counter */
354 this_param = param_num;
356 if (this_param > max_param)
357 max_param = this_param;
360 * The parameter with number 'i' should be used. Next, we need
361 * to get SIZE and TYPE of the parameter. Add the information
362 * to our array.
365 width = 0;
366 precision = 0;
368 /* Handle the flags */
370 while (dprintf_IsQualifierNoDollar(*fmt)) {
371 switch (*fmt++) {
372 case ' ':
373 flags |= FLAGS_SPACE;
374 break;
375 case '+':
376 flags |= FLAGS_SHOWSIGN;
377 break;
378 case '-':
379 flags |= FLAGS_LEFT;
380 flags &= ~FLAGS_PAD_NIL;
381 break;
382 case '#':
383 flags |= FLAGS_ALT;
384 break;
385 case '.':
386 flags |= FLAGS_PREC;
387 if ('*' == *fmt) {
388 /* The precision is picked from a specified parameter */
390 flags |= FLAGS_PRECPARAM;
391 fmt++;
392 param_num++;
394 i = dprintf_DollarString(fmt, &fmt);
395 if (i)
396 precision = i;
397 else
398 precision = param_num;
400 if (precision > max_param)
401 max_param = precision;
403 else {
404 flags |= FLAGS_PREC;
405 precision = strtol(fmt, &fmt, 10);
407 break;
408 case 'h':
409 flags |= FLAGS_SHORT;
410 break;
411 case 'l':
412 if (flags & FLAGS_LONG)
413 flags |= FLAGS_LONGLONG;
414 else
415 flags |= FLAGS_LONG;
416 break;
417 case 'L':
418 flags |= FLAGS_LONGDOUBLE;
419 break;
420 case 'q':
421 flags |= FLAGS_LONGLONG;
422 break;
423 case 'Z':
424 if (sizeof(size_t) > sizeof(unsigned long int))
425 flags |= FLAGS_LONGLONG;
426 if (sizeof(size_t) > sizeof(unsigned int))
427 flags |= FLAGS_LONG;
428 break;
429 case '0':
430 if (!(flags & FLAGS_LEFT))
431 flags |= FLAGS_PAD_NIL;
432 /* FALLTHROUGH */
433 case '1': case '2': case '3': case '4':
434 case '5': case '6': case '7': case '8': case '9':
435 flags |= FLAGS_WIDTH;
436 width = strtol(--fmt, &fmt, 10);
437 break;
438 case '*': /* Special case */
439 flags |= FLAGS_WIDTHPARAM;
440 param_num++;
442 i = dprintf_DollarString(fmt, &fmt);
443 if(i)
444 width = i;
445 else
446 width = param_num;
447 if(width > max_param)
448 max_param=width;
449 break;
450 default:
451 break;
453 } /* switch */
455 /* Handle the specifier */
457 i = this_param - 1;
459 switch (*fmt) {
460 case 'S':
461 flags |= FLAGS_ALT;
462 /* FALLTHROUGH */
463 case 's':
464 vto[i].type = FORMAT_STRING;
465 break;
466 case 'n':
467 vto[i].type = FORMAT_INTPTR;
468 break;
469 case 'p':
470 vto[i].type = FORMAT_PTR;
471 break;
472 case 'd': case 'i':
473 vto[i].type = FORMAT_INT;
474 break;
475 case 'u':
476 vto[i].type = FORMAT_INT;
477 flags |= FLAGS_UNSIGNED;
478 break;
479 case 'o':
480 vto[i].type = FORMAT_INT;
481 flags |= FLAGS_OCTAL;
482 break;
483 case 'x':
484 vto[i].type = FORMAT_INT;
485 flags |= FLAGS_HEX;
486 break;
487 case 'X':
488 vto[i].type = FORMAT_INT;
489 flags |= FLAGS_HEX|FLAGS_UPPER;
490 break;
491 case 'c':
492 vto[i].type = FORMAT_INT;
493 flags |= FLAGS_CHAR;
494 break;
495 case 'f':
496 vto[i].type = FORMAT_DOUBLE;
497 break;
498 case 'e': case 'E':
499 vto[i].type = FORMAT_DOUBLE;
500 flags |= FLAGS_FLOATE| (('E' == *fmt)?FLAGS_UPPER:0);
501 break;
502 case 'g': case 'G':
503 vto[i].type = FORMAT_DOUBLE;
504 flags |= FLAGS_FLOATG| (('G' == *fmt)?FLAGS_UPPER:0);
505 break;
506 default:
507 vto[i].type = FORMAT_UNKNOWN;
508 break;
509 } /* switch */
511 vto[i].flags = flags;
512 vto[i].width = width;
513 vto[i].precision = precision;
515 if (flags & FLAGS_WIDTHPARAM) {
516 /* we have the width specified from a parameter, so we make that
517 parameter's info setup properly */
518 vto[i].width = width - 1;
519 i = width - 1;
520 vto[i].type = FORMAT_WIDTH;
521 vto[i].flags = FLAGS_NEW;
522 vto[i].precision = vto[i].width = 0; /* can't use width or precision
523 of width! */
525 if (flags & FLAGS_PRECPARAM) {
526 /* we have the precision specified from a parameter, so we make that
527 parameter's info setup properly */
528 vto[i].precision = precision - 1;
529 i = precision - 1;
530 vto[i].type = FORMAT_WIDTH;
531 vto[i].flags = FLAGS_NEW;
532 vto[i].precision = vto[i].width = 0; /* can't use width or precision
533 of width! */
535 *endpos++ = fmt + 1; /* end of this sequence */
539 #ifdef DPRINTF_DEBUG2
540 dprintf_Pass1Report(vto, max_param);
541 #endif
543 /* Read the arg list parameters into our data list */
544 for (i=0; i<max_param; i++) {
545 if ((i + 1 < max_param) && (vto[i + 1].type == FORMAT_WIDTH))
547 /* Width/precision arguments must be read before the main argument
548 * they are attached to
550 vto[i + 1].data.num = va_arg(arglist, int);
553 switch (vto[i].type)
555 case FORMAT_STRING:
556 vto[i].data.str = va_arg(arglist, char *);
557 break;
559 case FORMAT_INTPTR:
560 case FORMAT_UNKNOWN:
561 case FORMAT_PTR:
562 vto[i].data.ptr = va_arg(arglist, void *);
563 break;
565 case FORMAT_INT:
566 #if SIZEOF_LONG_LONG
567 if(vto[i].flags & FLAGS_LONGLONG)
568 vto[i].data.lnum = va_arg(arglist, long long);
569 else
570 #endif
571 if(vto[i].flags & FLAGS_LONG)
572 vto[i].data.num = va_arg(arglist, long);
573 else
574 vto[i].data.num = va_arg(arglist, int);
575 break;
577 case FORMAT_DOUBLE:
578 #if SIZEOF_LONG_DOUBLE
579 if(vto[i].flags & FLAGS_LONG)
580 vto[i].data.ldnum = va_arg(arglist, long double);
581 else
582 #endif
583 vto[i].data.dnum = va_arg(arglist, double);
584 break;
586 case FORMAT_WIDTH:
587 /* Argument has been read. Silently convert it into an integer
588 * for later use
590 vto[i].type = FORMAT_INT;
591 break;
593 default:
594 break;
598 return max_param;
602 static int dprintf_formatf(
603 void *data, /* untouched by format(), just sent to the
604 stream() function in the first argument */
605 int (*stream)(int, FILE *), /* function pointer called for each
606 output character */
607 const char *format, /* %-formatted string */
608 va_list ap_save) /* list of parameters */
610 /* Base-36 digits for numbers. */
611 const char *digits = lower_digits;
613 /* Pointer into the format string. */
614 char *f;
616 /* Number of characters written. */
617 register size_t done = 0;
619 long param; /* current parameter to read */
620 long param_num=0; /* parameter counter */
622 va_stack_t vto[MAX_PARAMETERS];
623 char *endpos[MAX_PARAMETERS];
624 char **end;
626 char work[BUFFSIZE];
628 va_stack_t *p;
630 /* Do the actual %-code parsing */
631 dprintf_Pass1((char *)format, vto, endpos, ap_save);
633 end = &endpos[0]; /* the initial end-position from the list dprintf_Pass1()
634 created for us */
636 f = (char *)format;
637 while (*f != '\0') {
638 /* Format spec modifiers. */
639 char alt;
641 /* Width of a field. */
642 register long width;
643 /* Precision of a field. */
644 long prec;
646 /* Decimal integer is negative. */
647 char is_neg;
649 /* Base of a number to be written. */
650 long base;
652 /* Integral values to be written. */
653 #if SIZEOF_LONG_LONG
654 unsigned long long num;
655 #else
656 unsigned long num;
657 #endif
658 long signed_num;
660 if (*f != '%') {
661 /* This isn't a format spec, so write everything out until the next one
662 OR end of string is reached. */
663 do {
664 OUTCHAR(*f);
665 } while(*++f && ('%' != *f));
666 continue;
669 ++f;
671 /* Check for "%%". Note that although the ANSI standard lists
672 '%' as a conversion specifier, it says "The complete format
673 specification shall be `%%'," so we can avoid all the width
674 and precision processing. */
675 if (*f == '%') {
676 ++f;
677 OUTCHAR('%');
678 continue;
681 /* If this is a positional parameter, the position must follow imediately
682 after the %, thus create a %<num>$ sequence */
683 param=dprintf_DollarString(f, &f);
685 if(!param)
686 param = param_num;
687 else
688 --param;
690 param_num++; /* increase this always to allow "%2$s %1$s %s" and then the
691 third %s will pick the 3rd argument */
693 p = &vto[param];
695 /* pick up the specified width */
696 if(p->flags & FLAGS_WIDTHPARAM)
697 width = vto[p->width].data.num;
698 else
699 width = p->width;
701 /* pick up the specified precision */
702 if(p->flags & FLAGS_PRECPARAM)
703 prec = vto[p->precision].data.num;
704 else if(p->flags & FLAGS_PREC)
705 prec = p->precision;
706 else
707 prec = -1;
709 alt = p->flags & FLAGS_ALT;
711 switch (p->type) {
712 case FORMAT_INT:
713 num = p->data.num;
714 if(p->flags & FLAGS_CHAR) {
715 /* Character. */
716 if (!(p->flags & FLAGS_LEFT))
717 while (--width > 0)
718 OUTCHAR(' ');
719 OUTCHAR((char) num);
720 if (p->flags & FLAGS_LEFT)
721 while (--width > 0)
722 OUTCHAR(' ');
723 break;
725 if(p->flags & FLAGS_UNSIGNED) {
726 /* Decimal unsigned integer. */
727 base = 10;
728 goto unsigned_number;
730 if(p->flags & FLAGS_OCTAL) {
731 /* Octal unsigned integer. */
732 base = 8;
733 goto unsigned_number;
735 if(p->flags & FLAGS_HEX) {
736 /* Hexadecimal unsigned integer. */
738 digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits;
739 base = 16;
740 goto unsigned_number;
743 /* Decimal integer. */
744 base = 10;
746 #if SIZEOF_LONG_LONG
747 if(p->flags & FLAGS_LONGLONG) {
748 /* long long */
749 num = p->data.lnum;
750 is_neg = num < 0;
751 num = is_neg ? (- num) : num;
753 else
754 #endif
756 signed_num = (long) num;
758 is_neg = signed_num < 0;
759 num = is_neg ? (- signed_num) : signed_num;
761 goto number;
763 unsigned_number:;
764 /* Unsigned number of base BASE. */
765 is_neg = 0;
767 number:;
768 /* Number of base BASE. */
770 char *workend = &work[sizeof(work) - 1];
771 register char *w;
773 /* Supply a default precision if none was given. */
774 if (prec == -1)
775 prec = 1;
777 /* Put the number in WORK. */
778 w = workend;
779 while (num > 0) {
780 *w-- = digits[num % base];
781 num /= base;
783 width -= workend - w;
784 prec -= workend - w;
786 if (alt && base == 8 && prec <= 0) {
787 *w-- = '0';
788 --width;
791 if (prec > 0) {
792 width -= prec;
793 while (prec-- > 0)
794 *w-- = '0';
797 if (alt && base == 16)
798 width -= 2;
800 if (is_neg || (p->flags & FLAGS_SHOWSIGN) || (p->flags & FLAGS_SPACE))
801 --width;
803 if (!(p->flags & FLAGS_LEFT) && !(p->flags & FLAGS_PAD_NIL))
804 while (width-- > 0)
805 OUTCHAR(' ');
807 if (is_neg)
808 OUTCHAR('-');
809 else if (p->flags & FLAGS_SHOWSIGN)
810 OUTCHAR('+');
811 else if (p->flags & FLAGS_SPACE)
812 OUTCHAR(' ');
814 if (alt && base == 16) {
815 OUTCHAR('0');
816 if(p->flags & FLAGS_UPPER)
817 OUTCHAR('X');
818 else
819 OUTCHAR('x');
822 if (!(p->flags & FLAGS_LEFT) && (p->flags & FLAGS_PAD_NIL))
823 while (width-- > 0)
824 OUTCHAR('0');
826 /* Write the number. */
827 while (++w <= workend) {
828 OUTCHAR(*w);
831 if (p->flags & FLAGS_LEFT)
832 while (width-- > 0)
833 OUTCHAR(' ');
835 break;
837 case FORMAT_STRING:
838 /* String. */
840 static char null[] = "(nil)";
841 char *str;
842 size_t len;
844 str = (char *) p->data.str;
845 if ( str == NULL) {
846 /* Write null[] if there's space. */
847 if (prec == -1 || prec >= (long) sizeof(null) - 1) {
848 str = null;
849 len = sizeof(null) - 1;
850 /* Disable quotes around (nil) */
851 p->flags &= (~FLAGS_ALT);
853 else {
854 str = "";
855 len = 0;
858 else
859 len = strlen(str);
861 if (prec != -1 && (size_t) prec < len)
862 len = prec;
863 width -= len;
865 if (p->flags & FLAGS_ALT)
866 OUTCHAR('"');
868 if (!(p->flags&FLAGS_LEFT))
869 while (width-- > 0)
870 OUTCHAR(' ');
872 while (len-- > 0)
873 OUTCHAR(*str++);
874 if (p->flags&FLAGS_LEFT)
875 while (width-- > 0)
876 OUTCHAR(' ');
878 if (p->flags & FLAGS_ALT)
879 OUTCHAR('"');
881 break;
883 case FORMAT_PTR:
884 /* Generic pointer. */
886 void *ptr;
887 ptr = (void *) p->data.ptr;
888 if (ptr != NULL) {
889 /* If the pointer is not NULL, write it as a %#x spec. */
890 base = 16;
891 digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits;
892 alt = 1;
893 num = (unsigned long) ptr;
894 is_neg = 0;
895 goto number;
897 else {
898 /* Write "(nil)" for a nil pointer. */
899 static char nil[] = "(nil)";
900 register char *point;
902 width -= sizeof(nil) - 1;
903 if (p->flags & FLAGS_LEFT)
904 while (width-- > 0)
905 OUTCHAR(' ');
906 for (point = nil; *point != '\0'; ++point)
907 OUTCHAR(*point);
908 if (! (p->flags & FLAGS_LEFT))
909 while (width-- > 0)
910 OUTCHAR(' ');
913 break;
915 case FORMAT_DOUBLE:
917 char formatbuf[32]="%";
918 char *fptr;
920 width = -1;
921 if (p->flags & FLAGS_WIDTH)
922 width = p->width;
923 else if (p->flags & FLAGS_WIDTHPARAM)
924 width = vto[p->width].data.num;
926 prec = -1;
927 if (p->flags & FLAGS_PREC)
928 prec = p->precision;
929 else if (p->flags & FLAGS_PRECPARAM)
930 prec = vto[p->precision].data.num;
932 if (p->flags & FLAGS_LEFT)
933 strcat(formatbuf, "-");
934 if (p->flags & FLAGS_SHOWSIGN)
935 strcat(formatbuf, "+");
936 if (p->flags & FLAGS_SPACE)
937 strcat(formatbuf, " ");
938 if (p->flags & FLAGS_ALT)
939 strcat(formatbuf, "#");
941 fptr=&formatbuf[strlen(formatbuf)];
943 if(width >= 0) {
944 /* RECURSIVE USAGE */
945 fptr += msprintf(fptr, "%d", width);
947 if(prec >= 0) {
948 /* RECURSIVE USAGE */
949 fptr += msprintf(fptr, ".%d", prec);
951 if (p->flags & FLAGS_LONG)
952 strcat(fptr, "l");
954 if (p->flags & FLAGS_FLOATE)
955 strcat(fptr, p->flags&FLAGS_UPPER?"E":"e");
956 else if (p->flags & FLAGS_FLOATG)
957 strcat(fptr, (p->flags & FLAGS_UPPER) ? "G" : "g");
958 else
959 strcat(fptr, "f");
961 /* NOTE NOTE NOTE!! Not all sprintf() implementations returns number
962 of output characters */
963 #if SIZEOF_LONG_DOUBLE
964 if (p->flags & FLAGS_LONG)
965 /* This is for support of the 'long double' type */
966 (sprintf)(work, formatbuf, p->data.ldnum);
967 else
968 #endif
969 (sprintf)(work, formatbuf, p->data.dnum);
971 for(fptr=work; *fptr; fptr++)
972 OUTCHAR(*fptr);
974 break;
976 case FORMAT_INTPTR:
977 /* Answer the count of characters written. */
978 #if SIZEOF_LONG_LONG
979 if (p->flags & FLAGS_LONGLONG)
980 *(long long int *) p->data.ptr = done;
981 else
982 #endif
983 if (p->flags & FLAGS_LONG)
984 *(long int *) p->data.ptr = done;
985 else if (!(p->flags & FLAGS_SHORT))
986 *(int *) p->data.ptr = done;
987 else
988 *(short int *) p->data.ptr = done;
989 break;
991 default:
992 break;
994 f = *end++; /* goto end of %-code */
997 return done;
1000 static int StoreNonPrintable(int output, struct nsprintf *infop)
1002 /* If the character isn't printable then we convert it */
1003 char work[64], *w;
1004 int num = output;
1006 w = &work[sizeof(work)];
1007 *(--w) = (char)0;
1008 for(; num > 0; num /= 10) {
1009 *(--w) = lower_digits[num % 10];
1011 if (infop->length + strlen(w) + 1 < infop->max)
1013 infop->buffer[0] = '\\';
1014 infop->buffer++;
1015 infop->length++;
1016 for (; *w; w++)
1018 infop->buffer[0] = *w;
1019 infop->buffer++;
1020 infop->length++;
1022 return output;
1024 return -1;
1027 /* fputc() look-alike */
1028 static int addbyter(int output, FILE *data)
1030 struct nsprintf *infop=(struct nsprintf *)data;
1032 if(infop->length < infop->max) {
1033 /* only do this if we haven't reached max length yet */
1034 if (isprint(output) || isspace(output))
1036 infop->buffer[0] = (char)output; /* store */
1037 infop->buffer++; /* increase pointer */
1038 infop->length++; /* we are now one byte larger */
1040 else
1042 return StoreNonPrintable(output, infop);
1044 return output; /* fputc() returns like this on success */
1046 return -1;
1049 int msnprintf(char *buffer, size_t maxlength, const char *format, ...)
1051 va_list ap_save; /* argument pointer */
1052 int retcode;
1053 struct nsprintf info;
1055 info.buffer = buffer;
1056 info.length = 0;
1057 info.max = maxlength;
1059 va_start(ap_save, format);
1060 retcode = dprintf_formatf(&info, addbyter, format, ap_save);
1061 va_end(ap_save);
1062 info.buffer[0] = 0; /* we terminate this with a zero byte */
1064 /* we could even return things like */
1066 return retcode;
1069 int mvsnprintf(char *buffer, size_t maxlength, const char *format, va_list ap_save)
1071 int retcode;
1072 struct nsprintf info;
1074 info.buffer = buffer;
1075 info.length = 0;
1076 info.max = maxlength;
1078 retcode = dprintf_formatf(&info, addbyter, format, ap_save);
1079 info.buffer[0] = 0; /* we terminate this with a zero byte */
1080 return retcode;
1084 /* fputc() look-alike */
1085 static int alloc_addbyter(int output, FILE *data)
1087 struct asprintf *infop=(struct asprintf *)data;
1089 if(!infop->buffer) {
1090 infop->buffer=(char *)malloc(32);
1091 if(!infop->buffer)
1092 return -1; /* fail */
1093 infop->alloc = 32;
1094 infop->len =0;
1096 else if(infop->len+1 >= infop->alloc) {
1097 char *newptr;
1099 newptr = (char *)realloc(infop->buffer, infop->alloc*2);
1101 if(!newptr) {
1102 return -1;
1104 infop->buffer = newptr;
1105 infop->alloc *= 2;
1108 infop->buffer[ infop->len ] = output;
1110 infop->len++;
1112 return output; /* fputc() returns like this on success */
1116 char *maprintf(const char *format, ...)
1118 va_list ap_save; /* argument pointer */
1119 int retcode;
1120 struct asprintf info;
1122 info.buffer = NULL;
1123 info.len = 0;
1124 info.alloc = 0;
1126 va_start(ap_save, format);
1127 retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save);
1128 va_end(ap_save);
1129 if(info.len) {
1130 info.buffer[info.len] = 0; /* we terminate this with a zero byte */
1131 return info.buffer;
1133 else
1134 return NULL;
1137 char *mvaprintf(const char *format, va_list ap_save)
1139 int retcode;
1140 struct asprintf info;
1142 info.buffer = NULL;
1143 info.len = 0;
1144 info.alloc = 0;
1146 retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save);
1147 info.buffer[info.len] = 0; /* we terminate this with a zero byte */
1148 if(info.len) {
1149 info.buffer[info.len] = 0; /* we terminate this with a zero byte */
1150 return info.buffer;
1152 else
1153 return NULL;
1156 static int storebuffer(int output, FILE *data)
1158 char **buffer = (char **)data;
1159 **buffer = (char)output;
1160 (*buffer)++;
1161 return output; /* act like fputc() ! */
1164 int msprintf(char *buffer, const char *format, ...)
1166 va_list ap_save; /* argument pointer */
1167 int retcode;
1168 va_start(ap_save, format);
1169 retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save);
1170 va_end(ap_save);
1171 *buffer=0; /* we terminate this with a zero byte */
1172 return retcode;
1175 extern int fputc(int, FILE *);
1177 int mprintf(const char *format, ...)
1179 int retcode;
1180 va_list ap_save; /* argument pointer */
1181 va_start(ap_save, format);
1182 retcode = dprintf_formatf(stdout, fputc, format, ap_save);
1183 va_end(ap_save);
1184 return retcode;
1187 int mfprintf(FILE *whereto, const char *format, ...)
1189 int retcode;
1190 va_list ap_save; /* argument pointer */
1191 va_start(ap_save, format);
1192 retcode = dprintf_formatf(whereto, fputc, format, ap_save);
1193 va_end(ap_save);
1194 return retcode;
1197 int mvsprintf(char *buffer, const char *format, va_list ap_save)
1199 int retcode;
1200 retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save);
1201 *buffer=0; /* we terminate this with a zero byte */
1202 return retcode;
1205 int mvprintf(const char *format, va_list ap_save)
1207 return dprintf_formatf(stdout, fputc, format, ap_save);
1210 int mvfprintf(FILE *whereto, const char *format, va_list ap_save)
1212 return dprintf_formatf(whereto, fputc, format, ap_save);
1215 #ifdef DPRINTF_DEBUG
1216 int main()
1218 char buffer[129];
1219 char *ptr;
1220 #ifdef SIZEOF_LONG_LONG
1221 long long hullo;
1222 dprintf("%3$12s %1$s %2$qd %4$d\n", "daniel", hullo, "stenberg", 65);
1223 #endif
1225 mprintf("%3d %5d\n", 10, 1998);
1227 ptr=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);
1229 puts(ptr);
1231 memset(ptr, 55, strlen(ptr)+1);
1233 free(ptr);
1235 #if 1
1236 mprintf(buffer, "%s %s %d", "daniel", "stenberg", 19988);
1237 puts(buffer);
1239 mfprintf(stderr, "%s %#08x\n", "dummy", 65);
1241 printf("%s %#08x\n", "dummy", 65);
1243 double tryout = 3.14156592;
1244 mprintf(buffer, "%.2g %G %f %e %E", tryout, tryout, tryout, tryout, tryout);
1245 puts(buffer);
1246 printf("%.2g %G %f %e %E\n", tryout, tryout, tryout, tryout, tryout);
1248 #endif
1250 return 0;
1253 #endif