xref: /freebsd/contrib/tcpdump/missing/snprintf.c (revision 1b6c76a2fe091c74f08427e6c870851025a9cf67)
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.4 2001/01/02 22:33:04 guy Exp $ */
35 
36 #ifdef HAVE_CONFIG_H
37 #include <config.h>
38 #endif
39 
40 #ifndef lint
41 static const char rcsid[] =
42      "@(#) $Header: /tcpdump/master/tcpdump/missing/snprintf.c,v 1.4 2001/01/02 22:33:04 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 static int
97 as_reserve (struct state *state, size_t n)
98 {
99   if (state->s + n > state->theend) {
100     int off = state->s - state->str;
101     unsigned char *tmp;
102 
103     if (state->max_sz && state->sz >= state->max_sz)
104       return 1;
105 
106     state->sz = max(state->sz * 2, state->sz + n);
107     if (state->max_sz)
108       state->sz = min(state->sz, state->max_sz);
109     tmp = realloc (state->str, state->sz);
110     if (tmp == NULL)
111       return 1;
112     state->str = tmp;
113     state->s = state->str + off;
114     state->theend = state->str + state->sz - 1;
115   }
116   return 0;
117 }
118 
119 static int
120 as_append_char (struct state *state, unsigned char c)
121 {
122   if(as_reserve (state, 1))
123     return 1;
124   else {
125     *state->s++ = c;
126     return 0;
127   }
128 }
129 
130 static int
131 append_number(struct state *state,
132 	      unsigned long num, unsigned base, char *rep,
133 	      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,
221 	       unsigned char *arg,
222 	       int width,
223 	       int prec,
224 	       int flags)
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,
252 	    unsigned char arg,
253 	    int width,
254 	    int flags)
255 {
256   while(!(flags & minus_flag) && --width > 0)
257     if((*state->append_char) (state, ' '))
258       return 1;
259 
260   if((*state->append_char) (state, arg))
261     return 1;
262   while((flags & minus_flag) && --width > 0)
263     if((*state->append_char) (state, ' '))
264       return 1;
265 
266   return 0;
267 }
268 
269 /*
270  * This can't be made into a function...
271  */
272 
273 #define PARSE_INT_FORMAT(res, arg, unsig) \
274 if (long_flag) \
275      res = (unsig long)va_arg(arg, unsig long); \
276 else if (short_flag) \
277      res = (unsig short)va_arg(arg, unsig int); \
278 else \
279      res = (unsig int)va_arg(arg, unsig int)
280 
281 /*
282  * zyxprintf - return 0 or -1
283  */
284 
285 static int
286 xyzprintf (struct state *state, const char *char_format, va_list ap)
287 {
288   const unsigned char *format = (const unsigned char *)char_format;
289   unsigned char c;
290 
291   while((c = *format++)) {
292     if (c == '%') {
293       int flags      = 0;
294       int width      = 0;
295       int prec       = -1;
296       int long_flag  = 0;
297       int short_flag = 0;
298 
299       /* flags */
300       while((c = *format++)){
301 	if(c == '-')
302 	  flags |= minus_flag;
303 	else if(c == '+')
304 	  flags |= plus_flag;
305 	else if(c == ' ')
306 	  flags |= space_flag;
307 	else if(c == '#')
308 	  flags |= alternate_flag;
309 	else if(c == '0')
310 	  flags |= zero_flag;
311 	else
312 	  break;
313       }
314 
315       if((flags & space_flag) && (flags & plus_flag))
316 	flags ^= space_flag;
317 
318       if((flags & minus_flag) && (flags & zero_flag))
319 	flags ^= zero_flag;
320 
321       /* width */
322       if (isdigit(c))
323 	do {
324 	  width = width * 10 + c - '0';
325 	  c = *format++;
326 	} while(isdigit(c));
327       else if(c == '*') {
328 	width = va_arg(ap, int);
329 	c = *format++;
330       }
331 
332       /* precision */
333       if (c == '.') {
334 	prec = 0;
335 	c = *format++;
336 	if (isdigit(c))
337 	  do {
338 	    prec = prec * 10 + c - '0';
339 	    c = *format++;
340 	  } while(isdigit(c));
341 	else if (c == '*') {
342 	  prec = va_arg(ap, int);
343 	  c = *format++;
344 	}
345       }
346 
347       /* size */
348 
349       if (c == 'h') {
350 	short_flag = 1;
351 	c = *format++;
352       } else if (c == 'l') {
353 	long_flag = 1;
354 	c = *format++;
355       }
356 
357       switch (c) {
358       case 'c' :
359 	if(append_char(state, va_arg(ap, int), width, flags))
360 	  return -1;
361 	break;
362       case 's' :
363 	if (append_string(state,
364 			  va_arg(ap, unsigned char*),
365 			  width,
366 			  prec,
367 			  flags))
368 	  return -1;
369 	break;
370       case 'd' :
371       case 'i' : {
372 	long arg;
373 	unsigned long num;
374 	int minusp = 0;
375 
376 	PARSE_INT_FORMAT(arg, ap, signed);
377 
378 	if (arg < 0) {
379 	  minusp = 1;
380 	  num = -arg;
381 	} else
382 	  num = arg;
383 
384 	if (append_number (state, num, 10, "0123456789",
385 			   width, prec, flags, minusp))
386 	  return -1;
387 	break;
388       }
389       case 'u' : {
390 	unsigned long arg;
391 
392 	PARSE_INT_FORMAT(arg, ap, unsigned);
393 
394 	if (append_number (state, arg, 10, "0123456789",
395 			   width, prec, flags, 0))
396 	  return -1;
397 	break;
398       }
399       case 'o' : {
400 	unsigned long arg;
401 
402 	PARSE_INT_FORMAT(arg, ap, unsigned);
403 
404 	if (append_number (state, arg, 010, "01234567",
405 			   width, prec, flags, 0))
406 	  return -1;
407 	break;
408       }
409       case 'x' : {
410 	unsigned long arg;
411 
412 	PARSE_INT_FORMAT(arg, ap, unsigned);
413 
414 	if (append_number (state, arg, 0x10, "0123456789abcdef",
415 			   width, prec, flags, 0))
416 	  return -1;
417 	break;
418       }
419       case 'X' :{
420 	unsigned long arg;
421 
422 	PARSE_INT_FORMAT(arg, ap, unsigned);
423 
424 	if (append_number (state, arg, 0x10, "0123456789ABCDEF",
425 			   width, prec, flags, 0))
426 	  return -1;
427 	break;
428       }
429       case 'p' : {
430 	unsigned long arg = (unsigned long)va_arg(ap, void*);
431 
432 	if (append_number (state, arg, 0x10, "0123456789ABCDEF",
433 			   width, prec, flags, 0))
434 	  return -1;
435 	break;
436       }
437       case 'n' : {
438 	int *arg = va_arg(ap, int*);
439 	*arg = state->s - state->str;
440 	break;
441       }
442       case '\0' :
443 	  --format;
444 	  /* FALLTHROUGH */
445       case '%' :
446 	if ((*state->append_char)(state, c))
447 	  return -1;
448 	break;
449       default :
450 	if (   (*state->append_char)(state, '%')
451 	    || (*state->append_char)(state, c))
452 	  return -1;
453 	break;
454       }
455     } else
456       if ((*state->append_char) (state, c))
457 	return -1;
458   }
459   return 0;
460 }
461 
462 #ifndef HAVE_SNPRINTF
463 int
464 snprintf (char *str, size_t sz, const char *format, ...)
465 {
466   va_list args;
467   int ret;
468 
469   va_start(args, format);
470   ret = vsnprintf (str, sz, format, args);
471 
472 #ifdef PARANOIA
473   {
474     int ret2;
475     char *tmp;
476 
477     tmp = malloc (sz);
478     if (tmp == NULL)
479       abort ();
480 
481     ret2 = vsprintf (tmp, format, args);
482     if (ret != ret2 || strcmp(str, tmp))
483       abort ();
484     free (tmp);
485   }
486 #endif
487 
488   va_end(args);
489   return ret;
490 }
491 #endif
492 
493 #ifndef HAVE_ASPRINTF
494 int
495 asprintf (char **ret, const char *format, ...)
496 {
497   va_list args;
498   int val;
499 
500   va_start(args, format);
501   val = vasprintf (ret, format, args);
502 
503 #ifdef PARANOIA
504   {
505     int ret2;
506     char *tmp;
507     tmp = malloc (val + 1);
508     if (tmp == NULL)
509       abort ();
510 
511     ret2 = vsprintf (tmp, format, args);
512     if (val != ret2 || strcmp(*ret, tmp))
513       abort ();
514     free (tmp);
515   }
516 #endif
517 
518   va_end(args);
519   return val;
520 }
521 #endif
522 
523 #ifndef HAVE_ASNPRINTF
524 int
525 asnprintf (char **ret, size_t max_sz, const char *format, ...)
526 {
527   va_list args;
528   int val;
529 
530   va_start(args, format);
531   val = vasnprintf (ret, max_sz, format, args);
532 
533 #ifdef PARANOIA
534   {
535     int ret2;
536     char *tmp;
537     tmp = malloc (val + 1);
538     if (tmp == NULL)
539       abort ();
540 
541     ret2 = vsprintf (tmp, format, args);
542     if (val != ret2 || strcmp(*ret, tmp))
543       abort ();
544     free (tmp);
545   }
546 #endif
547 
548   va_end(args);
549   return val;
550 }
551 #endif
552 
553 #ifndef HAVE_VASPRINTF
554 int
555 vasprintf (char **ret, const char *format, va_list args)
556 {
557   return vasnprintf (ret, 0, format, args);
558 }
559 #endif
560 
561 
562 #ifndef HAVE_VASNPRINTF
563 int
564 vasnprintf (char **ret, size_t max_sz, const char *format, va_list args)
565 {
566   int st;
567   size_t len;
568   struct state state;
569 
570   state.max_sz = max_sz;
571   state.sz     = 1;
572   state.str    = malloc(state.sz);
573   if (state.str == NULL) {
574     *ret = NULL;
575     return -1;
576   }
577   state.s = state.str;
578   state.theend = state.s + state.sz - 1;
579   state.append_char = as_append_char;
580   state.reserve     = as_reserve;
581 
582   st = xyzprintf (&state, format, args);
583   if (st) {
584     free (state.str);
585     *ret = NULL;
586     return -1;
587   } else {
588     char *tmp;
589 
590     *state.s = '\0';
591     len = state.s - state.str;
592     tmp = realloc (state.str, len+1);
593     if (tmp == NULL) {
594       free (state.str);
595       *ret = NULL;
596       return -1;
597     }
598     *ret = tmp;
599     return len;
600   }
601 }
602 #endif
603 
604 #ifndef HAVE_VSNPRINTF
605 int
606 vsnprintf (char *str, size_t sz, const char *format, va_list args)
607 {
608   struct state state;
609   int ret;
610   unsigned char *ustr = (unsigned char *)str;
611 
612   state.max_sz = 0;
613   state.sz     = sz;
614   state.str    = ustr;
615   state.s      = ustr;
616   state.theend = ustr + sz - 1;
617   state.append_char = sn_append_char;
618   state.reserve     = sn_reserve;
619 
620   ret = xyzprintf (&state, format, args);
621   *state.s = '\0';
622   if (ret)
623     return sz;
624   else
625     return state.s - state.str;
626 }
627 #endif
628 
629