| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include <errno.h> | ||
| 2 | #include <stdbool.h> | ||
| 3 | #include <stdlib.h> | ||
| 4 | #include <string.h> | ||
| 5 | #include "state.h" | ||
| 6 | #include "command/args.h" | ||
| 7 | #include "command/error.h" | ||
| 8 | #include "command/run.h" | ||
| 9 | #include "config.h" | ||
| 10 | #include "editor.h" | ||
| 11 | #include "filetype.h" | ||
| 12 | #include "syntax/merge.h" | ||
| 13 | #include "util/bsearch.h" | ||
| 14 | #include "util/debug.h" | ||
| 15 | #include "util/errorcode.h" | ||
| 16 | #include "util/hashset.h" | ||
| 17 | #include "util/intern.h" | ||
| 18 | #include "util/path.h" | ||
| 19 | #include "util/readfile.h" | ||
| 20 | #include "util/strtonum.h" | ||
| 21 | #include "util/xmalloc.h" | ||
| 22 | #include "util/xsnprintf.h" | ||
| 23 | #include "util/xstring.h" | ||
| 24 | |||
| 25 | 3900 | static bool in_syntax(const SyntaxLoader *syn, ErrorBuffer *ebuf) | |
| 26 | { | ||
| 27 |
1/4✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→6) taken 3900 times.
✗ Branch 2 (4→5) not taken.
✗ Branch 3 (4→6) not taken.
|
3900 | return likely(syn->current_syntax) || error_msg(ebuf, "No syntax started"); |
| 28 | } | ||
| 29 | |||
| 30 | 2809 | static bool in_state(const SyntaxLoader *syn, ErrorBuffer *ebuf) | |
| 31 | { | ||
| 32 |
1/2✓ Branch 0 (3→4) taken 2809 times.
✗ Branch 1 (3→9) not taken.
|
2809 | if (unlikely(!in_syntax(syn, ebuf))) { |
| 33 | return false; | ||
| 34 | } | ||
| 35 |
1/4✗ Branch 0 (4→5) not taken.
✓ Branch 1 (4→8) taken 2809 times.
✗ Branch 2 (6→7) not taken.
✗ Branch 3 (6→8) not taken.
|
2809 | return likely(syn->current_state) || error_msg(ebuf, "No state started"); |
| 36 | } | ||
| 37 | |||
| 38 | 1242 | static bool close_state(SyntaxLoader *syn, ErrorBuffer *ebuf) | |
| 39 | { | ||
| 40 | 1242 | const State *state = syn->current_state; | |
| 41 |
2/2✓ Branch 0 (2→3) taken 1 times.
✓ Branch 1 (2→5) taken 1241 times.
|
1242 | if (!state) { |
| 42 | return true; | ||
| 43 | } | ||
| 44 | |||
| 45 | 1 | syn->current_state = NULL; | |
| 46 |
1/2✓ Branch 0 (3→4) taken 1 times.
✗ Branch 1 (3→5) not taken.
|
1 | if (likely(state->type != STATE_INVALID)) { |
| 47 | return true; | ||
| 48 | } | ||
| 49 | |||
| 50 | // This error applies to the state itself rather than the last command, so | ||
| 51 | // it doesn't make sense to include the command name in the error message | ||
| 52 | 1 | const char *name = state->name; | |
| 53 | 1 | return error_msg_for_cmd(ebuf, NULL, "No default action in state '%s'", name); | |
| 54 | } | ||
| 55 | |||
| 56 | 2713 | static State *find_or_add_state(const SyntaxLoader *syn, const char *name) | |
| 57 | { | ||
| 58 | 2713 | State *state = find_state(syn->current_syntax, name); | |
| 59 |
2/2✓ Branch 0 (3→4) taken 924 times.
✓ Branch 1 (3→9) taken 1789 times.
|
2713 | if (state) { |
| 60 | return state; | ||
| 61 | } | ||
| 62 | |||
| 63 | 924 | state = xcalloc1(sizeof(*state)); | |
| 64 | 924 | state->name = xstrdup(name); | |
| 65 | 924 | state->defined = false; | |
| 66 | 924 | state->type = STATE_INVALID; | |
| 67 | |||
| 68 |
2/2✓ Branch 0 (6→7) taken 151 times.
✓ Branch 1 (6→8) taken 773 times.
|
924 | if (syn->current_syntax->states.count == 0) { |
| 69 | 151 | syn->current_syntax->start_state = state; | |
| 70 | } | ||
| 71 | |||
| 72 | 924 | return hashmap_insert(&syn->current_syntax->states, state->name, state); | |
| 73 | } | ||
| 74 | |||
| 75 | 2447 | static State *reference_state ( | |
| 76 | const SyntaxLoader *syn, | ||
| 77 | ErrorBuffer *ebuf, | ||
| 78 | const char *name | ||
| 79 | ) { | ||
| 80 |
2/2✓ Branch 0 (2→3) taken 657 times.
✓ Branch 1 (2→4) taken 1790 times.
|
2447 | if (streq(name, "this")) { |
| 81 | 657 | return syn->current_state; | |
| 82 | } | ||
| 83 | |||
| 84 | 1790 | State *state = find_or_add_state(syn, name); | |
| 85 |
4/4✓ Branch 0 (5→6) taken 4 times.
✓ Branch 1 (5→8) taken 1786 times.
✓ Branch 2 (6→7) taken 1 times.
✓ Branch 3 (6→8) taken 3 times.
|
1790 | if (unlikely((syn->flags & SYN_LINT) && state == syn->current_state)) { |
| 86 | 1 | error_msg ( // Soft error | |
| 87 | ebuf, | ||
| 88 | "destination '%s' can be optimized to 'this' in '%s' syntax", | ||
| 89 | name, | ||
| 90 | 1 | syn->current_syntax->name | |
| 91 | ); | ||
| 92 | } | ||
| 93 | |||
| 94 | return state; | ||
| 95 | } | ||
| 96 | |||
| 97 | 272 | static bool in_subsyntax(const SyntaxLoader *syn, ErrorBuffer *ebuf) | |
| 98 | { | ||
| 99 | 272 | bool ss = likely(is_subsyntax(syn->current_syntax)); | |
| 100 |
1/4✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→6) taken 272 times.
✗ Branch 2 (4→5) not taken.
✗ Branch 3 (4→6) not taken.
|
272 | return ss || error_msg(ebuf, "Destination state 'END' only allowed in a subsyntax"); |
| 101 | } | ||
| 102 | |||
| 103 | 188 | static Syntax *must_find_subsyntax ( | |
| 104 | const HashMap *syntaxes, | ||
| 105 | ErrorBuffer *ebuf, | ||
| 106 | const char *name | ||
| 107 | ) { | ||
| 108 | 188 | Syntax *syntax = find_any_syntax(syntaxes, name); | |
| 109 |
1/2✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→6) taken 188 times.
|
188 | if (unlikely(!syntax)) { |
| 110 | ✗ | error_msg(ebuf, "No such syntax '%s'", name); | |
| 111 | ✗ | return NULL; | |
| 112 | } | ||
| 113 |
1/2✗ Branch 0 (6→7) not taken.
✓ Branch 1 (6→9) taken 188 times.
|
188 | if (unlikely(!is_subsyntax(syntax))) { |
| 114 | ✗ | error_msg(ebuf, "Syntax '%s' is not a subsyntax", name); | |
| 115 | ✗ | return NULL; | |
| 116 | } | ||
| 117 | return syntax; | ||
| 118 | } | ||
| 119 | |||
| 120 | 175 | static bool subsyntax_call ( | |
| 121 | EditorState *e, | ||
| 122 | const char *name, | ||
| 123 | const char *ret, | ||
| 124 | State **dest | ||
| 125 | ) { | ||
| 126 | 175 | Syntax *subsyn = must_find_subsyntax(&e->syntaxes, &e->err, name); | |
| 127 | |||
| 128 | 175 | SyntaxMerge m = { | |
| 129 | .subsyn = subsyn, | ||
| 130 | .return_state = NULL, | ||
| 131 | .delim = NULL, | ||
| 132 | .delim_len = 0, | ||
| 133 | }; | ||
| 134 | |||
| 135 |
2/2✓ Branch 0 (3→4) taken 17 times.
✓ Branch 1 (3→6) taken 158 times.
|
175 | if (streq(ret, "END")) { |
| 136 |
1/2✓ Branch 0 (5→9) taken 17 times.
✗ Branch 1 (5→12) not taken.
|
17 | if (!in_subsyntax(&e->syn, &e->err)) { |
| 137 | return false; | ||
| 138 | } | ||
| 139 |
1/2✓ Branch 0 (6→7) taken 158 times.
✗ Branch 1 (6→12) not taken.
|
158 | } else if (subsyn) { |
| 140 | 158 | m.return_state = reference_state(&e->syn, &e->err, ret); | |
| 141 | } | ||
| 142 | |||
| 143 |
1/2✓ Branch 0 (9→10) taken 175 times.
✗ Branch 1 (9→12) not taken.
|
175 | if (subsyn) { |
| 144 | 175 | *dest = merge_syntax(e->syn.current_syntax, &m, &e->styles); | |
| 145 | 175 | return true; | |
| 146 | } | ||
| 147 | |||
| 148 | return false; | ||
| 149 | } | ||
| 150 | |||
| 151 | 2719 | static bool destination_state(EditorState *e, const char *name, State **dest) | |
| 152 | { | ||
| 153 | 2719 | const char *sep = strchr(name, ':'); | |
| 154 |
2/2✓ Branch 0 (2→3) taken 175 times.
✓ Branch 1 (2→6) taken 2544 times.
|
2719 | if (sep) { |
| 155 | // subsyntax:returnstate | ||
| 156 | 175 | char *sub = xstrcut(name, sep - name); | |
| 157 | 175 | bool success = subsyntax_call(e, sub, sep + 1, dest); | |
| 158 | 175 | free(sub); | |
| 159 | 175 | return success; | |
| 160 | } | ||
| 161 | |||
| 162 |
2/2✓ Branch 0 (6→7) taken 255 times.
✓ Branch 1 (6→10) taken 2289 times.
|
2544 | if (streq(name, "END")) { |
| 163 |
1/2✓ Branch 0 (8→9) taken 255 times.
✗ Branch 1 (8→12) not taken.
|
255 | if (!in_subsyntax(&e->syn, &e->err)) { |
| 164 | return false; | ||
| 165 | } | ||
| 166 | 255 | *dest = NULL; | |
| 167 | 255 | return true; | |
| 168 | } | ||
| 169 | |||
| 170 | 2289 | *dest = reference_state(&e->syn, &e->err, name); | |
| 171 | 2289 | return true; | |
| 172 | } | ||
| 173 | |||
| 174 | 2149 | static void lint_emit_name ( | |
| 175 | ErrorBuffer *ebuf, | ||
| 176 | SyntaxLoadFlags flags, | ||
| 177 | const char *ename, | ||
| 178 | const State *dest | ||
| 179 | ) { | ||
| 180 | 2149 | if ( | |
| 181 |
2/2✓ Branch 0 (2→3) taken 14 times.
✓ Branch 1 (2→7) taken 2135 times.
|
2149 | (flags & SYN_LINT) |
| 182 | 14 | && ename | |
| 183 |
2/2✓ Branch 0 (3→4) taken 1 times.
✓ Branch 1 (3→7) taken 13 times.
|
14 | && dest |
| 184 |
1/2✓ Branch 0 (4→5) taken 1 times.
✗ Branch 1 (4→7) not taken.
|
1 | && dest->emit_name |
| 185 |
1/2✓ Branch 0 (5→6) taken 1 times.
✗ Branch 1 (5→7) not taken.
|
1 | && interned_strings_equal(ename, dest->emit_name) |
| 186 | ) { | ||
| 187 | 1 | error_msg ( // Soft error | |
| 188 | ebuf, | ||
| 189 | "emit-name '%s' not needed (destination state uses same emit-name)", | ||
| 190 | ename | ||
| 191 | ); | ||
| 192 | } | ||
| 193 | 2149 | } | |
| 194 | |||
| 195 | 1887 | static Condition *add_condition ( | |
| 196 | EditorState *e, | ||
| 197 | ConditionType type, | ||
| 198 | const char *dest, | ||
| 199 | const char *emit | ||
| 200 | ) { | ||
| 201 | 1887 | BUG_ON(!dest && cond_type_has_destination(type)); | |
| 202 |
1/2✓ Branch 0 (6→7) taken 1887 times.
✗ Branch 1 (6→18) not taken.
|
1887 | if (!in_state(&e->syn, &e->err)) { |
| 203 | return NULL; | ||
| 204 | } | ||
| 205 | |||
| 206 | 1887 | State *d = NULL; | |
| 207 |
3/4✓ Branch 0 (7→8) taken 1797 times.
✓ Branch 1 (7→10) taken 90 times.
✓ Branch 2 (9→10) taken 1797 times.
✗ Branch 3 (9→18) not taken.
|
1887 | if (dest && !destination_state(e, dest, &d)) { |
| 208 | return NULL; | ||
| 209 | } | ||
| 210 | |||
| 211 |
2/2✓ Branch 0 (10→11) taken 547 times.
✓ Branch 1 (10→12) taken 1340 times.
|
1887 | emit = emit ? str_intern(emit) : NULL; |
| 212 | |||
| 213 | 1887 | if ( | |
| 214 | 1887 | type != COND_HEREDOCEND | |
| 215 |
2/2✓ Branch 0 (12→13) taken 1772 times.
✓ Branch 1 (12→15) taken 115 times.
|
1887 | && type != COND_INLIST |
| 216 |
1/2✓ Branch 0 (13→14) taken 1772 times.
✗ Branch 1 (13→15) not taken.
|
1772 | && type != COND_INLIST_BUFFER |
| 217 | ) { | ||
| 218 | 1772 | lint_emit_name(&e->err, e->syn.flags, emit, d); | |
| 219 | } | ||
| 220 | |||
| 221 | 1887 | Condition *c = xcalloc1(sizeof(*c)); | |
| 222 | 1887 | c->a.destination = d; | |
| 223 | 1887 | c->a.emit_name = emit; | |
| 224 | 1887 | c->type = type; | |
| 225 | 1887 | ptr_array_append(&e->syn.current_state->conds, c); | |
| 226 | 1887 | return c; | |
| 227 | } | ||
| 228 | |||
| 229 | 18 | static bool cmd_bufis(EditorState *e, const CommandArgs *a) | |
| 230 | { | ||
| 231 | 18 | const char *str = a->args[0]; | |
| 232 | 18 | const size_t len = strlen(str); | |
| 233 | 18 | Condition *c; | |
| 234 |
1/2✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→4) taken 18 times.
|
18 | if (unlikely(len > ARRAYLEN(c->u.str.buf))) { |
| 235 | ✗ | return error_msg ( | |
| 236 | &e->err, | ||
| 237 | "Maximum length of string is %zu bytes", | ||
| 238 | ARRAYLEN(c->u.str.buf) | ||
| 239 | ); | ||
| 240 | } | ||
| 241 | |||
| 242 | 18 | ConditionType type = a->flags[0] == 'i' ? COND_BUFIS_ICASE : COND_BUFIS; | |
| 243 | 18 | c = add_condition(e, type, a->args[1], a->args[2]); | |
| 244 |
1/2✓ Branch 0 (5→6) taken 18 times.
✗ Branch 1 (5→7) not taken.
|
18 | if (!c) { |
| 245 | return false; | ||
| 246 | } | ||
| 247 | |||
| 248 | 18 | memcpy(c->u.str.buf, str, len); | |
| 249 | 18 | c->u.str.len = len; | |
| 250 | 18 | return true; | |
| 251 | } | ||
| 252 | |||
| 253 | 1546 | static bool cmd_char(EditorState *e, const CommandArgs *a) | |
| 254 | { | ||
| 255 | 1546 | const char *chars = a->args[0]; | |
| 256 |
1/2✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→4) taken 1546 times.
|
1546 | if (unlikely(chars[0] == '\0')) { |
| 257 | ✗ | return error_msg(&e->err, "char argument can't be empty"); | |
| 258 | } | ||
| 259 | |||
| 260 | 1546 | bool add_to_buffer = cmdargs_has_flag(a, 'b'); | |
| 261 | 1546 | bool invert = cmdargs_has_flag(a, 'n'); | |
| 262 | 1546 | ConditionType type; | |
| 263 |
2/2✓ Branch 0 (6→7) taken 1311 times.
✓ Branch 1 (6→10) taken 235 times.
|
1546 | if (add_to_buffer) { |
| 264 | type = COND_CHAR_BUFFER; | ||
| 265 |
4/4✓ Branch 0 (7→8) taken 1280 times.
✓ Branch 1 (7→9) taken 31 times.
✓ Branch 2 (8→9) taken 533 times.
✓ Branch 3 (8→10) taken 747 times.
|
1311 | } else if (!invert && chars[1] == '\0') { |
| 266 | type = COND_CHAR1; | ||
| 267 | } else { | ||
| 268 | 564 | type = COND_CHAR; | |
| 269 | } | ||
| 270 | |||
| 271 | 1546 | Condition *c = add_condition(e, type, a->args[1], a->args[2]); | |
| 272 |
1/2✓ Branch 0 (11→12) taken 1546 times.
✗ Branch 1 (11→17) not taken.
|
1546 | if (!c) { |
| 273 | return false; | ||
| 274 | } | ||
| 275 | |||
| 276 |
2/2✓ Branch 0 (12→13) taken 747 times.
✓ Branch 1 (12→14) taken 799 times.
|
1546 | if (type == COND_CHAR1) { |
| 277 | 747 | c->u.ch = (unsigned char)chars[0]; | |
| 278 | } else { | ||
| 279 | 799 | bitset_add_char_range(c->u.bitset, chars); | |
| 280 |
2/2✓ Branch 0 (15→16) taken 44 times.
✓ Branch 1 (15→17) taken 755 times.
|
799 | if (invert) { |
| 281 | 44 | BITSET_INVERT(c->u.bitset); | |
| 282 | } | ||
| 283 | } | ||
| 284 | |||
| 285 | return true; | ||
| 286 | } | ||
| 287 | |||
| 288 | 66 | static bool cmd_default(EditorState *e, const CommandArgs *a) | |
| 289 | { | ||
| 290 | 66 | SyntaxLoader *syn = &e->syn; | |
| 291 | 66 | ErrorBuffer *ebuf = &e->err; | |
| 292 |
2/4✓ Branch 0 (3→4) taken 66 times.
✗ Branch 1 (3→6) not taken.
✗ Branch 2 (5→6) not taken.
✓ Branch 3 (5→7) taken 66 times.
|
66 | if (!close_state(syn, ebuf) || !in_syntax(syn, ebuf)) { |
| 293 | ✗ | return false; | |
| 294 | } | ||
| 295 | |||
| 296 | 66 | const char *value = str_intern(a->args[0]); | |
| 297 | 66 | HashMap *map = &syn->current_syntax->default_styles; | |
| 298 |
2/2✓ Branch 0 (14→9) taken 78 times.
✓ Branch 1 (14→15) taken 66 times.
|
144 | for (size_t i = 1, n = a->nr_args; i < n; i++) { |
| 299 | 78 | const char *name = a->args[i]; | |
| 300 | 78 | const void *oldval = hashmap_insert_or_replace(map, xstrdup(name), (char*)value); | |
| 301 |
1/2✗ Branch 0 (11→12) not taken.
✓ Branch 1 (11→13) taken 78 times.
|
78 | if (unlikely(oldval)) { |
| 302 | // Soft error | ||
| 303 | ✗ | error_msg(ebuf, "'%s' argument specified multiple times", name); | |
| 304 | } | ||
| 305 | } | ||
| 306 | |||
| 307 | return true; | ||
| 308 | } | ||
| 309 | |||
| 310 | 377 | static bool cmd_eat(EditorState *e, const CommandArgs *a) | |
| 311 | { | ||
| 312 | 377 | SyntaxLoader *syn = &e->syn; | |
| 313 |
1/2✓ Branch 0 (3→4) taken 377 times.
✗ Branch 1 (3→10) not taken.
|
377 | if (!in_state(syn, &e->err)) { |
| 314 | return false; | ||
| 315 | } | ||
| 316 | |||
| 317 | 377 | const char *dest = a->args[0]; | |
| 318 | 377 | State *curstate = syn->current_state; | |
| 319 |
1/2✓ Branch 0 (5→6) taken 377 times.
✗ Branch 1 (5→10) not taken.
|
377 | if (!destination_state(e, dest, &curstate->default_action.destination)) { |
| 320 | return false; | ||
| 321 | } | ||
| 322 | |||
| 323 |
2/2✓ Branch 0 (6→7) taken 90 times.
✓ Branch 1 (6→8) taken 287 times.
|
377 | const char *emit = a->args[1] ? str_intern(a->args[1]) : NULL; |
| 324 | 377 | lint_emit_name(&e->err, syn->flags, emit, curstate->default_action.destination); | |
| 325 | 377 | curstate->default_action.emit_name = emit; | |
| 326 | 377 | curstate->type = STATE_EAT; | |
| 327 | 377 | syn->current_state = NULL; | |
| 328 | 377 | return true; | |
| 329 | } | ||
| 330 | |||
| 331 | 13 | static bool cmd_heredocbegin(EditorState *e, const CommandArgs *a) | |
| 332 | { | ||
| 333 | 13 | SyntaxLoader *syn = &e->syn; | |
| 334 |
1/2✓ Branch 0 (3→4) taken 13 times.
✗ Branch 1 (3→9) not taken.
|
13 | if (!in_state(syn, &e->err)) { |
| 335 | return false; | ||
| 336 | } | ||
| 337 | |||
| 338 | 13 | Syntax *subsyn = must_find_subsyntax(&e->syntaxes, &e->err, a->args[0]); | |
| 339 |
1/2✓ Branch 0 (5→6) taken 13 times.
✗ Branch 1 (5→9) not taken.
|
13 | if (!subsyn) { |
| 340 | return false; | ||
| 341 | } | ||
| 342 | |||
| 343 | // default_action.destination is used as the return state | ||
| 344 | 13 | const char *ret = a->args[1]; | |
| 345 |
1/2✓ Branch 0 (7→8) taken 13 times.
✗ Branch 1 (7→9) not taken.
|
13 | if (!destination_state(e, ret, &syn->current_state->default_action.destination)) { |
| 346 | return false; | ||
| 347 | } | ||
| 348 | |||
| 349 | 13 | syn->current_state->default_action.emit_name = NULL; | |
| 350 | 13 | syn->current_state->type = STATE_HEREDOCBEGIN; | |
| 351 | 13 | syn->current_state->heredoc.subsyntax = subsyn; | |
| 352 | 13 | syn->current_state = NULL; | |
| 353 | |||
| 354 | // Normally merge() marks subsyntax used but in case of heredocs merge() | ||
| 355 | // is not called when syntax file is loaded | ||
| 356 | 13 | subsyn->used = true; | |
| 357 | 13 | return true; | |
| 358 | } | ||
| 359 | |||
| 360 | 10 | static bool cmd_heredocend(EditorState *e, const CommandArgs *a) | |
| 361 | { | ||
| 362 | 10 | Condition *c = add_condition(e, COND_HEREDOCEND, a->args[0], a->args[1]); | |
| 363 |
1/2✓ Branch 0 (3→4) taken 10 times.
✗ Branch 1 (3→7) not taken.
|
10 | if (unlikely(!c)) { |
| 364 | return false; | ||
| 365 | } | ||
| 366 | |||
| 367 | 10 | Syntax *current_syntax = e->syn.current_syntax; | |
| 368 | 10 | BUG_ON(!current_syntax); | |
| 369 | 10 | current_syntax->heredoc = true; | |
| 370 | 10 | return true; | |
| 371 | } | ||
| 372 | |||
| 373 | // Forward declaration, used in cmd_include() and cmd_require() | ||
| 374 | static SystemErrno read_syntax(EditorState *e, const char *filename, SyntaxLoadFlags flags); | ||
| 375 | |||
| 376 | ✗ | static bool cmd_include(EditorState *e, const CommandArgs *a) | |
| 377 | { | ||
| 378 | ✗ | SyntaxLoadFlags flags = SYN_MUST_EXIST; | |
| 379 | ✗ | if (a->flags[0] == 'b') { | |
| 380 | ✗ | flags |= SYN_BUILTIN; | |
| 381 | } | ||
| 382 | ✗ | SystemErrno err = read_syntax(e, a->args[0], flags); | |
| 383 | ✗ | return !err; | |
| 384 | } | ||
| 385 | |||
| 386 | 102 | static bool cmd_list(EditorState *e, const CommandArgs *a) | |
| 387 | { | ||
| 388 | 102 | SyntaxLoader *syn = &e->syn; | |
| 389 | 102 | ErrorBuffer *ebuf = &e->err; | |
| 390 |
2/4✓ Branch 0 (3→4) taken 102 times.
✗ Branch 1 (3→6) not taken.
✗ Branch 2 (5→6) not taken.
✓ Branch 3 (5→7) taken 102 times.
|
102 | if (!close_state(syn, ebuf) || !in_syntax(syn, ebuf)) { |
| 391 | ✗ | return false; | |
| 392 | } | ||
| 393 | |||
| 394 | 102 | char **args = a->args; | |
| 395 | 102 | const char *name = args[0]; | |
| 396 | 102 | StringList *list = find_string_list(syn->current_syntax, name); | |
| 397 |
2/2✓ Branch 0 (8→9) taken 8 times.
✓ Branch 1 (8→12) taken 94 times.
|
102 | if (!list) { |
| 398 | 8 | list = xcalloc1(sizeof(*list)); | |
| 399 | 8 | hashmap_insert(&syn->current_syntax->string_lists, xstrdup(name), list); | |
| 400 |
1/2✗ Branch 0 (12→13) not taken.
✓ Branch 1 (12→14) taken 94 times.
|
94 | } else if (unlikely(list->defined)) { |
| 401 | ✗ | return error_msg(ebuf, "List '%s' already exists", name); | |
| 402 | } | ||
| 403 | 102 | list->defined = true; | |
| 404 | |||
| 405 | 102 | bool icase = a->flags[0] == 'i'; | |
| 406 | 102 | HashSet *set = &list->strings; | |
| 407 | 102 | hashset_init(set, a->nr_args - 1, icase); | |
| 408 |
2/2✓ Branch 0 (18→16) taken 3640 times.
✓ Branch 1 (18→19) taken 102 times.
|
3742 | for (size_t i = 1, n = a->nr_args; i < n; i++) { |
| 409 | 3640 | const char *str = args[i]; | |
| 410 | 3640 | hashset_insert(set, str, strlen(str)); | |
| 411 | } | ||
| 412 | return true; | ||
| 413 | } | ||
| 414 | |||
| 415 | 105 | static bool cmd_inlist(EditorState *e, const CommandArgs *a) | |
| 416 | { | ||
| 417 | 105 | char **args = a->args; | |
| 418 | 105 | const char *name = args[0]; | |
| 419 |
1/2✓ Branch 0 (3→4) taken 105 times.
✗ Branch 1 (3→5) not taken.
|
105 | ConditionType type = cmdargs_has_flag(a, 'b') ? COND_INLIST_BUFFER : COND_INLIST; |
| 420 |
2/2✓ Branch 0 (5→6) taken 27 times.
✓ Branch 1 (5→7) taken 78 times.
|
132 | Condition *c = add_condition(e, type, args[1], args[2] ? args[2] : name); |
| 421 | |||
| 422 |
1/2✓ Branch 0 (8→9) taken 105 times.
✗ Branch 1 (8→15) not taken.
|
105 | if (!c) { |
| 423 | return false; | ||
| 424 | } | ||
| 425 | |||
| 426 | 105 | StringList *list = find_string_list(e->syn.current_syntax, name); | |
| 427 |
2/2✓ Branch 0 (10→11) taken 95 times.
✓ Branch 1 (10→14) taken 10 times.
|
105 | if (unlikely(!list)) { |
| 428 | // Add undefined list | ||
| 429 | 95 | list = xcalloc1(sizeof(*list)); | |
| 430 | 95 | hashmap_insert(&e->syn.current_syntax->string_lists, xstrdup(name), list); | |
| 431 | } | ||
| 432 | |||
| 433 | 105 | list->used = true; | |
| 434 | 105 | c->u.str_list = list; | |
| 435 | 105 | return true; | |
| 436 | } | ||
| 437 | |||
| 438 | 532 | static bool cmd_noeat(EditorState *e, const CommandArgs *a) | |
| 439 | { | ||
| 440 | 532 | SyntaxLoader *syn = &e->syn; | |
| 441 | 532 | State *dest; | |
| 442 |
2/4✓ Branch 0 (3→4) taken 532 times.
✗ Branch 1 (3→6) not taken.
✗ Branch 2 (5→6) not taken.
✓ Branch 3 (5→7) taken 532 times.
|
532 | if (unlikely(!in_state(syn, &e->err) || !destination_state(e, a->args[0], &dest))) { |
| 443 | ✗ | return false; | |
| 444 | } | ||
| 445 | |||
| 446 | 532 | State *curstate = syn->current_state; | |
| 447 |
2/2✓ Branch 0 (7→8) taken 1 times.
✓ Branch 1 (7→9) taken 531 times.
|
532 | if (unlikely(dest == curstate)) { |
| 448 | 1 | return error_msg(&e->err, "using noeat to jump to same state causes infinite loop"); | |
| 449 | } | ||
| 450 | |||
| 451 | 531 | Action *defaction = &curstate->default_action; | |
| 452 | 531 | bool bflag = cmdargs_has_flag(a, 'b'); | |
| 453 | 531 | defaction->destination = dest; | |
| 454 | 531 | defaction->emit_name = NULL; | |
| 455 |
2/2✓ Branch 0 (10→11) taken 504 times.
✓ Branch 1 (10→12) taken 27 times.
|
531 | curstate->type = bflag ? STATE_NOEAT_BUFFER : STATE_NOEAT; |
| 456 | 531 | syn->current_state = NULL; | |
| 457 | 531 | return true; | |
| 458 | } | ||
| 459 | |||
| 460 | 90 | static bool cmd_recolor(EditorState *e, const CommandArgs *a) | |
| 461 | { | ||
| 462 | // If length is not specified then buffered bytes will be recolored | ||
| 463 | 90 | ConditionType type = COND_RECOLOR_BUFFER; | |
| 464 | 90 | size_t len = 0; | |
| 465 | |||
| 466 | 90 | const char *len_str = a->args[1]; | |
| 467 |
2/2✓ Branch 0 (2→3) taken 58 times.
✓ Branch 1 (2→8) taken 32 times.
|
90 | if (len_str) { |
| 468 | 58 | type = COND_RECOLOR; | |
| 469 |
1/2✗ Branch 0 (4→5) not taken.
✓ Branch 1 (4→6) taken 58 times.
|
58 | if (unlikely(!str_to_size(len_str, &len))) { |
| 470 | ✗ | return error_msg(&e->err, "invalid number: '%s'", len_str); | |
| 471 | } | ||
| 472 |
1/2✗ Branch 0 (6→7) not taken.
✓ Branch 1 (6→8) taken 58 times.
|
58 | if (unlikely(len < 1 || len > 2500)) { |
| 473 | ✗ | return error_msg(&e->err, "number of bytes must be between 1-2500 (got %zu)", len); | |
| 474 | } | ||
| 475 | } | ||
| 476 | |||
| 477 | 90 | Condition *c = add_condition(e, type, NULL, a->args[0]); | |
| 478 |
1/2✓ Branch 0 (9→10) taken 90 times.
✗ Branch 1 (9→12) not taken.
|
90 | if (!c) { |
| 479 | return false; | ||
| 480 | } | ||
| 481 | |||
| 482 |
2/2✓ Branch 0 (10→11) taken 58 times.
✓ Branch 1 (10→12) taken 32 times.
|
90 | if (type == COND_RECOLOR) { |
| 483 | 58 | c->u.recolor_len = len; | |
| 484 | } | ||
| 485 | |||
| 486 | return true; | ||
| 487 | } | ||
| 488 | |||
| 489 | 17 | static bool cmd_require(EditorState *e, const CommandArgs *a) | |
| 490 | { | ||
| 491 | 17 | char buf[8192]; | |
| 492 | 17 | char *path; | |
| 493 | 17 | size_t path_len; | |
| 494 | 17 | HashSet *set; | |
| 495 | 17 | SyntaxLoadFlags flags = SYN_MUST_EXIST; | |
| 496 | |||
| 497 |
1/2✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→4) taken 17 times.
|
17 | if (a->flags[0] == 'f') { |
| 498 | ✗ | set = &e->required_syntax_files; | |
| 499 | ✗ | path = a->args[0]; | |
| 500 | ✗ | path_len = strlen(path); | |
| 501 | } else { | ||
| 502 | 17 | set = &e->required_syntax_builtins; | |
| 503 | 17 | path_len = xsnprintf(buf, sizeof(buf), "syntax/inc/%s", a->args[0]); | |
| 504 | 17 | path = buf; | |
| 505 | 17 | flags |= SYN_BUILTIN; | |
| 506 | } | ||
| 507 | |||
| 508 |
2/2✓ Branch 0 (6→7) taken 5 times.
✓ Branch 1 (6→11) taken 12 times.
|
17 | if (hashset_get(set, path, path_len)) { |
| 509 | return true; | ||
| 510 | } | ||
| 511 | |||
| 512 | 5 | SyntaxLoader *syn = &e->syn; | |
| 513 | 5 | const SyntaxLoadFlags save = syn->flags; | |
| 514 | 5 | syn->flags &= ~SYN_WARN_ON_UNUSED_SUBSYN; | |
| 515 | 5 | SystemErrno err = read_syntax(e, path, flags); | |
| 516 | 5 | syn->flags = save; | |
| 517 |
1/2✓ Branch 0 (8→9) taken 5 times.
✗ Branch 1 (8→11) not taken.
|
5 | if (err) { |
| 518 | return false; | ||
| 519 | } | ||
| 520 | |||
| 521 | 5 | hashset_insert(set, path, path_len); | |
| 522 | 5 | return true; | |
| 523 | } | ||
| 524 | |||
| 525 | 923 | static bool cmd_state(EditorState *e, const CommandArgs *a) | |
| 526 | { | ||
| 527 | 923 | SyntaxLoader *syn = &e->syn; | |
| 528 | 923 | ErrorBuffer *ebuf = &e->err; | |
| 529 |
2/4✓ Branch 0 (3→4) taken 923 times.
✗ Branch 1 (3→6) not taken.
✗ Branch 2 (5→6) not taken.
✓ Branch 3 (5→7) taken 923 times.
|
923 | if (!close_state(syn, ebuf) || !in_syntax(syn, ebuf)) { |
| 530 | ✗ | return false; | |
| 531 | } | ||
| 532 | |||
| 533 | 923 | const char *name = a->args[0]; | |
| 534 |
2/4✓ Branch 0 (7→8) taken 923 times.
✗ Branch 1 (7→9) not taken.
✗ Branch 2 (8→9) not taken.
✓ Branch 3 (8→10) taken 923 times.
|
923 | if (unlikely(streq(name, "END") || streq(name, "this"))) { |
| 535 | ✗ | return error_msg(ebuf, "'%s' is reserved state name", name); | |
| 536 | } | ||
| 537 | |||
| 538 | 923 | State *state = find_or_add_state(syn, name); | |
| 539 |
1/2✗ Branch 0 (11→12) not taken.
✓ Branch 1 (11→13) taken 923 times.
|
923 | if (unlikely(state->defined)) { |
| 540 | ✗ | return error_msg(ebuf, "State '%s' already exists", name); | |
| 541 | } | ||
| 542 | |||
| 543 | 923 | const char *emit_name = a->args[1]; | |
| 544 |
3/6✓ Branch 0 (13→14) taken 16 times.
✓ Branch 1 (13→20) taken 907 times.
✓ Branch 2 (14→15) taken 16 times.
✗ Branch 3 (14→16) not taken.
✗ Branch 4 (16→17) not taken.
✗ Branch 5 (16→19) not taken.
|
923 | if ((syn->flags & SYN_LINT) && emit_name && streq(emit_name, name)) { |
| 545 | // Soft error | ||
| 546 | ✗ | error_msg(ebuf, "Redundant emit-name '%s'", emit_name); | |
| 547 | } | ||
| 548 | |||
| 549 | 923 | state->defined = true; | |
| 550 |
2/2✓ Branch 0 (20→21) taken 282 times.
✓ Branch 1 (20→22) taken 625 times.
|
1205 | state->emit_name = str_intern(emit_name ? emit_name : name); |
| 551 | 923 | syn->current_state = state; | |
| 552 | 923 | return true; | |
| 553 | } | ||
| 554 | |||
| 555 | 118 | static bool cmd_str(EditorState *e, const CommandArgs *a) | |
| 556 | { | ||
| 557 | 118 | const char *str = a->args[0]; | |
| 558 | 118 | size_t len = strlen(str); | |
| 559 |
1/2✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→4) taken 118 times.
|
118 | if (unlikely(len < 2)) { |
| 560 | ✗ | error_msg ( // Soft error | |
| 561 | &e->err, | ||
| 562 | "string should be at least 2 bytes; use 'char' for single bytes" | ||
| 563 | ); | ||
| 564 | } | ||
| 565 | |||
| 566 | 118 | Condition *c; | |
| 567 | 118 | size_t maxlen = ARRAYLEN(c->u.str.buf); | |
| 568 |
1/2✗ Branch 0 (4→5) not taken.
✓ Branch 1 (4→6) taken 118 times.
|
118 | if (unlikely(len > maxlen)) { |
| 569 | ✗ | return error_msg(&e->err, "maximum length of string is %zu bytes", maxlen); | |
| 570 | } | ||
| 571 | |||
| 572 | 118 | ConditionType type; | |
| 573 |
2/2✓ Branch 0 (7→8) taken 111 times.
✓ Branch 1 (7→10) taken 7 times.
|
118 | if (cmdargs_has_flag(a, 'i')) { |
| 574 | type = COND_STR_ICASE; | ||
| 575 | } else { | ||
| 576 |
2/2✓ Branch 0 (8→9) taken 36 times.
✓ Branch 1 (8→10) taken 75 times.
|
111 | type = (len == 2) ? COND_STR2 : COND_STR; |
| 577 | } | ||
| 578 | |||
| 579 | 118 | c = add_condition(e, type, a->args[1], a->args[2]); | |
| 580 |
1/2✓ Branch 0 (11→12) taken 118 times.
✗ Branch 1 (11→13) not taken.
|
118 | if (!c) { |
| 581 | return false; | ||
| 582 | } | ||
| 583 | |||
| 584 | 118 | memcpy(c->u.str.buf, str, len); | |
| 585 | 118 | c->u.str.len = len; | |
| 586 | 118 | return true; | |
| 587 | } | ||
| 588 | |||
| 589 | 151 | static bool finish_syntax(SyntaxLoader *syn, ErrorBuffer *ebuf, HashMap *syntaxes) | |
| 590 | { | ||
| 591 | 151 | Syntax *syntax = syn->current_syntax; | |
| 592 | 151 | BUG_ON(!syntax); | |
| 593 |
4/4✓ Branch 0 (5→6) taken 150 times.
✓ Branch 1 (5→8) taken 1 times.
✓ Branch 2 (7→8) taken 5 times.
✓ Branch 3 (7→9) taken 145 times.
|
151 | bool r = close_state(syn, ebuf) && finalize_syntax(syntaxes, syntax, ebuf); |
| 594 | 6 | if (!r) { | |
| 595 | 6 | free_syntax(syntax); | |
| 596 | } | ||
| 597 | 151 | syn->current_syntax = NULL; | |
| 598 | 151 | return r; | |
| 599 | } | ||
| 600 | |||
| 601 | 152 | static bool cmd_syntax(EditorState *e, const CommandArgs *a) | |
| 602 | { | ||
| 603 | 152 | SyntaxLoader *syn = &e->syn; | |
| 604 |
3/4✓ Branch 0 (2→3) taken 83 times.
✓ Branch 1 (2→5) taken 69 times.
✓ Branch 2 (4→5) taken 83 times.
✗ Branch 3 (4→11) not taken.
|
152 | if (syn->current_syntax && !finish_syntax(syn, &e->err, &e->syntaxes)) { |
| 605 | return false; | ||
| 606 | } | ||
| 607 | |||
| 608 | 152 | Syntax *syntax = xcalloc1(sizeof(*syntax)); | |
| 609 | 152 | syntax->name = xstrdup(a->args[0]); | |
| 610 |
4/4✓ Branch 0 (7→8) taken 82 times.
✓ Branch 1 (7→10) taken 70 times.
✓ Branch 2 (8→9) taken 7 times.
✓ Branch 3 (8→10) taken 75 times.
|
152 | if (is_subsyntax(syntax) && !(syn->flags & SYN_WARN_ON_UNUSED_SUBSYN)) { |
| 611 | 7 | syntax->warned_unused_subsyntax = true; | |
| 612 | } | ||
| 613 | |||
| 614 | 152 | syn->current_syntax = syntax; | |
| 615 | 152 | syn->current_state = NULL; | |
| 616 | 152 | return true; | |
| 617 | } | ||
| 618 | |||
| 619 | #define CMD(name, flags, min, max, func) \ | ||
| 620 | {name, flags, CMDOPT_ALLOW_IN_RC, min, max, func} | ||
| 621 | |||
| 622 | static const Command cmds[] = { | ||
| 623 | CMD("bufis", "i", 2, 3, cmd_bufis), | ||
| 624 | CMD("char", "bn", 2, 3, cmd_char), | ||
| 625 | CMD("default", "", 2, -1, cmd_default), | ||
| 626 | CMD("eat", "", 1, 2, cmd_eat), | ||
| 627 | CMD("heredocbegin", "", 2, 2, cmd_heredocbegin), | ||
| 628 | CMD("heredocend", "", 1, 2, cmd_heredocend), | ||
| 629 | CMD("include", "b", 1, 1, cmd_include), | ||
| 630 | CMD("inlist", "b", 2, 3, cmd_inlist), | ||
| 631 | CMD("list", "i", 2, -1, cmd_list), | ||
| 632 | CMD("noeat", "b", 1, 1, cmd_noeat), | ||
| 633 | CMD("recolor", "", 1, 2, cmd_recolor), | ||
| 634 | CMD("require", "f", 1, 1, cmd_require), | ||
| 635 | CMD("state", "", 1, 2, cmd_state), | ||
| 636 | CMD("str", "i", 2, 3, cmd_str), | ||
| 637 | CMD("syntax", "", 1, 1, cmd_syntax), | ||
| 638 | }; | ||
| 639 | |||
| 640 | 24 | UNITTEST { | |
| 641 | 24 | CHECK_BSEARCH_ARRAY(cmds, name); | |
| 642 | 24 | } | |
| 643 | |||
| 644 | 4069 | static const Command *find_syntax_command(const char *name) | |
| 645 | { | ||
| 646 | 4069 | return BSEARCH(name, cmds, command_cmp); | |
| 647 | } | ||
| 648 | |||
| 649 | ✗ | static char *expand_syntax_var(const EditorState *e, const char *name) | |
| 650 | { | ||
| 651 | ✗ | if (streq(name, "DTE_HOME")) { | |
| 652 | ✗ | return xstrdup(e->user_config_dir); | |
| 653 | } | ||
| 654 | return NULL; | ||
| 655 | } | ||
| 656 | |||
| 657 | static const CommandSet syntax_commands = { | ||
| 658 | .lookup = find_syntax_command, | ||
| 659 | }; | ||
| 660 | |||
| 661 | 75 | static CommandRunner cmdrunner_for_syntaxes(EditorState *e) | |
| 662 | { | ||
| 663 | 75 | CommandRunner runner = cmdrunner(e, &syntax_commands); | |
| 664 | 75 | runner.expand_variable = expand_syntax_var; | |
| 665 | 75 | runner.flags |= CMDRUNNER_STOP_AT_FIRST_ERROR; | |
| 666 | 75 | return runner; | |
| 667 | } | ||
| 668 | |||
| 669 | 5 | static ConfigFlags syn_flags_to_cfg_flags(SyntaxLoadFlags flags) | |
| 670 | { | ||
| 671 | 5 | static_assert(SYN_MUST_EXIST == (SyntaxLoadFlags)CFG_MUST_EXIST); | |
| 672 | 5 | static_assert(SYN_BUILTIN == (SyntaxLoadFlags)CFG_BUILTIN); | |
| 673 | 5 | SyntaxLoadFlags mask = SYN_MUST_EXIST | SYN_BUILTIN; | |
| 674 | 5 | return (ConfigFlags)(flags & mask); | |
| 675 | } | ||
| 676 | |||
| 677 | 5 | static SystemErrno read_syntax ( | |
| 678 | EditorState *e, | ||
| 679 | const char *filename, | ||
| 680 | SyntaxLoadFlags flags | ||
| 681 | ) { | ||
| 682 | 5 | CommandRunner runner = cmdrunner_for_syntaxes(e); | |
| 683 | 5 | return read_config(&runner, filename, syn_flags_to_cfg_flags(flags)); | |
| 684 | } | ||
| 685 | |||
| 686 | 70 | Syntax *load_syntax ( | |
| 687 | EditorState *e, | ||
| 688 | StringView config_text, | ||
| 689 | const char *config_filename, | ||
| 690 | SyntaxLoadFlags flags | ||
| 691 | ) { | ||
| 692 | 70 | SyntaxLoader *syn = &e->syn; | |
| 693 | 70 | *syn = (SyntaxLoader) { | |
| 694 | .current_syntax = NULL, | ||
| 695 | .current_state = NULL, | ||
| 696 | 70 | .flags = flags | SYN_WARN_ON_UNUSED_SUBSYN, | |
| 697 | }; | ||
| 698 | |||
| 699 | 70 | ErrorBuffer *ebuf = &e->err; | |
| 700 | 70 | const char *saved_file = ebuf->config_filename; | |
| 701 | 70 | const unsigned int saved_line = ebuf->config_line; | |
| 702 | 70 | CommandRunner runner = cmdrunner_for_syntaxes(e); | |
| 703 | |||
| 704 | 70 | ebuf->config_filename = config_filename; | |
| 705 | 70 | ebuf->config_line = 1; | |
| 706 | 70 | bool r = exec_config(&runner, config_text); | |
| 707 | |||
| 708 |
2/2✓ Branch 0 (3→4) taken 69 times.
✓ Branch 1 (3→10) taken 1 times.
|
70 | if (syn->current_syntax) { |
| 709 |
2/2✓ Branch 0 (4→5) taken 68 times.
✓ Branch 1 (4→8) taken 1 times.
|
69 | if (r) { |
| 710 | 68 | r = finish_syntax(syn, ebuf, &e->syntaxes); | |
| 711 |
2/2✓ Branch 0 (6→7) taken 62 times.
✓ Branch 1 (6→10) taken 6 times.
|
68 | if (r) { |
| 712 | 62 | find_unused_subsyntaxes(&e->syntaxes, ebuf); | |
| 713 | } | ||
| 714 | } else { | ||
| 715 | 1 | free_syntax(syn->current_syntax); | |
| 716 | 1 | syn->current_syntax = NULL; | |
| 717 | } | ||
| 718 | } | ||
| 719 | |||
| 720 | 70 | ebuf->config_filename = saved_file; | |
| 721 | 70 | ebuf->config_line = saved_line; | |
| 722 | |||
| 723 |
2/2✓ Branch 0 (10→11) taken 63 times.
✓ Branch 1 (10→17) taken 7 times.
|
70 | if (!r) { |
| 724 | return NULL; | ||
| 725 | } | ||
| 726 | |||
| 727 | 63 | const char *base = path_basename(config_filename); | |
| 728 | 63 | Syntax *syntax = find_syntax(&e->syntaxes, base); | |
| 729 |
2/2✓ Branch 0 (12→13) taken 2 times.
✓ Branch 1 (12→15) taken 61 times.
|
63 | if (!syntax) { |
| 730 | 2 | error_msg ( | |
| 731 | &e->err, | ||
| 732 | "%s: no main syntax found (i.e. with name '%s')", | ||
| 733 | config_filename, | ||
| 734 | base | ||
| 735 | ); | ||
| 736 | 2 | return NULL; | |
| 737 | } | ||
| 738 | |||
| 739 |
1/2✗ Branch 0 (15→16) not taken.
✓ Branch 1 (15→17) taken 61 times.
|
61 | if (e->status != EDITOR_INITIALIZING) { |
| 740 | ✗ | update_syntax_styles(syntax, &e->styles); | |
| 741 | } | ||
| 742 | |||
| 743 | return syntax; | ||
| 744 | } | ||
| 745 | |||
| 746 | 55 | static Syntax *load_syntax_builtin(EditorState *e, const char *name, SyntaxLoadFlags flags) | |
| 747 | { | ||
| 748 | 55 | const BuiltinConfig *cfg = get_builtin_config(name); | |
| 749 |
1/2✓ Branch 0 (2→3) taken 55 times.
✗ Branch 1 (2→6) not taken.
|
55 | if (!cfg) { |
| 750 |
1/2✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→5) taken 55 times.
|
55 | if (flags & SYN_MUST_EXIST) { |
| 751 | ✗ | error_msg(&e->err, "no built-in config with name '%s'", name); | |
| 752 | } | ||
| 753 | 55 | return NULL; | |
| 754 | } | ||
| 755 | ✗ | return load_syntax(e, cfg->text, name, flags | SYN_BUILTIN); | |
| 756 | } | ||
| 757 | |||
| 758 | 57 | Syntax *load_syntax_file(EditorState *e, const char *filename, SyntaxLoadFlags flags) | |
| 759 | { | ||
| 760 | 57 | char *alloc; | |
| 761 | 57 | ssize_t size = read_file(filename, &alloc, 0); | |
| 762 |
2/2✓ Branch 0 (3→4) taken 55 times.
✓ Branch 1 (3→10) taken 2 times.
|
57 | if (size < 0) { |
| 763 | 55 | int err = errno; | |
| 764 |
2/4✓ Branch 0 (4→5) taken 55 times.
✗ Branch 1 (4→6) not taken.
✗ Branch 2 (5→6) not taken.
✓ Branch 3 (5→9) taken 55 times.
|
55 | if (err != ENOENT || (flags & SYN_MUST_EXIST)) { |
| 765 | ✗ | error_msg(&e->err, "Error reading %s: %s", filename, strerror(err)); | |
| 766 | ✗ | errno = err; | |
| 767 | } | ||
| 768 | 55 | return NULL; | |
| 769 | } | ||
| 770 | |||
| 771 | 2 | StringView config = string_view(alloc, size); | |
| 772 | 2 | Syntax *syntax = load_syntax(e, config, filename, flags); | |
| 773 | 2 | free(alloc); | |
| 774 | |||
| 775 |
2/2✓ Branch 0 (11→12) taken 1 times.
✓ Branch 1 (11→13) taken 1 times.
|
2 | if (unlikely(!syntax)) { |
| 776 | 1 | errno = EINVAL; | |
| 777 | } | ||
| 778 | |||
| 779 | return syntax; | ||
| 780 | } | ||
| 781 | |||
| 782 | 55 | Syntax *load_syntax_by_filetype(EditorState *e, const char *filetype) | |
| 783 | { | ||
| 784 |
1/2✓ Branch 0 (2→3) taken 55 times.
✗ Branch 1 (2→8) not taken.
|
55 | if (!is_valid_filetype_name(filetype)) { |
| 785 | return NULL; | ||
| 786 | } | ||
| 787 | |||
| 788 | 55 | const char *cfgdir = e->user_config_dir; | |
| 789 | 55 | char filename[8192]; | |
| 790 | 55 | xsnprintf(filename, sizeof filename, "%s/syntax/%s", cfgdir, filetype); | |
| 791 | |||
| 792 | 55 | Syntax *syn = load_syntax_file(e, filename, 0); | |
| 793 |
2/4✓ Branch 0 (5→6) taken 55 times.
✗ Branch 1 (5→8) not taken.
✓ Branch 2 (6→7) taken 55 times.
✗ Branch 3 (6→8) not taken.
|
55 | if (syn || errno != ENOENT) { |
| 794 | return syn; | ||
| 795 | } | ||
| 796 | |||
| 797 | // Skip past "%s/" (cfgdir) part of formatted string from above and | ||
| 798 | // try to load a built-in syntax named `syntax/<filetype>` | ||
| 799 | 55 | const char *builtin_name = filename + strlen(cfgdir) + STRLEN("/"); | |
| 800 | 55 | return load_syntax_builtin(e, builtin_name, 0); | |
| 801 | } | ||
| 802 |