Commit c67972e3 authored by schneider's avatar schneider

refact(config): Make config parser more robust

Introduces a small state machine which parses the file character by
character. This eases the logic around parsing whole lines
significantly.

Now also trims lines, handling all non printable characters correctly.
parent a126e425
......@@ -125,25 +125,37 @@ static void add_config_pair(
slot->value_offset = value_offset;
}
static char *trim(char *str)
{
char *start = str;
while (*start && !isgraph((int)*start))
start++;
if (strlen(start) > 0) {
char *end = start + strlen(start) - 1;
while (*end && !isgraph((int)*end))
end--;
end[1] = 0;
}
return start;
}
// parses one line of the config file
static void
parse_line(char *line, char *eol, int line_number, size_t line_offset)
static void parse_line(char *line, int line_number, size_t line_offset)
{
char *line_start = line;
//skip leading whitespace
while (*line && isspace((int)*line))
++line;
line = trim(line);
char *key = line;
if (*key == '#') {
//printf(line);
if (*line == '#') {
//skip comments
return;
}
char *eq = strchr(line, '=');
if (!eq) {
if (*key) {
if (*line) {
LOG_WARN(
"card10.cfg",
"line %d: syntax error",
......@@ -152,26 +164,15 @@ parse_line(char *line, char *eol, int line_number, size_t line_offset)
}
return;
}
*eq = 0;
char *e_key = eq - 1;
//skip trailing whitespace in key
while (e_key > key && isspace((int)*e_key))
--e_key;
e_key[1] = '\0';
char *key = trim(line);
if (*key == '\0') {
LOG_WARN("card10.cfg", "line %d: empty key", line_number);
return;
}
char *value = eq + 1;
//skip leading whitespace
while (*value && isspace((int)*value))
++value;
char *e_val = eol - 1;
//skip trailing whitespace
while (e_val > value && isspace((int)*e_val))
--e_val;
char *value = trim(eq + 1);
if (*value == '\0') {
LOG_WARN(
"card10.cfg",
......@@ -187,16 +188,39 @@ parse_line(char *line, char *eol, int line_number, size_t line_offset)
add_config_pair(key, value, line_number, value_offset);
}
// convert windows line endings to unix line endings.
// we don't care about the extra empty lines
static void convert_crlf_to_lflf(char *buf, int n)
typedef struct {
int line_number;
int file_offset;
int line_start;
char line[MAX_LINE_LENGTH + 1];
int line_length;
} parser_state;
int parse_character(char c, parser_state *s)
{
while (n--) {
if (*buf == '\r') {
*buf = '\n';
if (c != '\r' && c != '\n') {
if (s->line_length == MAX_LINE_LENGTH) {
LOG_WARN(
"card10.cfg",
"line:%d: too long - aborting",
s->line_number
);
return -1;
}
s->line[s->line_length++] = c;
} else {
s->line[s->line_length] = 0;
//printf("New line: %s (%d %d)\n", s->line, s->line_number, s->line_start);
parse_line(s->line, s->line_number, s->line_start);
s->line_length = 0;
s->line_start = s->file_offset + 1;
if (c == '\n') {
s->line_number++;
}
buf++;
}
s->file_offset++;
return 0;
}
// parses the entire config file
......@@ -213,77 +237,21 @@ void load_config(void)
);
return;
}
char buf[MAX_LINE_LENGTH + 1];
int line_number = 0;
size_t file_offset = 0;
char buf[128];
int nread;
parser_state s;
memset(&s, 0, sizeof(s));
s.line_number = 1;
do {
nread = epic_file_read(fd, buf, MAX_LINE_LENGTH);
convert_crlf_to_lflf(buf, nread);
if (nread < MAX_LINE_LENGTH) {
//add fake EOL to ensure termination
buf[nread++] = '\n';
nread = epic_file_read(fd, buf, sizeof(buf));
int i;
for (i = 0; i < nread; i++) {
parse_character(buf[i], &s);
}
//zero-terminate buffer
buf[nread] = '\0';
char *line = buf;
char *eol = NULL;
int last_eol = 0;
while (line) {
//line points one character past the last (if any) '\n' hence '- 1'
last_eol = line - buf - 1;
eol = strchr(line, '\n');
++line_number;
if (eol) {
*eol = '\0';
parse_line(line, eol, line_number, file_offset);
file_offset += eol - line + 1;
line = eol + 1;
continue;
}
if (line == buf) {
//line did not fit into buf
LOG_WARN(
"card10.cfg",
"line:%d: too long - aborting",
line_number
);
return;
}
int seek_back = last_eol - nread;
} while (nread == sizeof(buf));
parse_character('\n', &s);
LOG_DEBUG(
"card10.cfg",
"nread, last_eol, seek_back: %d,%d,%d",
nread,
last_eol,
seek_back
);
assert(seek_back <= 0);
if (!seek_back) {
break;
}
int rc = epic_file_seek(fd, seek_back, SEEK_CUR);
if (rc < 0) {
LOG_ERR("card10.cfg", "seek failed, aborting");
return;
}
char newline;
rc = epic_file_read(fd, &newline, 1);
if (rc < 0 || (newline != '\n' && newline != '\r')) {
LOG_ERR("card10.cfg", "read failed, aborting");
LOG_DEBUG(
"card10.cfg",
"read failed at read-back of newline: rc: %d read: %d",
rc,
(int)newline
);
return;
}
break;
}
} while (nread == MAX_LINE_LENGTH);
epic_file_close(fd);
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment