snprintf.c revision 72445
118730Ssteve/*
294589Sobrien * Copyright (c) 1995-2000 Kungliga Tekniska H�gskolan
350477Speter * (Royal Institute of Technology, Stockholm, Sweden).
41590Srgrimes * All rights reserved.
51590Srgrimes *
6143657Sharti * Redistribution and use in source and binary forms, with or without
7146056Sharti * modification, are permitted provided that the following conditions
8146574Sharti * are met:
9146574Sharti *
101590Srgrimes * 1. Redistributions of source code must retain the above copyright
11139112Sru *    notice, this list of conditions and the following disclaimer.
1276801Ssobomax *
13228157Sfjoe * 2. Redistributions in binary form must reproduce the above copyright
14228157Sfjoe *    notice, this list of conditions and the following disclaimer in the
15228157Sfjoe *    documentation and/or other materials provided with the distribution.
16134843Sjmg *
17104475Sphk * 3. Neither the name of the Institute nor the names of its contributors
18104475Sphk *    may be used to endorse or promote products derived from this software
19146608Sharti *    without specific prior written permission.
20146608Sharti *
21146608Sharti * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22146608Sharti * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23146608Sharti * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24146608Sharti * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25146579Sharti * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2697121Sru * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27145616Sharti * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28150598Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29145616Sharti * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30145616Sharti * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31145612Sharti * SUCH DAMAGE.
32145612Sharti */
33145612Sharti
34145616Sharti#ifdef HAVE_CONFIG_H
35145616Sharti#include <config.h>
36145612ShartiRCSID("$Id: snprintf.c,v 1.28 2000/12/15 14:04:42 joda Exp $");
37145612Sharti#endif
38145612Sharti#include <stdio.h>
39145612Sharti#include <stdarg.h>
40145612Sharti#include <stdlib.h>
41145616Sharti#include <string.h>
42145616Sharti#include <ctype.h>
43145612Sharti#include <roken.h>
44145612Sharti
45145612Shartienum format_flags {
46145612Sharti    minus_flag     =  1,
47145612Sharti    plus_flag      =  2,
48145612Sharti    space_flag     =  4,
49145616Sharti    alternate_flag =  8,
50145612Sharti    zero_flag      = 16
51145612Sharti};
52145612Sharti
53145614Sharti/*
54145612Sharti * Common state
55145612Sharti */
56145612Sharti
57145612Shartistruct state {
58145612Sharti  unsigned char *str;
59145612Sharti  unsigned char *s;
60145612Sharti  unsigned char *theend;
61145683Sharti  size_t sz;
62145612Sharti  size_t max_sz;
63145612Sharti  int (*append_char)(struct state *, unsigned char);
64145612Sharti  int (*reserve)(struct state *, size_t);
65145612Sharti  /* XXX - methods */
66145612Sharti};
67145612Sharti
68145612Sharti#ifndef HAVE_VSNPRINTF
69145612Shartistatic int
70145612Shartisn_reserve (struct state *state, size_t n)
71145612Sharti{
72145612Sharti  return state->s + n > state->theend;
73145612Sharti}
74145612Sharti
75145612Shartistatic int
76145612Shartisn_append_char (struct state *state, unsigned char c)
77145612Sharti{
78145612Sharti  if (sn_reserve (state, 1)) {
79145616Sharti    return 1;
80145616Sharti  } else {
81145616Sharti    *state->s++ = c;
82145616Sharti    return 0;
83145616Sharti  }
84145616Sharti}
85145616Sharti#endif
86145616Sharti
87145616Shartistatic int
88145616Shartias_reserve (struct state *state, size_t n)
89145616Sharti{
90145616Sharti  if (state->s + n > state->theend) {
91145616Sharti    int off = state->s - state->str;
92145616Sharti    unsigned char *tmp;
93145616Sharti
94145616Sharti    if (state->max_sz && state->sz >= state->max_sz)
95145616Sharti      return 1;
96145683Sharti
97145612Sharti    state->sz = max(state->sz * 2, state->sz + n);
9864739Sgreen    if (state->max_sz)
9964739Sgreen      state->sz = min(state->sz, state->max_sz);
10064739Sgreen    tmp = realloc (state->str, state->sz);
10164739Sgreen    if (tmp == NULL)
10264739Sgreen      return 1;
10364739Sgreen    state->str = tmp;
104146571Sharti    state->s = state->str + off;
105146571Sharti    state->theend = state->str + state->sz - 1;
10664739Sgreen  }
10764739Sgreen  return 0;
10864739Sgreen}
10964739Sgreen
1101590Srgrimesstatic int
111as_append_char (struct state *state, unsigned char c)
112{
113  if(as_reserve (state, 1))
114    return 1;
115  else {
116    *state->s++ = c;
117    return 0;
118  }
119}
120
121static int
122append_number(struct state *state,
123	      unsigned long num, unsigned base, char *rep,
124	      int width, int prec, int flags, int minusp)
125{
126  int len = 0;
127  int i;
128
129  /* given precision, ignore zero flag */
130  if(prec != -1)
131    flags &= ~zero_flag;
132  else
133    prec = 1;
134  /* zero value with zero precision -> "" */
135  if(prec == 0 && num == 0)
136    return 0;
137  do{
138    if((*state->append_char)(state, rep[num % base]))
139      return 1;
140    len++;
141    num /= base;
142  }while(num);
143  prec -= len;
144  /* pad with prec zeros */
145  while(prec-- > 0){
146    if((*state->append_char)(state, '0'))
147      return 1;
148    len++;
149  }
150  /* add length of alternate prefix (added later) to len */
151  if(flags & alternate_flag && (base == 16 || base == 8))
152    len += base / 8;
153  /* pad with zeros */
154  if(flags & zero_flag){
155    width -= len;
156    if(minusp || (flags & space_flag) || (flags & plus_flag))
157      width--;
158    while(width-- > 0){
159      if((*state->append_char)(state, '0'))
160	return 1;
161      len++;
162    }
163  }
164  /* add alternate prefix */
165  if(flags & alternate_flag && (base == 16 || base == 8)){
166    if(base == 16)
167      if((*state->append_char)(state, rep[10] + 23)) /* XXX */
168	return 1;
169    if((*state->append_char)(state, '0'))
170      return 1;
171  }
172  /* add sign */
173  if(minusp){
174    if((*state->append_char)(state, '-'))
175      return 1;
176    len++;
177  } else if(flags & plus_flag) {
178    if((*state->append_char)(state, '+'))
179      return 1;
180    len++;
181  } else if(flags & space_flag) {
182    if((*state->append_char)(state, ' '))
183      return 1;
184    len++;
185  }
186  if(flags & minus_flag)
187    /* swap before padding with spaces */
188    for(i = 0; i < len / 2; i++){
189      char c = state->s[-i-1];
190      state->s[-i-1] = state->s[-len+i];
191      state->s[-len+i] = c;
192    }
193  width -= len;
194  while(width-- > 0){
195    if((*state->append_char)(state,  ' '))
196      return 1;
197    len++;
198  }
199  if(!(flags & minus_flag))
200    /* swap after padding with spaces */
201    for(i = 0; i < len / 2; i++){
202      char c = state->s[-i-1];
203      state->s[-i-1] = state->s[-len+i];
204      state->s[-len+i] = c;
205    }
206
207  return 0;
208}
209
210static int
211append_string (struct state *state,
212	       unsigned char *arg,
213	       int width,
214	       int prec,
215	       int flags)
216{
217    if(arg == NULL)
218	arg = (unsigned char*)"(null)";
219
220    if(prec != -1)
221	width -= prec;
222    else
223	width -= strlen((char *)arg);
224    if(!(flags & minus_flag))
225	while(width-- > 0)
226	    if((*state->append_char) (state, ' '))
227		return 1;
228    if (prec != -1) {
229	while (*arg && prec--)
230	    if ((*state->append_char) (state, *arg++))
231		return 1;
232    } else {
233	while (*arg)
234	    if ((*state->append_char) (state, *arg++))
235		return 1;
236    }
237    if(flags & minus_flag)
238	while(width-- > 0)
239	    if((*state->append_char) (state, ' '))
240		return 1;
241    return 0;
242}
243
244static int
245append_char(struct state *state,
246	    unsigned char arg,
247	    int width,
248	    int flags)
249{
250  while(!(flags & minus_flag) && --width > 0)
251    if((*state->append_char) (state, ' '))
252      return 1;
253
254  if((*state->append_char) (state, arg))
255    return 1;
256  while((flags & minus_flag) && --width > 0)
257    if((*state->append_char) (state, ' '))
258      return 1;
259
260  return 0;
261}
262
263/*
264 * This can't be made into a function...
265 */
266
267#define PARSE_INT_FORMAT(res, arg, unsig) \
268if (long_flag) \
269     res = (unsig long)va_arg(arg, unsig long); \
270else if (short_flag) \
271     res = (unsig short)va_arg(arg, unsig int); \
272else \
273     res = (unsig int)va_arg(arg, unsig int)
274
275/*
276 * zyxprintf - return 0 or -1
277 */
278
279static int
280xyzprintf (struct state *state, const char *char_format, va_list ap)
281{
282  const unsigned char *format = (const unsigned char *)char_format;
283  unsigned char c;
284
285  while((c = *format++)) {
286    if (c == '%') {
287      int flags      = 0;
288      int width      = 0;
289      int prec       = -1;
290      int long_flag  = 0;
291      int short_flag = 0;
292
293      /* flags */
294      while((c = *format++)){
295	if(c == '-')
296	  flags |= minus_flag;
297	else if(c == '+')
298	  flags |= plus_flag;
299	else if(c == ' ')
300	  flags |= space_flag;
301	else if(c == '#')
302	  flags |= alternate_flag;
303	else if(c == '0')
304	  flags |= zero_flag;
305	else
306	  break;
307      }
308
309      if((flags & space_flag) && (flags & plus_flag))
310	flags ^= space_flag;
311
312      if((flags & minus_flag) && (flags & zero_flag))
313	flags ^= zero_flag;
314
315      /* width */
316      if (isdigit(c))
317	do {
318	  width = width * 10 + c - '0';
319	  c = *format++;
320	} while(isdigit(c));
321      else if(c == '*') {
322	width = va_arg(ap, int);
323	c = *format++;
324      }
325
326      /* precision */
327      if (c == '.') {
328	prec = 0;
329	c = *format++;
330	if (isdigit(c))
331	  do {
332	    prec = prec * 10 + c - '0';
333	    c = *format++;
334	  } while(isdigit(c));
335	else if (c == '*') {
336	  prec = va_arg(ap, int);
337	  c = *format++;
338	}
339      }
340
341      /* size */
342
343      if (c == 'h') {
344	short_flag = 1;
345	c = *format++;
346      } else if (c == 'l') {
347	long_flag = 1;
348	c = *format++;
349      }
350
351      switch (c) {
352      case 'c' :
353	if(append_char(state, va_arg(ap, int), width, flags))
354	  return -1;
355	break;
356      case 's' :
357	if (append_string(state,
358			  va_arg(ap, unsigned char*),
359			  width,
360			  prec,
361			  flags))
362	  return -1;
363	break;
364      case 'd' :
365      case 'i' : {
366	long arg;
367	unsigned long num;
368	int minusp = 0;
369
370	PARSE_INT_FORMAT(arg, ap, signed);
371
372	if (arg < 0) {
373	  minusp = 1;
374	  num = -arg;
375	} else
376	  num = arg;
377
378	if (append_number (state, num, 10, "0123456789",
379			   width, prec, flags, minusp))
380	  return -1;
381	break;
382      }
383      case 'u' : {
384	unsigned long arg;
385
386	PARSE_INT_FORMAT(arg, ap, unsigned);
387
388	if (append_number (state, arg, 10, "0123456789",
389			   width, prec, flags, 0))
390	  return -1;
391	break;
392      }
393      case 'o' : {
394	unsigned long arg;
395
396	PARSE_INT_FORMAT(arg, ap, unsigned);
397
398	if (append_number (state, arg, 010, "01234567",
399			   width, prec, flags, 0))
400	  return -1;
401	break;
402      }
403      case 'x' : {
404	unsigned long arg;
405
406	PARSE_INT_FORMAT(arg, ap, unsigned);
407
408	if (append_number (state, arg, 0x10, "0123456789abcdef",
409			   width, prec, flags, 0))
410	  return -1;
411	break;
412      }
413      case 'X' :{
414	unsigned long arg;
415
416	PARSE_INT_FORMAT(arg, ap, unsigned);
417
418	if (append_number (state, arg, 0x10, "0123456789ABCDEF",
419			   width, prec, flags, 0))
420	  return -1;
421	break;
422      }
423      case 'p' : {
424	unsigned long arg = (unsigned long)va_arg(ap, void*);
425
426	if (append_number (state, arg, 0x10, "0123456789ABCDEF",
427			   width, prec, flags, 0))
428	  return -1;
429	break;
430      }
431      case 'n' : {
432	int *arg = va_arg(ap, int*);
433	*arg = state->s - state->str;
434	break;
435      }
436      case '\0' :
437	  --format;
438	  /* FALLTHROUGH */
439      case '%' :
440	if ((*state->append_char)(state, c))
441	  return -1;
442	break;
443      default :
444	if (   (*state->append_char)(state, '%')
445	    || (*state->append_char)(state, c))
446	  return -1;
447	break;
448      }
449    } else
450      if ((*state->append_char) (state, c))
451	return -1;
452  }
453  return 0;
454}
455
456#ifndef HAVE_SNPRINTF
457int
458snprintf (char *str, size_t sz, const char *format, ...)
459{
460  va_list args;
461  int ret;
462
463  va_start(args, format);
464  ret = vsnprintf (str, sz, format, args);
465
466#ifdef PARANOIA
467  {
468    int ret2;
469    char *tmp;
470
471    tmp = malloc (sz);
472    if (tmp == NULL)
473      abort ();
474
475    ret2 = vsprintf (tmp, format, args);
476    if (ret != ret2 || strcmp(str, tmp))
477      abort ();
478    free (tmp);
479  }
480#endif
481
482  va_end(args);
483  return ret;
484}
485#endif
486
487#ifndef HAVE_ASPRINTF
488int
489asprintf (char **ret, const char *format, ...)
490{
491  va_list args;
492  int val;
493
494  va_start(args, format);
495  val = vasprintf (ret, format, args);
496
497#ifdef PARANOIA
498  {
499    int ret2;
500    char *tmp;
501    tmp = malloc (val + 1);
502    if (tmp == NULL)
503      abort ();
504
505    ret2 = vsprintf (tmp, format, args);
506    if (val != ret2 || strcmp(*ret, tmp))
507      abort ();
508    free (tmp);
509  }
510#endif
511
512  va_end(args);
513  return val;
514}
515#endif
516
517#ifndef HAVE_ASNPRINTF
518int
519asnprintf (char **ret, size_t max_sz, const char *format, ...)
520{
521  va_list args;
522  int val;
523
524  va_start(args, format);
525  val = vasnprintf (ret, max_sz, format, args);
526
527#ifdef PARANOIA
528  {
529    int ret2;
530    char *tmp;
531    tmp = malloc (val + 1);
532    if (tmp == NULL)
533      abort ();
534
535    ret2 = vsprintf (tmp, format, args);
536    if (val != ret2 || strcmp(*ret, tmp))
537      abort ();
538    free (tmp);
539  }
540#endif
541
542  va_end(args);
543  return val;
544}
545#endif
546
547#ifndef HAVE_VASPRINTF
548int
549vasprintf (char **ret, const char *format, va_list args)
550{
551  return vasnprintf (ret, 0, format, args);
552}
553#endif
554
555
556#ifndef HAVE_VASNPRINTF
557int
558vasnprintf (char **ret, size_t max_sz, const char *format, va_list args)
559{
560  int st;
561  size_t len;
562  struct state state;
563
564  state.max_sz = max_sz;
565  state.sz     = 1;
566  state.str    = malloc(state.sz);
567  if (state.str == NULL) {
568    *ret = NULL;
569    return -1;
570  }
571  state.s = state.str;
572  state.theend = state.s + state.sz - 1;
573  state.append_char = as_append_char;
574  state.reserve     = as_reserve;
575
576  st = xyzprintf (&state, format, args);
577  if (st) {
578    free (state.str);
579    *ret = NULL;
580    return -1;
581  } else {
582    char *tmp;
583
584    *state.s = '\0';
585    len = state.s - state.str;
586    tmp = realloc (state.str, len+1);
587    if (tmp == NULL) {
588      free (state.str);
589      *ret = NULL;
590      return -1;
591    }
592    *ret = tmp;
593    return len;
594  }
595}
596#endif
597
598#ifndef HAVE_VSNPRINTF
599int
600vsnprintf (char *str, size_t sz, const char *format, va_list args)
601{
602  struct state state;
603  int ret;
604  unsigned char *ustr = (unsigned char *)str;
605
606  state.max_sz = 0;
607  state.sz     = sz;
608  state.str    = ustr;
609  state.s      = ustr;
610  state.theend = ustr + sz - 1;
611  state.append_char = sn_append_char;
612  state.reserve     = sn_reserve;
613
614  ret = xyzprintf (&state, format, args);
615  *state.s = '\0';
616  if (ret)
617    return sz;
618  else
619    return state.s - state.str;
620}
621#endif
622
623