/*** NMAKE.C - main module *****************************************************
*
*	Copyright (c) 1988-1990, Microsoft Corporation.  All rights reserved.
*
* Purpose:
*  This is the main module of nmake
*
* Revision History:
*  01-Feb-1994 HV Move messages to external file.
*  15-Nov-1993 JdR Major speed improvements
*  15-Oct-1993 HV Use tchar.h instead of mbstring.h directly, change STR*() to _ftcs*()
*  10-May-1993 HV Add include file mbstring.h
*                 Change the str* functions to STR*
*  26-Mar-1992 HV Rewrite filename() to use _splitpath()
*  06-Oct-1992 GBS Removed extern for _pgmptr
*  08-Jun-1992 SS add IDE feedback support
*  08-Jun-1992 SS Port to DOSX32
*  29-May-1990 SB Fix precedence of predefined inference rules ...
*  25-May-1990 SB Various fixes: 1> New inference rules for fortran and pascal;
*		  2> Resolving ties in timestamps in favour of building;
*		  3> error U1058 does not echo the filename anymore (ctrl-c
*		  caused filename and lineno to be dispalyed and this was
*		  trouble for PWB
*  01-May-1990 SB Add predefined rules and inference rules for FORTRAN
*  23-Apr-1990 SB Add predefined rules and inference rules for COBOL
*  20-Apr-1990 SB Don't show lineno for CTRL+C handler error
*  17-Apr-1990 SB Pass copy of makeflags to putenv() else freeing screws the
*		  DGROUP.
*  23-Feb-1990 SB chdir(MAKEDIR) to avoid returning to bad directory in DOS
*  02-Feb-1990 SB change fopen() to FILEOPEN()
*  31-Jan-1990 SB Postpone defineMAcro("MAKE") to doMAke(); Put freshly
*		  allocated strings in the macro table as freeStructures()
*		  free's the macroTable[]
*  24-Jan-1990 SB Add byte to call for sprintf() for "@del ..." case for /z
*  29-Dec-1989 SB ignore /Z when /T also specified
*  29-Dec-1989 SB nmake -? was giving error with TMP directory nonexistent
*  19-Dec-1989 SB nmake /z requests
*  14-Dec-1989 SB Trunc MAKEFLAGS averts GPF;Silently ignore /z in protect mode
*  12-Dec-1989 SB =c, =d for NMAKE /Z
*  08-Dec-1989 SB /NZ causes /N to override /Z; add #define TEST_RUNTIME stuff
*  01-Dec-1989 SB Contains an hack #ifdef'ed for Overlayed version
*  22-Nov-1989 SB Changed free() to FREE()
*  17-Nov-1989 SB defined INCL_NOPM; generate del commands to del temps
*  19-Oct-1989 SB ifdef SLASHK'ed stuff for -k
*  04-Sep-1989 SB echoing and redirection problem for -z fixed
*  17-Aug-1989 SB added #ifdef DEBUG's and error -nz incompatible
*  31-Jul-1989 SB Added check of return value -1 (error in spawning) for -help
*		  remove -z option help message
*  12-Jul-1989 SB readEnvironmentVars() was not using environ variable but an
*		  old pointer (envPtr) to it. In the meantime environ was
*		  getting updated. Safer to use environ directly.
*  29-Jun-1989 SB freeStructures() now deletes inlineFileList also
*  28-Jun-1989 SB changed deletion of inline files to end of mainmain() instead of
*		  doMake() to avoid deletion when a child make quits.
*  19-Jun-1989 SB modified .bas.obj to have ';' at end of cmd line
*  21-May-1989 SB freeRules() gets another parameter to avoid bogus messages
*  18-May-1989 SB change delScriptFiles() to do unlink instead of calling
*		  execLine. Thus, ^C handling is better now. No more hangs
*  15-May-1989 SB Added /make support; inherit /nologo
*  13-May-1989 SB Changed delScriptFiles(); added MAKEDIR; Added BASIC rules
*		  Changed chkPrecious()
*  01-May-1989 SB Changed FILEINFO to void *; OS/2 Version 1.2 support
*  17-Apr-1989 SB on -help spawn 'qh /u nmake' instead. rc = 3 signals error
*  14-Apr-1989 SB no 'del inlinefile' cmd for -n. -z now gives 'goto NmakeExit'
*		  CC and AS allocated of the heap and not from Data Segment
*  05-Apr-1989 SB made all funcs NEAR; Reqd to make all function calls NEAR
*  27-Mar-1989 SB Changed unlinkTmpFiles() to delScriptFiles()
*  10-Mar-1989 SB Removed blank link from PWB.SHL output
*  09-Mar-1989 SB changed param in call to findRule. fBuf is allocated on the
*		  heap in useDefaultMakefile()
*  24-Feb-1989 SB Inherit MAKEFLAGS to Env for XMake Compatibility
*  22-Feb-1989 SB Ignore '-' or '/' in parseCommandLine()
*  16-Feb-1989 SB add delScriptFiles() to delete temp script files at the
*		  end of the make. Also called on ^c and ^break
*  15-Feb-1989 SB Rewrote useDefaultMakefile(); MAKEFLAGS can contain all flags
*		  now
*  13-Feb-1989 SB Rewrote filename() for OS/2 1.2 support, now returns BOOL.
*   3-Feb-1989 SB Renamed freeUnusedRules() to freeRules(); moved prototype to
*		  proto.h
*   9-Jan-1989 SB Improved /help;added -?
*   3-Jan-1989 SB Changes for /help and /nologo
*   5-Dec-1988 SB Made chkPrecious() CDECL as signal() expects it
*		  main has CDECL too; cleaned prototypes (added void)
*  30-Nov-1988 SB Added for 'z' option in setFlags() and chkPrecious()
*  10-Nov-1988 SB Removed '#ifndef IBM' as IBM ver has a separate tree
*  21-Oct-1988 SB Added fInheritUserEnv to inherit macro definitions
*  22-Sep-1988 RB Changed a lingering reference of /B to /A.
*  15-Sep-1988 RB Move some def's out to GLOBALS.
*  17-Aug-1988 RB Clean up.
*  15-Aug-1988 RB /B ==> /A for XMAKE compatibility.
*  11-Jul-1988 rj Removed OSMODE definition.
*		  Removed NMAKE & NMAKEFLAGS (sob!).
*   8-Jul-1988 rj Added OSMODE definition.
*   7-Jul-1988 rj #ifndef IBM'ed NMAKE & NMAKEFLAGS
*   6-Jul-1988 rj Ditched shell, argVector, moved getComSpec to build.c.
*   5-Jul-1988 rj Fixed (*pfSPAWN) declarations.
*  28-Jun-1988 rj Added NMAKEFLAGS predefined macro.
*  24-Jun-1988 rj Added NMAKE predefined macro.
*		  Added doError flag to unlinkTmpFiles call.
*  23-Jun-1988 rj Fixed okToDelete to delete less often.
*  22-Jun-1988 rj Make chkPrecious use error messages
*  25-May-1988 rb Make InitLeadByte() smarter.
*  20-May-1988 rb Change built-in macro names.
*  18-May-1988 rb Remove comment about built-in rules and macros.
*  17-May-1988 rb Load built-in rules in right place.
*  16-May-1988 rb Conditionalize recursive make feature.
*   8-May-1988 rb Better initialization of system shell.
*
*******************************************************************************/


#include "nmake.h"
#include "nmmsg.h"
#include "proto.h"
#include "globals.h"
#include "grammar.h"

#define INCL_NOPM
#define INCL_DOSSIGNALS


#if !defined(DOS) && !defined(FLAT)
#include <os2.h>
#endif

#define MAX(A,B)  (A) > (B) ? (A) : (B)


/*  ----------------------------------------------------------------------------
 *  prototypes for main and all functions static to this module
 */

      void  __cdecl    main(unsigned, char**, char**);
LOCAL void	  NEAR readEnvironmentVars(void);
LOCAL void	  NEAR readMakeFiles(void);
LOCAL void	  NEAR useDefaultMakefile(void);
LOCAL BOOL	  NEAR filename(const char*, char**);
LOCAL void	  NEAR freeStructures(void);
LOCAL void	  NEAR loadBuiltInRules(void);
//void	      pascal	 chkPrecious(USHORT, USHORT);
LOCAL void  __cdecl    chkPrecious(int sig);
LOCAL void	  NEAR InitializeEnv(void);
LOCAL UCHAR	  NEAR isPrecious(char*);
      void	  NEAR removeTrailChars(char *);

extern void CDECL NEAR	makeIdeMessage (unsigned, unsigned,...);

void CDECL NEAR usage (void);

/*  ----------------------------------------------------------------------------
 *  global variables that live in this module but that are also used in
 *  other modules:
 */

#ifdef DEBUG
    char *dummy="dummy";
#endif

char	 *  makeStr    = NULL;	       /* this make invocation name	 */

#ifdef DOS
char	 *  startupDir;
#endif

char	    NEAR fileStr[MAXNAME];
char	 *  initSavPtr = NULL;	       /* save area for initialized vars */
unsigned    saveBytes  = 0;
char	 ** envPtr     = NULL;

UCHAR	    okToDelete = FALSE;        /* do not del unless exec'ing cmd */

#if defined(FLAT)
UCHAR       fRunningUnderTNT = FALSE;
extern UCHAR FIsTNT(void);
#endif

char	 *  builtInTarg[] = {".SUFFIXES",
			     ".c.obj",
			     ".c.exe",
			     ".cpp.obj",
			     ".cpp.exe",
			     ".cxx.obj",
			     ".cxx.exe",
			     ".asm.obj",
			     ".asm.exe",
			     ".bas.obj",
			     ".cbl.obj",
			     ".cbl.exe",
			     ".for.obj",
			     ".for.exe",
			     ".pas.obj",
			     ".pas.exe",
			     ".rc.res",
			     NULL};

char	 *  bltInCmd0[]  = {".exe", ".obj", ".asm", ".c",  ".cpp", ".cxx",
			    ".bas", ".cbl", ".for", ".pas", ".res", ".rc",
			    NULL};
char	 *  bltInCmd1[]  = {"$(CC) $(CFLAGS) /c $*.c", NULL};
char	 *  bltInCmd2[]  = {"$(CC) $(CFLAGS) $*.c", NULL};
char	 *  bltInCmd3[]  = {"$(CPP) $(CPPFLAGS) /c $*.cpp", NULL};
char	 *  bltInCmd4[]  = {"$(CPP) $(CPPFLAGS) $*.cpp", NULL};
char	 *  bltInCmd5[]  = {"$(CXX) $(CXXFLAGS) /c $*.cxx", NULL};
char	 *  bltInCmd6[]  = {"$(CXX) $(CXXFLAGS) $*.cxx", NULL};
char	 *  bltInCmd7[]  = {"$(AS) $(AFLAGS) /c $*.asm", NULL};
char	 *  bltInCmd8[]  = {"$(AS) $(AFLAGS) $*.asm", NULL};
char	 *  bltInCmd9[]  = {"$(BC) $(BFLAGS) $*.bas;", NULL};
char	 *  bltInCmd10[] = {"$(COBOL) $(COBFLAGS) $*.cbl;", NULL};
char	 *  bltInCmd11[] = {"$(COBOL) $(COBFLAGS) $*.cbl, $*.exe;", NULL};
char	 *  bltInCmd12[] = {"$(FOR) /c $(FFLAGS) $*.for", NULL};
char	 *  bltInCmd13[] = {"$(FOR) $(FFLAGS) $*.for", NULL};
char	 *  bltInCmd14[] = {"$(PASCAL) /c $(PFLAGS) $*.pas", NULL};
char	 *  bltInCmd15[] = {"$(PASCAL) $(PFLAGS) $*.pas", NULL};
char	 *  bltInCmd16[] = {"$(RC) $(RFLAGS) /r $*", NULL};

char	 ** builtInCom[] = {bltInCmd0,	bltInCmd1,  bltInCmd2,	bltInCmd3,
			    bltInCmd4,	bltInCmd5,  bltInCmd6,	bltInCmd7,
			    bltInCmd8,	bltInCmd9,  bltInCmd10, bltInCmd11,
			    bltInCmd12, bltInCmd13, bltInCmd14, bltInCmd15,
			    bltInCmd16, NULL};

/*  ----------------------------------------------------------------------------
 *  Local to this module
 */

#ifndef NO_OPTION_Z
LOCAL char nmakeExitLabelCmd[] = ":NMAKEEXIT";
#endif

/*  --------------------------------------------------------------------
 *  main
 *
 *  actions:        saves the initial global variables in a
 *		    block. calls doMake() and then delTempScriptFiles()
 */

void __cdecl
main(
    unsigned argc,
    char *argv[],
    char *envp[])			    /* environment variables  */
{
    extern unsigned saveBytes;
    extern char **envPtr;
    int status;				    /* returned by doMake */
    extern char *makeStr;
#ifdef OS2_SIGNALS
    PUSHORT prev;
    unsigned long _FAR *pfnsig;
#endif

    InitializeEnv();

#if defined(FLAT)
    fRunningUnderTNT = FIsTNT();
#endif

    initCharmap();

    initMacroTable(macroTable);

#ifdef DEBUG_MEMORY
    //This puts 0xff in all free entries in the heap
    _heapset(0xff);
#endif

    envPtr = envp;
#ifdef DEBUG_COMMANDLINE
    {
	int iArg = argc;
	char **chchArg = argv;
	for (; iArg--; chchArg++)
	    printf("'%s' ", *chchArg);
	printf("\n");
    }
#endif
#ifdef TEST_RUNTIME
    //Tests RunTime error R6001
    {char near *foo = NULL; *foo = '1';}
#endif

#ifdef DOS
    startupDir = getCurDir();
#endif

#ifdef FLAT
    resultbuf_size = sizeof(struct _finddata_t);
    #ifdef NT
	    ext_size  = CCHMAXPATHCOMP;
    	filename_size = CCHMAXPATHCOMP;
	    filenameext_size = CCHMAXPATH;
    #endif
#else
    /* If OS/2 1.2 and beyond then allowed max sizes vary
     */
    if (_osmajor < 10 || _osmode == DOS_MODE)
	resultbuf_size = sizeof(struct find_t);
    else if (_osminor < 20)
	resultbuf_size = sizeof(struct FileFindBuf);
    else {
	ext_size  = CCHMAXPATHCOMP;
	filename_size = CCHMAXPATHCOMP;
	filenameext_size = CCHMAXPATH;
	resultbuf_size = sizeof(struct _FILEFINDBUF);
    }
#endif

    if (!makeStr)				  /* extract file name	*/
	if (!filename(_ftcscpy(fileStr, _pgmptr), &makeStr))
	    makeStr = "NMAKE";

    // Initialize the message file
    SetErrorFile("nmake.err", _pgmptr, 1);	// 1=Search Exe Path

#if defined(SELF_RECURSE)
    initSavPtr = (char *)allocate(saveBytes = (&endOfSave - &startOfSave));
    memmove(initSavPtr, &startOfSave, saveBytes);
#endif

    /* set up handler for .PRECIOUS  the handler tries to remove the
     * current target when control-C'd, unless it is "precious"
     */

#ifdef OS2_SIGNALS
    This commented out part was trial for using OS/2 function calls
    It still has some problems
    DOSSETSIGHANDLER(chkPrecious, pfnsig, prev, SIGA_ACCEPT, SIG_CTRLC);
    if (_osmode == OS2_MODE) {
	DOSSETSIGHANDLER(chkPrecious, NULL, NULL, SIGA_ACCEPT, SIG_CTRLBREAK);
	DOSSETSIGHANDLER(chkPrecious, NULL, NULL, SIGA_ACCEPT, SIG_KILLPROCESS);
    }
#endif

    signal(SIGINT, chkPrecious);
    signal(SIGTERM, chkPrecious);

    makeIdeMessage(0, 0);
    status = doMake(argc, argv, NULL);

#ifndef NO_OPTION_Z
    /* If -Z is specified then NMAKE needs to have errorLevel check in the
     * batch file. So add the goto label for exit and print the Reverse batch
     * file
     */
    if (ON(gFlags, F1_REVERSE_BATCH_FILE)) {
	STRINGLIST *revCmd;
	//Adds ':NMAKEEXIT' to jump to end when error occurs
	revCmd = makeNewStrListElement();
	revCmd->text = nmakeExitLabelCmd;
	prependItem(&revList, revCmd);
	//'=c' means echo at current line
	revCmd = makeNewStrListElement();
	revCmd->text = makeString("=c");
	appendItem(&revList, revCmd);
	//'=d' turns echoing on (unless preceeded by @)
	revCmd = makeNewStrListElement();
	revCmd->text = makeString("=d");
	appendItem(&revList, revCmd);
    }
#endif
    delScriptFiles();
#ifndef NO_OPTION_Z
    if (ON(gFlags, F1_REVERSE_BATCH_FILE))
	printReverseFile();
#endif
#ifdef MEMORY_DEBUG
    mem_status();
#endif

#ifdef HEAP_DIAGNOSTICS
    printHeapDiagnostics();
#endif
#ifdef NMK_DEBUG
    fprintf(stderr, "Exiting...\n");
#endif
    if (!fSlashKStatus)
	//error when slashK specified
	status = 1;
#if !defined(NDEBUG) && !defined(NT_BUILD)
    printStats();
#endif
    exit(status);
}
//
// On some systems, the environment strings may be allocated as one large block,
// making it expensive to manipulate them.  In that case, we copy each environment
// strings to its own allocation and release the large block
//
LOCAL void NEAR
InitializeEnv(
void
) {
#if !defined(FLAT)
    char **ppch = copyEnviron(environ);

    free(*environ);
    free(environ);
    environ = ppch;
#endif
}

extern void NEAR endNameList(void);
extern void NEAR addItemToList(void);
extern void NEAR assignDependents(void);
extern void NEAR assignBuildCommands(void);

/* loadBuiltInRules() -- Loads built in Rules to the NMAKE Tables
*
* Modifies:
*  fInheritUserEnv  --	is set to TRUE to inherit CC, AS
*
* Notes:
*  Does this by calls to defineMacro(), which calls putMacro(). Since,
*  fInheritUserEnv is set to TRUE, putMacro() will add to the Environment.
*/

LOCAL void NEAR
loadBuiltInRules(void)
{
    char *tempTarg;
    char **tempCom;
    unsigned index;
    char *macroName, *macroValue;
    extern char *makestr;


    /* We dynamically allocate CC and AS because they need to be freed in a
     * recursive MAKE
     */
    macroName = makeString("CC");
    macroValue = makeString("cl");
    defineMacro(macroName, macroValue, 0);
    macroName = makeString("CXX");
    macroValue = makeString("cl");
    defineMacro(macroName, macroValue, 0);
    macroName = makeString("CPP");
    macroValue = makeString("cl");
    defineMacro(macroName, macroValue, 0);
    macroName = makeString("AS");
    macroValue = makeString("ml");
    defineMacro(macroName, macroValue, 0);
    macroName = makeString("BC");
    macroValue = makeString("bc");
    defineMacro(macroName, macroValue, 0);
    macroName = makeString("COBOL");
    macroValue = makeString("cobol");
    defineMacro(macroName, macroValue, 0);
    macroName = makeString("FOR");
    macroValue = makeString("fl");
    defineMacro(macroName, macroValue, 0);
    macroName = makeString("PASCAL");
    macroValue = makeString("pl");
    defineMacro(macroName, macroValue, 0);
    macroName = makeString("RC");
    macroValue = makeString("rc");
    defineMacro(macroName, macroValue, 0);
    macroName = makeString("MAKE");
    macroValue = makeString(makeStr);
    /* From environment so it won't get exported ; user can reset MAKE
     */
    defineMacro(macroName, macroValue, M_ENVIRONMENT_DEF|M_WARN_IF_RESET);

    for (index = 0; tempTarg = builtInTarg[index]; index++) {
	 name = makeString(tempTarg);
	 _ftcscpy(buf, ":");
	 endNameList();
	 for (tempCom=builtInCom[index];*tempCom;tempCom++) {
	      _ftcscpy(buf, *tempCom);
	      addItemToList();
	 }
	 if (index == 0)
	     assignDependents();
	 assignBuildCommands();
    }

}

/*  ----------------------------------------------------------------------------
 *  doMake()
 *
 *  actions:	    prints a version message
 *		    reads the environment variable MAKEFLAGS
 *		    if MAKEFLAGS defined
 *			defines MAKEFLAGS to have that value w/in nmake
 *			sets a flag for each option if MAKEFLAGS defined
 *		    else defines the macro MAKEFLAGS to be NULL
 *		    parses commandline (adding option letters to MAKEFLAGS)
 *		    reads all environment variables
 *		    reads tools.ini
 *		    reads makefile(s) (if -e flag set, new definitions in
 *			makefile won't override environment variable defs)
 *		    prints information if -p flag
 *		    processes makefile(s)
 *		    prints information if -d flag and not -p flag (using both
 *			is overkill)
 *
 *  In effect, the order for making assignments is (1 = least binding,
 *	4 = most binding):
 *
 *	1)  TOOLS.INI
 *	2)  environment (if -e flag, makefile)
 *	3)  makefile	(if -e flag, environment)
 *	4)  command line
 *
 *  The user can put anything he wants in the MAKEFLAGS environment variable.
 *  I don't check it for illegal flag values, because there are many xmake
 *  flags that we don't support.  He shouldn't have to change his MAKEFLAGS
 *  to use nmake. Xmake always puts 'b' in MAKEFLAGS for "backward com-
 *  patibility" (or "botch") for the original Murray Hill version of make.
 *  It doesn't make sense to use -f in MAKEFLAGS, thus it is disallowed.
 *  It also makes little sense to let the default flags be -r, -p, or -d,
 *  so they aren't allowed in MAKEFLAGS, either.
 *
 *  Even though DOS only uses uppercase in environment variables, this
 *  program may be ported to xenix in the future, thus we allow for the
 *  possibility that MAKEFLAGS and commandline options will be in upper
 *  and/or lower case.
 *
 *  modifies:   init    global flag set if tools.ini is being parsed...
 */


int NEAR
doMake(argc, argv, parentBlkPtr)
unsigned argc;
char *argv[];
char *parentBlkPtr;	 /* state of parent, restored prior to return */
{
    int status = 0;
    char *p;
    extern char *makeStr;		       /* the initial make invok name */
    extern unsigned saveBytes;
    char *makeDir, *curDir;

#ifdef DEBUG_ALL
    printf ("DEBUG: In doMake\n");
#endif
#if !defined(SELF_RECURSE)
    assert(parentBlkPtr == NULL);
#endif

    /*
     * Load built-ins here rather than in main().  Otherwise in a recursive
     * make, doMake() will initialize rules to some value which has been
     * freed by sortRules(). [RB]
     * UNDONE:	why is sortRules() not setting rules to NULL?  [RB]
     */

#ifdef DEBUG
    heapdump(__FILE__, __LINE__);
#endif

    inlineFileList = (STRINGLIST *)NULL;
    makeDir = makeString("MAKEDIR");
    curDir  = getCurDir();
    defineMacro(makeDir, curDir, 0);

    //TEMPFIX: We are truncating MAKEFLAGS environment variable to its limit
    //to avoid GP Faults
    if (p = getenv("MAKEFLAGS"))		    /*	but not MAKEFLAGS     */
	_ftcsncpy(makeflags+10, p, _ftcslen(makeflags + 10));
    /*
     * fInheritUserEnv is set to TRUE so that the changes made get inherited
     */
    fInheritUserEnv = (BOOL)TRUE;

    //
    // 07-05-92  BryanT    Simply adding global strings to the macro array
    //                     causes problems later when you go to free them
    //                     from a recursive $(MAKE).  Both the macro name
    //                     and the macro's value must be created with
    //                     makeString.

    defineMacro(makeString("MAKEFLAGS"), makeString(makeflags+10), M_NON_RESETTABLE|M_ENVIRONMENT_DEF);

    for (;p && *p; p++) 			    /* set flags in MAKEFLAGS */
	setFlags(*p, TRUE);			    /* TRUE says turn bits ON */

#ifdef DEBUG_ALL
    heapdump(__FILE__, __LINE__);
#endif

    parseCommandLine(--argc, ++argv);		    /* skip over program name */
#ifdef DEBUG_ALL
    printf ("DEBUG: Command Line parsed\n");
#endif
#ifndef NO_OPTION_Z
    if (ON(gFlags, F1_REVERSE_BATCH_FILE) &&
	     (ON(flags, F2_NO_EXECUTE) || ON(gFlags, F1_TOUCH_TARGETS)))
	// We ignore /Z option when /N or /T is also specified
	setFlags('Z', FALSE);
#endif
#ifdef DEAD_CODE
	makeError(0, CMDLINE_N_Z_INCOMPATIBLE, makeStr);
#endif
    if (!bannerDisplayed) displayBanner();	    /* version number, etc.   */
    if (OFF(gFlags, F1_IGNORE_EXTERN_RULES)) {	    /* read tools.ini	      */
#ifdef DEBUG_ALL
	printf ("DEBUG: Read Tools.ini\n");
#endif
	loadBuiltInRules();
#ifdef DEBUG_ALL
	printf ("DEBUG: loadBuiltInRules\n");
#endif
	fName = "tools.ini";
#ifdef DEBUG_ALL
	heapdump(__FILE__, __LINE__);
#endif
	if (tagOpen("INIT", fName, makeStr)) {
	    ++line;
	    init = TRUE;			    /* tools.ini being parsed */

#ifdef DEBUG_ALL
    heapdump(__FILE__, __LINE__);
#endif

#ifdef DEBUG_ALL
    printf ("DEBUG: Start Parse\n");
#endif
	    parse();
#ifdef DEBUG_ALL
    printf ("DEBUG: Parsed\n");
#endif
	    if (fclose(file) == EOF)
		makeError(0, ERROR_CLOSING_FILE, fName);
	}
    }

#ifdef DEBUG_ALL
    heapdump(__FILE__, __LINE__);
#endif
#ifdef DEBUG_ALL
    printf ("after tagopen\n");
#endif
    // For XMake Compatibility MAKEFLAGS should always be inherited to the Env
    // Put copy of makeflags so that the environment can be freed on return
    // from a recursive make
    if (PutEnv(makeString(makeflags)) == -1)
	makeError(0, OUT_OF_ENV_SPACE);
#ifdef DEBUG_ALL
    printf ("after putenv\n");
#endif
    if (!makeFiles) useDefaultMakefile();	    /* if no -f makefile given*/
#ifdef DEBUG_ALL
    printf ("DEBUG: Used default\n");
#endif
    readEnvironmentVars();
    readMakeFiles();				    /* read description files */

#ifdef DEBUG_ALL
    printf ("DEBUG: Read makefile\n");
#endif
    currentLine = 0;				    /* reset line after done  */
    sortRules();				    /*	reading files (for    */
    if (ON(gFlags, F1_PRINT_INFORMATION)) {	    /*	error messages)       */
	showMacros();
	showRules();
	showTargets();
    }
    /* free buffer used for conditional processing - not required now */
    if (lbufPtr)
	FREE(lbufPtr);


#ifdef DEBUG_ALL
    heapdump(__FILE__, __LINE__);
#endif

    status = processTree();

#if defined(SELF_RECURSE)
    /* restore state of the parent into the global area */
    if (parentBlkPtr) {
	/* free the space used up for this make invocation     */
	/* free the rule list, the macroTable, the targetTable */
	freeStructures();
	memmove(&startOfSave, parentBlkPtr, saveBytes);
	FREE(parentBlkPtr);
    }
#endif

#ifdef DEBUG_ALL
    heapdump(__FILE__, __LINE__);
#endif

    //We ignore retval from chdir because we cannot do anything if it fails
    //This accomplishes a 'cd $(MAKEDIR)'.
    _chdir(curDir);
    return(status);
}


/*** filename -- filename part of a name ***************************************
*
* Scope:
*  Local
*
* Purpose:
*  A complete file name is of the form	<drive:><path><filename><.ext>. This
*  function returns the filename part of the name.
*
* Input:
*  src -- The complete file name
*  dst -- filename part of the complete file name
*
* Output:
*  Returns TRUE if src has a filename part & FALSE otherwise
*
* Assumes:
*  That the file name could have either '/' or '\' as path separator.
*
* Modifies Globals:
*  None.
*
* Uses Globals:
*  None.
*
* Notes:
*  Allocates memory for filename part. Function was rewritten to support OS/2
*  Ver 1.2 filenames.
*
*  HV: One concern when I rewrite filename() to use _splitpath(): I declared
*  szFilename with size _MAX_FNAME, which could blow up the stack if _MAX_FNAME
*  is too large.
*
*******************************************************************************/

LOCAL BOOL NEAR
filename(const char *src, char **dst)
{
    char	szFilename[_MAX_FNAME];		// The filename part

    // Split the full pathname to components
    _splitpath(src, NULL, NULL, szFilename, NULL);

    // Allocate & copy the filename part to the return string
    *dst = makeString(szFilename);

    // Finished
    return (BOOL) _ftcslen(*dst);
}


/*  ----------------------------------------------------------------------------
 *  readMakeFiles()
 *
 *  actions:	    walks through the list calling parse on each makefile
 *		    resets the line number before parsing each file
 *		    removes name of parsed file from list
 *		    frees removed element's storage space
 *
 *  modifies:	    file	global file pointer (FILE*)
 *		    fName	global pointer to file name (char*)
 *		    line	global line number used and updated by the
 *				lexer
 *		    init        global flag reset for parsing makefiles
 *			        ( files other than tools.ini )
 *		    makeFiles	in main() by modifying contents of local
 *				pointer (list)
 *
 *  We keep from fragmenting memory by not allocating and then freeing space
 *  for the (probably few) names in the files and targets lists.  Instead
 *  we use the space already allocated for the argv[] vars, and use the space
 *  we alloc for the commandfile vars.	The commandfile vars that could be
 *  freed here, but they aren't because we can't tell them from the argv[]
 *  vars.  They will be freed at the end of the program.
 */

LOCAL void NEAR
readMakeFiles(void)
{
    STRINGLIST *q;

    for (q = makeFiles; q ; q = q->next) {	  /* for each name in list  */
	if ((q->text)[0] == '-' && !(q->text)[1]) {
	    fName = makeString("STDIN");
	    file = stdin;
	}
	else {
	    fName = makeString(q->text);
	    if (!(file = FILEOPEN(fName, "rt")))    /* open to read, text mode*/
		makeError(0, CANT_OPEN_FILE, fName);
	}
	line = 0;
	init = FALSE;				    /* not parsing tools.ini  */
	parse();
	if (file != stdin && fclose(file) == EOF)
	    makeError(0, ERROR_CLOSING_FILE, fName);
    }						

    //free the list of makefiles
    freeStringList(makeFiles);
}						



/*** readEnvironmentVars - Read in environment variables into Macro table ******
*
* Scope:
*  Local.
*
* Purpose:
*  Reads environment variables into the NMAKE macro Table. It walks through envp
*  using environ making entries in NMAKE's hash table of macros for each string
*  in the table.
*
* Input:
*
* Output:
*
* Errors/Warnings:
*
* Assumes:
*  That the env contains strings of the form "VAR=value" i.e. '=' present.
*
* Modifies Globals:
*  fInheritUserEnv - set to false.
*
* Uses Globals:
*  environ - Null terminated table of pointers to environment variable
*	     definitions of the form "name=value" (Std C Runtime variable)
*
* Notes:
*  If the user specifies "set name=value" as a build command for a target being
*  built, the change in the environment will not be reflected in nmake's set of
*  defined variables in the macro table.
*
* Undone/Incomplete:
*  1> Probably do not need envPtr global in NMAKE. (to be removed)
*  2> Probably don't need fInheritUserEnv (see PutMacro)
*
*******************************************************************************/
LOCAL void NEAR
readEnvironmentVars(void)
{
    char *macro, *value;
    char *t;
    char **envPtr;

    envPtr = environ;
    for (;*envPtr; ++envPtr) {
	if (t = _ftcschr(*envPtr, '=')) { 	   /* should always be TRUE  */
	    if (!_ftcsnicmp(*envPtr, "MAKEFLAGS", 8))
		continue;
	    *t = '\0';
	    // Don't add empty names.
	    if (**envPtr == '\0')
		continue;
	    // ALLOC: here we make copies of the macro name and value to define
	    macro = _ftcsupr(makeString(*envPtr));

	    value = makeString(t+1);
	    *t = '=';
	    fInheritUserEnv = (BOOL)FALSE;
	    if (!defineMacro(macro, value, M_ENVIRONMENT_DEF)) {
	    	// ALLOC: here we free the copies if they were not added.
		FREE(macro);
		FREE(value);
	    }
	}
    }
}



/*  ----------------------------------------------------------------------------
 *  parseCommandLine()
 *
 *  arguments:	argc	count of arguments in argv vector
 *		argv	table of pointers to commandline arguments
 *
 *  actions:	reads a command file if necessary
 *		sets switches
 *		defines macros
 *		makes a list of makefiles to read
 *		makes a list of targets to build
 *
 *  modifies:	makeFiles	in main() by modifying contents of parameter
 *				pointer (list) to STRINGLIST pointer
 *				(makeFiles)
 *		makeTargets	in main() by modifying contents of param
 *				pointer (targets) to STRINGLIST pointer
 *		fInheritUserEnv set to TRUE so that user defined changes in the
 *				environment variables get inherited by the Env
 *
 *  nmake doesn't make new copies of command line macro values or environment
 *  variables, but instead uses pointers to the space already allocated.
 *  This can cause problems if the envp, the environment pointer, is accessed
 *  elsewhere in the program (because the vector's strings will contain '\0'
 *  where they used to contain '=').  I don't foresee any need for envp[] to
 *  be used elsewhere.	Even if we did need to use the environment, we could
 *  access the environ variable or use getenv().
 *
 *  I don't care what the current DOS "switch" character is -- I always
 *  let the user give either.
 */


char *helpArguments[] = {"QH", "/u", "NMAKE.EXE", NULL };

void NEAR
parseCommandLine(argc, argv)
unsigned argc;
char *argv[];
{
    extern char *makeStr;
    STRINGLIST *p;
    char *s;
    char *t;
    FILE *out;
    BOOL fHelp = FALSE;
    BOOL fQuestion = FALSE;

    for (; argc; --argc, ++argv) {
	if (**argv == '@')					    /* cmdfile*/
	    readCommandFile((char*)*argv+1);
	else if (**argv == '-'|| **argv == '/') {		    /* switch */
	    s = *argv + 1;
	    if (!_ftcsicmp(s, "help")) {
		fHelp = TRUE;
		break;
	    }
	    // if '-' and '/' specified then ignores it
	    for (; *s; ++s) {
		if (!_ftcsicmp(s, "nologo")) {
		    setFlags(s[2], TRUE);
		    break;
		}
#ifdef HEAP
		else if (!_ftcsicmp(s, "debug")) {
		    fHeapChk = TRUE;
		    break;
		}
#endif
		else if (*s == '?') {
		    fQuestion = TRUE;
		    break;
		}
		else if (*s == 'f' || *s == 'F') {
		    char *mkfl = s+1;

		    //if '/ffoo' then use 'foo'; else use next argument
		    if (!*mkfl && (!--argc || !*++argv || !*(mkfl = *argv)))
			makeError(0, CMDLINE_F_NO_FILENAME);
		    p = makeNewStrListElement();
		    p->text = makeString(mkfl);
		    appendItem(&makeFiles, p);
		    break;
		}
		else if (*s == 'x' || *s == 'X') {
		    char *errfl = s+1;

		    //if '/xfoo' then use 'foo'; else use next argument
		    if (!*errfl && (!--argc || !*++argv || !*(errfl = *argv)))
			makeError(0, CMDLINE_X_NO_FILENAME);
		    if (*errfl == '-' && !errfl[1])
			_dup2(_fileno(stdout), _fileno(stderr));
		    else {
#if 0							    /* C5 runtime bug */
			if (freopen(errfl, "wt", stderr) == NULL)
			    makeError(0, CANT_OPEN_FILE, errfl);
#else
			if ((out = fopen(errfl, "wt")) == NULL)
			    makeError(0, CANT_WRITE_FILE, errfl);
			_dup2(_fileno(out), _fileno(stderr));
			fclose(out);
#endif
		    }
		    break;
		}
		else
		    setFlags(*s, TRUE);
	    }				    // of for
        }
	else {
	    if (s = _ftcschr(*argv, '=')) {			    /* macro  */
		if (s == *argv)
		    //User has specified "=value"
		    makeError(0, CMDLINE_NO_MACRONAME);
		*s = '\0';
		for (t = s++ - 1; WHITESPACE(*t); --t)
		    ;
		*(t+1) = '\0';
		fInheritUserEnv = (BOOL)TRUE;
		defineMacro(makeString(*argv+_ftcsspn(*argv, " \t")),
			    makeString( s+_ftcsspn(s," \t")),
			    M_NON_RESETTABLE);
	    }
	    else {
		removeTrailChars(*argv);
		if (**argv) {
		    p = makeNewStrListElement();		/* target */
		    p->text = makeString(*argv);   /* needs to be on heap [rm]*/
		    appendItem(&makeTargets, p);
		}
	    }
	    *argv = NULL;			    /* so we won't try to free*/
	}					    /*	this space if process-*/
    }						    /*	ing command file stuff*/

    if (fHelp) {
#ifdef QUICKHELP
	int rc = _spawnvp(P_WAIT, "QH.EXE", helpArguments);

	//Qh /u returns error code 3 if a cmd line topic is not found
	//If the spawn fails spawnvp will return -1 and we need -help
	//message, also
	//Qh never returns -1
	if (rc == 3 || rc == -1) {
	    usage();
	}
#else
	usage();
#endif
	exit(0);
    }
    else if (fQuestion) {
	usage();
	exit(0);
    }
}



/*** useDefaultMakefile -- tries to use the default makefile *******************
*
* Scope:
*  Local
*
* Purpose:
*  When no makefile has been specified by the user, set up the default makefile
*  to be used.
*
* Input:
* Output:
* Errors/Warnings:
*  CMDLINE_NO_MAKEFILE -- 'makefile' does not exist & no target specified
*
* Assumes:
* Modifies Globals:
*  makeTargets -- if 'makefile' does not exist then the first target is removed
*		      from this list,
*  makeFiles -- if 'makefile' does not exist then the first target is attached
*		    to this list.
*
* Uses Globals:
*  makeTargets -- the list of targets to be made
*
* Notes:
*  Given a commandline not containing a '-f makefile', this is how NMAKE
*  behaves --
*      If ['makefile' exists] then use it as the makefile,
*      if [(the first target exists and has no extension) or
*	   (if it exists and has an extension for which no inference rule
*	    exists)]
*      then use it as the makefile.
*
*******************************************************************************/
LOCAL void NEAR
useDefaultMakefile(void)
{
    STRINGLIST *p;
    char *s,
	 *ext;
    char nameBuf[MAXNAME];
    void *dBuf = _alloca(resultbuf_size);

    //if 'makefile' exists then use it
    if (!_access("makefile", READ)) {
	p = makeNewStrListElement();
	p->text = makeString("makefile");
	makeFiles = p;
    }
    //check first target
    else if (makeTargets) {
	s = makeTargets->text;
	if (_access(s, READ) ||			    //1st target does not exist
	  ((ext = _ftcsrchr(s, '.'))
	   && findRule(nameBuf, s, ext, dBuf))) {  //has no ext or inf rule
	    return;
	}
	p = makeTargets;
	makeTargets = makeTargets->next;	    //one less target
	makeFiles = p;				    //1st target is the makefile
    }
    //if -p and no makefile, simply give information ...
    else if (OFF(gFlags, F1_PRINT_INFORMATION))
	makeError(0, CMDLINE_NO_MAKEFILE);	   //no 'makefile' or target
}

/*  ----------------------------------------------------------------------------
 *  setFlags()
 *
 *  arguments:		line	current line number in makefile (or 0
 *				if still parsing commandline)
 *			c	letter presumed to be a commandline option
 *			value	TRUE if flag should be turned on, FALSE for off
 *
 *  actions:		checks to see if c is a valid option-letter
 *			if no, error, halt
 *			if value is TRUE, sets corresponding flag bit
 *			    and adds flag letter to MAKEFLAGS macro def
 *			else if flag is resettable, clears corresponding bit
 *			    and removes letter from MAKEFLAGS macro def
 *
 *  modifies:		flags	    external resettable-flags
 *			gFlags	    external non-resettable flags
 *			(MAKEFLAGS  nmake internal macrodefs)
 *
 *  Only the flags w/in the "flags" variable can be turned off.  Once the
 *  bits in "gFlags" are set, they remain unchanged.  The bits in "flags"
 *  are modified via the !CMDSWITCHES directive.
 */


void NEAR
setFlags(c, value)
char c;
BOOL value;
{
    /*
     * Use lexer's line count.  If this gets called w/in
     * mkfil, might be from directive, which never makes it
     * to the parser, so parser's line count might be out
     * of sync.
     */

    char d = c;
    UCHAR arg;
    UCHAR *f;
    char *s;
    extern char *makeStr;
    extern MACRODEF * NEAR pMacros;
    extern STRINGLIST * NEAR pValues;


    f = &flags;
    switch(c = (char) _totupper(c)) {
	case 'A':   arg = F2_FORCE_BUILD;			    break;
	case 'B':   fRebuildOnTie = TRUE;	return;
	case 'C':   arg = F1_CRYPTIC_OUTPUT;
		    f = &gFlags;
		    bannerDisplayed = TRUE;
		    break;
	case 'D':   arg = F2_DISPLAY_FILE_DATES;		    break;
	case 'E':   arg = F1_USE_ENVIRON_VARS;	    f = &gFlags;    break;
	case 'I':   arg = F2_IGNORE_EXIT_CODES; 		    break;
	case 'K':   fOptionK = TRUE;  return;
	case 'L':   arg = F1_NO_LOGO;
		    f = &gFlags;
		    bannerDisplayed = TRUE;
		    break;
#if defined(DOS) && !defined(FLAT)
	case 'M':   fNoEmsXms = TRUE; return;
#endif
	case 'N':   arg = F2_NO_EXECUTE;			    break;
	case 'O':   fDescRebuildOrder = TRUE; return;
	case 'P':   arg = F1_PRINT_INFORMATION;     f = &gFlags;    break;
	case 'Q':   arg = F1_QUESTION_STATUS;	    f = &gFlags;    break;
	case 'R':   arg = F1_IGNORE_EXTERN_RULES;   f = &gFlags;    break;
	case 'S':   arg = F2_NO_ECHO;				    break;
	case 'T':   arg = F1_TOUCH_TARGETS;	    f = &gFlags;    break;
#if defined(SELF_RECURSE)
	case 'V':   fInheritMacros = TRUE;	return;
#endif
#ifndef NO_OPTION_Z
	case 'Z':
		    //Silently ignore /Z in protect mode
  #if defined(DOS) && !defined(FLAT)
		    arg = F1_REVERSE_BATCH_FILE;
		    f = &gFlags;
		    bannerDisplayed = TRUE;
  #endif
		    break;
#endif
	case ' ':   return;			/* recursive make problem     */
	default:    makeError(0, CMDLINE_BAD_OPTION, d);
    }
    if (!pMacros) {
	pMacros = findMacro("MAKEFLAGS");
	pValues = pMacros->values;
    }
    if (value) {
	SET(*f, arg);				/* set bit in flags variable  */
	if (c == 'Q') SET(*f, F1_CRYPTIC_OUTPUT);
	if (!_ftcschr(pValues->text, c)) {	/* don't want to dup any chars*/
	    if (s = _ftcschr(pValues->text, ' ')) /*append ch to MAKEFLAGS      */
		*s = c;
	    if (PutEnv(makeString(makeflags)) == -1)	/*pValues->text pts into makeflags*/
		makeError(line, OUT_OF_ENV_SPACE);
	}
    }
    else if (f == &flags
#ifndef NO_OPTION_Z
	    || ON(gFlags, F1_REVERSE_BATCH_FILE)
#endif
	    ) {
	/* make sure pointer is valid (we can't change gFlags, except if /Z   */
	CLEAR(*f, arg);
	if (s = _ftcschr(pValues->text, c))	/* adjust MAKEFLAGS	      */
	    do {
		*s = *(s+1);			/*  move remaining chars over */
	    } while (*(++s));
	if (PutEnv(makeString(makeflags)) == -1)
	    makeError(line, OUT_OF_ENV_SPACE);
    }
}

#if defined(SELF_RECURSE)
extern void NEAR freeList(STRINGLIST*);
extern void NEAR freeMacroTable(MACRODEF *table[]);

LOCAL void NEAR
freeStructures(void)
{
    unsigned num;
    MAKEOBJECT *tmpObjectT;
    MAKEOBJECT *objectT;
    BUILDLIST *buildL, *tmpBuildL;

    freeMacroTable(macroTable);

    freeRules(rules, FALSE);		 //don't warn about rules in .SUFFIXES
    rules = NULL;

    for (num=0;(num < MAXTARGET);num++) {
	objectT = targetTable[num];
	while (tmpObjectT = objectT) {
	    objectT = objectT->next;
	    buildL = tmpObjectT->buildList;
	    while (tmpBuildL = buildL) {
		buildL = buildL->next;
		block = tmpBuildL->buildBlock;

                //
                // 15-May-92  BryanT  Macros are freed from the macroTable.
                //                    Don't do it here...  Commands and
                //                    dependents are freed by endNameList.
                //                    Not here
                //

                // freeList(block->dependents);
                // freeList(block->buildCommands);
                // freeList(block->dependentMacros);
                // freeList(block->buildMacros);

		FREE(block);
		FREE(tmpBuildL);
	    }
	    FREE(tmpObjectT->name);
	    FREE(tmpObjectT);
	}
    }
    freeList(inlineFileList);		//The Global inline file list
    freeList(dotSuffixList);
    FREE(fName);
}
#endif
/*
 * chkPrecious -- handle ^c or ^Break
 *
 *  Actions:	unlink all non-precious files and unrequired scriptFiles
 *		quit with error message (makeError unlinks temp. files)
 */
#ifdef OS2_SIGNALS
void pascal
#else
LOCAL void __cdecl
#endif
chkPrecious(
#ifdef OS2_SIGNALS
    USHORT usSigArg,
    USHORT usSigNum
#else
    int sig
#endif
) {
    extern UCHAR okToDelete;
#ifdef OS2_SIGNALS
    USHORT fAction;
#endif

#ifdef DOS
    //change directory to startup directory; ignore error code ... we
    //cannot handle it
    chdir(startupDir);
#endif

    /* disable ctrl-C during handler */
#ifdef OS2_SIGNALS
    DOSSETSIGHANDLER(NULL, NULL, &fAction, SIGA_IGNORE, SIG_CTRLC);
    if (_osmode == OS2_MODE) {
	DOSSETSIGHANDLER(NULL, NULL, &fAction, SIGA_IGNORE, SIG_CTRLBREAK);
	DOSSETSIGHANDLER(NULL, NULL, &fAction, SIGA_IGNORE, SIG_KILLPROCESS);
    }
#endif
    signal(SIGINT, SIG_IGN);
    signal(SIGTERM, SIG_IGN);
    delScriptFiles();
#ifdef OS2_SIGNALS
    DosExit(EXIT_PROCESS, 0L);
#endif
    if (okToDelete
#ifndef NO_OPTION_Z
	  && OFF(gFlags, F1_REVERSE_BATCH_FILE)
#endif
	  && OFF(flags, F2_NO_EXECUTE)
	  && OFF(gFlags, F1_TOUCH_TARGETS)
	  && dollarAt
	  && _access(dollarAt, 0x00)   //	 existence check
	  && !isPrecious(dollarAt))
	if (_unlink(dollarAt) == 0)
	    makeError(line, REMOVED_TARGET, dollarAt);
    makeError(0, USER_INTERRUPT);
}

LOCAL UCHAR NEAR
isPrecious(p)
char *p;
{
   STRINGLIST *temp;

   for (temp = dotPreciousList; temp; temp = temp->next)
       if (!_ftcsicmp(temp->text, p))
	   return(1);
   return(0);

}

/*** delScriptFiles -- deletes script files ************************************
*
* Scope:
*  Global
*
* Purpose:
*  Since script files may be reused in the makefile the script files which have
*  NOKEEP action specified are deleted at the end of the make.
*
* Input:
*
* Output:
*
* Errors/Warnings:
*
* Assumes:
*
* Modifies Globals:
*
* Uses Globals:
*  delList -- the list of script files to be deleted
*
* Notes:
*  We ignore the exit code as a result of a delete because the system will
*  inform the user that a delete failed.
*
*******************************************************************************/
void NEAR
delScriptFiles(void)
{
    STRINGLIST *del;
#ifndef NO_OPTION_Z
    STRINGLIST *revCmd;
#endif

    _fcloseall();

#ifndef NO_OPTION_Z
    if (ON(gFlags, F1_REVERSE_BATCH_FILE)) {
	revCmd = makeNewStrListElement();
	revCmd->text = makeString(":ABEND");
	prependItem(&revList, revCmd);
    }
#endif

    for (del = delList; del;del = del->next) {
#ifndef NO_OPTION_Z
	if (ON(gFlags, F1_REVERSE_BATCH_FILE)) {
	    revCmd = makeNewStrListElement();
	    revCmd->text = (char *)allocate(5 + _ftcslen(del->text) + 5 + 1);
	    sprintf(revCmd->text, "@del %s >nul", del->text);
	    //UNDONE: Is the next one more efficient than prev ? Investigate
	    //_ftcscat(_ftcscat(_ftcscpy(revCmd->text, "del "), del->text), " >nul");
	    prependItem(&revList, revCmd);
	}
	else
#endif
	    _unlink(del->text);
	//UNDONE: Investigate whether next is really needed
	if (ON(flags, F2_NO_EXECUTE)) {
	    printf("\tdel %s\n", del->text);
	    fflush(stdout);
	}
    }
}

/*** removeTrailChars - removes trailing blanks and dots ***********************
*
* Scope:
*  Local.
*
* Purpose:
*  OS/2 1.2 filenames dictate removal of trailing blanks and periods. This
*  function removes them from filenames provided to it.
*
* Input:
*  szFile - name of file
*
* Output:
*
* Errors/Warnings:
*
* Assumes:
*
* Modifies Globals:
*
* Uses Globals:
*
* Notes:
*  This function handles Quoted filenames as well. It maintains the quotes if
*  they were present. This is basically for OS/2 1.2 filename support.
*
*******************************************************************************/
void NEAR
removeTrailChars(szFile)
char *szFile;
{
    char *t = szFile + _ftcslen(szFile) - 1;
    BOOL fQuoted = FALSE;

    if (*szFile == '"' && *t == '"') {
	//Quoted so set flag
	t--;
	fQuoted = TRUE;
    }
    //Scan backwards for trailing characters
    while (t > szFile && (*t == ' ' || *t == '.'))
	t--;
    //t points to last non-trailing character
    //If it was quoted add quotes to the end
    if (fQuoted)
	*++t = '"';
    t[1] = '\0';
}