summaryrefslogtreecommitdiff
path: root/lib/libcfg
diff options
context:
space:
mode:
authorDavid Oberhollenzer <david.oberhollenzer@tele2.at>2018-05-23 22:26:17 +0200
committerDavid Oberhollenzer <david.oberhollenzer@tele2.at>2018-06-09 15:32:29 +0200
commitbf63f78b6698f33ed3acbd4aed8822a35688c1ba (patch)
tree4e7c79326dd6fd217f135234dbcf3d9bc09ebb82 /lib/libcfg
parent1eb8a4c792166360137cb5c83afa75eace2b4b1d (diff)
Split configuration parser and utility library
Signed-off-by: David Oberhollenzer <david.oberhollenzer@tele2.at>
Diffstat (limited to 'lib/libcfg')
-rw-r--r--lib/libcfg/pack_argv.c82
-rw-r--r--lib/libcfg/rdline.c174
-rw-r--r--lib/libcfg/unescape.c94
3 files changed, 350 insertions, 0 deletions
diff --git a/lib/libcfg/pack_argv.c b/lib/libcfg/pack_argv.c
new file mode 100644
index 0000000..4d90171
--- /dev/null
+++ b/lib/libcfg/pack_argv.c
@@ -0,0 +1,82 @@
+/* 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 <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include "libcfg.h"
+
+int pack_argv(char *str)
+{
+ char *dst, *start;
+ int count = 0;
+
+ dst = str;
+
+ for (;;) {
+ while (*str == ' ')
+ ++str;
+
+ if (*str == '\0')
+ break;
+
+ if (*str == '"') {
+ start = dst;
+ *(dst++) = *(str++);
+
+ while (*str != '"') {
+ if (*str == '\0')
+ goto fail_str;
+ if (str[0] == '\\' && str[1] != '\0')
+ *(dst++) = *(str++);
+ *(dst++) = *(str++);
+ }
+
+ *(dst++) = *(str++);
+
+ if (*str != ' ' && *str != '\0')
+ goto fail_str;
+ if (*str == ' ')
+ ++str;
+
+ *(dst++) = '\0';
+
+ if (unescape(start))
+ goto fail_str;
+
+ dst = start + strlen(start) + 1;
+ } else {
+ while (*str != '\0' && *str != ' ')
+ *(dst++) = *(str++);
+ if (*str == ' ') {
+ ++str;
+ *(dst++) = '\0';
+ }
+ }
+
+ ++count;
+ }
+
+ *dst = '\0';
+ return count;
+fail_str:
+ errno = EINVAL;
+ return -1;
+}
diff --git a/lib/libcfg/rdline.c b/lib/libcfg/rdline.c
new file mode 100644
index 0000000..ffbfd89
--- /dev/null
+++ b/lib/libcfg/rdline.c
@@ -0,0 +1,174 @@
+/* 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 <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdio.h>
+
+#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->filename = filename;
+ t->argc = argc;
+ t->argv = argv;
+}
+
+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;
+
+ 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;
+fail:
+ fprintf(stderr, "%s: %zu: %s\n", t->filename, t->lineno, errstr);
+ return -1;
+}
diff --git a/lib/libcfg/unescape.c b/lib/libcfg/unescape.c
new file mode 100644
index 0000000..e2b450c
--- /dev/null
+++ b/lib/libcfg/unescape.c
@@ -0,0 +1,94 @@
+/* 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 <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "libcfg.h"
+
+static int xdigit(int x)
+{
+ if (isupper(x))
+ return x - 'A' + 0x0A;
+ if (islower(x))
+ return x - 'a' + 0x0A;
+ return x - '0';
+}
+
+int unescape(char *src)
+{
+ char *dst = src;
+ int c;
+
+ for (;;) {
+ while (*src != '"' && *src != '\0')
+ *(dst++) = *(src++);
+
+ if (*src == '\0')
+ break;
+
+ ++src;
+
+ while ((c = *(src++)) != '"') {
+ if (c == '\0')
+ return -1;
+
+ if (c == '\\') {
+ c = *(src++);
+
+ switch (c) {
+ case 'a': c = '\a'; break;
+ case 'b': c = '\b'; break;
+ case 'f': c = '\f'; break;
+ case 'n': c = '\n'; break;
+ case 't': c = '\t'; break;
+ case '\\':
+ case '"':
+ case '%':
+ break;
+ case 'x':
+ c = 0;
+ if (isxdigit(*src))
+ c = (c<<4) | xdigit(*(src++));
+ if (isxdigit(*src))
+ c = (c<<4) | xdigit(*(src++));
+ break;
+ case '0':
+ c = 0;
+ if (isdigit(*src) && *src < '8')
+ c = (c<<3) | (*(src++) - '0');
+ if (isdigit(*src) && *src < '8')
+ c = (c<<3) | (*(src++) - '0');
+ if (isdigit(*src) && *src < '8')
+ c = (c<<3) | (*(src++) - '0');
+ break;
+ default:
+ return -1;
+ }
+
+ if (c == 0)
+ return -1;
+ }
+
+ *(dst++) = c;
+ }
+ }
+
+ *(dst++) = '\0';
+ return 0;
+}