Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / util / vbuf_print.c
blob649ad9be5f8e279620bd651ccde0ad2f5bd7250d
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* vbuf_print 3
6 /* SUMMARY
7 /* formatted print to generic buffer
8 /* SYNOPSIS
9 /* #include <stdarg.h>
10 /* #include <vbuf_print.h>
12 /* VBUF *vbuf_print(bp, format, ap)
13 /* VBUF *bp;
14 /* const char *format;
15 /* va_list ap;
16 /* DESCRIPTION
17 /* vbuf_print() appends data to the named buffer according to its
18 /* \fIformat\fR argument. It understands the s, c, d, u, o, x, X, p, e,
19 /* f and g format types, the l modifier, field width and precision,
20 /* sign, and padding with zeros or spaces.
22 /* In addition, vbuf_print() recognizes the %m format specifier
23 /* and expands it to the error message corresponding to the current
24 /* value of the global \fIerrno\fR variable.
25 /* REENTRANCY
26 /* .ad
27 /* .fi
28 /* vbuf_print() allocates a static buffer. After completion
29 /* of the first vbuf_print() call, this buffer is safe for
30 /* reentrant vbuf_print() calls by (asynchronous) terminating
31 /* signal handlers or by (synchronous) terminating error
32 /* handlers. vbuf_print() initialization typically happens
33 /* upon the first formatted output to a VSTRING or VSTREAM.
35 /* However, it is up to the caller to ensure that the destination
36 /* VSTREAM or VSTRING buffer is protected against reentrant usage.
37 /* LICENSE
38 /* .ad
39 /* .fi
40 /* The Secure Mailer license must be distributed with this software.
41 /* AUTHOR(S)
42 /* Wietse Venema
43 /* IBM T.J. Watson Research
44 /* P.O. Box 704
45 /* Yorktown Heights, NY 10598, USA
46 /*--*/
48 /* System library. */
50 #include "sys_defs.h"
51 #include <stdlib.h> /* 44BSD stdarg.h uses abort() */
52 #include <stdarg.h>
53 #include <string.h>
54 #include <ctype.h>
55 #include <stdlib.h> /* 44bsd stdarg.h uses abort() */
56 #include <stdio.h> /* sprintf() prototype */
57 #include <float.h> /* range of doubles */
58 #include <errno.h>
59 #include <limits.h> /* CHAR_BIT */
61 /* Application-specific. */
63 #include "msg.h"
64 #include "vbuf.h"
65 #include "vstring.h"
66 #include "vbuf_print.h"
69 * What we need here is a *sprintf() routine that can ask for more room (as
70 * in 4.4 BSD). However, that functionality is not widely available, and I
71 * have no plans to maintain a complete 4.4 BSD *sprintf() alternative.
73 * This means we're stuck with plain old ugly sprintf() for all non-trivial
74 * conversions. We cannot use snprintf() even if it is available, because
75 * that routine truncates output, and we want everything. Therefore, it is
76 * up to us to ensure that sprintf() output always stays within bounds.
78 * Due to the complexity of *printf() format strings we cannot easily predict
79 * how long results will be without actually doing the conversions. A trick
80 * used by some people is to print to a temporary file and to read the
81 * result back. In programs that do a lot of formatting, that might be too
82 * expensive.
84 * Guessing the output size of a string (%s) conversion is not hard. The
85 * problem is with numerical results. Instead of making an accurate guess we
86 * take a wide margin when reserving space. The INT_SPACE margin should be
87 * large enough to hold the result from any (octal, hex, decimal) integer
88 * conversion that has no explicit width or precision specifiers. With
89 * floating-point numbers, use a similar estimate, and add DBL_MAX_10_EXP
90 * just to be sure.
92 #define INT_SPACE ((CHAR_BIT * sizeof(long)) / 2)
93 #define DBL_SPACE ((CHAR_BIT * sizeof(double)) / 2 + DBL_MAX_10_EXP)
94 #define PTR_SPACE ((CHAR_BIT * sizeof(char *)) / 2)
97 * Helper macros... Note that there is no need to check the result from
98 * VSTRING_SPACE() because that always succeeds or never returns.
100 #define VBUF_SKIP(bp) { \
101 while ((bp)->cnt > 0 && *(bp)->ptr) \
102 (bp)->ptr++, (bp)->cnt--; \
105 #define VSTRING_ADDNUM(vp, n) { \
106 VSTRING_SPACE(vp, INT_SPACE); \
107 sprintf(vstring_end(vp), "%d", n); \
108 VBUF_SKIP(&vp->vbuf); \
111 #define VBUF_STRCAT(bp, s) { \
112 unsigned char *_cp = (unsigned char *) (s); \
113 int _ch; \
114 while ((_ch = *_cp++) != 0) \
115 VBUF_PUT((bp), _ch); \
118 /* vbuf_print - format string, vsprintf-like interface */
120 VBUF *vbuf_print(VBUF *bp, const char *format, va_list ap)
122 const char *myname = "vbuf_print";
123 static VSTRING *fmt; /* format specifier */
124 unsigned char *cp;
125 int width; /* width and numerical precision */
126 int prec; /* are signed for overflow defense */
127 unsigned long_flag; /* long or plain integer */
128 int ch;
129 char *s;
132 * Assume that format strings are short.
134 if (fmt == 0)
135 fmt = vstring_alloc(INT_SPACE);
138 * Iterate over characters in the format string, picking up arguments
139 * when format specifiers are found.
141 for (cp = (unsigned char *) format; *cp; cp++) {
142 if (*cp != '%') {
143 VBUF_PUT(bp, *cp); /* ordinary character */
144 } else if (cp[1] == '%') {
145 VBUF_PUT(bp, *cp++); /* %% becomes % */
146 } else {
149 * Handle format specifiers one at a time, since we can only deal
150 * with arguments one at a time. Try to determine the end of the
151 * format specifier. We do not attempt to fully parse format
152 * strings, since we are ging to let sprintf() do the hard work.
153 * In regular expression notation, we recognize:
155 * %-?0?([0-9]+|\*)?\.?([0-9]+|\*)?l?[a-zA-Z]
157 * which includes some combinations that do not make sense. Garbage
158 * in, garbage out.
160 VSTRING_RESET(fmt); /* clear format string */
161 VSTRING_ADDCH(fmt, *cp++);
162 if (*cp == '-') /* left-adjusted field? */
163 VSTRING_ADDCH(fmt, *cp++);
164 if (*cp == '+') /* signed field? */
165 VSTRING_ADDCH(fmt, *cp++);
166 if (*cp == '0') /* zero-padded field? */
167 VSTRING_ADDCH(fmt, *cp++);
168 if (*cp == '*') { /* dynamic field width */
169 width = va_arg(ap, int);
170 VSTRING_ADDNUM(fmt, width);
171 cp++;
172 } else { /* hard-coded field width */
173 for (width = 0; ch = *cp, ISDIGIT(ch); cp++) {
174 width = width * 10 + ch - '0';
175 VSTRING_ADDCH(fmt, ch);
178 if (width < 0) {
179 msg_warn("%s: bad width %d in %.50s", myname, width, format);
180 width = 0;
182 if (*cp == '.') /* width/precision separator */
183 VSTRING_ADDCH(fmt, *cp++);
184 if (*cp == '*') { /* dynamic precision */
185 prec = va_arg(ap, int);
186 VSTRING_ADDNUM(fmt, prec);
187 cp++;
188 } else { /* hard-coded precision */
189 for (prec = 0; ch = *cp, ISDIGIT(ch); cp++) {
190 prec = prec * 10 + ch - '0';
191 VSTRING_ADDCH(fmt, ch);
194 if (prec < 0) {
195 msg_warn("%s: bad precision %d in %.50s", myname, prec, format);
196 prec = 0;
198 if ((long_flag = (*cp == 'l')) != 0)/* long whatever */
199 VSTRING_ADDCH(fmt, *cp++);
200 if (*cp == 0) /* premature end, punt */
201 break;
202 VSTRING_ADDCH(fmt, *cp); /* type (checked below) */
203 VSTRING_TERMINATE(fmt); /* null terminate */
206 * Execute the format string - let sprintf() do the hard work for
207 * non-trivial cases only. For simple string conversions and for
208 * long string conversions, do a direct copy to the output
209 * buffer.
211 switch (*cp) {
212 case 's': /* string-valued argument */
213 s = va_arg(ap, char *);
214 if (prec > 0 || (width > 0 && width > strlen(s))) {
215 if (VBUF_SPACE(bp, (width > prec ? width : prec) + INT_SPACE))
216 return (bp);
217 sprintf((char *) bp->ptr, vstring_str(fmt), s);
218 VBUF_SKIP(bp);
219 } else {
220 VBUF_STRCAT(bp, s);
222 break;
223 case 'c': /* integral-valued argument */
224 case 'd':
225 case 'u':
226 case 'o':
227 case 'x':
228 case 'X':
229 if (VBUF_SPACE(bp, (width > prec ? width : prec) + INT_SPACE))
230 return (bp);
231 if (long_flag)
232 sprintf((char *) bp->ptr, vstring_str(fmt), va_arg(ap, long));
233 else
234 sprintf((char *) bp->ptr, vstring_str(fmt), va_arg(ap, int));
235 VBUF_SKIP(bp);
236 break;
237 case 'e': /* float-valued argument */
238 case 'f':
239 case 'g':
240 if (VBUF_SPACE(bp, (width > prec ? width : prec) + DBL_SPACE))
241 return (bp);
242 sprintf((char *) bp->ptr, vstring_str(fmt), va_arg(ap, double));
243 VBUF_SKIP(bp);
244 break;
245 case 'm':
246 VBUF_STRCAT(bp, strerror(errno));
247 break;
248 case 'p':
249 if (VBUF_SPACE(bp, (width > prec ? width : prec) + PTR_SPACE))
250 return (bp);
251 sprintf((char *) bp->ptr, vstring_str(fmt), va_arg(ap, char *));
252 VBUF_SKIP(bp);
253 break;
254 default: /* anything else is bad */
255 msg_panic("vbuf_print: unknown format type: %c", *cp);
256 /* NOTREACHED */
257 break;
261 return (bp);