subr_sbuf.c revision 73891
1/*- 2 * Copyright (c) 2000 Poul-Henning Kamp and Dag-Erling Co�dan Sm�rgrav 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer 10 * in this position and unchanged. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 * $FreeBSD: head/sys/kern/subr_sbuf.c 73891 2001-03-06 17:48:26Z des $ 29 */ 30 31#include <sys/param.h> 32#include <sys/kernel.h> 33#include <sys/malloc.h> 34#include <sys/sbuf.h> 35#include <sys/systm.h> 36 37#include <machine/stdarg.h> 38 39MALLOC_DEFINE(M_SBUF, "sbuf", "string buffers"); 40 41/* 42 * Predicates 43 */ 44#define SBUF_ISDYNAMIC(s) ((s)->s_flags & SBUF_DYNAMIC) 45#define SBUF_ISFINISHED(s) ((s)->s_flags & SBUF_FINISHED) 46#define SBUF_HASOVERFLOWED(s) ((s)->s_flags & SBUF_OVERFLOWED) 47#define SBUF_HASROOM(s) ((s)->s_len < (s)->s_size - 1) 48 49/* 50 * Set / clear flags 51 */ 52#define SBUF_SETFLAG(s, f) do { (s)->s_flags |= (f); } while (0) 53#define SBUF_CLEARFLAG(s, f) do { (s)->s_flags &= ~(f); } while (0) 54 55/* 56 * Debugging support 57 */ 58#ifdef INVARIANTS 59static void 60_assert_sbuf_integrity(char *fun, struct sbuf *s) 61{ 62 KASSERT(s != NULL, 63 ("%s called with a NULL sbuf pointer", fun)); 64 KASSERT(s->s_buf != NULL, 65 ("%s called with unitialized or corrupt sbuf", fun)); 66 KASSERT(s->s_len < s->s_size, 67 ("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size)); 68} 69 70static void 71_assert_sbuf_state(char *fun, struct sbuf *s, int state) 72{ 73 KASSERT((s->s_flags & SBUF_FINISHED) == state, 74 ("%s called with %sfinished or corrupt sbuf", fun, 75 (state ? "un" : ""))); 76} 77#define assert_sbuf_integrity(s) _assert_sbuf_integrity(__FUNCTION__, (s)) 78#define assert_sbuf_state(s, i) _assert_sbuf_state(__FUNCTION__, (s), (i)) 79#else 80#define assert_sbuf_integrity(s) do { } while (0) 81#define assert_sbuf_state(s, i) do { } while (0) 82#endif 83 84/* 85 * Initialize an sbuf. 86 * If buf is non-NULL, it points to a static or already-allocated string 87 * big enough to hold at least length characters. 88 */ 89int 90sbuf_new(struct sbuf *s, char *buf, int length, int flags) 91{ 92 KASSERT(length >= 0, 93 ("attempt to create an sbuf of negative length (%d)", length)); 94 KASSERT(flags == 0, 95 (__FUNCTION__ " called with non-zero flags")); 96 KASSERT(s != NULL, 97 (__FUNCTION__ " called with a NULL sbuf pointer")); 98 99 bzero(s, sizeof *s); 100 s->s_size = length; 101 if (buf) { 102 s->s_buf = buf; 103 return (0); 104 } 105 s->s_buf = malloc(s->s_size, M_SBUF, M_WAITOK); 106 if (s->s_buf == NULL) 107 return (-1); 108 SBUF_SETFLAG(s, SBUF_DYNAMIC); 109 return (0); 110} 111 112/* 113 * Clear an sbuf and reset its position 114 */ 115void 116sbuf_clear(struct sbuf *s) 117{ 118 assert_sbuf_integrity(s); 119 /* don't care if it's finished or not */ 120 121 SBUF_CLEARFLAG(s, SBUF_FINISHED); 122 SBUF_CLEARFLAG(s, SBUF_OVERFLOWED); 123 s->s_len = 0; 124} 125 126/* 127 * Set the sbuf's position to an arbitrary value 128 */ 129int 130sbuf_setpos(struct sbuf *s, int pos) 131{ 132 assert_sbuf_integrity(s); 133 assert_sbuf_state(s, 0); 134 135 KASSERT(pos >= 0, 136 ("attempt to seek to a negative position (%d)", pos)); 137 KASSERT(pos < s->s_size, 138 ("attempt to seek past end of sbuf (%d >= %d)", pos, s->s_size)); 139 140 if (pos < 0 || pos > s->s_len) 141 return (-1); 142 s->s_len = pos; 143 return (0); 144} 145 146/* 147 * Append a string to an sbuf. 148 */ 149int 150sbuf_cat(struct sbuf *s, char *str) 151{ 152 assert_sbuf_integrity(s); 153 assert_sbuf_state(s, 0); 154 155 if (SBUF_HASOVERFLOWED(s)) 156 return (-1); 157 158 while (*str && SBUF_HASROOM(s)) 159 s->s_buf[s->s_len++] = *str++; 160 if (*str) { 161 SBUF_SETFLAG(s, SBUF_OVERFLOWED); 162 return (-1); 163 } 164 return (0); 165} 166 167/* 168 * Copy a string into an sbuf. 169 */ 170int 171sbuf_cpy(struct sbuf *s, char *str) 172{ 173 assert_sbuf_integrity(s); 174 assert_sbuf_state(s, 0); 175 176 sbuf_clear(s); 177 return (sbuf_cat(s, str)); 178} 179 180/* 181 * PCHAR function for sbuf_printf() 182 */ 183static void 184_sbuf_pchar(int c, void *v) 185{ 186 sbuf_putc((struct sbuf *)v, c); 187} 188 189/* 190 * Format the given arguments and append the resulting string to an sbuf. 191 */ 192int 193sbuf_printf(struct sbuf *s, char *fmt, ...) 194{ 195 va_list ap; 196 int len; 197 198 assert_sbuf_integrity(s); 199 assert_sbuf_state(s, 0); 200 201 KASSERT(fmt != NULL, 202 (__FUNCTION__ " called with a NULL format string")); 203 204 if (SBUF_HASOVERFLOWED(s)) 205 return (-1); 206 207 va_start(ap, fmt); 208 len = kvprintf(fmt, _sbuf_pchar, s, 10, ap); 209 va_end(ap); 210 211 KASSERT(s->s_len < s->s_size, 212 ("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size)); 213 214 if (SBUF_HASOVERFLOWED(s)) 215 return (-1); 216 return (0); 217} 218 219/* 220 * Append a character to an sbuf. 221 */ 222int 223sbuf_putc(struct sbuf *s, int c) 224{ 225 assert_sbuf_integrity(s); 226 assert_sbuf_state(s, 0); 227 228 if (SBUF_HASOVERFLOWED(s)) 229 return (-1); 230 231 if (!SBUF_HASROOM(s)) { 232 SBUF_SETFLAG(s, SBUF_OVERFLOWED); 233 return (-1); 234 } 235 if (c != '\0') 236 s->s_buf[s->s_len++] = c; 237 return (0); 238} 239 240/* 241 * Check if an sbuf overflowed 242 */ 243int 244sbuf_overflowed(struct sbuf *s) 245{ 246 return SBUF_HASOVERFLOWED(s); 247} 248 249/* 250 * Finish off an sbuf. 251 */ 252void 253sbuf_finish(struct sbuf *s) 254{ 255 assert_sbuf_integrity(s); 256 assert_sbuf_state(s, 0); 257 258 s->s_buf[s->s_len] = '\0'; 259 SBUF_CLEARFLAG(s, SBUF_OVERFLOWED); 260 SBUF_SETFLAG(s, SBUF_FINISHED); 261} 262 263/* 264 * Return a pointer to the sbuf data. 265 */ 266char * 267sbuf_data(struct sbuf *s) 268{ 269 assert_sbuf_integrity(s); 270 assert_sbuf_state(s, SBUF_FINISHED); 271 272 return s->s_buf; 273} 274 275/* 276 * Return the length of the sbuf data. 277 */ 278int 279sbuf_len(struct sbuf *s) 280{ 281 assert_sbuf_integrity(s); 282 /* don't care if it's finished or not */ 283 284 if (SBUF_HASOVERFLOWED(s)) 285 return (-1); 286 return s->s_len; 287} 288 289/* 290 * Clear an sbuf, free its buffer if necessary. 291 */ 292void 293sbuf_delete(struct sbuf *s) 294{ 295 assert_sbuf_integrity(s); 296 /* don't care if it's finished or not */ 297 298 if (SBUF_ISDYNAMIC(s)) 299 free(s->s_buf, M_SBUF); 300 bzero(s, sizeof *s); 301} 302