subr_sbuf.c revision 71721
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 71721 2001-01-28 00:13:01Z 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 60assert_sbuf_integrity(struct sbuf *s) 61{ 62 KASSERT(s != NULL, 63 (__FUNCTION__ " called with a NULL sbuf pointer")); 64 KASSERT(s->s_buf != NULL, 65 (__FUNCTION__ " called with unitialized or corrupt sbuf")); 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 71assert_sbuf_state(struct sbuf *s, int state) 72{ 73 KASSERT((s->s_flags & SBUF_FINISHED) == state, 74 (__FUNCTION__ " called with %sfinished or corrupt sbuf", 75 (state ? "un" : ""))); 76} 77#else 78#define assert_sbuf_integrity(s) do { } while (0) 79#define assert_sbuf_state(s, i) do { } while (0) 80#endif 81 82/* 83 * Initialize an sbuf. 84 * If buf is non-NULL, it points to a static or already-allocated string 85 * big enough to hold at least length characters. 86 */ 87int 88sbuf_new(struct sbuf *s, char *buf, int length, int flags) 89{ 90 KASSERT(length >= 0, 91 ("attempt to create an sbuf of negative length (%d)", length)); 92 KASSERT(flags == 0, 93 (__FUNCTION__ " called with non-zero flags")); 94 KASSERT(s != NULL, 95 (__FUNCTION__ " called with a NULL sbuf pointer")); 96 97 bzero(s, sizeof *s); 98 s->s_size = length; 99 if (buf) { 100 s->s_buf = buf; 101 return (0); 102 } 103 s->s_buf = malloc(s->s_size, M_SBUF, M_WAITOK); 104 if (s->s_buf == NULL) 105 return (-1); 106 SBUF_SETFLAG(s, SBUF_DYNAMIC); 107 return (0); 108} 109 110/* 111 * Clear an sbuf and reset its position 112 */ 113void 114sbuf_clear(struct sbuf *s) 115{ 116 assert_sbuf_integrity(s); 117 118 SBUF_CLEARFLAG(s, SBUF_FINISHED); 119 SBUF_CLEARFLAG(s, SBUF_OVERFLOWED); 120 s->s_len = 0; 121} 122 123/* 124 * Set the sbuf's position to an arbitrary value 125 */ 126int 127sbuf_setpos(struct sbuf *s, int pos) 128{ 129 assert_sbuf_integrity(s); 130 assert_sbuf_state(s, 0); 131 132 KASSERT(pos >= 0, 133 ("attempt to seek to a negative position (%d)", pos)); 134 KASSERT(pos < s->s_size, 135 ("attempt to seek past end of sbuf (%d >= %d)", pos, s->s_size)); 136 137 if (pos < 0 || pos > s->s_len) 138 return (-1); 139 s->s_len = pos; 140 return (0); 141} 142 143/* 144 * Append a string to an sbuf. 145 */ 146int 147sbuf_cat(struct sbuf *s, char *str) 148{ 149 assert_sbuf_integrity(s); 150 assert_sbuf_state(s, 0); 151 152 if (SBUF_HASOVERFLOWED(s)) 153 return (-1); 154 155 while (*str && SBUF_HASROOM(s)) 156 s->s_buf[s->s_len++] = *str++; 157 if (*str) { 158 SBUF_SETFLAG(s, SBUF_OVERFLOWED); 159 return (-1); 160 } 161 return (0); 162} 163 164/* 165 * Copy a string into an sbuf. 166 */ 167int 168sbuf_cpy(struct sbuf *s, char *str) 169{ 170 assert_sbuf_integrity(s); 171 assert_sbuf_state(s, 0); 172 173 sbuf_clear(s); 174 return (sbuf_cat(s, str)); 175} 176 177/* 178 * PCHAR function for sbuf_printf() 179 */ 180static void 181_sbuf_pchar(int c, void *v) 182{ 183 struct sbuf *s = (struct sbuf *)v; 184 185 assert_sbuf_integrity(s); 186 assert_sbuf_state(s, 0); 187 188 if (SBUF_HASOVERFLOWED(s)) 189 return; 190 if (SBUF_HASROOM(s)) 191 s->s_buf[s->s_len++] = c; 192 else 193 SBUF_SETFLAG(s, SBUF_OVERFLOWED); 194} 195 196/* 197 * Format the given arguments and append the resulting string to an sbuf. 198 */ 199int 200sbuf_printf(struct sbuf *s, char *fmt, ...) 201{ 202 va_list ap; 203 int len; 204 205 assert_sbuf_integrity(s); 206 assert_sbuf_state(s, 0); 207 208 KASSERT(fmt != NULL, 209 (__FUNCTION__ " called with a NULL format string")); 210 211 if (SBUF_HASOVERFLOWED(s)) 212 return (-1); 213 214 va_start(ap, fmt); 215 len = kvprintf(fmt, _sbuf_pchar, s, 10, ap); 216 va_end(ap); 217 218 KASSERT(s->s_len < s->s_size, 219 ("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size)); 220 221 if (SBUF_HASOVERFLOWED(s)) 222 return (-1); 223 return (0); 224} 225 226/* 227 * Append a character to an sbuf. 228 */ 229int 230sbuf_putc(struct sbuf *s, int c) 231{ 232 assert_sbuf_integrity(s); 233 assert_sbuf_state(s, 0); 234 235 if (SBUF_HASOVERFLOWED(s)) 236 return (-1); 237 238 if (!SBUF_HASROOM(s)) { 239 SBUF_SETFLAG(s, SBUF_OVERFLOWED); 240 return (-1); 241 } 242 s->s_buf[s->s_len++] = c; 243 return (0); 244} 245 246/* 247 * Check if an sbuf overflowed 248 */ 249int 250sbuf_overflowed(struct sbuf *s) 251{ 252 return SBUF_HASOVERFLOWED(s); 253} 254 255/* 256 * Finish off an sbuf. 257 */ 258void 259sbuf_finish(struct sbuf *s) 260{ 261 assert_sbuf_integrity(s); 262 assert_sbuf_state(s, 0); 263 264 s->s_buf[s->s_len++] = '\0'; 265 SBUF_CLEARFLAG(s, SBUF_OVERFLOWED); 266 SBUF_SETFLAG(s, SBUF_FINISHED); 267} 268 269/* 270 * Return a pointer to the sbuf data. 271 */ 272char * 273sbuf_data(struct sbuf *s) 274{ 275 assert_sbuf_integrity(s); 276 assert_sbuf_state(s, SBUF_FINISHED); 277 278 return s->s_buf; 279} 280 281/* 282 * Return the length of the sbuf data. 283 */ 284int 285sbuf_len(struct sbuf *s) 286{ 287 assert_sbuf_integrity(s); 288 assert_sbuf_state(s, SBUF_FINISHED); 289 290 if (SBUF_HASOVERFLOWED(s)) 291 return (-1); 292 return s->s_len; 293} 294 295/* 296 * Clear an sbuf, free its buffer if necessary. 297 */ 298void 299sbuf_delete(struct sbuf *s) 300{ 301 assert_sbuf_integrity(s); 302 /* don't care if it's finished or not */ 303 304 if (SBUF_ISDYNAMIC(s)) 305 free(s->s_buf, M_SBUF); 306 bzero(s, sizeof *s); 307} 308