summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--share/man/man9/sbuf.925
-rw-r--r--sys/kern/subr_sbuf.c127
-rw-r--r--sys/sys/sbuf.h4
3 files changed, 114 insertions, 42 deletions
diff --git a/share/man/man9/sbuf.9 b/share/man/man9/sbuf.9
index a74a569..4dcb894 100644
--- a/share/man/man9/sbuf.9
+++ b/share/man/man9/sbuf.9
@@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd April 11, 2013
+.Dd March 14, 2015
.Dt SBUF 9
.Os
.Sh NAME
@@ -34,6 +34,9 @@
.Nm sbuf_new_auto ,
.Nm sbuf_new_for_sysctl ,
.Nm sbuf_clear ,
+.Nm sbuf_get_flags ,
+.Nm sbuf_set_flags ,
+.Nm sbuf_clear_flags ,
.Nm sbuf_setpos ,
.Nm sbuf_bcat ,
.Nm sbuf_bcopyin ,
@@ -69,6 +72,12 @@
.Ft void
.Fn sbuf_clear "struct sbuf *s"
.Ft int
+.Fn sbuf_get_flags "struct sbuf *s"
+.Ft void
+.Fn sbuf_set_flags "struct sbuf *s" "int flags"
+.Ft void
+.Fn sbuf_clear_flags "struct sbuf *s" "int flags"
+.Ft int
.Fn sbuf_setpos "struct sbuf *s" "int pos"
.Ft int
.Fn sbuf_bcat "struct sbuf *s" "const void *buf" "size_t len"
@@ -171,6 +180,8 @@ Attempting to extend the sbuf beyond this size results in an overflow condition.
.It Dv SBUF_AUTOEXTEND
This indicates that the storage buffer may be extended as necessary, so long
as resources allow, to hold additional data.
+.It Dv SBUF_INCLUDENUL
+This causes the final nulterm byte to be counted in the length of the data.
.El
.Pp
Note that if
@@ -223,6 +234,18 @@ function invalidates the contents of the
and resets its position to zero.
.Pp
The
+.Fn sbuf_get_flags
+function returns the current user flags.
+The
+.Fn sbuf_set_flags
+and
+.Fn sbuf_get_flags
+functions set or clear one or more user flags, respectively.
+The user flags are described under the
+.Fn sbuf_new
+function.
+.Pp
+The
.Fn sbuf_setpos
function sets the
.Fa sbuf Ns 's
diff --git a/sys/kern/subr_sbuf.c b/sys/kern/subr_sbuf.c
index 1490bc6..d7465f9d 100644
--- a/sys/kern/subr_sbuf.c
+++ b/sys/kern/subr_sbuf.c
@@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$");
#include <sys/ctype.h>
#include <sys/errno.h>
#include <sys/kernel.h>
+#include <sys/limits.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <sys/uio.h>
@@ -42,6 +43,7 @@ __FBSDID("$FreeBSD$");
#else /* _KERNEL */
#include <ctype.h>
#include <errno.h>
+#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@@ -70,6 +72,7 @@ static MALLOC_DEFINE(M_SBUF, "sbuf", "string buffers");
#define SBUF_FREESPACE(s) ((s)->s_size - ((s)->s_len + 1))
#define SBUF_CANEXTEND(s) ((s)->s_flags & SBUF_AUTOEXTEND)
#define SBUF_ISSECTION(s) ((s)->s_flags & SBUF_INSECTION)
+#define SBUF_NULINCLUDED(s) ((s)->s_flags & SBUF_INCLUDENUL)
/*
* Set / clear flags
@@ -77,6 +80,7 @@ static MALLOC_DEFINE(M_SBUF, "sbuf", "string buffers");
#define SBUF_SETFLAG(s, f) do { (s)->s_flags |= (f); } while (0)
#define SBUF_CLEARFLAG(s, f) do { (s)->s_flags &= ~(f); } while (0)
+#define SBUF_MINSIZE 2 /* Min is 1 byte + nulterm. */
#define SBUF_MINEXTENDSIZE 16 /* Should be power of 2. */
#ifdef PAGE_SIZE
@@ -100,9 +104,15 @@ _assert_sbuf_integrity(const char *fun, struct sbuf *s)
("%s called with a NULL sbuf pointer", fun));
KASSERT(s->s_buf != NULL,
("%s called with uninitialized or corrupt sbuf", fun));
- KASSERT(s->s_len < s->s_size,
- ("wrote past end of sbuf (%jd >= %jd)",
- (intmax_t)s->s_len, (intmax_t)s->s_size));
+ if (SBUF_ISFINISHED(s) && SBUF_NULINCLUDED(s)) {
+ KASSERT(s->s_len <= s->s_size,
+ ("wrote past end of sbuf (%jd >= %jd)",
+ (intmax_t)s->s_len, (intmax_t)s->s_size));
+ } else {
+ KASSERT(s->s_len < s->s_size,
+ ("wrote past end of sbuf (%jd >= %jd)",
+ (intmax_t)s->s_len, (intmax_t)s->s_size));
+ }
}
static void
@@ -185,8 +195,9 @@ sbuf_newbuf(struct sbuf *s, char *buf, int length, int flags)
s->s_buf = buf;
if ((s->s_flags & SBUF_AUTOEXTEND) == 0) {
- KASSERT(s->s_size >= 0,
- ("attempt to create a too small sbuf"));
+ KASSERT(s->s_size >= SBUF_MINSIZE,
+ ("attempt to create an sbuf smaller than %d bytes",
+ SBUF_MINSIZE));
}
if (s->s_buf != NULL)
@@ -262,6 +273,28 @@ sbuf_uionew(struct sbuf *s, struct uio *uio, int *error)
}
#endif
+int
+sbuf_get_flags(struct sbuf *s)
+{
+
+ return (s->s_flags & SBUF_USRFLAGMSK);
+}
+
+void
+sbuf_clear_flags(struct sbuf *s, int flags)
+{
+
+ s->s_flags &= ~(flags & SBUF_USRFLAGMSK);
+}
+
+void
+sbuf_set_flags(struct sbuf *s, int flags)
+{
+
+
+ s->s_flags |= (flags & SBUF_USRFLAGMSK);
+}
+
/*
* Clear an sbuf and reset its position.
*/
@@ -352,34 +385,51 @@ sbuf_drain(struct sbuf *s)
}
/*
- * Append a byte to an sbuf. This is the core function for appending
+ * Append bytes to an sbuf. This is the core function for appending
* to an sbuf and is the main place that deals with extending the
* buffer and marking overflow.
*/
static void
-sbuf_put_byte(struct sbuf *s, int c)
+sbuf_put_bytes(struct sbuf *s, const char *buf, size_t len)
{
+ size_t n;
assert_sbuf_integrity(s);
assert_sbuf_state(s, 0);
if (s->s_error != 0)
return;
- if (SBUF_FREESPACE(s) <= 0) {
- /*
- * If there is a drain, use it, otherwise extend the
- * buffer.
- */
- if (s->s_drain_func != NULL)
- (void)sbuf_drain(s);
- else if (sbuf_extend(s, 1) < 0)
- s->s_error = ENOMEM;
- if (s->s_error != 0)
- return;
+ while (len > 0) {
+ if (SBUF_FREESPACE(s) <= 0) {
+ /*
+ * If there is a drain, use it, otherwise extend the
+ * buffer.
+ */
+ if (s->s_drain_func != NULL)
+ (void)sbuf_drain(s);
+ else if (sbuf_extend(s, len > INT_MAX ? INT_MAX : len)
+ < 0)
+ s->s_error = ENOMEM;
+ if (s->s_error != 0)
+ return;
+ }
+ n = SBUF_FREESPACE(s);
+ if (len < n)
+ n = len;
+ memcpy(&s->s_buf[s->s_len], buf, n);
+ s->s_len += n;
+ if (SBUF_ISSECTION(s))
+ s->s_sect_len += n;
+ len -= n;
+ buf += n;
}
- s->s_buf[s->s_len++] = c;
- if (SBUF_ISSECTION(s))
- s->s_sect_len++;
+}
+
+static void
+sbuf_put_byte(struct sbuf *s, char c)
+{
+
+ sbuf_put_bytes(s, &c, 1);
}
/*
@@ -388,19 +438,10 @@ sbuf_put_byte(struct sbuf *s, int c)
int
sbuf_bcat(struct sbuf *s, const void *buf, size_t len)
{
- const char *str = buf;
- const char *end = str + len;
-
- assert_sbuf_integrity(s);
- assert_sbuf_state(s, 0);
+ sbuf_put_bytes(s, buf, len);
if (s->s_error != 0)
return (-1);
- for (; str < end; str++) {
- sbuf_put_byte(s, *str);
- if (s->s_error != 0)
- return (-1);
- }
return (0);
}
@@ -454,18 +495,12 @@ sbuf_bcpy(struct sbuf *s, const void *buf, size_t len)
int
sbuf_cat(struct sbuf *s, const char *str)
{
+ size_t n;
- assert_sbuf_integrity(s);
- assert_sbuf_state(s, 0);
-
+ n = strlen(str);
+ sbuf_put_bytes(s, str, n);
if (s->s_error != 0)
return (-1);
-
- while (*str != '\0') {
- sbuf_put_byte(s, *str++);
- if (s->s_error != 0)
- return (-1);
- }
return (0);
}
@@ -588,6 +623,10 @@ sbuf_vprintf(struct sbuf *s, const char *fmt, va_list ap)
va_copy(ap_copy, ap);
len = vsnprintf(&s->s_buf[s->s_len], SBUF_FREESPACE(s) + 1,
fmt, ap_copy);
+ if (len < 0) {
+ s->s_error = errno;
+ return (-1);
+ }
va_end(ap_copy);
if (SBUF_FREESPACE(s) >= len)
@@ -697,11 +736,13 @@ sbuf_finish(struct sbuf *s)
assert_sbuf_integrity(s);
assert_sbuf_state(s, 0);
+ s->s_buf[s->s_len] = '\0';
+ if (SBUF_NULINCLUDED(s))
+ s->s_len++;
if (s->s_drain_func != NULL) {
while (s->s_len > 0 && s->s_error == 0)
s->s_error = sbuf_drain(s);
}
- s->s_buf[s->s_len] = '\0';
SBUF_SETFLAG(s, SBUF_FINISHED);
#ifdef _KERNEL
return (s->s_error);
@@ -743,6 +784,10 @@ sbuf_len(struct sbuf *s)
if (s->s_error != 0)
return (-1);
+
+ /* If finished, nulterm is already in len, else add one. */
+ if (SBUF_NULINCLUDED(s) && !SBUF_ISFINISHED(s))
+ return (s->s_len + 1);
return (s->s_len);
}
diff --git a/sys/sys/sbuf.h b/sys/sys/sbuf.h
index 2b4f302..c05bafd 100644
--- a/sys/sys/sbuf.h
+++ b/sys/sys/sbuf.h
@@ -48,6 +48,7 @@ struct sbuf {
ssize_t s_len; /* current length of string */
#define SBUF_FIXEDLEN 0x00000000 /* fixed length buffer (default) */
#define SBUF_AUTOEXTEND 0x00000001 /* automatically extend buffer */
+#define SBUF_INCLUDENUL 0x00000002 /* nulterm byte is counted in len */
#define SBUF_USRFLAGMSK 0x0000ffff /* mask of flags the user may specify */
#define SBUF_DYNAMIC 0x00010000 /* s_buf must be freed */
#define SBUF_FINISHED 0x00020000 /* set by sbuf_finish() */
@@ -72,6 +73,9 @@ __BEGIN_DECLS
struct sbuf *sbuf_new(struct sbuf *, char *, int, int);
#define sbuf_new_auto() \
sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND)
+int sbuf_get_flags(struct sbuf *);
+void sbuf_clear_flags(struct sbuf *, int);
+void sbuf_set_flags(struct sbuf *, int);
void sbuf_clear(struct sbuf *);
int sbuf_setpos(struct sbuf *, ssize_t);
int sbuf_bcat(struct sbuf *, const void *, size_t);
OpenPOWER on IntegriCloud