xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/vstring.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: vstring.c,v 1.2 2017/02/14 01:16:49 christos Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	vstring 3
6 /* SUMMARY
7 /*	arbitrary-length string manager
8 /* SYNOPSIS
9 /*	#include <vstring.h>
10 /*
11 /*	VSTRING	*vstring_alloc(len)
12 /*	ssize_t	len;
13 /*
14 /*	vstring_ctl(vp, type, value, ..., VSTRING_CTL_END)
15 /*	VSTRING	*vp;
16 /*	int	type;
17 /*
18 /*	VSTRING	*vstring_free(vp)
19 /*	VSTRING	*vp;
20 /*
21 /*	char	*vstring_str(vp)
22 /*	VSTRING	*vp;
23 /*
24 /*	ssize_t	VSTRING_LEN(vp)
25 /*	VSTRING	*vp;
26 /*
27 /*	char	*vstring_end(vp)
28 /*	VSTRING	*vp;
29 /*
30 /*	void	VSTRING_ADDCH(vp, ch)
31 /*	VSTRING	*vp;
32 /*	int	ch;
33 /*
34 /*	int	VSTRING_SPACE(vp, len)
35 /*	VSTRING	*vp;
36 /*	ssize_t	len;
37 /*
38 /*	ssize_t	vstring_avail(vp)
39 /*	VSTRING	*vp;
40 /*
41 /*	VSTRING	*vstring_truncate(vp, len)
42 /*	VSTRING	*vp;
43 /*	ssize_t	len;
44 /*
45 /*	void	VSTRING_RESET(vp)
46 /*	VSTRING	*vp;
47 /*
48 /*	void	VSTRING_TERMINATE(vp)
49 /*	VSTRING	*vp;
50 /*
51 /*	void	VSTRING_SKIP(vp)
52 /*	VSTRING	*vp;
53 /*
54 /*	VSTRING	*vstring_strcpy(vp, src)
55 /*	VSTRING	*vp;
56 /*	const char *src;
57 /*
58 /*	VSTRING	*vstring_strncpy(vp, src, len)
59 /*	VSTRING	*vp;
60 /*	const char *src;
61 /*	ssize_t	len;
62 /*
63 /*	VSTRING	*vstring_strcat(vp, src)
64 /*	VSTRING	*vp;
65 /*	const char *src;
66 /*
67 /*	VSTRING	*vstring_strncat(vp, src, len)
68 /*	VSTRING	*vp;
69 /*	const char *src;
70 /*	ssize_t	len;
71 /*
72 /*	VSTRING	*vstring_memcpy(vp, src, len)
73 /*	VSTRING	*vp;
74 /*	const char *src;
75 /*	ssize_t	len;
76 /*
77 /*	VSTRING	*vstring_memcat(vp, src, len)
78 /*	VSTRING	*vp;
79 /*	const char *src;
80 /*	ssize_t	len;
81 /*
82 /*	char	*vstring_memchr(vp, ch)
83 /*	VSTRING	*vp;
84 /*	int	ch;
85 /*
86 /*	VSTRING	*vstring_insert(vp, start, src, len)
87 /*	VSTRING	*vp;
88 /*	ssize_t	start;
89 /*	const char *src;
90 /*	ssize_t	len;
91 /*
92 /*	VSTRING	*vstring_prepend(vp, src, len)
93 /*	VSTRING	*vp;
94 /*	const char *src;
95 /*	ssize_t	len;
96 /*
97 /*	VSTRING	*vstring_sprintf(vp, format, ...)
98 /*	VSTRING	*vp;
99 /*	const char *format;
100 /*
101 /*	VSTRING	*vstring_sprintf_append(vp, format, ...)
102 /*	VSTRING	*vp;
103 /*	const char *format;
104 /*
105 /*	VSTRING	*vstring_sprintf_prepend(vp, format, ...)
106 /*	VSTRING	*vp;
107 /*	const char *format;
108 /*
109 /*	VSTRING	*vstring_vsprintf(vp, format, ap)
110 /*	VSTRING	*vp;
111 /*	const char *format;
112 /*	va_list	ap;
113 /*
114 /*	VSTRING	*vstring_vsprintf_append(vp, format, ap)
115 /*	VSTRING	*vp;
116 /*	const char *format;
117 /*	va_list	ap;
118 /* AUXILIARY FUNCTIONS
119 /*	char	*vstring_export(vp)
120 /*	VSTRING	*vp;
121 /*
122 /*	VSTRING	*vstring_import(str)
123 /*	char	*str;
124 /* DESCRIPTION
125 /*	The functions and macros in this module implement arbitrary-length
126 /*	strings and common operations on those strings. The strings do not
127 /*	need to be null terminated and may contain arbitrary binary data.
128 /*	The strings manage their own memory and grow automatically when full.
129 /*	The optional string null terminator does not add to the string length.
130 /*
131 /*	vstring_alloc() allocates storage for a variable-length string
132 /*	of at least "len" bytes. The minimal length is 1. The result
133 /*	is a null-terminated string of length zero.
134 /*
135 /*	vstring_ctl() gives additional control over VSTRING behavior.
136 /*	The function takes a VSTRING pointer and a list of zero or
137 /*	more macros with zer or more arguments, terminated with
138 /*	CA_VSTRING_CTL_END which has none.
139 /* .IP "CA_VSTRING_CTL_MAXLEN(ssize_t len)"
140 /*	Specifies a hard upper limit on a string's length. When the
141 /*	length would be exceeded, the program simulates a memory
142 /*	allocation problem (i.e. it terminates through msg_fatal()).
143 /*	This fuctionality is currently unimplemented.
144 /* .IP "CA_VSTRING_CTL_END (no argument)"
145 /*	Specifies the end of the argument list. Forgetting to terminate
146 /*	the argument list may cause the program to crash.
147 /* .PP
148 /*	VSTRING_SPACE() ensures that the named string has room for
149 /*	"len" more characters. VSTRING_SPACE() is an unsafe macro
150 /*	that either returns zero or never returns.
151 /*
152 /*	vstring_avail() returns the number of bytes that can be placed
153 /*	into the buffer before the buffer would need to grow.
154 /*
155 /*	vstring_free() reclaims storage for a variable-length string.
156 /*	It conveniently returns a null pointer.
157 /*
158 /*	vstring_str() is a macro that returns the string value
159 /*	of a variable-length string. It is a safe macro that
160 /*	evaluates its argument only once.
161 /*
162 /*	VSTRING_LEN() is a macro that returns the current length of
163 /*	its argument (i.e. the distance from the start of the string
164 /*	to the current write position). VSTRING_LEN() is an unsafe macro
165 /*	that evaluates its argument more than once.
166 /*
167 /*	vstring_end() is a macro that returns the current write position of
168 /*	its argument. It is a safe macro that evaluates its argument only once.
169 /*
170 /*	VSTRING_ADDCH() adds a character to a variable-length string
171 /*	and extends the string if it fills up.  \fIvs\fP is a pointer
172 /*	to a VSTRING structure; \fIch\fP the character value to be written.
173 /*	The result is the written character.
174 /*	Note that VSTRING_ADDCH() is an unsafe macro that evaluates some
175 /*	arguments more than once. The result is NOT null-terminated.
176 /*
177 /*	vstring_truncate() truncates the named string to the specified
178 /*	length. If length is negative, the trailing portion is kept.
179 /*	The operation has no effect when the string is shorter.
180 /*	The string is not null-terminated.
181 /*
182 /*	VSTRING_RESET() is a macro that resets the write position of its
183 /*	string argument to the very beginning. Note that VSTRING_RESET()
184 /*	is an unsafe macro that evaluates some arguments more than once.
185 /*	The result is NOT null-terminated.
186 /*
187 /*	VSTRING_TERMINATE() null-terminates its string argument.
188 /*	VSTRING_TERMINATE() is an unsafe macro that evaluates some
189 /*	arguments more than once.
190 /*	VSTRING_TERMINATE() does not return an interesting result.
191 /*
192 /*	VSTRING_SKIP() is a macro that moves the write position to the first
193 /*	null byte after the current write position. VSTRING_SKIP() is an unsafe
194 /*	macro that evaluates some arguments more than once.
195 /*
196 /*	vstring_strcpy() copies a null-terminated string to a variable-length
197 /*	string. \fIsrc\fP provides the data to be copied; \fIvp\fP is the
198 /*	target and result value.  The result is null-terminated.
199 /*
200 /*	vstring_strncpy() copies at most \fIlen\fR characters. Otherwise it is
201 /*	identical to vstring_strcpy().
202 /*
203 /*	vstring_strcat() appends a null-terminated string to a variable-length
204 /*	string. \fIsrc\fP provides the data to be copied; \fIvp\fP is the
205 /*	target and result value.  The result is null-terminated.
206 /*
207 /*	vstring_strncat() copies at most \fIlen\fR characters. Otherwise it is
208 /*	identical to vstring_strcat().
209 /*
210 /*	vstring_memcpy() copies \fIlen\fR bytes to a variable-length string.
211 /*	\fIsrc\fP provides the data to be copied; \fIvp\fP is the
212 /*	target and result value.  The result is not null-terminated.
213 /*
214 /*	vstring_memcat() appends \fIlen\fR bytes to a variable-length string.
215 /*	\fIsrc\fP provides the data to be copied; \fIvp\fP is the
216 /*	target and result value.  The result is not null-terminated.
217 /*
218 /*	vstring_memchr() locates a byte in a variable-length string.
219 /*
220 /*	vstring_insert() inserts a buffer content into a variable-length
221 /*	string at the specified start position. The result is
222 /*	null-terminated.
223 /*
224 /*	vstring_prepend() prepends a buffer content to a variable-length
225 /*	string. The result is null-terminated.
226 /*
227 /*	vstring_sprintf() produces a formatted string according to its
228 /*	\fIformat\fR argument. See vstring_vsprintf() for details.
229 /*
230 /*	vstring_sprintf_append() is like vstring_sprintf(), but appends
231 /*	to the end of the result buffer.
232 /*
233 /*	vstring_sprintf_append() is like vstring_sprintf(), but prepends
234 /*	to the beginning of the result buffer.
235 /*
236 /*	vstring_vsprintf() returns a null-terminated string according to
237 /*	the \fIformat\fR argument. It understands the s, c, d, u,
238 /*	o, x, X, p, e, f and g format types, the l modifier, field width
239 /*	and precision, sign, and null or space padding. This module
240 /*	can format strings as large as available memory permits.
241 /*
242 /*	vstring_vsprintf_append() is like vstring_vsprintf(), but appends
243 /*	to the end of the result buffer.
244 /*
245 /*	In addition to stdio-like format specifiers, vstring_vsprintf()
246 /*	recognizes %m and expands it to the corresponding errno text.
247 /*
248 /*	vstring_export() extracts the string value from a VSTRING.
249 /*	The VSTRING is destroyed. The result should be passed to myfree().
250 /*
251 /*	vstring_import() takes a `bare' string and converts it to
252 /*	a VSTRING. The string argument must be obtained from mymalloc().
253 /*	The string argument is not copied.
254 /* DIAGNOSTICS
255 /*	Fatal errors: memory allocation failure.
256 /* BUGS
257 /*	Auto-resizing may change the address of the string data in
258 /*	a vstring structure. Beware of dangling pointers.
259 /* HISTORY
260 /* .ad
261 /* .fi
262 /*	A vstring module appears in the UNPROTO software by Wietse Venema.
263 /* AUTHOR(S)
264 /*	Wietse Venema
265 /*	IBM T.J. Watson Research
266 /*	P.O. Box 704
267 /*	Yorktown Heights, NY 10598, USA
268 /*--*/
269 
270 /* System libraries. */
271 
272 #include <sys_defs.h>
273 #include <stddef.h>
274 #include <stdlib.h>			/* 44BSD stdarg.h uses abort() */
275 #include <stdarg.h>
276 #include <string.h>
277 
278 /* Utility library. */
279 
280 #include "mymalloc.h"
281 #include "msg.h"
282 #include "vbuf_print.h"
283 #include "vstring.h"
284 
285 /* vstring_extend - variable-length string buffer extension policy */
286 
287 static void vstring_extend(VBUF *bp, ssize_t incr)
288 {
289     size_t  used = bp->ptr - bp->data;
290     ssize_t new_len;
291 
292     /*
293      * Note: vp->vbuf.len is the current buffer size (both on entry and on
294      * exit of this routine). We round up the increment size to the buffer
295      * size to avoid silly little buffer increments. With really large
296      * strings we might want to abandon the length doubling strategy, and go
297      * to fixed increments.
298      *
299      * The length overflow tests here and in vstring_alloc() should protect us
300      * against all length overflow problems within vstring library routines.
301      * (The tests are redundant as long as mymalloc() and myrealloc() reject
302      * negative length parameters).
303      */
304     new_len = bp->len + (bp->len > incr ? bp->len : incr);
305     if (new_len <= bp->len)
306 	msg_fatal("vstring_extend: length overflow");
307     bp->data = (unsigned char *) myrealloc((void *) bp->data, new_len);
308     bp->len = new_len;
309     bp->ptr = bp->data + used;
310     bp->cnt = bp->len - used;
311 }
312 
313 /* vstring_buf_get_ready - vbuf callback for read buffer empty condition */
314 
315 static int vstring_buf_get_ready(VBUF *unused_buf)
316 {
317     msg_panic("vstring_buf_get: write-only buffer");
318 }
319 
320 /* vstring_buf_put_ready - vbuf callback for write buffer full condition */
321 
322 static int vstring_buf_put_ready(VBUF *bp)
323 {
324     vstring_extend(bp, 0);
325     return (0);
326 }
327 
328 /* vstring_buf_space - vbuf callback to reserve space */
329 
330 static int vstring_buf_space(VBUF *bp, ssize_t len)
331 {
332     ssize_t need;
333 
334     if (len < 0)
335 	msg_panic("vstring_buf_space: bad length %ld", (long) len);
336     if ((need = len - bp->cnt) > 0)
337 	vstring_extend(bp, need);
338     return (0);
339 }
340 
341 /* vstring_alloc - create variable-length string */
342 
343 VSTRING *vstring_alloc(ssize_t len)
344 {
345     VSTRING *vp;
346 
347     if (len < 1)
348 	msg_panic("vstring_alloc: bad length %ld", (long) len);
349     vp = (VSTRING *) mymalloc(sizeof(*vp));
350     vp->vbuf.flags = 0;
351     vp->vbuf.len = 0;
352     vp->vbuf.data = (unsigned char *) mymalloc(len);
353     vp->vbuf.len = len;
354     VSTRING_RESET(vp);
355     vp->vbuf.data[0] = 0;
356     vp->vbuf.get_ready = vstring_buf_get_ready;
357     vp->vbuf.put_ready = vstring_buf_put_ready;
358     vp->vbuf.space = vstring_buf_space;
359     vp->maxlen = 0;
360     return (vp);
361 }
362 
363 /* vstring_free - destroy variable-length string */
364 
365 VSTRING *vstring_free(VSTRING *vp)
366 {
367     if (vp->vbuf.data)
368 	myfree((void *) vp->vbuf.data);
369     myfree((void *) vp);
370     return (0);
371 }
372 
373 /* vstring_ctl - modify memory management policy */
374 
375 void    vstring_ctl(VSTRING *vp,...)
376 {
377     va_list ap;
378     int     code;
379 
380     va_start(ap, vp);
381     while ((code = va_arg(ap, int)) != VSTRING_CTL_END) {
382 	switch (code) {
383 	default:
384 	    msg_panic("vstring_ctl: unknown code: %d", code);
385 	case VSTRING_CTL_MAXLEN:
386 	    vp->maxlen = va_arg(ap, ssize_t);
387 	    if (vp->maxlen < 0)
388 		msg_panic("vstring_ctl: bad max length %ld", (long) vp->maxlen);
389 	    break;
390 	}
391     }
392     va_end(ap);
393 }
394 
395 /* vstring_truncate - truncate string */
396 
397 VSTRING *vstring_truncate(VSTRING *vp, ssize_t len)
398 {
399     ssize_t move;
400 
401     if (len < 0) {
402 	len = (-len);
403 	if ((move = VSTRING_LEN(vp) - len) > 0)
404 	    memmove(vstring_str(vp), vstring_str(vp) + move, len);
405     }
406     if (len < VSTRING_LEN(vp))
407 	VSTRING_AT_OFFSET(vp, len);
408     return (vp);
409 }
410 
411 /* vstring_strcpy - copy string */
412 
413 VSTRING *vstring_strcpy(VSTRING *vp, const char *src)
414 {
415     VSTRING_RESET(vp);
416 
417     while (*src) {
418 	VSTRING_ADDCH(vp, *src);
419 	src++;
420     }
421     VSTRING_TERMINATE(vp);
422     return (vp);
423 }
424 
425 /* vstring_strncpy - copy string of limited length */
426 
427 VSTRING *vstring_strncpy(VSTRING *vp, const char *src, ssize_t len)
428 {
429     VSTRING_RESET(vp);
430 
431     while (len-- > 0 && *src) {
432 	VSTRING_ADDCH(vp, *src);
433 	src++;
434     }
435     VSTRING_TERMINATE(vp);
436     return (vp);
437 }
438 
439 /* vstring_strcat - append string */
440 
441 VSTRING *vstring_strcat(VSTRING *vp, const char *src)
442 {
443     while (*src) {
444 	VSTRING_ADDCH(vp, *src);
445 	src++;
446     }
447     VSTRING_TERMINATE(vp);
448     return (vp);
449 }
450 
451 /* vstring_strncat - append string of limited length */
452 
453 VSTRING *vstring_strncat(VSTRING *vp, const char *src, ssize_t len)
454 {
455     while (len-- > 0 && *src) {
456 	VSTRING_ADDCH(vp, *src);
457 	src++;
458     }
459     VSTRING_TERMINATE(vp);
460     return (vp);
461 }
462 
463 /* vstring_memcpy - copy buffer of limited length */
464 
465 VSTRING *vstring_memcpy(VSTRING *vp, const char *src, ssize_t len)
466 {
467     VSTRING_RESET(vp);
468 
469     VSTRING_SPACE(vp, len);
470     memcpy(vstring_str(vp), src, len);
471     VSTRING_AT_OFFSET(vp, len);
472     return (vp);
473 }
474 
475 /* vstring_memcat - append buffer of limited length */
476 
477 VSTRING *vstring_memcat(VSTRING *vp, const char *src, ssize_t len)
478 {
479     VSTRING_SPACE(vp, len);
480     memcpy(vstring_end(vp), src, len);
481     len += VSTRING_LEN(vp);
482     VSTRING_AT_OFFSET(vp, len);
483     return (vp);
484 }
485 
486 /* vstring_memchr - locate byte in buffer */
487 
488 char   *vstring_memchr(VSTRING *vp, int ch)
489 {
490     unsigned char *cp;
491 
492     for (cp = (unsigned char *) vstring_str(vp); cp < (unsigned char *) vstring_end(vp); cp++)
493 	if (*cp == ch)
494 	    return ((char *) cp);
495     return (0);
496 }
497 
498 /* vstring_insert - insert text into string */
499 
500 VSTRING *vstring_insert(VSTRING *vp, ssize_t start, const char *buf, ssize_t len)
501 {
502     ssize_t new_len;
503 
504     /*
505      * Sanity check.
506      */
507     if (start < 0 || start >= VSTRING_LEN(vp))
508 	msg_panic("vstring_insert: bad start %ld", (long) start);
509     if (len < 0)
510 	msg_panic("vstring_insert: bad length %ld", (long) len);
511 
512     /*
513      * Move the existing content and copy the new content.
514      */
515     new_len = VSTRING_LEN(vp) + len;
516     VSTRING_SPACE(vp, len);
517     memmove(vstring_str(vp) + start + len, vstring_str(vp) + start,
518 	    VSTRING_LEN(vp) - start);
519     memcpy(vstring_str(vp) + start, buf, len);
520     VSTRING_AT_OFFSET(vp, new_len);
521     VSTRING_TERMINATE(vp);
522     return (vp);
523 }
524 
525 /* vstring_prepend - prepend text to string */
526 
527 VSTRING *vstring_prepend(VSTRING *vp, const char *buf, ssize_t len)
528 {
529     ssize_t new_len;
530 
531     /*
532      * Sanity check.
533      */
534     if (len < 0)
535 	msg_panic("vstring_prepend: bad length %ld", (long) len);
536 
537     /*
538      * Move the existing content and copy the new content.
539      */
540     new_len = VSTRING_LEN(vp) + len;
541     VSTRING_SPACE(vp, len);
542     memmove(vstring_str(vp) + len, vstring_str(vp), VSTRING_LEN(vp));
543     memcpy(vstring_str(vp), buf, len);
544     VSTRING_AT_OFFSET(vp, new_len);
545     VSTRING_TERMINATE(vp);
546     return (vp);
547 }
548 
549 /* vstring_export - VSTRING to bare string */
550 
551 char   *vstring_export(VSTRING *vp)
552 {
553     char   *cp;
554 
555     cp = (char *) vp->vbuf.data;
556     vp->vbuf.data = 0;
557     myfree((void *) vp);
558     return (cp);
559 }
560 
561 /* vstring_import - bare string to vstring */
562 
563 VSTRING *vstring_import(char *str)
564 {
565     VSTRING *vp;
566     ssize_t len;
567 
568     vp = (VSTRING *) mymalloc(sizeof(*vp));
569     len = strlen(str);
570     vp->vbuf.flags = 0;
571     vp->vbuf.len = 0;
572     vp->vbuf.data = (unsigned char *) str;
573     vp->vbuf.len = len + 1;
574     VSTRING_AT_OFFSET(vp, len);
575     vp->vbuf.get_ready = vstring_buf_get_ready;
576     vp->vbuf.put_ready = vstring_buf_put_ready;
577     vp->vbuf.space = vstring_buf_space;
578     vp->maxlen = 0;
579     return (vp);
580 }
581 
582 /* vstring_sprintf - formatted string */
583 
584 VSTRING *vstring_sprintf(VSTRING *vp, const char *format,...)
585 {
586     va_list ap;
587 
588     va_start(ap, format);
589     vp = vstring_vsprintf(vp, format, ap);
590     va_end(ap);
591     return (vp);
592 }
593 
594 /* vstring_vsprintf - format string, vsprintf-like interface */
595 
596 VSTRING *vstring_vsprintf(VSTRING *vp, const char *format, va_list ap)
597 {
598     VSTRING_RESET(vp);
599     vbuf_print(&vp->vbuf, format, ap);
600     VSTRING_TERMINATE(vp);
601     return (vp);
602 }
603 
604 /* vstring_sprintf_append - append formatted string */
605 
606 VSTRING *vstring_sprintf_append(VSTRING *vp, const char *format,...)
607 {
608     va_list ap;
609 
610     va_start(ap, format);
611     vp = vstring_vsprintf_append(vp, format, ap);
612     va_end(ap);
613     return (vp);
614 }
615 
616 /* vstring_vsprintf_append - format + append string, vsprintf-like interface */
617 
618 VSTRING *vstring_vsprintf_append(VSTRING *vp, const char *format, va_list ap)
619 {
620     vbuf_print(&vp->vbuf, format, ap);
621     VSTRING_TERMINATE(vp);
622     return (vp);
623 }
624 
625 /* vstring_sprintf_prepend - format + prepend string, vsprintf-like interface */
626 
627 VSTRING *vstring_sprintf_prepend(VSTRING *vp, const char *format,...)
628 {
629     va_list ap;
630     ssize_t old_len = VSTRING_LEN(vp);
631     ssize_t result_len;
632 
633     /* Construct: old|new|free */
634     va_start(ap, format);
635     vp = vstring_vsprintf_append(vp, format, ap);
636     va_end(ap);
637     result_len = VSTRING_LEN(vp);
638 
639     /* Construct: old|new|old|free */
640     VSTRING_SPACE(vp, old_len);
641     vstring_memcat(vp, vstring_str(vp), old_len);
642 
643     /* Construct: new|old|free */
644     memmove(vstring_str(vp), vstring_str(vp) + old_len, result_len);
645     VSTRING_AT_OFFSET(vp, result_len);
646     VSTRING_TERMINATE(vp);
647     return (vp);
648 }
649 
650 #ifdef TEST
651 
652  /*
653   * Test program - concatenate all command-line arguments into one string.
654   */
655 #include <stdio.h>
656 
657 int     main(int argc, char **argv)
658 {
659     VSTRING *vp = vstring_alloc(1);
660 
661     while (argc-- > 0) {
662 	vstring_strcat(vp, *argv++);
663 	vstring_strcat(vp, ".");
664     }
665     printf("argv concatenated: %s\n", vstring_str(vp));
666     vstring_free(vp);
667     return (0);
668 }
669 
670 #endif
671