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>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
|
|
|
|
#include "util.h"
|
|
|
|
|
2018-04-02 21:26:45 +02:00
|
|
|
enum {
|
|
|
|
STATE_INITIAL = 0,
|
|
|
|
STATE_STRING = 1,
|
|
|
|
STATE_STRING_ESC = 2,
|
|
|
|
STATE_COMMENT = 3,
|
2018-04-04 11:55:59 +02:00
|
|
|
STATE_ARG = 4,
|
2018-04-02 21:26:45 +02:00
|
|
|
};
|
|
|
|
|
2018-04-04 11:55:59 +02:00
|
|
|
char *rdline(int fd, int argc, const char *const *argv)
|
2018-02-25 14:33:19 +01:00
|
|
|
{
|
|
|
|
size_t i = 0, bufsiz = 0, newsz;
|
2018-04-02 21:26:45 +02:00
|
|
|
int ret, state = STATE_INITIAL;
|
2018-02-25 14:33:19 +01:00
|
|
|
char c, *new, *buffer = NULL;
|
2018-04-04 11:55:59 +02:00
|
|
|
const char *argstr = NULL;
|
2018-02-25 14:33:19 +01:00
|
|
|
|
|
|
|
for (;;) {
|
2018-04-04 11:55:59 +02:00
|
|
|
if (argstr == NULL) {
|
|
|
|
switch (read(fd, &c, 1)) {
|
|
|
|
case 0:
|
|
|
|
if (i == 0) {
|
|
|
|
errno = 0;
|
|
|
|
return NULL;
|
|
|
|
}
|
2018-02-25 14:33:19 +01:00
|
|
|
c = '\0';
|
2018-04-04 11:55:59 +02:00
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (errno == EINTR)
|
|
|
|
continue;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
c = *(argstr++);
|
|
|
|
|
|
|
|
if (c == '\0') {
|
|
|
|
argstr = NULL;
|
2018-02-25 14:33:19 +01:00
|
|
|
continue;
|
2018-04-04 11:55:59 +02:00
|
|
|
}
|
2018-02-25 14:33:19 +01:00
|
|
|
}
|
|
|
|
|
2018-04-04 11:55:59 +02:00
|
|
|
if (c == '\n')
|
|
|
|
c = '\0';
|
|
|
|
|
2018-04-02 21:26:45 +02:00
|
|
|
switch (state) {
|
|
|
|
case STATE_STRING:
|
|
|
|
if (c == '\\')
|
|
|
|
state = STATE_STRING_ESC;
|
|
|
|
if (c == '"')
|
|
|
|
state = STATE_INITIAL;
|
|
|
|
break;
|
|
|
|
case STATE_STRING_ESC:
|
|
|
|
state = STATE_STRING;
|
|
|
|
break;
|
|
|
|
case STATE_COMMENT:
|
|
|
|
if (c != '\0')
|
|
|
|
continue;
|
|
|
|
break;
|
2018-04-04 11:55:59 +02:00
|
|
|
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;
|
2018-04-02 21:26:45 +02:00
|
|
|
default:
|
|
|
|
if (isspace(c))
|
|
|
|
c = ' ';
|
|
|
|
if (c == ' ' && (i == 0 || buffer[i - 1] == ' '))
|
|
|
|
continue;
|
|
|
|
if (c == '#') {
|
|
|
|
state = STATE_COMMENT;
|
|
|
|
continue;
|
|
|
|
}
|
2018-04-04 11:55:59 +02:00
|
|
|
if (c == '%') {
|
|
|
|
state = STATE_ARG;
|
|
|
|
continue;
|
|
|
|
}
|
2018-04-02 21:26:45 +02:00
|
|
|
if (c == '"')
|
|
|
|
state = STATE_STRING;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (c == '\0') {
|
|
|
|
while (i > 0 && buffer[i - 1] == ' ')
|
|
|
|
--i;
|
|
|
|
}
|
|
|
|
|
2018-02-25 14:33:19 +01:00
|
|
|
if (i == bufsiz) {
|
|
|
|
newsz = bufsiz ? bufsiz * 2 : 16;
|
|
|
|
new = realloc(buffer, newsz);
|
|
|
|
|
|
|
|
if (new == NULL)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
buffer = new;
|
|
|
|
bufsiz = newsz;
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer[i++] = c;
|
|
|
|
if (c == '\0')
|
|
|
|
break;
|
|
|
|
}
|
2018-04-02 21:26:45 +02:00
|
|
|
|
|
|
|
if (state == STATE_STRING || state == STATE_STRING_ESC) {
|
|
|
|
errno = EILSEQ;
|
|
|
|
goto fail;
|
|
|
|
}
|
2018-02-25 14:33:19 +01:00
|
|
|
return buffer;
|
|
|
|
fail:
|
|
|
|
ret = errno;
|
|
|
|
free(buffer);
|
|
|
|
errno = ret;
|
|
|
|
return NULL;
|
|
|
|
}
|