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) {
|
string string_slice(string* s, size_t front, size_t back) {
|
||||||
assert(front < back);
|
assert(front < back);
|
||||||
assert(front < s->len);
|
assert(front < s->len);
|
||||||
assert(back < s->len);
|
assert(back <= s->len);
|
||||||
size_t len = back - front;
|
size_t len = back - front;
|
||||||
string res = new_string_sized(len);
|
string res = new_string_sized(len);
|
||||||
res.len = len;
|
res.len = len;
|
||||||
@@ -387,10 +387,15 @@ string config_value_str(config_value* c, size_t indent) {
|
|||||||
string_cappend(&res, "\n");
|
string_cappend(&res, "\n");
|
||||||
sub = config_str(c->section, indent+2);
|
sub = config_str(c->section, indent+2);
|
||||||
string_append(&res, &sub);
|
string_append(&res, &sub);
|
||||||
|
free_string(sub);
|
||||||
return res;
|
return res;
|
||||||
case config_value_string:
|
case config_value_string:
|
||||||
string_cappend(&res, "\"");
|
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, "\"");
|
string_cappend(&res, "\"");
|
||||||
break;
|
break;
|
||||||
case config_value_number:
|
case config_value_number:
|
||||||
@@ -467,6 +472,7 @@ string config_str(config* c, size_t indent) {
|
|||||||
string_cappend(&res, "\n");
|
string_cappend(&res, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(keys.data);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -509,9 +515,10 @@ typedef struct parsed_config {
|
|||||||
|
|
||||||
bool config_parse_line(string* s, size_t* line, size_t* col) {
|
bool config_parse_line(string* s, size_t* line, size_t* col) {
|
||||||
bool seen = false;
|
bool seen = false;
|
||||||
while (*s->str == '\n') {
|
while (s->len > 0 && *s->str == '\n') {
|
||||||
seen = true;
|
seen = true;
|
||||||
s->str++;
|
s->str++;
|
||||||
|
s->len--;
|
||||||
(*line)++;
|
(*line)++;
|
||||||
*col = 1;
|
*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 config_parse_indent(string* s, size_t* col) {
|
||||||
ssize_t count = 0;
|
ssize_t count = 0;
|
||||||
while (*s->str == ' ') {
|
while (s->len > 0 && *s->str == ' ') {
|
||||||
s->str++;
|
s->str++;
|
||||||
|
s->len--;
|
||||||
count++;
|
count++;
|
||||||
(*col)++;
|
(*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 config_parse_list(string* s, size_t* line, size_t* col, size_t indent, size_t consumed) {
|
||||||
parsed_value elem;
|
parsed_value elem;
|
||||||
|
parsed_value err;
|
||||||
size_t ind;
|
size_t ind;
|
||||||
list_config_value* l = malloc(sizeof(list_config_value));
|
list_config_value* l = malloc(sizeof(list_config_value));
|
||||||
*l =new_list_config_value();
|
*l =new_list_config_value();
|
||||||
|
|
||||||
while(true) {
|
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->len == 0) { err = config_value_parse_error("unexpected EOF in list", *line, *col); goto fail; }
|
||||||
if (s->str[0] == ' ') s->str++;
|
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;
|
consumed += 1;
|
||||||
elem = config_parse_value(s, line, col, indent);
|
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);
|
list_push_config_value(l, elem.v);
|
||||||
consumed += elem.consumed;
|
consumed += elem.consumed;
|
||||||
s->str += elem.consumed;
|
|
||||||
|
|
||||||
if (s->str[0] == '\0') break;
|
if (s->len == 0 || s->str[0] == '\0') break;
|
||||||
if (!config_parse_line(s, line, col)) return config_value_parse_error("expected newline", *line, *col);
|
if (!config_parse_line(s, line, col)) { err = config_value_parse_error("expected newline", *line, *col); goto fail; }
|
||||||
|
|
||||||
ind = config_parse_indent(s, col);
|
ind = config_parse_indent(s, col);
|
||||||
|
|
||||||
if(ind < indent) { s->str -= ind; col -= ind; break; }
|
if(ind < indent) { s->str -= ind + 1; s->len += ind + 1; *col -= ind; (*line)--; break; }
|
||||||
if (ind > indent) return config_value_parse_error("unexpected indent", *line, *col);
|
if (ind > indent) { err = config_value_parse_error("unexpected indent", *line, *col); goto fail; }
|
||||||
consumed += ind;
|
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->str++;
|
||||||
|
s->len--;
|
||||||
consumed += 1;
|
consumed += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (parsed_value){.ok=true, .consumed=consumed, .v=config_list(l)};
|
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) {
|
parsed_value config_parse_section(string* s, size_t* line, size_t* col, size_t indent, size_t consumed) {
|
||||||
config* res;
|
config* res;
|
||||||
*col -= consumed;
|
*col -= consumed;
|
||||||
s->str -= consumed;
|
s->str -= consumed;
|
||||||
|
s->len += consumed;
|
||||||
parsed_config c = config_parse_internal(s, line, col, indent);
|
parsed_config c = config_parse_internal(s, line, col, indent);
|
||||||
|
|
||||||
if (!c.ok) return (parsed_value){.ok=false, .consumed=c.consumed, .err=c.err};
|
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("expected greater indent", *line, *col);
|
||||||
if (ind > indent) return config_value_parse_error("unexpected 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);
|
return config_parse_section(s, line, col, indent, ind);
|
||||||
}
|
}
|
||||||
|
|
||||||
parsed_value config_parse_string(string* s, size_t* line, size_t* col) {
|
parsed_value config_parse_string(string* s, size_t* line, size_t* col) {
|
||||||
size_t i;
|
size_t i;
|
||||||
|
string val = new_string();
|
||||||
for (i = 1; i < s->len; i++) {
|
for (i = 1; i < s->len; i++) {
|
||||||
(*col)++;
|
(*col)++;
|
||||||
if (s->str[i] == '\\') {
|
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++;
|
i++;
|
||||||
|
char ch[2] = {s->str[i], '\0'};
|
||||||
|
string_cappend(&val, ch);
|
||||||
} else if (s->str[i] == '"') {
|
} 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') {
|
} else if (s->str[i] == '\0') {
|
||||||
break;
|
break;
|
||||||
} else if (s->str[i] == '\n') {
|
} else if (s->str[i] == '\n') {
|
||||||
(*line)++;
|
(*line)++;
|
||||||
*col = 0;
|
*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);
|
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;
|
int i;
|
||||||
ssize_t end = string_find_whitespace(s);
|
ssize_t end = string_find_whitespace(s);
|
||||||
bool dot = false;
|
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++) {
|
for (i = 0; i < to_parse.len; i++) {
|
||||||
(*col)++;
|
(*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') {
|
} else if (to_parse.str[i] == '\0') {
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
|
free_string(to_parse);
|
||||||
return config_value_parse_error("Expected number, got unparseable", *line, *col);
|
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){
|
return (parsed_value){
|
||||||
.ok=true,
|
.ok=true,
|
||||||
.consumed=to_parse.len,
|
.consumed=to_parse_len,
|
||||||
.v=config_number(strtod(string_cstr(&to_parse), NULL))
|
.v=config_number(result)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
parsed_value config_parse_value(string* s, size_t* line, size_t* col, size_t indent) {
|
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] == '"') return config_parse_string(s, line, col);
|
||||||
if (s->str[0] == '\n') return config_parse_list_or_section(s, line, col, indent+2);
|
if (s->str[0] == '\n') return config_parse_list_or_section(s, line, col, indent+2);
|
||||||
return config_parse_number(s, line, col);
|
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) {
|
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)++;
|
(*col)++;
|
||||||
s->str++;
|
s->str++;
|
||||||
|
s->len--;
|
||||||
(*consumed)++;
|
(*consumed)++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -678,34 +718,43 @@ parsed_config config_parse_internal(string* s, size_t* line, size_t* col, size_t
|
|||||||
size_t consumed = 0;
|
size_t consumed = 0;
|
||||||
map_string_config_value values = new_map_string_config_value(1024);
|
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);
|
ind = config_parse_indent(s, col);
|
||||||
|
|
||||||
if (ind > indent) return config_parse_error("unexpected indent", *line, *col);
|
if (ind > indent) { res = config_parse_error("unexpected indent", *line, *col); goto fail; }
|
||||||
if (ind < indent) break;
|
if (ind < indent) { s->str -= ind + 1; s->len += ind + 1; *col -= ind; (*line)--; break; }
|
||||||
|
|
||||||
consumed += ind;
|
consumed += ind;
|
||||||
|
|
||||||
whitespace = string_find_whitespace(s);
|
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);
|
label = string_slice(s, 0, whitespace);
|
||||||
s->str += whitespace;
|
s->str += whitespace;
|
||||||
|
s->len -= whitespace;
|
||||||
*col += whitespace;
|
*col += whitespace;
|
||||||
consumed += consumed;
|
consumed += whitespace;
|
||||||
config_parse_trim(s, line, col, &consumed);
|
config_parse_trim(s, line, col, &consumed);
|
||||||
|
|
||||||
val = config_parse_value(s, line, col, indent);
|
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;
|
consumed += val.consumed;
|
||||||
|
|
||||||
map_put_string_config_value(&values, label, val.v);
|
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++;
|
consumed++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -713,6 +762,10 @@ parsed_config config_parse_internal(string* s, size_t* line, size_t* col, size_t
|
|||||||
res.ok = true;
|
res.ok = true;
|
||||||
res.consumed = consumed;
|
res.consumed = consumed;
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
free_map_string_config_value(values);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
parsed_config config_parse(string* s) {
|
parsed_config config_parse(string* s) {
|
||||||
|
|||||||
Reference in New Issue
Block a user