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