1 /*
2 * Copyright (C) 2004-2008, 2010 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 1999-2001, 2003 Internet Software Consortium.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 /* $Id: print.c,v 1.37 2010/10/18 23:47:08 tbox Exp $ */
19
20 /*! \file */
21
22 #include <config.h>
23
24 #include <ctype.h>
25 #include <stdio.h> /* for sprintf() */
26 #include <string.h> /* for strlen() */
27
28 #define ISC__PRINT_SOURCE /* Used to get the isc_print_* prototypes. */
29
30 #include <isc/assertions.h>
31 #include <isc/int.h>
32 #include <isc/msgs.h>
33 #include <isc/print.h>
34 #include <isc/stdlib.h>
35 #include <isc/util.h>
36
37 int
isc_print_sprintf(char * str,const char * format,...)38 isc_print_sprintf(char *str, const char *format, ...) {
39 va_list ap;
40
41 va_start(ap, format);
42 vsprintf(str, format, ap);
43 va_end(ap);
44 return (strlen(str));
45 }
46
47 /*!
48 * Return length of string that would have been written if not truncated.
49 */
50
51 int
isc_print_snprintf(char * str,size_t size,const char * format,...)52 isc_print_snprintf(char *str, size_t size, const char *format, ...) {
53 va_list ap;
54 int ret;
55
56 va_start(ap, format);
57 ret = vsnprintf(str, size, format, ap);
58 va_end(ap);
59 return (ret);
60
61 }
62
63 /*!
64 * Return length of string that would have been written if not truncated.
65 */
66
67 int
isc_print_vsnprintf(char * str,size_t size,const char * format,va_list ap)68 isc_print_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
69 int h;
70 int l;
71 int q;
72 int alt;
73 int zero;
74 int left;
75 int plus;
76 int space;
77 int neg;
78 isc_int64_t tmpi;
79 isc_uint64_t tmpui;
80 unsigned long width;
81 unsigned long precision;
82 unsigned int length;
83 char buf[1024];
84 char c;
85 void *v;
86 char *save = str;
87 const char *cp;
88 const char *head;
89 int count = 0;
90 int pad;
91 int zeropad;
92 int dot;
93 double dbl;
94 #ifdef HAVE_LONG_DOUBLE
95 long double ldbl;
96 #endif
97 char fmt[32];
98
99 INSIST(str != NULL);
100 INSIST(format != NULL);
101
102 while (*format != '\0') {
103 if (*format != '%') {
104 if (size > 1) {
105 *str++ = *format;
106 size--;
107 }
108 count++;
109 format++;
110 continue;
111 }
112 format++;
113
114 /*
115 * Reset flags.
116 */
117 dot = neg = space = plus = left = zero = alt = h = l = q = 0;
118 width = precision = 0;
119 head = "";
120 length = pad = zeropad = 0;
121
122 do {
123 if (*format == '#') {
124 alt = 1;
125 format++;
126 } else if (*format == '-') {
127 left = 1;
128 zero = 0;
129 format++;
130 } else if (*format == ' ') {
131 if (!plus)
132 space = 1;
133 format++;
134 } else if (*format == '+') {
135 plus = 1;
136 space = 0;
137 format++;
138 } else if (*format == '0') {
139 if (!left)
140 zero = 1;
141 format++;
142 } else
143 break;
144 } while (1);
145
146 /*
147 * Width.
148 */
149 if (*format == '*') {
150 width = va_arg(ap, int);
151 format++;
152 } else if (isdigit((unsigned char)*format)) {
153 char *e;
154 width = strtoul(format, &e, 10);
155 format = e;
156 }
157
158 /*
159 * Precision.
160 */
161 if (*format == '.') {
162 format++;
163 dot = 1;
164 if (*format == '*') {
165 precision = va_arg(ap, int);
166 format++;
167 } else if (isdigit((unsigned char)*format)) {
168 char *e;
169 precision = strtoul(format, &e, 10);
170 format = e;
171 }
172 }
173
174 switch (*format) {
175 case '\0':
176 continue;
177 case '%':
178 if (size > 1) {
179 *str++ = *format;
180 size--;
181 }
182 count++;
183 break;
184 case 'q':
185 q = 1;
186 format++;
187 goto doint;
188 case 'h':
189 h = 1;
190 format++;
191 goto doint;
192 case 'l':
193 l = 1;
194 format++;
195 if (*format == 'l') {
196 q = 1;
197 format++;
198 }
199 goto doint;
200 case 'n':
201 case 'i':
202 case 'd':
203 case 'o':
204 case 'u':
205 case 'x':
206 case 'X':
207 doint:
208 if (precision != 0)
209 zero = 0;
210 switch (*format) {
211 case 'n':
212 if (h) {
213 short int *p;
214 p = va_arg(ap, short *);
215 REQUIRE(p != NULL);
216 *p = str - save;
217 } else if (l) {
218 long int *p;
219 p = va_arg(ap, long *);
220 REQUIRE(p != NULL);
221 *p = str - save;
222 } else {
223 int *p;
224 p = va_arg(ap, int *);
225 REQUIRE(p != NULL);
226 *p = str - save;
227 }
228 break;
229 case 'i':
230 case 'd':
231 if (q)
232 tmpi = va_arg(ap, isc_int64_t);
233 else if (l)
234 tmpi = va_arg(ap, long int);
235 else
236 tmpi = va_arg(ap, int);
237 if (tmpi < 0) {
238 head = "-";
239 tmpui = -tmpi;
240 } else {
241 if (plus)
242 head = "+";
243 else if (space)
244 head = " ";
245 else
246 head = "";
247 tmpui = tmpi;
248 }
249 if (tmpui <= 0xffffffffU)
250 sprintf(buf, "%lu",
251 (unsigned long)tmpui);
252 else {
253 unsigned long mid;
254 unsigned long lo;
255 unsigned long hi;
256 lo = tmpui % 1000000000;
257 tmpui /= 1000000000;
258 mid = tmpui % 1000000000;
259 hi = tmpui / 1000000000;
260 if (hi != 0)
261 sprintf(buf, "%lu", hi);
262 else
263 buf[0] = '\n';
264 sprintf(buf + strlen(buf), "%lu", mid);
265 sprintf(buf + strlen(buf), "%lu", lo);
266 }
267 goto printint;
268 case 'o':
269 if (q)
270 tmpui = va_arg(ap, isc_uint64_t);
271 else if (l)
272 tmpui = va_arg(ap, long int);
273 else
274 tmpui = va_arg(ap, int);
275 if (tmpui <= 0xffffffffU)
276 sprintf(buf, alt ? "%#lo" : "%lo",
277 (unsigned long)tmpui);
278 else {
279 unsigned long mid;
280 unsigned long lo;
281 unsigned long hi;
282 lo = tmpui % 010000000000;
283 tmpui /= 010000000000;
284 mid = tmpui % 010000000000;
285 hi = tmpui / 010000000000;
286 if (hi != 0) {
287 sprintf(buf,
288 alt ? "%#lo" : "%lo",
289 hi);
290 sprintf(buf + strlen(buf),
291 "%lo", mid);
292 } else
293 sprintf(buf,
294 alt ? "%#lo" : "%lo",
295 mid);
296 sprintf(buf + strlen(buf), "%lo", lo);
297 }
298 goto printint;
299 case 'u':
300 if (q)
301 tmpui = va_arg(ap, isc_uint64_t);
302 else if (l)
303 tmpui = va_arg(ap, unsigned long int);
304 else
305 tmpui = va_arg(ap, unsigned int);
306 if (tmpui <= 0xffffffffU)
307 sprintf(buf, "%lu",
308 (unsigned long)tmpui);
309 else {
310 unsigned long mid;
311 unsigned long lo;
312 unsigned long hi;
313 lo = tmpui % 1000000000;
314 tmpui /= 1000000000;
315 mid = tmpui % 1000000000;
316 hi = tmpui / 1000000000;
317 if (hi != 0)
318 sprintf(buf, "%lu", hi);
319 else
320 buf[0] = '\n';
321 sprintf(buf + strlen(buf), "%lu", mid);
322 sprintf(buf + strlen(buf), "%lu", lo);
323 }
324 goto printint;
325 case 'x':
326 if (q)
327 tmpui = va_arg(ap, isc_uint64_t);
328 else if (l)
329 tmpui = va_arg(ap, unsigned long int);
330 else
331 tmpui = va_arg(ap, unsigned int);
332 if (alt) {
333 head = "0x";
334 if (precision > 2)
335 precision -= 2;
336 }
337 if (tmpui <= 0xffffffffU)
338 sprintf(buf, "%lx",
339 (unsigned long)tmpui);
340 else {
341 unsigned long hi = tmpui>>32;
342 unsigned long lo = tmpui & 0xffffffff;
343 sprintf(buf, "%lx", hi);
344 sprintf(buf + strlen(buf), "%lx", lo);
345 }
346 goto printint;
347 case 'X':
348 if (q)
349 tmpui = va_arg(ap, isc_uint64_t);
350 else if (l)
351 tmpui = va_arg(ap, unsigned long int);
352 else
353 tmpui = va_arg(ap, unsigned int);
354 if (alt) {
355 head = "0X";
356 if (precision > 2)
357 precision -= 2;
358 }
359 if (tmpui <= 0xffffffffU)
360 sprintf(buf, "%lX",
361 (unsigned long)tmpui);
362 else {
363 unsigned long hi = tmpui>>32;
364 unsigned long lo = tmpui & 0xffffffff;
365 sprintf(buf, "%lX", hi);
366 sprintf(buf + strlen(buf), "%lX", lo);
367 }
368 goto printint;
369 printint:
370 if (precision != 0 || width != 0) {
371 length = strlen(buf);
372 if (length < precision)
373 zeropad = precision - length;
374 else if (length < width && zero)
375 zeropad = width - length;
376 if (width != 0) {
377 pad = width - length -
378 zeropad - strlen(head);
379 if (pad < 0)
380 pad = 0;
381 }
382 }
383 count += strlen(head) + strlen(buf) + pad +
384 zeropad;
385 if (!left) {
386 while (pad > 0 && size > 1) {
387 *str++ = ' ';
388 size--;
389 pad--;
390 }
391 }
392 cp = head;
393 while (*cp != '\0' && size > 1) {
394 *str++ = *cp++;
395 size--;
396 }
397 while (zeropad > 0 && size > 1) {
398 *str++ = '0';
399 size--;
400 zeropad--;
401 }
402 cp = buf;
403 while (*cp != '\0' && size > 1) {
404 *str++ = *cp++;
405 size--;
406 }
407 while (pad > 0 && size > 1) {
408 *str++ = ' ';
409 size--;
410 pad--;
411 }
412 break;
413 default:
414 break;
415 }
416 break;
417 case 's':
418 cp = va_arg(ap, char *);
419 REQUIRE(cp != NULL);
420
421 if (precision != 0) {
422 /*
423 * cp need not be NULL terminated.
424 */
425 const char *tp;
426 unsigned long n;
427
428 n = precision;
429 tp = cp;
430 while (n != 0 && *tp != '\0')
431 n--, tp++;
432 length = precision - n;
433 } else {
434 length = strlen(cp);
435 }
436 if (width != 0) {
437 pad = width - length;
438 if (pad < 0)
439 pad = 0;
440 }
441 count += pad + length;
442 if (!left)
443 while (pad > 0 && size > 1) {
444 *str++ = ' ';
445 size--;
446 pad--;
447 }
448 if (precision != 0)
449 while (precision > 0 && *cp != '\0' &&
450 size > 1) {
451 *str++ = *cp++;
452 size--;
453 precision--;
454 }
455 else
456 while (*cp != '\0' && size > 1) {
457 *str++ = *cp++;
458 size--;
459 }
460 while (pad > 0 && size > 1) {
461 *str++ = ' ';
462 size--;
463 pad--;
464 }
465 break;
466 case 'c':
467 c = va_arg(ap, int);
468 if (width > 0) {
469 count += width;
470 width--;
471 if (left && size > 1) {
472 *str++ = c;
473 size--;
474 }
475 while (width-- > 0 && size > 1) {
476 *str++ = ' ';
477 size--;
478 }
479 if (!left && size > 1) {
480 *str++ = c;
481 size--;
482 }
483 } else {
484 count++;
485 if (size > 1) {
486 *str++ = c;
487 size--;
488 }
489 }
490 break;
491 case 'p':
492 v = va_arg(ap, void *);
493 sprintf(buf, "%p", v);
494 length = strlen(buf);
495 if (precision > length)
496 zeropad = precision - length;
497 if (width > 0) {
498 pad = width - length - zeropad;
499 if (pad < 0)
500 pad = 0;
501 }
502 count += length + pad + zeropad;
503 if (!left)
504 while (pad > 0 && size > 1) {
505 *str++ = ' ';
506 size--;
507 pad--;
508 }
509 cp = buf;
510 if (zeropad > 0 && buf[0] == '0' &&
511 (buf[1] == 'x' || buf[1] == 'X')) {
512 if (size > 1) {
513 *str++ = *cp++;
514 size--;
515 }
516 if (size > 1) {
517 *str++ = *cp++;
518 size--;
519 }
520 while (zeropad > 0 && size > 1) {
521 *str++ = '0';
522 size--;
523 zeropad--;
524 }
525 }
526 while (*cp != '\0' && size > 1) {
527 *str++ = *cp++;
528 size--;
529 }
530 while (pad > 0 && size > 1) {
531 *str++ = ' ';
532 size--;
533 pad--;
534 }
535 break;
536 case 'D': /*deprecated*/
537 INSIST("use %ld instead of %D" == NULL);
538 case 'O': /*deprecated*/
539 INSIST("use %lo instead of %O" == NULL);
540 case 'U': /*deprecated*/
541 INSIST("use %lu instead of %U" == NULL);
542
543 case 'L':
544 #ifdef HAVE_LONG_DOUBLE
545 l = 1;
546 #else
547 INSIST("long doubles are not supported" == NULL);
548 #endif
549 /*FALLTHROUGH*/
550 case 'e':
551 case 'E':
552 case 'f':
553 case 'g':
554 case 'G':
555 if (!dot)
556 precision = 6;
557 /*
558 * IEEE floating point.
559 * MIN 2.2250738585072014E-308
560 * MAX 1.7976931348623157E+308
561 * VAX floating point has a smaller range than IEEE.
562 *
563 * precisions > 324 don't make much sense.
564 * if we cap the precision at 512 we will not
565 * overflow buf.
566 */
567 if (precision > 512)
568 precision = 512;
569 sprintf(fmt, "%%%s%s.%lu%s%c", alt ? "#" : "",
570 plus ? "+" : space ? " " : "",
571 precision, l ? "L" : "", *format);
572 switch (*format) {
573 case 'e':
574 case 'E':
575 case 'f':
576 case 'g':
577 case 'G':
578 #ifdef HAVE_LONG_DOUBLE
579 if (l) {
580 ldbl = va_arg(ap, long double);
581 sprintf(buf, fmt, ldbl);
582 } else
583 #endif
584 {
585 dbl = va_arg(ap, double);
586 sprintf(buf, fmt, dbl);
587 }
588 length = strlen(buf);
589 if (width > 0) {
590 pad = width - length;
591 if (pad < 0)
592 pad = 0;
593 }
594 count += length + pad;
595 if (!left)
596 while (pad > 0 && size > 1) {
597 *str++ = ' ';
598 size--;
599 pad--;
600 }
601 cp = buf;
602 while (*cp != ' ' && size > 1) {
603 *str++ = *cp++;
604 size--;
605 }
606 while (pad > 0 && size > 1) {
607 *str++ = ' ';
608 size--;
609 pad--;
610 }
611 break;
612 default:
613 continue;
614 }
615 break;
616 default:
617 continue;
618 }
619 format++;
620 }
621 if (size > 0)
622 *str = '\0';
623 return (count);
624 }
625