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