~ubuntu-branches/ubuntu/vivid/postfix/vivid-proposed

« back to all changes in this revision

Viewing changes to src/util/vbuf_print.c

  • Committer: Bazaar Package Importer
  • Author(s): LaMont Jones
  • Date: 2005-02-27 09:33:07 UTC
  • Revision ID: james.westby@ubuntu.com-20050227093307-cn789t27ibnlh6tf
Tags: upstream-2.1.5
ImportĀ upstreamĀ versionĀ 2.1.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
/* LICENSE
 
24
/* .ad
 
25
/* .fi
 
26
/*      The Secure Mailer license must be distributed with this software.
 
27
/* AUTHOR(S)
 
28
/*      Wietse Venema
 
29
/*      IBM T.J. Watson Research
 
30
/*      P.O. Box 704
 
31
/*      Yorktown Heights, NY 10598, USA
 
32
/*--*/
 
33
 
 
34
/* System library. */
 
35
 
 
36
#include "sys_defs.h"
 
37
#include <stdlib.h>                     /* 44BSD stdarg.h uses abort() */
 
38
#include <stdarg.h>
 
39
#include <string.h>
 
40
#include <ctype.h>
 
41
#include <stdlib.h>                     /* 44bsd stdarg.h uses abort() */
 
42
#include <stdio.h>                      /* sprintf() prototype */
 
43
#include <float.h>                      /* range of doubles */
 
44
#include <errno.h>
 
45
 
 
46
/* Application-specific. */
 
47
 
 
48
#include "msg.h"
 
49
#include "vbuf.h"
 
50
#include "vstring.h"
 
51
#include "vbuf_print.h"
 
52
 
 
53
 /*
 
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.
 
57
  * 
 
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.
 
62
  * 
 
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
 
67
  * expensive.
 
68
  * 
 
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
 
75
  * just to be sure.
 
76
  */
 
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 *))
 
80
 
 
81
 /*
 
82
  * Helper macros... Note that there is no need to check the result from
 
83
  * VSTRING_SPACE() because that always succeeds or never returns.
 
84
  */
 
85
#define VBUF_SKIP(bp)   { \
 
86
        while ((bp)->cnt > 0 && *(bp)->ptr) \
 
87
            (bp)->ptr++, (bp)->cnt--; \
 
88
    }
 
89
 
 
90
#define VSTRING_ADDNUM(vp, n) { \
 
91
        VSTRING_SPACE(vp, INT_SPACE); \
 
92
        sprintf(vstring_end(vp), "%d", n); \
 
93
        VBUF_SKIP(&vp->vbuf); \
 
94
    }
 
95
 
 
96
#define VBUF_STRCAT(bp, s) { \
 
97
        unsigned char *_cp = (unsigned char *) (s); \
 
98
        int ch; \
 
99
        while ((ch = *_cp++) != 0) \
 
100
            VBUF_PUT((bp), ch); \
 
101
    }
 
102
 
 
103
/* vbuf_print - format string, vsprintf-like interface */
 
104
 
 
105
VBUF   *vbuf_print(VBUF *bp, const char *format, va_list ap)
 
106
{
 
107
    static VSTRING *fmt;                /* format specifier */
 
108
    unsigned char *cp;
 
109
    unsigned width;                     /* field width */
 
110
    unsigned prec;                      /* numerical precision */
 
111
    unsigned long_flag;                 /* long or plain integer */
 
112
    int     ch;
 
113
    char   *s;
 
114
 
 
115
    /*
 
116
     * Assume that format strings are short.
 
117
     */
 
118
    if (fmt == 0)
 
119
        fmt = vstring_alloc(INT_SPACE);
 
120
 
 
121
    /*
 
122
     * Iterate over characters in the format string, picking up arguments
 
123
     * when format specifiers are found.
 
124
     */
 
125
    for (cp = (unsigned char *) format; *cp; cp++) {
 
126
        if (*cp != '%') {
 
127
            VBUF_PUT(bp, *cp);                  /* ordinary character */
 
128
        } else if (cp[1] == '%') {
 
129
            VBUF_PUT(bp, *cp++);                /* %% becomes % */
 
130
        } else {
 
131
 
 
132
            /*
 
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:
 
138
             * 
 
139
             * %-?0?([0-9]+|\*)?\.?([0-9]+|\*)?l?[a-zA-Z]
 
140
             * 
 
141
             * which includes some combinations that do not make sense. Garbage
 
142
             * in, garbage out.
 
143
             */
 
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);
 
155
                cp++;
 
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);
 
160
                }
 
161
            }
 
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);
 
167
                cp++;
 
168
            } else {                            /* hard-coded precision */
 
169
                for (prec = 0; ISDIGIT(ch = *cp); cp++) {
 
170
                    prec = prec * 10 + ch - '0';
 
171
                    VSTRING_ADDCH(fmt, ch);
 
172
                }
 
173
            }
 
174
            if ((long_flag = (*cp == 'l')) != 0)/* long whatever */
 
175
                VSTRING_ADDCH(fmt, *cp++);
 
176
            if (*cp == 0)                       /* premature end, punt */
 
177
                break;
 
178
            VSTRING_ADDCH(fmt, *cp);            /* type (checked below) */
 
179
            VSTRING_TERMINATE(fmt);             /* null terminate */
 
180
 
 
181
            /*
 
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
 
185
             * buffer.
 
186
             */
 
187
            switch (*cp) {
 
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))
 
192
                        return (bp);
 
193
                    sprintf((char *) bp->ptr, vstring_str(fmt), s);
 
194
                    VBUF_SKIP(bp);
 
195
                } else {
 
196
                    VBUF_STRCAT(bp, s);
 
197
                }
 
198
                break;
 
199
            case 'c':                           /* integral-valued argument */
 
200
            case 'd':
 
201
            case 'u':
 
202
            case 'o':
 
203
            case 'x':
 
204
            case 'X':
 
205
                if (VBUF_SPACE(bp, (width > prec ? width : prec) + INT_SPACE))
 
206
                    return (bp);
 
207
                if (long_flag)
 
208
                    sprintf((char *) bp->ptr, vstring_str(fmt), va_arg(ap, long));
 
209
                else
 
210
                    sprintf((char *) bp->ptr, vstring_str(fmt), va_arg(ap, int));
 
211
                VBUF_SKIP(bp);
 
212
                break;
 
213
            case 'e':                           /* float-valued argument */
 
214
            case 'f':
 
215
            case 'g':
 
216
                if (VBUF_SPACE(bp, (width > prec ? width : prec) + DBL_SPACE))
 
217
                    return (bp);
 
218
                sprintf((char *) bp->ptr, vstring_str(fmt), va_arg(ap, double));
 
219
                VBUF_SKIP(bp);
 
220
                break;
 
221
            case 'm':
 
222
                VBUF_STRCAT(bp, strerror(errno));
 
223
                break;
 
224
            case 'p':
 
225
                if (VBUF_SPACE(bp, (width > prec ? width : prec) + PTR_SPACE))
 
226
                    return (bp);
 
227
                sprintf((char *) bp->ptr, vstring_str(fmt), va_arg(ap, char *));
 
228
                VBUF_SKIP(bp);
 
229
                break;
 
230
            default:                            /* anything else is bad */
 
231
                msg_panic("vbuf_print: unknown format type: %c", *cp);
 
232
                /* NOTREACHED */
 
233
                break;
 
234
            }
 
235
        }
 
236
    }
 
237
    return (bp);
 
238
}