[PATCH] attrib: Move implementation from cmd.exe to the standalone command

Christian Costa titan.costa at wanadoo.fr
Wed Aug 24 01:20:40 CDT 2011


--

I added a stub last year to make an installer work but didn't know the command existed as a builtin in cmd.exe until Frédéric Delanoy told me. So here is a patch that takes what's in cmd.exe and move it to the standalone command. Some few code was taken from xcopy.
---
 programs/attrib/Makefile.in |    4 +
 programs/attrib/attrib.c    |  190 +++++++++++++++++++++++++++++++++++++++++--
 programs/attrib/attrib.h    |   28 ++++++
 programs/attrib/attrib.rc   |   49 +++++++++++
 programs/cmd/builtins.c     |   83 -------------------
 programs/cmd/cmd.rc         |    2 
 programs/cmd/wcmd.h         |   94 ++++++++++-----------
 programs/cmd/wcmdmain.c     |    4 -
 8 files changed, 309 insertions(+), 145 deletions(-)
 create mode 100644 programs/attrib/attrib.h
 create mode 100644 programs/attrib/attrib.rc

diff --git a/programs/attrib/Makefile.in b/programs/attrib/Makefile.in
index 895697c..53f6809 100644
--- a/programs/attrib/Makefile.in
+++ b/programs/attrib/Makefile.in
@@ -1,7 +1,11 @@
 EXTRADEFS = -DWINE_NO_UNICODE_MACROS
 MODULE    = attrib.exe
 APPMODE   = -mconsole -municode
+IMPORTS   = user32
 
 C_SRCS = attrib.c
 
+RC_SRCS = attrib.rc
+PO_SRCS = attrib.rc
+
 @MAKE_PROG_RULES@
diff --git a/programs/attrib/attrib.c b/programs/attrib/attrib.c
index 9cb9d9f..8e60455 100644
--- a/programs/attrib/attrib.c
+++ b/programs/attrib/attrib.c
@@ -1,5 +1,7 @@
 /*
- * Copyright 2010 Christian Costa
+ * ATTRIB - Wine-compatible attrib program
+ *
+ * Copyright 2010-2011 Christian Costa
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -16,19 +18,191 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-#include "wine/debug.h"
+#include <windows.h>
+#include <wine/debug.h>
+#include <wine/unicode.h>
+#include "attrib.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(attrib);
 
+/* =========================================================================
+ * Load a string from the resource file, handling any error
+ * Returns string retrieved from resource file
+ * ========================================================================= */
+static WCHAR *ATTRIB_LoadMessage(UINT id) {
+    static WCHAR msg[MAXSTRING];
+    const WCHAR failedMsg[]  = {'F', 'a', 'i', 'l', 'e', 'd', '!', 0};
+
+    if (!LoadStringW(GetModuleHandleW(NULL), id, msg, sizeof(msg)/sizeof(WCHAR))) {
+        WINE_FIXME("LoadString failed with %d\n", GetLastError());
+        lstrcpyW(msg, failedMsg);
+    }
+    return msg;
+}
+
+/* =========================================================================
+ * Output a formatted unicode string. Ideally this will go to the console
+ *  and hence required WriteConsoleW to output it, however if file i/o is
+ *  redirected, it needs to be WriteFile'd using OEM (not ANSI) format
+ * ========================================================================= */
+int ATTRIB_wprintf(const WCHAR *format, ...) {
+
+    static WCHAR *output_bufW = NULL;
+    static char  *output_bufA = NULL;
+    static BOOL  toConsole    = TRUE;
+    static BOOL  traceOutput  = FALSE;
+#define MAX_WRITECONSOLE_SIZE 65535
+
+    va_list parms;
+    DWORD   nOut;
+    int len;
+    DWORD   res = 0;
+
+    /*
+     * Allocate buffer to use when writing to console
+     * Note: Not freed - memory will be allocated once and released when
+     *         xcopy ends
+     */
+
+    if (!output_bufW) output_bufW = HeapAlloc(GetProcessHeap(), 0,
+                                              MAX_WRITECONSOLE_SIZE);
+    if (!output_bufW) {
+        WINE_FIXME("Out of memory - could not allocate 2 x 64K buffers\n");
+        return 0;
+    }
+
+    va_start(parms, format);
+    len = vsnprintfW(output_bufW, MAX_WRITECONSOLE_SIZE/sizeof(WCHAR), format, parms);
+    va_end(parms);
+    if (len < 0) {
+        WINE_FIXME("String too long.\n");
+        return 0;
+    }
+
+    /* Try to write as unicode all the time we think its a console */
+    if (toConsole) {
+        res = WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),
+                            output_bufW, len, &nOut, NULL);
+    }
+
+    /* If writing to console has failed (ever) we assume its file
+       i/o so convert to OEM codepage and output                  */
+    if (!res) {
+        BOOL usedDefaultChar = FALSE;
+        DWORD convertedChars;
+
+        toConsole = FALSE;
+
+        /*
+         * Allocate buffer to use when writing to file. Not freed, as above
+         */
+        if (!output_bufA) output_bufA = HeapAlloc(GetProcessHeap(), 0,
+                                                MAX_WRITECONSOLE_SIZE);
+        if (!output_bufA) {
+          WINE_FIXME("Out of memory - could not allocate 2 x 64K buffers\n");
+          return 0;
+        }
+
+        /* Convert to OEM, then output */
+        convertedChars = WideCharToMultiByte(GetConsoleOutputCP(), 0, output_bufW,
+                            len, output_bufA, MAX_WRITECONSOLE_SIZE,
+                            "?", &usedDefaultChar);
+        WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), output_bufA, convertedChars,
+                  &nOut, FALSE);
+    }
+
+    /* Trace whether screen or console */
+    if (!traceOutput) {
+        WINE_TRACE("Writing to console? (%d)\n", toConsole);
+        traceOutput = TRUE;
+    }
+    return nOut;
+}
+
 int wmain(int argc, WCHAR *argv[])
 {
-    int i;
+    DWORD count;
+    HANDLE hff;
+    WIN32_FIND_DATAW fd;
+    WCHAR flags[9] = {' ',' ',' ',' ',' ',' ',' ',' ','\0'};
+    WCHAR name[128];
+    WCHAR *param = argc >= 2 ? argv[1] : NULL;
+    DWORD attrib_set = 0;
+    DWORD attrib_clear = 0;
+    WCHAR help_option[] = {'/','?'};
+
+    if (param && strcmpW(param, help_option))
+    {
+        ATTRIB_wprintf(ATTRIB_LoadMessage(STRING_HELP));
+        return 0;
+    }
+
+    if (param && (param[0] == '+' || param[0] == '-')) {
+        DWORD attrib = 0;
+        /* FIXME: the real cmd can handle many more than two args; this should be in a loop */
+        switch (param[1]) {
+        case 'H': case 'h': attrib |= FILE_ATTRIBUTE_HIDDEN; break;
+        case 'S': case 's': attrib |= FILE_ATTRIBUTE_SYSTEM; break;
+        case 'R': case 'r': attrib |= FILE_ATTRIBUTE_READONLY; break;
+        case 'A': case 'a': attrib |= FILE_ATTRIBUTE_ARCHIVE; break;
+        default:
+            ATTRIB_wprintf(ATTRIB_LoadMessage(STRING_NYI));
+            return 0;
+        }
+        switch (param[0]) {
+        case '+': attrib_set = attrib; break;
+        case '-': attrib_clear = attrib; break;
+        }
+        param = argc >= 3 ? argv[2] : NULL;
+    }
+
+    if (!param || strlenW(param) == 0) {
+        static const WCHAR slashStarW[]  = {'\\','*','\0'};
+
+        GetCurrentDirectoryW(sizeof(name)/sizeof(WCHAR), name);
+        strcatW (name, slashStarW);
+    } else {
+        strcpyW(name, param);
+    }
 
-    WINE_FIXME("attrib.exe is currently only a stub command\n");
-    WINE_FIXME("cmdline:");
-    for (i = 0; i < argc; i++)
-        WINE_FIXME(" %s", wine_dbgstr_w(argv[i]));
-    WINE_FIXME("\n");
+    hff = FindFirstFileW(name, &fd);
+    if (hff == INVALID_HANDLE_VALUE) {
+        ATTRIB_wprintf(ATTRIB_LoadMessage(STRING_FILENOTFOUND), name);
+    }
+    else {
+        do {
+            if (attrib_set || attrib_clear) {
+                fd.dwFileAttributes &= ~attrib_clear;
+                fd.dwFileAttributes |= attrib_set;
+                if (!fd.dwFileAttributes)
+                    fd.dwFileAttributes |= FILE_ATTRIBUTE_NORMAL;
+                SetFileAttributesW(name, fd.dwFileAttributes);
+            } else {
+                static const WCHAR fmt[] = {'%','s',' ',' ',' ','%','s','\n','\0'};
+                if (fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
+                    flags[0] = 'H';
+                }
+                if (fd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {
+                    flags[1] = 'S';
+                }
+                if (fd.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) {
+                    flags[2] = 'A';
+                }
+                if (fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
+                    flags[3] = 'R';
+                }
+                if (fd.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) {
+                    flags[4] = 'T';
+                }
+                if (fd.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) {
+                    flags[5] = 'C';
+                }
+                ATTRIB_wprintf(fmt, flags, fd.cFileName);
+                for (count=0; count < 8; count++) flags[count] = ' ';
+            }
+        } while (FindNextFileW(hff, &fd) != 0);
+    }
+    FindClose (hff);
 
     return 0;
 }
diff --git a/programs/attrib/attrib.h b/programs/attrib/attrib.h
new file mode 100644
index 0000000..41c0ae4
--- /dev/null
+++ b/programs/attrib/attrib.h
@@ -0,0 +1,28 @@
+/*
+ * ATTRIB - Wine-compatible attrib program
+ *
+ * Copyright 2011 Christian Costa
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <windef.h>
+
+#define MAXSTRING 8192
+
+/* Translation ids */
+#define STRING_NYI              101
+#define STRING_FILENOTFOUND     102
+#define STRING_HELP             103
diff --git a/programs/attrib/attrib.rc b/programs/attrib/attrib.rc
new file mode 100644
index 0000000..478a196
--- /dev/null
+++ b/programs/attrib/attrib.rc
@@ -0,0 +1,49 @@
+/*
+ * ATTRIB - Wine-compatible attrib program
+ *
+ * Copyright 2011 Christian Costa
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "attrib.h"
+
+LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT
+
+STRINGTABLE
+{
+    STRING_NYI, "Not Yet Implemented\n\n"
+    STRING_FILENOTFOUND, "%s: File Not Found\n"
+    STRING_HELP,
+"ATTRIB - Displays or changes file attributes.\n\
+\n\
+Syntax:\n\
+ATTRIB [+R | -R] [+A | -A ] [+S | -S] [+H | -H] [drive:][path][filename]\n\
+\t     [/S [/D]]\n\
+\n\
+Where:\n\
+\n\
+  +   Sets an attribute.\n\
+  -   Clears an attribute.\n\
+  R   Read-only file attribute\n\
+  A   Archive file attribute.\n\
+  S   System file attribute.\n\
+  H   Hidden file attribute.\n\
+  [drive:][path][filename]\n\
+      Specifies a file or files for attrib to process.\n\
+  /S  Processes matching files in the current folder\n\
+      and all subfolders.\n\
+  /D  Processes folders as well.\n"
+}
diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c
index cc3f5f6..993c2d2 100644
--- a/programs/cmd/builtins.c
+++ b/programs/cmd/builtins.c
@@ -1992,89 +1992,6 @@ void WCMD_endlocal (void) {
 }
 
 /*****************************************************************************
- * WCMD_setshow_attrib
- *
- * Display and optionally sets DOS attributes on a file or directory
- *
- */
-
-void WCMD_setshow_attrib (void) {
-
-  DWORD count;
-  HANDLE hff;
-  WIN32_FIND_DATAW fd;
-  WCHAR flags[9] = {' ',' ',' ',' ',' ',' ',' ',' ','\0'};
-  WCHAR *name = param1;
-  DWORD attrib_set=0;
-  DWORD attrib_clear=0;
-
-  if (param1[0] == '+' || param1[0] == '-') {
-    DWORD attrib = 0;
-    /* FIXME: the real cmd can handle many more than two args; this should be in a loop */
-    switch (param1[1]) {
-    case 'H': case 'h': attrib |= FILE_ATTRIBUTE_HIDDEN; break;
-    case 'S': case 's': attrib |= FILE_ATTRIBUTE_SYSTEM; break;
-    case 'R': case 'r': attrib |= FILE_ATTRIBUTE_READONLY; break;
-    case 'A': case 'a': attrib |= FILE_ATTRIBUTE_ARCHIVE; break;
-    default:
-      WCMD_output (WCMD_LoadMessage(WCMD_NYI));
-      return;
-    }
-    switch (param1[0]) {
-    case '+': attrib_set = attrib; break;
-    case '-': attrib_clear = attrib; break;
-    }
-    name = param2;
-  }
-
-  if (strlenW(name) == 0) {
-    static const WCHAR slashStarW[]  = {'\\','*','\0'};
-
-    GetCurrentDirectoryW(sizeof(param2)/sizeof(WCHAR), name);
-    strcatW (name, slashStarW);
-  }
-
-  hff = FindFirstFileW(name, &fd);
-  if (hff == INVALID_HANDLE_VALUE) {
-    WCMD_output (WCMD_LoadMessage(WCMD_FILENOTFOUND), name);
-  }
-  else {
-    do {
-      if (attrib_set || attrib_clear) {
-        fd.dwFileAttributes &= ~attrib_clear;
-        fd.dwFileAttributes |= attrib_set;
-        if (!fd.dwFileAttributes)
-           fd.dwFileAttributes |= FILE_ATTRIBUTE_NORMAL;
-        SetFileAttributesW(name, fd.dwFileAttributes);
-      } else {
-        static const WCHAR fmt[] = {'%','s',' ',' ',' ','%','s','\n','\0'};
-        if (fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
-	  flags[0] = 'H';
-	}
-        if (fd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {
-	  flags[1] = 'S';
-	}
-        if (fd.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) {
-	  flags[2] = 'A';
-	}
-        if (fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
-	  flags[3] = 'R';
-	}
-        if (fd.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) {
-	  flags[4] = 'T';
-	}
-        if (fd.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) {
-	  flags[5] = 'C';
-	}
-        WCMD_output (fmt, flags, fd.cFileName);
-	for (count=0; count < 8; count++) flags[count] = ' ';
-      }
-    } while (FindNextFileW(hff, &fd) != 0);
-  }
-  FindClose (hff);
-}
-
-/*****************************************************************************
  * WCMD_setshow_default
  *
  *	Set/Show the current default directory
diff --git a/programs/cmd/cmd.rc b/programs/cmd/cmd.rc
index 1ece9d8..eb439fc 100644
--- a/programs/cmd/cmd.rc
+++ b/programs/cmd/cmd.rc
@@ -27,7 +27,6 @@ LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT
 
 STRINGTABLE
 {
-  WCMD_ATTRIB, "ATTRIB shows or changes DOS file attributes.\n"
   WCMD_CALL,
 "CALL <batchfilename> is used within a batch file to execute commands\n\
 from another batch file. When the batch file exits, control returns to\n\
@@ -234,7 +233,6 @@ CHOICE is mainly used to build a menu selection in a batch file.\n"
 to the operating system or shell from which you invoked cmd.\n"
 
   WCMD_ALLHELP, "CMD built-in commands are:\n\
-ATTRIB\t\tShow or change DOS file attributes\n\
 CALL\t\tInvoke a batch file from inside another\n\
 CD (CHDIR)\tChange current default directory\n\
 CHOICE\t\tWait for an keypress from a selectable list\n\
diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h
index 19ba913..632bfe8 100644
--- a/programs/cmd/wcmd.h
+++ b/programs/cmd/wcmd.h
@@ -80,7 +80,6 @@ void WCMD_remove_dir (WCHAR *command);
 void WCMD_rename (void);
 void WCMD_run_program (WCHAR *command, int called);
 void WCMD_setlocal (const WCHAR *command);
-void WCMD_setshow_attrib (void);
 void WCMD_setshow_date (void);
 void WCMD_setshow_default (const WCHAR *command);
 void WCMD_setshow_env (WCHAR *command);
@@ -159,55 +158,54 @@ typedef struct _DIRECTORY_STACK
  *	Compiler won't accept resource IDs from enumerations :-(
  */
 
-#define WCMD_ATTRIB  0
-#define WCMD_CALL    1
-#define WCMD_CD      2
-#define WCMD_CHDIR   3
-#define WCMD_CLS     4
-#define WCMD_COPY    5
-#define WCMD_CTTY    6
-#define WCMD_DATE    7
-#define WCMD_DEL     8
-#define WCMD_DIR     9
-#define WCMD_ECHO   10
-#define	WCMD_ERASE  11
-#define WCMD_FOR    12
-#define WCMD_GOTO   13
-#define WCMD_HELP   14
-#define WCMD_IF     15
-#define WCMD_LABEL  16
-#define	WCMD_MD     17
-#define WCMD_MKDIR  18
-#define WCMD_MOVE   19
-#define WCMD_PATH   20
-#define WCMD_PAUSE  21
-#define WCMD_PROMPT 22
-#define	WCMD_REM    23
-#define WCMD_REN    24
-#define WCMD_RENAME 25
-#define WCMD_RD     26
-#define WCMD_RMDIR  27
-#define WCMD_SET    28
-#define	WCMD_SHIFT  29
-#define WCMD_TIME   30
-#define WCMD_TITLE  31
-#define WCMD_TYPE   32
-#define WCMD_VERIFY 33
-#define WCMD_VER    34
-#define WCMD_VOL    35
-
-#define WCMD_ENDLOCAL 36
-#define WCMD_SETLOCAL 37
-#define WCMD_PUSHD  38
-#define WCMD_POPD   39
-#define WCMD_ASSOC  40
-#define WCMD_COLOR  41
-#define WCMD_FTYPE  42
-#define WCMD_MORE   43
-#define WCMD_CHOICE 44
+#define WCMD_CALL      0
+#define WCMD_CD        1
+#define WCMD_CHDIR     2
+#define WCMD_CLS       3
+#define WCMD_COPY      4
+#define WCMD_CTTY      5
+#define WCMD_DATE      6
+#define WCMD_DEL       7
+#define WCMD_DIR       8
+#define WCMD_ECHO      9
+#define WCMD_ERASE    10
+#define WCMD_FOR      11
+#define WCMD_GOTO     12
+#define WCMD_HELP     13
+#define WCMD_IF       14
+#define WCMD_LABEL    15
+#define WCMD_MD       16
+#define WCMD_MKDIR    17
+#define WCMD_MOVE     18
+#define WCMD_PATH     19
+#define WCMD_PAUSE    20
+#define WCMD_PROMPT   21
+#define WCMD_REM      22
+#define WCMD_REN      23
+#define WCMD_RENAME   24
+#define WCMD_RD       25
+#define WCMD_RMDIR    26
+#define WCMD_SET      27
+#define WCMD_SHIFT    28
+#define WCMD_TIME     29
+#define WCMD_TITLE    30
+#define WCMD_TYPE     31
+#define WCMD_VERIFY   32
+#define WCMD_VER      33
+#define WCMD_VOL      34
+
+#define WCMD_ENDLOCAL 35
+#define WCMD_SETLOCAL 36
+#define WCMD_PUSHD    37
+#define WCMD_POPD     38
+#define WCMD_ASSOC    39
+#define WCMD_COLOR    40
+#define WCMD_FTYPE    41
+#define WCMD_MORE     42
+#define WCMD_CHOICE   43
 
 /* Must be last in list */
-#define WCMD_EXIT   45
+#define WCMD_EXIT     44
 
 /* Some standard messages */
 extern const WCHAR newline[];
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c
index d758857..4d6af58 100644
--- a/programs/cmd/wcmdmain.c
+++ b/programs/cmd/wcmdmain.c
@@ -32,7 +32,6 @@
 WINE_DEFAULT_DEBUG_CHANNEL(cmd);
 
 const WCHAR inbuilt[][10] = {
-        {'A','T','T','R','I','B','\0'},
         {'C','A','L','L','\0'},
         {'C','D','\0'},
         {'C','H','D','I','R','\0'},
@@ -1425,9 +1424,6 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects,
 
     switch (i) {
 
-      case WCMD_ATTRIB:
-        WCMD_setshow_attrib ();
-        break;
       case WCMD_CALL:
         WCMD_call (p);
         break;




More information about the wine-patches mailing list