xref: /freebsd/sys/kern/subr_sbuf.c (revision c678bc4f13a340ad88debe321afd0097db2590cb)
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$
29  */
30 
31 #include <sys/param.h>
32 #include <sys/sbuf.h>
33 
34 #ifdef _KERNEL
35 #include <sys/kernel.h>
36 #include <sys/malloc.h>
37 #include <sys/systm.h>
38 #include <machine/stdarg.h>
39 #else /* _KERNEL */
40 #include <stdarg.h>
41 #endif /* _KERNEL */
42 
43 #ifdef _KERNEL
44 MALLOC_DEFINE(M_SBUF, "sbuf", "string buffers");
45 #define SBMALLOC(size)		malloc(size, M_SBUF, M_WAITOK)
46 #define SBFREE(buf)		free(buf, M_SBUF)
47 #else /* _KERNEL */
48 #define KASSERT(e, m)
49 #define SBMALLOC(size)		malloc(size)
50 #define SBFREE(buf)		free(buf)
51 #define min(x,y)		MIN(x,y)
52 #endif /* _KERNEL */
53 
54 /*
55  * Predicates
56  */
57 #define SBUF_ISDYNAMIC(s)	((s)->s_flags & SBUF_DYNAMIC)
58 #define SBUF_ISFINISHED(s)	((s)->s_flags & SBUF_FINISHED)
59 #define SBUF_HASOVERFLOWED(s)	((s)->s_flags & SBUF_OVERFLOWED)
60 #define SBUF_HASROOM(s)		((s)->s_len < (s)->s_size - 1)
61 
62 /*
63  * Set / clear flags
64  */
65 #define SBUF_SETFLAG(s, f)	do { (s)->s_flags |= (f); } while (0)
66 #define SBUF_CLEARFLAG(s, f)	do { (s)->s_flags &= ~(f); } while (0)
67 
68 /*
69  * Debugging support
70  */
71 #if defined(_KERNEL) && defined(INVARIANTS)
72 static void
73 _assert_sbuf_integrity(char *fun, struct sbuf *s)
74 {
75 	KASSERT(s != NULL,
76 	    ("%s called with a NULL sbuf pointer", fun));
77 	KASSERT(s->s_buf != NULL,
78 	    ("%s called with unitialized or corrupt sbuf", fun));
79 	KASSERT(s->s_len < s->s_size,
80 	    ("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size));
81 }
82 
83 static void
84 _assert_sbuf_state(char *fun, struct sbuf *s, int state)
85 {
86 	KASSERT((s->s_flags & SBUF_FINISHED) == state,
87 	    ("%s called with %sfinished or corrupt sbuf", fun,
88 	    (state ? "un" : "")));
89 }
90 #define assert_sbuf_integrity(s) _assert_sbuf_integrity(__FUNCTION__, (s))
91 #define assert_sbuf_state(s, i)	 _assert_sbuf_state(__FUNCTION__, (s), (i))
92 #else /* _KERNEL && INVARIANTS */
93 #define assert_sbuf_integrity(s) do { } while (0)
94 #define assert_sbuf_state(s, i)	 do { } while (0)
95 #endif /* _KERNEL && INVARIANTS */
96 
97 /*
98  * Initialize an sbuf.
99  * If buf is non-NULL, it points to a static or already-allocated string
100  * big enough to hold at least length characters.
101  */
102 int
103 sbuf_new(struct sbuf *s, char *buf, int length, int flags)
104 {
105 	KASSERT(length >= 0,
106 	    ("attempt to create an sbuf of negative length (%d)", length));
107 	KASSERT(flags == 0,
108 	    (__FUNCTION__ " called with non-zero flags"));
109 	KASSERT(s != NULL,
110 	    (__FUNCTION__ " called with a NULL sbuf pointer"));
111 
112 	bzero(s, sizeof *s);
113 	s->s_size = length;
114 	if (buf) {
115 		s->s_buf = buf;
116 		return (0);
117 	}
118 	s->s_buf = (char *)SBMALLOC(s->s_size);
119 	if (s->s_buf == NULL)
120 		return (-1);
121 	SBUF_SETFLAG(s, SBUF_DYNAMIC);
122 	return (0);
123 }
124 
125 /*
126  * Clear an sbuf and reset its position
127  */
128 void
129 sbuf_clear(struct sbuf *s)
130 {
131 	assert_sbuf_integrity(s);
132 	/* don't care if it's finished or not */
133 
134 	SBUF_CLEARFLAG(s, SBUF_FINISHED);
135 	SBUF_CLEARFLAG(s, SBUF_OVERFLOWED);
136 	s->s_len = 0;
137 }
138 
139 /*
140  * Set the sbuf's position to an arbitrary value
141  */
142 int
143 sbuf_setpos(struct sbuf *s, int pos)
144 {
145 	assert_sbuf_integrity(s);
146 	assert_sbuf_state(s, 0);
147 
148 	KASSERT(pos >= 0,
149 	    ("attempt to seek to a negative position (%d)", pos));
150 	KASSERT(pos < s->s_size,
151 	    ("attempt to seek past end of sbuf (%d >= %d)", pos, s->s_size));
152 
153 	if (pos < 0 || pos > s->s_len)
154 		return (-1);
155 	s->s_len = pos;
156 	return (0);
157 }
158 
159 /*
160  * Append a string to an sbuf.
161  */
162 int
163 sbuf_cat(struct sbuf *s, const char *str)
164 {
165 	assert_sbuf_integrity(s);
166 	assert_sbuf_state(s, 0);
167 
168 	if (SBUF_HASOVERFLOWED(s))
169 		return (-1);
170 
171 	while (*str && SBUF_HASROOM(s))
172 		s->s_buf[s->s_len++] = *str++;
173 	if (*str) {
174 		SBUF_SETFLAG(s, SBUF_OVERFLOWED);
175 		return (-1);
176 	}
177 	return (0);
178 }
179 
180 /*
181  * Copy a string into an sbuf.
182  */
183 int
184 sbuf_cpy(struct sbuf *s, const char *str)
185 {
186 	assert_sbuf_integrity(s);
187 	assert_sbuf_state(s, 0);
188 
189 	sbuf_clear(s);
190 	return (sbuf_cat(s, str));
191 }
192 
193 /*
194  * Format the given arguments and append the resulting string to an sbuf.
195  */
196 int
197 sbuf_printf(struct sbuf *s, char *fmt, ...)
198 {
199 	va_list ap;
200 	int len;
201 
202 	assert_sbuf_integrity(s);
203 	assert_sbuf_state(s, 0);
204 
205 	KASSERT(fmt != NULL,
206 	    (__FUNCTION__ " called with a NULL format string"));
207 
208 	if (SBUF_HASOVERFLOWED(s))
209 		return (-1);
210 
211 	va_start(ap, fmt);
212 	len = vsnprintf(&s->s_buf[s->s_len], s->s_size - s->s_len, fmt, ap);
213 	va_end(ap);
214 
215 	/*
216 	 * s->s_len is the length of the string, without the terminating nul.
217 	 * When updating s->s_len, we must subtract 1 from the length that
218 	 * we passed into vsnprintf() because that length includes the
219 	 * terminating nul.
220 	 *
221 	 * vsnprintf() returns the amount that would have been copied,
222 	 * given sufficient space, hence the min() calculation below.
223 	 */
224 	s->s_len += min(len, s->s_size - s->s_len - 1);
225 	if (!SBUF_HASROOM(s))
226 		SBUF_SETFLAG(s, SBUF_OVERFLOWED);
227 
228 	KASSERT(s->s_len < s->s_size,
229 	    ("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size));
230 
231 	if (SBUF_HASOVERFLOWED(s))
232 		return (-1);
233 	return (0);
234 }
235 
236 /*
237  * Append a character to an sbuf.
238  */
239 int
240 sbuf_putc(struct sbuf *s, int c)
241 {
242 	assert_sbuf_integrity(s);
243 	assert_sbuf_state(s, 0);
244 
245 	if (SBUF_HASOVERFLOWED(s))
246 		return (-1);
247 
248 	if (!SBUF_HASROOM(s)) {
249 		SBUF_SETFLAG(s, SBUF_OVERFLOWED);
250 		return (-1);
251 	}
252 	if (c != '\0')
253 	    s->s_buf[s->s_len++] = c;
254 	return (0);
255 }
256 
257 /*
258  * Check if an sbuf overflowed
259  */
260 int
261 sbuf_overflowed(struct sbuf *s)
262 {
263     return SBUF_HASOVERFLOWED(s);
264 }
265 
266 /*
267  * Finish off an sbuf.
268  */
269 void
270 sbuf_finish(struct sbuf *s)
271 {
272 	assert_sbuf_integrity(s);
273 	assert_sbuf_state(s, 0);
274 
275 	s->s_buf[s->s_len] = '\0';
276 	SBUF_CLEARFLAG(s, SBUF_OVERFLOWED);
277 	SBUF_SETFLAG(s, SBUF_FINISHED);
278 }
279 
280 /*
281  * Return a pointer to the sbuf data.
282  */
283 char *
284 sbuf_data(struct sbuf *s)
285 {
286 	assert_sbuf_integrity(s);
287 	assert_sbuf_state(s, SBUF_FINISHED);
288 
289 	return s->s_buf;
290 }
291 
292 /*
293  * Return the length of the sbuf data.
294  */
295 int
296 sbuf_len(struct sbuf *s)
297 {
298 	assert_sbuf_integrity(s);
299 	/* don't care if it's finished or not */
300 
301 	if (SBUF_HASOVERFLOWED(s))
302 		return (-1);
303 	return s->s_len;
304 }
305 
306 /*
307  * Clear an sbuf, free its buffer if necessary.
308  */
309 void
310 sbuf_delete(struct sbuf *s)
311 {
312 	assert_sbuf_integrity(s);
313 	/* don't care if it's finished or not */
314 
315 	if (SBUF_ISDYNAMIC(s))
316 		SBFREE(s->s_buf);
317 	bzero(s, sizeof *s);
318 }
319