1/*	$NetBSD$	*/
2
3/*
4 * Copyright (C) 2004, 2005, 2007, 2011, 2012  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 */
21
22#include <config.h>
23
24#include <ctype.h>
25#include <stdio.h>		/* for sprintf */
26#include <string.h>
27
28#define	LWRES__PRINT_SOURCE	/* Used to get the lwres_print_* prototypes. */
29
30#include <lwres/stdlib.h>
31
32#include "assert_p.h"
33#include "print_p.h"
34
35#define LWRES_PRINT_QUADFORMAT LWRES_PLATFORM_QUADFORMAT
36
37int
38lwres__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
52lwres__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
68lwres__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	long long tmpi;
78	unsigned long long tmpui;
79	unsigned long width;
80	unsigned long precision;
81	unsigned int length;
82	char buf[1024];
83	char c;
84	void *v;
85	char *save = str;
86	const char *cp;
87	const char *head;
88	int count = 0;
89	int pad;
90	int zeropad;
91	int dot;
92	double dbl;
93#ifdef HAVE_LONG_DOUBLE
94	long double ldbl;
95#endif
96	char fmt[32];
97
98	INSIST(str != NULL);
99	INSIST(format != NULL);
100
101	while (*format != '\0') {
102		if (*format != '%') {
103			if (size > 1U) {
104				*str++ = *format;
105				size--;
106			}
107			count++;
108			format++;
109			continue;
110		}
111		format++;
112
113		/*
114		 * Reset flags.
115		 */
116		dot = space = plus = left = zero = alt = h = l = q = 0;
117		width = precision = 0;
118		head = "";
119		length = pad = zeropad = 0;
120		POST(length);
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 > 1U) {
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 != 0U)
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, long long int);
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				sprintf(buf, "%" LWRES_PRINT_QUADFORMAT "u",
250					tmpui);
251				goto printint;
252			case 'o':
253				if (q)
254					tmpui = va_arg(ap,
255						       unsigned long long int);
256				else if (l)
257					tmpui = va_arg(ap, long int);
258				else
259					tmpui = va_arg(ap, int);
260				sprintf(buf,
261					alt ? "%#" LWRES_PRINT_QUADFORMAT "o"
262					    : "%" LWRES_PRINT_QUADFORMAT "o",
263					tmpui);
264				goto printint;
265			case 'u':
266				if (q)
267					tmpui = va_arg(ap,
268						       unsigned long long int);
269				else if (l)
270					tmpui = va_arg(ap, unsigned long int);
271				else
272					tmpui = va_arg(ap, unsigned int);
273				sprintf(buf, "%" LWRES_PRINT_QUADFORMAT "u",
274					tmpui);
275				goto printint;
276			case 'x':
277				if (q)
278					tmpui = va_arg(ap,
279						       unsigned long long int);
280				else if (l)
281					tmpui = va_arg(ap, unsigned long int);
282				else
283					tmpui = va_arg(ap, unsigned int);
284				if (alt) {
285					head = "0x";
286					if (precision > 2U)
287						precision -= 2;
288				}
289				sprintf(buf, "%" LWRES_PRINT_QUADFORMAT "x",
290					tmpui);
291				goto printint;
292			case 'X':
293				if (q)
294					tmpui = va_arg(ap,
295						       unsigned long long int);
296				else if (l)
297					tmpui = va_arg(ap, unsigned long int);
298				else
299					tmpui = va_arg(ap, unsigned int);
300				if (alt) {
301					head = "0X";
302					if (precision > 2U)
303						precision -= 2;
304				}
305				sprintf(buf, "%" LWRES_PRINT_QUADFORMAT "X",
306					tmpui);
307				goto printint;
308			printint:
309				if (precision != 0U || width != 0U) {
310					length = strlen(buf);
311					if (length < precision)
312						zeropad = precision - length;
313					else if (length < width && zero)
314						zeropad = width - length;
315					if (width != 0U) {
316						pad = width - length -
317						      zeropad - strlen(head);
318						if (pad < 0)
319							pad = 0;
320					}
321				}
322				count += strlen(head) + strlen(buf) + pad +
323					 zeropad;
324				if (!left) {
325					while (pad > 0 && size > 1U) {
326						*str++ = ' ';
327						size--;
328						pad--;
329					}
330				}
331				cp = head;
332				while (*cp != '\0' && size > 1U) {
333					*str++ = *cp++;
334					size--;
335				}
336				while (zeropad > 0 && size > 1U) {
337					*str++ = '0';
338					size--;
339					zeropad--;
340				}
341				cp = buf;
342				while (*cp != '\0' && size > 1U) {
343					*str++ = *cp++;
344					size--;
345				}
346				while (pad > 0 && size > 1U) {
347					*str++ = ' ';
348					size--;
349					pad--;
350				}
351				break;
352			default:
353				break;
354			}
355			break;
356		case 's':
357			cp = va_arg(ap, char *);
358			REQUIRE(cp != NULL);
359
360			if (precision != 0U) {
361				/*
362				 * cp need not be NULL terminated.
363				 */
364				const char *tp;
365				unsigned long n;
366
367				n = precision;
368				tp = cp;
369				while (n != 0U && *tp != '\0')
370					n--, tp++;
371				length = precision - n;
372			} else {
373				length = strlen(cp);
374			}
375			if (width != 0U) {
376				pad = width - length;
377				if (pad < 0)
378					pad = 0;
379			}
380			count += pad + length;
381			if (!left)
382				while (pad > 0 && size > 1U) {
383					*str++ = ' ';
384					size--;
385					pad--;
386				}
387			if (precision != 0U)
388				while (precision > 0U && *cp != '\0' &&
389				       size > 1U) {
390					*str++ = *cp++;
391					size--;
392					precision--;
393				}
394			else
395				while (*cp != '\0' && size > 1U) {
396					*str++ = *cp++;
397					size--;
398				}
399			while (pad > 0 && size > 1U) {
400				*str++ = ' ';
401				size--;
402				pad--;
403			}
404			break;
405		case 'c':
406			c = va_arg(ap, int);
407			if (width > 0U) {
408				count += width;
409				width--;
410				if (left) {
411					*str++ = c;
412					size--;
413				}
414				while (width-- > 0U && size > 1U) {
415					*str++ = ' ';
416					size--;
417				}
418				if (!left && size > 1U) {
419					*str++ = c;
420					size--;
421				}
422			} else {
423				count++;
424				if (size > 1U) {
425					*str++ = c;
426					size--;
427				}
428			}
429			break;
430		case 'p':
431			v = va_arg(ap, void *);
432			sprintf(buf, "%p", v);
433			length = strlen(buf);
434			if (precision > length)
435				zeropad = precision - length;
436			if (width > 0U) {
437				pad = width - length - zeropad;
438				if (pad < 0)
439					pad = 0;
440			}
441			count += length + pad + zeropad;
442			if (!left)
443				while (pad > 0 && size > 1U) {
444					*str++ = ' ';
445					size--;
446					pad--;
447				}
448			cp = buf;
449			if (zeropad > 0 && buf[0] == '0' &&
450			    (buf[1] == 'x' || buf[1] == 'X')) {
451				if (size > 1U) {
452					*str++ = *cp++;
453					size--;
454				}
455				if (size > 1U) {
456					*str++ = *cp++;
457					size--;
458				}
459				while (zeropad > 0 && size > 1U) {
460					*str++ = '0';
461					size--;
462					zeropad--;
463				}
464			}
465			while (*cp != '\0' && size > 1U) {
466				*str++ = *cp++;
467				size--;
468			}
469			while (pad > 0 && size > 1U) {
470				*str++ = ' ';
471				size--;
472				pad--;
473			}
474			break;
475		case 'D':	/*deprecated*/
476			INSIST("use %ld instead of %D" == NULL);
477		case 'O':	/*deprecated*/
478			INSIST("use %lo instead of %O" == NULL);
479		case 'U':	/*deprecated*/
480			INSIST("use %lu instead of %U" == NULL);
481
482		case 'L':
483#ifdef HAVE_LONG_DOUBLE
484			l = 1;
485#else
486			INSIST("long doubles are not supported" == NULL);
487#endif
488			/*FALLTHROUGH*/
489		case 'e':
490		case 'E':
491		case 'f':
492		case 'g':
493		case 'G':
494			if (!dot)
495				precision = 6;
496			/*
497			 * IEEE floating point.
498			 * MIN 2.2250738585072014E-308
499			 * MAX 1.7976931348623157E+308
500			 * VAX floating point has a smaller range than IEEE.
501			 *
502			 * precisions > 324 don't make much sense.
503			 * if we cap the precision at 512 we will not
504			 * overflow buf.
505			 */
506			if (precision > 512U)
507				precision = 512;
508			sprintf(fmt, "%%%s%s.%lu%s%c", alt ? "#" : "",
509				plus ? "+" : space ? " " : "",
510				precision, l ? "L" : "", *format);
511			switch (*format) {
512			case 'e':
513			case 'E':
514			case 'f':
515			case 'g':
516			case 'G':
517#ifdef HAVE_LONG_DOUBLE
518				if (l) {
519					ldbl = va_arg(ap, long double);
520					sprintf(buf, fmt, ldbl);
521				} else
522#endif
523				{
524					dbl = va_arg(ap, double);
525					sprintf(buf, fmt, dbl);
526				}
527				length = strlen(buf);
528				if (width > 0U) {
529					pad = width - length;
530					if (pad < 0)
531						pad = 0;
532				}
533				count += length + pad;
534				if (!left)
535					while (pad > 0 && size > 1U) {
536						*str++ = ' ';
537						size--;
538						pad--;
539					}
540				cp = buf;
541				while (*cp != ' ' && size > 1U) {
542					*str++ = *cp++;
543					size--;
544				}
545				while (pad > 0 && size > 1U) {
546					*str++ = ' ';
547					size--;
548					pad--;
549				}
550				break;
551			default:
552				continue;
553			}
554			break;
555		default:
556			continue;
557		}
558		format++;
559	}
560	if (size > 0U)
561		*str = '\0';
562	return (count);
563}
564