chiark / gitweb /
Comment out argument in structure definition.
[mLib] / dstr.c
CommitLineData
0875b58f 1/* -*-c-*-
2 *
0fbb1412 3 * $Id: dstr.c,v 1.8 1999/06/01 09:47:52 mdw Exp $
0875b58f 4 *
5 * Handle dynamically growing strings
6 *
7 * (c) 1998 Straylight/Edgeware
8 */
9
c846879c 10/*----- Licensing notice --------------------------------------------------*
0875b58f 11 *
12 * This file is part of the mLib utilities library.
13 *
14 * mLib is free software; you can redistribute it and/or modify
c846879c 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 *
0875b58f 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
c846879c 22 * GNU Library General Public License for more details.
23 *
24 * You should have received a copy of the GNU Library General Public
0bd98442 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.
0875b58f 28 */
29
30/*----- Revision history --------------------------------------------------*
31 *
32 * $Log: dstr.c,v $
0fbb1412 33 * Revision 1.8 1999/06/01 09:47:52 mdw
34 * Fix nasty bugs in `dstr_vputf'.
35 *
0bfb1431 36 * Revision 1.7 1999/05/21 22:14:30 mdw
37 * Take advantage of the new dynamic string macros.
38 *
fb467548 39 * Revision 1.6 1999/05/21 08:38:33 mdw
40 * Implement some more functions in terms of macros.
41 *
96c5fe33 42 * Revision 1.5 1999/05/13 22:47:57 mdw
43 * Misc documentation fixes. Change `-ise' to `-ize' throughout.
44 *
0bd98442 45 * Revision 1.4 1999/05/06 19:51:35 mdw
46 * Reformatted the LGPL notice a little bit.
47 *
c846879c 48 * Revision 1.3 1999/05/05 18:50:31 mdw
49 * Change licensing conditions to LGPL.
50 *
00c7638b 51 * Revision 1.2 1998/12/15 23:53:22 mdw
52 * New functions `dstr_putf' and `dstr_vputf' which do `printf'-style
53 * formatting in a safe way.
54 *
55 * Revision 1.1.1.1 1998/06/17 23:44:42 mdw
56 * Initial version of mLib
0875b58f 57 *
58 */
59
60/*----- Header files ------------------------------------------------------*/
61
00c7638b 62#include <ctype.h>
63#include <float.h>
64#include <math.h>
65#include <stdarg.h>
0875b58f 66#include <stdio.h>
67#include <stdlib.h>
68#include <string.h>
69
70#include "alloc.h"
71#include "dstr.h"
72
73/*----- Tunable constants -------------------------------------------------*/
74
75#define DSTR_INITSZ 256 /* Initial buffer size */
00c7638b 76#define DSTR_INCSZ 4096 /* Threshhold for doubling */
77#define DSTR_PUTFSTEP 64 /* Buffer size for @putf@ */
0875b58f 78
79/*----- Main code ---------------------------------------------------------*/
80
81/* --- @dstr_create@ --- *
82 *
83 * Arguments: @dstr *d@ = pointer to a dynamic string block
84 *
85 * Returns: ---
86 *
96c5fe33 87 * Use: Initializes a dynamic string.
0875b58f 88 */
89
fb467548 90void dstr_create(dstr *d) { DCREATE(d); }
0875b58f 91
92/* --- @dstr_destroy@ --- *
93 *
94 * Arguments: @dstr *d@ = pointer to a dynamic string block
95 *
96 * Returns: ---
97 *
98 * Use: Reclaims the space used by a dynamic string.
99 */
100
fb467548 101void dstr_destroy(dstr *d) { DDESTROY(d); }
0875b58f 102
103/* --- @dstr_reset@ --- *
104 *
105 * Arguments: @dstr *d@ = pointer to a dynaimc string block
106 *
107 * Returns: ---
108 *
109 * Use: Resets a string so that new data gets put at the beginning.
110 */
111
fb467548 112void dstr_reset(dstr *d) { DRESET(d); }
0875b58f 113
114/* --- @dstr_ensure@ --- *
115 *
116 * Arguments: @dstr *d@ = pointer to a dynamic string block
117 * @size_t sz@ = amount of free space to ensure
118 *
119 * Returns: ---
120 *
121 * Use: Ensures that at least @sz@ bytes are available in the
122 * given string.
123 */
124
125void dstr_ensure(dstr *d, size_t sz)
126{
127 size_t rq = d->len + sz;
128 size_t nsz;
129
130 /* --- If we have enough space, just leave it --- */
131
132 if (rq <= d->sz)
133 return;
134
135 /* --- Grow the buffer --- *
136 *
137 * For small buffers, just double the size. For big buffers, make them
138 * a multiple of some suitably large chunk size.
139 */
140
141 nsz = d->sz;
142
143 do {
144 if (nsz == 0)
145 nsz = DSTR_INITSZ;
146 else if (d->sz < 0x1000)
147 nsz <<= 1;
148 else
149 nsz = (rq + 0x0fff) & ~0x0fff;
150 } while (rq > nsz);
151
152 if (d->buf)
153 d->buf = xrealloc(d->buf, nsz);
154 else
155 d->buf = xmalloc(nsz);
156 d->sz = nsz;
157}
158
159/* --- @dstr_putc@ --- *
160 *
161 * Arguments: @dstr *d@ = pointer to a dynamic string block
162 * @char ch@ = character to append
163 *
164 * Returns: ---
165 *
166 * Use: Appends a character to a string.
167 */
168
fb467548 169void dstr_putc(dstr *d, char ch) { DPUTC(d, ch); }
0875b58f 170
171/* --- @dstr_putz@ --- *
172 *
173 * Arguments: @dstr *d@ = pointer to a dynamic string block
174 *
175 * Returns: ---
176 *
177 * Use: Appends a null byte to a string. The null byte does not
178 * contribute to the string's length, and will be overwritten
179 * by subsequent `put' operations.
180 */
181
fb467548 182void dstr_putz(dstr *d) { DPUTZ(d); }
0875b58f 183
184/* --- @dstr_puts@ --- *
185 *
186 * Arguments: @dstr *d@ = pointer to a dynamic string block
187 * @const char *s@ = pointer to string to append
188 *
189 * Returns: ---
190 *
191 * Use: Appends a character string to a string. A trailing null
192 * byte is added, as for @dstr_putz@.
193 */
194
fb467548 195void dstr_puts(dstr *d, const char *s) { DPUTS(d, s); }
0875b58f 196
00c7638b 197/* --- @dstr_vputf@ --- *
198 *
199 * Arguments: @dstr *d@ = pointer to a dynamic string block
200 * @const char *p@ = pointer to @printf@-style format string
201 * @va_list ap@ = argument handle
202 *
96c5fe33 203 * Returns: The number of characters written to the string.
00c7638b 204 *
205 * Use: As for @dstr_putf@, but may be used as a back-end to user-
206 * supplied functions with @printf@-style interfaces.
207 */
208
209int dstr_vputf(dstr *d, const char *p, va_list ap)
210{
211 const char *q = p;
212 size_t n = d->len;
213 size_t sz;
0bfb1431 214 dstr dd = DSTR_INIT;
00c7638b 215
216 while (*p) {
217 unsigned f;
218 int wd, prec;
00c7638b 219
220 enum {
221 f_short = 1,
222 f_long = 2,
223 f_Long = 4,
224 f_wd = 8,
225 f_prec = 16
226 };
227
228 /* --- Most stuff gets passed on through --- */
229
230 if (*p != '%') {
231 p++;
232 continue;
233 }
234
235 /* --- Dump out what's between @q@ and @p@ --- */
236
237 DPUTM(d, q, p - q);
238 p++;
239
240 /* --- Sort out the various silly flags and things --- */
241
00c7638b 242 DPUTC(&dd, '%');
243 f = 0;
244 sz = DSTR_PUTFSTEP;
245
246 for (;;) {
247 switch (*p) {
248
249 /* --- Various simple flags --- */
250
251 case '+':
252 case '-':
253 case '#':
254 case '0':
255 goto putch;
256 case 'h':
257 f |= f_short;
258 goto putch;
259 case 'l':
260 f |= f_long;
261 goto putch;
262 case 'L':
263 f |= f_Long;
264 goto putch;
265 case 0:
266 goto finished;
267
268 /* --- Field widths and precision specifiers --- */
269
270 {
271 int *ip;
272
273 case '.':
274 DPUTC(&dd, '.');
275 ip = &prec;
276 f |= f_prec;
277 goto getnum;
278 case '*':
279 ip = &wd;
280 f |= f_wd;
281 goto getnum;
282 default:
283 if (isdigit((unsigned char)*p)) {
0fbb1412 284 f |= f_wd;
00c7638b 285 ip = &wd;
286 goto getnum;
287 }
288 DPUTC(d, *p);
289 goto formatted;
290 getnum:
291 *ip = 0;
292 if (*p == '*') {
293 *ip = va_arg(ap, int);
294 DENSURE(&dd, DSTR_PUTFSTEP);
295 dd.len += sprintf(dd.buf + dd.len, "%i", *ip);
296 } else {
0fbb1412 297 *ip = *p - '0';
00c7638b 298 DPUTC(&dd, *p);
299 p++;
300 while (isdigit((unsigned char)*p)) {
301 DPUTC(&dd, *p);
0fbb1412 302 *ip = 10 * *ip + *p++ - '0';
00c7638b 303 }
304 }
305 break;
306 }
307
308 /* --- Output formatting --- */
309
310 case 'd': case 'i': case 'x': case 'X': case 'o': case 'u':
311 DPUTC(&dd, *p);
312 DPUTZ(&dd);
313 if ((f & f_prec) && prec + 16 > sz)
314 sz = prec + 16;
315 if ((f & f_wd) && wd + 1> sz)
316 sz = wd + 1;
317 DENSURE(d, sz);
318 if (f & f_long)
319 d->len += sprintf(d->buf + d->len, dd.buf,
320 va_arg(ap, unsigned long));
321 else
322 d->len += sprintf(d->buf + d->len, dd.buf,
323 va_arg(ap, unsigned int));
324 goto formatted;
325
326 case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
327 DPUTC(&dd, *p);
328 DPUTZ(&dd);
329 if (*p == 'f') {
330 size_t mx = (f & f_Long ? LDBL_MAX_10_EXP : DBL_MAX_10_EXP) + 16;
331 if (mx > sz)
332 sz = mx;
333 }
334 if ((f & f_prec) == 0)
335 prec = 6;
336 if ((f & f_prec))
337 sz += prec + 16;
338 if ((f & f_wd) && wd + 1 > sz)
339 sz = wd + 1;
340 DENSURE(d, sz);
341 if (f & f_Long)
342 d->len += sprintf(d->buf + d->len, dd.buf,
343 va_arg(ap, long double));
344 else
345 d->len += sprintf(d->buf + d->len, dd.buf,
346 va_arg(ap, double));
347 goto formatted;
348
349 case 'c':
350 DPUTC(&dd, *p);
351 DPUTZ(&dd);
352 if ((f & f_wd) && wd + 1> sz)
353 sz = wd + 1;
354 DENSURE(d, sz);
355 d->len += sprintf(d->buf + d->len, dd.buf,
356 va_arg(ap, unsigned char));
357 goto formatted;
358
359 case 's': {
360 const char *s = va_arg(ap, const char *);
361 sz = strlen(s);
362 DPUTC(&dd, *p);
363 DPUTZ(&dd);
364 if (f & f_prec)
365 sz = prec;
366 if ((f & f_wd) && wd > sz)
367 sz = wd;
368 DENSURE(d, sz + 1);
369 d->len += sprintf(d->buf + d->len, dd.buf, s);
370 goto formatted;
371 }
372
373 case 'p':
374 DPUTC(&dd, *p);
375 DPUTZ(&dd);
376 if ((f & f_prec) && prec + 16 > sz)
377 sz = prec + 16;
378 if ((f & f_wd) && wd + 1> sz)
379 sz = wd + 1;
380 DENSURE(d, sz);
381 d->len += sprintf(d->buf + d->len, dd.buf,
382 va_arg(ap, const void *));
383 goto formatted;
384
385 case 'n':
386 if (f & f_long)
387 *va_arg(ap, long *) = (long)(d->len - n);
388 else if (f & f_short)
389 *va_arg(ap, short *) = (short)(d->len - n);
390 else
391 *va_arg(ap, int *) = (int)(d->len - n);
392 goto formatted;
393
394 /* --- Other random stuff --- */
395
396 putch:
397 DPUTC(&dd, *p);
398 p++;
399 break;
400 }
401 }
402
403 formatted:
0bfb1431 404 DRESET(&dd);
00c7638b 405 q = ++p;
406 }
407
408 DPUTM(d, q, p - q);
409finished:
410 DPUTZ(d);
0bfb1431 411 DDESTROY(&dd);
00c7638b 412 return (d->len - n);
413}
414
415/* --- @dstr_putf@ --- *
416 *
417 * Arguments: @dstr *d@ = pointer to a dynamic string block
418 * @const char *p@ = pointer to @printf@-style format string
419 * @...@ = argument handle
420 *
96c5fe33 421 * Returns: The number of characters written to the string.
00c7638b 422 *
423 * Use: Writes a piece of text to a dynamic string, doing @printf@-
424 * style substitutions as it goes. Intended to be robust if
425 * faced with malicious arguments, but not if the format string
426 * itself is malicious.
427 */
428
429int dstr_putf(dstr *d, const char *p, ...)
430{
431 int n;
432 va_list ap;
433 va_start(ap, p);
434 n = dstr_vputf(d, p, ap);
435 va_end(ap);
436 return (n);
437}
438
0875b58f 439/* --- @dstr_putd@ --- *
440 *
441 * Arguments: @dstr *d@ = pointer to a dynamic string block
442 * @const dstr *s@ = pointer to a dynamic string to append
443 *
444 * Returns: ---
445 *
446 * Use: Appends a dynamic string to a string. A trailing null
447 * byte is added, as for @dstr_putz@.
448 */
449
fb467548 450void dstr_putd(dstr *d, const dstr *s) { DPUTD(d, s); }
0875b58f 451
452/* --- @dstr_putm@ --- *
453 *
454 * Arguments: @dstr *d@ = pointer to a dynamic string block
455 * @const void *p@ = pointer to a block to append
456 * @size_t sz@ = size of the block
457 *
458 * Returns: Appends an arbitrary data block to a string. No trailing
459 * null is appended.
460 */
461
fb467548 462void dstr_putm(dstr *d, const void *p, size_t sz) { DPUTM(d, p, sz); }
0875b58f 463
464/* --- @dstr_tidy@ --- *
465 *
466 * Arguments: @dstr *d@ = pointer to a dynamic string block
467 *
468 * Returns: ---
469 *
470 * Use: Reduces the amount of memory used by a string. A trailing
471 * null byte is added, as for @dstr_putz@.
472 */
473
474void dstr_tidy(dstr *d)
475{
476 dstr_putz(d);
477 d->buf = xrealloc(d->buf, d->len + 1);
478 d->sz = d->len + 1;
479}
480
481/* --- @dstr_putline@ --- *
482 *
483 * Arguments: @dstr *d@ = pointer to a dynamic string block
484 * @FILE *fp@ = a stream to read from
485 *
486 * Returns: The number of characters read into the buffer, or @EOF@ if
487 * end-of-file was reached before any characters were read.
488 *
489 * Use: Appends the next line from the given input stream to the
490 * string. A trailing newline is not added; a trailing null
491 * byte is appended, as for @dstr_putz@.
492 */
493
494int dstr_putline(dstr *d, FILE *fp)
495{
496 size_t left = d->sz - d->len;
497 size_t off = d->len;
498 int rd = 0;
499 int ch;
500
501 for (;;) {
502
503 /* --- Make sure there's some buffer space --- */
504
505 if (!left) {
506 dstr_ensure(d, 1);
507 left = d->sz - off;
508 }
509
510 /* --- Read the next byte --- */
511
512 ch = getc(fp);
513
514 /* --- End-of-file when no characters read is special --- */
515
516 if (ch == EOF && !rd)
517 return (EOF);
518
519 /* --- End-of-file or newline ends the loop --- */
520
521 if (ch == EOF || ch == '\n') {
522 d->buf[off] = 0;
523 d->len = off;
524 return rd;
525 }
526
527 /* --- Append the character and continue --- */
528
529 d->buf[off++] = ch;
530 left--; rd++;
531 }
532}
533
534/* --- @dstr_write@ --- *
535 *
536 * Arguments: @dstr *d@ = pointer to a dynamic string block
537 * @FILE *fp@ = a stream to write on
538 *
539 * Returns: The number of bytes written (as for @fwrite@).
540 *
541 * Use: Writes a dynamic string to a file.
542 */
543
fb467548 544size_t dstr_write(const dstr *d, FILE *fp) { return (DWRITE(d, fp)); }
0875b58f 545
546/*----- That's all, folks -------------------------------------------------*/