xref: /freebsd/sys/kern/subr_sbuf.c (revision ee41f1b1cf5e3d4f586cb85b46123b416275862c)
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/kernel.h>
33 #include <sys/malloc.h>
34 #include <sys/sbuf.h>
35 #include <sys/systm.h>
36 
37 #include <machine/stdarg.h>
38 
39 MALLOC_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
59 static void
60 assert_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 
70 static void
71 assert_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  */
87 int
88 sbuf_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  */
113 void
114 sbuf_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  */
127 int
128 sbuf_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  */
147 int
148 sbuf_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  */
168 int
169 sbuf_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  */
181 static 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  */
200 int
201 sbuf_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  */
230 int
231 sbuf_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  */
250 int
251 sbuf_overflowed(struct sbuf *s)
252 {
253     return SBUF_HASOVERFLOWED(s);
254 }
255 
256 /*
257  * Finish off an sbuf.
258  */
259 void
260 sbuf_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  */
273 char *
274 sbuf_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  */
285 int
286 sbuf_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  */
299 void
300 sbuf_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