/* This file is part of Mailfromd. Copyright (C) 2005-2018 Sergey Poznyakoff This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include "mailfromd.h" #include "prog.h" extern int yy_flex_debug; static mu_list_t include_path; static mu_list_t std_include_path; struct file_data { const char *name; size_t namelen; char *buf; size_t buflen; int found; }; static int find_include_file(void *item, void *data) { char *dir = item; struct file_data *dptr = data; size_t size = strlen(dir) + 1 + dptr->namelen + 1; if (size > dptr->buflen) { dptr->buflen = size; dptr->buf = mu_realloc(dptr->buf, dptr->buflen); } strcpy(dptr->buf, dir); strcat(dptr->buf, "/"); strcat(dptr->buf, dptr->name); return dptr->found = access(dptr->buf, F_OK) == 0; } void include_path_setup() { mu_list_create(&include_path); mu_list_create(&std_include_path); mu_list_append(std_include_path, DEFAULT_VERSION_INCLUDE_DIR); mu_list_append(std_include_path, DEFAULT_INCLUDE_DIR); mu_list_append(std_include_path, "/usr/share/mailfromd/include"); mu_list_append(std_include_path, "/usr/local/share/mailfromd/include"); } void add_include_dir(const char *dir) { int rc; if (!include_path) { rc = mu_list_create(&include_path); if (rc) { mu_error(_("cannot create include path list: %s"), mu_strerror(rc)); return; } } rc = mu_list_append(include_path, mu_strdup(dir)); if (rc) mu_error(_("cannot append to the include path list: %s"), mu_strerror(rc)); } #define ID_EQ(a,b) \ ((a).i_node == (b).i_node && (a).device == (a).device) static int input_file_ident_cmp(const void *item, const void *data) { const struct input_file_ident *id1 = item; const struct input_file_ident *id2 = data; return !ID_EQ(*id1, *id2); } static void input_file_ident_free(void *item) { free(item); } int source_lookup(struct input_file_ident *idptr) { int rc; if (!top_module->incl_sources) { mu_list_create(&top_module->incl_sources); mu_list_set_comparator(top_module->incl_sources, input_file_ident_cmp); mu_list_set_destroy_item(top_module->incl_sources, input_file_ident_free); } rc = mu_list_locate (top_module->incl_sources, idptr, NULL); if (rc == MU_ERR_NOENT) { struct input_file_ident *new_id = mu_alloc(sizeof *new_id); *new_id = *idptr; mu_list_append(top_module->incl_sources, new_id); } return rc == 0; } static int try_file(const char *name, int allow_cwd, int err_not_found, char **newp) { static char *cwd = "."; struct file_data fd; fd.name = name; fd.namelen = strlen(name); fd.buf = NULL; fd.buflen = 0; fd.found = 0; if (allow_cwd) { mu_list_prepend(include_path, cwd); mu_list_foreach(include_path, find_include_file, &fd); mu_list_remove(include_path, cwd); } else mu_list_foreach(include_path, find_include_file, &fd); if (!fd.found) { mu_list_foreach(std_include_path, find_include_file, &fd); if (!fd.found && err_not_found) { parse_error(_("%s: no such file or directory"), name); *newp = NULL; } } if (fd.found) *newp = fd.buf; return fd.found; } int begin_module(const char *modname, const char *filename, struct import_rule *import_rules) { int rc; if (access(filename, R_OK)) { parse_error(_("input file %s is not readable: %s"), filename, mu_strerror(errno)); return 1; } if (set_top_module(modname, filename, import_rules, get_locus()) == 0) rc = lex_new_source(filename, LEX_MODULE); else rc = 0; return rc; } int parse_include(const char *text, int once) { struct mu_wordsplit ws; char *tmp = NULL; char *p = NULL; int rc = 1; if (mu_wordsplit(text, &ws, MU_WRDSF_DEFFLAGS)) { parse_error(_("cannot parse include line: %s"), mu_wordsplit_strerror (&ws)); return errno; } else if (ws.ws_wordc != 2) mu_wordsplit_free (&ws); else { size_t len; int allow_cwd; p = ws.ws_wordv[1]; len = strlen(p); if (p[0] == '<' && p[len - 1] == '>') { allow_cwd = 0; p[len - 1] = 0; p++; } else allow_cwd = 1; if (p[0] != '/' && try_file(p, allow_cwd, 1, &tmp)) p = tmp; } if (p) rc = lex_new_source(p, once ? LEX_ONCE : LEX_NONE); free(tmp); mu_wordsplit_free (&ws); return rc; } #define DEFAULT_SUFFIX ".mf" void require_module(const char *modname, struct import_rule *import_rules) { size_t len; char *fname, *pathname; len = strlen(modname); fname = mu_alloc(len + sizeof DEFAULT_SUFFIX); strcpy(fname, modname); strcat(fname, DEFAULT_SUFFIX); if (try_file(fname, 0, 1, &pathname)) begin_module(modname, pathname, import_rules); free(fname); } static int assign_locus(struct mu_locus_point *ploc, const char *name, const char *linestr) { char *p; unsigned long linenum; errno = 0; linenum = strtoul(linestr, &p, 10); if (errno) return errno; if (*p) return MU_ERR_PARSE; if (linenum > UINT_MAX) return ERANGE; mu_locus_point_init(ploc); mu_locus_point_set_file(ploc, name); ploc->mu_line = (unsigned) linenum; return 0; } int parse_line(char *text, struct mu_locus_point *ploc) { int rc; struct mu_wordsplit ws; while (*text && mu_isspace (*text)) text++; text++; if (mu_wordsplit(text, &ws, MU_WRDSF_DEFFLAGS)) { parse_error(_("cannot parse #line line: %s"), mu_wordsplit_strerror (&ws)); return 1; } else { if (ws.ws_wordc == 2) rc = assign_locus(ploc, NULL, ws.ws_wordv[1]); else if (ws.ws_wordc == 3) rc = assign_locus(ploc, ws.ws_wordv[2], ws.ws_wordv[1]); else rc = 1; if (rc) parse_error(_("malformed #line statement")); } mu_wordsplit_free(&ws); return rc; } int parse_line_cpp(char *text, struct mu_locus_point *ploc) { struct mu_wordsplit ws; int rc; if (mu_wordsplit(text, &ws, MU_WRDSF_DEFFLAGS)) { parse_error(_("cannot parse #line line: %s"), mu_wordsplit_strerror (&ws)); rc = 1; } else if (ws.ws_wordc < 3) { parse_error(_("invalid #line statement")); rc = 1; } else if (assign_locus(ploc, ws.ws_wordv[2], ws.ws_wordv[1])) { parse_error(_("malformed #line statement")); rc = 1; } else rc = 0; mu_wordsplit_free(&ws); return rc; } static void stderr_redirector(char *ppcmd, char **argv) { if (!logger_flags(LOGF_STDERR)) { int p[2]; char buf[1024]; FILE *fp; pid_t pid; signal(SIGCHLD, SIG_DFL); if (pipe(p)) { mu_diag_funcall(MU_DIAG_ERROR, "pipe", "p", errno); exit(127); } switch (pid = fork()) { /* Grandchild */ case 0: if (p[1] != 2) { close(2); dup2(p[1], 2); } close(p[0]); execvp(argv[0], argv); exit(127); case -1: /* Fork failed */ mf_server_log_setup(); mu_error("Cannot run `%s': %s", ppcmd, mu_strerror(errno)); exit(127); default: /* Sub-master */ close(p[1]); fp = fdopen(p[0], "r"); mf_server_log_setup(); while (fgets(buf, sizeof(buf), fp)) mu_error("%s", buf); exit(0); } } else { execvp(argv[0], argv); mf_server_log_setup(); mu_error("Cannot run `%s': %s", ppcmd, mu_strerror(errno)); exit(127); } } FILE * pp_extrn_start(const char *name, pid_t *ppid) { int pout[2]; pid_t pid; size_t size; char *ppcmd; struct mu_wordsplit ws; FILE *fp = NULL; size = strlen(ext_pp) + strlen(name) + 2; ppcmd = mu_alloc(size); strcpy(ppcmd, ext_pp); strcat(ppcmd, " "); strcat(ppcmd, name); mu_debug(MF_SOURCE_PP, MU_DEBUG_TRACE1, ("Running preprocessor: `%s'", ppcmd)); if (mu_wordsplit(ppcmd, &ws, MU_WRDSF_DEFFLAGS)) { parse_error(_("cannot parse line: %s"), mu_wordsplit_strerror (&ws)); exit(EX_UNAVAILABLE); } if (pipe(pout)) { mu_diag_funcall(MU_DIAG_ERROR, "pipe", "pout", errno); exit(EX_UNAVAILABLE); } switch (pid = fork()) { /* The child branch. */ case 0: if (pout[1] != 1) { close(1); dup2(pout[1], 1); } /* Close unneded descripitors */ close_fds_above(1); stderr_redirector(ppcmd, ws.ws_wordv); break; case -1: /* Fork failed */ mu_error("Cannot run `%s': %s", ppcmd, mu_strerror(errno)); break; default: close(pout[1]); fp = fdopen(pout[0], "r"); break; } mu_wordsplit_free(&ws); free(ppcmd); *ppid = pid; return fp; } void pp_extrn_shutdown(FILE *file, pid_t pid) { int status; fclose(file); waitpid(pid, &status, 0); if (WIFEXITED(status)) { status = WEXITSTATUS(status); if (status) { mu_error(_("preprocessor exited with status %d"), status); } } else if (WIFSIGNALED(status)) { mu_error(_("preprocessor terminated on signal %d"), WTERMSIG(status)); } else if (WIFSTOPPED(status)) { mu_error("%s", _("preprocessor stopped")); } else { mu_error("%s", _("preprocessor terminated abnormally")); } } static int _count_include_size(void *item, void *data) { size_t *psize = data; *psize += 3 + strlen((char*)item); return 0; } static int _append_includes(void *item, void *data) { char *str = data; strcat(str, " -I"); strcat(str, (char*) item); return 0; } static char *setup_file; void alloc_ext_pp() { size_t size; if (ext_pp_options_given && !ext_pp) { if (!DEF_EXT_PP) { mu_error(_("default preprocessor is not set; " "use --preprocessor option")); exit(EX_USAGE); } ext_pp = DEF_EXT_PP; } if (!ext_pp) return; ext_pp = mu_strdup(ext_pp); if (ext_pp_options) { size_t len; char *p; len = strlen(ext_pp) + strlen(ext_pp_options); p = mu_alloc(len + 1); strcpy(p, ext_pp); strcat(p, ext_pp_options); ext_pp = p; } size = 0; mu_list_foreach(include_path, _count_include_size, &size); if (size) { ext_pp = mu_realloc(ext_pp, strlen(ext_pp) + size + 1); mu_list_foreach(include_path, _append_includes, ext_pp); } if (try_file("pp-setup", 1, 0, &setup_file)) { ext_pp = mu_realloc(ext_pp, strlen(ext_pp) + strlen(setup_file) + 2); strcat(ext_pp, " "); strcat(ext_pp, setup_file); } } int preprocess_input() { char buffer[512]; FILE *file; pid_t pid; ssize_t n; file = pp_extrn_start(script_file, &pid); if (!file) return EX_NOINPUT; while ((n = fread(buffer, 1, sizeof buffer, file)) > 0) fwrite(buffer, 1, n, stdout); pp_extrn_shutdown(file, pid); return 0; }