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