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