Merge pull request #6891 from poettering/read-line

add read_line() helper as bounded getline() and make use of it at some places
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2017-09-24 20:51:01 +02:00
committed by GitHub
8 changed files with 331 additions and 37 deletions
+130 -6
View File
@@ -18,6 +18,8 @@
***/
#include "conf-parser.h"
#include "fd-util.h"
#include "fileio.h"
#include "log.h"
#include "macro.h"
#include "string-util.h"
@@ -25,12 +27,10 @@
#include "util.h"
static void test_config_parse_path_one(const char *rvalue, const char *expected) {
char *path = NULL;
_cleanup_free_ char *path = NULL;
assert_se(config_parse_path("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &path, NULL) >= 0);
assert_se(streq_ptr(expected, path));
free(path);
}
static void test_config_parse_log_level_one(const char *rvalue, int expected) {
@@ -76,12 +76,10 @@ static void test_config_parse_unsigned_one(const char *rvalue, unsigned expected
}
static void test_config_parse_strv_one(const char *rvalue, char **expected) {
char **strv = 0;
_cleanup_strv_free_ char **strv = NULL;
assert_se(config_parse_strv("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &strv, NULL) >= 0);
assert_se(strv_equal(expected, strv));
strv_free(strv);
}
static void test_config_parse_mode_one(const char *rvalue, mode_t expected) {
@@ -227,7 +225,130 @@ static void test_config_parse_iec_uint64(void) {
assert_se(config_parse_iec_uint64(NULL, "/this/file", 11, "Section", 22, "Size", 0, "4.5M", &offset, NULL) == 0);
}
#define x10(x) x x x x x x x x x x
#define x100(x) x10(x10(x))
#define x1000(x) x10(x100(x))
static const char* const config_file[] = {
"[Section]\n"
"setting1=1\n",
"[Section]\n"
"setting1=1", /* no terminating newline */
"\n\n\n\n[Section]\n\n\n"
"setting1=1", /* some whitespace, no terminating newline */
"[Section]\n"
"[Section]\n"
"setting1=1\n"
"setting1=2\n"
"setting1=1\n", /* repeated settings */
"[Section]\n"
"setting1=1\\\n" /* normal continuation */
"2\\\n"
"3\n",
"[Section]\n"
"setting1=1\\\\\\\n" /* continuation with trailing escape symbols */
"\\\\2\n", /* note that C requires one level of escaping, so the
* parser gets "…1 BS BS BS NL BS BS 2 NL", which
* it translates into "…1 BS BS SP BS BS 2" */
"\n[Section]\n\n"
"setting1=" /* a line above LINE_MAX length */
x1000("ABCD")
"\n",
"[Section]\n"
"setting1=" /* a line above LINE_MAX length, with continuation */
x1000("ABCD") "\\\n"
"foobar",
"[Section]\n"
"setting1=" /* a line above the allowed limit: 9 + 1050000 + 1 */
x1000(x1000("x") x10("abcde")) "\n",
"[Section]\n"
"setting1=" /* many continuation lines, together above the limit */
x1000(x1000("x") x10("abcde") "\\\n") "xxx",
};
static void test_config_parse(unsigned i, const char *s) {
char name[] = "/tmp/test-conf-parser.XXXXXX";
int fd, r;
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *setting1 = NULL;
const ConfigTableItem items[] = {
{ "Section", "setting1", config_parse_string, 0, &setting1},
{}
};
log_info("== %s[%i] ==", __func__, i);
fd = mkostemp_safe(name);
assert_se(fd >= 0);
assert_se((size_t) write(fd, s, strlen(s)) == strlen(s));
assert_se(lseek(fd, 0, SEEK_SET) == 0);
assert_se(f = fdopen(fd, "r"));
/*
int config_parse(const char *unit,
const char *filename,
FILE *f,
const char *sections,
ConfigItemLookup lookup,
const void *table,
bool relaxed,
bool allow_include,
bool warn,
void *userdata)
*/
r = config_parse(NULL, name, f,
"Section\0",
config_item_table_lookup, items,
false, false, true, NULL);
switch (i) {
case 0 ... 3:
assert_se(r == 0);
assert_se(streq(setting1, "1"));
break;
case 4:
assert_se(r == 0);
assert_se(streq(setting1, "1 2 3"));
break;
case 5:
assert_se(r == 0);
assert_se(streq(setting1, "1\\\\ \\\\2"));
break;
case 6:
assert_se(r == 0);
assert_se(streq(setting1, x1000("ABCD")));
break;
case 7:
assert_se(r == 0);
assert_se(streq(setting1, x1000("ABCD") " foobar"));
break;
case 8 ... 9:
assert_se(r == -ENOBUFS);
assert_se(setting1 == NULL);
break;
}
}
int main(int argc, char **argv) {
unsigned i;
log_parse_environment();
log_open();
@@ -244,5 +365,8 @@ int main(int argc, char **argv) {
test_config_parse_nsec();
test_config_parse_iec_uint64();
for (i = 0; i < ELEMENTSOF(config_file); i++)
test_config_parse(i, config_file[i]);
return 0;
}
+84 -2
View File
@@ -209,7 +209,7 @@ static void test_parse_multiline_env_file(void) {
static void test_merge_env_file(void) {
char t[] = "/tmp/test-fileio-XXXXXX";
int fd, r;
FILE *f;
_cleanup_fclose_ FILE *f = NULL;
_cleanup_strv_free_ char **a = NULL;
char **i;
@@ -278,7 +278,7 @@ static void test_merge_env_file(void) {
static void test_merge_env_file_invalid(void) {
char t[] = "/tmp/test-fileio-XXXXXX";
int fd, r;
FILE *f;
_cleanup_fclose_ FILE *f = NULL;
_cleanup_strv_free_ char **a = NULL;
char **i;
@@ -663,6 +663,85 @@ static void test_tempfn(void) {
free(ret);
}
static const char buffer[] =
"Some test data\n"
"With newlines, and a NUL byte\0"
"\n"
"an empty line\n"
"an ignored line\n"
"and a very long line that is supposed to be truncated, because it is so long\n";
static void test_read_line_one_file(FILE *f) {
_cleanup_free_ char *line = NULL;
assert_se(read_line(f, (size_t) -1, &line) == 15 && streq(line, "Some test data"));
line = mfree(line);
assert_se(read_line(f, 1024, &line) == 30 && streq(line, "With newlines, and a NUL byte"));
line = mfree(line);
assert_se(read_line(f, 1024, &line) == 1 && streq(line, ""));
line = mfree(line);
assert_se(read_line(f, 1024, &line) == 14 && streq(line, "an empty line"));
line = mfree(line);
assert_se(read_line(f, (size_t) -1, NULL) == 16);
assert_se(read_line(f, 16, &line) == -ENOBUFS);
line = mfree(line);
/* read_line() stopped when it hit the limit, that means when we continue reading we'll read at the first
* character after the previous limit. Let's make use of tha to continue our test. */
assert_se(read_line(f, 1024, &line) == 61 && streq(line, "line that is supposed to be truncated, because it is so long"));
line = mfree(line);
assert_se(read_line(f, 1024, &line) == 1 && streq(line, ""));
line = mfree(line);
assert_se(read_line(f, 1024, &line) == 0 && streq(line, ""));
}
static void test_read_line(void) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *line = NULL;
f = fmemopen((void*) buffer, sizeof(buffer), "re");
assert_se(f);
test_read_line_one_file(f);
}
static void test_read_line2(void) {
char name[] = "/tmp/test-fileio.XXXXXX";
int fd;
_cleanup_fclose_ FILE *f = NULL;
fd = mkostemp_safe(name);
assert_se(fd >= 0);
assert_se((size_t) write(fd, buffer, sizeof(buffer)) == sizeof(buffer));
assert_se(lseek(fd, 0, SEEK_SET) == 0);
assert_se(f = fdopen(fd, "r"));
test_read_line_one_file(f);
}
static void test_read_line3(void) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *line = NULL;
int r;
f = fopen("/proc/cmdline", "re");
if (!f && IN_SET(errno, ENOENT, EPERM))
return;
assert_se(f);
r = read_line(f, LINE_MAX, &line);
assert_se((size_t) r == strlen(line) + 1);
assert_se(read_line(f, LINE_MAX, NULL) == 0);
}
int main(int argc, char *argv[]) {
log_set_max_level(LOG_DEBUG);
log_parse_environment();
@@ -684,6 +763,9 @@ int main(int argc, char *argv[]) {
test_search_and_fopen_nulstr();
test_writing_tmpfile();
test_tempfn();
test_read_line();
test_read_line2();
test_read_line3();
return 0;
}