1
0
Fork 0
mirror of https://github.com/pygos/init.git synced 2024-12-22 15:20:49 +01:00

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>
This commit is contained in:
David Oberhollenzer 2018-10-10 11:28:46 +02:00
parent 7b647eefef
commit 24c90b7700
8 changed files with 176 additions and 218 deletions

View file

@ -41,7 +41,7 @@ static struct entry *parse_list(rdline_t *rd)
char *ptr;
while (rdline(rd) == 0) {
ptr = rd->buffer;
ptr = rd->line;
while (*ptr != '\0' && *ptr != ' ' && *ptr != '=')
++ptr;
@ -66,11 +66,11 @@ static struct entry *parse_list(rdline_t *rd)
continue;
}
e = calloc(1, sizeof(*e) + strlen(rd->buffer) + 1);
e = calloc(1, sizeof(*e) + strlen(rd->line) + 1);
if (e == NULL)
goto fail_oom;
strcpy(e->data, rd->buffer);
strcpy(e->data, rd->line);
e->next = list;
list = e;
}

View file

@ -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)

View file

@ -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;
}

View file

@ -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);
@ -110,15 +104,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.

View file

@ -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;
}

View file

@ -24,151 +24,144 @@
#include "libcfg.h"
static int rdline_getc(rdline_t *t)
{
int ret;
char c;
if (t->argstr != NULL) {
c = *(t->argstr++);
if (c != '\0')
goto out;
t->argstr = NULL;
}
do {
ret = read(t->fd, &c, 1);
} while (ret < 0 && errno == EINTR);
if (ret < 0)
return -1;
if (ret == 0) {
if (t->i == 0) {
errno = 0;
return -1;
}
c = '\0';
}
out:
return (c == '\n') ? '\0' : c;
}
static int rdline_append(rdline_t *t, int c)
{
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))
c = ' ';
if (c == ' ' && (t->i == 0 || t->buffer[t->i - 1] == ' '))
return 0;
if (c == '#') {
t->comment = true;
return 0;
}
if (c == '"')
t->string = true;
}
if (c == '\0') {
while (t->i > 0 && t->buffer[t->i - 1] == ' ')
t->i -= 1;
}
if (t->i == sizeof(t->buffer))
return -1;
t->buffer[t->i++] = c;
return 0;
}
void rdline_init(rdline_t *t, int fd, const char *filename,
int argc, const char *const *argv)
{
memset(t, 0, sizeof(*t));
t->fd = fd;
t->fp = fdopen(fd, "r");
t->filename = filename;
t->argc = argc;
t->argv = argv;
}
int rdline(rdline_t *t)
void rdline_cleanup(rdline_t *t)
{
const char *errstr;
int c;
retry:
t->i = 0;
t->argstr = NULL;
t->string = t->escape = t->comment = false;
free(t->line);
fclose(t->fp);
}
static int read_raw_line(rdline_t *t)
{
size_t len = 0;
free(t->line);
t->line = NULL;
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;
}
return 1;
}
t->lineno += 1;
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 (rdline_append(t, c)) {
errstr = "line too long";
goto fail;
}
} while (c != '\0');
if (t->buffer[0] == '\0')
goto retry;
return 0;
}
static int normalize_line(rdline_t *t)
{
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 (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++);
}
*(dst++) = c;
}
if (string) {
errstr = "missing \"";
goto fail;
}
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;
}
static void substitute(rdline_t *t, char *dst, char *src)
{
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)
{
char *buffer = NULL;
int ret;
do {
if ((ret = read_raw_line(t)))
goto out;
if ((ret = normalize_line(t)) < 0)
goto out;
} while (t->line[0] == '\0');
if (ret == 0)
return 0;
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;
}
substitute(t, buffer, t->line);
ret = 0;
out:
free(t->line);
t->line = buffer;
return ret;
}

View file

@ -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;
}

View file

@ -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);
close(fd);
if (rdcfg(svc, &rd, svc_params, ARRAY_SIZE(svc_params), flags)) {
delsvc(svc);
svc = NULL;
}
rdline_cleanup(&rd);
return svc;
fail_oom:
fputs("out of memory\n", stderr);
fail:
delsvc(svc);
close(fd);
return NULL;