attrib: Do not hardcode number of characters in flags array.
[wine] / programs / attrib / attrib.c
1 /*
2  * ATTRIB - Wine-compatible attrib program
3  *
4  * Copyright 2010-2012 Christian Costa
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <windows.h>
22 #include <wine/debug.h>
23 #include <wine/unicode.h>
24 #include "attrib.h"
25
26 WINE_DEFAULT_DEBUG_CHANNEL(attrib);
27
28 /* =========================================================================
29  * Load a string from the resource file, handling any error
30  * Returns string retrieved from resource file
31  * ========================================================================= */
32 static WCHAR *ATTRIB_LoadMessage(UINT id)
33 {
34     static WCHAR msg[MAXSTRING];
35     const WCHAR failedMsg[]  = {'F', 'a', 'i', 'l', 'e', 'd', '!', 0};
36
37     if (!LoadStringW(GetModuleHandleW(NULL), id, msg, sizeof(msg)/sizeof(WCHAR))) {
38         WINE_FIXME("LoadString failed with %d\n", GetLastError());
39         lstrcpyW(msg, failedMsg);
40     }
41     return msg;
42 }
43
44 /* =========================================================================
45  * Output a formatted unicode string. Ideally this will go to the console
46  *  and hence required WriteConsoleW to output it, however if file i/o is
47  *  redirected, it needs to be WriteFile'd using OEM (not ANSI) format
48  * ========================================================================= */
49 static int __cdecl ATTRIB_wprintf(const WCHAR *format, ...)
50 {
51     static WCHAR *output_bufW = NULL;
52     static char  *output_bufA = NULL;
53     static BOOL  toConsole    = TRUE;
54     static BOOL  traceOutput  = FALSE;
55 #define MAX_WRITECONSOLE_SIZE 65535
56
57     __ms_va_list parms;
58     DWORD   nOut;
59     int len;
60     DWORD   res = 0;
61
62     /*
63      * Allocate buffer to use when writing to console
64      * Note: Not freed - memory will be allocated once and released when
65      *         xcopy ends
66      */
67
68     if (!output_bufW) output_bufW = HeapAlloc(GetProcessHeap(), 0,
69                                               MAX_WRITECONSOLE_SIZE);
70     if (!output_bufW) {
71         WINE_FIXME("Out of memory - could not allocate 2 x 64K buffers\n");
72         return 0;
73     }
74
75     __ms_va_start(parms, format);
76     SetLastError(NO_ERROR);
77     len = FormatMessageW(FORMAT_MESSAGE_FROM_STRING, format, 0, 0, output_bufW,
78                    MAX_WRITECONSOLE_SIZE/sizeof(*output_bufW), &parms);
79     __ms_va_end(parms);
80     if (len == 0 && GetLastError() != NO_ERROR) {
81         WINE_FIXME("Could not format string: le=%u, fmt=%s\n", GetLastError(), wine_dbgstr_w(format));
82         return 0;
83     }
84
85     /* Try to write as unicode all the time we think its a console */
86     if (toConsole) {
87         res = WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),
88                             output_bufW, len, &nOut, NULL);
89     }
90
91     /* If writing to console has failed (ever) we assume its file
92        i/o so convert to OEM codepage and output                  */
93     if (!res) {
94         BOOL usedDefaultChar = FALSE;
95         DWORD convertedChars;
96
97         toConsole = FALSE;
98
99         /*
100          * Allocate buffer to use when writing to file. Not freed, as above
101          */
102         if (!output_bufA) output_bufA = HeapAlloc(GetProcessHeap(), 0,
103                                                 MAX_WRITECONSOLE_SIZE);
104         if (!output_bufA) {
105           WINE_FIXME("Out of memory - could not allocate 2 x 64K buffers\n");
106           return 0;
107         }
108
109         /* Convert to OEM, then output */
110         convertedChars = WideCharToMultiByte(GetConsoleOutputCP(), 0, output_bufW,
111                             len, output_bufA, MAX_WRITECONSOLE_SIZE,
112                             "?", &usedDefaultChar);
113         WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), output_bufA, convertedChars,
114                   &nOut, FALSE);
115     }
116
117     /* Trace whether screen or console */
118     if (!traceOutput) {
119         WINE_TRACE("Writing to console? (%d)\n", toConsole);
120         traceOutput = TRUE;
121     }
122     return nOut;
123 }
124
125 int wmain(int argc, WCHAR *argv[])
126 {
127     DWORD count;
128     HANDLE hff;
129     WIN32_FIND_DATAW fd;
130     WCHAR flags[] = {' ',' ',' ',' ',' ',' ',' ',' ','\0'};
131     WCHAR name[MAX_PATH];
132     WCHAR curdir[MAX_PATH];
133     DWORD attrib_set = 0;
134     DWORD attrib_clear = 0;
135     const WCHAR help_option[] = {'/','?','\0'};
136     const WCHAR slash[]  = {'\\','\0'};
137     const WCHAR start[]  = {'*','\0'};
138     int i = 1;
139
140     if ((argc >= 2) && !strcmpW(argv[1], help_option)) {
141         ATTRIB_wprintf(ATTRIB_LoadMessage(STRING_HELP));
142         return 0;
143     }
144
145     /* By default all files from current directory are taken into account */
146     GetCurrentDirectoryW(sizeof(curdir)/sizeof(WCHAR), curdir);
147     strcatW(curdir, slash);
148     strcpyW(name, curdir);
149     strcatW(name, start);
150
151     while (i < argc) {
152         WCHAR *param = argv[i++];
153         if ((param[0] == '+') || (param[0] == '-')) {
154             DWORD attrib = 0;
155             switch (param[1]) {
156             case 'H': case 'h': attrib |= FILE_ATTRIBUTE_HIDDEN; break;
157             case 'S': case 's': attrib |= FILE_ATTRIBUTE_SYSTEM; break;
158             case 'R': case 'r': attrib |= FILE_ATTRIBUTE_READONLY; break;
159             case 'A': case 'a': attrib |= FILE_ATTRIBUTE_ARCHIVE; break;
160             default:
161                 ATTRIB_wprintf(ATTRIB_LoadMessage(STRING_NYI));
162                 return 0;
163             }
164             switch (param[0]) {
165             case '+': attrib_set = attrib; break;
166             case '-': attrib_clear = attrib; break;
167             }
168         } else if (param[0] == '/') {
169             if (((param[1] == 'D') || (param[1] == 'd')) && !param[2]) {
170                 WINE_FIXME("Option /D not yet supported\n");
171             } else if (((param[1] == 'R') || (param[1] == 'r')) && !param[2]) {
172                 WINE_FIXME("Option /R not yet supported\n");
173             } else {
174                 WINE_FIXME("Unknown option %s\n", debugstr_w(param));
175             }
176         } else if (param[0]) {
177             strcpyW(name, param);
178         }
179     }
180
181     hff = FindFirstFileW(name, &fd);
182     if (hff == INVALID_HANDLE_VALUE) {
183         ATTRIB_wprintf(ATTRIB_LoadMessage(STRING_FILENOTFOUND), name);
184     }
185     else {
186         do {
187             const WCHAR dot[] = {'.', 0};
188             const WCHAR dotdot[] = {'.', '.', 0};
189
190             if (!strcmpW(fd.cFileName, dot) || !strcmpW(fd.cFileName, dotdot))
191                 continue;
192
193             if (attrib_set || attrib_clear) {
194                 fd.dwFileAttributes &= ~attrib_clear;
195                 fd.dwFileAttributes |= attrib_set;
196                 if (!fd.dwFileAttributes)
197                     fd.dwFileAttributes |= FILE_ATTRIBUTE_NORMAL;
198                 SetFileAttributesW(name, fd.dwFileAttributes);
199             } else {
200                 static const WCHAR fmt[] = {'%','1',' ',' ',' ',' ',' ','%','2','\n','\0'};
201                 if (fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
202                     flags[0] = 'H';
203                 }
204                 if (fd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {
205                     flags[1] = 'S';
206                 }
207                 if (fd.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) {
208                     flags[2] = 'A';
209                 }
210                 if (fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
211                     flags[3] = 'R';
212                 }
213                 if (fd.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) {
214                     flags[4] = 'T';
215                 }
216                 if (fd.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) {
217                     flags[5] = 'C';
218                 }
219                 strcpyW(name, curdir);
220                 strcatW(name, fd.cFileName);
221                 ATTRIB_wprintf(fmt, flags, name);
222                 for (count = 0; count < (sizeof(flags)/sizeof(WCHAR) - 1); count++) flags[count] = ' ';
223             }
224         } while (FindNextFileW(hff, &fd) != 0);
225     }
226     FindClose (hff);
227
228     return 0;
229 }