aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/src/rdline.c223
1 files changed, 123 insertions, 100 deletions
diff --git a/lib/src/rdline.c b/lib/src/rdline.c
index d6c4078..b108ef2 100644
--- a/lib/src/rdline.c
+++ b/lib/src/rdline.c
@@ -17,130 +17,153 @@
*/
#include <stdlib.h>
#include <unistd.h>
+#include <string.h>
#include <errno.h>
#include <ctype.h>
#include "util.h"
-enum {
- STATE_INITIAL = 0,
- STATE_STRING = 1,
- STATE_STRING_ESC = 2,
- STATE_COMMENT = 3,
- STATE_ARG = 4,
-};
+typedef struct {
+ int fd; /* input file descriptor */
+ const char *argstr; /* if not NULL, read from this instead */
-char *rdline(int fd, int argc, const char *const *argv)
+ size_t i; /* buffer offset */
+ 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)
{
- size_t i = 0, bufsiz = 0, newsz;
- int ret, state = STATE_INITIAL;
- char c, *new, *buffer = NULL;
- const char *argstr = NULL;
-
- for (;;) {
- if (argstr == NULL) {
- switch (read(fd, &c, 1)) {
- case 0:
- if (i == 0) {
- errno = 0;
- return NULL;
- }
- c = '\0';
- break;
- case 1:
- break;
- default:
- if (errno == EINTR)
- continue;
- goto fail;
- }
- } else {
- c = *(argstr++);
+ int ret;
+ char c;
- if (c == '\0') {
- argstr = NULL;
- continue;
- }
+ 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;
+}
- if (c == '\n')
- c = '\0';
+static int rdline_append(rdline_t *t, int c)
+{
+ size_t newsz;
+ char *new;
- switch (state) {
- case STATE_STRING:
+ if (t->comment) {
+ if (c != '\0')
+ return 0;
+ } else if (t->string) {
+ if (t->escape) {
+ t->escape = false;
+ } else {
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) {
- errno = EINVAL;
- goto fail;
- }
- if (argstr != NULL) {
- errno = ELOOP;
- goto fail;
- }
- argstr = argv[c - '0'];
- continue;
- default:
- if (isspace(c))
- c = ' ';
- if (c == ' ' && (i == 0 || buffer[i - 1] == ' '))
- continue;
- if (c == '#') {
- state = STATE_COMMENT;
- continue;
- }
- if (c == '%') {
- state = STATE_ARG;
- continue;
- }
+ t->escape = true;
if (c == '"')
- state = STATE_STRING;
- break;
+ t->string = false;
}
-
- if (c == '\0') {
- while (i > 0 && buffer[i - 1] == ' ')
- --i;
+ } 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 (i == bufsiz) {
- newsz = bufsiz ? bufsiz * 2 : 16;
- new = realloc(buffer, newsz);
+ 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;
+}
- if (new == NULL)
+char *rdline(int fd, int argc, const char *const *argv)
+{
+ rdline_t rd;
+ int ret;
+ char c;
+
+ memset(&rd, 0, sizeof(rd));
+ rd.fd = fd;
+
+ do {
+ c = rdline_getc(&rd);
+ if (c < 0)
+ goto fail;
+ if (c == 0 && rd.string) {
+ errno = EILSEQ;
+ goto fail;
+ }
+
+ if (c == '%') {
+ c = rdline_getc(&rd);
+ if (c == 0)
+ errno = EILSEQ;
+ if (c <= 0)
goto fail;
- buffer = new;
- bufsiz = newsz;
+ if (c != '%') {
+ if (!isdigit(c) || (c - '0') >= argc) {
+ errno = EINVAL;
+ goto fail;
+ }
+ if (rd.argstr != NULL) {
+ errno = ELOOP;
+ goto fail;
+ }
+ rd.argstr = argv[c - '0'];
+ continue;
+ }
}
- buffer[i++] = c;
- if (c == '\0')
- break;
- }
+ if (rdline_append(&rd, c))
+ goto fail;
+ } while (c != '\0');
- if (state == STATE_STRING || state == STATE_STRING_ESC) {
- errno = EILSEQ;
- goto fail;
- }
- return buffer;
+ return rd.buffer;
fail:
ret = errno;
- free(buffer);
+ free(rd.buffer);
errno = ret;
return NULL;
}