2018-03-24 22:31:05 +01:00
|
|
|
/* 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/>.
|
|
|
|
*/
|
2018-02-25 14:33:19 +01:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
2018-04-04 23:37:20 +02:00
|
|
|
#include <string.h>
|
2018-02-25 14:33:19 +01:00
|
|
|
#include <errno.h>
|
|
|
|
#include <ctype.h>
|
2018-04-11 00:04:33 +02:00
|
|
|
#include <stdio.h>
|
2018-02-25 14:33:19 +01:00
|
|
|
|
2018-05-23 22:26:17 +02:00
|
|
|
#include "libcfg.h"
|
2018-02-25 14:33:19 +01:00
|
|
|
|
2018-04-04 23:37:20 +02:00
|
|
|
static int rdline_getc(rdline_t *t)
|
2018-02-25 14:33:19 +01:00
|
|
|
{
|
2018-04-04 23:37:20 +02:00
|
|
|
int ret;
|
|
|
|
char c;
|
2018-04-04 11:55:59 +02:00
|
|
|
|
2018-04-04 23:37:20 +02:00
|
|
|
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;
|
2018-02-25 14:33:19 +01:00
|
|
|
}
|
2018-04-04 23:37:20 +02:00
|
|
|
c = '\0';
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
return (c == '\n') ? '\0' : c;
|
|
|
|
}
|
2018-02-25 14:33:19 +01:00
|
|
|
|
2018-04-04 23:37:20 +02:00
|
|
|
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 {
|
2018-04-02 21:26:45 +02:00
|
|
|
if (c == '\\')
|
2018-04-04 23:37:20 +02:00
|
|
|
t->escape = true;
|
2018-04-02 21:26:45 +02:00
|
|
|
if (c == '"')
|
2018-04-04 23:37:20 +02:00
|
|
|
t->string = false;
|
2018-04-02 21:26:45 +02:00
|
|
|
}
|
2018-04-04 23:37:20 +02:00
|
|
|
} else {
|
|
|
|
if (isspace(c))
|
|
|
|
c = ' ';
|
|
|
|
if (c == ' ' && (t->i == 0 || t->buffer[t->i - 1] == ' '))
|
|
|
|
return 0;
|
|
|
|
if (c == '#') {
|
|
|
|
t->comment = true;
|
|
|
|
return 0;
|
2018-04-02 21:26:45 +02:00
|
|
|
}
|
2018-04-04 23:37:20 +02:00
|
|
|
if (c == '"')
|
|
|
|
t->string = true;
|
|
|
|
}
|
2018-04-02 21:26:45 +02:00
|
|
|
|
2018-04-04 23:37:20 +02:00
|
|
|
if (c == '\0') {
|
|
|
|
while (t->i > 0 && t->buffer[t->i - 1] == ' ')
|
|
|
|
t->i -= 1;
|
|
|
|
}
|
|
|
|
|
2018-04-11 00:04:33 +02:00
|
|
|
if (t->i == sizeof(t->buffer))
|
|
|
|
return -1;
|
2018-04-04 23:37:20 +02:00
|
|
|
|
|
|
|
t->buffer[t->i++] = c;
|
|
|
|
return 0;
|
|
|
|
}
|
2018-02-25 14:33:19 +01:00
|
|
|
|
2018-04-11 00:04:33 +02:00
|
|
|
void rdline_init(rdline_t *t, int fd, const char *filename,
|
|
|
|
int argc, const char *const *argv)
|
2018-04-04 23:37:20 +02:00
|
|
|
{
|
2018-04-11 00:04:33 +02:00
|
|
|
memset(t, 0, sizeof(*t));
|
|
|
|
t->fd = fd;
|
|
|
|
t->filename = filename;
|
|
|
|
t->argc = argc;
|
|
|
|
t->argv = argv;
|
|
|
|
}
|
2018-04-04 23:37:20 +02:00
|
|
|
|
2018-04-11 00:04:33 +02:00
|
|
|
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;
|
2018-04-04 23:37:20 +02:00
|
|
|
|
|
|
|
do {
|
2018-04-11 00:04:33 +02:00
|
|
|
errno = 0;
|
|
|
|
c = rdline_getc(t);
|
|
|
|
if (c < 0) {
|
|
|
|
if (errno == 0)
|
|
|
|
return 1;
|
|
|
|
errstr = strerror(errno);
|
2018-04-04 23:37:20 +02:00
|
|
|
goto fail;
|
2018-04-11 00:04:33 +02:00
|
|
|
}
|
|
|
|
if (c == 0 && t->string) {
|
|
|
|
errstr = "missing \"";
|
2018-04-04 23:37:20 +02:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (c == '%') {
|
2018-04-11 00:04:33 +02:00
|
|
|
c = rdline_getc(t);
|
|
|
|
if (c == 0) {
|
|
|
|
errstr = "unexpected end of line after '%%'";
|
2018-02-25 14:33:19 +01:00
|
|
|
goto fail;
|
2018-04-11 00:04:33 +02:00
|
|
|
}
|
|
|
|
if (c < 0) {
|
|
|
|
errstr = strerror(errno);
|
|
|
|
goto fail;
|
|
|
|
}
|
2018-02-25 14:33:19 +01:00
|
|
|
|
2018-04-04 23:37:20 +02:00
|
|
|
if (c != '%') {
|
2018-04-11 00:04:33 +02:00
|
|
|
if (!isdigit(c)) {
|
|
|
|
errstr = "exptected digit after '%%'";
|
2018-04-04 23:37:20 +02:00
|
|
|
goto fail;
|
|
|
|
}
|
2018-04-11 00:04:33 +02:00
|
|
|
if ((c - '0') >= t->argc) {
|
|
|
|
errstr = "argument out of range";
|
2018-04-04 23:37:20 +02:00
|
|
|
goto fail;
|
|
|
|
}
|
2018-04-11 00:04:33 +02:00
|
|
|
if (t->argstr != NULL) {
|
|
|
|
errstr = "recursive argument "
|
|
|
|
"expansion";
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
t->argstr = t->argv[c - '0'];
|
2018-04-04 23:37:20 +02:00
|
|
|
continue;
|
|
|
|
}
|
2018-02-25 14:33:19 +01:00
|
|
|
}
|
|
|
|
|
2018-04-11 00:04:33 +02:00
|
|
|
if (rdline_append(t, c)) {
|
|
|
|
errstr = "line too long";
|
2018-04-04 23:37:20 +02:00
|
|
|
goto fail;
|
2018-04-11 00:04:33 +02:00
|
|
|
}
|
2018-04-04 23:37:20 +02:00
|
|
|
} while (c != '\0');
|
2018-04-02 21:26:45 +02:00
|
|
|
|
2018-04-11 00:04:33 +02:00
|
|
|
if (t->buffer[0] == '\0')
|
|
|
|
goto retry;
|
|
|
|
|
|
|
|
return 0;
|
2018-02-25 14:33:19 +01:00
|
|
|
fail:
|
2018-04-11 00:04:33 +02:00
|
|
|
fprintf(stderr, "%s: %zu: %s\n", t->filename, t->lineno, errstr);
|
|
|
|
return -1;
|
2018-02-25 14:33:19 +01:00
|
|
|
}
|