make leak-safe (hopefully)
This commit is contained in:
109
cfg.h
109
cfg.h
@@ -89,7 +89,7 @@ void string_append(string* a, string* b) {
|
||||
string string_slice(string* s, size_t front, size_t back) {
|
||||
assert(front < back);
|
||||
assert(front < s->len);
|
||||
assert(back < s->len);
|
||||
assert(back <= s->len);
|
||||
size_t len = back - front;
|
||||
string res = new_string_sized(len);
|
||||
res.len = len;
|
||||
@@ -387,10 +387,15 @@ string config_value_str(config_value* c, size_t indent) {
|
||||
string_cappend(&res, "\n");
|
||||
sub = config_str(c->section, indent+2);
|
||||
string_append(&res, &sub);
|
||||
free_string(sub);
|
||||
return res;
|
||||
case config_value_string:
|
||||
string_cappend(&res, "\"");
|
||||
string_append(&res, &c->s);
|
||||
for (i = 0; i < c->s.len; i++) {
|
||||
if (c->s.str[i] == '"' || c->s.str[i] == '\\') string_cappend(&res, "\\");
|
||||
char ch[2] = {c->s.str[i], '\0'};
|
||||
string_cappend(&res, ch);
|
||||
}
|
||||
string_cappend(&res, "\"");
|
||||
break;
|
||||
case config_value_number:
|
||||
@@ -467,6 +472,7 @@ string config_str(config* c, size_t indent) {
|
||||
string_cappend(&res, "\n");
|
||||
}
|
||||
|
||||
free(keys.data);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -509,9 +515,10 @@ typedef struct parsed_config {
|
||||
|
||||
bool config_parse_line(string* s, size_t* line, size_t* col) {
|
||||
bool seen = false;
|
||||
while (*s->str == '\n') {
|
||||
while (s->len > 0 && *s->str == '\n') {
|
||||
seen = true;
|
||||
s->str++;
|
||||
s->len--;
|
||||
(*line)++;
|
||||
*col = 1;
|
||||
}
|
||||
@@ -520,8 +527,9 @@ bool config_parse_line(string* s, size_t* line, size_t* col) {
|
||||
|
||||
ssize_t config_parse_indent(string* s, size_t* col) {
|
||||
ssize_t count = 0;
|
||||
while (*s->str == ' ') {
|
||||
while (s->len > 0 && *s->str == ' ') {
|
||||
s->str++;
|
||||
s->len--;
|
||||
count++;
|
||||
(*col)++;
|
||||
}
|
||||
@@ -542,43 +550,51 @@ parsed_config config_parse_internal(string*, size_t*, size_t*, size_t);
|
||||
|
||||
parsed_value config_parse_list(string* s, size_t* line, size_t* col, size_t indent, size_t consumed) {
|
||||
parsed_value elem;
|
||||
parsed_value err;
|
||||
size_t ind;
|
||||
list_config_value* l = malloc(sizeof(list_config_value));
|
||||
*l =new_list_config_value();
|
||||
|
||||
while(true) {
|
||||
if (s->str[0] != ' ' && s->str[0] != '\n') return config_value_parse_error("expected space to start list element", *line, *col);
|
||||
if (s->str[0] == ' ') s->str++;
|
||||
if (s->len == 0) { err = config_value_parse_error("unexpected EOF in list", *line, *col); goto fail; }
|
||||
if (s->str[0] != ' ' && s->str[0] != '\n') { err = config_value_parse_error("expected space to start list element", *line, *col); goto fail; }
|
||||
if (s->str[0] == ' ') { s->str++; s->len--; }
|
||||
consumed += 1;
|
||||
elem = config_parse_value(s, line, col, indent);
|
||||
|
||||
if (!elem.ok) return elem;
|
||||
if (!elem.ok) { err = elem; goto fail; }
|
||||
|
||||
list_push_config_value(l, elem.v);
|
||||
consumed += elem.consumed;
|
||||
s->str += elem.consumed;
|
||||
|
||||
if (s->str[0] == '\0') break;
|
||||
if (!config_parse_line(s, line, col)) return config_value_parse_error("expected newline", *line, *col);
|
||||
if (s->len == 0 || s->str[0] == '\0') break;
|
||||
if (!config_parse_line(s, line, col)) { err = config_value_parse_error("expected newline", *line, *col); goto fail; }
|
||||
|
||||
ind = config_parse_indent(s, col);
|
||||
|
||||
if(ind < indent) { s->str -= ind; col -= ind; break; }
|
||||
if (ind > indent) return config_value_parse_error("unexpected indent", *line, *col);
|
||||
if(ind < indent) { s->str -= ind + 1; s->len += ind + 1; *col -= ind; (*line)--; break; }
|
||||
if (ind > indent) { err = config_value_parse_error("unexpected indent", *line, *col); goto fail; }
|
||||
consumed += ind;
|
||||
|
||||
if (s->str[0] != '-') return config_value_parse_error("expected hyphen to start list element", *line, *col);
|
||||
if (s->len == 0 || s->str[0] != '-') { err = config_value_parse_error("expected hyphen to start list element", *line, *col); goto fail; }
|
||||
s->str++;
|
||||
s->len--;
|
||||
consumed += 1;
|
||||
}
|
||||
|
||||
return (parsed_value){.ok=true, .consumed=consumed, .v=config_list(l)};
|
||||
|
||||
fail:
|
||||
free_list_config_value(*l);
|
||||
free(l);
|
||||
return err;
|
||||
}
|
||||
|
||||
parsed_value config_parse_section(string* s, size_t* line, size_t* col, size_t indent, size_t consumed) {
|
||||
config* res;
|
||||
*col -= consumed;
|
||||
s->str -= consumed;
|
||||
s->len += consumed;
|
||||
parsed_config c = config_parse_internal(s, line, col, indent);
|
||||
|
||||
if (!c.ok) return (parsed_value){.ok=false, .consumed=c.consumed, .err=c.err};
|
||||
@@ -597,26 +613,38 @@ parsed_value config_parse_list_or_section(string* s, size_t* line, size_t* col,
|
||||
if (ind < indent) return config_value_parse_error("expected greater indent", *line, *col);
|
||||
if (ind > indent) return config_value_parse_error("unexpected indent", *line, *col);
|
||||
|
||||
if (s->str[0] == '-') { s->str++; return config_parse_list(s, line, col, indent, ind+1); }
|
||||
if (s->len == 0) return config_value_parse_error("unexpected EOF", *line, *col);
|
||||
if (s->str[0] == '-') { s->str++; s->len--; return config_parse_list(s, line, col, indent, ind+1); }
|
||||
return config_parse_section(s, line, col, indent, ind);
|
||||
}
|
||||
|
||||
parsed_value config_parse_string(string* s, size_t* line, size_t* col) {
|
||||
size_t i;
|
||||
string val = new_string();
|
||||
for (i = 1; i < s->len; i++) {
|
||||
(*col)++;
|
||||
if (s->str[i] == '\\') {
|
||||
if (i +1 == s->len) return config_value_parse_error("Unexpected EOF, expected end of string", *line, *col);
|
||||
if (i +1 == s->len) { free_string(val); return config_value_parse_error("Unexpected EOF, expected end of string", *line, *col); }
|
||||
i++;
|
||||
char ch[2] = {s->str[i], '\0'};
|
||||
string_cappend(&val, ch);
|
||||
} else if (s->str[i] == '"') {
|
||||
return (parsed_value){.ok=true, .consumed=i+1, .v=config_string(string_slice(s, 1, i))};
|
||||
size_t consumed = i+1;
|
||||
s->str += consumed;
|
||||
s->len -= consumed;
|
||||
return (parsed_value){.ok=true, .consumed=consumed, .v=config_string(val)};
|
||||
} else if (s->str[i] == '\0') {
|
||||
break;
|
||||
} else if (s->str[i] == '\n') {
|
||||
(*line)++;
|
||||
*col = 0;
|
||||
string_cappend(&val, "\n");
|
||||
} else {
|
||||
char ch[2] = {s->str[i], '\0'};
|
||||
string_cappend(&val, ch);
|
||||
}
|
||||
}
|
||||
free_string(val);
|
||||
return config_value_parse_error("Unterminated string", *line, *col);
|
||||
}
|
||||
|
||||
@@ -624,7 +652,7 @@ parsed_value config_parse_number(string* s, size_t* line, size_t* col) {
|
||||
int i;
|
||||
ssize_t end = string_find_whitespace(s);
|
||||
bool dot = false;
|
||||
string to_parse = string_slice(s, 0, end == -1 ? s->len-1 : end);
|
||||
string to_parse = string_slice(s, 0, end == -1 ? s->len : end);
|
||||
|
||||
for (i = 0; i < to_parse.len; i++) {
|
||||
(*col)++;
|
||||
@@ -635,18 +663,29 @@ parsed_value config_parse_number(string* s, size_t* line, size_t* col) {
|
||||
} else if (to_parse.str[i] == '\0') {
|
||||
break;
|
||||
} else {
|
||||
free_string(to_parse);
|
||||
return config_value_parse_error("Expected number, got unparseable", *line, *col);
|
||||
}
|
||||
}
|
||||
|
||||
char* to_parse_cstr = string_cstr(&to_parse);
|
||||
double result = strtod(to_parse_cstr, NULL);
|
||||
free(to_parse_cstr);
|
||||
size_t to_parse_len = to_parse.len;
|
||||
free_string(to_parse);
|
||||
|
||||
s->str += to_parse_len;
|
||||
s->len -= to_parse_len;
|
||||
|
||||
return (parsed_value){
|
||||
.ok=true,
|
||||
.consumed=to_parse.len,
|
||||
.v=config_number(strtod(string_cstr(&to_parse), NULL))
|
||||
.consumed=to_parse_len,
|
||||
.v=config_number(result)
|
||||
};
|
||||
}
|
||||
|
||||
parsed_value config_parse_value(string* s, size_t* line, size_t* col, size_t indent) {
|
||||
if (s->len == 0) return config_value_parse_error("unexpected EOF, expected value", *line, *col);
|
||||
if (s->str[0] == '"') return config_parse_string(s, line, col);
|
||||
if (s->str[0] == '\n') return config_parse_list_or_section(s, line, col, indent+2);
|
||||
return config_parse_number(s, line, col);
|
||||
@@ -662,9 +701,10 @@ parsed_config config_parse_error(char* msg, size_t line, size_t col) {
|
||||
}
|
||||
|
||||
void config_parse_trim(string* s, size_t* line, size_t* col, size_t* consumed) {
|
||||
while (*s->str == ' ' || *s->str == '\t') {
|
||||
while (s->len > 0 && (*s->str == ' ' || *s->str == '\t')) {
|
||||
(*col)++;
|
||||
s->str++;
|
||||
s->len--;
|
||||
(*consumed)++;
|
||||
}
|
||||
}
|
||||
@@ -678,34 +718,43 @@ parsed_config config_parse_internal(string* s, size_t* line, size_t* col, size_t
|
||||
size_t consumed = 0;
|
||||
map_string_config_value values = new_map_string_config_value(1024);
|
||||
|
||||
while(s->str[0] != '\0') {
|
||||
while(s->len > 0 && s->str[0] != '\0') {
|
||||
if (s->str[0] == '\n') {
|
||||
config_parse_line(s, line, col);
|
||||
continue;
|
||||
}
|
||||
|
||||
ind = config_parse_indent(s, col);
|
||||
|
||||
if (ind > indent) return config_parse_error("unexpected indent", *line, *col);
|
||||
if (ind < indent) break;
|
||||
if (ind > indent) { res = config_parse_error("unexpected indent", *line, *col); goto fail; }
|
||||
if (ind < indent) { s->str -= ind + 1; s->len += ind + 1; *col -= ind; (*line)--; break; }
|
||||
|
||||
consumed += ind;
|
||||
|
||||
whitespace = string_find_whitespace(s);
|
||||
|
||||
if (whitespace == -1) return config_parse_error("expected whitespace, got EOF", *line, (*col)+s->len);
|
||||
if (whitespace == -1) { res = config_parse_error("expected whitespace, got EOF", *line, (*col)+s->len); goto fail; }
|
||||
if (whitespace == 0) {
|
||||
if (s->len == 0 || s->str[0] != '\n') { res = config_parse_error("unexpected whitespace", *line, *col); goto fail; }
|
||||
config_parse_line(s, line, col); continue;
|
||||
}
|
||||
|
||||
label = string_slice(s, 0, whitespace);
|
||||
s->str += whitespace;
|
||||
s->len -= whitespace;
|
||||
*col += whitespace;
|
||||
consumed += consumed;
|
||||
consumed += whitespace;
|
||||
config_parse_trim(s, line, col, &consumed);
|
||||
|
||||
val = config_parse_value(s, line, col, indent);
|
||||
|
||||
if (!val.ok) return (parsed_config){.ok=false, .err=val.err};
|
||||
if (!val.ok) { free_string(label); res = (parsed_config){.ok=false, .err=val.err}; goto fail; }
|
||||
|
||||
s->str += val.consumed;
|
||||
consumed += val.consumed;
|
||||
|
||||
map_put_string_config_value(&values, label, val.v);
|
||||
|
||||
if (!config_parse_line(s, line, col) && s->str[0] != '\0') return config_parse_error("expected newline", *line, *col);
|
||||
if (!config_parse_line(s, line, col) && s->len > 0 && s->str[0] != '\0') { res = config_parse_error("expected newline", *line, *col); goto fail; }
|
||||
consumed++;
|
||||
}
|
||||
|
||||
@@ -713,6 +762,10 @@ parsed_config config_parse_internal(string* s, size_t* line, size_t* col, size_t
|
||||
res.ok = true;
|
||||
res.consumed = consumed;
|
||||
return res;
|
||||
|
||||
fail:
|
||||
free_map_string_config_value(values);
|
||||
return res;
|
||||
}
|
||||
|
||||
parsed_config config_parse(string* s) {
|
||||
|
||||
Reference in New Issue
Block a user