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) 19 * Fixed command line parsing bugs. 20 * 21 * 21-Jan-1999 (Eric Kohl) 22 * Started major rewrite using a new structure. 23 * 24 * 03-Feb-1999 (Eric Kohl) 25 * First working version. 26 * 27 * 30-Mar-1999 (Eric Kohl) 28 * Added quiet ("/Q"), wipe ("/W") and zap ("/Z") option. 29 * 30 * 06-Nov-1999 (Eric Kohl) 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 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 50 #ifdef INCLUDE_CMD_DEL 51 52 53 enum 54 { 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 */ 65 }; 66 67 enum 68 { 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 */ 77 }; 78 79 static TCHAR szDeleteWipe[RC_STRING_MAX_SIZE]; 80 static TCHAR CMDPath[MAX_PATH]; 81 82 static BOOLEAN StringsLoaded = FALSE; 83 84 static VOID LoadStrings(VOID) 85 { 86 LoadString(CMD_ModuleHandle, STRING_DELETE_WIPE, szDeleteWipe, ARRAYSIZE(szDeleteWipe)); 87 GetModuleFileName(NULL, CMDPath, ARRAYSIZE(CMDPath)); 88 StringsLoaded = TRUE; 89 } 90 91 static BOOL 92 RemoveFile (LPTSTR lpFileName, DWORD dwFlags, WIN32_FIND_DATA* f) 93 { 94 /*This function is called by CommandDelete and 95 does the actual process of deleting the single 96 file*/ 97 if (CheckCtrlBreak(BREAK_INPUT)) 98 return 1; 99 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 103 deleted*/ 104 if ((dwFlags & DEL_ATTRIBUTES) || (dwFlags & DEL_FORCE)) 105 { 106 if (f->dwFileAttributes & FILE_ATTRIBUTE_READONLY) 107 { 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 111 read only.*/ 112 SetFileAttributes(lpFileName,FILE_ATTRIBUTE_NORMAL); 113 } 114 } 115 116 if (dwFlags & DEL_WIPE) 117 { 118 HANDLE file; 119 DWORD temp; 120 #define BufferSize 65536 121 BYTE buffer[BufferSize]; 122 LONGLONG i; 123 LARGE_INTEGER FileSize; 124 125 FileSize.u.HighPart = f->nFileSizeHigh; 126 FileSize.u.LowPart = f->nFileSizeLow; 127 128 for(i = 0; i < BufferSize; i++) 129 { 130 buffer[i]=rand() % 256; 131 } 132 file = CreateFile (lpFileName, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH, NULL); 133 if (file != INVALID_HANDLE_VALUE) 134 { 135 for(i = 0; i < (FileSize.QuadPart - BufferSize); i += BufferSize) 136 { 137 WriteFile (file, buffer, BufferSize, &temp, NULL); 138 ConOutPrintf (_T("%I64d%% %s\r"),(i * (LONGLONG)100)/FileSize.QuadPart,szDeleteWipe); 139 } 140 WriteFile (file, buffer, (DWORD)(FileSize.QuadPart - i), &temp, NULL); 141 ConOutPrintf (_T("100%% %s\n"),szDeleteWipe); 142 CloseHandle (file); 143 } 144 } 145 146 return DeleteFile (lpFileName); 147 } 148 149 150 static DWORD 151 DeleteFiles(LPTSTR FileName, DWORD* dwFlags, DWORD dwAttrFlags) 152 { 153 TCHAR szFullPath[MAX_PATH]; 154 TCHAR szFileName[MAX_PATH]; 155 LPTSTR pFilePart; 156 HANDLE hFile; 157 WIN32_FIND_DATA f; 158 BOOL bExclusion; 159 INT res; 160 DWORD dwFiles = 0; 161 162 _tcscpy(szFileName, FileName); 163 164 if (_tcschr (szFileName, _T('*')) == NULL && 165 IsExistingDirectory (szFileName)) 166 { 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("*")); 172 } 173 174 if (!_tcscmp (szFileName, _T("*")) || 175 !_tcscmp (szFileName, _T("*.*")) || 176 (szFileName[_tcslen(szFileName) - 2] == _T('\\') && szFileName[_tcslen(szFileName) - 1] == _T('*'))) 177 { 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. */ 180 181 if (!((*dwFlags & DEL_YES) || (*dwFlags & DEL_QUIET) || (*dwFlags & DEL_PROMPT))) 182 { 183 ConOutPrintf (_T("Delete %s, "),szFileName); 184 res = FilePromptYNA (STRING_DEL_HELP2); 185 if ((res == PROMPT_NO) || (res == PROMPT_BREAK)) 186 return 0x80000000; 187 if (res == PROMPT_ALL) 188 *dwFlags |= DEL_YES; 189 } 190 } 191 192 GetFullPathName(szFileName, 193 MAX_PATH, 194 szFullPath, 195 &pFilePart); 196 197 hFile = FindFirstFile(szFullPath, &f); 198 if (hFile != INVALID_HANDLE_VALUE) 199 { 200 do 201 { 202 bExclusion = FALSE; 203 204 /*if it is going to be excluded by - no need to check attrs*/ 205 if (*dwFlags & DEL_ATTRIBUTES && !bExclusion) 206 { 207 /*save if file attr check if user doesnt care about that attr anyways*/ 208 if (dwAttrFlags & ATTR_ARCHIVE && !(f.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)) 209 bExclusion = TRUE; 210 if (dwAttrFlags & ATTR_HIDDEN && !(f.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)) 211 bExclusion = TRUE; 212 if (dwAttrFlags & ATTR_SYSTEM && !(f.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)) 213 bExclusion = TRUE; 214 if (dwAttrFlags & ATTR_READ_ONLY && !(f.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) 215 bExclusion = TRUE; 216 if (dwAttrFlags & ATTR_N_ARCHIVE && (f.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)) 217 bExclusion = TRUE; 218 if (dwAttrFlags & ATTR_N_HIDDEN && (f.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)) 219 bExclusion = TRUE; 220 if (dwAttrFlags & ATTR_N_SYSTEM && (f.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)) 221 bExclusion = TRUE; 222 if (dwAttrFlags & ATTR_N_READ_ONLY && (f.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) 223 bExclusion = TRUE; 224 } 225 if (bExclusion) continue; 226 227 /* ignore directories */ 228 if (f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) continue; 229 230 _tcscpy (pFilePart, f.cFileName); 231 232 /* We cant delete ourselves */ 233 if (!_tcscmp (CMDPath,szFullPath)) continue; 234 235 TRACE("Full filename: %s\n", debugstr_aw(szFullPath)); 236 237 /* ask for deleting */ 238 if (*dwFlags & DEL_PROMPT) 239 { 240 ConErrResPrintf(STRING_DEL_ERROR5, szFullPath); 241 242 res = FilePromptYN (STRING_DEL_ERROR6); 243 244 if ((res == PROMPT_NO) || (res == PROMPT_BREAK)) 245 { 246 nErrorLevel = 0; 247 continue; 248 } 249 } 250 251 /*user cant ask it to be quiet and tell you what it did*/ 252 if (!(*dwFlags & DEL_QUIET) && !(*dwFlags & DEL_TOTAL)) 253 { 254 ConErrResPrintf(STRING_DEL_ERROR7, szFullPath); 255 } 256 257 /* delete the file */ 258 if (*dwFlags & DEL_NOTHING) continue; 259 260 if (RemoveFile (szFullPath, *dwFlags, &f)) 261 { 262 dwFiles++; 263 } 264 else 265 { 266 ErrorMessage (GetLastError(), _T("")); 267 // FindClose(hFile); 268 // return -1; 269 } 270 } 271 while (FindNextFile (hFile, &f)); 272 FindClose (hFile); 273 } 274 else error_sfile_not_found(szFullPath); 275 return dwFiles; 276 } 277 278 static DWORD 279 ProcessDirectory(LPTSTR FileName, DWORD* dwFlags, DWORD dwAttrFlags) 280 { 281 TCHAR szFullPath[MAX_PATH]; 282 LPTSTR pFilePart; 283 LPTSTR pSearchPart; 284 HANDLE hFile; 285 WIN32_FIND_DATA f; 286 DWORD dwFiles = 0; 287 288 /* Get the full path to the file */ 289 GetFullPathName(FileName, 290 MAX_PATH, 291 szFullPath, 292 &pFilePart); 293 294 /* Delete all the files in this directory */ 295 dwFiles = DeleteFiles(szFullPath, dwFlags, dwAttrFlags); 296 if (dwFiles & 0x80000000) 297 return dwFiles; 298 299 if (*dwFlags & DEL_SUBDIR) 300 { 301 /* Get just the file name */ 302 pSearchPart = _T("*"); 303 if (!IsExistingDirectory(szFullPath)) 304 { 305 pSearchPart = _tcsrchr(FileName, _T('\\')); 306 if (pSearchPart != NULL) 307 pSearchPart++; 308 else 309 pSearchPart = FileName; 310 } 311 312 /* If no wildcard or file was specified and this is a directory, then 313 display all files in it */ 314 if (pFilePart == NULL || IsExistingDirectory(szFullPath)) 315 { 316 pFilePart = &szFullPath[_tcslen(szFullPath)]; 317 if (*(pFilePart-1) != _T('\\')) 318 *pFilePart++ = _T('\\'); 319 _tcscpy(pFilePart, _T("*")); 320 } 321 else 322 { 323 /* strip the filename off of it */ 324 _tcscpy(pFilePart, _T("*")); 325 } 326 327 /* Enumerate all the sub-directories */ 328 hFile = FindFirstFile(szFullPath, &f); 329 if (hFile != INVALID_HANDLE_VALUE) 330 { 331 do 332 { 333 if (!(f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) || 334 !_tcscmp(f.cFileName, _T(".")) || 335 !_tcscmp(f.cFileName, _T(".."))) 336 continue; 337 338 _tcscpy(pFilePart, f.cFileName); 339 _tcscat(pFilePart, _T("\\")); 340 _tcscat(pFilePart, pSearchPart); 341 342 dwFiles +=ProcessDirectory(szFullPath, dwFlags, dwAttrFlags); 343 if (dwFiles & 0x80000000) 344 { 345 break; 346 } 347 } 348 while (FindNextFile (hFile, &f)); 349 FindClose (hFile); 350 } 351 } 352 353 return dwFiles; 354 } 355 356 357 358 INT CommandDelete (LPTSTR param) 359 { 360 /*cmd is the command that was given, in this case it will always be "del" or "delete" 361 param is whatever is given after the command*/ 362 363 LPTSTR *arg = NULL; 364 INT args; 365 INT i; 366 INT nEvalArgs = 0; /* number of evaluated arguments */ 367 DWORD dwFlags = 0; 368 DWORD dwAttrFlags = 0; 369 DWORD dwFiles = 0; 370 LONG ch; 371 TCHAR szOriginalArg[MAX_PATH]; 372 373 /*checks the first two chars of param to see if it is /? 374 this however allows the following command to not show help 375 "del frog.txt /?" */ 376 377 if (!StringsLoaded) 378 { 379 LoadStrings(); 380 } 381 382 if (!_tcsncmp (param, _T("/?"), 2)) 383 { 384 ConOutResPaging(TRUE,STRING_DEL_HELP1); 385 return 0; 386 } 387 388 nErrorLevel = 0; 389 390 arg = split (param, &args, FALSE, FALSE); 391 392 if (args == 0) 393 { 394 /* only command given */ 395 error_req_param_missing (); 396 freep (arg); 397 return 1; 398 } 399 /* check for options anywhere in command line */ 400 for (i = 0; i < args; i++) 401 { 402 if (*arg[i] == _T('/')) 403 { 404 /*found a command, but check to make sure it has something after it*/ 405 if (_tcslen (arg[i]) >= 2) 406 { 407 ch = _totupper (arg[i][1]); 408 if (ch == _T('N')) 409 { 410 dwFlags |= DEL_NOTHING; 411 } 412 else if (ch == _T('P')) 413 { 414 dwFlags |= DEL_PROMPT; 415 } 416 else if (ch == _T('Q')) 417 { 418 dwFlags |= DEL_QUIET; 419 } 420 else if (ch == _T('F')) 421 { 422 dwFlags |= DEL_FORCE; 423 } 424 else if (ch == _T('S')) 425 { 426 dwFlags |= DEL_SUBDIR; 427 } 428 else if (ch == _T('T')) 429 { 430 dwFlags |= DEL_TOTAL; 431 } 432 else if (ch == _T('W')) 433 { 434 dwFlags |= DEL_WIPE; 435 } 436 else if (ch == _T('Y')) 437 { 438 dwFlags |= DEL_YES; 439 } 440 else if (ch == _T('A')) 441 { 442 dwFlags |= DEL_ATTRIBUTES; 443 /*the proper syntax for /A has a min of 4 chars 444 i.e. /A:R or /A:-H */ 445 if (_tcslen (arg[i]) < 4) 446 { 447 error_invalid_parameter_format(arg[i]); 448 return 0; 449 } 450 ch = _totupper (arg[i][3]); 451 if (_tcslen (arg[i]) == 4) 452 { 453 if (ch == _T('A')) 454 { 455 dwAttrFlags |= ATTR_ARCHIVE; 456 } 457 if (ch == _T('H')) 458 { 459 dwAttrFlags |= ATTR_HIDDEN; 460 } 461 if (ch == _T('S')) 462 { 463 dwAttrFlags |= ATTR_SYSTEM; 464 } 465 if (ch == _T('R')) 466 { 467 dwAttrFlags |= ATTR_READ_ONLY; 468 } 469 } 470 if (_tcslen (arg[i]) == 5) 471 { 472 if (ch == _T('-')) 473 { 474 ch = _totupper (arg[i][4]); 475 if (ch == _T('A')) 476 { 477 dwAttrFlags |= ATTR_N_ARCHIVE; 478 } 479 if (ch == _T('H')) 480 { 481 dwAttrFlags |= ATTR_N_HIDDEN; 482 } 483 if (ch == _T('S')) 484 { 485 dwAttrFlags |= ATTR_N_SYSTEM; 486 } 487 if (ch == _T('R')) 488 { 489 dwAttrFlags |= ATTR_N_READ_ONLY; 490 } 491 } 492 } 493 } 494 } 495 496 nEvalArgs++; 497 } 498 } 499 500 /* there are only options on the command line --> error!!! 501 there is the same number of args as there is flags, so none of the args were filenames*/ 502 if (args == nEvalArgs) 503 { 504 error_req_param_missing (); 505 freep (arg); 506 return 1; 507 } 508 509 /* keep quiet within batch files */ 510 if (bc != NULL) dwFlags |= DEL_QUIET; 511 512 /* check for filenames anywhere in command line */ 513 for (i = 0; i < args && !(dwFiles & 0x80000000); i++) 514 { 515 /*this checks to see if it is a flag; if it isn't, we assume it is a file name*/ 516 if ((*arg[i] == _T('/')) || (*arg[i] == _T('-'))) 517 continue; 518 519 /* We want to make a copies of the argument */ 520 if (_tcslen(arg[i]) == 2 && arg[i][1] == _T(':')) 521 { 522 /* Check for C: D: ... */ 523 GetRootPath(arg[i], szOriginalArg, MAX_PATH); 524 } 525 else 526 { 527 _tcscpy(szOriginalArg,arg[i]); 528 } 529 dwFiles += ProcessDirectory(szOriginalArg, &dwFlags, dwAttrFlags); 530 } 531 532 freep (arg); 533 534 /*Based on MS cmd, we only tell what files are being deleted when /S is used */ 535 if (dwFlags & DEL_TOTAL) 536 { 537 dwFiles &= 0x7fffffff; 538 if (dwFiles < 2) 539 { 540 ConOutResPrintf(STRING_DEL_HELP3, dwFiles); 541 } 542 else 543 { 544 ConOutResPrintf(STRING_DEL_HELP4, dwFiles); 545 } 546 } 547 548 return 0; 549 } 550 551 #endif 552