diff options
Diffstat (limited to '')
-rw-r--r-- | src/util/vstring.c | 717 |
1 files changed, 717 insertions, 0 deletions
diff --git a/src/util/vstring.c b/src/util/vstring.c new file mode 100644 index 0000000..6dca236 --- /dev/null +++ b/src/util/vstring.c @@ -0,0 +1,717 @@ +/*++ +/* NAME +/* vstring 3 +/* SUMMARY +/* arbitrary-length string manager +/* SYNOPSIS +/* #include <vstring.h> +/* +/* VSTRING *vstring_alloc(len) +/* ssize_t len; +/* +/* vstring_ctl(vp, type, value, ..., VSTRING_CTL_END) +/* VSTRING *vp; +/* int type; +/* +/* VSTRING *vstring_free(vp) +/* VSTRING *vp; +/* +/* char *vstring_str(vp) +/* VSTRING *vp; +/* +/* ssize_t VSTRING_LEN(vp) +/* VSTRING *vp; +/* +/* char *vstring_end(vp) +/* VSTRING *vp; +/* +/* void VSTRING_ADDCH(vp, ch) +/* VSTRING *vp; +/* int ch; +/* +/* int VSTRING_SPACE(vp, len) +/* VSTRING *vp; +/* ssize_t len; +/* +/* ssize_t vstring_avail(vp) +/* VSTRING *vp; +/* +/* VSTRING *vstring_truncate(vp, len) +/* VSTRING *vp; +/* ssize_t len; +/* +/* VSTRING *vstring_set_payload_size(vp, len) +/* VSTRING *vp; +/* ssize_t len; +/* +/* void VSTRING_RESET(vp) +/* VSTRING *vp; +/* +/* void VSTRING_TERMINATE(vp) +/* VSTRING *vp; +/* +/* void VSTRING_SKIP(vp) +/* VSTRING *vp; +/* +/* VSTRING *vstring_strcpy(vp, src) +/* VSTRING *vp; +/* const char *src; +/* +/* VSTRING *vstring_strncpy(vp, src, len) +/* VSTRING *vp; +/* const char *src; +/* ssize_t len; +/* +/* VSTRING *vstring_strcat(vp, src) +/* VSTRING *vp; +/* const char *src; +/* +/* VSTRING *vstring_strncat(vp, src, len) +/* VSTRING *vp; +/* const char *src; +/* ssize_t len; +/* +/* VSTRING *vstring_memcpy(vp, src, len) +/* VSTRING *vp; +/* const char *src; +/* ssize_t len; +/* +/* VSTRING *vstring_memcat(vp, src, len) +/* VSTRING *vp; +/* const char *src; +/* ssize_t len; +/* +/* char *vstring_memchr(vp, ch) +/* VSTRING *vp; +/* int ch; +/* +/* VSTRING *vstring_insert(vp, start, src, len) +/* VSTRING *vp; +/* ssize_t start; +/* const char *src; +/* ssize_t len; +/* +/* VSTRING *vstring_prepend(vp, src, len) +/* VSTRING *vp; +/* const char *src; +/* ssize_t len; +/* +/* VSTRING *vstring_sprintf(vp, format, ...) +/* VSTRING *vp; +/* const char *format; +/* +/* VSTRING *vstring_sprintf_append(vp, format, ...) +/* VSTRING *vp; +/* const char *format; +/* +/* VSTRING *vstring_sprintf_prepend(vp, format, ...) +/* VSTRING *vp; +/* const char *format; +/* +/* VSTRING *vstring_vsprintf(vp, format, ap) +/* VSTRING *vp; +/* const char *format; +/* va_list ap; +/* +/* VSTRING *vstring_vsprintf_append(vp, format, ap) +/* VSTRING *vp; +/* const char *format; +/* va_list ap; +/* AUXILIARY FUNCTIONS +/* char *vstring_export(vp) +/* VSTRING *vp; +/* +/* VSTRING *vstring_import(str) +/* char *str; +/* DESCRIPTION +/* The functions and macros in this module implement arbitrary-length +/* strings and common operations on those strings. The strings do not +/* need to be null terminated and may contain arbitrary binary data. +/* The strings manage their own memory and grow automatically when full. +/* The optional string null terminator does not add to the string length. +/* +/* vstring_alloc() allocates storage for a variable-length string +/* of at least "len" bytes. The minimal length is 1. The result +/* is a null-terminated string of length zero. +/* +/* vstring_ctl() gives additional control over VSTRING behavior. +/* The function takes a VSTRING pointer and a list of zero or +/* more macros with zer or more arguments, terminated with +/* CA_VSTRING_CTL_END which has none. +/* .IP "CA_VSTRING_CTL_MAXLEN(ssize_t len)" +/* Specifies a hard upper limit on a string's length. When the +/* length would be exceeded, the program simulates a memory +/* allocation problem (i.e. it terminates through msg_fatal()). +/* This fuctionality is currently unimplemented. +/* .IP "CA_VSTRING_CTL_EXACT (no argument)" +/* Allocate the requested amounts, instead of rounding up. +/* This should be used for tests only. +/* .IP "CA_VSTRING_CTL_END (no argument)" +/* Specifies the end of the argument list. Forgetting to terminate +/* the argument list may cause the program to crash. +/* .PP +/* VSTRING_SPACE() ensures that the named string has room for +/* "len" more characters. VSTRING_SPACE() is an unsafe macro +/* that either returns zero or never returns. +/* +/* vstring_avail() returns the number of bytes that can be placed +/* into the buffer before the buffer would need to grow. +/* +/* vstring_free() reclaims storage for a variable-length string. +/* It conveniently returns a null pointer. +/* +/* vstring_str() is a macro that returns the string value +/* of a variable-length string. It is a safe macro that +/* evaluates its argument only once. +/* +/* VSTRING_LEN() is a macro that returns the current length of +/* its argument (i.e. the distance from the start of the string +/* to the current write position). VSTRING_LEN() is an unsafe macro +/* that evaluates its argument more than once. +/* +/* vstring_end() is a macro that returns the current write position of +/* its argument. It is a safe macro that evaluates its argument only once. +/* +/* VSTRING_ADDCH() adds a character to a variable-length string +/* and extends the string if it fills up. \fIvs\fP is a pointer +/* to a VSTRING structure; \fIch\fP the character value to be written. +/* The result is the written character. +/* Note that VSTRING_ADDCH() is an unsafe macro that evaluates some +/* arguments more than once. The result is NOT null-terminated. +/* +/* vstring_truncate() truncates the named string to the specified +/* length. If length is negative, the trailing portion is kept. +/* The operation has no effect when the string is shorter. +/* The string is not null-terminated. +/* +/* vstring_set_payload_size() sets the number of 'used' bytes +/* in the named buffer's metadata. This determines the buffer +/* write position and the VSTRING_LEN() result. The payload +/* size must be within the closed range [0, number of allocated +/* bytes]. The typical usage is to request buffer space with +/* VSTRING_SPACE(), to use some non-VSTRING operations to write +/* to the buffer, and to call vstring_set_payload_size() to +/* update buffer metadata, perhaps followed by VSTRING_TERMINATE(). +/* +/* VSTRING_RESET() is a macro that resets the write position of its +/* string argument to the very beginning. Note that VSTRING_RESET() +/* is an unsafe macro that evaluates some arguments more than once. +/* The result is NOT null-terminated. +/* +/* VSTRING_TERMINATE() null-terminates its string argument. +/* VSTRING_TERMINATE() is an unsafe macro that evaluates some +/* arguments more than once. +/* VSTRING_TERMINATE() does not return an interesting result. +/* +/* VSTRING_SKIP() is a macro that moves the write position to the first +/* null byte after the current write position. VSTRING_SKIP() is an unsafe +/* macro that evaluates some arguments more than once. +/* +/* vstring_strcpy() copies a null-terminated string to a variable-length +/* string. \fIsrc\fP provides the data to be copied; \fIvp\fP is the +/* target and result value. The result is null-terminated. +/* +/* vstring_strncpy() copies at most \fIlen\fR characters. Otherwise it is +/* identical to vstring_strcpy(). +/* +/* vstring_strcat() appends a null-terminated string to a variable-length +/* string. \fIsrc\fP provides the data to be copied; \fIvp\fP is the +/* target and result value. The result is null-terminated. +/* +/* vstring_strncat() copies at most \fIlen\fR characters. Otherwise it is +/* identical to vstring_strcat(). +/* +/* vstring_memcpy() copies \fIlen\fR bytes to a variable-length string. +/* \fIsrc\fP provides the data to be copied; \fIvp\fP is the +/* target and result value. The result is not null-terminated. +/* +/* vstring_memcat() appends \fIlen\fR bytes to a variable-length string. +/* \fIsrc\fP provides the data to be copied; \fIvp\fP is the +/* target and result value. The result is not null-terminated. +/* +/* vstring_memchr() locates a byte in a variable-length string. +/* +/* vstring_insert() inserts a buffer content into a variable-length +/* string at the specified start position. The result is +/* null-terminated. +/* +/* vstring_prepend() prepends a buffer content to a variable-length +/* string. The result is null-terminated. +/* +/* vstring_sprintf() produces a formatted string according to its +/* \fIformat\fR argument. See vstring_vsprintf() for details. +/* +/* vstring_sprintf_append() is like vstring_sprintf(), but appends +/* to the end of the result buffer. +/* +/* vstring_sprintf_append() is like vstring_sprintf(), but prepends +/* to the beginning of the result buffer. +/* +/* vstring_vsprintf() returns a null-terminated string according to +/* the \fIformat\fR argument. It understands the s, c, d, u, +/* o, x, X, p, e, f and g format types, the l modifier, field width +/* and precision, sign, and null or space padding. This module +/* can format strings as large as available memory permits. +/* +/* vstring_vsprintf_append() is like vstring_vsprintf(), but appends +/* to the end of the result buffer. +/* +/* In addition to stdio-like format specifiers, vstring_vsprintf() +/* recognizes %m and expands it to the corresponding errno text. +/* +/* vstring_export() extracts the string value from a VSTRING. +/* The VSTRING is destroyed. The result should be passed to myfree(). +/* +/* vstring_import() takes a `bare' string and converts it to +/* a VSTRING. The string argument must be obtained from mymalloc(). +/* The string argument is not copied. +/* DIAGNOSTICS +/* Fatal errors: memory allocation failure. +/* BUGS +/* Auto-resizing may change the address of the string data in +/* a vstring structure. Beware of dangling pointers. +/* HISTORY +/* .ad +/* .fi +/* A vstring module appears in the UNPROTO software by Wietse Venema. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +/* System libraries. */ + +#include <sys_defs.h> +#include <stddef.h> +#include <stdlib.h> /* 44BSD stdarg.h uses abort() */ +#include <stdarg.h> +#include <string.h> + +/* Utility library. */ + +#define VSTRING_INTERNAL + +#include "mymalloc.h" +#include "msg.h" +#include "vbuf_print.h" +#include "vstring.h" + +/* vstring_extend - variable-length string buffer extension policy */ + +static void vstring_extend(VBUF *bp, ssize_t incr) +{ + size_t used = bp->ptr - bp->data; + ssize_t new_len; + + /* + * Note: vp->vbuf.len is the current buffer size (both on entry and on + * exit of this routine). We round up the increment size to the buffer + * size to avoid silly little buffer increments. With really large + * strings we might want to abandon the length doubling strategy, and go + * to fixed increments. + * + * The length overflow tests here and in vstring_alloc() should protect us + * against all length overflow problems within vstring library routines. + * + * Safety net: add a gratuitous null terminator so that C-style string + * operations won't scribble past the end. + */ + if ((bp->flags & VSTRING_FLAG_EXACT) == 0 && bp->len > incr) + incr = bp->len; + if (bp->len > SSIZE_T_MAX - incr - 1) + msg_fatal("vstring_extend: length overflow"); + new_len = bp->len + incr; + bp->data = (unsigned char *) myrealloc((void *) bp->data, new_len + 1); + bp->data[new_len] = 0; + bp->len = new_len; + bp->ptr = bp->data + used; + bp->cnt = bp->len - used; +} + +/* vstring_buf_get_ready - vbuf callback for read buffer empty condition */ + +static int vstring_buf_get_ready(VBUF *unused_buf) +{ + return (VBUF_EOF); /* be VSTREAM-friendly */ +} + +/* vstring_buf_put_ready - vbuf callback for write buffer full condition */ + +static int vstring_buf_put_ready(VBUF *bp) +{ + vstring_extend(bp, 1); + return (0); +} + +/* vstring_buf_space - vbuf callback to reserve space */ + +static int vstring_buf_space(VBUF *bp, ssize_t len) +{ + ssize_t need; + + if (len < 0) + msg_panic("vstring_buf_space: bad length %ld", (long) len); + if ((need = len - bp->cnt) > 0) + vstring_extend(bp, need); + return (0); +} + +/* vstring_alloc - create variable-length string */ + +VSTRING *vstring_alloc(ssize_t len) +{ + VSTRING *vp; + + /* + * Safety net: add a gratuitous null terminator so that C-style string + * operations won't scribble past the end. + */ + if (len < 1 || len > SSIZE_T_MAX - 1) + msg_panic("vstring_alloc: bad length %ld", (long) len); + vp = (VSTRING *) mymalloc(sizeof(*vp)); + vp->vbuf.flags = 0; + vp->vbuf.len = 0; + vp->vbuf.data = (unsigned char *) mymalloc(len + 1); + vp->vbuf.data[len] = 0; + vp->vbuf.len = len; + VSTRING_RESET(vp); + vp->vbuf.data[0] = 0; + vp->vbuf.get_ready = vstring_buf_get_ready; + vp->vbuf.put_ready = vstring_buf_put_ready; + vp->vbuf.space = vstring_buf_space; + return (vp); +} + +/* vstring_free - destroy variable-length string */ + +VSTRING *vstring_free(VSTRING *vp) +{ + if (vp->vbuf.data) + myfree((void *) vp->vbuf.data); + myfree((void *) vp); + return (0); +} + +/* vstring_ctl - modify memory management policy */ + +void vstring_ctl(VSTRING *vp,...) +{ + va_list ap; + int code; + + va_start(ap, vp); + while ((code = va_arg(ap, int)) != VSTRING_CTL_END) { + switch (code) { + default: + msg_panic("vstring_ctl: unknown code: %d", code); + case VSTRING_CTL_EXACT: + vp->vbuf.flags |= VSTRING_FLAG_EXACT; + break; + } + } + va_end(ap); +} + +/* vstring_truncate - truncate string */ + +VSTRING *vstring_truncate(VSTRING *vp, ssize_t len) +{ + ssize_t move; + + if (len < 0) { + len = (-len); + if ((move = VSTRING_LEN(vp) - len) > 0) + memmove(vstring_str(vp), vstring_str(vp) + move, len); + } + if (len < VSTRING_LEN(vp)) + VSTRING_AT_OFFSET(vp, len); + return (vp); +} + +/* vstring_set_payload_size - public version of VSTRING_AT_OFFSET */ + +VSTRING *vstring_set_payload_size(VSTRING *vp, ssize_t len) +{ + if (len < 0 || len > vp->vbuf.len) + msg_panic("vstring_set_payload_size: invalid offset: %ld", (long) len); + VSTRING_AT_OFFSET(vp, len); + return (vp); +} + +/* vstring_strcpy - copy string */ + +VSTRING *vstring_strcpy(VSTRING *vp, const char *src) +{ + VSTRING_RESET(vp); + + while (*src) { + VSTRING_ADDCH(vp, *src); + src++; + } + VSTRING_TERMINATE(vp); + return (vp); +} + +/* vstring_strncpy - copy string of limited length */ + +VSTRING *vstring_strncpy(VSTRING *vp, const char *src, ssize_t len) +{ + VSTRING_RESET(vp); + + while (len-- > 0 && *src) { + VSTRING_ADDCH(vp, *src); + src++; + } + VSTRING_TERMINATE(vp); + return (vp); +} + +/* vstring_strcat - append string */ + +VSTRING *vstring_strcat(VSTRING *vp, const char *src) +{ + while (*src) { + VSTRING_ADDCH(vp, *src); + src++; + } + VSTRING_TERMINATE(vp); + return (vp); +} + +/* vstring_strncat - append string of limited length */ + +VSTRING *vstring_strncat(VSTRING *vp, const char *src, ssize_t len) +{ + while (len-- > 0 && *src) { + VSTRING_ADDCH(vp, *src); + src++; + } + VSTRING_TERMINATE(vp); + return (vp); +} + +/* vstring_memcpy - copy buffer of limited length */ + +VSTRING *vstring_memcpy(VSTRING *vp, const char *src, ssize_t len) +{ + VSTRING_RESET(vp); + + VSTRING_SPACE(vp, len); + memcpy(vstring_str(vp), src, len); + VSTRING_AT_OFFSET(vp, len); + return (vp); +} + +/* vstring_memcat - append buffer of limited length */ + +VSTRING *vstring_memcat(VSTRING *vp, const char *src, ssize_t len) +{ + VSTRING_SPACE(vp, len); + memcpy(vstring_end(vp), src, len); + len += VSTRING_LEN(vp); + VSTRING_AT_OFFSET(vp, len); + return (vp); +} + +/* vstring_memchr - locate byte in buffer */ + +char *vstring_memchr(VSTRING *vp, int ch) +{ + unsigned char *cp; + + for (cp = (unsigned char *) vstring_str(vp); cp < (unsigned char *) vstring_end(vp); cp++) + if (*cp == ch) + return ((char *) cp); + return (0); +} + +/* vstring_insert - insert text into string */ + +VSTRING *vstring_insert(VSTRING *vp, ssize_t start, const char *buf, ssize_t len) +{ + ssize_t new_len; + + /* + * Sanity check. + */ + if (start < 0 || start >= VSTRING_LEN(vp)) + msg_panic("vstring_insert: bad start %ld", (long) start); + if (len < 0) + msg_panic("vstring_insert: bad length %ld", (long) len); + + /* + * Move the existing content and copy the new content. + */ + new_len = VSTRING_LEN(vp) + len; + VSTRING_SPACE(vp, len); + memmove(vstring_str(vp) + start + len, vstring_str(vp) + start, + VSTRING_LEN(vp) - start); + memcpy(vstring_str(vp) + start, buf, len); + VSTRING_AT_OFFSET(vp, new_len); + VSTRING_TERMINATE(vp); + return (vp); +} + +/* vstring_prepend - prepend text to string */ + +VSTRING *vstring_prepend(VSTRING *vp, const char *buf, ssize_t len) +{ + ssize_t new_len; + + /* + * Sanity check. + */ + if (len < 0) + msg_panic("vstring_prepend: bad length %ld", (long) len); + + /* + * Move the existing content and copy the new content. + */ + new_len = VSTRING_LEN(vp) + len; + VSTRING_SPACE(vp, len); + memmove(vstring_str(vp) + len, vstring_str(vp), VSTRING_LEN(vp)); + memcpy(vstring_str(vp), buf, len); + VSTRING_AT_OFFSET(vp, new_len); + VSTRING_TERMINATE(vp); + return (vp); +} + +/* vstring_export - VSTRING to bare string */ + +char *vstring_export(VSTRING *vp) +{ + char *cp; + + cp = (char *) vp->vbuf.data; + vp->vbuf.data = 0; + myfree((void *) vp); + return (cp); +} + +/* vstring_import - bare string to vstring */ + +VSTRING *vstring_import(char *str) +{ + VSTRING *vp; + ssize_t len; + + vp = (VSTRING *) mymalloc(sizeof(*vp)); + len = strlen(str); + vp->vbuf.flags = 0; + vp->vbuf.len = 0; + vp->vbuf.data = (unsigned char *) str; + vp->vbuf.len = len + 1; + VSTRING_AT_OFFSET(vp, len); + vp->vbuf.get_ready = vstring_buf_get_ready; + vp->vbuf.put_ready = vstring_buf_put_ready; + vp->vbuf.space = vstring_buf_space; + return (vp); +} + +/* vstring_sprintf - formatted string */ + +VSTRING *vstring_sprintf(VSTRING *vp, const char *format,...) +{ + va_list ap; + + va_start(ap, format); + vp = vstring_vsprintf(vp, format, ap); + va_end(ap); + return (vp); +} + +/* vstring_vsprintf - format string, vsprintf-like interface */ + +VSTRING *vstring_vsprintf(VSTRING *vp, const char *format, va_list ap) +{ + VSTRING_RESET(vp); + vbuf_print(&vp->vbuf, format, ap); + VSTRING_TERMINATE(vp); + return (vp); +} + +/* vstring_sprintf_append - append formatted string */ + +VSTRING *vstring_sprintf_append(VSTRING *vp, const char *format,...) +{ + va_list ap; + + va_start(ap, format); + vp = vstring_vsprintf_append(vp, format, ap); + va_end(ap); + return (vp); +} + +/* vstring_vsprintf_append - format + append string, vsprintf-like interface */ + +VSTRING *vstring_vsprintf_append(VSTRING *vp, const char *format, va_list ap) +{ + vbuf_print(&vp->vbuf, format, ap); + VSTRING_TERMINATE(vp); + return (vp); +} + +/* vstring_sprintf_prepend - format + prepend string, vsprintf-like interface */ + +VSTRING *vstring_sprintf_prepend(VSTRING *vp, const char *format,...) +{ + va_list ap; + ssize_t old_len = VSTRING_LEN(vp); + ssize_t result_len; + + /* Construct: old|new|free */ + va_start(ap, format); + vp = vstring_vsprintf_append(vp, format, ap); + va_end(ap); + result_len = VSTRING_LEN(vp); + + /* Construct: old|new|old|free */ + VSTRING_SPACE(vp, old_len); + vstring_memcat(vp, vstring_str(vp), old_len); + + /* Construct: new|old|free */ + memmove(vstring_str(vp), vstring_str(vp) + old_len, result_len); + VSTRING_AT_OFFSET(vp, result_len); + VSTRING_TERMINATE(vp); + return (vp); +} + +#ifdef TEST + + /* + * Test program - concatenate all command-line arguments into one string. + */ +#include <stdio.h> + +int main(int argc, char **argv) +{ + VSTRING *vp = vstring_alloc(1); + int n; + + /* + * Report the location of the gratuitous null terminator. + */ + for (n = 1; n <= 5; n++) { + VSTRING_ADDCH(vp, 'x'); + printf("payload/buffer size %d/%ld, strlen() %ld\n", + n, (long) (vp)->vbuf.len, (long) strlen(vstring_str(vp))); + } + + VSTRING_RESET(vp); + while (argc-- > 0) { + vstring_strcat(vp, *argv++); + vstring_strcat(vp, "."); + } + printf("argv concatenated: %s\n", vstring_str(vp)); + vstring_free(vp); + return (0); +} + +#endif |