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