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