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