xref: /freebsd/sys/kern/subr_sbuf.c (revision 2f1c4e0ebf68520d093658c3f8b10191f81e43b9)
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>
38*2f1c4e0eSConrad Meyer #include <sys/limits.h>
3960ec4130SDag-Erling Smørgrav #include <sys/malloc.h>
4060ec4130SDag-Erling Smørgrav #include <sys/systm.h>
415b6db477SDag-Erling Smørgrav #include <sys/uio.h>
4260ec4130SDag-Erling Smørgrav #include <machine/stdarg.h>
433393f8daSKenneth D. Merry #else /* _KERNEL */
445b6db477SDag-Erling Smørgrav #include <ctype.h>
454351ba27SMatthew D Fleming #include <errno.h>
46*2f1c4e0eSConrad Meyer #include <limits.h>
473393f8daSKenneth D. Merry #include <stdarg.h>
487195eb40SKelly Yancey #include <stdio.h>
499fa416caSJonathan Lemon #include <stdlib.h>
507195eb40SKelly Yancey #include <string.h>
513393f8daSKenneth D. Merry #endif /* _KERNEL */
5260ec4130SDag-Erling Smørgrav 
535b6db477SDag-Erling Smørgrav #include <sys/sbuf.h>
545b6db477SDag-Erling Smørgrav 
553393f8daSKenneth D. Merry #ifdef _KERNEL
56c711aea6SPoul-Henning Kamp static MALLOC_DEFINE(M_SBUF, "sbuf", "string buffers");
570f7bc112SDag-Erling Smørgrav #define	SBMALLOC(size)		malloc(size, M_SBUF, M_WAITOK|M_ZERO)
583393f8daSKenneth D. Merry #define	SBFREE(buf)		free(buf, M_SBUF)
593393f8daSKenneth D. Merry #else /* _KERNEL */
603393f8daSKenneth D. Merry #define	KASSERT(e, m)
610f7bc112SDag-Erling Smørgrav #define	SBMALLOC(size)		calloc(1, size)
623393f8daSKenneth D. Merry #define	SBFREE(buf)		free(buf)
633393f8daSKenneth D. Merry #endif /* _KERNEL */
6460ec4130SDag-Erling Smørgrav 
654dc14139SDag-Erling Smørgrav /*
664dc14139SDag-Erling Smørgrav  * Predicates
674dc14139SDag-Erling Smørgrav  */
684dc14139SDag-Erling Smørgrav #define	SBUF_ISDYNAMIC(s)	((s)->s_flags & SBUF_DYNAMIC)
69d6479358SDag-Erling Smørgrav #define	SBUF_ISDYNSTRUCT(s)	((s)->s_flags & SBUF_DYNSTRUCT)
704dc14139SDag-Erling Smørgrav #define	SBUF_ISFINISHED(s)	((s)->s_flags & SBUF_FINISHED)
714dc14139SDag-Erling Smørgrav #define	SBUF_HASROOM(s)		((s)->s_len < (s)->s_size - 1)
72eb05ee7aSPoul-Henning Kamp #define	SBUF_FREESPACE(s)	((s)->s_size - ((s)->s_len + 1))
737195eb40SKelly Yancey #define	SBUF_CANEXTEND(s)	((s)->s_flags & SBUF_AUTOEXTEND)
74ddb9b612SMikolaj Golub #define	SBUF_ISSECTION(s)	((s)->s_flags & SBUF_INSECTION)
758d5628fdSIan Lepore #define	SBUF_NULINCLUDED(s)	((s)->s_flags & SBUF_INCLUDENUL)
764dc14139SDag-Erling Smørgrav 
774dc14139SDag-Erling Smørgrav /*
784dc14139SDag-Erling Smørgrav  * Set / clear flags
794dc14139SDag-Erling Smørgrav  */
804dc14139SDag-Erling Smørgrav #define	SBUF_SETFLAG(s, f)	do { (s)->s_flags |= (f); } while (0)
814dc14139SDag-Erling Smørgrav #define	SBUF_CLEARFLAG(s, f)	do { (s)->s_flags &= ~(f); } while (0)
824dc14139SDag-Erling Smørgrav 
83612d9391SIan Lepore #define	SBUF_MINSIZE		 2		/* Min is 1 byte + nulterm. */
847195eb40SKelly Yancey #define	SBUF_MINEXTENDSIZE	16		/* Should be power of 2. */
85eb05ee7aSPoul-Henning Kamp 
86eb05ee7aSPoul-Henning Kamp #ifdef PAGE_SIZE
877195eb40SKelly Yancey #define	SBUF_MAXEXTENDSIZE	PAGE_SIZE
887195eb40SKelly Yancey #define	SBUF_MAXEXTENDINCR	PAGE_SIZE
89eb05ee7aSPoul-Henning Kamp #else
90eb05ee7aSPoul-Henning Kamp #define	SBUF_MAXEXTENDSIZE	4096
91eb05ee7aSPoul-Henning Kamp #define	SBUF_MAXEXTENDINCR	4096
92eb05ee7aSPoul-Henning Kamp #endif
937195eb40SKelly Yancey 
944dc14139SDag-Erling Smørgrav /*
954dc14139SDag-Erling Smørgrav  * Debugging support
964dc14139SDag-Erling Smørgrav  */
973393f8daSKenneth D. Merry #if defined(_KERNEL) && defined(INVARIANTS)
98546d7890SDag-Erling Smørgrav 
9960ec4130SDag-Erling Smørgrav static void
100a5e7c7daSPeter Wemm _assert_sbuf_integrity(const char *fun, struct sbuf *s)
10160ec4130SDag-Erling Smørgrav {
102546d7890SDag-Erling Smørgrav 
10360ec4130SDag-Erling Smørgrav 	KASSERT(s != NULL,
104cab5b963SDag-Erling Smørgrav 	    ("%s called with a NULL sbuf pointer", fun));
10560ec4130SDag-Erling Smørgrav 	KASSERT(s->s_buf != NULL,
1067195eb40SKelly Yancey 	    ("%s called with uninitialized or corrupt sbuf", fun));
1078d5628fdSIan Lepore         if (SBUF_ISFINISHED(s) && SBUF_NULINCLUDED(s)) {
1088d5628fdSIan Lepore 		KASSERT(s->s_len <= s->s_size,
1098d5628fdSIan Lepore 		    ("wrote past end of sbuf (%jd >= %jd)",
1108d5628fdSIan Lepore 		    (intmax_t)s->s_len, (intmax_t)s->s_size));
1118d5628fdSIan Lepore 	} else {
11260ec4130SDag-Erling Smørgrav 		KASSERT(s->s_len < s->s_size,
11371c2bc5cSPoul-Henning Kamp 		    ("wrote past end of sbuf (%jd >= %jd)",
11471c2bc5cSPoul-Henning Kamp 		    (intmax_t)s->s_len, (intmax_t)s->s_size));
11560ec4130SDag-Erling Smørgrav 	}
1168d5628fdSIan Lepore }
11760ec4130SDag-Erling Smørgrav 
11860ec4130SDag-Erling Smørgrav static void
119a5e7c7daSPeter Wemm _assert_sbuf_state(const char *fun, struct sbuf *s, int state)
12060ec4130SDag-Erling Smørgrav {
121546d7890SDag-Erling Smørgrav 
12260ec4130SDag-Erling Smørgrav 	KASSERT((s->s_flags & SBUF_FINISHED) == state,
123cab5b963SDag-Erling Smørgrav 	    ("%s called with %sfinished or corrupt sbuf", fun,
12460ec4130SDag-Erling Smørgrav 	    (state ? "un" : "")));
12560ec4130SDag-Erling Smørgrav }
126546d7890SDag-Erling Smørgrav 
127a48740b6SDavid E. O'Brien #define	assert_sbuf_integrity(s) _assert_sbuf_integrity(__func__, (s))
128a48740b6SDavid E. O'Brien #define	assert_sbuf_state(s, i)	 _assert_sbuf_state(__func__, (s), (i))
129546d7890SDag-Erling Smørgrav 
1303393f8daSKenneth D. Merry #else /* _KERNEL && INVARIANTS */
131546d7890SDag-Erling Smørgrav 
13260ec4130SDag-Erling Smørgrav #define	assert_sbuf_integrity(s) do { } while (0)
13360ec4130SDag-Erling Smørgrav #define	assert_sbuf_state(s, i)	 do { } while (0)
134546d7890SDag-Erling Smørgrav 
1353393f8daSKenneth D. Merry #endif /* _KERNEL && INVARIANTS */
13660ec4130SDag-Erling Smørgrav 
137181ff3d5SMatthew D Fleming #ifdef CTASSERT
138f4bafab8SMatthew D Fleming CTASSERT(powerof2(SBUF_MAXEXTENDSIZE));
139f4bafab8SMatthew D Fleming CTASSERT(powerof2(SBUF_MAXEXTENDINCR));
140969292fbSMatthew D Fleming #endif
141f4bafab8SMatthew D Fleming 
1427195eb40SKelly Yancey static int
1437195eb40SKelly Yancey sbuf_extendsize(int size)
1447195eb40SKelly Yancey {
1457195eb40SKelly Yancey 	int newsize;
1467195eb40SKelly Yancey 
147f4bafab8SMatthew D Fleming 	if (size < (int)SBUF_MAXEXTENDSIZE) {
1487195eb40SKelly Yancey 		newsize = SBUF_MINEXTENDSIZE;
149f4bafab8SMatthew D Fleming 		while (newsize < size)
1507195eb40SKelly Yancey 			newsize *= 2;
151f4bafab8SMatthew D Fleming 	} else {
152f4bafab8SMatthew D Fleming 		newsize = roundup2(size, SBUF_MAXEXTENDINCR);
1537195eb40SKelly Yancey 	}
154f5a5dc5dSMatthew D Fleming 	KASSERT(newsize >= size, ("%s: %d < %d\n", __func__, newsize, size));
1557195eb40SKelly Yancey 	return (newsize);
1567195eb40SKelly Yancey }
1577195eb40SKelly Yancey 
1587195eb40SKelly Yancey /*
1597195eb40SKelly Yancey  * Extend an sbuf.
1607195eb40SKelly Yancey  */
1617195eb40SKelly Yancey static int
1627195eb40SKelly Yancey sbuf_extend(struct sbuf *s, int addlen)
1637195eb40SKelly Yancey {
1647195eb40SKelly Yancey 	char *newbuf;
165adecd05bSPietro Cerutti 	int newsize;
1667195eb40SKelly Yancey 
1677195eb40SKelly Yancey 	if (!SBUF_CANEXTEND(s))
1687195eb40SKelly Yancey 		return (-1);
1697195eb40SKelly Yancey 	newsize = sbuf_extendsize(s->s_size + addlen);
170546d7890SDag-Erling Smørgrav 	newbuf = SBMALLOC(newsize);
1717195eb40SKelly Yancey 	if (newbuf == NULL)
1727195eb40SKelly Yancey 		return (-1);
173384bf94cSPoul-Henning Kamp 	memcpy(newbuf, s->s_buf, s->s_size);
1747195eb40SKelly Yancey 	if (SBUF_ISDYNAMIC(s))
1757195eb40SKelly Yancey 		SBFREE(s->s_buf);
1767195eb40SKelly Yancey 	else
1777195eb40SKelly Yancey 		SBUF_SETFLAG(s, SBUF_DYNAMIC);
1787195eb40SKelly Yancey 	s->s_buf = newbuf;
1797195eb40SKelly Yancey 	s->s_size = newsize;
1807195eb40SKelly Yancey 	return (0);
1817195eb40SKelly Yancey }
1827195eb40SKelly Yancey 
18360ec4130SDag-Erling Smørgrav /*
184384bf94cSPoul-Henning Kamp  * Initialize the internals of an sbuf.
185384bf94cSPoul-Henning Kamp  * If buf is non-NULL, it points to a static or already-allocated string
186384bf94cSPoul-Henning Kamp  * big enough to hold at least length characters.
187384bf94cSPoul-Henning Kamp  */
188384bf94cSPoul-Henning Kamp static struct sbuf *
189adecd05bSPietro Cerutti sbuf_newbuf(struct sbuf *s, char *buf, int length, int flags)
190384bf94cSPoul-Henning Kamp {
191384bf94cSPoul-Henning Kamp 
192384bf94cSPoul-Henning Kamp 	memset(s, 0, sizeof(*s));
193384bf94cSPoul-Henning Kamp 	s->s_flags = flags;
194384bf94cSPoul-Henning Kamp 	s->s_size = length;
195384bf94cSPoul-Henning Kamp 	s->s_buf = buf;
196384bf94cSPoul-Henning Kamp 
197384bf94cSPoul-Henning Kamp 	if ((s->s_flags & SBUF_AUTOEXTEND) == 0) {
198612d9391SIan Lepore 		KASSERT(s->s_size >= SBUF_MINSIZE,
199612d9391SIan Lepore 		    ("attempt to create an sbuf smaller than %d bytes",
200612d9391SIan Lepore 		    SBUF_MINSIZE));
201384bf94cSPoul-Henning Kamp 	}
202384bf94cSPoul-Henning Kamp 
203384bf94cSPoul-Henning Kamp 	if (s->s_buf != NULL)
204384bf94cSPoul-Henning Kamp 		return (s);
205384bf94cSPoul-Henning Kamp 
206384bf94cSPoul-Henning Kamp 	if ((flags & SBUF_AUTOEXTEND) != 0)
207384bf94cSPoul-Henning Kamp 		s->s_size = sbuf_extendsize(s->s_size);
208384bf94cSPoul-Henning Kamp 
209384bf94cSPoul-Henning Kamp 	s->s_buf = SBMALLOC(s->s_size);
210384bf94cSPoul-Henning Kamp 	if (s->s_buf == NULL)
211384bf94cSPoul-Henning Kamp 		return (NULL);
212384bf94cSPoul-Henning Kamp 	SBUF_SETFLAG(s, SBUF_DYNAMIC);
213384bf94cSPoul-Henning Kamp 	return (s);
214384bf94cSPoul-Henning Kamp }
215384bf94cSPoul-Henning Kamp 
216384bf94cSPoul-Henning Kamp /*
21760ec4130SDag-Erling Smørgrav  * Initialize an sbuf.
21860ec4130SDag-Erling Smørgrav  * If buf is non-NULL, it points to a static or already-allocated string
21960ec4130SDag-Erling Smørgrav  * big enough to hold at least length characters.
22060ec4130SDag-Erling Smørgrav  */
221d6479358SDag-Erling Smørgrav struct sbuf *
2224dc14139SDag-Erling Smørgrav sbuf_new(struct sbuf *s, char *buf, int length, int flags)
22360ec4130SDag-Erling Smørgrav {
224546d7890SDag-Erling Smørgrav 
2254dc14139SDag-Erling Smørgrav 	KASSERT(length >= 0,
2264dc14139SDag-Erling Smørgrav 	    ("attempt to create an sbuf of negative length (%d)", length));
2277195eb40SKelly Yancey 	KASSERT((flags & ~SBUF_USRFLAGMSK) == 0,
2287195eb40SKelly Yancey 	    ("%s called with invalid flags", __func__));
22960ec4130SDag-Erling Smørgrav 
2307195eb40SKelly Yancey 	flags &= SBUF_USRFLAGMSK;
231384bf94cSPoul-Henning Kamp 	if (s != NULL)
232384bf94cSPoul-Henning Kamp 		return (sbuf_newbuf(s, buf, length, flags));
233384bf94cSPoul-Henning Kamp 
234546d7890SDag-Erling Smørgrav 	s = SBMALLOC(sizeof(*s));
235d6479358SDag-Erling Smørgrav 	if (s == NULL)
236d6479358SDag-Erling Smørgrav 		return (NULL);
237384bf94cSPoul-Henning Kamp 	if (sbuf_newbuf(s, buf, length, flags) == NULL) {
238d6479358SDag-Erling Smørgrav 		SBFREE(s);
239d6479358SDag-Erling Smørgrav 		return (NULL);
240d6479358SDag-Erling Smørgrav 	}
241384bf94cSPoul-Henning Kamp 	SBUF_SETFLAG(s, SBUF_DYNSTRUCT);
242d6479358SDag-Erling Smørgrav 	return (s);
24360ec4130SDag-Erling Smørgrav }
24460ec4130SDag-Erling Smørgrav 
2455b6db477SDag-Erling Smørgrav #ifdef _KERNEL
2465b6db477SDag-Erling Smørgrav /*
2475b6db477SDag-Erling Smørgrav  * Create an sbuf with uio data
2485b6db477SDag-Erling Smørgrav  */
2495b6db477SDag-Erling Smørgrav struct sbuf *
2505b6db477SDag-Erling Smørgrav sbuf_uionew(struct sbuf *s, struct uio *uio, int *error)
2515b6db477SDag-Erling Smørgrav {
252546d7890SDag-Erling Smørgrav 
2535b6db477SDag-Erling Smørgrav 	KASSERT(uio != NULL,
254a48740b6SDavid E. O'Brien 	    ("%s called with NULL uio pointer", __func__));
2555b6db477SDag-Erling Smørgrav 	KASSERT(error != NULL,
256a48740b6SDavid E. O'Brien 	    ("%s called with NULL error pointer", __func__));
2575b6db477SDag-Erling Smørgrav 
2585b6db477SDag-Erling Smørgrav 	s = sbuf_new(s, NULL, uio->uio_resid + 1, 0);
2595b6db477SDag-Erling Smørgrav 	if (s == NULL) {
2605b6db477SDag-Erling Smørgrav 		*error = ENOMEM;
2615b6db477SDag-Erling Smørgrav 		return (NULL);
2625b6db477SDag-Erling Smørgrav 	}
2635b6db477SDag-Erling Smørgrav 	*error = uiomove(s->s_buf, uio->uio_resid, uio);
2645b6db477SDag-Erling Smørgrav 	if (*error != 0) {
2655b6db477SDag-Erling Smørgrav 		sbuf_delete(s);
2665b6db477SDag-Erling Smørgrav 		return (NULL);
2675b6db477SDag-Erling Smørgrav 	}
2685b6db477SDag-Erling Smørgrav 	s->s_len = s->s_size - 1;
269ddb9b612SMikolaj Golub 	if (SBUF_ISSECTION(s))
270ddb9b612SMikolaj Golub 		s->s_sect_len = s->s_size - 1;
2715b6db477SDag-Erling Smørgrav 	*error = 0;
2725b6db477SDag-Erling Smørgrav 	return (s);
2735b6db477SDag-Erling Smørgrav }
2745b6db477SDag-Erling Smørgrav #endif
2755b6db477SDag-Erling Smørgrav 
276f4d28142SIan Lepore int
277f4d28142SIan Lepore sbuf_get_flags(struct sbuf *s)
278f4d28142SIan Lepore {
279f4d28142SIan Lepore 
280f4d28142SIan Lepore 	return (s->s_flags & SBUF_USRFLAGMSK);
281f4d28142SIan Lepore }
282f4d28142SIan Lepore 
283f4d28142SIan Lepore void
284f4d28142SIan Lepore sbuf_clear_flags(struct sbuf *s, int flags)
285f4d28142SIan Lepore {
286f4d28142SIan Lepore 
287f4d28142SIan Lepore 	s->s_flags &= ~(flags & SBUF_USRFLAGMSK);
288f4d28142SIan Lepore }
289f4d28142SIan Lepore 
290f4d28142SIan Lepore void
291f4d28142SIan Lepore sbuf_set_flags(struct sbuf *s, int flags)
292f4d28142SIan Lepore {
293f4d28142SIan Lepore 
294f4d28142SIan Lepore 
295f4d28142SIan Lepore 	s->s_flags |= (flags & SBUF_USRFLAGMSK);
296f4d28142SIan Lepore }
297f4d28142SIan Lepore 
29860ec4130SDag-Erling Smørgrav /*
2997195eb40SKelly Yancey  * Clear an sbuf and reset its position.
3004dc14139SDag-Erling Smørgrav  */
3014dc14139SDag-Erling Smørgrav void
3024dc14139SDag-Erling Smørgrav sbuf_clear(struct sbuf *s)
3034dc14139SDag-Erling Smørgrav {
304546d7890SDag-Erling Smørgrav 
3054dc14139SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
3069fa2ef3dSDag-Erling Smørgrav 	/* don't care if it's finished or not */
3074dc14139SDag-Erling Smørgrav 
3084dc14139SDag-Erling Smørgrav 	SBUF_CLEARFLAG(s, SBUF_FINISHED);
3094351ba27SMatthew D Fleming 	s->s_error = 0;
3104dc14139SDag-Erling Smørgrav 	s->s_len = 0;
311ddb9b612SMikolaj Golub 	s->s_sect_len = 0;
3124dc14139SDag-Erling Smørgrav }
3134dc14139SDag-Erling Smørgrav 
3144dc14139SDag-Erling Smørgrav /*
3157195eb40SKelly Yancey  * Set the sbuf's end position to an arbitrary value.
3167195eb40SKelly Yancey  * Effectively truncates the sbuf at the new position.
31760ec4130SDag-Erling Smørgrav  */
31860ec4130SDag-Erling Smørgrav int
319adecd05bSPietro Cerutti sbuf_setpos(struct sbuf *s, ssize_t pos)
32060ec4130SDag-Erling Smørgrav {
321546d7890SDag-Erling Smørgrav 
32260ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
32360ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
32460ec4130SDag-Erling Smørgrav 
325adecd05bSPietro Cerutti 	KASSERT(pos >= 0,
326adecd05bSPietro Cerutti 	    ("attempt to seek to a negative position (%jd)", (intmax_t)pos));
32760ec4130SDag-Erling Smørgrav 	KASSERT(pos < s->s_size,
32871c2bc5cSPoul-Henning Kamp 	    ("attempt to seek past end of sbuf (%jd >= %jd)",
32971c2bc5cSPoul-Henning Kamp 	    (intmax_t)pos, (intmax_t)s->s_size));
330ddb9b612SMikolaj Golub 	KASSERT(!SBUF_ISSECTION(s),
331ddb9b612SMikolaj Golub 	    ("attempt to seek when in a section"));
33260ec4130SDag-Erling Smørgrav 
333adecd05bSPietro Cerutti 	if (pos < 0 || pos > s->s_len)
33460ec4130SDag-Erling Smørgrav 		return (-1);
33560ec4130SDag-Erling Smørgrav 	s->s_len = pos;
33660ec4130SDag-Erling Smørgrav 	return (0);
33760ec4130SDag-Erling Smørgrav }
33860ec4130SDag-Erling Smørgrav 
33960ec4130SDag-Erling Smørgrav /*
3404351ba27SMatthew D Fleming  * Set up a drain function and argument on an sbuf to flush data to
3414351ba27SMatthew D Fleming  * when the sbuf buffer overflows.
3424351ba27SMatthew D Fleming  */
3434351ba27SMatthew D Fleming void
3444351ba27SMatthew D Fleming sbuf_set_drain(struct sbuf *s, sbuf_drain_func *func, void *ctx)
3454351ba27SMatthew D Fleming {
3464351ba27SMatthew D Fleming 
3474351ba27SMatthew D Fleming 	assert_sbuf_state(s, 0);
3484351ba27SMatthew D Fleming 	assert_sbuf_integrity(s);
3494351ba27SMatthew D Fleming 	KASSERT(func == s->s_drain_func || s->s_len == 0,
3504351ba27SMatthew D Fleming 	    ("Cannot change drain to %p on non-empty sbuf %p", func, s));
3514351ba27SMatthew D Fleming 	s->s_drain_func = func;
3524351ba27SMatthew D Fleming 	s->s_drain_arg = ctx;
3534351ba27SMatthew D Fleming }
3544351ba27SMatthew D Fleming 
3554351ba27SMatthew D Fleming /*
3564351ba27SMatthew D Fleming  * Call the drain and process the return.
3574351ba27SMatthew D Fleming  */
3584351ba27SMatthew D Fleming static int
3594351ba27SMatthew D Fleming sbuf_drain(struct sbuf *s)
3604351ba27SMatthew D Fleming {
3614351ba27SMatthew D Fleming 	int len;
3624351ba27SMatthew D Fleming 
3634351ba27SMatthew D Fleming 	KASSERT(s->s_len > 0, ("Shouldn't drain empty sbuf %p", s));
3644d369413SMatthew D Fleming 	KASSERT(s->s_error == 0, ("Called %s with error on %p", __func__, s));
3654351ba27SMatthew D Fleming 	len = s->s_drain_func(s->s_drain_arg, s->s_buf, s->s_len);
3664351ba27SMatthew D Fleming 	if (len < 0) {
3674351ba27SMatthew D Fleming 		s->s_error = -len;
3684351ba27SMatthew D Fleming 		return (s->s_error);
3694351ba27SMatthew D Fleming 	}
3704e657159SMatthew D Fleming 	KASSERT(len > 0 && len <= s->s_len,
3714e657159SMatthew D Fleming 	    ("Bad drain amount %d for sbuf %p", len, s));
3724351ba27SMatthew D Fleming 	s->s_len -= len;
3734351ba27SMatthew D Fleming 	/*
3744351ba27SMatthew D Fleming 	 * Fast path for the expected case where all the data was
3754351ba27SMatthew D Fleming 	 * drained.
3764351ba27SMatthew D Fleming 	 */
3774351ba27SMatthew D Fleming 	if (s->s_len == 0)
3784351ba27SMatthew D Fleming 		return (0);
3794351ba27SMatthew D Fleming 	/*
3804351ba27SMatthew D Fleming 	 * Move the remaining characters to the beginning of the
3814351ba27SMatthew D Fleming 	 * string.
3824351ba27SMatthew D Fleming 	 */
3834351ba27SMatthew D Fleming 	memmove(s->s_buf, s->s_buf + len, s->s_len);
3844351ba27SMatthew D Fleming 	return (0);
3854351ba27SMatthew D Fleming }
3864351ba27SMatthew D Fleming 
3874351ba27SMatthew D Fleming /*
388*2f1c4e0eSConrad Meyer  * Append bytes to an sbuf.  This is the core function for appending
38901f6f5fcSMatthew D Fleming  * to an sbuf and is the main place that deals with extending the
39001f6f5fcSMatthew D Fleming  * buffer and marking overflow.
39101f6f5fcSMatthew D Fleming  */
39201f6f5fcSMatthew D Fleming static void
393*2f1c4e0eSConrad Meyer sbuf_put_bytes(struct sbuf *s, const char *buf, size_t len)
39401f6f5fcSMatthew D Fleming {
395*2f1c4e0eSConrad Meyer 	size_t n;
39601f6f5fcSMatthew D Fleming 
39701f6f5fcSMatthew D Fleming 	assert_sbuf_integrity(s);
39801f6f5fcSMatthew D Fleming 	assert_sbuf_state(s, 0);
39901f6f5fcSMatthew D Fleming 
4004d369413SMatthew D Fleming 	if (s->s_error != 0)
40101f6f5fcSMatthew D Fleming 		return;
402*2f1c4e0eSConrad Meyer 	while (len > 0) {
40301f6f5fcSMatthew D Fleming 		if (SBUF_FREESPACE(s) <= 0) {
4044351ba27SMatthew D Fleming 			/*
4054351ba27SMatthew D Fleming 			 * If there is a drain, use it, otherwise extend the
4064351ba27SMatthew D Fleming 			 * buffer.
4074351ba27SMatthew D Fleming 			 */
4084351ba27SMatthew D Fleming 			if (s->s_drain_func != NULL)
4094351ba27SMatthew D Fleming 				(void)sbuf_drain(s);
410*2f1c4e0eSConrad Meyer 			else if (sbuf_extend(s, len > INT_MAX ? INT_MAX : len)
411*2f1c4e0eSConrad Meyer 			    < 0)
4124d369413SMatthew D Fleming 				s->s_error = ENOMEM;
4134d369413SMatthew D Fleming 			if (s->s_error != 0)
41401f6f5fcSMatthew D Fleming 				return;
41501f6f5fcSMatthew D Fleming 		}
416*2f1c4e0eSConrad Meyer 		n = SBUF_FREESPACE(s);
417*2f1c4e0eSConrad Meyer 		if (len < n)
418*2f1c4e0eSConrad Meyer 			n = len;
419*2f1c4e0eSConrad Meyer 		memcpy(&s->s_buf[s->s_len], buf, n);
420*2f1c4e0eSConrad Meyer 		s->s_len += n;
421ddb9b612SMikolaj Golub 		if (SBUF_ISSECTION(s))
422*2f1c4e0eSConrad Meyer 			s->s_sect_len += n;
423*2f1c4e0eSConrad Meyer 		len -= n;
424*2f1c4e0eSConrad Meyer 		buf += n;
425*2f1c4e0eSConrad Meyer 	}
426*2f1c4e0eSConrad Meyer }
427*2f1c4e0eSConrad Meyer 
428*2f1c4e0eSConrad Meyer static void
429*2f1c4e0eSConrad Meyer sbuf_put_byte(struct sbuf *s, char c)
430*2f1c4e0eSConrad Meyer {
431*2f1c4e0eSConrad Meyer 
432*2f1c4e0eSConrad Meyer 	sbuf_put_bytes(s, &c, 1);
43301f6f5fcSMatthew D Fleming }
43401f6f5fcSMatthew D Fleming 
43501f6f5fcSMatthew D Fleming /*
436b0def2b5SDag-Erling Smørgrav  * Append a byte string to an sbuf.
437b0def2b5SDag-Erling Smørgrav  */
438b0def2b5SDag-Erling Smørgrav int
439520df276SDag-Erling Smørgrav sbuf_bcat(struct sbuf *s, const void *buf, size_t len)
440b0def2b5SDag-Erling Smørgrav {
441d751f0a9SDag-Erling Smørgrav 
442*2f1c4e0eSConrad Meyer 	sbuf_put_bytes(s, buf, len);
4434d369413SMatthew D Fleming 	if (s->s_error != 0)
444b0def2b5SDag-Erling Smørgrav 		return (-1);
445b0def2b5SDag-Erling Smørgrav 	return (0);
446b0def2b5SDag-Erling Smørgrav }
447b0def2b5SDag-Erling Smørgrav 
448b0def2b5SDag-Erling Smørgrav #ifdef _KERNEL
449b0def2b5SDag-Erling Smørgrav /*
450b0def2b5SDag-Erling Smørgrav  * Copy a byte string from userland into an sbuf.
451b0def2b5SDag-Erling Smørgrav  */
452b0def2b5SDag-Erling Smørgrav int
453b0def2b5SDag-Erling Smørgrav sbuf_bcopyin(struct sbuf *s, const void *uaddr, size_t len)
454b0def2b5SDag-Erling Smørgrav {
455546d7890SDag-Erling Smørgrav 
456b0def2b5SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
457b0def2b5SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
4584351ba27SMatthew D Fleming 	KASSERT(s->s_drain_func == NULL,
4594351ba27SMatthew D Fleming 	    ("Nonsensical copyin to sbuf %p with a drain", s));
460b0def2b5SDag-Erling Smørgrav 
4614d369413SMatthew D Fleming 	if (s->s_error != 0)
462b0def2b5SDag-Erling Smørgrav 		return (-1);
463b0def2b5SDag-Erling Smørgrav 	if (len == 0)
464b0def2b5SDag-Erling Smørgrav 		return (0);
4657195eb40SKelly Yancey 	if (len > SBUF_FREESPACE(s)) {
4667195eb40SKelly Yancey 		sbuf_extend(s, len - SBUF_FREESPACE(s));
467c05dbe7aSMatthew D Fleming 		if (SBUF_FREESPACE(s) < len)
468c05dbe7aSMatthew D Fleming 			len = SBUF_FREESPACE(s);
4697195eb40SKelly Yancey 	}
470e3b37322SDag-Erling Smørgrav 	if (copyin(uaddr, s->s_buf + s->s_len, len) != 0)
471e3b37322SDag-Erling Smørgrav 		return (-1);
472fe463496SDag-Erling Smørgrav 	s->s_len += len;
473b0def2b5SDag-Erling Smørgrav 
474b0def2b5SDag-Erling Smørgrav 	return (0);
475b0def2b5SDag-Erling Smørgrav }
476b0def2b5SDag-Erling Smørgrav #endif
477b0def2b5SDag-Erling Smørgrav 
478b0def2b5SDag-Erling Smørgrav /*
479b0def2b5SDag-Erling Smørgrav  * Copy a byte string into an sbuf.
480b0def2b5SDag-Erling Smørgrav  */
481b0def2b5SDag-Erling Smørgrav int
482520df276SDag-Erling Smørgrav sbuf_bcpy(struct sbuf *s, const void *buf, size_t len)
483b0def2b5SDag-Erling Smørgrav {
484546d7890SDag-Erling Smørgrav 
485b0def2b5SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
486b0def2b5SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
487b0def2b5SDag-Erling Smørgrav 
488b0def2b5SDag-Erling Smørgrav 	sbuf_clear(s);
489520df276SDag-Erling Smørgrav 	return (sbuf_bcat(s, buf, len));
490b0def2b5SDag-Erling Smørgrav }
491b0def2b5SDag-Erling Smørgrav 
492b0def2b5SDag-Erling Smørgrav /*
49360ec4130SDag-Erling Smørgrav  * Append a string to an sbuf.
49460ec4130SDag-Erling Smørgrav  */
49560ec4130SDag-Erling Smørgrav int
4963393f8daSKenneth D. Merry sbuf_cat(struct sbuf *s, const char *str)
49760ec4130SDag-Erling Smørgrav {
498*2f1c4e0eSConrad Meyer 	size_t n;
499546d7890SDag-Erling Smørgrav 
500*2f1c4e0eSConrad Meyer 	n = strlen(str);
501*2f1c4e0eSConrad Meyer 	sbuf_put_bytes(s, str, n);
5024d369413SMatthew D Fleming 	if (s->s_error != 0)
50360ec4130SDag-Erling Smørgrav 		return (-1);
50460ec4130SDag-Erling Smørgrav 	return (0);
50560ec4130SDag-Erling Smørgrav }
50660ec4130SDag-Erling Smørgrav 
507b0def2b5SDag-Erling Smørgrav #ifdef _KERNEL
508b0def2b5SDag-Erling Smørgrav /*
5097195eb40SKelly Yancey  * Append a string from userland to an sbuf.
510b0def2b5SDag-Erling Smørgrav  */
511b0def2b5SDag-Erling Smørgrav int
512b0def2b5SDag-Erling Smørgrav sbuf_copyin(struct sbuf *s, const void *uaddr, size_t len)
513b0def2b5SDag-Erling Smørgrav {
514b0def2b5SDag-Erling Smørgrav 	size_t done;
515b0def2b5SDag-Erling Smørgrav 
516b0def2b5SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
517b0def2b5SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
5184351ba27SMatthew D Fleming 	KASSERT(s->s_drain_func == NULL,
5194351ba27SMatthew D Fleming 	    ("Nonsensical copyin to sbuf %p with a drain", s));
520b0def2b5SDag-Erling Smørgrav 
5214d369413SMatthew D Fleming 	if (s->s_error != 0)
522b0def2b5SDag-Erling Smørgrav 		return (-1);
523b0def2b5SDag-Erling Smørgrav 
5247195eb40SKelly Yancey 	if (len == 0)
5257195eb40SKelly Yancey 		len = SBUF_FREESPACE(s);	/* XXX return 0? */
5267195eb40SKelly Yancey 	if (len > SBUF_FREESPACE(s)) {
5277195eb40SKelly Yancey 		sbuf_extend(s, len);
528c05dbe7aSMatthew D Fleming 		if (SBUF_FREESPACE(s) < len)
529c05dbe7aSMatthew D Fleming 			len = SBUF_FREESPACE(s);
5307195eb40SKelly Yancey 	}
531b0def2b5SDag-Erling Smørgrav 	switch (copyinstr(uaddr, s->s_buf + s->s_len, len + 1, &done)) {
532b0def2b5SDag-Erling Smørgrav 	case ENAMETOOLONG:
5334d369413SMatthew D Fleming 		s->s_error = ENOMEM;
534b0def2b5SDag-Erling Smørgrav 		/* fall through */
535b0def2b5SDag-Erling Smørgrav 	case 0:
536b0def2b5SDag-Erling Smørgrav 		s->s_len += done - 1;
537ddb9b612SMikolaj Golub 		if (SBUF_ISSECTION(s))
538ddb9b612SMikolaj Golub 			s->s_sect_len += done - 1;
539b0def2b5SDag-Erling Smørgrav 		break;
540b0def2b5SDag-Erling Smørgrav 	default:
541b0def2b5SDag-Erling Smørgrav 		return (-1);	/* XXX */
542b0def2b5SDag-Erling Smørgrav 	}
543b0def2b5SDag-Erling Smørgrav 
54449091c48SPoul-Henning Kamp 	return (done);
545b0def2b5SDag-Erling Smørgrav }
546b0def2b5SDag-Erling Smørgrav #endif
547b0def2b5SDag-Erling Smørgrav 
54860ec4130SDag-Erling Smørgrav /*
54960ec4130SDag-Erling Smørgrav  * Copy a string into an sbuf.
55060ec4130SDag-Erling Smørgrav  */
55160ec4130SDag-Erling Smørgrav int
5523393f8daSKenneth D. Merry sbuf_cpy(struct sbuf *s, const char *str)
55360ec4130SDag-Erling Smørgrav {
554546d7890SDag-Erling Smørgrav 
55560ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
55660ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
55760ec4130SDag-Erling Smørgrav 
5584dc14139SDag-Erling Smørgrav 	sbuf_clear(s);
55960ec4130SDag-Erling Smørgrav 	return (sbuf_cat(s, str));
56060ec4130SDag-Erling Smørgrav }
56160ec4130SDag-Erling Smørgrav 
56260ec4130SDag-Erling Smørgrav /*
5637195eb40SKelly Yancey  * Format the given argument list and append the resulting string to an sbuf.
56460ec4130SDag-Erling Smørgrav  */
56501f6f5fcSMatthew D Fleming #ifdef _KERNEL
566eb05ee7aSPoul-Henning Kamp 
567eb05ee7aSPoul-Henning Kamp /*
568eb05ee7aSPoul-Henning Kamp  * Append a non-NUL character to an sbuf.  This prototype signature is
569eb05ee7aSPoul-Henning Kamp  * suitable for use with kvprintf(9).
570eb05ee7aSPoul-Henning Kamp  */
571eb05ee7aSPoul-Henning Kamp static void
572eb05ee7aSPoul-Henning Kamp sbuf_putc_func(int c, void *arg)
573eb05ee7aSPoul-Henning Kamp {
574eb05ee7aSPoul-Henning Kamp 
575eb05ee7aSPoul-Henning Kamp 	if (c != '\0')
576eb05ee7aSPoul-Henning Kamp 		sbuf_put_byte(arg, c);
577eb05ee7aSPoul-Henning Kamp }
578eb05ee7aSPoul-Henning Kamp 
57901f6f5fcSMatthew D Fleming int
58001f6f5fcSMatthew D Fleming sbuf_vprintf(struct sbuf *s, const char *fmt, va_list ap)
58101f6f5fcSMatthew D Fleming {
58201f6f5fcSMatthew D Fleming 
58301f6f5fcSMatthew D Fleming 	assert_sbuf_integrity(s);
58401f6f5fcSMatthew D Fleming 	assert_sbuf_state(s, 0);
58501f6f5fcSMatthew D Fleming 
58601f6f5fcSMatthew D Fleming 	KASSERT(fmt != NULL,
58701f6f5fcSMatthew D Fleming 	    ("%s called with a NULL format string", __func__));
58801f6f5fcSMatthew D Fleming 
58901f6f5fcSMatthew D Fleming 	(void)kvprintf(fmt, sbuf_putc_func, s, 10, ap);
5904d369413SMatthew D Fleming 	if (s->s_error != 0)
59101f6f5fcSMatthew D Fleming 		return (-1);
59201f6f5fcSMatthew D Fleming 	return (0);
59301f6f5fcSMatthew D Fleming }
59401f6f5fcSMatthew D Fleming #else /* !_KERNEL */
59560ec4130SDag-Erling Smørgrav int
5967195eb40SKelly Yancey sbuf_vprintf(struct sbuf *s, const char *fmt, va_list ap)
59760ec4130SDag-Erling Smørgrav {
598a9a0bbadSPeter Wemm 	va_list ap_copy;
599adecd05bSPietro Cerutti 	int error, len;
60060ec4130SDag-Erling Smørgrav 
60160ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
60260ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
60360ec4130SDag-Erling Smørgrav 
60460ec4130SDag-Erling Smørgrav 	KASSERT(fmt != NULL,
605a48740b6SDavid E. O'Brien 	    ("%s called with a NULL format string", __func__));
60660ec4130SDag-Erling Smørgrav 
6074d369413SMatthew D Fleming 	if (s->s_error != 0)
60860ec4130SDag-Erling Smørgrav 		return (-1);
60960ec4130SDag-Erling Smørgrav 
61001f6f5fcSMatthew D Fleming 	/*
61101f6f5fcSMatthew D Fleming 	 * For the moment, there is no way to get vsnprintf(3) to hand
61201f6f5fcSMatthew D Fleming 	 * back a character at a time, to push everything into
61301f6f5fcSMatthew D Fleming 	 * sbuf_putc_func() as was done for the kernel.
6144351ba27SMatthew D Fleming 	 *
6154351ba27SMatthew D Fleming 	 * In userspace, while drains are useful, there's generally
6164351ba27SMatthew D Fleming 	 * not a problem attempting to malloc(3) on out of space.  So
6174351ba27SMatthew D Fleming 	 * expand a userland sbuf if there is not enough room for the
6184351ba27SMatthew D Fleming 	 * data produced by sbuf_[v]printf(3).
61901f6f5fcSMatthew D Fleming 	 */
62001f6f5fcSMatthew D Fleming 
6214351ba27SMatthew D Fleming 	error = 0;
6227195eb40SKelly Yancey 	do {
623a9a0bbadSPeter Wemm 		va_copy(ap_copy, ap);
6247195eb40SKelly Yancey 		len = vsnprintf(&s->s_buf[s->s_len], SBUF_FREESPACE(s) + 1,
625a9a0bbadSPeter Wemm 		    fmt, ap_copy);
626a9a0bbadSPeter Wemm 		va_end(ap_copy);
6274351ba27SMatthew D Fleming 
6284351ba27SMatthew D Fleming 		if (SBUF_FREESPACE(s) >= len)
6294351ba27SMatthew D Fleming 			break;
6304351ba27SMatthew D Fleming 		/* Cannot print with the current available space. */
6314351ba27SMatthew D Fleming 		if (s->s_drain_func != NULL && s->s_len > 0)
6324351ba27SMatthew D Fleming 			error = sbuf_drain(s);
6334351ba27SMatthew D Fleming 		else
6344351ba27SMatthew D Fleming 			error = sbuf_extend(s, len - SBUF_FREESPACE(s));
6354351ba27SMatthew D Fleming 	} while (error == 0);
63660ec4130SDag-Erling Smørgrav 
6373393f8daSKenneth D. Merry 	/*
6383393f8daSKenneth D. Merry 	 * s->s_len is the length of the string, without the terminating nul.
6393393f8daSKenneth D. Merry 	 * When updating s->s_len, we must subtract 1 from the length that
6403393f8daSKenneth D. Merry 	 * we passed into vsnprintf() because that length includes the
6413393f8daSKenneth D. Merry 	 * terminating nul.
6423393f8daSKenneth D. Merry 	 *
6433393f8daSKenneth D. Merry 	 * vsnprintf() returns the amount that would have been copied,
644c05dbe7aSMatthew D Fleming 	 * given sufficient space, so don't over-increment s_len.
6453393f8daSKenneth D. Merry 	 */
646c05dbe7aSMatthew D Fleming 	if (SBUF_FREESPACE(s) < len)
647c05dbe7aSMatthew D Fleming 		len = SBUF_FREESPACE(s);
648c05dbe7aSMatthew D Fleming 	s->s_len += len;
649ddb9b612SMikolaj Golub 	if (SBUF_ISSECTION(s))
650ddb9b612SMikolaj Golub 		s->s_sect_len += len;
6511a25c86bSPoul-Henning Kamp 	if (!SBUF_HASROOM(s) && !SBUF_CANEXTEND(s))
6524d369413SMatthew D Fleming 		s->s_error = ENOMEM;
6533393f8daSKenneth D. Merry 
65460ec4130SDag-Erling Smørgrav 	KASSERT(s->s_len < s->s_size,
65560ec4130SDag-Erling Smørgrav 	    ("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size));
65660ec4130SDag-Erling Smørgrav 
6574d369413SMatthew D Fleming 	if (s->s_error != 0)
65860ec4130SDag-Erling Smørgrav 		return (-1);
65960ec4130SDag-Erling Smørgrav 	return (0);
66060ec4130SDag-Erling Smørgrav }
66101f6f5fcSMatthew D Fleming #endif /* _KERNEL */
66260ec4130SDag-Erling Smørgrav 
66360ec4130SDag-Erling Smørgrav /*
6647195eb40SKelly Yancey  * Format the given arguments and append the resulting string to an sbuf.
6657195eb40SKelly Yancey  */
6667195eb40SKelly Yancey int
6677195eb40SKelly Yancey sbuf_printf(struct sbuf *s, const char *fmt, ...)
6687195eb40SKelly Yancey {
6697195eb40SKelly Yancey 	va_list ap;
6707195eb40SKelly Yancey 	int result;
6717195eb40SKelly Yancey 
6727195eb40SKelly Yancey 	va_start(ap, fmt);
6737195eb40SKelly Yancey 	result = sbuf_vprintf(s, fmt, ap);
6747195eb40SKelly Yancey 	va_end(ap);
6757195eb40SKelly Yancey 	return (result);
6767195eb40SKelly Yancey }
6777195eb40SKelly Yancey 
6787195eb40SKelly Yancey /*
67960ec4130SDag-Erling Smørgrav  * Append a character to an sbuf.
68060ec4130SDag-Erling Smørgrav  */
68160ec4130SDag-Erling Smørgrav int
68260ec4130SDag-Erling Smørgrav sbuf_putc(struct sbuf *s, int c)
68360ec4130SDag-Erling Smørgrav {
684546d7890SDag-Erling Smørgrav 
685eb05ee7aSPoul-Henning Kamp 	sbuf_put_byte(s, c);
6864d369413SMatthew D Fleming 	if (s->s_error != 0)
68760ec4130SDag-Erling Smørgrav 		return (-1);
68860ec4130SDag-Erling Smørgrav 	return (0);
68960ec4130SDag-Erling Smørgrav }
69060ec4130SDag-Erling Smørgrav 
69160ec4130SDag-Erling Smørgrav /*
6927195eb40SKelly Yancey  * Trim whitespace characters from end of an sbuf.
6935b6db477SDag-Erling Smørgrav  */
6945b6db477SDag-Erling Smørgrav int
6955b6db477SDag-Erling Smørgrav sbuf_trim(struct sbuf *s)
6965b6db477SDag-Erling Smørgrav {
697546d7890SDag-Erling Smørgrav 
6985b6db477SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
6995b6db477SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
7004351ba27SMatthew D Fleming 	KASSERT(s->s_drain_func == NULL,
7014351ba27SMatthew D Fleming 	    ("%s makes no sense on sbuf %p with drain", __func__, s));
7025b6db477SDag-Erling Smørgrav 
7034d369413SMatthew D Fleming 	if (s->s_error != 0)
7045b6db477SDag-Erling Smørgrav 		return (-1);
7055b6db477SDag-Erling Smørgrav 
706ddb9b612SMikolaj Golub 	while (s->s_len > 0 && isspace(s->s_buf[s->s_len-1])) {
7075b6db477SDag-Erling Smørgrav 		--s->s_len;
708ddb9b612SMikolaj Golub 		if (SBUF_ISSECTION(s))
709ddb9b612SMikolaj Golub 			s->s_sect_len--;
710ddb9b612SMikolaj Golub 	}
7115b6db477SDag-Erling Smørgrav 
7125b6db477SDag-Erling Smørgrav 	return (0);
7135b6db477SDag-Erling Smørgrav }
7145b6db477SDag-Erling Smørgrav 
7155b6db477SDag-Erling Smørgrav /*
7164d369413SMatthew D Fleming  * Check if an sbuf has an error.
71760ec4130SDag-Erling Smørgrav  */
71860ec4130SDag-Erling Smørgrav int
71971c2bc5cSPoul-Henning Kamp sbuf_error(const struct sbuf *s)
7204dc14139SDag-Erling Smørgrav {
721546d7890SDag-Erling Smørgrav 
7224d369413SMatthew D Fleming 	return (s->s_error);
7234dc14139SDag-Erling Smørgrav }
7244dc14139SDag-Erling Smørgrav 
7254dc14139SDag-Erling Smørgrav /*
7264dc14139SDag-Erling Smørgrav  * Finish off an sbuf.
7274dc14139SDag-Erling Smørgrav  */
7284351ba27SMatthew D Fleming int
72960ec4130SDag-Erling Smørgrav sbuf_finish(struct sbuf *s)
73060ec4130SDag-Erling Smørgrav {
731546d7890SDag-Erling Smørgrav 
73260ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
73360ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
73460ec4130SDag-Erling Smørgrav 
735f4d28142SIan Lepore 	s->s_buf[s->s_len] = '\0';
7368d5628fdSIan Lepore 	if (SBUF_NULINCLUDED(s))
737f4d28142SIan Lepore 		s->s_len++;
7384d369413SMatthew D Fleming 	if (s->s_drain_func != NULL) {
739eb05ee7aSPoul-Henning Kamp 		while (s->s_len > 0 && s->s_error == 0)
740eb05ee7aSPoul-Henning Kamp 			s->s_error = sbuf_drain(s);
7414d369413SMatthew D Fleming 	}
74260ec4130SDag-Erling Smørgrav 	SBUF_SETFLAG(s, SBUF_FINISHED);
7434351ba27SMatthew D Fleming #ifdef _KERNEL
744eb05ee7aSPoul-Henning Kamp 	return (s->s_error);
7454351ba27SMatthew D Fleming #else
746c532f8c4SJaakko Heinonen 	if (s->s_error != 0) {
747eb05ee7aSPoul-Henning Kamp 		errno = s->s_error;
7484351ba27SMatthew D Fleming 		return (-1);
749c532f8c4SJaakko Heinonen 	}
750eb05ee7aSPoul-Henning Kamp 	return (0);
7514351ba27SMatthew D Fleming #endif
75260ec4130SDag-Erling Smørgrav }
75360ec4130SDag-Erling Smørgrav 
75460ec4130SDag-Erling Smørgrav /*
75560ec4130SDag-Erling Smørgrav  * Return a pointer to the sbuf data.
75660ec4130SDag-Erling Smørgrav  */
75760ec4130SDag-Erling Smørgrav char *
75860ec4130SDag-Erling Smørgrav sbuf_data(struct sbuf *s)
75960ec4130SDag-Erling Smørgrav {
760546d7890SDag-Erling Smørgrav 
76160ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
76260ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, SBUF_FINISHED);
7634351ba27SMatthew D Fleming 	KASSERT(s->s_drain_func == NULL,
7644351ba27SMatthew D Fleming 	    ("%s makes no sense on sbuf %p with drain", __func__, s));
76560ec4130SDag-Erling Smørgrav 
766546d7890SDag-Erling Smørgrav 	return (s->s_buf);
76760ec4130SDag-Erling Smørgrav }
76860ec4130SDag-Erling Smørgrav 
76960ec4130SDag-Erling Smørgrav /*
77060ec4130SDag-Erling Smørgrav  * Return the length of the sbuf data.
77160ec4130SDag-Erling Smørgrav  */
77271c2bc5cSPoul-Henning Kamp ssize_t
77360ec4130SDag-Erling Smørgrav sbuf_len(struct sbuf *s)
77460ec4130SDag-Erling Smørgrav {
775546d7890SDag-Erling Smørgrav 
77660ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
7779fa2ef3dSDag-Erling Smørgrav 	/* don't care if it's finished or not */
7784351ba27SMatthew D Fleming 	KASSERT(s->s_drain_func == NULL,
7794351ba27SMatthew D Fleming 	    ("%s makes no sense on sbuf %p with drain", __func__, s));
78060ec4130SDag-Erling Smørgrav 
7814d369413SMatthew D Fleming 	if (s->s_error != 0)
7824dc14139SDag-Erling Smørgrav 		return (-1);
783f4d28142SIan Lepore 
784f4d28142SIan Lepore 	/* If finished, nulterm is already in len, else add one. */
7858d5628fdSIan Lepore 	if (SBUF_NULINCLUDED(s) && !SBUF_ISFINISHED(s))
786f4d28142SIan Lepore 		return (s->s_len + 1);
787546d7890SDag-Erling Smørgrav 	return (s->s_len);
78860ec4130SDag-Erling Smørgrav }
78960ec4130SDag-Erling Smørgrav 
79060ec4130SDag-Erling Smørgrav /*
79160ec4130SDag-Erling Smørgrav  * Clear an sbuf, free its buffer if necessary.
79260ec4130SDag-Erling Smørgrav  */
79360ec4130SDag-Erling Smørgrav void
79460ec4130SDag-Erling Smørgrav sbuf_delete(struct sbuf *s)
79560ec4130SDag-Erling Smørgrav {
796a57094a0SMatthew Dillon 	int isdyn;
797a57094a0SMatthew Dillon 
79860ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
79960ec4130SDag-Erling Smørgrav 	/* don't care if it's finished or not */
80060ec4130SDag-Erling Smørgrav 
80160ec4130SDag-Erling Smørgrav 	if (SBUF_ISDYNAMIC(s))
8023393f8daSKenneth D. Merry 		SBFREE(s->s_buf);
803a57094a0SMatthew Dillon 	isdyn = SBUF_ISDYNSTRUCT(s);
804384bf94cSPoul-Henning Kamp 	memset(s, 0, sizeof(*s));
805a57094a0SMatthew Dillon 	if (isdyn)
806d6479358SDag-Erling Smørgrav 		SBFREE(s);
80760ec4130SDag-Erling Smørgrav }
808c5f9218bSPoul-Henning Kamp 
809c5f9218bSPoul-Henning Kamp /*
810c5f9218bSPoul-Henning Kamp  * Check if an sbuf has been finished.
811c5f9218bSPoul-Henning Kamp  */
812c5f9218bSPoul-Henning Kamp int
81371c2bc5cSPoul-Henning Kamp sbuf_done(const struct sbuf *s)
814c5f9218bSPoul-Henning Kamp {
815c5f9218bSPoul-Henning Kamp 
816c5f9218bSPoul-Henning Kamp 	return (SBUF_ISFINISHED(s));
817c5f9218bSPoul-Henning Kamp }
818ddb9b612SMikolaj Golub 
819ddb9b612SMikolaj Golub /*
820ddb9b612SMikolaj Golub  * Start a section.
821ddb9b612SMikolaj Golub  */
822ddb9b612SMikolaj Golub void
823ddb9b612SMikolaj Golub sbuf_start_section(struct sbuf *s, ssize_t *old_lenp)
824ddb9b612SMikolaj Golub {
825ddb9b612SMikolaj Golub 
826ddb9b612SMikolaj Golub 	assert_sbuf_integrity(s);
827ddb9b612SMikolaj Golub 	assert_sbuf_state(s, 0);
828ddb9b612SMikolaj Golub 
829ddb9b612SMikolaj Golub 	if (!SBUF_ISSECTION(s)) {
830ddb9b612SMikolaj Golub 		KASSERT(s->s_sect_len == 0,
831ddb9b612SMikolaj Golub 		    ("s_sect_len != 0 when starting a section"));
832ddb9b612SMikolaj Golub 		if (old_lenp != NULL)
833ddb9b612SMikolaj Golub 			*old_lenp = -1;
834ddb9b612SMikolaj Golub 		SBUF_SETFLAG(s, SBUF_INSECTION);
835ddb9b612SMikolaj Golub 	} else {
836ddb9b612SMikolaj Golub 		KASSERT(old_lenp != NULL,
837ddb9b612SMikolaj Golub 		    ("s_sect_len should be saved when starting a subsection"));
838ddb9b612SMikolaj Golub 		*old_lenp = s->s_sect_len;
839ddb9b612SMikolaj Golub 		s->s_sect_len = 0;
840ddb9b612SMikolaj Golub 	}
841ddb9b612SMikolaj Golub }
842ddb9b612SMikolaj Golub 
843ddb9b612SMikolaj Golub /*
844ddb9b612SMikolaj Golub  * End the section padding to the specified length with the specified
845ddb9b612SMikolaj Golub  * character.
846ddb9b612SMikolaj Golub  */
847ddb9b612SMikolaj Golub ssize_t
848ddb9b612SMikolaj Golub sbuf_end_section(struct sbuf *s, ssize_t old_len, size_t pad, int c)
849ddb9b612SMikolaj Golub {
850ddb9b612SMikolaj Golub 	ssize_t len;
851ddb9b612SMikolaj Golub 
852ddb9b612SMikolaj Golub 	assert_sbuf_integrity(s);
853ddb9b612SMikolaj Golub 	assert_sbuf_state(s, 0);
854ddb9b612SMikolaj Golub 	KASSERT(SBUF_ISSECTION(s),
855ddb9b612SMikolaj Golub 	    ("attempt to end a section when not in a section"));
856ddb9b612SMikolaj Golub 
857ddb9b612SMikolaj Golub 	if (pad > 1) {
858ddb9b612SMikolaj Golub 		len = roundup(s->s_sect_len, pad) - s->s_sect_len;
859ddb9b612SMikolaj Golub 		for (; s->s_error == 0 && len > 0; len--)
860ddb9b612SMikolaj Golub 			sbuf_put_byte(s, c);
861ddb9b612SMikolaj Golub 	}
862ddb9b612SMikolaj Golub 	len = s->s_sect_len;
863ddb9b612SMikolaj Golub 	if (old_len == -1) {
864ddb9b612SMikolaj Golub 		s->s_sect_len = 0;
865ddb9b612SMikolaj Golub 		SBUF_CLEARFLAG(s, SBUF_INSECTION);
866ddb9b612SMikolaj Golub 	} else {
867ddb9b612SMikolaj Golub 		s->s_sect_len += old_len;
868ddb9b612SMikolaj Golub 	}
869ddb9b612SMikolaj Golub 	if (s->s_error != 0)
870ddb9b612SMikolaj Golub 		return (-1);
871ddb9b612SMikolaj Golub 	return (len);
872ddb9b612SMikolaj Golub }
873