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