1 /* $NetBSD: vstring.c,v 1.3 2020/03/18 19:05:22 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 fuctionality 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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