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