Crtl-C gives a new line when reading input
[reactos.git] / reactos / subsys / system / cmd / del.c
1 /*
2 * DEL.C - del internal command.
3 *
4 *
5 * History:
6 *
7 * 06/29/98 (Rob Lake rlake@cs.mun.ca)
8 * rewrote del to support wildcards
9 * added my name to the contributors
10 *
11 * 07/13/98 (Rob Lake)
12 * fixed bug that caused del not to delete file with out
13 * attribute. moved set, del, ren, and ver to there own files
14 *
15 * 27-Jul-1998 (John P Price <linux-guru@gcfl.net>)
16 * added config.h include
17 *
18 * 09-Dec-1998 (Eric Kohl <ekohl@abo.rhein-zeiung.de>)
19 * Fixed command line parsing bugs.
20 *
21 * 21-Jan-1999 (Eric Kohl <ekohl@abo.rhein-zeiung.de>)
22 * Started major rewrite using a new structure.
23 *
24 * 03-Feb-1999 (Eric Kohl <ekohl@abo.rhein-zeiung.de>)
25 * First working version.
26 *
27 * 30-Mar-1999 (Eric Kohl <ekohl@abo.rhein-zeiung.de>)
28 * Added quiet ("/Q"), wipe ("/W") and zap ("/Z") option.
29 *
30 * 06-Nov-1999 (Eric Kohl <ekohl@abo.rhein-zeiung.de>)
31 * Little fix to keep DEL quiet inside batch files.
32 *
33 * 28-Jan-2004 (Michael Fritscher <michael@fritscher.net>)
34 * Added prompt ("/P"), yes ("/Y") and wipe("/W") option.
35 *
36 * 22-Jun-2005 (Brandon Turner <turnerb7@msu.edu>)
37 * Added exclusive deletion "del * -abc.txt -text*.txt"
38 *
39 * 22-Jun-2005 (Brandon Turner <turnerb7@msu.edu>)
40 * Implemented /A example "del /A:H /A:-R *.exe -ping.exe"
41 *
42 * 07-Aug-2005 (Hartmut Birr)
43 * Removed the exclusive deletion (see two comments above) because '-' is a valid file name character.
44 * Optimized the recursive deletion in directories.
45 * Preload some nice strings.
46 */
47
48 #include <precomp.h>
49 #include "resource.h"
50
51 #ifdef INCLUDE_CMD_DEL
52
53
54 enum
55 {
56 DEL_ATTRIBUTES = 0x001, /* /A */
57 DEL_NOTHING = 0x004, /* /N */
58 DEL_PROMPT = 0x008, /* /P */
59 DEL_QUIET = 0x010, /* /Q */
60 DEL_SUBDIR = 0x020, /* /S */
61 DEL_TOTAL = 0x040, /* /T */
62 DEL_WIPE = 0x080, /* /W */
63 DEL_EMPTYDIR = 0x100, /* /X : not implemented */
64 DEL_YES = 0x200, /* /Y */
65 DEL_FORCE = 0x800 /* /F */
66 };
67
68 enum
69 {
70 ATTR_ARCHIVE = 0x001, /* /A:A */
71 ATTR_HIDDEN = 0x002, /* /A:H */
72 ATTR_SYSTEM = 0x004, /* /A:S */
73 ATTR_READ_ONLY = 0x008, /* /A:R */
74 ATTR_N_ARCHIVE = 0x010, /* /A:-A */
75 ATTR_N_HIDDEN = 0x020, /* /A:-H */
76 ATTR_N_SYSTEM = 0x040, /* /A:-S */
77 ATTR_N_READ_ONLY = 0x080 /* /A:-R */
78 };
79
80 static TCHAR szDeleteWipe[RC_STRING_MAX_SIZE];
81 static TCHAR szDelHelp2[RC_STRING_MAX_SIZE];
82 static TCHAR szDelHelp3[RC_STRING_MAX_SIZE];
83 static TCHAR szDelHelp4[RC_STRING_MAX_SIZE];
84 static TCHAR szDelError5[RC_STRING_MAX_SIZE];
85 static TCHAR szDelError6[RC_STRING_MAX_SIZE];
86 static TCHAR szDelError7[RC_STRING_MAX_SIZE];
87 static TCHAR CMDPath[MAX_PATH];
88
89 static BOOLEAN StringsLoaded = FALSE;
90
91 static VOID LoadStrings(VOID)
92 {
93 LoadString( CMD_ModuleHandle, STRING_DELETE_WIPE, szDeleteWipe, RC_STRING_MAX_SIZE);
94 LoadString( CMD_ModuleHandle, STRING_DEL_HELP2, szDelHelp2, RC_STRING_MAX_SIZE);
95 LoadString( CMD_ModuleHandle, STRING_DEL_HELP3, szDelHelp3, RC_STRING_MAX_SIZE);
96 LoadString( CMD_ModuleHandle, STRING_DEL_HELP4, szDelHelp4, RC_STRING_MAX_SIZE);
97 LoadString( CMD_ModuleHandle, STRING_DEL_ERROR5, szDelError5, RC_STRING_MAX_SIZE);
98 LoadString( CMD_ModuleHandle, STRING_DEL_ERROR6, szDelError6, RC_STRING_MAX_SIZE);
99 LoadString( CMD_ModuleHandle, STRING_DEL_ERROR7, szDelError7, RC_STRING_MAX_SIZE);
100 GetModuleFileName(NULL, CMDPath, MAX_PATH);
101 StringsLoaded = TRUE;
102 }
103
104 static BOOL
105 RemoveFile (LPTSTR lpFileName, DWORD dwFlags, WIN32_FIND_DATA* f)
106 {
107 /*This function is called by CommandDelete and
108 does the actual process of deleting the single
109 file*/
110 if(CheckCtrlBreak(BREAK_INPUT))
111 return 1;
112
113 /*check to see if it is read only and if this is done based on /A
114 if it is done by file name, access is denied. However, if it is done
115 using the /A switch you must un-read only the file and allow it to be
116 deleted*/
117 if((dwFlags & DEL_ATTRIBUTES) || (dwFlags & DEL_FORCE))
118 {
119 if(f->dwFileAttributes & FILE_ATTRIBUTE_READONLY)
120 {
121 /*setting file to normal, not saving old attrs first
122 because the file is going to be deleted anyways
123 so the only thing that matters is that it isnt
124 read only.*/
125 SetFileAttributes(lpFileName,FILE_ATTRIBUTE_NORMAL);
126 }
127 }
128
129 if (dwFlags & DEL_WIPE)
130 {
131
132 HANDLE file;
133 DWORD temp;
134 #define BufferSize 65536
135 BYTE buffer[BufferSize];
136 LONGLONG i;
137 LARGE_INTEGER FileSize;
138
139 FileSize.u.HighPart = f->nFileSizeHigh;
140 FileSize.u.LowPart = f->nFileSizeLow;
141
142 for(i = 0; i < BufferSize; i++)
143 {
144 buffer[i]=rand() % 256;
145 }
146 file = CreateFile (lpFileName, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH, NULL);
147 if (file != INVALID_HANDLE_VALUE)
148 {
149 for(i = 0; i < (FileSize.QuadPart - BufferSize); i += BufferSize)
150 {
151 WriteFile (file, buffer, BufferSize, &temp, NULL);
152 ConOutPrintf (_T("%I64d%% %s\r"),(i * (LONGLONG)100)/FileSize.QuadPart,szDeleteWipe);
153 }
154 WriteFile (file, buffer, (DWORD)(FileSize.QuadPart - i), &temp, NULL);
155 ConOutPrintf (_T("100%% %s\n"),szDeleteWipe);
156 CloseHandle (file);
157 }
158 }
159
160 return DeleteFile (lpFileName);
161 }
162
163
164 static DWORD
165 DeleteFiles(LPTSTR FileName, DWORD* dwFlags, DWORD dwAttrFlags)
166 {
167 TCHAR szFullPath[MAX_PATH];
168 TCHAR szFileName[MAX_PATH];
169 LPTSTR pFilePart;
170 HANDLE hFile;
171 WIN32_FIND_DATA f;
172 BOOL bExclusion;
173 INT res;
174 DWORD dwFiles = 0;
175
176 _tcscpy(szFileName, FileName);
177
178 if(_tcschr (szFileName, _T('*')) == NULL &&
179 IsExistingDirectory (szFileName))
180 {
181 /* If it doesnt have a \ at the end already then on needs to be added */
182 if(szFileName[_tcslen(szFileName) - 1] != _T('\\'))
183 _tcscat (szFileName, _T("\\"));
184 /* Add a wildcard after the \ */
185 _tcscat (szFileName, _T("*"));
186 }
187
188 if(!_tcscmp (szFileName, _T("*")) ||
189 !_tcscmp (szFileName, _T("*.*")) ||
190 (szFileName[_tcslen(szFileName) - 2] == _T('\\') && szFileName[_tcslen(szFileName) - 1] == _T('*')))
191 {
192 /* well, the user wants to delete everything but if they didnt yes DEL_YES, DEL_QUIET, or DEL_PROMPT
193 then we are going to want to make sure that in fact they want to do that. */
194
195 if (!((*dwFlags & DEL_YES) || (*dwFlags & DEL_QUIET) || (*dwFlags & DEL_PROMPT)))
196 {
197 res = FilePromptYNA (szDelHelp2);
198 if ((res == PROMPT_NO) || (res == PROMPT_BREAK))
199 return 0x80000000;
200 if(res == PROMPT_ALL)
201 *dwFlags |= DEL_YES;
202 }
203 }
204
205 GetFullPathName (szFileName,
206 MAX_PATH,
207 szFullPath,
208 &pFilePart);
209
210 hFile = FindFirstFile(szFullPath, &f);
211 if (hFile != INVALID_HANDLE_VALUE)
212 {
213 do
214 {
215 bExclusion = FALSE;
216
217 /*if it is going to be excluded by - no need to check attrs*/
218 if(*dwFlags & DEL_ATTRIBUTES && !bExclusion)
219 {
220
221 /*save if file attr check if user doesnt care about that attr anyways*/
222 if(dwAttrFlags & ATTR_ARCHIVE && !(f.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE))
223 bExclusion = TRUE;
224 if(dwAttrFlags & ATTR_HIDDEN && !(f.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN))
225 bExclusion = TRUE;
226 if(dwAttrFlags & ATTR_SYSTEM && !(f.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM))
227 bExclusion = TRUE;
228 if(dwAttrFlags & ATTR_READ_ONLY && !(f.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
229 bExclusion = TRUE;
230 if(dwAttrFlags & ATTR_N_ARCHIVE && (f.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE))
231 bExclusion = TRUE;
232 if(dwAttrFlags & ATTR_N_HIDDEN && (f.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN))
233 bExclusion = TRUE;
234 if(dwAttrFlags & ATTR_N_SYSTEM && (f.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM))
235 bExclusion = TRUE;
236 if(dwAttrFlags & ATTR_N_READ_ONLY && (f.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
237 bExclusion = TRUE;
238 }
239
240 if(bExclusion)
241 continue;
242
243 /* ignore directories */
244 if (f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
245 continue;
246
247
248 _tcscpy (pFilePart, f.cFileName);
249
250 /* We cant delete ourselves */
251 if(!_tcscmp (CMDPath,szFullPath))
252 continue;
253
254
255 #ifdef _DEBUG
256 ConErrPrintf(_T("Full filename: %s\n"), szFullPath);
257 #endif
258
259 /* ask for deleting */
260 if (*dwFlags & DEL_PROMPT)
261 {
262 ConErrPrintf(szDelError5, szFullPath);
263
264 res = FilePromptYN (szDelError6);
265
266 if ((res == PROMPT_NO) || (res == PROMPT_BREAK))
267 {
268 nErrorLevel = 0;
269 continue;
270 }
271 }
272
273 /*user cant ask it to be quiet and tell you what it did*/
274 if (!(*dwFlags & DEL_QUIET) && !(*dwFlags & DEL_TOTAL))
275 {
276 ConErrPrintf(szDelError7, szFullPath);
277 }
278
279 /* delete the file */
280 if(*dwFlags & DEL_NOTHING)
281 continue;
282
283 if(RemoveFile (szFullPath, *dwFlags, &f))
284 dwFiles++;
285 else
286 {
287 ErrorMessage (GetLastError(), _T(""));
288 // FindClose(hFile);
289 // return -1;
290 }
291 }
292 while (FindNextFile (hFile, &f));
293 FindClose (hFile);
294 }
295 return dwFiles;
296 }
297
298
299 static DWORD
300 ProcessDirectory(LPTSTR FileName, DWORD* dwFlags, DWORD dwAttrFlags)
301 {
302 TCHAR szFullPath[MAX_PATH];
303 LPTSTR pFilePart;
304 LPTSTR pSearchPart;
305 HANDLE hFile;
306 WIN32_FIND_DATA f;
307 DWORD dwFiles = 0;
308
309 GetFullPathName (FileName,
310 MAX_PATH,
311 szFullPath,
312 &pFilePart);
313
314 dwFiles = DeleteFiles(szFullPath, dwFlags, dwAttrFlags);
315 if (dwFiles & 0x80000000)
316 return dwFiles;
317
318 if (*dwFlags & DEL_SUBDIR)
319 {
320 /* Get just the file name */
321 pSearchPart = _tcsrchr(FileName,_T('\\'));
322 if(pSearchPart != NULL)
323 pSearchPart++;
324 else
325 pSearchPart = FileName;
326
327 /* Get the full path to the file */
328 GetFullPathName (FileName,MAX_PATH,szFullPath,NULL);
329
330 /* strip the filename off of it */
331 pFilePart = _tcsrchr(szFullPath, _T('\\'));
332 if (pFilePart == NULL)
333 {
334 pFilePart = szFullPath;
335 }
336 else
337 {
338 pFilePart++;
339 }
340
341 _tcscpy(pFilePart, _T("*"));
342
343 hFile = FindFirstFile(szFullPath, &f);
344 if (hFile != INVALID_HANDLE_VALUE)
345 {
346 do
347 {
348 if (!(f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
349 !_tcscmp(f.cFileName, _T(".")) ||
350 !_tcscmp(f.cFileName, _T("..")))
351 continue;
352
353 _tcscpy(pFilePart, f.cFileName);
354 _tcscat(pFilePart, _T("\\"));
355 _tcscat(pFilePart, pSearchPart);
356
357 dwFiles +=ProcessDirectory(szFullPath, dwFlags, dwAttrFlags);
358 if (dwFiles & 0x80000000)
359 {
360 break;
361 }
362 }
363 while (FindNextFile (hFile, &f));
364 FindClose (hFile);
365 }
366 }
367 return dwFiles;
368 }
369
370
371
372 INT CommandDelete (LPTSTR cmd, LPTSTR param)
373 {
374 /*cmd is the command that was given, in this case it will always be "del" or "delete"
375 param is whatever is given after the command*/
376
377 LPTSTR *arg = NULL;
378 INT args;
379 INT i;
380 INT nEvalArgs = 0; /* nunber of evaluated arguments */
381 DWORD dwFlags = 0;
382 DWORD dwAttrFlags = 0;
383 DWORD dwFiles = 0;
384 LONG ch;
385 TCHAR szOrginalArg[MAX_PATH];
386
387 /*checks the first two chars of param to see if it is /?
388 this however allows the following command to not show help
389 "del frog.txt /?" */
390
391 if (!StringsLoaded)
392 {
393 LoadStrings();
394 }
395
396 if (!_tcsncmp (param, _T("/?"), 2))
397 {
398 ConOutResPaging(TRUE,STRING_DEL_HELP1);
399 return 0;
400 }
401
402 nErrorLevel = 0;
403
404 arg = split (param, &args, FALSE);
405
406 if (args == 0)
407 {
408 /* only command given */
409 error_req_param_missing ();
410 freep (arg);
411 return 1;
412 }
413 /* check for options anywhere in command line */
414 for (i = 0; i < args; i++)
415 {
416 if (*arg[i] == _T('/'))
417 {
418 /*found a command, but check to make sure it has something after it*/
419 if (_tcslen (arg[i]) >= 2)
420 {
421 ch = _totupper (arg[i][1]);
422 if (ch == _T('N'))
423 {
424 dwFlags |= DEL_NOTHING;
425 }
426 else if (ch == _T('P'))
427 {
428 dwFlags |= DEL_PROMPT;
429 }
430 else if (ch == _T('Q'))
431 {
432 dwFlags |= DEL_QUIET;
433 }
434 else if (ch == _T('F'))
435 {
436 dwFlags |= DEL_FORCE;
437 }
438 else if (ch == _T('S'))
439 {
440 dwFlags |= DEL_SUBDIR;
441 }
442 else if (ch == _T('T'))
443 {
444 dwFlags |= DEL_TOTAL;
445 }
446 else if (ch == _T('W'))
447 {
448 dwFlags |= DEL_WIPE;
449 }
450 else if (ch == _T('Y'))
451 {
452 dwFlags |= DEL_YES;
453 }
454 else if (ch == _T('A'))
455 {
456
457 dwFlags |= DEL_ATTRIBUTES;
458 /*the proper syntax for /A has a min of 4 chars
459 i.e. /A:R or /A:-H */
460 if (_tcslen (arg[i]) < 4)
461 {
462 error_invalid_parameter_format(arg[i]);
463 return 0;
464 }
465 ch = _totupper (arg[i][3]);
466 if (_tcslen (arg[i]) == 4)
467 {
468 if(ch == _T('A'))
469 {
470 dwAttrFlags |= ATTR_ARCHIVE;
471 }
472 if(ch == _T('H'))
473 {
474 dwAttrFlags |= ATTR_HIDDEN;
475 }
476 if(ch == _T('S'))
477 {
478 dwAttrFlags |= ATTR_SYSTEM;
479 }
480 if(ch == _T('R'))
481 {
482 dwAttrFlags |= ATTR_READ_ONLY;
483 }
484 }
485 if (_tcslen (arg[i]) == 5)
486 {
487 if(ch == _T('-'))
488 {
489 ch = _totupper (arg[i][4]);
490 if(ch == _T('A'))
491 {
492 dwAttrFlags |= ATTR_N_ARCHIVE;
493 }
494 if(ch == _T('H'))
495 {
496 dwAttrFlags |= ATTR_N_HIDDEN;
497 }
498 if(ch == _T('S'))
499 {
500 dwAttrFlags |= ATTR_N_SYSTEM;
501 }
502 if(ch == _T('R'))
503 {
504 dwAttrFlags |= ATTR_N_READ_ONLY;
505 }
506 }
507 }
508 }
509 }
510
511 nEvalArgs++;
512 }
513 }
514
515 /* there are only options on the command line --> error!!!
516 there is the same number of args as there is flags, so none of the args were filenames*/
517 if (args == nEvalArgs)
518 {
519 error_req_param_missing ();
520 freep (arg);
521 return 1;
522 }
523
524 /* keep quiet within batch files */
525 if (bc != NULL)
526 dwFlags |= DEL_QUIET;
527
528 /* check for filenames anywhere in command line */
529 for (i = 0; i < args && !(dwFiles & 0x80000000); i++)
530 {
531
532 /*this checks to see if it isnt a flag, if it isnt, we assume it is a file name*/
533 if((*arg[i] == _T('/')) || (*arg[i] == _T('-')))
534 continue;
535
536 /* We want to make a copies of the argument */
537 if(_tcslen(arg[i]) == 2 && arg[i][1] == _T(':'))
538 {
539 /* Check for C: D: ... */
540 GetRootPath(arg[i],szOrginalArg,MAX_PATH);
541 }
542 else
543 {
544 _tcscpy(szOrginalArg,arg[i]);
545 }
546 dwFiles += ProcessDirectory(szOrginalArg, &dwFlags, dwAttrFlags);
547
548 }
549
550 freep (arg);
551
552 /*Based on MS cmd, we only tell what files are being deleted when /S is used */
553 if (dwFlags & DEL_TOTAL)
554 {
555 dwFiles &= 0x7fffffff;
556 if (dwFiles < 2)
557 {
558 ConOutPrintf(szDelHelp3, dwFiles);
559 }
560 else
561 {
562 ConOutPrintf(szDelHelp4, dwFiles);
563 }
564 }
565
566 return 0;
567 }
568
569
570 #endif