1135446Strhodes/*
2234010Sdougb * Copyright (C) 2004, 2005, 2007, 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
3135446Strhodes * Copyright (C) 1999-2001, 2003  Internet Software Consortium.
4135446Strhodes *
5193149Sdougb * Permission to use, copy, modify, and/or distribute this software for any
6135446Strhodes * purpose with or without fee is hereby granted, provided that the above
7135446Strhodes * copyright notice and this permission notice appear in all copies.
8135446Strhodes *
9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11135446Strhodes * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15135446Strhodes * PERFORMANCE OF THIS SOFTWARE.
16135446Strhodes */
17135446Strhodes
18234010Sdougb/* $Id$ */
19135446Strhodes
20135446Strhodes#include <config.h>
21135446Strhodes
22135446Strhodes#include <ctype.h>
23135446Strhodes#include <stdio.h>		/* for sprintf */
24143731Sdougb#include <string.h>
25135446Strhodes
26135446Strhodes#define	LWRES__PRINT_SOURCE	/* Used to get the lwres_print_* prototypes. */
27135446Strhodes
28153816Sdougb#include <lwres/stdlib.h>
29135446Strhodes
30135446Strhodes#include "assert_p.h"
31135446Strhodes#include "print_p.h"
32135446Strhodes
33153816Sdougb#define LWRES_PRINT_QUADFORMAT LWRES_PLATFORM_QUADFORMAT
34153816Sdougb
35135446Strhodesint
36135446Strhodeslwres__print_sprintf(char *str, const char *format, ...) {
37135446Strhodes	va_list ap;
38135446Strhodes
39135446Strhodes	va_start(ap, format);
40135446Strhodes	vsprintf(str, format, ap);
41135446Strhodes	va_end(ap);
42135446Strhodes	return (strlen(str));
43135446Strhodes}
44135446Strhodes
45135446Strhodes/*
46135446Strhodes * Return length of string that would have been written if not truncated.
47135446Strhodes */
48135446Strhodes
49135446Strhodesint
50135446Strhodeslwres__print_snprintf(char *str, size_t size, const char *format, ...) {
51135446Strhodes	va_list ap;
52135446Strhodes	int ret;
53135446Strhodes
54135446Strhodes	va_start(ap, format);
55135446Strhodes	ret = vsnprintf(str, size, format, ap);
56135446Strhodes	va_end(ap);
57135446Strhodes	return (ret);
58135446Strhodes
59135446Strhodes}
60135446Strhodes
61135446Strhodes/*
62135446Strhodes * Return length of string that would have been written if not truncated.
63135446Strhodes */
64135446Strhodes
65135446Strhodesint
66135446Strhodeslwres__print_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
67135446Strhodes	int h;
68135446Strhodes	int l;
69135446Strhodes	int q;
70135446Strhodes	int alt;
71135446Strhodes	int zero;
72135446Strhodes	int left;
73135446Strhodes	int plus;
74135446Strhodes	int space;
75135446Strhodes	long long tmpi;
76135446Strhodes	unsigned long long tmpui;
77135446Strhodes	unsigned long width;
78135446Strhodes	unsigned long precision;
79135446Strhodes	unsigned int length;
80135446Strhodes	char buf[1024];
81135446Strhodes	char c;
82135446Strhodes	void *v;
83135446Strhodes	char *save = str;
84135446Strhodes	const char *cp;
85135446Strhodes	const char *head;
86135446Strhodes	int count = 0;
87135446Strhodes	int pad;
88135446Strhodes	int zeropad;
89135446Strhodes	int dot;
90135446Strhodes	double dbl;
91135446Strhodes#ifdef HAVE_LONG_DOUBLE
92135446Strhodes	long double ldbl;
93135446Strhodes#endif
94135446Strhodes	char fmt[32];
95135446Strhodes
96135446Strhodes	INSIST(str != NULL);
97135446Strhodes	INSIST(format != NULL);
98135446Strhodes
99135446Strhodes	while (*format != '\0') {
100135446Strhodes		if (*format != '%') {
101143731Sdougb			if (size > 1U) {
102135446Strhodes				*str++ = *format;
103135446Strhodes				size--;
104135446Strhodes			}
105135446Strhodes			count++;
106135446Strhodes			format++;
107135446Strhodes			continue;
108135446Strhodes		}
109135446Strhodes		format++;
110135446Strhodes
111135446Strhodes		/*
112135446Strhodes		 * Reset flags.
113135446Strhodes		 */
114153816Sdougb		dot = space = plus = left = zero = alt = h = l = q = 0;
115135446Strhodes		width = precision = 0;
116135446Strhodes		head = "";
117135446Strhodes		length = pad = zeropad = 0;
118225361Sdougb		POST(length);
119135446Strhodes
120135446Strhodes		do {
121135446Strhodes			if (*format == '#') {
122135446Strhodes				alt = 1;
123135446Strhodes				format++;
124135446Strhodes			} else if (*format == '-') {
125135446Strhodes				left = 1;
126135446Strhodes				zero = 0;
127135446Strhodes				format++;
128135446Strhodes			} else if (*format == ' ') {
129135446Strhodes				if (!plus)
130135446Strhodes					space = 1;
131135446Strhodes				format++;
132135446Strhodes			} else if (*format == '+') {
133135446Strhodes				plus = 1;
134135446Strhodes				space = 0;
135135446Strhodes				format++;
136135446Strhodes			} else if (*format == '0') {
137135446Strhodes				if (!left)
138135446Strhodes					zero = 1;
139135446Strhodes				format++;
140135446Strhodes			} else
141135446Strhodes				break;
142135446Strhodes		} while (1);
143135446Strhodes
144135446Strhodes		/*
145135446Strhodes		 * Width.
146135446Strhodes		 */
147135446Strhodes		if (*format == '*') {
148135446Strhodes			width = va_arg(ap, int);
149135446Strhodes			format++;
150135446Strhodes		} else if (isdigit((unsigned char)*format)) {
151135446Strhodes			char *e;
152135446Strhodes			width = strtoul(format, &e, 10);
153135446Strhodes			format = e;
154135446Strhodes		}
155135446Strhodes
156135446Strhodes		/*
157135446Strhodes		 * Precision.
158135446Strhodes		 */
159135446Strhodes		if (*format == '.') {
160135446Strhodes			format++;
161135446Strhodes			dot = 1;
162135446Strhodes			if (*format == '*') {
163135446Strhodes				precision = va_arg(ap, int);
164135446Strhodes				format++;
165135446Strhodes			} else if (isdigit((unsigned char)*format)) {
166135446Strhodes				char *e;
167135446Strhodes				precision = strtoul(format, &e, 10);
168135446Strhodes				format = e;
169135446Strhodes			}
170135446Strhodes		}
171135446Strhodes
172135446Strhodes		switch (*format) {
173135446Strhodes		case '\0':
174135446Strhodes			continue;
175135446Strhodes		case '%':
176143731Sdougb			if (size > 1U) {
177135446Strhodes				*str++ = *format;
178135446Strhodes				size--;
179135446Strhodes			}
180135446Strhodes			count++;
181135446Strhodes			break;
182135446Strhodes		case 'q':
183135446Strhodes			q = 1;
184135446Strhodes			format++;
185135446Strhodes			goto doint;
186135446Strhodes		case 'h':
187135446Strhodes			h = 1;
188135446Strhodes			format++;
189135446Strhodes			goto doint;
190135446Strhodes		case 'l':
191135446Strhodes			l = 1;
192135446Strhodes			format++;
193135446Strhodes			if (*format == 'l') {
194135446Strhodes				q = 1;
195135446Strhodes				format++;
196135446Strhodes			}
197135446Strhodes			goto doint;
198135446Strhodes		case 'n':
199135446Strhodes		case 'i':
200135446Strhodes		case 'd':
201135446Strhodes		case 'o':
202135446Strhodes		case 'u':
203135446Strhodes		case 'x':
204135446Strhodes		case 'X':
205135446Strhodes		doint:
206143731Sdougb			if (precision != 0U)
207135446Strhodes				zero = 0;
208135446Strhodes			switch (*format) {
209135446Strhodes			case 'n':
210135446Strhodes				if (h) {
211135446Strhodes					short int *p;
212135446Strhodes					p = va_arg(ap, short *);
213135446Strhodes					REQUIRE(p != NULL);
214135446Strhodes					*p = str - save;
215135446Strhodes				} else if (l) {
216135446Strhodes					long int *p;
217135446Strhodes					p = va_arg(ap, long *);
218135446Strhodes					REQUIRE(p != NULL);
219135446Strhodes					*p = str - save;
220135446Strhodes				} else {
221135446Strhodes					int *p;
222135446Strhodes					p = va_arg(ap, int *);
223135446Strhodes					REQUIRE(p != NULL);
224135446Strhodes					*p = str - save;
225135446Strhodes				}
226135446Strhodes				break;
227135446Strhodes			case 'i':
228135446Strhodes			case 'd':
229135446Strhodes				if (q)
230135446Strhodes					tmpi = va_arg(ap, long long int);
231135446Strhodes				else if (l)
232135446Strhodes					tmpi = va_arg(ap, long int);
233135446Strhodes				else
234135446Strhodes					tmpi = va_arg(ap, int);
235135446Strhodes				if (tmpi < 0) {
236135446Strhodes					head = "-";
237135446Strhodes					tmpui = -tmpi;
238135446Strhodes				} else {
239135446Strhodes					if (plus)
240135446Strhodes						head = "+";
241135446Strhodes					else if (space)
242135446Strhodes						head = " ";
243135446Strhodes					else
244135446Strhodes						head = "";
245135446Strhodes					tmpui = tmpi;
246135446Strhodes				}
247153816Sdougb				sprintf(buf, "%" LWRES_PRINT_QUADFORMAT "u",
248135446Strhodes					tmpui);
249135446Strhodes				goto printint;
250135446Strhodes			case 'o':
251135446Strhodes				if (q)
252135446Strhodes					tmpui = va_arg(ap,
253135446Strhodes						       unsigned long long int);
254135446Strhodes				else if (l)
255135446Strhodes					tmpui = va_arg(ap, long int);
256135446Strhodes				else
257135446Strhodes					tmpui = va_arg(ap, int);
258135446Strhodes				sprintf(buf,
259153816Sdougb					alt ? "%#" LWRES_PRINT_QUADFORMAT "o"
260153816Sdougb					    : "%" LWRES_PRINT_QUADFORMAT "o",
261153816Sdougb					tmpui);
262135446Strhodes				goto printint;
263135446Strhodes			case 'u':
264135446Strhodes				if (q)
265135446Strhodes					tmpui = va_arg(ap,
266135446Strhodes						       unsigned long long int);
267135446Strhodes				else if (l)
268135446Strhodes					tmpui = va_arg(ap, unsigned long int);
269135446Strhodes				else
270135446Strhodes					tmpui = va_arg(ap, unsigned int);
271153816Sdougb				sprintf(buf, "%" LWRES_PRINT_QUADFORMAT "u",
272153816Sdougb					tmpui);
273135446Strhodes				goto printint;
274135446Strhodes			case 'x':
275135446Strhodes				if (q)
276135446Strhodes					tmpui = va_arg(ap,
277135446Strhodes						       unsigned long long int);
278135446Strhodes				else if (l)
279135446Strhodes					tmpui = va_arg(ap, unsigned long int);
280135446Strhodes				else
281135446Strhodes					tmpui = va_arg(ap, unsigned int);
282135446Strhodes				if (alt) {
283135446Strhodes					head = "0x";
284143731Sdougb					if (precision > 2U)
285135446Strhodes						precision -= 2;
286135446Strhodes				}
287153816Sdougb				sprintf(buf, "%" LWRES_PRINT_QUADFORMAT "x",
288153816Sdougb					tmpui);
289135446Strhodes				goto printint;
290135446Strhodes			case 'X':
291135446Strhodes				if (q)
292135446Strhodes					tmpui = va_arg(ap,
293135446Strhodes						       unsigned long long int);
294135446Strhodes				else if (l)
295135446Strhodes					tmpui = va_arg(ap, unsigned long int);
296135446Strhodes				else
297135446Strhodes					tmpui = va_arg(ap, unsigned int);
298135446Strhodes				if (alt) {
299135446Strhodes					head = "0X";
300143731Sdougb					if (precision > 2U)
301135446Strhodes						precision -= 2;
302135446Strhodes				}
303153816Sdougb				sprintf(buf, "%" LWRES_PRINT_QUADFORMAT "X",
304153816Sdougb					tmpui);
305135446Strhodes				goto printint;
306135446Strhodes			printint:
307143731Sdougb				if (precision != 0U || width != 0U) {
308135446Strhodes					length = strlen(buf);
309135446Strhodes					if (length < precision)
310135446Strhodes						zeropad = precision - length;
311135446Strhodes					else if (length < width && zero)
312135446Strhodes						zeropad = width - length;
313143731Sdougb					if (width != 0U) {
314135446Strhodes						pad = width - length -
315135446Strhodes						      zeropad - strlen(head);
316135446Strhodes						if (pad < 0)
317135446Strhodes							pad = 0;
318135446Strhodes					}
319135446Strhodes				}
320135446Strhodes				count += strlen(head) + strlen(buf) + pad +
321135446Strhodes					 zeropad;
322135446Strhodes				if (!left) {
323143731Sdougb					while (pad > 0 && size > 1U) {
324135446Strhodes						*str++ = ' ';
325135446Strhodes						size--;
326135446Strhodes						pad--;
327135446Strhodes					}
328135446Strhodes				}
329135446Strhodes				cp = head;
330143731Sdougb				while (*cp != '\0' && size > 1U) {
331135446Strhodes					*str++ = *cp++;
332135446Strhodes					size--;
333135446Strhodes				}
334143731Sdougb				while (zeropad > 0 && size > 1U) {
335135446Strhodes					*str++ = '0';
336135446Strhodes					size--;
337135446Strhodes					zeropad--;
338135446Strhodes				}
339135446Strhodes				cp = buf;
340143731Sdougb				while (*cp != '\0' && size > 1U) {
341135446Strhodes					*str++ = *cp++;
342135446Strhodes					size--;
343135446Strhodes				}
344143731Sdougb				while (pad > 0 && size > 1U) {
345135446Strhodes					*str++ = ' ';
346135446Strhodes					size--;
347135446Strhodes					pad--;
348135446Strhodes				}
349135446Strhodes				break;
350135446Strhodes			default:
351135446Strhodes				break;
352135446Strhodes			}
353135446Strhodes			break;
354135446Strhodes		case 's':
355135446Strhodes			cp = va_arg(ap, char *);
356135446Strhodes			REQUIRE(cp != NULL);
357135446Strhodes
358143731Sdougb			if (precision != 0U) {
359135446Strhodes				/*
360135446Strhodes				 * cp need not be NULL terminated.
361135446Strhodes				 */
362135446Strhodes				const char *tp;
363135446Strhodes				unsigned long n;
364135446Strhodes
365135446Strhodes				n = precision;
366135446Strhodes				tp = cp;
367143731Sdougb				while (n != 0U && *tp != '\0')
368135446Strhodes					n--, tp++;
369135446Strhodes				length = precision - n;
370135446Strhodes			} else {
371135446Strhodes				length = strlen(cp);
372135446Strhodes			}
373143731Sdougb			if (width != 0U) {
374135446Strhodes				pad = width - length;
375135446Strhodes				if (pad < 0)
376135446Strhodes					pad = 0;
377135446Strhodes			}
378135446Strhodes			count += pad + length;
379135446Strhodes			if (!left)
380143731Sdougb				while (pad > 0 && size > 1U) {
381135446Strhodes					*str++ = ' ';
382135446Strhodes					size--;
383135446Strhodes					pad--;
384135446Strhodes				}
385143731Sdougb			if (precision != 0U)
386143731Sdougb				while (precision > 0U && *cp != '\0' &&
387143731Sdougb				       size > 1U) {
388135446Strhodes					*str++ = *cp++;
389135446Strhodes					size--;
390135446Strhodes					precision--;
391135446Strhodes				}
392135446Strhodes			else
393143731Sdougb				while (*cp != '\0' && size > 1U) {
394135446Strhodes					*str++ = *cp++;
395135446Strhodes					size--;
396135446Strhodes				}
397143731Sdougb			while (pad > 0 && size > 1U) {
398135446Strhodes				*str++ = ' ';
399135446Strhodes				size--;
400135446Strhodes				pad--;
401135446Strhodes			}
402135446Strhodes			break;
403135446Strhodes		case 'c':
404135446Strhodes			c = va_arg(ap, int);
405143731Sdougb			if (width > 0U) {
406135446Strhodes				count += width;
407135446Strhodes				width--;
408135446Strhodes				if (left) {
409135446Strhodes					*str++ = c;
410135446Strhodes					size--;
411135446Strhodes				}
412143731Sdougb				while (width-- > 0U && size > 1U) {
413135446Strhodes					*str++ = ' ';
414135446Strhodes					size--;
415135446Strhodes				}
416143731Sdougb				if (!left && size > 1U) {
417135446Strhodes					*str++ = c;
418135446Strhodes					size--;
419135446Strhodes				}
420135446Strhodes			} else {
421135446Strhodes				count++;
422143731Sdougb				if (size > 1U) {
423135446Strhodes					*str++ = c;
424135446Strhodes					size--;
425135446Strhodes				}
426135446Strhodes			}
427135446Strhodes			break;
428135446Strhodes		case 'p':
429135446Strhodes			v = va_arg(ap, void *);
430135446Strhodes			sprintf(buf, "%p", v);
431135446Strhodes			length = strlen(buf);
432135446Strhodes			if (precision > length)
433135446Strhodes				zeropad = precision - length;
434143731Sdougb			if (width > 0U) {
435135446Strhodes				pad = width - length - zeropad;
436135446Strhodes				if (pad < 0)
437135446Strhodes					pad = 0;
438135446Strhodes			}
439135446Strhodes			count += length + pad + zeropad;
440135446Strhodes			if (!left)
441143731Sdougb				while (pad > 0 && size > 1U) {
442135446Strhodes					*str++ = ' ';
443135446Strhodes					size--;
444135446Strhodes					pad--;
445135446Strhodes				}
446135446Strhodes			cp = buf;
447135446Strhodes			if (zeropad > 0 && buf[0] == '0' &&
448135446Strhodes			    (buf[1] == 'x' || buf[1] == 'X')) {
449143731Sdougb				if (size > 1U) {
450135446Strhodes					*str++ = *cp++;
451135446Strhodes					size--;
452135446Strhodes				}
453143731Sdougb				if (size > 1U) {
454135446Strhodes					*str++ = *cp++;
455135446Strhodes					size--;
456135446Strhodes				}
457143731Sdougb				while (zeropad > 0 && size > 1U) {
458135446Strhodes					*str++ = '0';
459135446Strhodes					size--;
460135446Strhodes					zeropad--;
461135446Strhodes				}
462135446Strhodes			}
463143731Sdougb			while (*cp != '\0' && size > 1U) {
464135446Strhodes				*str++ = *cp++;
465135446Strhodes				size--;
466135446Strhodes			}
467143731Sdougb			while (pad > 0 && size > 1U) {
468135446Strhodes				*str++ = ' ';
469135446Strhodes				size--;
470135446Strhodes				pad--;
471135446Strhodes			}
472135446Strhodes			break;
473254402Serwin
474135446Strhodes		case 'D':	/*deprecated*/
475135446Strhodes			INSIST("use %ld instead of %D" == NULL);
476254402Serwin			break;
477135446Strhodes		case 'O':	/*deprecated*/
478135446Strhodes			INSIST("use %lo instead of %O" == NULL);
479254402Serwin			break;
480135446Strhodes		case 'U':	/*deprecated*/
481135446Strhodes			INSIST("use %lu instead of %U" == NULL);
482254402Serwin			break;
483135446Strhodes
484135446Strhodes		case 'L':
485135446Strhodes#ifdef HAVE_LONG_DOUBLE
486135446Strhodes			l = 1;
487135446Strhodes#else
488135446Strhodes			INSIST("long doubles are not supported" == NULL);
489135446Strhodes#endif
490135446Strhodes			/*FALLTHROUGH*/
491135446Strhodes		case 'e':
492135446Strhodes		case 'E':
493135446Strhodes		case 'f':
494135446Strhodes		case 'g':
495135446Strhodes		case 'G':
496135446Strhodes			if (!dot)
497135446Strhodes				precision = 6;
498135446Strhodes			/*
499135446Strhodes			 * IEEE floating point.
500135446Strhodes			 * MIN 2.2250738585072014E-308
501135446Strhodes			 * MAX 1.7976931348623157E+308
502135446Strhodes			 * VAX floating point has a smaller range than IEEE.
503135446Strhodes			 *
504135446Strhodes			 * precisions > 324 don't make much sense.
505135446Strhodes			 * if we cap the precision at 512 we will not
506135446Strhodes			 * overflow buf.
507135446Strhodes			 */
508143731Sdougb			if (precision > 512U)
509135446Strhodes				precision = 512;
510135446Strhodes			sprintf(fmt, "%%%s%s.%lu%s%c", alt ? "#" : "",
511135446Strhodes				plus ? "+" : space ? " " : "",
512135446Strhodes				precision, l ? "L" : "", *format);
513135446Strhodes			switch (*format) {
514135446Strhodes			case 'e':
515135446Strhodes			case 'E':
516135446Strhodes			case 'f':
517135446Strhodes			case 'g':
518135446Strhodes			case 'G':
519135446Strhodes#ifdef HAVE_LONG_DOUBLE
520135446Strhodes				if (l) {
521135446Strhodes					ldbl = va_arg(ap, long double);
522135446Strhodes					sprintf(buf, fmt, ldbl);
523135446Strhodes				} else
524135446Strhodes#endif
525135446Strhodes				{
526135446Strhodes					dbl = va_arg(ap, double);
527135446Strhodes					sprintf(buf, fmt, dbl);
528135446Strhodes				}
529135446Strhodes				length = strlen(buf);
530143731Sdougb				if (width > 0U) {
531135446Strhodes					pad = width - length;
532135446Strhodes					if (pad < 0)
533135446Strhodes						pad = 0;
534135446Strhodes				}
535135446Strhodes				count += length + pad;
536135446Strhodes				if (!left)
537143731Sdougb					while (pad > 0 && size > 1U) {
538135446Strhodes						*str++ = ' ';
539135446Strhodes						size--;
540135446Strhodes						pad--;
541135446Strhodes					}
542135446Strhodes				cp = buf;
543143731Sdougb				while (*cp != ' ' && size > 1U) {
544135446Strhodes					*str++ = *cp++;
545135446Strhodes					size--;
546135446Strhodes				}
547143731Sdougb				while (pad > 0 && size > 1U) {
548135446Strhodes					*str++ = ' ';
549135446Strhodes					size--;
550135446Strhodes					pad--;
551135446Strhodes				}
552135446Strhodes				break;
553135446Strhodes			default:
554135446Strhodes				continue;
555135446Strhodes			}
556135446Strhodes			break;
557135446Strhodes		default:
558135446Strhodes			continue;
559135446Strhodes		}
560135446Strhodes		format++;
561135446Strhodes	}
562143731Sdougb	if (size > 0U)
563135446Strhodes		*str = '\0';
564135446Strhodes	return (count);
565135446Strhodes}
566