diff options
| -rw-r--r-- | include/fstream.h | 35 | ||||
| -rw-r--r-- | lib/fstream/Makemodule.am | 2 | ||||
| -rw-r--r-- | lib/fstream/get_line.c | 118 | ||||
| -rw-r--r-- | lib/fstream/internal.h | 1 | ||||
| -rw-r--r-- | tests/Makemodule.am | 9 | ||||
| -rw-r--r-- | tests/get_line.c | 164 | ||||
| -rw-r--r-- | tests/get_line.txt | 11 | ||||
| -rw-r--r-- | tests/test.h | 5 | ||||
| -rw-r--r-- | tests/test_tar.h | 5 | 
9 files changed, 343 insertions, 7 deletions
diff --git a/include/fstream.h b/include/fstream.h index 8693fff..07ee5a7 100644 --- a/include/fstream.h +++ b/include/fstream.h @@ -56,6 +56,12 @@ enum {  };  enum { +	ISTREAM_LINE_LTRIM = 0x01, +	ISTREAM_LINE_RTRIM = 0x02, +	ISTREAM_LINE_SKIP_EMPTY = 0x04, +}; + +enum {  	/**  	 * @brief Deflate compressor with gzip headers.  	 * @@ -258,6 +264,35 @@ SQFS_INTERNAL const char *ostream_get_filename(ostream_t *strm);  SQFS_INTERNAL int ostream_printf(ostream_t *strm, const char *fmt, ...);  /** + * @brief Read a line of text from an input stream + * + * @memberof istream_t + * + * The line returned is allocated using malloc and must subsequently be + * freed when it is no longer needed. The line itself is always null-terminated + * and never includes the line break characters (LF or CR-LF). + * + * If the flag @ref ISTREAM_LINE_LTRIM is set, leading white space characters + * are removed. If the flag @ref ISTREAM_LINE_RTRIM is set, trailing white space + * characters are remvoed. + * + * If the flag @ref ISTREAM_LINE_SKIP_EMPTY is set and a line is discovered to + * be empty (after the optional trimming), the function discards the empty line + * and retries. The given line_num pointer is used to increment the line + * number. + * + * @param strm A pointer to an input stream. + * @param out Returns a pointer to a line on success. + * @param line_num This is incremented if lines are skipped. + * @param flags A combination of flags controling the functions behaviour. + * + * @return Zero on success, a negative value on error, a positive value if + *         end-of-file was reached without reading any data. + */ +SQFS_INTERNAL int istream_get_line(istream_t *strm, char **out, +				   size_t *line_num, int flags); + +/**   * @brief Read data from an input stream   *   * @memberof istream_t diff --git a/lib/fstream/Makemodule.am b/lib/fstream/Makemodule.am index 0c24c6f..ad5f426 100644 --- a/lib/fstream/Makemodule.am +++ b/lib/fstream/Makemodule.am @@ -1,7 +1,7 @@  libfstream_a_SOURCES = include/fstream.h  libfstream_a_SOURCES += lib/fstream/internal.h  libfstream_a_SOURCES += lib/fstream/ostream.c lib/fstream/printf.c -libfstream_a_SOURCES += lib/fstream/istream.c +libfstream_a_SOURCES += lib/fstream/istream.c lib/fstream/get_line.c  libfstream_a_SOURCES += lib/fstream/compressor.c  libfstream_a_SOURCES += lib/fstream/compress/ostream_compressor.c  libfstream_a_SOURCES += lib/fstream/uncompress/istream_compressor.c 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; +} diff --git a/lib/fstream/internal.h b/lib/fstream/internal.h index 2dc81e4..4f02f8c 100644 --- a/lib/fstream/internal.h +++ b/lib/fstream/internal.h @@ -16,6 +16,7 @@  #include <stdarg.h>  #include <stdlib.h>  #include <unistd.h> +#include <ctype.h>  #include <fcntl.h>  #include <errno.h>  #include <stdio.h> diff --git a/tests/Makemodule.am b/tests/Makemodule.am index 94bfd4b..58cd27e 100644 --- a/tests/Makemodule.am +++ b/tests/Makemodule.am @@ -230,6 +230,11 @@ fstree_fuzz_LDADD = libfstree.a libcompat.a  tar_fuzz_SOURCES = tests/tar_fuzz.c  tar_fuzz_LDADD = libtar.a libfstream.a libcompat.a +test_get_line_SOURCES = tests/get_line.c tests/test.h +test_get_line_LDADD = libfstream.a libcompat.a +test_get_line_CPPFLAGS = $(AM_CPPFLAGS) +test_get_line_CPPFLAGS += -DTESTFILE=$(top_srcdir)/tests/get_line.txt +  check_PROGRAMS += test_mknode_simple test_mknode_slink test_mknode_reg  check_PROGRAMS += test_mknode_dir test_gen_inode_numbers test_add_by_path  check_PROGRAMS += test_get_path test_fstree_sort test_fstree_from_file @@ -245,6 +250,7 @@ check_PROGRAMS += test_tar_sparse_gnu test_tar_sparse_gnu0 test_tar_sparse_gnu1  check_PROGRAMS += test_tar_sparse_gnu2 test_tar_sparse_gnu3  check_PROGRAMS += test_tar_xattr_bsd test_tar_xattr_schily  check_PROGRAMS += test_tar_xattr_schily_bin test_tar_target_filled +check_PROGRAMS += test_get_line  noinst_PROGRAMS += fstree_fuzz tar_fuzz @@ -261,7 +267,7 @@ TESTS += test_tar_pax5  TESTS += test_tar_sparse_gnu test_tar_sparse_gnu0  TESTS += test_tar_sparse_gnu1 test_tar_sparse_gnu2 test_tar_sparse_gnu3  TESTS += test_tar_xattr_bsd test_tar_xattr_schily -TESTS += test_tar_xattr_schily_bin test_tar_target_filled +TESTS += test_tar_xattr_schily_bin test_tar_target_filled test_get_line  if CORPORA_TESTS  check_SCRIPTS += tests/cantrbry.sh tests/test_tar_sqfs.sh tests/pack_dir_root.sh @@ -274,3 +280,4 @@ EXTRA_DIST += $(top_srcdir)/tests/fstree1.txt  EXTRA_DIST += $(top_srcdir)/tests/corpus/cantrbry.tar.xz  EXTRA_DIST += $(top_srcdir)/tests/corpus/cantrbry.sha512  EXTRA_DIST += $(top_srcdir)/tests/pack_dir_root.txt.ref +EXTRA_DIST += $(top_srcdir)/tests/get_line.txt diff --git a/tests/get_line.c b/tests/get_line.c new file mode 100644 index 0000000..c317c0e --- /dev/null +++ b/tests/get_line.c @@ -0,0 +1,164 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * get_line.c + * + * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at> + */ +#include "config.h" + +#include "fstream.h" +#include "test.h" + +typedef struct { +	size_t line_num; +	const char *str; +} line_t; + +static void run_test_case(const line_t *lines, size_t count, +			  int flags) +{ +	size_t i, line_num, old_line_num; +	istream_t *fp; +	char *line; +	int ret; + +	fp = istream_open_file(STRVALUE(TESTFILE)); +	TEST_NOT_NULL(fp); + +	line_num = 1; +	line = NULL; + +	for (i = 0; i < count; ++i) { +		old_line_num = line_num; +		ret = istream_get_line(fp, &line, &line_num, flags); + +		TEST_ASSERT(line_num >= old_line_num); +		TEST_EQUAL_I(ret, 0); +		TEST_NOT_NULL(line); + +		TEST_EQUAL_UI(line_num, lines[i].line_num); +		TEST_STR_EQUAL(line, lines[i].str); + +		free(line); +		line = NULL; +		line_num += 1; +	} + +	ret = istream_get_line(fp, &line, &line_num, flags); +	TEST_ASSERT(ret > 0); + +	sqfs_destroy(fp); +} + +static const line_t lines_raw[] = { +	{ 1, "" }, +	{ 2, "The quick" }, +	{ 3, "  " }, +	{ 4, "  brown fox  " }, +	{ 5, "" }, +	{ 6, "jumps over" }, +	{ 7, "the" }, +	{ 8, "lazy" }, +	{ 9, "" }, +	{ 10, "dog" }, +	{ 11, "" }, +}; + +static const line_t lines_ltrim[] = { +	{ 1, "" }, +	{ 2, "The quick" }, +	{ 3, "" }, +	{ 4, "brown fox  " }, +	{ 5, "" }, +	{ 6, "jumps over" }, +	{ 7, "the" }, +	{ 8, "lazy" }, +	{ 9, "" }, +	{ 10, "dog" }, +	{ 11, "" }, +}; + +static const line_t lines_rtrim[] = { +	{ 1, "" }, +	{ 2, "The quick" }, +	{ 3, "" }, +	{ 4, "  brown fox" }, +	{ 5, "" }, +	{ 6, "jumps over" }, +	{ 7, "the" }, +	{ 8, "lazy" }, +	{ 9, "" }, +	{ 10, "dog" }, +	{ 11, "" }, +}; + +static const line_t lines_trim[] = { +	{ 1, "" }, +	{ 2, "The quick" }, +	{ 3, "" }, +	{ 4, "brown fox" }, +	{ 5, "" }, +	{ 6, "jumps over" }, +	{ 7, "the" }, +	{ 8, "lazy" }, +	{ 9, "" }, +	{ 10, "dog" }, +	{ 11, "" }, +}; + +static const line_t lines_no_empty[] = { +	{ 2, "The quick" }, +	{ 3, "  " }, +	{ 4, "  brown fox  " }, +	{ 6, "jumps over" }, +	{ 7, "the" }, +	{ 8, "lazy" }, +	{ 10, "dog" }, +}; + +static const line_t lines_no_empty_ltrim[] = { +	{ 2, "The quick" }, +	{ 4, "brown fox  " }, +	{ 6, "jumps over" }, +	{ 7, "the" }, +	{ 8, "lazy" }, +	{ 10, "dog" }, +}; + +static const line_t lines_no_empty_rtrim[] = { +	{ 2, "The quick" }, +	{ 4, "  brown fox" }, +	{ 6, "jumps over" }, +	{ 7, "the" }, +	{ 8, "lazy" }, +	{ 10, "dog" }, +}; + +static const line_t lines_no_empty_trim[] = { +	{ 2, "The quick" }, +	{ 4, "brown fox" }, +	{ 6, "jumps over" }, +	{ 7, "the" }, +	{ 8, "lazy" }, +	{ 10, "dog" }, +}; + +int main(void) +{ +	run_test_case(lines_raw, 11, 0); +	run_test_case(lines_ltrim, 11, ISTREAM_LINE_LTRIM); +	run_test_case(lines_rtrim, 11, ISTREAM_LINE_RTRIM); +	run_test_case(lines_trim, 11, +		      ISTREAM_LINE_LTRIM | ISTREAM_LINE_RTRIM); + +	run_test_case(lines_no_empty, 7, ISTREAM_LINE_SKIP_EMPTY); +	run_test_case(lines_no_empty_ltrim, 6, +		      ISTREAM_LINE_SKIP_EMPTY | ISTREAM_LINE_LTRIM); +	run_test_case(lines_no_empty_rtrim, 6, +		      ISTREAM_LINE_SKIP_EMPTY | ISTREAM_LINE_RTRIM); +	run_test_case(lines_no_empty_trim, 6, +		      ISTREAM_LINE_SKIP_EMPTY | ISTREAM_LINE_LTRIM | +		      ISTREAM_LINE_RTRIM); + +	return EXIT_SUCCESS; +} diff --git a/tests/get_line.txt b/tests/get_line.txt new file mode 100644 index 0000000..a1994f0 --- /dev/null +++ b/tests/get_line.txt @@ -0,0 +1,11 @@ +
 +The quick
 +  
 +  brown fox  
 +
 +jumps over
 +the
 +lazy
 +
 +dog
 +
 diff --git a/tests/test.h b/tests/test.h index 7d38fd0..bc3ac92 100644 --- a/tests/test.h +++ b/tests/test.h @@ -13,6 +13,11 @@  #include <stdio.h>  #include <errno.h> +#define STR(x) #x +#define STRVALUE(x) STR(x) + +#define TEST_PATH STRVALUE(TESTPATH) +  #if defined(__GNUC__) || defined(__clang__)  #	define ATTRIB_UNUSED __attribute__ ((unused))  #else diff --git a/tests/test_tar.h b/tests/test_tar.h index 9ec2b12..0d5ccc1 100644 --- a/tests/test_tar.h +++ b/tests/test_tar.h @@ -11,9 +11,4 @@  #include "tar.h"  #include "test.h" -#define STR(x) #x -#define STRVALUE(x) STR(x) - -#define TEST_PATH STRVALUE(TESTPATH) -  #endif /* TEST_TAR_H */  | 
