1 /* grecs - Gray's Extensible Configuration System
2 Copyright (C) 2007-2016 Sergey Poznyakoff
3
4 Grecs is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License as published by the
6 Free Software Foundation; either version 3 of the License, or (at your
7 option) any later version.
8
9 Grecs is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with Grecs. If not, see <http://www.gnu.org/licenses/>. */
16
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20 #include <grecs.h>
21 #include <wordsplit.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <sys/wait.h>
25 #include <ctype.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <signal.h>
32 #include <glob.h>
33 #include <unistd.h>
34
35
36 int grecs_log_to_stderr = 1;
37 void (*grecs_log_setup_hook) () = NULL;
38
39 struct input_file_ident {
40 ino_t i_node;
41 dev_t device;
42 };
43
44 struct buffer_ctx {
45 struct buffer_ctx *prev; /* Pointer to previous context */
46 grecs_locus_t locus; /* Current input location */
47 size_t namelen; /* Length of the file name */
48 size_t xlines; /* Number of #line directives output so far */
49 struct input_file_ident id;
50 FILE *infile;
51 };
52
53 extern int grecs_grecs__flex_debug;
54 static struct buffer_ctx *context_stack;
55 static char *linebufbase = NULL;
56 static size_t linebufsize = 0;
57
58 #define INFILE context_stack->infile
59 #define LOCUS context_stack->locus
60 #define POINT context_stack->locus.beg
61
62 static char *linebuf;
63 static size_t bufsize;
64 static char *putback_buffer;
65 static size_t putback_size;
66 static size_t putback_max;
67 static glob_t include_glob;
68 static size_t include_pos;
69 static int include_once;
70
71 static int push_source (const char *name, int once);
72 static int pop_source (void);
73 static int parse_include (const char *text, int once);
74
75 ssize_t
grecs_getline(char ** pbuf,size_t * psize,FILE * fp)76 grecs_getline(char **pbuf, size_t *psize, FILE *fp)
77 {
78 char *buf = *pbuf;
79 size_t size = *psize;
80 ssize_t off = 0;
81
82 if (!buf) {
83 size = 1;
84 buf = grecs_malloc(size);
85 }
86
87 do {
88 if (off == size - 1) {
89 size_t nsize = 2 * size;
90 if (nsize < size)
91 grecs_alloc_die();
92 buf = grecs_realloc(buf, nsize);
93 size = nsize;
94 }
95 if (!fgets(buf + off, size - off, fp)) {
96 if (off == 0)
97 off = -1;
98 break;
99 }
100 off += strlen(buf + off);
101 } while (buf[off - 1] != '\n');
102
103 *pbuf = buf;
104 *psize = size;
105 return off;
106 }
107
108 static void
putback(const char * str)109 putback(const char *str)
110 {
111 size_t len;
112
113 if (!*str)
114 return;
115 len = strlen(str) + 1;
116 if (len > putback_max) {
117 putback_max = len;
118 putback_buffer = grecs_realloc(putback_buffer, putback_max);
119 }
120 strcpy(putback_buffer, str);
121 putback_size = len - 1;
122 }
123
124 static void
pp_line_stmt()125 pp_line_stmt()
126 {
127 size_t ls_size;
128 size_t pb_size;
129
130 if (grecs_asprintf(&linebufbase, &linebufsize,
131 "#line %lu \"%s\" %lu\n",
132 (unsigned long) POINT.line,
133 POINT.file, (unsigned long) context_stack->xlines))
134 grecs_alloc_die();
135
136 ls_size = strlen(linebufbase);
137 pb_size = putback_size + ls_size + 1;
138
139 if (pb_size > putback_max) {
140 putback_max = pb_size;
141 putback_buffer = grecs_realloc(putback_buffer, putback_max);
142 }
143
144 context_stack->xlines++;
145 strcpy(putback_buffer + putback_size, linebufbase);
146 putback_size += ls_size;
147 }
148
149 #define STRMATCH(p, len, s) (len >= sizeof(s) \
150 && memcmp(p, s, sizeof(s) - 1) == 0 \
151 && isspace(p[sizeof(s) - 1]))
152
153 static int
next_line()154 next_line()
155 {
156 ssize_t rc;
157
158 do {
159 if (putback_size) {
160 if (putback_size + 1 > bufsize) {
161 bufsize = putback_size + 1;
162 linebuf = grecs_realloc(linebuf, bufsize);
163 }
164 strcpy(linebuf, putback_buffer);
165 rc = putback_size;
166 putback_size = 0;
167 }
168 else if (!context_stack)
169 return 0;
170 else
171 rc = grecs_getline(&linebuf, &bufsize, INFILE);
172 } while (rc == -1 && pop_source() == 0);
173 return rc;
174 }
175
176 size_t
grecs_preproc_fill_buffer(char * buf,size_t size)177 grecs_preproc_fill_buffer(char *buf, size_t size)
178 {
179 size_t bufsize = size;
180
181 while (next_line() > 0) {
182 char *p;
183 size_t len;
184 int is_line = 0;
185
186 for (p = linebuf; *p && isspace(*p); p++)
187 ;
188 if (*p == '#') {
189 size_t l;
190 for (p++; *p && isspace(*p); p++)
191 ;
192 l = strlen(p);
193 if (STRMATCH(p, l, "include_once")) {
194 if (parse_include(linebuf, 1))
195 putback("/*include_once*/\n");
196 continue;
197 } else if (STRMATCH(p, l, "include")) {
198 if (parse_include(linebuf, 0))
199 putback("/*include*/\n");
200 continue;
201 } else if (STRMATCH(p, l, "line"))
202 is_line = 1;
203 }
204
205 len = strlen(linebuf);
206
207 if (len > size)
208 len = size;
209
210 memcpy(buf, linebuf, len);
211 buf += len;
212 size -= len;
213
214 if (size == 0) {
215 putback(linebuf + len);
216 break;
217 }
218
219 if (!is_line && len > 0 && linebuf[len - 1] == '\n')
220 POINT.line++;
221 }
222 return bufsize - size;
223 }
224
225 #define STAT_ID_EQ(st,id) ((id).i_node == (st).st_ino \
226 && (id).device == (st).st_dev)
227
228 static struct buffer_ctx *
ctx_lookup(struct stat * st)229 ctx_lookup(struct stat *st)
230 {
231 struct buffer_ctx *ctx;
232
233 if (!context_stack)
234 return NULL;
235
236 for (ctx = context_stack->prev; ctx; ctx = ctx->prev)
237 if (STAT_ID_EQ(*st, ctx->id))
238 break;
239 return ctx;
240 }
241
242 const char *grecs_preprocessor = NULL;
243 static struct grecs_list *grecs_usr_include_path;
244 static struct grecs_list *grecs_std_include_path;
245
246 size_t
grecs_include_path_count(int flag)247 grecs_include_path_count(int flag)
248 {
249 size_t count = 0;
250 if (flag & GRECS_STD_INCLUDE)
251 count += grecs_list_size(grecs_std_include_path);
252 if (flag & GRECS_USR_INCLUDE)
253 count += grecs_list_size(grecs_usr_include_path);
254 return count;
255 }
256
257 static int
foreach_dir(struct grecs_list * list,int flag,int (* fun)(int,const char *,void *),void * data)258 foreach_dir(struct grecs_list *list, int flag,
259 int (*fun)(int, const char *, void *), void *data)
260 {
261 int rc = 0;
262 struct grecs_list_entry *ep;
263
264 for (ep = list->head; rc == 0 && ep; ep = ep->next)
265 rc = fun(flag, ep->data, data);
266 return rc;
267 }
268
269 int
grecs_foreach_include_dir(int flag,int (* fun)(int,const char *,void *),void * data)270 grecs_foreach_include_dir(int flag, int (*fun)(int, const char *, void *),
271 void *data)
272 {
273 int rc = 0;
274
275 if (flag & GRECS_STD_INCLUDE)
276 rc = foreach_dir(grecs_std_include_path, GRECS_STD_INCLUDE, fun, data);
277 if (rc == 0 && (flag & GRECS_USR_INCLUDE))
278 rc = foreach_dir(grecs_usr_include_path, GRECS_USR_INCLUDE, fun, data);
279 return rc;
280 }
281
282
283 struct file_data {
284 const char *name;
285 size_t namelen;
286 char *buf;
287 size_t buflen;
288 int found;
289 };
290
291 static int
pp_list_find(struct grecs_list * list,struct file_data * dptr)292 pp_list_find(struct grecs_list *list, struct file_data *dptr)
293 {
294 struct grecs_list_entry *ep;
295
296 if (!list)
297 return 0;
298 for (ep = list->head; !dptr->found && ep; ep = ep->next) {
299 const char *dir = ep->data;
300 size_t size = strlen (dir) + 1 + dptr->namelen + 1;
301 if (size > dptr->buflen) {
302 dptr->buflen = size;
303 dptr->buf = grecs_realloc(dptr->buf, dptr->buflen);
304 }
305 strcpy(dptr->buf, dir);
306 strcat(dptr->buf, "/");
307 strcat(dptr->buf, dptr->name);
308 dptr->found = access(dptr->buf, F_OK) == 0;
309 }
310 return dptr->found;
311 }
312
313 static void
incl_free(void * data)314 incl_free(void *data)
315 {
316 grecs_free(data);
317 }
318
319 void
grecs_include_path_clear()320 grecs_include_path_clear()
321 {
322 if (grecs_usr_include_path)
323 grecs_list_clear(grecs_usr_include_path);
324 if (grecs_std_include_path)
325 grecs_list_clear(grecs_std_include_path);
326 }
327
328 void
grecs_include_path_setup_v(char ** dirs)329 grecs_include_path_setup_v(char **dirs)
330 {
331 if (!grecs_usr_include_path) {
332 grecs_usr_include_path = grecs_list_create();
333 grecs_usr_include_path->free_entry = incl_free;
334 }
335 grecs_std_include_path = grecs_list_create();
336 grecs_std_include_path->free_entry = incl_free;
337 if (dirs) {
338 int i;
339 for (i = 0; dirs[i]; i++)
340 /* FIXME: Element never freed */
341 grecs_list_append(grecs_std_include_path,
342 grecs_strdup(dirs[i]));
343 }
344 }
345
346 void
grecs_include_path_setup(const char * dir,...)347 grecs_include_path_setup(const char *dir, ...)
348 {
349 const char *p;
350 char **argv = NULL;
351 size_t argc = 0;
352 size_t argi = 0;
353 va_list ap;
354
355 va_start(ap, dir);
356 p = dir;
357 while (1) {
358 if (argi == argc) {
359 if (argc == 0)
360 argc = 16;
361 else
362 argc += 16;
363 argv = grecs_realloc(argv, argc * sizeof(argv[0]));
364 }
365 argv[argi++] = (char*) p;
366 if (!p)
367 break;
368 p = va_arg(ap, const char*);
369 }
370 grecs_include_path_setup_v(argv);
371 grecs_free(argv);
372 va_end(ap);
373 }
374
375 void
grecs_preproc_add_include_dir(char * dir)376 grecs_preproc_add_include_dir(char *dir)
377 {
378 if (!grecs_usr_include_path) {
379 grecs_usr_include_path = grecs_list_create();
380 grecs_usr_include_path->free_entry = incl_free;
381 }
382 grecs_list_append(grecs_usr_include_path, grecs_strdup(dir));
383 }
384
385 static struct grecs_symtab *incl_sources;
386
387 /* Calculate the hash of a struct input_file_ident. */
388 static unsigned
incl_hasher(void * data,unsigned long n_buckets)389 incl_hasher(void *data, unsigned long n_buckets)
390 {
391 const struct input_file_ident *id = data;
392 return (id->i_node + id->device) % n_buckets;
393 }
394
395 /* Compare two input_file_idents for equality. */
396 static int
incl_compare(void const * data1,void const * data2)397 incl_compare(void const *data1, void const *data2)
398 {
399 const struct input_file_ident *id1 = data1;
400 const struct input_file_ident *id2 = data2;
401 return !(id1->device == id2->device && id1->i_node == id2->i_node);
402 }
403
404 static int
incl_copy(void * dst,void * src)405 incl_copy(void *dst, void *src)
406 {
407 memcpy(dst, src, sizeof(struct input_file_ident));
408 return 0;
409 }
410
411 static int
source_lookup(struct stat * st)412 source_lookup(struct stat *st)
413 {
414 struct input_file_ident key;
415 int install = 1;
416
417 if (!incl_sources) {
418 incl_sources = grecs_symtab_create(
419 sizeof(struct input_file_ident),
420 incl_hasher,
421 incl_compare,
422 incl_copy,
423 NULL,/*FIXME: alloc*/
424 NULL);
425 if (!incl_sources)
426 grecs_alloc_die();
427 }
428
429 key.i_node = st->st_ino;
430 key.device = st->st_dev;
431 if (!grecs_symtab_lookup_or_install(incl_sources, &key, &install))
432 grecs_alloc_die();
433 return !install;
434 }
435
436
437 static int
push_source(const char * name,int once)438 push_source(const char *name, int once)
439 {
440 FILE *fp;
441 struct buffer_ctx *ctx;
442 struct stat st;
443 int rc = stat(name, &st);
444
445 if (context_stack) {
446 if (rc) {
447 grecs_error(&LOCUS, errno,
448 _("Cannot stat `%s'"), name);
449 return 1;
450 }
451
452 if (POINT.file && STAT_ID_EQ(st, context_stack->id)) {
453 grecs_error(&LOCUS, 0, _("Recursive inclusion"));
454 return 1;
455 }
456
457 if ((ctx = ctx_lookup(&st))) {
458 grecs_error(&LOCUS, 0, _("Recursive inclusion"));
459 if (ctx->prev)
460 grecs_error(&ctx->prev->locus, 0,
461 _("`%s' already included here"),
462 name);
463 else
464 grecs_error(&LOCUS, 0,
465 _("`%s' already included at top level"),
466 name);
467 return 1;
468 }
469 } else if (rc) {
470 grecs_error(NULL, errno, _("Cannot stat `%s'"), name);
471 return 1;
472 }
473
474 if (once && source_lookup(&st))
475 return -1;
476
477 fp = fopen(name, "r");
478 if (!fp) {
479 grecs_error(context_stack ? &LOCUS : NULL, errno,
480 _("Cannot open `%s'"), name);
481 return 1;
482 }
483
484 /* Push current context */
485 ctx = grecs_malloc(sizeof(*ctx));
486 ctx->locus.beg.file = grecs_install_text(name);
487 ctx->locus.beg.line = 1;
488 ctx->locus.beg.col = 0;
489 ctx->locus.end.file = NULL;
490 ctx->locus.end.line = ctx->locus.end.col = 0;
491 ctx->xlines = 0;
492 ctx->namelen = strlen(ctx->locus.beg.file);
493 ctx->id.i_node = st.st_ino;
494 ctx->id.device = st.st_dev;
495 ctx->infile = fp;
496 ctx->prev = context_stack;
497 context_stack = ctx;
498
499 if (grecs_grecs__flex_debug)
500 fprintf (stderr, "Processing file `%s'\n", name);
501
502 pp_line_stmt();
503
504 return 0;
505 }
506
507 static int
pop_source()508 pop_source()
509 {
510 struct buffer_ctx *ctx;
511
512 if (!context_stack)
513 return 1;
514
515 fclose(INFILE);
516
517 /* Restore previous context */
518 ctx = context_stack->prev;
519 grecs_free(context_stack);
520 context_stack = ctx;
521
522 if (include_pos < include_glob.gl_pathc) {
523 push_source(include_glob.gl_pathv[include_pos++], include_once);
524 return 0;
525 } else if (include_glob.gl_pathc) {
526 globfree(&include_glob);
527 include_pos = include_glob.gl_pathc = 0;
528 }
529
530 if (!context_stack) {
531 if (grecs_grecs__flex_debug)
532 fprintf(stderr, "End of input\n");
533 return 1;
534 }
535
536 POINT.line++;
537
538 if (grecs_grecs__flex_debug)
539 fprintf(stderr, "Resuming file `%s' at line %lu\n",
540 POINT.file, (unsigned long) POINT.line);
541
542 pp_line_stmt();
543
544 return 0;
545 }
546
547 char *
grecs_find_include_file(const char * name,int allow_cwd)548 grecs_find_include_file(const char *name, int allow_cwd)
549 {
550 static char *cwd = ".";
551 struct file_data fd;
552
553 fd.name = name;
554 fd.namelen = strlen(name);
555 fd.buf = NULL;
556 fd.buflen = 0;
557 fd.found = 0;
558
559 if (!grecs_usr_include_path)
560 grecs_include_path_setup(NULL);
561 if (allow_cwd) {
562 grecs_list_append(grecs_usr_include_path, cwd);
563 pp_list_find(grecs_usr_include_path, &fd);
564 grecs_list_remove_tail(grecs_usr_include_path);
565 } else
566 pp_list_find(grecs_usr_include_path, &fd);
567
568 if (!fd.found) {
569 pp_list_find(grecs_std_include_path, &fd);
570 if (!fd.found)
571 return NULL;
572 }
573 return fd.buf;
574 }
575
576 static int
isglob(const char * s)577 isglob(const char *s)
578 {
579 for (; *s; s++) {
580 if (strchr("*?[", *s))
581 return 1;
582 }
583 return 0;
584 }
585
586 static int
parse_include(const char * text,int once)587 parse_include(const char *text, int once)
588 {
589 struct wordsplit ws;
590 char *tmp = NULL;
591 char *p = NULL;
592 int rc = 1;
593
594 if (wordsplit(text, &ws, WRDSF_DEFFLAGS))
595 grecs_error(&LOCUS, 0, _("Cannot parse include line"));
596 else if (ws.ws_wordc != 2) {
597 wordsplit_free(&ws);
598 grecs_error(&LOCUS, 0, _("invalid include statement"));
599 } else {
600 size_t len;
601 int allow_cwd;
602
603 p = ws.ws_wordv[1];
604 len = strlen (p);
605
606 if (p[0] == '<' && p[len - 1] == '>') {
607 allow_cwd = 0;
608 p[len - 1] = 0;
609 p++;
610 } else
611 allow_cwd = 1;
612
613 if (isglob(p)) {
614 switch (glob(p, 0, NULL, &include_glob)) {
615 case 0:
616 include_pos = 0;
617 include_once = once;
618 break;
619 case GLOB_NOSPACE:
620 grecs_alloc_die();
621 case GLOB_NOMATCH:
622 break;
623 default:
624 grecs_error(&LOCUS, 0, _("read error"));
625 }
626 p = NULL;
627 } else if (p[0] != '/') {
628 char *q = p;
629 p = grecs_find_include_file(q, allow_cwd);
630 if (!p)
631 grecs_error(&LOCUS, 0,
632 _("%s: No such file or directory"),
633 q);
634 }
635 }
636
637 if (p)
638 rc = push_source(p, once);
639 else if (include_pos < include_glob.gl_pathc)
640 rc = push_source(include_glob.gl_pathv[include_pos++], once);
641
642 grecs_free(tmp);
643 wordsplit_free(&ws);
644 return rc;
645 }
646
647 int
grecs_preproc_init(const char * name)648 grecs_preproc_init(const char *name)
649 {
650 return push_source(name, 0);
651 }
652
653 void
grecs_preproc_done()654 grecs_preproc_done()
655 {
656 grecs_symtab_free(incl_sources);
657 incl_sources = NULL;
658
659 grecs_free(linebuf);
660 linebuf = NULL;
661 bufsize = 0;
662
663 grecs_free(putback_buffer);
664 putback_buffer = NULL;
665 putback_size = putback_max = 0;
666
667 free(linebufbase); /* Allocated via standard malloc/realloc */
668 linebufbase = NULL;
669 linebufsize = 0;
670 }
671
672 int
grecs_preproc_run(const char * config_file,const char * extpp)673 grecs_preproc_run(const char *config_file, const char *extpp)
674 {
675 size_t i;
676 char buffer[512];
677
678 if (grecs_preproc_init(config_file))
679 return 1;
680 if (extpp) {
681 FILE *outfile;
682 char *setup_file;
683 char *cmd = NULL;
684
685 setup_file = grecs_find_include_file("pp-setup", 1);
686 if (setup_file) {
687 size_t size = 0;
688 if (grecs_asprintf(&cmd, &size,
689 "%s %s -", extpp, setup_file))
690 grecs_alloc_die();
691 grecs_free(setup_file);
692 } else
693 cmd = grecs_strdup(extpp);
694 /*FIXME_DEBUG_F1 (2, "Running preprocessor: `%s'", cmd);*/
695 outfile = popen(cmd, "w");
696 if (!outfile) {
697 grecs_error(NULL, errno,
698 _("Unable to start external preprocessor `%s'"),
699 cmd);
700 grecs_free(cmd);
701 return 1;
702 }
703
704 while ((i = grecs_preproc_fill_buffer(buffer, sizeof buffer)))
705 fwrite(buffer, 1, i, outfile);
706 pclose(outfile);
707 grecs_free(cmd);
708 } else {
709 while ((i = grecs_preproc_fill_buffer(buffer, sizeof buffer)))
710 fwrite(buffer, 1, i, stdout);
711 }
712 grecs_preproc_done();
713 return 0;
714 }
715
716 FILE *
grecs_preproc_extrn_start(const char * file_name,pid_t * ppid)717 grecs_preproc_extrn_start(const char *file_name, pid_t *ppid)
718 {
719 int pout[2];
720 pid_t pid;
721 int i;
722 FILE *fp = NULL;
723
724 /*FIXME_DEBUG_F1 (2, "Running preprocessor: `%s'", ppcmd);*/
725
726 if (pipe(pout)) {
727 grecs_error(NULL, errno, "pipe");
728 return NULL;
729 }
730 switch (pid = fork()) {
731 /* The child branch. */
732 case 0:
733 if (pout[1] != 1) {
734 if (dup2(pout[1], 1) == -1) {
735 grecs_error(NULL, errno, "dup2");
736 exit(127);
737 }
738 }
739
740 /* Close unneeded descripitors */
741 for (i = getdtablesize(); i > 2; i--)
742 close(i);
743
744 if (!grecs_log_to_stderr) {
745 int p[2];
746 char *buf = NULL;
747 size_t size = 0;
748 FILE *fp;
749
750 signal(SIGCHLD, SIG_DFL);
751 if (pipe(p)) {
752 grecs_error(NULL, errno, "pipe");
753 exit(127);
754 }
755 switch (pid = fork()) {
756 /* Grandchild */
757 case 0:
758 if (p[1] != 2 && dup2(p[1], 2) == -1) {
759 grecs_error(NULL, errno, "dup2");
760 exit(127);
761 }
762 close(p[0]);
763
764 if (grecs_preproc_run(file_name,
765 grecs_preprocessor))
766 exit(127);
767 exit(0);
768
769 case -1:
770 /* Fork failed */
771 if (grecs_log_setup_hook)
772 grecs_log_setup_hook();
773 grecs_error(NULL, errno, _("Cannot run `%s'"),
774 grecs_preprocessor);
775 exit(127);
776
777 default:
778 /* Sub-master */
779 close (p[1]);
780 fp = fdopen(p[0], "r");
781 if (grecs_log_setup_hook)
782 grecs_log_setup_hook();
783 while (grecs_getline(&buf, &size, fp) > 0)
784 grecs_error(NULL, 0, "%s", buf);
785 }
786 } else {
787 grecs_preproc_run(file_name, grecs_preprocessor);
788 }
789 exit (0);
790
791 case -1:
792 /* Fork failed */
793 grecs_error(NULL, errno, _("Cannot run `%s'"),
794 grecs_preprocessor);
795 break;
796
797 default:
798 close(pout[1]);
799 fp = fdopen(pout[0], "r");
800 break;
801 }
802 *ppid = pid;
803 return fp;
804 }
805
806 void
grecs_preproc_extrn_shutdown(pid_t pid)807 grecs_preproc_extrn_shutdown(pid_t pid)
808 {
809 int status;
810 waitpid(pid, &status, 0);
811 }
812
813