Crtl-C gives a new line when reading input
[reactos.git] / reactos / subsys / system / cmd / attrib.c
1 /*
2 * ATTRIB.C - attrib internal command.
3 *
4 *
5 * History:
6 *
7 * 04-Dec-1998 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
8 * started
9 *
10 * 09-Dec-1998 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
11 * implementation works, except recursion ("attrib /s").
12 *
13 * 05-Jan-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
14 * major rewrite.
15 * fixed recursion ("attrib /s").
16 * started directory support ("attrib /s /d").
17 * updated help text.
18 *
19 * 14-Jan-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
20 * Unicode ready!
21 *
22 * 19-Jan-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
23 * Redirection ready!
24 *
25 * 21-Jan-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
26 * Added check for invalid filenames.
27 *
28 * 23-Jan-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
29 * Added handling of multiple filenames.
30 *
31 * 02-Apr-2005 (Magnus Olsen) <magnus@greatlord.com>)
32 * Remove all hardcode string to En.rc
33 */
34
35 #include <precomp.h>
36 #include "resource.h"
37
38 #ifdef INCLUDE_CMD_ATTRIB
39
40
41 static VOID
42 PrintAttribute (LPTSTR pszPath, LPTSTR pszFile, BOOL bRecurse)
43 {
44 WIN32_FIND_DATA findData;
45 HANDLE hFind;
46 TCHAR szFullName[MAX_PATH];
47 LPTSTR pszFileName;
48
49 /* prepare full file name buffer */
50 _tcscpy (szFullName, pszPath);
51 pszFileName = szFullName + _tcslen (szFullName);
52
53 /* display all subdirectories */
54 if (bRecurse)
55 {
56 /* append file name */
57 _tcscpy (pszFileName, pszFile);
58
59 hFind = FindFirstFile (szFullName, &findData);
60 if (hFind == INVALID_HANDLE_VALUE)
61 {
62 ErrorMessage (GetLastError (), pszFile);
63 return;
64 }
65
66 do
67 {
68 if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
69 continue;
70
71 if (!_tcscmp (findData.cFileName, _T(".")) ||
72 !_tcscmp (findData.cFileName, _T("..")))
73 continue;
74
75 _tcscpy (pszFileName, findData.cFileName);
76 _tcscat (pszFileName, _T("\\"));
77 PrintAttribute (szFullName, pszFile, bRecurse);
78 }
79 while (FindNextFile (hFind, &findData));
80 FindClose (hFind);
81 }
82
83 /* append file name */
84 _tcscpy (pszFileName, pszFile);
85
86 /* display current directory */
87 hFind = FindFirstFile (szFullName, &findData);
88 if (hFind == INVALID_HANDLE_VALUE)
89 {
90 ErrorMessage (GetLastError (), pszFile);
91 return;
92 }
93
94 do
95 {
96 if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
97 continue;
98
99 _tcscpy (pszFileName, findData.cFileName);
100
101 ConOutPrintf (_T("%c %c%c%c %s\n"),
102 (findData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) ? _T('A') : _T(' '),
103 (findData.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) ? _T('S') : _T(' '),
104 (findData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ? _T('H') : _T(' '),
105 (findData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? _T('R') : _T(' '),
106 szFullName);
107 }
108 while (FindNextFile (hFind, &findData));
109 FindClose (hFind);
110 }
111
112
113 static VOID
114 ChangeAttribute (LPTSTR pszPath, LPTSTR pszFile, DWORD dwMask,
115 DWORD dwAttrib, BOOL bRecurse, BOOL bDirectories)
116 {
117 WIN32_FIND_DATA findData;
118 HANDLE hFind;
119 DWORD dwAttribute;
120 TCHAR szFullName[MAX_PATH];
121 LPTSTR pszFileName;
122
123
124 /* prepare full file name buffer */
125 _tcscpy (szFullName, pszPath);
126 pszFileName = szFullName + _tcslen (szFullName);
127
128 /* change all subdirectories */
129 if (bRecurse)
130 {
131 /* append file name */
132 _tcscpy (pszFileName, _T("*.*"));
133
134 hFind = FindFirstFile (szFullName, &findData);
135 if (hFind == INVALID_HANDLE_VALUE)
136 {
137 ErrorMessage (GetLastError (), pszFile);
138 nErrorLevel = 1;
139 return;
140 }
141
142 do
143 {
144 if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
145 {
146 if (!_tcscmp (findData.cFileName, _T(".")) ||
147 !_tcscmp (findData.cFileName, _T("..")))
148 continue;
149
150 _tcscpy (pszFileName, findData.cFileName);
151 _tcscat (pszFileName, _T("\\"));
152
153 ChangeAttribute (szFullName, pszFile, dwMask,
154 dwAttrib, bRecurse, bDirectories);
155 }
156 }
157 while (FindNextFile (hFind, &findData));
158 FindClose (hFind);
159 }
160
161 /* append file name */
162 _tcscpy (pszFileName, pszFile);
163
164 hFind = FindFirstFile (szFullName, &findData);
165 if (hFind == INVALID_HANDLE_VALUE)
166 {
167 ErrorMessage (GetLastError (), pszFile);
168 nErrorLevel = 1;
169 return;
170 }
171
172 do
173 {
174 if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
175 continue;
176
177 _tcscpy (pszFileName, findData.cFileName);
178
179 dwAttribute = GetFileAttributes (szFullName);
180
181 if (dwAttribute != 0xFFFFFFFF)
182 {
183 dwAttribute = (dwAttribute & ~dwMask) | dwAttrib;
184 SetFileAttributes (szFullName, dwAttribute);
185 }
186 }
187 while (FindNextFile (hFind, &findData));
188 FindClose (hFind);
189 }
190
191
192 INT CommandAttrib (LPTSTR cmd, LPTSTR param)
193 {
194 LPTSTR *arg;
195 INT argc, i;
196 TCHAR szPath[MAX_PATH];
197 TCHAR szFileName [MAX_PATH];
198 BOOL bRecurse = FALSE;
199 BOOL bDirectories = FALSE;
200 DWORD dwAttrib = 0;
201 DWORD dwMask = 0;
202
203 /* initialize strings */
204 szPath[0] = _T('\0');
205 szFileName[0] = _T('\0');
206
207 /* print help */
208 if (!_tcsncmp (param, _T("/?"), 2))
209 {
210 ConOutResPaging(TRUE,STRING_ATTRIB_HELP);
211 return 0;
212 }
213
214 nErrorLevel = 0;
215
216 /* build parameter array */
217 arg = split (param, &argc, FALSE);
218
219 /* check for options */
220 for (i = 0; i < argc; i++)
221 {
222 if (_tcsicmp (arg[i], _T("/s")) == 0)
223 bRecurse = TRUE;
224 else if (_tcsicmp (arg[i], _T("/d")) == 0)
225 bDirectories = TRUE;
226 }
227
228 /* create attributes and mask */
229 for (i = 0; i < argc; i++)
230 {
231 if (*arg[i] == _T('+'))
232 {
233 if (_tcslen (arg[i]) != 2)
234 {
235 error_invalid_parameter_format (arg[i]);
236 freep (arg);
237 return -1;
238 }
239
240 switch ((TCHAR)_totupper (arg[i][1]))
241 {
242 case _T('A'):
243 dwMask |= FILE_ATTRIBUTE_ARCHIVE;
244 dwAttrib |= FILE_ATTRIBUTE_ARCHIVE;
245 break;
246
247 case _T('H'):
248 dwMask |= FILE_ATTRIBUTE_HIDDEN;
249 dwAttrib |= FILE_ATTRIBUTE_HIDDEN;
250 break;
251
252 case _T('R'):
253 dwMask |= FILE_ATTRIBUTE_READONLY;
254 dwAttrib |= FILE_ATTRIBUTE_READONLY;
255 break;
256
257 case _T('S'):
258 dwMask |= FILE_ATTRIBUTE_SYSTEM;
259 dwAttrib |= FILE_ATTRIBUTE_SYSTEM;
260 break;
261
262 default:
263 error_invalid_parameter_format (arg[i]);
264 freep (arg);
265 return -1;
266 }
267 }
268 else if (*arg[i] == _T('-'))
269 {
270 if (_tcslen (arg[i]) != 2)
271 {
272 error_invalid_parameter_format (arg[i]);
273 freep (arg);
274 return -1;
275 }
276
277 switch ((TCHAR)_totupper (arg[i][1]))
278 {
279 case _T('A'):
280 dwMask |= FILE_ATTRIBUTE_ARCHIVE;
281 dwAttrib &= ~FILE_ATTRIBUTE_ARCHIVE;
282 break;
283
284 case _T('H'):
285 dwMask |= FILE_ATTRIBUTE_HIDDEN;
286 dwAttrib &= ~FILE_ATTRIBUTE_HIDDEN;
287 break;
288
289 case _T('R'):
290 dwMask |= FILE_ATTRIBUTE_READONLY;
291 dwAttrib &= ~FILE_ATTRIBUTE_READONLY;
292 break;
293
294 case _T('S'):
295 dwMask |= FILE_ATTRIBUTE_SYSTEM;
296 dwAttrib &= ~FILE_ATTRIBUTE_SYSTEM;
297 break;
298
299 default:
300 error_invalid_parameter_format (arg[i]);
301 freep (arg);
302 return -1;
303 }
304 }
305 }
306
307 if (argc == 0)
308 {
309 DWORD len;
310
311 len = GetCurrentDirectory (MAX_PATH, szPath);
312 if (szPath[len-1] != _T('\\'))
313 {
314 szPath[len] = _T('\\');
315 szPath[len + 1] = 0;
316 }
317 _tcscpy (szFileName, _T("*.*"));
318 PrintAttribute (szPath, szFileName, bRecurse);
319 freep (arg);
320 return 0;
321 }
322
323 /* get full file name */
324 for (i = 0; i < argc; i++)
325 {
326 if ((*arg[i] != _T('+')) && (*arg[i] != _T('-')) && (*arg[i] != _T('/')))
327 {
328 LPTSTR p;
329 GetFullPathName (arg[i], MAX_PATH, szPath, NULL);
330 p = _tcsrchr (szPath, _T('\\')) + 1;
331 _tcscpy (szFileName, p);
332 *p = _T('\0');
333
334 if (dwMask == 0)
335 PrintAttribute (szPath, szFileName, bRecurse);
336 else
337 ChangeAttribute (szPath, szFileName, dwMask,
338 dwAttrib, bRecurse, bDirectories);
339 }
340 }
341
342 freep (arg);
343 return 0;
344 }
345
346 #endif /* INCLUDE_CMD_ATTRIB */