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