subr_sbuf.c revision 71724
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 71724 2001-01-28 00:33:58Z 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 /* don't care if it's finished or not */ 118 119 SBUF_CLEARFLAG(s, SBUF_FINISHED); 120 SBUF_CLEARFLAG(s, SBUF_OVERFLOWED); 121 s->s_len = 0; 122} 123 124/* 125 * Set the sbuf's position to an arbitrary value 126 */ 127int 128sbuf_setpos(struct sbuf *s, int pos) 129{ 130 assert_sbuf_integrity(s); 131 assert_sbuf_state(s, 0); 132 133 KASSERT(pos >= 0, 134 ("attempt to seek to a negative position (%d)", pos)); 135 KASSERT(pos < s->s_size, 136 ("attempt to seek past end of sbuf (%d >= %d)", pos, s->s_size)); 137 138 if (pos < 0 || pos > s->s_len) 139 return (-1); 140 s->s_len = pos; 141 return (0); 142} 143 144/* 145 * Append a string to an sbuf. 146 */ 147int 148sbuf_cat(struct sbuf *s, char *str) 149{ 150 assert_sbuf_integrity(s); 151 assert_sbuf_state(s, 0); 152 153 if (SBUF_HASOVERFLOWED(s)) 154 return (-1); 155 156 while (*str && SBUF_HASROOM(s)) 157 s->s_buf[s->s_len++] = *str++; 158 if (*str) { 159 SBUF_SETFLAG(s, SBUF_OVERFLOWED); 160 return (-1); 161 } 162 return (0); 163} 164 165/* 166 * Copy a string into an sbuf. 167 */ 168int 169sbuf_cpy(struct sbuf *s, char *str) 170{ 171 assert_sbuf_integrity(s); 172 assert_sbuf_state(s, 0); 173 174 sbuf_clear(s); 175 return (sbuf_cat(s, str)); 176} 177 178/* 179 * PCHAR function for sbuf_printf() 180 */ 181static void 182_sbuf_pchar(int c, void *v) 183{ 184 struct sbuf *s = (struct sbuf *)v; 185 186 assert_sbuf_integrity(s); 187 assert_sbuf_state(s, 0); 188 189 if (SBUF_HASOVERFLOWED(s)) 190 return; 191 if (SBUF_HASROOM(s)) 192 s->s_buf[s->s_len++] = c; 193 else 194 SBUF_SETFLAG(s, SBUF_OVERFLOWED); 195} 196 197/* 198 * Format the given arguments and append the resulting string to an sbuf. 199 */ 200int 201sbuf_printf(struct sbuf *s, char *fmt, ...) 202{ 203 va_list ap; 204 int len; 205 206 assert_sbuf_integrity(s); 207 assert_sbuf_state(s, 0); 208 209 KASSERT(fmt != NULL, 210 (__FUNCTION__ " called with a NULL format string")); 211 212 if (SBUF_HASOVERFLOWED(s)) 213 return (-1); 214 215 va_start(ap, fmt); 216 len = kvprintf(fmt, _sbuf_pchar, s, 10, ap); 217 va_end(ap); 218 219 KASSERT(s->s_len < s->s_size, 220 ("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size)); 221 222 if (SBUF_HASOVERFLOWED(s)) 223 return (-1); 224 return (0); 225} 226 227/* 228 * Append a character to an sbuf. 229 */ 230int 231sbuf_putc(struct sbuf *s, int c) 232{ 233 assert_sbuf_integrity(s); 234 assert_sbuf_state(s, 0); 235 236 if (SBUF_HASOVERFLOWED(s)) 237 return (-1); 238 239 if (!SBUF_HASROOM(s)) { 240 SBUF_SETFLAG(s, SBUF_OVERFLOWED); 241 return (-1); 242 } 243 s->s_buf[s->s_len++] = c; 244 return (0); 245} 246 247/* 248 * Check if an sbuf overflowed 249 */ 250int 251sbuf_overflowed(struct sbuf *s) 252{ 253 return SBUF_HASOVERFLOWED(s); 254} 255 256/* 257 * Finish off an sbuf. 258 */ 259void 260sbuf_finish(struct sbuf *s) 261{ 262 assert_sbuf_integrity(s); 263 assert_sbuf_state(s, 0); 264 265 s->s_buf[s->s_len++] = '\0'; 266 SBUF_CLEARFLAG(s, SBUF_OVERFLOWED); 267 SBUF_SETFLAG(s, SBUF_FINISHED); 268} 269 270/* 271 * Return a pointer to the sbuf data. 272 */ 273char * 274sbuf_data(struct sbuf *s) 275{ 276 assert_sbuf_integrity(s); 277 assert_sbuf_state(s, SBUF_FINISHED); 278 279 return s->s_buf; 280} 281 282/* 283 * Return the length of the sbuf data. 284 */ 285int 286sbuf_len(struct sbuf *s) 287{ 288 assert_sbuf_integrity(s); 289 /* don't care if it's finished or not */ 290 291 if (SBUF_HASOVERFLOWED(s)) 292 return (-1); 293 return s->s_len; 294} 295 296/* 297 * Clear an sbuf, free its buffer if necessary. 298 */ 299void 300sbuf_delete(struct sbuf *s) 301{ 302 assert_sbuf_integrity(s); 303 /* don't care if it's finished or not */ 304 305 if (SBUF_ISDYNAMIC(s)) 306 free(s->s_buf, M_SBUF); 307 bzero(s, sizeof *s); 308} 309