1 /* 2 * DIR.C - dir internal command. 3 * 4 * 5 * History: 6 * 7 * 01/29/97 (Tim Norman) 8 * started. 9 * 10 * 06/13/97 (Tim Norman) 11 * Fixed code. 12 * 13 * 07/12/97 (Tim Norman) 14 * Fixed bug that caused the root directory to be unlistable 15 * 16 * 07/12/97 (Marc Desrochers) 17 * Changed to use maxx, maxy instead of findxy() 18 * 19 * 06/08/98 (Rob Lake) 20 * Added compatibility for /w in dir 21 * 22 * 06/09/98 (Rob Lake) 23 * Compatibility for dir/s started 24 * Tested that program finds directories off root fine 25 * 26 * 06/10/98 (Rob Lake) 27 * do_recurse saves the cwd and also stores it in Root 28 * build_tree adds the cwd to the beginning of its' entries 29 * Program runs fine, added print_tree -- works fine.. as EXE, 30 * program won't work properly as COM. 31 * 32 * 06/11/98 (Rob Lake) 33 * Found problem that caused COM not to work 34 * 35 * 06/12/98 (Rob Lake) 36 * debugged... 37 * added free mem routine 38 * 39 * 06/13/98 (Rob Lake) 40 * debugged the free mem routine 41 * debugged whole thing some more 42 * Notes: 43 * ReadDir stores Root name and _Read_Dir does the hard work 44 * PrintDir prints Root and _Print_Dir does the hard work 45 * KillDir kills Root _after_ _Kill_Dir does the hard work 46 * Integrated program into DIR.C(this file) and made some same 47 * changes throughout 48 * 49 * 06/14/98 (Rob Lake) 50 * Cleaned up code a bit, added comments 51 * 52 * 06/16/98 (Rob Lake) 53 * Added error checking to my previously added routines 54 * 55 * 06/17/98 (Rob Lake) 56 * Rewrote recursive functions, again! Most other recursive 57 * functions are now obsolete -- ReadDir, PrintDir, _Print_Dir, 58 * KillDir and _Kill_Dir. do_recurse does what PrintDir did 59 * and _Read_Dir did what it did before along with what _Print_Dir 60 * did. Makes /s a lot faster! 61 * Reports 2 more files/dirs that MS-DOS actually reports 62 * when used in root directory(is this because dir defaults 63 * to look for read only files?) 64 * Added support for /b, /a and /l 65 * Made error message similar to DOS error messages 66 * Added help screen 67 * 68 * 06/20/98 (Rob Lake) 69 * Added check for /-(switch) to turn off previously defined 70 * switches. 71 * Added ability to check for DIRCMD in environment and 72 * process it 73 * 74 * 06/21/98 (Rob Lake) 75 * Fixed up /B 76 * Now can dir *.ext/X, no spaces! 77 * 78 * 06/29/98 (Rob Lake) 79 * error message now found in command.h 80 * 81 * 07/08/1998 (John P. Price) 82 * removed extra returns; closer to MSDOS 83 * fixed wide display so that an extra return is not displayed 84 * when there is five filenames in the last line. 85 * 86 * 07/12/98 (Rob Lake) 87 * Changed error messages 88 * 89 * 27-Jul-1998 (John P Price <linux-guru@gcfl.net>) 90 * added config.h include 91 * 92 * 93 * 04-Dec-1998 (Eric Kohl) 94 * Converted source code to Win32, except recursive dir ("dir /s"). 95 * 96 * 10-Dec-1998 (Eric Kohl) 97 * Fixed recursive dir ("dir /s"). 98 * 99 * 14-Dec-1998 (Eric Kohl) 100 * Converted to Win32 directory functions and 101 * fixed some output bugs. There are still some more ;) 102 * 103 * 10-Jan-1999 (Eric Kohl) 104 * Added "/N" and "/4" options, "/O" is a dummy. 105 * Added locale support. 106 * 107 * 20-Jan-1999 (Eric Kohl) 108 * Redirection safe! 109 * 110 * 01-Mar-1999 (Eric Kohl) 111 * Replaced all runtime io functions by their Win32 counterparts. 112 * 113 * 23-Feb-2001 (Carl Nettelblad <cnettel@hem.passagen.se>) 114 * dir /s now works in deeper trees 115 * 116 * 28-Jan-2004 (Michael Fritscher <michael@fritscher.net>) 117 * Fix for /p, so it is working under Windows in GUI-mode, too. 118 * 119 * 30-Apr-2004 (Filip Navara <xnavara@volny.cz>) 120 * Fix /w to print long names. 121 * 122 * 27-Feb-2005 (Konstantinos Paliouras <squarious@gmail.com>) 123 * Implemented all the switches that were missing, and made 124 * the ros dir very similar to windows dir. Major part of 125 * the code is rewritten. /p is removed, to be rewritten in 126 * the main cmd code. 127 * 128 * 1-Jul-2004 (Brandon Turner <turnerb7@msu.edu>) 129 * Added /p back in using ConOutPrintfPaging 130 * 131 * 3-feb-2007 (Paolo Devoti devotip at gmail) 132 * Removed variables formerly in use to handle pagination 133 * Pagination belongs to ConOutPrintfPaging 134 * Removed already commented out code of old pagination 135 * 136 * 25-Aug-2015 (Pierre Schweitzer) 137 * Implemented /R switch 138 */ 139 140 #include "precomp.h" 141 142 #ifdef INCLUDE_CMD_DIR 143 144 145 146 /* Time Field enumeration */ 147 enum ETimeField 148 { 149 TF_CREATIONDATE = 0, 150 TF_MODIFIEDDATE = 1, 151 TF_LASTACCESSEDDATE = 2 152 }; 153 154 /* Ordered by enumeration */ 155 enum EOrderBy 156 { 157 ORDER_NAME = 0, 158 ORDER_SIZE = 1, 159 ORDER_DIRECTORY = 2, 160 ORDER_EXTENSION = 3, 161 ORDER_TIME = 4 162 }; 163 164 /* The struct for holding the switches */ 165 typedef struct _DirSwitchesFlags 166 { 167 BOOL bBareFormat; /* Bare Format */ 168 BOOL bTSeparator; /* Thousands separator */ 169 BOOL bWideList; /* Wide list format */ 170 BOOL bWideListColSort; /* Wide list format but sorted by column */ 171 BOOL bLowerCase; /* Uses lower case */ 172 BOOL bNewLongList; /* New long list */ 173 BOOL bPause; /* Pause per page */ 174 BOOL bUser; /* Displays the owner of file */ 175 BOOL bRecursive; /* Displays files in specified directory and all sub */ 176 BOOL bShortName; /* Displays the sort name of files if exist */ 177 BOOL b4Digit; /* Four digit year */ 178 BOOL bDataStreams; /* Displays alternate data streams */ 179 struct 180 { 181 DWORD dwAttribVal; /* The desired state of attribute */ 182 DWORD dwAttribMask; /* Which attributes to check */ 183 } stAttribs; /* Displays files with this attributes only */ 184 struct 185 { 186 enum EOrderBy eCriteria[3]; /* Criterias used to order by */ 187 BOOL bCriteriaRev[3]; /* If the criteria is in reversed order */ 188 short sCriteriaCount; /* The quantity of criterias */ 189 } stOrderBy; /* Ordered by criterias */ 190 struct 191 { 192 enum ETimeField eTimeField; /* The time field that will be used for */ 193 } stTimeField; /* The time field to display or use for sorting */ 194 } DIRSWITCHFLAGS, *LPDIRSWITCHFLAGS; 195 196 typedef struct _DIRFINDSTREAMNODE 197 { 198 WIN32_FIND_STREAM_DATA stStreamInfo; 199 struct _DIRFINDSTREAMNODE *ptrNext; 200 } DIRFINDSTREAMNODE, *PDIRFINDSTREAMNODE; 201 202 typedef struct _DIRFINDINFO 203 { 204 WIN32_FIND_DATA stFindInfo; 205 PDIRFINDSTREAMNODE ptrHead; 206 } DIRFINDINFO, *PDIRFINDINFO; 207 208 typedef struct _DIRFINDLISTNODE 209 { 210 DIRFINDINFO stInfo; 211 struct _DIRFINDLISTNODE *ptrNext; 212 } DIRFINDLISTNODE, *PDIRFINDLISTNODE; 213 214 typedef BOOL 215 (WINAPI *PGETFREEDISKSPACEEX)(LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER); 216 217 /* Globally save the # of dirs, files and bytes, 218 * probably later pass them to functions. Rob Lake */ 219 static ULONG recurse_dir_cnt; 220 static ULONG recurse_file_cnt; 221 static ULONGLONG recurse_bytes; 222 223 /* 224 * help 225 * 226 * displays help screen for dir 227 * Rob Lake 228 */ 229 static VOID 230 DirHelp(VOID) 231 { 232 ConOutResPaging(TRUE, STRING_DIR_HELP1); 233 } 234 235 /* 236 * DirReadParameters 237 * 238 * Parse the parameters and switches of the command line and exports them 239 */ 240 static BOOL 241 DirReadParam(LPTSTR Line, /* [IN] The line with the parameters & switches */ 242 LPTSTR** params, /* [OUT] The parameters after parsing */ 243 LPINT entries, /* [OUT] The number of parameters after parsing */ 244 LPDIRSWITCHFLAGS lpFlags) /* [IN/OUT] The flags after calculating switches */ 245 { 246 TCHAR cCurSwitch; /* The current switch */ 247 TCHAR cCurChar; /* Current examined character */ 248 TCHAR cCurUChar; /* Current upper examined character */ 249 BOOL bNegative; /* Negative switch */ 250 BOOL bPNegative; /* Negative switch parameter */ 251 BOOL bIntoQuotes; /* A flag showing if we are in quotes (") */ 252 LPTSTR ptrStart; /* A pointer to the first character of a parameter */ 253 LPTSTR ptrEnd; /* A pointer to the last character of a parameter */ 254 BOOL bOrderByNoPar; /* A flag to indicate /O with no switch parameter */ 255 LPTSTR temp; 256 257 /* Initialize parameter array */ 258 *params = NULL; 259 *entries = 0; 260 261 /* Initialize variables; */ 262 cCurSwitch = _T(' '); 263 bNegative = FALSE; 264 bPNegative = FALSE; 265 266 /* We suppose that switch parameters 267 were given to avoid setting them to default 268 if the switch was not given */ 269 bOrderByNoPar = FALSE; 270 271 /* Main Loop (see README_DIR.txt) */ 272 /* scan the command line char per char, and we process its char */ 273 while (*Line) 274 { 275 /* we save current character as it is and its upper case */ 276 cCurChar = *Line; 277 cCurUChar = _totupper(*Line); 278 279 /* 1st section (see README_DIR.txt) */ 280 /* When a switch is expecting */ 281 if (cCurSwitch == _T('/')) 282 { 283 while (_istspace(*Line)) 284 Line++; 285 286 bNegative = (*Line == _T('-')); 287 Line += bNegative; 288 289 cCurChar = *Line; 290 cCurUChar = _totupper(*Line); 291 292 if ((cCurUChar == _T('A')) ||(cCurUChar == _T('T')) || (cCurUChar == _T('O'))) 293 { 294 /* If positive, prepare for parameters... if negative, reset to defaults */ 295 switch (cCurUChar) 296 { 297 case _T('A'): 298 lpFlags->stAttribs.dwAttribVal = 0L; 299 lpFlags->stAttribs.dwAttribMask = 0L; 300 if (bNegative) 301 lpFlags->stAttribs.dwAttribMask = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM; 302 break; 303 case _T('T'): 304 if (bNegative) 305 lpFlags->stTimeField.eTimeField = TF_MODIFIEDDATE; 306 break; 307 case _T('O'): 308 bOrderByNoPar = !bNegative; 309 lpFlags->stOrderBy.sCriteriaCount = 0; 310 break; 311 } 312 313 if (!bNegative) 314 { 315 /* Positive switch, so it can take parameters. */ 316 cCurSwitch = cCurUChar; 317 Line++; 318 /* Skip optional leading colon */ 319 if (*Line == _T(':')) 320 Line++; 321 continue; 322 } 323 } 324 else if (cCurUChar == _T('L')) 325 lpFlags->bLowerCase = ! bNegative; 326 else if (cCurUChar == _T('B')) 327 lpFlags->bBareFormat = ! bNegative; 328 else if (cCurUChar == _T('C')) 329 lpFlags->bTSeparator = ! bNegative; 330 else if (cCurUChar == _T('W')) 331 lpFlags->bWideList = ! bNegative; 332 else if (cCurUChar == _T('D')) 333 lpFlags->bWideListColSort = ! bNegative; 334 else if (cCurUChar == _T('N')) 335 lpFlags->bNewLongList = ! bNegative; 336 else if (cCurUChar == _T('P')) 337 lpFlags->bPause = ! bNegative; 338 else if (cCurUChar == _T('Q')) 339 lpFlags->bUser = ! bNegative; 340 else if (cCurUChar == _T('S')) 341 lpFlags->bRecursive = ! bNegative; 342 else if (cCurUChar == _T('X')) 343 lpFlags->bShortName = ! bNegative; 344 else if (cCurUChar == _T('R')) 345 lpFlags->bDataStreams = ! bNegative; 346 else if (cCurChar == _T('4')) 347 lpFlags->b4Digit = ! bNegative; 348 else if (cCurChar == _T('?')) 349 { 350 DirHelp(); 351 return FALSE; 352 } 353 else 354 { 355 error_invalid_switch ((TCHAR)_totupper(*Line)); 356 return FALSE; 357 } 358 359 /* Make sure there's no extra characters at the end of the switch */ 360 if (Line[1] && Line[1] != _T('/') && !_istspace(Line[1])) 361 { 362 error_parameter_format(Line[1]); 363 return FALSE; 364 } 365 366 cCurSwitch = _T(' '); 367 } 368 else if (cCurSwitch == _T(' ')) 369 { 370 /* 2nd section (see README_DIR.txt) */ 371 /* We are expecting parameter or the unknown */ 372 373 if (cCurChar == _T('/')) 374 cCurSwitch = _T('/'); 375 else if (_istspace(cCurChar)) 376 /* do nothing */; 377 else 378 { 379 /* This is a file/directory name parameter. Find its end */ 380 ptrStart = Line; 381 bIntoQuotes = FALSE; 382 while (*Line) 383 { 384 if (!bIntoQuotes && (*Line == _T('/') || _istspace(*Line))) 385 break; 386 bIntoQuotes ^= (*Line == _T('"')); 387 Line++; 388 } 389 ptrEnd = Line; 390 391 /* Copy it to the entries list */ 392 temp = cmd_alloc((ptrEnd - ptrStart + 1) * sizeof(TCHAR)); 393 if (!temp) 394 return FALSE; 395 memcpy(temp, ptrStart, (ptrEnd - ptrStart) * sizeof(TCHAR)); 396 temp[ptrEnd - ptrStart] = _T('\0'); 397 StripQuotes(temp); 398 if (!add_entry(entries, params, temp)) 399 { 400 cmd_free(temp); 401 freep(*params); 402 return FALSE; 403 } 404 405 cmd_free(temp); 406 continue; 407 } 408 } 409 else 410 { 411 /* 3rd section (see README_DIR.txt) */ 412 /* We are waiting for switch parameters */ 413 414 /* Check if there are no more switch parameters */ 415 if ((cCurChar == _T('/')) || _istspace(cCurChar)) 416 { 417 /* Wrong decision path, reprocess current character */ 418 cCurSwitch = _T(' '); 419 continue; 420 } 421 /* Process parameter switch */ 422 switch (cCurSwitch) 423 { 424 case _T('A'): /* Switch parameters for /A (attributes filter) */ 425 if (cCurChar == _T('-')) 426 bPNegative = TRUE; 427 else if (cCurUChar == _T('D')) 428 { 429 lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_DIRECTORY; 430 if (bPNegative) 431 lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_DIRECTORY; 432 else 433 lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_DIRECTORY; 434 } 435 else if (cCurUChar == _T('R')) 436 { 437 lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_READONLY; 438 if (bPNegative) 439 lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_READONLY; 440 else 441 lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_READONLY; 442 } 443 else if (cCurUChar == _T('H')) 444 { 445 lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_HIDDEN; 446 if (bPNegative) 447 lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_HIDDEN; 448 else 449 lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_HIDDEN; 450 } 451 else if (cCurUChar == _T('A')) 452 { 453 lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_ARCHIVE; 454 if (bPNegative) 455 lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_ARCHIVE; 456 else 457 lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_ARCHIVE; 458 } 459 else if (cCurUChar == _T('S')) 460 { 461 lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_SYSTEM; 462 if (bPNegative) 463 lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_SYSTEM; 464 else 465 lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_SYSTEM; 466 } 467 else 468 { 469 error_parameter_format((TCHAR)_totupper (*Line)); 470 return FALSE; 471 } 472 break; 473 case _T('T'): /* Switch parameters for /T (time field) */ 474 if (cCurUChar == _T('C')) 475 lpFlags->stTimeField.eTimeField= TF_CREATIONDATE ; 476 else if (cCurUChar == _T('A')) 477 lpFlags->stTimeField.eTimeField= TF_LASTACCESSEDDATE ; 478 else if (cCurUChar == _T('W')) 479 lpFlags->stTimeField.eTimeField= TF_MODIFIEDDATE ; 480 else 481 { 482 error_parameter_format((TCHAR)_totupper (*Line)); 483 return FALSE; 484 } 485 break; 486 case _T('O'): /* Switch parameters for /O (order) */ 487 /* Ok a switch parameter was given */ 488 bOrderByNoPar = FALSE; 489 490 if (cCurChar == _T('-')) 491 bPNegative = TRUE; 492 else if (cCurUChar == _T('N')) 493 { 494 if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++; 495 lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative; 496 lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_NAME; 497 } 498 else if (cCurUChar == _T('S')) 499 { 500 if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++; 501 lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative; 502 lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_SIZE; 503 } 504 else if (cCurUChar == _T('G')) 505 { 506 if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++; 507 lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative; 508 lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_DIRECTORY; 509 } 510 else if (cCurUChar == _T('E')) 511 { 512 if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++; 513 lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative; 514 lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_EXTENSION; 515 } 516 else if (cCurUChar == _T('D')) 517 { 518 if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++; 519 lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative; 520 lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_TIME; 521 } 522 523 else 524 { 525 error_parameter_format((TCHAR)_totupper (*Line)); 526 return FALSE; 527 } 528 529 530 } 531 /* We check if we calculated the negative value and release the flag */ 532 if ((cCurChar != _T('-')) && bPNegative) 533 bPNegative = FALSE; 534 } 535 536 Line++; 537 } 538 539 /* /O with no switch parameters acts like /O:GN */ 540 if (bOrderByNoPar) 541 { 542 lpFlags->stOrderBy.sCriteriaCount = 2; 543 lpFlags->stOrderBy.eCriteria[0] = ORDER_DIRECTORY; 544 lpFlags->stOrderBy.bCriteriaRev[0] = FALSE; 545 lpFlags->stOrderBy.eCriteria[1] = ORDER_NAME; 546 lpFlags->stOrderBy.bCriteriaRev[1] = FALSE; 547 } 548 549 return TRUE; 550 } 551 552 /* Print either with or without paging, depending on /P switch */ 553 static BOOL 554 DirPrintf(LPDIRSWITCHFLAGS lpFlags, LPTSTR szFormat, ...) 555 { 556 BOOL Done = TRUE; 557 va_list arg_ptr; 558 va_start(arg_ptr, szFormat); 559 if (lpFlags->bPause) 560 Done = ConPrintfVPaging(&StdOutPager, FALSE, szFormat, arg_ptr); 561 else 562 ConPrintfV(StdOut, szFormat, arg_ptr); 563 va_end(arg_ptr); 564 return Done; 565 } 566 567 568 /* 569 * PrintDirectoryHeader 570 * 571 * print the header for the dir command 572 */ 573 static BOOL 574 PrintDirectoryHeader(LPTSTR szPath, LPDIRSWITCHFLAGS lpFlags) 575 { 576 TCHAR szMsg[RC_STRING_MAX_SIZE]; 577 TCHAR szFullDir[MAX_PATH]; 578 TCHAR szRootName[MAX_PATH]; 579 TCHAR szVolName[80]; 580 LPTSTR pszFilePart; 581 DWORD dwSerialNr; 582 583 if (lpFlags->bBareFormat) 584 return TRUE; 585 586 if (GetFullPathName(szPath, ARRAYSIZE(szFullDir), szFullDir, &pszFilePart) == 0) 587 { 588 ErrorMessage(GetLastError(), _T("Failed to build full directory path")); 589 return FALSE; 590 } 591 592 if (pszFilePart != NULL) 593 *pszFilePart = _T('\0'); 594 595 /* get the media ID of the drive */ 596 if (!GetVolumePathName(szFullDir, szRootName, ARRAYSIZE(szRootName)) || 597 !GetVolumeInformation(szRootName, szVolName, 80, &dwSerialNr, 598 NULL, NULL, NULL, 0)) 599 { 600 return TRUE; 601 } 602 603 /* print drive info */ 604 if (szVolName[0] != _T('\0')) 605 { 606 LoadString(CMD_ModuleHandle, STRING_DIR_HELP2, szMsg, ARRAYSIZE(szMsg)); 607 DirPrintf(lpFlags, szMsg, szRootName[0], szVolName); 608 } 609 else 610 { 611 LoadString(CMD_ModuleHandle, STRING_DIR_HELP3, szMsg, ARRAYSIZE(szMsg)); 612 DirPrintf(lpFlags, szMsg, szRootName[0]); 613 } 614 615 /* print the volume serial number if the return was successful */ 616 LoadString(CMD_ModuleHandle, STRING_DIR_HELP4, szMsg, ARRAYSIZE(szMsg)); 617 DirPrintf(lpFlags, szMsg, HIWORD(dwSerialNr), LOWORD(dwSerialNr)); 618 619 return TRUE; 620 } 621 622 623 static VOID 624 DirPrintFileDateTime(TCHAR *lpDate, 625 TCHAR *lpTime, 626 LPWIN32_FIND_DATA lpFile, 627 LPDIRSWITCHFLAGS lpFlags) 628 { 629 FILETIME ft; 630 SYSTEMTIME dt; 631 632 /* Select the right time field */ 633 switch (lpFlags->stTimeField.eTimeField) 634 { 635 case TF_CREATIONDATE: 636 if (!FileTimeToLocalFileTime(&lpFile->ftCreationTime, &ft)) 637 return; 638 FileTimeToSystemTime(&ft, &dt); 639 break; 640 641 case TF_LASTACCESSEDDATE : 642 if (!FileTimeToLocalFileTime(&lpFile->ftLastAccessTime, &ft)) 643 return; 644 FileTimeToSystemTime(&ft, &dt); 645 break; 646 647 case TF_MODIFIEDDATE: 648 if (!FileTimeToLocalFileTime(&lpFile->ftLastWriteTime, &ft)) 649 return; 650 FileTimeToSystemTime(&ft, &dt); 651 break; 652 } 653 654 FormatDate(lpDate, &dt, lpFlags->b4Digit); 655 FormatTime(lpTime, &dt); 656 } 657 658 INT 659 FormatDate(TCHAR *lpDate, LPSYSTEMTIME dt, BOOL b4Digit) 660 { 661 /* Format date */ 662 WORD wYear = b4Digit ? dt->wYear : dt->wYear%100; 663 switch (nDateFormat) 664 { 665 case 0: /* mmddyy */ 666 default: 667 return _stprintf(lpDate, _T("%02d%c%02d%c%0*d"), 668 dt->wMonth, cDateSeparator, 669 dt->wDay, cDateSeparator, 670 b4Digit?4:2, wYear); 671 break; 672 673 case 1: /* ddmmyy */ 674 return _stprintf(lpDate, _T("%02d%c%02d%c%0*d"), 675 dt->wDay, cDateSeparator, dt->wMonth, 676 cDateSeparator, b4Digit?4:2, wYear); 677 break; 678 679 case 2: /* yymmdd */ 680 return _stprintf(lpDate, _T("%0*d%c%02d%c%02d"), 681 b4Digit?4:2, wYear, cDateSeparator, 682 dt->wMonth, cDateSeparator, dt->wDay); 683 break; 684 } 685 } 686 687 INT 688 FormatTime(TCHAR *lpTime, LPSYSTEMTIME dt) 689 { 690 /* Format Time */ 691 switch (nTimeFormat) 692 { 693 case 0: /* 12 hour format */ 694 default: 695 return _stprintf(lpTime,_T("%02d%c%02u %cM"), 696 (dt->wHour == 0 ? 12 : (dt->wHour <= 12 ? dt->wHour : dt->wHour - 12)), 697 cTimeSeparator, 698 dt->wMinute, (dt->wHour <= 11 ? _T('A') : _T('P'))); 699 break; 700 701 case 1: /* 24 hour format */ 702 return _stprintf(lpTime, _T("%02d%c%02u"), 703 dt->wHour, cTimeSeparator, dt->wMinute); 704 break; 705 } 706 } 707 708 709 static VOID 710 GetUserDiskFreeSpace(LPCTSTR lpRoot, 711 PULARGE_INTEGER lpFreeSpace) 712 { 713 PGETFREEDISKSPACEEX pGetFreeDiskSpaceEx; 714 HINSTANCE hInstance; 715 DWORD dwSecPerCl; 716 DWORD dwBytPerSec; 717 DWORD dwFreeCl; 718 DWORD dwTotCl; 719 ULARGE_INTEGER TotalNumberOfBytes, TotalNumberOfFreeBytes; 720 721 lpFreeSpace->QuadPart = 0; 722 723 hInstance = GetModuleHandle(_T("KERNEL32")); 724 if (hInstance != NULL) 725 { 726 pGetFreeDiskSpaceEx = (PGETFREEDISKSPACEEX)GetProcAddress(hInstance, 727 #ifdef _UNICODE 728 "GetDiskFreeSpaceExW"); 729 #else 730 "GetDiskFreeSpaceExA"); 731 #endif 732 if (pGetFreeDiskSpaceEx != NULL) 733 { 734 if (pGetFreeDiskSpaceEx(lpRoot, lpFreeSpace, &TotalNumberOfBytes, &TotalNumberOfFreeBytes) != FALSE) 735 return; 736 } 737 } 738 739 GetDiskFreeSpace(lpRoot, 740 &dwSecPerCl, 741 &dwBytPerSec, 742 &dwFreeCl, 743 &dwTotCl); 744 745 lpFreeSpace->QuadPart = dwSecPerCl * dwBytPerSec * dwFreeCl; 746 } 747 748 749 /* 750 * print_summary: prints dir summary 751 * Added by Rob Lake 06/17/98 to compact code 752 * Just copied Tim's Code and patched it a bit 753 */ 754 static INT 755 PrintSummary(LPTSTR szPath, 756 ULONG ulFiles, 757 ULONG ulDirs, 758 ULONGLONG u64Bytes, 759 LPDIRSWITCHFLAGS lpFlags, 760 BOOL TotalSummary) 761 { 762 TCHAR szMsg[RC_STRING_MAX_SIZE]; 763 TCHAR szBuffer[64]; 764 ULARGE_INTEGER uliFree; 765 766 /* Here we check if we didn't find anything */ 767 if (!(ulFiles + ulDirs)) 768 { 769 if (!lpFlags->bRecursive || (TotalSummary && lpFlags->bRecursive)) 770 error_file_not_found(); 771 return 1; 772 } 773 774 /* In bare format we don't print results */ 775 if (lpFlags->bBareFormat) 776 return 0; 777 778 /* Print recursive specific results */ 779 780 /* Take this code offline to fix /S does not print double info */ 781 if (TotalSummary && lpFlags->bRecursive) 782 { 783 ConvertULargeInteger(u64Bytes, szBuffer, ARRAYSIZE(szBuffer), lpFlags->bTSeparator); 784 LoadString(CMD_ModuleHandle, STRING_DIR_HELP5, szMsg, ARRAYSIZE(szMsg)); 785 DirPrintf(lpFlags, szMsg, ulFiles, szBuffer); 786 } 787 else 788 { 789 /* Print File Summary */ 790 /* Condition to print summary is: 791 If we are not in bare format and if we have results! */ 792 ConvertULargeInteger(u64Bytes, szBuffer, 20, lpFlags->bTSeparator); 793 LoadString(CMD_ModuleHandle, STRING_DIR_HELP8, szMsg, ARRAYSIZE(szMsg)); 794 DirPrintf(lpFlags, szMsg, ulFiles, szBuffer); 795 } 796 797 /* Print total directories and freespace */ 798 if (!lpFlags->bRecursive || TotalSummary) 799 { 800 GetUserDiskFreeSpace(szPath, &uliFree); 801 ConvertULargeInteger(uliFree.QuadPart, szBuffer, ARRAYSIZE(szBuffer), lpFlags->bTSeparator); 802 LoadString(CMD_ModuleHandle, STRING_DIR_HELP6, szMsg, ARRAYSIZE(szMsg)); 803 DirPrintf(lpFlags, szMsg, ulDirs, szBuffer); 804 } 805 806 return 0; 807 } 808 809 /* 810 * getExt 811 * 812 * Get the extension of a filename 813 */ 814 TCHAR* getExt(const TCHAR* file) 815 { 816 static TCHAR *NoExt = _T(""); 817 TCHAR* lastdot = _tcsrchr(file, _T('.')); 818 return (lastdot != NULL ? lastdot + 1 : NoExt); 819 } 820 821 /* 822 * getName 823 * 824 * Get the name of the file without extension 825 */ 826 static LPTSTR 827 getName(const TCHAR* file, TCHAR * dest) 828 { 829 INT_PTR iLen; 830 LPTSTR end; 831 832 /* Check for "." and ".." folders */ 833 if ((_tcscmp(file, _T(".")) == 0) || 834 (_tcscmp(file, _T("..")) == 0)) 835 { 836 _tcscpy(dest,file); 837 return dest; 838 } 839 840 end = _tcsrchr(file, _T('.')); 841 if (!end) 842 iLen = _tcslen(file); 843 else 844 iLen = (end - file); 845 846 _tcsncpy(dest, file, iLen); 847 *(dest + iLen) = _T('\0'); 848 849 return dest; 850 } 851 852 853 /* 854 * DirPrintNewList 855 * 856 * The function that prints in new style 857 */ 858 static VOID 859 DirPrintNewList(PDIRFINDINFO ptrFiles[], /* [IN]Files' Info */ 860 DWORD dwCount, /* [IN] The quantity of files */ 861 TCHAR *szCurPath, /* [IN] Full path of current directory */ 862 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */ 863 { 864 DWORD i; 865 TCHAR szSize[30]; 866 TCHAR szShortName[15]; 867 TCHAR szDate[20]; 868 TCHAR szTime[20]; 869 INT iSizeFormat; 870 ULARGE_INTEGER u64FileSize; 871 PDIRFINDSTREAMNODE ptrCurStream; 872 873 for (i = 0; i < dwCount && !CheckCtrlBreak(BREAK_INPUT); i++) 874 { 875 /* Calculate size */ 876 if (ptrFiles[i]->stFindInfo.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) 877 { 878 /* Junction */ 879 iSizeFormat = -14; 880 _tcscpy(szSize, _T("<JUNCTION>")); 881 } 882 else if (ptrFiles[i]->stFindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 883 { 884 /* Directory */ 885 iSizeFormat = -14; 886 _tcscpy(szSize, _T("<DIR>")); 887 } 888 else 889 { 890 /* File */ 891 iSizeFormat = 14; 892 u64FileSize.HighPart = ptrFiles[i]->stFindInfo.nFileSizeHigh; 893 u64FileSize.LowPart = ptrFiles[i]->stFindInfo.nFileSizeLow; 894 ConvertULargeInteger(u64FileSize.QuadPart, szSize, 20, lpFlags->bTSeparator); 895 } 896 897 /* Calculate short name */ 898 szShortName[0] = _T('\0'); 899 if (lpFlags->bShortName) 900 _stprintf(szShortName, _T(" %-12s"), ptrFiles[i]->stFindInfo.cAlternateFileName); 901 902 /* Format date and time */ 903 DirPrintFileDateTime(szDate, szTime, &ptrFiles[i]->stFindInfo, lpFlags); 904 905 /* Print the line */ 906 DirPrintf(lpFlags, _T("%10s %-6s %*s%s %s\n"), 907 szDate, 908 szTime, 909 iSizeFormat, 910 szSize, 911 szShortName, 912 ptrFiles[i]->stFindInfo.cFileName); 913 914 /* Now, loop on the streams */ 915 ptrCurStream = ptrFiles[i]->ptrHead; 916 while (ptrCurStream) 917 { 918 ConvertULargeInteger(ptrCurStream->stStreamInfo.StreamSize.QuadPart, szSize, 20, lpFlags->bTSeparator); 919 920 /* Print the line */ 921 DirPrintf(lpFlags, _T("%10s %-6s %*s%s %s%s\n"), 922 L"", 923 L"", 924 16, 925 szSize, 926 L"", 927 ptrFiles[i]->stFindInfo.cFileName, 928 ptrCurStream->stStreamInfo.cStreamName); 929 ptrCurStream = ptrCurStream->ptrNext; 930 } 931 } 932 } 933 934 935 /* 936 * DirPrintWideList 937 * 938 * The function that prints in wide list 939 */ 940 static VOID 941 DirPrintWideList(PDIRFINDINFO ptrFiles[], /* [IN] Files' Info */ 942 DWORD dwCount, /* [IN] The quantity of files */ 943 TCHAR *szCurPath, /* [IN] Full path of current directory */ 944 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */ 945 { 946 SHORT iScreenWidth; 947 USHORT iColumns; 948 USHORT iLines; 949 UINT_PTR iLongestName; 950 TCHAR szTempFname[MAX_PATH]; 951 DWORD i; 952 DWORD j; 953 DWORD temp; 954 955 /* Calculate longest name */ 956 iLongestName = 1; 957 for (i = 0; i < dwCount; i++) 958 { 959 if (ptrFiles[i]->stFindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 960 { 961 /* Directories need 2 additional characters for brackets */ 962 if ((_tcslen(ptrFiles[i]->stFindInfo.cFileName) + 2) > iLongestName) 963 iLongestName = _tcslen(ptrFiles[i]->stFindInfo.cFileName) + 2; 964 } 965 else 966 { 967 if (_tcslen(ptrFiles[i]->stFindInfo.cFileName) > iLongestName) 968 iLongestName = _tcslen(ptrFiles[i]->stFindInfo.cFileName); 969 } 970 } 971 972 /* Count the highest number of columns */ 973 GetScreenSize(&iScreenWidth, NULL); 974 iColumns = (USHORT)(iScreenWidth / iLongestName); 975 976 /* Check if there is enough space for spaces between names */ 977 if (((iLongestName * iColumns) + iColumns) >= (UINT)iScreenWidth) 978 iColumns --; 979 980 /* A last check at iColumns to avoid division by zero */ 981 if (!iColumns) iColumns = 1; 982 983 /* Calculate the lines that will be printed */ 984 iLines = (USHORT)((dwCount + iColumns - 1) / iColumns); 985 986 for (i = 0; i < iLines && !CheckCtrlBreak(BREAK_INPUT); i++) 987 { 988 for (j = 0; j < iColumns; j++) 989 { 990 if (lpFlags->bWideListColSort) 991 { 992 /* Print Column sorted */ 993 temp = (j * iLines) + i; 994 } 995 else 996 { 997 /* Print Line sorted */ 998 temp = (i * iColumns) + j; 999 } 1000 1001 if (temp >= dwCount) break; 1002 1003 if (ptrFiles[temp]->stFindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 1004 _stprintf(szTempFname, _T("[%s]"), ptrFiles[temp]->stFindInfo.cFileName); 1005 else 1006 _stprintf(szTempFname, _T("%s"), ptrFiles[temp]->stFindInfo.cFileName); 1007 1008 DirPrintf(lpFlags, _T("%-*s"), iLongestName + 1, szTempFname); 1009 } 1010 1011 /* Add a new line after the last item in the column */ 1012 DirPrintf(lpFlags, _T("\n")); 1013 } 1014 } 1015 1016 1017 /* 1018 * DirPrintOldList 1019 * 1020 * The function that prints in old style 1021 */ 1022 static VOID 1023 DirPrintOldList(PDIRFINDINFO ptrFiles[], /* [IN] Files' Info */ 1024 DWORD dwCount, /* [IN] The quantity of files */ 1025 TCHAR * szCurPath, /* [IN] Full path of current directory */ 1026 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */ 1027 { 1028 DWORD i; /* An indexer for "for"s */ 1029 TCHAR szName[10]; /* The name of file */ 1030 TCHAR szExt[5]; /* The extension of file */ 1031 TCHAR szDate[30],szTime[30]; /* Used to format time and date */ 1032 TCHAR szSize[30]; /* The size of file */ 1033 int iSizeFormat; /* The format of size field */ 1034 ULARGE_INTEGER u64FileSize; /* The file size */ 1035 1036 for (i = 0; i < dwCount && !CheckCtrlBreak(BREAK_INPUT); i++) 1037 { 1038 /* Broke 8.3 format */ 1039 if (*ptrFiles[i]->stFindInfo.cAlternateFileName ) 1040 { 1041 /* If the file is long named then we read the alter name */ 1042 getName( ptrFiles[i]->stFindInfo.cAlternateFileName, szName); 1043 _tcscpy(szExt, getExt( ptrFiles[i]->stFindInfo.cAlternateFileName)); 1044 } 1045 else 1046 { 1047 /* If the file is not long name we read its original name */ 1048 getName( ptrFiles[i]->stFindInfo.cFileName, szName); 1049 _tcscpy(szExt, getExt( ptrFiles[i]->stFindInfo.cFileName)); 1050 } 1051 1052 /* Calculate size */ 1053 if (ptrFiles[i]->stFindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 1054 { 1055 /* Directory, no size it's a directory */ 1056 iSizeFormat = -17; 1057 _tcscpy(szSize, _T("<DIR>")); 1058 } 1059 else 1060 { 1061 /* File */ 1062 iSizeFormat = 17; 1063 u64FileSize.HighPart = ptrFiles[i]->stFindInfo.nFileSizeHigh; 1064 u64FileSize.LowPart = ptrFiles[i]->stFindInfo.nFileSizeLow; 1065 ConvertULargeInteger(u64FileSize.QuadPart, szSize, 20, lpFlags->bTSeparator); 1066 } 1067 1068 /* Format date and time */ 1069 DirPrintFileDateTime(szDate,szTime,&ptrFiles[i]->stFindInfo,lpFlags); 1070 1071 /* Print the line */ 1072 DirPrintf(lpFlags, _T("%-8s %-3s %*s %s %s\n"), 1073 szName, /* The file's 8.3 name */ 1074 szExt, /* The file's 8.3 extension */ 1075 iSizeFormat, /* print format for size column */ 1076 szSize, /* The size of file or "<DIR>" for dirs */ 1077 szDate, /* The date of file/dir */ 1078 szTime); /* The time of file/dir */ 1079 } 1080 } 1081 1082 /* 1083 * DirPrintBareList 1084 * 1085 * The function that prints in bare format 1086 */ 1087 static VOID 1088 DirPrintBareList(PDIRFINDINFO ptrFiles[], /* [IN] Files' Info */ 1089 DWORD dwCount, /* [IN] The number of files */ 1090 LPTSTR lpCurPath, /* [IN] Full path of current directory */ 1091 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */ 1092 { 1093 DWORD i; 1094 1095 for (i = 0; i < dwCount && !CheckCtrlBreak(BREAK_INPUT); i++) 1096 { 1097 if ((_tcscmp(ptrFiles[i]->stFindInfo.cFileName, _T(".")) == 0) || 1098 (_tcscmp(ptrFiles[i]->stFindInfo.cFileName, _T("..")) == 0)) 1099 { 1100 /* at bare format we don't print "." and ".." folder */ 1101 continue; 1102 } 1103 if (lpFlags->bRecursive) 1104 { 1105 /* at recursive mode we print full path of file */ 1106 DirPrintf(lpFlags, _T("%s\\%s\n"), lpCurPath, ptrFiles[i]->stFindInfo.cFileName); 1107 } 1108 else 1109 { 1110 /* if we are not in recursive mode we print the file names */ 1111 DirPrintf(lpFlags, _T("%s\n"), ptrFiles[i]->stFindInfo.cFileName); 1112 } 1113 } 1114 } 1115 1116 1117 /* 1118 * DirPrintFiles 1119 * 1120 * The functions that prints the files list 1121 */ 1122 static VOID 1123 DirPrintFiles(PDIRFINDINFO ptrFiles[], /* [IN] Files' Info */ 1124 DWORD dwCount, /* [IN] The quantity of files */ 1125 TCHAR *szCurPath, /* [IN] Full path of current directory */ 1126 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */ 1127 { 1128 TCHAR szMsg[RC_STRING_MAX_SIZE]; 1129 TCHAR szTemp[MAX_PATH]; /* A buffer to format the directory header */ 1130 1131 /* Print trailing backslash for root directory of drive */ 1132 _tcscpy(szTemp, szCurPath); 1133 if (_tcslen(szTemp) == 2 && szTemp[1] == _T(':')) 1134 _tcscat(szTemp, _T("\\")); 1135 1136 /* Condition to print header: 1137 We are not printing in bare format 1138 and if we are in recursive mode... we must have results */ 1139 if (!lpFlags->bBareFormat && !(lpFlags->bRecursive && (dwCount <= 0))) 1140 { 1141 LoadString(CMD_ModuleHandle, STRING_DIR_HELP7, szMsg, ARRAYSIZE(szMsg)); 1142 if (!DirPrintf(lpFlags, szMsg, szTemp)) 1143 return; 1144 } 1145 1146 if (lpFlags->bBareFormat) 1147 { 1148 /* Bare format */ 1149 DirPrintBareList(ptrFiles, dwCount, szCurPath, lpFlags); 1150 } 1151 else if (lpFlags->bShortName) 1152 { 1153 /* New list style / Short names */ 1154 DirPrintNewList(ptrFiles, dwCount, szCurPath, lpFlags); 1155 } 1156 else if (lpFlags->bWideListColSort || lpFlags->bWideList) 1157 { 1158 /* Wide list */ 1159 DirPrintWideList(ptrFiles, dwCount, szCurPath, lpFlags); 1160 } 1161 else if (lpFlags->bNewLongList ) 1162 { 1163 /* New list style*/ 1164 DirPrintNewList(ptrFiles, dwCount, szCurPath, lpFlags); 1165 } 1166 else 1167 { 1168 /* If nothing is selected old list is the default */ 1169 DirPrintOldList(ptrFiles, dwCount, szCurPath, lpFlags); 1170 } 1171 } 1172 1173 /* 1174 * CompareFiles 1175 * 1176 * Compares 2 files based on the order criteria 1177 */ 1178 static BOOL 1179 CompareFiles(PDIRFINDINFO lpFile1, /* [IN] A pointer to WIN32_FIND_DATA of file 1 */ 1180 PDIRFINDINFO lpFile2, /* [IN] A pointer to WIN32_FIND_DATA of file 2 */ 1181 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags that we use to list */ 1182 { 1183 ULARGE_INTEGER u64File1; 1184 ULARGE_INTEGER u64File2; 1185 int i; 1186 long iComp = 0; /* The comparison result */ 1187 1188 /* Calculate criteria by order given from user */ 1189 for (i = 0; i < lpFlags->stOrderBy.sCriteriaCount; i++) 1190 { 1191 1192 /* Calculate criteria */ 1193 switch (lpFlags->stOrderBy.eCriteria[i]) 1194 { 1195 case ORDER_SIZE: /* Order by size /o:s */ 1196 /* concat the 32bit integers to a 64bit */ 1197 u64File1.LowPart = lpFile1->stFindInfo.nFileSizeLow; 1198 u64File1.HighPart = lpFile1->stFindInfo.nFileSizeHigh; 1199 u64File2.LowPart = lpFile2->stFindInfo.nFileSizeLow; 1200 u64File2.HighPart = lpFile2->stFindInfo.nFileSizeHigh; 1201 1202 /* In case that difference is too big for a long */ 1203 if (u64File1.QuadPart < u64File2.QuadPart) 1204 iComp = -1; 1205 else if (u64File1.QuadPart > u64File2.QuadPart) 1206 iComp = 1; 1207 else 1208 iComp = 0; 1209 break; 1210 1211 case ORDER_DIRECTORY: /* Order by directory attribute /o:g */ 1212 iComp = ((lpFile2->stFindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)- 1213 (lpFile1->stFindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)); 1214 break; 1215 1216 case ORDER_EXTENSION: /* Order by extension name /o:e */ 1217 iComp = _tcsicmp(getExt(lpFile1->stFindInfo.cFileName),getExt(lpFile2->stFindInfo.cFileName)); 1218 break; 1219 1220 case ORDER_NAME: /* Order by filename /o:n */ 1221 iComp = _tcsicmp(lpFile1->stFindInfo.cFileName, lpFile2->stFindInfo.cFileName); 1222 break; 1223 1224 case ORDER_TIME: /* Order by file's time /o:t */ 1225 /* We compare files based on the time field selected by /t */ 1226 switch (lpFlags->stTimeField.eTimeField) 1227 { 1228 case TF_CREATIONDATE: 1229 /* concat the 32bit integers to a 64bit */ 1230 u64File1.LowPart = lpFile1->stFindInfo.ftCreationTime.dwLowDateTime; 1231 u64File1.HighPart = lpFile1->stFindInfo.ftCreationTime.dwHighDateTime ; 1232 u64File2.LowPart = lpFile2->stFindInfo.ftCreationTime.dwLowDateTime; 1233 u64File2.HighPart = lpFile2->stFindInfo.ftCreationTime.dwHighDateTime ; 1234 break; 1235 case TF_LASTACCESSEDDATE : 1236 /* concat the 32bit integers to a 64bit */ 1237 u64File1.LowPart = lpFile1->stFindInfo.ftLastAccessTime.dwLowDateTime; 1238 u64File1.HighPart = lpFile1->stFindInfo.ftLastAccessTime.dwHighDateTime ; 1239 u64File2.LowPart = lpFile2->stFindInfo.ftLastAccessTime.dwLowDateTime; 1240 u64File2.HighPart = lpFile2->stFindInfo.ftLastAccessTime.dwHighDateTime ; 1241 break; 1242 case TF_MODIFIEDDATE: 1243 /* concat the 32bit integers to a 64bit */ 1244 u64File1.LowPart = lpFile1->stFindInfo.ftLastWriteTime.dwLowDateTime; 1245 u64File1.HighPart = lpFile1->stFindInfo.ftLastWriteTime.dwHighDateTime ; 1246 u64File2.LowPart = lpFile2->stFindInfo.ftLastWriteTime.dwLowDateTime; 1247 u64File2.HighPart = lpFile2->stFindInfo.ftLastWriteTime.dwHighDateTime ; 1248 break; 1249 } 1250 1251 /* In case that difference is too big for a long */ 1252 if (u64File1.QuadPart < u64File2.QuadPart) 1253 iComp = -1; 1254 else if (u64File1.QuadPart > u64File2.QuadPart) 1255 iComp = 1; 1256 else 1257 iComp = 0; 1258 break; 1259 } 1260 1261 /* Reverse if desired */ 1262 if (lpFlags->stOrderBy.bCriteriaRev[i]) 1263 iComp *= -1; 1264 1265 /* If that criteria was enough for distinguishing 1266 the files/dirs,there is no need to calculate the others*/ 1267 if (iComp != 0) break; 1268 } 1269 1270 /* Translate the value of iComp to boolean */ 1271 return iComp > 0; 1272 } 1273 1274 /* 1275 * QsortFiles 1276 * 1277 * Sort files by the order criterias using quicksort method 1278 */ 1279 static VOID 1280 QsortFiles(PDIRFINDINFO ptrArray[], /* [IN/OUT] The array with file info pointers */ 1281 int i, /* [IN] The index of first item in array */ 1282 int j, /* [IN] The index to last item in array */ 1283 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags that we will use to sort */ 1284 { 1285 PDIRFINDINFO lpTemp; /* A temporary pointer */ 1286 BOOL Way; 1287 1288 if (i < j) 1289 { 1290 int First = i, Last = j, Temp; 1291 Way = TRUE; 1292 while (i != j) 1293 { 1294 if (Way == CompareFiles(ptrArray[i], ptrArray[j], lpFlags)) 1295 { 1296 /* Swap the pointers of the array */ 1297 lpTemp = ptrArray[i]; 1298 ptrArray[i]= ptrArray[j]; 1299 ptrArray[j] = lpTemp; 1300 1301 /* Swap the indexes for inverting sorting */ 1302 Temp = i; 1303 i = j; 1304 j =Temp; 1305 1306 Way = !Way; 1307 } 1308 1309 j += (!Way - Way); 1310 } 1311 1312 QsortFiles(ptrArray,First, i-1, lpFlags); 1313 QsortFiles(ptrArray,i+1,Last, lpFlags); 1314 } 1315 } 1316 1317 /* 1318 * DirList 1319 * 1320 * The functions that does everything except for printing results 1321 */ 1322 static INT 1323 DirList(LPTSTR szPath, /* [IN] The path that dir starts */ 1324 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags of the listing */ 1325 { 1326 BOOL fPoint; /* If szPath is a file with extension fPoint will be True */ 1327 HANDLE hSearch; /* The handle of the search */ 1328 HANDLE hRecSearch; /* The handle for searching recursively */ 1329 HANDLE hStreams; /* The handle for alternate streams */ 1330 WIN32_FIND_DATA wfdFileInfo; /* The info of file that found */ 1331 PDIRFINDINFO * ptrFileArray; /* An array of pointers with all the files */ 1332 PDIRFINDLISTNODE ptrStartNode; /* The pointer to the first node */ 1333 PDIRFINDLISTNODE ptrNextNode; /* A pointer used for relatives references */ 1334 TCHAR szFullPath[MAX_PATH]; /* The full path that we are listing with trailing '\' */ 1335 TCHAR szSubPath[MAX_PATH]; 1336 LPTSTR pszFilePart; 1337 DWORD dwCount; /* A counter of files found in directory */ 1338 DWORD dwCountFiles; /* Counter for files */ 1339 DWORD dwCountDirs; /* Counter for directories */ 1340 ULONGLONG u64CountBytes; /* Counter for bytes */ 1341 ULARGE_INTEGER u64Temp; /* A temporary counter */ 1342 WIN32_FIND_STREAM_DATA wfsdStreamInfo; 1343 PDIRFINDSTREAMNODE * ptrCurNode; /* The pointer to the first stream */ 1344 PDIRFINDSTREAMNODE ptrFreeNode; /* The pointer used during cleanup */ 1345 static HANDLE (WINAPI *pFindFirstStreamW)(LPCWSTR, STREAM_INFO_LEVELS, LPVOID, DWORD); 1346 static BOOL (WINAPI *pFindNextStreamW)(HANDLE, LPVOID); 1347 1348 /* Initialize Variables */ 1349 ptrStartNode = NULL; 1350 ptrNextNode = NULL; 1351 dwCount = 0; 1352 dwCountFiles = 0; 1353 dwCountDirs = 0; 1354 u64CountBytes = 0; 1355 fPoint= FALSE; 1356 1357 /* Create szFullPath */ 1358 if (GetFullPathName(szPath, ARRAYSIZE(szFullPath), szFullPath, &pszFilePart) == 0) 1359 { 1360 _tcscpy (szFullPath, szPath); 1361 pszFilePart = NULL; 1362 } 1363 1364 /* If no wildcard or file was specified and this is a directory, then 1365 display all files in it */ 1366 if (pszFilePart == NULL || IsExistingDirectory(szFullPath)) 1367 { 1368 pszFilePart = &szFullPath[_tcslen(szFullPath)]; 1369 if (pszFilePart[-1] != _T('\\')) 1370 *pszFilePart++ = _T('\\'); 1371 _tcscpy(pszFilePart, _T("*")); 1372 } 1373 1374 /* Prepare the linked list, first node is allocated */ 1375 ptrStartNode = cmd_alloc(sizeof(DIRFINDLISTNODE)); 1376 if (ptrStartNode == NULL) 1377 { 1378 WARN("DEBUG: Cannot allocate memory for ptrStartNode!\n"); 1379 return 1; /* Error cannot allocate memory for 1st object */ 1380 } 1381 ptrStartNode->stInfo.ptrHead = NULL; 1382 ptrNextNode = ptrStartNode; 1383 1384 /* Checking if szPath is a File with/wout extension */ 1385 if (szPath[_tcslen(szPath) - 1] == _T('.')) 1386 fPoint= TRUE; 1387 1388 /* Collect the results for the current folder */ 1389 hSearch = FindFirstFile(szFullPath, &wfdFileInfo); 1390 if (hSearch != INVALID_HANDLE_VALUE) 1391 { 1392 do 1393 { 1394 /* If retrieved FileName has extension,and szPath doesnt have extension then JUMP the retrieved FileName */ 1395 if (_tcschr(wfdFileInfo.cFileName,_T('.')) && (fPoint != FALSE)) 1396 { 1397 continue; 1398 /* Here we filter all the specified attributes */ 1399 } 1400 else if ((wfdFileInfo.dwFileAttributes & lpFlags->stAttribs.dwAttribMask ) 1401 == (lpFlags->stAttribs.dwAttribMask & lpFlags->stAttribs.dwAttribVal )) 1402 { 1403 ptrNextNode->ptrNext = cmd_alloc(sizeof(DIRFINDLISTNODE)); 1404 if (ptrNextNode->ptrNext == NULL) 1405 { 1406 WARN("DEBUG: Cannot allocate memory for ptrNextNode->ptrNext!\n"); 1407 while (ptrStartNode) 1408 { 1409 ptrNextNode = ptrStartNode->ptrNext; 1410 while (ptrStartNode->stInfo.ptrHead) 1411 { 1412 ptrFreeNode = ptrStartNode->stInfo.ptrHead; 1413 ptrStartNode->stInfo.ptrHead = ptrFreeNode->ptrNext; 1414 cmd_free(ptrFreeNode); 1415 } 1416 cmd_free(ptrStartNode); 1417 ptrStartNode = ptrNextNode; 1418 dwCount--; 1419 } 1420 FindClose(hSearch); 1421 return 1; 1422 } 1423 1424 /* Copy the info of search at linked list */ 1425 memcpy(&ptrNextNode->ptrNext->stInfo.stFindInfo, 1426 &wfdFileInfo, 1427 sizeof(WIN32_FIND_DATA)); 1428 1429 /* If lower case is selected do it here */ 1430 if (lpFlags->bLowerCase) 1431 { 1432 _tcslwr(ptrNextNode->ptrNext->stInfo.stFindInfo.cAlternateFileName); 1433 _tcslwr(ptrNextNode->ptrNext->stInfo.stFindInfo.cFileName); 1434 } 1435 1436 /* No streams (yet?) */ 1437 ptrNextNode->ptrNext->stInfo.ptrHead = NULL; 1438 1439 /* Alternate streams are only displayed with new long list */ 1440 if (lpFlags->bNewLongList && lpFlags->bDataStreams) 1441 { 1442 if (!pFindFirstStreamW) 1443 { 1444 pFindFirstStreamW = (PVOID)GetProcAddress(GetModuleHandle(_T("kernel32")), "FindFirstStreamW"); 1445 pFindNextStreamW = (PVOID)GetProcAddress(GetModuleHandle(_T("kernel32")), "FindNextStreamW"); 1446 } 1447 1448 /* Try to get stream information */ 1449 if (pFindFirstStreamW && pFindNextStreamW) 1450 { 1451 hStreams = pFindFirstStreamW(wfdFileInfo.cFileName, FindStreamInfoStandard, &wfsdStreamInfo, 0); 1452 } 1453 else 1454 { 1455 hStreams = INVALID_HANDLE_VALUE; 1456 ERR("FindFirstStreamW not supported!\n"); 1457 } 1458 1459 if (hStreams != INVALID_HANDLE_VALUE) 1460 { 1461 /* We totally ignore first stream. It contains data about ::$DATA */ 1462 ptrCurNode = &ptrNextNode->ptrNext->stInfo.ptrHead; 1463 while (pFindNextStreamW(hStreams, &wfsdStreamInfo)) 1464 { 1465 *ptrCurNode = cmd_alloc(sizeof(DIRFINDSTREAMNODE)); 1466 if (*ptrCurNode == NULL) 1467 { 1468 WARN("DEBUG: Cannot allocate memory for *ptrCurNode!\n"); 1469 while (ptrStartNode) 1470 { 1471 ptrNextNode = ptrStartNode->ptrNext; 1472 while (ptrStartNode->stInfo.ptrHead) 1473 { 1474 ptrFreeNode = ptrStartNode->stInfo.ptrHead; 1475 ptrStartNode->stInfo.ptrHead = ptrFreeNode->ptrNext; 1476 cmd_free(ptrFreeNode); 1477 } 1478 cmd_free(ptrStartNode); 1479 ptrStartNode = ptrNextNode; 1480 dwCount--; 1481 } 1482 FindClose(hStreams); 1483 FindClose(hSearch); 1484 return 1; 1485 } 1486 1487 memcpy(&(*ptrCurNode)->stStreamInfo, &wfsdStreamInfo, 1488 sizeof(WIN32_FIND_STREAM_DATA)); 1489 1490 /* If lower case is selected do it here */ 1491 if (lpFlags->bLowerCase) 1492 { 1493 _tcslwr((*ptrCurNode)->stStreamInfo.cStreamName); 1494 } 1495 1496 ptrCurNode = &(*ptrCurNode)->ptrNext; 1497 } 1498 1499 FindClose(hStreams); 1500 *ptrCurNode = NULL; 1501 } 1502 } 1503 1504 /* Continue at next node at linked list */ 1505 ptrNextNode = ptrNextNode->ptrNext; 1506 dwCount ++; 1507 1508 /* Grab statistics */ 1509 if (wfdFileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 1510 { 1511 /* Directory */ 1512 dwCountDirs++; 1513 } 1514 else 1515 { 1516 /* File */ 1517 dwCountFiles++; 1518 u64Temp.HighPart = wfdFileInfo.nFileSizeHigh; 1519 u64Temp.LowPart = wfdFileInfo.nFileSizeLow; 1520 u64CountBytes += u64Temp.QuadPart; 1521 } 1522 } 1523 } while (FindNextFile(hSearch, &wfdFileInfo)); 1524 FindClose(hSearch); 1525 } 1526 1527 /* Terminate list */ 1528 ptrNextNode->ptrNext = NULL; 1529 1530 /* Calculate and allocate space need for making an array of pointers */ 1531 ptrFileArray = cmd_alloc(sizeof(PDIRFINDINFO) * dwCount); 1532 if (ptrFileArray == NULL) 1533 { 1534 WARN("DEBUG: Cannot allocate memory for ptrFileArray!\n"); 1535 while (ptrStartNode) 1536 { 1537 ptrNextNode = ptrStartNode->ptrNext; 1538 while (ptrStartNode->stInfo.ptrHead) 1539 { 1540 ptrFreeNode = ptrStartNode->stInfo.ptrHead; 1541 ptrStartNode->stInfo.ptrHead = ptrFreeNode->ptrNext; 1542 cmd_free(ptrFreeNode); 1543 } 1544 cmd_free(ptrStartNode); 1545 ptrStartNode = ptrNextNode; 1546 dwCount --; 1547 } 1548 return 1; 1549 } 1550 1551 /* 1552 * Create an array of pointers from the linked list 1553 * this will be used to sort and print data, rather than the list 1554 */ 1555 ptrNextNode = ptrStartNode; 1556 dwCount = 0; 1557 while (ptrNextNode->ptrNext) 1558 { 1559 ptrFileArray[dwCount] = &ptrNextNode->ptrNext->stInfo; 1560 ptrNextNode = ptrNextNode->ptrNext; 1561 dwCount++; 1562 } 1563 1564 /* Sort Data if requested */ 1565 if (lpFlags->stOrderBy.sCriteriaCount > 0) 1566 QsortFiles(ptrFileArray, 0, dwCount-1, lpFlags); 1567 1568 /* Print Data */ 1569 pszFilePart[-1] = _T('\0'); /* truncate to directory name only */ 1570 DirPrintFiles(ptrFileArray, dwCount, szFullPath, lpFlags); 1571 pszFilePart[-1] = _T('\\'); 1572 1573 if (lpFlags->bRecursive) 1574 { 1575 PrintSummary(szFullPath, 1576 dwCountFiles, 1577 dwCountDirs, 1578 u64CountBytes, 1579 lpFlags, 1580 FALSE); 1581 } 1582 1583 /* Free array */ 1584 cmd_free(ptrFileArray); 1585 /* Free linked list */ 1586 while (ptrStartNode) 1587 { 1588 ptrNextNode = ptrStartNode->ptrNext; 1589 while (ptrStartNode->stInfo.ptrHead) 1590 { 1591 ptrFreeNode = ptrStartNode->stInfo.ptrHead; 1592 ptrStartNode->stInfo.ptrHead = ptrFreeNode->ptrNext; 1593 cmd_free(ptrFreeNode); 1594 } 1595 cmd_free(ptrStartNode); 1596 ptrStartNode = ptrNextNode; 1597 dwCount --; 1598 } 1599 1600 if (CheckCtrlBreak(BREAK_INPUT)) 1601 return 1; 1602 1603 /* Add statistics to recursive statistics */ 1604 recurse_dir_cnt += dwCountDirs; 1605 recurse_file_cnt += dwCountFiles; 1606 recurse_bytes += u64CountBytes; 1607 1608 /* 1609 * Do the recursive job if requested. 1610 * The recursion is done on ALL (independent of their attributes) 1611 * directories of the current one. 1612 */ 1613 if (lpFlags->bRecursive) 1614 { 1615 /* The new search is involving any *.* file */ 1616 memcpy(szSubPath, szFullPath, (pszFilePart - szFullPath) * sizeof(TCHAR)); 1617 _tcscpy(&szSubPath[pszFilePart - szFullPath], _T("*.*")); 1618 1619 hRecSearch = FindFirstFile (szSubPath, &wfdFileInfo); 1620 if (hRecSearch != INVALID_HANDLE_VALUE) 1621 { 1622 do 1623 { 1624 /* We search for directories other than "." and ".." */ 1625 if ((_tcsicmp(wfdFileInfo.cFileName, _T(".")) != 0) && 1626 (_tcsicmp(wfdFileInfo.cFileName, _T("..")) != 0 ) && 1627 (wfdFileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) 1628 { 1629 /* Concat the path and the directory to do recursive */ 1630 memcpy(szSubPath, szFullPath, (pszFilePart - szFullPath) * sizeof(TCHAR)); 1631 _tcscpy(&szSubPath[pszFilePart - szFullPath], wfdFileInfo.cFileName); 1632 _tcscat(szSubPath, _T("\\")); 1633 _tcscat(szSubPath, pszFilePart); 1634 1635 /* We do the same for the folder */ 1636 if (DirList(szSubPath, lpFlags) != 0) 1637 { 1638 FindClose(hRecSearch); 1639 return 1; 1640 } 1641 } 1642 } while (FindNextFile(hRecSearch, &wfdFileInfo)); 1643 } 1644 FindClose(hRecSearch); 1645 } 1646 1647 return 0; 1648 } 1649 1650 /* 1651 * dir 1652 * 1653 * internal dir command 1654 */ 1655 INT 1656 CommandDir(LPTSTR rest) 1657 { 1658 TCHAR dircmd[256]; /* A variable to store the DIRCMD enviroment variable */ 1659 TCHAR path[MAX_PATH]; 1660 TCHAR prev_volume[MAX_PATH]; 1661 LPTSTR* params = NULL; 1662 LPTSTR pszFilePart; 1663 INT entries = 0; 1664 UINT loop = 0; 1665 DIRSWITCHFLAGS stFlags; 1666 INT ret = 1; 1667 BOOL ChangedVolume; 1668 1669 /* Initialize Switch Flags < Default switches are setted here!> */ 1670 stFlags.b4Digit = TRUE; 1671 stFlags.bBareFormat = FALSE; 1672 stFlags.bDataStreams = FALSE; 1673 stFlags.bLowerCase = FALSE; 1674 stFlags.bNewLongList = TRUE; 1675 stFlags.bPause = FALSE; 1676 stFlags.bRecursive = FALSE; 1677 stFlags.bShortName = FALSE; 1678 stFlags.bTSeparator = TRUE; 1679 stFlags.bUser = FALSE; 1680 stFlags.bWideList = FALSE; 1681 stFlags.bWideListColSort = FALSE; 1682 stFlags.stTimeField.eTimeField = TF_MODIFIEDDATE; 1683 stFlags.stAttribs.dwAttribMask = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM; 1684 stFlags.stAttribs.dwAttribVal = 0L; 1685 stFlags.stOrderBy.sCriteriaCount = 0; 1686 1687 nErrorLevel = 0; 1688 1689 /* read the parameters from the DIRCMD environment variable */ 1690 if (GetEnvironmentVariable (_T("DIRCMD"), dircmd, 256)) 1691 { 1692 if (!DirReadParam(dircmd, ¶ms, &entries, &stFlags)) 1693 { 1694 nErrorLevel = 1; 1695 goto cleanup; 1696 } 1697 } 1698 1699 /* read the parameters */ 1700 if (!DirReadParam(rest, ¶ms, &entries, &stFlags) || CheckCtrlBreak(BREAK_INPUT)) 1701 { 1702 nErrorLevel = 1; 1703 goto cleanup; 1704 } 1705 1706 /* default to current directory */ 1707 if (entries == 0) 1708 { 1709 if (!add_entry(&entries, ¶ms, _T("*"))) 1710 { 1711 nErrorLevel = 1; 1712 goto cleanup; 1713 } 1714 } 1715 1716 prev_volume[0] = _T('\0'); 1717 1718 /* Reset paging state */ 1719 if (stFlags.bPause) 1720 ConOutPrintfPaging(TRUE, _T("")); 1721 1722 for (loop = 0; loop < (UINT)entries; loop++) 1723 { 1724 if (CheckCtrlBreak(BREAK_INPUT)) 1725 { 1726 nErrorLevel = 1; 1727 goto cleanup; 1728 } 1729 1730 recurse_dir_cnt = 0L; 1731 recurse_file_cnt = 0L; 1732 recurse_bytes = 0; 1733 1734 /* <Debug :> 1735 Uncomment this to show the final state of switch flags*/ 1736 { 1737 int i; 1738 TRACE("Attributes mask/value %x/%x\n",stFlags.stAttribs.dwAttribMask,stFlags.stAttribs.dwAttribVal ); 1739 TRACE("(B) Bare format : %i\n", stFlags.bBareFormat ); 1740 TRACE("(C) Thousand : %i\n", stFlags.bTSeparator ); 1741 TRACE("(W) Wide list : %i\n", stFlags.bWideList ); 1742 TRACE("(D) Wide list sort by column : %i\n", stFlags.bWideListColSort ); 1743 TRACE("(L) Lowercase : %i\n", stFlags.bLowerCase ); 1744 TRACE("(N) New : %i\n", stFlags.bNewLongList ); 1745 TRACE("(O) Order : %i\n", stFlags.stOrderBy.sCriteriaCount ); 1746 for (i =0;i<stFlags.stOrderBy.sCriteriaCount;i++) 1747 TRACE(" Order Criteria [%i]: %i (Reversed: %i)\n",i, stFlags.stOrderBy.eCriteria[i], stFlags.stOrderBy.bCriteriaRev[i] ); 1748 TRACE("(P) Pause : %i\n", stFlags.bPause ); 1749 TRACE("(Q) Owner : %i\n", stFlags.bUser ); 1750 TRACE("(R) Data stream : %i\n", stFlags.bDataStreams ); 1751 TRACE("(S) Recursive : %i\n", stFlags.bRecursive ); 1752 TRACE("(T) Time field : %i\n", stFlags.stTimeField.eTimeField ); 1753 TRACE("(X) Short names : %i\n", stFlags.bShortName ); 1754 TRACE("Parameter : %s\n", debugstr_aw(params[loop]) ); 1755 } 1756 1757 /* Print the drive header if the volume changed */ 1758 ChangedVolume = TRUE; 1759 1760 if (!stFlags.bBareFormat && 1761 GetVolumePathName(params[loop], path, ARRAYSIZE(path))) 1762 { 1763 if (!_tcscmp(path, prev_volume)) 1764 ChangedVolume = FALSE; 1765 else 1766 _tcscpy(prev_volume, path); 1767 } 1768 else if (GetFullPathName(params[loop], ARRAYSIZE(path), path, &pszFilePart) != 0) 1769 { 1770 if (pszFilePart != NULL) 1771 *pszFilePart = _T('\0'); 1772 } 1773 else 1774 { 1775 _tcscpy(path, params[loop]); 1776 } 1777 1778 if (ChangedVolume && !stFlags.bBareFormat) 1779 { 1780 if (!PrintDirectoryHeader (params[loop], &stFlags)) 1781 { 1782 nErrorLevel = 1; 1783 goto cleanup; 1784 } 1785 } 1786 1787 /* do the actual dir */ 1788 if (DirList(params[loop], &stFlags)) 1789 { 1790 nErrorLevel = 1; 1791 goto cleanup; 1792 } 1793 1794 /* print the footer */ 1795 PrintSummary(path, 1796 recurse_file_cnt, 1797 recurse_dir_cnt, 1798 recurse_bytes, 1799 &stFlags, 1800 TRUE); 1801 } 1802 1803 ret = 0; 1804 1805 cleanup: 1806 freep(params); 1807 1808 return ret; 1809 } 1810 1811 #endif 1812 1813 /* EOF */ 1814