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