Real WCMD Patch

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

These changes have been verified against the CMD.EXE shipped with Windows XP 
SP1.

-Ryan

ChangeLog:
-Never allocate a new console
-Recognize .CMD as a valid extension for batch files
-Fix case sensitivity in batch file extension checks
-Fix case sensitivity on command line parameter checks
-CTTY is only present as a builtin in COMMAND.COM, not CMD.EXE. Remove its 
stub from WCMD
-Implement COLOR
-Don't show the version banner on /K
-Remove the (disabled) AUTOEXEC.BAT code. CMD.EXE does not run AUTOEXEC on 
startup, that's another COMMAND.COMism.
-Fix parsing of REM command
-Fix the "ECHO ." special case
-Fix the CALL command to be able to invoke non-batch files
-Add support for $A, $C, $F, and $S in PROMPT strings
-Set our default prompt to $P$G to match Windows
-A few fixes to format our output more like the real CMD.EXE, especially wrt 
newlines
Index: batch.c
===================================================================
RCS file: /home/wine/wine/programs/wcmd/batch.c,v
retrieving revision 1.9
diff -u -r1.9 batch.c
--- batch.c	24 Jul 2002 19:00:05 -0000	1.9
+++ batch.c	6 Nov 2002 05:42:24 -0000
@@ -38,8 +38,8 @@
  * Open and execute a batch file.
  * 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. Called is 1 for a CALL, 0 otherwise.
+ * *file is the name of the file, which might not exist
+ * 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.
@@ -53,7 +53,7 @@
 
   strcpy (string, file);
   CharLower (string);
-  if (strstr (string, ".bat") == NULL) strcat (string, ".bat");
+
   h = CreateFile (string, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
   if (h == INVALID_HANDLE_VALUE) {
     SetLastError (ERROR_FILE_NOT_FOUND);
@@ -167,7 +167,7 @@
     WCMD_output ("%s\n", cmd2);
   }
 
-  WCMD_process_command (cmd2);
+  WCMD_process_command (cmd2, 0);
 }
 
 /*******************************************************************
Index: builtins.c
===================================================================
RCS file: /home/wine/wine/programs/wcmd/builtins.c,v
retrieving revision 1.15
diff -u -r1.15 builtins.c
--- builtins.c	24 Jul 2002 19:00:25 -0000	1.15
+++ builtins.c	6 Nov 2002 05:42:25 -0000
@@ -74,15 +74,65 @@
 }
 
 /****************************************************************************
- * WCMD_change_tty
+ * WCMD_color
  *
- * Change the default i/o device (ie redirect STDin/STDout).
+ * Change the console colors
  */
 
-void WCMD_change_tty () {
+void WCMD_color (char *parm) {
 
-  WCMD_output (nyi);
+  unsigned int color;
+  WORD attrs = 0;
+  HANDLE hstdout;
+  CONSOLE_SCREEN_BUFFER_INFO screen_info;
+  COORD topleft;
 
+  if (!*parm) {
+    parm = "0F";
+  }
+
+  /* The color is a two-digit hex number */
+  /* The first digit is the background, the second is the foreground */
+  sscanf(parm, "%x", &color);
+ 
+  if (color & 0x1) {
+    attrs |= FOREGROUND_BLUE;
+  }
+  if (color & 0x2) {
+    attrs |= FOREGROUND_GREEN;
+  }
+  if (color & 0x4) {
+    attrs |= FOREGROUND_RED;
+  }
+  if (color & 0x8) {
+    attrs |= FOREGROUND_INTENSITY;
+  }
+
+  if (color & 0x10) {
+    attrs |= BACKGROUND_BLUE;
+  }
+  if (color & 0x20) {
+    attrs |= BACKGROUND_GREEN;
+  }
+  if (color & 0x40) {
+    attrs |= BACKGROUND_RED;
+  }
+  if (color & 0x80) {
+    attrs |= BACKGROUND_INTENSITY;
+  }
+
+  hstdout = GetStdHandle(STD_OUTPUT_HANDLE);
+
+  /* Figure out how big the console is */
+  GetConsoleScreenBufferInfo(hstdout, &screen_info);
+
+  /* Set the attributes of all the existing characters */
+  topleft.X = 0;
+  topleft.Y = 0;
+  FillConsoleOutputAttribute(hstdout, attrs, screen_info.dwSize.X * screen_info.dwSize.Y, topleft, NULL);
+
+  /* Set the attributes of all the new characters */
+  SetConsoleTextAttribute(hstdout, attrs);
 }
 
 /****************************************************************************
@@ -215,10 +265,6 @@
     else WCMD_output (eoff);
     return;
   }
-  if ((count == 1) && (command[0] == '.')) {
-    WCMD_output (newline);
-    return;
-  }
   if (lstrcmpi(command, "ON") == 0) {
     echo_mode = 1;
     return;
@@ -841,7 +887,7 @@
 DWORD count, serial;
 char string[MAX_PATH], label[MAX_PATH], curdir[MAX_PATH];
 BOOL status;
-static char syntax[] = "Syntax Error\n\n";
+static char syntax[] = "Syntax error\n";
 
   if (lstrlen(path) == 0) {
     status = GetCurrentDirectory (sizeof(curdir), curdir);
@@ -865,7 +911,7 @@
     WCMD_print_error ();
     return 0;
   }
-  WCMD_output ("Volume in drive %c is %s\nVolume Serial Number is %04x-%04x\n\n",
+  WCMD_output ("Volume in drive %c is %s\nVolume Serial Number is %04x-%04x\n",
     	curdir[0], label, HIWORD(serial), LOWORD(serial));
   if (mode) {
     WCMD_output ("Volume label (11 characters, ENTER for none)?");
Index: directory.c
===================================================================
RCS file: /home/wine/wine/programs/wcmd/directory.c,v
retrieving revision 1.13
diff -u -r1.13 directory.c
--- directory.c	28 Oct 2002 23:54:08 -0000	1.13
+++ directory.c	6 Nov 2002 05:42:25 -0000
@@ -100,10 +100,10 @@
      if (recurse) {
        WCMD_output ("\n\n     Total files listed:\n%8d files%25s bytes\n",
             file_total, WCMD_filesize64 (byte_total));
-       WCMD_output ("%8d directories %18s bytes free\n\n",
+       WCMD_output ("%8d directories %18s bytes free\n",
             dir_total, WCMD_filesize64 (free.QuadPart));
      } else {
-       WCMD_output (" %18s bytes free\n\n", WCMD_filesize64 (free.QuadPart));
+       WCMD_output (" %18s bytes free\n", WCMD_filesize64 (free.QuadPart));
      }
   }
 }
Index: wcmd.h
===================================================================
RCS file: /home/wine/wine/programs/wcmd/wcmd.h,v
retrieving revision 1.8
diff -u -r1.8 wcmd.h
--- wcmd.h	31 May 2002 23:40:59 -0000	1.8
+++ wcmd.h	6 Nov 2002 05:42:25 -0000
@@ -33,8 +33,8 @@
 #endif /* !WINELIB */
 
 void WCMD_batch (char *, char *, int);
-void WCMD_change_tty (void);
 void WCMD_clear_screen (void);
+void WCMD_color (char *);
 void WCMD_copy (void);
 void WCMD_create_dir (void);
 void WCMD_delete (int recurse);
@@ -51,11 +51,11 @@
 void WCMD_pause (void);
 void WCMD_pipe (char *command);
 void WCMD_print_error (void);
-void WCMD_process_command (char *command);
+void WCMD_process_command (char *command, int called);
 int WCMD_read_console (char *string, int str_len);
 void WCMD_remove_dir (void);
 void WCMD_rename (void);
-void WCMD_run_program (char *command);
+void WCMD_run_program (char *command, int called);
 void WCMD_setshow_attrib (void);
 void WCMD_setshow_date (void);
 void WCMD_setshow_default (void);
@@ -75,6 +75,7 @@
 char *WCMD_parameter (char *s, int n, char **where);
 char *WCMD_strtrim_leading_spaces (char *string);
 void WCMD_strtrim_trailing_spaces (char *string);
+char *WCMD_strcasestr (const char *haystack, const char *needle);
 
 /*	Data structure to hold context when executing batch files */
 
@@ -101,8 +102,8 @@
 #define WCMD_CD      2
 #define WCMD_CHDIR   3
 #define WCMD_CLS     4
-#define WCMD_COPY    5
-#define WCMD_CTTY    6
+#define WCMD_COLOR   5
+#define WCMD_COPY    6
 #define WCMD_DATE    7
 #define WCMD_DEL     8
 #define WCMD_DIR     9
Index: wcmdmain.c
===================================================================
RCS file: /home/wine/wine/programs/wcmd/wcmdmain.c,v
retrieving revision 1.18
diff -u -r1.18 wcmdmain.c
--- wcmdmain.c	4 Nov 2002 22:36:07 -0000	1.18
+++ wcmdmain.c	6 Nov 2002 05:42:25 -0000
@@ -26,7 +26,7 @@
 
 #include "wcmd.h"
 
-char *inbuilt[] = {"ATTRIB", "CALL", "CD", "CHDIR", "CLS", "COPY", "CTTY",
+char *inbuilt[] = {"ATTRIB", "CALL", "CD", "CHDIR", "CLS", "COLOR", "COPY", 
 		"DATE", "DEL", "DIR", "ECHO", "ERASE", "FOR", "GOTO",
 		"HELP", "IF", "LABEL", "MD", "MKDIR", "MOVE", "PATH", "PAUSE",
 		"PROMPT", "REM", "REN", "RENAME", "RD", "RMDIR", "SET", "SHIFT",
@@ -35,10 +35,10 @@
 HINSTANCE hinst;
 DWORD errorlevel;
 int echo_mode = 1, verify_mode = 0;
-char nyi[] = "Not Yet Implemented\n\n";
+char nyi[] = "Not Yet Implemented\n";
 char newline[] = "\n";
-char version_string[] = "WCMD Version 0.17\n\n";
-char anykey[] = "Press any key to continue: ";
+char version_string[] = "WCMD Version 0.17\n";
+char anykey[] = "Press any key to continue . . . ";
 char quals[MAX_PATH], param1[MAX_PATH], param2[MAX_PATH];
 BATCH_CONTEXT *context = NULL;
 
@@ -50,9 +50,8 @@
 int main (int argc, char *argv[]) {
 
 char string[1024], args[MAX_PATH], param[MAX_PATH];
-int status, i;
+int i;
 DWORD count;
-HANDLE h;
 
   args[0] = param[0] = '\0';
   if (argc > 1) {
@@ -67,24 +66,16 @@
     }
   }
 
-  /* If we do a "wcmd /c command", we don't want to allocate a new
-   * console since the command returns immediately. Rather, we use
-   * the surrently allocated input and output handles. This allows
-   * us to pipe to and read from the command interpreter.
-   */
-  if (strstr(args, "/c") != NULL) {
-    WCMD_process_command (param);
+  if (WCMD_strcasestr(args, "/c") != NULL) {
+    WCMD_process_command (param, 0);
     return 0;
   }
 
 /*
- *	Allocate a console and set it up.
+ *	Set up the current console, if any
+ *      Never allocate a new console
  */
 
-  status = FreeConsole ();
-  if (!status) WCMD_print_error();
-  status = AllocConsole();
-  if (!status) WCMD_print_error();
   SetConsoleMode (GetStdHandle(STD_INPUT_HANDLE), ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT |
   	ENABLE_PROCESSED_INPUT);
   SetConsoleTitle("Wine Command Prompt");
@@ -93,32 +84,23 @@
  *	Execute any command-line options.
  */
 
-  if (strstr(args, "/q") != NULL) {
+  if (WCMD_strcasestr(args, "/q") != NULL) {
     WCMD_echo ("OFF");
   }
 
-  if (strstr(args, "/k") != NULL) {
-    WCMD_process_command (param);
+  if (WCMD_strcasestr(args, "/k") != NULL) {
+    WCMD_process_command (param, 0);
   }
-
-/*
- *	If there is an AUTOEXEC.BAT file, try to execute it.
- */
-
-  GetFullPathName ("\\autoexec.bat", sizeof(string), string, NULL);
-  h = CreateFile (string, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
-  if (h != INVALID_HANDLE_VALUE) {
-    CloseHandle (h);
-#if 0
-    WCMD_batch (string, " ");
-#endif
+  else {
+    WCMD_version ();
   }
 
+  WCMD_output(newline);
+
 /*
  *	Loop forever getting commands and executing them.
  */
 
-  WCMD_version ();
   while (TRUE) {
     WCMD_show_prompt ();
     ReadFile (GetStdHandle(STD_INPUT_HANDLE), string, sizeof(string), &count, NULL);
@@ -130,8 +112,10 @@
           WCMD_pipe (string);
         }
 	else {
-          WCMD_process_command (string);
+          WCMD_process_command (string, 0);
 	}
+
+        WCMD_output(newline);
       }
     }
   }
@@ -144,7 +128,7 @@
  */
 
 
-void WCMD_process_command (char *command) {
+void WCMD_process_command (char *command, int called) {
 
 char cmd[1024];
 char *p;
@@ -174,8 +158,25 @@
       return;
     }
 
+/*
+ *	Strip leading whitespaces, and a '@' if supplied
+ */
+    whichcmd = WCMD_strtrim_leading_spaces(cmd);
+    if (whichcmd[0] == '@') whichcmd++;
+
+/*
+ *	REM is a "special" command, exit before the redirection code
+ */
+    if (lstrlen(whichcmd) >= 3) {
+      if (CompareString (LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
+          whichcmd, 3, "REM", 3) == CSTR_EQUAL) {
+        return;
+      }
+    }
+
     /* Dont issue newline WCMD_output (newline);           @JED*/
 
+
 /*
  *	Redirect stdin and/or stdout if required.
  */
@@ -204,12 +205,6 @@
     if ((p = strchr(cmd,'<')) != NULL) *p = '\0';
 
 /*
- * Strip leading whitespaces, and a '@' if supplied
- */
-    whichcmd = WCMD_strtrim_leading_spaces(cmd);
-    if (whichcmd[0] == '@') whichcmd++;
-
-/*
  *	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.
  */
@@ -220,7 +215,7 @@
     }
     for (i=0; i<=WCMD_EXIT; i++) {
       if (CompareString (LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
-      	  whichcmd, count, inbuilt[i], -1) == 2) break;
+      	  whichcmd, count, inbuilt[i], -1) == CSTR_EQUAL) break;
     }
     p = WCMD_strtrim_leading_spaces (&whichcmd[count]);
     WCMD_parse (p, quals, param1, param2);
@@ -230,7 +225,7 @@
         WCMD_setshow_attrib ();
         break;
       case WCMD_CALL:
-        WCMD_batch (param1, p, 1);
+        WCMD_process_command (p, 1);
         break;
       case WCMD_CD:
       case WCMD_CHDIR:
@@ -239,12 +234,12 @@
       case WCMD_CLS:
         WCMD_clear_screen ();
         break;
+      case WCMD_COLOR:
+        WCMD_color(p);
+        break;
       case WCMD_COPY:
         WCMD_copy ();
         break;
-      case WCMD_CTTY:
-        WCMD_change_tty ();
-        break;
       case WCMD_DATE:
         WCMD_setshow_date ();
 	break;
@@ -332,7 +327,7 @@
       case WCMD_EXIT:
         ExitProcess (0);
       default:
-        WCMD_run_program (whichcmd);
+        WCMD_run_program (whichcmd, called);
     };
     if (old_stdin) {
       CloseHandle (GetStdHandle (STD_INPUT_HANDLE));
@@ -349,11 +344,9 @@
  *
  *	Execute a command line as an external program. If no extension given then
  *	precedence is given to .BAT files. Must allow recursion.
- *
- *	FIXME: Case sensitivity in suffixes!
  */
 
-void WCMD_run_program (char *command) {
+void WCMD_run_program (char *command, int called) {
 
 STARTUPINFO st;
 PROCESS_INFORMATION pe;
@@ -368,16 +361,25 @@
   if (!(*param1) && !(*param2))
     return;
   if (strpbrk (param1, "\\:") == NULL) {	/* No explicit path given */
-    if ((strchr (param1, '.') == NULL) || (strstr (param1, ".bat") != NULL)) {
+    if ((strchr (param1, '.') == NULL) || (WCMD_strcasestr (param1, ".bat") != NULL) ||
+        (WCMD_strcasestr (param1, ".cmd") != NULL)) {
       if (SearchPath (NULL, param1, ".bat", sizeof(filetorun), filetorun, NULL)) {
-        WCMD_batch (filetorun, command, 0);
+        WCMD_batch (filetorun, command, called);
+        return;
+      }
+      if (SearchPath (NULL, param1, ".cmd", sizeof(filetorun), filetorun, NULL)) {
+        WCMD_batch (filetorun, command, called);
         return;
       }
     }
   }
   else {                                        /* Explicit path given */
-    if (strstr (param1, ".bat") != NULL) {
-      WCMD_batch (param1, command, 0);
+    if (WCMD_strcasestr (param1, ".bat") != NULL) {
+      WCMD_batch (param1, command, called);
+      return;
+    }
+    if (WCMD_strcasestr (param1, ".cmd") != NULL) {
+      WCMD_batch (param1, command, called);
       return;
     }
     if (strchr (param1, '.') == NULL) {
@@ -386,7 +388,15 @@
       h = CreateFile (filetorun, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
       if (h != INVALID_HANDLE_VALUE) {
         CloseHandle (h);
-        WCMD_batch (param1, command, 0);
+        WCMD_batch (filetorun, command, called);
+        return;
+      }
+      strcpy (filetorun, param1);
+      strcat (filetorun, ".cmd");
+      h = CreateFile (filetorun, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+      if (h != INVALID_HANDLE_VALUE) {
+        CloseHandle (h);
+        WCMD_batch (filetorun, command, called);
         return;
       }
     }
@@ -432,7 +442,7 @@
 
   status = GetEnvironmentVariable ("PROMPT", prompt_string, sizeof(prompt_string));
   if ((status == 0) || (status > sizeof(prompt_string))) {
-    lstrcpy (prompt_string, "$N$G");
+    lstrcpy (prompt_string, "$P$G");
   }
   p = prompt_string;
   q = out_string;
@@ -448,9 +458,15 @@
         case '$':
 	  *q++ = '$';
 	  break;
+        case 'A':
+	  *q++ = '&';
+	  break;
 	case 'B':
 	  *q++ = '|';
 	  break;
+	case 'C':
+	  *q++ = '(';
+	  break;
 	case 'D':
 	  GetDateFormat (LOCALE_USER_DEFAULT, DATE_SHORTDATE, NULL, NULL, q, MAX_PATH);
 	  while (*q) q++;
@@ -458,6 +474,9 @@
 	case 'E':
 	  *q++ = '\E';
 	  break;
+	case 'F':
+	  *q++ = ')';
+	  break;
 	case 'G':
 	  *q++ = '>';
 	  break;
@@ -480,6 +499,9 @@
 	case 'Q':
 	  *q++ = '=';
 	  break;
+	case 'S':
+	  *q++ = ' ';
+	  break;
 	case 'T':
 	  GetTimeFormat (LOCALE_USER_DEFAULT, 0, NULL, NULL, q, MAX_PATH);
 	  while (*q) q++;
@@ -516,7 +538,6 @@
   }
   WCMD_output (lpMsgBuf);
   LocalFree ((HLOCAL)lpMsgBuf);
-  WCMD_output (newline);
   return;
 }
 
@@ -652,18 +673,52 @@
   p = strchr(command, '|');
   *p++ = '\0';
   wsprintf (temp_cmd, "%s > %s", command, temp_file);
-  WCMD_process_command (temp_cmd);
+  WCMD_process_command (temp_cmd, 0);
   command = p;
   while ((p = strchr(command, '|'))) {
     *p++ = '\0';
     GetTempFileName (temp_path, "WCMD", 0, temp_file2);
     wsprintf (temp_cmd, "%s < %s > %s", command, temp_file, temp_file2);
-    WCMD_process_command (temp_cmd);
+    WCMD_process_command (temp_cmd, 0);
     DeleteFile (temp_file);
     lstrcpy (temp_file, temp_file2);
     command = p;
   }
   wsprintf (temp_cmd, "%s < %s", command, temp_file);
-  WCMD_process_command (temp_cmd);
+  WCMD_process_command (temp_cmd, 0);
   DeleteFile (temp_file);
+}
+
+/***************************************************************************
+ * WCMD_strcasestr
+ *
+ *	Case-insensitive version of strstr
+ *      Simply lowercases both strings and calls strstr, could be a lot more
+ *      clever
+ */
+
+char *WCMD_strcasestr (const char *haystack, const char *needle) {
+
+char *duphaystack;
+char *dupneedle;
+char *ret;
+
+  duphaystack = strdup(haystack);
+  dupneedle = strdup(needle);
+
+  CharLower(duphaystack);
+  CharLower(dupneedle);
+
+  ret = strstr(duphaystack, dupneedle);
+
+  if (ret) {
+    /* Convert from an address within the duplicated haystack to one 
+       within the original */
+    ret = (ret - duphaystack) + (char*)haystack;
+  }
+
+  free(duphaystack);
+  free(dupneedle);
+
+  return ret;
 }

[Index of Archives]     [Gimp for Windows]     [Red Hat]     [Samba]     [Yosemite Camping]     [Graphics Cards]     [Wine Home]

  Powered by Linux