xref: /freebsd/sys/kern/subr_sbuf.c (revision 60ec41303838ce1024aa0a51577b00af85b05728)
160ec4130SDag-Erling Smørgrav /*-
260ec4130SDag-Erling Smørgrav  * Copyright (c) 2000 Poul-Henning Kamp and Dag-Erling Co�dan Sm�rgrav
360ec4130SDag-Erling Smørgrav  * All rights reserved.
460ec4130SDag-Erling Smørgrav  *
560ec4130SDag-Erling Smørgrav  * Redistribution and use in source and binary forms, with or without
660ec4130SDag-Erling Smørgrav  * modification, are permitted provided that the following conditions
760ec4130SDag-Erling Smørgrav  * are met:
860ec4130SDag-Erling Smørgrav  * 1. Redistributions of source code must retain the above copyright
960ec4130SDag-Erling Smørgrav  *    notice, this list of conditions and the following disclaimer
1060ec4130SDag-Erling Smørgrav  *    in this position and unchanged.
1160ec4130SDag-Erling Smørgrav  * 2. Redistributions in binary form must reproduce the above copyright
1260ec4130SDag-Erling Smørgrav  *    notice, this list of conditions and the following disclaimer in the
1360ec4130SDag-Erling Smørgrav  *    documentation and/or other materials provided with the distribution.
1460ec4130SDag-Erling Smørgrav  * 3. The name of the author may not be used to endorse or promote products
1560ec4130SDag-Erling Smørgrav  *    derived from this software without specific prior written permission.
1660ec4130SDag-Erling Smørgrav  *
1760ec4130SDag-Erling Smørgrav  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1860ec4130SDag-Erling Smørgrav  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1960ec4130SDag-Erling Smørgrav  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2060ec4130SDag-Erling Smørgrav  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2160ec4130SDag-Erling Smørgrav  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2260ec4130SDag-Erling Smørgrav  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2360ec4130SDag-Erling Smørgrav  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2460ec4130SDag-Erling Smørgrav  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2560ec4130SDag-Erling Smørgrav  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2660ec4130SDag-Erling Smørgrav  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2760ec4130SDag-Erling Smørgrav  *
2860ec4130SDag-Erling Smørgrav  *      $FreeBSD$
2960ec4130SDag-Erling Smørgrav  */
3060ec4130SDag-Erling Smørgrav 
3160ec4130SDag-Erling Smørgrav #include <sys/param.h>
3260ec4130SDag-Erling Smørgrav #include <sys/kernel.h>
3360ec4130SDag-Erling Smørgrav #include <sys/malloc.h>
3460ec4130SDag-Erling Smørgrav #include <sys/sbuf.h>
3560ec4130SDag-Erling Smørgrav #include <sys/systm.h>
3660ec4130SDag-Erling Smørgrav 
3760ec4130SDag-Erling Smørgrav #include <machine/stdarg.h>
3860ec4130SDag-Erling Smørgrav 
3960ec4130SDag-Erling Smørgrav MALLOC_DEFINE(M_SBUF, "sbuf", "string buffers");
4060ec4130SDag-Erling Smørgrav 
4160ec4130SDag-Erling Smørgrav #ifdef INVARIANTS
4260ec4130SDag-Erling Smørgrav static void
4360ec4130SDag-Erling Smørgrav assert_sbuf_integrity(struct sbuf *s)
4460ec4130SDag-Erling Smørgrav {
4560ec4130SDag-Erling Smørgrav 	KASSERT(s != NULL,
4660ec4130SDag-Erling Smørgrav 	    (__FUNCTION__ " called with a NULL sbuf pointer"));
4760ec4130SDag-Erling Smørgrav 	KASSERT(s->s_buf != NULL,
4860ec4130SDag-Erling Smørgrav 	    (__FUNCTION__ " called with unitialized or corrupt sbuf"));
4960ec4130SDag-Erling Smørgrav 	KASSERT(s->s_len < s->s_size,
5060ec4130SDag-Erling Smørgrav 	    ("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size));
5160ec4130SDag-Erling Smørgrav }
5260ec4130SDag-Erling Smørgrav 
5360ec4130SDag-Erling Smørgrav static void
5460ec4130SDag-Erling Smørgrav assert_sbuf_state(struct sbuf *s, int state)
5560ec4130SDag-Erling Smørgrav {
5660ec4130SDag-Erling Smørgrav 	KASSERT((s->s_flags & SBUF_FINISHED) == state,
5760ec4130SDag-Erling Smørgrav 	    (__FUNCTION__ " called with %sfinished or corrupt sbuf",
5860ec4130SDag-Erling Smørgrav 	    (state ? "un" : "")));
5960ec4130SDag-Erling Smørgrav }
6060ec4130SDag-Erling Smørgrav #else
6160ec4130SDag-Erling Smørgrav #define assert_sbuf_integrity(s) do { } while (0)
6260ec4130SDag-Erling Smørgrav #define assert_sbuf_state(s, i)	 do { } while (0)
6360ec4130SDag-Erling Smørgrav #endif
6460ec4130SDag-Erling Smørgrav 
6560ec4130SDag-Erling Smørgrav /*
6660ec4130SDag-Erling Smørgrav  * Initialize an sbuf.
6760ec4130SDag-Erling Smørgrav  * If buf is non-NULL, it points to a static or already-allocated string
6860ec4130SDag-Erling Smørgrav  * big enough to hold at least length characters.
6960ec4130SDag-Erling Smørgrav  */
7060ec4130SDag-Erling Smørgrav int
7160ec4130SDag-Erling Smørgrav sbuf_new(struct sbuf *s, char *buf, size_t length, int flags)
7260ec4130SDag-Erling Smørgrav {
7360ec4130SDag-Erling Smørgrav 	KASSERT(flags == 0,
7460ec4130SDag-Erling Smørgrav 	    (__FUNCTION__ " called with non-zero flags"));
7560ec4130SDag-Erling Smørgrav 	KASSERT(s != NULL,
7660ec4130SDag-Erling Smørgrav 	    (__FUNCTION__ " called with a NULL sbuf pointer"));
7760ec4130SDag-Erling Smørgrav 
7860ec4130SDag-Erling Smørgrav 	bzero(s, sizeof *s);
7960ec4130SDag-Erling Smørgrav 	s->s_size = length;
8060ec4130SDag-Erling Smørgrav 	if (buf) {
8160ec4130SDag-Erling Smørgrav 		s->s_buf = buf;
8260ec4130SDag-Erling Smørgrav 		return (0);
8360ec4130SDag-Erling Smørgrav 	}
8460ec4130SDag-Erling Smørgrav 	s->s_buf = malloc(s->s_size, M_SBUF, M_WAITOK);
8560ec4130SDag-Erling Smørgrav 	if (s->s_buf == NULL)
8660ec4130SDag-Erling Smørgrav 		return (-1);
8760ec4130SDag-Erling Smørgrav 	SBUF_SETFLAG(s, SBUF_DYNAMIC);
8860ec4130SDag-Erling Smørgrav 	return (0);
8960ec4130SDag-Erling Smørgrav }
9060ec4130SDag-Erling Smørgrav 
9160ec4130SDag-Erling Smørgrav /*
9260ec4130SDag-Erling Smørgrav  * Set the sbuf's position to an arbitrary value
9360ec4130SDag-Erling Smørgrav  */
9460ec4130SDag-Erling Smørgrav int
9560ec4130SDag-Erling Smørgrav sbuf_setpos(struct sbuf *s, size_t pos)
9660ec4130SDag-Erling Smørgrav {
9760ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
9860ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
9960ec4130SDag-Erling Smørgrav 
10060ec4130SDag-Erling Smørgrav 	KASSERT(pos >= 0,
10160ec4130SDag-Erling Smørgrav 	    ("attempt to seek to a negative position (%d)", pos));
10260ec4130SDag-Erling Smørgrav 	KASSERT(pos < s->s_size,
10360ec4130SDag-Erling Smørgrav 	    ("attempt to seek past end of sbuf (%d >= %d)", pos, s->s_size));
10460ec4130SDag-Erling Smørgrav 
10560ec4130SDag-Erling Smørgrav 	if (pos < 0 || pos > s->s_len)
10660ec4130SDag-Erling Smørgrav 		return (-1);
10760ec4130SDag-Erling Smørgrav 	s->s_len = pos;
10860ec4130SDag-Erling Smørgrav 	return (0);
10960ec4130SDag-Erling Smørgrav }
11060ec4130SDag-Erling Smørgrav 
11160ec4130SDag-Erling Smørgrav /*
11260ec4130SDag-Erling Smørgrav  * Append a string to an sbuf.
11360ec4130SDag-Erling Smørgrav  */
11460ec4130SDag-Erling Smørgrav int
11560ec4130SDag-Erling Smørgrav sbuf_cat(struct sbuf *s, char *str)
11660ec4130SDag-Erling Smørgrav {
11760ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
11860ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
11960ec4130SDag-Erling Smørgrav 
12060ec4130SDag-Erling Smørgrav 	if (SBUF_HASOVERFLOWED(s))
12160ec4130SDag-Erling Smørgrav 		return (-1);
12260ec4130SDag-Erling Smørgrav 
12360ec4130SDag-Erling Smørgrav 	while (*str && SBUF_HASROOM(s))
12460ec4130SDag-Erling Smørgrav 		s->s_buf[s->s_len++] = *str++;
12560ec4130SDag-Erling Smørgrav 	if (*str) {
12660ec4130SDag-Erling Smørgrav 		SBUF_SETFLAG(s, SBUF_OVERFLOWED);
12760ec4130SDag-Erling Smørgrav 		return (-1);
12860ec4130SDag-Erling Smørgrav 	}
12960ec4130SDag-Erling Smørgrav 	return (0);
13060ec4130SDag-Erling Smørgrav }
13160ec4130SDag-Erling Smørgrav 
13260ec4130SDag-Erling Smørgrav /*
13360ec4130SDag-Erling Smørgrav  * Copy a string into an sbuf.
13460ec4130SDag-Erling Smørgrav  */
13560ec4130SDag-Erling Smørgrav int
13660ec4130SDag-Erling Smørgrav sbuf_cpy(struct sbuf *s, char *str)
13760ec4130SDag-Erling Smørgrav {
13860ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
13960ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
14060ec4130SDag-Erling Smørgrav 
14160ec4130SDag-Erling Smørgrav 	s->s_len = 0;
14260ec4130SDag-Erling Smørgrav 	return (sbuf_cat(s, str));
14360ec4130SDag-Erling Smørgrav }
14460ec4130SDag-Erling Smørgrav 
14560ec4130SDag-Erling Smørgrav /*
14660ec4130SDag-Erling Smørgrav  * PCHAR function for sbuf_printf()
14760ec4130SDag-Erling Smørgrav  */
14860ec4130SDag-Erling Smørgrav static void
14960ec4130SDag-Erling Smørgrav _sbuf_pchar(int c, void *v)
15060ec4130SDag-Erling Smørgrav {
15160ec4130SDag-Erling Smørgrav 	struct sbuf *s = (struct sbuf *)v;
15260ec4130SDag-Erling Smørgrav 
15360ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
15460ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
15560ec4130SDag-Erling Smørgrav 
15660ec4130SDag-Erling Smørgrav 	if (SBUF_HASOVERFLOWED(s))
15760ec4130SDag-Erling Smørgrav 		return;
15860ec4130SDag-Erling Smørgrav 	if (SBUF_HASROOM(s))
15960ec4130SDag-Erling Smørgrav 		s->s_buf[s->s_len++] = c;
16060ec4130SDag-Erling Smørgrav 	else
16160ec4130SDag-Erling Smørgrav 		SBUF_SETFLAG(s, SBUF_OVERFLOWED);
16260ec4130SDag-Erling Smørgrav }
16360ec4130SDag-Erling Smørgrav 
16460ec4130SDag-Erling Smørgrav /*
16560ec4130SDag-Erling Smørgrav  * Format the given arguments and append the resulting string to an sbuf.
16660ec4130SDag-Erling Smørgrav  */
16760ec4130SDag-Erling Smørgrav int
16860ec4130SDag-Erling Smørgrav sbuf_printf(struct sbuf *s, char *fmt, ...)
16960ec4130SDag-Erling Smørgrav {
17060ec4130SDag-Erling Smørgrav 	va_list ap;
17160ec4130SDag-Erling Smørgrav 	size_t len;
17260ec4130SDag-Erling Smørgrav 
17360ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
17460ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
17560ec4130SDag-Erling Smørgrav 
17660ec4130SDag-Erling Smørgrav 	KASSERT(fmt != NULL,
17760ec4130SDag-Erling Smørgrav 	    (__FUNCTION__ " called with a NULL format string"));
17860ec4130SDag-Erling Smørgrav 
17960ec4130SDag-Erling Smørgrav 	if (SBUF_HASOVERFLOWED(s))
18060ec4130SDag-Erling Smørgrav 		return (-1);
18160ec4130SDag-Erling Smørgrav 
18260ec4130SDag-Erling Smørgrav 	va_start(ap, fmt);
18360ec4130SDag-Erling Smørgrav 	len = kvprintf(fmt, _sbuf_pchar, s, 10, ap);
18460ec4130SDag-Erling Smørgrav 	va_end(ap);
18560ec4130SDag-Erling Smørgrav 
18660ec4130SDag-Erling Smørgrav 	KASSERT(s->s_len < s->s_size,
18760ec4130SDag-Erling Smørgrav 	    ("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size));
18860ec4130SDag-Erling Smørgrav 
18960ec4130SDag-Erling Smørgrav 	if (SBUF_HASOVERFLOWED(s))
19060ec4130SDag-Erling Smørgrav 		return (-1);
19160ec4130SDag-Erling Smørgrav 	return (0);
19260ec4130SDag-Erling Smørgrav }
19360ec4130SDag-Erling Smørgrav 
19460ec4130SDag-Erling Smørgrav /*
19560ec4130SDag-Erling Smørgrav  * Append a character to an sbuf.
19660ec4130SDag-Erling Smørgrav  */
19760ec4130SDag-Erling Smørgrav int
19860ec4130SDag-Erling Smørgrav sbuf_putc(struct sbuf *s, int c)
19960ec4130SDag-Erling Smørgrav {
20060ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
20160ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
20260ec4130SDag-Erling Smørgrav 
20360ec4130SDag-Erling Smørgrav 	if (SBUF_HASOVERFLOWED(s))
20460ec4130SDag-Erling Smørgrav 		return (-1);
20560ec4130SDag-Erling Smørgrav 
20660ec4130SDag-Erling Smørgrav 	if (!SBUF_HASROOM(s)) {
20760ec4130SDag-Erling Smørgrav 		SBUF_SETFLAG(s, SBUF_OVERFLOWED);
20860ec4130SDag-Erling Smørgrav 		return (-1);
20960ec4130SDag-Erling Smørgrav 	}
21060ec4130SDag-Erling Smørgrav 	s->s_buf[s->s_len++] = c;
21160ec4130SDag-Erling Smørgrav 	return (0);
21260ec4130SDag-Erling Smørgrav }
21360ec4130SDag-Erling Smørgrav 
21460ec4130SDag-Erling Smørgrav /*
21560ec4130SDag-Erling Smørgrav  * Finish off an sbuf.
21660ec4130SDag-Erling Smørgrav  */
21760ec4130SDag-Erling Smørgrav int
21860ec4130SDag-Erling Smørgrav sbuf_finish(struct sbuf *s)
21960ec4130SDag-Erling Smørgrav {
22060ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
22160ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
22260ec4130SDag-Erling Smørgrav 
22360ec4130SDag-Erling Smørgrav 	if (SBUF_HASOVERFLOWED(s))
22460ec4130SDag-Erling Smørgrav 		return (-1);
22560ec4130SDag-Erling Smørgrav 
22660ec4130SDag-Erling Smørgrav 	s->s_buf[s->s_len++] = '\0';
22760ec4130SDag-Erling Smørgrav 	SBUF_SETFLAG(s, SBUF_FINISHED);
22860ec4130SDag-Erling Smørgrav 	return (0);
22960ec4130SDag-Erling Smørgrav }
23060ec4130SDag-Erling Smørgrav 
23160ec4130SDag-Erling Smørgrav /*
23260ec4130SDag-Erling Smørgrav  * Return a pointer to the sbuf data.
23360ec4130SDag-Erling Smørgrav  */
23460ec4130SDag-Erling Smørgrav char *
23560ec4130SDag-Erling Smørgrav sbuf_data(struct sbuf *s)
23660ec4130SDag-Erling Smørgrav {
23760ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
23860ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, SBUF_FINISHED);
23960ec4130SDag-Erling Smørgrav 
24060ec4130SDag-Erling Smørgrav 	if (SBUF_HASOVERFLOWED(s))
24160ec4130SDag-Erling Smørgrav 		return (NULL);
24260ec4130SDag-Erling Smørgrav 	return s->s_buf;
24360ec4130SDag-Erling Smørgrav }
24460ec4130SDag-Erling Smørgrav 
24560ec4130SDag-Erling Smørgrav /*
24660ec4130SDag-Erling Smørgrav  * Return the length of the sbuf data.
24760ec4130SDag-Erling Smørgrav  */
24860ec4130SDag-Erling Smørgrav size_t
24960ec4130SDag-Erling Smørgrav sbuf_len(struct sbuf *s)
25060ec4130SDag-Erling Smørgrav {
25160ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
25260ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, SBUF_FINISHED);
25360ec4130SDag-Erling Smørgrav 
25460ec4130SDag-Erling Smørgrav 	if (SBUF_HASOVERFLOWED(s))
25560ec4130SDag-Erling Smørgrav 		return (0);
25660ec4130SDag-Erling Smørgrav 	return s->s_len;
25760ec4130SDag-Erling Smørgrav }
25860ec4130SDag-Erling Smørgrav 
25960ec4130SDag-Erling Smørgrav /*
26060ec4130SDag-Erling Smørgrav  * Clear an sbuf, free its buffer if necessary.
26160ec4130SDag-Erling Smørgrav  */
26260ec4130SDag-Erling Smørgrav void
26360ec4130SDag-Erling Smørgrav sbuf_delete(struct sbuf *s)
26460ec4130SDag-Erling Smørgrav {
26560ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
26660ec4130SDag-Erling Smørgrav 	/* don't care if it's finished or not */
26760ec4130SDag-Erling Smørgrav 
26860ec4130SDag-Erling Smørgrav 	if (SBUF_ISDYNAMIC(s))
26960ec4130SDag-Erling Smørgrav 		free(s->s_buf, M_SBUF);
27060ec4130SDag-Erling Smørgrav 	bzero(s, sizeof *s);
27160ec4130SDag-Erling Smørgrav }
272