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 <ekohl@abo.rhein-zeiung.de>)
19 * Fixed command line parsing bugs.
21 * 21-Jan-1999 (Eric Kohl <ekohl@abo.rhein-zeiung.de>)
22 * Started major rewrite using a new structure.
24 * 03-Feb-1999 (Eric Kohl <ekohl@abo.rhein-zeiung.de>)
25 * First working version.
27 * 30-Mar-1999 (Eric Kohl <ekohl@abo.rhein-zeiung.de>)
28 * Added quiet ("/Q"), wipe ("/W") and zap ("/Z") option.
30 * 06-Nov-1999 (Eric Kohl <ekohl@abo.rhein-zeiung.de>)
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.
51 #ifdef INCLUDE_CMD_DEL
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 */
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 */
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
];
89 static BOOLEAN StringsLoaded
= FALSE
;
91 static VOID
LoadStrings(VOID
)
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
;
105 RemoveFile (LPTSTR lpFileName
, DWORD dwFlags
, WIN32_FIND_DATA
* f
)
107 /*This function is called by CommandDelete and
108 does the actual process of deleting the single
110 if(CheckCtrlBreak(BREAK_INPUT
))
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
117 if((dwFlags
& DEL_ATTRIBUTES
) || (dwFlags
& DEL_FORCE
))
119 if(f
->dwFileAttributes
& FILE_ATTRIBUTE_READONLY
)
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
125 SetFileAttributes(lpFileName
,FILE_ATTRIBUTE_NORMAL
);
129 if (dwFlags
& DEL_WIPE
)
134 #define BufferSize 65536
135 BYTE buffer
[BufferSize
];
137 LARGE_INTEGER FileSize
;
139 FileSize
.u
.HighPart
= f
->nFileSizeHigh
;
140 FileSize
.u
.LowPart
= f
->nFileSizeLow
;
142 for(i
= 0; i
< BufferSize
; i
++)
144 buffer
[i
]=rand() % 256;
146 file
= CreateFile (lpFileName
, GENERIC_WRITE
, 0, NULL
, OPEN_EXISTING
, FILE_FLAG_WRITE_THROUGH
, NULL
);
147 if (file
!= INVALID_HANDLE_VALUE
)
149 for(i
= 0; i
< (FileSize
.QuadPart
- BufferSize
); i
+= BufferSize
)
151 WriteFile (file
, buffer
, BufferSize
, &temp
, NULL
);
152 ConOutPrintf (_T("%I64d%% %s\r"),(i
* (LONGLONG
)100)/FileSize
.QuadPart
,szDeleteWipe
);
154 WriteFile (file
, buffer
, (DWORD
)(FileSize
.QuadPart
- i
), &temp
, NULL
);
155 ConOutPrintf (_T("100%% %s\n"),szDeleteWipe
);
160 return DeleteFile (lpFileName
);
165 DeleteFiles(LPTSTR FileName
, DWORD
* dwFlags
, DWORD dwAttrFlags
)
167 TCHAR szFullPath
[MAX_PATH
];
168 TCHAR szFileName
[MAX_PATH
];
176 _tcscpy(szFileName
, FileName
);
178 if(_tcschr (szFileName
, _T('*')) == NULL
&&
179 IsExistingDirectory (szFileName
))
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("*"));
188 if(!_tcscmp (szFileName
, _T("*")) ||
189 !_tcscmp (szFileName
, _T("*.*")) ||
190 (szFileName
[_tcslen(szFileName
) - 2] == _T('\\') && szFileName
[_tcslen(szFileName
) - 1] == _T('*')))
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. */
195 if (!((*dwFlags
& DEL_YES
) || (*dwFlags
& DEL_QUIET
) || (*dwFlags
& DEL_PROMPT
)))
197 res
= FilePromptYNA (szDelHelp2
);
198 if ((res
== PROMPT_NO
) || (res
== PROMPT_BREAK
))
200 if(res
== PROMPT_ALL
)
205 GetFullPathName (szFileName
,
210 hFile
= FindFirstFile(szFullPath
, &f
);
211 if (hFile
!= INVALID_HANDLE_VALUE
)
217 /*if it is going to be excluded by - no need to check attrs*/
218 if(*dwFlags
& DEL_ATTRIBUTES
&& !bExclusion
)
221 /*save if file attr check if user doesnt care about that attr anyways*/
222 if(dwAttrFlags
& ATTR_ARCHIVE
&& !(f
.dwFileAttributes
& FILE_ATTRIBUTE_ARCHIVE
))
224 if(dwAttrFlags
& ATTR_HIDDEN
&& !(f
.dwFileAttributes
& FILE_ATTRIBUTE_HIDDEN
))
226 if(dwAttrFlags
& ATTR_SYSTEM
&& !(f
.dwFileAttributes
& FILE_ATTRIBUTE_SYSTEM
))
228 if(dwAttrFlags
& ATTR_READ_ONLY
&& !(f
.dwFileAttributes
& FILE_ATTRIBUTE_READONLY
))
230 if(dwAttrFlags
& ATTR_N_ARCHIVE
&& (f
.dwFileAttributes
& FILE_ATTRIBUTE_ARCHIVE
))
232 if(dwAttrFlags
& ATTR_N_HIDDEN
&& (f
.dwFileAttributes
& FILE_ATTRIBUTE_HIDDEN
))
234 if(dwAttrFlags
& ATTR_N_SYSTEM
&& (f
.dwFileAttributes
& FILE_ATTRIBUTE_SYSTEM
))
236 if(dwAttrFlags
& ATTR_N_READ_ONLY
&& (f
.dwFileAttributes
& FILE_ATTRIBUTE_READONLY
))
243 /* ignore directories */
244 if (f
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
248 _tcscpy (pFilePart
, f
.cFileName
);
250 /* We cant delete ourselves */
251 if(!_tcscmp (CMDPath
,szFullPath
))
256 ConErrPrintf(_T("Full filename: %s\n"), szFullPath
);
259 /* ask for deleting */
260 if (*dwFlags
& DEL_PROMPT
)
262 ConErrPrintf(szDelError5
, szFullPath
);
264 res
= FilePromptYN (szDelError6
);
266 if ((res
== PROMPT_NO
) || (res
== PROMPT_BREAK
))
273 /*user cant ask it to be quiet and tell you what it did*/
274 if (!(*dwFlags
& DEL_QUIET
) && !(*dwFlags
& DEL_TOTAL
))
276 ConErrPrintf(szDelError7
, szFullPath
);
279 /* delete the file */
280 if(*dwFlags
& DEL_NOTHING
)
283 if(RemoveFile (szFullPath
, *dwFlags
, &f
))
287 ErrorMessage (GetLastError(), _T(""));
292 while (FindNextFile (hFile
, &f
));
300 ProcessDirectory(LPTSTR FileName
, DWORD
* dwFlags
, DWORD dwAttrFlags
)
302 TCHAR szFullPath
[MAX_PATH
];
309 GetFullPathName (FileName
,
314 dwFiles
= DeleteFiles(szFullPath
, dwFlags
, dwAttrFlags
);
315 if (dwFiles
& 0x80000000)
318 if (*dwFlags
& DEL_SUBDIR
)
320 /* Get just the file name */
321 pSearchPart
= _tcsrchr(FileName
,_T('\\'));
322 if(pSearchPart
!= NULL
)
325 pSearchPart
= FileName
;
327 /* Get the full path to the file */
328 GetFullPathName (FileName
,MAX_PATH
,szFullPath
,NULL
);
330 /* strip the filename off of it */
331 pFilePart
= _tcsrchr(szFullPath
, _T('\\'));
332 if (pFilePart
== NULL
)
334 pFilePart
= szFullPath
;
341 _tcscpy(pFilePart
, _T("*"));
343 hFile
= FindFirstFile(szFullPath
, &f
);
344 if (hFile
!= INVALID_HANDLE_VALUE
)
348 if (!(f
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) ||
349 !_tcscmp(f
.cFileName
, _T(".")) ||
350 !_tcscmp(f
.cFileName
, _T("..")))
353 _tcscpy(pFilePart
, f
.cFileName
);
354 _tcscat(pFilePart
, _T("\\"));
355 _tcscat(pFilePart
, pSearchPart
);
357 dwFiles
+=ProcessDirectory(szFullPath
, dwFlags
, dwAttrFlags
);
358 if (dwFiles
& 0x80000000)
363 while (FindNextFile (hFile
, &f
));
372 INT
CommandDelete (LPTSTR cmd
, LPTSTR param
)
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*/
380 INT nEvalArgs
= 0; /* nunber of evaluated arguments */
382 DWORD dwAttrFlags
= 0;
385 TCHAR szOrginalArg
[MAX_PATH
];
387 /*checks the first two chars of param to see if it is /?
388 this however allows the following command to not show help
396 if (!_tcsncmp (param
, _T("/?"), 2))
398 ConOutResPaging(TRUE
,STRING_DEL_HELP1
);
404 arg
= split (param
, &args
, FALSE
);
408 /* only command given */
409 error_req_param_missing ();
413 /* check for options anywhere in command line */
414 for (i
= 0; i
< args
; i
++)
416 if (*arg
[i
] == _T('/'))
418 /*found a command, but check to make sure it has something after it*/
419 if (_tcslen (arg
[i
]) >= 2)
421 ch
= _totupper (arg
[i
][1]);
424 dwFlags
|= DEL_NOTHING
;
426 else if (ch
== _T('P'))
428 dwFlags
|= DEL_PROMPT
;
430 else if (ch
== _T('Q'))
432 dwFlags
|= DEL_QUIET
;
434 else if (ch
== _T('F'))
436 dwFlags
|= DEL_FORCE
;
438 else if (ch
== _T('S'))
440 dwFlags
|= DEL_SUBDIR
;
442 else if (ch
== _T('T'))
444 dwFlags
|= DEL_TOTAL
;
446 else if (ch
== _T('W'))
450 else if (ch
== _T('Y'))
454 else if (ch
== _T('A'))
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)
462 error_invalid_parameter_format(arg
[i
]);
465 ch
= _totupper (arg
[i
][3]);
466 if (_tcslen (arg
[i
]) == 4)
470 dwAttrFlags
|= ATTR_ARCHIVE
;
474 dwAttrFlags
|= ATTR_HIDDEN
;
478 dwAttrFlags
|= ATTR_SYSTEM
;
482 dwAttrFlags
|= ATTR_READ_ONLY
;
485 if (_tcslen (arg
[i
]) == 5)
489 ch
= _totupper (arg
[i
][4]);
492 dwAttrFlags
|= ATTR_N_ARCHIVE
;
496 dwAttrFlags
|= ATTR_N_HIDDEN
;
500 dwAttrFlags
|= ATTR_N_SYSTEM
;
504 dwAttrFlags
|= ATTR_N_READ_ONLY
;
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
)
519 error_req_param_missing ();
524 /* keep quiet within batch files */
526 dwFlags
|= DEL_QUIET
;
528 /* check for filenames anywhere in command line */
529 for (i
= 0; i
< args
&& !(dwFiles
& 0x80000000); i
++)
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('-')))
536 /* We want to make a copies of the argument */
537 if(_tcslen(arg
[i
]) == 2 && arg
[i
][1] == _T(':'))
539 /* Check for C: D: ... */
540 GetRootPath(arg
[i
],szOrginalArg
,MAX_PATH
);
544 _tcscpy(szOrginalArg
,arg
[i
]);
546 dwFiles
+= ProcessDirectory(szOrginalArg
, &dwFlags
, dwAttrFlags
);
552 /*Based on MS cmd, we only tell what files are being deleted when /S is used */
553 if (dwFlags
& DEL_TOTAL
)
555 dwFiles
&= 0x7fffffff;
558 ConOutPrintf(szDelHelp3
, dwFiles
);
562 ConOutPrintf(szDelHelp4
, dwFiles
);