1459 lines
44 KiB
C
1459 lines
44 KiB
C
/*** Exec.C - Contains routines that have do to with execing programs ********
|
||
*
|
||
* Copyright (c) 1988-1991, Microsoft Corporation. All Rights Reserved.
|
||
*
|
||
* Purpose:
|
||
* Contains routines that spawn programs ...
|
||
*
|
||
* Revision History:
|
||
* 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*
|
||
* 06-Oct-1992 GBS Removed extern for _pgmptr
|
||
* 10-Aug-1992 GBS Change file parsing in execLine to use splitpath
|
||
* 19-Aug-1992 SS Remove Quotes from cd argument.
|
||
* 08-Jun-1992 SS add IDE feedback support
|
||
* 08-Jun-1992 SS Port to DOSX32
|
||
* 16-May-1991 SB Created from routines that existed elsewhere
|
||
*
|
||
* Notes:
|
||
*
|
||
* Notes:
|
||
* Functions currently in this module ...
|
||
*
|
||
* buildArgumentVector - local
|
||
* doCommands - public (build.c)
|
||
* execLine - public (rpn.c)
|
||
* execCommand - local (undone, currently common to do & iterate)
|
||
* expandCommandLine - local
|
||
* fDoRedirection - local
|
||
* fEmulateCommand - local
|
||
* getComSpec - local
|
||
* iterateCommand - local
|
||
* redirect - local
|
||
* removeQuotes - local
|
||
* touch - local
|
||
*
|
||
*****************************************************************************/
|
||
|
||
/* INCLUDEs */
|
||
|
||
#include "nmake.h"
|
||
#include "nmmsg.h"
|
||
#include "proto.h"
|
||
#include "globals.h"
|
||
#include "grammar.h"
|
||
|
||
|
||
/* Constant DEFINEs */
|
||
|
||
#define numInternals (sizeof(internals) / sizeof(char *))
|
||
#define SLASH '\\'
|
||
#define PUBLIC
|
||
#define QUOTE '\"'
|
||
|
||
/* Extern PROTOTYPEs */
|
||
|
||
#ifndef NO_OPTION_Z
|
||
extern STRINGLIST * NEAR canonCmdLine(char *);
|
||
#endif
|
||
extern void NEAR copyMacroTable(MACRODEF *old[], MACRODEF *new[]);
|
||
|
||
#if defined(DOS) && !defined(FLAT)
|
||
extern int NEAR doSuperSpawn(char *, char **);
|
||
#endif
|
||
|
||
extern void NEAR freeEnviron(char **);
|
||
extern void NEAR freeMacroTable(MACRODEF *table[]);
|
||
extern BOOL NEAR processInline(char *, char **, STRINGLIST **);
|
||
extern char * NEAR SearchRunPath(char *, char *);
|
||
extern void CDECL NEAR makeIdeMessage (unsigned, unsigned,...);
|
||
|
||
#if defined(FLAT)
|
||
extern UCHAR fRunningUnderTNT;
|
||
#endif
|
||
|
||
/* Local PROTOTYPEs */
|
||
|
||
LOCAL void NEAR buildArgumentVector(unsigned*, char**, char *);
|
||
LOCAL char * NEAR expandCommandLine(void);
|
||
#if defined(DOS)
|
||
LOCAL BOOL NEAR fDoRedirection(char*, int*, int*);
|
||
LOCAL BOOL NEAR redirect(char*, unsigned);
|
||
#endif
|
||
LOCAL BOOL NEAR fEmulateCommand(int argc, char **argv, int *pStatus);
|
||
LOCAL char * NEAR getComSpec(void);
|
||
LOCAL BOOL NEAR iterateCommand(char*, STRINGLIST*, UCHAR, UCHAR, char *, unsigned*);
|
||
LOCAL void NEAR removeQuotes(int, char **);
|
||
LOCAL void NEAR touch(char*, BOOL);
|
||
|
||
|
||
/* Extern VARIABLEs */
|
||
|
||
//buffer for path of .cmd/.bat
|
||
extern char NEAR bufPath[];
|
||
extern char NEAR fileStr[MAXNAME];
|
||
extern char * NEAR initSavPtr;
|
||
extern char * NEAR makeStr;
|
||
#ifdef DEBUG_MEMORY
|
||
extern FILE *memory;
|
||
#endif
|
||
extern char * NEAR progName;
|
||
extern unsigned NEAR saveBytes;
|
||
extern char * NEAR shellName;
|
||
|
||
|
||
/* Local VARIABLEs */
|
||
|
||
#ifndef NO_OPTION_Z
|
||
LOCAL char batchIfCmd[] = "@if errorlevel %3d @goto NMAKEEXIT";
|
||
#endif
|
||
//cmd.exe and command.com internal commands
|
||
LOCAL char *internals[] = {
|
||
"BREAK", "CD", "CHDIR", "CLS", "COPY", "CTTY", "DATE", "DEL", "DIR",
|
||
"DIR.", "ECHO", "ECHO.", "ERASE", "EXIT", "FOR", "GOTO", "IF", "MD",
|
||
"MKDIR", "PATH", "PAUSE", "PROMPT", "RD", "REM", "REN", "RENAME",
|
||
"RMDIR", "SET", "SHIFT", "TIME", "TYPE", "VER", "VERIFY", "VOL"
|
||
};
|
||
LOCAL char szCmdLineBuf[MAXCMDLINELENGTH];
|
||
|
||
/* FUNCTIONs in Alphabetical order */
|
||
|
||
/*** buildArgumentVector -- builds an argument vector from a command line ****
|
||
*
|
||
* Scope:
|
||
* Local.
|
||
*
|
||
* Purpose:
|
||
* It builds an argument vector for a command line. This argument vector can
|
||
* be used by spawnvX routines. The algorithm is explained in the notes below.
|
||
*
|
||
* Input:
|
||
* argc -- The number of arguments created in the argument vector
|
||
* argv -- The actual argument vector created
|
||
* cmdline -- The command line whose vector is required
|
||
*
|
||
* Output:
|
||
* Returns the number of arguments and the argument vector as parameters
|
||
*
|
||
* Errors/Warnings:
|
||
* Assumes:
|
||
* That the behaviour of cmd.exe i.e. parses quotes but does not disturb them.
|
||
* Assumes that the SpawnVX routines will handle quotes as well as escaped
|
||
* chars.
|
||
*
|
||
* Modifies Globals:
|
||
* Uses Globals:
|
||
* Notes:
|
||
* Scan the cmdline from left to the end building the argument vector along
|
||
* the way. Whitespace delimits arguments except for the first argument for
|
||
* which the switch char '/' is also allowed. Backslash can be used to escape
|
||
* a char and so ignore the character following it. Parse the quotes along
|
||
* the way. If an argument begins with a double-quote then all characters till
|
||
* an unescaped double-quote are part of that argument. Likewise, if an
|
||
* unescaped Doublequote occurs within an argument then the above follows. If
|
||
* the end of the command line comes before the closing quote then the
|
||
* argument goes as far as that.
|
||
*
|
||
*****************************************************************************/
|
||
LOCAL void NEAR
|
||
buildArgumentVector(argc, argv, cmdline)
|
||
unsigned *argc;
|
||
char **argv;
|
||
char *cmdline;
|
||
{
|
||
char *p; /* current loc in cmdline */
|
||
char *end; /* end of command line */
|
||
BOOL fFirstTime = TRUE; /* true if 1st argument */
|
||
|
||
// 11-May-1993 HV _mbschr() bug: return NULL
|
||
// end = _ftcschr(p = cmdline, '\0');
|
||
// Work around:
|
||
end = p = cmdline;
|
||
while (*end)
|
||
end++;
|
||
|
||
for (*argc = 0; *argc < MAXARG && p < end; ++*argc) {
|
||
p += _ftcsspn(p, " \t"); /* skip whitespace*/
|
||
if (p >= end)
|
||
break;
|
||
*argv++ = p;
|
||
if (*p == '\"') {
|
||
/* If the word begins with double-quote, find the next
|
||
* occurrence of double-quote which is not preceded by backslash
|
||
* (same escape as C runtime), or end of string, whichever is
|
||
* first. From there, find the next whitespace character.
|
||
*/
|
||
for (++p; p < end; ++p) {
|
||
if (*p == '\\')
|
||
++p; //skip escaped character
|
||
else if (*p == '\"')
|
||
break;
|
||
}
|
||
if (p >= end)
|
||
continue;
|
||
++p;
|
||
p = _ftcspbrk(p, " \t");
|
||
}
|
||
else {
|
||
/* For the first word on the command line, accept the switch
|
||
* character and whitespace as terminators. Otherwise, just
|
||
* whitespace.
|
||
*/
|
||
p = _ftcspbrk(p, " \t\"/");
|
||
for (;p && p < end;p = _ftcspbrk(p+1, " \t\"/")) {
|
||
if (*p == '/' && !fFirstTime)
|
||
continue; //after 1st word '/' is !terminator
|
||
else break;
|
||
}
|
||
if (p && *p == '\"') {
|
||
for (p++;p < end;p++) { //inside quote so skip to next one
|
||
if (*p == '\"')
|
||
break;
|
||
}
|
||
p = _ftcspbrk(p, " \t"); //after quote go to first whitespace
|
||
}
|
||
if (fFirstTime) {
|
||
fFirstTime = FALSE;
|
||
/* If switch char terminates the word, replace it with 0,
|
||
* re-allocate the word on the heap, restore the switch and
|
||
* set p just before the switch. It would be easier to
|
||
* shift everything right but then we have to worry about
|
||
* overflow.
|
||
*/
|
||
if (p && *p == '/') {
|
||
*p = '\0';
|
||
argv[-1] = makeString(argv[-1]);
|
||
*p-- = '/';
|
||
}
|
||
|
||
}
|
||
}
|
||
if (!p)
|
||
p = end;
|
||
/* Now, p points to end of command line argument */
|
||
*p++ = '\0';
|
||
}
|
||
*argv = NULL;
|
||
}
|
||
|
||
PUBLIC int NEAR
|
||
doCommands(
|
||
char *name,
|
||
STRINGLIST *s,
|
||
STRINGLIST *t,
|
||
UCHAR buildFlags,
|
||
char *pFirstDep
|
||
) {
|
||
char *u,
|
||
*v;
|
||
UCHAR cFlags;
|
||
unsigned status = 0;
|
||
char c;
|
||
char *Cmd;
|
||
char *pLine;
|
||
BOOL fExpanded;
|
||
char *pCmd;
|
||
|
||
#ifndef NO_OPTION_Z
|
||
STRINGLIST *z, *zList; //For -z option
|
||
#endif
|
||
|
||
#ifdef DEBUG_ALL
|
||
if (fDebug)
|
||
{
|
||
printf("* doCommands: %s,\n", name);
|
||
DumpList(s);
|
||
DumpList(t);
|
||
}
|
||
#endif
|
||
|
||
#ifdef DEBUG_ALL
|
||
printf ("DEBUG: doCommands 1\n");
|
||
#endif
|
||
++numCommands;
|
||
if (ON(gFlags, F1_QUESTION_STATUS))
|
||
return(0);
|
||
|
||
makeIdeMessage (3, MSG_IDE_BUILD, name);
|
||
|
||
if (ON(gFlags, F1_TOUCH_TARGETS)) {
|
||
touch(name, (USHORT) ON(buildFlags, F2_NO_EXECUTE));
|
||
return(0);
|
||
}
|
||
|
||
#ifdef DEBUG_ALL
|
||
printf ("DEBUG: doCommands 2\n");
|
||
#endif
|
||
|
||
for (; s; s = s->next) {
|
||
fExpanded = processInline(s->text, &Cmd, &t);
|
||
cFlags = 0;
|
||
errorLevel = 0L;
|
||
u = Cmd;
|
||
for (v = u; *v; ++v) {
|
||
if (*v == ESCH) ++v;
|
||
else if (*v == '$') {
|
||
if (*++v == '$') continue;
|
||
// commented out 15-Apr-93 by JonM. This code forces recursive nmake to be
|
||
// executed even if -n, but it's hosed (the -n is not passed to the recursive
|
||
// nmake), and the whole thing sounds like a bad idea anyway, so I'm going to
|
||
// turn it off.
|
||
// if (!_ftcsncmp(v, "(MAKE)", 6)) {
|
||
// SET(cFlags, C_EXECUTE);
|
||
// break;
|
||
// }
|
||
}
|
||
}
|
||
#ifdef DEBUG_ALL
|
||
printf ("DEBUG: doCommands 2.1\n");
|
||
#endif
|
||
for (c = *u; c == '!'
|
||
|| c == '-'
|
||
|| c == '@'
|
||
|| c == ESCH
|
||
|| WHITESPACE(c); c = *++u) {
|
||
switch (c) {
|
||
case ESCH: if (c = *++u, WHITESPACE(c)) c = ' '; /*keep going*/
|
||
else c = ESCH;
|
||
break;
|
||
case '!': SET(cFlags, C_ITERATE);
|
||
break;
|
||
case '-': SET(cFlags, C_IGNORE);
|
||
++u;
|
||
if (_istdigit(*u)) {
|
||
char *pNumber = u;
|
||
|
||
errorLevel = strtol(u, &u, 10);
|
||
if (errno == ERANGE) {
|
||
*u = '\0';
|
||
makeError(line, CONST_TOO_BIG, pNumber);
|
||
}
|
||
while(_istspace(*u))
|
||
u++;
|
||
}
|
||
else errorLevel = 255;
|
||
--u;
|
||
break;
|
||
case '@': if (
|
||
#ifndef NO_OPTION_Z
|
||
OFF(gFlags, F1_REVERSE_BATCH_FILE) ||
|
||
#endif
|
||
OFF(flags, F2_NO_EXECUTE))
|
||
SET(cFlags, C_SILENT);
|
||
break;
|
||
}
|
||
if (c == ESCH) break; /* stop parsing for cmd-line options */
|
||
}
|
||
#ifdef DEBUG_ALL
|
||
printf ("DEBUG: doCommands 2.2\n");
|
||
#endif
|
||
if (ON(cFlags, C_ITERATE) &&
|
||
iterateCommand(u, t, buildFlags, cFlags, pFirstDep, &status)) {
|
||
//The macros used by the command have to be freed & so we do so
|
||
v = u;
|
||
#ifdef DEBUG_ALL
|
||
printf ("DEBUG: doCommands 2.21\n");
|
||
#endif
|
||
if (_ftcschr(u, '$'))
|
||
u = expandMacros(u, &t);
|
||
#ifdef DEBUG_ALL
|
||
printf ("DEBUG: doCommands 2.22\n");
|
||
#endif
|
||
if (v != u) FREE(u);
|
||
if (OFF(buildFlags, F2_IGNORE_EXIT_CODES) && fOptionK && status &&
|
||
status > (unsigned)errorLevel)
|
||
break;
|
||
continue;
|
||
}
|
||
v = u;
|
||
#ifdef DEBUG_ALL
|
||
printf ("DEBUG: doCommands 2.23\n");
|
||
#endif
|
||
if (!fExpanded && _ftcschr(u, '$'))
|
||
u = expandMacros(u, &t);
|
||
#ifdef DEBUG_ALL
|
||
printf ("DEBUG: doCommands 2.24\n");
|
||
#endif
|
||
|
||
expandExtmake(CmdLine, u, pFirstDep);
|
||
#ifndef NO_OPTION_Z
|
||
if (ON(gFlags, F1_REVERSE_BATCH_FILE))
|
||
zList = canonCmdLine(CmdLine);
|
||
else {
|
||
zList = makeNewStrListElement();
|
||
zList->text = CmdLine;
|
||
}
|
||
|
||
#ifdef DEBUG_ALL
|
||
printf ("DEBUG: doCommands 2.3\n");
|
||
#endif
|
||
for (z = zList; z; z = z->next) {
|
||
pLine = z->text;
|
||
#else
|
||
pLine = CmdLine;
|
||
#endif
|
||
status = execLine(pLine,
|
||
(BOOL)(ON(buildFlags, F2_NO_EXECUTE)
|
||
|| (OFF(buildFlags,F2_NO_ECHO)
|
||
&& OFF(cFlags,C_SILENT))),
|
||
(BOOL)((OFF(buildFlags, F2_NO_EXECUTE)
|
||
#ifndef NO_OPTION_Z
|
||
&& OFF(gFlags, F1_REVERSE_BATCH_FILE)
|
||
#endif
|
||
)
|
||
|| ON(cFlags, C_EXECUTE)),
|
||
(BOOL)ON(cFlags, C_IGNORE), &pCmd);
|
||
if (OFF(buildFlags, F2_IGNORE_EXIT_CODES)) {
|
||
#ifndef NO_OPTION_Z
|
||
if (ON(gFlags, F1_REVERSE_BATCH_FILE)) {
|
||
STRINGLIST *revCmd;
|
||
revCmd = makeNewStrListElement();
|
||
revCmd->text = (char *)rallocate(_ftcslen(batchIfCmd) + 1);
|
||
sprintf(revCmd->text, batchIfCmd,
|
||
(errorLevel == 255 ? errorLevel: errorLevel + 1));
|
||
prependItem(&revList, revCmd);
|
||
}
|
||
else
|
||
#endif
|
||
if (status && status > (unsigned)errorLevel) {
|
||
if (!fOptionK)
|
||
makeError(0, BAD_RETURN_CODE, pCmd, status);
|
||
#ifndef NO_OPTION_Z
|
||
else
|
||
break;
|
||
#endif
|
||
}
|
||
}
|
||
#ifndef NO_OPTION_Z
|
||
}
|
||
#endif
|
||
if (v != u)
|
||
FREE(u);
|
||
#ifndef NO_OPTION_Z
|
||
if (ON(gFlags, F1_REVERSE_BATCH_FILE))
|
||
freeList(zList);
|
||
else
|
||
FREE_STRINGLIST(zList);
|
||
#endif
|
||
FREE(Cmd);
|
||
if (OFF(buildFlags, F2_IGNORE_EXIT_CODES) && fOptionK && status &&
|
||
status > (unsigned)errorLevel)
|
||
break;
|
||
}
|
||
#ifdef DEBUG_ALL
|
||
printf ("DEBUG: doCommands 3\n");
|
||
#endif
|
||
if (OFF(buildFlags, F2_IGNORE_EXIT_CODES) && fOptionK)
|
||
return(status);
|
||
else
|
||
return(0);
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
/*** execLine -- execute a command line **************************************
|
||
*
|
||
* Scope:
|
||
* Global (build.c, rpn.c)
|
||
*
|
||
* Purpose:
|
||
* Parses the command line for redirection characters and redirects stdin and
|
||
* stdout if "<", ">", or ">>" are seen. If any of the following occur,
|
||
* restore the original stdin and stdout, pass the command to the shell, and
|
||
* invoke the shell:
|
||
* - the command line contains "|" (pipe)
|
||
* - a syntax error occurs in parsing the command line
|
||
* - an error occurs in redirection
|
||
* Otherwise, attempt to invoke the command directly, then restore the
|
||
* original stdin and stdout. If this invocation failed because of
|
||
* file-not-found then pass the command to the shell and invoke the shell.
|
||
*
|
||
* Input:
|
||
* line -- The command line to be executed
|
||
* echoCmd -- determines if the command line is to be echoed
|
||
* doCmd -- determines if the command is to be actually executed
|
||
* ignoreReturn -- determines if NMAKE is to ignore the return code on
|
||
* execution
|
||
* ppCmd -- if non-null then on error returns command executed
|
||
*
|
||
* Output:
|
||
* Returns ... return code from child process
|
||
* ... -1 if error occurs
|
||
*
|
||
* Assumes:
|
||
* Whatever it assumes
|
||
*
|
||
* Modifies Globals:
|
||
* global -- how/what
|
||
*
|
||
* Uses Globals:
|
||
* global used and why
|
||
*
|
||
* Notes:
|
||
* 1/ Quoted strings can have redir chars "<>" which will be skipped over.
|
||
* 2/ Unmatched quotes cause error; redir chars are replaced by space char.
|
||
* 3/ Dup stdin file handle then redirect it. If we have to use the shell,
|
||
* restore the original command line.
|
||
* 4/ Emulate certain commands such as "cd" to help prevent some makefiles
|
||
* from breaking when ported from DOS to OS/2.
|
||
*
|
||
* Algorithm for spawning commands:
|
||
* If we can't handle the syntax, let the shell do everything. Otherwise,
|
||
* first check to see if the command (without extension) is a DOS built-in &
|
||
* if it is, call the shell to execute it (this is how cmd.exe behaves)
|
||
* If it's not a built-in, we check to see if it has a .cmd or a .bat
|
||
* extension (depending on whether we're in DOS or OS/2). If it does, we
|
||
* call system() to execute it.
|
||
* If it has some other extension, we ignore the extension and go looking for
|
||
* a .cmd or .bat file. If we find it, we execute it with system().
|
||
* Otherwise, we try to spawn it (without extension). If the spawn fails,
|
||
* we issue an unknown program error.
|
||
*
|
||
*****************************************************************************/
|
||
int NEAR
|
||
execLine(
|
||
char *line,
|
||
BOOL echoCmd,
|
||
BOOL doCmd,
|
||
BOOL ignoreReturn,
|
||
char **ppCmd
|
||
) {
|
||
char *argv[3+MAXNAME/2];
|
||
BOOL fUseShell;
|
||
int oldIn = -1, //old stdin file handle
|
||
oldOut = -1, //old stdout file handle
|
||
status;
|
||
unsigned argc;
|
||
static char bufName[MAXNAME] = { 0 }; //Buffer for program name
|
||
BOOL fInternalCmd = FALSE;
|
||
static char szDrive[_MAX_DRIVE], szDir[_MAX_DIR], szFileName[_MAX_FNAME];
|
||
|
||
progName = NULL;
|
||
if (!shellName)
|
||
shellName = getComSpec();
|
||
|
||
switch (*line)
|
||
{
|
||
case '@':
|
||
// Turn off echo if it was on. This handles the case where the "@"
|
||
// was in a macro.
|
||
//
|
||
line++;
|
||
if (doCmd)
|
||
echoCmd = 0;
|
||
break;
|
||
|
||
case '-':
|
||
ignoreReturn = TRUE;
|
||
++line;
|
||
if (_istdigit(*line))
|
||
{
|
||
char * pNumber = line;
|
||
errorLevel = strtol(line, &line, 10);
|
||
if (errno == ERANGE) {
|
||
*line = '\0';
|
||
makeError(0, CONST_TOO_BIG, pNumber); // Todo: replace 0 with line number
|
||
}
|
||
while(_istspace(*line))
|
||
line++;
|
||
}
|
||
else
|
||
errorLevel = 255;
|
||
break;
|
||
}
|
||
|
||
//handle null command ...
|
||
if (!line[0])
|
||
return(0);
|
||
//copy command line into buffer
|
||
if (_ftcslen(line) < MAXCMDLINELENGTH)
|
||
_ftcscpy(szCmdLineBuf, line);
|
||
else
|
||
makeError(0, COMMAND_TOO_LONG, line);
|
||
#ifndef NO_OPTION_Z
|
||
//If -z and '$(MAKE)' then echo it
|
||
if (echoCmd && ON(gFlags, F1_REVERSE_BATCH_FILE)
|
||
&& !_ftcsnicmp(szCmdLineBuf, makeStr, _ftcslen(makeStr))) {
|
||
STRINGLIST *revCmd;
|
||
revCmd = makeNewStrListElement();
|
||
revCmd->text = (char *)rallocate(1 + _ftcslen(szCmdLineBuf) + 3 + 1);
|
||
|
||
sprintf(revCmd->text, "\t%s /Z%s", makeStr, szCmdLineBuf + _ftcslen(makeStr));
|
||
prependItem(&revList, revCmd);
|
||
return(0);
|
||
}
|
||
#endif
|
||
//If -n then echo command if not '$(MAKE)'
|
||
if (echoCmd
|
||
// 15-Apr-93 JonM ... we are no longer executing recursive makes if -n, so
|
||
// we want to echo them.
|
||
// && (_strnicmp(szCmdLineBuf, makeStr, strlen(makeStr)) ||
|
||
// OFF(flags, F2_NO_EXECUTE))
|
||
)
|
||
{
|
||
printf("\t%s\n", szCmdLineBuf);
|
||
fflush(stdout);
|
||
}
|
||
#if defined(DOS)
|
||
//for DOS use shell only if we have to because COMMAND.COM does not
|
||
//return child return codes; redirect, except for -n
|
||
fUseShell =
|
||
#if defined(FLAT)
|
||
!fRunningUnderTNT || // use shell only if TNT, not NT
|
||
#endif
|
||
(BOOL) (OFF(flags, F2_NO_EXECUTE) &&
|
||
fDoRedirection(szCmdLineBuf, &oldIn, &oldOut))
|
||
#ifndef NO_OPTION_Z
|
||
|| ON(gFlags, F1_REVERSE_BATCH_FILE)
|
||
#endif
|
||
;
|
||
#else
|
||
//for OS/2 let the shell do the work
|
||
fUseShell = TRUE;
|
||
#endif
|
||
|
||
/* Allocate a copy of the command line on the heap because in a
|
||
* recursive call to doMake(), argv pointers will be allocated from
|
||
* the static buffer which will then be trashed. For buildArg...().
|
||
*/
|
||
pCmdLineCopy = makeString(szCmdLineBuf);
|
||
/* Build arg vector. This is a waste on OS/2 since we're probably
|
||
* going to use the shell, except we have to check for cd, $(MAKE),
|
||
* etc. so we take advantage of the parsing code.
|
||
*/
|
||
|
||
buildArgumentVector(&argc, argv, pCmdLineCopy);
|
||
|
||
// 11-May-1993 HV The _mbsicmp() does not like NULL pointer
|
||
// so I have to check before calling it.
|
||
if (argv[0] && makeStr && !_ftcsicmp(argv[0], makeStr))
|
||
*argv = _pgmptr;
|
||
|
||
/* Copy program name into buffer. Can't just use argv[0] since this is
|
||
* from heap and will be freed before it may be used in an error message.
|
||
*/
|
||
if (argc)
|
||
progName = _ftcsncpy(bufName, argv[0], sizeof(bufName) - 1);
|
||
else
|
||
return(0); // for case when macro command is null
|
||
|
||
if (!doCmd) { /* don't execute command if doCmd false*/
|
||
#ifndef NO_OPTION_Z
|
||
if (ON(gFlags, F1_REVERSE_BATCH_FILE)) {
|
||
STRINGLIST *revCmd;
|
||
char *echoStr;
|
||
revCmd = makeNewStrListElement();
|
||
revCmd->text = (char *)rallocate(1 + _ftcslen(szCmdLineBuf) + 1);
|
||
echoStr = echoCmd ? "\t" : "@";
|
||
_ftcscat(_ftcscpy(revCmd->text, echoStr), szCmdLineBuf);
|
||
prependItem(&revList, revCmd);
|
||
}
|
||
#endif
|
||
//For -n, emulate if possible.
|
||
if (fEmulateCommand(argc, argv, &status)) {
|
||
if (status && ppCmd)
|
||
*ppCmd = makeString(*argv);
|
||
return(status); /* return status */
|
||
}
|
||
else
|
||
return(0);
|
||
}
|
||
/* Try emulating the command if appropriate. If not, and we should not
|
||
* use the shell, try spawning command directly.
|
||
*/
|
||
//Check status when emulating
|
||
if (fEmulateCommand(argc, argv, &status))
|
||
fUseShell = FALSE;
|
||
#if defined(DOS)
|
||
else if (!fUseShell) {
|
||
int lo = 0, mid, result, hi = numInternals; //for binary search
|
||
errno = 0;
|
||
/* Do binary search of *argv in internal commands. */
|
||
for (mid = (hi+lo) / 2; hi - lo > 1; mid = (hi+lo) / 2) {
|
||
if (!(result = _ftcsicmp(*argv, internals[mid]))) {
|
||
fUseShell = TRUE;
|
||
break;
|
||
}
|
||
else if (result < 0) hi = mid;
|
||
else lo = mid;
|
||
}
|
||
fInternalCmd = TRUE;
|
||
if (!fUseShell) {
|
||
char *p;
|
||
|
||
/* Ignore any given extention. This is what DOS does. */
|
||
_splitpath( progName, szDrive, szDir, szFileName, NULL );
|
||
_makepath( progName, szDrive, szDir, szFileName, NULL );
|
||
|
||
// p = _ftcsrchr(progName, '.');
|
||
// if (p && p[1] != '\\' && p[1] != '/')
|
||
// *p = 0;
|
||
|
||
/* Search for the program in the search path. If found,
|
||
* p points to extention else NULL.
|
||
*/
|
||
p = SearchRunPath(progName, bufPath);
|
||
if (!p) {
|
||
/* If not found, set up an error since COMMAND will
|
||
* return 0. This risks future incompatibility if new
|
||
* DOS built-in commands are added.
|
||
*/
|
||
errno = ENOENT;
|
||
status = -1;
|
||
} else if (p[1] == 'b' || _ftcsicmp(p, ".cmd") == 0)
|
||
//If .bat extention, use COMMAND.COM.
|
||
fUseShell = TRUE;
|
||
else {
|
||
//Spawn command directly. Capitalize argv[0] since
|
||
//COMMAND.COM does.
|
||
for (p = *argv; *p; p++)
|
||
*p = (char)_totupper(*p);
|
||
#if defined(DOS)
|
||
#ifdef CHECK_CMD_LIMIT
|
||
if (_ftcslen(line) >= DOSCMDLINELIMIT)
|
||
makeError(0, COMMAND_TOO_LONG, line);
|
||
#endif
|
||
#endif
|
||
#ifdef USE_SUPER
|
||
status = doSuperSpawn(bufPath, argv);
|
||
#else
|
||
{
|
||
char * t = argv[0];
|
||
argv[0] = bufPath;
|
||
status = SPAWNVP(P_WAIT, bufPath, argv);
|
||
argv[0] = t;
|
||
}
|
||
#endif
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
|
||
if (oldIn != -1) {
|
||
if (_dup2(oldIn, _fileno(stdin)) == -1)
|
||
makeError(0, BUILD_INTERNAL);
|
||
_close(oldIn);
|
||
}
|
||
if (oldOut != -1) {
|
||
if (_dup2(oldOut, _fileno(stdout)) == -1)
|
||
makeError(0, BUILD_INTERNAL);
|
||
_close(oldOut);
|
||
}
|
||
|
||
if (fUseShell) {
|
||
_ftcscpy(szCmdLineBuf, line);
|
||
#if defined(DOS)
|
||
if (_ftcslen(line) >= DOSCMDLINELIMIT)
|
||
makeError(0, COMMAND_TOO_LONG, line);
|
||
for (p = szCmdLineBuf; *p && *p != ' ' && *p != '\t'; p++)
|
||
*p = (char)_totupper(*p);
|
||
#endif
|
||
|
||
#ifdef DEBUG_MEMORY
|
||
if (fDebug) {
|
||
mem_status();
|
||
fprintf(memory, "Spawning '%s'\n", szCmdLineBuf);
|
||
}
|
||
#endif
|
||
if (fInternalCmd)
|
||
status = SYSTEM(szCmdLineBuf);
|
||
else {
|
||
int i;
|
||
|
||
for (i=argc; i >= 0 ; i--) {
|
||
argv[i+2] = argv[i];
|
||
}
|
||
argv[0] = shellName;
|
||
argv[1] = "/c";
|
||
|
||
#ifdef USE_SUPER
|
||
status = doSuperSpawn(argv[0], argv);
|
||
#else
|
||
status = SPAWNVP(P_WAIT, argv[0], argv);
|
||
#endif
|
||
}
|
||
|
||
#ifdef DEBUG_MEMORY
|
||
if (fDebug)
|
||
mem_status();
|
||
#endif
|
||
}
|
||
|
||
//BUGBUG: NT version 262 has a problem with the way the run-time execs
|
||
// a process. When the run-time posts a WaitForSingleObject to
|
||
// make sure the process has finished, the kernal occasionally sets
|
||
// the exit status of the process to STATUS_THREAD_IS_TERMINATING
|
||
// (0xC000004B). We test here to make sure that case doesn't cause
|
||
// problems later on...
|
||
|
||
if (status == 0xc000004b)
|
||
{
|
||
fprintf(stderr, "spawn returned 0xc000004b ... Benign\n");
|
||
status = 0;
|
||
}
|
||
|
||
/* Check for errors spawning command (distinct from errors *returned*
|
||
* from a successfully spawned command).
|
||
*/
|
||
if (status == -1) {
|
||
if (ignoreReturn) {
|
||
status = 0;
|
||
} else {
|
||
switch (errno) {
|
||
case 0:
|
||
// We (ie: nmake) didn't fail, but the spawned program did.
|
||
break;
|
||
|
||
case ENOENT:
|
||
makeError(0, CANT_FIND_PROGRAM, argv[0]);
|
||
break;
|
||
|
||
case ENOMEM:
|
||
makeError(0, EXEC_NO_MEM, fUseShell? argv[2]: argv[0]);
|
||
break;
|
||
|
||
default:
|
||
/* Done to flag possibly erroneous decision made here [SB] */
|
||
makeError(0, SPAWN_FAILED_ERROR, _strerror(NULL));
|
||
}
|
||
}
|
||
}
|
||
|
||
if (status && ppCmd)
|
||
*ppCmd = makeString(*argv);
|
||
|
||
FREE(pCmdLineCopy);
|
||
return(status);
|
||
}
|
||
|
||
|
||
/*** expandCommandLine -- expands %name% strings in the Command Line *******
|
||
*
|
||
* Purpose:
|
||
* The function expands '%name%' type strings in the Command Line. Its main
|
||
* job is to assist fEmulateCommand() in emulating set for OS/2.
|
||
*
|
||
* Modifies:
|
||
* buf -- The Command Line available globally
|
||
*
|
||
* Output:
|
||
* Returns -- the position of 'name=value' part in the Command Line.
|
||
* -- Null when no '=' is found so that fEmulateCommand() can pass the
|
||
* line to the shell to signal syntax error.
|
||
* Note:
|
||
* The shell does not give a syntax error for unmatched '%' and assumes it
|
||
* as just another character in this case. This behaviour is duplicated
|
||
* by expandCommandLine()
|
||
*
|
||
************************************************************************/
|
||
|
||
LOCAL char * NEAR
|
||
expandCommandLine(
|
||
void
|
||
) {
|
||
char Buf[MAXCMDLINELENGTH]; //Buffer for expanded string
|
||
char *pBuf;
|
||
char EnvBuf[MAXCMDLINELENGTH]; //getenv returned string copy
|
||
char *posName, //position of 'name=string' in Buf or buf
|
||
*p, //points into buf
|
||
*pEnv; //points into Env
|
||
char ExpandName[MAXNAME]; //%name% string
|
||
char *pExpandName;
|
||
|
||
|
||
pBuf = Buf;
|
||
_ftcscpy(pBuf, "set");
|
||
p = szCmdLineBuf + 3; // go beyond 'set'
|
||
pBuf +=3;
|
||
/* Skip whitespace */
|
||
for (;;p++) {
|
||
if (!(WHITESPACE(*p)))
|
||
break; // argc>1 <20> this will happen
|
||
else *pBuf++ = *p;
|
||
}
|
||
if (!_ftcschr(p, '='))
|
||
return(""); //Syntax error so pass to the shell
|
||
else
|
||
posName = pBuf; //fixes position of Name in Buf
|
||
/* Now we look for environment variables and expand if required */
|
||
for (;*p != '=';p++)
|
||
*pBuf++ = (char)_totupper(*p);
|
||
|
||
for (;*p;) {
|
||
if (*p == '%') {
|
||
pExpandName = &ExpandName[0];
|
||
while (*++p != '%' && *p)
|
||
*pExpandName++ = (char)_totupper(*p);
|
||
*pExpandName = '\0';
|
||
if (!*p++) { //unmatched %;so don't expand
|
||
*pBuf='\0'; //from the environment; like set
|
||
_ftcscat(Buf, ExpandName);
|
||
pBuf += _ftcslen(ExpandName);
|
||
}
|
||
else { //matched %;so expand from the environment
|
||
EnvBuf[0] = '\0';
|
||
if ((pEnv = getenv(ExpandName)) != (char *)NULL) {
|
||
_ftcscat(EnvBuf, pEnv);
|
||
*pBuf='\0';
|
||
_ftcscat(Buf,EnvBuf);
|
||
pBuf += _ftcslen(EnvBuf);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
*pBuf++ = *p++;
|
||
}
|
||
*pBuf = '\0';
|
||
_ftcscpy(szCmdLineBuf, Buf);
|
||
*posName = '\0';
|
||
posName = szCmdLineBuf + _ftcslen(Buf); //Offset into buf
|
||
return(posName);
|
||
}
|
||
|
||
#if defined(DOS)
|
||
/*
|
||
* fDoRedirection -- handle redirection if possible, else return TRUE
|
||
*
|
||
*/
|
||
LOCAL BOOL NEAR
|
||
fDoRedirection(p, oldIn, oldOut)
|
||
char *p;
|
||
int *oldIn;
|
||
int *oldOut;
|
||
{
|
||
BOOL in = FALSE,
|
||
out = FALSE;
|
||
BOOL fReturn = FALSE;
|
||
char *q;
|
||
unsigned which;
|
||
//save original string
|
||
char *t = p;
|
||
char *save = NULL;
|
||
|
||
while (q = _ftcspbrk(p, "\"<>|")) {
|
||
switch (*q) {
|
||
case '\"':
|
||
if (!(q = _ftcschr(q+1, '\"'))) {
|
||
fReturn = TRUE;
|
||
break;
|
||
}
|
||
p = ++q;
|
||
break;
|
||
case '<':
|
||
if (in) {
|
||
fReturn = TRUE;
|
||
break;
|
||
}
|
||
if (!save)
|
||
save = makeString(p);
|
||
*q++ = ' ';
|
||
p = q;
|
||
in = TRUE;
|
||
*oldIn = _dup(_fileno(stdin));
|
||
if ((*oldIn == -1)
|
||
|| !redirect(q, READ)) {
|
||
fReturn = TRUE;
|
||
break;
|
||
}
|
||
break;
|
||
case '>':
|
||
if (out) {
|
||
fReturn = TRUE;
|
||
break;
|
||
}
|
||
if (!save)
|
||
save = makeString(p);
|
||
*q++ = ' ';
|
||
p = q;
|
||
out = TRUE;
|
||
if ((*q) == '>') {
|
||
*q++ = ' ';
|
||
which = APPEND;
|
||
}
|
||
else
|
||
which = WRITE;
|
||
*oldOut = _dup(_fileno(stdout));
|
||
if ((*oldOut == -1)
|
||
|| !redirect(q, which)) {
|
||
fReturn = TRUE;
|
||
break;
|
||
}
|
||
break;
|
||
case '|':
|
||
fReturn = TRUE;
|
||
break;
|
||
default :
|
||
makeError(0, BUILD_INTERNAL);
|
||
}
|
||
if (fReturn)
|
||
break;
|
||
}
|
||
if (fReturn) {
|
||
if (save) {
|
||
_ftcscpy(p, save);
|
||
FREE(save);
|
||
}
|
||
if (in && *oldIn != -1) {
|
||
if (_dup2(*oldIn, _fileno(stdout)) == -1)
|
||
makeError(0, BUILD_INTERNAL);
|
||
_close(*oldIn);
|
||
*oldIn = -1;
|
||
}
|
||
if (out && *oldOut != -1) {
|
||
if (_dup2(*oldOut, _fileno(stdout)) == -1)
|
||
makeError(0, BUILD_INTERNAL);
|
||
_close(*oldOut);
|
||
*oldOut = -1;
|
||
}
|
||
|
||
}
|
||
return(fReturn);
|
||
}
|
||
|
||
#endif // DOS
|
||
|
||
|
||
/*** fEmulateCommand - look for certain commands and emulate them
|
||
*
|
||
* Emulate $(MAKE), cd, chdir, and <drive letter>:.
|
||
* Also emulates 'set'.
|
||
*
|
||
* RETURNS: TRUE if command emulated, FALSE if not.
|
||
*
|
||
* Note:
|
||
* In set emulation if a syntax error is discovered then it lets the
|
||
* shell handle it. It does this by returning FALSE.
|
||
*/
|
||
LOCAL BOOL NEAR
|
||
fEmulateCommand(
|
||
int argc,
|
||
char **argv,
|
||
int *pStatus
|
||
) {
|
||
char *pArg0 = *argv;
|
||
char *pArg1 = argv[1];
|
||
#if defined(SELF_RECURSE)
|
||
char *parentPtr;
|
||
MACRODEF **oldTable;
|
||
int i;
|
||
/* use local because global gets overwritten by second memmove */
|
||
BOOL fInhMacs = fInheritMacros;
|
||
#endif
|
||
|
||
/*
|
||
* If $(MAKE), save memory on recursive make's by saving the current
|
||
* state of the world and recursively calling doMake(). This saves
|
||
* the amount of memory taken up by NMAKE itself.
|
||
*/
|
||
|
||
#if !defined(SELF_RECURSE)
|
||
if (0) {}
|
||
#else
|
||
if (pArg0 == _pgmptr) { // if this is a recursive invocation
|
||
char **oldEnv;
|
||
|
||
#if defined(HEAP) && defined(TEST_RECURSION)
|
||
printf("\n**** BEFORE RECURSION ****\n");
|
||
heapdump(__FILE__, __LINE__);
|
||
#endif
|
||
|
||
parentPtr = (char *)rallocate(saveBytes);
|
||
memmove(parentPtr, &startOfSave, saveBytes);
|
||
if (fInhMacs) {
|
||
oldTable = (MACRODEF **)rallocate(MAXMACRO * sizeof(MACRODEF *));
|
||
copyMacroTable(macroTable, oldTable);
|
||
}
|
||
|
||
memmove(&startOfSave, initSavPtr, saveBytes);
|
||
|
||
/* UNDONE: Need to inherit /K and /O */
|
||
|
||
if (fInhMacs) {
|
||
for (i = 0; i < MAXMACRO; i++)
|
||
macroTable[i] = oldTable[i];
|
||
}
|
||
#ifndef NO_OPTION_Z
|
||
/* reinitialize makeflags variable */
|
||
if (ON(gFlags, F1_REVERSE_BATCH_FILE)) {
|
||
#ifdef PWB_HELP
|
||
char *p;
|
||
p = _ftcschr(getenv("MAKEFLAGS"), ' ');
|
||
*p = 'Z';
|
||
#else
|
||
return(TRUE);
|
||
#endif
|
||
}
|
||
#endif
|
||
removeQuotes(argc, argv);
|
||
// save old environ
|
||
oldEnv = environ;
|
||
// get new environ
|
||
environ = copyEnviron(environ);
|
||
|
||
*pStatus = doMake(argc, argv, parentPtr);
|
||
|
||
// free new environ; not needed anymore
|
||
freeEnviron(environ);
|
||
// restore old environ
|
||
environ = oldEnv;
|
||
|
||
if (fInhMacs) {
|
||
freeMacroTable(oldTable);
|
||
FREE(oldTable);
|
||
}
|
||
|
||
// make the heap less clustered by returning cleared area to the OS
|
||
_heapmin();
|
||
|
||
#if defined(HEAP) && defined(TEST_RECURSION)
|
||
printf("\n**** AFTER RECURSION ****\n");
|
||
heapdump(__FILE__, __LINE__);
|
||
#endif
|
||
|
||
return(TRUE);
|
||
}
|
||
#endif //of self-recursive section
|
||
else
|
||
/* If "<drive letter>:" then change drives. Ignore everything after
|
||
* the drive letter, just like the shell does.
|
||
*/
|
||
if (_istalpha(*pArg0) && pArg0[1] == ':' && !pArg0[2]) {
|
||
setdrive(_totupper(*pArg0) - 'A' + 1);
|
||
*pStatus = 0;
|
||
return(TRUE);
|
||
}
|
||
/* If "set" then pass it to the shell and if "set string" then put it
|
||
* into the environment. Let the shell handle the syntax errors.
|
||
*/
|
||
else if (!_ftcsicmp(pArg0, "set")) {
|
||
if (argc == 1)
|
||
return(FALSE); // pass it to the shell
|
||
else {
|
||
char *pNameVal; // the "name=value" string
|
||
pNameVal = expandCommandLine();
|
||
/* if there is a syntax error let the shell handle it */
|
||
if (!*pNameVal)
|
||
return(FALSE);
|
||
if ((*pStatus = PutEnv(makeString(pNameVal))) == -1)
|
||
makeError(currentLine, OUT_OF_ENV_SPACE);
|
||
}
|
||
}
|
||
/* If "cd foo" or "chdir foo", do a chdir() else in protect mode this
|
||
* would be a no-op. Ignore everything after 1st arg, just like the
|
||
* shell does.
|
||
*/
|
||
else {
|
||
if (!_ftcsnicmp(pArg0, "cd", 2))
|
||
pArg0 += 2;
|
||
else if (!_ftcsnicmp(pArg0, "chdir", 5))
|
||
pArg0 += 5;
|
||
else
|
||
return(FALSE);
|
||
/* At this point, a prefix of argv[0] matches cd or chdir and pArg0
|
||
* points to the next char. Check for a path separator in argv[0]
|
||
* (e.g., cd..\foo) or else use the next arg if present.
|
||
*/
|
||
// Remove quotes, if any from the argument
|
||
removeQuotes(argc, argv);
|
||
|
||
//if there are more than two arguments then let the shell handle it
|
||
if (argc > 2)
|
||
return(FALSE);
|
||
else if (!*pArg0 && pArg1) {
|
||
//Under certain circumstances the C RunTime does not help us
|
||
//e.g. 'd:', in this case let the shell do it ...
|
||
if (isalpha(*pArg1) && pArg1[1] == ':' && !pArg1[2])
|
||
return(FALSE);
|
||
*pStatus = _chdir(pArg1);
|
||
}
|
||
else if (*pArg0 == '.' || PATH_SEPARATOR(*pArg0))
|
||
*pStatus = _chdir(pArg0);
|
||
else
|
||
/* Unrecognized syntax--we can't emulate. */
|
||
return(FALSE);
|
||
}
|
||
/* If error, simulate a return code of 1. */
|
||
if (*pStatus != 0)
|
||
*pStatus = 1;
|
||
return(TRUE);
|
||
}
|
||
|
||
|
||
/* ----------------------------------------------------------------------------
|
||
* getComSpec()
|
||
*
|
||
* actions: Attempts to find system shell.
|
||
*
|
||
* First look for COMSPEC. If not found, look for COMMAND.COM or CMD.EXE
|
||
* in the current directory then the path. If not found, fatal error.
|
||
* It would make sense to give an error if COMSPEC is not defined but
|
||
* test suites are easier if no user-defined environment variables are
|
||
* required.
|
||
*/
|
||
LOCAL char * NEAR
|
||
getComSpec()
|
||
{
|
||
void *findBuf = _alloca(resultbuf_size);
|
||
NMHANDLE searchHandle;
|
||
char *p;
|
||
char *shell;
|
||
|
||
if ((shell = getenv("COMSPEC")) != NULL) {
|
||
return(shell);
|
||
}
|
||
if ((p = getenv("PATH")) == NULL)
|
||
p = "";
|
||
#ifdef DOS
|
||
shell = searchPath(p, "COMMAND.COM", findBuf, &searchHandle);
|
||
#else
|
||
shell = searchPath(p, "CMD.EXE", findBuf, &searchHandle);
|
||
#endif
|
||
if (shell == NULL)
|
||
makeError(0, NO_COMMAND_COM);
|
||
return(shell);
|
||
}
|
||
|
||
|
||
LOCAL BOOL NEAR
|
||
iterateCommand(
|
||
char *u,
|
||
STRINGLIST *t,
|
||
UCHAR buildFlags,
|
||
UCHAR cFlags,
|
||
char *pFirstDep,
|
||
unsigned *status
|
||
) {
|
||
BOOL parens;
|
||
char c = '\0';
|
||
char *v;
|
||
STRINGLIST *p = NULL,
|
||
*q;
|
||
char *pLine;
|
||
#ifndef NO_OPTION_Z
|
||
STRINGLIST *z, *zList; //For -z option
|
||
#endif
|
||
char *pCmd;
|
||
|
||
for (v = u; *v ; ++v) {
|
||
parens = FALSE;
|
||
if (*v == '$') {
|
||
if (*(v+1) == '(') {
|
||
++v;
|
||
parens = TRUE;
|
||
}
|
||
if (*(v+1) == '?') {
|
||
if (parens
|
||
&& !(_ftcschr("DFBR", *(v+2)) && *(v+3) == ')')
|
||
&& *(v+2) != ')')
|
||
continue;
|
||
p = dollarQuestion;
|
||
c = '?';
|
||
break;
|
||
}
|
||
if (*++v == '*' && *(v+1) == '*') {
|
||
if (parens
|
||
&& !(_ftcschr("DFBR", *(v+2)) && *(v+3) == ')')
|
||
&& *(v+2) != ')')
|
||
continue;
|
||
p = dollarStarStar;
|
||
c = '*';
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
if (!*v) return(FALSE);
|
||
v = u;
|
||
q = p;
|
||
while (p) {
|
||
macros = t;
|
||
if (c == '*') {
|
||
p = dollarStarStar->next;
|
||
dollarStarStar->next = NULL;
|
||
}
|
||
else {
|
||
p = dollarQuestion->next;
|
||
dollarQuestion->next = NULL;
|
||
}
|
||
u = expandMacros(v, ¯os);
|
||
|
||
expandExtmake(CmdLine, u, pFirstDep);
|
||
#ifndef NO_OPTION_Z
|
||
if (ON(gFlags, F1_REVERSE_BATCH_FILE))
|
||
zList = canonCmdLine(CmdLine);
|
||
else {
|
||
zList = makeNewStrListElement();
|
||
zList->text = CmdLine;
|
||
}
|
||
|
||
for (z = zList; z; z = z->next) {
|
||
pLine = z->text;
|
||
#else
|
||
pLine = CmdLine;
|
||
#endif
|
||
*status = execLine(pLine,
|
||
(BOOL)(ON(buildFlags, F2_NO_EXECUTE)
|
||
|| (OFF(buildFlags,F2_NO_ECHO)
|
||
&& OFF(cFlags,C_SILENT))),
|
||
(BOOL)((OFF(buildFlags, F2_NO_EXECUTE)
|
||
#ifndef NO_OPTION_Z
|
||
&& OFF(gFlags, F1_REVERSE_BATCH_FILE)
|
||
#endif
|
||
)
|
||
|| ON(cFlags, C_EXECUTE)),
|
||
(BOOL)ON(cFlags, C_IGNORE), &pCmd);
|
||
if (OFF(buildFlags, F2_IGNORE_EXIT_CODES)) {
|
||
#ifndef NO_OPTION_Z
|
||
if (ON(gFlags, F1_REVERSE_BATCH_FILE)) {
|
||
STRINGLIST *revCmd;
|
||
revCmd = makeNewStrListElement();
|
||
revCmd->text = (char *)rallocate(_ftcslen(batchIfCmd) + 1);
|
||
sprintf(revCmd->text, batchIfCmd,
|
||
(errorLevel == 255 ? errorLevel: errorLevel + 1));
|
||
prependItem(&revList, revCmd);
|
||
}
|
||
else
|
||
#endif
|
||
if (*status && *status > (unsigned)errorLevel)
|
||
if (!fOptionK)
|
||
makeError(0, BAD_RETURN_CODE, pCmd, *status);
|
||
}
|
||
#ifndef NO_OPTION_Z
|
||
}
|
||
#endif
|
||
if (c == '*')
|
||
dollarStarStar = dollarStarStar->next = p;
|
||
else dollarQuestion = dollarQuestion->next = p;
|
||
FREE(u);
|
||
#ifndef NO_OPTION_Z
|
||
if (ON(gFlags, F1_REVERSE_BATCH_FILE))
|
||
freeList(zList);
|
||
else
|
||
FREE_STRINGLIST(zList);
|
||
#endif
|
||
if (OFF(buildFlags, F2_IGNORE_EXIT_CODES) && fOptionK && *status &&
|
||
*status > (unsigned)errorLevel)
|
||
break;
|
||
}
|
||
if (c == '*') dollarStarStar = q;
|
||
else dollarQuestion = q;
|
||
return(TRUE);
|
||
}
|
||
|
||
#if defined(DOS)
|
||
|
||
/* redirect -- handles redirection of input or output.
|
||
*
|
||
* arguments: dir - READ => input,
|
||
* WRITE => output,
|
||
* APPEND => append to end of the file.
|
||
*
|
||
* p - pointer to buffer that has the filename as
|
||
* well as the rest of the command string.
|
||
*
|
||
* return value FALSE => error (freopen fails)
|
||
* TRUE => normal return.
|
||
*
|
||
* the freopen() call sets up the redirection. the rest of the
|
||
* command string is then copied forward.
|
||
*
|
||
*/
|
||
|
||
LOCAL BOOL NEAR
|
||
redirect(name, which)
|
||
char *name;
|
||
unsigned which;
|
||
{
|
||
char *p,
|
||
c = '\0';
|
||
BOOL fStatus;
|
||
char *mode;
|
||
FILE *stream;
|
||
FILE *new;
|
||
|
||
while (WHITESPACE(*name)) ++name;
|
||
if (p = _ftcspbrk(name, " \t<>\r")) {
|
||
c = *p;
|
||
*p = '\0';
|
||
}
|
||
if (which == READ) {
|
||
mode = "r";
|
||
stream = stdin;
|
||
}
|
||
else {
|
||
stream = stdout;
|
||
if (which == WRITE)
|
||
mode = "w";
|
||
else
|
||
mode = "a";
|
||
}
|
||
|
||
new = freopen(name, mode, stream);
|
||
|
||
// if (!new) { // REVIEW: consider notifying the user
|
||
// perror(name); // REVIEW: that we failed here?
|
||
// } // REVIEW: this could save grief later...
|
||
|
||
fStatus = (BOOL)(new ? TRUE : FALSE);
|
||
if (fStatus && which == APPEND)
|
||
_lseek(_fileno(new), 0L, SEEK_END);
|
||
|
||
while(*name)
|
||
*name++ = ' ';
|
||
if (p)
|
||
*p = c;
|
||
return(fStatus);
|
||
}
|
||
|
||
#endif // DOS
|
||
|
||
LOCAL void NEAR
|
||
removeQuotes(argc, argv)
|
||
int argc;
|
||
char **argv;
|
||
{
|
||
char *t,
|
||
*string;
|
||
|
||
for (; argc--; argv++) {
|
||
string = *argv;
|
||
for (t = string; *t;) {
|
||
if (*t == SLASH || *t == ESCH) {
|
||
if (t[1] == QUOTE)
|
||
*(string)++ = *(t++);
|
||
*(string++) = *(t++);
|
||
continue;
|
||
}
|
||
if (*t == QUOTE)
|
||
++t;
|
||
else
|
||
*(string++) = *(t++);
|
||
}
|
||
*string = '\0';
|
||
}
|
||
}
|
||
|
||
LOCAL void NEAR
|
||
touch(s, minusN)
|
||
char *s;
|
||
BOOL minusN;
|
||
{
|
||
int fd;
|
||
char c;
|
||
FILE * file;
|
||
|
||
makeMessage(TOUCHING_TARGET, s);
|
||
if (!minusN &&
|
||
((file = FILEOPEN(s, "r+b")) != NULL)) {
|
||
fd = _fileno(file);
|
||
if (_read(fd, &c, 1) > 0) {
|
||
_lseek(fd, 0L, SEEK_SET);
|
||
_write(fd, &c, 1);
|
||
}
|
||
_close(fd);
|
||
}
|
||
}
|