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