Use public domain snprintf() implementation by Patrick Powell to avoid
[openssl.git] / crypto / bio / b_print.c
1 /* crypto/bio/b_print.c */
2 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
3  * All rights reserved.
4  *
5  * This package is an SSL implementation written
6  * by Eric Young (eay@cryptsoft.com).
7  * The implementation was written so as to conform with Netscapes SSL.
8  * 
9  * This library is free for commercial and non-commercial use as long as
10  * the following conditions are aheared to.  The following conditions
11  * apply to all code found in this distribution, be it the RC4, RSA,
12  * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
13  * included with this distribution is covered by the same copyright terms
14  * except that the holder is Tim Hudson (tjh@cryptsoft.com).
15  * 
16  * Copyright remains Eric Young's, and as such any Copyright notices in
17  * the code are not to be removed.
18  * If this package is used in a product, Eric Young should be given attribution
19  * as the author of the parts of the library used.
20  * This can be in the form of a textual message at program startup or
21  * in documentation (online or textual) provided with the package.
22  * 
23  * Redistribution and use in source and binary forms, with or without
24  * modification, are permitted provided that the following conditions
25  * are met:
26  * 1. Redistributions of source code must retain the copyright
27  *    notice, this list of conditions and the following disclaimer.
28  * 2. Redistributions in binary form must reproduce the above copyright
29  *    notice, this list of conditions and the following disclaimer in the
30  *    documentation and/or other materials provided with the distribution.
31  * 3. All advertising materials mentioning features or use of this software
32  *    must display the following acknowledgement:
33  *    "This product includes cryptographic software written by
34  *     Eric Young (eay@cryptsoft.com)"
35  *    The word 'cryptographic' can be left out if the rouines from the library
36  *    being used are not cryptographic related :-).
37  * 4. If you include any Windows specific code (or a derivative thereof) from 
38  *    the apps directory (application code) you must include an acknowledgement:
39  *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
40  * 
41  * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
42  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
45  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
46  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
47  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
49  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
50  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51  * SUCH DAMAGE.
52  * 
53  * The licence and distribution terms for any publically available version or
54  * derivative of this code cannot be changed.  i.e. this code cannot simply be
55  * copied and put under another distribution licence
56  * [including the GNU Public Licence.]
57  */
58
59 /* 
60  * Stolen from tjh's ssl/ssl_trc.c stuff.
61  */
62
63 #include <stdio.h>
64 #include <stdarg.h>
65 #include "cryptlib.h"
66 #include <openssl/bio.h>
67
68 static void dopr (char *buffer, size_t maxlen, const char *format, 
69                   va_list args);
70
71 int BIO_printf (BIO *bio, ...)
72         {
73         va_list args;
74         char *format;
75         int ret;
76         MS_STATIC char hugebuf[1024*2]; /* 10k in one chunk is the limit */
77
78         va_start(args, bio);
79         format=va_arg(args, char *);
80
81         hugebuf[0]='\0';
82
83         dopr(hugebuf, sizeof(hugebuf), format, args);
84
85         ret=BIO_write(bio,hugebuf,strlen(hugebuf));
86
87         va_end(args);
88         return(ret);
89         }
90
91 /*
92  * Copyright Patrick Powell 1995
93  * This code is based on code written by Patrick Powell (papowell@astart.com)
94  * It may be used for any purpose as long as this notice remains intact
95  * on all source code distributions
96  */
97
98 #include <string.h>
99 #include <ctype.h>
100 #include <sys/types.h>
101 #include <stdarg.h>
102
103 static void fmtstr (char *buffer, size_t *currlen, size_t maxlen,
104                     char *value, int flags, int min, int max);
105 static void fmtint (char *buffer, size_t *currlen, size_t maxlen,
106                     long value, int base, int min, int max, int flags);
107 static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
108                    long double fvalue, int min, int max, int flags);
109 static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c );
110
111 /*
112  * dopr(): poor man's version of doprintf
113  */
114
115 /* format read states */
116 #define DP_S_DEFAULT 0
117 #define DP_S_FLAGS   1
118 #define DP_S_MIN     2
119 #define DP_S_DOT     3
120 #define DP_S_MAX     4
121 #define DP_S_MOD     5
122 #define DP_S_CONV    6
123 #define DP_S_DONE    7
124
125 /* format flags - Bits */
126 #define DP_F_MINUS      (1 << 0)
127 #define DP_F_PLUS       (1 << 1)
128 #define DP_F_SPACE      (1 << 2)
129 #define DP_F_NUM        (1 << 3)
130 #define DP_F_ZERO       (1 << 4)
131 #define DP_F_UP         (1 << 5)
132 #define DP_F_UNSIGNED   (1 << 6)
133
134 /* Conversion Flags */
135 #define DP_C_SHORT   1
136 #define DP_C_LONG    2
137 #define DP_C_LDOUBLE 3
138
139 #define char_to_int(p) (p - '0')
140 #define MAX(p,q) ((p >= q) ? p : q)
141
142 static void dopr (char *buffer, size_t maxlen, const char *format, va_list args)
143 {
144   char ch;
145   long value;
146   long double fvalue;
147   char *strvalue;
148   int min;
149   int max;
150   int state;
151   int flags;
152   int cflags;
153   size_t currlen;
154   
155   state = DP_S_DEFAULT;
156   currlen = flags = cflags = min = 0;
157   max = -1;
158   ch = *format++;
159
160   while (state != DP_S_DONE)
161   {
162     if ((ch == '\0') || (currlen >= maxlen)) 
163       state = DP_S_DONE;
164
165     switch(state) 
166     {
167     case DP_S_DEFAULT:
168       if (ch == '%') 
169         state = DP_S_FLAGS;
170       else 
171         dopr_outch (buffer, &currlen, maxlen, ch);
172       ch = *format++;
173       break;
174     case DP_S_FLAGS:
175       switch (ch) 
176       {
177       case '-':
178         flags |= DP_F_MINUS;
179         ch = *format++;
180         break;
181       case '+':
182         flags |= DP_F_PLUS;
183         ch = *format++;
184         break;
185       case ' ':
186         flags |= DP_F_SPACE;
187         ch = *format++;
188         break;
189       case '#':
190         flags |= DP_F_NUM;
191         ch = *format++;
192         break;
193       case '0':
194         flags |= DP_F_ZERO;
195         ch = *format++;
196         break;
197       default:
198         state = DP_S_MIN;
199         break;
200       }
201       break;
202     case DP_S_MIN:
203       if (isdigit((unsigned char)ch)) 
204       {
205         min = 10*min + char_to_int (ch);
206         ch = *format++;
207       } 
208       else if (ch == '*') 
209       {
210         min = va_arg (args, int);
211         ch = *format++;
212         state = DP_S_DOT;
213       } 
214       else 
215         state = DP_S_DOT;
216       break;
217     case DP_S_DOT:
218       if (ch == '.') 
219       {
220         state = DP_S_MAX;
221         ch = *format++;
222       } 
223       else 
224         state = DP_S_MOD;
225       break;
226     case DP_S_MAX:
227       if (isdigit((unsigned char)ch)) 
228       {
229         if (max < 0)
230           max = 0;
231         max = 10*max + char_to_int (ch);
232         ch = *format++;
233       } 
234       else if (ch == '*') 
235       {
236         max = va_arg (args, int);
237         ch = *format++;
238         state = DP_S_MOD;
239       } 
240       else 
241         state = DP_S_MOD;
242       break;
243     case DP_S_MOD:
244       /* Currently, we don't support Long Long, bummer */
245       switch (ch) 
246       {
247       case 'h':
248         cflags = DP_C_SHORT;
249         ch = *format++;
250         break;
251       case 'l':
252         cflags = DP_C_LONG;
253         ch = *format++;
254         break;
255       case 'L':
256         cflags = DP_C_LDOUBLE;
257         ch = *format++;
258         break;
259       default:
260         break;
261       }
262       state = DP_S_CONV;
263       break;
264     case DP_S_CONV:
265       switch (ch) 
266       {
267       case 'd':
268       case 'i':
269         if (cflags == DP_C_SHORT) 
270           value = va_arg (args, short int);
271         else if (cflags == DP_C_LONG)
272           value = va_arg (args, long int);
273         else
274           value = va_arg (args, int);
275         fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
276         break;
277       case 'o':
278         flags |= DP_F_UNSIGNED;
279         if (cflags == DP_C_SHORT)
280           value = va_arg (args, unsigned short int);
281         else if (cflags == DP_C_LONG)
282           value = va_arg (args, unsigned long int);
283         else
284           value = va_arg (args, unsigned int);
285         fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
286         break;
287       case 'u':
288         flags |= DP_F_UNSIGNED;
289         if (cflags == DP_C_SHORT)
290           value = va_arg (args, unsigned short int);
291         else if (cflags == DP_C_LONG)
292           value = va_arg (args, unsigned long int);
293         else
294           value = va_arg (args, unsigned int);
295         fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
296         break;
297       case 'X':
298         flags |= DP_F_UP;
299       case 'x':
300         flags |= DP_F_UNSIGNED;
301         if (cflags == DP_C_SHORT)
302           value = va_arg (args, unsigned short int);
303         else if (cflags == DP_C_LONG)
304           value = va_arg (args, unsigned long int);
305         else
306           value = va_arg (args, unsigned int);
307         fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
308         break;
309       case 'f':
310         if (cflags == DP_C_LDOUBLE)
311           fvalue = va_arg (args, long double);
312         else
313           fvalue = va_arg (args, double);
314         /* um, floating point? */
315         fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
316         break;
317       case 'E':
318         flags |= DP_F_UP;
319       case 'e':
320         if (cflags == DP_C_LDOUBLE)
321           fvalue = va_arg (args, long double);
322         else
323           fvalue = va_arg (args, double);
324         break;
325       case 'G':
326         flags |= DP_F_UP;
327       case 'g':
328         if (cflags == DP_C_LDOUBLE)
329           fvalue = va_arg (args, long double);
330         else
331           fvalue = va_arg (args, double);
332         break;
333       case 'c':
334         dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
335         break;
336       case 's':
337         strvalue = va_arg (args, char *);
338         if (max < 0) 
339           max = maxlen; /* ie, no max */
340         fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
341         break;
342       case 'p':
343         strvalue = va_arg (args, void *);
344         fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
345         break;
346       case 'n':
347         if (cflags == DP_C_SHORT) 
348         {
349           short int *num;
350           num = va_arg (args, short int *);
351           *num = currlen;
352         } 
353         else if (cflags == DP_C_LONG) 
354         {
355           long int *num;
356           num = va_arg (args, long int *);
357           *num = currlen;
358         } 
359         else 
360         {
361           int *num;
362           num = va_arg (args, int *);
363           *num = currlen;
364         }
365         break;
366       case '%':
367         dopr_outch (buffer, &currlen, maxlen, ch);
368         break;
369       case 'w':
370         /* not supported yet, treat as next char */
371         ch = *format++;
372         break;
373       default:
374         /* Unknown, skip */
375         break;
376       }
377       ch = *format++;
378       state = DP_S_DEFAULT;
379       flags = cflags = min = 0;
380       max = -1;
381       break;
382     case DP_S_DONE:
383       break;
384     default:
385       /* hmm? */
386       break; /* some picky compilers need this */
387     }
388   }
389   if (currlen < maxlen - 1) 
390     buffer[currlen] = '\0';
391   else 
392     buffer[maxlen - 1] = '\0';
393 }
394
395 static void fmtstr (char *buffer, size_t *currlen, size_t maxlen,
396                     char *value, int flags, int min, int max)
397 {
398   int padlen, strln;     /* amount to pad */
399   int cnt = 0;
400   
401   if (value == 0)
402   {
403     value = "<NULL>";
404   }
405
406   for (strln = 0; value[strln]; ++strln); /* strlen */
407   padlen = min - strln;
408   if (padlen < 0) 
409     padlen = 0;
410   if (flags & DP_F_MINUS) 
411     padlen = -padlen; /* Left Justify */
412
413   while ((padlen > 0) && (cnt < max)) 
414   {
415     dopr_outch (buffer, currlen, maxlen, ' ');
416     --padlen;
417     ++cnt;
418   }
419   while (*value && (cnt < max)) 
420   {
421     dopr_outch (buffer, currlen, maxlen, *value++);
422     ++cnt;
423   }
424   while ((padlen < 0) && (cnt < max)) 
425   {
426     dopr_outch (buffer, currlen, maxlen, ' ');
427     ++padlen;
428     ++cnt;
429   }
430 }
431
432 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
433
434 static void fmtint (char *buffer, size_t *currlen, size_t maxlen,
435                     long value, int base, int min, int max, int flags)
436 {
437   int signvalue = 0;
438   unsigned long uvalue;
439   char convert[20];
440   int place = 0;
441   int spadlen = 0; /* amount to space pad */
442   int zpadlen = 0; /* amount to zero pad */
443   int caps = 0;
444   
445   if (max < 0)
446     max = 0;
447
448   uvalue = value;
449
450   if(!(flags & DP_F_UNSIGNED))
451   {
452     if( value < 0 ) {
453       signvalue = '-';
454       uvalue = -value;
455     }
456     else
457       if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
458         signvalue = '+';
459     else
460       if (flags & DP_F_SPACE)
461         signvalue = ' ';
462   }
463   
464   if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
465
466   do {
467     convert[place++] =
468       (caps? "0123456789ABCDEF":"0123456789abcdef")
469       [uvalue % (unsigned)base  ];
470     uvalue = (uvalue / (unsigned)base );
471   } while(uvalue && (place < 20));
472   if (place == 20) place--;
473   convert[place] = 0;
474
475   zpadlen = max - place;
476   spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
477   if (zpadlen < 0) zpadlen = 0;
478   if (spadlen < 0) spadlen = 0;
479   if (flags & DP_F_ZERO)
480   {
481     zpadlen = MAX(zpadlen, spadlen);
482     spadlen = 0;
483   }
484   if (flags & DP_F_MINUS) 
485     spadlen = -spadlen; /* Left Justifty */
486
487 #ifdef DEBUG_SNPRINTF
488   dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
489       zpadlen, spadlen, min, max, place));
490 #endif
491
492   /* Spaces */
493   while (spadlen > 0) 
494   {
495     dopr_outch (buffer, currlen, maxlen, ' ');
496     --spadlen;
497   }
498
499   /* Sign */
500   if (signvalue) 
501     dopr_outch (buffer, currlen, maxlen, signvalue);
502
503   /* Zeros */
504   if (zpadlen > 0) 
505   {
506     while (zpadlen > 0)
507     {
508       dopr_outch (buffer, currlen, maxlen, '0');
509       --zpadlen;
510     }
511   }
512
513   /* Digits */
514   while (place > 0) 
515     dopr_outch (buffer, currlen, maxlen, convert[--place]);
516   
517   /* Left Justified spaces */
518   while (spadlen < 0) {
519     dopr_outch (buffer, currlen, maxlen, ' ');
520     ++spadlen;
521   }
522 }
523
524 static long double abs_val (long double value)
525 {
526   long double result = value;
527
528   if (value < 0)
529     result = -value;
530
531   return result;
532 }
533
534 static long double pow10 (int exp)
535 {
536   long double result = 1;
537
538   while (exp)
539   {
540     result *= 10;
541     exp--;
542   }
543   
544   return result;
545 }
546
547 static long round (long double value)
548 {
549   long intpart;
550
551   intpart = value;
552   value = value - intpart;
553   if (value >= 0.5)
554     intpart++;
555
556   return intpart;
557 }
558
559 static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
560                    long double fvalue, int min, int max, int flags)
561 {
562   int signvalue = 0;
563   long double ufvalue;
564   char iconvert[20];
565   char fconvert[20];
566   int iplace = 0;
567   int fplace = 0;
568   int padlen = 0; /* amount to pad */
569   int zpadlen = 0; 
570   int caps = 0;
571   long intpart;
572   long fracpart;
573   
574   /* 
575    * AIX manpage says the default is 0, but Solaris says the default
576    * is 6, and sprintf on AIX defaults to 6
577    */
578   if (max < 0)
579     max = 6;
580
581   ufvalue = abs_val (fvalue);
582
583   if (fvalue < 0)
584     signvalue = '-';
585   else
586     if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
587       signvalue = '+';
588     else
589       if (flags & DP_F_SPACE)
590         signvalue = ' ';
591
592 #if 0
593   if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
594 #endif
595
596   intpart = ufvalue;
597
598   /* 
599    * Sorry, we only support 9 digits past the decimal because of our 
600    * conversion method
601    */
602   if (max > 9)
603     max = 9;
604
605   /* We "cheat" by converting the fractional part to integer by
606    * multiplying by a factor of 10
607    */
608   fracpart = round ((pow10 (max)) * (ufvalue - intpart));
609
610   if (fracpart >= pow10 (max))
611   {
612     intpart++;
613     fracpart -= pow10 (max);
614   }
615
616 #ifdef DEBUG_SNPRINTF
617   dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart));
618 #endif
619
620   /* Convert integer part */
621   do {
622     iconvert[iplace++] =
623       (caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10];
624     intpart = (intpart / 10);
625   } while(intpart && (iplace < 20));
626   if (iplace == 20) iplace--;
627   iconvert[iplace] = 0;
628
629   /* Convert fractional part */
630   do {
631     fconvert[fplace++] =
632       (caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10];
633     fracpart = (fracpart / 10);
634   } while(fracpart && (fplace < 20));
635   if (fplace == 20) fplace--;
636   fconvert[fplace] = 0;
637
638   /* -1 for decimal point, another -1 if we are printing a sign */
639   padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); 
640   zpadlen = max - fplace;
641   if (zpadlen < 0)
642     zpadlen = 0;
643   if (padlen < 0) 
644     padlen = 0;
645   if (flags & DP_F_MINUS) 
646     padlen = -padlen; /* Left Justifty */
647
648   if ((flags & DP_F_ZERO) && (padlen > 0)) 
649   {
650     if (signvalue) 
651     {
652       dopr_outch (buffer, currlen, maxlen, signvalue);
653       --padlen;
654       signvalue = 0;
655     }
656     while (padlen > 0)
657     {
658       dopr_outch (buffer, currlen, maxlen, '0');
659       --padlen;
660     }
661   }
662   while (padlen > 0)
663   {
664     dopr_outch (buffer, currlen, maxlen, ' ');
665     --padlen;
666   }
667   if (signvalue) 
668     dopr_outch (buffer, currlen, maxlen, signvalue);
669
670   while (iplace > 0) 
671     dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
672
673   /*
674    * Decimal point.  This should probably use locale to find the correct
675    * char to print out.
676    */
677   dopr_outch (buffer, currlen, maxlen, '.');
678
679   while (fplace > 0) 
680     dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
681
682   while (zpadlen > 0)
683   {
684     dopr_outch (buffer, currlen, maxlen, '0');
685     --zpadlen;
686   }
687
688   while (padlen < 0) 
689   {
690     dopr_outch (buffer, currlen, maxlen, ' ');
691     ++padlen;
692   }
693 }
694
695 static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c)
696 {
697   if (*currlen < maxlen)
698     buffer[(*currlen)++] = c;
699 }