diff options
Diffstat (limited to 'lib/fstream/get_line.c')
-rw-r--r-- | lib/fstream/get_line.c | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/lib/fstream/get_line.c b/lib/fstream/get_line.c new file mode 100644 index 0000000..f7e0b59 --- /dev/null +++ b/lib/fstream/get_line.c @@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * get_line.c + * + * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at> + */ +#include "internal.h" + +static void ltrim(char *buffer) +{ + size_t i = 0; + + while (isspace(buffer[i])) + ++i; + + if (i > 0) + memmove(buffer, buffer + i, strlen(buffer + i) + 1); +} + +static void rtrim(char *buffer) +{ + size_t i = strlen(buffer); + + while (i > 0 && isspace(buffer[i - 1])) + --i; + + buffer[i] = '\0'; +} + +static size_t trim(char *buffer, int flags) +{ + if (flags & ISTREAM_LINE_LTRIM) + ltrim(buffer); + + if (flags & ISTREAM_LINE_RTRIM) + rtrim(buffer); + + return strlen(buffer); +} + +int istream_get_line(istream_t *strm, char **out, + size_t *line_num, int flags) +{ + char *line = NULL, *new; + size_t i, line_len = 0; + bool have_line = false; + + *out = NULL; + + for (;;) { + if (istream_precache(strm)) + return -1; + + if (strm->buffer_used == 0) { + if (line_len == 0) + goto out_eof; + + line_len = trim(line, flags); + + if (line_len == 0 && + (flags & ISTREAM_LINE_SKIP_EMPTY)) { + goto out_eof; + } + break; + } + + for (i = 0; i < strm->buffer_used; ++i) { + if (strm->buffer[i] == '\n') + break; + } + + if (i < strm->buffer_used) { + have_line = true; + strm->buffer_offset = i + 1; + + if (i > 0 && strm->buffer[i - 1] == '\r') + --i; + } else { + strm->buffer_offset = i; + } + + new = realloc(line, line_len + i + 1); + if (new == NULL) + goto fail_errno; + + line = new; + memcpy(line + line_len, strm->buffer, i); + line_len += i; + line[line_len] = '\0'; + + if (have_line) { + line_len = trim(line, flags); + + if (line_len == 0 && + (flags & ISTREAM_LINE_SKIP_EMPTY)) { + free(line); + line = NULL; + have_line = false; + *line_num += 1; + continue; + } + break; + } + } + + *out = line; + return 0; +fail_errno: + fprintf(stderr, "%s: " PRI_SZ ": %s.\n", strm->get_filename(strm), + *line_num, strerror(errno)); + free(line); + *out = NULL; + return -1; +out_eof: + free(line); + *out = NULL; + return 1; +} |