/*			 Setup Instatllation Program
 *		      (C) Copyright 1987 by Microsoft
 *			   Written By Steven Zeck
 *
 *  This a script based installation aid for installing produces onto a
 *  users hard disk.  Major componets include:
 *
 *	- script based, no hard coding
 *	- batch like commands and conditional statements
 *	- full screen oriented user input/output
 *	- built in data compression/expansion
 *	- chainable scripts
 *************************************************************************/

#include "core.h"
#include <direct.h>
#include <stdlib.h>
#include <crtapi.h>


KY  theKY[] = { 	/* Key word table, don't change order */

    "if",               doIf,       nilKYF,
    "else",		NIL,	    returnKYF,
    "endif",            NIL,        returnKYF,
    "set",		doSet,	    nilKYF,
    "echo",		doEcho,     nilKYF,
    "exit",             doExit,     nilKYF,
    "goto",             doGoto,     nilKYF,
    "copy",		doCopy,     nilKYF,
    "copyto",           doCopyTo,   nilKYF,
    "md",		doMd,	    nilKYF,
    "cd",		doCd,	    nilKYF,
    "exec",		doExec,     nilKYF,
    "cls",		doCls,	    nilKYF,
    "dialog",		doDialog,   nilKYF,
    "call",		doCall	,   nilKYF,
    "appendfile",	doAppendFile,nilKYF,
    NIL
};

xx() {return(_nheapchk());}


/*  main - handles the top level initialization
 *
 * Inputs
 *	command line arguments
 * Returns
 *	the exit status
 *
 ****************************************************************************/
int main(argc, argv)
int argc;
char **argv;
{
    pSZ pFileName = NIL;
    pSZ pSelfName, p;

    pCopyBuff  = fmemory(COPYBUF_MAX*2);
    pCopyBuff2 = pCopyBuff + COPYBUF_MAX;

    pSTCur = &rootST;
    getCrt();
    getEnviorment();

    pSelfName = *argv++;
    argc--;

    while (argc > 0) {
	if (**argv == '-'){
	    for(p = &(argv[0][1]); *p; p++)

		switch(*p){

#ifndef RUNONLY
		  case 'd':
		    (*pDebug)++;
		    break;
#endif
		  case 'f':
		    argv++; argc--;
		    pFileName = *argv;
		    break;

		}
	}
	else
	    dispatcher(sprintf(lineBuff, "set arg%d = \"%s\"",
		       ++pArgCount->v.val, *argv));

	argv++;
	argc--;
    }

    dispatcher("cls");
    dispatcher("echo \"Microsoft Setup Utility - Version 1.10\"");

    if (! pFileName) {

	/* first look in the current directory for setup, then in Drive that we
	 * where run in A: */

	pFileName = "A:\\setup";
	if (_osmajor > 2)
	    pFileName[0] = *pSelfName;

	// if setup.sus is in the current directory use it

	if (! (fileAttrFet("setup.sus") & ATTR_NOTF))
	    pFileName += 3;

    }


    // set the source drive & path to be the same as where setup.sus is

    if (pFileName[1] != ':')
	pCopyDrive->v.pVal = newStr(RpcGetcwd(lineBuff, sizeof(lineBuff)));
    else
	pCopyDrive->v.pVal[0] = pFileName[0];

    runScript(pFileName);

#ifndef RUNONLY

    if (*pDebug)
	dumpDict(rootST.pMySY);
#endif

    resetCrt();
    return (*pStatus);
}


/*  runScript - run a script file
 *
 * Inputs
 *	Name of the script file to run
 *	wherther the dictionary should be cloned
 * Returns
 *
 * Create a new state instance run the given script file
 ****************************************************************************/
int runScript(pFileName)
pSZ pFileName;
{
    ST *pST;
    SY *pSY;

    pST = (ST *)memory(sizeof(ST));
    pST->pSTPrev = pSTCur;
    pSTCur = pST;
    pST->fileName = pFileName;

    loadFile(pST);		/* Read the file into memory */
    volIDFet();

    setjmp(pST->rootLevel);
    runFile(FALSE);		/* excute file */

    /* go through and release all the resources held by the file,
     * all the symbols created, the file buffer, and the ST itself */

#ifndef RUNONLY

    if (*pDebug)
	dumpDict(pST->pMySY);
#endif

    for (pSY = pST->pMySY; pSY; pSY = pSY->pSYNext) {

	if (pSY->type == charSYT)
	    free(pSY->v.pVal);

	free(pSY);
    }
    _ffree(pST->pBuff);
    free(pST);

    pSTCur = pST->pSTPrev;
}


/*  runFile/dispatcher - parse the first token and dispatch to the action routine
 *
 * Inputs
 *	Wherether your in a false conditional
 * Returns
 *
 ****************************************************************************/
void runFile (fFalseCon)
Bool fFalseCon;
{
    Bool fFalseLast;

    fFalseLast = pSTCur->fInFalse;
    pSTCur->fInFalse = fFalseCon;

    while(getline()) {
	if (*lineBuff && !dispatcher(NIL))
	    break;
    }
    pSTCur->fInFalse = fFalseLast;
}

#pragma check_stack (on)

Bool pascal dispatcher (aLine)
pSZ aLine;
{
    register KY *pKY;
    pSZ pLineSave;
    int tokenSave;
    char tokenValSave[TOKEN_MAX];

    if (aLine) {
	pLineSave = pLineCur;
	pLineCur = aLine;
	tokenSave = tokenPeek;
	tokenPeek = NIL;
	strcpy(tokenValSave, tokenVal);
    }

#ifndef RUNONLY

    if (*pDebug) {
	outStat("%d: %s", pSTCur->lineCur, pLineCur);

	if (*pDebug > 1)
	    RpcGetch();
    }

    if (_nheapchk() < -2 )
	terminate("Corrupt heap");

#endif

    if (getoken() != labelTT) {

	if (*tokenVal != ':' && *tokenVal != ';')
	    synError("Bady formed line: %s\n", pLineCur);

	return(TRUE);
    }
    for (pKY = theKY; pKY->name; pKY++)
	if (memcmp(tokenVal, pKY->name, cbToken) == 0)
	    goto found;

    synError("Unknow keyword: %s\n", tokenVal);
    return(TRUE);

found:

    pKYCur = pKY;
    if (pKY->flags == returnKYF)
	return(FALSE);

    if (!pSTCur->fInFalse || pKY == PIFKY)
	(pKY->pAction)();

    if (aLine) {
	pLineCur = pLineSave;
	tokenPeek = tokenSave;
	strcpy(tokenVal, tokenValSave);
    }

    return(TRUE);
}
#pragma check_stack (off)


/*  terminate - terminate processing on an error
 *
 * Inputs
 *	Descriptive string for reason of termination
 *
 *  Never returns, cleans up and exits to OS
 ****************************************************************************/
void terminate (reason, a1, a2)
pSZ reason;
int a1, a2;
{
    resetCrt();
    printf("Setup terminated:", reason);
    printf(reason, a1, a2);
    exit(1);
}