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