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
This commit is contained in:
parent
07683c3cc6
commit
efd8ed9206
@ -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 two-byte buffer.
|
||||
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
|
||||
@ -476,6 +483,11 @@ 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
|
||||
function formats its arguments according to the format string pointed
|
||||
to by
|
||||
|
@ -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 flags)
|
||||
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.
|
||||
@ -716,6 +734,38 @@ sbuf_putc(struct sbuf *s, int c)
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Trim whitespace characters from end of an sbuf.
|
||||
*/
|
||||
|
@ -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 *);
|
||||
|
Loading…
Reference in New Issue
Block a user