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
41static 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
54enum 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
66struct 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
78static int
79sn_reserve (struct state *state, size_t n)
80{
81  return state->s + n > state->theend;
82}
83
84static int
85sn_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
97static int
98as_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
120static int
121as_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
132static int
133append_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
221static int
222append_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
252static int
253append_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) \
276if (long_flag) \
277     res = (unsig long)va_arg(arg, unsig long); \
278else if (short_flag) \
279     res = (unsig short)va_arg(arg, unsig int); \
280else \
281     res = (unsig int)va_arg(arg, unsig int)
282
283/*
284 * zyxprintf - return 0 or -1
285 */
286
287static int
288xyzprintf (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
465int
466snprintf (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
497int
498asprintf (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
527int
528asnprintf (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
557int
558vasprintf (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
566int
567vasnprintf (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
609int
610vsnprintf (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