summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorDavid Oberhollenzer <david.oberhollenzer@tele2.at>2018-10-10 11:28:46 +0200
committerDavid Oberhollenzer <david.oberhollenzer@tele2.at>2018-10-10 16:45:11 +0200
commit24c90b7700e18d0668799f8f343bc854a42dea20 (patch)
tree224d9b5e81a46e27f354c6975fc3fa4cd1f1fb79 /lib
parent7b647eefef00afb6104e84fae2a2fbf0481d4364 (diff)
Configuration parser cleanup
- Do a getline() & process in rdline instead of doing a read per character and feeding it through a state machine. - Move splitkv to rdcfg.c, the only place where it is used Signed-off-by: David Oberhollenzer <david.oberhollenzer@tele2.at>
Diffstat (limited to 'lib')
-rw-r--r--lib/Makemodule.am3
-rw-r--r--lib/cron/rdcron.c6
-rw-r--r--lib/include/libcfg.h29
-rw-r--r--lib/libcfg/rdcfg.c32
-rw-r--r--lib/libcfg/rdline.c227
-rw-r--r--lib/libcfg/splitkv.c48
-rw-r--r--lib/util/rdsvc.c13
7 files changed, 158 insertions, 200 deletions
diff --git a/lib/Makemodule.am b/lib/Makemodule.am
index fb6ca92..51bce72 100644
--- a/lib/Makemodule.am
+++ b/lib/Makemodule.am
@@ -8,8 +8,7 @@ libinit_a_SOURCES += lib/util/print_version.c lib/util/argv_exec.c $(HEADRS)
libinit_a_CPPFLAGS = $(AM_CPPFLAGS)
libinit_a_CFLAGS = $(AM_CFLAGS)
-libcfg_a_SOURCES = lib/libcfg/rdline.c lib/libcfg/unescape.c
-libcfg_a_SOURCES += lib/libcfg/splitkv.c lib/libcfg/rdcfg.c
+libcfg_a_SOURCES = lib/libcfg/rdline.c lib/libcfg/unescape.c lib/libcfg/rdcfg.c
libcfg_a_SOURCES += lib/libcfg/pack_argv.c lib/include/libcfg.h
libcfg_a_CPPFLAGS = $(AM_CPPFLAGS)
libcfg_a_CFLAGS = $(AM_CFLAGS)
diff --git a/lib/cron/rdcron.c b/lib/cron/rdcron.c
index 1c11cbb..520f969 100644
--- a/lib/cron/rdcron.c
+++ b/lib/cron/rdcron.c
@@ -486,7 +486,8 @@ crontab_t *rdcron(int dirfd, const char *filename)
cron = calloc(1, sizeof(*cron));
if (cron == NULL) {
fputs("out of memory\n", stderr);
- goto out;
+ close(fd);
+ return NULL;
}
cron->minute = 0xFFFFFFFFFFFFFFFFUL;
@@ -501,7 +502,6 @@ crontab_t *rdcron(int dirfd, const char *filename)
delcron(cron);
cron = NULL;
}
-out:
- close(fd);
+ rdline_cleanup(&rd);
return cron;
}
diff --git a/lib/include/libcfg.h b/lib/include/libcfg.h
index 8096f1b..95f91a8 100644
--- a/lib/include/libcfg.h
+++ b/lib/include/libcfg.h
@@ -20,23 +20,16 @@
#include <stdbool.h>
#include <stddef.h>
+#include <stdio.h>
typedef struct {
- int fd; /* input file descriptor */
- const char *argstr; /* if not NULL, read from this instead */
-
const char *filename; /* input file name */
size_t lineno; /* current line number */
-
- size_t i; /* buffer offset */
- char buffer[256]; /* current line, null-terminated */
+ FILE *fp;
+ char *line;
int argc;
const char *const *argv;
-
- bool string; /* inside a string? */
- bool escape; /* reading an escape sequence? */
- bool comment; /* inside a comment */
} rdline_t;
typedef struct {
@@ -63,6 +56,8 @@ typedef struct {
void rdline_init(rdline_t *t, int fd, const char *filename,
int argc, const char *const *argv);
+void rdline_cleanup(rdline_t *t);
+
/*
Read from file until end-of-file or a line feed is encountered.
@@ -84,9 +79,8 @@ void rdline_init(rdline_t *t, int fd, const char *filename,
outside the bounds set by argc, processing fails. On success,
the argv value is inserted and processed as described above.
- A '%' character can be escaped by writing '%%' or, if inside
- a double quite string, by writing \%.
- - An attempt to use such an indexed argument inside an argument
- expansion, results in failure.
+ a double quoted string, by writing \%.
+ - Arguments are pasted as is. Substitution is not recursive.
- If the resulting line is empty, processing is restarted.
*/
int rdline(rdline_t *t);
@@ -111,15 +105,6 @@ int unescape(char *src);
int pack_argv(char *str);
/*
- Split the current input line into a space seperted keyword
- (alphabetical characters only) and a value (the rest of the line).
-
- If errors are encounted, prints a diagnostic message to stderr and
- returns -1. On success, zero is returned.
- */
-int splitkv(rdline_t *rd, char **k, char **v);
-
-/*
Parse a configuration file containing '<keyword> [arguments...]' lines.
The cfgobj and flags are passed to the callback in the params array.
diff --git a/lib/libcfg/rdcfg.c b/lib/libcfg/rdcfg.c
index 71a994f..ccbcf7b 100644
--- a/lib/libcfg/rdcfg.c
+++ b/lib/libcfg/rdcfg.c
@@ -19,6 +19,7 @@
#include <string.h>
#include <stdio.h>
+#include <ctype.h>
static const cfg_param_t *find_param(rdline_t *rd, const char *name,
const cfg_param_t *params, size_t count)
@@ -35,6 +36,33 @@ static const cfg_param_t *find_param(rdline_t *rd, const char *name,
return NULL;
}
+static int splitkv(rdline_t *rd, char **k, char **v)
+{
+ char *key = rd->line, *value = rd->line;
+
+ while (*value != ' ' && *value != '\0') {
+ if (!isalpha(*value)) {
+ fprintf(stderr,
+ "%s: %zu: unexpected '%c' in keyword\n",
+ rd->filename, rd->lineno, *value);
+ return -1;
+ }
+ ++value;
+ }
+
+ if (*value != ' ') {
+ fprintf(stderr, "%s: %zu: expected argument after '%s'\n",
+ rd->filename, rd->lineno, key);
+ return -1;
+ }
+
+ *(value++) = '\0';
+
+ *k = key;
+ *v = value;
+ return 0;
+}
+
int rdcfg(void *cfgobj, rdline_t *rd, const cfg_param_t *params, size_t count,
int flags)
{
@@ -61,9 +89,9 @@ int rdcfg(void *cfgobj, rdline_t *rd, const cfg_param_t *params, size_t count,
}
while ((ret = rdline(rd)) == 0) {
- if (strcmp(rd->buffer, "}") == 0)
+ if (strcmp(rd->line, "}") == 0)
break;
- if (p->handle(cfgobj, rd->buffer, rd, flags))
+ if (p->handle(cfgobj, rd->line, rd, flags))
return -1;
}
diff --git a/lib/libcfg/rdline.c b/lib/libcfg/rdline.c
index ffbfd89..16ca5cf 100644
--- a/lib/libcfg/rdline.c
+++ b/lib/libcfg/rdline.c
@@ -24,151 +24,144 @@
#include "libcfg.h"
-static int rdline_getc(rdline_t *t)
+void rdline_init(rdline_t *t, int fd, const char *filename,
+ int argc, const char *const *argv)
{
- int ret;
- char c;
+ memset(t, 0, sizeof(*t));
+ t->fp = fdopen(fd, "r");
+ t->filename = filename;
+ t->argc = argc;
+ t->argv = argv;
+}
- if (t->argstr != NULL) {
- c = *(t->argstr++);
- if (c != '\0')
- goto out;
+void rdline_cleanup(rdline_t *t)
+{
+ free(t->line);
+ fclose(t->fp);
+}
- t->argstr = NULL;
- }
+static int read_raw_line(rdline_t *t)
+{
+ size_t len = 0;
- do {
- ret = read(t->fd, &c, 1);
- } while (ret < 0 && errno == EINTR);
+ free(t->line);
+ t->line = NULL;
- if (ret < 0)
- return -1;
+ errno = 0;
- if (ret == 0) {
- if (t->i == 0) {
- errno = 0;
+ if (getline(&t->line, &len, t->fp) < 0) {
+ if (errno) {
+ fprintf(stderr, "%s: %zu: %s\n", t->filename,
+ t->lineno, strerror(errno));
return -1;
}
- c = '\0';
+ return 1;
}
-out:
- return (c == '\n') ? '\0' : c;
+
+ t->lineno += 1;
+ return 0;
}
-static int rdline_append(rdline_t *t, int c)
+static int normalize_line(rdline_t *t)
{
- if (t->comment) {
- if (c != '\0')
- return 0;
- } else if (t->string) {
- if (t->escape) {
- t->escape = false;
- } else {
- if (c == '\\')
- t->escape = true;
- if (c == '"')
- t->string = false;
- }
- } else {
- if (isspace(c))
+ char *dst = t->line, *src = t->line;
+ bool string = false;
+ const char *errstr;
+ int c, ret = 0;
+
+ while (isspace(*src))
+ ++src;
+
+ while (*src != '\0' && (string || *src != '#')) {
+ c = *(src++);
+
+ if (c == '"') {
+ string = !string;
+ } else if (!string && isspace(c)) {
c = ' ';
- if (c == ' ' && (t->i == 0 || t->buffer[t->i - 1] == ' '))
- return 0;
- if (c == '#') {
- t->comment = true;
- return 0;
+ if (dst > t->line && dst[-1] == ' ')
+ continue;
+ } else if (c == '%') {
+ *(dst++) = c;
+ c = *(src++);
+ if (c != '%' && !isdigit(c)) {
+ errstr = "expected digit after '%%'";
+ goto fail;
+ }
+ if (isdigit(c) && (c - '0') >= t->argc) {
+ errstr = "argument out of range";
+ goto fail;
+ }
+ ret += strlen(t->argv[c - '0']);
+ } else if (string && c == '\\' && *src != '\0') {
+ *(dst++) = c;
+ c = *(src++);
}
- if (c == '"')
- t->string = true;
- }
- if (c == '\0') {
- while (t->i > 0 && t->buffer[t->i - 1] == ' ')
- t->i -= 1;
+ *(dst++) = c;
}
- if (t->i == sizeof(t->buffer))
- return -1;
+ if (string) {
+ errstr = "missing \"";
+ goto fail;
+ }
- t->buffer[t->i++] = c;
- return 0;
+ while (dst > t->line && dst[-1] == ' ')
+ --dst;
+ *dst = '\0';
+ return ret;
+fail:
+ fprintf(stderr, "%s: %zu: %s\n", t->filename, t->lineno, errstr);
+ return -1;
}
-void rdline_init(rdline_t *t, int fd, const char *filename,
- int argc, const char *const *argv)
+static void substitute(rdline_t *t, char *dst, char *src)
{
- memset(t, 0, sizeof(*t));
- t->fd = fd;
- t->filename = filename;
- t->argc = argc;
- t->argv = argv;
+ bool string = false;
+
+ while (*src != '\0') {
+ if (src[0] == '%' && isdigit(src[1])) {
+ strcpy(dst, t->argv[src[1] - '0']);
+ src += 2;
+ while (*dst != '\0')
+ ++dst;
+ } else {
+ if (*src == '"')
+ string = !string;
+ if (string && *src == '\\')
+ *(dst++) = *(src++);
+ *(dst++) = *(src++);
+ }
+ }
}
int rdline(rdline_t *t)
{
- const char *errstr;
- int c;
-retry:
- t->i = 0;
- t->argstr = NULL;
- t->string = t->escape = t->comment = false;
- t->lineno += 1;
+ char *buffer = NULL;
+ int ret;
do {
- errno = 0;
- c = rdline_getc(t);
- if (c < 0) {
- if (errno == 0)
- return 1;
- errstr = strerror(errno);
- goto fail;
- }
- if (c == 0 && t->string) {
- errstr = "missing \"";
- goto fail;
- }
-
- if (c == '%') {
- c = rdline_getc(t);
- if (c == 0) {
- errstr = "unexpected end of line after '%%'";
- goto fail;
- }
- if (c < 0) {
- errstr = strerror(errno);
- goto fail;
- }
-
- if (c != '%') {
- if (!isdigit(c)) {
- errstr = "exptected digit after '%%'";
- goto fail;
- }
- if ((c - '0') >= t->argc) {
- errstr = "argument out of range";
- goto fail;
- }
- if (t->argstr != NULL) {
- errstr = "recursive argument "
- "expansion";
- goto fail;
- }
- t->argstr = t->argv[c - '0'];
- continue;
- }
- }
+ if ((ret = read_raw_line(t)))
+ goto out;
+ if ((ret = normalize_line(t)) < 0)
+ goto out;
+ } while (t->line[0] == '\0');
- if (rdline_append(t, c)) {
- errstr = "line too long";
- goto fail;
- }
- } while (c != '\0');
+ if (ret == 0)
+ return 0;
- if (t->buffer[0] == '\0')
- goto retry;
+ buffer = calloc(1, strlen(t->line) + ret + 1);
+ if (buffer == NULL) {
+ fprintf(stderr, "%s: %zu: out of memory\n",
+ t->filename, t->lineno);
+ ret = -1;
+ goto out;
+ }
- return 0;
-fail:
- fprintf(stderr, "%s: %zu: %s\n", t->filename, t->lineno, errstr);
- return -1;
+ substitute(t, buffer, t->line);
+ ret = 0;
+out:
+ free(t->line);
+ t->line = buffer;
+ return ret;
}
diff --git a/lib/libcfg/splitkv.c b/lib/libcfg/splitkv.c
deleted file mode 100644
index 49f8ebe..0000000
--- a/lib/libcfg/splitkv.c
+++ /dev/null
@@ -1,48 +0,0 @@
-/* SPDX-License-Identifier: GPL-3.0-or-later */
-/*
- * Copyright (C) 2018 - David Oberhollenzer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-#include <ctype.h>
-#include <stdio.h>
-
-#include "libcfg.h"
-
-int splitkv(rdline_t *rd, char **k, char **v)
-{
- char *key = rd->buffer, *value = rd->buffer;
-
- while (*value != ' ' && *value != '\0') {
- if (!isalpha(*value)) {
- fprintf(stderr,
- "%s: %zu: unexpected '%c' in keyword\n",
- rd->filename, rd->lineno, *value);
- return -1;
- }
- ++value;
- }
-
- if (*value != ' ') {
- fprintf(stderr, "%s: %zu: expected argument after '%s'\n",
- rd->filename, rd->lineno, key);
- return -1;
- }
-
- *(value++) = '\0';
-
- *k = key;
- *v = value;
- return 0;
-}
diff --git a/lib/util/rdsvc.c b/lib/util/rdsvc.c
index 747d753..ca60731 100644
--- a/lib/util/rdsvc.c
+++ b/lib/util/rdsvc.c
@@ -266,8 +266,6 @@ service_t *rdsvc(int dirfd, const char *filename, int flags)
argc = 0;
}
- rdline_init(&rd, fd, filename, argc, args);
-
nlen = (arg != NULL) ? (size_t)(arg - filename) : strlen(filename);
svc = calloc(1, sizeof(*svc) + nlen + 1);
@@ -282,14 +280,17 @@ service_t *rdsvc(int dirfd, const char *filename, int flags)
memcpy(svc->name, filename, nlen);
- if (rdcfg(svc, &rd, svc_params, ARRAY_SIZE(svc_params), flags))
- goto fail;
+ rdline_init(&rd, fd, filename, argc, args);
+
+ if (rdcfg(svc, &rd, svc_params, ARRAY_SIZE(svc_params), flags)) {
+ delsvc(svc);
+ svc = NULL;
+ }
- close(fd);
+ rdline_cleanup(&rd);
return svc;
fail_oom:
fputs("out of memory\n", stderr);
-fail:
delsvc(svc);
close(fd);
return NULL;