
/*
 *  autogen.c
 *  $Id: autogen.c,v 2.14 1999/07/07 19:30:52 bkorb Exp $
 *  This is the main routine for autogen.
 */

/*
 *  AutoGen copyright 1992-1999 Bruce Korb
 *
 *  AutoGen is free software.
 *  You may redistribute it and/or modify it under the terms of the
 *  GNU General Public License, as published by the Free Software
 *  Foundation; either version 2, 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 AutoGen.  See the file "COPYING".  If not,
 *  write to:  The Free Software Foundation, Inc.,
 *             59 Temple Place - Suite 330,
 *             Boston,  MA  02111-1307, USA.
 */

#include <time.h>
#include <utime.h>
#include <signal.h>

#ifdef MEMDEBUG
#  include "snprintfv/mem.h"
   static void finalMemCheck( void );
#endif

#define DEFINING
#include "autogen.h"

/*
 *  AutoGen is broken up into 4 major sections:
 *
 *  1.  Definition parsing
 *      agGetDef.c read in definitions, call yyparse then order def tree
 *      agParse.y  Defines the grammar and productions.  It calls yylex() in
 *      agLex.c    tokenizes the input and processes "C" style directives
 *      agDirect.c handles all lex directives (e.g. "#ifdef", etc.)
 *      agReduce.c contains the routines called by the productions
 *
 *  2.  Miscelleaneous support routines
 *      autogen.c  The main routine
 *      agPrint.c  provides an "sprintf()" routine that handles page faults
 *      agShell.c  submits a command to a shell interpreter and puts its
 *                 stdout text into an allocated string.
 *      agTempl.c  Searches for the template file, determines by examination
 *                 what the macro start/stop markers are, finds the
 *                 suffix list to be generated from the special "autogen"
 *                 macro, and loads the actual template before each
 *                 suffix pass over the data.
 *                 contains the base text gen routine "generateBlock()"
 *
 *  3.  Template macro functions
 *      agFunc.c    contains the function dispatch table and simple functions
 *      agFunIf.c   handles the "IF" macro function
 *      agFunCase.c handles the "CASE" macro function
 *      agFunFor.c  handles the "FOR" macro function
 *      agFunOut.c  handles the "OUTPUT" macro function
 *
 *  4.  Template expression functions
 *      agEval.c    evaluates an expression
 *      agExpr.c    handles all the expression functions
 */
STATIC sigjmp_buf abendJumpEnv;


    STATIC void
abendSignal( int sig )
{
    fprintf( stderr, "AutoGen aborting on signal %d (%s)\n",
             sig, strsignal( sig ));
    siglongjmp( abendJumpEnv, sig );
}


    int
main( int argc, char** argv )
{
    if (sigsetjmp( abendJumpEnv, 0 ) != 0)
        exit( EXIT_FAILURE );

#ifdef MEMDEBUG
    atexit( &finalMemCheck );
    /* Set the snprintfv library's memory management function pointers
       to use AutoGen's debugging malloc. */
    snv_malloc  = (snv_pointer(*)SNV_PARAMS((size_t)))ag_alloc;
    snv_realloc = (snv_pointer(*)SNV_PARAMS((snv_pointer, size_t)))ag_realloc;
    snv_free    = (void(*)SNV_PARAMS((snv_pointer)))ag_free;
#endif

    {
        struct sigaction  sa;
        int    sigNo = 0;

        sa.sa_handler = abendSignal;
        sa.sa_flags   = 0;
        sigemptyset( &sa.sa_mask );

        do  {
            sigaction( ++sigNo,  &sa, (struct sigaction*)NULL );
        } while (sigNo < NSIG);

        sa.sa_handler = SIG_IGN;
        sigaction( SIGCHLD,  &sa, (struct sigaction*)NULL );
    }

    procState = PROC_STATE_OPTIONS;

    /*
     *  Advance the argument counters and pointers past any
     *  command line options
     */
    {
        int  optCt = optionProcess( &AutoGenOptions, argc, argv );

        /*
         *  Make sure we have a source file, even if it is "-" (stdin)
         */
        argc -= optCt;
        switch (argc) {
        case 1:
            pzDefineFileName = *(argv + optCt);
            break;

        case 0:
            pzDefineFileName = "-";
            break;

        default:
        {
            tSCC zOnlyOneSrc[] = "%s ERROR:  Too many definition files\n";
            fprintf( stderr, zOnlyOneSrc, pzProg );
            USAGE( EXIT_FAILURE );
        }
        }
    }

    strequate( OPT_ARG( EQUATE ));

    /*
     *  IF we have some defines to put in our environment, ...
     */
    if (HAVE_OPT( DEFINE )) {
        int        ct  = STACKCT_OPT(  DEFINE );
        char**     ppz = STACKLST_OPT( DEFINE );

        do  {
            char* pz = *(ppz++);
            /*
             *  IF there is no associated value,
             *  THEN append a NUL value to it.
             */
            if (strchr( pz, '=' ) == (char*)NULL) {
                size_t siz = strlen( pz )+3;
                char*  p   = AGALOC( siz );
                strcpy( p, pz );
                strcat( p, "=1" );
                pz = p;
            }

            /*
             *  Now put it in the environment
             */
            putenv( pz );
        } while (--ct > 0);
    }

    procState = PROC_STATE_LOAD_DEFS;
    readDefines( pzDefineFileName );

    procState = PROC_STATE_LOAD_TPL;

    /*
     *  Load, process and unload the primary (likely only) template
     */
    {
        tTemplFile* pTF = loadTemplate( pzTemplFileName );

        procState = PROC_STATE_EMITTING;
        processTemplate( pTF );
        unloadTemplate( pTF );
    }

    unloadDefs();

    return EXIT_SUCCESS;
}


    void
upcase( char* pz, teCaseType  caseMode )
{
    switch (caseMode) {
    case CC_Cap:
        while (isspace( *pz )) pz++;
        if (*pz != NUL)
            PTRUP(pz);
        /*FALLTHROUGH*/

    case CC_lower:
        while (isspace( *pz )) pz++;
        while ((*pz != NUL) && (isalnum( *pz ) || (*pz == '_')))
            PTRDN(pz);
        break;

    default:
    case CC_UPPER:
        while (isspace( *pz )) pz++;
        while ((*pz != NUL) && (isalnum( *pz ) || (*pz == '_')))
            PTRUP(pz);
        break;

    case CC_ALL_UP:
        while (*pz != NUL)
            PTRUP(pz);
        break;

    case CC_all_low:
        while (*pz != NUL)
            PTRDN(pz);
        break;

    case CC_All_Cap:
    {
        ag_bool  doCap = AG_TRUE;

        while (*pz != NUL) {
            if (isalnum( *pz ) && (*pz != '_')) {
                if (doCap) {
                    PTRUP(pz);
                    doCap = AG_FALSE;
                } else
                    PTRDN(pz);
            } else {
                pz++;
                doCap = AG_TRUE;
            }
        }
        break;
    }
    }
}


    const char*
getDefine( const char* pzDefName )
{
    char**  ppz;
    int     ct;
    if (HAVE_OPT( DEFINE )) {
        ct  = STACKCT_OPT(  DEFINE );
        ppz = STACKLST_OPT( DEFINE );

        while (ct-- > 0) {
            char* pz   = *(ppz++);
            char* pzEq = strchr( pz, '=' );
            int   res;

            if (pzEq != (char*)NULL)
                *pzEq = NUL;

            res = strcmp( pzDefName, pz );
            if (pzEq != (char*)NULL)
                *pzEq = '=';

            if (res == 0)
                return (pzEq != (char*)NULL) ? pzEq+1 : "";
        }
    }
    return getenv( pzDefName );
}


#ifdef MEMDEBUG
STATIC tMemMgmt memHead = { &memHead, &memHead, (char*)NULL, "ROOT" };
#define CHECK_CT 128
#define SPARE    128



    void*
ag_alloc( size_t sz, const char* pz )
{
    size_t    asz = sz + sizeof( tMemMgmt ) + CHECK_CT + SPARE;
    tMemMgmt* p = (tMemMgmt*)malloc( asz & ~0x3F );

    if (p == (tMemMgmt*)NULL)
        return (void*)NULL;

    /*
     *  Link new entry to end of chain
     */
    p->pPrev        = memHead.pPrev;
    p->pPrev->pNext = p;
    memHead.pPrev   = p;
    p->pNext        = &memHead;

    p->pEnd = ((char*)(p+1)) + sz;
    memset( (void*)p->pEnd, '~', CHECK_CT );
    p->pzWhence = pz;

    return (void*)(p+1);
}


    STATIC void
checkMem( tMemMgmt* pMM )
{
    char* p  = pMM->pEnd;
    int   ct = CHECK_CT;

    do  {
        if (*(p++) != '~') {
            fprintf( stderr, "Stray pointer %d bytes after %d-byte %s end\n",
                     CHECK_CT - ct, pMM->pEnd - (char*)(pMM+1),
                     pMM->pzWhence );
            p = (char*)NULL;
            fclose( stderr );
            fclose( stdout );
            *p = '~';
            exit( EXIT_FAILURE );
        }
    } while (--ct > 0);
}


    void*
ag_realloc( void* p, size_t sz, const char* pz )
{
    size_t      asz = sz + sizeof( tMemMgmt ) + CHECK_CT + SPARE;
    tMemMgmt*   np  = ((tMemMgmt*)p)-1;
    tMemMgmt    sv  = *np;

    checkMem( np );
    np = (tMemMgmt*)realloc( (void*)np, asz & ~0x3F );

    if (np == (tMemMgmt*)NULL) {
        /*
         *  Unlink old entry
         */
        sv.pPrev->pNext = sv.pNext;
        sv.pNext->pPrev = sv.pPrev;
        return (void*)NULL;
    }

    /*
     *  Link other entries to new allocation
     */
    np->pPrev->pNext = np;
    np->pNext->pPrev = np;

    np->pEnd = ((char*)(np+1)) + sz;
    memset( (void*)np->pEnd, '~', CHECK_CT );
    np->pzWhence = pz;

    return (void*)(np+1);
}


    void
ag_free( void* p )
{
    tMemMgmt*   np  = ((tMemMgmt*)p)-1;
    checkMem( np );

    /*
     *  Unlink old entry
     */
    np->pPrev->pNext = np->pNext;
    np->pNext->pPrev = np->pPrev;
    free( (void*)np );
}


    STATIC void
finalMemCheck( void )
{
    tMemMgmt*  pMM = memHead.pNext;

    fputs( "\nResidual allocation list:\n", stderr );
    while (pMM != &memHead) {
        checkMem( pMM );
        fprintf( stderr, "%12d bytes from %s\n",
                 pMM->pEnd - (char*)(pMM+1), pMM->pzWhence );
        pMM = pMM->pNext;
    }
}


    char*
dupString( const char* pz, const char* pzDupFrom )
{
    char*   pzRes;
    size_t  len = strlen( pz )+1;

    /*
     *  There are some systems out there where autogen is
     *  broken if "strdup" is allowed to duplicate strings
     *  smaller than 32 bytes.  This ensures that we work.
     *  We also round up everything up to 32 bytes.
     */
    if (len < 0x20)
        len = 0x20;
    else len = (len + 0x20) & ~0x1F;

    pzRes = ag_alloc( len, pzDupFrom );

    if (pzRes == (char*)NULL) {
        fprintf( stderr, zAllocErr, pzProg,
                 len, "strdup" );
        LOAD_ABORT;
    }

    strcpy( pzRes, pz );
    return pzRes;
}
#endif
/* end of autogen.c */
