Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 7 Aug 2019 19:27:14 +0000 (UTC)
From:      Conrad Meyer <cem@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r350693 - in head: share/man/man9 sys/kern sys/sys
Message-ID:  <201908071927.x77JREeu083971@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: cem
Date: Wed Aug  7 19:27:14 2019
New Revision: 350693
URL: https://svnweb.freebsd.org/changeset/base/350693

Log:
  sbuf(9): Add sbuf_nl_terminate() API
  
  The API is used to gracefully terminate text line(s) with a single \n.  If
  the formatted buffer was empty or already ended in \n, it is unmodified.
  Otherwise, a newline character is appended to it.  The API, like other
  sbuf-modifying routines, is only valid while the sbuf is not FINISHED.
  
  Reviewed by:	rlibby
  Sponsored by:	Dell EMC Isilon
  Differential Revision:	https://reviews.freebsd.org/D21030

Modified:
  head/share/man/man9/sbuf.9
  head/sys/kern/subr_sbuf.c
  head/sys/sys/sbuf.h

Modified: head/share/man/man9/sbuf.9
==============================================================================
--- head/share/man/man9/sbuf.9	Wed Aug  7 19:25:56 2019	(r350692)
+++ head/share/man/man9/sbuf.9	Wed Aug  7 19:27:14 2019	(r350693)
@@ -44,6 +44,7 @@
 .Nm sbuf_cat ,
 .Nm sbuf_copyin ,
 .Nm sbuf_cpy ,
+.Nm sbuf_nl_terminate ,
 .Nm sbuf_printf ,
 .Nm sbuf_vprintf ,
 .Nm sbuf_putc ,
@@ -123,6 +124,8 @@
 .Fa "const char *str"
 .Fc
 .Ft int
+.Fn sbuf_nl_terminate "struct sbuf *"
+.Ft int
 .Fo sbuf_printf
 .Fa "struct sbuf *s"
 .Fa "const char *fmt" "..."
@@ -440,10 +443,14 @@ To do unbuffered draining, initialize the sbuf with a 
 The drain will be called for every byte added to the sbuf.
 The
 .Fn sbuf_bcopyin ,
+.Fn sbuf_bcpy ,
+.Fn sbuf_clear ,
 .Fn sbuf_copyin ,
+.Fn sbuf_cpy ,
 .Fn sbuf_trim ,
+.Fn sbuf_data ,
 and
-.Fn sbuf_data
+.Fn sbuf_len
 functions cannot be used on an sbuf with a drain.
 .Pp
 The
@@ -474,6 +481,11 @@ or one which position has been reset to zero with
 .Fn sbuf_clear
 or
 .Fn sbuf_setpos .
+.Pp
+The
+.Fn sbuf_nl_terminate
+function appends a trailing newline character, if the current line is non-empty
+and not already terminated by a newline character.
 .Pp
 The
 .Fn sbuf_printf

Modified: head/sys/kern/subr_sbuf.c
==============================================================================
--- head/sys/kern/subr_sbuf.c	Wed Aug  7 19:25:56 2019	(r350692)
+++ head/sys/kern/subr_sbuf.c	Wed Aug  7 19:27:14 2019	(r350693)
@@ -70,6 +70,7 @@ static MALLOC_DEFINE(M_SBUF, "sbuf", "string buffers")
 #define	SBUF_ISDYNAMIC(s)	((s)->s_flags & SBUF_DYNAMIC)
 #define	SBUF_ISDYNSTRUCT(s)	((s)->s_flags & SBUF_DYNSTRUCT)
 #define	SBUF_ISFINISHED(s)	((s)->s_flags & SBUF_FINISHED)
+#define	SBUF_ISDRAINATEOL(s)	((s)->s_flags & SBUF_DRAINATEOL)
 #define	SBUF_HASROOM(s)		((s)->s_len < (s)->s_size - 1)
 #define	SBUF_FREESPACE(s)	((s)->s_size - ((s)->s_len + 1))
 #define	SBUF_CANEXTEND(s)	((s)->s_flags & SBUF_AUTOEXTEND)
@@ -225,6 +226,10 @@ sbuf_new(struct sbuf *s, char *buf, int length, int fl
 	s->s_flags |= flags;
 	s->s_size = length;
 	s->s_buf = buf;
+	/*
+	 * Never-written sbufs do not need \n termination.
+	 */
+	SBUF_SETFLAG(s, SBUF_DRAINATEOL);
 
 	/*
 	 * Allocate DYNAMIC, i.e., heap data buffer backing the sbuf, if no
@@ -310,6 +315,8 @@ sbuf_clear(struct sbuf *s)
 
 	assert_sbuf_integrity(s);
 	/* don't care if it's finished or not */
+	KASSERT(s->s_drain_func == NULL,
+	    ("%s makes no sense on sbuf %p with drain", __func__, s));
 
 	SBUF_CLEARFLAG(s, SBUF_FINISHED);
 	s->s_error = 0;
@@ -384,6 +391,7 @@ sbuf_drain(struct sbuf *s)
 
 	KASSERT(s->s_len > 0, ("Shouldn't drain empty sbuf %p", s));
 	KASSERT(s->s_error == 0, ("Called %s with error on %p", __func__, s));
+
 	if (SBUF_DODRAINTOEOR(s) && s->s_rec_off == 0)
 		return (s->s_error = EDEADLK);
 	len = s->s_drain_func(s->s_drain_arg, s->s_buf,
@@ -400,8 +408,18 @@ sbuf_drain(struct sbuf *s)
 	 * Fast path for the expected case where all the data was
 	 * drained.
 	 */
-	if (s->s_len == 0)
+	if (s->s_len == 0) {
+		/*
+		 * When the s_buf is entirely drained, we need to remember if
+		 * the last character was a '\n' or not for
+		 * sbuf_nl_terminate().
+		 */
+		if (s->s_buf[len - 1] == '\n')
+			SBUF_SETFLAG(s, SBUF_DRAINATEOL);
+		else
+			SBUF_CLEARFLAG(s, SBUF_DRAINATEOL);
 		return (0);
+	}
 	/*
 	 * Move the remaining characters to the beginning of the
 	 * string.
@@ -711,6 +729,38 @@ sbuf_putc(struct sbuf *s, int c)
 {
 
 	sbuf_put_byte(s, c);
+	if (s->s_error != 0)
+		return (-1);
+	return (0);
+}
+
+/*
+ * Append a trailing newline to a non-empty sbuf, if one is not already
+ * present.  Handles sbufs with drain functions correctly.
+ */
+int
+sbuf_nl_terminate(struct sbuf *s)
+{
+
+	assert_sbuf_integrity(s);
+	assert_sbuf_state(s, 0);
+
+	/*
+	 * If the s_buf isn't empty, the last byte is simply s_buf[s_len - 1].
+	 *
+	 * If the s_buf is empty because a drain function drained it, we
+	 * remember if the last byte was a \n with the SBUF_DRAINATEOL flag in
+	 * sbuf_drain().
+	 *
+	 * In either case, we only append a \n if the previous character was
+	 * something else.
+	 */
+	if (s->s_len == 0) {
+		if (!SBUF_ISDRAINATEOL(s))
+			sbuf_put_byte(s, '\n');
+	} else if (s->s_buf[s->s_len - 1] != '\n')
+		sbuf_put_byte(s, '\n');
+
 	if (s->s_error != 0)
 		return (-1);
 	return (0);

Modified: head/sys/sys/sbuf.h
==============================================================================
--- head/sys/sys/sbuf.h	Wed Aug  7 19:25:56 2019	(r350692)
+++ head/sys/sys/sbuf.h	Wed Aug  7 19:27:14 2019	(r350693)
@@ -58,6 +58,7 @@ struct sbuf {
 #define	SBUF_FINISHED	0x00020000	/* set by sbuf_finish() */
 #define	SBUF_DYNSTRUCT	0x00080000	/* sbuf must be freed */
 #define	SBUF_INSECTION	0x00100000	/* set by sbuf_start_section() */
+#define	SBUF_DRAINATEOL	0x00200000	/* drained contents ended in \n */
 	int		 s_flags;	/* flags */
 	ssize_t		 s_sect_len;	/* current length of section */
 	ssize_t		 s_rec_off;	/* current record start offset */
@@ -91,6 +92,7 @@ int		 sbuf_printf(struct sbuf *, const char *, ...)
 	__printflike(2, 3);
 int		 sbuf_vprintf(struct sbuf *, const char *, __va_list)
 	__printflike(2, 0);
+int		 sbuf_nl_terminate(struct sbuf *);
 int		 sbuf_putc(struct sbuf *, int);
 void		 sbuf_set_drain(struct sbuf *, sbuf_drain_func *, void *);
 int		 sbuf_trim(struct sbuf *);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201908071927.x77JREeu083971>