1 /*
2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
7  *
8  * See the COPYRIGHT file distributed with this work for additional
9  * information regarding copyright ownership.
10  */
11 
12 #include <config.h>
13 
14 #include <ctype.h>
15 #include <inttypes.h>
16 #include <stdio.h>		/* for sprintf */
17 #include <string.h>
18 
19 #define	LWRES__PRINT_SOURCE	/* Used to get the lwres_print_* prototypes. */
20 
21 #include <lwres/stdlib.h>
22 #include <lwres/string.h>
23 
24 #include "assert_p.h"
25 #include "print_p.h"
26 
27 int
lwres__print_sprintf(char * str,const char * format,...)28 lwres__print_sprintf(char *str, const char *format, ...) {
29 	va_list ap;
30 
31 	va_start(ap, format);
32 	vsprintf(str, format, ap);
33 	va_end(ap);
34 	return (strlen(str));
35 }
36 
37 /*
38  * Return length of string that would have been written if not truncated.
39  */
40 
41 int
lwres__print_snprintf(char * str,size_t size,const char * format,...)42 lwres__print_snprintf(char *str, size_t size, const char *format, ...) {
43 	va_list ap;
44 	int ret;
45 
46 	va_start(ap, format);
47 	ret = vsnprintf(str, size, format, ap);
48 	va_end(ap);
49 	return (ret);
50 
51 }
52 
53 /*
54  * Return length of string that would have been written if not truncated.
55  */
56 
57 int
lwres__print_vsnprintf(char * str,size_t size,const char * format,va_list ap)58 lwres__print_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
59 	int h;
60 	int l;
61 	int q;
62 	int z;
63 	int alt;
64 	int zero;
65 	int left;
66 	int plus;
67 	int space;
68 	long long tmpi;
69 	unsigned long long tmpui;
70 	unsigned long width;
71 	unsigned long precision;
72 	unsigned int length;
73 	char buf[1024];
74 	char c;
75 	void *v;
76 	char *save = str;
77 	const char *cp;
78 	const char *head;
79 	int count = 0;
80 	int pad;
81 	int zeropad;
82 	int dot;
83 	double dbl;
84 #ifdef HAVE_LONG_DOUBLE
85 	long double ldbl;
86 #endif
87 	char fmt[32];
88 
89 	INSIST(str != NULL);
90 	INSIST(format != NULL);
91 
92 	while (*format != '\0') {
93 		if (*format != '%') {
94 			if (size > 1U) {
95 				*str++ = *format;
96 				size--;
97 			}
98 			count++;
99 			format++;
100 			continue;
101 		}
102 		format++;
103 
104 		/*
105 		 * Reset flags.
106 		 */
107 		dot = space = plus = left = zero = alt = h = l = q = z = 0;
108 		width = precision = 0;
109 		head = "";
110 		length = pad = zeropad = 0;
111 		POST(length);
112 
113 		do {
114 			if (*format == '#') {
115 				alt = 1;
116 				format++;
117 			} else if (*format == '-') {
118 				left = 1;
119 				zero = 0;
120 				format++;
121 			} else if (*format == ' ') {
122 				if (!plus)
123 					space = 1;
124 				format++;
125 			} else if (*format == '+') {
126 				plus = 1;
127 				space = 0;
128 				format++;
129 			} else if (*format == '0') {
130 				if (!left)
131 					zero = 1;
132 				format++;
133 			} else
134 				break;
135 		} while (1);
136 
137 		/*
138 		 * Width.
139 		 */
140 		if (*format == '*') {
141 			width = va_arg(ap, int);
142 			format++;
143 		} else if (isdigit((unsigned char)*format)) {
144 			char *e;
145 			width = strtoul(format, &e, 10);
146 			format = e;
147 		}
148 
149 		/*
150 		 * Precision.
151 		 */
152 		if (*format == '.') {
153 			format++;
154 			dot = 1;
155 			if (*format == '*') {
156 				precision = va_arg(ap, int);
157 				format++;
158 			} else if (isdigit((unsigned char)*format)) {
159 				char *e;
160 				precision = strtoul(format, &e, 10);
161 				format = e;
162 			}
163 		}
164 
165 		switch (*format) {
166 		case '\0':
167 			continue;
168 		case '%':
169 			if (size > 1U) {
170 				*str++ = *format;
171 				size--;
172 			}
173 			count++;
174 			break;
175 		case 'q':
176 			q = 1;
177 			format++;
178 			goto doint;
179 		case 'h':
180 			h = 1;
181 			format++;
182 			goto doint;
183 		case 'l':
184 			l = 1;
185 			format++;
186 			if (*format == 'l') {
187 				q = 1;
188 				format++;
189 			}
190 			goto doint;
191 		case 'z':
192 			z = 1;
193 			format++;
194 			goto doint;
195 		case 'n':
196 		case 'i':
197 		case 'd':
198 		case 'o':
199 		case 'u':
200 		case 'x':
201 		case 'X':
202 		doint:
203 			if (precision != 0U)
204 				zero = 0;
205 			switch (*format) {
206 			case 'n':
207 				if (h) {
208 					short int *p;
209 					p = va_arg(ap, short *);
210 					REQUIRE(p != NULL);
211 					*p = str - save;
212 				} else if (l) {
213 					long int *p;
214 					p = va_arg(ap, long *);
215 					REQUIRE(p != NULL);
216 					*p = str - save;
217 				} else if (z) {
218 					size_t *p;
219 					p = va_arg(ap, size_t *);
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, long long int);
233 				else if (l)
234 					tmpi = va_arg(ap, long int);
235 				else if (z)
236 					tmpi = va_arg(ap, size_t);
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 				sprintf(buf, "%llu",
252 					tmpui);
253 				goto printint;
254 			case 'o':
255 				if (q)
256 					tmpui = va_arg(ap,
257 						       unsigned long long int);
258 				else if (l)
259 					tmpui = va_arg(ap, long int);
260 				else if (z)
261 					tmpui = va_arg(ap, size_t);
262 				else
263 					tmpui = va_arg(ap, int);
264 				sprintf(buf,
265 					alt ? "%#llo"
266 					    : "%llo",
267 					tmpui);
268 				goto printint;
269 			case 'u':
270 				if (q)
271 					tmpui = va_arg(ap,
272 						       unsigned long long int);
273 				else if (l)
274 					tmpui = va_arg(ap, unsigned long int);
275 				else if (z)
276 					tmpui = va_arg(ap, size_t);
277 				else
278 					tmpui = va_arg(ap, unsigned int);
279 				sprintf(buf, "%llu",
280 					tmpui);
281 				goto printint;
282 			case 'x':
283 				if (q)
284 					tmpui = va_arg(ap,
285 						       unsigned long long int);
286 				else if (l)
287 					tmpui = va_arg(ap, unsigned long int);
288 				else if (z)
289 					tmpui = va_arg(ap, size_t);
290 				else
291 					tmpui = va_arg(ap, unsigned int);
292 				if (alt) {
293 					head = "0x";
294 					if (precision > 2U)
295 						precision -= 2;
296 				}
297 				sprintf(buf, "%llx",
298 					tmpui);
299 				goto printint;
300 			case 'X':
301 				if (q)
302 					tmpui = va_arg(ap,
303 						       unsigned long long int);
304 				else if (l)
305 					tmpui = va_arg(ap, unsigned long int);
306 				else if (z)
307 					tmpui = va_arg(ap, size_t);
308 				else
309 					tmpui = va_arg(ap, unsigned int);
310 				if (alt) {
311 					head = "0X";
312 					if (precision > 2U)
313 						precision -= 2;
314 				}
315 				sprintf(buf, "%llX",
316 					tmpui);
317 				goto printint;
318 			printint:
319 				if (precision != 0U || width != 0U) {
320 					length = strlen(buf);
321 					if (length < precision)
322 						zeropad = precision - length;
323 					else if (length < width && zero)
324 						zeropad = width - length;
325 					if (width != 0U) {
326 						pad = width - length -
327 						      zeropad - strlen(head);
328 						if (pad < 0)
329 							pad = 0;
330 					}
331 				}
332 				count += strlen(head) + strlen(buf) + pad +
333 					 zeropad;
334 				if (!left) {
335 					while (pad > 0 && size > 1U) {
336 						*str++ = ' ';
337 						size--;
338 						pad--;
339 					}
340 				}
341 				cp = head;
342 				while (*cp != '\0' && size > 1U) {
343 					*str++ = *cp++;
344 					size--;
345 				}
346 				while (zeropad > 0 && size > 1U) {
347 					*str++ = '0';
348 					size--;
349 					zeropad--;
350 				}
351 				cp = buf;
352 				while (*cp != '\0' && size > 1U) {
353 					*str++ = *cp++;
354 					size--;
355 				}
356 				while (pad > 0 && size > 1U) {
357 					*str++ = ' ';
358 					size--;
359 					pad--;
360 				}
361 				break;
362 			default:
363 				break;
364 			}
365 			break;
366 		case 's':
367 			cp = va_arg(ap, char *);
368 			REQUIRE(cp != NULL);
369 
370 			if (precision != 0U) {
371 				/*
372 				 * cp need not be NULL terminated.
373 				 */
374 				const char *tp;
375 				unsigned long n;
376 
377 				n = precision;
378 				tp = cp;
379 				while (n != 0U && *tp != '\0')
380 					n--, tp++;
381 				length = precision - n;
382 			} else {
383 				length = strlen(cp);
384 			}
385 			if (width != 0U) {
386 				pad = width - length;
387 				if (pad < 0)
388 					pad = 0;
389 			}
390 			count += pad + length;
391 			if (!left)
392 				while (pad > 0 && size > 1U) {
393 					*str++ = ' ';
394 					size--;
395 					pad--;
396 				}
397 			if (precision != 0U)
398 				while (precision > 0U && *cp != '\0' &&
399 				       size > 1U) {
400 					*str++ = *cp++;
401 					size--;
402 					precision--;
403 				}
404 			else
405 				while (*cp != '\0' && size > 1U) {
406 					*str++ = *cp++;
407 					size--;
408 				}
409 			while (pad > 0 && size > 1U) {
410 				*str++ = ' ';
411 				size--;
412 				pad--;
413 			}
414 			break;
415 		case 'c':
416 			c = va_arg(ap, int);
417 			if (width > 0U) {
418 				count += width;
419 				width--;
420 				if (left) {
421 					*str++ = c;
422 					size--;
423 				}
424 				while (width-- > 0U && size > 1U) {
425 					*str++ = ' ';
426 					size--;
427 				}
428 				if (!left && size > 1U) {
429 					*str++ = c;
430 					size--;
431 				}
432 			} else {
433 				count++;
434 				if (size > 1U) {
435 					*str++ = c;
436 					size--;
437 				}
438 			}
439 			break;
440 		case 'p':
441 			v = va_arg(ap, void *);
442 			sprintf(buf, "%p", v);
443 			length = strlen(buf);
444 			if (precision > length)
445 				zeropad = precision - length;
446 			if (width > 0U) {
447 				pad = width - length - zeropad;
448 				if (pad < 0)
449 					pad = 0;
450 			}
451 			count += length + pad + zeropad;
452 			if (!left)
453 				while (pad > 0 && size > 1U) {
454 					*str++ = ' ';
455 					size--;
456 					pad--;
457 				}
458 			cp = buf;
459 			if (zeropad > 0 && buf[0] == '0' &&
460 			    (buf[1] == 'x' || buf[1] == 'X')) {
461 				if (size > 1U) {
462 					*str++ = *cp++;
463 					size--;
464 				}
465 				if (size > 1U) {
466 					*str++ = *cp++;
467 					size--;
468 				}
469 				while (zeropad > 0 && size > 1U) {
470 					*str++ = '0';
471 					size--;
472 					zeropad--;
473 				}
474 			}
475 			while (*cp != '\0' && size > 1U) {
476 				*str++ = *cp++;
477 				size--;
478 			}
479 			while (pad > 0 && size > 1U) {
480 				*str++ = ' ';
481 				size--;
482 				pad--;
483 			}
484 			break;
485 
486 		case 'D':	/*deprecated*/
487 			/* cppcheck-suppress literalWithCharPtrCompare */
488 			INSIST("use %ld instead of %D" == NULL);
489 			break;
490 		case 'O':	/*deprecated*/
491 			/* cppcheck-suppress literalWithCharPtrCompare */
492 			INSIST("use %lo instead of %O" == NULL);
493 			break;
494 		case 'U':	/*deprecated*/
495 			/* cppcheck-suppress literalWithCharPtrCompare */
496 			INSIST("use %lu instead of %U" == NULL);
497 			break;
498 
499 		case 'L':
500 #ifdef HAVE_LONG_DOUBLE
501 			l = 1;
502 #else
503 			/* cppcheck-suppress literalWithCharPtrCompare */
504 			INSIST("long doubles are not supported" == NULL);
505 #endif
506 			/* FALLTHROUGH */
507 		case 'e':
508 		case 'E':
509 		case 'f':
510 		case 'g':
511 		case 'G':
512 			if (!dot)
513 				precision = 6;
514 			/*
515 			 * IEEE floating point.
516 			 * MIN 2.2250738585072014E-308
517 			 * MAX 1.7976931348623157E+308
518 			 * VAX floating point has a smaller range than IEEE.
519 			 *
520 			 * precisions > 324 don't make much sense.
521 			 * if we cap the precision at 512 we will not
522 			 * overflow buf.
523 			 */
524 			if (precision > 512U)
525 				precision = 512;
526 			sprintf(fmt, "%%%s%s.%lu%s%c", alt ? "#" : "",
527 				plus ? "+" : space ? " " : "",
528 				precision, l ? "L" : "", *format);
529 			switch (*format) {
530 			case 'e':
531 			case 'E':
532 			case 'f':
533 			case 'g':
534 			case 'G':
535 #ifdef HAVE_LONG_DOUBLE
536 				if (l) {
537 					ldbl = va_arg(ap, long double);
538 					sprintf(buf, fmt, ldbl);
539 				} else
540 #endif
541 				{
542 					dbl = va_arg(ap, double);
543 					sprintf(buf, fmt, dbl);
544 				}
545 				length = strlen(buf);
546 				if (width > 0U) {
547 					pad = width - length;
548 					if (pad < 0)
549 						pad = 0;
550 				}
551 				count += length + pad;
552 				if (!left)
553 					while (pad > 0 && size > 1U) {
554 						*str++ = ' ';
555 						size--;
556 						pad--;
557 					}
558 				cp = buf;
559 				while (*cp != ' ' && size > 1U) {
560 					*str++ = *cp++;
561 					size--;
562 				}
563 				while (pad > 0 && size > 1U) {
564 					*str++ = ' ';
565 					size--;
566 					pad--;
567 				}
568 				break;
569 			default:
570 				continue;
571 			}
572 			break;
573 		default:
574 			continue;
575 		}
576 		format++;
577 	}
578 	if (size > 0U)
579 		*str = '\0';
580 	return (count);
581 }
582