chiark / gitweb /
(dstr_vputf): Don't try calling @va_arg@ on things @char@-sized.
[mLib] / dputf.c
CommitLineData
002eaee3 1/* -*-c-*-
2 *
e12e2b8d 3 * $Id: dputf.c,v 1.2 2000/08/15 21:26:45 mdw Exp $
002eaee3 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 $
e12e2b8d 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 *
002eaee3 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
76int 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,
e12e2b8d 223 va_arg(ap, unsigned));
002eaee3 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);
276finished:
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
296int 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 -------------------------------------------------*/