subr_sbuf.c revision 255805
1/*-
2 * Copyright (c) 2000-2008 Poul-Henning Kamp
3 * Copyright (c) 2000-2008 Dag-Erling Co��dan Sm��rgrav
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer
11 *    in this position and unchanged.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: head/sys/kern/subr_sbuf.c 255805 2013-09-22 23:47:56Z des $");
31
32#include <sys/param.h>
33
34#ifdef _KERNEL
35#include <sys/ctype.h>
36#include <sys/errno.h>
37#include <sys/kernel.h>
38#include <sys/malloc.h>
39#include <sys/systm.h>
40#include <sys/uio.h>
41#include <machine/stdarg.h>
42#else /* _KERNEL */
43#include <ctype.h>
44#include <errno.h>
45#include <stdarg.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <string.h>
49#endif /* _KERNEL */
50
51#include <sys/sbuf.h>
52
53#ifdef _KERNEL
54static MALLOC_DEFINE(M_SBUF, "sbuf", "string buffers");
55#define	SBMALLOC(size)		malloc(size, M_SBUF, M_WAITOK|M_ZERO)
56#define	SBFREE(buf)		free(buf, M_SBUF)
57#else /* _KERNEL */
58#define	KASSERT(e, m)
59#define	SBMALLOC(size)		calloc(1, size)
60#define	SBFREE(buf)		free(buf)
61#endif /* _KERNEL */
62
63/*
64 * Predicates
65 */
66#define	SBUF_ISDYNAMIC(s)	((s)->s_flags & SBUF_DYNAMIC)
67#define	SBUF_ISDYNSTRUCT(s)	((s)->s_flags & SBUF_DYNSTRUCT)
68#define	SBUF_ISFINISHED(s)	((s)->s_flags & SBUF_FINISHED)
69#define	SBUF_HASROOM(s)		((s)->s_len < (s)->s_size - 1)
70#define	SBUF_FREESPACE(s)	((s)->s_size - ((s)->s_len + 1))
71#define	SBUF_CANEXTEND(s)	((s)->s_flags & SBUF_AUTOEXTEND)
72#define	SBUF_ISSECTION(s)	((s)->s_flags & SBUF_INSECTION)
73
74/*
75 * Set / clear flags
76 */
77#define	SBUF_SETFLAG(s, f)	do { (s)->s_flags |= (f); } while (0)
78#define	SBUF_CLEARFLAG(s, f)	do { (s)->s_flags &= ~(f); } while (0)
79
80#define	SBUF_MINEXTENDSIZE	16		/* Should be power of 2. */
81
82#ifdef PAGE_SIZE
83#define	SBUF_MAXEXTENDSIZE	PAGE_SIZE
84#define	SBUF_MAXEXTENDINCR	PAGE_SIZE
85#else
86#define	SBUF_MAXEXTENDSIZE	4096
87#define	SBUF_MAXEXTENDINCR	4096
88#endif
89
90/*
91 * Debugging support
92 */
93#if defined(_KERNEL) && defined(INVARIANTS)
94
95static void
96_assert_sbuf_integrity(const char *fun, struct sbuf *s)
97{
98
99	KASSERT(s != NULL,
100	    ("%s called with a NULL sbuf pointer", fun));
101	KASSERT(s->s_buf != NULL,
102	    ("%s called with uninitialized or corrupt sbuf", fun));
103	KASSERT(s->s_len < s->s_size,
104	    ("wrote past end of sbuf (%jd >= %jd)",
105	    (intmax_t)s->s_len, (intmax_t)s->s_size));
106}
107
108static void
109_assert_sbuf_state(const char *fun, struct sbuf *s, int state)
110{
111
112	KASSERT((s->s_flags & SBUF_FINISHED) == state,
113	    ("%s called with %sfinished or corrupt sbuf", fun,
114	    (state ? "un" : "")));
115}
116
117#define	assert_sbuf_integrity(s) _assert_sbuf_integrity(__func__, (s))
118#define	assert_sbuf_state(s, i)	 _assert_sbuf_state(__func__, (s), (i))
119
120#else /* _KERNEL && INVARIANTS */
121
122#define	assert_sbuf_integrity(s) do { } while (0)
123#define	assert_sbuf_state(s, i)	 do { } while (0)
124
125#endif /* _KERNEL && INVARIANTS */
126
127#ifdef CTASSERT
128CTASSERT(powerof2(SBUF_MAXEXTENDSIZE));
129CTASSERT(powerof2(SBUF_MAXEXTENDINCR));
130#endif
131
132static int
133sbuf_extendsize(int size)
134{
135	int newsize;
136
137	if (size < (int)SBUF_MAXEXTENDSIZE) {
138		newsize = SBUF_MINEXTENDSIZE;
139		while (newsize < size)
140			newsize *= 2;
141	} else {
142		newsize = roundup2(size, SBUF_MAXEXTENDINCR);
143	}
144	KASSERT(newsize >= size, ("%s: %d < %d\n", __func__, newsize, size));
145	return (newsize);
146}
147
148/*
149 * Extend an sbuf.
150 */
151static int
152sbuf_extend(struct sbuf *s, int addlen)
153{
154	char *newbuf;
155	int newsize;
156
157	if (!SBUF_CANEXTEND(s))
158		return (-1);
159	newsize = sbuf_extendsize(s->s_size + addlen);
160	newbuf = SBMALLOC(newsize);
161	if (newbuf == NULL)
162		return (-1);
163	memcpy(newbuf, s->s_buf, s->s_size);
164	if (SBUF_ISDYNAMIC(s))
165		SBFREE(s->s_buf);
166	else
167		SBUF_SETFLAG(s, SBUF_DYNAMIC);
168	s->s_buf = newbuf;
169	s->s_size = newsize;
170	return (0);
171}
172
173/*
174 * Initialize the internals of an sbuf.
175 * If buf is non-NULL, it points to a static or already-allocated string
176 * big enough to hold at least length characters.
177 */
178static struct sbuf *
179sbuf_newbuf(struct sbuf *s, char *buf, int length, int flags)
180{
181
182	memset(s, 0, sizeof(*s));
183	s->s_flags = flags;
184	s->s_size = length;
185	s->s_buf = buf;
186
187	if ((s->s_flags & SBUF_AUTOEXTEND) == 0) {
188		KASSERT(s->s_size >= 0,
189		    ("attempt to create a too small sbuf"));
190	}
191
192	if (s->s_buf != NULL)
193		return (s);
194
195	if ((flags & SBUF_AUTOEXTEND) != 0)
196		s->s_size = sbuf_extendsize(s->s_size);
197
198	s->s_buf = SBMALLOC(s->s_size);
199	if (s->s_buf == NULL)
200		return (NULL);
201	SBUF_SETFLAG(s, SBUF_DYNAMIC);
202	return (s);
203}
204
205/*
206 * Initialize an sbuf.
207 * If buf is non-NULL, it points to a static or already-allocated string
208 * big enough to hold at least length characters.
209 */
210struct sbuf *
211sbuf_new(struct sbuf *s, char *buf, int length, int flags)
212{
213
214	KASSERT(length >= 0,
215	    ("attempt to create an sbuf of negative length (%d)", length));
216	KASSERT((flags & ~SBUF_USRFLAGMSK) == 0,
217	    ("%s called with invalid flags", __func__));
218
219	flags &= SBUF_USRFLAGMSK;
220	if (s != NULL)
221		return (sbuf_newbuf(s, buf, length, flags));
222
223	s = SBMALLOC(sizeof(*s));
224	if (s == NULL)
225		return (NULL);
226	if (sbuf_newbuf(s, buf, length, flags) == NULL) {
227		SBFREE(s);
228		return (NULL);
229	}
230	SBUF_SETFLAG(s, SBUF_DYNSTRUCT);
231	return (s);
232}
233
234#ifdef _KERNEL
235/*
236 * Create an sbuf with uio data
237 */
238struct sbuf *
239sbuf_uionew(struct sbuf *s, struct uio *uio, int *error)
240{
241
242	KASSERT(uio != NULL,
243	    ("%s called with NULL uio pointer", __func__));
244	KASSERT(error != NULL,
245	    ("%s called with NULL error pointer", __func__));
246
247	s = sbuf_new(s, NULL, uio->uio_resid + 1, 0);
248	if (s == NULL) {
249		*error = ENOMEM;
250		return (NULL);
251	}
252	*error = uiomove(s->s_buf, uio->uio_resid, uio);
253	if (*error != 0) {
254		sbuf_delete(s);
255		return (NULL);
256	}
257	s->s_len = s->s_size - 1;
258	if (SBUF_ISSECTION(s))
259		s->s_sect_len = s->s_size - 1;
260	*error = 0;
261	return (s);
262}
263#endif
264
265/*
266 * Clear an sbuf and reset its position.
267 */
268void
269sbuf_clear(struct sbuf *s)
270{
271
272	assert_sbuf_integrity(s);
273	/* don't care if it's finished or not */
274
275	SBUF_CLEARFLAG(s, SBUF_FINISHED);
276	s->s_error = 0;
277	s->s_len = 0;
278	s->s_sect_len = 0;
279}
280
281/*
282 * Set the sbuf's end position to an arbitrary value.
283 * Effectively truncates the sbuf at the new position.
284 */
285int
286sbuf_setpos(struct sbuf *s, ssize_t pos)
287{
288
289	assert_sbuf_integrity(s);
290	assert_sbuf_state(s, 0);
291
292	KASSERT(pos >= 0,
293	    ("attempt to seek to a negative position (%jd)", (intmax_t)pos));
294	KASSERT(pos < s->s_size,
295	    ("attempt to seek past end of sbuf (%jd >= %jd)",
296	    (intmax_t)pos, (intmax_t)s->s_size));
297	KASSERT(!SBUF_ISSECTION(s),
298	    ("attempt to seek when in a section"));
299
300	if (pos < 0 || pos > s->s_len)
301		return (-1);
302	s->s_len = pos;
303	return (0);
304}
305
306/*
307 * Set up a drain function and argument on an sbuf to flush data to
308 * when the sbuf buffer overflows.
309 */
310void
311sbuf_set_drain(struct sbuf *s, sbuf_drain_func *func, void *ctx)
312{
313
314	assert_sbuf_state(s, 0);
315	assert_sbuf_integrity(s);
316	KASSERT(func == s->s_drain_func || s->s_len == 0,
317	    ("Cannot change drain to %p on non-empty sbuf %p", func, s));
318	s->s_drain_func = func;
319	s->s_drain_arg = ctx;
320}
321
322/*
323 * Call the drain and process the return.
324 */
325static int
326sbuf_drain(struct sbuf *s)
327{
328	int len;
329
330	KASSERT(s->s_len > 0, ("Shouldn't drain empty sbuf %p", s));
331	KASSERT(s->s_error == 0, ("Called %s with error on %p", __func__, s));
332	len = s->s_drain_func(s->s_drain_arg, s->s_buf, s->s_len);
333	if (len < 0) {
334		s->s_error = -len;
335		return (s->s_error);
336	}
337	KASSERT(len > 0 && len <= s->s_len,
338	    ("Bad drain amount %d for sbuf %p", len, s));
339	s->s_len -= len;
340	/*
341	 * Fast path for the expected case where all the data was
342	 * drained.
343	 */
344	if (s->s_len == 0)
345		return (0);
346	/*
347	 * Move the remaining characters to the beginning of the
348	 * string.
349	 */
350	memmove(s->s_buf, s->s_buf + len, s->s_len);
351	return (0);
352}
353
354/*
355 * Append a byte to an sbuf.  This is the core function for appending
356 * to an sbuf and is the main place that deals with extending the
357 * buffer and marking overflow.
358 */
359static void
360sbuf_put_byte(struct sbuf *s, int c)
361{
362
363	assert_sbuf_integrity(s);
364	assert_sbuf_state(s, 0);
365
366	if (s->s_error != 0)
367		return;
368	if (SBUF_FREESPACE(s) <= 0) {
369		/*
370		 * If there is a drain, use it, otherwise extend the
371		 * buffer.
372		 */
373		if (s->s_drain_func != NULL)
374			(void)sbuf_drain(s);
375		else if (sbuf_extend(s, 1) < 0)
376			s->s_error = ENOMEM;
377		if (s->s_error != 0)
378			return;
379	}
380	s->s_buf[s->s_len++] = c;
381	if (SBUF_ISSECTION(s))
382		s->s_sect_len++;
383}
384
385/*
386 * Append a byte string to an sbuf.
387 */
388int
389sbuf_bcat(struct sbuf *s, const void *buf, size_t len)
390{
391	const char *str = buf;
392	const char *end = str + len;
393
394	assert_sbuf_integrity(s);
395	assert_sbuf_state(s, 0);
396
397	if (s->s_error != 0)
398		return (-1);
399	for (; str < end; str++) {
400		sbuf_put_byte(s, *str);
401		if (s->s_error != 0)
402			return (-1);
403	}
404	return (0);
405}
406
407#ifdef _KERNEL
408/*
409 * Copy a byte string from userland into an sbuf.
410 */
411int
412sbuf_bcopyin(struct sbuf *s, const void *uaddr, size_t len)
413{
414
415	assert_sbuf_integrity(s);
416	assert_sbuf_state(s, 0);
417	KASSERT(s->s_drain_func == NULL,
418	    ("Nonsensical copyin to sbuf %p with a drain", s));
419
420	if (s->s_error != 0)
421		return (-1);
422	if (len == 0)
423		return (0);
424	if (len > SBUF_FREESPACE(s)) {
425		sbuf_extend(s, len - SBUF_FREESPACE(s));
426		if (SBUF_FREESPACE(s) < len)
427			len = SBUF_FREESPACE(s);
428	}
429	if (copyin(uaddr, s->s_buf + s->s_len, len) != 0)
430		return (-1);
431	s->s_len += len;
432
433	return (0);
434}
435#endif
436
437/*
438 * Copy a byte string into an sbuf.
439 */
440int
441sbuf_bcpy(struct sbuf *s, const void *buf, size_t len)
442{
443
444	assert_sbuf_integrity(s);
445	assert_sbuf_state(s, 0);
446
447	sbuf_clear(s);
448	return (sbuf_bcat(s, buf, len));
449}
450
451/*
452 * Append a string to an sbuf.
453 */
454int
455sbuf_cat(struct sbuf *s, const char *str)
456{
457
458	assert_sbuf_integrity(s);
459	assert_sbuf_state(s, 0);
460
461	if (s->s_error != 0)
462		return (-1);
463
464	while (*str != '\0') {
465		sbuf_put_byte(s, *str++);
466		if (s->s_error != 0)
467			return (-1);
468	}
469	return (0);
470}
471
472#ifdef _KERNEL
473/*
474 * Append a string from userland to an sbuf.
475 */
476int
477sbuf_copyin(struct sbuf *s, const void *uaddr, size_t len)
478{
479	size_t done;
480
481	assert_sbuf_integrity(s);
482	assert_sbuf_state(s, 0);
483	KASSERT(s->s_drain_func == NULL,
484	    ("Nonsensical copyin to sbuf %p with a drain", s));
485
486	if (s->s_error != 0)
487		return (-1);
488
489	if (len == 0)
490		len = SBUF_FREESPACE(s);	/* XXX return 0? */
491	if (len > SBUF_FREESPACE(s)) {
492		sbuf_extend(s, len);
493		if (SBUF_FREESPACE(s) < len)
494			len = SBUF_FREESPACE(s);
495	}
496	switch (copyinstr(uaddr, s->s_buf + s->s_len, len + 1, &done)) {
497	case ENAMETOOLONG:
498		s->s_error = ENOMEM;
499		/* fall through */
500	case 0:
501		s->s_len += done - 1;
502		if (SBUF_ISSECTION(s))
503			s->s_sect_len += done - 1;
504		break;
505	default:
506		return (-1);	/* XXX */
507	}
508
509	return (done);
510}
511#endif
512
513/*
514 * Copy a string into an sbuf.
515 */
516int
517sbuf_cpy(struct sbuf *s, const char *str)
518{
519
520	assert_sbuf_integrity(s);
521	assert_sbuf_state(s, 0);
522
523	sbuf_clear(s);
524	return (sbuf_cat(s, str));
525}
526
527/*
528 * Format the given argument list and append the resulting string to an sbuf.
529 */
530#ifdef _KERNEL
531
532/*
533 * Append a non-NUL character to an sbuf.  This prototype signature is
534 * suitable for use with kvprintf(9).
535 */
536static void
537sbuf_putc_func(int c, void *arg)
538{
539
540	if (c != '\0')
541		sbuf_put_byte(arg, c);
542}
543
544int
545sbuf_vprintf(struct sbuf *s, const char *fmt, va_list ap)
546{
547
548	assert_sbuf_integrity(s);
549	assert_sbuf_state(s, 0);
550
551	KASSERT(fmt != NULL,
552	    ("%s called with a NULL format string", __func__));
553
554	(void)kvprintf(fmt, sbuf_putc_func, s, 10, ap);
555	if (s->s_error != 0)
556		return (-1);
557	return (0);
558}
559#else /* !_KERNEL */
560int
561sbuf_vprintf(struct sbuf *s, const char *fmt, va_list ap)
562{
563	va_list ap_copy;
564	int error, len;
565
566	assert_sbuf_integrity(s);
567	assert_sbuf_state(s, 0);
568
569	KASSERT(fmt != NULL,
570	    ("%s called with a NULL format string", __func__));
571
572	if (s->s_error != 0)
573		return (-1);
574
575	/*
576	 * For the moment, there is no way to get vsnprintf(3) to hand
577	 * back a character at a time, to push everything into
578	 * sbuf_putc_func() as was done for the kernel.
579	 *
580	 * In userspace, while drains are useful, there's generally
581	 * not a problem attempting to malloc(3) on out of space.  So
582	 * expand a userland sbuf if there is not enough room for the
583	 * data produced by sbuf_[v]printf(3).
584	 */
585
586	error = 0;
587	do {
588		va_copy(ap_copy, ap);
589		len = vsnprintf(&s->s_buf[s->s_len], SBUF_FREESPACE(s) + 1,
590		    fmt, ap_copy);
591		va_end(ap_copy);
592
593		if (SBUF_FREESPACE(s) >= len)
594			break;
595		/* Cannot print with the current available space. */
596		if (s->s_drain_func != NULL && s->s_len > 0)
597			error = sbuf_drain(s);
598		else
599			error = sbuf_extend(s, len - SBUF_FREESPACE(s));
600	} while (error == 0);
601
602	/*
603	 * s->s_len is the length of the string, without the terminating nul.
604	 * When updating s->s_len, we must subtract 1 from the length that
605	 * we passed into vsnprintf() because that length includes the
606	 * terminating nul.
607	 *
608	 * vsnprintf() returns the amount that would have been copied,
609	 * given sufficient space, so don't over-increment s_len.
610	 */
611	if (SBUF_FREESPACE(s) < len)
612		len = SBUF_FREESPACE(s);
613	s->s_len += len;
614	if (SBUF_ISSECTION(s))
615		s->s_sect_len += len;
616	if (!SBUF_HASROOM(s) && !SBUF_CANEXTEND(s))
617		s->s_error = ENOMEM;
618
619	KASSERT(s->s_len < s->s_size,
620	    ("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size));
621
622	if (s->s_error != 0)
623		return (-1);
624	return (0);
625}
626#endif /* _KERNEL */
627
628/*
629 * Format the given arguments and append the resulting string to an sbuf.
630 */
631int
632sbuf_printf(struct sbuf *s, const char *fmt, ...)
633{
634	va_list ap;
635	int result;
636
637	va_start(ap, fmt);
638	result = sbuf_vprintf(s, fmt, ap);
639	va_end(ap);
640	return (result);
641}
642
643/*
644 * Append a character to an sbuf.
645 */
646int
647sbuf_putc(struct sbuf *s, int c)
648{
649
650	sbuf_put_byte(s, c);
651	if (s->s_error != 0)
652		return (-1);
653	return (0);
654}
655
656/*
657 * Trim whitespace characters from end of an sbuf.
658 */
659int
660sbuf_trim(struct sbuf *s)
661{
662
663	assert_sbuf_integrity(s);
664	assert_sbuf_state(s, 0);
665	KASSERT(s->s_drain_func == NULL,
666	    ("%s makes no sense on sbuf %p with drain", __func__, s));
667
668	if (s->s_error != 0)
669		return (-1);
670
671	while (s->s_len > 0 && isspace(s->s_buf[s->s_len-1])) {
672		--s->s_len;
673		if (SBUF_ISSECTION(s))
674			s->s_sect_len--;
675	}
676
677	return (0);
678}
679
680/*
681 * Check if an sbuf has an error.
682 */
683int
684sbuf_error(const struct sbuf *s)
685{
686
687	return (s->s_error);
688}
689
690/*
691 * Finish off an sbuf.
692 */
693int
694sbuf_finish(struct sbuf *s)
695{
696
697	assert_sbuf_integrity(s);
698	assert_sbuf_state(s, 0);
699
700	if (s->s_drain_func != NULL) {
701		while (s->s_len > 0 && s->s_error == 0)
702			s->s_error = sbuf_drain(s);
703	}
704	s->s_buf[s->s_len] = '\0';
705	SBUF_SETFLAG(s, SBUF_FINISHED);
706#ifdef _KERNEL
707	return (s->s_error);
708#else
709	if (s->s_error != 0) {
710		errno = s->s_error;
711		return (-1);
712	}
713	return (0);
714#endif
715}
716
717/*
718 * Return a pointer to the sbuf data.
719 */
720char *
721sbuf_data(struct sbuf *s)
722{
723
724	assert_sbuf_integrity(s);
725	assert_sbuf_state(s, SBUF_FINISHED);
726	KASSERT(s->s_drain_func == NULL,
727	    ("%s makes no sense on sbuf %p with drain", __func__, s));
728
729	return (s->s_buf);
730}
731
732/*
733 * Return the length of the sbuf data.
734 */
735ssize_t
736sbuf_len(struct sbuf *s)
737{
738
739	assert_sbuf_integrity(s);
740	/* don't care if it's finished or not */
741	KASSERT(s->s_drain_func == NULL,
742	    ("%s makes no sense on sbuf %p with drain", __func__, s));
743
744	if (s->s_error != 0)
745		return (-1);
746	return (s->s_len);
747}
748
749/*
750 * Clear an sbuf, free its buffer if necessary.
751 */
752void
753sbuf_delete(struct sbuf *s)
754{
755	int isdyn;
756
757	assert_sbuf_integrity(s);
758	/* don't care if it's finished or not */
759
760	if (SBUF_ISDYNAMIC(s))
761		SBFREE(s->s_buf);
762	isdyn = SBUF_ISDYNSTRUCT(s);
763	memset(s, 0, sizeof(*s));
764	if (isdyn)
765		SBFREE(s);
766}
767
768/*
769 * Check if an sbuf has been finished.
770 */
771int
772sbuf_done(const struct sbuf *s)
773{
774
775	return (SBUF_ISFINISHED(s));
776}
777
778/*
779 * Start a section.
780 */
781void
782sbuf_start_section(struct sbuf *s, ssize_t *old_lenp)
783{
784
785	assert_sbuf_integrity(s);
786	assert_sbuf_state(s, 0);
787
788	if (!SBUF_ISSECTION(s)) {
789		KASSERT(s->s_sect_len == 0,
790		    ("s_sect_len != 0 when starting a section"));
791		if (old_lenp != NULL)
792			*old_lenp = -1;
793		SBUF_SETFLAG(s, SBUF_INSECTION);
794	} else {
795		KASSERT(old_lenp != NULL,
796		    ("s_sect_len should be saved when starting a subsection"));
797		*old_lenp = s->s_sect_len;
798		s->s_sect_len = 0;
799	}
800}
801
802/*
803 * End the section padding to the specified length with the specified
804 * character.
805 */
806ssize_t
807sbuf_end_section(struct sbuf *s, ssize_t old_len, size_t pad, int c)
808{
809	ssize_t len;
810
811	assert_sbuf_integrity(s);
812	assert_sbuf_state(s, 0);
813	KASSERT(SBUF_ISSECTION(s),
814	    ("attempt to end a section when not in a section"));
815
816	if (pad > 1) {
817		len = roundup(s->s_sect_len, pad) - s->s_sect_len;
818		for (; s->s_error == 0 && len > 0; len--)
819			sbuf_put_byte(s, c);
820	}
821	len = s->s_sect_len;
822	if (old_len == -1) {
823		s->s_sect_len = 0;
824		SBUF_CLEARFLAG(s, SBUF_INSECTION);
825	} else {
826		s->s_sect_len += old_len;
827	}
828	if (s->s_error != 0)
829		return (-1);
830	return (len);
831}
832