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