Initial import from FreeBSD RELENG_4:
[dragonfly.git] / crypto / kerberosIV / lib / roken / snprintf.c
1 /*
2  * Copyright (c) 1995-2000 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  * 
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  * 
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #ifdef HAVE_CONFIG_H
35 #include <config.h>
36 RCSID("$Id: snprintf.c,v 1.24.2.1 2000/06/14 07:26:49 joda Exp $");
37 #endif
38 #include <stdio.h>
39 #include <stdarg.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <ctype.h>
43 #include <roken.h>
44
45 enum format_flags {
46     minus_flag     =  1,
47     plus_flag      =  2,
48     space_flag     =  4,
49     alternate_flag =  8,
50     zero_flag      = 16
51 };
52
53 /*
54  * Common state
55  */
56
57 struct state {
58   unsigned char *str;
59   unsigned char *s;
60   unsigned char *theend;
61   size_t sz;
62   size_t max_sz;
63   int (*append_char)(struct state *, unsigned char);
64   int (*reserve)(struct state *, size_t);
65   /* XXX - methods */
66 };
67
68 #ifndef HAVE_VSNPRINTF
69 static int
70 sn_reserve (struct state *state, size_t n)
71 {
72   return state->s + n > state->theend;
73 }
74
75 static int
76 sn_append_char (struct state *state, unsigned char c)
77 {
78   if (sn_reserve (state, 1)) {
79     return 1;
80   } else {
81     *state->s++ = c;
82     return 0;
83   }
84 }
85 #endif
86
87 static int
88 as_reserve (struct state *state, size_t n)
89 {
90   if (state->s + n > state->theend) {
91     int off = state->s - state->str;
92     unsigned char *tmp;
93
94     if (state->max_sz && state->sz >= state->max_sz)
95       return 1;
96
97     state->sz = max(state->sz * 2, state->sz + n);
98     if (state->max_sz)
99       state->sz = min(state->sz, state->max_sz);
100     tmp = realloc (state->str, state->sz);
101     if (tmp == NULL)
102       return 1;
103     state->str = tmp;
104     state->s = state->str + off;
105     state->theend = state->str + state->sz - 1;
106   }
107   return 0;
108 }
109
110 static int
111 as_append_char (struct state *state, unsigned char c)
112 {
113   if(as_reserve (state, 1))
114     return 1;
115   else {
116     *state->s++ = c;
117     return 0;
118   }
119 }
120
121 static int
122 append_number(struct state *state,
123               unsigned long num, unsigned base, char *rep,
124               int width, int prec, int flags, int minusp)
125 {
126   int len = 0;
127   int i;
128
129   /* given precision, ignore zero flag */
130   if(prec != -1)
131     flags &= ~zero_flag;
132   else
133     prec = 1;
134   /* zero value with zero precision -> "" */
135   if(prec == 0 && num == 0)
136     return 0;
137   do{
138     if((*state->append_char)(state, rep[num % base]))
139       return 1;
140     len++;
141     num /= base;
142   }while(num);
143   prec -= len;
144   /* pad with prec zeros */
145   while(prec-- > 0){
146     if((*state->append_char)(state, '0'))
147       return 1;
148     len++;
149   }
150   /* add length of alternate prefix (added later) to len */
151   if(flags & alternate_flag && (base == 16 || base == 8))
152     len += base / 8;
153   /* pad with zeros */
154   if(flags & zero_flag){
155     width -= len;
156     if(minusp || (flags & space_flag) || (flags & plus_flag))
157       width--;
158     while(width-- > 0){
159       if((*state->append_char)(state, '0'))
160         return 1;
161       len++;
162     }
163   }
164   /* add alternate prefix */
165   if(flags & alternate_flag && (base == 16 || base == 8)){
166     if(base == 16)
167       if((*state->append_char)(state, rep[10] + 23)) /* XXX */
168         return 1;
169     if((*state->append_char)(state, '0'))
170       return 1;
171   }
172   /* add sign */
173   if(minusp){
174     if((*state->append_char)(state, '-'))
175       return 1;
176     len++;
177   } else if(flags & plus_flag) {
178     if((*state->append_char)(state, '+'))
179       return 1;
180     len++;
181   } else if(flags & space_flag) {
182     if((*state->append_char)(state, ' '))
183       return 1;
184     len++;
185   }
186   if(flags & minus_flag)
187     /* swap before padding with spaces */
188     for(i = 0; i < len / 2; i++){
189       char c = state->s[-i-1];
190       state->s[-i-1] = state->s[-len+i];
191       state->s[-len+i] = c;
192     }
193   width -= len;
194   while(width-- > 0){
195     if((*state->append_char)(state,  ' '))
196       return 1;
197     len++;
198   }
199   if(!(flags & minus_flag))
200     /* swap after padding with spaces */
201     for(i = 0; i < len / 2; i++){
202       char c = state->s[-i-1];
203       state->s[-i-1] = state->s[-len+i];
204       state->s[-len+i] = c;
205     }
206     
207   return 0;
208 }
209
210 static int
211 append_string (struct state *state,
212                unsigned char *arg,
213                int width,
214                int prec,
215                int flags)
216 {
217   if(prec != -1)
218     width -= prec;
219   else
220     width -= strlen((char *)arg);
221   if(!(flags & minus_flag))
222     while(width-- > 0)
223       if((*state->append_char) (state, ' '))
224         return 1;
225   if (prec != -1) {
226     while (*arg && prec--)
227       if ((*state->append_char) (state, *arg++))
228         return 1;
229   } else {
230     while (*arg)
231       if ((*state->append_char) (state, *arg++))
232         return 1;
233   }
234   if(flags & minus_flag)
235     while(width-- > 0)
236       if((*state->append_char) (state, ' '))
237         return 1;
238   return 0;
239 }
240
241 static int
242 append_char(struct state *state,
243             unsigned char arg,
244             int width,
245             int flags)
246 {
247   while(!(flags & minus_flag) && --width > 0)
248     if((*state->append_char) (state, ' '))
249       return 1;
250     
251   if((*state->append_char) (state, arg))
252     return 1;
253   while((flags & minus_flag) && --width > 0)
254     if((*state->append_char) (state, ' '))
255       return 1;
256     
257   return 0;
258 }
259
260 /*
261  * This can't be made into a function...
262  */
263
264 #define PARSE_INT_FORMAT(res, arg, unsig) \
265 if (long_flag) \
266      res = (unsig long)va_arg(arg, unsig long); \
267 else if (short_flag) \
268      res = (unsig short)va_arg(arg, unsig int); \
269 else \
270      res = (unsig int)va_arg(arg, unsig int)
271
272 /*
273  * zyxprintf - return 0 or -1
274  */
275
276 static int
277 xyzprintf (struct state *state, const char *char_format, va_list ap)
278 {
279   const unsigned char *format = (const unsigned char *)char_format;
280   unsigned char c;
281
282   while((c = *format++)) {
283     if (c == '%') {
284       int flags      = 0;
285       int width      = 0;
286       int prec       = -1;
287       int long_flag  = 0;
288       int short_flag = 0;
289
290       /* flags */
291       while((c = *format++)){
292         if(c == '-')
293           flags |= minus_flag;
294         else if(c == '+')
295           flags |= plus_flag;
296         else if(c == ' ')
297           flags |= space_flag;
298         else if(c == '#')
299           flags |= alternate_flag;
300         else if(c == '0')
301           flags |= zero_flag;
302         else
303           break;
304       }
305       
306       if((flags & space_flag) && (flags & plus_flag))
307         flags ^= space_flag;
308
309       if((flags & minus_flag) && (flags & zero_flag))
310         flags ^= zero_flag;
311
312       /* width */
313       if (isdigit(c))
314         do {
315           width = width * 10 + c - '0';
316           c = *format++;
317         } while(isdigit(c));
318       else if(c == '*') {
319         width = va_arg(ap, int);
320         c = *format++;
321       }
322
323       /* precision */
324       if (c == '.') {
325         prec = 0;
326         c = *format++;
327         if (isdigit(c))
328           do {
329             prec = prec * 10 + c - '0';
330             c = *format++;
331           } while(isdigit(c));
332         else if (c == '*') {
333           prec = va_arg(ap, int);
334           c = *format++;
335         }
336       }
337
338       /* size */
339
340       if (c == 'h') {
341         short_flag = 1;
342         c = *format++;
343       } else if (c == 'l') {
344         long_flag = 1;
345         c = *format++;
346       }
347
348       switch (c) {
349       case 'c' :
350         if(append_char(state, va_arg(ap, int), width, flags))
351           return -1;
352         break;
353       case 's' :
354         if (append_string(state,
355                           va_arg(ap, unsigned char*),
356                           width,
357                           prec, 
358                           flags))
359           return -1;
360         break;
361       case 'd' :
362       case 'i' : {
363         long arg;
364         unsigned long num;
365         int minusp = 0;
366
367         PARSE_INT_FORMAT(arg, ap, signed);
368
369         if (arg < 0) {
370           minusp = 1;
371           num = -arg;
372         } else
373           num = arg;
374
375         if (append_number (state, num, 10, "0123456789",
376                            width, prec, flags, minusp))
377           return -1;
378         break;
379       }
380       case 'u' : {
381         unsigned long arg;
382
383         PARSE_INT_FORMAT(arg, ap, unsigned);
384
385         if (append_number (state, arg, 10, "0123456789",
386                            width, prec, flags, 0))
387           return -1;
388         break;
389       }
390       case 'o' : {
391         unsigned long arg;
392
393         PARSE_INT_FORMAT(arg, ap, unsigned);
394
395         if (append_number (state, arg, 010, "01234567",
396                            width, prec, flags, 0))
397           return -1;
398         break;
399       }
400       case 'x' : {
401         unsigned long arg;
402
403         PARSE_INT_FORMAT(arg, ap, unsigned);
404
405         if (append_number (state, arg, 0x10, "0123456789abcdef",
406                            width, prec, flags, 0))
407           return -1;
408         break;
409       }
410       case 'X' :{
411         unsigned long arg;
412
413         PARSE_INT_FORMAT(arg, ap, unsigned);
414
415         if (append_number (state, arg, 0x10, "0123456789ABCDEF",
416                            width, prec, flags, 0))
417           return -1;
418         break;
419       }
420       case 'p' : {
421         unsigned long arg = (unsigned long)va_arg(ap, void*);
422
423         if (append_number (state, arg, 0x10, "0123456789ABCDEF",
424                            width, prec, flags, 0))
425           return -1;
426         break;
427       }
428       case 'n' : {
429         int *arg = va_arg(ap, int*);
430         *arg = state->s - state->str;
431         break;
432       }
433       case '\0' :
434           --format;
435           /* FALLTHROUGH */
436       case '%' :
437         if ((*state->append_char)(state, c))
438           return -1;
439         break;
440       default :
441         if (   (*state->append_char)(state, '%')
442             || (*state->append_char)(state, c))
443           return -1;
444         break;
445       }
446     } else
447       if ((*state->append_char) (state, c))
448         return -1;
449   }
450   return 0;
451 }
452
453 #ifndef HAVE_SNPRINTF
454 int
455 snprintf (char *str, size_t sz, const char *format, ...)
456 {
457   va_list args;
458   int ret;
459
460   va_start(args, format);
461   ret = vsnprintf (str, sz, format, args);
462
463 #ifdef PARANOIA
464   {
465     int ret2;
466     char *tmp;
467
468     tmp = malloc (sz);
469     if (tmp == NULL)
470       abort ();
471
472     ret2 = vsprintf (tmp, format, args);
473     if (ret != ret2 || strcmp(str, tmp))
474       abort ();
475     free (tmp);
476   }
477 #endif
478
479   va_end(args);
480   return ret;
481 }
482 #endif
483
484 #ifndef HAVE_ASPRINTF
485 int
486 asprintf (char **ret, const char *format, ...)
487 {
488   va_list args;
489   int val;
490
491   va_start(args, format);
492   val = vasprintf (ret, format, args);
493
494 #ifdef PARANOIA
495   {
496     int ret2;
497     char *tmp;
498     tmp = malloc (val + 1);
499     if (tmp == NULL)
500       abort ();
501
502     ret2 = vsprintf (tmp, format, args);
503     if (val != ret2 || strcmp(*ret, tmp))
504       abort ();
505     free (tmp);
506   }
507 #endif
508
509   va_end(args);
510   return val;
511 }
512 #endif
513
514 #ifndef HAVE_ASNPRINTF
515 int
516 asnprintf (char **ret, size_t max_sz, const char *format, ...)
517 {
518   va_list args;
519   int val;
520
521   va_start(args, format);
522   val = vasnprintf (ret, max_sz, format, args);
523
524 #ifdef PARANOIA
525   {
526     int ret2;
527     char *tmp;
528     tmp = malloc (val + 1);
529     if (tmp == NULL)
530       abort ();
531
532     ret2 = vsprintf (tmp, format, args);
533     if (val != ret2 || strcmp(*ret, tmp))
534       abort ();
535     free (tmp);
536   }
537 #endif
538
539   va_end(args);
540   return val;
541 }
542 #endif
543
544 #ifndef HAVE_VASPRINTF
545 int
546 vasprintf (char **ret, const char *format, va_list args)
547 {
548   return vasnprintf (ret, 0, format, args);
549 }
550 #endif
551
552
553 #ifndef HAVE_VASNPRINTF
554 int
555 vasnprintf (char **ret, size_t max_sz, const char *format, va_list args)
556 {
557   int st;
558   size_t len;
559   struct state state;
560
561   state.max_sz = max_sz;
562   state.sz     = 1;
563   state.str    = malloc(state.sz);
564   if (state.str == NULL) {
565     *ret = NULL;
566     return -1;
567   }
568   state.s = state.str;
569   state.theend = state.s + state.sz - 1;
570   state.append_char = as_append_char;
571   state.reserve     = as_reserve;
572
573   st = xyzprintf (&state, format, args);
574   if (st) {
575     free (state.str);
576     *ret = NULL;
577     return -1;
578   } else {
579     char *tmp;
580
581     *state.s = '\0';
582     len = state.s - state.str;
583     tmp = realloc (state.str, len+1);
584     if (tmp == NULL) {
585       free (state.str);
586       *ret = NULL;
587       return -1;
588     }
589     *ret = tmp;
590     return len;
591   }
592 }
593 #endif
594
595 #ifndef HAVE_VSNPRINTF
596 int
597 vsnprintf (char *str, size_t sz, const char *format, va_list args)
598 {
599   struct state state;
600   int ret;
601   unsigned char *ustr = (unsigned char *)str;
602
603   state.max_sz = 0;
604   state.sz     = sz;
605   state.str    = ustr;
606   state.s      = ustr;
607   state.theend = ustr + sz - 1;
608   state.append_char = sn_append_char;
609   state.reserve     = sn_reserve;
610
611   ret = xyzprintf (&state, format, args);
612   *state.s = '\0';
613   if (ret)
614     return sz;
615   else
616     return state.s - state.str;
617 }
618 #endif
619