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