1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 #include "editorconfig.h"
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <sys/stat.h>
33 #include <errno.h>
34
35 #include "../util/pathutils.h"
36 #include "../util/ec_glob.h"
37
38 #include "../util/nedit_malloc.h"
39
40 #define ec_strdup NEditStrndup
41
42 #define EC_BUFSIZE 4096
43
44 #define ISCOMMENT(c) (c ==
';' || c ==
'#' ?
1 :
0)
45
46
47 EditorConfig EditorConfigGet(
const char *path,
const char *name) {
48 EditorConfig ec;
49 memset(&ec,
0,
sizeof(EditorConfig));
50
51 size_t pathlen = strlen(path);
52 if(pathlen ==
0 || path[
0] !=
'/') {
53
54 return ec;
55 }
56
57 char *filepath = ConcatPath(path, name);
58
59 EditorConfig editorconfig;
60 memset(&editorconfig,
0,
sizeof(EditorConfig));
61
62 char *parent = strdup(path);
63 int root =
0;
64 while(parent) {
65 char *ecfilename = ConcatPath(parent,
".editorconfig");
66 ECFile *ecfile = ECLoadContent(ecfilename);
67 if(ecfile) {
68 root = ECGetConfig(ecfile, filepath, &editorconfig);
69 ECDestroy(ecfile);
70 editorconfig.found =
1;
71 }
else if(errno !=
ENOENT) {
72 fprintf(stderr,
"Cannot open editorconfig file ''%s'': %s", ecfilename, strerror(errno));
73 }
74
75 free(ecfilename);
76 if(root || strlen(parent) ==
1) {
77 free(parent);
78 parent =
NULL;
79 }
else {
80 char *newparent = ParentPath(parent);
81 free(parent);
82 parent = newparent;
83 }
84 }
85
86 free(filepath);
87
88 return editorconfig;
89 }
90
91 ECFile* ECLoadContent(
const char *path) {
92 int fd = open(path,
O_RDONLY);
93 if(fd <
0) {
94 return NULL;
95 }
96
97 struct stat s;
98 if(fstat(fd, &s)) {
99 close(fd);
100 return NULL;
101 }
102
103 size_t alloc = s.st_size;
104 char *content = malloc(alloc+
1);
105 if(!content) {
106 close(fd);
107 return NULL;
108 }
109 content[alloc] =
0;
110
111 size_t len =
0;
112
113 char buf[
EC_BUFSIZE];
114 ssize_t r;
115 while((r = read(fd, buf,
EC_BUFSIZE)) >
0) {
116 if(alloc-len ==
0) {
117 alloc +=
1024;
118 char *newcontent = realloc(content, alloc);
119 if(!newcontent) {
120 close(fd);
121 free(content);
122 return NULL;
123 }
124 content = newcontent;
125 }
126 memcpy(content+len, buf, r);
127 len += r;
128 }
129
130 close(fd);
131
132 ECFile *ecf = malloc(
sizeof(ECFile));
133 if(!ecf) {
134 free(content);
135 return NULL;
136 }
137 memset(ecf,
0,
sizeof(ECFile));
138
139 ecf->parent = ParentPath((
char*)path);
140 ecf->content = content;
141 ecf->length = len;
142
143 return ecf;
144 }
145
146 static const char* ec_strnchr(
const char *str,
int len,
char c) {
147 for(
int i=
0;i<len;i++) {
148 if(str[i] == c) {
149 return str+i;
150 }
151 }
152 return NULL;
153 }
154
155 static ECSection* create_section(
char *name,
int len) {
156 ECSection *sec = malloc(
sizeof(ECSection));
157 memset(sec,
0,
sizeof(ECSection));
158
159 if(name && len >
0) {
160 const char *s = ec_strnchr(name, len,
'/');
161 if(!s) {
162
163 int newlen = len+
3;
164 sec->name = malloc(newlen+
1);
165 memcpy(sec->name,
"**/",
3);
166 memcpy(sec->name+
3, name, len);
167 sec->name[newlen] =
0;
168 }
else if(name[
0] ==
'/') {
169 sec->name = ec_strdup(name, len);
170 }
else {
171
172 int newlen = len+
1;
173 sec->name = malloc(newlen+
1);
174 sec->name[
0] =
'/';
175 memcpy(sec->name+
1, name, len);
176 sec->name[newlen] =
0;
177 }
178
179 }
180
181 return sec;
182 }
183
184
185 static ECSection* parse_section_name(ECFile *ec, ECSection *last,
char *line,
int len) {
186 int name_begin =
0;
187 int name_end =
0;
188 int comment =
0;
189 for(
int i=
0;i<len;i++) {
190 if(!comment) {
191 char c = line[i];
192 if(c ==
'[') {
193 if(name_begin >
0) {
194 return NULL;
195 }
196 name_begin = i+
1;
197 }
else if(c ==
']') {
198 name_end = i;
199 }
else if(
ISCOMMENT(c)) {
200 comment =
1;
201 }
202 }
203 }
204
205 if(name_begin ==
0 || name_end <= name_begin) {
206 return NULL;
207 }
208
209 int name_len = name_end - name_begin;
210 char *name = line+name_begin;
211
212 ECSection *new_sec = create_section(name, name_len);
213 if(ec->sections) {
214 last->next = new_sec;
215 }
else {
216 ec->sections = new_sec;
217 }
218
219 return new_sec;
220 }
221
222 static void string_trim(
char **str,
int *len) {
223 char *s = *str;
224 int l = *len;
225 while(l >
0 && isspace(*s)) {
226 s++;
227 l--;
228 }
229
230 if(l >
0) {
231 int k = l-
1;
232 while(isspace(s[k])) {
233 k--;
234 l--;
235 }
236 }
237
238 *str = s;
239 *len = l;
240 }
241
242 static int parse_key_value(ECFile *ec, ECSection *sec,
char *line,
int len) {
243 int end = len;
244 int comment =
0;
245 int separator =
0;
246 for(
int i=
0;i<len;i++) {
247 if(!comment) {
248 char c = line[i];
249 if(
ISCOMMENT(c)) {
250 end = len;
251 comment =
1;
252 }
else if(c ==
'=') {
253 if(separator >
0) {
254 return 1;
255 }
256 separator = i;
257 }
258 }
259 }
260
261 if(separator ==
0) {
262 return 0;
263 }
264
265 char *name = line;
266 char *value = line + separator +
1;
267 int name_len = separator;
268 int value_len = end - separator -
1;
269
270 string_trim(&name, &name_len);
271 string_trim(&value, &value_len);
272
273 ECKeyValue *kv = malloc(
sizeof(ECKeyValue));
274 kv->name = ec_strdup(name, name_len);
275 kv->value = ec_strdup(value, value_len);
276
277 kv->next = sec->values;
278 sec->values = kv;
279
280 return 0;
281 }
282
283 int ECParse(ECFile *ec) {
284 ECSection *current_section = create_section(
NULL,
0);
285 ec->preamble = current_section;
286
287 int line_begin =
0;
288
289
290
291
292
293
294 int line_type =
0;
295 int comment =
0;
296
297 for(
int i=
0;i<ec->length;i++) {
298 char c = ec->content[i];
299 if(c ==
'\n') {
300 int line_len = i - line_begin;
301 char *line = ec->content + line_begin;
302
303 switch(line_type) {
304 case 0: {
305
306 break;
307 }
308 case 1: {
309
310 break;
311 }
312 case 2: {
313 current_section = parse_section_name(ec, current_section, line, line_len);
314 if(!current_section) {
315 return 1;
316 }
317 break;
318 }
319 case 3: {
320 if(parse_key_value(ec, current_section, line, line_len)) {
321 return 1;
322 }
323 break;
324 }
325 }
326
327
328 line_begin = i+
1;
329 line_type =
0;
330 comment =
0;
331 }
else if(!comment) {
332 if(!isspace(c)) {
333 if(line_type ==
0) {
334
335 if(c ==
'#') {
336 line_type =
1;
337 comment =
1;
338 }
else if(c ==
'[') {
339 line_type =
2;
340 }
else {
341 line_type =
3;
342 }
343 }
344 }
345 }
346 }
347
348 return 0;
349 }
350
351 static int ec_getbool(
const char *v) {
352 if(v && (v[
0] ==
't' || v[
0] ==
'T')) {
353 return 1;
354 }
355 return 0;
356 }
357
358 static int ec_getint(
const char *str,
int *value) {
359 char *end;
360 errno =
0;
361 long val = strtol(str, &end,
0);
362 if(errno ==
0) {
363 *value = val;
364 return 1;
365 }
else {
366 return 0;
367 }
368 }
369
370 static int ec_isroot(ECFile *ecf) {
371 if(ecf->preamble) {
372 ECKeyValue *v = ecf->preamble->values;
373 while(v) {
374 if(!strcmp(v->name,
"root")) {
375 return ec_getbool(v->value);
376 }
377 v = v->next;
378 }
379 }
380 return 0;
381 }
382
383 static int sec_loadvalues(ECSection *sec, EditorConfig *config) {
384 ECKeyValue *v = sec->values;
385 while(v) {
386 int unset_value =
0;
387 if(!strcmp(v->value,
"unset")) {
388 unset_value =
1;
389 }
390
391 if(!strcmp(v->name,
"indent_style")) {
392 if(unset_value) {
393 config->indent_style =
EC_INDENT_STYLE_UNSET;
394 }
else if(!strcmp(v->value,
"space")) {
395 config->indent_style =
EC_SPACE;
396 }
else if(!strcmp(v->value,
"tab")) {
397 config->indent_style =
EC_TAB;
398 }
399 }
else if(!strcmp(v->name,
"indent_size")) {
400 int val =
0;
401 if(ec_getint(v->value, &val)) {
402 config->indent_size = val;
403 }
404 }
else if(!strcmp(v->name,
"tab_width")) {
405 int val =
0;
406 if(ec_getint(v->value, &val)) {
407 config->tab_width = val;
408 }
409 }
else if(!strcmp(v->name,
"end_of_line")) {
410 if(unset_value) {
411 config->end_of_line =
EC_EOL_UNSET;
412 }
else if(!strcmp(v->value,
"lf")) {
413 config->end_of_line =
EC_LF;
414 }
else if(!strcmp(v->value,
"cr")) {
415 config->end_of_line =
EC_CR;
416 }
else if(!strcmp(v->value,
"crlf")) {
417 config->end_of_line =
EC_CRLF;
418 }
419 }
else if(!strcmp(v->name,
"charset")) {
420 if(config->charset) {
421 free(config->charset);
422 config->charset =
NULL;
423 }
424
425 if(!strcmp(v->value,
"utf-8-bom")) {
426 config->charset = strdup(
"utf-8");
427 config->bom =
EC_BOM;
428 }
else if(!unset_value) {
429 config->charset = strdup(v->value);
430 size_t vlen = strlen(v->value);
431 if(vlen >=
6 && (!memcmp(v->value,
"utf-16",
6) || !memcmp(v->value,
"utf-32",
6))) {
432 config->bom =
EC_BOM;
433 }
434 }
435 }
436
437 v = v->next;
438 }
439
440
441 if( config->indent_style !=
EC_INDENT_STYLE_UNSET &&
442 config->indent_size !=
0 &&
443 config->tab_width !=
0 &&
444 config->end_of_line !=
EC_EOL_UNSET &&
445 config->charset)
446 {
447 return 1;
448 }
449
450 return 0;
451 }
452
453 int ECGetConfig(ECFile *ecf,
const char *filepath, EditorConfig *config) {
454 size_t parentlen = strlen(ecf->parent);
455 const char *relpath = filepath + parentlen -
1;
456
457 EditorConfig local_ec;
458 memset(&local_ec,
0,
sizeof(EditorConfig));
459
460 if(ECParse(ecf)) {
461 char *f = ConcatPath(ecf->parent,
".editorconfig");
462 fprintf(stderr,
"Cannot parse editorconfig file %s\n", f);
463 free(f);
464 return 0;
465 }
466
467 int root = ec_isroot(ecf);
468
469 ECSection *sec = ecf->sections;
470 while(sec) {
471 if(sec->name && !ec_glob(sec->name, relpath)) {
472 if(sec_loadvalues(sec, &local_ec)) {
473
474
475 root =
1;
476 }
477 }
478 sec = sec->next;
479 }
480
481
482 if(config->indent_style ==
EC_INDENT_STYLE_UNSET) {
483 config->indent_style = local_ec.indent_style;
484 }
485 if(config->indent_size ==
0) {
486 config->indent_size = local_ec.indent_size;
487 }
488 if(config->tab_width ==
0) {
489 config->tab_width = local_ec.tab_width;
490 }
491 if(config->end_of_line ==
EC_EOL_UNSET) {
492 config->end_of_line = local_ec.end_of_line;
493 }
494 if(!config->charset) {
495 config->charset = local_ec.charset;
496 }
497 if(config->bom ==
EC_BOM_UNSET) {
498 config->bom = local_ec.bom;
499 }
500
501 return root;
502 }
503
504 static void destroy_section(ECSection *sec) {
505 if(!sec)
return;
506 if(sec->name) free(sec->name);
507
508 ECKeyValue *v = sec->values;
509 while(v) {
510 if(v->name) free(v->name);
511 if(v->value) free(v->value);
512 v = v->next;
513 }
514 }
515
516 void ECDestroy(ECFile *ecf) {
517 destroy_section(ecf->preamble);
518 ECSection *sec = ecf->sections;
519 while(sec) {
520 ECSection *next = sec->next;
521 destroy_section(sec);
522 sec = next;
523 }
524 free(ecf->parent);
525 free(ecf->content);
526 free(ecf);
527 }
528
529