xref: /freebsd/sys/kern/subr_sbuf.c (revision ddb9b61248f4aa43b5eb2b8fb2f97a84b235130f)
160ec4130SDag-Erling Smørgrav /*-
2546d7890SDag-Erling Smørgrav  * Copyright (c) 2000-2008 Poul-Henning Kamp
3546d7890SDag-Erling Smørgrav  * Copyright (c) 2000-2008 Dag-Erling Coïdan Smørgrav
460ec4130SDag-Erling Smørgrav  * All rights reserved.
560ec4130SDag-Erling Smørgrav  *
660ec4130SDag-Erling Smørgrav  * Redistribution and use in source and binary forms, with or without
760ec4130SDag-Erling Smørgrav  * modification, are permitted provided that the following conditions
860ec4130SDag-Erling Smørgrav  * are met:
960ec4130SDag-Erling Smørgrav  * 1. Redistributions of source code must retain the above copyright
1060ec4130SDag-Erling Smørgrav  *    notice, this list of conditions and the following disclaimer
1160ec4130SDag-Erling Smørgrav  *    in this position and unchanged.
1260ec4130SDag-Erling Smørgrav  * 2. Redistributions in binary form must reproduce the above copyright
1360ec4130SDag-Erling Smørgrav  *    notice, this list of conditions and the following disclaimer in the
1460ec4130SDag-Erling Smørgrav  *    documentation and/or other materials provided with the distribution.
1560ec4130SDag-Erling Smørgrav  *
16546d7890SDag-Erling Smørgrav  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17546d7890SDag-Erling Smørgrav  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18546d7890SDag-Erling Smørgrav  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19546d7890SDag-Erling Smørgrav  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20546d7890SDag-Erling Smørgrav  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21546d7890SDag-Erling Smørgrav  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22546d7890SDag-Erling Smørgrav  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23546d7890SDag-Erling Smørgrav  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24546d7890SDag-Erling Smørgrav  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25546d7890SDag-Erling Smørgrav  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26546d7890SDag-Erling Smørgrav  * SUCH DAMAGE.
2760ec4130SDag-Erling Smørgrav  */
2860ec4130SDag-Erling Smørgrav 
29677b542eSDavid E. O'Brien #include <sys/cdefs.h>
30677b542eSDavid E. O'Brien __FBSDID("$FreeBSD$");
31677b542eSDavid E. O'Brien 
3260ec4130SDag-Erling Smørgrav #include <sys/param.h>
333393f8daSKenneth D. Merry 
343393f8daSKenneth D. Merry #ifdef _KERNEL
355b6db477SDag-Erling Smørgrav #include <sys/ctype.h>
364351ba27SMatthew D Fleming #include <sys/errno.h>
3760ec4130SDag-Erling Smørgrav #include <sys/kernel.h>
3860ec4130SDag-Erling Smørgrav #include <sys/malloc.h>
3960ec4130SDag-Erling Smørgrav #include <sys/systm.h>
405b6db477SDag-Erling Smørgrav #include <sys/uio.h>
4160ec4130SDag-Erling Smørgrav #include <machine/stdarg.h>
423393f8daSKenneth D. Merry #else /* _KERNEL */
435b6db477SDag-Erling Smørgrav #include <ctype.h>
444351ba27SMatthew D Fleming #include <errno.h>
453393f8daSKenneth D. Merry #include <stdarg.h>
467195eb40SKelly Yancey #include <stdio.h>
479fa416caSJonathan Lemon #include <stdlib.h>
487195eb40SKelly Yancey #include <string.h>
493393f8daSKenneth D. Merry #endif /* _KERNEL */
5060ec4130SDag-Erling Smørgrav 
515b6db477SDag-Erling Smørgrav #include <sys/sbuf.h>
525b6db477SDag-Erling Smørgrav 
533393f8daSKenneth D. Merry #ifdef _KERNEL
54c711aea6SPoul-Henning Kamp static MALLOC_DEFINE(M_SBUF, "sbuf", "string buffers");
55a163d034SWarner Losh #define	SBMALLOC(size)		malloc(size, M_SBUF, M_WAITOK)
563393f8daSKenneth D. Merry #define	SBFREE(buf)		free(buf, M_SBUF)
573393f8daSKenneth D. Merry #else /* _KERNEL */
583393f8daSKenneth D. Merry #define	KASSERT(e, m)
593393f8daSKenneth D. Merry #define	SBMALLOC(size)		malloc(size)
603393f8daSKenneth D. Merry #define	SBFREE(buf)		free(buf)
613393f8daSKenneth D. Merry #endif /* _KERNEL */
6260ec4130SDag-Erling Smørgrav 
634dc14139SDag-Erling Smørgrav /*
644dc14139SDag-Erling Smørgrav  * Predicates
654dc14139SDag-Erling Smørgrav  */
664dc14139SDag-Erling Smørgrav #define	SBUF_ISDYNAMIC(s)	((s)->s_flags & SBUF_DYNAMIC)
67d6479358SDag-Erling Smørgrav #define	SBUF_ISDYNSTRUCT(s)	((s)->s_flags & SBUF_DYNSTRUCT)
684dc14139SDag-Erling Smørgrav #define	SBUF_ISFINISHED(s)	((s)->s_flags & SBUF_FINISHED)
694dc14139SDag-Erling Smørgrav #define	SBUF_HASROOM(s)		((s)->s_len < (s)->s_size - 1)
70eb05ee7aSPoul-Henning Kamp #define	SBUF_FREESPACE(s)	((s)->s_size - ((s)->s_len + 1))
717195eb40SKelly Yancey #define	SBUF_CANEXTEND(s)	((s)->s_flags & SBUF_AUTOEXTEND)
72*ddb9b612SMikolaj Golub #define	SBUF_ISSECTION(s)	((s)->s_flags & SBUF_INSECTION)
734dc14139SDag-Erling Smørgrav 
744dc14139SDag-Erling Smørgrav /*
754dc14139SDag-Erling Smørgrav  * Set / clear flags
764dc14139SDag-Erling Smørgrav  */
774dc14139SDag-Erling Smørgrav #define	SBUF_SETFLAG(s, f)	do { (s)->s_flags |= (f); } while (0)
784dc14139SDag-Erling Smørgrav #define	SBUF_CLEARFLAG(s, f)	do { (s)->s_flags &= ~(f); } while (0)
794dc14139SDag-Erling Smørgrav 
807195eb40SKelly Yancey #define	SBUF_MINEXTENDSIZE	16		/* Should be power of 2. */
81eb05ee7aSPoul-Henning Kamp 
82eb05ee7aSPoul-Henning Kamp #ifdef PAGE_SIZE
837195eb40SKelly Yancey #define	SBUF_MAXEXTENDSIZE	PAGE_SIZE
847195eb40SKelly Yancey #define	SBUF_MAXEXTENDINCR	PAGE_SIZE
85eb05ee7aSPoul-Henning Kamp #else
86eb05ee7aSPoul-Henning Kamp #define	SBUF_MAXEXTENDSIZE	4096
87eb05ee7aSPoul-Henning Kamp #define	SBUF_MAXEXTENDINCR	4096
88eb05ee7aSPoul-Henning Kamp #endif
897195eb40SKelly Yancey 
904dc14139SDag-Erling Smørgrav /*
914dc14139SDag-Erling Smørgrav  * Debugging support
924dc14139SDag-Erling Smørgrav  */
933393f8daSKenneth D. Merry #if defined(_KERNEL) && defined(INVARIANTS)
94546d7890SDag-Erling Smørgrav 
9560ec4130SDag-Erling Smørgrav static void
96a5e7c7daSPeter Wemm _assert_sbuf_integrity(const char *fun, struct sbuf *s)
9760ec4130SDag-Erling Smørgrav {
98546d7890SDag-Erling Smørgrav 
9960ec4130SDag-Erling Smørgrav 	KASSERT(s != NULL,
100cab5b963SDag-Erling Smørgrav 	    ("%s called with a NULL sbuf pointer", fun));
10160ec4130SDag-Erling Smørgrav 	KASSERT(s->s_buf != NULL,
1027195eb40SKelly Yancey 	    ("%s called with uninitialized or corrupt sbuf", fun));
10360ec4130SDag-Erling Smørgrav 	KASSERT(s->s_len < s->s_size,
10471c2bc5cSPoul-Henning Kamp 	    ("wrote past end of sbuf (%jd >= %jd)",
10571c2bc5cSPoul-Henning Kamp 	    (intmax_t)s->s_len, (intmax_t)s->s_size));
10660ec4130SDag-Erling Smørgrav }
10760ec4130SDag-Erling Smørgrav 
10860ec4130SDag-Erling Smørgrav static void
109a5e7c7daSPeter Wemm _assert_sbuf_state(const char *fun, struct sbuf *s, int state)
11060ec4130SDag-Erling Smørgrav {
111546d7890SDag-Erling Smørgrav 
11260ec4130SDag-Erling Smørgrav 	KASSERT((s->s_flags & SBUF_FINISHED) == state,
113cab5b963SDag-Erling Smørgrav 	    ("%s called with %sfinished or corrupt sbuf", fun,
11460ec4130SDag-Erling Smørgrav 	    (state ? "un" : "")));
11560ec4130SDag-Erling Smørgrav }
116546d7890SDag-Erling Smørgrav 
117a48740b6SDavid E. O'Brien #define	assert_sbuf_integrity(s) _assert_sbuf_integrity(__func__, (s))
118a48740b6SDavid E. O'Brien #define	assert_sbuf_state(s, i)	 _assert_sbuf_state(__func__, (s), (i))
119546d7890SDag-Erling Smørgrav 
1203393f8daSKenneth D. Merry #else /* _KERNEL && INVARIANTS */
121546d7890SDag-Erling Smørgrav 
12260ec4130SDag-Erling Smørgrav #define	assert_sbuf_integrity(s) do { } while (0)
12360ec4130SDag-Erling Smørgrav #define	assert_sbuf_state(s, i)	 do { } while (0)
124546d7890SDag-Erling Smørgrav 
1253393f8daSKenneth D. Merry #endif /* _KERNEL && INVARIANTS */
12660ec4130SDag-Erling Smørgrav 
127181ff3d5SMatthew D Fleming #ifdef CTASSERT
128f4bafab8SMatthew D Fleming CTASSERT(powerof2(SBUF_MAXEXTENDSIZE));
129f4bafab8SMatthew D Fleming CTASSERT(powerof2(SBUF_MAXEXTENDINCR));
130969292fbSMatthew D Fleming #endif
131f4bafab8SMatthew D Fleming 
1327195eb40SKelly Yancey static int
1337195eb40SKelly Yancey sbuf_extendsize(int size)
1347195eb40SKelly Yancey {
1357195eb40SKelly Yancey 	int newsize;
1367195eb40SKelly Yancey 
137f4bafab8SMatthew D Fleming 	if (size < (int)SBUF_MAXEXTENDSIZE) {
1387195eb40SKelly Yancey 		newsize = SBUF_MINEXTENDSIZE;
139f4bafab8SMatthew D Fleming 		while (newsize < size)
1407195eb40SKelly Yancey 			newsize *= 2;
141f4bafab8SMatthew D Fleming 	} else {
142f4bafab8SMatthew D Fleming 		newsize = roundup2(size, SBUF_MAXEXTENDINCR);
1437195eb40SKelly Yancey 	}
144f5a5dc5dSMatthew D Fleming 	KASSERT(newsize >= size, ("%s: %d < %d\n", __func__, newsize, size));
1457195eb40SKelly Yancey 	return (newsize);
1467195eb40SKelly Yancey }
1477195eb40SKelly Yancey 
1487195eb40SKelly Yancey /*
1497195eb40SKelly Yancey  * Extend an sbuf.
1507195eb40SKelly Yancey  */
1517195eb40SKelly Yancey static int
1527195eb40SKelly Yancey sbuf_extend(struct sbuf *s, int addlen)
1537195eb40SKelly Yancey {
1547195eb40SKelly Yancey 	char *newbuf;
1557195eb40SKelly Yancey 	int newsize;
1567195eb40SKelly Yancey 
1577195eb40SKelly Yancey 	if (!SBUF_CANEXTEND(s))
1587195eb40SKelly Yancey 		return (-1);
1597195eb40SKelly Yancey 	newsize = sbuf_extendsize(s->s_size + addlen);
160546d7890SDag-Erling Smørgrav 	newbuf = SBMALLOC(newsize);
1617195eb40SKelly Yancey 	if (newbuf == NULL)
1627195eb40SKelly Yancey 		return (-1);
163384bf94cSPoul-Henning Kamp 	memcpy(newbuf, s->s_buf, s->s_size);
1647195eb40SKelly Yancey 	if (SBUF_ISDYNAMIC(s))
1657195eb40SKelly Yancey 		SBFREE(s->s_buf);
1667195eb40SKelly Yancey 	else
1677195eb40SKelly Yancey 		SBUF_SETFLAG(s, SBUF_DYNAMIC);
1687195eb40SKelly Yancey 	s->s_buf = newbuf;
1697195eb40SKelly Yancey 	s->s_size = newsize;
1707195eb40SKelly Yancey 	return (0);
1717195eb40SKelly Yancey }
1727195eb40SKelly Yancey 
17360ec4130SDag-Erling Smørgrav /*
174384bf94cSPoul-Henning Kamp  * Initialize the internals of an sbuf.
175384bf94cSPoul-Henning Kamp  * If buf is non-NULL, it points to a static or already-allocated string
176384bf94cSPoul-Henning Kamp  * big enough to hold at least length characters.
177384bf94cSPoul-Henning Kamp  */
178384bf94cSPoul-Henning Kamp static struct sbuf *
179384bf94cSPoul-Henning Kamp sbuf_newbuf(struct sbuf *s, char *buf, int length, int flags)
180384bf94cSPoul-Henning Kamp {
181384bf94cSPoul-Henning Kamp 
182384bf94cSPoul-Henning Kamp 	memset(s, 0, sizeof(*s));
183384bf94cSPoul-Henning Kamp 	s->s_flags = flags;
184384bf94cSPoul-Henning Kamp 	s->s_size = length;
185384bf94cSPoul-Henning Kamp 	s->s_buf = buf;
186384bf94cSPoul-Henning Kamp 
187384bf94cSPoul-Henning Kamp 	if ((s->s_flags & SBUF_AUTOEXTEND) == 0) {
188311fa10bSRobert Watson 		KASSERT(s->s_size >= 0,
189384bf94cSPoul-Henning Kamp 		    ("attempt to create a too small sbuf"));
190384bf94cSPoul-Henning Kamp 	}
191384bf94cSPoul-Henning Kamp 
192384bf94cSPoul-Henning Kamp 	if (s->s_buf != NULL)
193384bf94cSPoul-Henning Kamp 		return (s);
194384bf94cSPoul-Henning Kamp 
195384bf94cSPoul-Henning Kamp 	if ((flags & SBUF_AUTOEXTEND) != 0)
196384bf94cSPoul-Henning Kamp 		s->s_size = sbuf_extendsize(s->s_size);
197384bf94cSPoul-Henning Kamp 
198384bf94cSPoul-Henning Kamp 	s->s_buf = SBMALLOC(s->s_size);
199384bf94cSPoul-Henning Kamp 	if (s->s_buf == NULL)
200384bf94cSPoul-Henning Kamp 		return (NULL);
201384bf94cSPoul-Henning Kamp 	SBUF_SETFLAG(s, SBUF_DYNAMIC);
202384bf94cSPoul-Henning Kamp 	return (s);
203384bf94cSPoul-Henning Kamp }
204384bf94cSPoul-Henning Kamp 
205384bf94cSPoul-Henning Kamp /*
20660ec4130SDag-Erling Smørgrav  * Initialize an sbuf.
20760ec4130SDag-Erling Smørgrav  * If buf is non-NULL, it points to a static or already-allocated string
20860ec4130SDag-Erling Smørgrav  * big enough to hold at least length characters.
20960ec4130SDag-Erling Smørgrav  */
210d6479358SDag-Erling Smørgrav struct sbuf *
2114dc14139SDag-Erling Smørgrav sbuf_new(struct sbuf *s, char *buf, int length, int flags)
21260ec4130SDag-Erling Smørgrav {
213546d7890SDag-Erling Smørgrav 
2144dc14139SDag-Erling Smørgrav 	KASSERT(length >= 0,
2154dc14139SDag-Erling Smørgrav 	    ("attempt to create an sbuf of negative length (%d)", length));
2167195eb40SKelly Yancey 	KASSERT((flags & ~SBUF_USRFLAGMSK) == 0,
2177195eb40SKelly Yancey 	    ("%s called with invalid flags", __func__));
21860ec4130SDag-Erling Smørgrav 
2197195eb40SKelly Yancey 	flags &= SBUF_USRFLAGMSK;
220384bf94cSPoul-Henning Kamp 	if (s != NULL)
221384bf94cSPoul-Henning Kamp 		return (sbuf_newbuf(s, buf, length, flags));
222384bf94cSPoul-Henning Kamp 
223546d7890SDag-Erling Smørgrav 	s = SBMALLOC(sizeof(*s));
224d6479358SDag-Erling Smørgrav 	if (s == NULL)
225d6479358SDag-Erling Smørgrav 		return (NULL);
226384bf94cSPoul-Henning Kamp 	if (sbuf_newbuf(s, buf, length, flags) == NULL) {
227d6479358SDag-Erling Smørgrav 		SBFREE(s);
228d6479358SDag-Erling Smørgrav 		return (NULL);
229d6479358SDag-Erling Smørgrav 	}
230384bf94cSPoul-Henning Kamp 	SBUF_SETFLAG(s, SBUF_DYNSTRUCT);
231d6479358SDag-Erling Smørgrav 	return (s);
23260ec4130SDag-Erling Smørgrav }
23360ec4130SDag-Erling Smørgrav 
2345b6db477SDag-Erling Smørgrav #ifdef _KERNEL
2355b6db477SDag-Erling Smørgrav /*
2365b6db477SDag-Erling Smørgrav  * Create an sbuf with uio data
2375b6db477SDag-Erling Smørgrav  */
2385b6db477SDag-Erling Smørgrav struct sbuf *
2395b6db477SDag-Erling Smørgrav sbuf_uionew(struct sbuf *s, struct uio *uio, int *error)
2405b6db477SDag-Erling Smørgrav {
241546d7890SDag-Erling Smørgrav 
2425b6db477SDag-Erling Smørgrav 	KASSERT(uio != NULL,
243a48740b6SDavid E. O'Brien 	    ("%s called with NULL uio pointer", __func__));
2445b6db477SDag-Erling Smørgrav 	KASSERT(error != NULL,
245a48740b6SDavid E. O'Brien 	    ("%s called with NULL error pointer", __func__));
2465b6db477SDag-Erling Smørgrav 
2475b6db477SDag-Erling Smørgrav 	s = sbuf_new(s, NULL, uio->uio_resid + 1, 0);
2485b6db477SDag-Erling Smørgrav 	if (s == NULL) {
2495b6db477SDag-Erling Smørgrav 		*error = ENOMEM;
2505b6db477SDag-Erling Smørgrav 		return (NULL);
2515b6db477SDag-Erling Smørgrav 	}
2525b6db477SDag-Erling Smørgrav 	*error = uiomove(s->s_buf, uio->uio_resid, uio);
2535b6db477SDag-Erling Smørgrav 	if (*error != 0) {
2545b6db477SDag-Erling Smørgrav 		sbuf_delete(s);
2555b6db477SDag-Erling Smørgrav 		return (NULL);
2565b6db477SDag-Erling Smørgrav 	}
2575b6db477SDag-Erling Smørgrav 	s->s_len = s->s_size - 1;
258*ddb9b612SMikolaj Golub 	if (SBUF_ISSECTION(s))
259*ddb9b612SMikolaj Golub 		s->s_sect_len = s->s_size - 1;
2605b6db477SDag-Erling Smørgrav 	*error = 0;
2615b6db477SDag-Erling Smørgrav 	return (s);
2625b6db477SDag-Erling Smørgrav }
2635b6db477SDag-Erling Smørgrav #endif
2645b6db477SDag-Erling Smørgrav 
26560ec4130SDag-Erling Smørgrav /*
2667195eb40SKelly Yancey  * Clear an sbuf and reset its position.
2674dc14139SDag-Erling Smørgrav  */
2684dc14139SDag-Erling Smørgrav void
2694dc14139SDag-Erling Smørgrav sbuf_clear(struct sbuf *s)
2704dc14139SDag-Erling Smørgrav {
271546d7890SDag-Erling Smørgrav 
2724dc14139SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
2739fa2ef3dSDag-Erling Smørgrav 	/* don't care if it's finished or not */
2744dc14139SDag-Erling Smørgrav 
2754dc14139SDag-Erling Smørgrav 	SBUF_CLEARFLAG(s, SBUF_FINISHED);
2764351ba27SMatthew D Fleming 	s->s_error = 0;
2774dc14139SDag-Erling Smørgrav 	s->s_len = 0;
278*ddb9b612SMikolaj Golub 	s->s_sect_len = 0;
2794dc14139SDag-Erling Smørgrav }
2804dc14139SDag-Erling Smørgrav 
2814dc14139SDag-Erling Smørgrav /*
2827195eb40SKelly Yancey  * Set the sbuf's end position to an arbitrary value.
2837195eb40SKelly Yancey  * Effectively truncates the sbuf at the new position.
28460ec4130SDag-Erling Smørgrav  */
28560ec4130SDag-Erling Smørgrav int
28671c2bc5cSPoul-Henning Kamp sbuf_setpos(struct sbuf *s, ssize_t pos)
28760ec4130SDag-Erling Smørgrav {
288546d7890SDag-Erling Smørgrav 
28960ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
29060ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
29160ec4130SDag-Erling Smørgrav 
29260ec4130SDag-Erling Smørgrav 	KASSERT(pos >= 0,
29371c2bc5cSPoul-Henning Kamp 	    ("attempt to seek to a negative position (%jd)", (intmax_t)pos));
29460ec4130SDag-Erling Smørgrav 	KASSERT(pos < s->s_size,
29571c2bc5cSPoul-Henning Kamp 	    ("attempt to seek past end of sbuf (%jd >= %jd)",
29671c2bc5cSPoul-Henning Kamp 	    (intmax_t)pos, (intmax_t)s->s_size));
297*ddb9b612SMikolaj Golub 	KASSERT(!SBUF_ISSECTION(s),
298*ddb9b612SMikolaj Golub 	    ("attempt to seek when in a section"));
29960ec4130SDag-Erling Smørgrav 
30060ec4130SDag-Erling Smørgrav 	if (pos < 0 || pos > s->s_len)
30160ec4130SDag-Erling Smørgrav 		return (-1);
30260ec4130SDag-Erling Smørgrav 	s->s_len = pos;
30360ec4130SDag-Erling Smørgrav 	return (0);
30460ec4130SDag-Erling Smørgrav }
30560ec4130SDag-Erling Smørgrav 
30660ec4130SDag-Erling Smørgrav /*
3074351ba27SMatthew D Fleming  * Set up a drain function and argument on an sbuf to flush data to
3084351ba27SMatthew D Fleming  * when the sbuf buffer overflows.
3094351ba27SMatthew D Fleming  */
3104351ba27SMatthew D Fleming void
3114351ba27SMatthew D Fleming sbuf_set_drain(struct sbuf *s, sbuf_drain_func *func, void *ctx)
3124351ba27SMatthew D Fleming {
3134351ba27SMatthew D Fleming 
3144351ba27SMatthew D Fleming 	assert_sbuf_state(s, 0);
3154351ba27SMatthew D Fleming 	assert_sbuf_integrity(s);
3164351ba27SMatthew D Fleming 	KASSERT(func == s->s_drain_func || s->s_len == 0,
3174351ba27SMatthew D Fleming 	    ("Cannot change drain to %p on non-empty sbuf %p", func, s));
3184351ba27SMatthew D Fleming 	s->s_drain_func = func;
3194351ba27SMatthew D Fleming 	s->s_drain_arg = ctx;
3204351ba27SMatthew D Fleming }
3214351ba27SMatthew D Fleming 
3224351ba27SMatthew D Fleming /*
3234351ba27SMatthew D Fleming  * Call the drain and process the return.
3244351ba27SMatthew D Fleming  */
3254351ba27SMatthew D Fleming static int
3264351ba27SMatthew D Fleming sbuf_drain(struct sbuf *s)
3274351ba27SMatthew D Fleming {
3284351ba27SMatthew D Fleming 	int len;
3294351ba27SMatthew D Fleming 
3304351ba27SMatthew D Fleming 	KASSERT(s->s_len > 0, ("Shouldn't drain empty sbuf %p", s));
3314d369413SMatthew D Fleming 	KASSERT(s->s_error == 0, ("Called %s with error on %p", __func__, s));
3324351ba27SMatthew D Fleming 	len = s->s_drain_func(s->s_drain_arg, s->s_buf, s->s_len);
3334351ba27SMatthew D Fleming 	if (len < 0) {
3344351ba27SMatthew D Fleming 		s->s_error = -len;
3354351ba27SMatthew D Fleming 		return (s->s_error);
3364351ba27SMatthew D Fleming 	}
3374e657159SMatthew D Fleming 	KASSERT(len > 0 && len <= s->s_len,
3384e657159SMatthew D Fleming 	    ("Bad drain amount %d for sbuf %p", len, s));
3394351ba27SMatthew D Fleming 	s->s_len -= len;
3404351ba27SMatthew D Fleming 	/*
3414351ba27SMatthew D Fleming 	 * Fast path for the expected case where all the data was
3424351ba27SMatthew D Fleming 	 * drained.
3434351ba27SMatthew D Fleming 	 */
3444351ba27SMatthew D Fleming 	if (s->s_len == 0)
3454351ba27SMatthew D Fleming 		return (0);
3464351ba27SMatthew D Fleming 	/*
3474351ba27SMatthew D Fleming 	 * Move the remaining characters to the beginning of the
3484351ba27SMatthew D Fleming 	 * string.
3494351ba27SMatthew D Fleming 	 */
3504351ba27SMatthew D Fleming 	memmove(s->s_buf, s->s_buf + len, s->s_len);
3514351ba27SMatthew D Fleming 	return (0);
3524351ba27SMatthew D Fleming }
3534351ba27SMatthew D Fleming 
3544351ba27SMatthew D Fleming /*
35501f6f5fcSMatthew D Fleming  * Append a byte to an sbuf.  This is the core function for appending
35601f6f5fcSMatthew D Fleming  * to an sbuf and is the main place that deals with extending the
35701f6f5fcSMatthew D Fleming  * buffer and marking overflow.
35801f6f5fcSMatthew D Fleming  */
35901f6f5fcSMatthew D Fleming static void
360eb05ee7aSPoul-Henning Kamp sbuf_put_byte(struct sbuf *s, int c)
36101f6f5fcSMatthew D Fleming {
36201f6f5fcSMatthew D Fleming 
36301f6f5fcSMatthew D Fleming 	assert_sbuf_integrity(s);
36401f6f5fcSMatthew D Fleming 	assert_sbuf_state(s, 0);
36501f6f5fcSMatthew D Fleming 
3664d369413SMatthew D Fleming 	if (s->s_error != 0)
36701f6f5fcSMatthew D Fleming 		return;
36801f6f5fcSMatthew D Fleming 	if (SBUF_FREESPACE(s) <= 0) {
3694351ba27SMatthew D Fleming 		/*
3704351ba27SMatthew D Fleming 		 * If there is a drain, use it, otherwise extend the
3714351ba27SMatthew D Fleming 		 * buffer.
3724351ba27SMatthew D Fleming 		 */
3734351ba27SMatthew D Fleming 		if (s->s_drain_func != NULL)
3744351ba27SMatthew D Fleming 			(void)sbuf_drain(s);
3754351ba27SMatthew D Fleming 		else if (sbuf_extend(s, 1) < 0)
3764d369413SMatthew D Fleming 			s->s_error = ENOMEM;
3774d369413SMatthew D Fleming 		if (s->s_error != 0)
37801f6f5fcSMatthew D Fleming 			return;
37901f6f5fcSMatthew D Fleming 	}
38001f6f5fcSMatthew D Fleming 	s->s_buf[s->s_len++] = c;
381*ddb9b612SMikolaj Golub 	if (SBUF_ISSECTION(s))
382*ddb9b612SMikolaj Golub 		s->s_sect_len++;
38301f6f5fcSMatthew D Fleming }
38401f6f5fcSMatthew D Fleming 
38501f6f5fcSMatthew D Fleming /*
386b0def2b5SDag-Erling Smørgrav  * Append a byte string to an sbuf.
387b0def2b5SDag-Erling Smørgrav  */
388b0def2b5SDag-Erling Smørgrav int
389520df276SDag-Erling Smørgrav sbuf_bcat(struct sbuf *s, const void *buf, size_t len)
390b0def2b5SDag-Erling Smørgrav {
391520df276SDag-Erling Smørgrav 	const char *str = buf;
39201f6f5fcSMatthew D Fleming 	const char *end = str + len;
393d751f0a9SDag-Erling Smørgrav 
394b0def2b5SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
395b0def2b5SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
396b0def2b5SDag-Erling Smørgrav 
3974d369413SMatthew D Fleming 	if (s->s_error != 0)
398b0def2b5SDag-Erling Smørgrav 		return (-1);
39901f6f5fcSMatthew D Fleming 	for (; str < end; str++) {
400eb05ee7aSPoul-Henning Kamp 		sbuf_put_byte(s, *str);
4014d369413SMatthew D Fleming 		if (s->s_error != 0)
402b0def2b5SDag-Erling Smørgrav 			return (-1);
403b0def2b5SDag-Erling Smørgrav 	}
404b0def2b5SDag-Erling Smørgrav 	return (0);
405b0def2b5SDag-Erling Smørgrav }
406b0def2b5SDag-Erling Smørgrav 
407b0def2b5SDag-Erling Smørgrav #ifdef _KERNEL
408b0def2b5SDag-Erling Smørgrav /*
409b0def2b5SDag-Erling Smørgrav  * Copy a byte string from userland into an sbuf.
410b0def2b5SDag-Erling Smørgrav  */
411b0def2b5SDag-Erling Smørgrav int
412b0def2b5SDag-Erling Smørgrav sbuf_bcopyin(struct sbuf *s, const void *uaddr, size_t len)
413b0def2b5SDag-Erling Smørgrav {
414546d7890SDag-Erling Smørgrav 
415b0def2b5SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
416b0def2b5SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
4174351ba27SMatthew D Fleming 	KASSERT(s->s_drain_func == NULL,
4184351ba27SMatthew D Fleming 	    ("Nonsensical copyin to sbuf %p with a drain", s));
419b0def2b5SDag-Erling Smørgrav 
4204d369413SMatthew D Fleming 	if (s->s_error != 0)
421b0def2b5SDag-Erling Smørgrav 		return (-1);
422b0def2b5SDag-Erling Smørgrav 	if (len == 0)
423b0def2b5SDag-Erling Smørgrav 		return (0);
4247195eb40SKelly Yancey 	if (len > SBUF_FREESPACE(s)) {
4257195eb40SKelly Yancey 		sbuf_extend(s, len - SBUF_FREESPACE(s));
426c05dbe7aSMatthew D Fleming 		if (SBUF_FREESPACE(s) < len)
427c05dbe7aSMatthew D Fleming 			len = SBUF_FREESPACE(s);
4287195eb40SKelly Yancey 	}
429e3b37322SDag-Erling Smørgrav 	if (copyin(uaddr, s->s_buf + s->s_len, len) != 0)
430e3b37322SDag-Erling Smørgrav 		return (-1);
431fe463496SDag-Erling Smørgrav 	s->s_len += len;
432b0def2b5SDag-Erling Smørgrav 
433b0def2b5SDag-Erling Smørgrav 	return (0);
434b0def2b5SDag-Erling Smørgrav }
435b0def2b5SDag-Erling Smørgrav #endif
436b0def2b5SDag-Erling Smørgrav 
437b0def2b5SDag-Erling Smørgrav /*
438b0def2b5SDag-Erling Smørgrav  * Copy a byte string into an sbuf.
439b0def2b5SDag-Erling Smørgrav  */
440b0def2b5SDag-Erling Smørgrav int
441520df276SDag-Erling Smørgrav sbuf_bcpy(struct sbuf *s, const void *buf, size_t len)
442b0def2b5SDag-Erling Smørgrav {
443546d7890SDag-Erling Smørgrav 
444b0def2b5SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
445b0def2b5SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
446b0def2b5SDag-Erling Smørgrav 
447b0def2b5SDag-Erling Smørgrav 	sbuf_clear(s);
448520df276SDag-Erling Smørgrav 	return (sbuf_bcat(s, buf, len));
449b0def2b5SDag-Erling Smørgrav }
450b0def2b5SDag-Erling Smørgrav 
451b0def2b5SDag-Erling Smørgrav /*
45260ec4130SDag-Erling Smørgrav  * Append a string to an sbuf.
45360ec4130SDag-Erling Smørgrav  */
45460ec4130SDag-Erling Smørgrav int
4553393f8daSKenneth D. Merry sbuf_cat(struct sbuf *s, const char *str)
45660ec4130SDag-Erling Smørgrav {
457546d7890SDag-Erling Smørgrav 
45860ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
45960ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
46060ec4130SDag-Erling Smørgrav 
4614d369413SMatthew D Fleming 	if (s->s_error != 0)
46260ec4130SDag-Erling Smørgrav 		return (-1);
46360ec4130SDag-Erling Smørgrav 
464c05dbe7aSMatthew D Fleming 	while (*str != '\0') {
465eb05ee7aSPoul-Henning Kamp 		sbuf_put_byte(s, *str++);
4664d369413SMatthew D Fleming 		if (s->s_error != 0)
46760ec4130SDag-Erling Smørgrav 			return (-1);
46860ec4130SDag-Erling Smørgrav 	}
46960ec4130SDag-Erling Smørgrav 	return (0);
47060ec4130SDag-Erling Smørgrav }
47160ec4130SDag-Erling Smørgrav 
472b0def2b5SDag-Erling Smørgrav #ifdef _KERNEL
473b0def2b5SDag-Erling Smørgrav /*
4747195eb40SKelly Yancey  * Append a string from userland to an sbuf.
475b0def2b5SDag-Erling Smørgrav  */
476b0def2b5SDag-Erling Smørgrav int
477b0def2b5SDag-Erling Smørgrav sbuf_copyin(struct sbuf *s, const void *uaddr, size_t len)
478b0def2b5SDag-Erling Smørgrav {
479b0def2b5SDag-Erling Smørgrav 	size_t done;
480b0def2b5SDag-Erling Smørgrav 
481b0def2b5SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
482b0def2b5SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
4834351ba27SMatthew D Fleming 	KASSERT(s->s_drain_func == NULL,
4844351ba27SMatthew D Fleming 	    ("Nonsensical copyin to sbuf %p with a drain", s));
485b0def2b5SDag-Erling Smørgrav 
4864d369413SMatthew D Fleming 	if (s->s_error != 0)
487b0def2b5SDag-Erling Smørgrav 		return (-1);
488b0def2b5SDag-Erling Smørgrav 
4897195eb40SKelly Yancey 	if (len == 0)
4907195eb40SKelly Yancey 		len = SBUF_FREESPACE(s);	/* XXX return 0? */
4917195eb40SKelly Yancey 	if (len > SBUF_FREESPACE(s)) {
4927195eb40SKelly Yancey 		sbuf_extend(s, len);
493c05dbe7aSMatthew D Fleming 		if (SBUF_FREESPACE(s) < len)
494c05dbe7aSMatthew D Fleming 			len = SBUF_FREESPACE(s);
4957195eb40SKelly Yancey 	}
496b0def2b5SDag-Erling Smørgrav 	switch (copyinstr(uaddr, s->s_buf + s->s_len, len + 1, &done)) {
497b0def2b5SDag-Erling Smørgrav 	case ENAMETOOLONG:
4984d369413SMatthew D Fleming 		s->s_error = ENOMEM;
499b0def2b5SDag-Erling Smørgrav 		/* fall through */
500b0def2b5SDag-Erling Smørgrav 	case 0:
501b0def2b5SDag-Erling Smørgrav 		s->s_len += done - 1;
502*ddb9b612SMikolaj Golub 		if (SBUF_ISSECTION(s))
503*ddb9b612SMikolaj Golub 			s->s_sect_len += done - 1;
504b0def2b5SDag-Erling Smørgrav 		break;
505b0def2b5SDag-Erling Smørgrav 	default:
506b0def2b5SDag-Erling Smørgrav 		return (-1);	/* XXX */
507b0def2b5SDag-Erling Smørgrav 	}
508b0def2b5SDag-Erling Smørgrav 
50949091c48SPoul-Henning Kamp 	return (done);
510b0def2b5SDag-Erling Smørgrav }
511b0def2b5SDag-Erling Smørgrav #endif
512b0def2b5SDag-Erling Smørgrav 
51360ec4130SDag-Erling Smørgrav /*
51460ec4130SDag-Erling Smørgrav  * Copy a string into an sbuf.
51560ec4130SDag-Erling Smørgrav  */
51660ec4130SDag-Erling Smørgrav int
5173393f8daSKenneth D. Merry sbuf_cpy(struct sbuf *s, const char *str)
51860ec4130SDag-Erling Smørgrav {
519546d7890SDag-Erling Smørgrav 
52060ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
52160ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
52260ec4130SDag-Erling Smørgrav 
5234dc14139SDag-Erling Smørgrav 	sbuf_clear(s);
52460ec4130SDag-Erling Smørgrav 	return (sbuf_cat(s, str));
52560ec4130SDag-Erling Smørgrav }
52660ec4130SDag-Erling Smørgrav 
52760ec4130SDag-Erling Smørgrav /*
5287195eb40SKelly Yancey  * Format the given argument list and append the resulting string to an sbuf.
52960ec4130SDag-Erling Smørgrav  */
53001f6f5fcSMatthew D Fleming #ifdef _KERNEL
531eb05ee7aSPoul-Henning Kamp 
532eb05ee7aSPoul-Henning Kamp /*
533eb05ee7aSPoul-Henning Kamp  * Append a non-NUL character to an sbuf.  This prototype signature is
534eb05ee7aSPoul-Henning Kamp  * suitable for use with kvprintf(9).
535eb05ee7aSPoul-Henning Kamp  */
536eb05ee7aSPoul-Henning Kamp static void
537eb05ee7aSPoul-Henning Kamp sbuf_putc_func(int c, void *arg)
538eb05ee7aSPoul-Henning Kamp {
539eb05ee7aSPoul-Henning Kamp 
540eb05ee7aSPoul-Henning Kamp 	if (c != '\0')
541eb05ee7aSPoul-Henning Kamp 		sbuf_put_byte(arg, c);
542eb05ee7aSPoul-Henning Kamp }
543eb05ee7aSPoul-Henning Kamp 
54401f6f5fcSMatthew D Fleming int
54501f6f5fcSMatthew D Fleming sbuf_vprintf(struct sbuf *s, const char *fmt, va_list ap)
54601f6f5fcSMatthew D Fleming {
54701f6f5fcSMatthew D Fleming 
54801f6f5fcSMatthew D Fleming 	assert_sbuf_integrity(s);
54901f6f5fcSMatthew D Fleming 	assert_sbuf_state(s, 0);
55001f6f5fcSMatthew D Fleming 
55101f6f5fcSMatthew D Fleming 	KASSERT(fmt != NULL,
55201f6f5fcSMatthew D Fleming 	    ("%s called with a NULL format string", __func__));
55301f6f5fcSMatthew D Fleming 
55401f6f5fcSMatthew D Fleming 	(void)kvprintf(fmt, sbuf_putc_func, s, 10, ap);
5554d369413SMatthew D Fleming 	if (s->s_error != 0)
55601f6f5fcSMatthew D Fleming 		return (-1);
55701f6f5fcSMatthew D Fleming 	return (0);
55801f6f5fcSMatthew D Fleming }
55901f6f5fcSMatthew D Fleming #else /* !_KERNEL */
56060ec4130SDag-Erling Smørgrav int
5617195eb40SKelly Yancey sbuf_vprintf(struct sbuf *s, const char *fmt, va_list ap)
56260ec4130SDag-Erling Smørgrav {
563a9a0bbadSPeter Wemm 	va_list ap_copy;
5644351ba27SMatthew D Fleming 	int error, len;
56560ec4130SDag-Erling Smørgrav 
56660ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
56760ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
56860ec4130SDag-Erling Smørgrav 
56960ec4130SDag-Erling Smørgrav 	KASSERT(fmt != NULL,
570a48740b6SDavid E. O'Brien 	    ("%s called with a NULL format string", __func__));
57160ec4130SDag-Erling Smørgrav 
5724d369413SMatthew D Fleming 	if (s->s_error != 0)
57360ec4130SDag-Erling Smørgrav 		return (-1);
57460ec4130SDag-Erling Smørgrav 
57501f6f5fcSMatthew D Fleming 	/*
57601f6f5fcSMatthew D Fleming 	 * For the moment, there is no way to get vsnprintf(3) to hand
57701f6f5fcSMatthew D Fleming 	 * back a character at a time, to push everything into
57801f6f5fcSMatthew D Fleming 	 * sbuf_putc_func() as was done for the kernel.
5794351ba27SMatthew D Fleming 	 *
5804351ba27SMatthew D Fleming 	 * In userspace, while drains are useful, there's generally
5814351ba27SMatthew D Fleming 	 * not a problem attempting to malloc(3) on out of space.  So
5824351ba27SMatthew D Fleming 	 * expand a userland sbuf if there is not enough room for the
5834351ba27SMatthew D Fleming 	 * data produced by sbuf_[v]printf(3).
58401f6f5fcSMatthew D Fleming 	 */
58501f6f5fcSMatthew D Fleming 
5864351ba27SMatthew D Fleming 	error = 0;
5877195eb40SKelly Yancey 	do {
588a9a0bbadSPeter Wemm 		va_copy(ap_copy, ap);
5897195eb40SKelly Yancey 		len = vsnprintf(&s->s_buf[s->s_len], SBUF_FREESPACE(s) + 1,
590a9a0bbadSPeter Wemm 		    fmt, ap_copy);
591a9a0bbadSPeter Wemm 		va_end(ap_copy);
5924351ba27SMatthew D Fleming 
5934351ba27SMatthew D Fleming 		if (SBUF_FREESPACE(s) >= len)
5944351ba27SMatthew D Fleming 			break;
5954351ba27SMatthew D Fleming 		/* Cannot print with the current available space. */
5964351ba27SMatthew D Fleming 		if (s->s_drain_func != NULL && s->s_len > 0)
5974351ba27SMatthew D Fleming 			error = sbuf_drain(s);
5984351ba27SMatthew D Fleming 		else
5994351ba27SMatthew D Fleming 			error = sbuf_extend(s, len - SBUF_FREESPACE(s));
6004351ba27SMatthew D Fleming 	} while (error == 0);
60160ec4130SDag-Erling Smørgrav 
6023393f8daSKenneth D. Merry 	/*
6033393f8daSKenneth D. Merry 	 * s->s_len is the length of the string, without the terminating nul.
6043393f8daSKenneth D. Merry 	 * When updating s->s_len, we must subtract 1 from the length that
6053393f8daSKenneth D. Merry 	 * we passed into vsnprintf() because that length includes the
6063393f8daSKenneth D. Merry 	 * terminating nul.
6073393f8daSKenneth D. Merry 	 *
6083393f8daSKenneth D. Merry 	 * vsnprintf() returns the amount that would have been copied,
609c05dbe7aSMatthew D Fleming 	 * given sufficient space, so don't over-increment s_len.
6103393f8daSKenneth D. Merry 	 */
611c05dbe7aSMatthew D Fleming 	if (SBUF_FREESPACE(s) < len)
612c05dbe7aSMatthew D Fleming 		len = SBUF_FREESPACE(s);
613c05dbe7aSMatthew D Fleming 	s->s_len += len;
614*ddb9b612SMikolaj Golub 	if (SBUF_ISSECTION(s))
615*ddb9b612SMikolaj Golub 		s->s_sect_len += len;
6161a25c86bSPoul-Henning Kamp 	if (!SBUF_HASROOM(s) && !SBUF_CANEXTEND(s))
6174d369413SMatthew D Fleming 		s->s_error = ENOMEM;
6183393f8daSKenneth D. Merry 
61960ec4130SDag-Erling Smørgrav 	KASSERT(s->s_len < s->s_size,
62060ec4130SDag-Erling Smørgrav 	    ("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size));
62160ec4130SDag-Erling Smørgrav 
6224d369413SMatthew D Fleming 	if (s->s_error != 0)
62360ec4130SDag-Erling Smørgrav 		return (-1);
62460ec4130SDag-Erling Smørgrav 	return (0);
62560ec4130SDag-Erling Smørgrav }
62601f6f5fcSMatthew D Fleming #endif /* _KERNEL */
62760ec4130SDag-Erling Smørgrav 
62860ec4130SDag-Erling Smørgrav /*
6297195eb40SKelly Yancey  * Format the given arguments and append the resulting string to an sbuf.
6307195eb40SKelly Yancey  */
6317195eb40SKelly Yancey int
6327195eb40SKelly Yancey sbuf_printf(struct sbuf *s, const char *fmt, ...)
6337195eb40SKelly Yancey {
6347195eb40SKelly Yancey 	va_list ap;
6357195eb40SKelly Yancey 	int result;
6367195eb40SKelly Yancey 
6377195eb40SKelly Yancey 	va_start(ap, fmt);
6387195eb40SKelly Yancey 	result = sbuf_vprintf(s, fmt, ap);
6397195eb40SKelly Yancey 	va_end(ap);
6407195eb40SKelly Yancey 	return (result);
6417195eb40SKelly Yancey }
6427195eb40SKelly Yancey 
6437195eb40SKelly Yancey /*
64460ec4130SDag-Erling Smørgrav  * Append a character to an sbuf.
64560ec4130SDag-Erling Smørgrav  */
64660ec4130SDag-Erling Smørgrav int
64760ec4130SDag-Erling Smørgrav sbuf_putc(struct sbuf *s, int c)
64860ec4130SDag-Erling Smørgrav {
649546d7890SDag-Erling Smørgrav 
650eb05ee7aSPoul-Henning Kamp 	sbuf_put_byte(s, c);
6514d369413SMatthew D Fleming 	if (s->s_error != 0)
65260ec4130SDag-Erling Smørgrav 		return (-1);
65360ec4130SDag-Erling Smørgrav 	return (0);
65460ec4130SDag-Erling Smørgrav }
65560ec4130SDag-Erling Smørgrav 
65660ec4130SDag-Erling Smørgrav /*
6577195eb40SKelly Yancey  * Trim whitespace characters from end of an sbuf.
6585b6db477SDag-Erling Smørgrav  */
6595b6db477SDag-Erling Smørgrav int
6605b6db477SDag-Erling Smørgrav sbuf_trim(struct sbuf *s)
6615b6db477SDag-Erling Smørgrav {
662546d7890SDag-Erling Smørgrav 
6635b6db477SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
6645b6db477SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
6654351ba27SMatthew D Fleming 	KASSERT(s->s_drain_func == NULL,
6664351ba27SMatthew D Fleming 	    ("%s makes no sense on sbuf %p with drain", __func__, s));
6675b6db477SDag-Erling Smørgrav 
6684d369413SMatthew D Fleming 	if (s->s_error != 0)
6695b6db477SDag-Erling Smørgrav 		return (-1);
6705b6db477SDag-Erling Smørgrav 
671*ddb9b612SMikolaj Golub 	while (s->s_len > 0 && isspace(s->s_buf[s->s_len-1])) {
6725b6db477SDag-Erling Smørgrav 		--s->s_len;
673*ddb9b612SMikolaj Golub 		if (SBUF_ISSECTION(s))
674*ddb9b612SMikolaj Golub 			s->s_sect_len--;
675*ddb9b612SMikolaj Golub 	}
6765b6db477SDag-Erling Smørgrav 
6775b6db477SDag-Erling Smørgrav 	return (0);
6785b6db477SDag-Erling Smørgrav }
6795b6db477SDag-Erling Smørgrav 
6805b6db477SDag-Erling Smørgrav /*
6814d369413SMatthew D Fleming  * Check if an sbuf has an error.
68260ec4130SDag-Erling Smørgrav  */
68360ec4130SDag-Erling Smørgrav int
68471c2bc5cSPoul-Henning Kamp sbuf_error(const struct sbuf *s)
6854dc14139SDag-Erling Smørgrav {
686546d7890SDag-Erling Smørgrav 
6874d369413SMatthew D Fleming 	return (s->s_error);
6884dc14139SDag-Erling Smørgrav }
6894dc14139SDag-Erling Smørgrav 
6904dc14139SDag-Erling Smørgrav /*
6914dc14139SDag-Erling Smørgrav  * Finish off an sbuf.
6924dc14139SDag-Erling Smørgrav  */
6934351ba27SMatthew D Fleming int
69460ec4130SDag-Erling Smørgrav sbuf_finish(struct sbuf *s)
69560ec4130SDag-Erling Smørgrav {
696546d7890SDag-Erling Smørgrav 
69760ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
69860ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
69960ec4130SDag-Erling Smørgrav 
7004d369413SMatthew D Fleming 	if (s->s_drain_func != NULL) {
701eb05ee7aSPoul-Henning Kamp 		while (s->s_len > 0 && s->s_error == 0)
702eb05ee7aSPoul-Henning Kamp 			s->s_error = sbuf_drain(s);
7034d369413SMatthew D Fleming 	}
704cab5b963SDag-Erling Smørgrav 	s->s_buf[s->s_len] = '\0';
70560ec4130SDag-Erling Smørgrav 	SBUF_SETFLAG(s, SBUF_FINISHED);
7064351ba27SMatthew D Fleming #ifdef _KERNEL
707eb05ee7aSPoul-Henning Kamp 	return (s->s_error);
7084351ba27SMatthew D Fleming #else
709eb05ee7aSPoul-Henning Kamp 	errno = s->s_error;
710eb05ee7aSPoul-Henning Kamp 	if (s->s_error)
7114351ba27SMatthew D Fleming 		return (-1);
712eb05ee7aSPoul-Henning Kamp 	return (0);
7134351ba27SMatthew D Fleming #endif
71460ec4130SDag-Erling Smørgrav }
71560ec4130SDag-Erling Smørgrav 
71660ec4130SDag-Erling Smørgrav /*
71760ec4130SDag-Erling Smørgrav  * Return a pointer to the sbuf data.
71860ec4130SDag-Erling Smørgrav  */
71960ec4130SDag-Erling Smørgrav char *
72060ec4130SDag-Erling Smørgrav sbuf_data(struct sbuf *s)
72160ec4130SDag-Erling Smørgrav {
722546d7890SDag-Erling Smørgrav 
72360ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
72460ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, SBUF_FINISHED);
7254351ba27SMatthew D Fleming 	KASSERT(s->s_drain_func == NULL,
7264351ba27SMatthew D Fleming 	    ("%s makes no sense on sbuf %p with drain", __func__, s));
72760ec4130SDag-Erling Smørgrav 
728546d7890SDag-Erling Smørgrav 	return (s->s_buf);
72960ec4130SDag-Erling Smørgrav }
73060ec4130SDag-Erling Smørgrav 
73160ec4130SDag-Erling Smørgrav /*
73260ec4130SDag-Erling Smørgrav  * Return the length of the sbuf data.
73360ec4130SDag-Erling Smørgrav  */
73471c2bc5cSPoul-Henning Kamp ssize_t
73560ec4130SDag-Erling Smørgrav sbuf_len(struct sbuf *s)
73660ec4130SDag-Erling Smørgrav {
737546d7890SDag-Erling Smørgrav 
73860ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
7399fa2ef3dSDag-Erling Smørgrav 	/* don't care if it's finished or not */
7404351ba27SMatthew D Fleming 	KASSERT(s->s_drain_func == NULL,
7414351ba27SMatthew D Fleming 	    ("%s makes no sense on sbuf %p with drain", __func__, s));
74260ec4130SDag-Erling Smørgrav 
7434d369413SMatthew D Fleming 	if (s->s_error != 0)
7444dc14139SDag-Erling Smørgrav 		return (-1);
745546d7890SDag-Erling Smørgrav 	return (s->s_len);
74660ec4130SDag-Erling Smørgrav }
74760ec4130SDag-Erling Smørgrav 
74860ec4130SDag-Erling Smørgrav /*
74960ec4130SDag-Erling Smørgrav  * Clear an sbuf, free its buffer if necessary.
75060ec4130SDag-Erling Smørgrav  */
75160ec4130SDag-Erling Smørgrav void
75260ec4130SDag-Erling Smørgrav sbuf_delete(struct sbuf *s)
75360ec4130SDag-Erling Smørgrav {
754a57094a0SMatthew Dillon 	int isdyn;
755a57094a0SMatthew Dillon 
75660ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
75760ec4130SDag-Erling Smørgrav 	/* don't care if it's finished or not */
75860ec4130SDag-Erling Smørgrav 
75960ec4130SDag-Erling Smørgrav 	if (SBUF_ISDYNAMIC(s))
7603393f8daSKenneth D. Merry 		SBFREE(s->s_buf);
761a57094a0SMatthew Dillon 	isdyn = SBUF_ISDYNSTRUCT(s);
762384bf94cSPoul-Henning Kamp 	memset(s, 0, sizeof(*s));
763a57094a0SMatthew Dillon 	if (isdyn)
764d6479358SDag-Erling Smørgrav 		SBFREE(s);
76560ec4130SDag-Erling Smørgrav }
766c5f9218bSPoul-Henning Kamp 
767c5f9218bSPoul-Henning Kamp /*
768c5f9218bSPoul-Henning Kamp  * Check if an sbuf has been finished.
769c5f9218bSPoul-Henning Kamp  */
770c5f9218bSPoul-Henning Kamp int
77171c2bc5cSPoul-Henning Kamp sbuf_done(const struct sbuf *s)
772c5f9218bSPoul-Henning Kamp {
773c5f9218bSPoul-Henning Kamp 
774c5f9218bSPoul-Henning Kamp 	return (SBUF_ISFINISHED(s));
775c5f9218bSPoul-Henning Kamp }
776*ddb9b612SMikolaj Golub 
777*ddb9b612SMikolaj Golub /*
778*ddb9b612SMikolaj Golub  * Start a section.
779*ddb9b612SMikolaj Golub  */
780*ddb9b612SMikolaj Golub void
781*ddb9b612SMikolaj Golub sbuf_start_section(struct sbuf *s, ssize_t *old_lenp)
782*ddb9b612SMikolaj Golub {
783*ddb9b612SMikolaj Golub 
784*ddb9b612SMikolaj Golub 	assert_sbuf_integrity(s);
785*ddb9b612SMikolaj Golub 	assert_sbuf_state(s, 0);
786*ddb9b612SMikolaj Golub 
787*ddb9b612SMikolaj Golub 	if (!SBUF_ISSECTION(s)) {
788*ddb9b612SMikolaj Golub 		KASSERT(s->s_sect_len == 0,
789*ddb9b612SMikolaj Golub 		    ("s_sect_len != 0 when starting a section"));
790*ddb9b612SMikolaj Golub 		if (old_lenp != NULL)
791*ddb9b612SMikolaj Golub 			*old_lenp = -1;
792*ddb9b612SMikolaj Golub 		SBUF_SETFLAG(s, SBUF_INSECTION);
793*ddb9b612SMikolaj Golub 	} else {
794*ddb9b612SMikolaj Golub 		KASSERT(old_lenp != NULL,
795*ddb9b612SMikolaj Golub 		    ("s_sect_len should be saved when starting a subsection"));
796*ddb9b612SMikolaj Golub 		*old_lenp = s->s_sect_len;
797*ddb9b612SMikolaj Golub 		s->s_sect_len = 0;
798*ddb9b612SMikolaj Golub 	}
799*ddb9b612SMikolaj Golub }
800*ddb9b612SMikolaj Golub 
801*ddb9b612SMikolaj Golub /*
802*ddb9b612SMikolaj Golub  * End the section padding to the specified length with the specified
803*ddb9b612SMikolaj Golub  * character.
804*ddb9b612SMikolaj Golub  */
805*ddb9b612SMikolaj Golub ssize_t
806*ddb9b612SMikolaj Golub sbuf_end_section(struct sbuf *s, ssize_t old_len, size_t pad, int c)
807*ddb9b612SMikolaj Golub {
808*ddb9b612SMikolaj Golub 	ssize_t len;
809*ddb9b612SMikolaj Golub 
810*ddb9b612SMikolaj Golub 	assert_sbuf_integrity(s);
811*ddb9b612SMikolaj Golub 	assert_sbuf_state(s, 0);
812*ddb9b612SMikolaj Golub 	KASSERT(SBUF_ISSECTION(s),
813*ddb9b612SMikolaj Golub 	    ("attempt to end a section when not in a section"));
814*ddb9b612SMikolaj Golub 
815*ddb9b612SMikolaj Golub 	if (pad > 1) {
816*ddb9b612SMikolaj Golub 		len = roundup(s->s_sect_len, pad) - s->s_sect_len;
817*ddb9b612SMikolaj Golub 		for (; s->s_error == 0 && len > 0; len--)
818*ddb9b612SMikolaj Golub 			sbuf_put_byte(s, c);
819*ddb9b612SMikolaj Golub 	}
820*ddb9b612SMikolaj Golub 	len = s->s_sect_len;
821*ddb9b612SMikolaj Golub 	if (old_len == -1) {
822*ddb9b612SMikolaj Golub 		s->s_sect_len = 0;
823*ddb9b612SMikolaj Golub 		SBUF_CLEARFLAG(s, SBUF_INSECTION);
824*ddb9b612SMikolaj Golub 	} else {
825*ddb9b612SMikolaj Golub 		s->s_sect_len += old_len;
826*ddb9b612SMikolaj Golub 	}
827*ddb9b612SMikolaj Golub 	if (s->s_error != 0)
828*ddb9b612SMikolaj Golub 		return (-1);
829*ddb9b612SMikolaj Golub 	return (len);
830*ddb9b612SMikolaj Golub }
831