xref: /freebsd/sys/kern/subr_sbuf.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
160ec4130SDag-Erling Smørgrav /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
38a36da99SPedro F. Giffuni  *
4546d7890SDag-Erling Smørgrav  * Copyright (c) 2000-2008 Poul-Henning Kamp
5*e738085bSDag-Erling Smørgrav  * Copyright (c) 2000-2008 Dag-Erling Smørgrav
660ec4130SDag-Erling Smørgrav  * All rights reserved.
760ec4130SDag-Erling Smørgrav  *
860ec4130SDag-Erling Smørgrav  * Redistribution and use in source and binary forms, with or without
960ec4130SDag-Erling Smørgrav  * modification, are permitted provided that the following conditions
1060ec4130SDag-Erling Smørgrav  * are met:
1160ec4130SDag-Erling Smørgrav  * 1. Redistributions of source code must retain the above copyright
1260ec4130SDag-Erling Smørgrav  *    notice, this list of conditions and the following disclaimer
1360ec4130SDag-Erling Smørgrav  *    in this position and unchanged.
1460ec4130SDag-Erling Smørgrav  * 2. Redistributions in binary form must reproduce the above copyright
1560ec4130SDag-Erling Smørgrav  *    notice, this list of conditions and the following disclaimer in the
1660ec4130SDag-Erling Smørgrav  *    documentation and/or other materials provided with the distribution.
1760ec4130SDag-Erling Smørgrav  *
18546d7890SDag-Erling Smørgrav  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19546d7890SDag-Erling Smørgrav  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20546d7890SDag-Erling Smørgrav  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21546d7890SDag-Erling Smørgrav  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22546d7890SDag-Erling Smørgrav  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23546d7890SDag-Erling Smørgrav  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24546d7890SDag-Erling Smørgrav  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25546d7890SDag-Erling Smørgrav  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26546d7890SDag-Erling Smørgrav  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27546d7890SDag-Erling Smørgrav  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28546d7890SDag-Erling Smørgrav  * SUCH DAMAGE.
2960ec4130SDag-Erling Smørgrav  */
3060ec4130SDag-Erling Smørgrav 
3160ec4130SDag-Erling Smørgrav #include <sys/param.h>
323393f8daSKenneth D. Merry 
333393f8daSKenneth D. Merry #ifdef _KERNEL
345b6db477SDag-Erling Smørgrav #include <sys/ctype.h>
354351ba27SMatthew D Fleming #include <sys/errno.h>
3660ec4130SDag-Erling Smørgrav #include <sys/kernel.h>
372f1c4e0eSConrad Meyer #include <sys/limits.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>
452f1c4e0eSConrad Meyer #include <limits.h>
463393f8daSKenneth D. Merry #include <stdarg.h>
477195eb40SKelly Yancey #include <stdio.h>
489fa416caSJonathan Lemon #include <stdlib.h>
497195eb40SKelly Yancey #include <string.h>
503393f8daSKenneth D. Merry #endif /* _KERNEL */
5160ec4130SDag-Erling Smørgrav 
525b6db477SDag-Erling Smørgrav #include <sys/sbuf.h>
535b6db477SDag-Erling Smørgrav 
543393f8daSKenneth D. Merry #ifdef _KERNEL
55c711aea6SPoul-Henning Kamp static MALLOC_DEFINE(M_SBUF, "sbuf", "string buffers");
5671db411eSConrad Meyer #define	SBMALLOC(size, flags)	malloc(size, M_SBUF, (flags) | M_ZERO)
573393f8daSKenneth D. Merry #define	SBFREE(buf)		free(buf, M_SBUF)
583393f8daSKenneth D. Merry #else /* _KERNEL */
593393f8daSKenneth D. Merry #define	KASSERT(e, m)
6071db411eSConrad Meyer #define	SBMALLOC(size, flags)	calloc(1, size)
613393f8daSKenneth D. Merry #define	SBFREE(buf)		free(buf)
623393f8daSKenneth D. Merry #endif /* _KERNEL */
6360ec4130SDag-Erling Smørgrav 
644dc14139SDag-Erling Smørgrav /*
654dc14139SDag-Erling Smørgrav  * Predicates
664dc14139SDag-Erling Smørgrav  */
674dc14139SDag-Erling Smørgrav #define	SBUF_ISDYNAMIC(s)	((s)->s_flags & SBUF_DYNAMIC)
68d6479358SDag-Erling Smørgrav #define	SBUF_ISDYNSTRUCT(s)	((s)->s_flags & SBUF_DYNSTRUCT)
694dc14139SDag-Erling Smørgrav #define	SBUF_ISFINISHED(s)	((s)->s_flags & SBUF_FINISHED)
7076cb1112SConrad Meyer #define	SBUF_ISDRAINATEOL(s)	((s)->s_flags & SBUF_DRAINATEOL)
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)
76a8ec96afSLawrence Stewart #define	SBUF_ISDRAINTOEOR(s)	((s)->s_flags & SBUF_DRAINTOEOR)
77a8ec96afSLawrence Stewart #define	SBUF_DODRAINTOEOR(s)	(SBUF_ISSECTION(s) && SBUF_ISDRAINTOEOR(s))
7871db411eSConrad Meyer #define	SBUF_MALLOCFLAG(s)	\
7971db411eSConrad Meyer 	(((s)->s_flags & SBUF_NOWAIT) ? M_NOWAIT : M_WAITOK)
804dc14139SDag-Erling Smørgrav 
814dc14139SDag-Erling Smørgrav /*
824dc14139SDag-Erling Smørgrav  * Set / clear flags
834dc14139SDag-Erling Smørgrav  */
844dc14139SDag-Erling Smørgrav #define	SBUF_SETFLAG(s, f)	do { (s)->s_flags |= (f); } while (0)
854dc14139SDag-Erling Smørgrav #define	SBUF_CLEARFLAG(s, f)	do { (s)->s_flags &= ~(f); } while (0)
864dc14139SDag-Erling Smørgrav 
87612d9391SIan Lepore #define	SBUF_MINSIZE		 2		/* Min is 1 byte + nulterm. */
887195eb40SKelly Yancey #define	SBUF_MINEXTENDSIZE	16		/* Should be power of 2. */
89eb05ee7aSPoul-Henning Kamp 
90eb05ee7aSPoul-Henning Kamp #ifdef PAGE_SIZE
917195eb40SKelly Yancey #define	SBUF_MAXEXTENDSIZE	PAGE_SIZE
927195eb40SKelly Yancey #define	SBUF_MAXEXTENDINCR	PAGE_SIZE
93eb05ee7aSPoul-Henning Kamp #else
94eb05ee7aSPoul-Henning Kamp #define	SBUF_MAXEXTENDSIZE	4096
95eb05ee7aSPoul-Henning Kamp #define	SBUF_MAXEXTENDINCR	4096
96eb05ee7aSPoul-Henning Kamp #endif
977195eb40SKelly Yancey 
984dc14139SDag-Erling Smørgrav /*
994dc14139SDag-Erling Smørgrav  * Debugging support
1004dc14139SDag-Erling Smørgrav  */
1013393f8daSKenneth D. Merry #if defined(_KERNEL) && defined(INVARIANTS)
102546d7890SDag-Erling Smørgrav 
10360ec4130SDag-Erling Smørgrav static void
_assert_sbuf_integrity(const char * fun,struct sbuf * s)104a5e7c7daSPeter Wemm _assert_sbuf_integrity(const char *fun, struct sbuf *s)
10560ec4130SDag-Erling Smørgrav {
106546d7890SDag-Erling Smørgrav 
10760ec4130SDag-Erling Smørgrav 	KASSERT(s != NULL,
108cab5b963SDag-Erling Smørgrav 	    ("%s called with a NULL sbuf pointer", fun));
10960ec4130SDag-Erling Smørgrav 	KASSERT(s->s_buf != NULL,
1107195eb40SKelly Yancey 	    ("%s called with uninitialized or corrupt sbuf", fun));
1118d5628fdSIan Lepore 	if (SBUF_ISFINISHED(s) && SBUF_NULINCLUDED(s)) {
1128d5628fdSIan Lepore 		KASSERT(s->s_len <= s->s_size,
1138d5628fdSIan Lepore 		    ("wrote past end of sbuf (%jd >= %jd)",
1148d5628fdSIan Lepore 		    (intmax_t)s->s_len, (intmax_t)s->s_size));
1158d5628fdSIan Lepore 	} else {
11660ec4130SDag-Erling Smørgrav 		KASSERT(s->s_len < s->s_size,
11771c2bc5cSPoul-Henning Kamp 		    ("wrote past end of sbuf (%jd >= %jd)",
11871c2bc5cSPoul-Henning Kamp 		    (intmax_t)s->s_len, (intmax_t)s->s_size));
11960ec4130SDag-Erling Smørgrav 	}
1208d5628fdSIan Lepore }
12160ec4130SDag-Erling Smørgrav 
12260ec4130SDag-Erling Smørgrav static void
_assert_sbuf_state(const char * fun,struct sbuf * s,int state)123a5e7c7daSPeter Wemm _assert_sbuf_state(const char *fun, struct sbuf *s, int state)
12460ec4130SDag-Erling Smørgrav {
125546d7890SDag-Erling Smørgrav 
12660ec4130SDag-Erling Smørgrav 	KASSERT((s->s_flags & SBUF_FINISHED) == state,
127cab5b963SDag-Erling Smørgrav 	    ("%s called with %sfinished or corrupt sbuf", fun,
12860ec4130SDag-Erling Smørgrav 	    (state ? "un" : "")));
12960ec4130SDag-Erling Smørgrav }
130546d7890SDag-Erling Smørgrav 
131a48740b6SDavid E. O'Brien #define	assert_sbuf_integrity(s) _assert_sbuf_integrity(__func__, (s))
132a48740b6SDavid E. O'Brien #define	assert_sbuf_state(s, i)	 _assert_sbuf_state(__func__, (s), (i))
133546d7890SDag-Erling Smørgrav 
1343393f8daSKenneth D. Merry #else /* _KERNEL && INVARIANTS */
135546d7890SDag-Erling Smørgrav 
13660ec4130SDag-Erling Smørgrav #define	assert_sbuf_integrity(s) do { } while (0)
13760ec4130SDag-Erling Smørgrav #define	assert_sbuf_state(s, i)	 do { } while (0)
138546d7890SDag-Erling Smørgrav 
1393393f8daSKenneth D. Merry #endif /* _KERNEL && INVARIANTS */
14060ec4130SDag-Erling Smørgrav 
141181ff3d5SMatthew D Fleming #ifdef CTASSERT
142f4bafab8SMatthew D Fleming CTASSERT(powerof2(SBUF_MAXEXTENDSIZE));
143f4bafab8SMatthew D Fleming CTASSERT(powerof2(SBUF_MAXEXTENDINCR));
144969292fbSMatthew D Fleming #endif
145f4bafab8SMatthew D Fleming 
1467195eb40SKelly Yancey static int
sbuf_extendsize(int size)1477195eb40SKelly Yancey sbuf_extendsize(int size)
1487195eb40SKelly Yancey {
1497195eb40SKelly Yancey 	int newsize;
1507195eb40SKelly Yancey 
151f4bafab8SMatthew D Fleming 	if (size < (int)SBUF_MAXEXTENDSIZE) {
1527195eb40SKelly Yancey 		newsize = SBUF_MINEXTENDSIZE;
153f4bafab8SMatthew D Fleming 		while (newsize < size)
1547195eb40SKelly Yancey 			newsize *= 2;
155f4bafab8SMatthew D Fleming 	} else {
156f4bafab8SMatthew D Fleming 		newsize = roundup2(size, SBUF_MAXEXTENDINCR);
1577195eb40SKelly Yancey 	}
158f5a5dc5dSMatthew D Fleming 	KASSERT(newsize >= size, ("%s: %d < %d\n", __func__, newsize, size));
1597195eb40SKelly Yancey 	return (newsize);
1607195eb40SKelly Yancey }
1617195eb40SKelly Yancey 
1627195eb40SKelly Yancey /*
1637195eb40SKelly Yancey  * Extend an sbuf.
1647195eb40SKelly Yancey  */
1657195eb40SKelly Yancey static int
sbuf_extend(struct sbuf * s,int addlen)1667195eb40SKelly Yancey sbuf_extend(struct sbuf *s, int addlen)
1677195eb40SKelly Yancey {
1687195eb40SKelly Yancey 	char *newbuf;
169adecd05bSPietro Cerutti 	int newsize;
1707195eb40SKelly Yancey 
1717195eb40SKelly Yancey 	if (!SBUF_CANEXTEND(s))
1727195eb40SKelly Yancey 		return (-1);
1737195eb40SKelly Yancey 	newsize = sbuf_extendsize(s->s_size + addlen);
17471db411eSConrad Meyer 	newbuf = SBMALLOC(newsize, SBUF_MALLOCFLAG(s));
1757195eb40SKelly Yancey 	if (newbuf == NULL)
1767195eb40SKelly Yancey 		return (-1);
177384bf94cSPoul-Henning Kamp 	memcpy(newbuf, s->s_buf, s->s_size);
1787195eb40SKelly Yancey 	if (SBUF_ISDYNAMIC(s))
1797195eb40SKelly Yancey 		SBFREE(s->s_buf);
1807195eb40SKelly Yancey 	else
1817195eb40SKelly Yancey 		SBUF_SETFLAG(s, SBUF_DYNAMIC);
1827195eb40SKelly Yancey 	s->s_buf = newbuf;
1837195eb40SKelly Yancey 	s->s_size = newsize;
1847195eb40SKelly Yancey 	return (0);
1857195eb40SKelly Yancey }
1867195eb40SKelly Yancey 
18760ec4130SDag-Erling Smørgrav /*
18860ec4130SDag-Erling Smørgrav  * Initialize an sbuf.
18960ec4130SDag-Erling Smørgrav  * If buf is non-NULL, it points to a static or already-allocated string
19060ec4130SDag-Erling Smørgrav  * big enough to hold at least length characters.
19160ec4130SDag-Erling Smørgrav  */
192d6479358SDag-Erling Smørgrav struct sbuf *
sbuf_new(struct sbuf * s,char * buf,int length,int flags)1934dc14139SDag-Erling Smørgrav sbuf_new(struct sbuf *s, char *buf, int length, int flags)
19460ec4130SDag-Erling Smørgrav {
195546d7890SDag-Erling Smørgrav 
1964dc14139SDag-Erling Smørgrav 	KASSERT(length >= 0,
1974dc14139SDag-Erling Smørgrav 	    ("attempt to create an sbuf of negative length (%d)", length));
1987195eb40SKelly Yancey 	KASSERT((flags & ~SBUF_USRFLAGMSK) == 0,
1997195eb40SKelly Yancey 	    ("%s called with invalid flags", __func__));
200d23813cdSConrad Meyer 	KASSERT((flags & SBUF_AUTOEXTEND) || length >= SBUF_MINSIZE,
201d23813cdSConrad Meyer 	    ("sbuf buffer %d smaller than minimum %d bytes", length,
202d23813cdSConrad Meyer 	    SBUF_MINSIZE));
20360ec4130SDag-Erling Smørgrav 
2047195eb40SKelly Yancey 	flags &= SBUF_USRFLAGMSK;
205384bf94cSPoul-Henning Kamp 
206d23813cdSConrad Meyer 	/*
207d23813cdSConrad Meyer 	 * Allocate 'DYNSTRUCT' sbuf from the heap, if NULL 's' was provided.
208d23813cdSConrad Meyer 	 */
209d23813cdSConrad Meyer 	if (s == NULL) {
210d23813cdSConrad Meyer 		s = SBMALLOC(sizeof(*s),
211d23813cdSConrad Meyer 		    (flags & SBUF_NOWAIT) ?  M_NOWAIT : M_WAITOK);
212d6479358SDag-Erling Smørgrav 		if (s == NULL)
213d23813cdSConrad Meyer 			goto out;
214384bf94cSPoul-Henning Kamp 		SBUF_SETFLAG(s, SBUF_DYNSTRUCT);
215d23813cdSConrad Meyer 	} else {
216d23813cdSConrad Meyer 		/*
217d23813cdSConrad Meyer 		 * DYNSTRUCT SBMALLOC sbufs are allocated with M_ZERO, but
218d23813cdSConrad Meyer 		 * user-provided sbuf objects must be initialized.
219d23813cdSConrad Meyer 		 */
220d23813cdSConrad Meyer 		memset(s, 0, sizeof(*s));
221d23813cdSConrad Meyer 	}
222d23813cdSConrad Meyer 
223d23813cdSConrad Meyer 	s->s_flags |= flags;
224d23813cdSConrad Meyer 	s->s_size = length;
225d23813cdSConrad Meyer 	s->s_buf = buf;
22676cb1112SConrad Meyer 	/*
22776cb1112SConrad Meyer 	 * Never-written sbufs do not need \n termination.
22876cb1112SConrad Meyer 	 */
22976cb1112SConrad Meyer 	SBUF_SETFLAG(s, SBUF_DRAINATEOL);
230d23813cdSConrad Meyer 
231d23813cdSConrad Meyer 	/*
232d23813cdSConrad Meyer 	 * Allocate DYNAMIC, i.e., heap data buffer backing the sbuf, if no
233d23813cdSConrad Meyer 	 * buffer was provided.
234d23813cdSConrad Meyer 	 */
235d23813cdSConrad Meyer 	if (s->s_buf == NULL) {
236d23813cdSConrad Meyer 		if (SBUF_CANEXTEND(s))
237d23813cdSConrad Meyer 			s->s_size = sbuf_extendsize(s->s_size);
238d23813cdSConrad Meyer 		s->s_buf = SBMALLOC(s->s_size, SBUF_MALLOCFLAG(s));
239d23813cdSConrad Meyer 		if (s->s_buf == NULL)
240d23813cdSConrad Meyer 			goto out;
241d23813cdSConrad Meyer 		SBUF_SETFLAG(s, SBUF_DYNAMIC);
242d23813cdSConrad Meyer 	}
243d23813cdSConrad Meyer 
244d23813cdSConrad Meyer out:
245d23813cdSConrad Meyer 	if (s != NULL && s->s_buf == NULL) {
246d23813cdSConrad Meyer 		if (SBUF_ISDYNSTRUCT(s))
247d23813cdSConrad Meyer 			SBFREE(s);
248d23813cdSConrad Meyer 		s = NULL;
249d23813cdSConrad Meyer 	}
250d6479358SDag-Erling Smørgrav 	return (s);
25160ec4130SDag-Erling Smørgrav }
25260ec4130SDag-Erling Smørgrav 
2535b6db477SDag-Erling Smørgrav #ifdef _KERNEL
2545b6db477SDag-Erling Smørgrav /*
2555b6db477SDag-Erling Smørgrav  * Create an sbuf with uio data
2565b6db477SDag-Erling Smørgrav  */
2575b6db477SDag-Erling Smørgrav struct sbuf *
sbuf_uionew(struct sbuf * s,struct uio * uio,int * error)2585b6db477SDag-Erling Smørgrav sbuf_uionew(struct sbuf *s, struct uio *uio, int *error)
2595b6db477SDag-Erling Smørgrav {
260546d7890SDag-Erling Smørgrav 
2615b6db477SDag-Erling Smørgrav 	KASSERT(uio != NULL,
262a48740b6SDavid E. O'Brien 	    ("%s called with NULL uio pointer", __func__));
2635b6db477SDag-Erling Smørgrav 	KASSERT(error != NULL,
264a48740b6SDavid E. O'Brien 	    ("%s called with NULL error pointer", __func__));
2655b6db477SDag-Erling Smørgrav 
266116f26f9SKonstantin Belousov 	if (uio->uio_resid >= INT_MAX || uio->uio_resid < SBUF_MINSIZE - 1) {
267116f26f9SKonstantin Belousov 		*error = EINVAL;
268116f26f9SKonstantin Belousov 		return (NULL);
269116f26f9SKonstantin Belousov 	}
2705b6db477SDag-Erling Smørgrav 	s = sbuf_new(s, NULL, uio->uio_resid + 1, 0);
2715b6db477SDag-Erling Smørgrav 	if (s == NULL) {
2725b6db477SDag-Erling Smørgrav 		*error = ENOMEM;
2735b6db477SDag-Erling Smørgrav 		return (NULL);
2745b6db477SDag-Erling Smørgrav 	}
2755b6db477SDag-Erling Smørgrav 	*error = uiomove(s->s_buf, uio->uio_resid, uio);
2765b6db477SDag-Erling Smørgrav 	if (*error != 0) {
2775b6db477SDag-Erling Smørgrav 		sbuf_delete(s);
2785b6db477SDag-Erling Smørgrav 		return (NULL);
2795b6db477SDag-Erling Smørgrav 	}
2805b6db477SDag-Erling Smørgrav 	s->s_len = s->s_size - 1;
281ddb9b612SMikolaj Golub 	if (SBUF_ISSECTION(s))
282ddb9b612SMikolaj Golub 		s->s_sect_len = s->s_size - 1;
2835b6db477SDag-Erling Smørgrav 	*error = 0;
2845b6db477SDag-Erling Smørgrav 	return (s);
2855b6db477SDag-Erling Smørgrav }
2865b6db477SDag-Erling Smørgrav #endif
2875b6db477SDag-Erling Smørgrav 
288f4d28142SIan Lepore int
sbuf_get_flags(struct sbuf * s)289f4d28142SIan Lepore sbuf_get_flags(struct sbuf *s)
290f4d28142SIan Lepore {
291f4d28142SIan Lepore 
292f4d28142SIan Lepore 	return (s->s_flags & SBUF_USRFLAGMSK);
293f4d28142SIan Lepore }
294f4d28142SIan Lepore 
295f4d28142SIan Lepore void
sbuf_clear_flags(struct sbuf * s,int flags)296f4d28142SIan Lepore sbuf_clear_flags(struct sbuf *s, int flags)
297f4d28142SIan Lepore {
298f4d28142SIan Lepore 
299f4d28142SIan Lepore 	s->s_flags &= ~(flags & SBUF_USRFLAGMSK);
300f4d28142SIan Lepore }
301f4d28142SIan Lepore 
302f4d28142SIan Lepore void
sbuf_set_flags(struct sbuf * s,int flags)303f4d28142SIan Lepore sbuf_set_flags(struct sbuf *s, int flags)
304f4d28142SIan Lepore {
305f4d28142SIan Lepore 
306f4d28142SIan Lepore 	s->s_flags |= (flags & SBUF_USRFLAGMSK);
307f4d28142SIan Lepore }
308f4d28142SIan Lepore 
30960ec4130SDag-Erling Smørgrav /*
3107195eb40SKelly Yancey  * Clear an sbuf and reset its position.
3114dc14139SDag-Erling Smørgrav  */
3124dc14139SDag-Erling Smørgrav void
sbuf_clear(struct sbuf * s)3134dc14139SDag-Erling Smørgrav sbuf_clear(struct sbuf *s)
3144dc14139SDag-Erling Smørgrav {
315546d7890SDag-Erling Smørgrav 
3164dc14139SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
3179fa2ef3dSDag-Erling Smørgrav 	/* don't care if it's finished or not */
31876cb1112SConrad Meyer 	KASSERT(s->s_drain_func == NULL,
31976cb1112SConrad Meyer 	    ("%s makes no sense on sbuf %p with drain", __func__, s));
3204dc14139SDag-Erling Smørgrav 
3214dc14139SDag-Erling Smørgrav 	SBUF_CLEARFLAG(s, SBUF_FINISHED);
3224351ba27SMatthew D Fleming 	s->s_error = 0;
3234dc14139SDag-Erling Smørgrav 	s->s_len = 0;
324a8ec96afSLawrence Stewart 	s->s_rec_off = 0;
325ddb9b612SMikolaj Golub 	s->s_sect_len = 0;
3264dc14139SDag-Erling Smørgrav }
3274dc14139SDag-Erling Smørgrav 
3284dc14139SDag-Erling Smørgrav /*
3297195eb40SKelly Yancey  * Set the sbuf's end position to an arbitrary value.
3307195eb40SKelly Yancey  * Effectively truncates the sbuf at the new position.
33160ec4130SDag-Erling Smørgrav  */
33260ec4130SDag-Erling Smørgrav int
sbuf_setpos(struct sbuf * s,ssize_t pos)333adecd05bSPietro Cerutti sbuf_setpos(struct sbuf *s, ssize_t pos)
33460ec4130SDag-Erling Smørgrav {
335546d7890SDag-Erling Smørgrav 
33660ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
33760ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
33860ec4130SDag-Erling Smørgrav 
339adecd05bSPietro Cerutti 	KASSERT(pos >= 0,
340adecd05bSPietro Cerutti 	    ("attempt to seek to a negative position (%jd)", (intmax_t)pos));
34160ec4130SDag-Erling Smørgrav 	KASSERT(pos < s->s_size,
34271c2bc5cSPoul-Henning Kamp 	    ("attempt to seek past end of sbuf (%jd >= %jd)",
34371c2bc5cSPoul-Henning Kamp 	    (intmax_t)pos, (intmax_t)s->s_size));
344ddb9b612SMikolaj Golub 	KASSERT(!SBUF_ISSECTION(s),
345ddb9b612SMikolaj Golub 	    ("attempt to seek when in a section"));
34660ec4130SDag-Erling Smørgrav 
347adecd05bSPietro Cerutti 	if (pos < 0 || pos > s->s_len)
34860ec4130SDag-Erling Smørgrav 		return (-1);
34960ec4130SDag-Erling Smørgrav 	s->s_len = pos;
35060ec4130SDag-Erling Smørgrav 	return (0);
35160ec4130SDag-Erling Smørgrav }
35260ec4130SDag-Erling Smørgrav 
35360ec4130SDag-Erling Smørgrav /*
354eeea0fcfSAlexander Motin  * Drain into a counter.  Counts amount of data without producing output.
3555c32e9fcSAlexander Motin  * Useful for cases like sysctl, where user may first request only size.
3565c32e9fcSAlexander Motin  * This allows to avoid pointless allocation/freeing of large buffers.
3575c32e9fcSAlexander Motin  */
3585c32e9fcSAlexander Motin int
sbuf_count_drain(void * arg,const char * data __unused,int len)3595c32e9fcSAlexander Motin sbuf_count_drain(void *arg, const char *data __unused, int len)
3605c32e9fcSAlexander Motin {
3615c32e9fcSAlexander Motin 	size_t *sizep;
3625c32e9fcSAlexander Motin 
3635c32e9fcSAlexander Motin 	sizep = (size_t *)arg;
3645c32e9fcSAlexander Motin 	*sizep += len;
3655c32e9fcSAlexander Motin 	return (len);
3665c32e9fcSAlexander Motin }
3675c32e9fcSAlexander Motin 
3685c32e9fcSAlexander Motin /*
3694351ba27SMatthew D Fleming  * Set up a drain function and argument on an sbuf to flush data to
3704351ba27SMatthew D Fleming  * when the sbuf buffer overflows.
3714351ba27SMatthew D Fleming  */
3724351ba27SMatthew D Fleming void
sbuf_set_drain(struct sbuf * s,sbuf_drain_func * func,void * ctx)3734351ba27SMatthew D Fleming sbuf_set_drain(struct sbuf *s, sbuf_drain_func *func, void *ctx)
3744351ba27SMatthew D Fleming {
3754351ba27SMatthew D Fleming 
3764351ba27SMatthew D Fleming 	assert_sbuf_state(s, 0);
3774351ba27SMatthew D Fleming 	assert_sbuf_integrity(s);
3784351ba27SMatthew D Fleming 	KASSERT(func == s->s_drain_func || s->s_len == 0,
3794351ba27SMatthew D Fleming 	    ("Cannot change drain to %p on non-empty sbuf %p", func, s));
3804351ba27SMatthew D Fleming 	s->s_drain_func = func;
3814351ba27SMatthew D Fleming 	s->s_drain_arg = ctx;
3824351ba27SMatthew D Fleming }
3834351ba27SMatthew D Fleming 
3844351ba27SMatthew D Fleming /*
3854351ba27SMatthew D Fleming  * Call the drain and process the return.
3864351ba27SMatthew D Fleming  */
387c804c8f2SRichard Scheffenegger int
sbuf_drain(struct sbuf * s)3884351ba27SMatthew D Fleming sbuf_drain(struct sbuf *s)
3894351ba27SMatthew D Fleming {
3904351ba27SMatthew D Fleming 	int len;
3914351ba27SMatthew D Fleming 
392cad4fd03SRichard Scheffenegger 	/*
393cad4fd03SRichard Scheffenegger 	 * Immediately return when no work to do,
394cad4fd03SRichard Scheffenegger 	 * or an error has already been accumulated.
395cad4fd03SRichard Scheffenegger 	 */
396cad4fd03SRichard Scheffenegger 	if ((s->s_len == 0) || (s->s_error != 0))
397cad4fd03SRichard Scheffenegger 		return(s->s_error);
39876cb1112SConrad Meyer 
399a8ec96afSLawrence Stewart 	if (SBUF_DODRAINTOEOR(s) && s->s_rec_off == 0)
400a8ec96afSLawrence Stewart 		return (s->s_error = EDEADLK);
401a8ec96afSLawrence Stewart 	len = s->s_drain_func(s->s_drain_arg, s->s_buf,
402a8ec96afSLawrence Stewart 	    SBUF_DODRAINTOEOR(s) ? s->s_rec_off : s->s_len);
4039a61faf6SLawrence Stewart 	if (len <= 0) {
4049a61faf6SLawrence Stewart 		s->s_error = len ? -len : EDEADLK;
4054351ba27SMatthew D Fleming 		return (s->s_error);
4064351ba27SMatthew D Fleming 	}
4074e657159SMatthew D Fleming 	KASSERT(len > 0 && len <= s->s_len,
4084e657159SMatthew D Fleming 	    ("Bad drain amount %d for sbuf %p", len, s));
4094351ba27SMatthew D Fleming 	s->s_len -= len;
410a8ec96afSLawrence Stewart 	s->s_rec_off -= len;
4114351ba27SMatthew D Fleming 	/*
4124351ba27SMatthew D Fleming 	 * Fast path for the expected case where all the data was
4134351ba27SMatthew D Fleming 	 * drained.
4144351ba27SMatthew D Fleming 	 */
41576cb1112SConrad Meyer 	if (s->s_len == 0) {
41676cb1112SConrad Meyer 		/*
41776cb1112SConrad Meyer 		 * When the s_buf is entirely drained, we need to remember if
41876cb1112SConrad Meyer 		 * the last character was a '\n' or not for
41976cb1112SConrad Meyer 		 * sbuf_nl_terminate().
42076cb1112SConrad Meyer 		 */
42176cb1112SConrad Meyer 		if (s->s_buf[len - 1] == '\n')
42276cb1112SConrad Meyer 			SBUF_SETFLAG(s, SBUF_DRAINATEOL);
42376cb1112SConrad Meyer 		else
42476cb1112SConrad Meyer 			SBUF_CLEARFLAG(s, SBUF_DRAINATEOL);
4254351ba27SMatthew D Fleming 		return (0);
42676cb1112SConrad Meyer 	}
4274351ba27SMatthew D Fleming 	/*
4284351ba27SMatthew D Fleming 	 * Move the remaining characters to the beginning of the
4294351ba27SMatthew D Fleming 	 * string.
4304351ba27SMatthew D Fleming 	 */
4314351ba27SMatthew D Fleming 	memmove(s->s_buf, s->s_buf + len, s->s_len);
4324351ba27SMatthew D Fleming 	return (0);
4334351ba27SMatthew D Fleming }
4344351ba27SMatthew D Fleming 
4354351ba27SMatthew D Fleming /*
4362f1c4e0eSConrad Meyer  * Append bytes to an sbuf.  This is the core function for appending
43701f6f5fcSMatthew D Fleming  * to an sbuf and is the main place that deals with extending the
43801f6f5fcSMatthew D Fleming  * buffer and marking overflow.
43901f6f5fcSMatthew D Fleming  */
44001f6f5fcSMatthew D Fleming static void
sbuf_put_bytes(struct sbuf * s,const char * buf,size_t len)4412f1c4e0eSConrad Meyer sbuf_put_bytes(struct sbuf *s, const char *buf, size_t len)
44201f6f5fcSMatthew D Fleming {
4432f1c4e0eSConrad Meyer 	size_t n;
44401f6f5fcSMatthew D Fleming 
44501f6f5fcSMatthew D Fleming 	assert_sbuf_integrity(s);
44601f6f5fcSMatthew D Fleming 	assert_sbuf_state(s, 0);
44701f6f5fcSMatthew D Fleming 
4484d369413SMatthew D Fleming 	if (s->s_error != 0)
44901f6f5fcSMatthew D Fleming 		return;
4502f1c4e0eSConrad Meyer 	while (len > 0) {
45101f6f5fcSMatthew D Fleming 		if (SBUF_FREESPACE(s) <= 0) {
4524351ba27SMatthew D Fleming 			/*
4534351ba27SMatthew D Fleming 			 * If there is a drain, use it, otherwise extend the
4544351ba27SMatthew D Fleming 			 * buffer.
4554351ba27SMatthew D Fleming 			 */
4564351ba27SMatthew D Fleming 			if (s->s_drain_func != NULL)
4574351ba27SMatthew D Fleming 				(void)sbuf_drain(s);
4582f1c4e0eSConrad Meyer 			else if (sbuf_extend(s, len > INT_MAX ? INT_MAX : len)
4592f1c4e0eSConrad Meyer 			    < 0)
4604d369413SMatthew D Fleming 				s->s_error = ENOMEM;
4614d369413SMatthew D Fleming 			if (s->s_error != 0)
46201f6f5fcSMatthew D Fleming 				return;
46301f6f5fcSMatthew D Fleming 		}
4642f1c4e0eSConrad Meyer 		n = SBUF_FREESPACE(s);
4652f1c4e0eSConrad Meyer 		if (len < n)
4662f1c4e0eSConrad Meyer 			n = len;
4672f1c4e0eSConrad Meyer 		memcpy(&s->s_buf[s->s_len], buf, n);
4682f1c4e0eSConrad Meyer 		s->s_len += n;
469ddb9b612SMikolaj Golub 		if (SBUF_ISSECTION(s))
4702f1c4e0eSConrad Meyer 			s->s_sect_len += n;
4712f1c4e0eSConrad Meyer 		len -= n;
4722f1c4e0eSConrad Meyer 		buf += n;
4732f1c4e0eSConrad Meyer 	}
4742f1c4e0eSConrad Meyer }
4752f1c4e0eSConrad Meyer 
4762f1c4e0eSConrad Meyer static void
sbuf_put_byte(struct sbuf * s,char c)4772f1c4e0eSConrad Meyer sbuf_put_byte(struct sbuf *s, char c)
4782f1c4e0eSConrad Meyer {
4792f1c4e0eSConrad Meyer 
4807835b2cbSAlexander Motin 	assert_sbuf_integrity(s);
4817835b2cbSAlexander Motin 	assert_sbuf_state(s, 0);
4827835b2cbSAlexander Motin 
4837835b2cbSAlexander Motin 	if (__predict_false(s->s_error != 0))
4847835b2cbSAlexander Motin 		return;
4857835b2cbSAlexander Motin 	if (__predict_false(SBUF_FREESPACE(s) <= 0)) {
4867835b2cbSAlexander Motin 		/*
4877835b2cbSAlexander Motin 		 * If there is a drain, use it, otherwise extend the
4887835b2cbSAlexander Motin 		 * buffer.
4897835b2cbSAlexander Motin 		 */
4907835b2cbSAlexander Motin 		if (s->s_drain_func != NULL)
4917835b2cbSAlexander Motin 			(void)sbuf_drain(s);
4927835b2cbSAlexander Motin 		else if (sbuf_extend(s, 1) < 0)
4937835b2cbSAlexander Motin 			s->s_error = ENOMEM;
4947835b2cbSAlexander Motin 		if (s->s_error != 0)
4957835b2cbSAlexander Motin 			return;
4967835b2cbSAlexander Motin 	}
4977835b2cbSAlexander Motin 	s->s_buf[s->s_len++] = c;
4987835b2cbSAlexander Motin 	if (SBUF_ISSECTION(s))
4997835b2cbSAlexander Motin 		s->s_sect_len++;
50001f6f5fcSMatthew D Fleming }
50101f6f5fcSMatthew D Fleming 
50201f6f5fcSMatthew D Fleming /*
503b0def2b5SDag-Erling Smørgrav  * Append a byte string to an sbuf.
504b0def2b5SDag-Erling Smørgrav  */
505b0def2b5SDag-Erling Smørgrav int
sbuf_bcat(struct sbuf * s,const void * buf,size_t len)506520df276SDag-Erling Smørgrav sbuf_bcat(struct sbuf *s, const void *buf, size_t len)
507b0def2b5SDag-Erling Smørgrav {
508d751f0a9SDag-Erling Smørgrav 
5092f1c4e0eSConrad Meyer 	sbuf_put_bytes(s, buf, len);
5104d369413SMatthew D Fleming 	if (s->s_error != 0)
511b0def2b5SDag-Erling Smørgrav 		return (-1);
512b0def2b5SDag-Erling Smørgrav 	return (0);
513b0def2b5SDag-Erling Smørgrav }
514b0def2b5SDag-Erling Smørgrav 
515b0def2b5SDag-Erling Smørgrav #ifdef _KERNEL
516b0def2b5SDag-Erling Smørgrav /*
517b0def2b5SDag-Erling Smørgrav  * Copy a byte string from userland into an sbuf.
518b0def2b5SDag-Erling Smørgrav  */
519b0def2b5SDag-Erling Smørgrav int
sbuf_bcopyin(struct sbuf * s,const void * uaddr,size_t len)520b0def2b5SDag-Erling Smørgrav sbuf_bcopyin(struct sbuf *s, const void *uaddr, size_t len)
521b0def2b5SDag-Erling Smørgrav {
522546d7890SDag-Erling Smørgrav 
523b0def2b5SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
524b0def2b5SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
5254351ba27SMatthew D Fleming 	KASSERT(s->s_drain_func == NULL,
5264351ba27SMatthew D Fleming 	    ("Nonsensical copyin to sbuf %p with a drain", s));
527b0def2b5SDag-Erling Smørgrav 
5284d369413SMatthew D Fleming 	if (s->s_error != 0)
529b0def2b5SDag-Erling Smørgrav 		return (-1);
530b0def2b5SDag-Erling Smørgrav 	if (len == 0)
531b0def2b5SDag-Erling Smørgrav 		return (0);
5327195eb40SKelly Yancey 	if (len > SBUF_FREESPACE(s)) {
5337195eb40SKelly Yancey 		sbuf_extend(s, len - SBUF_FREESPACE(s));
534c05dbe7aSMatthew D Fleming 		if (SBUF_FREESPACE(s) < len)
535c05dbe7aSMatthew D Fleming 			len = SBUF_FREESPACE(s);
5367195eb40SKelly Yancey 	}
537e3b37322SDag-Erling Smørgrav 	if (copyin(uaddr, s->s_buf + s->s_len, len) != 0)
538e3b37322SDag-Erling Smørgrav 		return (-1);
539fe463496SDag-Erling Smørgrav 	s->s_len += len;
540b0def2b5SDag-Erling Smørgrav 
541b0def2b5SDag-Erling Smørgrav 	return (0);
542b0def2b5SDag-Erling Smørgrav }
543b0def2b5SDag-Erling Smørgrav #endif
544b0def2b5SDag-Erling Smørgrav 
545b0def2b5SDag-Erling Smørgrav /*
546b0def2b5SDag-Erling Smørgrav  * Copy a byte string into an sbuf.
547b0def2b5SDag-Erling Smørgrav  */
548b0def2b5SDag-Erling Smørgrav int
sbuf_bcpy(struct sbuf * s,const void * buf,size_t len)549520df276SDag-Erling Smørgrav sbuf_bcpy(struct sbuf *s, const void *buf, size_t len)
550b0def2b5SDag-Erling Smørgrav {
551546d7890SDag-Erling Smørgrav 
552b0def2b5SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
553b0def2b5SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
554b0def2b5SDag-Erling Smørgrav 
555b0def2b5SDag-Erling Smørgrav 	sbuf_clear(s);
556520df276SDag-Erling Smørgrav 	return (sbuf_bcat(s, buf, len));
557b0def2b5SDag-Erling Smørgrav }
558b0def2b5SDag-Erling Smørgrav 
559b0def2b5SDag-Erling Smørgrav /*
56060ec4130SDag-Erling Smørgrav  * Append a string to an sbuf.
56160ec4130SDag-Erling Smørgrav  */
56260ec4130SDag-Erling Smørgrav int
sbuf_cat(struct sbuf * s,const char * str)5633393f8daSKenneth D. Merry sbuf_cat(struct sbuf *s, const char *str)
56460ec4130SDag-Erling Smørgrav {
5652f1c4e0eSConrad Meyer 	size_t n;
566546d7890SDag-Erling Smørgrav 
5672f1c4e0eSConrad Meyer 	n = strlen(str);
5682f1c4e0eSConrad Meyer 	sbuf_put_bytes(s, str, n);
5694d369413SMatthew D Fleming 	if (s->s_error != 0)
57060ec4130SDag-Erling Smørgrav 		return (-1);
57160ec4130SDag-Erling Smørgrav 	return (0);
57260ec4130SDag-Erling Smørgrav }
57360ec4130SDag-Erling Smørgrav 
574b0def2b5SDag-Erling Smørgrav #ifdef _KERNEL
575b0def2b5SDag-Erling Smørgrav /*
5767195eb40SKelly Yancey  * Append a string from userland to an sbuf.
577b0def2b5SDag-Erling Smørgrav  */
578b0def2b5SDag-Erling Smørgrav int
sbuf_copyin(struct sbuf * s,const void * uaddr,size_t len)579b0def2b5SDag-Erling Smørgrav sbuf_copyin(struct sbuf *s, const void *uaddr, size_t len)
580b0def2b5SDag-Erling Smørgrav {
581b0def2b5SDag-Erling Smørgrav 	size_t done;
582b0def2b5SDag-Erling Smørgrav 
583b0def2b5SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
584b0def2b5SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
5854351ba27SMatthew D Fleming 	KASSERT(s->s_drain_func == NULL,
5864351ba27SMatthew D Fleming 	    ("Nonsensical copyin to sbuf %p with a drain", s));
587b0def2b5SDag-Erling Smørgrav 
5884d369413SMatthew D Fleming 	if (s->s_error != 0)
589b0def2b5SDag-Erling Smørgrav 		return (-1);
590b0def2b5SDag-Erling Smørgrav 
5917195eb40SKelly Yancey 	if (len == 0)
5927195eb40SKelly Yancey 		len = SBUF_FREESPACE(s);	/* XXX return 0? */
5937195eb40SKelly Yancey 	if (len > SBUF_FREESPACE(s)) {
5947195eb40SKelly Yancey 		sbuf_extend(s, len);
595c05dbe7aSMatthew D Fleming 		if (SBUF_FREESPACE(s) < len)
596c05dbe7aSMatthew D Fleming 			len = SBUF_FREESPACE(s);
5977195eb40SKelly Yancey 	}
598b0def2b5SDag-Erling Smørgrav 	switch (copyinstr(uaddr, s->s_buf + s->s_len, len + 1, &done)) {
599b0def2b5SDag-Erling Smørgrav 	case ENAMETOOLONG:
6004d369413SMatthew D Fleming 		s->s_error = ENOMEM;
601b0def2b5SDag-Erling Smørgrav 		/* fall through */
602b0def2b5SDag-Erling Smørgrav 	case 0:
603b0def2b5SDag-Erling Smørgrav 		s->s_len += done - 1;
604ddb9b612SMikolaj Golub 		if (SBUF_ISSECTION(s))
605ddb9b612SMikolaj Golub 			s->s_sect_len += done - 1;
606b0def2b5SDag-Erling Smørgrav 		break;
607b0def2b5SDag-Erling Smørgrav 	default:
608b0def2b5SDag-Erling Smørgrav 		return (-1);	/* XXX */
609b0def2b5SDag-Erling Smørgrav 	}
610b0def2b5SDag-Erling Smørgrav 
61149091c48SPoul-Henning Kamp 	return (done);
612b0def2b5SDag-Erling Smørgrav }
613b0def2b5SDag-Erling Smørgrav #endif
614b0def2b5SDag-Erling Smørgrav 
61560ec4130SDag-Erling Smørgrav /*
61660ec4130SDag-Erling Smørgrav  * Copy a string into an sbuf.
61760ec4130SDag-Erling Smørgrav  */
61860ec4130SDag-Erling Smørgrav int
sbuf_cpy(struct sbuf * s,const char * str)6193393f8daSKenneth D. Merry sbuf_cpy(struct sbuf *s, const char *str)
62060ec4130SDag-Erling Smørgrav {
621546d7890SDag-Erling Smørgrav 
62260ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
62360ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
62460ec4130SDag-Erling Smørgrav 
6254dc14139SDag-Erling Smørgrav 	sbuf_clear(s);
62660ec4130SDag-Erling Smørgrav 	return (sbuf_cat(s, str));
62760ec4130SDag-Erling Smørgrav }
62860ec4130SDag-Erling Smørgrav 
62960ec4130SDag-Erling Smørgrav /*
6307195eb40SKelly Yancey  * Format the given argument list and append the resulting string to an sbuf.
63160ec4130SDag-Erling Smørgrav  */
63201f6f5fcSMatthew D Fleming #ifdef _KERNEL
633eb05ee7aSPoul-Henning Kamp 
634eb05ee7aSPoul-Henning Kamp /*
635eb05ee7aSPoul-Henning Kamp  * Append a non-NUL character to an sbuf.  This prototype signature is
636eb05ee7aSPoul-Henning Kamp  * suitable for use with kvprintf(9).
637eb05ee7aSPoul-Henning Kamp  */
638eb05ee7aSPoul-Henning Kamp static void
sbuf_putc_func(int c,void * arg)639eb05ee7aSPoul-Henning Kamp sbuf_putc_func(int c, void *arg)
640eb05ee7aSPoul-Henning Kamp {
641eb05ee7aSPoul-Henning Kamp 
6427835b2cbSAlexander Motin 	if (__predict_true(c != '\0'))
643eb05ee7aSPoul-Henning Kamp 		sbuf_put_byte(arg, c);
644eb05ee7aSPoul-Henning Kamp }
645eb05ee7aSPoul-Henning Kamp 
64601f6f5fcSMatthew D Fleming int
sbuf_vprintf(struct sbuf * s,const char * fmt,va_list ap)64701f6f5fcSMatthew D Fleming sbuf_vprintf(struct sbuf *s, const char *fmt, va_list ap)
64801f6f5fcSMatthew D Fleming {
64901f6f5fcSMatthew D Fleming 
65001f6f5fcSMatthew D Fleming 	assert_sbuf_integrity(s);
65101f6f5fcSMatthew D Fleming 	assert_sbuf_state(s, 0);
65201f6f5fcSMatthew D Fleming 
65301f6f5fcSMatthew D Fleming 	KASSERT(fmt != NULL,
65401f6f5fcSMatthew D Fleming 	    ("%s called with a NULL format string", __func__));
65501f6f5fcSMatthew D Fleming 
65601f6f5fcSMatthew D Fleming 	(void)kvprintf(fmt, sbuf_putc_func, s, 10, ap);
6574d369413SMatthew D Fleming 	if (s->s_error != 0)
65801f6f5fcSMatthew D Fleming 		return (-1);
65901f6f5fcSMatthew D Fleming 	return (0);
66001f6f5fcSMatthew D Fleming }
66101f6f5fcSMatthew D Fleming #else /* !_KERNEL */
66260ec4130SDag-Erling Smørgrav int
sbuf_vprintf(struct sbuf * s,const char * fmt,va_list ap)6637195eb40SKelly Yancey sbuf_vprintf(struct sbuf *s, const char *fmt, va_list ap)
66460ec4130SDag-Erling Smørgrav {
665a9a0bbadSPeter Wemm 	va_list ap_copy;
666adecd05bSPietro Cerutti 	int error, len;
66760ec4130SDag-Erling Smørgrav 
66860ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
66960ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
67060ec4130SDag-Erling Smørgrav 
67160ec4130SDag-Erling Smørgrav 	KASSERT(fmt != NULL,
672a48740b6SDavid E. O'Brien 	    ("%s called with a NULL format string", __func__));
67360ec4130SDag-Erling Smørgrav 
6744d369413SMatthew D Fleming 	if (s->s_error != 0)
67560ec4130SDag-Erling Smørgrav 		return (-1);
67660ec4130SDag-Erling Smørgrav 
67701f6f5fcSMatthew D Fleming 	/*
67801f6f5fcSMatthew D Fleming 	 * For the moment, there is no way to get vsnprintf(3) to hand
67901f6f5fcSMatthew D Fleming 	 * back a character at a time, to push everything into
68001f6f5fcSMatthew D Fleming 	 * sbuf_putc_func() as was done for the kernel.
6814351ba27SMatthew D Fleming 	 *
6824351ba27SMatthew D Fleming 	 * In userspace, while drains are useful, there's generally
6834351ba27SMatthew D Fleming 	 * not a problem attempting to malloc(3) on out of space.  So
6844351ba27SMatthew D Fleming 	 * expand a userland sbuf if there is not enough room for the
6854351ba27SMatthew D Fleming 	 * data produced by sbuf_[v]printf(3).
68601f6f5fcSMatthew D Fleming 	 */
68701f6f5fcSMatthew D Fleming 
6884351ba27SMatthew D Fleming 	error = 0;
6897195eb40SKelly Yancey 	do {
690a9a0bbadSPeter Wemm 		va_copy(ap_copy, ap);
6917195eb40SKelly Yancey 		len = vsnprintf(&s->s_buf[s->s_len], SBUF_FREESPACE(s) + 1,
692a9a0bbadSPeter Wemm 		    fmt, ap_copy);
693d58b610fSPoul-Henning Kamp 		if (len < 0) {
694d58b610fSPoul-Henning Kamp 			s->s_error = errno;
695d58b610fSPoul-Henning Kamp 			return (-1);
696d58b610fSPoul-Henning Kamp 		}
697a9a0bbadSPeter Wemm 		va_end(ap_copy);
6984351ba27SMatthew D Fleming 
6994351ba27SMatthew D Fleming 		if (SBUF_FREESPACE(s) >= len)
7004351ba27SMatthew D Fleming 			break;
7014351ba27SMatthew D Fleming 		/* Cannot print with the current available space. */
7024351ba27SMatthew D Fleming 		if (s->s_drain_func != NULL && s->s_len > 0)
7039a61faf6SLawrence Stewart 			error = sbuf_drain(s); /* sbuf_drain() sets s_error. */
7049a61faf6SLawrence Stewart 		else if (sbuf_extend(s, len - SBUF_FREESPACE(s)) != 0)
7059a61faf6SLawrence Stewart 			s->s_error = error = ENOMEM;
7064351ba27SMatthew D Fleming 	} while (error == 0);
70760ec4130SDag-Erling Smørgrav 
7083393f8daSKenneth D. Merry 	/*
7093393f8daSKenneth D. Merry 	 * s->s_len is the length of the string, without the terminating nul.
7103393f8daSKenneth D. Merry 	 * When updating s->s_len, we must subtract 1 from the length that
7113393f8daSKenneth D. Merry 	 * we passed into vsnprintf() because that length includes the
7123393f8daSKenneth D. Merry 	 * terminating nul.
7133393f8daSKenneth D. Merry 	 *
7143393f8daSKenneth D. Merry 	 * vsnprintf() returns the amount that would have been copied,
715c05dbe7aSMatthew D Fleming 	 * given sufficient space, so don't over-increment s_len.
7163393f8daSKenneth D. Merry 	 */
717c05dbe7aSMatthew D Fleming 	if (SBUF_FREESPACE(s) < len)
718c05dbe7aSMatthew D Fleming 		len = SBUF_FREESPACE(s);
719c05dbe7aSMatthew D Fleming 	s->s_len += len;
720ddb9b612SMikolaj Golub 	if (SBUF_ISSECTION(s))
721ddb9b612SMikolaj Golub 		s->s_sect_len += len;
7223393f8daSKenneth D. Merry 
72360ec4130SDag-Erling Smørgrav 	KASSERT(s->s_len < s->s_size,
72460ec4130SDag-Erling Smørgrav 	    ("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size));
72560ec4130SDag-Erling Smørgrav 
7264d369413SMatthew D Fleming 	if (s->s_error != 0)
72760ec4130SDag-Erling Smørgrav 		return (-1);
72860ec4130SDag-Erling Smørgrav 	return (0);
72960ec4130SDag-Erling Smørgrav }
73001f6f5fcSMatthew D Fleming #endif /* _KERNEL */
73160ec4130SDag-Erling Smørgrav 
73260ec4130SDag-Erling Smørgrav /*
7337195eb40SKelly Yancey  * Format the given arguments and append the resulting string to an sbuf.
7347195eb40SKelly Yancey  */
7357195eb40SKelly Yancey int
sbuf_printf(struct sbuf * s,const char * fmt,...)7367195eb40SKelly Yancey sbuf_printf(struct sbuf *s, const char *fmt, ...)
7377195eb40SKelly Yancey {
7387195eb40SKelly Yancey 	va_list ap;
7397195eb40SKelly Yancey 	int result;
7407195eb40SKelly Yancey 
7417195eb40SKelly Yancey 	va_start(ap, fmt);
7427195eb40SKelly Yancey 	result = sbuf_vprintf(s, fmt, ap);
7437195eb40SKelly Yancey 	va_end(ap);
7447195eb40SKelly Yancey 	return (result);
7457195eb40SKelly Yancey }
7467195eb40SKelly Yancey 
7477195eb40SKelly Yancey /*
74860ec4130SDag-Erling Smørgrav  * Append a character to an sbuf.
74960ec4130SDag-Erling Smørgrav  */
75060ec4130SDag-Erling Smørgrav int
sbuf_putc(struct sbuf * s,int c)75160ec4130SDag-Erling Smørgrav sbuf_putc(struct sbuf *s, int c)
75260ec4130SDag-Erling Smørgrav {
753546d7890SDag-Erling Smørgrav 
754eb05ee7aSPoul-Henning Kamp 	sbuf_put_byte(s, c);
7554d369413SMatthew D Fleming 	if (s->s_error != 0)
75660ec4130SDag-Erling Smørgrav 		return (-1);
75760ec4130SDag-Erling Smørgrav 	return (0);
75860ec4130SDag-Erling Smørgrav }
75960ec4130SDag-Erling Smørgrav 
76060ec4130SDag-Erling Smørgrav /*
76176cb1112SConrad Meyer  * Append a trailing newline to a non-empty sbuf, if one is not already
76276cb1112SConrad Meyer  * present.  Handles sbufs with drain functions correctly.
76376cb1112SConrad Meyer  */
76476cb1112SConrad Meyer int
sbuf_nl_terminate(struct sbuf * s)76576cb1112SConrad Meyer sbuf_nl_terminate(struct sbuf *s)
76676cb1112SConrad Meyer {
76776cb1112SConrad Meyer 
76876cb1112SConrad Meyer 	assert_sbuf_integrity(s);
76976cb1112SConrad Meyer 	assert_sbuf_state(s, 0);
77076cb1112SConrad Meyer 
77176cb1112SConrad Meyer 	/*
77276cb1112SConrad Meyer 	 * If the s_buf isn't empty, the last byte is simply s_buf[s_len - 1].
77376cb1112SConrad Meyer 	 *
77476cb1112SConrad Meyer 	 * If the s_buf is empty because a drain function drained it, we
77576cb1112SConrad Meyer 	 * remember if the last byte was a \n with the SBUF_DRAINATEOL flag in
77676cb1112SConrad Meyer 	 * sbuf_drain().
77776cb1112SConrad Meyer 	 *
77876cb1112SConrad Meyer 	 * In either case, we only append a \n if the previous character was
77976cb1112SConrad Meyer 	 * something else.
78076cb1112SConrad Meyer 	 */
78176cb1112SConrad Meyer 	if (s->s_len == 0) {
78276cb1112SConrad Meyer 		if (!SBUF_ISDRAINATEOL(s))
78376cb1112SConrad Meyer 			sbuf_put_byte(s, '\n');
78476cb1112SConrad Meyer 	} else if (s->s_buf[s->s_len - 1] != '\n')
78576cb1112SConrad Meyer 		sbuf_put_byte(s, '\n');
78676cb1112SConrad Meyer 
78776cb1112SConrad Meyer 	if (s->s_error != 0)
78876cb1112SConrad Meyer 		return (-1);
78976cb1112SConrad Meyer 	return (0);
79076cb1112SConrad Meyer }
79176cb1112SConrad Meyer 
79276cb1112SConrad Meyer /*
7937195eb40SKelly Yancey  * Trim whitespace characters from end of an sbuf.
7945b6db477SDag-Erling Smørgrav  */
7955b6db477SDag-Erling Smørgrav int
sbuf_trim(struct sbuf * s)7965b6db477SDag-Erling Smørgrav sbuf_trim(struct sbuf *s)
7975b6db477SDag-Erling Smørgrav {
798546d7890SDag-Erling Smørgrav 
7995b6db477SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
8005b6db477SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
8014351ba27SMatthew D Fleming 	KASSERT(s->s_drain_func == NULL,
8024351ba27SMatthew D Fleming 	    ("%s makes no sense on sbuf %p with drain", __func__, s));
8035b6db477SDag-Erling Smørgrav 
8044d369413SMatthew D Fleming 	if (s->s_error != 0)
8055b6db477SDag-Erling Smørgrav 		return (-1);
8065b6db477SDag-Erling Smørgrav 
807ddb9b612SMikolaj Golub 	while (s->s_len > 0 && isspace(s->s_buf[s->s_len-1])) {
8085b6db477SDag-Erling Smørgrav 		--s->s_len;
809ddb9b612SMikolaj Golub 		if (SBUF_ISSECTION(s))
810ddb9b612SMikolaj Golub 			s->s_sect_len--;
811ddb9b612SMikolaj Golub 	}
8125b6db477SDag-Erling Smørgrav 
8135b6db477SDag-Erling Smørgrav 	return (0);
8145b6db477SDag-Erling Smørgrav }
8155b6db477SDag-Erling Smørgrav 
8165b6db477SDag-Erling Smørgrav /*
8174d369413SMatthew D Fleming  * Check if an sbuf has an error.
81860ec4130SDag-Erling Smørgrav  */
81960ec4130SDag-Erling Smørgrav int
sbuf_error(const struct sbuf * s)82071c2bc5cSPoul-Henning Kamp sbuf_error(const struct sbuf *s)
8214dc14139SDag-Erling Smørgrav {
822546d7890SDag-Erling Smørgrav 
8234d369413SMatthew D Fleming 	return (s->s_error);
8244dc14139SDag-Erling Smørgrav }
8254dc14139SDag-Erling Smørgrav 
8264dc14139SDag-Erling Smørgrav /*
8274dc14139SDag-Erling Smørgrav  * Finish off an sbuf.
8284dc14139SDag-Erling Smørgrav  */
8294351ba27SMatthew D Fleming int
sbuf_finish(struct sbuf * s)83060ec4130SDag-Erling Smørgrav sbuf_finish(struct sbuf *s)
83160ec4130SDag-Erling Smørgrav {
832546d7890SDag-Erling Smørgrav 
83360ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
83460ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
83560ec4130SDag-Erling Smørgrav 
836f4d28142SIan Lepore 	s->s_buf[s->s_len] = '\0';
8378d5628fdSIan Lepore 	if (SBUF_NULINCLUDED(s))
838f4d28142SIan Lepore 		s->s_len++;
8394d369413SMatthew D Fleming 	if (s->s_drain_func != NULL) {
840eb05ee7aSPoul-Henning Kamp 		while (s->s_len > 0 && s->s_error == 0)
841eb05ee7aSPoul-Henning Kamp 			s->s_error = sbuf_drain(s);
8424d369413SMatthew D Fleming 	}
84360ec4130SDag-Erling Smørgrav 	SBUF_SETFLAG(s, SBUF_FINISHED);
8444351ba27SMatthew D Fleming #ifdef _KERNEL
845eb05ee7aSPoul-Henning Kamp 	return (s->s_error);
8464351ba27SMatthew D Fleming #else
847c532f8c4SJaakko Heinonen 	if (s->s_error != 0) {
848eb05ee7aSPoul-Henning Kamp 		errno = s->s_error;
8494351ba27SMatthew D Fleming 		return (-1);
850c532f8c4SJaakko Heinonen 	}
851eb05ee7aSPoul-Henning Kamp 	return (0);
8524351ba27SMatthew D Fleming #endif
85360ec4130SDag-Erling Smørgrav }
85460ec4130SDag-Erling Smørgrav 
85560ec4130SDag-Erling Smørgrav /*
85660ec4130SDag-Erling Smørgrav  * Return a pointer to the sbuf data.
85760ec4130SDag-Erling Smørgrav  */
85860ec4130SDag-Erling Smørgrav char *
sbuf_data(struct sbuf * s)85960ec4130SDag-Erling Smørgrav sbuf_data(struct sbuf *s)
86060ec4130SDag-Erling Smørgrav {
861546d7890SDag-Erling Smørgrav 
86260ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
86360ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, SBUF_FINISHED);
8644351ba27SMatthew D Fleming 	KASSERT(s->s_drain_func == NULL,
8654351ba27SMatthew D Fleming 	    ("%s makes no sense on sbuf %p with drain", __func__, s));
86660ec4130SDag-Erling Smørgrav 
867546d7890SDag-Erling Smørgrav 	return (s->s_buf);
86860ec4130SDag-Erling Smørgrav }
86960ec4130SDag-Erling Smørgrav 
87060ec4130SDag-Erling Smørgrav /*
87160ec4130SDag-Erling Smørgrav  * Return the length of the sbuf data.
87260ec4130SDag-Erling Smørgrav  */
87371c2bc5cSPoul-Henning Kamp ssize_t
sbuf_len(struct sbuf * s)87460ec4130SDag-Erling Smørgrav sbuf_len(struct sbuf *s)
87560ec4130SDag-Erling Smørgrav {
876546d7890SDag-Erling Smørgrav 
87760ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
8789fa2ef3dSDag-Erling Smørgrav 	/* don't care if it's finished or not */
8794351ba27SMatthew D Fleming 	KASSERT(s->s_drain_func == NULL,
8804351ba27SMatthew D Fleming 	    ("%s makes no sense on sbuf %p with drain", __func__, s));
88160ec4130SDag-Erling Smørgrav 
8824d369413SMatthew D Fleming 	if (s->s_error != 0)
8834dc14139SDag-Erling Smørgrav 		return (-1);
884f4d28142SIan Lepore 
885f4d28142SIan Lepore 	/* If finished, nulterm is already in len, else add one. */
8868d5628fdSIan Lepore 	if (SBUF_NULINCLUDED(s) && !SBUF_ISFINISHED(s))
887f4d28142SIan Lepore 		return (s->s_len + 1);
888546d7890SDag-Erling Smørgrav 	return (s->s_len);
88960ec4130SDag-Erling Smørgrav }
89060ec4130SDag-Erling Smørgrav 
89160ec4130SDag-Erling Smørgrav /*
89260ec4130SDag-Erling Smørgrav  * Clear an sbuf, free its buffer if necessary.
89360ec4130SDag-Erling Smørgrav  */
89460ec4130SDag-Erling Smørgrav void
sbuf_delete(struct sbuf * s)89560ec4130SDag-Erling Smørgrav sbuf_delete(struct sbuf *s)
89660ec4130SDag-Erling Smørgrav {
897a57094a0SMatthew Dillon 	int isdyn;
898a57094a0SMatthew Dillon 
89960ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
90060ec4130SDag-Erling Smørgrav 	/* don't care if it's finished or not */
90160ec4130SDag-Erling Smørgrav 
90260ec4130SDag-Erling Smørgrav 	if (SBUF_ISDYNAMIC(s))
9033393f8daSKenneth D. Merry 		SBFREE(s->s_buf);
904a57094a0SMatthew Dillon 	isdyn = SBUF_ISDYNSTRUCT(s);
905384bf94cSPoul-Henning Kamp 	memset(s, 0, sizeof(*s));
906a57094a0SMatthew Dillon 	if (isdyn)
907d6479358SDag-Erling Smørgrav 		SBFREE(s);
90860ec4130SDag-Erling Smørgrav }
909c5f9218bSPoul-Henning Kamp 
910c5f9218bSPoul-Henning Kamp /*
911c5f9218bSPoul-Henning Kamp  * Check if an sbuf has been finished.
912c5f9218bSPoul-Henning Kamp  */
913c5f9218bSPoul-Henning Kamp int
sbuf_done(const struct sbuf * s)91471c2bc5cSPoul-Henning Kamp sbuf_done(const struct sbuf *s)
915c5f9218bSPoul-Henning Kamp {
916c5f9218bSPoul-Henning Kamp 
917c5f9218bSPoul-Henning Kamp 	return (SBUF_ISFINISHED(s));
918c5f9218bSPoul-Henning Kamp }
919ddb9b612SMikolaj Golub 
920ddb9b612SMikolaj Golub /*
921ddb9b612SMikolaj Golub  * Start a section.
922ddb9b612SMikolaj Golub  */
923ddb9b612SMikolaj Golub void
sbuf_start_section(struct sbuf * s,ssize_t * old_lenp)924ddb9b612SMikolaj Golub sbuf_start_section(struct sbuf *s, ssize_t *old_lenp)
925ddb9b612SMikolaj Golub {
926ddb9b612SMikolaj Golub 
927ddb9b612SMikolaj Golub 	assert_sbuf_integrity(s);
928ddb9b612SMikolaj Golub 	assert_sbuf_state(s, 0);
929ddb9b612SMikolaj Golub 
930ddb9b612SMikolaj Golub 	if (!SBUF_ISSECTION(s)) {
931ddb9b612SMikolaj Golub 		KASSERT(s->s_sect_len == 0,
932ddb9b612SMikolaj Golub 		    ("s_sect_len != 0 when starting a section"));
933ddb9b612SMikolaj Golub 		if (old_lenp != NULL)
934ddb9b612SMikolaj Golub 			*old_lenp = -1;
935a8ec96afSLawrence Stewart 		s->s_rec_off = s->s_len;
936ddb9b612SMikolaj Golub 		SBUF_SETFLAG(s, SBUF_INSECTION);
937ddb9b612SMikolaj Golub 	} else {
938ddb9b612SMikolaj Golub 		KASSERT(old_lenp != NULL,
939ddb9b612SMikolaj Golub 		    ("s_sect_len should be saved when starting a subsection"));
940ddb9b612SMikolaj Golub 		*old_lenp = s->s_sect_len;
941ddb9b612SMikolaj Golub 		s->s_sect_len = 0;
942ddb9b612SMikolaj Golub 	}
943ddb9b612SMikolaj Golub }
944ddb9b612SMikolaj Golub 
945ddb9b612SMikolaj Golub /*
946ddb9b612SMikolaj Golub  * End the section padding to the specified length with the specified
947ddb9b612SMikolaj Golub  * character.
948ddb9b612SMikolaj Golub  */
949ddb9b612SMikolaj Golub ssize_t
sbuf_end_section(struct sbuf * s,ssize_t old_len,size_t pad,int c)950ddb9b612SMikolaj Golub sbuf_end_section(struct sbuf *s, ssize_t old_len, size_t pad, int c)
951ddb9b612SMikolaj Golub {
952ddb9b612SMikolaj Golub 	ssize_t len;
953ddb9b612SMikolaj Golub 
954ddb9b612SMikolaj Golub 	assert_sbuf_integrity(s);
955ddb9b612SMikolaj Golub 	assert_sbuf_state(s, 0);
956ddb9b612SMikolaj Golub 	KASSERT(SBUF_ISSECTION(s),
957ddb9b612SMikolaj Golub 	    ("attempt to end a section when not in a section"));
958ddb9b612SMikolaj Golub 
959ddb9b612SMikolaj Golub 	if (pad > 1) {
960ddb9b612SMikolaj Golub 		len = roundup(s->s_sect_len, pad) - s->s_sect_len;
961ddb9b612SMikolaj Golub 		for (; s->s_error == 0 && len > 0; len--)
962ddb9b612SMikolaj Golub 			sbuf_put_byte(s, c);
963ddb9b612SMikolaj Golub 	}
964ddb9b612SMikolaj Golub 	len = s->s_sect_len;
965ddb9b612SMikolaj Golub 	if (old_len == -1) {
966a8ec96afSLawrence Stewart 		s->s_rec_off = s->s_sect_len = 0;
967ddb9b612SMikolaj Golub 		SBUF_CLEARFLAG(s, SBUF_INSECTION);
968ddb9b612SMikolaj Golub 	} else {
969ddb9b612SMikolaj Golub 		s->s_sect_len += old_len;
970ddb9b612SMikolaj Golub 	}
971ddb9b612SMikolaj Golub 	if (s->s_error != 0)
972ddb9b612SMikolaj Golub 		return (-1);
973ddb9b612SMikolaj Golub 	return (len);
974ddb9b612SMikolaj Golub }
975