2 * DEL.C - del internal command.
7 * 06/29/98 (Rob Lake rlake@cs.mun.ca)
8 * rewrote del to support wildcards
9 * added my name to the contributors
12 * fixed bug that caused del not to delete file with out
13 * attribute. moved set, del, ren, and ver to there own files
15 * 27-Jul-1998 (John P Price <linux-guru@gcfl.net>)
16 * added config.h include
18 * 09-Dec-1998 (Eric Kohl)
19 * Fixed command line parsing bugs.
21 * 21-Jan-1999 (Eric Kohl)
22 * Started major rewrite using a new structure.
24 * 03-Feb-1999 (Eric Kohl)
25 * First working version.
27 * 30-Mar-1999 (Eric Kohl)
28 * Added quiet ("/Q"), wipe ("/W") and zap ("/Z") option.
30 * 06-Nov-1999 (Eric Kohl)
31 * Little fix to keep DEL quiet inside batch files.
33 * 28-Jan-2004 (Michael Fritscher <michael@fritscher.net>)
34 * Added prompt ("/P"), yes ("/Y") and wipe("/W") option.
36 * 22-Jun-2005 (Brandon Turner <turnerb7@msu.edu>)
37 * Added exclusive deletion "del * -abc.txt -text*.txt"
39 * 22-Jun-2005 (Brandon Turner <turnerb7@msu.edu>)
40 * Implemented /A example "del /A:H /A:-R *.exe -ping.exe"
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.
50 #ifdef INCLUDE_CMD_DEL
55 DEL_ATTRIBUTES
= 0x001, /* /A */
56 DEL_NOTHING
= 0x004, /* /N */
57 DEL_PROMPT
= 0x008, /* /P */
58 DEL_QUIET
= 0x010, /* /Q */
59 DEL_SUBDIR
= 0x020, /* /S */
60 DEL_TOTAL
= 0x040, /* /T */
61 DEL_WIPE
= 0x080, /* /W */
62 DEL_EMPTYDIR
= 0x100, /* /X : not implemented */
63 DEL_YES
= 0x200, /* /Y */
64 DEL_FORCE
= 0x800 /* /F */
69 ATTR_ARCHIVE
= 0x001, /* /A:A */
70 ATTR_HIDDEN
= 0x002, /* /A:H */
71 ATTR_SYSTEM
= 0x004, /* /A:S */
72 ATTR_READ_ONLY
= 0x008, /* /A:R */
73 ATTR_N_ARCHIVE
= 0x010, /* /A:-A */
74 ATTR_N_HIDDEN
= 0x020, /* /A:-H */
75 ATTR_N_SYSTEM
= 0x040, /* /A:-S */
76 ATTR_N_READ_ONLY
= 0x080 /* /A:-R */
79 static TCHAR szDeleteWipe
[RC_STRING_MAX_SIZE
];
80 static TCHAR CMDPath
[MAX_PATH
];
82 static BOOLEAN StringsLoaded
= FALSE
;
84 static VOID
LoadStrings(VOID
)
86 LoadString(CMD_ModuleHandle
, STRING_DELETE_WIPE
, szDeleteWipe
, ARRAYSIZE(szDeleteWipe
));
87 GetModuleFileName(NULL
, CMDPath
, ARRAYSIZE(CMDPath
));
92 RemoveFile (LPTSTR lpFileName
, DWORD dwFlags
, WIN32_FIND_DATA
* f
)
94 /*This function is called by CommandDelete and
95 does the actual process of deleting the single
97 if (CheckCtrlBreak(BREAK_INPUT
))
100 /*check to see if it is read only and if this is done based on /A
101 if it is done by file name, access is denied. However, if it is done
102 using the /A switch you must un-read only the file and allow it to be
104 if ((dwFlags
& DEL_ATTRIBUTES
) || (dwFlags
& DEL_FORCE
))
106 if (f
->dwFileAttributes
& FILE_ATTRIBUTE_READONLY
)
108 /*setting file to normal, not saving old attrs first
109 because the file is going to be deleted anyways
110 so the only thing that matters is that it isn't
112 SetFileAttributes(lpFileName
,FILE_ATTRIBUTE_NORMAL
);
116 if (dwFlags
& DEL_WIPE
)
120 #define BufferSize 65536
121 BYTE buffer
[BufferSize
];
123 LARGE_INTEGER FileSize
;
125 FileSize
.u
.HighPart
= f
->nFileSizeHigh
;
126 FileSize
.u
.LowPart
= f
->nFileSizeLow
;
128 for(i
= 0; i
< BufferSize
; i
++)
130 buffer
[i
]=rand() % 256;
132 file
= CreateFile (lpFileName
, GENERIC_WRITE
, 0, NULL
, OPEN_EXISTING
, FILE_FLAG_WRITE_THROUGH
, NULL
);
133 if (file
!= INVALID_HANDLE_VALUE
)
135 for(i
= 0; i
< (FileSize
.QuadPart
- BufferSize
); i
+= BufferSize
)
137 WriteFile (file
, buffer
, BufferSize
, &temp
, NULL
);
138 ConOutPrintf (_T("%I64d%% %s\r"),(i
* (LONGLONG
)100)/FileSize
.QuadPart
,szDeleteWipe
);
140 WriteFile (file
, buffer
, (DWORD
)(FileSize
.QuadPart
- i
), &temp
, NULL
);
141 ConOutPrintf (_T("100%% %s\n"),szDeleteWipe
);
146 return DeleteFile (lpFileName
);
151 DeleteFiles(LPTSTR FileName
, DWORD
* dwFlags
, DWORD dwAttrFlags
)
153 TCHAR szFullPath
[MAX_PATH
];
154 TCHAR szFileName
[MAX_PATH
];
162 _tcscpy(szFileName
, FileName
);
164 if (_tcschr (szFileName
, _T('*')) == NULL
&&
165 IsExistingDirectory (szFileName
))
167 /* If it doesnt have a \ at the end already then on needs to be added */
168 if (szFileName
[_tcslen(szFileName
) - 1] != _T('\\'))
169 _tcscat (szFileName
, _T("\\"));
170 /* Add a wildcard after the \ */
171 _tcscat (szFileName
, _T("*"));
174 if (!_tcscmp (szFileName
, _T("*")) ||
175 !_tcscmp (szFileName
, _T("*.*")) ||
176 (szFileName
[_tcslen(szFileName
) - 2] == _T('\\') && szFileName
[_tcslen(szFileName
) - 1] == _T('*')))
178 /* well, the user wants to delete everything but if they didnt yes DEL_YES, DEL_QUIET, or DEL_PROMPT
179 then we are going to want to make sure that in fact they want to do that. */
181 if (!((*dwFlags
& DEL_YES
) || (*dwFlags
& DEL_QUIET
) || (*dwFlags
& DEL_PROMPT
)))
183 res
= FilePromptYNA (STRING_DEL_HELP2
);
184 if ((res
== PROMPT_NO
) || (res
== PROMPT_BREAK
))
186 if (res
== PROMPT_ALL
)
191 GetFullPathName(szFileName
,
196 hFile
= FindFirstFile(szFullPath
, &f
);
197 if (hFile
!= INVALID_HANDLE_VALUE
)
203 /*if it is going to be excluded by - no need to check attrs*/
204 if (*dwFlags
& DEL_ATTRIBUTES
&& !bExclusion
)
206 /*save if file attr check if user doesnt care about that attr anyways*/
207 if (dwAttrFlags
& ATTR_ARCHIVE
&& !(f
.dwFileAttributes
& FILE_ATTRIBUTE_ARCHIVE
))
209 if (dwAttrFlags
& ATTR_HIDDEN
&& !(f
.dwFileAttributes
& FILE_ATTRIBUTE_HIDDEN
))
211 if (dwAttrFlags
& ATTR_SYSTEM
&& !(f
.dwFileAttributes
& FILE_ATTRIBUTE_SYSTEM
))
213 if (dwAttrFlags
& ATTR_READ_ONLY
&& !(f
.dwFileAttributes
& FILE_ATTRIBUTE_READONLY
))
215 if (dwAttrFlags
& ATTR_N_ARCHIVE
&& (f
.dwFileAttributes
& FILE_ATTRIBUTE_ARCHIVE
))
217 if (dwAttrFlags
& ATTR_N_HIDDEN
&& (f
.dwFileAttributes
& FILE_ATTRIBUTE_HIDDEN
))
219 if (dwAttrFlags
& ATTR_N_SYSTEM
&& (f
.dwFileAttributes
& FILE_ATTRIBUTE_SYSTEM
))
221 if (dwAttrFlags
& ATTR_N_READ_ONLY
&& (f
.dwFileAttributes
& FILE_ATTRIBUTE_READONLY
))
224 if (bExclusion
) continue;
226 /* ignore directories */
227 if (f
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) continue;
229 _tcscpy (pFilePart
, f
.cFileName
);
231 /* We cant delete ourselves */
232 if (!_tcscmp (CMDPath
,szFullPath
)) continue;
234 TRACE("Full filename: %s\n", debugstr_aw(szFullPath
));
236 /* ask for deleting */
237 if (*dwFlags
& DEL_PROMPT
)
239 ConErrResPrintf(STRING_DEL_ERROR5
, szFullPath
);
241 res
= FilePromptYN (STRING_DEL_ERROR6
);
243 if ((res
== PROMPT_NO
) || (res
== PROMPT_BREAK
))
250 /*user cant ask it to be quiet and tell you what it did*/
251 if (!(*dwFlags
& DEL_QUIET
) && !(*dwFlags
& DEL_TOTAL
))
253 ConErrResPrintf(STRING_DEL_ERROR7
, szFullPath
);
256 /* delete the file */
257 if (*dwFlags
& DEL_NOTHING
) continue;
259 if (RemoveFile (szFullPath
, *dwFlags
, &f
))
265 ErrorMessage (GetLastError(), _T(""));
270 while (FindNextFile (hFile
, &f
));
273 else error_sfile_not_found(szFullPath
);
278 ProcessDirectory(LPTSTR FileName
, DWORD
* dwFlags
, DWORD dwAttrFlags
)
280 TCHAR szFullPath
[MAX_PATH
];
287 GetFullPathName(FileName
,
292 dwFiles
= DeleteFiles(szFullPath
, dwFlags
, dwAttrFlags
);
293 if (dwFiles
& 0x80000000) return dwFiles
;
295 if (*dwFlags
& DEL_SUBDIR
)
297 /* Get just the file name */
298 pSearchPart
= _tcsrchr(FileName
,_T('\\'));
299 if (pSearchPart
!= NULL
)
302 pSearchPart
= FileName
;
304 /* Get the full path to the file */
305 GetFullPathName (FileName
,MAX_PATH
,szFullPath
,NULL
);
307 /* strip the filename off of it */
308 pFilePart
= _tcsrchr(szFullPath
, _T('\\'));
309 if (pFilePart
== NULL
)
311 pFilePart
= szFullPath
;
318 _tcscpy(pFilePart
, _T("*"));
320 hFile
= FindFirstFile(szFullPath
, &f
);
321 if (hFile
!= INVALID_HANDLE_VALUE
)
325 if (!(f
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) ||
326 !_tcscmp(f
.cFileName
, _T(".")) ||
327 !_tcscmp(f
.cFileName
, _T("..")))
330 _tcscpy(pFilePart
, f
.cFileName
);
331 _tcscat(pFilePart
, _T("\\"));
332 _tcscat(pFilePart
, pSearchPart
);
334 dwFiles
+=ProcessDirectory(szFullPath
, dwFlags
, dwAttrFlags
);
335 if (dwFiles
& 0x80000000)
340 while (FindNextFile (hFile
, &f
));
349 INT
CommandDelete (LPTSTR param
)
351 /*cmd is the command that was given, in this case it will always be "del" or "delete"
352 param is whatever is given after the command*/
357 INT nEvalArgs
= 0; /* number of evaluated arguments */
359 DWORD dwAttrFlags
= 0;
362 TCHAR szOriginalArg
[MAX_PATH
];
364 /*checks the first two chars of param to see if it is /?
365 this however allows the following command to not show help
373 if (!_tcsncmp (param
, _T("/?"), 2))
375 ConOutResPaging(TRUE
,STRING_DEL_HELP1
);
381 arg
= split (param
, &args
, FALSE
, FALSE
);
385 /* only command given */
386 error_req_param_missing ();
390 /* check for options anywhere in command line */
391 for (i
= 0; i
< args
; i
++)
393 if (*arg
[i
] == _T('/'))
395 /*found a command, but check to make sure it has something after it*/
396 if (_tcslen (arg
[i
]) >= 2)
398 ch
= _totupper (arg
[i
][1]);
401 dwFlags
|= DEL_NOTHING
;
403 else if (ch
== _T('P'))
405 dwFlags
|= DEL_PROMPT
;
407 else if (ch
== _T('Q'))
409 dwFlags
|= DEL_QUIET
;
411 else if (ch
== _T('F'))
413 dwFlags
|= DEL_FORCE
;
415 else if (ch
== _T('S'))
417 dwFlags
|= DEL_SUBDIR
;
419 else if (ch
== _T('T'))
421 dwFlags
|= DEL_TOTAL
;
423 else if (ch
== _T('W'))
427 else if (ch
== _T('Y'))
431 else if (ch
== _T('A'))
433 dwFlags
|= DEL_ATTRIBUTES
;
434 /*the proper syntax for /A has a min of 4 chars
435 i.e. /A:R or /A:-H */
436 if (_tcslen (arg
[i
]) < 4)
438 error_invalid_parameter_format(arg
[i
]);
441 ch
= _totupper (arg
[i
][3]);
442 if (_tcslen (arg
[i
]) == 4)
446 dwAttrFlags
|= ATTR_ARCHIVE
;
450 dwAttrFlags
|= ATTR_HIDDEN
;
454 dwAttrFlags
|= ATTR_SYSTEM
;
458 dwAttrFlags
|= ATTR_READ_ONLY
;
461 if (_tcslen (arg
[i
]) == 5)
465 ch
= _totupper (arg
[i
][4]);
468 dwAttrFlags
|= ATTR_N_ARCHIVE
;
472 dwAttrFlags
|= ATTR_N_HIDDEN
;
476 dwAttrFlags
|= ATTR_N_SYSTEM
;
480 dwAttrFlags
|= ATTR_N_READ_ONLY
;
491 /* there are only options on the command line --> error!!!
492 there is the same number of args as there is flags, so none of the args were filenames*/
493 if (args
== nEvalArgs
)
495 error_req_param_missing ();
500 /* keep quiet within batch files */
501 if (bc
!= NULL
) dwFlags
|= DEL_QUIET
;
503 /* check for filenames anywhere in command line */
504 for (i
= 0; i
< args
&& !(dwFiles
& 0x80000000); i
++)
506 /*this checks to see if it is a flag; if it isn't, we assume it is a file name*/
507 if ((*arg
[i
] == _T('/')) || (*arg
[i
] == _T('-')))
510 /* We want to make a copies of the argument */
511 if (_tcslen(arg
[i
]) == 2 && arg
[i
][1] == _T(':'))
513 /* Check for C: D: ... */
514 GetRootPath(arg
[i
], szOriginalArg
, MAX_PATH
);
518 _tcscpy(szOriginalArg
,arg
[i
]);
520 dwFiles
+= ProcessDirectory(szOriginalArg
, &dwFlags
, dwAttrFlags
);
525 /*Based on MS cmd, we only tell what files are being deleted when /S is used */
526 if (dwFlags
& DEL_TOTAL
)
528 dwFiles
&= 0x7fffffff;
531 ConOutResPrintf(STRING_DEL_HELP3
, dwFiles
);
535 ConOutResPrintf(STRING_DEL_HELP4
, dwFiles
);