Postfix3.3.1
vbuf_print.c
[詳解]
1 /*++
2 /* NAME
3 /* vbuf_print 3
4 /* SUMMARY
5 /* formatted print to generic buffer
6 /* SYNOPSIS
7 /* #include <stdarg.h>
8 /* #include <vbuf_print.h>
9 /*
10 /* VBUF *vbuf_print(bp, format, ap)
11 /* VBUF *bp;
12 /* const char *format;
13 /* va_list ap;
14 /* DESCRIPTION
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.
19 /*
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.
23 /* REENTRANCY
24 /* .ad
25 /* .fi
26 /* vbuf_print() allocates a static buffer. After completion
27 /* of the first vbuf_print() call, this buffer is safe for
28 /* reentrant vbuf_print() calls by (asynchronous) terminating
29 /* signal handlers or by (synchronous) terminating error
30 /* handlers. vbuf_print() initialization typically happens
31 /* upon the first formatted output to a VSTRING or VSTREAM.
32 /*
33 /* However, it is up to the caller to ensure that the destination
34 /* VSTREAM or VSTRING buffer is protected against reentrant usage.
35 /* LICENSE
36 /* .ad
37 /* .fi
38 /* The Secure Mailer license must be distributed with this software.
39 /* AUTHOR(S)
40 /* Wietse Venema
41 /* IBM T.J. Watson Research
42 /* P.O. Box 704
43 /* Yorktown Heights, NY 10598, USA
44 /*
45 /* Wietse Venema
46 /* Google, Inc.
47 /* 111 8th Avenue
48 /* New York, NY 10011, USA
49 /*--*/
50 
51 /* System library. */
52 
53 #include "sys_defs.h"
54 #include <stdlib.h> /* 44BSD stdarg.h uses abort() */
55 #include <stdarg.h>
56 #include <string.h>
57 #include <ctype.h>
58 #include <stdlib.h> /* 44bsd stdarg.h uses abort() */
59 #include <stdio.h> /* sprintf() prototype */
60 #include <float.h> /* range of doubles */
61 #include <errno.h>
62 #include <limits.h> /* CHAR_BIT, INT_MAX */
63 
64 /* Application-specific. */
65 
66 #include "msg.h"
67 #include "mymalloc.h"
68 #include "vbuf.h"
69 #include "vstring.h"
70 #include "vbuf_print.h"
71 
72  /*
73  * What we need here is a *sprintf() routine that can ask for more room (as
74  * in 4.4 BSD). However, that functionality is not widely available, and I
75  * have no plans to maintain a complete 4.4 BSD *sprintf() alternative.
76  *
77  * Postfix vbuf_print() was implemented when many mainstream systems had no
78  * usable snprintf() implementation (usable means: return the length,
79  * excluding terminator, that the output would have if the buffer were large
80  * enough). For example, GLIBC before 2.1 (1999) snprintf() did not
81  * distinguish between formatting error and buffer size error, while SUN had
82  * no snprintf() implementation before Solaris 2.6 (1997).
83  *
84  * For the above reasons, vbuf_print() was implemented with sprintf() and a
85  * generously-sized output buffer. Current vbuf_print() implementations use
86  * snprintf(), and report an error if the output does not fit (in that case,
87  * the old sprintf()-based implementation would have had a buffer overflow
88  * vulnerability). The old implementation is still available for building
89  * Postfix on ancient systems.
90  *
91  * Guessing the output size of a string (%s) conversion is not hard. The
92  * problem is with numerical results. Instead of making an accurate guess we
93  * take a wide margin when reserving space. The INT_SPACE margin should be
94  * large enough to hold the result from any (octal, hex, decimal) integer
95  * conversion that has no explicit width or precision specifiers. With
96  * floating-point numbers, use a similar estimate, and add DBL_MAX_10_EXP
97  * just to be sure.
98  */
99 #define INT_SPACE ((CHAR_BIT * sizeof(long)) / 2)
100 #define DBL_SPACE ((CHAR_BIT * sizeof(double)) / 2 + DBL_MAX_10_EXP)
101 #define PTR_SPACE ((CHAR_BIT * sizeof(char *)) / 2)
102 
103  /*
104  * Helper macros... Note that there is no need to check the result from
105  * VSTRING_SPACE() because that always succeeds or never returns.
106  */
107 #ifndef NO_SNPRINTF
108 #define VBUF_SNPRINTF(bp, sz, fmt, arg) do { \
109  ssize_t _ret; \
110  if (VBUF_SPACE((bp), (sz)) != 0) \
111  return (bp); \
112  _ret = snprintf((char *) (bp)->ptr, (bp)->cnt, (fmt), (arg)); \
113  if (_ret < 0) \
114  msg_panic("%s: output error for '%s'", myname, mystrdup(fmt)); \
115  if (_ret >= (bp)->cnt) \
116  msg_panic("%s: output for '%s' exceeds space %ld", \
117  myname, mystrdup(fmt), (long) (bp)->cnt); \
118  VBUF_SKIP(bp); \
119  } while (0)
120 #else
121 #define VBUF_SNPRINTF(bp, sz, fmt, arg) do { \
122  if (VBUF_SPACE((bp), (sz)) != 0) \
123  return (bp); \
124  sprintf((char *) (bp)->ptr, (fmt), (arg)); \
125  VBUF_SKIP(bp); \
126  } while (0)
127 #endif
128 
129 #define VBUF_SKIP(bp) do { \
130  while ((bp)->cnt > 0 && *(bp)->ptr) \
131  (bp)->ptr++, (bp)->cnt--; \
132  } while (0)
133 
134 #define VSTRING_ADDNUM(vp, n) do { \
135  VBUF_SNPRINTF(&(vp)->vbuf, INT_SPACE, "%d", n); \
136  } while (0)
137 
138 #define VBUF_STRCAT(bp, s) do { \
139  unsigned char *_cp = (unsigned char *) (s); \
140  int _ch; \
141  while ((_ch = *_cp++) != 0) \
142  VBUF_PUT((bp), _ch); \
143  } while (0)
144 
145 /* vbuf_print - format string, vsprintf-like interface */
146 
147 VBUF *vbuf_print(VBUF *bp, const char *format, va_list ap)
148 {
149  const char *myname = "vbuf_print";
150  static VSTRING *fmt; /* format specifier */
151  unsigned char *cp;
152  int width; /* width and numerical precision */
153  int prec; /* are signed for overflow defense */
154  unsigned long_flag; /* long or plain integer */
155  int ch;
156  char *s;
157  int saved_errno = errno; /* VBUF_SPACE() may clobber it */
158 
159  /*
160  * Assume that format strings are short.
161  */
162  if (fmt == 0)
163  fmt = vstring_alloc(INT_SPACE);
164 
165  /*
166  * Iterate over characters in the format string, picking up arguments
167  * when format specifiers are found.
168  */
169  for (cp = (unsigned char *) format; *cp; cp++) {
170  if (*cp != '%') {
171  VBUF_PUT(bp, *cp); /* ordinary character */
172  } else if (cp[1] == '%') {
173  VBUF_PUT(bp, *cp++); /* %% becomes % */
174  } else {
175 
176  /*
177  * Handle format specifiers one at a time, since we can only deal
178  * with arguments one at a time. Try to determine the end of the
179  * format specifier. We do not attempt to fully parse format
180  * strings, since we are ging to let sprintf() do the hard work.
181  * In regular expression notation, we recognize:
182  *
183  * %-?+?0?([0-9]+|\*)?(\.([0-9]+|\*))?l?[a-zA-Z]
184  *
185  * which includes some combinations that do not make sense. Garbage
186  * in, garbage out.
187  */
188  VSTRING_RESET(fmt); /* clear format string */
189  VSTRING_ADDCH(fmt, *cp++);
190  if (*cp == '-') /* left-adjusted field? */
191  VSTRING_ADDCH(fmt, *cp++);
192  if (*cp == '+') /* signed field? */
193  VSTRING_ADDCH(fmt, *cp++);
194  if (*cp == '0') /* zero-padded field? */
195  VSTRING_ADDCH(fmt, *cp++);
196  if (*cp == '*') { /* dynamic field width */
197  width = va_arg(ap, int);
198  if (width < 0) {
199  msg_warn("%s: bad width %d in %.50s",
200  myname, width, format);
201  width = 0;
202  } else
203  VSTRING_ADDNUM(fmt, width);
204  cp++;
205  } else { /* hard-coded field width */
206  for (width = 0; ch = *cp, ISDIGIT(ch); cp++) {
207  int digit = ch - '0';
208 
209  if (width > INT_MAX / 10
210  || (width *= 10) > INT_MAX - digit)
211  msg_panic("%s: bad width %d... in %.50s",
212  myname, width, format);
213  width += digit;
214  VSTRING_ADDCH(fmt, ch);
215  }
216  }
217  if (*cp == '.') { /* width/precision separator */
218  VSTRING_ADDCH(fmt, *cp++);
219  if (*cp == '*') { /* dynamic precision */
220  prec = va_arg(ap, int);
221  if (prec < 0) {
222  msg_warn("%s: bad precision %d in %.50s",
223  myname, prec, format);
224  prec = -1;
225  } else
226  VSTRING_ADDNUM(fmt, prec);
227  cp++;
228  } else { /* hard-coded precision */
229  for (prec = 0; ch = *cp, ISDIGIT(ch); cp++) {
230  int digit = ch - '0';
231 
232  if (prec > INT_MAX / 10
233  || (prec *= 10) > INT_MAX - digit)
234  msg_panic("%s: bad precision %d... in %.50s",
235  myname, prec, format);
236  prec += digit;
237  VSTRING_ADDCH(fmt, ch);
238  }
239  }
240  } else {
241  prec = -1;
242  }
243  if ((long_flag = (*cp == 'l')) != 0)/* long whatever */
244  VSTRING_ADDCH(fmt, *cp++);
245  if (*cp == 0) /* premature end, punt */
246  break;
247  VSTRING_ADDCH(fmt, *cp); /* type (checked below) */
248  VSTRING_TERMINATE(fmt); /* null terminate */
249 
250  /*
251  * Execute the format string - let sprintf() do the hard work for
252  * non-trivial cases only. For simple string conversions and for
253  * long string conversions, do a direct copy to the output
254  * buffer.
255  */
256  switch (*cp) {
257  case 's': /* string-valued argument */
258  if (long_flag)
259  msg_panic("%s: %%l%c is not supported", myname, *cp);
260  s = va_arg(ap, char *);
261  if (prec >= 0 || (width > 0 && width > strlen(s))) {
262  VBUF_SNPRINTF(bp, (width > prec ? width : prec) + INT_SPACE,
263  vstring_str(fmt), s);
264  } else {
265  VBUF_STRCAT(bp, s);
266  }
267  break;
268  case 'c': /* integral-valued argument */
269  if (long_flag)
270  msg_panic("%s: %%l%c is not supported", myname, *cp);
271  /* FALLTHROUGH */
272  case 'd':
273  case 'u':
274  case 'o':
275  case 'x':
276  case 'X':
277  if (long_flag)
278  VBUF_SNPRINTF(bp, (width > prec ? width : prec) + INT_SPACE,
279  vstring_str(fmt), va_arg(ap, long));
280  else
281  VBUF_SNPRINTF(bp, (width > prec ? width : prec) + INT_SPACE,
282  vstring_str(fmt), va_arg(ap, int));
283  break;
284  case 'e': /* float-valued argument */
285  case 'f':
286  case 'g':
287  /* C99 *printf ignore the 'l' modifier. */
288  VBUF_SNPRINTF(bp, (width > prec ? width : prec) + DBL_SPACE,
289  vstring_str(fmt), va_arg(ap, double));
290  break;
291  case 'm':
292  /* Ignore the 'l' modifier, width and precision. */
293  VBUF_STRCAT(bp, strerror(saved_errno));
294  break;
295  case 'p':
296  if (long_flag)
297  msg_panic("%s: %%l%c is not supported", myname, *cp);
298  VBUF_SNPRINTF(bp, (width > prec ? width : prec) + PTR_SPACE,
299  vstring_str(fmt), va_arg(ap, char *));
300  break;
301  default: /* anything else is bad */
302  msg_panic("vbuf_print: unknown format type: %c", *cp);
303  /* NOTREACHED */
304  break;
305  }
306  }
307  }
308  return (bp);
309 }
310 
311 #ifdef TEST
312 #include <argv.h>
313 #include <msg_vstream.h>
314 #include <vstring.h>
315 #include <vstring_vstream.h>
316 
317 int main(int argc, char **argv)
318 {
319  VSTRING *ibuf = vstring_alloc(100);
320 
321  msg_vstream_init(argv[0], VSTREAM_ERR);
322 
323  while (vstring_fgets_nonl(ibuf, VSTREAM_IN)) {
324  ARGV *args = argv_split(vstring_str(ibuf), CHARS_SPACE);
325  char *cp;
326 
327  if (args->argc == 0 || *(cp = args->argv[0]) == '#') {
328  /* void */ ;
329  } else if (args->argc != 2 || *cp != '%') {
330  msg_warn("usage: format number");
331  } else {
332  char *fmt = cp++;
333  int lflag;
334 
335  /* Determine the vstring_sprintf() argument type. */
336  cp += strspn(cp, "+-*0123456789.");
337  if ((lflag = (*cp == 'l')) != 0)
338  cp++;
339  if (cp[1] != 0) {
340  msg_warn("bad format: \"%s\"", fmt);
341  } else {
342  VSTRING *obuf = vstring_alloc(1);
343  char *val = args->argv[1];
344 
345  /* Test the worst-case memory allocation. */
346 #ifdef CA_VSTRING_CTL_EXACT
348 #endif
349  switch (*cp) {
350  case 'c':
351  case 'd':
352  case 'o':
353  case 'u':
354  case 'x':
355  case 'X':
356  if (lflag)
357  vstring_sprintf(obuf, fmt, atol(val));
358  else
359  vstring_sprintf(obuf, fmt, atoi(val));
360  msg_info("\"%s\"", vstring_str(obuf));
361  break;
362  case 's':
363  vstring_sprintf(obuf, fmt, val);
364  msg_info("\"%s\"", vstring_str(obuf));
365  break;
366  case 'f':
367  case 'g':
368  vstring_sprintf(obuf, fmt, atof(val));
369  msg_info("\"%s\"", vstring_str(obuf));
370  break;
371  default:
372  msg_warn("bad format: \"%s\"", fmt);
373  break;
374  }
375  vstring_free(obuf);
376  }
377  }
378  argv_free(args);
379  }
380  vstring_free(ibuf);
381  return (0);
382 }
383 
384 #endif
#define vstring_fgets_nonl(s, p)
#define PTR_SPACE
Definition: vbuf_print.c:101
#define CA_VSTRING_CTL_END
Definition: vstring.h:59
ARGV * argv_free(ARGV *argvp)
Definition: argv.c:136
Definition: argv.h:17
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
#define vstring_str(vp)
Definition: vstring.h:71
int main(int argc, char **argv)
Definition: anvil.c:1010
char ** argv
Definition: argv.h:20
#define VSTREAM_IN
Definition: vstream.h:66
#define DBL_SPACE
Definition: vbuf_print.c:100
#define VBUF_PUT(v, c)
Definition: vbuf.h:87
void vstring_ctl(VSTRING *vp,...)
Definition: vstring.c:390
#define VSTRING_TERMINATE(vp)
Definition: vstring.h:74
int const char * fmt
#define INT_SPACE
Definition: vbuf_print.c:99
#define VSTRING_ADDCH(vp, ch)
Definition: vstring.h:81
#define ISDIGIT(c)
Definition: sys_defs.h:1748
#define VSTRING_RESET(vp)
Definition: vstring.h:77
Definition: vbuf.h:37
void msg_warn(const char *fmt,...)
Definition: msg.c:215
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
#define CHARS_SPACE
Definition: sys_defs.h:1762
VSTRING * vstring_sprintf(VSTRING *vp, const char *format,...)
Definition: vstring.c:602
#define VBUF_STRCAT(bp, s)
Definition: vbuf_print.c:138
#define CA_VSTRING_CTL_EXACT
Definition: vstring.h:60
ARGV * argv_split(const char *, const char *)
Definition: argv_split.c:63
VBUF * vbuf_print(VBUF *bp, const char *format, va_list ap)
Definition: vbuf_print.c:147
#define VBUF_SNPRINTF(bp, sz, fmt, arg)
Definition: vbuf_print.c:108
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
#define VSTRING_ADDNUM(vp, n)
Definition: vbuf_print.c:134
void msg_vstream_init(const char *name, VSTREAM *vp)
Definition: msg_vstream.c:77
ssize_t argc
Definition: argv.h:19
#define VSTREAM_ERR
Definition: vstream.h:68
void msg_info(const char *fmt,...)
Definition: msg.c:199