mirror of
https://github.com/reactos/reactos.git
synced 2024-09-28 13:34:53 +00:00
Implement complete support for FOR command (including /D, /F, /L, and /R switches). cmd is now close to being able to run RosBE 1.3's initialization without errors.
svn path=/trunk/; revision=39742
This commit is contained in:
parent
93ce0dce1f
commit
b18a547c1b
|
@ -188,12 +188,6 @@ VOID ExitBatch (LPTSTR msg)
|
||||||
if (bc->params)
|
if (bc->params)
|
||||||
cmd_free(bc->params);
|
cmd_free(bc->params);
|
||||||
|
|
||||||
if (bc->forproto)
|
|
||||||
cmd_free(bc->forproto);
|
|
||||||
|
|
||||||
if (bc->ffind)
|
|
||||||
cmd_free(bc->ffind);
|
|
||||||
|
|
||||||
UndoRedirection(bc->RedirList, NULL);
|
UndoRedirection(bc->RedirList, NULL);
|
||||||
FreeRedirection(bc->RedirList);
|
FreeRedirection(bc->RedirList);
|
||||||
|
|
||||||
|
@ -270,9 +264,8 @@ BOOL Batch (LPTSTR fullname, LPTSTR firstword, LPTSTR param, BOOL forcenew)
|
||||||
bc->bEcho = bEcho; /* Preserve echo across batch calls */
|
bc->bEcho = bEcho; /* Preserve echo across batch calls */
|
||||||
bc->shiftlevel = 0;
|
bc->shiftlevel = 0;
|
||||||
|
|
||||||
bc->ffind = NULL;
|
|
||||||
bc->forvar = _T('\0');
|
bc->forvar = _T('\0');
|
||||||
bc->forproto = NULL;
|
bc->forvarcount = 0;
|
||||||
bc->params = BatchParams (firstword, param);
|
bc->params = BatchParams (firstword, param);
|
||||||
//
|
//
|
||||||
// Allocate enough memory to hold the params and copy them over without modifications
|
// Allocate enough memory to hold the params and copy them over without modifications
|
||||||
|
@ -351,76 +344,6 @@ LPTSTR ReadBatchLine ()
|
||||||
if (bc == NULL)
|
if (bc == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* If its a FOR context... */
|
|
||||||
if (bc->forvar)
|
|
||||||
{
|
|
||||||
LPTSTR sp = bc->forproto; /* pointer to prototype command */
|
|
||||||
LPTSTR dp = textline; /* Place to expand protoype */
|
|
||||||
LPTSTR fv = FindArg (0); /* Next list element */
|
|
||||||
|
|
||||||
/* End of list so... */
|
|
||||||
if ((fv == NULL) || (*fv == _T('\0')))
|
|
||||||
{
|
|
||||||
/* just exit this context */
|
|
||||||
ExitBatch (NULL);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_tcscspn (fv, _T("?*")) == _tcslen (fv))
|
|
||||||
{
|
|
||||||
/* element is wild file */
|
|
||||||
bc->shiftlevel++; /* No use it and shift list */
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Wild file spec, find first (or next) file name */
|
|
||||||
if (bc->ffind)
|
|
||||||
{
|
|
||||||
/* First already done so do next */
|
|
||||||
|
|
||||||
fv = FindNextFile (bc->hFind, bc->ffind) ? bc->ffind->cFileName : NULL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* For first find, allocate a find first block */
|
|
||||||
if ((bc->ffind = (LPWIN32_FIND_DATA)cmd_alloc (sizeof (WIN32_FIND_DATA))) == NULL)
|
|
||||||
{
|
|
||||||
error_out_of_memory();
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bc->hFind = FindFirstFile (fv, bc->ffind);
|
|
||||||
|
|
||||||
fv = !(bc->hFind==INVALID_HANDLE_VALUE) ? bc->ffind->cFileName : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fv == NULL)
|
|
||||||
{
|
|
||||||
/* Null indicates no more files.. */
|
|
||||||
cmd_free (bc->ffind); /* free the buffer */
|
|
||||||
bc->ffind = NULL;
|
|
||||||
bc->shiftlevel++; /* On to next list element */
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* At this point, fv points to parameter string */
|
|
||||||
bc->forvalue = fv;
|
|
||||||
|
|
||||||
/* Double up % signs so they will get through the parser intact */
|
|
||||||
while (*sp)
|
|
||||||
{
|
|
||||||
if (*sp == _T('%'))
|
|
||||||
*dp++ = _T('%');
|
|
||||||
*dp++ = *sp++;
|
|
||||||
}
|
|
||||||
|
|
||||||
*dp++ = _T('\n');
|
|
||||||
*dp = _T('\0');
|
|
||||||
|
|
||||||
return textline;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!FileGetString (bc->hBatchFile, textline, sizeof (textline) / sizeof (textline[0]) - 1))
|
if (!FileGetString (bc->hBatchFile, textline, sizeof (textline) / sizeof (textline[0]) - 1))
|
||||||
{
|
{
|
||||||
TRACE ("ReadBatchLine(): Reached EOF!\n");
|
TRACE ("ReadBatchLine(): Reached EOF!\n");
|
||||||
|
|
|
@ -10,18 +10,16 @@
|
||||||
typedef struct tagBATCHCONTEXT
|
typedef struct tagBATCHCONTEXT
|
||||||
{
|
{
|
||||||
struct tagBATCHCONTEXT *prev;
|
struct tagBATCHCONTEXT *prev;
|
||||||
LPWIN32_FIND_DATA ffind;
|
|
||||||
HANDLE hBatchFile;
|
HANDLE hBatchFile;
|
||||||
TCHAR BatchFilePath[MAX_PATH];
|
TCHAR BatchFilePath[MAX_PATH];
|
||||||
LPTSTR forproto;
|
|
||||||
LPTSTR params;
|
LPTSTR params;
|
||||||
LPTSTR raw_params; /* Holds the raw params given by the input */
|
LPTSTR raw_params; /* Holds the raw params given by the input */
|
||||||
INT shiftlevel;
|
INT shiftlevel;
|
||||||
BOOL bEcho; /* Preserve echo flag across batch calls */
|
BOOL bEcho; /* Preserve echo flag across batch calls */
|
||||||
HANDLE hFind; /* Preserve find handle when doing a for */
|
|
||||||
REDIRECTION *RedirList;
|
REDIRECTION *RedirList;
|
||||||
TCHAR forvar;
|
TCHAR forvar;
|
||||||
LPTSTR forvalue;
|
UINT forvarcount;
|
||||||
|
LPTSTR *forvalues;
|
||||||
} BATCH_CONTEXT, *LPBATCH_CONTEXT;
|
} BATCH_CONTEXT, *LPBATCH_CONTEXT;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,7 @@ INT cmd_call (LPTSTR param)
|
||||||
bc->params = NULL;
|
bc->params = NULL;
|
||||||
bc->shiftlevel = 0;
|
bc->shiftlevel = 0;
|
||||||
bc->forvar = 0; /* HBP004 */
|
bc->forvar = 0; /* HBP004 */
|
||||||
bc->forproto = NULL; /* HBP004 */
|
bc->forvarcount = 0;
|
||||||
bc->RedirList = NULL;
|
bc->RedirList = NULL;
|
||||||
ParseCommandLine (param);
|
ParseCommandLine (param);
|
||||||
|
|
||||||
|
|
|
@ -914,6 +914,9 @@ ExecuteCommand(PARSED_COMMAND *Cmd)
|
||||||
case C_IF:
|
case C_IF:
|
||||||
Success = ExecuteIf(Cmd);
|
Success = ExecuteIf(Cmd);
|
||||||
break;
|
break;
|
||||||
|
case C_FOR:
|
||||||
|
Success = ExecuteFor(Cmd);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
UndoRedirection(Cmd->Redirections, NULL);
|
UndoRedirection(Cmd->Redirections, NULL);
|
||||||
|
@ -1271,14 +1274,15 @@ SubstituteForVars(TCHAR *Src, TCHAR *Dest)
|
||||||
{
|
{
|
||||||
/* This might be a variable. Search the list of contexts for it */
|
/* This might be a variable. Search the list of contexts for it */
|
||||||
BATCH_CONTEXT *Ctx = bc;
|
BATCH_CONTEXT *Ctx = bc;
|
||||||
while (Ctx && Ctx->forvar != Src[1])
|
while (Ctx && (UINT)(Src[1] - Ctx->forvar) >= Ctx->forvarcount)
|
||||||
Ctx = Ctx->prev;
|
Ctx = Ctx->prev;
|
||||||
if (Ctx)
|
if (Ctx)
|
||||||
{
|
{
|
||||||
/* Found it */
|
/* Found it */
|
||||||
if (Dest + _tcslen(Ctx->forvalue) > DestEnd)
|
LPTSTR Value = Ctx->forvalues[Src[1] - Ctx->forvar];
|
||||||
|
if (Dest + _tcslen(Value) > DestEnd)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
Dest = _stpcpy(Dest, Ctx->forvalue);
|
Dest = _stpcpy(Dest, Value);
|
||||||
Src += 2;
|
Src += 2;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -237,7 +237,12 @@ VOID CompleteFilename (LPTSTR, BOOL, LPTSTR, UINT);
|
||||||
|
|
||||||
|
|
||||||
/* Prototypes for FOR.C */
|
/* Prototypes for FOR.C */
|
||||||
|
#define FOR_DIRS 1 /* /D */
|
||||||
|
#define FOR_F 2 /* /F */
|
||||||
|
#define FOR_LOOP 4 /* /L */
|
||||||
|
#define FOR_RECURSIVE 8 /* /R */
|
||||||
INT cmd_for (LPTSTR);
|
INT cmd_for (LPTSTR);
|
||||||
|
BOOL ExecuteFor(struct _PARSED_COMMAND *Cmd);
|
||||||
|
|
||||||
|
|
||||||
/* Prototypes for FREE.C */
|
/* Prototypes for FREE.C */
|
||||||
|
@ -342,7 +347,7 @@ INT CommandMsgbox (LPTSTR);
|
||||||
|
|
||||||
|
|
||||||
/* Prototypes from PARSER.C */
|
/* Prototypes from PARSER.C */
|
||||||
enum { C_COMMAND, C_QUIET, C_BLOCK, C_MULTI, C_IFFAILURE, C_IFSUCCESS, C_PIPE, C_IF };
|
enum { C_COMMAND, C_QUIET, C_BLOCK, C_MULTI, C_IFFAILURE, C_IFSUCCESS, C_PIPE, C_IF, C_FOR };
|
||||||
typedef struct _PARSED_COMMAND
|
typedef struct _PARSED_COMMAND
|
||||||
{
|
{
|
||||||
struct _PARSED_COMMAND *Subcommands;
|
struct _PARSED_COMMAND *Subcommands;
|
||||||
|
@ -363,6 +368,14 @@ typedef struct _PARSED_COMMAND
|
||||||
TCHAR *LeftArg;
|
TCHAR *LeftArg;
|
||||||
TCHAR *RightArg;
|
TCHAR *RightArg;
|
||||||
} If;
|
} If;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
BYTE Switches;
|
||||||
|
TCHAR Variable;
|
||||||
|
LPTSTR Params;
|
||||||
|
LPTSTR List;
|
||||||
|
struct tagBATCHCONTEXT *Context;
|
||||||
|
} For;
|
||||||
};
|
};
|
||||||
} PARSED_COMMAND;
|
} PARSED_COMMAND;
|
||||||
PARSED_COMMAND *ParseCommand(LPTSTR Line);
|
PARSED_COMMAND *ParseCommand(LPTSTR Line);
|
||||||
|
|
|
@ -33,27 +33,9 @@
|
||||||
#include <precomp.h>
|
#include <precomp.h>
|
||||||
|
|
||||||
|
|
||||||
/*
|
/* FOR is a special command, so this function is only used for showing help now */
|
||||||
* Perform FOR command.
|
|
||||||
*
|
|
||||||
* First check syntax is correct : FOR %v IN ( <list> ) DO <command>
|
|
||||||
* v must be alphabetic, <command> must not be empty.
|
|
||||||
*
|
|
||||||
* If all is correct build a new bcontext structure which preserves
|
|
||||||
* the necessary information so that readbatchline can expand
|
|
||||||
* each the command prototype for each list element.
|
|
||||||
*
|
|
||||||
* You might look on a FOR as being a called batch file with one line
|
|
||||||
* per list element.
|
|
||||||
*/
|
|
||||||
|
|
||||||
INT cmd_for (LPTSTR param)
|
INT cmd_for (LPTSTR param)
|
||||||
{
|
{
|
||||||
LPBATCH_CONTEXT lpNew;
|
|
||||||
LPTSTR pp;
|
|
||||||
TCHAR var;
|
|
||||||
TCHAR szMsg[RC_STRING_MAX_SIZE];
|
|
||||||
|
|
||||||
TRACE ("cmd_for (\'%s\')\n", debugstr_aw(param));
|
TRACE ("cmd_for (\'%s\')\n", debugstr_aw(param));
|
||||||
|
|
||||||
if (!_tcsncmp (param, _T("/?"), 2))
|
if (!_tcsncmp (param, _T("/?"), 2))
|
||||||
|
@ -62,87 +44,459 @@ INT cmd_for (LPTSTR param)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check that first element is % then an alpha char followed by space */
|
error_syntax(param);
|
||||||
if ((*param != _T('%')) || !_istalpha (*(param + 1)) || !_istspace (*(param + 2)))
|
|
||||||
{
|
|
||||||
LoadString( CMD_ModuleHandle, STRING_FOR_ERROR, szMsg, RC_STRING_MAX_SIZE);
|
|
||||||
error_syntax (szMsg);
|
|
||||||
return 1;
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the next element of the FOR's list */
|
||||||
|
static BOOL GetNextElement(TCHAR **pStart, TCHAR **pEnd)
|
||||||
|
{
|
||||||
|
TCHAR *p = *pEnd;
|
||||||
|
BOOL InQuotes = FALSE;
|
||||||
|
while (_istspace(*p))
|
||||||
|
p++;
|
||||||
|
if (!*p)
|
||||||
|
return FALSE;
|
||||||
|
*pStart = p;
|
||||||
|
while (*p && (InQuotes || !_istspace(*p)))
|
||||||
|
InQuotes ^= (*p++ == _T('"'));
|
||||||
|
*pEnd = p;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Execute a single instance of a FOR command */
|
||||||
|
static void RunInstance(PARSED_COMMAND *Cmd)
|
||||||
|
{
|
||||||
|
if (bEcho && Cmd->Subcommands->Type != C_QUIET)
|
||||||
|
{
|
||||||
|
ConOutChar(_T('\n'));
|
||||||
|
PrintPrompt();
|
||||||
|
EchoCommand(Cmd->Subcommands);
|
||||||
|
ConOutChar(_T('\n'));
|
||||||
|
}
|
||||||
|
/* Just run the command (variable expansion is done in DoDelayedExpansion) */
|
||||||
|
ExecuteCommand(Cmd->Subcommands);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if this FOR should be terminated early */
|
||||||
|
static BOOL Exiting(PARSED_COMMAND *Cmd)
|
||||||
|
{
|
||||||
|
/* Someone might have removed our context by calling ExitBatch */
|
||||||
|
return bCtrlBreak || bc != Cmd->For.Context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read the contents of a text file into memory,
|
||||||
|
* dynamically allocating enough space to hold it all */
|
||||||
|
static LPTSTR ReadFileContents(FILE *InputFile, TCHAR *Buffer)
|
||||||
|
{
|
||||||
|
DWORD Len = 0;
|
||||||
|
DWORD AllocLen = 1000;
|
||||||
|
LPTSTR Contents = cmd_alloc(AllocLen * sizeof(TCHAR));
|
||||||
|
if (!Contents)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
while (_fgetts(Buffer, CMDLINE_LENGTH, InputFile))
|
||||||
|
{
|
||||||
|
DWORD CharsRead = _tcslen(Buffer);
|
||||||
|
while (Len + CharsRead >= AllocLen)
|
||||||
|
{
|
||||||
|
Contents = cmd_realloc(Contents, (AllocLen *= 2) * sizeof(TCHAR));
|
||||||
|
if (!Contents)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
_tcscpy(&Contents[Len], Buffer);
|
||||||
|
Len += CharsRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
param++;
|
Contents[Len] = _T('\0');
|
||||||
var = *param++; /* Save FOR var name */
|
return Contents;
|
||||||
|
}
|
||||||
|
|
||||||
while (_istspace (*param))
|
static BOOL ForF(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
|
||||||
param++;
|
{
|
||||||
|
LPTSTR Delims = _T(" \t");
|
||||||
|
TCHAR Eol = _T(';');
|
||||||
|
INT SkipLines = 0;
|
||||||
|
DWORD Tokens = (1 << 1);
|
||||||
|
BOOL RemainderVar = FALSE;
|
||||||
|
TCHAR StringQuote = _T('"');
|
||||||
|
TCHAR CommandQuote = _T('\'');
|
||||||
|
LPTSTR Variables[32];
|
||||||
|
TCHAR *Start, *End;
|
||||||
|
INT i;
|
||||||
|
|
||||||
/* Check next element is 'IN' */
|
if (Cmd->For.Params)
|
||||||
if ((_tcsnicmp (param, _T("in"), 2) != 0) || !_istspace (*(param + 2)))
|
|
||||||
{
|
{
|
||||||
LoadString(CMD_ModuleHandle, STRING_FOR_ERROR1, szMsg, RC_STRING_MAX_SIZE);
|
TCHAR Quote = 0;
|
||||||
error_syntax(szMsg);
|
TCHAR *Param = Cmd->For.Params;
|
||||||
return 1;
|
if (*Param == _T('"') || *Param == _T('\''))
|
||||||
|
Quote = *Param++;
|
||||||
|
|
||||||
|
while (*Param && *Param != Quote)
|
||||||
|
{
|
||||||
|
if (*Param <= _T(' '))
|
||||||
|
{
|
||||||
|
Param++;
|
||||||
|
}
|
||||||
|
else if (_tcsnicmp(Param, _T("delims="), 7) == 0)
|
||||||
|
{
|
||||||
|
Param += 7;
|
||||||
|
/* delims=xxx: Specifies the list of characters that separate tokens */
|
||||||
|
Delims = Param;
|
||||||
|
while (*Param && *Param != Quote)
|
||||||
|
{
|
||||||
|
if (*Param == _T(' '))
|
||||||
|
{
|
||||||
|
TCHAR *FirstSpace = Param;
|
||||||
|
Param += _tcsspn(Param, _T(" "));
|
||||||
|
/* Exclude trailing spaces if this is not the last parameter */
|
||||||
|
if (*Param && *Param != Quote)
|
||||||
|
*FirstSpace = _T('\0');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Param++;
|
||||||
|
}
|
||||||
|
if (*Param == Quote)
|
||||||
|
*Param++ = _T('\0');
|
||||||
|
}
|
||||||
|
else if (_tcsnicmp(Param, _T("eol="), 4) == 0)
|
||||||
|
{
|
||||||
|
Param += 4;
|
||||||
|
/* eol=c: Lines starting with this character (may be
|
||||||
|
* preceded by delimiters) are skipped. */
|
||||||
|
Eol = *Param;
|
||||||
|
if (Eol != _T('\0'))
|
||||||
|
Param++;
|
||||||
|
}
|
||||||
|
else if (_tcsnicmp(Param, _T("skip="), 5) == 0)
|
||||||
|
{
|
||||||
|
/* skip=n: Number of lines to skip at the beginning of each file */
|
||||||
|
SkipLines = _tcstol(Param + 5, &Param, 0);
|
||||||
|
if (SkipLines <= 0)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
else if (_tcsnicmp(Param, _T("tokens="), 7) == 0)
|
||||||
|
{
|
||||||
|
Param += 7;
|
||||||
|
/* tokens=x,y,m-n: List of token numbers (must be between
|
||||||
|
* 1 and 31) that will be assigned into variables. */
|
||||||
|
Tokens = 0;
|
||||||
|
while (*Param && *Param != Quote && *Param != _T('*'))
|
||||||
|
{
|
||||||
|
INT First = _tcstol(Param, &Param, 0);
|
||||||
|
INT Last = First;
|
||||||
|
if (First < 1)
|
||||||
|
goto error;
|
||||||
|
if (*Param == _T('-'))
|
||||||
|
{
|
||||||
|
/* It's a range of tokens */
|
||||||
|
Last = _tcstol(Param + 1, &Param, 0);
|
||||||
|
if (Last < First || Last > 31)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
Tokens |= (2 << Last) - (1 << First);
|
||||||
|
|
||||||
|
if (*Param != _T(','))
|
||||||
|
break;
|
||||||
|
Param++;
|
||||||
|
}
|
||||||
|
/* With an asterisk at the end, an additional variable
|
||||||
|
* will be created to hold the remainder of the line
|
||||||
|
* (after the last token specified). */
|
||||||
|
if (*Param == _T('*'))
|
||||||
|
{
|
||||||
|
RemainderVar = TRUE;
|
||||||
|
Param++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (_tcsnicmp(Param, _T("useback"), 7) == 0)
|
||||||
|
{
|
||||||
|
Param += 7;
|
||||||
|
/* usebackq: Use alternate quote characters */
|
||||||
|
StringQuote = _T('\'');
|
||||||
|
CommandQuote = _T('`');
|
||||||
|
/* Can be written as either "useback" or "usebackq" */
|
||||||
|
if (_totlower(*Param) == _T('q'))
|
||||||
|
Param++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
error:
|
||||||
|
error_syntax(Param);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
param += 2;
|
/* Count how many variables will be set: one for each token,
|
||||||
while (_istspace (*param))
|
* plus maybe one for the remainder */
|
||||||
param++;
|
bc->forvarcount = RemainderVar;
|
||||||
|
for (i = 1; i < 32; i++)
|
||||||
|
bc->forvarcount += (Tokens >> i & 1);
|
||||||
|
bc->forvalues = Variables;
|
||||||
|
|
||||||
/* Folowed by a '(', find also matching ')' */
|
if (*List == StringQuote || *List == CommandQuote)
|
||||||
if ((*param != _T('(')) || (NULL == (pp = _tcsrchr (param, _T(')')))))
|
|
||||||
{
|
{
|
||||||
LoadString(CMD_ModuleHandle, STRING_FOR_ERROR2, szMsg, RC_STRING_MAX_SIZE);
|
/* Treat the entire "list" as one single element */
|
||||||
error_syntax(szMsg);
|
Start = List;
|
||||||
return 1;
|
End = &List[_tcslen(List)];
|
||||||
|
goto single_element;
|
||||||
}
|
}
|
||||||
|
|
||||||
*pp++ = _T('\0');
|
End = List;
|
||||||
param++; /* param now points at null terminated list */
|
while (GetNextElement(&Start, &End))
|
||||||
|
|
||||||
while (_istspace (*pp))
|
|
||||||
pp++;
|
|
||||||
|
|
||||||
/* Check DO follows */
|
|
||||||
if ((_tcsnicmp (pp, _T("do"), 2) != 0) || !_istspace (*(pp + 2)))
|
|
||||||
{
|
{
|
||||||
LoadString(CMD_ModuleHandle, STRING_FOR_ERROR3, szMsg, RC_STRING_MAX_SIZE);
|
FILE *InputFile;
|
||||||
error_syntax(szMsg);
|
LPTSTR FullInput, In, NextLine;
|
||||||
return 1;
|
INT Skip;
|
||||||
|
single_element:
|
||||||
|
|
||||||
|
if (*Start == StringQuote && End[-1] == StringQuote)
|
||||||
|
{
|
||||||
|
/* Input given directly as a string */
|
||||||
|
End[-1] = _T('\0');
|
||||||
|
FullInput = cmd_dup(Start + 1);
|
||||||
|
}
|
||||||
|
else if (*Start == CommandQuote && End[-1] == CommandQuote)
|
||||||
|
{
|
||||||
|
/* Read input from a command */
|
||||||
|
End[-1] = _T('\0');
|
||||||
|
InputFile = _tpopen(Start + 1, _T("r"));
|
||||||
|
if (!InputFile)
|
||||||
|
{
|
||||||
|
error_bad_command();
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
FullInput = ReadFileContents(InputFile, Buffer);
|
||||||
|
_pclose(InputFile);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Read input from a file */
|
||||||
|
TCHAR Temp = *End;
|
||||||
|
*End = _T('\0');
|
||||||
|
StripQuotes(Start);
|
||||||
|
InputFile = _tfopen(Start, _T("r"));
|
||||||
|
*End = Temp;
|
||||||
|
if (!InputFile)
|
||||||
|
{
|
||||||
|
error_sfile_not_found(Start);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
FullInput = ReadFileContents(InputFile, Buffer);
|
||||||
|
fclose(InputFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
pp += 2;
|
if (!FullInput)
|
||||||
while (_istspace (*pp))
|
|
||||||
pp++;
|
|
||||||
|
|
||||||
/* Check that command tail is not empty */
|
|
||||||
if (*pp == _T('\0'))
|
|
||||||
{
|
{
|
||||||
LoadString(CMD_ModuleHandle, STRING_FOR_ERROR4, szMsg, RC_STRING_MAX_SIZE);
|
error_out_of_memory();
|
||||||
error_syntax(szMsg);
|
return FALSE;
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* OK all is correct, build a bcontext.... */
|
/* Loop over the input line by line */
|
||||||
lpNew = (LPBATCH_CONTEXT)cmd_alloc (sizeof (BATCH_CONTEXT));
|
In = FullInput;
|
||||||
|
Skip = SkipLines;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
DWORD RemainingTokens = Tokens;
|
||||||
|
LPTSTR *CurVar = Variables;
|
||||||
|
|
||||||
|
NextLine = _tcschr(In, _T('\n'));
|
||||||
|
if (NextLine)
|
||||||
|
*NextLine++ = _T('\0');
|
||||||
|
|
||||||
|
if (--Skip >= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Ignore lines where the first token starts with the eol character */
|
||||||
|
In += _tcsspn(In, Delims);
|
||||||
|
if (*In == Eol)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
while ((RemainingTokens >>= 1) != 0)
|
||||||
|
{
|
||||||
|
/* Save pointer to this token in a variable if requested */
|
||||||
|
if (RemainingTokens & 1)
|
||||||
|
*CurVar++ = In;
|
||||||
|
/* Find end of token */
|
||||||
|
In += _tcscspn(In, Delims);
|
||||||
|
/* Nul-terminate it and advance to next token */
|
||||||
|
if (*In)
|
||||||
|
{
|
||||||
|
*In++ = _T('\0');
|
||||||
|
In += _tcsspn(In, Delims);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Save pointer to remainder of line */
|
||||||
|
*CurVar = In;
|
||||||
|
|
||||||
|
/* Don't run unless the line had enough tokens to fill at least one variable */
|
||||||
|
if (*Variables[0])
|
||||||
|
RunInstance(Cmd);
|
||||||
|
} while (!Exiting(Cmd) && (In = NextLine) != NULL);
|
||||||
|
cmd_free(FullInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FOR /L: Do a numeric loop */
|
||||||
|
static void ForLoop(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
|
||||||
|
{
|
||||||
|
enum { START, STEP, END };
|
||||||
|
INT params[3] = { 0, 0, 0 };
|
||||||
|
INT i;
|
||||||
|
|
||||||
|
TCHAR *Start, *End = List;
|
||||||
|
for (i = 0; i < 3 && GetNextElement(&Start, &End); i++)
|
||||||
|
params[i] = _tcstol(Start, NULL, 0);
|
||||||
|
|
||||||
|
i = params[START];
|
||||||
|
while (!Exiting(Cmd) &&
|
||||||
|
(params[STEP] >= 0 ? (i <= params[END]) : (i >= params[END])))
|
||||||
|
{
|
||||||
|
_itot(i, Buffer, 10);
|
||||||
|
RunInstance(Cmd);
|
||||||
|
i += params[STEP];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process a FOR in one directory. Stored in Buffer (up to BufPos) is a
|
||||||
|
* string which is prefixed to each element of the list. In a normal FOR
|
||||||
|
* it will be empty, but in FOR /R it will be the directory name. */
|
||||||
|
static void ForDir(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer, TCHAR *BufPos)
|
||||||
|
{
|
||||||
|
TCHAR *Start, *End = List;
|
||||||
|
while (!Exiting(Cmd) && GetNextElement(&Start, &End))
|
||||||
|
{
|
||||||
|
if (BufPos + (End - Start) > &Buffer[CMDLINE_LENGTH])
|
||||||
|
continue;
|
||||||
|
memcpy(BufPos, Start, (End - Start) * sizeof(TCHAR));
|
||||||
|
BufPos[End - Start] = _T('\0');
|
||||||
|
|
||||||
|
if (_tcschr(BufPos, _T('?')) || _tcschr(BufPos, _T('*')))
|
||||||
|
{
|
||||||
|
WIN32_FIND_DATA w32fd;
|
||||||
|
HANDLE hFind;
|
||||||
|
TCHAR *FilePart;
|
||||||
|
|
||||||
|
StripQuotes(BufPos);
|
||||||
|
hFind = FindFirstFile(Buffer, &w32fd);
|
||||||
|
if (hFind == INVALID_HANDLE_VALUE)
|
||||||
|
continue;
|
||||||
|
FilePart = _tcsrchr(BufPos, _T('\\'));
|
||||||
|
FilePart = FilePart ? FilePart + 1 : BufPos;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
|
||||||
|
continue;
|
||||||
|
if (!(w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||||
|
!= !(Cmd->For.Switches & FOR_DIRS))
|
||||||
|
continue;
|
||||||
|
if (_tcscmp(w32fd.cFileName, _T(".")) == 0 ||
|
||||||
|
_tcscmp(w32fd.cFileName, _T("..")) == 0)
|
||||||
|
continue;
|
||||||
|
_tcscpy(FilePart, w32fd.cFileName);
|
||||||
|
RunInstance(Cmd);
|
||||||
|
} while (!Exiting(Cmd) && FindNextFile(hFind, &w32fd));
|
||||||
|
FindClose(hFind);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RunInstance(Cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FOR /R: Process a FOR in each directory of a tree, recursively. */
|
||||||
|
static void ForRecursive(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer, TCHAR *BufPos)
|
||||||
|
{
|
||||||
|
HANDLE hFind;
|
||||||
|
WIN32_FIND_DATA w32fd;
|
||||||
|
|
||||||
|
if (BufPos[-1] != _T('\\'))
|
||||||
|
{
|
||||||
|
*BufPos++ = _T('\\');
|
||||||
|
*BufPos = _T('\0');
|
||||||
|
}
|
||||||
|
|
||||||
|
ForDir(Cmd, List, Buffer, BufPos);
|
||||||
|
|
||||||
|
_tcscpy(BufPos, _T("*"));
|
||||||
|
hFind = FindFirstFile(Buffer, &w32fd);
|
||||||
|
if (hFind == INVALID_HANDLE_VALUE)
|
||||||
|
return;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (!(w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
||||||
|
continue;
|
||||||
|
if (_tcscmp(w32fd.cFileName, _T(".")) == 0 ||
|
||||||
|
_tcscmp(w32fd.cFileName, _T("..")) == 0)
|
||||||
|
continue;
|
||||||
|
ForRecursive(Cmd, List, Buffer, _stpcpy(BufPos, w32fd.cFileName));
|
||||||
|
} while (!Exiting(Cmd) && FindNextFile(hFind, &w32fd));
|
||||||
|
FindClose(hFind);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL
|
||||||
|
ExecuteFor(PARSED_COMMAND *Cmd)
|
||||||
|
{
|
||||||
|
TCHAR Buffer[CMDLINE_LENGTH]; /* Buffer to hold the variable value */
|
||||||
|
LPTSTR BufferPtr = Buffer;
|
||||||
|
LPBATCH_CONTEXT lpNew;
|
||||||
|
BOOL Success = TRUE;
|
||||||
|
LPTSTR List = DoDelayedExpansion(Cmd->For.List);
|
||||||
|
|
||||||
|
if (!List)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* Create our pseudo-batch-context */
|
||||||
|
lpNew = cmd_alloc(sizeof(BATCH_CONTEXT));
|
||||||
|
if (!lpNew)
|
||||||
|
return FALSE;
|
||||||
|
Cmd->For.Context = lpNew;
|
||||||
|
|
||||||
lpNew->prev = bc;
|
lpNew->prev = bc;
|
||||||
bc = lpNew;
|
bc = lpNew;
|
||||||
|
|
||||||
bc->hBatchFile = INVALID_HANDLE_VALUE;
|
bc->hBatchFile = INVALID_HANDLE_VALUE;
|
||||||
bc->ffind = NULL;
|
|
||||||
bc->raw_params = NULL;
|
bc->raw_params = NULL;
|
||||||
bc->params = BatchParams (_T(""), param); /* Split out list */
|
bc->params = NULL;
|
||||||
bc->shiftlevel = 0;
|
bc->shiftlevel = 0;
|
||||||
bc->forvar = var;
|
bc->forvar = Cmd->For.Variable;
|
||||||
bc->forproto = cmd_dup (pp);
|
bc->forvarcount = 1;
|
||||||
|
bc->forvalues = &BufferPtr;
|
||||||
if (bc->prev)
|
if (bc->prev)
|
||||||
bc->bEcho = bc->prev->bEcho;
|
bc->bEcho = bc->prev->bEcho;
|
||||||
else
|
else
|
||||||
bc->bEcho = bEcho;
|
bc->bEcho = bEcho;
|
||||||
bc->RedirList = NULL;
|
bc->RedirList = NULL;
|
||||||
|
|
||||||
|
if (Cmd->For.Switches & FOR_F)
|
||||||
|
{
|
||||||
|
Success = ForF(Cmd, List, Buffer);
|
||||||
|
}
|
||||||
|
else if (Cmd->For.Switches & FOR_LOOP)
|
||||||
|
{
|
||||||
|
ForLoop(Cmd, List, Buffer);
|
||||||
|
}
|
||||||
|
else if (Cmd->For.Switches & FOR_RECURSIVE)
|
||||||
|
{
|
||||||
|
DWORD Len = GetFullPathName(Cmd->For.Params ? Cmd->For.Params : _T("."),
|
||||||
|
MAX_PATH, Buffer, NULL);
|
||||||
|
ForRecursive(Cmd, List, Buffer, &Buffer[Len]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ForDir(Cmd, List, Buffer, Buffer);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
/* Remove our context, unless someone already did that */
|
||||||
|
if (bc == lpNew)
|
||||||
|
ExitBatch(NULL);
|
||||||
|
|
||||||
|
cmd_free(List);
|
||||||
|
return Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* EOF */
|
/* EOF */
|
||||||
|
|
|
@ -413,6 +413,115 @@ condition_done:
|
||||||
return Cmd;
|
return Cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Parse a FOR command.
|
||||||
|
* Syntax is: FOR [options] %var IN (list) DO command */
|
||||||
|
static PARSED_COMMAND *ParseFor(void)
|
||||||
|
{
|
||||||
|
PARSED_COMMAND *Cmd = cmd_alloc(sizeof(PARSED_COMMAND));
|
||||||
|
TCHAR List[CMDLINE_LENGTH];
|
||||||
|
TCHAR *Pos = List;
|
||||||
|
|
||||||
|
memset(Cmd, 0, sizeof(PARSED_COMMAND));
|
||||||
|
Cmd->Type = C_FOR;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
if (_tcsicmp(CurrentToken, _T("/D")) == 0)
|
||||||
|
Cmd->For.Switches |= FOR_DIRS;
|
||||||
|
else if (_tcsicmp(CurrentToken, _T("/F")) == 0)
|
||||||
|
{
|
||||||
|
Cmd->For.Switches |= FOR_F;
|
||||||
|
if (!Cmd->For.Params)
|
||||||
|
{
|
||||||
|
ParseToken(0, STANDARD_SEPS);
|
||||||
|
if (CurrentToken[0] == _T('/') || CurrentToken[0] == _T('%'))
|
||||||
|
break;
|
||||||
|
Cmd->For.Params = cmd_dup(CurrentToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (_tcsicmp(CurrentToken, _T("/L")) == 0)
|
||||||
|
Cmd->For.Switches |= FOR_LOOP;
|
||||||
|
else if (_tcsicmp(CurrentToken, _T("/R")) == 0)
|
||||||
|
{
|
||||||
|
Cmd->For.Switches |= FOR_RECURSIVE;
|
||||||
|
if (!Cmd->For.Params)
|
||||||
|
{
|
||||||
|
ParseToken(0, STANDARD_SEPS);
|
||||||
|
if (CurrentToken[0] == _T('/') || CurrentToken[0] == _T('%'))
|
||||||
|
break;
|
||||||
|
StripQuotes(CurrentToken);
|
||||||
|
Cmd->For.Params = cmd_dup(CurrentToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
ParseToken(0, STANDARD_SEPS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure there aren't two different switches specified
|
||||||
|
* at the same time, unless they're /D and /R */
|
||||||
|
if ((Cmd->For.Switches & (Cmd->For.Switches - 1)) != 0
|
||||||
|
&& Cmd->For.Switches != (FOR_DIRS | FOR_RECURSIVE))
|
||||||
|
{
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Variable name should be % and just one other character */
|
||||||
|
if (CurrentToken[0] != _T('%') || _tcslen(CurrentToken) != 2)
|
||||||
|
goto error;
|
||||||
|
Cmd->For.Variable = CurrentToken[1];
|
||||||
|
|
||||||
|
ParseToken(0, STANDARD_SEPS);
|
||||||
|
if (_tcsicmp(CurrentToken, _T("in")) != 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (ParseToken(_T('('), STANDARD_SEPS) != TOK_BEGIN_BLOCK)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
int Type;
|
||||||
|
|
||||||
|
/* Pretend we're inside a block so the tokenizer will stop on ')' */
|
||||||
|
InsideBlock++;
|
||||||
|
Type = ParseToken(0, STANDARD_SEPS);
|
||||||
|
InsideBlock--;
|
||||||
|
|
||||||
|
if (Type == TOK_END_BLOCK)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (Type != TOK_NORMAL)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (Pos != List)
|
||||||
|
*Pos++ = _T(' ');
|
||||||
|
|
||||||
|
if (Pos + _tcslen(CurrentToken) >= &List[CMDLINE_LENGTH])
|
||||||
|
goto error;
|
||||||
|
Pos = _stpcpy(Pos, CurrentToken);
|
||||||
|
}
|
||||||
|
*Pos = _T('\0');
|
||||||
|
Cmd->For.List = cmd_dup(List);
|
||||||
|
|
||||||
|
ParseToken(0, STANDARD_SEPS);
|
||||||
|
if (_tcsicmp(CurrentToken, _T("do")) != 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
Cmd->Subcommands = ParseCommandOp(C_OP_LOWEST);
|
||||||
|
if (Cmd->Subcommands == NULL)
|
||||||
|
{
|
||||||
|
FreeCommand(Cmd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Cmd;
|
||||||
|
|
||||||
|
error:
|
||||||
|
FreeCommand(Cmd);
|
||||||
|
ParseError();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static PARSED_COMMAND *ParseCommandPart(void)
|
static PARSED_COMMAND *ParseCommandPart(void)
|
||||||
{
|
{
|
||||||
TCHAR ParsedLine[CMDLINE_LENGTH];
|
TCHAR ParsedLine[CMDLINE_LENGTH];
|
||||||
|
@ -486,7 +595,8 @@ static PARSED_COMMAND *ParseCommandPart(void)
|
||||||
TailOffset = Pos - ParsedLine;
|
TailOffset = Pos - ParsedLine;
|
||||||
|
|
||||||
/* Check for special forms */
|
/* Check for special forms */
|
||||||
if (_tcsicmp(ParsedLine, _T("if")) == 0)
|
if (_tcsicmp(ParsedLine, _T("for")) == 0 ||
|
||||||
|
_tcsicmp(ParsedLine, _T("if")) == 0)
|
||||||
{
|
{
|
||||||
ParseToken(0, STANDARD_SEPS);
|
ParseToken(0, STANDARD_SEPS);
|
||||||
/* Do special parsing only if it's not followed by /? */
|
/* Do special parsing only if it's not followed by /? */
|
||||||
|
@ -498,7 +608,7 @@ static PARSED_COMMAND *ParseCommandPart(void)
|
||||||
FreeRedirection(RedirList);
|
FreeRedirection(RedirList);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return ParseIf();
|
return _totlower(*ParsedLine) == _T('f') ? ParseFor() : ParseIf();
|
||||||
}
|
}
|
||||||
Pos = _stpcpy(Pos, _T(" /?"));
|
Pos = _stpcpy(Pos, _T(" /?"));
|
||||||
}
|
}
|
||||||
|
@ -665,6 +775,17 @@ EchoCommand(PARSED_COMMAND *Cmd)
|
||||||
EchoCommand(Sub->Next);
|
EchoCommand(Sub->Next);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case C_FOR:
|
||||||
|
ConOutPrintf(_T("for"));
|
||||||
|
if (Cmd->For.Switches & FOR_DIRS) ConOutPrintf(_T(" /D"));
|
||||||
|
if (Cmd->For.Switches & FOR_F) ConOutPrintf(_T(" /F"));
|
||||||
|
if (Cmd->For.Switches & FOR_LOOP) ConOutPrintf(_T(" /L"));
|
||||||
|
if (Cmd->For.Switches & FOR_RECURSIVE) ConOutPrintf(_T(" /R"));
|
||||||
|
if (Cmd->For.Params)
|
||||||
|
ConOutPrintf(_T(" %s"), Cmd->For.Params);
|
||||||
|
ConOutPrintf(_T(" %%%c in (%s) do "), Cmd->For.Variable, Cmd->For.List);
|
||||||
|
EchoCommand(Cmd->Subcommands);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Redir = Cmd->Redirections; Redir; Redir = Redir->Next)
|
for (Redir = Cmd->Redirections; Redir; Redir = Redir->Next)
|
||||||
|
@ -688,5 +809,10 @@ FreeCommand(PARSED_COMMAND *Cmd)
|
||||||
cmd_free(Cmd->If.LeftArg);
|
cmd_free(Cmd->If.LeftArg);
|
||||||
cmd_free(Cmd->If.RightArg);
|
cmd_free(Cmd->If.RightArg);
|
||||||
}
|
}
|
||||||
|
else if (Cmd->Type == C_FOR)
|
||||||
|
{
|
||||||
|
cmd_free(Cmd->For.Params);
|
||||||
|
cmd_free(Cmd->For.List);
|
||||||
|
}
|
||||||
cmd_free(Cmd);
|
cmd_free(Cmd);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue