1
0
Fork 0
mirror of https://github.com/pygos/init.git synced 2024-11-22 19:19:47 +01:00

Split rdline into multiple, easier to read functions

Signed-off-by: David Oberhollenzer <david.oberhollenzer@tele2.at>
This commit is contained in:
David Oberhollenzer 2018-04-04 23:37:20 +02:00
parent 65d2abc3f0
commit 00daf470d1

View file

@ -17,130 +17,153 @@
*/ */
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <string.h>
#include <errno.h> #include <errno.h>
#include <ctype.h> #include <ctype.h>
#include "util.h" #include "util.h"
enum { typedef struct {
STATE_INITIAL = 0, int fd; /* input file descriptor */
STATE_STRING = 1, const char *argstr; /* if not NULL, read from this instead */
STATE_STRING_ESC = 2,
STATE_COMMENT = 3, size_t i; /* buffer offset */
STATE_ARG = 4, size_t bufsiz; /* buffer size */
}; char *buffer;
bool string; /* inside a string? */
bool escape; /* reading an escape sequence? */
bool comment; /* inside a comment */
} rdline_t;
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)
{
size_t newsz;
char *new;
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 == t->bufsiz) {
newsz = t->bufsiz ? t->bufsiz * 2 : 16;
new = realloc(t->buffer, newsz);
if (new == NULL)
return -1;
t->buffer = new;
t->bufsiz = newsz;
}
t->buffer[t->i++] = c;
return 0;
}
char *rdline(int fd, int argc, const char *const *argv) char *rdline(int fd, int argc, const char *const *argv)
{ {
size_t i = 0, bufsiz = 0, newsz; rdline_t rd;
int ret, state = STATE_INITIAL; int ret;
char c, *new, *buffer = NULL; char c;
const char *argstr = NULL;
for (;;) { memset(&rd, 0, sizeof(rd));
if (argstr == NULL) { rd.fd = fd;
switch (read(fd, &c, 1)) {
case 0: do {
if (i == 0) { c = rdline_getc(&rd);
errno = 0; if (c < 0)
return NULL; goto fail;
} if (c == 0 && rd.string) {
c = '\0'; errno = EILSEQ;
break;
case 1:
break;
default:
if (errno == EINTR)
continue;
goto fail; goto fail;
} }
} else {
c = *(argstr++);
if (c == '\0') { if (c == '%') {
argstr = NULL; c = rdline_getc(&rd);
continue; if (c == 0)
} errno = EILSEQ;
} if (c <= 0)
goto fail;
if (c == '\n') if (c != '%') {
c = '\0';
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;
case STATE_ARG:
state = STATE_INITIAL;
if (c == '%')
break;
if (!isdigit(c) || (c - '0') >= argc) { if (!isdigit(c) || (c - '0') >= argc) {
errno = EINVAL; errno = EINVAL;
goto fail; goto fail;
} }
if (argstr != NULL) { if (rd.argstr != NULL) {
errno = ELOOP; errno = ELOOP;
goto fail; goto fail;
} }
argstr = argv[c - '0']; rd.argstr = argv[c - '0'];
continue;
default:
if (isspace(c))
c = ' ';
if (c == ' ' && (i == 0 || buffer[i - 1] == ' '))
continue;
if (c == '#') {
state = STATE_COMMENT;
continue; continue;
} }
if (c == '%') {
state = STATE_ARG;
continue;
}
if (c == '"')
state = STATE_STRING;
break;
} }
if (c == '\0') { if (rdline_append(&rd, c))
while (i > 0 && buffer[i - 1] == ' ')
--i;
}
if (i == bufsiz) {
newsz = bufsiz ? bufsiz * 2 : 16;
new = realloc(buffer, newsz);
if (new == NULL)
goto fail; goto fail;
} while (c != '\0');
buffer = new; return rd.buffer;
bufsiz = newsz;
}
buffer[i++] = c;
if (c == '\0')
break;
}
if (state == STATE_STRING || state == STATE_STRING_ESC) {
errno = EILSEQ;
goto fail;
}
return buffer;
fail: fail:
ret = errno; ret = errno;
free(buffer); free(rd.buffer);
errno = ret; errno = ret;
return NULL; return NULL;
} }