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