chiark / gitweb /
buf.c: Step over terminating null byte.
[mLib] / dputf.c
1 /* -*-c-*-
2  *
3  * $Id$
4  *
5  * `printf'-style formatting for dynamic strings
6  *
7  * (c) 1999 Straylight/Edgeware
8  */
9
10 /*----- Licensing notice --------------------------------------------------*
11  *
12  * This file is part of the mLib utilities library.
13  *
14  * mLib is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU Library General Public License as
16  * published by the Free Software Foundation; either version 2 of the
17  * License, or (at your option) any later version.
18  *
19  * mLib is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU Library General Public License for more details.
23  *
24  * You should have received a copy of the GNU Library General Public
25  * License along with mLib; if not, write to the Free
26  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
27  * MA 02111-1307, USA.
28  */
29
30 /*----- Header files ------------------------------------------------------*/
31
32 #include "config.h"
33
34 #include <ctype.h>
35 #include <math.h>
36 #include <stdarg.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40
41 #ifdef HAVE_FLOAT_H
42 #  include <float.h>
43 #endif
44
45 #include "dstr.h"
46
47 /*----- Tunable constants -------------------------------------------------*/
48
49 /*
50  * For each format specifier, at least @DSTR_PUTFSTEP@ bytes are ensured
51  * before writing the formatted result.
52  */
53
54 #define DSTR_PUTFSTEP 64                /* Buffer size for @putf@ */
55
56 /*----- Main code ---------------------------------------------------------*/
57
58 /* --- @dstr_vputf@ --- *
59  *
60  * Arguments:   @dstr *d@ = pointer to a dynamic string block
61  *              @const char *p@ = pointer to @printf@-style format string
62  *              @va_list *ap@ = argument handle
63  *
64  * Returns:     The number of characters written to the string.
65  *
66  * Use:         As for @dstr_putf@, but may be used as a back-end to user-
67  *              supplied functions with @printf@-style interfaces.
68  */
69
70 int dstr_vputf(dstr *d, const char *p, va_list *ap)
71 {
72   const char *q = p;
73   size_t n = d->len;
74   size_t sz;
75   dstr dd = DSTR_INIT;
76
77   while (*p) {
78     unsigned f;
79     int wd, prec;
80
81 #define f_short 1u
82 #define f_long 2u
83 #define f_Long 4u
84 #define f_wd 8u
85 #define f_prec 16u
86
87     /* --- Most stuff gets passed on through --- */
88
89     if (*p != '%') {
90       p++;
91       continue;
92     }
93
94     /* --- Dump out what's between @q@ and @p@ --- */
95
96     DPUTM(d, q, p - q);
97     p++;
98
99     /* --- Sort out the various silly flags and things --- */
100
101     DPUTC(&dd, '%');
102     f = 0;
103     sz = DSTR_PUTFSTEP;
104
105     for (;;) {
106       switch (*p) {
107
108         /* --- Various simple flags --- */
109
110         case '+':
111         case '-':
112         case '#':
113         case '0':
114           goto putch;
115         case 'h':
116           f |= f_short;
117           goto putch;
118         case 'l':
119           f |= f_long;
120           goto putch;
121         case 'L':
122           f |= f_Long;
123           goto putch;
124         case 0:
125           goto finished;
126
127         /* --- Field widths and precision specifiers --- */
128
129         {
130           int *ip;
131
132         case '.':
133           DPUTC(&dd, '.');
134           ip = &prec;
135           f |= f_prec;
136           p++;
137           goto getnum;
138         case '*':
139           ip = &wd;
140           f |= f_wd;
141           goto getnum;
142         default:
143           if (isdigit((unsigned char)*p)) {
144             f |= f_wd;
145             ip = &wd;
146             goto getnum;
147           }
148           DPUTC(d, *p);
149           goto formatted;
150         getnum:
151           *ip = 0;
152           if (*p == '*') {
153             *ip = va_arg(*ap, int);
154             DENSURE(&dd, DSTR_PUTFSTEP);
155             dd.len += sprintf(dd.buf + dd.len, "%i", *ip);
156           } else {
157             *ip = *p - '0';
158             DPUTC(&dd, *p);
159             p++;
160             while (isdigit((unsigned char)*p)) {
161               DPUTC(&dd, *p);
162               *ip = 10 * *ip + *p++ - '0';
163             }
164           }
165           break;
166         }
167
168         /* --- Output formatting --- */
169
170         case 'd': case 'i': case 'x': case 'X': case 'o': case 'u':
171           DPUTC(&dd, *p);
172           DPUTZ(&dd);
173           if ((f & f_prec) && prec + 16 > sz)
174             sz = prec + 16;
175           if ((f & f_wd) && wd + 1> sz)
176             sz = wd + 1;
177           DENSURE(d, sz);
178           if (f & f_long)
179             d->len += sprintf(d->buf + d->len, dd.buf,
180                               va_arg(*ap, unsigned long));
181           else
182             d->len += sprintf(d->buf + d->len, dd.buf,
183                               va_arg(*ap, unsigned int));
184           goto formatted;
185
186         case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
187 #ifdef HAVE_FLOAT_H
188           DPUTC(&dd, *p);
189           DPUTZ(&dd);
190           if (*p == 'f') {
191             size_t mx = (f & f_Long ? LDBL_MAX_10_EXP : DBL_MAX_10_EXP) + 16;
192             if (mx > sz)
193               sz = mx;
194           }
195           if (!(f & f_prec))
196             prec = 6;
197           else
198             sz += prec + 16;
199           if ((f & f_wd) && wd + 1 > sz)
200             sz = wd + 1;
201           DENSURE(d, sz);
202           if (f & f_Long)
203             d->len += sprintf(d->buf + d->len, dd.buf,
204                               va_arg(*ap, long double));
205           else
206             d->len += sprintf(d->buf + d->len, dd.buf,
207                               va_arg(*ap, double));
208           goto formatted;
209 #else
210           DPUTS(d, "<no float support>");
211 #endif
212
213         case 'c':
214           DPUTC(&dd, *p);
215           DPUTZ(&dd);
216           if ((f & f_wd) && wd + 1> sz)
217             sz = wd + 1;
218           DENSURE(d, sz);
219           d->len += sprintf(d->buf + d->len, dd.buf,
220                             va_arg(*ap, unsigned));
221           goto formatted;
222
223         case 's': {
224           const char *s = va_arg(*ap, const char *);
225           sz = strlen(s);
226           DPUTC(&dd, *p);
227           DPUTZ(&dd);
228           if (f & f_prec)
229             sz = prec;
230           if ((f & f_wd) && wd > sz)
231             sz = wd;
232           DENSURE(d, sz + 1);
233           d->len += sprintf(d->buf + d->len, dd.buf, s);
234           goto formatted;
235         }
236
237         case 'p':
238           DPUTC(&dd, *p);
239           DPUTZ(&dd);
240           if ((f & f_prec) && prec + 16 > sz)
241             sz = prec + 16;
242           if ((f & f_wd) && wd + 1> sz)
243             sz = wd + 1;
244           DENSURE(d, sz);
245           d->len += sprintf(d->buf + d->len, dd.buf,
246                             va_arg(*ap, const void *));
247           goto formatted;
248
249         case 'n':
250           if (f & f_long)
251             *va_arg(*ap, long *) = (long)(d->len - n);
252           else if (f & f_short)
253             *va_arg(*ap, short *) = (short)(d->len - n);
254           else
255             *va_arg(*ap, int *) = (int)(d->len - n);
256           goto formatted;
257
258         /* --- Other random stuff --- */
259
260         putch:
261           DPUTC(&dd, *p);
262           p++;
263           break;
264       }
265     }
266
267   formatted:
268     DRESET(&dd);
269     q = ++p;
270
271 #undef f_short
272 #undef f_long
273 #undef f_Long
274 #undef f_wd
275 #undef f_prec
276   }
277
278   DPUTM(d, q, p - q);
279 finished:
280   DPUTZ(d);
281   DDESTROY(&dd);
282   return (d->len - n);
283 }
284
285 /* --- @dstr_putf@ --- *
286  *
287  * Arguments:   @dstr *d@ = pointer to a dynamic string block
288  *              @const char *p@ = pointer to @printf@-style format string
289  *              @...@ = argument handle
290  *
291  * Returns:     The number of characters written to the string.
292  *
293  * Use:         Writes a piece of text to a dynamic string, doing @printf@-
294  *              style substitutions as it goes.  Intended to be robust if
295  *              faced with malicious arguments, but not if the format string
296  *              itself is malicious.
297  */
298
299 int dstr_putf(dstr *d, const char *p, ...)
300 {
301   int n;
302   va_list ap;
303   va_start(ap, p);
304   n = dstr_vputf(d, p, &ap);
305   va_end(ap);
306   return (n);
307 }
308
309 /*----- That's all, folks -------------------------------------------------*/