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