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