subr_sbuf.c revision 74840
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 74840 2001-03-27 05:45:52Z ken $
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
44MALLOC_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)
72static 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
83static 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 */
102int
103sbuf_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 */
128void
129sbuf_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 */
142int
143sbuf_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 */
162int
163sbuf_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 */
183int
184sbuf_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 */
196int
197sbuf_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 */
239int
240sbuf_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 */
260int
261sbuf_overflowed(struct sbuf *s)
262{
263    return SBUF_HASOVERFLOWED(s);
264}
265
266/*
267 * Finish off an sbuf.
268 */
269void
270sbuf_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 */
283char *
284sbuf_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 */
295int
296sbuf_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 */
309void
310sbuf_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