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
41 static 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
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
sn_reserve(struct state * state,size_t n)79 sn_reserve (struct state *state, size_t n)
80 {
81 return state->s + n > state->theend;
82 }
83
84 static int
sn_append_char(struct state * state,unsigned char c)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 #if 0
97 static int
98 as_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
120 static int
121 as_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
132 static int
append_number(struct state * state,unsigned long num,unsigned base,char * rep,int width,int prec,int flags,int minusp)133 append_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
221 static int
append_string(struct state * state,unsigned char * arg,int width,int prec,int flags)222 append_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
252 static int
append_char(struct state * state,unsigned char arg,int width,int flags)253 append_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) \
276 if (long_flag) \
277 res = (unsig long)va_arg(arg, unsig long); \
278 else if (short_flag) \
279 res = (unsig short)va_arg(arg, unsig int); \
280 else \
281 res = (unsig int)va_arg(arg, unsig int)
282
283 /*
284 * zyxprintf - return 0 or -1
285 */
286
287 static int
xyzprintf(struct state * state,const char * char_format,va_list ap)288 xyzprintf (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
465 int
snprintf(char * str,size_t sz,const char * format,...)466 snprintf (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
497 int
498 asprintf (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
527 int
528 asnprintf (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
557 int
558 vasprintf (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
566 int
567 vasnprintf (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
609 int
vsnprintf(char * str,size_t sz,const char * format,va_list args)610 vsnprintf (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