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
37int
38isc_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
51int
52isc_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
67int
68isc_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