diff --git a/programs/wcmd/ChangeLog b/programs/wcmd/ChangeLog index 5777341721d..5bc9f7610d4 100644 --- a/programs/wcmd/ChangeLog +++ b/programs/wcmd/ChangeLog @@ -1,3 +1,8 @@ +v0.11 - 20 June 1999 +Batch command parameters (and the SHIFT command) added. +GOTO added. +Batch invokation (including CALL) now functions correctly +VERIFY command added as a stub. v0.10 - 2 June 1999 Additional help text and error codes. diff --git a/programs/wcmd/README b/programs/wcmd/README index 7141f269829..1ac0a065ce2 100644 --- a/programs/wcmd/README +++ b/programs/wcmd/README @@ -12,9 +12,10 @@ WHAT'S INCLUDED WHAT'S MISSING - Redirection, shell parameters and pipes - Command-line qualifiers for most builtin commands -- MOVE command (plus the batch-only ones) +- MOVE command +- IF and FOR commands - Wildcards and relative paths in COPY and RENAME -- Set functionality in DATE, TIME, ATTRIB, SET, LABEL +- Set functionality in DATE, TIME, ATTRIB, LABEL - Full internationalisation of the text (and commands?). WHAT DOESN'T WORK @@ -37,9 +38,6 @@ 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. -- Simple batch files work, ie a list of commands as they would be typed. However -invoking a batch file from within another invokes the CALL function, control -returns to the calling batch file when the subfile exits. WINE OR WIN32 BINARY? Wcmd can be built as a Wine binary, or (using a Win32 compiler) as a Win32 .EXE diff --git a/programs/wcmd/batch.c b/programs/wcmd/batch.c index cba1c3ec0f0..2f51b375eef 100644 --- a/programs/wcmd/batch.c +++ b/programs/wcmd/batch.c @@ -8,9 +8,7 @@ #include "wcmd.h" -void WCMD_batch_command (HANDLE h, char *command); -char *WCMD_parameter (char *s, int n); -BOOL WCMD_go_to (HANDLE h, char *label); +void WCMD_batch_command (char *line); extern HANDLE STDin, STDout; extern char nyi[]; @@ -18,6 +16,7 @@ extern char newline[]; extern char version_string[]; extern int echo_mode; extern char quals[MAX_PATH], param1[MAX_PATH], param2[MAX_PATH]; +extern BATCH_CONTEXT *context; @@ -28,16 +27,17 @@ extern char quals[MAX_PATH], param1[MAX_PATH], param2[MAX_PATH]; * On entry *command includes the complete command line beginning with the name * of the batch file (if a CALL command was entered the CALL has been removed). * *file is the name of the file, which might not exist and may not have the - * .BAT suffix on. + * .BAT suffix on. Called is 1 for a CALL, 0 otherwise. * * We need to handle recursion correctly, since one batch program might call another. + * So parameters for this batch file are held in a BATCH_CONTEXT structure. */ -void WCMD_batch (char *file, char *command) { +void WCMD_batch (char *file, char *command, int called) { HANDLE h; char string[MAX_PATH]; -int n; +BATCH_CONTEXT *prev_context; strcpy (string, file); CharLower (string); @@ -48,70 +48,89 @@ int n; return; } +/* + * Create a context structure for this batch file. + */ + + prev_context = context; + context = (BATCH_CONTEXT *)LocalAlloc (LMEM_FIXED, sizeof (BATCH_CONTEXT)); + context -> h = h; + context -> command = command; + context -> shift_count = 0; + context -> prev_context = prev_context; + /* * Work through the file line by line. Specific batch commands are processed here, * the rest are handled by the main command processor. */ while (WCMD_fgets (string, sizeof(string), h)) { - n = strlen (string); - if (string[n-1] == '\n') string[n-1] = '\0'; - if (string[n-2] == '\r') string[n-2] = '\0'; /* Under Windoze we get CRLF! */ - WCMD_batch_command (h, string); + if (string[0] != ':') { /* Skip over labels */ + WCMD_batch_command (string); + } } CloseHandle (h); + +/* + * If invoked by a CALL, we return to the context of our caller. Otherwise return + * to the caller's caller. + */ + + LocalFree ((HANDLE)context); + if ((prev_context != NULL) && (!called)) { + CloseHandle (prev_context -> h); + context = prev_context -> prev_context; + LocalFree ((HANDLE)prev_context); + } + else { + context = prev_context; + } } /**************************************************************************** * WCMD_batch_command * - * Execute one line from a batch file. + * Execute one line from a batch file, expanding parameters. */ -void WCMD_batch_command (HANDLE h, char *command) { +void WCMD_batch_command (char *line) { DWORD status; char cmd[1024]; +char *p, *s, *t; +int i; - if (echo_mode && (command[0] != '@')) WCMD_output ("%s", command); - status = ExpandEnvironmentStrings (command, cmd, sizeof(cmd)); + if (echo_mode && (line[0] != '@')) WCMD_output ("%s", line); + status = ExpandEnvironmentStrings (line, cmd, sizeof(cmd)); if (!status) { WCMD_print_error (); return; } - WCMD_process_command (cmd); + 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); + strcpy (p, t); + strcat (p, s); + free (s); } - -/**************************************************************************** - * WCMD_go_to - * - * Batch file jump instruction. Not the most efficient algorithm ;-) - * Returns FALSE if the specified label cannot be found - the file pointer is - * then at EOF. - */ - -BOOL WCMD_go_to (HANDLE h, char *label) { - -char string[MAX_PATH]; - - SetFilePointer (h, 0, NULL, FILE_BEGIN); - while (WCMD_fgets (string, sizeof(string), h)) { - if ((string[0] == ':') && (strcmp (&string[1], label) == 0)) return TRUE; } - return FALSE; + WCMD_process_command (cmd); } /******************************************************************* * WCMD_parameter - extract a parameter from a command line. * - * Returns the 'n'th space-delimited parameter on the command line. + * 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 are handled. + * Parameters in quotes (and brackets) are handled. */ char *WCMD_parameter (char *s, int n) { -int i = -1; +int i = 0; static char param[MAX_PATH]; char *p; @@ -136,6 +155,21 @@ char *p; } if (*s == '"') s++; break; + case '(': + s++; + while ((*s != '\0') && (*s != ')')) { + *p++ = *s++; + } + if (i == n) { + *p = '\0'; + return param; + } + else { + param[0] = '\0'; + i++; + } + if (*s == '"') s++; + break; case '\0': return param; default: @@ -148,6 +182,7 @@ char *p; } else { param[0] = '\0'; + p = param; i++; } } @@ -158,7 +193,8 @@ char *p; * WCMD_fgets * * Get one line from a batch file. We can't use the native f* functions because - * of the filename syntax differences between DOS and Unix. + * of the filename syntax differences between DOS and Unix. Also need to lose + * the LF (or CRLF) from the line. */ char *WCMD_fgets (char *s, int n, HANDLE h) { @@ -170,10 +206,13 @@ char *p; p = s; do { status = ReadFile (h, s, 1, &bytes, NULL); - if ((status == 0) || (bytes == 0)) return NULL; + if ((status == 0) || ((bytes == 0) && (s == p))) return NULL; if (*s == '\n') bytes = 0; - *++s = '\0'; + else if (*s != '\r') { + s++; n--; + } + *s = '\0'; } while ((bytes == 1) && (n > 1)); return p; } diff --git a/programs/wcmd/builtins.c b/programs/wcmd/builtins.c index ad62eaa4271..cea4d33f2fa 100644 --- a/programs/wcmd/builtins.c +++ b/programs/wcmd/builtins.c @@ -10,7 +10,7 @@ /* * FIXME: - * - No support for redirection, pipes, batch files, shell parameters + * - No support for redirection, pipes, shell parameters * - 32-bit limit on file sizes in DIR command * - Lots of functionality missing from builtins * - Messages etc need international support @@ -25,21 +25,11 @@ extern char nyi[]; extern char newline[]; extern char version_string[]; extern char anykey[]; -extern int echo_mode; +extern int echo_mode, verify_mode; extern char quals[MAX_PATH], param1[MAX_PATH], param2[MAX_PATH]; +extern BATCH_CONTEXT *context; -/**************************************************************************** - * WCMD_call - * - * Call another batch file. - */ - -void WCMD_call () { - - WCMD_output (nyi); - -} /**************************************************************************** * WCMD_clear_screen @@ -201,8 +191,12 @@ int count; * Batch file loop processing. */ -void WCMD_for () { +void WCMD_for (char *p) { + if (lstrcmpi (WCMD_parameter (p, 1), "in") || lstrcmpi (WCMD_parameter (p, 3), "do")) { + WCMD_output ("Syntax error\n"); + return; + } WCMD_output (nyi); } @@ -237,6 +231,30 @@ char buffer[2048]; return; } +/**************************************************************************** + * WCMD_go_to + * + * Batch file jump instruction. Not the most efficient algorithm ;-) + * Prints error message if the specified label cannot be found - the file pointer is + * then at EOF, effectively stopping the batch file. + * FIXME: DOS is supposed to allow labels with spaces - we don't. + */ + +void WCMD_goto () { + +char string[MAX_PATH]; + + if (context != NULL) { + SetFilePointer (context -> h, 0, NULL, FILE_BEGIN); + while (WCMD_fgets (string, sizeof(string), context -> h)) { + if ((string[0] == ':') && (strcmp (&string[1], param1) == 0)) return; + } + WCMD_output ("Target to GOTO not found\n"); + } + return; +} + + /**************************************************************************** * WCMD_if * @@ -541,7 +559,7 @@ DWORD count; void WCMD_shift () { - WCMD_output (nyi); + if (context != NULL) context -> shift_count++; } @@ -574,12 +592,30 @@ DWORD count; * WCMD_verify * * Display verify flag. + * FIXME: We don't actually do anything with the verify flag other than toggle + * it... */ -void WCMD_verify () { +void WCMD_verify (char *command) { - WCMD_output (nyi); +static char *von = "Verify is ON\n", *voff = "Verify is OFF\n"; +int count; + count = strlen(command); + if (count == 0) { + if (verify_mode) WCMD_output (von); + else WCMD_output (voff); + return; + } + if (lstrcmpi(command, "ON") == 0) { + verify_mode = 1; + return; + } + else if (lstrcmpi(command, "OFF") == 0) { + verify_mode = 0; + return; + } + else WCMD_output ("Verify must be ON or OFF\n"); } /**************************************************************************** diff --git a/programs/wcmd/wcmd.h b/programs/wcmd/wcmd.h index f12104cc0f4..d5f5164bb71 100644 --- a/programs/wcmd/wcmd.h +++ b/programs/wcmd/wcmd.h @@ -19,8 +19,7 @@ #include #endif /* !WINELIB */ -void WCMD_batch (char *, char *); -void WCMD_call (void); +void WCMD_batch (char *, char *, int); void WCMD_change_tty (void); void WCMD_clear_screen (void); void WCMD_copy (void); @@ -28,8 +27,9 @@ void WCMD_create_dir (void); void WCMD_delete (int recurse); void WCMD_directory (void); void WCMD_echo (char *); -void WCMD_for (void); +void WCMD_for (char *); void WCMD_give_help (char *command); +void WCMD_goto (void); void WCMD_if (void); void WCMD_move (void); void WCMD_output (char *format, ...); @@ -50,13 +50,24 @@ void WCMD_setshow_time (void); void WCMD_shift (void); void WCMD_show_prompt (void); void WCMD_type (void); -void WCMD_verify (void); +void WCMD_verify (char *command); 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_strtrim_leading_spaces (char *string); void WCMD_strtrim_trailing_spaces (char *string); + +/* Data structure to hold context when executing batch files */ + +typedef struct { + char *command; /* The command which invoked the batch file */ + HANDLE h; /* Handle to the open batch file */ + int shift_count; /* Number of SHIFT commands executed */ + void *prev_context; /* Pointer to the previous context block */ +} BATCH_CONTEXT; + #endif /* !RC_INVOKED */ /* diff --git a/programs/wcmd/wcmdmain.c b/programs/wcmd/wcmdmain.c index 4fc0d7233a0..3fa45897882 100644 --- a/programs/wcmd/wcmdmain.c +++ b/programs/wcmd/wcmdmain.c @@ -6,7 +6,7 @@ /* * FIXME: - * - No support for redirection, pipes, batch commands + * - No support for redirection, pipes * - 32-bit limit on file sizes in DIR command * - Cannot handle parameters in quotes * - Lots of functionality missing from builtins @@ -31,12 +31,13 @@ char *inbuilt[] = {"ATTRIB", "CALL", "CD", "CHDIR", "CLS", "COPY", "CTTY", HANDLE STDin, STDout; HINSTANCE hinst; -int echo_mode = 1; +int echo_mode = 1, verify_mode = 0; char nyi[] = "Not Yet Implemented\n\n"; char newline[] = "\n"; -char version_string[] = "WCMD Version 0.10\n\n"; +char version_string[] = "WCMD Version 0.11\n\n"; char anykey[] = "Press any key to continue: "; char quals[MAX_PATH], param1[MAX_PATH], param2[MAX_PATH]; +BATCH_CONTEXT *context = NULL; /***************************************************************************** * Main entry point. This is a console application so we have a main() not a @@ -198,7 +199,7 @@ DWORD count; WCMD_setshow_attrib (); break; case WCMD_CALL: - WCMD_batch (param1, p); + WCMD_batch (param1, p, 1); break; case WCMD_CD: case WCMD_CHDIR: @@ -227,9 +228,10 @@ DWORD count; WCMD_echo (p); break; case WCMD_FOR: - WCMD_for (); + WCMD_for (p); break; case WCMD_GOTO: + WCMD_goto (); break; case WCMD_HELP: WCMD_give_help (p); @@ -282,7 +284,7 @@ DWORD count; WCMD_version (); break; case WCMD_VERIFY: - WCMD_verify (); + WCMD_verify (p); break; case WCMD_VOL: WCMD_volume (0, p); @@ -315,14 +317,14 @@ char filetorun[MAX_PATH]; if (strpbrk (param1, "\\:") == NULL) { /* No explicit path given */ if ((strchr (param1, '.') == NULL) || (strstr (param1, ".bat") != NULL)) { if (SearchPath (NULL, param1, ".bat", sizeof(filetorun), filetorun, NULL)) { - WCMD_batch (filetorun, command); + WCMD_batch (filetorun, command, 0); return; } } } else { /* Explicit path given */ if (strstr (param1, ".bat") != NULL) { - WCMD_batch (param1, command); + WCMD_batch (param1, command, 0); return; } if (strchr (param1, '.') == NULL) { @@ -331,7 +333,7 @@ char filetorun[MAX_PATH]; h = CreateFile (filetorun, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if (h != INVALID_HANDLE_VALUE) { CloseHandle (h); - WCMD_batch (param1, command); + WCMD_batch (param1, command, 0); return; } }