Skip to content
Snippets Groups Projects
Commit c67972e3 authored by schneider's avatar schneider
Browse files

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
No related branches found
No related tags found
1 merge request!375Default main app selector
...@@ -125,25 +125,37 @@ static void add_config_pair( ...@@ -125,25 +125,37 @@ static void add_config_pair(
slot->value_offset = value_offset; 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 // parses one line of the config file
static void static void parse_line(char *line, int line_number, size_t line_offset)
parse_line(char *line, char *eol, int line_number, size_t line_offset)
{ {
char *line_start = line; char *line_start = line;
//skip leading whitespace line = trim(line);
while (*line && isspace((int)*line))
++line;
char *key = line; //printf(line);
if (*key == '#') { if (*line == '#') {
//skip comments //skip comments
return; return;
} }
char *eq = strchr(line, '='); char *eq = strchr(line, '=');
if (!eq) { if (!eq) {
if (*key) { if (*line) {
LOG_WARN( LOG_WARN(
"card10.cfg", "card10.cfg",
"line %d: syntax error", "line %d: syntax error",
...@@ -152,26 +164,15 @@ parse_line(char *line, char *eol, int line_number, size_t line_offset) ...@@ -152,26 +164,15 @@ parse_line(char *line, char *eol, int line_number, size_t line_offset)
} }
return; return;
} }
*eq = 0;
char *e_key = eq - 1; char *key = trim(line);
//skip trailing whitespace in key
while (e_key > key && isspace((int)*e_key))
--e_key;
e_key[1] = '\0';
if (*key == '\0') { if (*key == '\0') {
LOG_WARN("card10.cfg", "line %d: empty key", line_number); LOG_WARN("card10.cfg", "line %d: empty key", line_number);
return; return;
} }
char *value = eq + 1; char *value = trim(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;
if (*value == '\0') { if (*value == '\0') {
LOG_WARN( LOG_WARN(
"card10.cfg", "card10.cfg",
...@@ -187,18 +188,41 @@ parse_line(char *line, char *eol, int line_number, size_t line_offset) ...@@ -187,18 +188,41 @@ parse_line(char *line, char *eol, int line_number, size_t line_offset)
add_config_pair(key, value, line_number, value_offset); add_config_pair(key, value, line_number, value_offset);
} }
// convert windows line endings to unix line endings. typedef struct {
// we don't care about the extra empty lines int line_number;
static void convert_crlf_to_lflf(char *buf, int n) 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 (c != '\r' && c != '\n') {
if (*buf == '\r') { if (s->line_length == MAX_LINE_LENGTH) {
*buf = '\n'; LOG_WARN(
"card10.cfg",
"line:%d: too long - aborting",
s->line_number
);
return -1;
} }
buf++; 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++;
} }
} }
s->file_offset++;
return 0;
}
// parses the entire config file // parses the entire config file
void load_config(void) void load_config(void)
{ {
...@@ -213,77 +237,21 @@ void load_config(void) ...@@ -213,77 +237,21 @@ void load_config(void)
); );
return; return;
} }
char buf[MAX_LINE_LENGTH + 1];
int line_number = 0; char buf[128];
size_t file_offset = 0;
int nread; int nread;
parser_state s;
memset(&s, 0, sizeof(s));
s.line_number = 1;
do { do {
nread = epic_file_read(fd, buf, MAX_LINE_LENGTH); nread = epic_file_read(fd, buf, sizeof(buf));
convert_crlf_to_lflf(buf, nread); int i;
if (nread < MAX_LINE_LENGTH) { for (i = 0; i < nread; i++) {
//add fake EOL to ensure termination parse_character(buf[i], &s);
buf[nread++] = '\n';
}
//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;
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;
} }
} while (nread == sizeof(buf));
parse_character('\n', &s);
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); epic_file_close(fd);
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment