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