Cludge fix for djgpp 2.03 or older; it doesn't have snprintf() etc.
[platform/upstream/curl.git] / lib / mprintf.c
1 /****************************************************************************
2  *
3  * $Id$
4  *
5  *************************************************************************
6  *
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.
10  *
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.
15  *
16  * Purpose:
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).
22  *
23  * Current restrictions:
24  * - Max 128 parameters
25  * - No 'long double' support.
26  *
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/
30  */
31
32
33 #include "setup.h"
34 #include <sys/types.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <stdarg.h>
38 #include <ctype.h>
39 #include <string.h>
40
41 #if defined(DJGPP) && (DJGPP_MINOR < 4)
42 #undef CURLDEBUG  /* don't use x_was_used() here */
43 #endif
44
45 #include <curl/mprintf.h>
46
47 #ifndef SIZEOF_LONG_DOUBLE
48 #define SIZEOF_LONG_DOUBLE 0
49 #endif
50
51 #ifndef SIZEOF_SIZE_T
52 /* default to 4 bytes for size_t unless defined in the config.h */
53 #define SIZEOF_SIZE_T 4
54 #endif
55
56 #ifdef DPRINTF_DEBUG
57 #define HAVE_LONGLONG
58 #define LONG_LONG long long
59 #define ENABLE_64BIT
60 #endif
61
62 #include "memory.h"
63 /* The last #include file should be: */
64 #include "memdebug.h"
65
66 #define BUFFSIZE 256 /* buffer for long-to-str and float-to-str calcs */
67 #define MAX_PARAMETERS 128 /* lame static limit */
68
69 #undef TRUE
70 #undef FALSE
71 #undef BOOL
72 #ifdef __cplusplus
73 # define TRUE true
74 # define FALSE false
75 # define BOOL bool
76 #else
77 # define TRUE  ((char)(1 == 1))
78 # define FALSE ((char)(0 == 1))
79 # define BOOL char
80 #endif
81
82 #ifdef _AMIGASF
83 # undef FORMAT_INT
84 #endif
85
86 /* Lower-case digits.  */
87 static const char lower_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
88
89 /* Upper-case digits.  */
90 static const char upper_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
91
92 #define OUTCHAR(x) \
93   do{ \
94     if(stream((unsigned char)(x), (FILE *)data) != -1) \
95       done++; \
96     else \
97      return done; /* return immediately on failure */ \
98   } while(0)
99
100 /* Data type to read from the arglist */
101 typedef enum  {
102   FORMAT_UNKNOWN = 0,
103   FORMAT_STRING,
104   FORMAT_PTR,
105   FORMAT_INT,
106   FORMAT_INTPTR,
107   FORMAT_LONG,
108   FORMAT_LONGLONG,
109   FORMAT_DOUBLE,
110   FORMAT_LONGDOUBLE,
111   FORMAT_WIDTH /* For internal use */
112 } FormatType;
113
114 /* convertion and display flags */
115 enum {
116   FLAGS_NEW        = 0,
117   FLAGS_SPACE      = 1<<0,
118   FLAGS_SHOWSIGN   = 1<<1,
119   FLAGS_LEFT       = 1<<2,
120   FLAGS_ALT        = 1<<3,
121   FLAGS_SHORT      = 1<<4,
122   FLAGS_LONG       = 1<<5,
123   FLAGS_LONGLONG   = 1<<6,
124   FLAGS_LONGDOUBLE = 1<<7,
125   FLAGS_PAD_NIL    = 1<<8,
126   FLAGS_UNSIGNED   = 1<<9,
127   FLAGS_OCTAL      = 1<<10,
128   FLAGS_HEX        = 1<<11,
129   FLAGS_UPPER      = 1<<12,
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 */
137 };
138
139 typedef struct {
140   FormatType type;
141   int flags;
142   long width;     /* width OR width parameter number */
143   long precision; /* precision OR precision parameter number */
144   union {
145     char *str;
146     void *ptr;
147     long num;
148 #ifdef ENABLE_64BIT
149     LONG_LONG lnum;
150 #endif
151     double dnum;
152   } data;
153 } va_stack_t;
154
155 struct nsprintf {
156   char *buffer;
157   size_t length;
158   size_t max;
159 };
160
161 struct asprintf {
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
166                    the complete data */
167 };
168
169 int curl_msprintf(char *buffer, const char *format, ...);
170
171 static long dprintf_DollarString(char *input, char **end)
172 {
173   int number=0;
174   while(isdigit((int)*input)) {
175     number *= 10;
176     number += *input-'0';
177     input++;
178   }
179   if(number && ('$'==*input++)) {
180     *end = input;
181     return number;
182   }
183   return 0;
184 }
185
186 static BOOL dprintf_IsQualifierNoDollar(char c)
187 {
188   switch (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':
193   case '*': case 'O':
194     return TRUE;
195   default:
196     return FALSE;
197   }
198 }
199
200 #ifdef DPRINTF_DEBUG2
201 int dprintf_Pass1Report(va_stack_t *vto, int max)
202 {
203   int i;
204   char buffer[128];
205   int bit;
206   int flags;
207
208   for(i=0; i<max; i++) {
209     char *type;
210     switch(vto[i].type) {
211     case FORMAT_UNKNOWN:
212       type = "unknown";
213       break;
214     case FORMAT_STRING:
215       type ="string";
216       break;
217     case FORMAT_PTR:
218       type ="pointer";
219       break;
220     case FORMAT_INT:
221       type = "int";
222       break;
223     case FORMAT_LONG:
224       type = "long";
225       break;
226     case FORMAT_LONGLONG:
227       type = "long long";
228       break;
229     case FORMAT_DOUBLE:
230       type = "double";
231       break;
232     case FORMAT_LONGDOUBLE:
233       type = "long double";
234       break;
235     }
236
237
238     buffer[0]=0;
239
240     for(bit=0; bit<31; bit++) {
241       flags = vto[i].flags & (1<<bit);
242
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 ");
283     }
284     printf("REPORT: %d. %s [%s]\n", i, type, buffer);
285
286   }
287
288
289 }
290 #endif
291
292 /******************************************************************
293  *
294  * Pass 1:
295  * Create an index with the type of each parameter entry and its
296  * value (may vary in size)
297  *
298  ******************************************************************/
299
300 static long dprintf_Pass1(char *format, va_stack_t *vto, char **endpos,
301                           va_list arglist)
302 {
303   char *fmt = format;
304   int param_num = 0;
305   long this_param;
306   long width;
307   long precision;
308   int flags;
309   long max_param=0;
310   long i;
311
312   while (*fmt) {
313     if (*fmt++ == '%') {
314       if (*fmt == '%') {
315         fmt++;
316         continue; /* while */
317       }
318
319       flags = FLAGS_NEW;
320
321       /* Handle the positional case (N$) */
322
323       param_num++;
324
325       this_param = dprintf_DollarString(fmt, &fmt);
326       if (0 == this_param)
327         /* we got no positional, get the next counter */
328         this_param = param_num;
329
330       if (this_param > max_param)
331         max_param = this_param;
332
333       /*
334        * The parameter with number 'i' should be used. Next, we need
335        * to get SIZE and TYPE of the parameter. Add the information
336        * to our array.
337        */
338
339       width = 0;
340       precision = 0;
341
342       /* Handle the flags */
343
344       while (dprintf_IsQualifierNoDollar(*fmt)) {
345         switch (*fmt++) {
346         case ' ':
347           flags |= FLAGS_SPACE;
348           break;
349         case '+':
350           flags |= FLAGS_SHOWSIGN;
351           break;
352         case '-':
353           flags |= FLAGS_LEFT;
354           flags &= ~FLAGS_PAD_NIL;
355           break;
356         case '#':
357           flags |= FLAGS_ALT;
358           break;
359         case '.':
360           flags |= FLAGS_PREC;
361           if ('*' == *fmt) {
362             /* The precision is picked from a specified parameter */
363
364             flags |= FLAGS_PRECPARAM;
365             fmt++;
366             param_num++;
367
368             i = dprintf_DollarString(fmt, &fmt);
369             if (i)
370               precision = i;
371             else
372               precision = param_num;
373
374             if (precision > max_param)
375               max_param = precision;
376           }
377           else {
378             flags |= FLAGS_PREC;
379             precision = strtol(fmt, &fmt, 10);
380           }
381           break;
382         case 'h':
383           flags |= FLAGS_SHORT;
384           break;
385         case 'l':
386           if (flags & FLAGS_LONG)
387             flags |= FLAGS_LONGLONG;
388           else
389             flags |= FLAGS_LONG;
390           break;
391         case 'L':
392           flags |= FLAGS_LONGDOUBLE;
393           break;
394         case 'q':
395           flags |= FLAGS_LONGLONG;
396           break;
397         case 'z':
398           /* the code below generates a warning if -Wunreachable-code is
399              used */
400 #if SIZEOF_SIZE_T>4
401           flags |= FLAGS_LONGLONG;
402 #else
403           flags |= FLAGS_LONG;
404 #endif
405           break;
406         case 'O':
407 #if SIZEOF_CURL_OFF_T > 4
408           flags |= FLAGS_LONGLONG;
409 #else
410           flags |= FLAGS_LONG;
411 #endif
412           break;
413         case '0':
414           if (!(flags & FLAGS_LEFT))
415             flags |= FLAGS_PAD_NIL;
416           /* FALLTHROUGH */
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);
421           break;
422         case '*':  /* Special case */
423           flags |= FLAGS_WIDTHPARAM;
424           param_num++;
425
426           i = dprintf_DollarString(fmt, &fmt);
427           if(i)
428             width = i;
429           else
430             width = param_num;
431           if(width > max_param)
432             max_param=width;
433           break;
434         default:
435           break;
436         }
437       } /* switch */
438
439       /* Handle the specifier */
440
441       i = this_param - 1;
442
443       switch (*fmt) {
444       case 'S':
445         flags |= FLAGS_ALT;
446         /* FALLTHROUGH */
447       case 's':
448         vto[i].type = FORMAT_STRING;
449         break;
450       case 'n':
451         vto[i].type = FORMAT_INTPTR;
452         break;
453       case 'p':
454         vto[i].type = FORMAT_PTR;
455         break;
456       case 'd': case 'i':
457         vto[i].type = FORMAT_INT;
458         break;
459       case 'u':
460         vto[i].type = FORMAT_INT;
461         flags |= FLAGS_UNSIGNED;
462         break;
463       case 'o':
464         vto[i].type = FORMAT_INT;
465         flags |= FLAGS_OCTAL;
466         break;
467       case 'x':
468         vto[i].type = FORMAT_INT;
469         flags |= FLAGS_HEX;
470         break;
471       case 'X':
472         vto[i].type = FORMAT_INT;
473         flags |= FLAGS_HEX|FLAGS_UPPER;
474         break;
475       case 'c':
476         vto[i].type = FORMAT_INT;
477         flags |= FLAGS_CHAR;
478         break;
479       case 'f':
480         vto[i].type = FORMAT_DOUBLE;
481         break;
482       case 'e':
483         vto[i].type = FORMAT_DOUBLE;
484         flags |= FLAGS_FLOATE;
485         break;
486       case 'E':
487         vto[i].type = FORMAT_DOUBLE;
488         flags |= FLAGS_FLOATE|FLAGS_UPPER;
489         break;
490       case 'g':
491         vto[i].type = FORMAT_DOUBLE;
492         flags |= FLAGS_FLOATG;
493         break;
494       case 'G':
495         vto[i].type = FORMAT_DOUBLE;
496         flags |= FLAGS_FLOATG|FLAGS_UPPER;
497         break;
498       default:
499         vto[i].type = FORMAT_UNKNOWN;
500         break;
501       } /* switch */
502
503       vto[i].flags = flags;
504       vto[i].width = width;
505       vto[i].precision = precision;
506
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;
511         i = 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
515                                                 of width! */
516       }
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;
521         i = 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
525                                                 of width! */
526       }
527       *endpos++ = fmt + 1; /* end of this sequence */
528     }
529   }
530
531 #ifdef DPRINTF_DEBUG2
532   dprintf_Pass1Report(vto, max_param);
533 #endif
534
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))
538       {
539         /* Width/precision arguments must be read before the main argument
540          * they are attached to
541          */
542         vto[i + 1].data.num = va_arg(arglist, int);
543       }
544
545     switch (vto[i].type)
546       {
547       case FORMAT_STRING:
548         vto[i].data.str = va_arg(arglist, char *);
549         break;
550
551       case FORMAT_INTPTR:
552       case FORMAT_UNKNOWN:
553       case FORMAT_PTR:
554         vto[i].data.ptr = va_arg(arglist, void *);
555         break;
556
557       case FORMAT_INT:
558 #ifdef ENABLE_64BIT
559         if(vto[i].flags & FLAGS_LONGLONG)
560           vto[i].data.lnum = va_arg(arglist, LONG_LONG);
561         else
562 #endif
563           if(vto[i].flags & FLAGS_LONG)
564             vto[i].data.num = va_arg(arglist, long);
565         else
566           vto[i].data.num = va_arg(arglist, int);
567         break;
568
569       case FORMAT_DOUBLE:
570         vto[i].data.dnum = va_arg(arglist, double);
571         break;
572
573       case FORMAT_WIDTH:
574         /* Argument has been read. Silently convert it into an integer
575          * for later use
576          */
577         vto[i].type = FORMAT_INT;
578         break;
579
580       default:
581         break;
582       }
583   }
584
585   return max_param;
586
587 }
588
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 */
596 {
597   /* Base-36 digits for numbers.  */
598   const char *digits = lower_digits;
599
600   /* Pointer into the format string.  */
601   char *f;
602
603   /* Number of characters written.  */
604   int done = 0;
605
606   long param; /* current parameter to read */
607   long param_num=0; /* parameter counter */
608
609   va_stack_t vto[MAX_PARAMETERS];
610   char *endpos[MAX_PARAMETERS];
611   char **end;
612
613   char work[BUFFSIZE];
614
615   va_stack_t *p;
616
617   /* Do the actual %-code parsing */
618   dprintf_Pass1((char *)format, vto, endpos, ap_save);
619
620   end = &endpos[0]; /* the initial end-position from the list dprintf_Pass1()
621                        created for us */
622
623   f = (char *)format;
624   while (*f != '\0') {
625     /* Format spec modifiers.  */
626     char alt;
627
628     /* Width of a field.  */
629     long width;
630
631     /* Precision of a field.  */
632     long prec;
633
634     /* Decimal integer is negative.  */
635     char is_neg;
636
637     /* Base of a number to be written.  */
638     long base;
639
640     /* Integral values to be written.  */
641 #ifdef ENABLE_64BIT
642     unsigned LONG_LONG num;
643 #else
644     unsigned long num;
645 #endif
646     long signed_num;
647
648     if (*f != '%') {
649       /* This isn't a format spec, so write everything out until the next one
650          OR end of string is reached.  */
651       do {
652         OUTCHAR(*f);
653       } while(*++f && ('%' != *f));
654       continue;
655     }
656
657     ++f;
658
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.  */
663     if (*f == '%') {
664       ++f;
665       OUTCHAR('%');
666       continue;
667     }
668
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);
672
673     if(!param)
674       param = param_num;
675     else
676       --param;
677
678     param_num++; /* increase this always to allow "%2$s %1$s %s" and then the
679                     third %s will pick the 3rd argument */
680
681     p = &vto[param];
682
683     /* pick up the specified width */
684     if(p->flags & FLAGS_WIDTHPARAM)
685       width = vto[p->width].data.num;
686     else
687       width = p->width;
688
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)
693       prec = p->precision;
694     else
695       prec = -1;
696
697     alt = (p->flags & FLAGS_ALT)?TRUE:FALSE;
698
699     switch (p->type) {
700     case FORMAT_INT:
701       num = p->data.num;
702       if(p->flags & FLAGS_CHAR) {
703         /* Character.  */
704         if (!(p->flags & FLAGS_LEFT))
705           while (--width > 0)
706             OUTCHAR(' ');
707         OUTCHAR((char) num);
708         if (p->flags & FLAGS_LEFT)
709           while (--width > 0)
710             OUTCHAR(' ');
711         break;
712       }
713       if(p->flags & FLAGS_UNSIGNED) {
714         /* Decimal unsigned integer.  */
715         base = 10;
716         goto unsigned_number;
717       }
718       if(p->flags & FLAGS_OCTAL) {
719         /* Octal unsigned integer.  */
720         base = 8;
721         goto unsigned_number;
722       }
723       if(p->flags & FLAGS_HEX) {
724         /* Hexadecimal unsigned integer.  */
725
726         digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits;
727         base = 16;
728         goto unsigned_number;
729       }
730
731       /* Decimal integer.  */
732       base = 10;
733
734 #ifdef ENABLE_64BIT
735       if(p->flags & FLAGS_LONGLONG) {
736         /* long long */
737         is_neg = p->data.lnum < 0;
738         num = is_neg ? (- p->data.lnum) : p->data.lnum;
739       }
740       else
741 #endif
742       {
743         signed_num = (long) num;
744         is_neg = signed_num < 0;
745         num = is_neg ? (- signed_num) : signed_num;
746       }
747       goto number;
748
749       unsigned_number:
750       /* Unsigned number of base BASE.  */
751       is_neg = 0;
752
753       number:
754       /* Number of base BASE.  */
755       {
756         char *workend = &work[sizeof(work) - 1];
757         char *w;
758
759         /* Supply a default precision if none was given.  */
760         if (prec == -1)
761           prec = 1;
762
763         /* Put the number in WORK.  */
764         w = workend;
765         while (num > 0) {
766           *w-- = digits[num % base];
767           num /= base;
768         }
769         width -= (long)(workend - w);
770         prec -= (long)(workend - w);
771
772         if (alt && base == 8 && prec <= 0) {
773           *w-- = '0';
774           --width;
775         }
776
777         if (prec > 0) {
778           width -= prec;
779           while (prec-- > 0)
780             *w-- = '0';
781         }
782
783         if (alt && base == 16)
784           width -= 2;
785
786         if (is_neg || (p->flags & FLAGS_SHOWSIGN) || (p->flags & FLAGS_SPACE))
787           --width;
788
789         if (!(p->flags & FLAGS_LEFT) && !(p->flags & FLAGS_PAD_NIL))
790           while (width-- > 0)
791             OUTCHAR(' ');
792
793         if (is_neg)
794           OUTCHAR('-');
795         else if (p->flags & FLAGS_SHOWSIGN)
796           OUTCHAR('+');
797         else if (p->flags & FLAGS_SPACE)
798           OUTCHAR(' ');
799
800         if (alt && base == 16) {
801           OUTCHAR('0');
802           if(p->flags & FLAGS_UPPER)
803             OUTCHAR('X');
804           else
805             OUTCHAR('x');
806         }
807
808         if (!(p->flags & FLAGS_LEFT) && (p->flags & FLAGS_PAD_NIL))
809           while (width-- > 0)
810             OUTCHAR('0');
811
812         /* Write the number.  */
813         while (++w <= workend) {
814           OUTCHAR(*w);
815         }
816
817         if (p->flags & FLAGS_LEFT)
818           while (width-- > 0)
819             OUTCHAR(' ');
820       }
821       break;
822
823     case FORMAT_STRING:
824             /* String.  */
825       {
826         static const char null[] = "(nil)";
827         const char *str;
828         size_t len;
829
830         str = (char *) p->data.str;
831         if ( str == NULL) {
832           /* Write null[] if there's space.  */
833           if (prec == -1 || prec >= (long) sizeof(null) - 1) {
834             str = null;
835             len = sizeof(null) - 1;
836             /* Disable quotes around (nil) */
837             p->flags &= (~FLAGS_ALT);
838           }
839           else {
840             str = "";
841             len = 0;
842           }
843         }
844         else
845           len = strlen(str);
846
847         if (prec != -1 && (size_t) prec < len)
848           len = prec;
849         width -= (long)len;
850
851         if (p->flags & FLAGS_ALT)
852           OUTCHAR('"');
853
854         if (!(p->flags&FLAGS_LEFT))
855           while (width-- > 0)
856             OUTCHAR(' ');
857
858         while (len-- > 0)
859           OUTCHAR(*str++);
860         if (p->flags&FLAGS_LEFT)
861           while (width-- > 0)
862             OUTCHAR(' ');
863
864         if (p->flags & FLAGS_ALT)
865           OUTCHAR('"');
866       }
867       break;
868
869     case FORMAT_PTR:
870       /* Generic pointer.  */
871       {
872         void *ptr;
873         ptr = (void *) p->data.ptr;
874         if (ptr != NULL) {
875           /* If the pointer is not NULL, write it as a %#x spec.  */
876           base = 16;
877           digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits;
878           alt = 1;
879           num = (size_t) ptr;
880           is_neg = 0;
881           goto number;
882         }
883         else {
884           /* Write "(nil)" for a nil pointer.  */
885           static const char strnil[] = "(nil)";
886           const char *point;
887
888           width -= sizeof(strnil) - 1;
889           if (p->flags & FLAGS_LEFT)
890             while (width-- > 0)
891               OUTCHAR(' ');
892           for (point = strnil; *point != '\0'; ++point)
893             OUTCHAR(*point);
894           if (! (p->flags & FLAGS_LEFT))
895             while (width-- > 0)
896               OUTCHAR(' ');
897         }
898       }
899       break;
900
901     case FORMAT_DOUBLE:
902       {
903         char formatbuf[32]="%";
904         char *fptr;
905         size_t left = sizeof(formatbuf)-strlen(formatbuf);
906         int len;
907
908         width = -1;
909         if (p->flags & FLAGS_WIDTH)
910           width = p->width;
911         else if (p->flags & FLAGS_WIDTHPARAM)
912           width = vto[p->width].data.num;
913
914         prec = -1;
915         if (p->flags & FLAGS_PREC)
916           prec = p->precision;
917         else if (p->flags & FLAGS_PRECPARAM)
918           prec = vto[p->precision].data.num;
919
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, "#");
928
929         fptr=&formatbuf[strlen(formatbuf)];
930
931         if(width >= 0) {
932           /* RECURSIVE USAGE */
933           len = curl_msnprintf(fptr, left, "%ld", width);
934           fptr += len;
935           left -= len;
936         }
937         if(prec >= 0) {
938           /* RECURSIVE USAGE */
939           len = curl_msnprintf(fptr, left, ".%ld", prec);
940           fptr += len;
941           left -= len;
942         }
943         if (p->flags & FLAGS_LONG)
944           *fptr++ = 'l';
945
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';
950         else
951           *fptr++ = 'f';
952
953         *fptr = 0; /* and a final zero termination */
954
955         /* NOTE NOTE NOTE!! Not all sprintf() implementations returns number
956            of output characters */
957         (sprintf)(work, formatbuf, p->data.dnum);
958
959         for(fptr=work; *fptr; fptr++)
960           OUTCHAR(*fptr);
961       }
962       break;
963
964     case FORMAT_INTPTR:
965       /* Answer the count of characters written.  */
966 #ifdef ENABLE_64BIT
967       if (p->flags & FLAGS_LONGLONG)
968         *(LONG_LONG *) p->data.ptr = (LONG_LONG)done;
969       else
970 #endif
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;
975       else
976         *(short *) p->data.ptr = (short)done;
977       break;
978
979     default:
980       break;
981     }
982     f = *end++; /* goto end of %-code */
983
984   }
985   return done;
986 }
987
988 /* fputc() look-alike */
989 static int addbyter(int output, FILE *data)
990 {
991   struct nsprintf *infop=(struct nsprintf *)data;
992   unsigned char outc = (unsigned char)output;
993
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 */
1000   }
1001   return -1;
1002 }
1003
1004 int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format,
1005                     va_list ap_save)
1006 {
1007   int retcode;
1008   struct nsprintf info;
1009
1010   info.buffer = buffer;
1011   info.length = 0;
1012   info.max = maxlength;
1013
1014   retcode = dprintf_formatf(&info, addbyter, format, ap_save);
1015   if(info.max) {
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;
1020     else
1021       info.buffer[0] = 0;
1022   }
1023   return retcode;
1024 }
1025
1026 int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...)
1027 {
1028   int retcode;
1029   va_list ap_save; /* argument pointer */
1030   va_start(ap_save, format);
1031   retcode = curl_mvsnprintf(buffer, maxlength, format, ap_save);
1032   va_end(ap_save);
1033   return retcode;
1034 }
1035
1036 /* fputc() look-alike */
1037 static int alloc_addbyter(int output, FILE *data)
1038 {
1039   struct asprintf *infop=(struct asprintf *)data;
1040   unsigned char outc = (unsigned char)output;
1041
1042   if(!infop->buffer) {
1043     infop->buffer=(char *)malloc(32);
1044     if(!infop->buffer) {
1045       infop->fail = TRUE;
1046       return -1; /* fail */
1047     }
1048     infop->alloc = 32;
1049     infop->len =0;
1050   }
1051   else if(infop->len+1 >= infop->alloc) {
1052     char *newptr;
1053
1054     newptr = (char *)realloc(infop->buffer, infop->alloc*2);
1055
1056     if(!newptr) {
1057       infop->fail = TRUE;
1058       return -1;
1059     }
1060     infop->buffer = newptr;
1061     infop->alloc *= 2;
1062   }
1063
1064   infop->buffer[ infop->len ] = outc;
1065
1066   infop->len++;
1067
1068   return outc; /* fputc() returns like this on success */
1069 }
1070
1071 char *curl_maprintf(const char *format, ...)
1072 {
1073   va_list ap_save; /* argument pointer */
1074   int retcode;
1075   struct asprintf info;
1076
1077   info.buffer = NULL;
1078   info.len = 0;
1079   info.alloc = 0;
1080   info.fail = FALSE;
1081
1082   va_start(ap_save, format);
1083   retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save);
1084   va_end(ap_save);
1085   if((-1 == retcode) || info.fail) {
1086     if(info.alloc)
1087       free(info.buffer);
1088     return NULL;
1089   }
1090   if(info.alloc) {
1091     info.buffer[info.len] = 0; /* we terminate this with a zero byte */
1092     return info.buffer;
1093   }
1094   else
1095     return strdup("");
1096 }
1097
1098 char *curl_mvaprintf(const char *format, va_list ap_save)
1099 {
1100   int retcode;
1101   struct asprintf info;
1102
1103   info.buffer = NULL;
1104   info.len = 0;
1105   info.alloc = 0;
1106   info.fail = FALSE;
1107
1108   retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save);
1109   if((-1 == retcode) || info.fail) {
1110     if(info.alloc)
1111       free(info.buffer);
1112     return NULL;
1113   }
1114
1115   if(info.alloc) {
1116     info.buffer[info.len] = 0; /* we terminate this with a zero byte */
1117     return info.buffer;
1118   }
1119   else
1120     return strdup("");
1121 }
1122
1123 static int storebuffer(int output, FILE *data)
1124 {
1125   char **buffer = (char **)data;
1126   unsigned char outc = (unsigned char)output;
1127   **buffer = outc;
1128   (*buffer)++;
1129   return outc; /* act like fputc() ! */
1130 }
1131
1132 int curl_msprintf(char *buffer, const char *format, ...)
1133 {
1134   va_list ap_save; /* argument pointer */
1135   int retcode;
1136   va_start(ap_save, format);
1137   retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save);
1138   va_end(ap_save);
1139   *buffer=0; /* we terminate this with a zero byte */
1140   return retcode;
1141 }
1142
1143 int curl_mprintf(const char *format, ...)
1144 {
1145   int retcode;
1146   va_list ap_save; /* argument pointer */
1147   va_start(ap_save, format);
1148
1149   retcode = dprintf_formatf(stdout, fputc, format, ap_save);
1150   va_end(ap_save);
1151   return retcode;
1152 }
1153
1154 int curl_mfprintf(FILE *whereto, const char *format, ...)
1155 {
1156   int retcode;
1157   va_list ap_save; /* argument pointer */
1158   va_start(ap_save, format);
1159   retcode = dprintf_formatf(whereto, fputc, format, ap_save);
1160   va_end(ap_save);
1161   return retcode;
1162 }
1163
1164 int curl_mvsprintf(char *buffer, const char *format, va_list ap_save)
1165 {
1166   int retcode;
1167   retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save);
1168   *buffer=0; /* we terminate this with a zero byte */
1169   return retcode;
1170 }
1171
1172 int curl_mvprintf(const char *format, va_list ap_save)
1173 {
1174   return dprintf_formatf(stdout, fputc, format, ap_save);
1175 }
1176
1177 int curl_mvfprintf(FILE *whereto, const char *format, va_list ap_save)
1178 {
1179   return dprintf_formatf(whereto, fputc, format, ap_save);
1180 }
1181
1182 #ifdef DPRINTF_DEBUG
1183 int main()
1184 {
1185   char buffer[129];
1186   char *ptr;
1187 #ifdef ENABLE_64BIT
1188   long long one=99;
1189   long long two=100;
1190   long long test = 0x1000000000LL;
1191   curl_mprintf("%lld %lld %lld\n", one, two, test);
1192 #endif
1193
1194   curl_mprintf("%3d %5d\n", 10, 1998);
1195
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);
1197
1198   puts(ptr);
1199
1200   memset(ptr, 55, strlen(ptr)+1);
1201
1202   free(ptr);
1203
1204 #if 1
1205   curl_mprintf(buffer, "%s %s %d", "daniel", "stenberg", 19988);
1206   puts(buffer);
1207
1208   curl_mfprintf(stderr, "%s %#08x\n", "dummy", 65);
1209
1210   printf("%s %#08x\n", "dummy", 65);
1211   {
1212     double tryout = 3.14156592;
1213     curl_mprintf(buffer, "%.2g %G %f %e %E", tryout, tryout, tryout, tryout, tryout);
1214     puts(buffer);
1215     printf("%.2g %G %f %e %E\n", tryout, tryout, tryout, tryout, tryout);
1216   }
1217 #endif
1218
1219   return 0;
1220 }
1221
1222 #endif