chiark / gitweb /
Lots of new source files.
[mLib] / dputf.c
1 /* -*-c-*-
2  *
3  * $Id: dputf.c,v 1.1 1999/10/04 21:44:47 mdw Exp $
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 /*----- Revision history --------------------------------------------------* 
31  *
32  * $Log: dputf.c,v $
33  * Revision 1.1  1999/10/04 21:44:47  mdw
34  * Move `dstr_putf' and `dstr_vputf' into a separate source file.
35  *
36  */
37
38 /*----- Header files ------------------------------------------------------*/
39
40 #include <ctype.h>
41 #include <float.h>
42 #include <math.h>
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47
48 #include "dstr.h"
49
50 /*----- Tunable constants -------------------------------------------------*/
51
52 /*
53  * For each format specifier, at least @DSTR_PUTFSTEP@ bytes are ensured
54  * before writing the formatted result.
55  */
56
57 #define DSTR_PUTFSTEP 64                /* Buffer size for @putf@ */
58
59 /*----- Main code ---------------------------------------------------------*/
60
61 /* --- @dstr_vputf@ --- *
62  *
63  * Arguments:   @dstr *d@ = pointer to a dynamic string block
64  *              @const char *p@ = pointer to @printf@-style format string
65  *              @va_list ap@ = argument handle
66  *
67  * Returns:     The number of characters written to the string.
68  *
69  * Use:         As for @dstr_putf@, but may be used as a back-end to user-
70  *              supplied functions with @printf@-style interfaces.
71  */
72
73 int dstr_vputf(dstr *d, const char *p, va_list ap)
74 {
75   const char *q = p;
76   size_t n = d->len;
77   size_t sz;
78   dstr dd = DSTR_INIT;
79
80   while (*p) {
81     unsigned f;
82     int wd, prec;
83
84     enum {
85       f_short = 1,
86       f_long = 2,
87       f_Long = 4,
88       f_wd = 8,
89       f_prec = 16
90     };
91
92     /* --- Most stuff gets passed on through --- */
93
94     if (*p != '%') {
95       p++;
96       continue;
97     }
98
99     /* --- Dump out what's between @q@ and @p@ --- */
100
101     DPUTM(d, q, p - q);
102     p++;
103
104     /* --- Sort out the various silly flags and things --- */
105
106     DPUTC(&dd, '%');
107     f = 0;
108     sz = DSTR_PUTFSTEP;
109
110     for (;;) {
111       switch (*p) {
112
113         /* --- Various simple flags --- */
114
115         case '+':
116         case '-':
117         case '#':
118         case '0':
119           goto putch;
120         case 'h':
121           f |= f_short;
122           goto putch;
123         case 'l':
124           f |= f_long;
125           goto putch;
126         case 'L':
127           f |= f_Long;
128           goto putch;
129         case 0:
130           goto finished;
131
132         /* --- Field widths and precision specifiers --- */
133
134         {
135           int *ip;
136
137         case '.':
138           DPUTC(&dd, '.');
139           ip = &prec;
140           f |= f_prec;
141           goto getnum;
142         case '*':
143           ip = &wd;
144           f |= f_wd;
145           goto getnum;
146         default:
147           if (isdigit((unsigned char)*p)) {
148             f |= f_wd;
149             ip = &wd;
150             goto getnum;
151           }
152           DPUTC(d, *p);
153           goto formatted;
154         getnum:
155           *ip = 0;
156           if (*p == '*') {
157             *ip = va_arg(ap, int);
158             DENSURE(&dd, DSTR_PUTFSTEP);
159             dd.len += sprintf(dd.buf + dd.len, "%i", *ip);
160           } else {
161             *ip = *p - '0';
162             DPUTC(&dd, *p);
163             p++;
164             while (isdigit((unsigned char)*p)) {
165               DPUTC(&dd, *p);
166               *ip = 10 * *ip + *p++ - '0';
167             }
168           }
169           break;
170         }
171
172         /* --- Output formatting --- */
173
174         case 'd': case 'i': case 'x': case 'X': case 'o': case 'u':
175           DPUTC(&dd, *p);
176           DPUTZ(&dd);
177           if ((f & f_prec) && prec + 16 > sz)
178             sz = prec + 16;
179           if ((f & f_wd) && wd + 1> sz)
180             sz = wd + 1;
181           DENSURE(d, sz);
182           if (f & f_long)
183             d->len += sprintf(d->buf + d->len, dd.buf,
184                               va_arg(ap, unsigned long));
185           else
186             d->len += sprintf(d->buf + d->len, dd.buf,
187                               va_arg(ap, unsigned int));
188           goto formatted;
189
190         case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
191           DPUTC(&dd, *p);
192           DPUTZ(&dd);
193           if (*p == 'f') {
194             size_t mx = (f & f_Long ? LDBL_MAX_10_EXP : DBL_MAX_10_EXP) + 16;
195             if (mx > sz)
196               sz = mx;
197           }
198           if ((f & f_prec) == 0)
199             prec = 6;
200           if ((f & f_prec))
201             sz += prec + 16;        
202           if ((f & f_wd) && wd + 1 > sz)
203             sz = wd + 1;
204           DENSURE(d, sz);
205           if (f & f_Long)
206             d->len += sprintf(d->buf + d->len, dd.buf,
207                               va_arg(ap, long double));
208           else
209             d->len += sprintf(d->buf + d->len, dd.buf,
210                               va_arg(ap, double));
211           goto formatted;
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 char));
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
272   DPUTM(d, q, p - q);
273 finished:
274   DPUTZ(d);
275   DDESTROY(&dd);
276   return (d->len - n);
277 }
278
279 /* --- @dstr_putf@ --- *
280  *
281  * Arguments:   @dstr *d@ = pointer to a dynamic string block
282  *              @const char *p@ = pointer to @printf@-style format string
283  *              @...@ = argument handle
284  *
285  * Returns:     The number of characters written to the string.
286  *
287  * Use:         Writes a piece of text to a dynamic string, doing @printf@-
288  *              style substitutions as it goes.  Intended to be robust if
289  *              faced with malicious arguments, but not if the format string
290  *              itself is malicious.
291  */
292
293 int dstr_putf(dstr *d, const char *p, ...)
294 {
295   int n;
296   va_list ap;
297   va_start(ap, p);
298   n = dstr_vputf(d, p, ap);
299   va_end(ap);
300   return (n);
301 }
302
303 /*----- That's all, folks -------------------------------------------------*/