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