diff options
| author | David Oberhollenzer <david.oberhollenzer@tele2.at> | 2018-10-10 11:28:46 +0200 | 
|---|---|---|
| committer | David Oberhollenzer <david.oberhollenzer@tele2.at> | 2018-10-10 16:45:11 +0200 | 
| commit | 24c90b7700e18d0668799f8f343bc854a42dea20 (patch) | |
| tree | 224d9b5e81a46e27f354c6975fc3fa4cd1f1fb79 /lib/libcfg | |
| parent | 7b647eefef00afb6104e84fae2a2fbf0481d4364 (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/libcfg')
| -rw-r--r-- | lib/libcfg/rdcfg.c | 32 | ||||
| -rw-r--r-- | lib/libcfg/rdline.c | 227 | ||||
| -rw-r--r-- | lib/libcfg/splitkv.c | 48 | 
3 files changed, 140 insertions, 167 deletions
| 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; -} | 
