summaryrefslogtreecommitdiff
path: root/agen5/autogen.c
diff options
context:
space:
mode:
Diffstat (limited to 'agen5/autogen.c')
-rw-r--r--agen5/autogen.c657
1 files changed, 657 insertions, 0 deletions
diff --git a/agen5/autogen.c b/agen5/autogen.c
new file mode 100644
index 0000000..73ea05c
--- /dev/null
+++ b/agen5/autogen.c
@@ -0,0 +1,657 @@
+
+/**
+ * @file autogen.c
+ *
+ * Time-stamp: "2012-06-10 12:17:05 bkorb"
+ *
+ * This is the main routine for autogen.
+ *
+ * This file is part of AutoGen.
+ * Copyright (c) 1992-2012 Bruce Korb - all rights reserved
+ *
+ * AutoGen 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 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen 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 <http://www.gnu.org/licenses/>.
+ */
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+typedef void (void_main_proc_t)(int, char **);
+
+typedef enum {
+ EXIT_PCLOSE_WAIT,
+ EXIT_PCLOSE_NOWAIT
+} wait_for_pclose_enum_t;
+
+#define _State_(n) #n,
+static char const * const state_names[] = { STATE_TABLE };
+#undef _State_
+
+static sigjmp_buf abendJumpEnv;
+static int abendJumpSignal = 0;
+
+typedef void (sighandler_proc_t)(int sig);
+static sighandler_proc_t ignore_signal, catch_sig_and_bail;
+
+/* = = = START-STATIC-FORWARD = = = */
+static void
+inner_main(void * closure, int argc, char ** argv);
+
+static void
+exit_cleanup(wait_for_pclose_enum_t cl_wait);
+
+static void
+cleanup_and_abort(int sig);
+
+static void
+catch_sig_and_bail(int sig);
+
+static void
+ignore_signal(int sig);
+
+static void
+done_check(void);
+
+static void
+setup_signals(sighandler_proc_t * chldHandler,
+ sighandler_proc_t * abrtHandler,
+ sighandler_proc_t * dfltHandler);
+/* = = = END-STATIC-FORWARD = = = */
+
+#ifndef HAVE_CHMOD
+# include "compat/chmod.c"
+#endif
+
+/**
+ * main routine under Guile guidance
+ *
+ * @param closure Guile closure parameter. Not used.
+ * @param argc argument count
+ * @param argv argument vector
+ */
+static void
+inner_main(void * closure, int argc, char ** argv)
+{
+ atexit(done_check);
+ initialize(argc, argv);
+
+ processing_state = PROC_STATE_LOAD_DEFS;
+ read_defs();
+
+ /*
+ * Activate the AutoGen specific Scheme functions.
+ * Then load, process and unload the main template
+ */
+ processing_state = PROC_STATE_LOAD_TPL;
+
+ {
+ templ_t* pTF = tpl_load(tpl_fname, NULL);
+
+ processing_state = PROC_STATE_EMITTING;
+ process_tpl(pTF);
+
+ processing_state = PROC_STATE_CLEANUP;
+ cleanup(pTF);
+ }
+
+ processing_state = PROC_STATE_DONE;
+ setup_signals(SIG_DFL, SIG_IGN, SIG_DFL);
+ exit_code = AUTOGEN_EXIT_SUCCESS;
+ done_check();
+ /* NOTREACHED */
+ (void)closure;
+}
+
+/**
+ * main() called from _start()
+ *
+ * @param argc argument count
+ * @param argv argument vector
+ * @return nothing -- Guile never returns, but calls exit(2).
+ */
+int
+main(int argc, char ** argv)
+{
+ /*
+ * IF we've been kicked with a signal,
+ * THEN abort, passing the signal that whacked us.
+ */
+ if (sigsetjmp(abendJumpEnv, 0) != 0)
+ cleanup_and_abort(abendJumpSignal);
+
+ setup_signals(ignore_signal, SIG_DFL, catch_sig_and_bail);
+ optionSaveState(&autogenOptions);
+ trace_fp = stderr;
+
+ /*
+ * as of 2.0.2, Guile will fiddle with strings all on its own accord.
+ * Coerce the environment into being POSIX ASCII strings so it keeps
+ * its bloody stinking nose out of our data.
+ */
+ putenv((char*)(void*)LC_ALL_IS_C);
+
+ /*
+ * If GUILE_WARN_DEPRECATED has not been defined, then likely we are
+ * not in a development environment and likely we don't want to give
+ * our users any angst.
+ */
+ if (getenv(GUILE_WARN_DEP_STR) == NULL)
+ putenv((char*)(void*)GUILE_WARN_NO_ENV);
+
+ AG_SCM_BOOT_GUILE(argc, argv, inner_main);
+
+ /* NOT REACHED */
+ return EXIT_FAILURE;
+}
+
+/**
+ * This code must run regardless of which exit path is taken
+ *
+ * @param [in] cl_wait Whether or not a child process should be waited for.
+ */
+static void
+exit_cleanup(wait_for_pclose_enum_t cl_wait)
+{
+ /*
+ * There are contexts wherein this function can get run twice.
+ */
+ {
+ static int exit_cleanup_done = 0;
+ if (exit_cleanup_done) {
+ if (OPT_VALUE_TRACE > TRACE_NOTHING)
+ fprintf(trace_fp, EXIT_CLEANUP_MULLIGAN);
+ return;
+ }
+
+ exit_cleanup_done = 1;
+ }
+
+#ifdef SHELL_ENABLED
+ SCM_EVAL_CONST(EXIT_CLEANUP_STR);
+#endif
+
+ close_server_shell();
+
+ if (OPT_VALUE_TRACE > TRACE_NOTHING)
+ fprintf(trace_fp, EXIT_CLEANUP_DONE_FMT,
+ (cl_wait == EXIT_PCLOSE_WAIT)
+ ? EXIT_CLEANUP_WAITED : EXIT_CLEANUP_NOWAIT,
+ (unsigned int)getpid());
+
+ do {
+ if (trace_fp == stderr)
+ break;
+
+ if (! trace_is_to_pipe) {
+ fclose(trace_fp);
+ break;
+ }
+
+
+ fflush(trace_fp);
+ pclose(trace_fp);
+ if (cl_wait == EXIT_PCLOSE_WAIT) {
+ int status;
+ while (wait(&status) > 0) ;
+ }
+ } while (false);
+
+ fflush(stdout);
+ fflush(stderr);
+}
+
+/**
+ * A signal was caught, siglongjmp called and main() has called this.
+ * We do not deallocate stuff so it can be found in the core dump.
+ *
+ * @param[in] sig the signal number
+ */
+static void
+cleanup_and_abort(int sig)
+{
+ if (*oops_pfx != NUL) {
+ fputs(oops_pfx, stderr);
+ oops_pfx = zNil;
+ }
+
+ fprintf(stderr, AG_SIG_ABORT_FMT, sig, strsignal(sig),
+ ((unsigned)processing_state <= PROC_STATE_DONE)
+ ? state_names[ processing_state ] : BOGUS_TAG);
+
+ if (processing_state == PROC_STATE_ABORTING) {
+ exit_cleanup(EXIT_PCLOSE_NOWAIT);
+ _exit(sig + 128);
+ }
+
+ processing_state = PROC_STATE_ABORTING;
+ setup_signals(SIG_DFL, SIG_DFL, SIG_DFL);
+
+ /*
+ * IF there is a current template, then we should report where we are
+ * so that the template writer knows where to look for their problem.
+ */
+ if (current_tpl != NULL) {
+ int line;
+ mac_func_t fnCd;
+ char const * pzFn;
+ char const * pzFl;
+
+ if (cur_macro == NULL) {
+ pzFn = PSEUDO_MACRO_NAME_STR;
+ line = 0;
+ fnCd = (mac_func_t)-1;
+
+ } else {
+ mac_func_t f =
+ (cur_macro->md_code > FUNC_CT)
+ ? FTYP_SELECT : cur_macro->md_code;
+ pzFn = ag_fun_names[ f ];
+ line = cur_macro->md_line;
+ fnCd = cur_macro->md_code;
+ }
+ pzFl = current_tpl->td_file;
+ if (pzFl == NULL)
+ pzFl = NULL_FILE_NAME_STR;
+ fprintf(stderr, AG_ABORT_LOC_FMT, pzFl, line, pzFn, fnCd);
+ }
+
+#ifdef HAVE_SYS_RESOURCE_H
+ /*
+ * If `--core' has been specified and if we have get/set rlimit,
+ * then try to set the core limit to the "hard" maximum before aborting.
+ */
+ if (HAVE_OPT(CORE)) {
+ struct rlimit rlim;
+ getrlimit(RLIMIT_CORE, &rlim);
+ rlim.rlim_cur = rlim.rlim_max;
+ setrlimit(RLIMIT_CORE, &rlim);
+ }
+#endif
+
+ exit_cleanup(EXIT_PCLOSE_NOWAIT);
+ abort();
+}
+
+
+/**
+ * catch_sig_and_bail catches signals we abend on. The "siglongjmp"
+ * goes back to the real "main()" procedure and it will call
+ * "cleanup_and_abort()", above.
+ *
+ * @param[in] sig the signal number
+ */
+static void
+catch_sig_and_bail(int sig)
+{
+ switch (processing_state) {
+ case PROC_STATE_ABORTING:
+ exit_code = AUTOGEN_EXIT_SIGNAL;
+
+ case PROC_STATE_DONE:
+ break;
+
+ default:
+ abendJumpSignal = sig;
+ exit_code = AUTOGEN_EXIT_SIGNAL;
+ siglongjmp(abendJumpEnv, sig);
+ }
+}
+
+
+/**
+ * ignore_signal is the handler for SIGCHLD. If we set it to default,
+ * it will kill us. If we set it to ignore, it will be inherited.
+ * Therefore, always in all programs set it to call a procedure.
+ * The "wait(3)" call will do magical things, but will not override SIGIGN.
+ *
+ * @param[in] sig the signal number
+ */
+static void
+ignore_signal(int sig)
+{
+ (void)sig;
+ return;
+}
+
+
+/**
+ * This is _always_ called for exit processing.
+ * This only returns if "exit(3C)" is called during option processing.
+ * We have no way of determining the correct exit code.
+ * (Requested version or help exits EXIT_SUCCESS. Option failures
+ * are EXIT_FAILURE. We cannot determine here.)
+ */
+static void
+done_check(void)
+{
+ /*
+ * There are contexts wherein this function can get called twice.
+ */
+ {
+ static int done_check_done = 0;
+ if (done_check_done) {
+ if (OPT_VALUE_TRACE > TRACE_NOTHING)
+ fprintf(trace_fp, DONE_CHECK_REDONE);
+ return;
+ }
+ done_check_done = 1;
+ }
+
+ switch (processing_state) {
+ case PROC_STATE_EMITTING:
+ case PROC_STATE_INCLUDING:
+ /*
+ * A library (viz., Guile) procedure has called exit(3C). The
+ * AutoGen abort paths all set processing_state to PROC_STATE_ABORTING.
+ */
+ if (*oops_pfx != NUL) {
+ /*
+ * Emit the CGI page header for an error message. We will rewind
+ * stderr and write the contents to stdout momentarily.
+ */
+ fputs(oops_pfx, stdout);
+ oops_pfx = zNil;
+ }
+
+ if (OPT_VALUE_TRACE > TRACE_NOTHING)
+ scm_backtrace();
+
+ fprintf(stderr, SCHEME_EVAL_ERR_FMT, current_tpl->td_file,
+ cur_macro->md_line);
+
+ /*
+ * We got here because someone called exit early.
+ */
+ do {
+#ifndef DEBUG_ENABLED
+ out_close(false);
+#else
+ out_close(true);
+#endif
+ } while (cur_fpstack->stk_prev != NULL);
+ exit_code = AUTOGEN_EXIT_BAD_TEMPLATE;
+ break; /* continue failure exit */
+
+ case PROC_STATE_LOAD_DEFS:
+ exit_code = AUTOGEN_EXIT_BAD_DEFINITIONS;
+ /* FALLTHROUGH */
+
+ default:
+ fprintf(stderr, AG_ABEND_STATE_FMT, state_names[processing_state]);
+ goto autogen_aborts;
+
+ case PROC_STATE_ABORTING:
+ exit_code = AUTOGEN_EXIT_BAD_TEMPLATE;
+
+ autogen_aborts:
+ if (*oops_pfx != NUL) {
+ /*
+ * Emit the CGI page header for an error message. We will rewind
+ * stderr and write the contents to stdout momentarily.
+ */
+ fputs(oops_pfx, stdout);
+ oops_pfx = zNil;
+ }
+ break; /* continue failure exit */
+
+ case PROC_STATE_OPTIONS:
+ /* Exiting in option processing state is verbose enough */
+ break;
+
+ case PROC_STATE_DONE:
+ break; /* continue normal exit */
+ }
+
+ if (last_scm_cmd != NULL)
+ fprintf(stderr, GUILE_CMD_FAIL_FMT, last_scm_cmd);
+
+ /*
+ * IF we diverted stderr, then now is the time to copy the text to stdout.
+ * This is done for CGI mode wherein we produce an error page in case of
+ * an error, but otherwise discard stderr.
+ */
+ if (cgi_stderr != NULL) {
+ do {
+ long pos = ftell(stderr);
+ char* pz;
+
+ /*
+ * Don't bother with the overhead if there is no work to do.
+ */
+ if (pos <= 0)
+ break;
+ pz = AGALOC(pos, "stderr redir");
+ rewind(stderr);
+ fread( pz, (size_t)1, (size_t)pos, stderr);
+ fwrite(pz, (size_t)1, (size_t)pos, stdout);
+ AGFREE(pz);
+ } while (false);
+
+ unlink(cgi_stderr);
+ AGFREE(cgi_stderr);
+ cgi_stderr = NULL;
+ }
+
+ ag_scribble_deinit();
+
+ if (OPT_VALUE_TRACE > TRACE_NOTHING)
+ fprintf(trace_fp, DONE_CHECK_DONE);
+
+ exit_cleanup(EXIT_PCLOSE_WAIT);
+
+ /*
+ * When processing options, return so that the option processing exit code
+ * is used. Otherwise, terminate execution now with the decided upon
+ * exit code. (Always EXIT_FAILURE, unless this was called at the end
+ * of inner_main().)
+ */
+ if (processing_state != PROC_STATE_OPTIONS)
+ _exit(exit_code);
+}
+
+
+LOCAL void
+ag_abend_at(char const * pzMsg
+#ifdef DEBUG_ENABLED
+ , char const * pzFile, int line
+#endif
+ )
+{
+ if (*oops_pfx != NUL) {
+ fputs(oops_pfx, stderr);
+ oops_pfx = zNil;
+ }
+
+#ifdef DEBUG_ENABLED
+ fprintf(stderr, "Giving up in %s line %d\n", pzFile, line);
+#endif
+
+ if ((processing_state >= PROC_STATE_LIB_LOAD) && (current_tpl != NULL)) {
+ int l_no = (cur_macro == NULL) ? -1 : cur_macro->md_line;
+ fprintf(stderr, ERROR_IN_TPL_FMT, current_tpl->td_file, l_no);
+ }
+ fputs(pzMsg, stderr);
+ pzMsg += strlen(pzMsg);
+ if (pzMsg[-1] != NL)
+ fputc(NL, stderr);
+
+ {
+ proc_state_t oldState = processing_state;
+ processing_state = PROC_STATE_ABORTING;
+
+ switch (oldState) {
+ case PROC_STATE_EMITTING:
+ case PROC_STATE_INCLUDING:
+ case PROC_STATE_CLEANUP:
+ longjmp(abort_jmp_buf, FAILURE);
+ /* NOTREACHED */
+ default:
+ exit(EXIT_FAILURE);
+ /* NOTREACHED */
+ }
+ }
+}
+
+
+static void
+setup_signals(sighandler_proc_t * chldHandler,
+ sighandler_proc_t * abrtHandler,
+ sighandler_proc_t * dfltHandler)
+{
+ struct sigaction sa;
+ int sigNo = 1;
+#ifdef SIGRTMIN
+ const int maxSig = SIGRTMIN-1;
+#else
+ const int maxSig = NSIG;
+#endif
+
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+
+ do {
+ switch (sigNo) {
+ /*
+ * Signal handling for SIGCHLD needs to be ignored. Do *NOT* use
+ * SIG_IGN to do this. That gets inherited by programs that need
+ * to be able to use wait(2) calls. At the same time, we don't
+ * want our children to become zombies. We may run out of zombie
+ * space. Therefore, set the handler to an empty procedure.
+ * POSIX oversight. Allowed to be fixed for next POSIX rev, tho
+ * it is "optional" to reset SIGCHLD on exec(2).
+ */
+#ifndef SIGCHLD
+# define SIGCHLD SIGCLD
+#endif
+ case SIGCHLD:
+ sa.sa_handler = chldHandler;
+ break;
+
+ case SIGABRT:
+ sa.sa_handler = abrtHandler;
+ break;
+
+ /*
+ * Signals we must leave alone.
+ */
+ case SIGKILL:
+ case SIGSTOP:
+
+ /*
+ * Signals we choose to leave alone.
+ */
+#ifdef SIGTSTP
+ case SIGTSTP:
+#endif
+ continue;
+
+#if defined(DEBUG_ENABLED)
+ case SIGBUS:
+ case SIGSEGV:
+ /*
+ * While debugging AutoGen, we want seg faults to happen and
+ * trigger core dumps. Make sure this happens.
+ */
+ sa.sa_handler = SIG_DFL;
+ break;
+#endif
+
+ /*
+ * Signals to ignore with SIG_IGN.
+ */
+ case 0: /* cannot happen, but the following might not be defined */
+#ifdef SIGWINCH
+ case SIGWINCH:
+#endif
+#ifdef SIGTTIN
+ case SIGTTIN: /* tty input */
+#endif
+#ifdef SIGTTOU
+ case SIGTTOU: /* tty output */
+#endif
+ sa.sa_handler = SIG_IGN;
+ break;
+
+#ifdef DAEMON_ENABLED
+# error DAEMON-ization of AutoGen is not ready for prime time
+ Choke Me.
+ case SIGHUP:
+ if (HAVE_OPT(DAEMON)) {
+ sa.sa_handler = handleSighup;
+ break;
+ }
+ /* FALLTHROUGH */
+#endif
+
+ default:
+ sa.sa_handler = dfltHandler;
+ }
+ sigaction(sigNo, &sa, NULL);
+ } while (++sigNo < maxSig);
+}
+
+#ifndef HAVE_STRFTIME
+# include <compat/strftime.c>
+#endif
+
+#ifndef HAVE_STRSIGNAL
+# include <compat/strsignal.c>
+#endif
+
+LOCAL void *
+ao_malloc (size_t sz)
+{
+ void * res = malloc(sz);
+ if (res == NULL) {
+ fprintf(stderr, MALLOC_FAIL_FMT, sz);
+ exit(EXIT_FAILURE);
+ }
+ return res;
+}
+
+
+LOCAL void *
+ao_realloc (void *p, size_t sz)
+{
+ void * res = (p == NULL) ? malloc(sz) : realloc(p, sz);
+ if (res == NULL) {
+ fprintf(stderr, REALLOC_FAIL_FMT, sz, p);
+ exit(EXIT_FAILURE);
+ }
+ return res;
+}
+
+LOCAL char *
+ao_strdup (char const * str)
+{
+ char * res = strdup(str);
+ if (res == NULL) {
+ fprintf(stderr, STRDUP_FAIL_FMT, (int)strlen(str));
+ exit(EXIT_FAILURE);
+ }
+ return res;
+}
+
+#ifdef __GNUC__
+ void ignore_vars(void);
+ void ignore_vars(void) {
+ (void)option_load_mode, (void)program_pkgdatadir;
+ }
+#endif
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/autogen.c */