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