Changes from sbuf-20001210b.diff: - minor changes to the man page, including the addition of SEE ALSO and HISTORY sections. - sbuf_printf() modified to call kvprintf() directly. - s_next added to struct sbuf in preparation for auto-extensible sbufs. Notes: - sbuf builds, but has not been tested. - sbuf is currently very strict, with KASSERTS every which way and a requirement that the string always be NULL-terminated. Some of these assertions may later be relaxed for perfomance reasons. - possible uses for the flags parameter to sbuf_new(): - specify that an sbuf is to aut-extend its buffer rather than overflow - make sbuf_new() use M_NOWAIT instead of M_WAITOK if the caller is able to get along without the sbuf (perhaps by outputting or returning unformatted data) in low-memory situations. -- DES Index: sys/kern/subr_sbuf.c =================================================================== RCS file: subr_sbuf.c diff -N subr_sbuf.c --- /dev/null Mon Dec 11 03:19:15 2000 +++ subr_sbuf.c Mon Dec 11 18:37:07 2000 @@ -0,0 +1,293 @@ +/*- + * Copyright (c) 2000 Poul-Henning Kamp and Dag-Erling Coïdan Smørgrav + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include + +#include + +MALLOC_DEFINE(sbuf_malloc_type, "sbuf", "string buffers"); + +/* + * Initialize an sbuf. + * If ptr is non-NULL, it points to a static or already-allocated string + * big enough to hold at least length characters. + */ +int +sbuf_new(struct sbuf *s, char *ptr, size_t length, int flags) +{ + KASSERT(s != NULL, + (__FUNCTION__ " called with a NULL sbuf pointer")); + + /* flags are currently ignored */ + s->s_len = 0; + s->s_size = length; + if (ptr) { + s->s_buf = ptr; + s->s_dynamic = 0; + return (0); + } + s->s_buf = malloc(s->s_size, sbuf_malloc_type, M_WAITOK); + if (s->s_buf == NULL) + return (-1); + s->s_dynamic = 1; + s->s_done = 0; + return (0); +} + +/* + * Set the sbuf's position to an arbitrary value + */ +int +sbuf_setpos(struct sbuf *s, size_t pos) +{ + KASSERT(s != NULL, + (__FUNCTION__ " called with a NULL sbuf pointer")); + KASSERT(s->s_buf != NULL, + (__FUNCTION__ " called with unitialized or corrupt sbuf")); + KASSERT(s->s_done == 0, + (__FUNCTION__ " called with a finished or corrupt sbuf")); + KASSERT(pos >= 0, + ("attempt to seek to a negative position (%d)", pos)); + KASSERT(pos < s->s_size, + ("attempt to seek past end of sbuf (%d >= %d)", pos, s->s_size)); + + if (pos < 0 || pos > s->s_len) + return (-1); + s->s_len = pos; + return (0); +} + +/* + * Append the string pointed to by ptr to an sbuf. + */ +int +sbuf_cat(struct sbuf *s, char *ptr) +{ + KASSERT(s != NULL, + (__FUNCTION__ " called with a NULL sbuf pointer")); + KASSERT(s->s_buf != NULL, + (__FUNCTION__ " called with unitialized or corrupt sbuf")); + KASSERT(s->s_done == 0, + (__FUNCTION__ " called with a finished or corrupt sbuf")); + KASSERT(s->s_len <= s->s_size, + ("sbuf overflowed (%d > %d)", s->s_len, s->s_size)); + + if (s->s_len == s->s_size) + return (-1); + + while (*ptr && s->s_len < s->s_size - 1) + s->s_buf[s->s_len++] = *ptr++; + s->s_buf[s->s_len] = '\0'; + if (*ptr) { + s->s_len = s->s_size; + return (-1); + } + return (0); +} + +/* + * Copy the string pointed to by ptr into an sbuf. + */ +int +sbuf_cpy(struct sbuf *s, char *ptr) +{ + KASSERT(s != NULL, + (__FUNCTION__ " called with a NULL sbuf pointer")); + KASSERT(s->s_buf != NULL, + (__FUNCTION__ " called with unitialized or corrupt sbuf")); + KASSERT(s->s_done == 0, + (__FUNCTION__ " called with a finished or corrupt sbuf")); + + s->s_len = 0; + return (sbuf_cat(s, ptr)); +} + +/* + * PCHAR function for sbuf_printf() + */ +static void +_sbuf_pchar(int c, void *v) +{ + struct sbuf *s = (struct sbuf *)v; + + if (s->s_len < s->s_size) + s->s_buf[s->s_len++] = c; +} + +/* + * Format the given arguments and append the resulting string to an sbuf. + */ +int +sbuf_printf(struct sbuf *s, char *fmt, ...) +{ + va_list ap; + size_t len; + + KASSERT(s != NULL, + (__FUNCTION__ " called with a NULL sbuf pointer")); + KASSERT(s->s_buf != NULL, + (__FUNCTION__ " called with unitialized or corrupt sbuf")); + KASSERT(s->s_done == 0, + (__FUNCTION__ " called with a finished or corrupt sbuf")); + KASSERT(s->s_len <= s->s_size, + ("sbuf overflowed (%d > %d)", s->s_len, s->s_size)); + KASSERT(fmt != NULL, + (__FUNCTION__ " called with a NULL format string")); + + if (s->s_len >= s->s_size) + return (-1); + + va_start(ap, fmt); + len = kvprintf(fmt, _sbuf_pchar, s, 10, ap); + va_end(ap); + + /* + * Note that the length returned by vsnprintf does not count + * the terminating '\0'. + */ + s->s_len += len; + if (s->s_len >= s->s_size) { + s->s_len = s->s_size; + s->s_buf[s->s_len - 1] = '\0'; + return (-1); + } + return (0); +} + +/* + * Append a character to an sbuf. + */ +int +sbuf_putc(struct sbuf *s, int c) +{ + KASSERT(s != NULL, + (__FUNCTION__ " called with a NULL sbuf pointer")); + KASSERT(s->s_buf != NULL, + (__FUNCTION__ " called with unitialized or corrupt sbuf")); + KASSERT(s->s_done == 0, + (__FUNCTION__ " called with a finished or corrupt sbuf")); + KASSERT(s->s_len <= s->s_size, + ("sbuf overflowed (%d > %d)", s->s_len, s->s_size)); + + if (s->s_len >= s->s_size) + return (-1); + + if (s->s_len == s->s_size - 1) { + s->s_len = s->s_size; + return (-1); + } + s->s_buf[s->s_len++] = c; + s->s_buf[s->s_len] = 0; + return (0); +} + +/* + * Finish off an sbuf. + */ +int +sbuf_finish(struct sbuf *s) +{ + KASSERT(s != NULL, + (__FUNCTION__ " called with a NULL sbuf pointer")); + KASSERT(s->s_buf != NULL, + (__FUNCTION__ " called with unitialized or corrupt sbuf")); + KASSERT(s->s_done == 0, + (__FUNCTION__ " called with a finished or corrupt sbuf")); + KASSERT(s->s_len <= s->s_size, + ("sbuf overflowed (%d > %d)", s->s_len, s->s_size)); + + if (s->s_len >= s->s_size) + return (-1); + + KASSERT(s->s_buf[s->s_len] == '\0', ("sbuf not terminated")); + + s->s_done = 1; + return (0); +} + +/* + * Return a pointer to the sbuf data. + */ +char * +sbuf_data(struct sbuf *s) +{ + KASSERT(s != NULL, + (__FUNCTION__ " called with a NULL sbuf pointer")); + KASSERT(s->s_buf != NULL, + (__FUNCTION__ " called with unitialized or corrupt sbuf")); + KASSERT(s->s_done == 1, + (__FUNCTION__ " called with an unfinished or corrupt sbuf")); + KASSERT(s->s_len < s->s_size, + ("sbuf overflowed (%d > %d)", s->s_len, s->s_size)); + + if (s->s_len >= s->s_size) + return (NULL); + return s->s_buf; +} + +/* + * Return the length of the sbuf data. + */ +size_t +sbuf_len(struct sbuf *s) +{ + KASSERT(s != NULL, + (__FUNCTION__ " called with a NULL sbuf pointer")); + KASSERT(s->s_buf != NULL, + (__FUNCTION__ " called with unitialized or corrupt sbuf")); + KASSERT(s->s_done == 1, + (__FUNCTION__ " called with an unfinished or corrupt sbuf")); + KASSERT(s->s_len <= s->s_size, + ("sbuf overflowed (%d > %d)", s->s_len, s->s_size)); + + if (s->s_len >= s->s_size) + return (0); + return s->s_len; +} + +/* + * Clear an sbuf, free its buffer if necessary. + */ +void +sbuf_delete(struct sbuf *s) +{ + KASSERT(s != NULL, + (__FUNCTION__ " called with a NULL sbuf pointer")); + KASSERT(s->s_buf != NULL, + (__FUNCTION__ " called with unitialized or corrupt sbuf")); + + if (s->s_dynamic) + free(s->s_buf, sbuf_malloc_type); + bzero(s, sizeof *s); +} Index: sys/sys/sbuf.h =================================================================== RCS file: sbuf.h diff -N sbuf.h --- /dev/null Mon Dec 11 03:19:15 2000 +++ sbuf.h Mon Dec 11 18:17:09 2000 @@ -0,0 +1,55 @@ +/*- + * Copyright (c) 2000 Poul-Henning Kamp and Dag-Erling Coïdan Smørgrav + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _SYS_SBUF_H_ +#define _SYS_SBUF_H_ + +struct sbuf { + char *s_buf; /* storage buffer */ + size_t s_size; /* size of storage buffer */ + size_t s_len; /* current length of string */ + int s_dynamic; /* storage buffer must be freed */ + int s_done; /* sbuf is finished and read-only */ + int s_flags; /* flags */ + struct sbuf *s_next; /* next in chain */ +}; + +int sbuf_new(struct sbuf *s, char *ptr, size_t length, int flags); +int sbuf_setpos(struct sbuf *s, size_t pos); +int sbuf_cat(struct sbuf *s, char *ptr); +int sbuf_cpy(struct sbuf *s, char *ptr); +int sbuf_printf(struct sbuf *s, char *fmt, ...); +int sbuf_putc(struct sbuf *s, int c); +int sbuf_finish(struct sbuf *s); +char *sbuf_data(struct sbuf *s); +size_t sbuf_len(struct sbuf *s); +void sbuf_delete(struct sbuf *s); + +#endif Index: sys/conf/files =================================================================== RCS file: /home/ncvs/src/sys/conf/files,v retrieving revision 1.448 diff -u -r1.448 files --- sys/conf/files 2000/12/02 05:42:30 1.448 +++ sys/conf/files 2000/12/10 16:18:38 @@ -648,6 +648,7 @@ kern/subr_prf.c standard kern/subr_prof.c standard kern/subr_rman.c standard +kern/subr_sbuf.c standard kern/subr_scanf.c standard kern/subr_taskqueue.c standard kern/subr_xxx.c standard Index: share/man/man9/Makefile =================================================================== RCS file: /home/ncvs/src/share/man/man9/Makefile,v retrieving revision 1.87 diff -u -r1.87 Makefile --- share/man/man9/Makefile 2000/11/22 03:44:55 1.87 +++ share/man/man9/Makefile 2000/12/10 19:50:11 @@ -48,6 +48,7 @@ bus_alloc_resource.9 bus_release_resource.9 \ VOP_ACLCHECK.9 VOP_GETACL.9 VOP_GETEXTATTR.9 VOP_SETACL.9 \ VOP_SETEXTATTR.9 acl.9 extattr.9 kobj.9 taskqueue.9 accept_filter.9 \ + sbuf.9 \ sysctl_add_oid.9 sysctl_ctx_init.9 MLINKS+=MD5.9 MD5Init.9 MD5.9 MD5Transform.9 @@ -179,5 +180,16 @@ MLINKS+=sysctl_ctx_init.9 sysctl_ctx_entry_add.9 MLINKS+=sysctl_ctx_init.9 sysctl_ctx_entry_del.9 MLINKS+=sysctl_ctx_init.9 sysctl_ctx_entry_find.9 + +MLINKS+=sbuf.9 sbuf_new.9 +MLINKS+=sbuf.9 sbuf_setpos.9 +MLINKS+=sbuf.9 sbuf_cat.9 +MLINKS+=sbuf.9 sbuf_cpy.9 +MLINKS+=sbuf.9 sbuf_printf.9 +MLINKS+=sbuf.9 sbuf_putc.9 +MLINKS+=sbuf.9 sbuf_finish.9 +MLINKS+=sbuf.9 sbuf_data.9 +MLINKS+=sbuf.9 sbuf_len.9 +MLINKS+=sbuf.9 sbuf_delete.9 .include Index: share/man/man9/sbuf.9 =================================================================== RCS file: sbuf.9 diff -N sbuf.9 --- /dev/null Mon Dec 11 03:19:15 2000 +++ sbuf.9 Sun Dec 10 22:45:02 2000 @@ -0,0 +1,209 @@ +.\"- +.\" Copyright (c) 2000 Poul Henning Kamp and Dag-Erling Coïdan Smørgrav +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd December 10th, 2000 +.Dt sbuf 9 +.Os FreeBSD +.Sh NAME +.Nm sbuf_new , +.Nm sbuf_setpos , +.Nm sbuf_cat , +.Nm sbuf_cpy , +.Nm sbuf_printf , +.Nm sbuf_putc , +.Nm sbuf_finish , +.Nm sbuf_data , +.Nm sbuf_len , +.Nm sbuf_delete +.Nd safe string formatting +.Sh SYNOPSIS +.Fd #include +.Ft int +.Fn sbuf_new "struct sbuf *s" "char *ptr" "size_t length" "int flags" +.Ft int +.Fn sbuf_setpos "struct sbuf *s" "size_t pos" +.Ft int +.Fn sbuf_cat "struct sbuf *s" "char *ptr" +.Ft int +.Fn sbuf_cpy "struct sbuf *s" "char *ptr" +.Ft int +.Fn sbuf_printf "struct sbuf *s" "char *fmt" "..." +.Ft int +.Fn sbuf_putc "struct sbuf *s" "int c" +.Ft int +.Fn sbuf_finish "struct sbuf *s" +.Ft char * +.Fn sbuf_data "struct sbuf *s" +.Ft size_t +.Fn sbuf_len "struct sbuf *s" +.Ft void +.Fn sbuf_delete "struct sbuf *s" +.Sh DESCRIPTION +The +.Nm sbuf +family of functions allow one to safely allocate, construct and +release bounded null-terminated strings in kernel space. +Instead of arrays of characters, these functions operate on structures +called +.Fa sbufs , +defined in +.Aq Pa sys/sbuf.h . +.Pp +The +.Nm sbuf_new +function initializes the +.Fa sbuf +pointed to by its first argument. +The second argument is a pointer to a buffer in which to store the +actual string; if it is +.Dv NULL , +.Nm sbuf_new +will allocate its own buffer of the appropriate size. +The third argument is the desired size of the storage buffer; if a +buffer is provided by the caller, this must not exceed its actual +size. +The fourth argument, +.Fa flags , +is currently unused and should always be set to zero. +.Pp +The +.Nm sbuf_setpos +function sets the +.Fa sbuf 's +current position to +.Fa pos , +which is a value between zero and one less than the size of the +storage buffer. +.Pp +The +.Nm sbuf_cat +function appends the string pointed to by +.Fa ptr +to the +.Fa sbuf +at the current position. +.Pp +The +.Nm sbuf_cpy +function replaces the contents of the +.Fa sbuf +with those of the string pointed to by +.Fa ptr . +This is equivalent to calling +.Nm sbuf_cat +with a fresh +.Fa sbuf +or one which position has been reset to zero with +.Nm sbuf_setpos . +.Pp +The +.Nm sbuf_printf +function formats its arguments according to the format string pointed +to by +.Fa fmt +and appends the resulting string to the +.Fa sbuf +at the current position. +.Pp +The +.Nm sbuf_putc +function appends the character +.Fa c +to the +.Fa sbuf +at the current position. +.Pp +The +.Nm sbuf_finish +function marks the +.Fa sbuf +as finished, which means that it may no longer be modified using +.Nm sbuf_setpos , +.Nm sbuf_cat , +.Nm sbuf_cpu , +.Nm sbuf_printf +or +.Nm sbuf_putc . +.Pp +The +.Nm sbuf_data +and +.Nm sbuf_len +functions return the actual string and its length, respectively, and +only work on a finished +.Fa sbuf . +.Pp +Finally, the +.Nm sbuf_delete +function clears the +.Fa sbuf +and frees its storage buffer if it was allocated by +.Nm sbuf_new . +.Sh RETURN VALUES +.Nm sbuf_new +returns \-1 if it failed to allocate a storage buffer, and zero +otherwise. +.Pp +.Nm sbuf_setpos +returns \-1 if +.Fa pos +was invalid, and zero otherwise. +.Pp +.Nm sbuf_cat , +.Nm sbuf_cpy , +.Nm sbuf_printf , +.Nm sbuf_putc , +and +.Nm sbuf_finish +all return \-1 if the buffer overflowed, and zero otherwise. +.Pp +.Nm sbuf_data +and +.Nm sbuf_len +return +.Fa NULL +and 0, respectively, if the buffer overflowed. +.Sh SEE ALSO +.Xr printf 3 , +.Xr strcat 3 , +.Xr strcpy 3 +.Sh HISTORY +The +.Nm sbuf +family of functions first appeared in +.Fx 4.3 . +.Sh AUTHORS +.An -nosplit +The +.Nm sbuf +family of functions was designed by +.An Poul-Henning Kamp Aq phk@FreeBSD.org +and implemented by +.An Dag-Erling Co\(:idan Sm\(/orgrav Aq des@FreeBSD.org . +.Pp +This manual page was written by +.An Dag-Erling Co\(:idan Sm\(/orgrav Aq des@FreeBSD.org .