1594 lines
44 KiB
C++
1594 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:
|
|||
|
// 04-Feb-2000 BTF Ported to Win64
|
|||
|
// 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
|
|||
|
|
|||
|
#include "precomp.h"
|
|||
|
#pragma hdrstop
|
|||
|
|
|||
|
#define SLASH '\\'
|
|||
|
#define PUBLIC
|
|||
|
#define QUOTE '\"'
|
|||
|
|
|||
|
extern BOOL processInline(char *, char **, STRINGLIST **, BOOL);
|
|||
|
|
|||
|
#ifdef _M_IX86
|
|||
|
extern UCHAR fRunningUnderChicago;
|
|||
|
#else
|
|||
|
#define fRunningUnderChicago FALSE
|
|||
|
#endif
|
|||
|
|
|||
|
char * getComSpec(void);
|
|||
|
BOOL iterateCommand(char*, STRINGLIST*, UCHAR, UCHAR, char *, unsigned*);
|
|||
|
void removeQuotes(int, char **);
|
|||
|
void touch(char*, BOOL);
|
|||
|
|
|||
|
|
|||
|
//buffer for path of .cmd/.bat
|
|||
|
extern char * makeStr;
|
|||
|
extern char * shellName;
|
|||
|
|
|||
|
|
|||
|
char szCmdLineBuf[MAXCMDLINELENGTH];
|
|||
|
char *szNmakeProgName;
|
|||
|
|
|||
|
// 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
|
|||
|
// (Ignored if NULL)
|
|||
|
// 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.
|
|||
|
|
|||
|
void
|
|||
|
buildArgumentVector(
|
|||
|
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 = _tcschr(p = cmdline, '\0');
|
|||
|
// Work around:
|
|||
|
end = p = cmdline;
|
|||
|
while (*end)
|
|||
|
end++;
|
|||
|
|
|||
|
for (*argc = 0; p < end; ++*argc) {
|
|||
|
p += _tcsspn(p, " \t"); // skip whitespace
|
|||
|
if (p >= end)
|
|||
|
break;
|
|||
|
if (argv)
|
|||
|
*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 = _tcsinc(p)) {
|
|||
|
if (*p == '\\')
|
|||
|
++p; // skip escaped character
|
|||
|
else if (*p == '\"')
|
|||
|
break;
|
|||
|
}
|
|||
|
if (p >= end)
|
|||
|
continue;
|
|||
|
++p;
|
|||
|
p = _tcspbrk(p, " \t");
|
|||
|
} else {
|
|||
|
|
|||
|
// For the first word on the command line, accept the switch
|
|||
|
// character and whitespace as terminators. Otherwise, just
|
|||
|
// whitespace.
|
|||
|
|
|||
|
p = _tcspbrk(p, " \t\"/");
|
|||
|
for (;p && p < end;p = _tcspbrk(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 = _tcspbrk(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 == '/' && argv) {
|
|||
|
*p = '\0';
|
|||
|
argv[-1] = makeString(argv[-1]);
|
|||
|
*p-- = '/';
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
if (!p)
|
|||
|
p = end;
|
|||
|
// Now, p points to end of command line argument
|
|||
|
if (argv)
|
|||
|
*p++ = '\0';
|
|||
|
}
|
|||
|
if (argv)
|
|||
|
*argv = NULL;
|
|||
|
}
|
|||
|
|
|||
|
PUBLIC int
|
|||
|
doCommands(
|
|||
|
char *name,
|
|||
|
STRINGLIST *s,
|
|||
|
STRINGLIST *t,
|
|||
|
UCHAR buildFlags,
|
|||
|
char *pFirstDep
|
|||
|
)
|
|||
|
{
|
|||
|
STRINGLIST *temp;
|
|||
|
int rc;
|
|||
|
temp = makeNewStrListElement();
|
|||
|
temp->text = makeString(name);
|
|||
|
rc = doCommandsEx (temp, s, t, buildFlags, pFirstDep);
|
|||
|
free_stringlist(temp);
|
|||
|
return rc;
|
|||
|
}
|
|||
|
|
|||
|
PUBLIC int
|
|||
|
doCommandsEx(
|
|||
|
STRINGLIST *nameList,
|
|||
|
STRINGLIST *s,
|
|||
|
STRINGLIST *t,
|
|||
|
UCHAR buildFlags,
|
|||
|
char *pFirstDep
|
|||
|
)
|
|||
|
{
|
|||
|
char *u, *v;
|
|||
|
UCHAR cFlags;
|
|||
|
unsigned status = 0;
|
|||
|
int retryCount = 0;
|
|||
|
char c;
|
|||
|
char *Cmd;
|
|||
|
char *pLine;
|
|||
|
BOOL fExpanded;
|
|||
|
char *pCmd;
|
|||
|
size_t cbLine;
|
|||
|
|
|||
|
#ifdef DEBUG_ALL
|
|||
|
if (fDebug) {
|
|||
|
printf("* doCommands:");
|
|||
|
DumpList(nameList);
|
|||
|
DumpList(s);
|
|||
|
DumpList(t);
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
#ifdef DEBUG_ALL
|
|||
|
printf("DEBUG: doCommands 1\n");
|
|||
|
#endif
|
|||
|
++numCommands;
|
|||
|
if (ON(gFlags, F1_QUESTION_STATUS))
|
|||
|
return(0);
|
|||
|
|
|||
|
if (ON(gFlags, F1_TOUCH_TARGETS)) {
|
|||
|
STRINGLIST *pName;
|
|||
|
for (pName = nameList; pName; pName = pName->next) {
|
|||
|
touch(pName->text, (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,
|
|||
|
ON(buildFlags, F2_DUMP_INLINE));
|
|||
|
cFlags = 0;
|
|||
|
errorLevel = 0;
|
|||
|
u = Cmd;
|
|||
|
for (v = u; *v; v = _tcsinc(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 (!_tcsncmp(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); u = _tcsinc(u), 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 = _tcstoul(u, &u, 10);
|
|||
|
if (errno == ERANGE) {
|
|||
|
*u = '\0';
|
|||
|
makeError(line, CONST_TOO_BIG, pNumber);
|
|||
|
}
|
|||
|
while(_istspace(*u))
|
|||
|
u++;
|
|||
|
} else
|
|||
|
errorLevel = UINT_MAX;
|
|||
|
--u;
|
|||
|
break;
|
|||
|
case '@':
|
|||
|
if (
|
|||
|
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 (_tcschr(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 > errorLevel)
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
continue;
|
|||
|
}
|
|||
|
v = u;
|
|||
|
|
|||
|
#ifdef DEBUG_ALL
|
|||
|
printf("DEBUG: doCommands 2.23\n");
|
|||
|
#endif
|
|||
|
if (!fExpanded && _tcschr(u, '$'))
|
|||
|
u = expandMacros(u, &t);
|
|||
|
|
|||
|
#ifdef DEBUG_ALL
|
|||
|
printf("DEBUG: doCommands 2.24\n");
|
|||
|
#endif
|
|||
|
|
|||
|
cbLine = _tcslen(u) + 1;
|
|||
|
pLine = (char *) rallocate (__max(cbLine, MAXCMDLINELENGTH));
|
|||
|
_tcscpy(pLine, u);
|
|||
|
|
|||
|
// by this time $< has already been expanded.
|
|||
|
// in order to allow processing of long commands that are due to
|
|||
|
// batch-mode rules, use a buffer that may be larger than MAXCMDLINELENGTH
|
|||
|
// Later we'll attempt to execute the long command directly, instead of
|
|||
|
// passing it to the shell.
|
|||
|
// Note: the macros expanded by ZFormat are not normally found in the
|
|||
|
// command block of a batch-mode rule, so it should be safe to use
|
|||
|
// max(cbLine, MAXCMDLINELENGTH) as a limit for ZFormat
|
|||
|
if (ZFormat (pLine, __max(cbLine, MAXCMDLINELENGTH), u, pFirstDep))
|
|||
|
makeError(0, COMMAND_TOO_LONG, u);
|
|||
|
|
|||
|
retry:
|
|||
|
status = execLine(pLine,
|
|||
|
(BOOL)(ON(buildFlags, F2_NO_EXECUTE)
|
|||
|
|| (OFF(buildFlags,F2_NO_ECHO)
|
|||
|
&& OFF(cFlags,C_SILENT))),
|
|||
|
(BOOL)((OFF(buildFlags, F2_NO_EXECUTE)
|
|||
|
)
|
|||
|
|| ON(cFlags, C_EXECUTE)),
|
|||
|
(BOOL)ON(cFlags, C_IGNORE), &pCmd);
|
|||
|
if (OFF(buildFlags, F2_IGNORE_EXIT_CODES)) {
|
|||
|
if (status == STATUS_PENDING) {
|
|||
|
// Hack for ntvdm problem returning correct error code.
|
|||
|
if (retryCount < 10) {
|
|||
|
retryCount++;
|
|||
|
goto retry;
|
|||
|
}
|
|||
|
}
|
|||
|
if (status && status > errorLevel) {
|
|||
|
if (!fOptionK)
|
|||
|
makeError(0, BAD_RETURN_CODE, pCmd, status);
|
|||
|
}
|
|||
|
}
|
|||
|
if (v != u)
|
|||
|
FREE(u);
|
|||
|
FREE(Cmd);
|
|||
|
FREE(pLine);
|
|||
|
if (OFF(buildFlags, F2_IGNORE_EXIT_CODES) &&
|
|||
|
fOptionK &&
|
|||
|
status &&
|
|||
|
status > errorLevel)
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#ifdef DEBUG_ALL
|
|||
|
printf("DEBUG: doCommands 3\n");
|
|||
|
#endif
|
|||
|
|
|||
|
if (OFF(buildFlags, F2_IGNORE_EXIT_CODES) && fOptionK &&
|
|||
|
(status > errorLevel))
|
|||
|
return(status);
|
|||
|
else
|
|||
|
return(0);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// 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()
|
|||
|
|
|||
|
char *
|
|||
|
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;
|
|||
|
_tcscpy(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 (!_tcschr(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++ = *p;
|
|||
|
|
|||
|
for (;*p;) {
|
|||
|
if (*p == '%') {
|
|||
|
pExpandName = &ExpandName[0];
|
|||
|
while (*++p != '%' && *p)
|
|||
|
*pExpandName++ = *p;
|
|||
|
*pExpandName = '\0';
|
|||
|
if (!*p++) { // unmatched %;so don't expand
|
|||
|
*pBuf='\0'; // from the environment; like set
|
|||
|
_tcscat(Buf, ExpandName);
|
|||
|
pBuf += _tcslen(ExpandName);
|
|||
|
break; // Done precessing quit #43290
|
|||
|
} else { // matched %;so expand from the environment
|
|||
|
EnvBuf[0] = '\0';
|
|||
|
if ((pEnv = getenv(ExpandName)) != (char *)NULL) {
|
|||
|
*pBuf='\0';
|
|||
|
|
|||
|
// If the expanded command line is too long
|
|||
|
// just say that we can't expand it!!! #43290
|
|||
|
size_t len = _tcslen(pEnv) + _tcslen(Buf);
|
|||
|
if (len > MAXCMDLINELENGTH)
|
|||
|
return NULL;
|
|||
|
|
|||
|
_tcscat(EnvBuf, pEnv);
|
|||
|
_tcscat(Buf,EnvBuf);
|
|||
|
pBuf += _tcslen(EnvBuf);
|
|||
|
}
|
|||
|
}
|
|||
|
} else
|
|||
|
*pBuf++ = *p++;
|
|||
|
}
|
|||
|
*pBuf = '\0';
|
|||
|
_tcscpy(szCmdLineBuf, Buf);
|
|||
|
*posName = '\0';
|
|||
|
posName = szCmdLineBuf + _tcslen(Buf); // Offset into buf
|
|||
|
return(posName);
|
|||
|
}
|
|||
|
|
|||
|
// expandEnvVars -- expands %name% strings in szArg
|
|||
|
//
|
|||
|
// Returns -- szNew: the resulting expanded string
|
|||
|
// (szNew should be FREEd by the caller)
|
|||
|
//
|
|||
|
char *
|
|||
|
expandEnvVars(
|
|||
|
char *szArg
|
|||
|
)
|
|||
|
{
|
|||
|
char *pchLeft = NULL;
|
|||
|
char *pchRight = NULL;
|
|||
|
char *pchStart = szArg;
|
|||
|
|
|||
|
char *szNew = makeString("");
|
|||
|
|
|||
|
while (*pchStart) {
|
|||
|
pchLeft = _tcschr(pchStart, '%');
|
|||
|
if (pchLeft) {
|
|||
|
pchRight = _tcschr(pchLeft + 1, '%');
|
|||
|
}
|
|||
|
|
|||
|
if (pchLeft && pchRight) {
|
|||
|
char *szEnv;
|
|||
|
*pchLeft = '\0';
|
|||
|
*pchRight = '\0';
|
|||
|
szNew = reallocString(szNew, pchStart);
|
|||
|
if (szEnv = getenv(pchLeft + 1)) {
|
|||
|
szNew = reallocString(szNew, szEnv);
|
|||
|
}
|
|||
|
else {
|
|||
|
// no matching env var was found
|
|||
|
// append the %..% string literary
|
|||
|
*pchLeft = '%';
|
|||
|
szNew = reallocString(szNew, pchLeft);
|
|||
|
szNew = reallocString(szNew, "%");
|
|||
|
}
|
|||
|
*pchLeft = '%';
|
|||
|
*pchRight = '%';
|
|||
|
pchStart = pchRight + 1;
|
|||
|
pchLeft = NULL;
|
|||
|
pchRight = NULL;
|
|||
|
}
|
|||
|
else {
|
|||
|
szNew = reallocString(szNew, pchStart);
|
|||
|
pchStart += _tcslen(pchStart);
|
|||
|
}
|
|||
|
}
|
|||
|
return szNew;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// 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.
|
|||
|
|
|||
|
BOOL
|
|||
|
FEmulateCommand(
|
|||
|
int argc,
|
|||
|
char **argv,
|
|||
|
int *pStatus
|
|||
|
)
|
|||
|
{
|
|||
|
char *pArg0 = argv[0];
|
|||
|
char *pArg1 = argv[1];
|
|||
|
|
|||
|
if (_istalpha(*pArg0) && pArg0[1] == ':' && !pArg0[2]) {
|
|||
|
// If "<drive letter>:" then change drives. Ignore everything after
|
|||
|
// the drive letter, just like the shell does.
|
|||
|
|
|||
|
_chdrive(_totupper(*pArg0) - 'A' + 1);
|
|||
|
*pStatus = 0;
|
|||
|
return(TRUE);
|
|||
|
}
|
|||
|
|
|||
|
if (!_tcsicmp(pArg0, "set")) {
|
|||
|
char *pNameVal; // the "name=value" string
|
|||
|
|
|||
|
// 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.
|
|||
|
|
|||
|
if (argc == 1) {
|
|||
|
return(FALSE); // pass it to the shell
|
|||
|
}
|
|||
|
|
|||
|
// expandCommandLine cannot handle lines > MAXCMDLINELENGTH
|
|||
|
// In that case szCmdLineBuf will be empty
|
|||
|
if (!szCmdLineBuf[0])
|
|||
|
return (FALSE);
|
|||
|
|
|||
|
pNameVal = expandCommandLine();
|
|||
|
|
|||
|
if (pNameVal == NULL)
|
|||
|
{
|
|||
|
// Expanded commad line too long
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if (!*pNameVal) {
|
|||
|
// If there is a syntax error let the shell handle it
|
|||
|
|
|||
|
return(FALSE);
|
|||
|
}
|
|||
|
|
|||
|
if ((*pStatus = PutEnv(makeString(pNameVal))) == -1) {
|
|||
|
makeError(currentLine, OUT_OF_ENV_SPACE);
|
|||
|
}
|
|||
|
} else {
|
|||
|
// 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.
|
|||
|
|
|||
|
char *szArg;
|
|||
|
|
|||
|
if (!_tcsnicmp(pArg0, "cd", 2)) {
|
|||
|
pArg0 += 2;
|
|||
|
} else if (!_tcsnicmp(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.
|
|||
|
|
|||
|
// if there are more than two arguments then let the shell handle it
|
|||
|
if (argc > 2) {
|
|||
|
return(FALSE);
|
|||
|
}
|
|||
|
|
|||
|
// Remove quotes, if any from the argument
|
|||
|
removeQuotes(argc, argv);
|
|||
|
|
|||
|
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);
|
|||
|
}
|
|||
|
|
|||
|
szArg = expandEnvVars(pArg1); // [VS98 2251]
|
|||
|
*pStatus = _chdir(szArg);
|
|||
|
FREE (szArg);
|
|||
|
} else if (*pArg0 == '.' || PATH_SEPARATOR(*pArg0)) {
|
|||
|
szArg = expandEnvVars(pArg0); // [VS98 2251]
|
|||
|
*pStatus = _chdir(szArg);
|
|||
|
FREE (szArg);
|
|||
|
} else {
|
|||
|
// Unrecognized syntax--we can't emulate.
|
|||
|
|
|||
|
return(FALSE);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// If error, simulate a return code of 1.
|
|||
|
|
|||
|
if (*pStatus != 0) {
|
|||
|
*pStatus = 1;
|
|||
|
}
|
|||
|
|
|||
|
return(TRUE);
|
|||
|
}
|
|||
|
|
|||
|
#ifdef WIN95
|
|||
|
|
|||
|
int __cdecl
|
|||
|
cmpSzPsz(
|
|||
|
const void *sz,
|
|||
|
const void *psz
|
|||
|
)
|
|||
|
{
|
|||
|
const char *sz1 = (char *) sz;
|
|||
|
const char *sz2 = *(char **) psz;
|
|||
|
|
|||
|
return(_tcsicmp(sz1, sz2));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
FInternalCommand(
|
|||
|
const char *szName
|
|||
|
)
|
|||
|
{
|
|||
|
const char * const *pszInternal;
|
|||
|
|
|||
|
static const char * const rgszInternal[] =
|
|||
|
{
|
|||
|
"BREAK",
|
|||
|
"CALL",
|
|||
|
"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"
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
pszInternal = (const char * const *) bsearch(szName,
|
|||
|
rgszInternal,
|
|||
|
sizeof(rgszInternal) / sizeof(rgszInternal[0]),
|
|||
|
sizeof(rgszInternal[0]),
|
|||
|
&cmpSzPsz);
|
|||
|
|
|||
|
return(pszInternal != NULL);
|
|||
|
}
|
|||
|
|
|||
|
#endif // WIN95
|
|||
|
|
|||
|
// 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.
|
|||
|
|
|||
|
BOOL
|
|||
|
redirect(
|
|||
|
char *name,
|
|||
|
unsigned which
|
|||
|
)
|
|||
|
{
|
|||
|
char *p;
|
|||
|
char c = '\0';
|
|||
|
BOOL fStatus;
|
|||
|
char *mode;
|
|||
|
FILE *stream;
|
|||
|
FILE *newFile;
|
|||
|
|
|||
|
while (WHITESPACE(*name)) {
|
|||
|
name++;
|
|||
|
}
|
|||
|
|
|||
|
if (p = _tcspbrk(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";
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
newFile = freopen(name, mode, stream);
|
|||
|
|
|||
|
fStatus = (newFile != NULL);
|
|||
|
|
|||
|
if (fStatus && (which == APPEND)) {
|
|||
|
_lseek(_fileno(newFile), 0L, SEEK_END);
|
|||
|
}
|
|||
|
|
|||
|
while (*name) {
|
|||
|
*name++ = ' ';
|
|||
|
}
|
|||
|
|
|||
|
if (p) {
|
|||
|
*p = c;
|
|||
|
}
|
|||
|
|
|||
|
return(fStatus);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
FDoRedirection(
|
|||
|
char *p,
|
|||
|
int *oldIn,
|
|||
|
int *oldOut
|
|||
|
)
|
|||
|
{
|
|||
|
BOOL in = FALSE;
|
|||
|
BOOL out = FALSE;
|
|||
|
BOOL fReturn = FALSE;
|
|||
|
char *q;
|
|||
|
unsigned which;
|
|||
|
char *save = NULL;
|
|||
|
|
|||
|
|
|||
|
while (q = _tcspbrk(p, "<>|")) {
|
|||
|
switch (*q) {
|
|||
|
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 != NULL) {
|
|||
|
_tcscpy(p, save);
|
|||
|
FREE(save);
|
|||
|
}
|
|||
|
|
|||
|
if (in && (*oldIn != -1)) {
|
|||
|
if (_dup2(*oldIn, _fileno(stdin)) == -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);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
FSearchForExecutableExt(
|
|||
|
const char *szFilename,
|
|||
|
const char *szExt,
|
|||
|
BOOL fHasPath,
|
|||
|
char *szPath
|
|||
|
)
|
|||
|
{
|
|||
|
char szFullName[_MAX_PATH];
|
|||
|
|
|||
|
_tcscpy(szFullName, szFilename);
|
|||
|
_tcscat(szFullName, szExt);
|
|||
|
if (fHasPath) {
|
|||
|
if (_access(szFullName, 0) == 0) {
|
|||
|
_tcscpy(szPath, szFullName);
|
|||
|
|
|||
|
return(TRUE);
|
|||
|
}
|
|||
|
|
|||
|
return(FALSE);
|
|||
|
}
|
|||
|
|
|||
|
_searchenv(szFullName, "PATH", szPath);
|
|||
|
|
|||
|
return(szPath[0] != '\0');
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
FSearchForExecutable(char *szFullName, char *szPath, BOOL *fBat)
|
|||
|
{
|
|||
|
char szDrive[_MAX_DRIVE];
|
|||
|
char szDir[_MAX_DIR];
|
|||
|
char szFileName[_MAX_FNAME];
|
|||
|
char szNoExt[_MAX_PATH];
|
|||
|
BOOL fHasPath;
|
|||
|
char *szEndQuote;
|
|||
|
BOOL fHasQuotes = FALSE;
|
|||
|
|
|||
|
// Ignore any given extension. This is what COMMAND.COM does,
|
|||
|
|
|||
|
char *szToPass = szFullName;
|
|||
|
|
|||
|
if (*szFullName == QUOTE) {
|
|||
|
// get rid of any number of quotes at the beginning and at the end of the
|
|||
|
// string. This allows handling names enclosed in multiple quotes that are
|
|||
|
// accepted by the shell (DS 14300)
|
|||
|
szEndQuote = _tcsdec(szFullName, szFullName + _tcslen(szFullName));
|
|||
|
|
|||
|
if (QUOTE == *szEndQuote) {
|
|||
|
fHasQuotes = TRUE;
|
|||
|
|
|||
|
while (QUOTE == *szToPass)
|
|||
|
szToPass ++;
|
|||
|
|
|||
|
while (szEndQuote > szToPass) {
|
|||
|
char *szPrev = _tcsdec (szToPass, szEndQuote);
|
|||
|
if (QUOTE != *szPrev)
|
|||
|
break;
|
|||
|
szEndQuote = szPrev;
|
|||
|
}
|
|||
|
|
|||
|
*szEndQuote = '\0';
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
_splitpath(szToPass, szDrive, szDir, szFileName, NULL);
|
|||
|
_makepath(szNoExt, szDrive, szDir, szFileName, NULL);
|
|||
|
fHasPath = (szDrive[0] != '\0') || (szDir[0] != '\0');
|
|||
|
|
|||
|
*fBat = FALSE;
|
|||
|
|
|||
|
// Search for .COM file
|
|||
|
|
|||
|
if (FSearchForExecutableExt(szNoExt, ".com", fHasPath, szPath)) {
|
|||
|
goto success;
|
|||
|
}
|
|||
|
|
|||
|
// Search for .EXE file
|
|||
|
|
|||
|
if (FSearchForExecutableExt(szNoExt, ".exe", fHasPath, szPath)) {
|
|||
|
goto success;
|
|||
|
}
|
|||
|
|
|||
|
// Search for .BAT file
|
|||
|
|
|||
|
if (FSearchForExecutableExt(szNoExt, ".bat", fHasPath, szPath)) {
|
|||
|
*fBat = TRUE;
|
|||
|
|
|||
|
goto success;
|
|||
|
}
|
|||
|
|
|||
|
return(FALSE);
|
|||
|
|
|||
|
success:
|
|||
|
if (fHasQuotes) {
|
|||
|
size_t size = _tcslen(szPath);
|
|||
|
memmove(szPath+1, szPath, size);
|
|||
|
*szPath = '"';
|
|||
|
*(szPath + size + 1) = '"';
|
|||
|
*(szPath + size + 2) = '\0';
|
|||
|
*szEndQuote = '"';
|
|||
|
}
|
|||
|
return TRUE;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// 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
|
|||
|
//
|
|||
|
// 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
|
|||
|
execLine(
|
|||
|
char *line,
|
|||
|
BOOL echoCmd,
|
|||
|
BOOL doCmd,
|
|||
|
BOOL ignoreReturn,
|
|||
|
char **ppCmd
|
|||
|
)
|
|||
|
{
|
|||
|
char **argv;
|
|||
|
BOOL fUseShell;
|
|||
|
BOOL fLongCommand;
|
|||
|
int status;
|
|||
|
unsigned argc;
|
|||
|
|
|||
|
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 = _tcstoul(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 = UINT_MAX;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
// handle null command ...
|
|||
|
if (!line[0])
|
|||
|
return(0);
|
|||
|
|
|||
|
#if 0
|
|||
|
// 10/10/96: disabled to allow execution of long
|
|||
|
// commands that are produced by batch-mode rules
|
|||
|
|
|||
|
// copy command line into buffer
|
|||
|
if (_tcslen(line) < MAXCMDLINELENGTH)
|
|||
|
_tcscpy(szCmdLineBuf, line);
|
|||
|
else
|
|||
|
makeError(0, COMMAND_TOO_LONG, line);
|
|||
|
#endif
|
|||
|
|
|||
|
fLongCommand = _tcslen(line) >= MAXCMDLINELENGTH;
|
|||
|
if (!fLongCommand)
|
|||
|
_tcscpy(szCmdLineBuf, line);
|
|||
|
else
|
|||
|
*szCmdLineBuf = '\0';
|
|||
|
|
|||
|
// 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(line);
|
|||
|
|
|||
|
// If -n then echo command if not '$(MAKE)'
|
|||
|
if (echoCmd) {
|
|||
|
printf("\t%s\n", pCmdLineCopy);
|
|||
|
fflush(stdout);
|
|||
|
}
|
|||
|
|
|||
|
// Build arg vector. This is a waste on Windows NT 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, NULL, pCmdLineCopy);
|
|||
|
|
|||
|
if (argc == 0) {
|
|||
|
return(0); // for case when macro command is null
|
|||
|
}
|
|||
|
|
|||
|
// allocate argv. Leave space for extra arguments
|
|||
|
// (like "cmd", "/k", quotes) that may be added later
|
|||
|
argv = (char **) rallocate((argc + 5) * sizeof (char *));
|
|||
|
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 && !_tcsicmp(argv[0], makeStr)) {
|
|||
|
if(!szNmakeProgName) {
|
|||
|
szNmakeProgName = _pgmptr;
|
|||
|
if( _tcspbrk( szNmakeProgName," " )) { // If the program name has an embedded space in it
|
|||
|
// Let's put quotes around it
|
|||
|
szNmakeProgName = (char *)rallocate(_tcslen(szNmakeProgName)+3);
|
|||
|
*szNmakeProgName = QUOTE; // First quote
|
|||
|
*(szNmakeProgName+1) = '\0';
|
|||
|
_tcscat( szNmakeProgName, _pgmptr ); // copy the full program name (self)
|
|||
|
_tcscat( szNmakeProgName, "\""); // Final quote and \0
|
|||
|
}
|
|||
|
}
|
|||
|
argv[0]=szNmakeProgName;
|
|||
|
}
|
|||
|
|
|||
|
if (!doCmd) { // don't execute command if doCmd false
|
|||
|
// For -n, emulate if possible.
|
|||
|
|
|||
|
if (FEmulateCommand(argc, argv, &status)) {
|
|||
|
if (status && ppCmd) {
|
|||
|
*ppCmd = makeString(*argv);
|
|||
|
}
|
|||
|
|
|||
|
return(status); // return status
|
|||
|
}
|
|||
|
|
|||
|
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)) {
|
|||
|
// Command has been emulated. Don't execute it again.
|
|||
|
|
|||
|
fUseShell = FALSE;
|
|||
|
|
|||
|
} else if (!fRunningUnderChicago && !fLongCommand) {
|
|||
|
// Use the shell for Windows NT unless the command is too long
|
|||
|
|
|||
|
fUseShell = TRUE;
|
|||
|
|
|||
|
#ifdef WIN95
|
|||
|
} else if (fRunningUnderChicago && FInternalCommand(argv[0])) {
|
|||
|
// Under Windows 95 or MS-DOS, use the shell for internal commands
|
|||
|
|
|||
|
fUseShell = TRUE;
|
|||
|
#endif // WIN95
|
|||
|
|
|||
|
} else {
|
|||
|
int oldIn = -1; // Old stdin file handle
|
|||
|
int oldOut = -1; // Old stdout file handle
|
|||
|
|
|||
|
// Under Windows 95 or MS-DOS, COMMAND.COM doesn't return child return
|
|||
|
// codes. Try spawning the child application directly.
|
|||
|
|
|||
|
// This code is also now used if the line is too long to be handled by
|
|||
|
// the NT command interpreter.
|
|||
|
|
|||
|
fUseShell = FDoRedirection(line, &oldIn, &oldOut);
|
|||
|
|
|||
|
if (!fUseShell) {
|
|||
|
char szPath[_MAX_PATH];
|
|||
|
char szQuotedPath[_MAX_PATH];
|
|||
|
BOOL fBat;
|
|||
|
|
|||
|
if (oldIn != -1 || oldOut != -1) { // If there was a redirection
|
|||
|
// Need to re-build the argument vector without the
|
|||
|
// redirection characters
|
|||
|
FREE(pCmdLineCopy);
|
|||
|
pCmdLineCopy = makeString(line);
|
|||
|
buildArgumentVector(&argc, argv, pCmdLineCopy);
|
|||
|
}
|
|||
|
|
|||
|
if (!FSearchForExecutable(argv[0], szPath, &fBat)) {
|
|||
|
/* If not found, set up an error since COMMAND will
|
|||
|
* return 0. This risks future incompatibility if new
|
|||
|
* COMMAND.COM internal commands are added.
|
|||
|
*/
|
|||
|
if (fRunningUnderChicago) {
|
|||
|
errno = ENOENT;
|
|||
|
status = -1;
|
|||
|
} else {
|
|||
|
fUseShell = TRUE;
|
|||
|
}
|
|||
|
} else if (fBat) {
|
|||
|
// If .bat extension, use COMMAND.COM.
|
|||
|
|
|||
|
// UNDONE: CreateProcess is supposed to handle this. Try it.
|
|||
|
|
|||
|
fUseShell = TRUE;
|
|||
|
} else {
|
|||
|
// Spawn command directly.
|
|||
|
// DevStudio#8911, cannot use quotes in szPath
|
|||
|
if (*szPath == QUOTE && *(szPath + _tcslen(szPath) - 1) == QUOTE) {
|
|||
|
// unquote the path.
|
|||
|
size_t cb = _tcslen(szPath);
|
|||
|
memmove(szPath, szPath + 1, cb);
|
|||
|
*(szPath + cb - 2) = '\0';
|
|||
|
}
|
|||
|
#if 0
|
|||
|
{
|
|||
|
int i;
|
|||
|
printf("Spawning \"%s\" directly\n", szPath);
|
|||
|
for (i = 0; i < argc; i++) {
|
|||
|
printf ( "Arg[%d] = \"%s\"\n", i, argv[i] );
|
|||
|
}
|
|||
|
}
|
|||
|
#endif
|
|||
|
// DS 14300: Use full path for argv[0]
|
|||
|
// otherwise a shell command may be invoked
|
|||
|
// instead of an intended executable with the
|
|||
|
// same name. Enclosing quotes are needed if
|
|||
|
// string has embedded spaces
|
|||
|
|
|||
|
argv[0] = szPath;
|
|||
|
if (_tcschr (argv[0], ' ')) {
|
|||
|
*szQuotedPath = QUOTE;
|
|||
|
_tcscpy (szQuotedPath+1, szPath);
|
|||
|
_tcscat (szQuotedPath, "\"");
|
|||
|
argv[0] = szQuotedPath;
|
|||
|
}
|
|||
|
status = (int)_spawnvp(P_WAIT, szPath, argv); // REVIEW:WIN64 cast
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
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) {
|
|||
|
int i;
|
|||
|
BOOL fExtraQuote = FALSE;
|
|||
|
|
|||
|
// copy command line into buffer
|
|||
|
if (_tcslen(line) < MAXCMDLINELENGTH)
|
|||
|
_tcscpy(szCmdLineBuf, line);
|
|||
|
else
|
|||
|
makeError(0, COMMAND_TOO_LONG, line);
|
|||
|
|
|||
|
// Workaround for cmd bug (DevStudio #11253):
|
|||
|
// IF argv[0] (before we rearrange with cmd.exe /c) is quoted AND
|
|||
|
// any of the other argv[1...n] args have quotes AND
|
|||
|
// running on NT
|
|||
|
// THEN we add an extra quote before argv[0] and one after argv[n].
|
|||
|
|
|||
|
if ((*argv[0] == QUOTE) &&
|
|||
|
(*(argv[0] + _tcslen(argv[0]) - 1) == QUOTE) &&
|
|||
|
!fRunningUnderChicago) {
|
|||
|
for (i = argc - 1; i >= 1; i--) {
|
|||
|
if( _tcspbrk( argv[i],"\"" )) {
|
|||
|
fExtraQuote = TRUE;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (fExtraQuote) {
|
|||
|
argv[argc++] = "\"";
|
|||
|
argv[argc] = NULL;
|
|||
|
}
|
|||
|
|
|||
|
for (i = argc; i >= 0; i--) {
|
|||
|
argv[i+2] = argv[i];
|
|||
|
}
|
|||
|
|
|||
|
argv[0] = shellName;
|
|||
|
argv[1] = fExtraQuote ? "/c \"" : "/c";
|
|||
|
|
|||
|
#if 0
|
|||
|
printf("Shelling \"%s\"\n", szCmdLineBuf);
|
|||
|
for (i = 0; i < argc + 2; i++) {
|
|||
|
printf ( "Arg[%d] = \"%s\"\n", i, argv[i] );
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
status = (int)_spawnvp(P_WAIT, argv[0], (const char * const *) argv); // REVIEW:WIN64 cast
|
|||
|
}
|
|||
|
|
|||
|
// 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(fUseShell ? argv[2] : argv[0]);
|
|||
|
}
|
|||
|
|
|||
|
FREE(argv);
|
|||
|
FREE(pCmdLineCopy);
|
|||
|
return(status);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// 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.
|
|||
|
|
|||
|
char *
|
|||
|
getComSpec()
|
|||
|
{
|
|||
|
char *szShell;
|
|||
|
char szPath[_MAX_PATH];
|
|||
|
|
|||
|
if ((szShell = getenv("COMSPEC")) != NULL) {
|
|||
|
return(szShell);
|
|||
|
}
|
|||
|
|
|||
|
if (fRunningUnderChicago) {
|
|||
|
szShell = "COMMAND.COM";
|
|||
|
} else {
|
|||
|
szShell = "CMD.EXE";
|
|||
|
}
|
|||
|
|
|||
|
_searchenv(szShell, "PATH", szPath);
|
|||
|
|
|||
|
if (szPath[0] == '\0') {
|
|||
|
makeError(0, NO_COMMAND_COM);
|
|||
|
}
|
|||
|
|
|||
|
return(makeString(szPath));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
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;
|
|||
|
char *pCmd;
|
|||
|
|
|||
|
for (v = u; *v ; ++v) {
|
|||
|
parens = FALSE;
|
|||
|
if (*v == '$') {
|
|||
|
if (*(v+1) == '(') {
|
|||
|
++v;
|
|||
|
parens = TRUE;
|
|||
|
}
|
|||
|
if (*(v+1) == '?') {
|
|||
|
if (parens
|
|||
|
&& !(_tcschr("DFBR", *(v+2)) && *(v+3) == ')')
|
|||
|
&& *(v+2) != ')')
|
|||
|
continue;
|
|||
|
p = dollarQuestion;
|
|||
|
c = '?';
|
|||
|
break;
|
|||
|
}
|
|||
|
if (*++v == '*' && *(v+1) == '*') {
|
|||
|
if (parens
|
|||
|
&& !(_tcschr("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);
|
|||
|
pLine = CmdLine;
|
|||
|
*status = execLine(pLine,
|
|||
|
(BOOL)(ON(buildFlags, F2_NO_EXECUTE)
|
|||
|
|| (OFF(buildFlags,F2_NO_ECHO)
|
|||
|
&& OFF(cFlags,C_SILENT))),
|
|||
|
(BOOL)((OFF(buildFlags, F2_NO_EXECUTE)
|
|||
|
)
|
|||
|
|| ON(cFlags, C_EXECUTE)),
|
|||
|
(BOOL)ON(cFlags, C_IGNORE), &pCmd);
|
|||
|
if (OFF(buildFlags, F2_IGNORE_EXIT_CODES)) {
|
|||
|
if (*status && *status > errorLevel)
|
|||
|
if (!fOptionK)
|
|||
|
makeError(0, BAD_RETURN_CODE, pCmd, *status);
|
|||
|
}
|
|||
|
|
|||
|
if (c == '*')
|
|||
|
dollarStarStar = dollarStarStar->next = p;
|
|||
|
else
|
|||
|
dollarQuestion = dollarQuestion->next = p;
|
|||
|
FREE(u);
|
|||
|
if (OFF(buildFlags, F2_IGNORE_EXIT_CODES) &&
|
|||
|
fOptionK &&
|
|||
|
*status &&
|
|||
|
*status > errorLevel)
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
if (c == '*')
|
|||
|
dollarStarStar = q;
|
|||
|
else
|
|||
|
dollarQuestion = q;
|
|||
|
return(TRUE);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
void
|
|||
|
removeQuotes(
|
|||
|
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 {
|
|||
|
if (_istlead(* (unsigned char *)t))
|
|||
|
*(string++) = *(t++);
|
|||
|
*(string++) = *(t++);
|
|||
|
}
|
|||
|
}
|
|||
|
*string = '\0';
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void
|
|||
|
touch(
|
|||
|
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);
|
|||
|
}
|
|||
|
}
|