subr_sbuf.c revision 89121
1239281Sgonzo/*-
2239281Sgonzo * Copyright (c) 2000 Poul-Henning Kamp and Dag-Erling Co�dan Sm�rgrav
3239281Sgonzo * All rights reserved.
4239281Sgonzo *
5239281Sgonzo * Redistribution and use in source and binary forms, with or without
6239281Sgonzo * modification, are permitted provided that the following conditions
7239281Sgonzo * are met:
8239281Sgonzo * 1. Redistributions of source code must retain the above copyright
9239281Sgonzo *    notice, this list of conditions and the following disclaimer
10239281Sgonzo *    in this position and unchanged.
11239281Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
12239281Sgonzo *    notice, this list of conditions and the following disclaimer in the
13239281Sgonzo *    documentation and/or other materials provided with the distribution.
14239281Sgonzo * 3. The name of the author may not be used to endorse or promote products
15239281Sgonzo *    derived from this software without specific prior written permission.
16239281Sgonzo *
17239281Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18239281Sgonzo * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19239281Sgonzo * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20239281Sgonzo * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21239281Sgonzo * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22239281Sgonzo * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23239281Sgonzo * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24239281Sgonzo * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25239281Sgonzo * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26239281Sgonzo * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27239281Sgonzo *
28239281Sgonzo *      $FreeBSD: head/sys/kern/subr_sbuf.c 89121 2002-01-09 07:29:28Z kbyanc $
29239281Sgonzo */
30239281Sgonzo
31239281Sgonzo#include <sys/param.h>
32239281Sgonzo
33239281Sgonzo#ifdef _KERNEL
34239281Sgonzo#include <sys/ctype.h>
35239281Sgonzo#include <sys/kernel.h>
36239281Sgonzo#include <sys/malloc.h>
37239281Sgonzo#include <sys/systm.h>
38239281Sgonzo#include <sys/uio.h>
39239281Sgonzo#include <machine/stdarg.h>
40239281Sgonzo#else /* _KERNEL */
41239281Sgonzo#include <ctype.h>
42239281Sgonzo#include <stdarg.h>
43239281Sgonzo#include <stdio.h>
44239281Sgonzo#include <stdlib.h>
45239281Sgonzo#include <string.h>
46239281Sgonzo#endif /* _KERNEL */
47239281Sgonzo
48239281Sgonzo#include <sys/sbuf.h>
49239281Sgonzo
50239281Sgonzo#ifdef _KERNEL
51239281SgonzoMALLOC_DEFINE(M_SBUF, "sbuf", "string buffers");
52239281Sgonzo#define	SBMALLOC(size)		malloc(size, M_SBUF, M_WAITOK)
53239281Sgonzo#define	SBFREE(buf)		free(buf, M_SBUF)
54239281Sgonzo#else /* _KERNEL */
55239281Sgonzo#define	KASSERT(e, m)
56239281Sgonzo#define	SBMALLOC(size)		malloc(size)
57239281Sgonzo#define	SBFREE(buf)		free(buf)
58239281Sgonzo#define	min(x,y)		MIN(x,y)
59239281Sgonzo#endif /* _KERNEL */
60239281Sgonzo
61239281Sgonzo/*
62239281Sgonzo * Predicates
63239281Sgonzo */
64239281Sgonzo#define	SBUF_ISDYNAMIC(s)	((s)->s_flags & SBUF_DYNAMIC)
65239281Sgonzo#define	SBUF_ISDYNSTRUCT(s)	((s)->s_flags & SBUF_DYNSTRUCT)
66239281Sgonzo#define	SBUF_ISFINISHED(s)	((s)->s_flags & SBUF_FINISHED)
67239281Sgonzo#define	SBUF_HASOVERFLOWED(s)	((s)->s_flags & SBUF_OVERFLOWED)
68239281Sgonzo#define	SBUF_HASROOM(s)		((s)->s_len < (s)->s_size - 1)
69239281Sgonzo#define	SBUF_FREESPACE(s)	((s)->s_size - (s)->s_len - 1)
70239281Sgonzo#define	SBUF_CANEXTEND(s)	((s)->s_flags & SBUF_AUTOEXTEND)
71239281Sgonzo
72239281Sgonzo/*
73239281Sgonzo * Set / clear flags
74239281Sgonzo */
75239281Sgonzo#define	SBUF_SETFLAG(s, f)	do { (s)->s_flags |= (f); } while (0)
76239281Sgonzo#define	SBUF_CLEARFLAG(s, f)	do { (s)->s_flags &= ~(f); } while (0)
77239281Sgonzo
78239281Sgonzo#define	SBUF_MINEXTENDSIZE	16		/* Should be power of 2. */
79239281Sgonzo#define	SBUF_MAXEXTENDSIZE	PAGE_SIZE
80239281Sgonzo#define	SBUF_MAXEXTENDINCR	PAGE_SIZE
81239281Sgonzo
82239281Sgonzo/*
83239281Sgonzo * Debugging support
84239281Sgonzo */
85239281Sgonzo#if defined(_KERNEL) && defined(INVARIANTS)
86239281Sgonzostatic void
87239281Sgonzo_assert_sbuf_integrity(char *fun, struct sbuf *s)
88239281Sgonzo{
89239281Sgonzo	KASSERT(s != NULL,
90239281Sgonzo	    ("%s called with a NULL sbuf pointer", fun));
91239281Sgonzo	KASSERT(s->s_buf != NULL,
92239281Sgonzo	    ("%s called with uninitialized or corrupt sbuf", fun));
93239281Sgonzo	KASSERT(s->s_len < s->s_size,
94239281Sgonzo	    ("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size));
95239281Sgonzo}
96239281Sgonzo
97239281Sgonzostatic void
98239281Sgonzo_assert_sbuf_state(char *fun, struct sbuf *s, int state)
99239281Sgonzo{
100239281Sgonzo	KASSERT((s->s_flags & SBUF_FINISHED) == state,
101239281Sgonzo	    ("%s called with %sfinished or corrupt sbuf", fun,
102239281Sgonzo	    (state ? "un" : "")));
103239281Sgonzo}
104239281Sgonzo#define	assert_sbuf_integrity(s) _assert_sbuf_integrity(__func__, (s))
105239281Sgonzo#define	assert_sbuf_state(s, i)	 _assert_sbuf_state(__func__, (s), (i))
106239281Sgonzo#else /* _KERNEL && INVARIANTS */
107239281Sgonzo#define	assert_sbuf_integrity(s) do { } while (0)
108239281Sgonzo#define	assert_sbuf_state(s, i)	 do { } while (0)
109239281Sgonzo#endif /* _KERNEL && INVARIANTS */
110239281Sgonzo
111239281Sgonzostatic int
112239281Sgonzosbuf_extendsize(int size)
113239281Sgonzo{
114239281Sgonzo	int newsize;
115239281Sgonzo
116239281Sgonzo	newsize = SBUF_MINEXTENDSIZE;
117239281Sgonzo	while (newsize < size) {
118239281Sgonzo		if (newsize < SBUF_MAXEXTENDSIZE)
119239281Sgonzo			newsize *= 2;
120239281Sgonzo		else
121239281Sgonzo			newsize += SBUF_MAXEXTENDINCR;
122239281Sgonzo	}
123239281Sgonzo
124239281Sgonzo	return (newsize);
125239281Sgonzo}
126239281Sgonzo
127239281Sgonzo
128239281Sgonzo/*
129239281Sgonzo * Extend an sbuf.
130239281Sgonzo */
131239281Sgonzostatic int
132239281Sgonzosbuf_extend(struct sbuf *s, int addlen)
133239281Sgonzo{
134239281Sgonzo	char *newbuf;
135239281Sgonzo	int newsize;
136239281Sgonzo
137239281Sgonzo	if (!SBUF_CANEXTEND(s))
138239281Sgonzo		return (-1);
139239281Sgonzo
140239281Sgonzo	newsize = sbuf_extendsize(s->s_size + addlen);
141239281Sgonzo	newbuf = (char *)SBMALLOC(newsize);
142239281Sgonzo	if (newbuf == NULL)
143239281Sgonzo		return (-1);
144239281Sgonzo	bcopy(s->s_buf, newbuf, s->s_size);
145239281Sgonzo	if (SBUF_ISDYNAMIC(s))
146239281Sgonzo		SBFREE(s->s_buf);
147239281Sgonzo	else
148239281Sgonzo		SBUF_SETFLAG(s, SBUF_DYNAMIC);
149239281Sgonzo	s->s_buf = newbuf;
150239281Sgonzo	s->s_size = newsize;
151239281Sgonzo	return (0);
152239281Sgonzo}
153239281Sgonzo
154239281Sgonzo/*
155239281Sgonzo * Initialize an sbuf.
156239281Sgonzo * If buf is non-NULL, it points to a static or already-allocated string
157245672Skientzle * big enough to hold at least length characters.
158245672Skientzle */
159239281Sgonzostruct sbuf *
160245672Skientzlesbuf_new(struct sbuf *s, char *buf, int length, int flags)
161239281Sgonzo{
162239281Sgonzo	KASSERT(length >= 0,
163239281Sgonzo	    ("attempt to create an sbuf of negative length (%d)", length));
164239281Sgonzo	KASSERT((flags & ~SBUF_USRFLAGMSK) == 0,
165252229Srpaulo	    ("%s called with invalid flags", __func__));
166252229Srpaulo
167252229Srpaulo	flags &= SBUF_USRFLAGMSK;
168239281Sgonzo	if (s == NULL) {
169239281Sgonzo		s = (struct sbuf *)SBMALLOC(sizeof *s);
170239281Sgonzo		if (s == NULL)
171239281Sgonzo			return (NULL);
172239281Sgonzo		bzero(s, sizeof *s);
173239281Sgonzo		s->s_flags = flags;
174239281Sgonzo		SBUF_SETFLAG(s, SBUF_DYNSTRUCT);
175239281Sgonzo	} else {
176239281Sgonzo		bzero(s, sizeof *s);
177239281Sgonzo		s->s_flags = flags;
178239281Sgonzo	}
179239281Sgonzo	s->s_size = length;
180239281Sgonzo	if (buf) {
181239281Sgonzo		s->s_buf = buf;
182239281Sgonzo		return (s);
183239281Sgonzo	}
184239281Sgonzo	if (flags & SBUF_AUTOEXTEND)
185239281Sgonzo		s->s_size = sbuf_extendsize(s->s_size);
186239281Sgonzo	s->s_buf = (char *)SBMALLOC(s->s_size);
187239281Sgonzo	if (s->s_buf == NULL) {
188239281Sgonzo		if (SBUF_ISDYNSTRUCT(s))
189239281Sgonzo			SBFREE(s);
190239281Sgonzo		return (NULL);
191239281Sgonzo	}
192239281Sgonzo	SBUF_SETFLAG(s, SBUF_DYNAMIC);
193239281Sgonzo	return (s);
194239281Sgonzo}
195239281Sgonzo
196239281Sgonzo#ifdef _KERNEL
197239281Sgonzo/*
198239281Sgonzo * Create an sbuf with uio data
199239281Sgonzo */
200239281Sgonzostruct sbuf *
201239281Sgonzosbuf_uionew(struct sbuf *s, struct uio *uio, int *error)
202239281Sgonzo{
203239281Sgonzo	KASSERT(uio != NULL,
204239281Sgonzo	    ("%s called with NULL uio pointer", __func__));
205239281Sgonzo	KASSERT(error != NULL,
206239281Sgonzo	    ("%s called with NULL error pointer", __func__));
207239281Sgonzo
208240518Seadler	s = sbuf_new(s, NULL, uio->uio_resid + 1, 0);
209239281Sgonzo	if (s == NULL) {
210239281Sgonzo		*error = ENOMEM;
211239281Sgonzo		return (NULL);
212239281Sgonzo	}
213239281Sgonzo	*error = uiomove(s->s_buf, uio->uio_resid, uio);
214239281Sgonzo	if (*error != 0) {
215239281Sgonzo		sbuf_delete(s);
216239281Sgonzo		return (NULL);
217239281Sgonzo	}
218239281Sgonzo	s->s_len = s->s_size - 1;
219239281Sgonzo	*error = 0;
220239281Sgonzo	return (s);
221239281Sgonzo}
222239281Sgonzo#endif
223239281Sgonzo
224239281Sgonzo/*
225239281Sgonzo * Clear an sbuf and reset its position.
226239281Sgonzo */
227239281Sgonzovoid
228239281Sgonzosbuf_clear(struct sbuf *s)
229239281Sgonzo{
230239281Sgonzo	assert_sbuf_integrity(s);
231239281Sgonzo	/* don't care if it's finished or not */
232239281Sgonzo
233239281Sgonzo	SBUF_CLEARFLAG(s, SBUF_FINISHED);
234239281Sgonzo	SBUF_CLEARFLAG(s, SBUF_OVERFLOWED);
235239281Sgonzo	s->s_len = 0;
236239281Sgonzo}
237239281Sgonzo
238239281Sgonzo/*
239239281Sgonzo * Set the sbuf's end position to an arbitrary value.
240239281Sgonzo * Effectively truncates the sbuf at the new position.
241239281Sgonzo */
242239281Sgonzoint
243239281Sgonzosbuf_setpos(struct sbuf *s, int pos)
244239281Sgonzo{
245239281Sgonzo	assert_sbuf_integrity(s);
246239281Sgonzo	assert_sbuf_state(s, 0);
247239281Sgonzo
248239281Sgonzo	KASSERT(pos >= 0,
249239281Sgonzo	    ("attempt to seek to a negative position (%d)", pos));
250239281Sgonzo	KASSERT(pos < s->s_size,
251239281Sgonzo	    ("attempt to seek past end of sbuf (%d >= %d)", pos, s->s_size));
252239281Sgonzo
253239281Sgonzo	if (pos < 0 || pos > s->s_len)
254239281Sgonzo		return (-1);
255239281Sgonzo	s->s_len = pos;
256239281Sgonzo	return (0);
257239281Sgonzo}
258239281Sgonzo
259239281Sgonzo/*
260239281Sgonzo * Append a byte string to an sbuf.
261239281Sgonzo */
262239281Sgonzoint
263239281Sgonzosbuf_bcat(struct sbuf *s, const char *str, size_t len)
264239281Sgonzo{
265239281Sgonzo	assert_sbuf_integrity(s);
266239281Sgonzo	assert_sbuf_state(s, 0);
267239281Sgonzo
268239281Sgonzo	if (SBUF_HASOVERFLOWED(s))
269239281Sgonzo		return (-1);
270239281Sgonzo
271239281Sgonzo	while (len--) {
272239281Sgonzo		if (!SBUF_HASROOM(s) && sbuf_extend(s, len) < 0)
273239281Sgonzo			break;
274239281Sgonzo		s->s_buf[s->s_len++] = *str++;
275239281Sgonzo	}
276239281Sgonzo	if (len) {
277239281Sgonzo		SBUF_SETFLAG(s, SBUF_OVERFLOWED);
278239281Sgonzo		return (-1);
279239281Sgonzo	}
280239281Sgonzo	return (0);
281239281Sgonzo}
282239281Sgonzo
283239281Sgonzo#ifdef _KERNEL
284239281Sgonzo/*
285239281Sgonzo * Copy a byte string from userland into an sbuf.
286239281Sgonzo */
287239281Sgonzoint
288239281Sgonzosbuf_bcopyin(struct sbuf *s, const void *uaddr, size_t len)
289239281Sgonzo{
290239281Sgonzo	assert_sbuf_integrity(s);
291239281Sgonzo	assert_sbuf_state(s, 0);
292239281Sgonzo
293239281Sgonzo	if (SBUF_HASOVERFLOWED(s))
294239281Sgonzo		return (-1);
295239281Sgonzo
296239281Sgonzo	if (len == 0)
297239281Sgonzo		return (0);
298239281Sgonzo	if (len > SBUF_FREESPACE(s)) {
299239281Sgonzo		sbuf_extend(s, len - SBUF_FREESPACE(s));
300239281Sgonzo		len = min(len, SBUF_FREESPACE(s));
301239281Sgonzo	}
302239281Sgonzo	if (copyin(uaddr, s->s_buf + s->s_len, len) != 0)
303239281Sgonzo		return (-1);
304239281Sgonzo	s->s_len += len;
305239281Sgonzo
306239281Sgonzo	return (0);
307239281Sgonzo}
308239281Sgonzo#endif
309239281Sgonzo
310239281Sgonzo/*
311239281Sgonzo * Copy a byte string into an sbuf.
312239281Sgonzo */
313239281Sgonzoint
314239281Sgonzosbuf_bcpy(struct sbuf *s, const char *str, size_t len)
315239281Sgonzo{
316239281Sgonzo	assert_sbuf_integrity(s);
317239281Sgonzo	assert_sbuf_state(s, 0);
318239281Sgonzo
319239281Sgonzo	sbuf_clear(s);
320239281Sgonzo	return (sbuf_bcat(s, str, len));
321239281Sgonzo}
322239281Sgonzo
323239281Sgonzo/*
324239281Sgonzo * Append a string to an sbuf.
325239281Sgonzo */
326239281Sgonzoint
327239281Sgonzosbuf_cat(struct sbuf *s, const char *str)
328239281Sgonzo{
329239281Sgonzo	assert_sbuf_integrity(s);
330239281Sgonzo	assert_sbuf_state(s, 0);
331239281Sgonzo
332239281Sgonzo	if (SBUF_HASOVERFLOWED(s))
333239281Sgonzo		return (-1);
334239281Sgonzo
335239281Sgonzo	while (*str) {
336239281Sgonzo		if (!SBUF_HASROOM(s) && sbuf_extend(s, strlen(str)) < 0)
337239281Sgonzo			break;
338239281Sgonzo		s->s_buf[s->s_len++] = *str++;
339239281Sgonzo	}
340239281Sgonzo	if (*str) {
341239281Sgonzo		SBUF_SETFLAG(s, SBUF_OVERFLOWED);
342239281Sgonzo		return (-1);
343239281Sgonzo	}
344239281Sgonzo	return (0);
345239281Sgonzo}
346239281Sgonzo
347239281Sgonzo#ifdef _KERNEL
348239281Sgonzo/*
349239281Sgonzo * Append a string from userland to an sbuf.
350239281Sgonzo */
351239281Sgonzoint
352239281Sgonzosbuf_copyin(struct sbuf *s, const void *uaddr, size_t len)
353239281Sgonzo{
354239281Sgonzo	size_t done;
355239281Sgonzo
356239281Sgonzo	assert_sbuf_integrity(s);
357239281Sgonzo	assert_sbuf_state(s, 0);
358239281Sgonzo
359239281Sgonzo	if (SBUF_HASOVERFLOWED(s))
360239281Sgonzo		return (-1);
361239281Sgonzo
362239281Sgonzo	if (len == 0)
363239281Sgonzo		len = SBUF_FREESPACE(s);	/* XXX return 0? */
364239281Sgonzo	if (len > SBUF_FREESPACE(s)) {
365239281Sgonzo		sbuf_extend(s, len);
366239281Sgonzo		len = min(len, SBUF_FREESPACE(s));
367239281Sgonzo	}
368239281Sgonzo	switch (copyinstr(uaddr, s->s_buf + s->s_len, len + 1, &done)) {
369239281Sgonzo	case ENAMETOOLONG:
370239281Sgonzo		SBUF_SETFLAG(s, SBUF_OVERFLOWED);
371239281Sgonzo		/* fall through */
372239281Sgonzo	case 0:
373239281Sgonzo		s->s_len += done - 1;
374239281Sgonzo		break;
375239281Sgonzo	default:
376239281Sgonzo		return (-1);	/* XXX */
377239281Sgonzo	}
378239281Sgonzo
379239281Sgonzo	return (0);
380239281Sgonzo}
381239281Sgonzo#endif
382239281Sgonzo
383239281Sgonzo/*
384239281Sgonzo * Copy a string into an sbuf.
385239281Sgonzo */
386239281Sgonzoint
387239281Sgonzosbuf_cpy(struct sbuf *s, const char *str)
388239281Sgonzo{
389239281Sgonzo	assert_sbuf_integrity(s);
390239281Sgonzo	assert_sbuf_state(s, 0);
391239281Sgonzo
392239281Sgonzo	sbuf_clear(s);
393239281Sgonzo	return (sbuf_cat(s, str));
394239281Sgonzo}
395239281Sgonzo
396239281Sgonzo/*
397245672Skientzle * Format the given argument list and append the resulting string to an sbuf.
398239281Sgonzo */
399239281Sgonzoint
400239281Sgonzosbuf_vprintf(struct sbuf *s, const char *fmt, va_list ap)
401239281Sgonzo{
402245672Skientzle	int len;
403245672Skientzle
404245672Skientzle	assert_sbuf_integrity(s);
405245672Skientzle	assert_sbuf_state(s, 0);
406245672Skientzle
407239281Sgonzo	KASSERT(fmt != NULL,
408239281Sgonzo	    ("%s called with a NULL format string", __func__));
409239281Sgonzo
410239281Sgonzo	if (SBUF_HASOVERFLOWED(s))
411239281Sgonzo		return (-1);
412239281Sgonzo
413239281Sgonzo	do {
414239281Sgonzo		len = vsnprintf(&s->s_buf[s->s_len], SBUF_FREESPACE(s) + 1,
415239281Sgonzo		    fmt, ap);
416239281Sgonzo	} while (len > SBUF_FREESPACE(s) &&
417239281Sgonzo	    sbuf_extend(s, len - SBUF_FREESPACE(s)) == 0);
418239281Sgonzo
419239281Sgonzo	/*
420239281Sgonzo	 * s->s_len is the length of the string, without the terminating nul.
421266152Sian	 * When updating s->s_len, we must subtract 1 from the length that
422266152Sian	 * we passed into vsnprintf() because that length includes the
423266152Sian	 * terminating nul.
424266152Sian	 *
425239281Sgonzo	 * vsnprintf() returns the amount that would have been copied,
426239281Sgonzo	 * given sufficient space, hence the min() calculation below.
427239281Sgonzo	 */
428239281Sgonzo	s->s_len += min(len, SBUF_FREESPACE(s));
429239281Sgonzo	if (!SBUF_HASROOM(s))
430239281Sgonzo		SBUF_SETFLAG(s, SBUF_OVERFLOWED);
431239281Sgonzo
432239281Sgonzo	KASSERT(s->s_len < s->s_size,
433239281Sgonzo	    ("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size));
434239281Sgonzo
435239281Sgonzo	if (SBUF_HASOVERFLOWED(s))
436239281Sgonzo		return (-1);
437239281Sgonzo	return (0);
438239281Sgonzo}
439239281Sgonzo
440239281Sgonzo/*
441239281Sgonzo * Format the given arguments and append the resulting string to an sbuf.
442239281Sgonzo */
443239281Sgonzoint
444239281Sgonzosbuf_printf(struct sbuf *s, const char *fmt, ...)
445239281Sgonzo{
446239281Sgonzo	va_list ap;
447239281Sgonzo	int result;
448239281Sgonzo
449239281Sgonzo	va_start(ap, fmt);
450239281Sgonzo	result = sbuf_vprintf(s, fmt, ap);
451239281Sgonzo	va_end(ap);
452239281Sgonzo	return(result);
453239281Sgonzo}
454239281Sgonzo
455239281Sgonzo/*
456239281Sgonzo * Append a character to an sbuf.
457239281Sgonzo */
458239281Sgonzoint
459239281Sgonzosbuf_putc(struct sbuf *s, int c)
460239281Sgonzo{
461239281Sgonzo	assert_sbuf_integrity(s);
462239281Sgonzo	assert_sbuf_state(s, 0);
463239281Sgonzo
464239281Sgonzo	if (SBUF_HASOVERFLOWED(s))
465239281Sgonzo		return (-1);
466239281Sgonzo
467239281Sgonzo	if (!SBUF_HASROOM(s) && sbuf_extend(s, 1) < 0) {
468239281Sgonzo		SBUF_SETFLAG(s, SBUF_OVERFLOWED);
469239281Sgonzo		return (-1);
470239281Sgonzo	}
471239281Sgonzo	if (c != '\0')
472239281Sgonzo	    s->s_buf[s->s_len++] = c;
473239281Sgonzo	return (0);
474239281Sgonzo}
475239281Sgonzo
476239281Sgonzo/*
477239281Sgonzo * Trim whitespace characters from end of an sbuf.
478239281Sgonzo */
479239281Sgonzoint
480239281Sgonzosbuf_trim(struct sbuf *s)
481239281Sgonzo{
482239281Sgonzo	assert_sbuf_integrity(s);
483239281Sgonzo	assert_sbuf_state(s, 0);
484239281Sgonzo
485239281Sgonzo	if (SBUF_HASOVERFLOWED(s))
486239281Sgonzo		return (-1);
487239281Sgonzo
488239281Sgonzo	while (s->s_len && isspace(s->s_buf[s->s_len-1]))
489239281Sgonzo		--s->s_len;
490239281Sgonzo
491239281Sgonzo	return (0);
492239281Sgonzo}
493239281Sgonzo
494239281Sgonzo/*
495239281Sgonzo * Check if an sbuf overflowed
496239281Sgonzo */
497239281Sgonzoint
498239281Sgonzosbuf_overflowed(struct sbuf *s)
499239281Sgonzo{
500239281Sgonzo    return SBUF_HASOVERFLOWED(s);
501239281Sgonzo}
502239281Sgonzo
503239281Sgonzo/*
504 * Finish off an sbuf.
505 */
506void
507sbuf_finish(struct sbuf *s)
508{
509	assert_sbuf_integrity(s);
510	assert_sbuf_state(s, 0);
511
512	s->s_buf[s->s_len] = '\0';
513	SBUF_CLEARFLAG(s, SBUF_OVERFLOWED);
514	SBUF_SETFLAG(s, SBUF_FINISHED);
515}
516
517/*
518 * Return a pointer to the sbuf data.
519 */
520char *
521sbuf_data(struct sbuf *s)
522{
523	assert_sbuf_integrity(s);
524	assert_sbuf_state(s, SBUF_FINISHED);
525
526	return s->s_buf;
527}
528
529/*
530 * Return the length of the sbuf data.
531 */
532int
533sbuf_len(struct sbuf *s)
534{
535	assert_sbuf_integrity(s);
536	/* don't care if it's finished or not */
537
538	if (SBUF_HASOVERFLOWED(s))
539		return (-1);
540	return s->s_len;
541}
542
543/*
544 * Clear an sbuf, free its buffer if necessary.
545 */
546void
547sbuf_delete(struct sbuf *s)
548{
549	int isdyn;
550
551	assert_sbuf_integrity(s);
552	/* don't care if it's finished or not */
553
554	if (SBUF_ISDYNAMIC(s))
555		SBFREE(s->s_buf);
556	isdyn = SBUF_ISDYNSTRUCT(s);
557	bzero(s, sizeof *s);
558	if (isdyn)
559		SBFREE(s);
560}
561