chiark / gitweb /
sym-gtest: Don't include empty words.
[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
32#include <ctype.h>
002eaee3 33#include <math.h>
34#include <stdarg.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38
5d45633f 39#ifdef HAVE_FLOAT_H
40# include <float.h>
41#endif
42
002eaee3 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
5a18a126 60 * @va_list *ap@ = argument handle
002eaee3 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
5a18a126 68int dstr_vputf(dstr *d, const char *p, va_list *ap)
002eaee3 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
393cf1d9 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
002eaee3 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;
7c3f378b 134 p++;
002eaee3 135 goto getnum;
136 case '*':
137 ip = &wd;
138 f |= f_wd;
139 goto getnum;
140 default:
141 if (isdigit((unsigned char)*p)) {
142 f |= f_wd;
143 ip = &wd;
144 goto getnum;
145 }
146 DPUTC(d, *p);
147 goto formatted;
d4efbcd9 148 getnum:
002eaee3 149 *ip = 0;
150 if (*p == '*') {
5a18a126 151 *ip = va_arg(*ap, int);
002eaee3 152 DENSURE(&dd, DSTR_PUTFSTEP);
153 dd.len += sprintf(dd.buf + dd.len, "%i", *ip);
154 } else {
155 *ip = *p - '0';
156 DPUTC(&dd, *p);
157 p++;
158 while (isdigit((unsigned char)*p)) {
159 DPUTC(&dd, *p);
160 *ip = 10 * *ip + *p++ - '0';
161 }
162 }
163 break;
164 }
165
166 /* --- Output formatting --- */
167
168 case 'd': case 'i': case 'x': case 'X': case 'o': case 'u':
169 DPUTC(&dd, *p);
170 DPUTZ(&dd);
171 if ((f & f_prec) && prec + 16 > sz)
172 sz = prec + 16;
173 if ((f & f_wd) && wd + 1> sz)
174 sz = wd + 1;
175 DENSURE(d, sz);
176 if (f & f_long)
177 d->len += sprintf(d->buf + d->len, dd.buf,
5a18a126 178 va_arg(*ap, unsigned long));
002eaee3 179 else
180 d->len += sprintf(d->buf + d->len, dd.buf,
5a18a126 181 va_arg(*ap, unsigned int));
002eaee3 182 goto formatted;
183
184 case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
5d45633f 185#ifdef HAVE_FLOAT_H
002eaee3 186 DPUTC(&dd, *p);
187 DPUTZ(&dd);
188 if (*p == 'f') {
189 size_t mx = (f & f_Long ? LDBL_MAX_10_EXP : DBL_MAX_10_EXP) + 16;
190 if (mx > sz)
191 sz = mx;
192 }
393cf1d9 193 if (!(f & f_prec))
002eaee3 194 prec = 6;
393cf1d9 195 else
d4efbcd9 196 sz += prec + 16;
002eaee3 197 if ((f & f_wd) && wd + 1 > sz)
198 sz = wd + 1;
199 DENSURE(d, sz);
200 if (f & f_Long)
201 d->len += sprintf(d->buf + d->len, dd.buf,
5a18a126 202 va_arg(*ap, long double));
002eaee3 203 else
204 d->len += sprintf(d->buf + d->len, dd.buf,
5a18a126 205 va_arg(*ap, double));
002eaee3 206 goto formatted;
5d45633f 207#else
208 DPUTS(d, "<no float support>");
209#endif
002eaee3 210
211 case 'c':
212 DPUTC(&dd, *p);
213 DPUTZ(&dd);
214 if ((f & f_wd) && wd + 1> sz)
215 sz = wd + 1;
216 DENSURE(d, sz);
217 d->len += sprintf(d->buf + d->len, dd.buf,
5a18a126 218 va_arg(*ap, unsigned));
002eaee3 219 goto formatted;
220
221 case 's': {
5a18a126 222 const char *s = va_arg(*ap, const char *);
002eaee3 223 sz = strlen(s);
224 DPUTC(&dd, *p);
225 DPUTZ(&dd);
226 if (f & f_prec)
227 sz = prec;
228 if ((f & f_wd) && wd > sz)
229 sz = wd;
230 DENSURE(d, sz + 1);
231 d->len += sprintf(d->buf + d->len, dd.buf, s);
232 goto formatted;
233 }
234
235 case 'p':
236 DPUTC(&dd, *p);
237 DPUTZ(&dd);
238 if ((f & f_prec) && prec + 16 > sz)
239 sz = prec + 16;
240 if ((f & f_wd) && wd + 1> sz)
241 sz = wd + 1;
242 DENSURE(d, sz);
243 d->len += sprintf(d->buf + d->len, dd.buf,
5a18a126 244 va_arg(*ap, const void *));
002eaee3 245 goto formatted;
246
247 case 'n':
248 if (f & f_long)
5a18a126 249 *va_arg(*ap, long *) = (long)(d->len - n);
002eaee3 250 else if (f & f_short)
5a18a126 251 *va_arg(*ap, short *) = (short)(d->len - n);
002eaee3 252 else
5a18a126 253 *va_arg(*ap, int *) = (int)(d->len - n);
002eaee3 254 goto formatted;
255
256 /* --- Other random stuff --- */
257
258 putch:
259 DPUTC(&dd, *p);
260 p++;
261 break;
262 }
263 }
264
265 formatted:
266 DRESET(&dd);
267 q = ++p;
393cf1d9 268
269#undef f_short
270#undef f_long
271#undef f_Long
272#undef f_wd
273#undef f_prec
002eaee3 274 }
275
276 DPUTM(d, q, p - q);
277finished:
278 DPUTZ(d);
279 DDESTROY(&dd);
280 return (d->len - n);
281}
282
283/* --- @dstr_putf@ --- *
284 *
285 * Arguments: @dstr *d@ = pointer to a dynamic string block
286 * @const char *p@ = pointer to @printf@-style format string
287 * @...@ = argument handle
288 *
289 * Returns: The number of characters written to the string.
290 *
291 * Use: Writes a piece of text to a dynamic string, doing @printf@-
292 * style substitutions as it goes. Intended to be robust if
293 * faced with malicious arguments, but not if the format string
294 * itself is malicious.
295 */
296
297int dstr_putf(dstr *d, const char *p, ...)
298{
299 int n;
300 va_list ap;
301 va_start(ap, p);
5a18a126 302 n = dstr_vputf(d, p, &ap);
002eaee3 303 va_end(ap);
304 return (n);
305}
306
307/*----- That's all, folks -------------------------------------------------*/