dte test coverage


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 50.0% high: ≥ 85.0%
Coverage Exec / Excl / Total
Lines: 90.6% 87 / 1 / 97
Functions: 100.0% 10 / 0 / 10
Branches: 55.3% 42 / 2 / 78

src/editorconfig/editorconfig.c
Line Branch Exec Source
1 #include <stddef.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include "editorconfig.h"
5 #include "ini.h"
6 #include "match.h"
7 #include "options.h"
8 #include "util/debug.h"
9 #include "util/path.h"
10 #include "util/readfile.h"
11 #include "util/string-view.h"
12 #include "util/strtonum.h"
13 #include "util/xstring.h"
14
15 enum {
16 MAX_FILESIZE = 32u << 20, // 32 MiB
17 };
18
19 typedef struct {
20 const char *const pathname;
21 StringView config_file_dir;
22 EditorConfigOptions options;
23 bool match;
24 } EditorConfigContext;
25
26 typedef enum {
27 ECONF_CHARSET,
28 ECONF_END_OF_LINE,
29 ECONF_INDENT_SIZE,
30 ECONF_INDENT_STYLE,
31 ECONF_INSERT_FINAL_NL,
32 ECONF_MAX_LINE_LENGTH,
33 ECONF_TAB_WIDTH,
34 ECONF_TRIM_TRAILING_WS,
35 ECONF_UNKNOWN_PROPERTY,
36 } PropertyType;
37
38 #define CMP(s, val) if (mem_equal(name.data, s, STRLEN(s))) return val;
39
40 9 static PropertyType lookup_property(StringView name)
41 {
42
4/8
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 2 times.
✓ Branch 2 → 9 taken 3 times.
✓ Branch 2 → 14 taken 3 times.
✓ Branch 2 → 17 taken 1 time.
✗ Branch 2 → 20 not taken.
✗ Branch 2 → 23 not taken.
✗ Branch 2 → 26 not taken.
9 switch (name.length) {
43 case 7: CMP("charset", ECONF_CHARSET); break;
44
1/2
✓ Branch 7 → 8 taken 2 times.
✗ Branch 7 → 26 not taken.
2 case 9: CMP("tab_width", ECONF_TAB_WIDTH); break;
45 3 case 11:
46
1/2
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 26 taken 3 times.
3 CMP("indent_size", ECONF_INDENT_SIZE);
47 CMP("end_of_line", ECONF_END_OF_LINE);
48 break;
49
1/2
✓ Branch 15 → 16 taken 3 times.
✗ Branch 15 → 26 not taken.
3 case 12: CMP("indent_style", ECONF_INDENT_STYLE); break;
50
1/2
✓ Branch 18 → 19 taken 1 time.
✗ Branch 18 → 26 not taken.
1 case 15: CMP("max_line_length", ECONF_MAX_LINE_LENGTH); break;
51 case 20: CMP("insert_final_newline", ECONF_INSERT_FINAL_NL); break;
52 case 24: CMP("trim_trailing_whitespace", ECONF_TRIM_TRAILING_WS); break;
53 }
54 return ECONF_UNKNOWN_PROPERTY;
55 }
56
57 3 static EditorConfigIndentStyle lookup_indent_style(StringView val)
58 {
59
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 7 taken 3 times.
3 if (strview_equal_icase(val, strview("space"))) {
60 return INDENT_STYLE_SPACE;
61 } else if (strview_equal_icase(val, strview("tab"))) {
62 return INDENT_STYLE_TAB;
63 }
64 return INDENT_STYLE_UNSPECIFIED;
65 }
66
67 5 static unsigned int parse_indent_digit(StringView val)
68 {
69 5 static_assert(INDENT_WIDTH_MAX == TAB_WIDTH_MAX);
70
1/2
✓ Branch 2 → 3 taken 5 times.
✗ Branch 2 → 5 not taken.
5 unsigned int indent = (val.length == 1) ? val.data[0] - '0' : 0;
71
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 5 times.
5 return (indent <= INDENT_WIDTH_MAX) ? indent : 0;
72 }
73
74 3 static void parse_indent_size(EditorConfigOptions *options, StringView val)
75 {
76 3 bool tab = strview_equal_icase(val, strview("tab"));
77 3 options->indent_size_is_tab = tab;
78
1/2
✓ Branch 3 → 4 taken 3 times.
✗ Branch 3 → 5 not taken.
3 options->indent_size = tab ? 0 : parse_indent_digit(val);
79 3 }
80
81 1 static unsigned int parse_max_line_length(StringView val)
82 {
83 1 unsigned int maxlen = 0;
84 1 size_t ndigits = buf_parse_uint(val, &maxlen);
85
1/2
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 5 not taken.
1 return (ndigits == val.length) ? MIN(maxlen, TEXT_WIDTH_MAX) : 0;
86 }
87
88 9 static void editorconfig_option_set (
89 EditorConfigOptions *options,
90 StringView name,
91 StringView val
92 ) {
93
4/6
✓ Branch 3 → 4 taken 3 times.
✓ Branch 3 → 6 taken 3 times.
✓ Branch 3 → 7 taken 2 times.
✓ Branch 3 → 8 taken 1 time.
✗ Branch 3 → 10 not taken.
✗ Branch 3 → 12 not taken.
9 switch (lookup_property(name)) {
94 3 case ECONF_INDENT_STYLE:
95 3 options->indent_style = lookup_indent_style(val);
96 3 break;
97 3 case ECONF_INDENT_SIZE:
98 3 parse_indent_size(options, val);
99 3 break;
100 2 case ECONF_TAB_WIDTH:
101 2 options->tab_width = parse_indent_digit(val);
102 2 break;
103 1 case ECONF_MAX_LINE_LENGTH:
104 1 options->max_line_length = parse_max_line_length(val);
105 1 break;
106 case ECONF_CHARSET:
107 case ECONF_END_OF_LINE:
108 case ECONF_INSERT_FINAL_NL:
109 case ECONF_TRIM_TRAILING_WS:
110 case ECONF_UNKNOWN_PROPERTY:
111 break;
112 default:
113 BUG("unhandled property type");
114 }
115 9 }
116
117 4 static bool is_root_key(const IniParser *ini)
118 {
119 4 return strview_equal_icase(ini->name, strview("root"))
120
2/4
✓ Branch 3 → 4 taken 4 times.
✗ Branch 3 → 7 not taken.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 4 times.
4 && strview_equal_icase(ini->value, strview("true"));
121 }
122
123 6 static EditorConfigOptions editorconfig_options_init(void)
124 {
125 6 return (EditorConfigOptions){.indent_style = INDENT_STYLE_UNSPECIFIED};
126 }
127
128 4 static void editorconfig_parse(StringView input, EditorConfigContext *ctx)
129 {
130 4 static const StringView utf8_bom = STRING_VIEW("\xEF\xBB\xBF");
131 4 bool has_utf8_bom = strview_has_sv_prefix(input, utf8_bom);
132
133 8 IniParser ini = {
134 .input = input,
135
2/2
✓ Branch 3 → 4 taken 2 times.
✓ Branch 3 → 5 taken 2 times.
4 .pos = has_utf8_bom ? utf8_bom.length : 0,
136 };
137
138
2/2
✓ Branch 18 → 6 taken 30 times.
✓ Branch 18 → 19 taken 4 times.
34 while (ini_parse(&ini)) {
139
2/2
✓ Branch 6 → 7 taken 4 times.
✓ Branch 6 → 11 taken 26 times.
30 if (ini.section.length == 0) {
140
1/2
✓ Branch 8 → 9 taken 4 times.
✗ Branch 8 → 10 not taken.
4 if (is_root_key(&ini)) {
141 // root=true, clear all previous values
142 4 ctx->options = editorconfig_options_init();
143 }
144 4 continue;
145 }
146
147
2/2
✓ Branch 11 → 12 taken 12 times.
✓ Branch 11 → 14 taken 14 times.
26 if (ini.name_count == 1) {
148 // If name_count is 1, it indicates that the name/value pair is
149 // the first in the section and therefore requires a new pattern
150 // to be built and tested for a match
151 12 StringView dir = ctx->config_file_dir;
152 12 ctx->match = ec_pattern_match(ini.section, dir, ctx->pathname);
153 } else {
154 // Otherwise, the section is the same as was passed for the first
155 // name/value pair in the section and the value of ctx->match
156 // can just be reused
157 26 }
158
159
2/2
✓ Branch 14 → 15 taken 9 times.
✓ Branch 14 → 16 taken 17 times.
26 if (ctx->match) {
160 9 editorconfig_option_set(&ctx->options, ini.name, ini.value);
161 }
162 }
163 4 }
164
165 2 EditorConfigOptions get_editorconfig_options(const char *pathname)
166 {
167 2 BUG_ON(!path_is_absolute(pathname));
168 2 EditorConfigContext ctx = {
169 .pathname = pathname,
170 2 .config_file_dir = strview(NULL),
171 2 .options = editorconfig_options_init(),
172 .match = false,
173 };
174
175 2 static const char ecfilename[16] = "/.editorconfig";
176 2 char buf[8192];
177 2 memcpy(buf, ecfilename, sizeof ecfilename);
178
179 2 const char *ptr = pathname + 1;
180 2 size_t dir_len = 1;
181
182 // Iterate up directory tree, looking for ".editorconfig" at each level
183 22 while (1) {
184 12 char *text;
185 12 ssize_t len = read_file(buf, &text, MAX_FILESIZE);
186
2/2
✓ Branch 6 → 7 taken 4 times.
✓ Branch 6 → 9 taken 8 times.
12 if (len >= 0) {
187 4 ctx.config_file_dir = string_view(buf, dir_len);
188 4 editorconfig_parse(string_view(text, len), &ctx);
189 4 free(text);
190 }
191
192 12 const char *slash = strchr(ptr, '/');
193
2/2
✓ Branch 9 → 10 taken 10 times.
✓ Branch 9 → 12 taken 2 times.
12 if (!slash) {
194 break;
195 }
196
197 10 dir_len = slash - pathname;
198 10 xmempcpy2(buf, pathname, dir_len, ecfilename, sizeof ecfilename);
199 10 ptr = slash + 1;
200 }
201
202 // Set indent_size to "tab" if indent_size is not specified and
203 // indent_style is set to "tab"
204 2 EditorConfigOptions *o = &ctx.options;
205
3/4
✓ Branch 12 → 13 taken 1 time.
✓ Branch 12 → 15 taken 1 time.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 1 time.
2 if (o->indent_size == 0 && o->indent_style == INDENT_STYLE_TAB) {
206 o->indent_size_is_tab = true;
207 }
208
209 // Set indent_size to tab_width if indent_size is "tab" and
210 // tab_width is specified
211
1/4
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 18 taken 2 times.
✗ Branch 16 → 17 not taken.
✗ Branch 16 → 18 not taken.
2 if (o->indent_size_is_tab && o->tab_width > 0) {
212 o->indent_size = o->tab_width;
213 }
214
215 // Set tab_width to indent_size if indent_size is specified as
216 // something other than "tab" and tab_width is unspecified
217
4/6
✓ Branch 18 → 19 taken 1 time.
✓ Branch 18 → 22 taken 1 time.
✓ Branch 19 → 20 taken 1 time.
✗ Branch 19 → 22 not taken.
✓ Branch 20 → 21 taken 1 time.
✗ Branch 20 → 22 not taken.
2 if (o->indent_size != 0 && o->tab_width == 0 && !o->indent_size_is_tab) {
218 1 o->tab_width = o->indent_size;
219 }
220
221 2 return ctx.options;
222 }
223