attrib: Skip processing of . and .. entries.
[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[128];
132     DWORD attrib_set = 0;
133     DWORD attrib_clear = 0;
134     const WCHAR help_option[] = {'/','?','\0'};
135     const WCHAR slashStarW[]  = {'\\','*','\0'};
136     int i = 1;
137
138     if ((argc >= 2) && !strcmpW(argv[1], help_option)) {
139         ATTRIB_wprintf(ATTRIB_LoadMessage(STRING_HELP));
140         return 0;
141     }
142
143     /* By default all files from current directory are taken into account */
144     GetCurrentDirectoryW(sizeof(name)/sizeof(WCHAR), name);
145     strcatW (name, slashStarW);
146
147     while (i < argc) {
148         WCHAR *param = argv[i++];
149         if ((param[0] == '+') || (param[0] == '-')) {
150             DWORD attrib = 0;
151             switch (param[1]) {
152             case 'H': case 'h': attrib |= FILE_ATTRIBUTE_HIDDEN; break;
153             case 'S': case 's': attrib |= FILE_ATTRIBUTE_SYSTEM; break;
154             case 'R': case 'r': attrib |= FILE_ATTRIBUTE_READONLY; break;
155             case 'A': case 'a': attrib |= FILE_ATTRIBUTE_ARCHIVE; break;
156             default:
157                 ATTRIB_wprintf(ATTRIB_LoadMessage(STRING_NYI));
158                 return 0;
159             }
160             switch (param[0]) {
161             case '+': attrib_set = attrib; break;
162             case '-': attrib_clear = attrib; break;
163             }
164         } else if (param[0] == '/') {
165             if (((param[1] == 'D') || (param[1] == 'd')) && !param[2]) {
166                 WINE_FIXME("Option /D not yet supported\n");
167             } else if (((param[1] == 'R') || (param[1] == 'r')) && !param[2]) {
168                 WINE_FIXME("Option /R not yet supported\n");
169             } else {
170                 WINE_FIXME("Unknown option %s\n", debugstr_w(param));
171             }
172         } else if (param[0]) {
173             strcpyW(name, param);
174         }
175     }
176
177     hff = FindFirstFileW(name, &fd);
178     if (hff == INVALID_HANDLE_VALUE) {
179         ATTRIB_wprintf(ATTRIB_LoadMessage(STRING_FILENOTFOUND), name);
180     }
181     else {
182         do {
183             const WCHAR dot[] = {'.', 0};
184             const WCHAR dotdot[] = {'.', '.', 0};
185
186             if (!strcmpW(fd.cFileName, dot) || !strcmpW(fd.cFileName, dotdot))
187                 continue;
188
189             if (attrib_set || attrib_clear) {
190                 fd.dwFileAttributes &= ~attrib_clear;
191                 fd.dwFileAttributes |= attrib_set;
192                 if (!fd.dwFileAttributes)
193                     fd.dwFileAttributes |= FILE_ATTRIBUTE_NORMAL;
194                 SetFileAttributesW(name, fd.dwFileAttributes);
195             } else {
196                 static const WCHAR fmt[] = {'%','1',' ',' ',' ','%','2','\n','\0'};
197                 if (fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
198                     flags[0] = 'H';
199                 }
200                 if (fd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {
201                     flags[1] = 'S';
202                 }
203                 if (fd.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) {
204                     flags[2] = 'A';
205                 }
206                 if (fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
207                     flags[3] = 'R';
208                 }
209                 if (fd.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) {
210                     flags[4] = 'T';
211                 }
212                 if (fd.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) {
213                     flags[5] = 'C';
214                 }
215                 ATTRIB_wprintf(fmt, flags, fd.cFileName);
216                 for (count=0; count < 8; count++) flags[count] = ' ';
217             }
218         } while (FindNextFileW(hff, &fd) != 0);
219     }
220     FindClose (hff);
221
222     return 0;
223 }