[PATCH 4/6] CMD.EXE: Make dir support multiple parameters

Jason Edmeades us at edmeades.me.uk
Thu Apr 5 16:47:56 CDT 2007


Note: This patch is slightly more complicated than an 'easy' solution
but puts in the infrastructure for the next patch, rather than code
something and then rip it out
---
 programs/cmd/directory.c |  312 ++++++++++++++++++++++++++++++++--------------
 programs/cmd/wcmd.h      |    2 +-
 programs/cmd/wcmdmain.c  |    2 +-
 3 files changed, 223 insertions(+), 93 deletions(-)

diff --git a/programs/cmd/directory.c b/programs/cmd/directory.c
index b6bd10a..736a8b1 100644
--- a/programs/cmd/directory.c
+++ b/programs/cmd/directory.c
@@ -33,10 +33,11 @@
 WINE_DEFAULT_DEBUG_CHANNEL(cmd);
 
 int WCMD_dir_sort (const void *a, const void *b);
-void WCMD_list_directory (char *path, int level);
+static void WCMD_list_directory (DIRECTORY_STACK *parms, int level);
 char * WCMD_filesize64 (ULONGLONG free);
 char * WCMD_strrev (char *buff);
 static void WCMD_getfileowner(char *filename, char *owner, int ownerlen);
+static void WCMD_dir_trailer(char drive);
 
 extern int echo_mode;
 extern char quals[MAX_PATH], param1[MAX_PATH], param2[MAX_PATH];
@@ -57,11 +58,12 @@ typedef enum _DISPLAYORDER
     Date
 } DISPLAYORDER;
 
-struct directory_stack
+typedef struct _DIRECTORY_STACK
 {
-  struct directory_stack *next;
-  char  *name;
-};
+  struct _DIRECTORY_STACK *next;
+  char  *dirName;
+  char  *fileName;
+} DIRECTORY_STACK;
 
 static int file_total, dir_total, recurse, wide, bare, max_width, lower;
 static int shortname, usernames;
@@ -79,14 +81,25 @@ static ULONG showattrs, attrsbits;
  *
  */
 
-void WCMD_directory (void) {
+void WCMD_directory (char *cmd) {
 
-  char path[MAX_PATH], drive[8];
+  char path[MAX_PATH], cwd[MAX_PATH];
   int status, paged_mode;
-  ULARGE_INTEGER avail, total, free;
   CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
   char *p;
   char string[MAXSTRING];
+  int   argno         = 0;
+  int   argsProcessed = 0;
+  char *argN          = cmd;
+  char  lastDrive;
+  BOOL  trailerReqd = FALSE;
+  DIRECTORY_STACK *fullParms = NULL;
+  DIRECTORY_STACK *prevEntry = NULL;
+  DIRECTORY_STACK *thisEntry = NULL;
+  char drive[10];
+  char dir[MAX_PATH];
+  char fname[MAX_PATH];
+  char ext[MAX_PATH];
 
   errorlevel = 0;
 
@@ -267,40 +280,155 @@ void WCMD_directory (void) {
      WCMD_enter_paged_mode();
   }
 
-  if (param1[0] == '\0') strcpy (param1, ".");
-  status = GetFullPathName (param1, sizeof(path), path, NULL);
-  if (!status) {
-    WCMD_print_error();
-    if (paged_mode) WCMD_leave_paged_mode();
-    errorlevel = 1;
-    return;
+  argno         = 0;
+  argsProcessed = 0;
+  argN          = cmd;
+  GetCurrentDirectory (1024, cwd);
+  strcat(cwd, "\\");
+
+  /* Loop through all args, calculating full effective directory */
+  fullParms = NULL;
+  prevEntry = NULL;
+  while (argN) {
+    char fullname[MAXSTRING];
+    char *thisArg = WCMD_parameter (cmd, argno++, &argN);
+    if (argN && argN[0] != '/') {
+
+      WINE_TRACE("Found parm '%s'\n", thisArg);
+      if (thisArg[1] == ':' && thisArg[2] == '\\') {
+        strcpy(fullname, thisArg);
+      } else if (thisArg[1] == ':' && thisArg[2] != '\\') {
+        char envvar[4];
+        sprintf(envvar, "=%c:", thisArg[0]);
+        if (!GetEnvironmentVariable(envvar, fullname, MAX_PATH)) {
+          sprintf(fullname, "%c:", thisArg[0]);
+        }
+        strcat(fullname, "\\");
+        strcat(fullname, &thisArg[2]);
+      } else if (thisArg[0] == '\\') {
+        strncpy(fullname, cwd, 2);
+        fullname[2] = 0x00;
+        strcat((fullname+2), thisArg);
+      } else {
+        strcpy(fullname, cwd);
+        strcat(fullname, thisArg);
+      }
+      WINE_TRACE("Using location '%s'\n", fullname);
+
+      status = GetFullPathName (fullname, sizeof(path), path, NULL);
+      WINE_TRACE("Using path '%s'\n", path);
+
+      /*
+       *  If the path supplied does not include a wildcard, and the endpoint of the
+       *  path references a directory, we need to list the *contents* of that
+       *  directory not the directory file itself. 
+       */
+      if ((strchr(path, '*') == NULL) && (strchr(path, '%') == NULL)) {
+        status = GetFileAttributes (path);
+        if ((status != INVALID_FILE_ATTRIBUTES) && (status & FILE_ATTRIBUTE_DIRECTORY)) {
+          if (path[strlen(path)-1] == '\\') {
+            strcat (path, "*");
+          }
+          else {
+            strcat (path, "\\*");
+          }
+        }
+      }
+
+      thisEntry = (DIRECTORY_STACK *) HeapAlloc(GetProcessHeap(),0,sizeof(DIRECTORY_STACK));
+      if (fullParms == NULL) fullParms = thisEntry;
+      if (prevEntry != NULL) prevEntry->next = thisEntry;
+      prevEntry = thisEntry;
+      thisEntry->next = NULL;
+
+      /* Split into components */
+      WCMD_splitpath(path, drive, dir, fname, ext);
+      WINE_TRACE("Path Parts: drive: '%s' dir: '%s' name: '%s' ext:'%s'\n", 
+                 drive, dir, fname, ext);
+
+      thisEntry->dirName = HeapAlloc(GetProcessHeap(),0,strlen(drive)+strlen(dir)+1);
+      strcpy(thisEntry->dirName, drive);
+      strcat(thisEntry->dirName, dir);
+
+      thisEntry->fileName = HeapAlloc(GetProcessHeap(),0,strlen(fname)+strlen(ext)+1);
+      strcpy(thisEntry->fileName, fname);
+      strcat(thisEntry->fileName, ext);
+
+    }
   }
-  lstrcpyn (drive, path, 3);
 
-  if (!bare) {
-     status = WCMD_volume (0, drive);
-     if (!status) {
-         if (paged_mode) WCMD_leave_paged_mode();
-       errorlevel = 1;
-       return;
-     }
+  /* If just 'dir' entered, a '*' parameter is assumed */
+  if (fullParms == NULL) {
+    WINE_TRACE("Inserting default '*'\n");
+    fullParms = (DIRECTORY_STACK *) HeapAlloc(GetProcessHeap(),0, sizeof(DIRECTORY_STACK));
+    fullParms->next = NULL;
+    fullParms->dirName = HeapAlloc(GetProcessHeap(),0,(strlen(cwd)+1));
+    strcpy(fullParms->dirName, cwd);
+    fullParms->fileName = HeapAlloc(GetProcessHeap(),0,2);
+    strcpy(fullParms->fileName, "*");
   }
 
-  WCMD_list_directory (path, 0);
-  lstrcpyn (drive, path, 4);
-  GetDiskFreeSpaceEx (drive, &avail, &total, &free);
-
-  if (errorlevel==0 && !bare) {
-     if (recurse) {
-       WCMD_output ("\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",
-            dir_total, WCMD_filesize64 (free.QuadPart));
-     } else {
-       WCMD_output (" %18s bytes free\n\n", WCMD_filesize64 (free.QuadPart));
-     }
+  lastDrive = '?';
+  prevEntry = NULL;
+  thisEntry = fullParms;
+  trailerReqd = FALSE;
+
+  while (thisEntry != NULL) {
+
+    /* Output disk free (trailer) and volume information (header) if the drive
+       letter changes */
+    if (lastDrive != toupper(thisEntry->dirName[0])) {
+
+      /* Trailer Information */
+      if (lastDrive != '?') {
+        trailerReqd = FALSE;
+        WCMD_dir_trailer(prevEntry->dirName[0]);
+      }
+
+      lastDrive = toupper(thisEntry->dirName[0]);
+
+      if (!bare) {
+         char drive[3];
+
+         WINE_TRACE("Writing volume for '%c:'\n", thisEntry->dirName[0]);
+         strncpy(drive, thisEntry->dirName, 2);
+         drive[2] = 0x00;
+         status = WCMD_volume (0, drive);
+         trailerReqd = TRUE;
+         if (!status) {
+           errorlevel = 1;
+           goto exit;
+         }
+      }
+    } else {
+      if (!bare) WCMD_output ("\n\n");
+    }
+
+    /* Clear any errors from previous invocations, and process it */
+    errorlevel = 0;
+    WCMD_list_directory (thisEntry, 0);
+
+    /* Step to next parm */
+    prevEntry = thisEntry;
+    thisEntry = thisEntry->next;
+  }
+
+  /* Trailer Information */
+  if (trailerReqd) {
+    WCMD_dir_trailer(prevEntry->dirName[0]);
   }
+
+exit:
   if (paged_mode) WCMD_leave_paged_mode();
+
+  /* Free storage allocated for parms */
+  while (fullParms != NULL) {
+    prevEntry = fullParms;
+    fullParms = prevEntry->next;
+    HeapFree(GetProcessHeap(),0,prevEntry->dirName);
+    HeapFree(GetProcessHeap(),0,prevEntry->fileName);
+    HeapFree(GetProcessHeap(),0,prevEntry);
+  }
 }
 
 /*****************************************************************************
@@ -309,22 +437,18 @@ void WCMD_directory (void) {
  * List a single file directory. This function (and those below it) can be called
  * recursively when the /S switch is used.
  *
- * FIXME: Entries sorted by name only. Should we support DIRCMD??
  * FIXME: Assumes 24-line display for the /P qualifier.
- * FIXME: Other command qualifiers not supported.
- * FIXME: DIR /S FILENAME fails if at least one matching file is not found in the top level.
  */
 
-void WCMD_list_directory (char *search_path, int level) {
+static void WCMD_list_directory (DIRECTORY_STACK *parms, int level) {
 
   char string[1024], datestring[32], timestring[32];
-  char *p;
   char real_path[MAX_PATH];
   WIN32_FIND_DATA *fd;
   FILETIME ft;
   SYSTEMTIME st;
   HANDLE hff;
-  int status, dir_count, file_count, entry_count, i, widest, cur_width, tmp_width;
+  int dir_count, file_count, entry_count, i, widest, cur_width, tmp_width;
   int numCols, numRows;
   int rows, cols;
   ULARGE_INTEGER byte_count, file_size;
@@ -336,35 +460,14 @@ void WCMD_list_directory (char *search_path, int level) {
   widest = 0;
   cur_width = 0;
 
-/*
- *  If the path supplied does not include a wildcard, and the endpoint of the
- *  path references a directory, we need to list the *contents* of that
- *  directory not the directory file itself. However, only do this on first
- *  entry and not subsequent recursion
- */
-
-  if ((level == 0) &&
-      (strchr(search_path, '*') == NULL) && (strchr(search_path, '%') == NULL)) {
-    status = GetFileAttributes (search_path);
-    if ((status != INVALID_FILE_ATTRIBUTES) && (status & FILE_ATTRIBUTE_DIRECTORY)) {
-      if (search_path[strlen(search_path)-1] == '\\') {
-        strcat (search_path, "*");
-      }
-      else {
-        strcat (search_path, "\\*");
-      }
-    }
-  }
-
-  /* Work out the actual current directory name */
-  p = strrchr (search_path, '\\');
-  memset(real_path, 0x00, sizeof(real_path));
-  lstrcpyn (real_path, search_path, (p-search_path+2));
+  /* Work out the full path + filename */
+  strcpy(real_path, parms->dirName);
+  strcat(real_path, parms->fileName);
 
   /* Load all files into an in memory structure */
   fd = HeapAlloc(GetProcessHeap(),0,sizeof(WIN32_FIND_DATA));
-  WINE_TRACE("Looking for matches to '%s'\n", search_path);
-  hff = FindFirstFile (search_path, fd);
+  WINE_TRACE("Looking for matches to '%s'\n", real_path);
+  hff = FindFirstFile (real_path, fd);
   if (hff == INVALID_HANDLE_VALUE) {
     entry_count = 0;
   } else {
@@ -392,6 +495,10 @@ void WCMD_list_directory (char *search_path, int level) {
     FindClose (hff);
   }
 
+  /* Work out the actual current directory name without a trailing \ */
+  strcpy(real_path, parms->dirName);
+  real_path[strlen(parms->dirName)-1] = 0x00;
+
   /* Output the results */
   if (!bare) {
      if (level != 0 && (entry_count > 0)) WCMD_output ("\n");
@@ -438,8 +545,7 @@ void WCMD_list_directory (char *search_path, int level) {
 
       /* /Q gets file ownership information */
       if (usernames) {
-          p = strrchr (search_path, '\\');
-          lstrcpyn (string, search_path, (p-search_path+2));
+          lstrcpy (string, parms->dirName);
           lstrcat (string, (fd+i)->cFileName);
           WCMD_getfileowner(string, username, sizeof(username));
       }
@@ -491,7 +597,7 @@ void WCMD_list_directory (char *search_path, int level) {
         } else {
            if (!((strcmp((fd+i)->cFileName, ".") == 0) ||
                  (strcmp((fd+i)->cFileName, "..") == 0))) {
-              WCMD_output ("%s%s", recurse?real_path:"", (fd+i)->cFileName);
+              WCMD_output ("%s%s", recurse?parms->dirName:"", (fd+i)->cFileName);
            } else {
               addNewLine = FALSE;
            }
@@ -509,7 +615,7 @@ void WCMD_list_directory (char *search_path, int level) {
            if (usernames) WCMD_output ("%-23s", username);
            WCMD_output("%s",(fd+i)->cFileName);
         } else {
-           WCMD_output ("%s%s", recurse?real_path:"", (fd+i)->cFileName);
+           WCMD_output ("%s%s", recurse?parms->dirName:"", (fd+i)->cFileName);
         }
       }
      }
@@ -538,14 +644,13 @@ void WCMD_list_directory (char *search_path, int level) {
 
   /* When recursing, look in all subdirectories for matches */
   if (recurse) {
-    struct directory_stack *dirStack = NULL;
-    struct directory_stack *lastEntry = NULL;
+    DIRECTORY_STACK *dirStack = NULL;
+    DIRECTORY_STACK *lastEntry = NULL;
     WIN32_FIND_DATA finddata;
 
     /* Build path to search */
-    strcpy(string, search_path);
-    p = strrchr (string, '\\');
-    strcpy(p+1, "*");
+    strcpy(string, parms->dirName);
+    strcat(string, "*");
 
     WINE_TRACE("Recursive, looking for '%s'\n", string);
     hff = FindFirstFile (string, &finddata);
@@ -555,34 +660,35 @@ void WCMD_list_directory (char *search_path, int level) {
             (strcmp(finddata.cFileName, "..") != 0) &&
             (strcmp(finddata.cFileName, ".") != 0)) {
 
-          struct directory_stack *thisDir;
+          DIRECTORY_STACK *thisDir;
 
           /* Work out search parameter in sub dir */
-          p = strrchr (search_path, '\\');
-          lstrcpyn (string, search_path, (p-search_path+2));
-          string[(p-search_path+2)] = 0x00;
-          lstrcat (string, finddata.cFileName);
-          lstrcat (string, p);
+          strcpy (string, parms->dirName);
+          strcat (string, finddata.cFileName);
+          strcat (string, "\\");
           WINE_TRACE("Recursive, Adding to search list '%s'\n", string);
 
           /* Allocate memory, add to list */
-          thisDir = (struct directory_stack *) HeapAlloc(GetProcessHeap(),0,sizeof(struct directory_stack));
+          thisDir = (DIRECTORY_STACK *) HeapAlloc(GetProcessHeap(),0,sizeof(DIRECTORY_STACK));
           if (dirStack == NULL) dirStack = thisDir;
           if (lastEntry != NULL) lastEntry->next = thisDir;
           lastEntry = thisDir;
           thisDir->next = NULL;
-          thisDir->name = HeapAlloc(GetProcessHeap(),0,(strlen(string)+1));
-          strcpy(thisDir->name, string);
+          thisDir->dirName = HeapAlloc(GetProcessHeap(),0,(strlen(string)+1));
+          strcpy(thisDir->dirName, string);
+          thisDir->fileName = HeapAlloc(GetProcessHeap(),0,(strlen(parms->fileName)+1));
+          strcpy(thisDir->fileName, parms->fileName);
         }
       } while (FindNextFile(hff, &finddata) != 0);
       FindClose (hff);
 
       while (dirStack != NULL) {
-        struct directory_stack *thisDir = dirStack;
+        DIRECTORY_STACK *thisDir = dirStack;
         dirStack = thisDir->next;
 
-        WCMD_list_directory (thisDir->name, 1);
-        HeapFree(GetProcessHeap(),0,thisDir->name);
+        WCMD_list_directory (thisDir, 1);
+        HeapFree(GetProcessHeap(),0,thisDir->dirName);
+        HeapFree(GetProcessHeap(),0,thisDir->fileName);
         HeapFree(GetProcessHeap(),0,thisDir);
       }
     }
@@ -594,8 +700,6 @@ void WCMD_list_directory (char *search_path, int level) {
     WCMD_print_error ();
     errorlevel = 1;
   }
-
-  return;
 }
 
 /*****************************************************************************
@@ -779,3 +883,29 @@ void WCMD_getfileowner(char *filename, char *owner, int ownerlen) {
     }
     return;
 }
+
+/*****************************************************************************
+ * WCMD_dir_trailer
+ *
+ * Print out the trailer for the supplied drive letter
+ */
+static void WCMD_dir_trailer(char drive) {
+    ULARGE_INTEGER avail, total, freebytes;
+    DWORD status;
+    char driveName[4] = "c:\\";
+
+    driveName[0] = drive;
+    status = GetDiskFreeSpaceEx (driveName, &avail, &total, &freebytes);
+    WINE_TRACE("Writing trailer for '%s' gave %d(%d)\n", driveName, status, GetLastError());
+
+    if (errorlevel==0 && !bare) {
+      if (recurse) {
+        WCMD_output ("\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",
+             dir_total, WCMD_filesize64 (freebytes.QuadPart));
+      } else {
+        WCMD_output (" %18s bytes free\n\n", WCMD_filesize64 (freebytes.QuadPart));
+      }
+    }
+}
diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h
index 671019b..29efbe4 100644
--- a/programs/cmd/wcmd.h
+++ b/programs/cmd/wcmd.h
@@ -36,7 +36,7 @@ void WCMD_color (void);
 void WCMD_copy (void);
 void WCMD_create_dir (void);
 void WCMD_delete (char *);
-void WCMD_directory (void);
+void WCMD_directory (char *);
 void WCMD_echo (const char *);
 void WCMD_endlocal (void);
 void WCMD_enter_paged_mode(void);
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c
index c627421..c87b0ad 100644
--- a/programs/cmd/wcmdmain.c
+++ b/programs/cmd/wcmdmain.c
@@ -622,7 +622,7 @@ void WCMD_process_command (char *command)
         WCMD_delete (p);
         break;
       case WCMD_DIR:
-        WCMD_directory ();
+        WCMD_directory (p);
         break;
       case WCMD_ECHO:
         WCMD_echo(&whichcmd[count]);
-- 
1.5.0




More information about the wine-patches mailing list