/* * autogen.c * $Id: autogen.c,v 4.23 2007/07/28 17:59:16 bkorb Exp $ * * Time-stamp: "2007-07-22 09:00:32 bkorb" * Last Committed: $Date: 2007/07/28 17:59:16 $ * * This is the main routine for autogen. * * This file is part of AutoGen. * AutoGen copyright (c) 1992-2007 by 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 . */ tSCC zClientInput[] = "client-input"; #define _State_(n) #n, tSCC* apzStateName[] = { STATE_TABLE }; #undef _State_ static sigjmp_buf abendJumpEnv; static int abendJumpSignal = 0; typedef void (sighandler_proc_t)( int sig ); static sighandler_proc_t ignoreSignal, abendSignal; /* = = = START-STATIC-FORWARD = = = */ /* static forward declarations maintained by :mkfwd */ static void inner_main( int argc, char** argv ); static void signalExit( int sig ); static void abendSignal( int sig ); static void ignoreSignal( int sig ); static void doneCheck( void ); static void signalSetup( sighandler_proc_t* chldHandler, sighandler_proc_t* dfltHandler ); /* = = = END-STATIC-FORWARD = = = */ static void inner_main( int argc, char** argv ) { initialize( argc, argv ); procState = PROC_STATE_LOAD_DEFS; ag_scmStrings_init(); readDefines(); /* * Activate the AutoGen specific Scheme functions. * Then load, process and unload the main template */ procState = PROC_STATE_LOAD_TPL; { tTemplate* pTF = loadTemplate(pzTemplFileName, NULL); procState = PROC_STATE_EMITTING; processTemplate( pTF ); procState = PROC_STATE_CLEANUP; cleanup( pTF ); } procState = PROC_STATE_DONE; exit( EXIT_SUCCESS ); } int main( int argc, char** argv ) { pfTrace = stderr; /* * IF sigsetjmp returns with a signal number, * THEN you cannot capture the value portably. So, the jumper has * stashed it for use now. */ if (sigsetjmp( abendJumpEnv, 0 ) != 0) signalExit( abendJumpSignal ); signalSetup( ignoreSignal, abendSignal ); #if GUILE_VERSION >= 107000 if (getenv( "GUILE_WARN_DEPRECATED" ) == NULL) putenv( (char*)(void*)"GUILE_WARN_DEPRECATED=no" ); #endif gh_enter( argc, argv, inner_main ); /* NOT REACHED */ return EXIT_FAILURE; } static void signalExit( int sig ) { tSCC zErr[] = "AutoGen aborting on signal %d (%s) in state %s\n"; if (*pzOopsPrefix != NUL) { fputs( pzOopsPrefix, stderr ); pzOopsPrefix = zNil; } fprintf( stderr, zErr, sig, strsignal( sig ), ((unsigned)procState <= PROC_STATE_DONE) ? apzStateName[ procState ] : "** BOGUS **" ); fflush( stderr ); fflush( stdout ); if (pfTrace != stderr ) fflush( pfTrace ); if (procState == PROC_STATE_ABORTING) _exit( sig + 128 ); procState = PROC_STATE_ABORTING; /* * 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 (pCurTemplate != NULL) { tSCC zAt[] = "processing template %s\n" " on line %d\n" " for function %s (%d)\n"; int line; teFuncType fnCd; tCC* pzFn; tCC* pzFl; if ( pCurMacro == NULL ) { pzFn = "pseudo-macro"; line = 0; fnCd = -1; } else { teFuncType f = (pCurMacro->funcCode > FUNC_CT) ? FTYP_SELECT : pCurMacro->funcCode; pzFn = apzFuncNames[ f ]; line = pCurMacro->lineNo; fnCd = pCurMacro->funcCode; } pzFl = pCurTemplate->pzTplFile; if (pzFl == NULL) pzFl = "NULL file name"; fprintf( stderr, zAt, pzFl, line, pzFn, fnCd ); } signalSetup( SIG_DFL, SIG_DFL ); abort(); } /* * abendSignal catches signals we abend on. The "siglongjmp" goes back * to the real "main()" procedure and it will call "signalExit()", above. */ static void abendSignal( int sig ) { abendJumpSignal = sig; siglongjmp( abendJumpEnv, sig ); } /* * ignoreSignal 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. */ static void ignoreSignal( int sig ) { #ifdef DEBUG_ENABLED fprintf( pfTrace, "Ignored signal %d (%s)\n", sig, strsignal( sig )); #endif return; } /* * This is called during normal exit processing. */ static void doneCheck( void ) { tSCC zErr[] = "Scheme evaluation error. AutoGen ABEND-ing in template\n" "\t%s on line %d\n"; #if GUILE_VERSION >= 107000 int exit_code = EXIT_SUCCESS; #endif #ifdef SHELL_ENABLED ag_scm_c_eval_string_from_file_line( "(if (> (string-length shell-cleanup) 0)" " (shell shell-cleanup) )", __FILE__, __LINE__ ); closeServer(); #endif fflush( stdout ); fflush( stderr ); if (pfTrace != stderr ) { if (* OPT_ARG( TRACE_OUT ) == '|') { int status; pclose( pfTrace ); while (wait( &status ) > 0) ; } else fclose( pfTrace ); } switch (procState) { case PROC_STATE_EMITTING: case PROC_STATE_INCLUDING: /* * A library (viz., Guile) procedure has called exit(3C). * The AutoGen abort paths all set procState to PROC_STATE_ABORTING. */ if (*pzOopsPrefix != NUL) { /* * Emit the CGI page header for an error message. We will rewind * stderr and write the contents to stdout momentarily. */ fputs( pzOopsPrefix, stdout ); pzOopsPrefix = zNil; } if (OPT_VALUE_TRACE > TRACE_NOTHING) scm_backtrace(); fprintf( stderr, zErr, pCurTemplate->pzTplFile, pCurMacro->lineNo ); /* * We got here because someone called exit early. */ do { #ifndef DEBUG_ENABLED closeOutput( AG_FALSE ); #else closeOutput( AG_TRUE ); #endif } while (pCurFp->pPrev != NULL); #if GUILE_VERSION >= 107000 exit_code = EXIT_FAILURE; #endif break; /* continue failure exit */ default: fprintf( stderr, "ABEND-ing in %s state\n", apzStateName[procState] ); /* FALLTHROUGH */ case PROC_STATE_ABORTING: if (*pzOopsPrefix != NUL) { /* * Emit the CGI page header for an error message. We will rewind * stderr and write the contents to stdout momentarily. */ fputs( pzOopsPrefix, stdout ); pzOopsPrefix = zNil; } break; /* continue failure exit */ case PROC_STATE_OPTIONS: /* Exiting in option processing state is verbose enough */ case PROC_STATE_DONE: break; /* continue normal exit */ } if (pzLastScheme != NULL) { tSCC zGuileFail[] = "Failing Guile command: = = = = =\n\n%s\n\n" "=================================\n"; fprintf( stderr, zGuileFail, pzLastScheme ); } /* * IF we diverted stderr, then now is the time to copy the text to stdout. */ if (pzTmpStderr == NULL) { #if GUILE_VERSION >= 107000 if (exit_code != EXIT_SUCCESS) _exit( exit_code ); #endif return; } 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 redirected text" ); rewind( stderr ); fread( pz, (size_t)1, (size_t)pos, stderr ); fwrite( pz, (size_t)1, (size_t)pos, stdout ); AGFREE( pz ); } while (0); fclose( stderr ); unlink( pzTmpStderr ); AGFREE( pzTmpStderr ); pzTmpStderr = NULL; #if GUILE_VERSION >= 107000 if (exit_code != EXIT_SUCCESS) _exit( exit_code ); #endif } LOCAL void ag_abend_at( tCC* pzMsg #ifdef DEBUG_ENABLED , tCC* pzFile, int line #endif ) { if (*pzOopsPrefix != NUL) { fputs( pzOopsPrefix, stderr ); pzOopsPrefix = zNil; } #ifdef DEBUG_ENABLED fprintf( stderr, "Giving up in %s line %d\n", pzFile, line ); #endif if ((procState >= PROC_STATE_LIB_LOAD) && (pCurTemplate != NULL)) { int line = (pCurMacro == NULL) ? -1 : pCurMacro->lineNo; fprintf( stderr, "Error in template %s, line %d\n\t", pCurTemplate->pzTplFile, line ); } fputs( pzMsg, stderr ); pzMsg += strlen( pzMsg ); if (pzMsg[-1] != '\n') fputc( '\n', stderr ); { teProcState oldState = procState; procState = PROC_STATE_ABORTING; switch (oldState) { case PROC_STATE_EMITTING: case PROC_STATE_INCLUDING: case PROC_STATE_CLEANUP: longjmp( fileAbort, FAILURE ); /* NOTREACHED */ default: exit(EXIT_FAILURE); /* NOTREACHED */ } } } static void signalSetup( sighandler_proc_t* chldHandler, sighandler_proc_t* dfltHandler ) { struct sigaction sa; int sigNo = 1; #ifdef SIGRTMIN const int maxSig = SIGRTMIN-1; #else const int maxSig = NSIG; #endif atexit( doneCheck ); 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; /* * 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 #endif #ifndef HAVE_STRSIGNAL # include #endif LOCAL void * ao_malloc (size_t sz) { void * res = malloc(sz); if (res == NULL) { fprintf(stderr, "malloc of %zd bytes failed\n", sz); exit( EXIT_FAILURE ); } return res; } LOCAL void * ao_realloc (void *p, size_t sz) { void * res = realloc(p, sz); if (res == NULL) { fprintf(stderr, "realloc of %zd bytes at 0x%p failed\n", sz, p); exit( EXIT_FAILURE ); } return res; } LOCAL void ao_free (void *p) { if (p != NULL) free(p); } LOCAL char * ao_strdup (char const * str) { char * res = strdup(str); if (res == NULL) { fprintf(stderr, "strdup of %d byte string failed\n", (int)strlen(str)); exit( EXIT_FAILURE ); } return res; } /* * Local Variables: * mode: C * c-file-style: "stroustrup" * indent-tabs-mode: nil * End: * end of agen5/autogen.c */