Commit 036a9f79 authored by Dave Pickles's avatar Dave Pickles Committed by Alexandre Julliard

FOR and IF commands added.

MOVE command added, but no wildcard support. Redirection added. Fixed TIME (was reporting in GMT). More help text.
parent 39b4e098
v0.12 - 4 July 1999
FOR and IF commands added.
MOVE command added, but no wildcard support.
Redirection added.
Fixed TIME (was reporting in GMT).
More help text.
v0.11 - 20 June 1999
Batch command parameters (and the SHIFT command) added.
GOTO added.
......
......@@ -10,18 +10,13 @@ WHAT'S INCLUDED
- A Makefile for Borland C++ (needs editing for directories).
WHAT'S MISSING
- Redirection, shell parameters and pipes
- Pipes
- Command-line qualifiers for most builtin commands
- MOVE command
- IF and FOR commands
- Wildcards and relative paths in COPY and RENAME
- Wildcards and relative paths in COPY, MOVE and RENAME
- Set functionality in DATE, TIME, ATTRIB, LABEL
- Full internationalisation of the text (and commands?).
WHAT DOESN'T WORK
- At present it is not possible to launch Windows GDI programs from the command
line. This is result of the way the CreateProcess() API call is implemented in
Wine, and will be fixed in a later Wine release.
- The ATTRIB command reports all files having their Archive flag set, and the
READONLY setting depends on the Unix file permissions. All other flags are
always clear. The Wine attributes API calls map to the Unix stat() function
......@@ -38,6 +33,12 @@ free space are computed to 64 bits.
but one does exist in a lower directory.
- Copy, rename, move, need the source and destination to be specified fully
with an absolute or relative path but no wildcards or partial filenames.
- The IF ERRORLEVEL construct is not implemented.
- Redirection is implemented as a command line is parsed. This means that ">"
and "<" symbols cannot appear in command arguments even within quotes.
- In many cases parsing and syntax checking is less rigorous than DOS. Thus an
existing DOS batch file will probably run unchanged under Wcmd but the reverse
may not be the case.
WINE OR WIN32 BINARY?
Wcmd can be built as a Wine binary, or (using a Win32 compiler) as a Win32 .EXE
......
......@@ -10,7 +10,6 @@
void WCMD_batch_command (char *line);
extern HANDLE STDin, STDout;
extern char nyi[];
extern char newline[];
extern char version_string[];
......@@ -106,12 +105,13 @@ int i;
WCMD_print_error ();
return;
}
p = cmd;
while ((p = strchr(p, '%'))) {
i = *(p+1) - '0';
if ((i >= 0) && (i <= 9)) {
s = strdup (p+2);
t = WCMD_parameter (context -> command, i + context -> shift_count);
t = WCMD_parameter (context -> command, i + context -> shift_count, NULL);
strcpy (p, t);
strcat (p, s);
free (s);
......@@ -126,9 +126,10 @@ int i;
* Returns the 'n'th space-delimited parameter on the command line (zero-based).
* Parameter is in static storage overwritten on the next call.
* Parameters in quotes (and brackets) are handled.
* Also returns a pointer to the location of the parameter in the command line.
*/
char *WCMD_parameter (char *s, int n) {
char *WCMD_parameter (char *s, int n, char **where) {
int i = 0;
static char param[MAX_PATH];
......@@ -141,6 +142,7 @@ char *p;
s++;
break;
case '"':
if (where != NULL) *where = s;
s++;
while ((*s != '\0') && (*s != '"')) {
*p++ = *s++;
......@@ -149,13 +151,13 @@ char *p;
*p = '\0';
return param;
}
else {
if (*s == '"') s++;
param[0] = '\0';
i++;
}
if (*s == '"') s++;
p = param;
break;
case '(':
if (where != NULL) *where = s;
s++;
while ((*s != '\0') && (*s != ')')) {
*p++ = *s++;
......@@ -164,15 +166,15 @@ char *p;
*p = '\0';
return param;
}
else {
if (*s == ')') s++;
param[0] = '\0';
i++;
}
if (*s == '"') s++;
p = param;
break;
case '\0':
return param;
default:
if (where != NULL) *where = s;
while ((*s != '\0') && (*s != ' ')) {
*p++ = *s++;
}
......@@ -180,11 +182,9 @@ char *p;
*p = '\0';
return param;
}
else {
param[0] = '\0';
p = param;
i++;
}
p = param;
}
}
}
......
......@@ -10,7 +10,7 @@
/*
* FIXME:
* - No support for redirection, pipes, shell parameters
* - No support for pipes, shell parameters
* - 32-bit limit on file sizes in DIR command
* - Lots of functionality missing from builtins
* - Messages etc need international support
......@@ -18,7 +18,8 @@
#include "wcmd.h"
extern HANDLE STDin, STDout;
void WCMD_execute (char *orig_command, char *parameter, char *substitution);
extern HINSTANCE hinst;
extern char *inbuilt[];
extern char nyi[];
......@@ -83,7 +84,7 @@ char string[8], outpath[MAX_PATH];
if (hff != INVALID_HANDLE_VALUE) {
FindClose (hff);
WCMD_output (overwrite);
ReadFile (STDin, string, sizeof(string), &count, NULL);
ReadFile (GetStdHandle(STD_INPUT_HANDLE), string, sizeof(string), &count, NULL);
if (toupper(string[0]) == 'Y') force = TRUE;
}
else force = TRUE;
......@@ -185,22 +186,84 @@ int count;
}
/****************************************************************************
/**************************************************************************
* WCMD_for
*
* Batch file loop processing.
* FIXME: We don't exhaustively check syntax. Any command which works in MessDOS
* will probably work here, but the reverse is not necessarily the case...
*/
void WCMD_for (char *p) {
if (lstrcmpi (WCMD_parameter (p, 1), "in") || lstrcmpi (WCMD_parameter (p, 3), "do")) {
WIN32_FIND_DATA fd;
HANDLE hff;
char *cmd, *item;
char set[MAX_PATH], param[MAX_PATH];
int i;
if (lstrcmpi (WCMD_parameter (p, 1, NULL), "in")
|| lstrcmpi (WCMD_parameter (p, 3, NULL), "do")
|| (param1[0] != '%')) {
WCMD_output ("Syntax error\n");
return;
}
WCMD_output (nyi);
lstrcpyn (set, WCMD_parameter (p, 2, NULL), sizeof(set));
WCMD_parameter (p, 4, &cmd);
lstrcpy (param, param1);
/*
* If the parameter within the set has a wildcard then search for matching files
* otherwise do a literal substitution.
*/
i = 0;
while (*(item = WCMD_parameter (set, i, NULL))) {
if (strpbrk (item, "*?")) {
hff = FindFirstFile (item, &fd);
if (hff == INVALID_HANDLE_VALUE) {
return;
}
do {
WCMD_execute (cmd, param, fd.cFileName);
} while (FindNextFile(hff, &fd) != 0);
FindClose (hff);
}
else {
WCMD_execute (cmd, param, item);
}
i++;
}
}
/*
* Execute a command after substituting variable text for the supplied parameter
*/
void WCMD_execute (char *orig_cmd, char *param, char *subst) {
char *new_cmd, *p, *s, *dup;
int size;
size = lstrlen (orig_cmd);
new_cmd = (char *) LocalAlloc (LMEM_FIXED | LMEM_ZEROINIT, size);
dup = s = strdup (orig_cmd);
while ((p = strstr (s, param))) {
*p = '\0';
size += lstrlen (subst);
new_cmd = (char *) LocalReAlloc ((HANDLE)new_cmd, size, 0);
strcat (new_cmd, s);
strcat (new_cmd, subst);
s = p + lstrlen (param);
}
strcat (new_cmd, s);
WCMD_process_command (new_cmd);
free (dup);
LocalFree ((HANDLE)new_cmd);
}
/**************************************************************************
* WCMD_give_help
*
......@@ -259,23 +322,67 @@ char string[MAX_PATH];
* WCMD_if
*
* Batch file conditional.
* FIXME: The "errorlevel" version is not supported.
* FIXME: Much more syntax checking needed!
*/
void WCMD_if () {
void WCMD_if (char *p) {
WCMD_output (nyi);
HANDLE h;
int negate = 0, test = 0;
char condition[MAX_PATH], *command, *s;
if (!lstrcmpi (param1, "not")) {
negate = 1;
lstrcpy (condition, param2);
}
else {
lstrcpy (condition, param1);
}
if (!lstrcmpi (condition, "errorlevel")) {
WCMD_output (nyi);
return;
}
else if (!lstrcmpi (condition, "exist")) {
if ((h = CreateFile (WCMD_parameter (p, 1+negate, NULL), GENERIC_READ, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)) != INVALID_HANDLE_VALUE) {
CloseHandle (h);
test = 1;
}
}
else if ((s = strstr (p, "=="))) {
s += 2;
if (!lstrcmpi (condition, WCMD_parameter (s, 0, NULL))) test = 1;
}
else {
WCMD_output ("Syntax error\n");
return;
}
if (test != negate) {
WCMD_parameter (p, 2+negate, &s);
command = strdup (s);
WCMD_process_command (command);
free (command);
}
}
/****************************************************************************
* WCMD_move
*
* Move a file, directory tree or wildcarded set of files.
* FIXME: Needs input and output files to be fully specified.
*/
void WCMD_move () {
WCMD_output (nyi);
int status;
if ((strchr(param1,'*') != NULL) || (strchr(param1,'%') != NULL)) {
WCMD_output ("Wildcards not yet supported\n");
return;
}
status = MoveFile (param1, param2);
if (!status) WCMD_print_error ();
}
/****************************************************************************
......@@ -290,7 +397,7 @@ DWORD count;
char string[32];
WCMD_output (anykey);
ReadFile (STDin, string, sizeof(string), &count, NULL);
ReadFile (GetStdHandle(STD_INPUT_HANDLE), string, sizeof(string), &count, NULL);
}
/****************************************************************************
......@@ -338,6 +445,8 @@ static char *dirmsg = "Input file is a directory. Use the MOVE command\n\n";
* As a result only the Readonly flag is correctly reported, the Archive bit
* is always set and the rest are not implemented. We do the Right Thing anyway.
*
* FIXME: No SET functionality.
*
*/
void WCMD_setshow_attrib () {
......@@ -347,6 +456,11 @@ HANDLE hff;
WIN32_FIND_DATA fd;
char flags[9] = {" "};
if (param1[0] == '-') {
WCMD_output (nyi);
return;
}
if (lstrlen(param1) == 0) {
GetCurrentDirectory (sizeof(param1), param1);
strcat (param1, "\\*");
......@@ -427,7 +541,7 @@ DWORD count;
if (GetDateFormat (LOCALE_USER_DEFAULT, 0, NULL, NULL,
curdate, sizeof(curdate))) {
WCMD_output ("Current Date is %s\nEnter new date: ", curdate);
ReadFile (STDin, buffer, sizeof(buffer), &count, NULL);
ReadFile (GetStdHandle(STD_INPUT_HANDLE), buffer, sizeof(buffer), &count, NULL);
if (count > 2) {
WCMD_output (nyi);
}
......@@ -534,12 +648,14 @@ void WCMD_setshow_time () {
char curtime[64], buffer[64];
DWORD count;
SYSTEMTIME st;
if (strlen(param1) == 0) {
if (GetTimeFormat (LOCALE_USER_DEFAULT, 0, NULL, NULL,
GetLocalTime(&st);
if (GetTimeFormat (LOCALE_USER_DEFAULT, 0, &st, NULL,
curtime, sizeof(curtime))) {
WCMD_output ("Current Time is %s\nEnter new time: ", curtime);
ReadFile (STDin, buffer, sizeof(buffer), &count, NULL);
ReadFile (GetStdHandle(STD_INPUT_HANDLE), buffer, sizeof(buffer), &count, NULL);
if (count > 2) {
WCMD_output (nyi);
}
......@@ -583,7 +699,7 @@ DWORD count;
}
while (ReadFile (h, buffer, sizeof(buffer), &count, NULL)) {
if (count == 0) break; /* ReadFile reports success on EOF! */
WriteFile (STDout, buffer, count, &count, NULL);
WriteFile (GetStdHandle(STD_OUTPUT_HANDLE), buffer, count, &count, NULL);
}
CloseHandle (h);
}
......@@ -654,7 +770,7 @@ static char syntax[] = "Syntax Error\n\n";
}
else {
if ((path[1] != ':') || (lstrlen(path) != 2)) {
WriteFile (STDout, syntax, strlen(syntax), &count, NULL);
WriteFile (GetStdHandle(STD_OUTPUT_HANDLE), syntax, strlen(syntax), &count, NULL);
return 0;
}
wsprintf (curdir, "%s\\", path);
......@@ -669,7 +785,11 @@ static char syntax[] = "Syntax Error\n\n";
curdir[0], label, HIWORD(serial), LOWORD(serial));
if (mode) {
WCMD_output ("Volume label (11 characters, ENTER for none)?");
ReadFile (STDin, string, sizeof(string), &count, NULL);
ReadFile (GetStdHandle(STD_INPUT_HANDLE), string, sizeof(string), &count, NULL);
if (count > 1) {
string[count-1] = '\0'; /* ReadFile output is not null-terminated! */
if (string[count-2] == '\r') string[count-2] = '\0'; /* Under Windoze we get CRLF! */
}
if (lstrlen(path) != 0) {
if (!SetVolumeLabel (curdir, string)) WCMD_print_error ();
}
......
......@@ -23,7 +23,6 @@ char * WCMD_filesize32 (int n);
char * WCMD_strrev (char *buff);
extern HANDLE STDin, STDout;
extern char nyi[];
extern char newline[];
extern char version_string[];
......@@ -144,7 +143,7 @@ __int64 byte_count;
if (line_count > 23) {
line_count = 0;
WCMD_output (anykey);
ReadFile (STDin, string, sizeof(string), &count, NULL);
ReadFile (GetStdHandle(STD_INPUT_HANDLE), string, sizeof(string), &count, NULL);
}
}
for (i=0; i<entry_count; i++) {
......@@ -170,7 +169,7 @@ __int64 byte_count;
if (++line_count > 23) {
line_count = 0;
WCMD_output (anykey);
ReadFile (STDin, string, sizeof(string), &count, NULL);
ReadFile (GetStdHandle(STD_INPUT_HANDLE), string, sizeof(string), &count, NULL);
}
}
}
......@@ -184,7 +183,7 @@ __int64 byte_count;
if (++line_count > 23) {
line_count = 0;
WCMD_output (anykey);
ReadFile (STDin, string, sizeof(string), &count, NULL);
ReadFile (GetStdHandle(STD_INPUT_HANDLE), string, sizeof(string), &count, NULL);
}
}
byte_total = byte_total + byte_count;
......@@ -196,7 +195,7 @@ __int64 byte_count;
if (++line_count > 23) {
line_count = 0;
WCMD_output (anykey);
ReadFile (STDin, string, sizeof(string), &count, NULL);
ReadFile (GetStdHandle(STD_INPUT_HANDLE), string, sizeof(string), &count, NULL);
}
}
for (i=0; i<entry_count; i++) {
......
......@@ -30,7 +30,7 @@ void WCMD_echo (char *);
void WCMD_for (char *);
void WCMD_give_help (char *command);
void WCMD_goto (void);
void WCMD_if (void);
void WCMD_if (char *);
void WCMD_move (void);
void WCMD_output (char *format, ...);
void WCMD_parse (char *s, char *q, char *p1, char *p2);
......@@ -55,7 +55,7 @@ void WCMD_version (void);
int WCMD_volume (int mode, char *command);
char *WCMD_fgets (char *s, int n, HANDLE stream);
char *WCMD_parameter (char *s, int n);
char *WCMD_parameter (char *s, int n, char **where);
char *WCMD_strtrim_leading_spaces (char *string);
void WCMD_strtrim_trailing_spaces (char *string);
......
......@@ -6,7 +6,7 @@
/*
* FIXME:
* - No support for redirection, pipes
* - No support for pipes
* - 32-bit limit on file sizes in DIR command
* - Cannot handle parameters in quotes
* - Lots of functionality missing from builtins
......@@ -29,12 +29,11 @@ char *inbuilt[] = {"ATTRIB", "CALL", "CD", "CHDIR", "CLS", "COPY", "CTTY",
"PROMPT", "REM", "REN", "RENAME", "RD", "RMDIR", "SET", "SHIFT",
"TIME", "TYPE", "VERIFY", "VER", "VOL", "EXIT"};
HANDLE STDin, STDout;
HINSTANCE hinst;
int echo_mode = 1, verify_mode = 0;
char nyi[] = "Not Yet Implemented\n\n";
char newline[] = "\n";
char version_string[] = "WCMD Version 0.11\n\n";
char version_string[] = "WCMD Version 0.12\n\n";
char anykey[] = "Press any key to continue: ";
char quals[MAX_PATH], param1[MAX_PATH], param2[MAX_PATH];
BATCH_CONTEXT *context = NULL;
......@@ -79,9 +78,7 @@ HANDLE h;
if (!status) WCMD_print_error();
status = AllocConsole();
if (!status) WCMD_print_error();
STDout = GetStdHandle (STD_OUTPUT_HANDLE);
STDin = GetStdHandle (STD_INPUT_HANDLE);
SetConsoleMode (STDin, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT |
SetConsoleMode (GetStdHandle(STD_INPUT_HANDLE), ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT |
ENABLE_PROCESSED_INPUT);
/*
......@@ -119,7 +116,7 @@ HANDLE h;
WCMD_version ();
while (TRUE) {
WCMD_show_prompt ();
ReadFile (STDin, string, sizeof(string), &count, NULL);
ReadFile (GetStdHandle(STD_INPUT_HANDLE), string, sizeof(string), &count, NULL);
if (count > 1) {
string[count-1] = '\0'; /* ReadFile output is not null-terminated! */
if (string[count-2] == '\r') string[count-2] = '\0'; /* Under Windoze we get CRLF! */
......@@ -143,15 +140,12 @@ char cmd[1024];
char *p;
int status, i;
DWORD count;
HANDLE old_stdin = 0, old_stdout = 0, h;
/*
* Throw away constructs we don't support yet
*/
if ((strchr(command,'<') != NULL) || (strchr(command,'>') != NULL)) {
WCMD_output ("Redirection not yet implemented\n");
return;
}
if (strchr(command,'|') != NULL) {
WCMD_output ("Pipes not yet implemented\n");
return;
......@@ -179,6 +173,33 @@ DWORD count;
WCMD_output (newline);
/*
* Redirect stdin and/or stdout if required.
*/
if ((p = strchr(cmd,'<')) != NULL) {
h = CreateFile (WCMD_parameter (++p, 0, NULL), GENERIC_READ, 0, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, 0);
if (h == INVALID_HANDLE_VALUE) {
WCMD_print_error ();
return;
}
old_stdin = GetStdHandle (STD_INPUT_HANDLE);
SetStdHandle (STD_INPUT_HANDLE, h);
}
if ((p = strchr(cmd,'>')) != NULL) {
h = CreateFile (WCMD_parameter (++p, 0, NULL), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, 0);
if (h == INVALID_HANDLE_VALUE) {
WCMD_print_error ();
return;
}
old_stdout = GetStdHandle (STD_OUTPUT_HANDLE);
SetStdHandle (STD_OUTPUT_HANDLE, h);
*--p = '\0';
}
if ((p = strchr(cmd,'<')) != NULL) *p = '\0';
/*
* Check if the command entered is internal. If it is, pass the rest of the
* line down to the command. If not try to run a program.
*/
......@@ -237,7 +258,7 @@ DWORD count;
WCMD_give_help (p);
break;
case WCMD_IF:
WCMD_if ();
WCMD_if (p);
break;
case WCMD_LABEL:
WCMD_volume (1, p);
......@@ -294,6 +315,14 @@ DWORD count;
default:
WCMD_run_program (cmd);
};
if (old_stdin) {
CloseHandle (GetStdHandle (STD_INPUT_HANDLE));
SetStdHandle (STD_INPUT_HANDLE, old_stdin);
}
if (old_stdout) {
CloseHandle (GetStdHandle (STD_OUTPUT_HANDLE));
SetStdHandle (STD_OUTPUT_HANDLE, old_stdout);
}
}
/******************************************************************************
......@@ -524,7 +553,7 @@ DWORD count;
va_start(ap,format);
vsprintf (string, format, ap);
WriteFile (STDout, string, lstrlen(string), &count, NULL);
WriteFile (GetStdHandle(STD_OUTPUT_HANDLE), string, lstrlen(string), &count, NULL);
va_end(ap);
}
......
......@@ -4,7 +4,15 @@
STRINGTABLE
{
WCMD_ATTRIB, "Help about ATTRIB\n"
WCMD_CALL, "Help about CALL\n"
WCMD_CALL,
"CALL <batchfilename> is used within a batch file to execute commands \
from another batch file. When the batch file exits, control returns to \
the file which called it. The CALL command may supply parameters to the \
called procedure. \
\
Changes to default directory, environment variables etc made within a \
called procedure are inherited by the caller.\n"
WCMD_CD, "Help about CD\n"
WCMD_CHDIR, "Help about CHDIR\n"
......@@ -28,14 +36,51 @@ default). The ECHO OFF command can be prevented from displaying by\
preceding it with an @ sign.\n"
WCMD_ERASE, "Help about ERASE\n"
WCMD_FOR, "Help about FOR\n"
WCMD_GOTO, "Help about GOTO\n"
WCMD_FOR,
"The FOR command is used to execute a command for each of a set of files. \
\
Syntax: FOR %variable IN (set) DO command \
\
The requirement to double the % sign when using FOR in a batch file does \
not exist in wcmd.\n"
WCMD_GOTO,
"The GOTO command transfers execution to another statement within a \
batch file. \
\
The label which is the target of a GOTO may be up to 255 characters \
long but may not include spaces (this is different to other operating \
systems). If two or more identical labels exist in a batch file the \
first one will always be executed. Attempting to GOTO a non-existent \
label terminates the batch file execution. \
\
GOTO has no effect when used interactively.\n"
WCMD_HELP, "Help about HELP\n"
WCMD_IF, "Help about IF\n"
WCMD_IF,
"IF is used to conditionally execute a command.\
\
Syntax: IF [NOT] EXIST filename command \
IF [NOT] string1==string2 command \
\
In the second form of the command, string1 and string2 must be in double \
quotes. The comparison is not case-sensitive.\
\
The form IF [NOT] ERRORLEVEL number is not implemented in Wcmd.\n"
WCMD_LABEL, "Help about LABEL\n"
WCMD_MD, "Help about MD\n"
WCMD_MKDIR, "Help about MKDIR\n"
WCMD_MOVE, "Help about MOVE\n"
WCMD_MOVE,
"MOVE relocates a file or directory to a new point within the file system. \
\
If the item being moved is a directory then all the files and subdirectories \
below the item are moved as well. \
\
MOVE fails if the old and new locations are on different DOS drive letters.\n"
WCMD_PATH,
"PATH displays or changes the wcmd search path. \
\
......@@ -100,14 +145,25 @@ included into the Win32 environment, there will generally therefore be\
many more values than in a native Win32 implementation. Note that it is\
not possible to affect the operating system environment from within wcmd.\n"
WCMD_SHIFT, "Help about SHIFT\n"
WCMD_SHIFT,
"SHIFT is used in a batch file to remove one parameter from the head of \
the list, so parameter 2 becomes parameter 1 and so on. It has no effect \
if called from the command line.\n"
WCMD_TIME, "Help about TIME\n"
WCMD_TYPE,
"TYPE <filename> copies <filename> to the console device (or elsewhere\
if redirected). No check is made that the file is readable text.\n"
WCMD_VERIFY, "Help about VERIFY\n"
WCMD_VERIFY,
"VERIFY is used to set, clear or test the verify flag. Valid forms are: \
\
VERIFY ON Set the flag \
VERIFY OFF Clear the flag \
VERIFY Displays ON or OFF as appropriate. \
\
The verify flag has no function in Wine.\n"
WCMD_VER,
"VER displays the version of wcmd you are running\n"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment