xref: /freebsd/sys/kern/subr_sbuf.c (revision 1d66272a85cde1c8a69c58f4b5dd649babd6eca6)
1 /*-
2  * Copyright (c) 2000 Poul-Henning Kamp and Dag-Erling Co�dan Sm�rgrav
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer
10  *    in this position and unchanged.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  *      $FreeBSD$
29  */
30 
31 #include <sys/param.h>
32 #include <sys/kernel.h>
33 #include <sys/malloc.h>
34 #include <sys/sbuf.h>
35 #include <sys/systm.h>
36 
37 #include <machine/stdarg.h>
38 
39 MALLOC_DEFINE(M_SBUF, "sbuf", "string buffers");
40 
41 #ifdef INVARIANTS
42 static void
43 assert_sbuf_integrity(struct sbuf *s)
44 {
45 	KASSERT(s != NULL,
46 	    (__FUNCTION__ " called with a NULL sbuf pointer"));
47 	KASSERT(s->s_buf != NULL,
48 	    (__FUNCTION__ " called with unitialized or corrupt sbuf"));
49 	KASSERT(s->s_len < s->s_size,
50 	    ("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size));
51 }
52 
53 static void
54 assert_sbuf_state(struct sbuf *s, int state)
55 {
56 	KASSERT((s->s_flags & SBUF_FINISHED) == state,
57 	    (__FUNCTION__ " called with %sfinished or corrupt sbuf",
58 	    (state ? "un" : "")));
59 }
60 #else
61 #define assert_sbuf_integrity(s) do { } while (0)
62 #define assert_sbuf_state(s, i)	 do { } while (0)
63 #endif
64 
65 /*
66  * Initialize an sbuf.
67  * If buf is non-NULL, it points to a static or already-allocated string
68  * big enough to hold at least length characters.
69  */
70 int
71 sbuf_new(struct sbuf *s, char *buf, size_t length, int flags)
72 {
73 	KASSERT(flags == 0,
74 	    (__FUNCTION__ " called with non-zero flags"));
75 	KASSERT(s != NULL,
76 	    (__FUNCTION__ " called with a NULL sbuf pointer"));
77 
78 	bzero(s, sizeof *s);
79 	s->s_size = length;
80 	if (buf) {
81 		s->s_buf = buf;
82 		return (0);
83 	}
84 	s->s_buf = malloc(s->s_size, M_SBUF, M_WAITOK);
85 	if (s->s_buf == NULL)
86 		return (-1);
87 	SBUF_SETFLAG(s, SBUF_DYNAMIC);
88 	return (0);
89 }
90 
91 /*
92  * Set the sbuf's position to an arbitrary value
93  */
94 int
95 sbuf_setpos(struct sbuf *s, size_t pos)
96 {
97 	assert_sbuf_integrity(s);
98 	assert_sbuf_state(s, 0);
99 
100 	KASSERT(pos >= 0,
101 	    ("attempt to seek to a negative position (%d)", pos));
102 	KASSERT(pos < s->s_size,
103 	    ("attempt to seek past end of sbuf (%d >= %d)", pos, s->s_size));
104 
105 	if (pos < 0 || pos > s->s_len)
106 		return (-1);
107 	s->s_len = pos;
108 	return (0);
109 }
110 
111 /*
112  * Append a string to an sbuf.
113  */
114 int
115 sbuf_cat(struct sbuf *s, char *str)
116 {
117 	assert_sbuf_integrity(s);
118 	assert_sbuf_state(s, 0);
119 
120 	if (SBUF_HASOVERFLOWED(s))
121 		return (-1);
122 
123 	while (*str && SBUF_HASROOM(s))
124 		s->s_buf[s->s_len++] = *str++;
125 	if (*str) {
126 		SBUF_SETFLAG(s, SBUF_OVERFLOWED);
127 		return (-1);
128 	}
129 	return (0);
130 }
131 
132 /*
133  * Copy a string into an sbuf.
134  */
135 int
136 sbuf_cpy(struct sbuf *s, char *str)
137 {
138 	assert_sbuf_integrity(s);
139 	assert_sbuf_state(s, 0);
140 
141 	s->s_len = 0;
142 	return (sbuf_cat(s, str));
143 }
144 
145 /*
146  * PCHAR function for sbuf_printf()
147  */
148 static void
149 _sbuf_pchar(int c, void *v)
150 {
151 	struct sbuf *s = (struct sbuf *)v;
152 
153 	assert_sbuf_integrity(s);
154 	assert_sbuf_state(s, 0);
155 
156 	if (SBUF_HASOVERFLOWED(s))
157 		return;
158 	if (SBUF_HASROOM(s))
159 		s->s_buf[s->s_len++] = c;
160 	else
161 		SBUF_SETFLAG(s, SBUF_OVERFLOWED);
162 }
163 
164 /*
165  * Format the given arguments and append the resulting string to an sbuf.
166  */
167 int
168 sbuf_printf(struct sbuf *s, char *fmt, ...)
169 {
170 	va_list ap;
171 	size_t len;
172 
173 	assert_sbuf_integrity(s);
174 	assert_sbuf_state(s, 0);
175 
176 	KASSERT(fmt != NULL,
177 	    (__FUNCTION__ " called with a NULL format string"));
178 
179 	if (SBUF_HASOVERFLOWED(s))
180 		return (-1);
181 
182 	va_start(ap, fmt);
183 	len = kvprintf(fmt, _sbuf_pchar, s, 10, ap);
184 	va_end(ap);
185 
186 	KASSERT(s->s_len < s->s_size,
187 	    ("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size));
188 
189 	if (SBUF_HASOVERFLOWED(s))
190 		return (-1);
191 	return (0);
192 }
193 
194 /*
195  * Append a character to an sbuf.
196  */
197 int
198 sbuf_putc(struct sbuf *s, int c)
199 {
200 	assert_sbuf_integrity(s);
201 	assert_sbuf_state(s, 0);
202 
203 	if (SBUF_HASOVERFLOWED(s))
204 		return (-1);
205 
206 	if (!SBUF_HASROOM(s)) {
207 		SBUF_SETFLAG(s, SBUF_OVERFLOWED);
208 		return (-1);
209 	}
210 	s->s_buf[s->s_len++] = c;
211 	return (0);
212 }
213 
214 /*
215  * Finish off an sbuf.
216  */
217 int
218 sbuf_finish(struct sbuf *s)
219 {
220 	assert_sbuf_integrity(s);
221 	assert_sbuf_state(s, 0);
222 
223 	if (SBUF_HASOVERFLOWED(s))
224 		return (-1);
225 
226 	s->s_buf[s->s_len++] = '\0';
227 	SBUF_SETFLAG(s, SBUF_FINISHED);
228 	return (0);
229 }
230 
231 /*
232  * Return a pointer to the sbuf data.
233  */
234 char *
235 sbuf_data(struct sbuf *s)
236 {
237 	assert_sbuf_integrity(s);
238 	assert_sbuf_state(s, SBUF_FINISHED);
239 
240 	if (SBUF_HASOVERFLOWED(s))
241 		return (NULL);
242 	return s->s_buf;
243 }
244 
245 /*
246  * Return the length of the sbuf data.
247  */
248 size_t
249 sbuf_len(struct sbuf *s)
250 {
251 	assert_sbuf_integrity(s);
252 	assert_sbuf_state(s, SBUF_FINISHED);
253 
254 	if (SBUF_HASOVERFLOWED(s))
255 		return (0);
256 	return s->s_len;
257 }
258 
259 /*
260  * Clear an sbuf, free its buffer if necessary.
261  */
262 void
263 sbuf_delete(struct sbuf *s)
264 {
265 	assert_sbuf_integrity(s);
266 	/* don't care if it's finished or not */
267 
268 	if (SBUF_ISDYNAMIC(s))
269 		free(s->s_buf, M_SBUF);
270 	bzero(s, sizeof *s);
271 }
272