subr_sbuf.c revision 79162
169990Sdes/*- 269990Sdes * Copyright (c) 2000 Poul-Henning Kamp and Dag-Erling Co�dan Sm�rgrav 369990Sdes * All rights reserved. 469990Sdes * 569990Sdes * Redistribution and use in source and binary forms, with or without 669990Sdes * modification, are permitted provided that the following conditions 769990Sdes * are met: 869990Sdes * 1. Redistributions of source code must retain the above copyright 969990Sdes * notice, this list of conditions and the following disclaimer 1069990Sdes * in this position and unchanged. 1169990Sdes * 2. Redistributions in binary form must reproduce the above copyright 1269990Sdes * notice, this list of conditions and the following disclaimer in the 1369990Sdes * documentation and/or other materials provided with the distribution. 1469990Sdes * 3. The name of the author may not be used to endorse or promote products 1569990Sdes * derived from this software without specific prior written permission. 1669990Sdes * 1769990Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1869990Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1969990Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2069990Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2169990Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2269990Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2369990Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2469990Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2569990Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2669990Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2769990Sdes * 2869990Sdes * $FreeBSD: head/sys/kern/subr_sbuf.c 79162 2001-07-03 21:46:43Z des $ 2969990Sdes */ 3069990Sdes 3169990Sdes#include <sys/param.h> 3274840Sken#include <sys/sbuf.h> 3374840Sken 3474840Sken#ifdef _KERNEL 3569990Sdes#include <sys/kernel.h> 3669990Sdes#include <sys/malloc.h> 3769990Sdes#include <sys/systm.h> 3869990Sdes#include <machine/stdarg.h> 3974840Sken#else /* _KERNEL */ 4074840Sken#include <stdarg.h> 4178340Sjlemon#include <stdlib.h> 4274840Sken#endif /* _KERNEL */ 4369990Sdes 4474840Sken#ifdef _KERNEL 4569990SdesMALLOC_DEFINE(M_SBUF, "sbuf", "string buffers"); 4674840Sken#define SBMALLOC(size) malloc(size, M_SBUF, M_WAITOK) 4774840Sken#define SBFREE(buf) free(buf, M_SBUF) 4874840Sken#else /* _KERNEL */ 4974840Sken#define KASSERT(e, m) 5074840Sken#define SBMALLOC(size) malloc(size) 5174840Sken#define SBFREE(buf) free(buf) 5274840Sken#define min(x,y) MIN(x,y) 5374840Sken#endif /* _KERNEL */ 5469990Sdes 5571721Sdes/* 5671721Sdes * Predicates 5771721Sdes */ 5871721Sdes#define SBUF_ISDYNAMIC(s) ((s)->s_flags & SBUF_DYNAMIC) 5977989Sdes#define SBUF_ISDYNSTRUCT(s) ((s)->s_flags & SBUF_DYNSTRUCT) 6071721Sdes#define SBUF_ISFINISHED(s) ((s)->s_flags & SBUF_FINISHED) 6171721Sdes#define SBUF_HASOVERFLOWED(s) ((s)->s_flags & SBUF_OVERFLOWED) 6271721Sdes#define SBUF_HASROOM(s) ((s)->s_len < (s)->s_size - 1) 6371721Sdes 6471721Sdes/* 6571721Sdes * Set / clear flags 6671721Sdes */ 6771721Sdes#define SBUF_SETFLAG(s, f) do { (s)->s_flags |= (f); } while (0) 6871721Sdes#define SBUF_CLEARFLAG(s, f) do { (s)->s_flags &= ~(f); } while (0) 6971721Sdes 7071721Sdes/* 7171721Sdes * Debugging support 7271721Sdes */ 7374840Sken#if defined(_KERNEL) && defined(INVARIANTS) 7469990Sdesstatic void 7573891Sdes_assert_sbuf_integrity(char *fun, struct sbuf *s) 7669990Sdes{ 7769990Sdes KASSERT(s != NULL, 7873891Sdes ("%s called with a NULL sbuf pointer", fun)); 7969990Sdes KASSERT(s->s_buf != NULL, 8073891Sdes ("%s called with unitialized or corrupt sbuf", fun)); 8169990Sdes KASSERT(s->s_len < s->s_size, 8269990Sdes ("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size)); 8369990Sdes} 8469990Sdes 8569990Sdesstatic void 8673891Sdes_assert_sbuf_state(char *fun, struct sbuf *s, int state) 8769990Sdes{ 8869990Sdes KASSERT((s->s_flags & SBUF_FINISHED) == state, 8973891Sdes ("%s called with %sfinished or corrupt sbuf", fun, 9069990Sdes (state ? "un" : ""))); 9169990Sdes} 9273891Sdes#define assert_sbuf_integrity(s) _assert_sbuf_integrity(__FUNCTION__, (s)) 9373891Sdes#define assert_sbuf_state(s, i) _assert_sbuf_state(__FUNCTION__, (s), (i)) 9474840Sken#else /* _KERNEL && INVARIANTS */ 9569990Sdes#define assert_sbuf_integrity(s) do { } while (0) 9669990Sdes#define assert_sbuf_state(s, i) do { } while (0) 9774840Sken#endif /* _KERNEL && INVARIANTS */ 9869990Sdes 9969990Sdes/* 10069990Sdes * Initialize an sbuf. 10169990Sdes * If buf is non-NULL, it points to a static or already-allocated string 10269990Sdes * big enough to hold at least length characters. 10369990Sdes */ 10477989Sdesstruct sbuf * 10571721Sdessbuf_new(struct sbuf *s, char *buf, int length, int flags) 10669990Sdes{ 10771721Sdes KASSERT(length >= 0, 10871721Sdes ("attempt to create an sbuf of negative length (%d)", length)); 10969990Sdes KASSERT(flags == 0, 11069990Sdes (__FUNCTION__ " called with non-zero flags")); 11169990Sdes 11277989Sdes if (s == NULL) { 11377989Sdes s = (struct sbuf *)SBMALLOC(sizeof *s); 11477989Sdes if (s == NULL) 11577989Sdes return (NULL); 11677989Sdes bzero(s, sizeof *s); 11777989Sdes SBUF_SETFLAG(s, SBUF_DYNSTRUCT); 11877989Sdes } else { 11977989Sdes bzero(s, sizeof *s); 12077989Sdes } 12169990Sdes s->s_size = length; 12269990Sdes if (buf) { 12369990Sdes s->s_buf = buf; 12477989Sdes return (s); 12569990Sdes } 12674840Sken s->s_buf = (char *)SBMALLOC(s->s_size); 12777989Sdes if (s->s_buf == NULL) { 12877989Sdes if (SBUF_ISDYNSTRUCT(s)) 12977989Sdes SBFREE(s); 13077989Sdes return (NULL); 13177989Sdes } 13269990Sdes SBUF_SETFLAG(s, SBUF_DYNAMIC); 13377989Sdes return (s); 13469990Sdes} 13569990Sdes 13669990Sdes/* 13771721Sdes * Clear an sbuf and reset its position 13871721Sdes */ 13971721Sdesvoid 14071721Sdessbuf_clear(struct sbuf *s) 14171721Sdes{ 14271721Sdes assert_sbuf_integrity(s); 14371724Sdes /* don't care if it's finished or not */ 14471721Sdes 14571721Sdes SBUF_CLEARFLAG(s, SBUF_FINISHED); 14671721Sdes SBUF_CLEARFLAG(s, SBUF_OVERFLOWED); 14771721Sdes s->s_len = 0; 14871721Sdes} 14971721Sdes 15071721Sdes/* 15169990Sdes * Set the sbuf's position to an arbitrary value 15269990Sdes */ 15369990Sdesint 15471721Sdessbuf_setpos(struct sbuf *s, int pos) 15569990Sdes{ 15669990Sdes assert_sbuf_integrity(s); 15769990Sdes assert_sbuf_state(s, 0); 15869990Sdes 15969990Sdes KASSERT(pos >= 0, 16069990Sdes ("attempt to seek to a negative position (%d)", pos)); 16169990Sdes KASSERT(pos < s->s_size, 16269990Sdes ("attempt to seek past end of sbuf (%d >= %d)", pos, s->s_size)); 16369990Sdes 16469990Sdes if (pos < 0 || pos > s->s_len) 16569990Sdes return (-1); 16669990Sdes s->s_len = pos; 16769990Sdes return (0); 16869990Sdes} 16969990Sdes 17069990Sdes/* 17178077Sdes * Append a byte string to an sbuf. 17278077Sdes */ 17378077Sdesint 17478077Sdessbuf_bcat(struct sbuf *s, const char *str, size_t len) 17578077Sdes{ 17678077Sdes assert_sbuf_integrity(s); 17778077Sdes assert_sbuf_state(s, 0); 17878077Sdes 17978077Sdes if (SBUF_HASOVERFLOWED(s)) 18078077Sdes return (-1); 18178077Sdes 18278077Sdes while (len-- && SBUF_HASROOM(s)) 18378077Sdes s->s_buf[s->s_len++] = *str++; 18478077Sdes if (len) { 18578077Sdes SBUF_SETFLAG(s, SBUF_OVERFLOWED); 18678077Sdes return (-1); 18778077Sdes } 18878077Sdes return (0); 18978077Sdes} 19078077Sdes 19178077Sdes#ifdef _KERNEL 19278077Sdes/* 19378077Sdes * Copy a byte string from userland into an sbuf. 19478077Sdes */ 19578077Sdesint 19678077Sdessbuf_bcopyin(struct sbuf *s, const void *uaddr, size_t len) 19778077Sdes{ 19878077Sdes assert_sbuf_integrity(s); 19978077Sdes assert_sbuf_state(s, 0); 20078077Sdes 20178077Sdes if (SBUF_HASOVERFLOWED(s)) 20278077Sdes return (-1); 20378077Sdes 20478077Sdes if (len == 0) 20578077Sdes return (0); 20678077Sdes if (len > (s->s_size - s->s_len - 1)) 20778077Sdes len = s->s_size - s->s_len - 1; 20878092Sdes if (copyin(uaddr, s->s_buf + s->s_len, len) != 0) 20978092Sdes return (-1); 21078095Sdes s->s_len += len; 21178077Sdes 21278077Sdes return (0); 21378077Sdes} 21478077Sdes#endif 21578077Sdes 21678077Sdes/* 21778077Sdes * Copy a byte string into an sbuf. 21878077Sdes */ 21978077Sdesint 22078077Sdessbuf_bcpy(struct sbuf *s, const char *str, size_t len) 22178077Sdes{ 22278077Sdes assert_sbuf_integrity(s); 22378077Sdes assert_sbuf_state(s, 0); 22478077Sdes 22578077Sdes sbuf_clear(s); 22678077Sdes return (sbuf_bcat(s, str, len)); 22778077Sdes} 22878077Sdes 22978077Sdes/* 23069990Sdes * Append a string to an sbuf. 23169990Sdes */ 23269990Sdesint 23374840Skensbuf_cat(struct sbuf *s, const char *str) 23469990Sdes{ 23569990Sdes assert_sbuf_integrity(s); 23669990Sdes assert_sbuf_state(s, 0); 23769990Sdes 23869990Sdes if (SBUF_HASOVERFLOWED(s)) 23969990Sdes return (-1); 24069990Sdes 24169990Sdes while (*str && SBUF_HASROOM(s)) 24269990Sdes s->s_buf[s->s_len++] = *str++; 24369990Sdes if (*str) { 24469990Sdes SBUF_SETFLAG(s, SBUF_OVERFLOWED); 24569990Sdes return (-1); 24669990Sdes } 24769990Sdes return (0); 24869990Sdes} 24969990Sdes 25078077Sdes#ifdef _KERNEL 25169990Sdes/* 25278077Sdes * Copy a string from userland into an sbuf. 25378077Sdes */ 25478077Sdesint 25578077Sdessbuf_copyin(struct sbuf *s, const void *uaddr, size_t len) 25678077Sdes{ 25778077Sdes size_t done; 25878077Sdes 25978077Sdes assert_sbuf_integrity(s); 26078077Sdes assert_sbuf_state(s, 0); 26178077Sdes 26278077Sdes if (SBUF_HASOVERFLOWED(s)) 26378077Sdes return (-1); 26478077Sdes 26578077Sdes if (len == 0 || len > (s->s_size - s->s_len - 1)) 26678077Sdes len = s->s_size - s->s_len - 1; 26778077Sdes switch (copyinstr(uaddr, s->s_buf + s->s_len, len + 1, &done)) { 26878077Sdes case ENAMETOOLONG: 26978077Sdes SBUF_SETFLAG(s, SBUF_OVERFLOWED); 27078077Sdes /* fall through */ 27178077Sdes case 0: 27278077Sdes s->s_len += done - 1; 27378077Sdes break; 27478077Sdes default: 27578077Sdes return (-1); /* XXX */ 27678077Sdes } 27778077Sdes 27878077Sdes return (0); 27978077Sdes} 28078077Sdes#endif 28178077Sdes 28278077Sdes/* 28369990Sdes * Copy a string into an sbuf. 28469990Sdes */ 28569990Sdesint 28674840Skensbuf_cpy(struct sbuf *s, const char *str) 28769990Sdes{ 28869990Sdes assert_sbuf_integrity(s); 28969990Sdes assert_sbuf_state(s, 0); 29069990Sdes 29171721Sdes sbuf_clear(s); 29269990Sdes return (sbuf_cat(s, str)); 29369990Sdes} 29469990Sdes 29569990Sdes/* 29669990Sdes * Format the given arguments and append the resulting string to an sbuf. 29769990Sdes */ 29869990Sdesint 29979162Sdessbuf_printf(struct sbuf *s, const char *fmt, ...) 30069990Sdes{ 30169990Sdes va_list ap; 30271721Sdes int len; 30369990Sdes 30469990Sdes assert_sbuf_integrity(s); 30569990Sdes assert_sbuf_state(s, 0); 30669990Sdes 30769990Sdes KASSERT(fmt != NULL, 30869990Sdes (__FUNCTION__ " called with a NULL format string")); 30969990Sdes 31069990Sdes if (SBUF_HASOVERFLOWED(s)) 31169990Sdes return (-1); 31269990Sdes 31369990Sdes va_start(ap, fmt); 31474840Sken len = vsnprintf(&s->s_buf[s->s_len], s->s_size - s->s_len, fmt, ap); 31569990Sdes va_end(ap); 31669990Sdes 31774840Sken /* 31874840Sken * s->s_len is the length of the string, without the terminating nul. 31974840Sken * When updating s->s_len, we must subtract 1 from the length that 32074840Sken * we passed into vsnprintf() because that length includes the 32174840Sken * terminating nul. 32274840Sken * 32374840Sken * vsnprintf() returns the amount that would have been copied, 32474840Sken * given sufficient space, hence the min() calculation below. 32574840Sken */ 32674840Sken s->s_len += min(len, s->s_size - s->s_len - 1); 32774840Sken if (!SBUF_HASROOM(s)) 32874840Sken SBUF_SETFLAG(s, SBUF_OVERFLOWED); 32974840Sken 33069990Sdes KASSERT(s->s_len < s->s_size, 33169990Sdes ("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size)); 33269990Sdes 33369990Sdes if (SBUF_HASOVERFLOWED(s)) 33469990Sdes return (-1); 33569990Sdes return (0); 33669990Sdes} 33769990Sdes 33869990Sdes/* 33969990Sdes * Append a character to an sbuf. 34069990Sdes */ 34169990Sdesint 34269990Sdessbuf_putc(struct sbuf *s, int c) 34369990Sdes{ 34469990Sdes assert_sbuf_integrity(s); 34569990Sdes assert_sbuf_state(s, 0); 34669990Sdes 34769990Sdes if (SBUF_HASOVERFLOWED(s)) 34869990Sdes return (-1); 34969990Sdes 35069990Sdes if (!SBUF_HASROOM(s)) { 35169990Sdes SBUF_SETFLAG(s, SBUF_OVERFLOWED); 35269990Sdes return (-1); 35369990Sdes } 35473891Sdes if (c != '\0') 35573891Sdes s->s_buf[s->s_len++] = c; 35669990Sdes return (0); 35769990Sdes} 35869990Sdes 35969990Sdes/* 36071721Sdes * Check if an sbuf overflowed 36171721Sdes */ 36271721Sdesint 36371721Sdessbuf_overflowed(struct sbuf *s) 36471721Sdes{ 36571721Sdes return SBUF_HASOVERFLOWED(s); 36671721Sdes} 36771721Sdes 36871721Sdes/* 36969990Sdes * Finish off an sbuf. 37069990Sdes */ 37171721Sdesvoid 37269990Sdessbuf_finish(struct sbuf *s) 37369990Sdes{ 37469990Sdes assert_sbuf_integrity(s); 37569990Sdes assert_sbuf_state(s, 0); 37669990Sdes 37773891Sdes s->s_buf[s->s_len] = '\0'; 37871721Sdes SBUF_CLEARFLAG(s, SBUF_OVERFLOWED); 37969990Sdes SBUF_SETFLAG(s, SBUF_FINISHED); 38069990Sdes} 38169990Sdes 38269990Sdes/* 38369990Sdes * Return a pointer to the sbuf data. 38469990Sdes */ 38569990Sdeschar * 38669990Sdessbuf_data(struct sbuf *s) 38769990Sdes{ 38869990Sdes assert_sbuf_integrity(s); 38969990Sdes assert_sbuf_state(s, SBUF_FINISHED); 39069990Sdes 39169990Sdes return s->s_buf; 39269990Sdes} 39369990Sdes 39469990Sdes/* 39569990Sdes * Return the length of the sbuf data. 39669990Sdes */ 39771721Sdesint 39869990Sdessbuf_len(struct sbuf *s) 39969990Sdes{ 40069990Sdes assert_sbuf_integrity(s); 40171724Sdes /* don't care if it's finished or not */ 40269990Sdes 40369990Sdes if (SBUF_HASOVERFLOWED(s)) 40471721Sdes return (-1); 40569990Sdes return s->s_len; 40669990Sdes} 40769990Sdes 40869990Sdes/* 40969990Sdes * Clear an sbuf, free its buffer if necessary. 41069990Sdes */ 41169990Sdesvoid 41269990Sdessbuf_delete(struct sbuf *s) 41369990Sdes{ 41469990Sdes assert_sbuf_integrity(s); 41569990Sdes /* don't care if it's finished or not */ 41669990Sdes 41769990Sdes if (SBUF_ISDYNAMIC(s)) 41874840Sken SBFREE(s->s_buf); 41969990Sdes bzero(s, sizeof *s); 42077989Sdes if (SBUF_ISDYNSTRUCT(s)) 42177989Sdes SBFREE(s); 42269990Sdes} 423