diff options
author | Michael Brown | 2015-07-28 17:59:11 +0200 |
---|---|---|
committer | Michael Brown | 2015-07-28 17:59:11 +0200 |
commit | 1e4ff872be639e93e8df3918a965bb08675dcc77 (patch) | |
tree | 77aed8f6f542ebe09cd66f0871ef4a184b160756 /src/core | |
parent | [pool] Add a generic concept of a pooled connection (diff) | |
download | ipxe-1e4ff872be639e93e8df3918a965bb08675dcc77.tar.gz ipxe-1e4ff872be639e93e8df3918a965bb08675dcc77.tar.xz ipxe-1e4ff872be639e93e8df3918a965bb08675dcc77.zip |
[linebuf] Support buffering of multiple lines
Allow line buffer to accumulate multiple lines, with buffered_line()
returning each freshly-completed line as it is encountered. This
allows buffered lines to be subsequently processed as a group.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/linebuf.c | 53 |
1 files changed, 40 insertions, 13 deletions
diff --git a/src/core/linebuf.c b/src/core/linebuf.c index 7930d19a..c197e383 100644 --- a/src/core/linebuf.c +++ b/src/core/linebuf.c @@ -43,7 +43,18 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * @ret line Buffered line, or NULL if no line ready to read */ char * buffered_line ( struct line_buffer *linebuf ) { - return ( linebuf->ready ? linebuf->data : NULL ); + char *line = &linebuf->data[ linebuf->len ]; + + /* Fail unless we have a newly completed line to retrieve */ + if ( ( linebuf->len == 0 ) || ( linebuf->consumed == 0 ) || + ( *(--line) != '\0' ) ) + return NULL; + + /* Identify start of line */ + while ( ( line > linebuf->data ) && ( line[-1] != '\0' ) ) + line--; + + return line; } /** @@ -52,10 +63,11 @@ char * buffered_line ( struct line_buffer *linebuf ) { * @v linebuf Line buffer */ void empty_line_buffer ( struct line_buffer *linebuf ) { + free ( linebuf->data ); linebuf->data = NULL; linebuf->len = 0; - linebuf->ready = 0; + linebuf->consumed = 0; } /** @@ -76,16 +88,13 @@ void empty_line_buffer ( struct line_buffer *linebuf ) { * should call empty_line_buffer() before freeing a @c struct @c * line_buffer. */ -ssize_t line_buffer ( struct line_buffer *linebuf, - const char *data, size_t len ) { +int line_buffer ( struct line_buffer *linebuf, const char *data, size_t len ) { const char *eol; size_t consume; size_t new_len; char *new_data; - - /* Free any completed line from previous iteration */ - if ( linebuf->ready ) - empty_line_buffer ( linebuf ); + char *lf; + char *cr; /* Search for line terminator */ if ( ( eol = memchr ( data, '\n', len ) ) ) { @@ -94,6 +103,10 @@ ssize_t line_buffer ( struct line_buffer *linebuf, consume = len; } + /* Reject any embedded NULs within the data to be consumed */ + if ( memchr ( data, '\0', consume ) ) + return -EINVAL; + /* Reallocate data buffer and copy in new data */ new_len = ( linebuf->len + consume ); new_data = realloc ( linebuf->data, ( new_len + 1 ) ); @@ -104,13 +117,27 @@ ssize_t line_buffer ( struct line_buffer *linebuf, linebuf->data = new_data; linebuf->len = new_len; - /* If we have reached end of line, trim the line and mark as ready */ + /* If we have reached end of line, terminate the line */ if ( eol ) { - linebuf->data[--linebuf->len] = '\0'; /* trim NL */ - if ( linebuf->data[linebuf->len - 1] == '\r' ) - linebuf->data[--linebuf->len] = '\0'; /* trim CR */ - linebuf->ready = 1; + + /* Overwrite trailing LF (which must exist at this point) */ + assert ( linebuf->len > 0 ); + lf = &linebuf->data[ linebuf->len - 1 ]; + assert ( *lf == '\n' ); + *lf = '\0'; + + /* Trim (and overwrite) trailing CR, if present */ + if ( linebuf->len > 1 ) { + cr = ( lf - 1 ); + if ( *cr == '\r' ) { + linebuf->len--; + *cr = '\0'; + } + } } + /* Record consumed length */ + linebuf->consumed = consume; + return consume; } |