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