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 szDelHelp2
[RC_STRING_MAX_SIZE
];
81 static TCHAR szDelHelp3
[RC_STRING_MAX_SIZE
];
82 static TCHAR szDelHelp4
[RC_STRING_MAX_SIZE
];
83 static TCHAR szDelError5
[RC_STRING_MAX_SIZE
];
84 static TCHAR szDelError6
[RC_STRING_MAX_SIZE
];
85 static TCHAR szDelError7
[RC_STRING_MAX_SIZE
];
86 static TCHAR CMDPath
[MAX_PATH
];
88 static BOOLEAN StringsLoaded
= FALSE
;
90 static VOID
LoadStrings(VOID
)
92 LoadString( CMD_ModuleHandle
, STRING_DELETE_WIPE
, szDeleteWipe
, RC_STRING_MAX_SIZE
);
93 LoadString( CMD_ModuleHandle
, STRING_DEL_HELP2
, szDelHelp2
, RC_STRING_MAX_SIZE
);
94 LoadString( CMD_ModuleHandle
, STRING_DEL_HELP3
, szDelHelp3
, RC_STRING_MAX_SIZE
);
95 LoadString( CMD_ModuleHandle
, STRING_DEL_HELP4
, szDelHelp4
, RC_STRING_MAX_SIZE
);
96 LoadString( CMD_ModuleHandle
, STRING_DEL_ERROR5
, szDelError5
, RC_STRING_MAX_SIZE
);
97 LoadString( CMD_ModuleHandle
, STRING_DEL_ERROR6
, szDelError6
, RC_STRING_MAX_SIZE
);
98 LoadString( CMD_ModuleHandle
, STRING_DEL_ERROR7
, szDelError7
, RC_STRING_MAX_SIZE
);
99 GetModuleFileName(NULL
, CMDPath
, MAX_PATH
);
100 StringsLoaded
= TRUE
;
104 RemoveFile (LPTSTR lpFileName
, DWORD dwFlags
, WIN32_FIND_DATA
* f
)
106 /*This function is called by CommandDelete and
107 does the actual process of deleting the single
109 if(CheckCtrlBreak(BREAK_INPUT
))
112 /*check to see if it is read only and if this is done based on /A
113 if it is done by file name, access is denied. However, if it is done
114 using the /A switch you must un-read only the file and allow it to be
116 if((dwFlags
& DEL_ATTRIBUTES
) || (dwFlags
& DEL_FORCE
))
118 if(f
->dwFileAttributes
& FILE_ATTRIBUTE_READONLY
)
120 /*setting file to normal, not saving old attrs first
121 because the file is going to be deleted anyways
122 so the only thing that matters is that it isnt
124 SetFileAttributes(lpFileName
,FILE_ATTRIBUTE_NORMAL
);
128 if (dwFlags
& DEL_WIPE
)
133 #define BufferSize 65536
134 BYTE buffer
[BufferSize
];
136 LARGE_INTEGER FileSize
;
138 FileSize
.u
.HighPart
= f
->nFileSizeHigh
;
139 FileSize
.u
.LowPart
= f
->nFileSizeLow
;
141 for(i
= 0; i
< BufferSize
; i
++)
143 buffer
[i
]=rand() % 256;
145 file
= CreateFile (lpFileName
, GENERIC_WRITE
, 0, NULL
, OPEN_EXISTING
, FILE_FLAG_WRITE_THROUGH
, NULL
);
146 if (file
!= INVALID_HANDLE_VALUE
)
148 for(i
= 0; i
< (FileSize
.QuadPart
- BufferSize
); i
+= BufferSize
)
150 WriteFile (file
, buffer
, BufferSize
, &temp
, NULL
);
151 ConOutPrintf (_T("%I64d%% %s\r"),(i
* (LONGLONG
)100)/FileSize
.QuadPart
,szDeleteWipe
);
153 WriteFile (file
, buffer
, (DWORD
)(FileSize
.QuadPart
- i
), &temp
, NULL
);
154 ConOutPrintf (_T("100%% %s\n"),szDeleteWipe
);
159 return DeleteFile (lpFileName
);
164 DeleteFiles(LPTSTR FileName
, DWORD
* dwFlags
, DWORD dwAttrFlags
)
166 TCHAR szFullPath
[MAX_PATH
];
167 TCHAR szFileName
[MAX_PATH
];
175 _tcscpy(szFileName
, FileName
);
177 if(_tcschr (szFileName
, _T('*')) == NULL
&&
178 IsExistingDirectory (szFileName
))
180 /* If it doesnt have a \ at the end already then on needs to be added */
181 if(szFileName
[_tcslen(szFileName
) - 1] != _T('\\'))
182 _tcscat (szFileName
, _T("\\"));
183 /* Add a wildcard after the \ */
184 _tcscat (szFileName
, _T("*"));
187 if(!_tcscmp (szFileName
, _T("*")) ||
188 !_tcscmp (szFileName
, _T("*.*")) ||
189 (szFileName
[_tcslen(szFileName
) - 2] == _T('\\') && szFileName
[_tcslen(szFileName
) - 1] == _T('*')))
191 /* well, the user wants to delete everything but if they didnt yes DEL_YES, DEL_QUIET, or DEL_PROMPT
192 then we are going to want to make sure that in fact they want to do that. */
194 if (!((*dwFlags
& DEL_YES
) || (*dwFlags
& DEL_QUIET
) || (*dwFlags
& DEL_PROMPT
)))
196 res
= FilePromptYNA (szDelHelp2
);
197 if ((res
== PROMPT_NO
) || (res
== PROMPT_BREAK
))
199 if(res
== PROMPT_ALL
)
204 GetFullPathName (szFileName
,
209 hFile
= FindFirstFile(szFullPath
, &f
);
210 if (hFile
!= INVALID_HANDLE_VALUE
)
216 /*if it is going to be excluded by - no need to check attrs*/
217 if(*dwFlags
& DEL_ATTRIBUTES
&& !bExclusion
)
220 /*save if file attr check if user doesnt care about that attr anyways*/
221 if(dwAttrFlags
& ATTR_ARCHIVE
&& !(f
.dwFileAttributes
& FILE_ATTRIBUTE_ARCHIVE
))
223 if(dwAttrFlags
& ATTR_HIDDEN
&& !(f
.dwFileAttributes
& FILE_ATTRIBUTE_HIDDEN
))
225 if(dwAttrFlags
& ATTR_SYSTEM
&& !(f
.dwFileAttributes
& FILE_ATTRIBUTE_SYSTEM
))
227 if(dwAttrFlags
& ATTR_READ_ONLY
&& !(f
.dwFileAttributes
& FILE_ATTRIBUTE_READONLY
))
229 if(dwAttrFlags
& ATTR_N_ARCHIVE
&& (f
.dwFileAttributes
& FILE_ATTRIBUTE_ARCHIVE
))
231 if(dwAttrFlags
& ATTR_N_HIDDEN
&& (f
.dwFileAttributes
& FILE_ATTRIBUTE_HIDDEN
))
233 if(dwAttrFlags
& ATTR_N_SYSTEM
&& (f
.dwFileAttributes
& FILE_ATTRIBUTE_SYSTEM
))
235 if(dwAttrFlags
& ATTR_N_READ_ONLY
&& (f
.dwFileAttributes
& FILE_ATTRIBUTE_READONLY
))
242 /* ignore directories */
243 if (f
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
247 _tcscpy (pFilePart
, f
.cFileName
);
249 /* We cant delete ourselves */
250 if(!_tcscmp (CMDPath
,szFullPath
))
254 TRACE("Full filename: %s\n", debugstr_aw(szFullPath
));
256 /* ask for deleting */
257 if (*dwFlags
& DEL_PROMPT
)
259 ConErrPrintf(szDelError5
, szFullPath
);
261 res
= FilePromptYN (szDelError6
);
263 if ((res
== PROMPT_NO
) || (res
== PROMPT_BREAK
))
270 /*user cant ask it to be quiet and tell you what it did*/
271 if (!(*dwFlags
& DEL_QUIET
) && !(*dwFlags
& DEL_TOTAL
))
273 ConErrPrintf(szDelError7
, szFullPath
);
276 /* delete the file */
277 if(*dwFlags
& DEL_NOTHING
)
280 if(RemoveFile (szFullPath
, *dwFlags
, &f
))
284 ErrorMessage (GetLastError(), _T(""));
289 while (FindNextFile (hFile
, &f
));
297 ProcessDirectory(LPTSTR FileName
, DWORD
* dwFlags
, DWORD dwAttrFlags
)
299 TCHAR szFullPath
[MAX_PATH
];
306 GetFullPathName (FileName
,
311 dwFiles
= DeleteFiles(szFullPath
, dwFlags
, dwAttrFlags
);
312 if (dwFiles
& 0x80000000)
315 if (*dwFlags
& DEL_SUBDIR
)
317 /* Get just the file name */
318 pSearchPart
= _tcsrchr(FileName
,_T('\\'));
319 if(pSearchPart
!= NULL
)
322 pSearchPart
= FileName
;
324 /* Get the full path to the file */
325 GetFullPathName (FileName
,MAX_PATH
,szFullPath
,NULL
);
327 /* strip the filename off of it */
328 pFilePart
= _tcsrchr(szFullPath
, _T('\\'));
329 if (pFilePart
== NULL
)
331 pFilePart
= szFullPath
;
338 _tcscpy(pFilePart
, _T("*"));
340 hFile
= FindFirstFile(szFullPath
, &f
);
341 if (hFile
!= INVALID_HANDLE_VALUE
)
345 if (!(f
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) ||
346 !_tcscmp(f
.cFileName
, _T(".")) ||
347 !_tcscmp(f
.cFileName
, _T("..")))
350 _tcscpy(pFilePart
, f
.cFileName
);
351 _tcscat(pFilePart
, _T("\\"));
352 _tcscat(pFilePart
, pSearchPart
);
354 dwFiles
+=ProcessDirectory(szFullPath
, dwFlags
, dwAttrFlags
);
355 if (dwFiles
& 0x80000000)
360 while (FindNextFile (hFile
, &f
));
369 INT
CommandDelete (LPTSTR cmd
, LPTSTR param
)
371 /*cmd is the command that was given, in this case it will always be "del" or "delete"
372 param is whatever is given after the command*/
377 INT nEvalArgs
= 0; /* nunber of evaluated arguments */
379 DWORD dwAttrFlags
= 0;
382 TCHAR szOrginalArg
[MAX_PATH
];
384 /*checks the first two chars of param to see if it is /?
385 this however allows the following command to not show help
393 if (!_tcsncmp (param
, _T("/?"), 2))
395 ConOutResPaging(TRUE
,STRING_DEL_HELP1
);
401 arg
= split (param
, &args
, FALSE
);
405 /* only command given */
406 error_req_param_missing ();
410 /* check for options anywhere in command line */
411 for (i
= 0; i
< args
; i
++)
413 if (*arg
[i
] == _T('/'))
415 /*found a command, but check to make sure it has something after it*/
416 if (_tcslen (arg
[i
]) >= 2)
418 ch
= _totupper (arg
[i
][1]);
421 dwFlags
|= DEL_NOTHING
;
423 else if (ch
== _T('P'))
425 dwFlags
|= DEL_PROMPT
;
427 else if (ch
== _T('Q'))
429 dwFlags
|= DEL_QUIET
;
431 else if (ch
== _T('F'))
433 dwFlags
|= DEL_FORCE
;
435 else if (ch
== _T('S'))
437 dwFlags
|= DEL_SUBDIR
;
439 else if (ch
== _T('T'))
441 dwFlags
|= DEL_TOTAL
;
443 else if (ch
== _T('W'))
447 else if (ch
== _T('Y'))
451 else if (ch
== _T('A'))
454 dwFlags
|= DEL_ATTRIBUTES
;
455 /*the proper syntax for /A has a min of 4 chars
456 i.e. /A:R or /A:-H */
457 if (_tcslen (arg
[i
]) < 4)
459 error_invalid_parameter_format(arg
[i
]);
462 ch
= _totupper (arg
[i
][3]);
463 if (_tcslen (arg
[i
]) == 4)
467 dwAttrFlags
|= ATTR_ARCHIVE
;
471 dwAttrFlags
|= ATTR_HIDDEN
;
475 dwAttrFlags
|= ATTR_SYSTEM
;
479 dwAttrFlags
|= ATTR_READ_ONLY
;
482 if (_tcslen (arg
[i
]) == 5)
486 ch
= _totupper (arg
[i
][4]);
489 dwAttrFlags
|= ATTR_N_ARCHIVE
;
493 dwAttrFlags
|= ATTR_N_HIDDEN
;
497 dwAttrFlags
|= ATTR_N_SYSTEM
;
501 dwAttrFlags
|= ATTR_N_READ_ONLY
;
512 /* there are only options on the command line --> error!!!
513 there is the same number of args as there is flags, so none of the args were filenames*/
514 if (args
== nEvalArgs
)
516 error_req_param_missing ();
521 /* keep quiet within batch files */
523 dwFlags
|= DEL_QUIET
;
525 /* check for filenames anywhere in command line */
526 for (i
= 0; i
< args
&& !(dwFiles
& 0x80000000); i
++)
529 /*this checks to see if it isnt a flag, if it isnt, we assume it is a file name*/
530 if((*arg
[i
] == _T('/')) || (*arg
[i
] == _T('-')))
533 /* We want to make a copies of the argument */
534 if(_tcslen(arg
[i
]) == 2 && arg
[i
][1] == _T(':'))
536 /* Check for C: D: ... */
537 GetRootPath(arg
[i
],szOrginalArg
,MAX_PATH
);
541 _tcscpy(szOrginalArg
,arg
[i
]);
543 dwFiles
+= ProcessDirectory(szOrginalArg
, &dwFlags
, dwAttrFlags
);
549 /*Based on MS cmd, we only tell what files are being deleted when /S is used */
550 if (dwFlags
& DEL_TOTAL
)
552 dwFiles
&= 0x7fffffff;
555 ConOutPrintf(szDelHelp3
, dwFiles
);
559 ConOutPrintf(szDelHelp4
, dwFiles
);