chiark / gitweb /
Rename for consistency's sake.
[mLib] / dputf.c
CommitLineData
002eaee3 1/* -*-c-*-
2 *
5a18a126 3 * $Id: dputf.c,v 1.5 2002/01/13 13:30:48 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 $
5a18a126 33 * Revision 1.5 2002/01/13 13:30:48 mdw
34 * Change interface for @dstr_vputf@.
35 *
5d45633f 36 * Revision 1.4 2001/06/22 19:35:29 mdw
37 * Find out whether @<float.h>@ exists (hack for uC-Linux).
38 *
393cf1d9 39 * Revision 1.3 2001/01/20 12:06:01 mdw
40 * Define flags with macros, to ensure unsignedness.
41 *
e12e2b8d 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 *
002eaee3 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>
002eaee3 53#include <math.h>
54#include <stdarg.h>
55#include <stdio.h>
56#include <stdlib.h>
57#include <string.h>
58
5d45633f 59#ifdef HAVE_FLOAT_H
60# include <float.h>
61#endif
62
002eaee3 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
5a18a126 80 * @va_list *ap@ = argument handle
002eaee3 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
5a18a126 88int dstr_vputf(dstr *d, const char *p, va_list *ap)
002eaee3 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
393cf1d9 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
002eaee3 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 == '*') {
5a18a126 170 *ip = va_arg(*ap, int);
002eaee3 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,
5a18a126 197 va_arg(*ap, unsigned long));
002eaee3 198 else
199 d->len += sprintf(d->buf + d->len, dd.buf,
5a18a126 200 va_arg(*ap, unsigned int));
002eaee3 201 goto formatted;
202
203 case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
5d45633f 204#ifdef HAVE_FLOAT_H
002eaee3 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 }
393cf1d9 212 if (!(f & f_prec))
002eaee3 213 prec = 6;
393cf1d9 214 else
002eaee3 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,
5a18a126 221 va_arg(*ap, long double));
002eaee3 222 else
223 d->len += sprintf(d->buf + d->len, dd.buf,
5a18a126 224 va_arg(*ap, double));
002eaee3 225 goto formatted;
5d45633f 226#else
227 DPUTS(d, "<no float support>");
228#endif
002eaee3 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,
5a18a126 237 va_arg(*ap, unsigned));
002eaee3 238 goto formatted;
239
240 case 's': {
5a18a126 241 const char *s = va_arg(*ap, const char *);
002eaee3 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,
5a18a126 263 va_arg(*ap, const void *));
002eaee3 264 goto formatted;
265
266 case 'n':
267 if (f & f_long)
5a18a126 268 *va_arg(*ap, long *) = (long)(d->len - n);
002eaee3 269 else if (f & f_short)
5a18a126 270 *va_arg(*ap, short *) = (short)(d->len - n);
002eaee3 271 else
5a18a126 272 *va_arg(*ap, int *) = (int)(d->len - n);
002eaee3 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;
393cf1d9 287
288#undef f_short
289#undef f_long
290#undef f_Long
291#undef f_wd
292#undef f_prec
002eaee3 293 }
294
295 DPUTM(d, q, p - q);
296finished:
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
316int dstr_putf(dstr *d, const char *p, ...)
317{
318 int n;
319 va_list ap;
320 va_start(ap, p);
5a18a126 321 n = dstr_vputf(d, p, &ap);
002eaee3 322 va_end(ap);
323 return (n);
324}
325
326/*----- That's all, folks -------------------------------------------------*/