subr_sbuf.c revision 78077
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 78077 2001-06-11 17:05:52Z des $
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_ISDYNSTRUCT(s)	((s)->s_flags & SBUF_DYNSTRUCT)
59#define SBUF_ISFINISHED(s)	((s)->s_flags & SBUF_FINISHED)
60#define SBUF_HASOVERFLOWED(s)	((s)->s_flags & SBUF_OVERFLOWED)
61#define SBUF_HASROOM(s)		((s)->s_len < (s)->s_size - 1)
62
63/*
64 * Set / clear flags
65 */
66#define SBUF_SETFLAG(s, f)	do { (s)->s_flags |= (f); } while (0)
67#define SBUF_CLEARFLAG(s, f)	do { (s)->s_flags &= ~(f); } while (0)
68
69/*
70 * Debugging support
71 */
72#if defined(_KERNEL) && defined(INVARIANTS)
73static void
74_assert_sbuf_integrity(char *fun, struct sbuf *s)
75{
76	KASSERT(s != NULL,
77	    ("%s called with a NULL sbuf pointer", fun));
78	KASSERT(s->s_buf != NULL,
79	    ("%s called with unitialized or corrupt sbuf", fun));
80	KASSERT(s->s_len < s->s_size,
81	    ("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size));
82}
83
84static void
85_assert_sbuf_state(char *fun, struct sbuf *s, int state)
86{
87	KASSERT((s->s_flags & SBUF_FINISHED) == state,
88	    ("%s called with %sfinished or corrupt sbuf", fun,
89	    (state ? "un" : "")));
90}
91#define assert_sbuf_integrity(s) _assert_sbuf_integrity(__FUNCTION__, (s))
92#define assert_sbuf_state(s, i)	 _assert_sbuf_state(__FUNCTION__, (s), (i))
93#else /* _KERNEL && INVARIANTS */
94#define assert_sbuf_integrity(s) do { } while (0)
95#define assert_sbuf_state(s, i)	 do { } while (0)
96#endif /* _KERNEL && INVARIANTS */
97
98/*
99 * Initialize an sbuf.
100 * If buf is non-NULL, it points to a static or already-allocated string
101 * big enough to hold at least length characters.
102 */
103struct sbuf *
104sbuf_new(struct sbuf *s, char *buf, int length, int flags)
105{
106	KASSERT(length >= 0,
107	    ("attempt to create an sbuf of negative length (%d)", length));
108	KASSERT(flags == 0,
109	    (__FUNCTION__ " called with non-zero flags"));
110
111	if (s == NULL) {
112		s = (struct sbuf *)SBMALLOC(sizeof *s);
113		if (s == NULL)
114			return (NULL);
115		bzero(s, sizeof *s);
116		SBUF_SETFLAG(s, SBUF_DYNSTRUCT);
117	} else {
118		bzero(s, sizeof *s);
119	}
120	s->s_size = length;
121	if (buf) {
122		s->s_buf = buf;
123		return (s);
124	}
125	s->s_buf = (char *)SBMALLOC(s->s_size);
126	if (s->s_buf == NULL) {
127		if (SBUF_ISDYNSTRUCT(s))
128			SBFREE(s);
129		return (NULL);
130	}
131	SBUF_SETFLAG(s, SBUF_DYNAMIC);
132	return (s);
133}
134
135/*
136 * Clear an sbuf and reset its position
137 */
138void
139sbuf_clear(struct sbuf *s)
140{
141	assert_sbuf_integrity(s);
142	/* don't care if it's finished or not */
143
144	SBUF_CLEARFLAG(s, SBUF_FINISHED);
145	SBUF_CLEARFLAG(s, SBUF_OVERFLOWED);
146	s->s_len = 0;
147}
148
149/*
150 * Set the sbuf's position to an arbitrary value
151 */
152int
153sbuf_setpos(struct sbuf *s, int pos)
154{
155	assert_sbuf_integrity(s);
156	assert_sbuf_state(s, 0);
157
158	KASSERT(pos >= 0,
159	    ("attempt to seek to a negative position (%d)", pos));
160	KASSERT(pos < s->s_size,
161	    ("attempt to seek past end of sbuf (%d >= %d)", pos, s->s_size));
162
163	if (pos < 0 || pos > s->s_len)
164		return (-1);
165	s->s_len = pos;
166	return (0);
167}
168
169/*
170 * Append a byte string to an sbuf.
171 */
172int
173sbuf_bcat(struct sbuf *s, const char *str, size_t len)
174{
175	assert_sbuf_integrity(s);
176	assert_sbuf_state(s, 0);
177
178	if (SBUF_HASOVERFLOWED(s))
179		return (-1);
180
181	while (len-- && SBUF_HASROOM(s))
182		s->s_buf[s->s_len++] = *str++;
183	if (len) {
184		SBUF_SETFLAG(s, SBUF_OVERFLOWED);
185		return (-1);
186	}
187	return (0);
188}
189
190#ifdef _KERNEL
191/*
192 * Copy a byte string from userland into an sbuf.
193 */
194int
195sbuf_bcopyin(struct sbuf *s, const void *uaddr, size_t len)
196{
197	assert_sbuf_integrity(s);
198	assert_sbuf_state(s, 0);
199
200	if (SBUF_HASOVERFLOWED(s))
201		return (-1);
202
203	if (len == 0)
204		return (0);
205	if (len > (s->s_size - s->s_len - 1))
206		len = s->s_size - s->s_len - 1;
207	switch (copyin(uaddr, s->s_buf + s->s_len, len)) {
208	case ENAMETOOLONG:
209		SBUF_SETFLAG(s, SBUF_OVERFLOWED);
210		/* fall through */
211	case 0:
212		s->s_len += len;
213		break;
214	default:
215		return (-1);	/* XXX */
216	}
217
218	return (0);
219}
220#endif
221
222/*
223 * Copy a byte string into an sbuf.
224 */
225int
226sbuf_bcpy(struct sbuf *s, const char *str, size_t len)
227{
228	assert_sbuf_integrity(s);
229	assert_sbuf_state(s, 0);
230
231	sbuf_clear(s);
232	return (sbuf_bcat(s, str, len));
233}
234
235/*
236 * Append a string to an sbuf.
237 */
238int
239sbuf_cat(struct sbuf *s, const char *str)
240{
241	assert_sbuf_integrity(s);
242	assert_sbuf_state(s, 0);
243
244	if (SBUF_HASOVERFLOWED(s))
245		return (-1);
246
247	while (*str && SBUF_HASROOM(s))
248		s->s_buf[s->s_len++] = *str++;
249	if (*str) {
250		SBUF_SETFLAG(s, SBUF_OVERFLOWED);
251		return (-1);
252	}
253	return (0);
254}
255
256#ifdef _KERNEL
257/*
258 * Copy a string from userland into an sbuf.
259 */
260int
261sbuf_copyin(struct sbuf *s, const void *uaddr, size_t len)
262{
263	size_t done;
264
265	assert_sbuf_integrity(s);
266	assert_sbuf_state(s, 0);
267
268	if (SBUF_HASOVERFLOWED(s))
269		return (-1);
270
271	if (len == 0 || len > (s->s_size - s->s_len - 1))
272		len = s->s_size - s->s_len - 1;
273	switch (copyinstr(uaddr, s->s_buf + s->s_len, len + 1, &done)) {
274	case ENAMETOOLONG:
275		SBUF_SETFLAG(s, SBUF_OVERFLOWED);
276		/* fall through */
277	case 0:
278		s->s_len += done - 1;
279		break;
280	default:
281		return (-1);	/* XXX */
282	}
283
284	return (0);
285}
286#endif
287
288/*
289 * Copy a string into an sbuf.
290 */
291int
292sbuf_cpy(struct sbuf *s, const char *str)
293{
294	assert_sbuf_integrity(s);
295	assert_sbuf_state(s, 0);
296
297	sbuf_clear(s);
298	return (sbuf_cat(s, str));
299}
300
301/*
302 * Format the given arguments and append the resulting string to an sbuf.
303 */
304int
305sbuf_printf(struct sbuf *s, char *fmt, ...)
306{
307	va_list ap;
308	int len;
309
310	assert_sbuf_integrity(s);
311	assert_sbuf_state(s, 0);
312
313	KASSERT(fmt != NULL,
314	    (__FUNCTION__ " called with a NULL format string"));
315
316	if (SBUF_HASOVERFLOWED(s))
317		return (-1);
318
319	va_start(ap, fmt);
320	len = vsnprintf(&s->s_buf[s->s_len], s->s_size - s->s_len, fmt, ap);
321	va_end(ap);
322
323	/*
324	 * s->s_len is the length of the string, without the terminating nul.
325	 * When updating s->s_len, we must subtract 1 from the length that
326	 * we passed into vsnprintf() because that length includes the
327	 * terminating nul.
328	 *
329	 * vsnprintf() returns the amount that would have been copied,
330	 * given sufficient space, hence the min() calculation below.
331	 */
332	s->s_len += min(len, s->s_size - s->s_len - 1);
333	if (!SBUF_HASROOM(s))
334		SBUF_SETFLAG(s, SBUF_OVERFLOWED);
335
336	KASSERT(s->s_len < s->s_size,
337	    ("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size));
338
339	if (SBUF_HASOVERFLOWED(s))
340		return (-1);
341	return (0);
342}
343
344/*
345 * Append a character to an sbuf.
346 */
347int
348sbuf_putc(struct sbuf *s, int c)
349{
350	assert_sbuf_integrity(s);
351	assert_sbuf_state(s, 0);
352
353	if (SBUF_HASOVERFLOWED(s))
354		return (-1);
355
356	if (!SBUF_HASROOM(s)) {
357		SBUF_SETFLAG(s, SBUF_OVERFLOWED);
358		return (-1);
359	}
360	if (c != '\0')
361	    s->s_buf[s->s_len++] = c;
362	return (0);
363}
364
365/*
366 * Check if an sbuf overflowed
367 */
368int
369sbuf_overflowed(struct sbuf *s)
370{
371    return SBUF_HASOVERFLOWED(s);
372}
373
374/*
375 * Finish off an sbuf.
376 */
377void
378sbuf_finish(struct sbuf *s)
379{
380	assert_sbuf_integrity(s);
381	assert_sbuf_state(s, 0);
382
383	s->s_buf[s->s_len] = '\0';
384	SBUF_CLEARFLAG(s, SBUF_OVERFLOWED);
385	SBUF_SETFLAG(s, SBUF_FINISHED);
386}
387
388/*
389 * Return a pointer to the sbuf data.
390 */
391char *
392sbuf_data(struct sbuf *s)
393{
394	assert_sbuf_integrity(s);
395	assert_sbuf_state(s, SBUF_FINISHED);
396
397	return s->s_buf;
398}
399
400/*
401 * Return the length of the sbuf data.
402 */
403int
404sbuf_len(struct sbuf *s)
405{
406	assert_sbuf_integrity(s);
407	/* don't care if it's finished or not */
408
409	if (SBUF_HASOVERFLOWED(s))
410		return (-1);
411	return s->s_len;
412}
413
414/*
415 * Clear an sbuf, free its buffer if necessary.
416 */
417void
418sbuf_delete(struct sbuf *s)
419{
420	assert_sbuf_integrity(s);
421	/* don't care if it's finished or not */
422
423	if (SBUF_ISDYNAMIC(s))
424		SBFREE(s->s_buf);
425	bzero(s, sizeof *s);
426	if (SBUF_ISDYNSTRUCT(s))
427		SBFREE(s);
428}
429