[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