xref: /freebsd/sys/kern/subr_sbuf.c (revision 9fa2ef3da263684a4b19cb1b08dfe185b6ffd5a2)
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 
414dc14139SDag-Erling Smørgrav /*
424dc14139SDag-Erling Smørgrav  * Predicates
434dc14139SDag-Erling Smørgrav  */
444dc14139SDag-Erling Smørgrav #define SBUF_ISDYNAMIC(s)	((s)->s_flags & SBUF_DYNAMIC)
454dc14139SDag-Erling Smørgrav #define SBUF_ISFINISHED(s)	((s)->s_flags & SBUF_FINISHED)
464dc14139SDag-Erling Smørgrav #define SBUF_HASOVERFLOWED(s)	((s)->s_flags & SBUF_OVERFLOWED)
474dc14139SDag-Erling Smørgrav #define SBUF_HASROOM(s)		((s)->s_len < (s)->s_size - 1)
484dc14139SDag-Erling Smørgrav 
494dc14139SDag-Erling Smørgrav /*
504dc14139SDag-Erling Smørgrav  * Set / clear flags
514dc14139SDag-Erling Smørgrav  */
524dc14139SDag-Erling Smørgrav #define SBUF_SETFLAG(s, f)	do { (s)->s_flags |= (f); } while (0)
534dc14139SDag-Erling Smørgrav #define SBUF_CLEARFLAG(s, f)	do { (s)->s_flags &= ~(f); } while (0)
544dc14139SDag-Erling Smørgrav 
554dc14139SDag-Erling Smørgrav /*
564dc14139SDag-Erling Smørgrav  * Debugging support
574dc14139SDag-Erling Smørgrav  */
5860ec4130SDag-Erling Smørgrav #ifdef INVARIANTS
5960ec4130SDag-Erling Smørgrav static void
6060ec4130SDag-Erling Smørgrav assert_sbuf_integrity(struct sbuf *s)
6160ec4130SDag-Erling Smørgrav {
6260ec4130SDag-Erling Smørgrav 	KASSERT(s != NULL,
6360ec4130SDag-Erling Smørgrav 	    (__FUNCTION__ " called with a NULL sbuf pointer"));
6460ec4130SDag-Erling Smørgrav 	KASSERT(s->s_buf != NULL,
6560ec4130SDag-Erling Smørgrav 	    (__FUNCTION__ " called with unitialized or corrupt sbuf"));
6660ec4130SDag-Erling Smørgrav 	KASSERT(s->s_len < s->s_size,
6760ec4130SDag-Erling Smørgrav 	    ("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size));
6860ec4130SDag-Erling Smørgrav }
6960ec4130SDag-Erling Smørgrav 
7060ec4130SDag-Erling Smørgrav static void
7160ec4130SDag-Erling Smørgrav assert_sbuf_state(struct sbuf *s, int state)
7260ec4130SDag-Erling Smørgrav {
7360ec4130SDag-Erling Smørgrav 	KASSERT((s->s_flags & SBUF_FINISHED) == state,
7460ec4130SDag-Erling Smørgrav 	    (__FUNCTION__ " called with %sfinished or corrupt sbuf",
7560ec4130SDag-Erling Smørgrav 	    (state ? "un" : "")));
7660ec4130SDag-Erling Smørgrav }
7760ec4130SDag-Erling Smørgrav #else
7860ec4130SDag-Erling Smørgrav #define assert_sbuf_integrity(s) do { } while (0)
7960ec4130SDag-Erling Smørgrav #define assert_sbuf_state(s, i)	 do { } while (0)
8060ec4130SDag-Erling Smørgrav #endif
8160ec4130SDag-Erling Smørgrav 
8260ec4130SDag-Erling Smørgrav /*
8360ec4130SDag-Erling Smørgrav  * Initialize an sbuf.
8460ec4130SDag-Erling Smørgrav  * If buf is non-NULL, it points to a static or already-allocated string
8560ec4130SDag-Erling Smørgrav  * big enough to hold at least length characters.
8660ec4130SDag-Erling Smørgrav  */
8760ec4130SDag-Erling Smørgrav int
884dc14139SDag-Erling Smørgrav sbuf_new(struct sbuf *s, char *buf, int length, int flags)
8960ec4130SDag-Erling Smørgrav {
904dc14139SDag-Erling Smørgrav 	KASSERT(length >= 0,
914dc14139SDag-Erling Smørgrav 	    ("attempt to create an sbuf of negative length (%d)", length));
9260ec4130SDag-Erling Smørgrav 	KASSERT(flags == 0,
9360ec4130SDag-Erling Smørgrav 	    (__FUNCTION__ " called with non-zero flags"));
9460ec4130SDag-Erling Smørgrav 	KASSERT(s != NULL,
9560ec4130SDag-Erling Smørgrav 	    (__FUNCTION__ " called with a NULL sbuf pointer"));
9660ec4130SDag-Erling Smørgrav 
9760ec4130SDag-Erling Smørgrav 	bzero(s, sizeof *s);
9860ec4130SDag-Erling Smørgrav 	s->s_size = length;
9960ec4130SDag-Erling Smørgrav 	if (buf) {
10060ec4130SDag-Erling Smørgrav 		s->s_buf = buf;
10160ec4130SDag-Erling Smørgrav 		return (0);
10260ec4130SDag-Erling Smørgrav 	}
10360ec4130SDag-Erling Smørgrav 	s->s_buf = malloc(s->s_size, M_SBUF, M_WAITOK);
10460ec4130SDag-Erling Smørgrav 	if (s->s_buf == NULL)
10560ec4130SDag-Erling Smørgrav 		return (-1);
10660ec4130SDag-Erling Smørgrav 	SBUF_SETFLAG(s, SBUF_DYNAMIC);
10760ec4130SDag-Erling Smørgrav 	return (0);
10860ec4130SDag-Erling Smørgrav }
10960ec4130SDag-Erling Smørgrav 
11060ec4130SDag-Erling Smørgrav /*
1114dc14139SDag-Erling Smørgrav  * Clear an sbuf and reset its position
1124dc14139SDag-Erling Smørgrav  */
1134dc14139SDag-Erling Smørgrav void
1144dc14139SDag-Erling Smørgrav sbuf_clear(struct sbuf *s)
1154dc14139SDag-Erling Smørgrav {
1164dc14139SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
1179fa2ef3dSDag-Erling Smørgrav 	/* don't care if it's finished or not */
1184dc14139SDag-Erling Smørgrav 
1194dc14139SDag-Erling Smørgrav 	SBUF_CLEARFLAG(s, SBUF_FINISHED);
1204dc14139SDag-Erling Smørgrav 	SBUF_CLEARFLAG(s, SBUF_OVERFLOWED);
1214dc14139SDag-Erling Smørgrav 	s->s_len = 0;
1224dc14139SDag-Erling Smørgrav }
1234dc14139SDag-Erling Smørgrav 
1244dc14139SDag-Erling Smørgrav /*
12560ec4130SDag-Erling Smørgrav  * Set the sbuf's position to an arbitrary value
12660ec4130SDag-Erling Smørgrav  */
12760ec4130SDag-Erling Smørgrav int
1284dc14139SDag-Erling Smørgrav sbuf_setpos(struct sbuf *s, int pos)
12960ec4130SDag-Erling Smørgrav {
13060ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
13160ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
13260ec4130SDag-Erling Smørgrav 
13360ec4130SDag-Erling Smørgrav 	KASSERT(pos >= 0,
13460ec4130SDag-Erling Smørgrav 	    ("attempt to seek to a negative position (%d)", pos));
13560ec4130SDag-Erling Smørgrav 	KASSERT(pos < s->s_size,
13660ec4130SDag-Erling Smørgrav 	    ("attempt to seek past end of sbuf (%d >= %d)", pos, s->s_size));
13760ec4130SDag-Erling Smørgrav 
13860ec4130SDag-Erling Smørgrav 	if (pos < 0 || pos > s->s_len)
13960ec4130SDag-Erling Smørgrav 		return (-1);
14060ec4130SDag-Erling Smørgrav 	s->s_len = pos;
14160ec4130SDag-Erling Smørgrav 	return (0);
14260ec4130SDag-Erling Smørgrav }
14360ec4130SDag-Erling Smørgrav 
14460ec4130SDag-Erling Smørgrav /*
14560ec4130SDag-Erling Smørgrav  * Append a string to an sbuf.
14660ec4130SDag-Erling Smørgrav  */
14760ec4130SDag-Erling Smørgrav int
14860ec4130SDag-Erling Smørgrav sbuf_cat(struct sbuf *s, char *str)
14960ec4130SDag-Erling Smørgrav {
15060ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
15160ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
15260ec4130SDag-Erling Smørgrav 
15360ec4130SDag-Erling Smørgrav 	if (SBUF_HASOVERFLOWED(s))
15460ec4130SDag-Erling Smørgrav 		return (-1);
15560ec4130SDag-Erling Smørgrav 
15660ec4130SDag-Erling Smørgrav 	while (*str && SBUF_HASROOM(s))
15760ec4130SDag-Erling Smørgrav 		s->s_buf[s->s_len++] = *str++;
15860ec4130SDag-Erling Smørgrav 	if (*str) {
15960ec4130SDag-Erling Smørgrav 		SBUF_SETFLAG(s, SBUF_OVERFLOWED);
16060ec4130SDag-Erling Smørgrav 		return (-1);
16160ec4130SDag-Erling Smørgrav 	}
16260ec4130SDag-Erling Smørgrav 	return (0);
16360ec4130SDag-Erling Smørgrav }
16460ec4130SDag-Erling Smørgrav 
16560ec4130SDag-Erling Smørgrav /*
16660ec4130SDag-Erling Smørgrav  * Copy a string into an sbuf.
16760ec4130SDag-Erling Smørgrav  */
16860ec4130SDag-Erling Smørgrav int
16960ec4130SDag-Erling Smørgrav sbuf_cpy(struct sbuf *s, char *str)
17060ec4130SDag-Erling Smørgrav {
17160ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
17260ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
17360ec4130SDag-Erling Smørgrav 
1744dc14139SDag-Erling Smørgrav 	sbuf_clear(s);
17560ec4130SDag-Erling Smørgrav 	return (sbuf_cat(s, str));
17660ec4130SDag-Erling Smørgrav }
17760ec4130SDag-Erling Smørgrav 
17860ec4130SDag-Erling Smørgrav /*
17960ec4130SDag-Erling Smørgrav  * PCHAR function for sbuf_printf()
18060ec4130SDag-Erling Smørgrav  */
18160ec4130SDag-Erling Smørgrav static void
18260ec4130SDag-Erling Smørgrav _sbuf_pchar(int c, void *v)
18360ec4130SDag-Erling Smørgrav {
18460ec4130SDag-Erling Smørgrav 	struct sbuf *s = (struct sbuf *)v;
18560ec4130SDag-Erling Smørgrav 
18660ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
18760ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
18860ec4130SDag-Erling Smørgrav 
18960ec4130SDag-Erling Smørgrav 	if (SBUF_HASOVERFLOWED(s))
19060ec4130SDag-Erling Smørgrav 		return;
19160ec4130SDag-Erling Smørgrav 	if (SBUF_HASROOM(s))
19260ec4130SDag-Erling Smørgrav 		s->s_buf[s->s_len++] = c;
19360ec4130SDag-Erling Smørgrav 	else
19460ec4130SDag-Erling Smørgrav 		SBUF_SETFLAG(s, SBUF_OVERFLOWED);
19560ec4130SDag-Erling Smørgrav }
19660ec4130SDag-Erling Smørgrav 
19760ec4130SDag-Erling Smørgrav /*
19860ec4130SDag-Erling Smørgrav  * Format the given arguments and append the resulting string to an sbuf.
19960ec4130SDag-Erling Smørgrav  */
20060ec4130SDag-Erling Smørgrav int
20160ec4130SDag-Erling Smørgrav sbuf_printf(struct sbuf *s, char *fmt, ...)
20260ec4130SDag-Erling Smørgrav {
20360ec4130SDag-Erling Smørgrav 	va_list ap;
2044dc14139SDag-Erling Smørgrav 	int len;
20560ec4130SDag-Erling Smørgrav 
20660ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
20760ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
20860ec4130SDag-Erling Smørgrav 
20960ec4130SDag-Erling Smørgrav 	KASSERT(fmt != NULL,
21060ec4130SDag-Erling Smørgrav 	    (__FUNCTION__ " called with a NULL format string"));
21160ec4130SDag-Erling Smørgrav 
21260ec4130SDag-Erling Smørgrav 	if (SBUF_HASOVERFLOWED(s))
21360ec4130SDag-Erling Smørgrav 		return (-1);
21460ec4130SDag-Erling Smørgrav 
21560ec4130SDag-Erling Smørgrav 	va_start(ap, fmt);
21660ec4130SDag-Erling Smørgrav 	len = kvprintf(fmt, _sbuf_pchar, s, 10, ap);
21760ec4130SDag-Erling Smørgrav 	va_end(ap);
21860ec4130SDag-Erling Smørgrav 
21960ec4130SDag-Erling Smørgrav 	KASSERT(s->s_len < s->s_size,
22060ec4130SDag-Erling Smørgrav 	    ("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size));
22160ec4130SDag-Erling Smørgrav 
22260ec4130SDag-Erling Smørgrav 	if (SBUF_HASOVERFLOWED(s))
22360ec4130SDag-Erling Smørgrav 		return (-1);
22460ec4130SDag-Erling Smørgrav 	return (0);
22560ec4130SDag-Erling Smørgrav }
22660ec4130SDag-Erling Smørgrav 
22760ec4130SDag-Erling Smørgrav /*
22860ec4130SDag-Erling Smørgrav  * Append a character to an sbuf.
22960ec4130SDag-Erling Smørgrav  */
23060ec4130SDag-Erling Smørgrav int
23160ec4130SDag-Erling Smørgrav sbuf_putc(struct sbuf *s, int c)
23260ec4130SDag-Erling Smørgrav {
23360ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
23460ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
23560ec4130SDag-Erling Smørgrav 
23660ec4130SDag-Erling Smørgrav 	if (SBUF_HASOVERFLOWED(s))
23760ec4130SDag-Erling Smørgrav 		return (-1);
23860ec4130SDag-Erling Smørgrav 
23960ec4130SDag-Erling Smørgrav 	if (!SBUF_HASROOM(s)) {
24060ec4130SDag-Erling Smørgrav 		SBUF_SETFLAG(s, SBUF_OVERFLOWED);
24160ec4130SDag-Erling Smørgrav 		return (-1);
24260ec4130SDag-Erling Smørgrav 	}
24360ec4130SDag-Erling Smørgrav 	s->s_buf[s->s_len++] = c;
24460ec4130SDag-Erling Smørgrav 	return (0);
24560ec4130SDag-Erling Smørgrav }
24660ec4130SDag-Erling Smørgrav 
24760ec4130SDag-Erling Smørgrav /*
2484dc14139SDag-Erling Smørgrav  * Check if an sbuf overflowed
24960ec4130SDag-Erling Smørgrav  */
25060ec4130SDag-Erling Smørgrav int
2514dc14139SDag-Erling Smørgrav sbuf_overflowed(struct sbuf *s)
2524dc14139SDag-Erling Smørgrav {
2534dc14139SDag-Erling Smørgrav     return SBUF_HASOVERFLOWED(s);
2544dc14139SDag-Erling Smørgrav }
2554dc14139SDag-Erling Smørgrav 
2564dc14139SDag-Erling Smørgrav /*
2574dc14139SDag-Erling Smørgrav  * Finish off an sbuf.
2584dc14139SDag-Erling Smørgrav  */
2594dc14139SDag-Erling Smørgrav void
26060ec4130SDag-Erling Smørgrav sbuf_finish(struct sbuf *s)
26160ec4130SDag-Erling Smørgrav {
26260ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
26360ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, 0);
26460ec4130SDag-Erling Smørgrav 
26560ec4130SDag-Erling Smørgrav 	s->s_buf[s->s_len++] = '\0';
2664dc14139SDag-Erling Smørgrav 	SBUF_CLEARFLAG(s, SBUF_OVERFLOWED);
26760ec4130SDag-Erling Smørgrav 	SBUF_SETFLAG(s, SBUF_FINISHED);
26860ec4130SDag-Erling Smørgrav }
26960ec4130SDag-Erling Smørgrav 
27060ec4130SDag-Erling Smørgrav /*
27160ec4130SDag-Erling Smørgrav  * Return a pointer to the sbuf data.
27260ec4130SDag-Erling Smørgrav  */
27360ec4130SDag-Erling Smørgrav char *
27460ec4130SDag-Erling Smørgrav sbuf_data(struct sbuf *s)
27560ec4130SDag-Erling Smørgrav {
27660ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
27760ec4130SDag-Erling Smørgrav 	assert_sbuf_state(s, SBUF_FINISHED);
27860ec4130SDag-Erling Smørgrav 
27960ec4130SDag-Erling Smørgrav 	return s->s_buf;
28060ec4130SDag-Erling Smørgrav }
28160ec4130SDag-Erling Smørgrav 
28260ec4130SDag-Erling Smørgrav /*
28360ec4130SDag-Erling Smørgrav  * Return the length of the sbuf data.
28460ec4130SDag-Erling Smørgrav  */
2854dc14139SDag-Erling Smørgrav int
28660ec4130SDag-Erling Smørgrav sbuf_len(struct sbuf *s)
28760ec4130SDag-Erling Smørgrav {
28860ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
2899fa2ef3dSDag-Erling Smørgrav 	/* don't care if it's finished or not */
29060ec4130SDag-Erling Smørgrav 
29160ec4130SDag-Erling Smørgrav 	if (SBUF_HASOVERFLOWED(s))
2924dc14139SDag-Erling Smørgrav 		return (-1);
29360ec4130SDag-Erling Smørgrav 	return s->s_len;
29460ec4130SDag-Erling Smørgrav }
29560ec4130SDag-Erling Smørgrav 
29660ec4130SDag-Erling Smørgrav /*
29760ec4130SDag-Erling Smørgrav  * Clear an sbuf, free its buffer if necessary.
29860ec4130SDag-Erling Smørgrav  */
29960ec4130SDag-Erling Smørgrav void
30060ec4130SDag-Erling Smørgrav sbuf_delete(struct sbuf *s)
30160ec4130SDag-Erling Smørgrav {
30260ec4130SDag-Erling Smørgrav 	assert_sbuf_integrity(s);
30360ec4130SDag-Erling Smørgrav 	/* don't care if it's finished or not */
30460ec4130SDag-Erling Smørgrav 
30560ec4130SDag-Erling Smørgrav 	if (SBUF_ISDYNAMIC(s))
30660ec4130SDag-Erling Smørgrav 		free(s->s_buf, M_SBUF);
30760ec4130SDag-Erling Smørgrav 	bzero(s, sizeof *s);
30860ec4130SDag-Erling Smørgrav }
309