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