Imported Upstream version 0.4.8
[platform/upstream/libsmi.git] / lib / snprintf.c
1 /*
2  * Copyright (c) 1995-1999 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 /* $Id: snprintf.c 1429 2002-07-23 23:12:20Z strauss $ */
35
36 #include <config.h>
37
38 #include <stdio.h>
39 #include <stdarg.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <ctype.h>
43 #include <sys/types.h>
44
45 #ifdef HAVE_DMALLOC_H
46 #include <dmalloc.h>
47 #endif
48
49 #include "snprintf.h"
50
51 #ifndef MIN
52 #define MIN(a, b)       ((a) < (b) ? (a) : (b))
53 #define MAX(a, b)       ((a) < (b) ? (b) : (a))
54 #endif
55
56
57
58 enum format_flags {
59     minus_flag     =  1,
60     plus_flag      =  2,
61     space_flag     =  4,
62     alternate_flag =  8,
63     zero_flag      = 16
64 };
65
66 /*
67  * Common state
68  */
69
70 struct state {
71   unsigned char *str;
72   unsigned char *s;
73   unsigned char *theend;
74   size_t sz;
75   size_t max_sz;
76   int (*append_char)(struct state *, unsigned char);
77   int (*reserve)(struct state *, size_t);
78   /* XXX - methods */
79 };
80
81 #ifndef HAVE_VSNPRINTF
82 static int
83 sn_reserve (struct state *state, size_t n)
84 {
85   return state->s + n > state->theend;
86 }
87
88 static int
89 sn_append_char (struct state *state, unsigned char c)
90 {
91   if (sn_reserve (state, 1)) {
92     return 1;
93   } else {
94     *state->s++ = c;
95     return 0;
96   }
97 }
98 #endif
99
100 static int
101 as_reserve (struct state *state, size_t n)
102 {
103   if (state->s + n > state->theend) {
104     int off = state->s - state->str;
105     unsigned char *tmp;
106
107     if (state->max_sz && state->sz >= state->max_sz)
108       return 1;
109
110     state->sz = MAX(state->sz * 2, state->sz + n);
111     if (state->max_sz)
112       state->sz = MIN(state->sz, state->max_sz);
113     tmp = realloc (state->str, state->sz);
114     if (tmp == NULL)
115       return 1;
116     state->str = tmp;
117     state->s = state->str + off;
118     state->theend = state->str + state->sz - 1;
119   }
120   return 0;
121 }
122
123 static int
124 as_append_char (struct state *state, unsigned char c)
125 {
126   if(as_reserve (state, 1))
127     return 1;
128   else {
129     *state->s++ = c;
130     return 0;
131   }
132 }
133
134 static int
135 append_number(struct state *state,
136               unsigned long num, unsigned base, char *rep,
137               int width, int prec, int flags, int minusp)
138 {
139   int len = 0;
140   int i;
141
142   /* given precision, ignore zero flag */
143   if(prec != -1)
144     flags &= ~zero_flag;
145   else
146     prec = 1;
147   /* zero value with zero precision -> "" */
148   if(prec == 0 && num == 0)
149     return 0;
150   do{
151     if((*state->append_char)(state, rep[num % base]))
152       return 1;
153     len++;
154     num /= base;
155   }while(num);
156   prec -= len;
157   /* pad with prec zeros */
158   while(prec-- > 0){
159     if((*state->append_char)(state, '0'))
160       return 1;
161     len++;
162   }
163   /* add length of alternate prefix (added later) to len */
164   if(flags & alternate_flag && (base == 16 || base == 8))
165     len += base / 8;
166   /* pad with zeros */
167   if(flags & zero_flag){
168     width -= len;
169     if(minusp || (flags & space_flag) || (flags & plus_flag))
170       width--;
171     while(width-- > 0){
172       if((*state->append_char)(state, '0'))
173         return 1;
174       len++;
175     }
176   }
177   /* add alternate prefix */
178   if(flags & alternate_flag && (base == 16 || base == 8)){
179     if(base == 16)
180       if((*state->append_char)(state, rep[10] + 23)) /* XXX */
181         return 1;
182     if((*state->append_char)(state, '0'))
183       return 1;
184   }
185   /* add sign */
186   if(minusp){
187     if((*state->append_char)(state, '-'))
188       return 1;
189     len++;
190   } else if(flags & plus_flag) {
191     if((*state->append_char)(state, '+'))
192       return 1;
193     len++;
194   } else if(flags & space_flag) {
195     if((*state->append_char)(state, ' '))
196       return 1;
197     len++;
198   }
199   if(flags & minus_flag)
200     /* swap before 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   width -= len;
207   while(width-- > 0){
208     if((*state->append_char)(state,  ' '))
209       return 1;
210     len++;
211   }
212   if(!(flags & minus_flag))
213     /* swap after padding with spaces */
214     for(i = 0; i < len / 2; i++){
215       char c = state->s[-i-1];
216       state->s[-i-1] = state->s[-len+i];
217       state->s[-len+i] = c;
218     }
219     
220   return 0;
221 }
222
223 static int
224 append_string (struct state *state,
225                unsigned char *arg,
226                int width,
227                int prec,
228                int flags)
229 {
230   if(prec != -1)
231     width -= prec;
232   else
233     width -= strlen((char *)arg);
234   if(!(flags & minus_flag))
235     while(width-- > 0)
236       if((*state->append_char) (state, ' '))
237         return 1;
238   if (prec != -1) {
239     while (*arg && prec--)
240       if ((*state->append_char) (state, *arg++))
241         return 1;
242   } else {
243     while (*arg)
244       if ((*state->append_char) (state, *arg++))
245         return 1;
246   }
247   if(flags & minus_flag)
248     while(width-- > 0)
249       if((*state->append_char) (state, ' '))
250         return 1;
251   return 0;
252 }
253
254 static int
255 append_char(struct state *state,
256             unsigned char arg,
257             int width,
258             int flags)
259 {
260   while(!(flags & minus_flag) && --width > 0)
261     if((*state->append_char) (state, ' '))
262       return 1;
263     
264   if((*state->append_char) (state, arg))
265     return 1;
266   while((flags & minus_flag) && --width > 0)
267     if((*state->append_char) (state, ' '))
268       return 1;
269     
270   return 0;
271 }
272
273 /*
274  * This can't be made into a function...
275  */
276
277 #define PARSE_INT_FORMAT(res, arg, unsig) \
278 if (long_flag) \
279      res = (unsig long)va_arg(arg, unsig long); \
280 else if (short_flag) \
281      res = (unsig short)va_arg(arg, unsig int); \
282 else \
283      res = (unsig int)va_arg(arg, unsig int)
284
285 /*
286  * zyxprintf - return 0 or -1
287  */
288
289 static int
290 xyzprintf (struct state *state, const char *char_format, va_list ap)
291 {
292   const unsigned char *format = (const unsigned char *)char_format;
293   unsigned char c;
294
295   while((c = *format++)) {
296     if (c == '%') {
297       int flags      = 0;
298       int width      = 0;
299       int prec       = -1;
300       int long_flag  = 0;
301       int short_flag = 0;
302
303       /* flags */
304       while((c = *format++)){
305         if(c == '-')
306           flags |= minus_flag;
307         else if(c == '+')
308           flags |= plus_flag;
309         else if(c == ' ')
310           flags |= space_flag;
311         else if(c == '#')
312           flags |= alternate_flag;
313         else if(c == '0')
314           flags |= zero_flag;
315         else
316           break;
317       }
318       
319       if((flags & space_flag) && (flags & plus_flag))
320         flags ^= space_flag;
321
322       if((flags & minus_flag) && (flags & zero_flag))
323         flags ^= zero_flag;
324
325       /* width */
326       if (isdigit(c))
327         do {
328           width = width * 10 + c - '0';
329           c = *format++;
330         } while(isdigit(c));
331       else if(c == '*') {
332         width = va_arg(ap, int);
333         c = *format++;
334       }
335
336       /* precision */
337       if (c == '.') {
338         prec = 0;
339         c = *format++;
340         if (isdigit(c))
341           do {
342             prec = prec * 10 + c - '0';
343             c = *format++;
344           } while(isdigit(c));
345         else if (c == '*') {
346           prec = va_arg(ap, int);
347           c = *format++;
348         }
349       }
350
351       /* size */
352
353       if (c == 'h') {
354         short_flag = 1;
355         c = *format++;
356       } else if (c == 'l') {
357         long_flag = 1;
358         c = *format++;
359       }
360
361       switch (c) {
362       case 'c' :
363         if(append_char(state, va_arg(ap, int), width, flags))
364           return -1;
365         break;
366       case 's' :
367         if (append_string(state,
368                           va_arg(ap, unsigned char*),
369                           width,
370                           prec, 
371                           flags))
372           return -1;
373         break;
374       case 'd' :
375       case 'i' : {
376         long arg;
377         unsigned long num;
378         int minusp = 0;
379
380         PARSE_INT_FORMAT(arg, ap, signed);
381
382         if (arg < 0) {
383           minusp = 1;
384           num = -arg;
385         } else
386           num = arg;
387
388         if (append_number (state, num, 10, "0123456789",
389                            width, prec, flags, minusp))
390           return -1;
391         break;
392       }
393       case 'u' : {
394         unsigned long arg;
395
396         PARSE_INT_FORMAT(arg, ap, unsigned);
397
398         if (append_number (state, arg, 10, "0123456789",
399                            width, prec, flags, 0))
400           return -1;
401         break;
402       }
403       case 'o' : {
404         unsigned long arg;
405
406         PARSE_INT_FORMAT(arg, ap, unsigned);
407
408         if (append_number (state, arg, 010, "01234567",
409                            width, prec, flags, 0))
410           return -1;
411         break;
412       }
413       case 'x' : {
414         unsigned long arg;
415
416         PARSE_INT_FORMAT(arg, ap, unsigned);
417
418         if (append_number (state, arg, 0x10, "0123456789abcdef",
419                            width, prec, flags, 0))
420           return -1;
421         break;
422       }
423       case 'X' :{
424         unsigned long arg;
425
426         PARSE_INT_FORMAT(arg, ap, unsigned);
427
428         if (append_number (state, arg, 0x10, "0123456789ABCDEF",
429                            width, prec, flags, 0))
430           return -1;
431         break;
432       }
433       case 'p' : {
434         unsigned long arg = (unsigned long)va_arg(ap, void*);
435
436         if (append_number (state, arg, 0x10, "0123456789ABCDEF",
437                            width, prec, flags, 0))
438           return -1;
439         break;
440       }
441       case 'n' : {
442         int *arg = va_arg(ap, int*);
443         *arg = state->s - state->str;
444         break;
445       }
446       case '\0' :
447           --format;
448           /* FALLTHROUGH */
449       case '%' :
450         if ((*state->append_char)(state, c))
451           return -1;
452         break;
453       default :
454         if (   (*state->append_char)(state, '%')
455             || (*state->append_char)(state, c))
456           return -1;
457         break;
458       }
459     } else
460       if ((*state->append_char) (state, c))
461         return -1;
462   }
463   return 0;
464 }
465
466 #ifndef HAVE_SNPRINTF
467 int
468 snprintf (char *str, size_t sz, const char *format, ...)
469 {
470   va_list args;
471   int ret;
472
473   va_start(args, format);
474   ret = vsnprintf (str, sz, format, args);
475
476 #ifdef PARANOIA
477   {
478     int ret2;
479     char *tmp;
480
481     tmp = malloc (sz);
482     if (tmp == NULL)
483       abort ();
484
485     ret2 = vsprintf (tmp, format, args);
486     if (ret != ret2 || strcmp(str, tmp))
487       abort ();
488     free (tmp);
489   }
490 #endif
491
492   va_end(args);
493   return ret;
494 }
495 #endif
496
497 #ifndef HAVE_ASPRINTF
498 int
499 asprintf (char **ret, const char *format, ...)
500 {
501   va_list args;
502   int val;
503
504   va_start(args, format);
505   val = vasprintf (ret, format, args);
506
507 #ifdef PARANOIA
508   {
509     int ret2;
510     char *tmp;
511     tmp = malloc (val + 1);
512     if (tmp == NULL)
513       abort ();
514
515     ret2 = vsprintf (tmp, format, args);
516     if (val != ret2 || strcmp(*ret, tmp))
517       abort ();
518     free (tmp);
519   }
520 #endif
521
522   va_end(args);
523   return val;
524 }
525 #endif
526
527 #ifndef HAVE_ASNPRINTF
528 int
529 asnprintf (char **ret, size_t max_sz, const char *format, ...)
530 {
531   va_list args;
532   int val;
533
534   va_start(args, format);
535   val = vasnprintf (ret, max_sz, format, args);
536
537 #ifdef PARANOIA
538   {
539     int ret2;
540     char *tmp;
541     tmp = malloc (val + 1);
542     if (tmp == NULL)
543       abort ();
544
545     ret2 = vsprintf (tmp, format, args);
546     if (val != ret2 || strcmp(*ret, tmp))
547       abort ();
548     free (tmp);
549   }
550 #endif
551
552   va_end(args);
553   return val;
554 }
555 #endif
556
557 #ifndef HAVE_VASPRINTF
558 int
559 vasprintf (char **ret, const char *format, va_list args)
560 {
561   return vasnprintf (ret, 0, format, args);
562 }
563 #endif
564
565
566 #ifndef HAVE_VASNPRINTF
567 int
568 vasnprintf (char **ret, size_t max_sz, const char *format, va_list args)
569 {
570   int st;
571   size_t len;
572   struct state state;
573
574   state.max_sz = max_sz;
575   state.sz     = 1;
576   state.str    = malloc(state.sz);
577   if (state.str == NULL) {
578     *ret = NULL;
579     return -1;
580   }
581   state.s = state.str;
582   state.theend = state.s + state.sz - 1;
583   state.append_char = as_append_char;
584   state.reserve     = as_reserve;
585
586   st = xyzprintf (&state, format, args);
587   if (st) {
588     free (state.str);
589     *ret = NULL;
590     return -1;
591   } else {
592     char *tmp;
593
594     *state.s = '\0';
595     len = state.s - state.str;
596     tmp = realloc (state.str, len+1);
597     if (tmp == NULL) {
598       free (state.str);
599       *ret = NULL;
600       return -1;
601     }
602     *ret = tmp;
603     return len;
604   }
605 }
606 #endif
607
608 #ifndef HAVE_VSNPRINTF
609 int
610 vsnprintf (char *str, size_t sz, const char *format, va_list args)
611 {
612   struct state state;
613   int ret;
614   unsigned char *ustr = (unsigned char *)str;
615
616   state.max_sz = 0;
617   state.sz     = sz;
618   state.str    = ustr;
619   state.s      = ustr;
620   state.theend = ustr + sz - 1;
621   state.append_char = sn_append_char;
622   state.reserve     = sn_reserve;
623
624   ret = xyzprintf (&state, format, args);
625   *state.s = '\0';
626   if (ret)
627     return sz;
628   else
629     return state.s - state.str;
630 }
631 #endif