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