diff options
-rw-r--r-- | lib/Makemodule.am | 2 | ||||
-rw-r--r-- | lib/include/util.h | 18 | ||||
-rw-r--r-- | lib/src/rdline.c | 60 | ||||
-rw-r--r-- | lib/src/rdsvc.c | 31 | ||||
-rw-r--r-- | lib/src/strexpand.c | 72 |
5 files changed, 79 insertions, 104 deletions
diff --git a/lib/Makemodule.am b/lib/Makemodule.am index 1d7da73..221a15b 100644 --- a/lib/Makemodule.am +++ b/lib/Makemodule.am @@ -2,7 +2,7 @@ HEADRS = lib/include/util.h lib/include/service.h lib/include/telinit.h libinit_a_SOURCES = lib/src/delsvc.c lib/src/rdline.c lib/src/svcmap.c libinit_a_SOURCES += lib/src/splitkv.c lib/src/enum_by_name.c -libinit_a_SOURCES += lib/src/strexpand.c lib/src/rdsvc.c lib/src/svcscan.c +libinit_a_SOURCES += lib/src/rdsvc.c lib/src/svcscan.c libinit_a_SOURCES += lib/src/del_svc_list.c lib/src/svc_tsort.c libinit_a_SOURCES += lib/src/opensock.c lib/src/enum_to_name.c $(HEADRS) libinit_a_CPPFLAGS = $(AM_CPPFLAGS) diff --git a/lib/include/util.h b/lib/include/util.h index 6943567..e99be3b 100644 --- a/lib/include/util.h +++ b/lib/include/util.h @@ -55,8 +55,17 @@ typedef struct { it with a backslash. - If a second, coresponding '"' is not found, processing fails with errno set to EILSEQ. + - If a '%' character is encountered, the next character is expected + to be a single digit index into argv. If it is not a digit or + outside the bounds set by argc, processing fails and sets errno + to EINVAL. 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 with errno set to ELOOP. */ -char *rdline(int fd); +char *rdline(int fd, int argc, const char *const *argv); /* Split a line of the shape "key = value" into key and value part. @@ -88,12 +97,5 @@ const enum_map_t *enum_by_name(const enum_map_t *map, const char *name); */ const char *enum_to_name(const enum_map_t *map, int value); -/* - Create a copy of the input string inp, but replace all occourances - of %<number> with argv[number] if the number is within the bounds - specified by argc. -*/ -char *strexpand(const char *inp, size_t argc, const char *const *argv); - #endif /* UTIL_H */ diff --git a/lib/src/rdline.c b/lib/src/rdline.c index 4e72a25..d6c4078 100644 --- a/lib/src/rdline.c +++ b/lib/src/rdline.c @@ -27,33 +27,45 @@ enum { STATE_STRING = 1, STATE_STRING_ESC = 2, STATE_COMMENT = 3, + STATE_ARG = 4, }; -char *rdline(int fd) +char *rdline(int fd, int argc, const char *const *argv) { size_t i = 0, bufsiz = 0, newsz; int ret, state = STATE_INITIAL; char c, *new, *buffer = NULL; + const char *argstr = NULL; for (;;) { - switch (read(fd, &c, 1)) { - case 0: - if (i == 0) { - errno = 0; - return NULL; - } - c = '\0'; - break; - case 1: - if (c == '\n') + if (argstr == NULL) { + switch (read(fd, &c, 1)) { + case 0: + if (i == 0) { + errno = 0; + return NULL; + } c = '\0'; - break; - default: - if (errno == EINTR) + break; + case 1: + break; + default: + if (errno == EINTR) + continue; + goto fail; + } + } else { + c = *(argstr++); + + if (c == '\0') { + argstr = NULL; continue; - goto fail; + } } + if (c == '\n') + c = '\0'; + switch (state) { case STATE_STRING: if (c == '\\') @@ -68,6 +80,20 @@ char *rdline(int fd) if (c != '\0') continue; break; + case STATE_ARG: + state = STATE_INITIAL; + if (c == '%') + break; + if (!isdigit(c) || (c - '0') >= argc) { + errno = EINVAL; + goto fail; + } + if (argstr != NULL) { + errno = ELOOP; + goto fail; + } + argstr = argv[c - '0']; + continue; default: if (isspace(c)) c = ' '; @@ -77,6 +103,10 @@ char *rdline(int fd) state = STATE_COMMENT; continue; } + if (c == '%') { + state = STATE_ARG; + continue; + } if (c == '"') state = STATE_STRING; break; diff --git a/lib/src/rdsvc.c b/lib/src/rdsvc.c index ee898c3..b186cd9 100644 --- a/lib/src/rdsvc.c +++ b/lib/src/rdsvc.c @@ -171,7 +171,7 @@ static const struct { service_t *rdsvc(int dirfd, const char *filename) { - const char *arg, *args[1]; + const char *arg, *args[1], *error; char *line, *key, *value; size_t i, argc, lineno; service_t *svc; @@ -213,15 +213,30 @@ service_t *rdsvc(int dirfd, const char *filename) for (lineno = 1; ; ++lineno) { errno = 0; - line = rdline(fd); + line = rdline(fd, argc, args); if (line == NULL) { - if (errno != 0) { - fprintf(stderr, "read: %s: %zu: %s\n", - filename, lineno, strerror(errno)); - goto fail; + if (errno == 0) + break; + + switch (errno) { + case EINVAL: + error = "error in argument expansion"; + break; + case ELOOP: + error = "recursive argument expansion"; + break; + case EILSEQ: + error = "missing \""; + break; + default: + error = strerror(errno); + break; } - break; + + fprintf(stderr, "%s: %zu: %s\n", + filename, lineno, error); + goto fail; } if (splitkv(line, &key, &value)) { @@ -258,7 +273,7 @@ service_t *rdsvc(int dirfd, const char *filename) goto fail_line; } - value = strexpand(value, argc, args); + value = strdup(value); if (value == NULL) { fputs("out of memory", stderr); goto fail_line; diff --git a/lib/src/strexpand.c b/lib/src/strexpand.c deleted file mode 100644 index 58b69a0..0000000 --- a/lib/src/strexpand.c +++ /dev/null @@ -1,72 +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 <string.h> -#include <stdlib.h> -#include <ctype.h> - -#include "util.h" - -char *strexpand(const char *inp, size_t argc, const char *const *argv) -{ - char *out, *dst; - const char *ptr; - size_t i, len; - - ptr = inp; - len = 0; - - while (*ptr != '\0') { - if (ptr[0] == '%' && isdigit(ptr[1])) { - i = ptr[1] - '0'; - if (i < argc) - len += strlen(argv[i]); - ptr += 2; - } else if (ptr[0] == '%' && ptr[1] == '%') { - ptr += 2; - len += 1; - } else { - ++ptr; - ++len; - } - } - - out = calloc(1, len + 1); - if (out == NULL) - return NULL; - - dst = out; - - while (*inp != '\0') { - if (inp[0] == '%' && isdigit(inp[1])) { - i = inp[1] - '0'; - if (i < argc) { - len = strlen(argv[i]); - memcpy(dst, argv[i], len); - dst += len; - } - inp += 2; - } else if (inp[0] == '%' && inp[1] == '%') { - *(dst++) = '%'; - inp += 2; - } else { - *(dst++) = *(inp++); - } - } - - return out; -} |