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