summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/include/util.h12
-rw-r--r--lib/src/rdline.c47
2 files changed, 58 insertions, 1 deletions
diff --git a/lib/include/util.h b/lib/include/util.h
index 9ba9ffb..6943567 100644
--- a/lib/include/util.h
+++ b/lib/include/util.h
@@ -43,6 +43,18 @@ typedef struct {
cleared if end-of-file is reached.
The line must be deallocated with free().
+
+ The following transformations are applied:
+ - Space characters are replaced with regular white space characters.
+ - Sequences of space characters are truncated to a single space.
+ - A '#' sign is interpreted as the start of a comment and removed,
+ together with everything that follows.
+ - Padding spaces are removed from the line.
+ - If a '"' is encounterd, the above rules are disabled, until a
+ after the matching '"' is read. A '"' can be escaped by preceeding
+ it with a backslash.
+ - If a second, coresponding '"' is not found, processing fails with
+ errno set to EILSEQ.
*/
char *rdline(int fd);
diff --git a/lib/src/rdline.c b/lib/src/rdline.c
index 1b91008..4e72a25 100644
--- a/lib/src/rdline.c
+++ b/lib/src/rdline.c
@@ -22,11 +22,18 @@
#include "util.h"
+enum {
+ STATE_INITIAL = 0,
+ STATE_STRING = 1,
+ STATE_STRING_ESC = 2,
+ STATE_COMMENT = 3,
+};
+
char *rdline(int fd)
{
size_t i = 0, bufsiz = 0, newsz;
+ int ret, state = STATE_INITIAL;
char c, *new, *buffer = NULL;
- int ret;
for (;;) {
switch (read(fd, &c, 1)) {
@@ -47,6 +54,39 @@ char *rdline(int fd)
goto fail;
}
+ 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;
+ default:
+ if (isspace(c))
+ c = ' ';
+ if (c == ' ' && (i == 0 || buffer[i - 1] == ' '))
+ continue;
+ if (c == '#') {
+ state = STATE_COMMENT;
+ continue;
+ }
+ if (c == '"')
+ state = STATE_STRING;
+ break;
+ }
+
+ if (c == '\0') {
+ while (i > 0 && buffer[i - 1] == ' ')
+ --i;
+ }
+
if (i == bufsiz) {
newsz = bufsiz ? bufsiz * 2 : 16;
new = realloc(buffer, newsz);
@@ -62,6 +102,11 @@ char *rdline(int fd)
if (c == '\0')
break;
}
+
+ if (state == STATE_STRING || state == STATE_STRING_ESC) {
+ errno = EILSEQ;
+ goto fail;
+ }
return buffer;
fail:
ret = errno;