different snprintf version.
[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 <string.h>
66 #include <ctype.h>
67 #include <sys/types.h>
68 #include "cryptlib.h"
69 #include <openssl/bio.h>
70
71 #ifdef BN_LLONG
72 # ifndef HAVE_LONG_LONG
73 #  define HAVE_LONG_LONG
74 # endif
75 #endif
76
77 static void dopr (char *buffer, size_t maxlen, size_t *retlen,
78         const char *format, va_list args);
79
80 int BIO_printf (BIO *bio, ...)
81         {
82         va_list args;
83         char *format;
84         int ret;
85         MS_STATIC char hugebuf[1024*2]; /* 10k in one chunk is the limit */
86
87         va_start(args, bio);
88         format=va_arg(args, char *);
89
90         hugebuf[0]='\0';
91         dopr(hugebuf, sizeof(hugebuf), &ret, format, args);
92         ret=BIO_write(bio, hugebuf, ret);
93
94         va_end(args);
95         return(ret);
96         }
97
98 /*
99  * Copyright Patrick Powell 1995
100  * This code is based on code written by Patrick Powell <papowell@astart.com>
101  * It may be used for any purpose as long as this notice remains intact
102  * on all source code distributions.
103  */
104
105 /*
106  * This code contains numerious changes and enhancements which were
107  * made by lots of contributors over the last years to Patrick Powell's
108  * original code:
109  *
110  * o Patrick Powell <papowell@astart.com>      (1995)
111  * o Brandon Long <blong@fiction.net>          (1996, for Mutt)
112  * o Thomas Roessler <roessler@guug.de>        (1998, for Mutt)
113  * o Michael Elkins <me@cs.hmc.edu>            (1998, for Mutt)
114  * o Andrew Tridgell <tridge@samba.org>        (1998, for Samba)
115  * o Luke Mewburn <lukem@netbsd.org>           (1999, for LukemFTP)
116  * o Ralf S. Engelschall <rse@engelschall.com> (1999, for Pth)
117  */
118
119 #if HAVE_LONG_DOUBLE
120 #define LDOUBLE long double
121 #else
122 #define LDOUBLE double
123 #endif
124
125 #if HAVE_LONG_LONG
126 #define LLONG long long
127 #else
128 #define LLONG long
129 #endif
130
131 static void fmtstr     (char *, size_t *, size_t, char *, int, int, int);
132 static void fmtint     (char *, size_t *, size_t, LLONG, int, int, int, int);
133 static void fmtfp      (char *, size_t *, size_t, LDOUBLE, int, int, int);
134 static void dopr_outch (char *, size_t *, size_t, int);
135
136 /* format read states */
137 #define DP_S_DEFAULT    0
138 #define DP_S_FLAGS      1
139 #define DP_S_MIN        2
140 #define DP_S_DOT        3
141 #define DP_S_MAX        4
142 #define DP_S_MOD        5
143 #define DP_S_CONV       6
144 #define DP_S_DONE       7
145
146 /* format flags - Bits */
147 #define DP_F_MINUS      (1 << 0)
148 #define DP_F_PLUS       (1 << 1)
149 #define DP_F_SPACE      (1 << 2)
150 #define DP_F_NUM        (1 << 3)
151 #define DP_F_ZERO       (1 << 4)
152 #define DP_F_UP         (1 << 5)
153 #define DP_F_UNSIGNED   (1 << 6)
154
155 /* conversion flags */
156 #define DP_C_SHORT      1
157 #define DP_C_LONG       2
158 #define DP_C_LDOUBLE    3
159 #define DP_C_LLONG      4
160
161 /* some handy macros */
162 #define char_to_int(p) (p - '0')
163 #define MAX(p,q) ((p >= q) ? p : q)
164
165 static void
166 dopr(
167     char *buffer,
168     size_t maxlen,
169     size_t *retlen,
170     const char *format,
171     va_list args)
172 {
173     char ch;
174     LLONG value;
175     LDOUBLE fvalue;
176     char *strvalue;
177     int min;
178     int max;
179     int state;
180     int flags;
181     int cflags;
182     size_t currlen;
183
184     state = DP_S_DEFAULT;
185     flags = currlen = cflags = min = 0;
186     max = -1;
187     ch = *format++;
188
189     while (state != DP_S_DONE) {
190         if ((ch == '\0') || (currlen >= maxlen))
191             state = DP_S_DONE;
192
193         switch (state) {
194         case DP_S_DEFAULT:
195             if (ch == '%')
196                 state = DP_S_FLAGS;
197             else
198                 dopr_outch(buffer, &currlen, maxlen, ch);
199             ch = *format++;
200             break;
201         case DP_S_FLAGS:
202             switch (ch) {
203             case '-':
204                 flags |= DP_F_MINUS;
205                 ch = *format++;
206                 break;
207             case '+':
208                 flags |= DP_F_PLUS;
209                 ch = *format++;
210                 break;
211             case ' ':
212                 flags |= DP_F_SPACE;
213                 ch = *format++;
214                 break;
215             case '#':
216                 flags |= DP_F_NUM;
217                 ch = *format++;
218                 break;
219             case '0':
220                 flags |= DP_F_ZERO;
221                 ch = *format++;
222                 break;
223             default:
224                 state = DP_S_MIN;
225                 break;
226             }
227             break;
228         case DP_S_MIN:
229             if (isdigit((unsigned char)ch)) {
230                 min = 10 * min + char_to_int(ch);
231                 ch = *format++;
232             } else if (ch == '*') {
233                 min = va_arg(args, int);
234                 ch = *format++;
235                 state = DP_S_DOT;
236             } else
237                 state = DP_S_DOT;
238             break;
239         case DP_S_DOT:
240             if (ch == '.') {
241                 state = DP_S_MAX;
242                 ch = *format++;
243             } else
244                 state = DP_S_MOD;
245             break;
246         case DP_S_MAX:
247             if (isdigit((unsigned char)ch)) {
248                 if (max < 0)
249                     max = 0;
250                 max = 10 * max + char_to_int(ch);
251                 ch = *format++;
252             } else if (ch == '*') {
253                 max = va_arg(args, int);
254                 ch = *format++;
255                 state = DP_S_MOD;
256             } else
257                 state = DP_S_MOD;
258             break;
259         case DP_S_MOD:
260             switch (ch) {
261             case 'h':
262                 cflags = DP_C_SHORT;
263                 ch = *format++;
264                 break;
265             case 'l':
266                 if (*format == 'l') {
267                     cflags = DP_C_LLONG;
268                     format++;
269                 } else
270                     cflags = DP_C_LONG;
271                 ch = *format++;
272                 break;
273             case 'q':
274                 cflags = DP_C_LLONG;
275                 ch = *format++;
276                 break;
277             case 'L':
278                 cflags = DP_C_LDOUBLE;
279                 ch = *format++;
280                 break;
281             default:
282                 break;
283             }
284             state = DP_S_CONV;
285             break;
286         case DP_S_CONV:
287             switch (ch) {
288             case 'd':
289             case 'i':
290                 switch (cflags) {
291                 case DP_C_SHORT:
292                     value = va_arg(args, short int);
293                     break;
294                 case DP_C_LONG:
295                     value = va_arg(args, long int);
296                     break;
297                 case DP_C_LLONG:
298                     value = va_arg(args, LLONG);
299                     break;
300                 default:
301                     value = va_arg(args, int);
302                     break;
303                 }
304                 fmtint(buffer, &currlen, maxlen, value, 10, min, max, flags);
305                 break;
306             case 'X':
307                 flags |= DP_F_UP;
308                 /* FALLTHROUGH */
309             case 'x':
310             case 'o':
311             case 'u':
312                 flags |= DP_F_UNSIGNED;
313                 switch (cflags) {
314                 case DP_C_SHORT:
315                     value = va_arg(args,
316                         unsigned short int);
317                     break;
318                 case DP_C_LONG:
319                     value = (LLONG) va_arg(args,
320                         unsigned long int);
321                     break;
322                 case DP_C_LLONG:
323                     value = va_arg(args, unsigned LLONG);
324                     break;
325                 default:
326                     value = (LLONG) va_arg(args,
327                         unsigned int);
328                     break;
329                 }
330                 fmtint(buffer, &currlen, maxlen, value,
331                        ch == 'o' ? 8 : (ch == 'u' ? 10 : 16),
332                        min, max, flags);
333                 break;
334             case 'f':
335                 if (cflags == DP_C_LDOUBLE)
336                     fvalue = va_arg(args, LDOUBLE);
337                 else
338                     fvalue = va_arg(args, double);
339                 fmtfp(buffer, &currlen, maxlen, fvalue, min, max, flags);
340                 break;
341             case 'E':
342                 flags |= DP_F_UP;
343             case 'e':
344                 if (cflags == DP_C_LDOUBLE)
345                     fvalue = va_arg(args, LDOUBLE);
346                 else
347                     fvalue = va_arg(args, double);
348                 break;
349             case 'G':
350                 flags |= DP_F_UP;
351             case 'g':
352                 if (cflags == DP_C_LDOUBLE)
353                     fvalue = va_arg(args, LDOUBLE);
354                 else
355                     fvalue = va_arg(args, double);
356                 break;
357             case 'c':
358                 dopr_outch(buffer, &currlen, maxlen,
359                     va_arg(args, int));
360                 break;
361             case 's':
362                 strvalue = va_arg(args, char *);
363                 if (max < 0)
364                     max = maxlen;
365                 fmtstr(buffer, &currlen, maxlen, strvalue,
366                     flags, min, max);
367                 break;
368             case 'p':
369                 value = (long)va_arg(args, void *);
370                 fmtint(buffer, &currlen, maxlen,
371                     value, 16, min, max, flags);
372                 break;
373             case 'n': /* XXX */
374                 if (cflags == DP_C_SHORT) {
375                     short int *num;
376                     num = va_arg(args, short int *);
377                     *num = currlen;
378                 } else if (cflags == DP_C_LONG) { /* XXX */
379                     long int *num;
380                     num = va_arg(args, long int *);
381                     *num = (long int) currlen;
382                 } else if (cflags == DP_C_LLONG) { /* XXX */
383                     LLONG *num;
384                     num = va_arg(args, LLONG *);
385                     *num = (LLONG) currlen;
386                 } else {
387                     int    *num;
388                     num = va_arg(args, int *);
389                     *num = currlen;
390                 }
391                 break;
392             case '%':
393                 dopr_outch(buffer, &currlen, maxlen, ch);
394                 break;
395             case 'w':
396                 /* not supported yet, treat as next char */
397                 ch = *format++;
398                 break;
399             default:
400                 /* unknown, skip */
401                 break;
402             }
403             ch = *format++;
404             state = DP_S_DEFAULT;
405             flags = cflags = min = 0;
406             max = -1;
407             break;
408         case DP_S_DONE:
409             break;
410         default:
411             break;
412         }
413     }
414     if (currlen >= maxlen - 1)
415         currlen = maxlen - 1;
416     buffer[currlen] = '\0';
417     *retlen = currlen;
418     return;
419 }
420
421 static void
422 fmtstr(
423     char *buffer,
424     size_t *currlen,
425     size_t maxlen,
426     char *value,
427     int flags,
428     int min,
429     int max)
430 {
431     int padlen, strln;
432     int cnt = 0;
433
434     if (value == 0)
435         value = "<NULL>";
436     for (strln = 0; value[strln]; ++strln)
437         ;
438     padlen = min - strln;
439     if (padlen < 0)
440         padlen = 0;
441     if (flags & DP_F_MINUS)
442         padlen = -padlen;
443
444     while ((padlen > 0) && (cnt < max)) {
445         dopr_outch(buffer, currlen, maxlen, ' ');
446         --padlen;
447         ++cnt;
448     }
449     while (*value && (cnt < max)) {
450         dopr_outch(buffer, currlen, maxlen, *value++);
451         ++cnt;
452     }
453     while ((padlen < 0) && (cnt < max)) {
454         dopr_outch(buffer, currlen, maxlen, ' ');
455         ++padlen;
456         ++cnt;
457     }
458 }
459
460 static void
461 fmtint(
462     char *buffer,
463     size_t *currlen,
464     size_t maxlen,
465     LLONG value,
466     int base,
467     int min,
468     int max,
469     int flags)
470 {
471     int signvalue = 0;
472     unsigned LLONG uvalue;
473     char convert[20];
474     int place = 0;
475     int spadlen = 0;
476     int zpadlen = 0;
477     int caps = 0;
478
479     if (max < 0)
480         max = 0;
481     uvalue = value;
482     if (!(flags & DP_F_UNSIGNED)) {
483         if (value < 0) {
484             signvalue = '-';
485             uvalue = -value;
486         } else if (flags & DP_F_PLUS)
487             signvalue = '+';
488         else if (flags & DP_F_SPACE)
489             signvalue = ' ';
490     }
491     if (flags & DP_F_UP)
492         caps = 1;
493     do {
494         convert[place++] =
495             (caps ? "0123456789ABCDEF" : "0123456789abcdef")
496             [uvalue % (unsigned) base];
497         uvalue = (uvalue / (unsigned) base);
498     } while (uvalue && (place < 20));
499     if (place == 20)
500         place--;
501     convert[place] = 0;
502
503     zpadlen = max - place;
504     spadlen = min - MAX(max, place) - (signvalue ? 1 : 0);
505     if (zpadlen < 0)
506         zpadlen = 0;
507     if (spadlen < 0)
508         spadlen = 0;
509     if (flags & DP_F_ZERO) {
510         zpadlen = MAX(zpadlen, spadlen);
511         spadlen = 0;
512     }
513     if (flags & DP_F_MINUS)
514         spadlen = -spadlen;
515
516     /* spaces */
517     while (spadlen > 0) {
518         dopr_outch(buffer, currlen, maxlen, ' ');
519         --spadlen;
520     }
521
522     /* sign */
523     if (signvalue)
524         dopr_outch(buffer, currlen, maxlen, signvalue);
525
526     /* zeros */
527     if (zpadlen > 0) {
528         while (zpadlen > 0) {
529             dopr_outch(buffer, currlen, maxlen, '0');
530             --zpadlen;
531         }
532     }
533     /* digits */
534     while (place > 0)
535         dopr_outch(buffer, currlen, maxlen, convert[--place]);
536
537     /* left justified spaces */
538     while (spadlen < 0) {
539         dopr_outch(buffer, currlen, maxlen, ' ');
540         ++spadlen;
541     }
542     return;
543 }
544
545 static LDOUBLE
546 abs_val(LDOUBLE value)
547 {
548     LDOUBLE result = value;
549     if (value < 0)
550         result = -value;
551     return result;
552 }
553
554 static LDOUBLE
555 pow10(int exp)
556 {
557     LDOUBLE result = 1;
558     while (exp) {
559         result *= 10;
560         exp--;
561     }
562     return result;
563 }
564
565 static long
566 round(LDOUBLE value)
567 {
568     long intpart;
569     intpart = (long) value;
570     value = value - intpart;
571     if (value >= 0.5)
572         intpart++;
573     return intpart;
574 }
575
576 static void
577 fmtfp(
578     char *buffer,
579     size_t *currlen,
580     size_t maxlen,
581     LDOUBLE fvalue,
582     int min,
583     int max,
584     int flags)
585 {
586     int signvalue = 0;
587     LDOUBLE ufvalue;
588     char iconvert[20];
589     char fconvert[20];
590     int iplace = 0;
591     int fplace = 0;
592     int padlen = 0;
593     int zpadlen = 0;
594     int caps = 0;
595     long intpart;
596     long fracpart;
597
598     if (max < 0)
599         max = 6;
600     ufvalue = abs_val(fvalue);
601     if (fvalue < 0)
602         signvalue = '-';
603     else if (flags & DP_F_PLUS)
604         signvalue = '+';
605     else if (flags & DP_F_SPACE)
606         signvalue = ' ';
607
608     intpart = (long)ufvalue;
609
610     /* sorry, we only support 9 digits past the decimal because of our
611        conversion method */
612     if (max > 9)
613         max = 9;
614
615     /* we "cheat" by converting the fractional part to integer by
616        multiplying by a factor of 10 */
617     fracpart = round((pow10(max)) * (ufvalue - intpart));
618
619     if (fracpart >= pow10(max)) {
620         intpart++;
621         fracpart -= pow10(max);
622     }
623
624     /* convert integer part */
625     do {
626         iconvert[iplace++] =
627             (caps ? "0123456789ABCDEF"
628               : "0123456789abcdef")[intpart % 10];
629         intpart = (intpart / 10);
630     } while (intpart && (iplace < 20));
631     if (iplace == 20)
632         iplace--;
633     iconvert[iplace] = 0;
634
635     /* convert fractional part */
636     do {
637         fconvert[fplace++] =
638             (caps ? "0123456789ABCDEF"
639               : "0123456789abcdef")[fracpart % 10];
640         fracpart = (fracpart / 10);
641     } while (fracpart && (fplace < 20));
642     if (fplace == 20)
643         fplace--;
644     fconvert[fplace] = 0;
645
646     /* -1 for decimal point, another -1 if we are printing a sign */
647     padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
648     zpadlen = max - fplace;
649     if (zpadlen < 0)
650         zpadlen = 0;
651     if (padlen < 0)
652         padlen = 0;
653     if (flags & DP_F_MINUS)
654         padlen = -padlen;
655
656     if ((flags & DP_F_ZERO) && (padlen > 0)) {
657         if (signvalue) {
658             dopr_outch(buffer, currlen, maxlen, signvalue);
659             --padlen;
660             signvalue = 0;
661         }
662         while (padlen > 0) {
663             dopr_outch(buffer, currlen, maxlen, '0');
664             --padlen;
665         }
666     }
667     while (padlen > 0) {
668         dopr_outch(buffer, currlen, maxlen, ' ');
669         --padlen;
670     }
671     if (signvalue)
672         dopr_outch(buffer, currlen, maxlen, signvalue);
673
674     while (iplace > 0)
675         dopr_outch(buffer, currlen, maxlen, iconvert[--iplace]);
676
677     /*
678      * Decimal point. This should probably use locale to find the correct
679      * char to print out.
680      */
681     if (max > 0) {
682         dopr_outch(buffer, currlen, maxlen, '.');
683
684         while (fplace > 0)
685             dopr_outch(buffer, currlen, maxlen, fconvert[--fplace]);
686     }
687     while (zpadlen > 0) {
688         dopr_outch(buffer, currlen, maxlen, '0');
689         --zpadlen;
690     }
691
692     while (padlen < 0) {
693         dopr_outch(buffer, currlen, maxlen, ' ');
694         ++padlen;
695     }
696 }
697
698 static void
699 dopr_outch(
700     char *buffer,
701     size_t *currlen,
702     size_t maxlen,
703     int c)
704 {
705     if (*currlen < maxlen)
706         buffer[(*currlen)++] = (char)c;
707     return;
708 }