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