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 res = FilePromptYNA (STRING_DEL_HELP2); 184 if ((res == PROMPT_NO) || (res == PROMPT_BREAK)) 185 return 0x80000000; 186 if (res == PROMPT_ALL) 187 *dwFlags |= DEL_YES; 188 } 189 } 190 191 GetFullPathName(szFileName, 192 MAX_PATH, 193 szFullPath, 194 &pFilePart); 195 196 hFile = FindFirstFile(szFullPath, &f); 197 if (hFile != INVALID_HANDLE_VALUE) 198 { 199 do 200 { 201 bExclusion = FALSE; 202 203 /*if it is going to be excluded by - no need to check attrs*/ 204 if (*dwFlags & DEL_ATTRIBUTES && !bExclusion) 205 { 206 /*save if file attr check if user doesnt care about that attr anyways*/ 207 if (dwAttrFlags & ATTR_ARCHIVE && !(f.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)) 208 bExclusion = TRUE; 209 if (dwAttrFlags & ATTR_HIDDEN && !(f.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)) 210 bExclusion = TRUE; 211 if (dwAttrFlags & ATTR_SYSTEM && !(f.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)) 212 bExclusion = TRUE; 213 if (dwAttrFlags & ATTR_READ_ONLY && !(f.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) 214 bExclusion = TRUE; 215 if (dwAttrFlags & ATTR_N_ARCHIVE && (f.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)) 216 bExclusion = TRUE; 217 if (dwAttrFlags & ATTR_N_HIDDEN && (f.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)) 218 bExclusion = TRUE; 219 if (dwAttrFlags & ATTR_N_SYSTEM && (f.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)) 220 bExclusion = TRUE; 221 if (dwAttrFlags & ATTR_N_READ_ONLY && (f.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) 222 bExclusion = TRUE; 223 } 224 if (bExclusion) continue; 225 226 /* ignore directories */ 227 if (f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) continue; 228 229 _tcscpy (pFilePart, f.cFileName); 230 231 /* We cant delete ourselves */ 232 if (!_tcscmp (CMDPath,szFullPath)) continue; 233 234 TRACE("Full filename: %s\n", debugstr_aw(szFullPath)); 235 236 /* ask for deleting */ 237 if (*dwFlags & DEL_PROMPT) 238 { 239 ConErrResPrintf(STRING_DEL_ERROR5, szFullPath); 240 241 res = FilePromptYN (STRING_DEL_ERROR6); 242 243 if ((res == PROMPT_NO) || (res == PROMPT_BREAK)) 244 { 245 nErrorLevel = 0; 246 continue; 247 } 248 } 249 250 /*user cant ask it to be quiet and tell you what it did*/ 251 if (!(*dwFlags & DEL_QUIET) && !(*dwFlags & DEL_TOTAL)) 252 { 253 ConErrResPrintf(STRING_DEL_ERROR7, szFullPath); 254 } 255 256 /* delete the file */ 257 if (*dwFlags & DEL_NOTHING) continue; 258 259 if (RemoveFile (szFullPath, *dwFlags, &f)) 260 { 261 dwFiles++; 262 } 263 else 264 { 265 ErrorMessage (GetLastError(), _T("")); 266 // FindClose(hFile); 267 // return -1; 268 } 269 } 270 while (FindNextFile (hFile, &f)); 271 FindClose (hFile); 272 } 273 else error_sfile_not_found(szFullPath); 274 return dwFiles; 275 } 276 277 static DWORD 278 ProcessDirectory(LPTSTR FileName, DWORD* dwFlags, DWORD dwAttrFlags) 279 { 280 TCHAR szFullPath[MAX_PATH]; 281 LPTSTR pFilePart; 282 LPTSTR pSearchPart; 283 HANDLE hFile; 284 WIN32_FIND_DATA f; 285 DWORD dwFiles = 0; 286 287 /* Get the full path to the file */ 288 GetFullPathName(FileName, 289 MAX_PATH, 290 szFullPath, 291 &pFilePart); 292 293 /* Delete all the files in this directory */ 294 dwFiles = DeleteFiles(szFullPath, dwFlags, dwAttrFlags); 295 if (dwFiles & 0x80000000) 296 return dwFiles; 297 298 if (*dwFlags & DEL_SUBDIR) 299 { 300 /* Get just the file name */ 301 pSearchPart = _T("*"); 302 if (!IsExistingDirectory(szFullPath)) 303 { 304 pSearchPart = _tcsrchr(FileName, _T('\\')); 305 if (pSearchPart != NULL) 306 pSearchPart++; 307 else 308 pSearchPart = FileName; 309 } 310 311 /* If no wildcard or file was specified and this is a directory, then 312 display all files in it */ 313 if (pFilePart == NULL || IsExistingDirectory(szFullPath)) 314 { 315 pFilePart = &szFullPath[_tcslen(szFullPath)]; 316 if (*(pFilePart-1) != _T('\\')) 317 *pFilePart++ = _T('\\'); 318 _tcscpy(pFilePart, _T("*")); 319 } 320 else 321 { 322 /* strip the filename off of it */ 323 _tcscpy(pFilePart, _T("*")); 324 } 325 326 /* Enumerate all the sub-directories */ 327 hFile = FindFirstFile(szFullPath, &f); 328 if (hFile != INVALID_HANDLE_VALUE) 329 { 330 do 331 { 332 if (!(f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) || 333 !_tcscmp(f.cFileName, _T(".")) || 334 !_tcscmp(f.cFileName, _T(".."))) 335 continue; 336 337 _tcscpy(pFilePart, f.cFileName); 338 _tcscat(pFilePart, _T("\\")); 339 _tcscat(pFilePart, pSearchPart); 340 341 dwFiles +=ProcessDirectory(szFullPath, dwFlags, dwAttrFlags); 342 if (dwFiles & 0x80000000) 343 { 344 break; 345 } 346 } 347 while (FindNextFile (hFile, &f)); 348 FindClose (hFile); 349 } 350 } 351 352 return dwFiles; 353 } 354 355 356 357 INT CommandDelete (LPTSTR param) 358 { 359 /*cmd is the command that was given, in this case it will always be "del" or "delete" 360 param is whatever is given after the command*/ 361 362 LPTSTR *arg = NULL; 363 INT args; 364 INT i; 365 INT nEvalArgs = 0; /* number of evaluated arguments */ 366 DWORD dwFlags = 0; 367 DWORD dwAttrFlags = 0; 368 DWORD dwFiles = 0; 369 LONG ch; 370 TCHAR szOriginalArg[MAX_PATH]; 371 372 /*checks the first two chars of param to see if it is /? 373 this however allows the following command to not show help 374 "del frog.txt /?" */ 375 376 if (!StringsLoaded) 377 { 378 LoadStrings(); 379 } 380 381 if (!_tcsncmp (param, _T("/?"), 2)) 382 { 383 ConOutResPaging(TRUE,STRING_DEL_HELP1); 384 return 0; 385 } 386 387 nErrorLevel = 0; 388 389 arg = split (param, &args, FALSE, FALSE); 390 391 if (args == 0) 392 { 393 /* only command given */ 394 error_req_param_missing (); 395 freep (arg); 396 return 1; 397 } 398 /* check for options anywhere in command line */ 399 for (i = 0; i < args; i++) 400 { 401 if (*arg[i] == _T('/')) 402 { 403 /*found a command, but check to make sure it has something after it*/ 404 if (_tcslen (arg[i]) >= 2) 405 { 406 ch = _totupper (arg[i][1]); 407 if (ch == _T('N')) 408 { 409 dwFlags |= DEL_NOTHING; 410 } 411 else if (ch == _T('P')) 412 { 413 dwFlags |= DEL_PROMPT; 414 } 415 else if (ch == _T('Q')) 416 { 417 dwFlags |= DEL_QUIET; 418 } 419 else if (ch == _T('F')) 420 { 421 dwFlags |= DEL_FORCE; 422 } 423 else if (ch == _T('S')) 424 { 425 dwFlags |= DEL_SUBDIR; 426 } 427 else if (ch == _T('T')) 428 { 429 dwFlags |= DEL_TOTAL; 430 } 431 else if (ch == _T('W')) 432 { 433 dwFlags |= DEL_WIPE; 434 } 435 else if (ch == _T('Y')) 436 { 437 dwFlags |= DEL_YES; 438 } 439 else if (ch == _T('A')) 440 { 441 dwFlags |= DEL_ATTRIBUTES; 442 /*the proper syntax for /A has a min of 4 chars 443 i.e. /A:R or /A:-H */ 444 if (_tcslen (arg[i]) < 4) 445 { 446 error_invalid_parameter_format(arg[i]); 447 return 0; 448 } 449 ch = _totupper (arg[i][3]); 450 if (_tcslen (arg[i]) == 4) 451 { 452 if (ch == _T('A')) 453 { 454 dwAttrFlags |= ATTR_ARCHIVE; 455 } 456 if (ch == _T('H')) 457 { 458 dwAttrFlags |= ATTR_HIDDEN; 459 } 460 if (ch == _T('S')) 461 { 462 dwAttrFlags |= ATTR_SYSTEM; 463 } 464 if (ch == _T('R')) 465 { 466 dwAttrFlags |= ATTR_READ_ONLY; 467 } 468 } 469 if (_tcslen (arg[i]) == 5) 470 { 471 if (ch == _T('-')) 472 { 473 ch = _totupper (arg[i][4]); 474 if (ch == _T('A')) 475 { 476 dwAttrFlags |= ATTR_N_ARCHIVE; 477 } 478 if (ch == _T('H')) 479 { 480 dwAttrFlags |= ATTR_N_HIDDEN; 481 } 482 if (ch == _T('S')) 483 { 484 dwAttrFlags |= ATTR_N_SYSTEM; 485 } 486 if (ch == _T('R')) 487 { 488 dwAttrFlags |= ATTR_N_READ_ONLY; 489 } 490 } 491 } 492 } 493 } 494 495 nEvalArgs++; 496 } 497 } 498 499 /* there are only options on the command line --> error!!! 500 there is the same number of args as there is flags, so none of the args were filenames*/ 501 if (args == nEvalArgs) 502 { 503 error_req_param_missing (); 504 freep (arg); 505 return 1; 506 } 507 508 /* keep quiet within batch files */ 509 if (bc != NULL) dwFlags |= DEL_QUIET; 510 511 /* check for filenames anywhere in command line */ 512 for (i = 0; i < args && !(dwFiles & 0x80000000); i++) 513 { 514 /*this checks to see if it is a flag; if it isn't, we assume it is a file name*/ 515 if ((*arg[i] == _T('/')) || (*arg[i] == _T('-'))) 516 continue; 517 518 /* We want to make a copies of the argument */ 519 if (_tcslen(arg[i]) == 2 && arg[i][1] == _T(':')) 520 { 521 /* Check for C: D: ... */ 522 GetRootPath(arg[i], szOriginalArg, MAX_PATH); 523 } 524 else 525 { 526 _tcscpy(szOriginalArg,arg[i]); 527 } 528 dwFiles += ProcessDirectory(szOriginalArg, &dwFlags, dwAttrFlags); 529 } 530 531 freep (arg); 532 533 /*Based on MS cmd, we only tell what files are being deleted when /S is used */ 534 if (dwFlags & DEL_TOTAL) 535 { 536 dwFiles &= 0x7fffffff; 537 if (dwFiles < 2) 538 { 539 ConOutResPrintf(STRING_DEL_HELP3, dwFiles); 540 } 541 else 542 { 543 ConOutResPrintf(STRING_DEL_HELP4, dwFiles); 544 } 545 } 546 547 return 0; 548 } 549 550 #endif 551