5
/* formatted print to generic buffer
8
/* #include <vbuf_print.h>
10
/* VBUF *vbuf_print(bp, format, ap)
12
/* const char *format;
15
/* vbuf_print() appends data to the named buffer according to its
16
/* \fIformat\fR argument. It understands the s, c, d, u, o, x, X, p, e,
17
/* f and g format types, the l modifier, field width and precision,
18
/* sign, and padding with zeros or spaces.
20
/* In addition, vbuf_print() recognizes the %m format specifier
21
/* and expands it to the error message corresponding to the current
22
/* value of the global \fIerrno\fR variable.
26
/* The Secure Mailer license must be distributed with this software.
29
/* IBM T.J. Watson Research
31
/* Yorktown Heights, NY 10598, USA
37
#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
41
#include <stdlib.h> /* 44bsd stdarg.h uses abort() */
42
#include <stdio.h> /* sprintf() prototype */
43
#include <float.h> /* range of doubles */
46
/* Application-specific. */
51
#include "vbuf_print.h"
54
* What we need here is a *sprintf() routine that can ask for more room (as
55
* in 4.4 BSD). However, that functionality is not widely available, and I
56
* have no plans to maintain a complete 4.4 BSD *sprintf() alternative.
58
* This means we're stuck with plain old ugly sprintf() for all non-trivial
59
* conversions. We cannot use snprintf() even if it is available, because
60
* that routine truncates output, and we want everything. Therefore, it is
61
* up to us to ensure that sprintf() output always stays within bounds.
63
* Due to the complexity of *printf() format strings we cannot easily predict
64
* how long results will be without actually doing the conversions. A trick
65
* used by some people is to print to a temporary file and to read the
66
* result back. In programs that do a lot of formatting, that might be too
69
* Guessing the output size of a string (%s) conversion is not hard. The
70
* problem is with numerical results. Instead of making an accurate guess we
71
* take a wide margin when reserving space. The INT_SPACE margin should be
72
* large enough to hold the result from any (octal, hex, decimal) integer
73
* conversion that has no explicit width or precision specifiers. With
74
* floating-point numbers, use a similar estimate, and add DBL_MAX_10_EXP
77
#define INT_SPACE (4 * sizeof(long))
78
#define DBL_SPACE (4 * sizeof(double) + DBL_MAX_10_EXP)
79
#define PTR_SPACE (4 * sizeof(char *))
82
* Helper macros... Note that there is no need to check the result from
83
* VSTRING_SPACE() because that always succeeds or never returns.
85
#define VBUF_SKIP(bp) { \
86
while ((bp)->cnt > 0 && *(bp)->ptr) \
87
(bp)->ptr++, (bp)->cnt--; \
90
#define VSTRING_ADDNUM(vp, n) { \
91
VSTRING_SPACE(vp, INT_SPACE); \
92
sprintf(vstring_end(vp), "%d", n); \
93
VBUF_SKIP(&vp->vbuf); \
96
#define VBUF_STRCAT(bp, s) { \
97
unsigned char *_cp = (unsigned char *) (s); \
99
while ((ch = *_cp++) != 0) \
100
VBUF_PUT((bp), ch); \
103
/* vbuf_print - format string, vsprintf-like interface */
105
VBUF *vbuf_print(VBUF *bp, const char *format, va_list ap)
107
static VSTRING *fmt; /* format specifier */
109
unsigned width; /* field width */
110
unsigned prec; /* numerical precision */
111
unsigned long_flag; /* long or plain integer */
116
* Assume that format strings are short.
119
fmt = vstring_alloc(INT_SPACE);
122
* Iterate over characters in the format string, picking up arguments
123
* when format specifiers are found.
125
for (cp = (unsigned char *) format; *cp; cp++) {
127
VBUF_PUT(bp, *cp); /* ordinary character */
128
} else if (cp[1] == '%') {
129
VBUF_PUT(bp, *cp++); /* %% becomes % */
133
* Handle format specifiers one at a time, since we can only deal
134
* with arguments one at a time. Try to determine the end of the
135
* format specifier. We do not attempt to fully parse format
136
* strings, since we are ging to let sprintf() do the hard work.
137
* In regular expression notation, we recognize:
139
* %-?0?([0-9]+|\*)?\.?([0-9]+|\*)?l?[a-zA-Z]
141
* which includes some combinations that do not make sense. Garbage
144
VSTRING_RESET(fmt); /* clear format string */
145
VSTRING_ADDCH(fmt, *cp++);
146
if (*cp == '-') /* left-adjusted field? */
147
VSTRING_ADDCH(fmt, *cp++);
148
if (*cp == '+') /* signed field? */
149
VSTRING_ADDCH(fmt, *cp++);
150
if (*cp == '0') /* zero-padded field? */
151
VSTRING_ADDCH(fmt, *cp++);
152
if (*cp == '*') { /* dynamic field width */
153
width = va_arg(ap, int);
154
VSTRING_ADDNUM(fmt, width);
156
} else { /* hard-coded field width */
157
for (width = 0; ISDIGIT(ch = *cp); cp++) {
158
width = width * 10 + ch - '0';
159
VSTRING_ADDCH(fmt, ch);
162
if (*cp == '.') /* width/precision separator */
163
VSTRING_ADDCH(fmt, *cp++);
164
if (*cp == '*') { /* dynamic precision */
165
prec = va_arg(ap, int);
166
VSTRING_ADDNUM(fmt, prec);
168
} else { /* hard-coded precision */
169
for (prec = 0; ISDIGIT(ch = *cp); cp++) {
170
prec = prec * 10 + ch - '0';
171
VSTRING_ADDCH(fmt, ch);
174
if ((long_flag = (*cp == 'l')) != 0)/* long whatever */
175
VSTRING_ADDCH(fmt, *cp++);
176
if (*cp == 0) /* premature end, punt */
178
VSTRING_ADDCH(fmt, *cp); /* type (checked below) */
179
VSTRING_TERMINATE(fmt); /* null terminate */
182
* Execute the format string - let sprintf() do the hard work for
183
* non-trivial cases only. For simple string conversions and for
184
* long string conversions, do a direct copy to the output
188
case 's': /* string-valued argument */
189
s = va_arg(ap, char *);
190
if (prec > 0 || (width > 0 && width > strlen(s))) {
191
if (VBUF_SPACE(bp, (width > prec ? width : prec) + INT_SPACE))
193
sprintf((char *) bp->ptr, vstring_str(fmt), s);
199
case 'c': /* integral-valued argument */
205
if (VBUF_SPACE(bp, (width > prec ? width : prec) + INT_SPACE))
208
sprintf((char *) bp->ptr, vstring_str(fmt), va_arg(ap, long));
210
sprintf((char *) bp->ptr, vstring_str(fmt), va_arg(ap, int));
213
case 'e': /* float-valued argument */
216
if (VBUF_SPACE(bp, (width > prec ? width : prec) + DBL_SPACE))
218
sprintf((char *) bp->ptr, vstring_str(fmt), va_arg(ap, double));
222
VBUF_STRCAT(bp, strerror(errno));
225
if (VBUF_SPACE(bp, (width > prec ? width : prec) + PTR_SPACE))
227
sprintf((char *) bp->ptr, vstring_str(fmt), va_arg(ap, char *));
230
default: /* anything else is bad */
231
msg_panic("vbuf_print: unknown format type: %c", *cp);