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