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 * 6-Aug-2018 (Hermes Belusca-Maito and Katayama Hirofumi MZ) 140 * Fix handling of patterns containing trailing dots. 141 */ 142 143 #include "precomp.h" 144 145 #ifdef INCLUDE_CMD_DIR 146 147 /* Time Field enumeration */ 148 enum ETimeField 149 { 150 TF_CREATIONDATE = 0, 151 TF_MODIFIEDDATE = 1, 152 TF_LASTACCESSEDDATE = 2 153 }; 154 155 /* Ordered by enumeration */ 156 enum EOrderBy 157 { 158 ORDER_NAME = 0, 159 ORDER_SIZE = 1, 160 ORDER_DIRECTORY = 2, 161 ORDER_EXTENSION = 3, 162 ORDER_TIME = 4 163 }; 164 165 /* The struct for holding the switches */ 166 typedef struct _DirSwitchesFlags 167 { 168 BOOL bBareFormat; /* Bare Format */ 169 BOOL bTSeparator; /* Thousands separator */ 170 BOOL bWideList; /* Wide list format */ 171 BOOL bWideListColSort; /* Wide list format but sorted by column */ 172 BOOL bLowerCase; /* Uses lower case */ 173 BOOL bNewLongList; /* New long list */ 174 BOOL bPause; /* Pause per page */ 175 BOOL bUser; /* Displays the owner of file */ 176 BOOL bRecursive; /* Displays files in specified directory and all sub */ 177 BOOL bShortName; /* Displays the sort name of files if exist */ 178 BOOL b4Digit; /* Four digit year */ 179 BOOL bDataStreams; /* Displays alternate data streams */ 180 struct 181 { 182 DWORD dwAttribVal; /* The desired state of attribute */ 183 DWORD dwAttribMask; /* Which attributes to check */ 184 } stAttribs; /* Displays files with this attributes only */ 185 struct 186 { 187 enum EOrderBy eCriteria[3]; /* Criterias used to order by */ 188 BOOL bCriteriaRev[3]; /* If the criteria is in reversed order */ 189 short sCriteriaCount; /* The quantity of criterias */ 190 } stOrderBy; /* Ordered by criterias */ 191 struct 192 { 193 enum ETimeField eTimeField; /* The time field that will be used for */ 194 } stTimeField; /* The time field to display or use for sorting */ 195 } DIRSWITCHFLAGS, *LPDIRSWITCHFLAGS; 196 197 typedef struct _DIRFINDSTREAMNODE 198 { 199 WIN32_FIND_STREAM_DATA stStreamInfo; 200 struct _DIRFINDSTREAMNODE *ptrNext; 201 } DIRFINDSTREAMNODE, *PDIRFINDSTREAMNODE; 202 203 typedef struct _DIRFINDINFO 204 { 205 WIN32_FIND_DATA stFindInfo; 206 PDIRFINDSTREAMNODE ptrHead; 207 } DIRFINDINFO, *PDIRFINDINFO; 208 209 typedef struct _DIRFINDLISTNODE 210 { 211 DIRFINDINFO stInfo; 212 struct _DIRFINDLISTNODE *ptrNext; 213 } DIRFINDLISTNODE, *PDIRFINDLISTNODE; 214 215 typedef BOOL 216 (WINAPI *PGETFREEDISKSPACEEX)(LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER); 217 218 /* Globally save the # of dirs, files and bytes, 219 * probably later pass them to functions. Rob Lake */ 220 static ULONG recurse_dir_cnt; 221 static ULONG recurse_file_cnt; 222 static ULONGLONG recurse_bytes; 223 224 /* 225 * help 226 * 227 * displays help screen for dir 228 * Rob Lake 229 */ 230 static VOID 231 DirHelp(VOID) 232 { 233 ConOutResPaging(TRUE, STRING_DIR_HELP1); 234 } 235 236 /* Check whether this is a dot-directory "." or "..", speed-optimized */ 237 FORCEINLINE 238 BOOL 239 IsDotDirectory( 240 IN LPCTSTR pszPath) 241 { 242 return ( pszPath[0] == _T('.') && 243 ( pszPath[1] == 0 || /* pszPath[1] == _T('\\') || */ 244 (pszPath[1] == _T('.') && (pszPath[2] == 0 /* || pszPath[2] == _T('\\') */)) 245 ) ); 246 } 247 248 FORCEINLINE 249 BOOL 250 IsDotDirectoryN( 251 IN const TCHAR* pPath, 252 IN SIZE_T Length) 253 { 254 return ((Length == 1 && pPath[0] == _T('.')) || 255 (Length == 2 && pPath[0] == _T('.') && pPath[1] == _T('.'))); 256 } 257 258 /* 259 * DirReadParameters 260 * 261 * Parse the parameters and switches of the command line and exports them 262 */ 263 static BOOL 264 DirReadParam(LPTSTR Line, /* [IN] The line with the parameters & switches */ 265 LPTSTR** params, /* [OUT] The parameters after parsing */ 266 LPINT entries, /* [OUT] The number of parameters after parsing */ 267 LPDIRSWITCHFLAGS lpFlags) /* [IN/OUT] The flags after calculating switches */ 268 { 269 TCHAR cCurSwitch; /* The current switch */ 270 TCHAR cCurChar; /* Current examined character */ 271 TCHAR cCurUChar; /* Current upper examined character */ 272 BOOL bNegative; /* Negative switch */ 273 BOOL bPNegative; /* Negative switch parameter */ 274 BOOL bIntoQuotes; /* A flag showing if we are in quotes (") */ 275 LPTSTR ptrStart; /* A pointer to the first character of a parameter */ 276 LPTSTR ptrEnd; /* A pointer to the last character of a parameter */ 277 BOOL bOrderByNoPar; /* A flag to indicate /O with no switch parameter */ 278 LPTSTR temp; 279 280 /* Initialize parameter array */ 281 *params = NULL; 282 *entries = 0; 283 284 /* Initialize variables; */ 285 cCurSwitch = _T(' '); 286 bNegative = FALSE; 287 bPNegative = FALSE; 288 289 /* We suppose that switch parameters 290 were given to avoid setting them to default 291 if the switch was not given */ 292 bOrderByNoPar = FALSE; 293 294 /* Main Loop (see README_DIR.txt) */ 295 /* scan the command line char per char, and we process its char */ 296 while (*Line) 297 { 298 /* we save current character as it is and its upper case */ 299 cCurChar = *Line; 300 cCurUChar = _totupper(*Line); 301 302 /* 1st section (see README_DIR.txt) */ 303 /* When a switch is expecting */ 304 if (cCurSwitch == _T('/')) 305 { 306 while (_istspace(*Line)) 307 Line++; 308 309 bNegative = (*Line == _T('-')); 310 Line += bNegative; 311 312 cCurChar = *Line; 313 cCurUChar = _totupper(*Line); 314 315 if ((cCurUChar == _T('A')) ||(cCurUChar == _T('T')) || (cCurUChar == _T('O'))) 316 { 317 /* If positive, prepare for parameters... if negative, reset to defaults */ 318 switch (cCurUChar) 319 { 320 case _T('A'): 321 lpFlags->stAttribs.dwAttribVal = 0L; 322 lpFlags->stAttribs.dwAttribMask = 0L; 323 if (bNegative) 324 lpFlags->stAttribs.dwAttribMask = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM; 325 break; 326 case _T('T'): 327 if (bNegative) 328 lpFlags->stTimeField.eTimeField = TF_MODIFIEDDATE; 329 break; 330 case _T('O'): 331 bOrderByNoPar = !bNegative; 332 lpFlags->stOrderBy.sCriteriaCount = 0; 333 break; 334 } 335 336 if (!bNegative) 337 { 338 /* Positive switch, so it can take parameters. */ 339 cCurSwitch = cCurUChar; 340 Line++; 341 /* Skip optional leading colon */ 342 if (*Line == _T(':')) 343 Line++; 344 continue; 345 } 346 } 347 else if (cCurUChar == _T('L')) 348 lpFlags->bLowerCase = ! bNegative; 349 else if (cCurUChar == _T('B')) 350 lpFlags->bBareFormat = ! bNegative; 351 else if (cCurUChar == _T('C')) 352 lpFlags->bTSeparator = ! bNegative; 353 else if (cCurUChar == _T('W')) 354 lpFlags->bWideList = ! bNegative; 355 else if (cCurUChar == _T('D')) 356 lpFlags->bWideListColSort = ! bNegative; 357 else if (cCurUChar == _T('N')) 358 lpFlags->bNewLongList = ! bNegative; 359 else if (cCurUChar == _T('P')) 360 lpFlags->bPause = ! bNegative; 361 else if (cCurUChar == _T('Q')) 362 lpFlags->bUser = ! bNegative; 363 else if (cCurUChar == _T('S')) 364 lpFlags->bRecursive = ! bNegative; 365 else if (cCurUChar == _T('X')) 366 lpFlags->bShortName = ! bNegative; 367 else if (cCurUChar == _T('R')) 368 lpFlags->bDataStreams = ! bNegative; 369 else if (cCurChar == _T('4')) 370 lpFlags->b4Digit = ! bNegative; 371 else if (cCurChar == _T('?')) 372 { 373 DirHelp(); 374 return FALSE; 375 } 376 else 377 { 378 error_invalid_switch ((TCHAR)_totupper(*Line)); 379 return FALSE; 380 } 381 382 /* Make sure there's no extra characters at the end of the switch */ 383 if (Line[1] && Line[1] != _T('/') && !_istspace(Line[1])) 384 { 385 error_parameter_format(Line[1]); 386 return FALSE; 387 } 388 389 cCurSwitch = _T(' '); 390 } 391 else if (cCurSwitch == _T(' ')) 392 { 393 /* 2nd section (see README_DIR.txt) */ 394 /* We are expecting parameter or the unknown */ 395 396 if (cCurChar == _T('/')) 397 cCurSwitch = _T('/'); 398 else if (_istspace(cCurChar)) 399 /* do nothing */; 400 else 401 { 402 /* This is a file/directory name parameter. Find its end */ 403 ptrStart = Line; 404 bIntoQuotes = FALSE; 405 while (*Line) 406 { 407 if (!bIntoQuotes && (*Line == _T('/') || _istspace(*Line))) 408 break; 409 bIntoQuotes ^= (*Line == _T('"')); 410 Line++; 411 } 412 ptrEnd = Line; 413 414 /* Copy it to the entries list */ 415 temp = cmd_alloc((ptrEnd - ptrStart + 1) * sizeof(TCHAR)); 416 if (!temp) 417 return FALSE; 418 memcpy(temp, ptrStart, (ptrEnd - ptrStart) * sizeof(TCHAR)); 419 temp[ptrEnd - ptrStart] = _T('\0'); 420 StripQuotes(temp); 421 if (!add_entry(entries, params, temp)) 422 { 423 cmd_free(temp); 424 freep(*params); 425 return FALSE; 426 } 427 428 cmd_free(temp); 429 continue; 430 } 431 } 432 else 433 { 434 /* 3rd section (see README_DIR.txt) */ 435 /* We are waiting for switch parameters */ 436 437 /* Check if there are no more switch parameters */ 438 if ((cCurChar == _T('/')) || _istspace(cCurChar)) 439 { 440 /* Wrong decision path, reprocess current character */ 441 cCurSwitch = _T(' '); 442 continue; 443 } 444 /* Process parameter switch */ 445 switch (cCurSwitch) 446 { 447 case _T('A'): /* Switch parameters for /A (attributes filter) */ 448 if (cCurChar == _T('-')) 449 bPNegative = TRUE; 450 else if (cCurUChar == _T('D')) 451 { 452 lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_DIRECTORY; 453 if (bPNegative) 454 lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_DIRECTORY; 455 else 456 lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_DIRECTORY; 457 } 458 else if (cCurUChar == _T('R')) 459 { 460 lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_READONLY; 461 if (bPNegative) 462 lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_READONLY; 463 else 464 lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_READONLY; 465 } 466 else if (cCurUChar == _T('H')) 467 { 468 lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_HIDDEN; 469 if (bPNegative) 470 lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_HIDDEN; 471 else 472 lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_HIDDEN; 473 } 474 else if (cCurUChar == _T('A')) 475 { 476 lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_ARCHIVE; 477 if (bPNegative) 478 lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_ARCHIVE; 479 else 480 lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_ARCHIVE; 481 } 482 else if (cCurUChar == _T('S')) 483 { 484 lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_SYSTEM; 485 if (bPNegative) 486 lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_SYSTEM; 487 else 488 lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_SYSTEM; 489 } 490 else 491 { 492 error_parameter_format((TCHAR)_totupper (*Line)); 493 return FALSE; 494 } 495 break; 496 case _T('T'): /* Switch parameters for /T (time field) */ 497 if (cCurUChar == _T('C')) 498 lpFlags->stTimeField.eTimeField= TF_CREATIONDATE ; 499 else if (cCurUChar == _T('A')) 500 lpFlags->stTimeField.eTimeField= TF_LASTACCESSEDDATE ; 501 else if (cCurUChar == _T('W')) 502 lpFlags->stTimeField.eTimeField= TF_MODIFIEDDATE ; 503 else 504 { 505 error_parameter_format((TCHAR)_totupper (*Line)); 506 return FALSE; 507 } 508 break; 509 case _T('O'): /* Switch parameters for /O (order) */ 510 /* Ok a switch parameter was given */ 511 bOrderByNoPar = FALSE; 512 513 if (cCurChar == _T('-')) 514 bPNegative = TRUE; 515 else if (cCurUChar == _T('N')) 516 { 517 if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++; 518 lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative; 519 lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_NAME; 520 } 521 else if (cCurUChar == _T('S')) 522 { 523 if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++; 524 lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative; 525 lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_SIZE; 526 } 527 else if (cCurUChar == _T('G')) 528 { 529 if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++; 530 lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative; 531 lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_DIRECTORY; 532 } 533 else if (cCurUChar == _T('E')) 534 { 535 if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++; 536 lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative; 537 lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_EXTENSION; 538 } 539 else if (cCurUChar == _T('D')) 540 { 541 if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++; 542 lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative; 543 lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_TIME; 544 } 545 546 else 547 { 548 error_parameter_format((TCHAR)_totupper (*Line)); 549 return FALSE; 550 } 551 552 553 } 554 /* We check if we calculated the negative value and release the flag */ 555 if ((cCurChar != _T('-')) && bPNegative) 556 bPNegative = FALSE; 557 } 558 559 Line++; 560 } 561 562 /* /O with no switch parameters acts like /O:GN */ 563 if (bOrderByNoPar) 564 { 565 lpFlags->stOrderBy.sCriteriaCount = 2; 566 lpFlags->stOrderBy.eCriteria[0] = ORDER_DIRECTORY; 567 lpFlags->stOrderBy.bCriteriaRev[0] = FALSE; 568 lpFlags->stOrderBy.eCriteria[1] = ORDER_NAME; 569 lpFlags->stOrderBy.bCriteriaRev[1] = FALSE; 570 } 571 572 return TRUE; 573 } 574 575 /* Print either with or without paging, depending on /P switch */ 576 static BOOL 577 DirPrintf(LPDIRSWITCHFLAGS lpFlags, LPTSTR szFormat, ...) 578 { 579 BOOL Done = TRUE; 580 va_list arg_ptr; 581 va_start(arg_ptr, szFormat); 582 if (lpFlags->bPause) 583 Done = ConPrintfVPaging(&StdOutPager, FALSE, szFormat, arg_ptr); 584 else 585 ConPrintfV(StdOut, szFormat, arg_ptr); 586 va_end(arg_ptr); 587 return Done; 588 } 589 590 591 /* 592 * PrintDirectoryHeader 593 * 594 * print the header for the dir command 595 */ 596 static BOOL 597 PrintDirectoryHeader(LPCTSTR szPath, LPDIRSWITCHFLAGS lpFlags) 598 { 599 TCHAR szMsg[RC_STRING_MAX_SIZE]; 600 LPCTSTR szFullDir; 601 TCHAR szRootName[MAX_PATH]; 602 TCHAR szVolName[80]; 603 DWORD dwSerialNr; 604 605 if (lpFlags->bBareFormat) 606 return TRUE; 607 608 szFullDir = szPath; 609 610 /* Get the media ID of the drive */ 611 if (!GetVolumePathName(szFullDir, szRootName, ARRAYSIZE(szRootName)) || 612 !GetVolumeInformation(szRootName, szVolName, ARRAYSIZE(szVolName), 613 &dwSerialNr, NULL, NULL, NULL, 0)) 614 { 615 return TRUE; 616 } 617 618 /* Print drive info */ 619 if (szVolName[0] != _T('\0')) 620 { 621 LoadString(CMD_ModuleHandle, STRING_DIR_HELP2, szMsg, ARRAYSIZE(szMsg)); 622 DirPrintf(lpFlags, szMsg, _totupper(szRootName[0]), szVolName); 623 } 624 else 625 { 626 LoadString(CMD_ModuleHandle, STRING_DIR_HELP3, szMsg, ARRAYSIZE(szMsg)); 627 DirPrintf(lpFlags, szMsg, _totupper(szRootName[0])); 628 } 629 630 /* Print the volume serial number if the return was successful */ 631 LoadString(CMD_ModuleHandle, STRING_DIR_HELP4, szMsg, ARRAYSIZE(szMsg)); 632 DirPrintf(lpFlags, szMsg, HIWORD(dwSerialNr), LOWORD(dwSerialNr)); 633 634 return TRUE; 635 } 636 637 638 static VOID 639 DirPrintFileDateTime(TCHAR *lpDate, 640 TCHAR *lpTime, 641 LPWIN32_FIND_DATA lpFile, 642 LPDIRSWITCHFLAGS lpFlags) 643 { 644 FILETIME ft; 645 SYSTEMTIME dt; 646 647 /* Select the right time field */ 648 switch (lpFlags->stTimeField.eTimeField) 649 { 650 case TF_CREATIONDATE: 651 if (!FileTimeToLocalFileTime(&lpFile->ftCreationTime, &ft)) 652 return; 653 FileTimeToSystemTime(&ft, &dt); 654 break; 655 656 case TF_LASTACCESSEDDATE : 657 if (!FileTimeToLocalFileTime(&lpFile->ftLastAccessTime, &ft)) 658 return; 659 FileTimeToSystemTime(&ft, &dt); 660 break; 661 662 case TF_MODIFIEDDATE: 663 if (!FileTimeToLocalFileTime(&lpFile->ftLastWriteTime, &ft)) 664 return; 665 FileTimeToSystemTime(&ft, &dt); 666 break; 667 } 668 669 FormatDate(lpDate, &dt, lpFlags->b4Digit); 670 FormatTime(lpTime, &dt); 671 } 672 673 INT 674 FormatDate(TCHAR *lpDate, LPSYSTEMTIME dt, BOOL b4Digit) 675 { 676 /* Format date */ 677 WORD wYear = b4Digit ? dt->wYear : dt->wYear%100; 678 switch (nDateFormat) 679 { 680 case 0: /* mmddyy */ 681 default: 682 return _stprintf(lpDate, _T("%02d%c%02d%c%0*d"), 683 dt->wMonth, cDateSeparator, 684 dt->wDay, cDateSeparator, 685 b4Digit?4:2, wYear); 686 break; 687 688 case 1: /* ddmmyy */ 689 return _stprintf(lpDate, _T("%02d%c%02d%c%0*d"), 690 dt->wDay, cDateSeparator, dt->wMonth, 691 cDateSeparator, b4Digit?4:2, wYear); 692 break; 693 694 case 2: /* yymmdd */ 695 return _stprintf(lpDate, _T("%0*d%c%02d%c%02d"), 696 b4Digit?4:2, wYear, cDateSeparator, 697 dt->wMonth, cDateSeparator, dt->wDay); 698 break; 699 } 700 } 701 702 INT 703 FormatTime(TCHAR *lpTime, LPSYSTEMTIME dt) 704 { 705 /* Format Time */ 706 switch (nTimeFormat) 707 { 708 case 0: /* 12 hour format */ 709 default: 710 return _stprintf(lpTime,_T("%02d%c%02u %cM"), 711 (dt->wHour == 0 ? 12 : (dt->wHour <= 12 ? dt->wHour : dt->wHour - 12)), 712 cTimeSeparator, 713 dt->wMinute, (dt->wHour <= 11 ? _T('A') : _T('P'))); 714 break; 715 716 case 1: /* 24 hour format */ 717 return _stprintf(lpTime, _T("%02d%c%02u"), 718 dt->wHour, cTimeSeparator, dt->wMinute); 719 break; 720 } 721 } 722 723 724 static VOID 725 GetUserDiskFreeSpace(LPCTSTR lpRoot, 726 PULARGE_INTEGER lpFreeSpace) 727 { 728 PGETFREEDISKSPACEEX pGetFreeDiskSpaceEx; 729 HINSTANCE hInstance; 730 DWORD dwSecPerCl; 731 DWORD dwBytPerSec; 732 DWORD dwFreeCl; 733 DWORD dwTotCl; 734 ULARGE_INTEGER TotalNumberOfBytes, TotalNumberOfFreeBytes; 735 736 lpFreeSpace->QuadPart = 0; 737 738 hInstance = GetModuleHandle(_T("KERNEL32")); 739 if (hInstance != NULL) 740 { 741 pGetFreeDiskSpaceEx = (PGETFREEDISKSPACEEX)GetProcAddress(hInstance, 742 #ifdef _UNICODE 743 "GetDiskFreeSpaceExW"); 744 #else 745 "GetDiskFreeSpaceExA"); 746 #endif 747 if (pGetFreeDiskSpaceEx != NULL) 748 { 749 if (pGetFreeDiskSpaceEx(lpRoot, lpFreeSpace, &TotalNumberOfBytes, &TotalNumberOfFreeBytes) != FALSE) 750 return; 751 } 752 } 753 754 GetDiskFreeSpace(lpRoot, 755 &dwSecPerCl, 756 &dwBytPerSec, 757 &dwFreeCl, 758 &dwTotCl); 759 760 lpFreeSpace->QuadPart = dwSecPerCl * dwBytPerSec * dwFreeCl; 761 } 762 763 764 /* 765 * print_summary: prints dir summary 766 * Added by Rob Lake 06/17/98 to compact code 767 * Just copied Tim's Code and patched it a bit 768 */ 769 static INT 770 PrintSummary(LPCTSTR szPath, 771 ULONG ulFiles, 772 ULONG ulDirs, 773 ULONGLONG u64Bytes, 774 LPDIRSWITCHFLAGS lpFlags, 775 BOOL TotalSummary) 776 { 777 TCHAR szMsg[RC_STRING_MAX_SIZE]; 778 TCHAR szBuffer[64]; 779 ULARGE_INTEGER uliFree; 780 781 /* Here we check if we didn't find anything */ 782 if (!(ulFiles + ulDirs)) 783 { 784 if (!lpFlags->bRecursive || (TotalSummary && lpFlags->bRecursive)) 785 error_file_not_found(); 786 return 1; 787 } 788 789 /* In bare format we don't print results */ 790 if (lpFlags->bBareFormat) 791 return 0; 792 793 /* Print recursive specific results */ 794 795 /* Take this code offline to fix /S does not print double info */ 796 if (TotalSummary && lpFlags->bRecursive) 797 { 798 ConvertULargeInteger(u64Bytes, szBuffer, ARRAYSIZE(szBuffer), lpFlags->bTSeparator); 799 LoadString(CMD_ModuleHandle, STRING_DIR_HELP5, szMsg, ARRAYSIZE(szMsg)); 800 DirPrintf(lpFlags, szMsg, ulFiles, szBuffer); 801 } 802 else 803 { 804 /* Print File Summary */ 805 /* Condition to print summary is: 806 If we are not in bare format and if we have results! */ 807 ConvertULargeInteger(u64Bytes, szBuffer, ARRAYSIZE(szBuffer), lpFlags->bTSeparator); 808 LoadString(CMD_ModuleHandle, STRING_DIR_HELP8, szMsg, ARRAYSIZE(szMsg)); 809 DirPrintf(lpFlags, szMsg, ulFiles, szBuffer); 810 } 811 812 /* Print total directories and free space */ 813 if (!lpFlags->bRecursive || TotalSummary) 814 { 815 GetUserDiskFreeSpace(szPath, &uliFree); 816 ConvertULargeInteger(uliFree.QuadPart, szBuffer, ARRAYSIZE(szBuffer), lpFlags->bTSeparator); 817 LoadString(CMD_ModuleHandle, STRING_DIR_HELP6, szMsg, ARRAYSIZE(szMsg)); 818 DirPrintf(lpFlags, szMsg, ulDirs, szBuffer); 819 } 820 821 return 0; 822 } 823 824 /* 825 * getExt 826 * 827 * Get the extension of a filename 828 */ 829 TCHAR* getExt(const TCHAR* file) 830 { 831 static TCHAR *NoExt = _T(""); 832 TCHAR* lastdot = _tcsrchr(file, _T('.')); 833 return (lastdot != NULL ? lastdot + 1 : NoExt); 834 } 835 836 /* 837 * getName 838 * 839 * Get the name of the file without extension 840 */ 841 static LPTSTR 842 getName(const TCHAR* file, TCHAR * dest) 843 { 844 INT_PTR iLen; 845 LPTSTR end; 846 847 /* Check for dot-directories "." and ".." */ 848 if (IsDotDirectory(file)) 849 { 850 _tcscpy(dest, file); 851 return dest; 852 } 853 854 end = _tcsrchr(file, _T('.')); 855 if (!end) 856 iLen = _tcslen(file); 857 else 858 iLen = (end - file); 859 860 _tcsncpy(dest, file, iLen); 861 *(dest + iLen) = _T('\0'); 862 863 return dest; 864 } 865 866 867 /* 868 * DirPrintNewList 869 * 870 * The function that prints in new style 871 */ 872 static VOID 873 DirPrintNewList(PDIRFINDINFO ptrFiles[], /* [IN]Files' Info */ 874 DWORD dwCount, /* [IN] The quantity of files */ 875 LPCTSTR szCurPath, /* [IN] Full path of current directory */ 876 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */ 877 { 878 DWORD i; 879 TCHAR szSize[30]; 880 TCHAR szShortName[15]; 881 TCHAR szDate[20]; 882 TCHAR szTime[20]; 883 INT iSizeFormat; 884 ULARGE_INTEGER u64FileSize; 885 PDIRFINDSTREAMNODE ptrCurStream; 886 887 for (i = 0; i < dwCount && !CheckCtrlBreak(BREAK_INPUT); i++) 888 { 889 /* Calculate size */ 890 if (ptrFiles[i]->stFindInfo.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) 891 { 892 /* Junction */ 893 iSizeFormat = -14; 894 _tcscpy(szSize, _T("<JUNCTION>")); 895 } 896 else if (ptrFiles[i]->stFindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 897 { 898 /* Directory */ 899 iSizeFormat = -14; 900 _tcscpy(szSize, _T("<DIR>")); 901 } 902 else 903 { 904 /* File */ 905 iSizeFormat = 14; 906 u64FileSize.HighPart = ptrFiles[i]->stFindInfo.nFileSizeHigh; 907 u64FileSize.LowPart = ptrFiles[i]->stFindInfo.nFileSizeLow; 908 ConvertULargeInteger(u64FileSize.QuadPart, szSize, 20, lpFlags->bTSeparator); 909 } 910 911 /* Calculate short name */ 912 szShortName[0] = _T('\0'); 913 if (lpFlags->bShortName) 914 _stprintf(szShortName, _T(" %-12s"), ptrFiles[i]->stFindInfo.cAlternateFileName); 915 916 /* Format date and time */ 917 DirPrintFileDateTime(szDate, szTime, &ptrFiles[i]->stFindInfo, lpFlags); 918 919 /* Print the line */ 920 DirPrintf(lpFlags, _T("%10s %-6s %*s%s %s\n"), 921 szDate, 922 szTime, 923 iSizeFormat, 924 szSize, 925 szShortName, 926 ptrFiles[i]->stFindInfo.cFileName); 927 928 /* Now, loop on the streams */ 929 ptrCurStream = ptrFiles[i]->ptrHead; 930 while (ptrCurStream) 931 { 932 ConvertULargeInteger(ptrCurStream->stStreamInfo.StreamSize.QuadPart, szSize, 20, lpFlags->bTSeparator); 933 934 /* Print the line */ 935 DirPrintf(lpFlags, _T("%10s %-6s %*s%s %s%s\n"), 936 L"", 937 L"", 938 16, 939 szSize, 940 L"", 941 ptrFiles[i]->stFindInfo.cFileName, 942 ptrCurStream->stStreamInfo.cStreamName); 943 ptrCurStream = ptrCurStream->ptrNext; 944 } 945 } 946 } 947 948 949 /* 950 * DirPrintWideList 951 * 952 * The function that prints in wide list 953 */ 954 static VOID 955 DirPrintWideList(PDIRFINDINFO ptrFiles[], /* [IN] Files' Info */ 956 DWORD dwCount, /* [IN] The quantity of files */ 957 LPCTSTR szCurPath, /* [IN] Full path of current directory */ 958 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */ 959 { 960 SHORT iScreenWidth; 961 USHORT iColumns; 962 USHORT iLines; 963 UINT_PTR iLongestName; 964 TCHAR szTempFname[MAX_PATH]; 965 DWORD i; 966 DWORD j; 967 DWORD temp; 968 969 /* Calculate longest name */ 970 iLongestName = 1; 971 for (i = 0; i < dwCount; i++) 972 { 973 if (ptrFiles[i]->stFindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 974 { 975 /* Directories need 2 additional characters for brackets */ 976 if ((_tcslen(ptrFiles[i]->stFindInfo.cFileName) + 2) > iLongestName) 977 iLongestName = _tcslen(ptrFiles[i]->stFindInfo.cFileName) + 2; 978 } 979 else 980 { 981 if (_tcslen(ptrFiles[i]->stFindInfo.cFileName) > iLongestName) 982 iLongestName = _tcslen(ptrFiles[i]->stFindInfo.cFileName); 983 } 984 } 985 986 /* Count the highest number of columns */ 987 GetScreenSize(&iScreenWidth, NULL); 988 iColumns = (USHORT)(iScreenWidth / iLongestName); 989 990 /* Check if there is enough space for spaces between names */ 991 if (((iLongestName * iColumns) + iColumns) >= (UINT)iScreenWidth) 992 iColumns --; 993 994 /* A last check at iColumns to avoid division by zero */ 995 if (!iColumns) iColumns = 1; 996 997 /* Calculate the lines that will be printed */ 998 iLines = (USHORT)((dwCount + iColumns - 1) / iColumns); 999 1000 for (i = 0; i < iLines && !CheckCtrlBreak(BREAK_INPUT); i++) 1001 { 1002 for (j = 0; j < iColumns; j++) 1003 { 1004 if (lpFlags->bWideListColSort) 1005 { 1006 /* Print Column sorted */ 1007 temp = (j * iLines) + i; 1008 } 1009 else 1010 { 1011 /* Print Line sorted */ 1012 temp = (i * iColumns) + j; 1013 } 1014 1015 if (temp >= dwCount) break; 1016 1017 if (ptrFiles[temp]->stFindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 1018 _stprintf(szTempFname, _T("[%s]"), ptrFiles[temp]->stFindInfo.cFileName); 1019 else 1020 _stprintf(szTempFname, _T("%s"), ptrFiles[temp]->stFindInfo.cFileName); 1021 1022 DirPrintf(lpFlags, _T("%-*s"), iLongestName + 1, szTempFname); 1023 } 1024 1025 /* Add a new line after the last item in the column */ 1026 DirPrintf(lpFlags, _T("\n")); 1027 } 1028 } 1029 1030 1031 /* 1032 * DirPrintOldList 1033 * 1034 * The function that prints in old style 1035 */ 1036 static VOID 1037 DirPrintOldList(PDIRFINDINFO ptrFiles[], /* [IN] Files' Info */ 1038 DWORD dwCount, /* [IN] The quantity of files */ 1039 LPCTSTR szCurPath, /* [IN] Full path of current directory */ 1040 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */ 1041 { 1042 DWORD i; /* An indexer for "for"s */ 1043 TCHAR szName[10]; /* The name of file */ 1044 TCHAR szExt[5]; /* The extension of file */ 1045 TCHAR szDate[30],szTime[30]; /* Used to format time and date */ 1046 TCHAR szSize[30]; /* The size of file */ 1047 int iSizeFormat; /* The format of size field */ 1048 ULARGE_INTEGER u64FileSize; /* The file size */ 1049 1050 for (i = 0; i < dwCount && !CheckCtrlBreak(BREAK_INPUT); i++) 1051 { 1052 /* Broke 8.3 format */ 1053 if (*ptrFiles[i]->stFindInfo.cAlternateFileName ) 1054 { 1055 /* If the file is long named then we read the alter name */ 1056 getName( ptrFiles[i]->stFindInfo.cAlternateFileName, szName); 1057 _tcscpy(szExt, getExt( ptrFiles[i]->stFindInfo.cAlternateFileName)); 1058 } 1059 else 1060 { 1061 /* If the file is not long name we read its original name */ 1062 getName( ptrFiles[i]->stFindInfo.cFileName, szName); 1063 _tcscpy(szExt, getExt( ptrFiles[i]->stFindInfo.cFileName)); 1064 } 1065 1066 /* Calculate size */ 1067 if (ptrFiles[i]->stFindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 1068 { 1069 /* Directory, no size it's a directory */ 1070 iSizeFormat = -17; 1071 _tcscpy(szSize, _T("<DIR>")); 1072 } 1073 else 1074 { 1075 /* File */ 1076 iSizeFormat = 17; 1077 u64FileSize.HighPart = ptrFiles[i]->stFindInfo.nFileSizeHigh; 1078 u64FileSize.LowPart = ptrFiles[i]->stFindInfo.nFileSizeLow; 1079 ConvertULargeInteger(u64FileSize.QuadPart, szSize, 20, lpFlags->bTSeparator); 1080 } 1081 1082 /* Format date and time */ 1083 DirPrintFileDateTime(szDate,szTime,&ptrFiles[i]->stFindInfo,lpFlags); 1084 1085 /* Print the line */ 1086 DirPrintf(lpFlags, _T("%-8s %-3s %*s %s %s\n"), 1087 szName, /* The file's 8.3 name */ 1088 szExt, /* The file's 8.3 extension */ 1089 iSizeFormat, /* print format for size column */ 1090 szSize, /* The size of file or "<DIR>" for dirs */ 1091 szDate, /* The date of file/dir */ 1092 szTime); /* The time of file/dir */ 1093 } 1094 } 1095 1096 /* 1097 * DirPrintBareList 1098 * 1099 * The function that prints in bare format 1100 */ 1101 static VOID 1102 DirPrintBareList(PDIRFINDINFO ptrFiles[], /* [IN] Files' Info */ 1103 DWORD dwCount, /* [IN] The number of files */ 1104 LPCTSTR szCurPath, /* [IN] Full path of current directory */ 1105 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */ 1106 { 1107 DWORD i; 1108 1109 for (i = 0; i < dwCount && !CheckCtrlBreak(BREAK_INPUT); i++) 1110 { 1111 if (IsDotDirectory(ptrFiles[i]->stFindInfo.cFileName)) 1112 { 1113 /* At bare format we don't print the dot-directories "." and ".." */ 1114 continue; 1115 } 1116 if (lpFlags->bRecursive) 1117 { 1118 /* At recursive mode we print full path of file */ 1119 DirPrintf(lpFlags, _T("%s\\%s\n"), szCurPath, ptrFiles[i]->stFindInfo.cFileName); 1120 } 1121 else 1122 { 1123 /* If we are not in recursive mode we print the file names */ 1124 DirPrintf(lpFlags, _T("%s\n"), ptrFiles[i]->stFindInfo.cFileName); 1125 } 1126 } 1127 } 1128 1129 1130 /* 1131 * DirPrintFiles 1132 * 1133 * The functions that prints the files list 1134 */ 1135 static VOID 1136 DirPrintFiles(PDIRFINDINFO ptrFiles[], /* [IN] Files' Info */ 1137 DWORD dwCount, /* [IN] The quantity of files */ 1138 LPCTSTR szCurPath, /* [IN] Full path of current directory */ 1139 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */ 1140 { 1141 TCHAR szMsg[RC_STRING_MAX_SIZE]; 1142 TCHAR szTemp[MAX_PATH]; /* A buffer to format the directory header */ 1143 1144 /* Print trailing backslash for root directory of drive */ 1145 _tcscpy(szTemp, szCurPath); 1146 if (_tcslen(szTemp) == 2 && szTemp[1] == _T(':')) 1147 _tcscat(szTemp, _T("\\")); 1148 1149 /* Condition to print header: 1150 We are not printing in bare format 1151 and if we are in recursive mode... we must have results */ 1152 if (!lpFlags->bBareFormat && !(lpFlags->bRecursive && (dwCount <= 0))) 1153 { 1154 LoadString(CMD_ModuleHandle, STRING_DIR_HELP7, szMsg, ARRAYSIZE(szMsg)); 1155 if (!DirPrintf(lpFlags, szMsg, szTemp)) 1156 return; 1157 } 1158 1159 if (lpFlags->bBareFormat) 1160 { 1161 /* Bare format */ 1162 DirPrintBareList(ptrFiles, dwCount, szCurPath, lpFlags); 1163 } 1164 else if (lpFlags->bShortName) 1165 { 1166 /* New list style / Short names */ 1167 DirPrintNewList(ptrFiles, dwCount, szCurPath, lpFlags); 1168 } 1169 else if (lpFlags->bWideListColSort || lpFlags->bWideList) 1170 { 1171 /* Wide list */ 1172 DirPrintWideList(ptrFiles, dwCount, szCurPath, lpFlags); 1173 } 1174 else if (lpFlags->bNewLongList ) 1175 { 1176 /* New list style*/ 1177 DirPrintNewList(ptrFiles, dwCount, szCurPath, lpFlags); 1178 } 1179 else 1180 { 1181 /* If nothing is selected old list is the default */ 1182 DirPrintOldList(ptrFiles, dwCount, szCurPath, lpFlags); 1183 } 1184 } 1185 1186 /* 1187 * CompareFiles 1188 * 1189 * Compares 2 files based on the order criteria 1190 */ 1191 static BOOL 1192 CompareFiles(PDIRFINDINFO lpFile1, /* [IN] A pointer to WIN32_FIND_DATA of file 1 */ 1193 PDIRFINDINFO lpFile2, /* [IN] A pointer to WIN32_FIND_DATA of file 2 */ 1194 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags that we use to list */ 1195 { 1196 ULARGE_INTEGER u64File1; 1197 ULARGE_INTEGER u64File2; 1198 int i; 1199 long iComp = 0; /* The comparison result */ 1200 1201 /* Calculate criteria by order given from user */ 1202 for (i = 0; i < lpFlags->stOrderBy.sCriteriaCount; i++) 1203 { 1204 1205 /* Calculate criteria */ 1206 switch (lpFlags->stOrderBy.eCriteria[i]) 1207 { 1208 case ORDER_SIZE: /* Order by size /o:s */ 1209 /* concat the 32bit integers to a 64bit */ 1210 u64File1.LowPart = lpFile1->stFindInfo.nFileSizeLow; 1211 u64File1.HighPart = lpFile1->stFindInfo.nFileSizeHigh; 1212 u64File2.LowPart = lpFile2->stFindInfo.nFileSizeLow; 1213 u64File2.HighPart = lpFile2->stFindInfo.nFileSizeHigh; 1214 1215 /* In case that difference is too big for a long */ 1216 if (u64File1.QuadPart < u64File2.QuadPart) 1217 iComp = -1; 1218 else if (u64File1.QuadPart > u64File2.QuadPart) 1219 iComp = 1; 1220 else 1221 iComp = 0; 1222 break; 1223 1224 case ORDER_DIRECTORY: /* Order by directory attribute /o:g */ 1225 iComp = ((lpFile2->stFindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)- 1226 (lpFile1->stFindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)); 1227 break; 1228 1229 case ORDER_EXTENSION: /* Order by extension name /o:e */ 1230 iComp = _tcsicmp(getExt(lpFile1->stFindInfo.cFileName),getExt(lpFile2->stFindInfo.cFileName)); 1231 break; 1232 1233 case ORDER_NAME: /* Order by filename /o:n */ 1234 iComp = _tcsicmp(lpFile1->stFindInfo.cFileName, lpFile2->stFindInfo.cFileName); 1235 break; 1236 1237 case ORDER_TIME: /* Order by file's time /o:t */ 1238 /* We compare files based on the time field selected by /t */ 1239 switch (lpFlags->stTimeField.eTimeField) 1240 { 1241 case TF_CREATIONDATE: 1242 /* concat the 32bit integers to a 64bit */ 1243 u64File1.LowPart = lpFile1->stFindInfo.ftCreationTime.dwLowDateTime; 1244 u64File1.HighPart = lpFile1->stFindInfo.ftCreationTime.dwHighDateTime ; 1245 u64File2.LowPart = lpFile2->stFindInfo.ftCreationTime.dwLowDateTime; 1246 u64File2.HighPart = lpFile2->stFindInfo.ftCreationTime.dwHighDateTime ; 1247 break; 1248 case TF_LASTACCESSEDDATE : 1249 /* concat the 32bit integers to a 64bit */ 1250 u64File1.LowPart = lpFile1->stFindInfo.ftLastAccessTime.dwLowDateTime; 1251 u64File1.HighPart = lpFile1->stFindInfo.ftLastAccessTime.dwHighDateTime ; 1252 u64File2.LowPart = lpFile2->stFindInfo.ftLastAccessTime.dwLowDateTime; 1253 u64File2.HighPart = lpFile2->stFindInfo.ftLastAccessTime.dwHighDateTime ; 1254 break; 1255 case TF_MODIFIEDDATE: 1256 /* concat the 32bit integers to a 64bit */ 1257 u64File1.LowPart = lpFile1->stFindInfo.ftLastWriteTime.dwLowDateTime; 1258 u64File1.HighPart = lpFile1->stFindInfo.ftLastWriteTime.dwHighDateTime ; 1259 u64File2.LowPart = lpFile2->stFindInfo.ftLastWriteTime.dwLowDateTime; 1260 u64File2.HighPart = lpFile2->stFindInfo.ftLastWriteTime.dwHighDateTime ; 1261 break; 1262 } 1263 1264 /* In case that difference is too big for a long */ 1265 if (u64File1.QuadPart < u64File2.QuadPart) 1266 iComp = -1; 1267 else if (u64File1.QuadPart > u64File2.QuadPart) 1268 iComp = 1; 1269 else 1270 iComp = 0; 1271 break; 1272 } 1273 1274 /* Reverse if desired */ 1275 if (lpFlags->stOrderBy.bCriteriaRev[i]) 1276 iComp *= -1; 1277 1278 /* If that criteria was enough for distinguishing 1279 the files/dirs,there is no need to calculate the others*/ 1280 if (iComp != 0) break; 1281 } 1282 1283 /* Translate the value of iComp to boolean */ 1284 return iComp > 0; 1285 } 1286 1287 /* 1288 * QsortFiles 1289 * 1290 * Sort files by the order criterias using quicksort method 1291 */ 1292 static VOID 1293 QsortFiles(PDIRFINDINFO ptrArray[], /* [IN/OUT] The array with file info pointers */ 1294 int i, /* [IN] The index of first item in array */ 1295 int j, /* [IN] The index to last item in array */ 1296 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags that we will use to sort */ 1297 { 1298 PDIRFINDINFO lpTemp; /* A temporary pointer */ 1299 BOOL Way; 1300 1301 if (i < j) 1302 { 1303 int First = i, Last = j, Temp; 1304 Way = TRUE; 1305 while (i != j) 1306 { 1307 if (Way == CompareFiles(ptrArray[i], ptrArray[j], lpFlags)) 1308 { 1309 /* Swap the pointers of the array */ 1310 lpTemp = ptrArray[i]; 1311 ptrArray[i]= ptrArray[j]; 1312 ptrArray[j] = lpTemp; 1313 1314 /* Swap the indexes for inverting sorting */ 1315 Temp = i; 1316 i = j; 1317 j =Temp; 1318 1319 Way = !Way; 1320 } 1321 1322 j += (!Way - Way); 1323 } 1324 1325 QsortFiles(ptrArray,First, i-1, lpFlags); 1326 QsortFiles(ptrArray,i+1,Last, lpFlags); 1327 } 1328 } 1329 1330 static VOID 1331 DirNodeCleanup(PDIRFINDLISTNODE ptrStartNode, 1332 PDWORD pdwCount) 1333 { 1334 PDIRFINDLISTNODE ptrNextNode; 1335 PDIRFINDSTREAMNODE ptrFreeNode; 1336 while (ptrStartNode) 1337 { 1338 ptrNextNode = ptrStartNode->ptrNext; 1339 while (ptrStartNode->stInfo.ptrHead) 1340 { 1341 ptrFreeNode = ptrStartNode->stInfo.ptrHead; 1342 ptrStartNode->stInfo.ptrHead = ptrFreeNode->ptrNext; 1343 cmd_free(ptrFreeNode); 1344 } 1345 cmd_free(ptrStartNode); 1346 ptrStartNode = ptrNextNode; 1347 --(*pdwCount); 1348 } 1349 } 1350 1351 /* 1352 * DirList 1353 * 1354 * The function that does everything except for printing results 1355 */ 1356 static INT 1357 DirList(IN OUT LPTSTR szFullPath, /* [IN] The full path we are listing with trailing '\', where dir starts */ 1358 IN LPTSTR pszFilePart, /* [IN] Pointer in the szFullPath buffer where the file (pattern) part starts*/ 1359 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags of the listing */ 1360 { 1361 HANDLE hSearch; /* The handle of the search */ 1362 HANDLE hRecSearch; /* The handle for searching recursively */ 1363 HANDLE hStreams; /* The handle for alternate streams */ 1364 WIN32_FIND_DATA wfdFileInfo; /* The info of file that found */ 1365 PDIRFINDINFO * ptrFileArray; /* An array of pointers with all the files */ 1366 PDIRFINDLISTNODE ptrStartNode; /* The pointer to the first node */ 1367 PDIRFINDLISTNODE ptrNextNode; /* A pointer used for relatives references */ 1368 TCHAR szSubPath[MAX_PATH]; /* The full path used for the recursive search */ 1369 LPTSTR pszSubFilePart; 1370 TCHAR cPathSep; 1371 DWORD dwCount; /* A counter of files found in directory */ 1372 DWORD dwCountFiles; /* Counter for files */ 1373 DWORD dwCountDirs; /* Counter for directories */ 1374 ULONGLONG u64CountBytes; /* Counter for bytes */ 1375 ULARGE_INTEGER u64Temp; /* A temporary counter */ 1376 WIN32_FIND_STREAM_DATA wfsdStreamInfo; 1377 PDIRFINDSTREAMNODE * ptrCurNode; /* The pointer to the first stream */ 1378 static HANDLE (WINAPI *pFindFirstStreamW)(LPCWSTR, STREAM_INFO_LEVELS, LPVOID, DWORD); 1379 static BOOL (WINAPI *pFindNextStreamW)(HANDLE, LPVOID); 1380 1381 /* Initialize variables */ 1382 ptrStartNode = NULL; 1383 ptrNextNode = NULL; 1384 dwCount = 0; 1385 dwCountFiles = 0; 1386 dwCountDirs = 0; 1387 u64CountBytes = 0; 1388 1389 /* Prepare the linked list, first node is allocated */ 1390 ptrStartNode = cmd_alloc(sizeof(DIRFINDLISTNODE)); 1391 if (ptrStartNode == NULL) 1392 { 1393 WARN("Cannot allocate memory for ptrStartNode!\n"); 1394 return 1; /* Error cannot allocate memory for 1st object */ 1395 } 1396 ptrStartNode->stInfo.ptrHead = NULL; 1397 ptrNextNode = ptrStartNode; 1398 1399 /* Collect the results for the current directory */ 1400 hSearch = FindFirstFile(szFullPath, &wfdFileInfo); 1401 if (hSearch != INVALID_HANDLE_VALUE) 1402 { 1403 do 1404 { 1405 if ((wfdFileInfo.dwFileAttributes & lpFlags->stAttribs.dwAttribMask) == 1406 (lpFlags->stAttribs.dwAttribMask & lpFlags->stAttribs.dwAttribVal)) 1407 { 1408 ptrNextNode->ptrNext = cmd_alloc(sizeof(DIRFINDLISTNODE)); 1409 if (ptrNextNode->ptrNext == NULL) 1410 { 1411 WARN("Cannot allocate memory for ptrNextNode->ptrNext!\n"); 1412 DirNodeCleanup(ptrStartNode, &dwCount); 1413 FindClose(hSearch); 1414 return 1; 1415 } 1416 1417 /* Copy the info of search at linked list */ 1418 memcpy(&ptrNextNode->ptrNext->stInfo.stFindInfo, 1419 &wfdFileInfo, 1420 sizeof(WIN32_FIND_DATA)); 1421 1422 /* If lower case is selected do it here */ 1423 if (lpFlags->bLowerCase) 1424 { 1425 _tcslwr(ptrNextNode->ptrNext->stInfo.stFindInfo.cAlternateFileName); 1426 _tcslwr(ptrNextNode->ptrNext->stInfo.stFindInfo.cFileName); 1427 } 1428 1429 /* No streams (yet?) */ 1430 ptrNextNode->ptrNext->stInfo.ptrHead = NULL; 1431 1432 /* Alternate streams are only displayed with new long list */ 1433 if (lpFlags->bNewLongList && lpFlags->bDataStreams) 1434 { 1435 if (!pFindFirstStreamW) 1436 { 1437 pFindFirstStreamW = (PVOID)GetProcAddress(GetModuleHandle(_T("kernel32")), "FindFirstStreamW"); 1438 pFindNextStreamW = (PVOID)GetProcAddress(GetModuleHandle(_T("kernel32")), "FindNextStreamW"); 1439 } 1440 1441 /* Try to get stream information */ 1442 if (pFindFirstStreamW && pFindNextStreamW) 1443 { 1444 hStreams = pFindFirstStreamW(wfdFileInfo.cFileName, FindStreamInfoStandard, &wfsdStreamInfo, 0); 1445 } 1446 else 1447 { 1448 hStreams = INVALID_HANDLE_VALUE; 1449 ERR("FindFirstStreamW not supported!\n"); 1450 } 1451 1452 if (hStreams != INVALID_HANDLE_VALUE) 1453 { 1454 /* We totally ignore first stream. It contains data about ::$DATA */ 1455 ptrCurNode = &ptrNextNode->ptrNext->stInfo.ptrHead; 1456 while (pFindNextStreamW(hStreams, &wfsdStreamInfo)) 1457 { 1458 *ptrCurNode = cmd_alloc(sizeof(DIRFINDSTREAMNODE)); 1459 if (*ptrCurNode == NULL) 1460 { 1461 WARN("Cannot allocate memory for *ptrCurNode!\n"); 1462 DirNodeCleanup(ptrStartNode, &dwCount); 1463 FindClose(hStreams); 1464 FindClose(hSearch); 1465 return 1; 1466 } 1467 1468 memcpy(&(*ptrCurNode)->stStreamInfo, &wfsdStreamInfo, 1469 sizeof(WIN32_FIND_STREAM_DATA)); 1470 1471 /* If lower case is selected do it here */ 1472 if (lpFlags->bLowerCase) 1473 { 1474 _tcslwr((*ptrCurNode)->stStreamInfo.cStreamName); 1475 } 1476 1477 ptrCurNode = &(*ptrCurNode)->ptrNext; 1478 } 1479 1480 FindClose(hStreams); 1481 *ptrCurNode = NULL; 1482 } 1483 } 1484 1485 /* Continue at next node at linked list */ 1486 ptrNextNode = ptrNextNode->ptrNext; 1487 dwCount++; 1488 1489 /* Grab statistics */ 1490 if (wfdFileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 1491 { 1492 /* Directory */ 1493 dwCountDirs++; 1494 } 1495 else 1496 { 1497 /* File */ 1498 dwCountFiles++; 1499 u64Temp.HighPart = wfdFileInfo.nFileSizeHigh; 1500 u64Temp.LowPart = wfdFileInfo.nFileSizeLow; 1501 u64CountBytes += u64Temp.QuadPart; 1502 } 1503 } 1504 } while (FindNextFile(hSearch, &wfdFileInfo)); 1505 FindClose(hSearch); 1506 } 1507 1508 /* Terminate list */ 1509 ptrNextNode->ptrNext = NULL; 1510 1511 /* Calculate and allocate space need for making an array of pointers */ 1512 ptrFileArray = cmd_alloc(sizeof(PDIRFINDINFO) * dwCount); 1513 if (ptrFileArray == NULL) 1514 { 1515 WARN("Cannot allocate memory for ptrFileArray!\n"); 1516 DirNodeCleanup(ptrStartNode, &dwCount); 1517 return 1; 1518 } 1519 1520 /* 1521 * Create an array of pointers from the linked list 1522 * this will be used to sort and print data, rather than the list 1523 */ 1524 ptrNextNode = ptrStartNode; 1525 dwCount = 0; 1526 while (ptrNextNode->ptrNext) 1527 { 1528 ptrFileArray[dwCount] = &ptrNextNode->ptrNext->stInfo; 1529 ptrNextNode = ptrNextNode->ptrNext; 1530 dwCount++; 1531 } 1532 1533 /* Sort Data if requested */ 1534 if (lpFlags->stOrderBy.sCriteriaCount > 0) 1535 QsortFiles(ptrFileArray, 0, dwCount-1, lpFlags); 1536 1537 /* Print Data */ 1538 cPathSep = pszFilePart[-1]; 1539 pszFilePart[-1] = _T('\0'); /* Truncate to directory name only */ 1540 DirPrintFiles(ptrFileArray, dwCount, szFullPath, lpFlags); 1541 1542 if (lpFlags->bRecursive) 1543 { 1544 PrintSummary(szFullPath, 1545 dwCountFiles, 1546 dwCountDirs, 1547 u64CountBytes, 1548 lpFlags, 1549 FALSE); 1550 } 1551 pszFilePart[-1] = cPathSep; 1552 1553 /* Free array */ 1554 cmd_free(ptrFileArray); 1555 1556 /* Free linked list */ 1557 DirNodeCleanup(ptrStartNode, &dwCount); 1558 1559 if (CheckCtrlBreak(BREAK_INPUT)) 1560 return 1; 1561 1562 /* Add statistics to recursive statistics */ 1563 recurse_dir_cnt += dwCountDirs; 1564 recurse_file_cnt += dwCountFiles; 1565 recurse_bytes += u64CountBytes; 1566 1567 /* 1568 * Do the recursive job if requested. 1569 * The recursion is done on ALL (independent of their attributes) 1570 * directories of the current one. 1571 */ 1572 if (lpFlags->bRecursive) 1573 { 1574 /* The new search is involving any *.* file */ 1575 memcpy(szSubPath, szFullPath, (pszFilePart - szFullPath) * sizeof(TCHAR)); 1576 _tcscpy(&szSubPath[pszFilePart - szFullPath], _T("*.*")); 1577 1578 hRecSearch = FindFirstFile(szSubPath, &wfdFileInfo); 1579 if (hRecSearch != INVALID_HANDLE_VALUE) 1580 { 1581 do 1582 { 1583 /* We search for directories other than "." and ".." */ 1584 if (!IsDotDirectory(wfdFileInfo.cFileName) && 1585 (wfdFileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) 1586 { 1587 /* Concat the path and the directory to do recursive */ 1588 memcpy(szSubPath, szFullPath, (pszFilePart - szFullPath) * sizeof(TCHAR)); 1589 _tcscpy(&szSubPath[pszFilePart - szFullPath], wfdFileInfo.cFileName); 1590 _tcscat(szSubPath, _T("\\")); 1591 pszSubFilePart = &szSubPath[_tcslen(szSubPath)]; 1592 _tcscat(pszSubFilePart, pszFilePart); 1593 1594 /* We do the same for the directory */ 1595 if (DirList(szSubPath, pszSubFilePart, lpFlags) != 0) 1596 { 1597 FindClose(hRecSearch); 1598 return 1; 1599 } 1600 } 1601 } while (FindNextFile(hRecSearch, &wfdFileInfo)); 1602 } 1603 FindClose(hRecSearch); 1604 } 1605 1606 return 0; 1607 } 1608 1609 static VOID 1610 ResolvePattern( 1611 IN LPTSTR pszPattern, 1612 IN DWORD nBufferLength, 1613 OUT LPTSTR pszFullPath, 1614 OUT LPTSTR* ppszPatternPart OPTIONAL) 1615 { 1616 LPTSTR pCurDir, pNextDir, ptr; 1617 LPTSTR pszPatternPart; 1618 TCHAR szNewPattern[MAX_PATH]; 1619 1620 /* 1621 * We are going to use GetFullPathName() to properly determine the actual 1622 * full path from the pattern. However, due to the fact GetFullPathName() 1623 * strips parts of the file name component in case the pattern contains 1624 * path specification with trailing dots, it is required to perform a 1625 * pre-treatment on the pattern and a post-treatment on the obtained path. 1626 * This is mandatory in order to use the correct file search criterion. 1627 * 1628 * One particular case is when the pattern specifies a dots-only directory 1629 * followed by either the "." or ".." special directories. In this case the 1630 * GetFullPathName() function may completely miss the dots-only directory. 1631 * An example is given by the pattern (C-string notation) "\\...\\." . 1632 * To cope with this problem we need to partially canonicalize the pattern 1633 * by collapsing any "." or ".." special directory that immediately follows 1634 * a dots-only directory. We collapse in addition consecutive backslashes. 1635 * 1636 * Finally, trailing dots are skipped by GetFullPathName(). Therefore 1637 * a pattern that matches files with no extension, for example: "*." , 1638 * or: "dir\\noextfile." , are reduced to simply "*" or "dir\\noextfile", 1639 * that match files with extensions. Or, a pattern specifying a trailing 1640 * dots-only directory: "dir\\..." gets completely ignored and only the 1641 * full path to "dir" is returned. 1642 * To fix this second problem we need to restore the last part of the path 1643 * pattern using the pattern that has been first partially canonicalized. 1644 * 1645 * Note however that the "." or ".." special directories are always 1646 * interpreted correctly by GetFullPathName(). 1647 */ 1648 1649 /* Make a copy of the path pattern */ 1650 ASSERT(_tcslen(pszPattern) < ARRAYSIZE(szNewPattern)); 1651 _tcscpy(szNewPattern, pszPattern); 1652 pszPattern = szNewPattern; 1653 1654 TRACE("Original pszPattern: %S\n", pszPattern); 1655 1656 /* Convert slashes into backslashes */ 1657 pNextDir = pszPattern; 1658 while ((pNextDir = _tcschr(pNextDir, _T('/')))) 1659 *pNextDir++ = _T('\\'); 1660 1661 /* 1662 * Find any dots-only directory and collapse any "." or ".." special 1663 * directory that immediately follows it. 1664 * Note that we just start looking after the first path separator. Indeed, 1665 * dots-only directories that are not preceded by a path separator, and so 1666 * appear first in the pattern, for example: "...\dir", or: "..." , are 1667 * either correctly handled by GetFullPathName() because they are followed 1668 * by a non-pathological directory, or because they are handled when we 1669 * restore the trailing dots pattern piece in the next step. 1670 */ 1671 pNextDir = pszPattern; 1672 while (pNextDir) 1673 { 1674 pCurDir = pNextDir; 1675 1676 /* Find the next path separator in the pattern */ 1677 pNextDir = _tcschr(pNextDir, _T('\\')); 1678 if (!pNextDir) 1679 break; 1680 1681 /* Ignore the special "." and ".." directories that are correctly handled */ 1682 if ((pNextDir - pCurDir == 0) || IsDotDirectoryN(pCurDir, pNextDir - pCurDir)) 1683 { 1684 /* Found such a directory, ignore */ 1685 ++pNextDir; 1686 continue; 1687 } 1688 1689 /* Check whether this is a dots-only directory */ 1690 for (ptr = pCurDir; ptr < pNextDir; ++ptr) 1691 { 1692 if (*ptr != _T('.')) 1693 break; 1694 } 1695 if (ptr < pNextDir) 1696 { 1697 /* Not a dots-only directory, ignore */ 1698 ++pNextDir; 1699 continue; 1700 } 1701 1702 /* Skip any consecutive backslashes */ 1703 for (ptr = pNextDir; *ptr == _T('\\'); ++ptr) ; 1704 1705 /* pCurDir is a dots-only directory, perform partial canonicalization */ 1706 1707 /* Remove any following "." directory */ 1708 if (ptr[0] == _T('.') && (ptr[1] == _T('\\') || ptr[1] == 0)) 1709 { 1710 memmove(pNextDir, ptr + 1, (_tcslen(ptr + 1) + 1) * sizeof(TCHAR)); 1711 } 1712 /* Remove any following ".." directory */ 1713 else if (ptr[0] == _T('.') && ptr[1] == _T('.') && (ptr[2] == _T('\\') || ptr[2] == 0)) 1714 { 1715 /* Skip any consecutive backslashes before the next directory */ 1716 for (ptr = ptr + 2; *ptr == _T('\\'); ++ptr) ; 1717 1718 memmove(pCurDir, ptr, (_tcslen(ptr) + 1) * sizeof(TCHAR)); 1719 pNextDir = pCurDir; 1720 } 1721 else 1722 { 1723 ++pNextDir; 1724 1725 /* Collapse consecutive backslashes */ 1726 if (ptr > pNextDir) 1727 memmove(pNextDir, ptr, (_tcslen(ptr) + 1) * sizeof(TCHAR)); 1728 } 1729 } 1730 1731 /* An empty pattern means we enumerate all files in the current directory */ 1732 if (!*pszPattern) 1733 _tcscpy(pszPattern, _T("*")); 1734 1735 TRACE("New pszPattern: %S\n", pszPattern); 1736 1737 /* Create the full path */ 1738 if (GetFullPathName(pszPattern, nBufferLength, pszFullPath, &pszPatternPart) == 0) 1739 { 1740 _tcscpy(pszFullPath, pszPattern); 1741 pszPatternPart = NULL; 1742 } 1743 1744 TRACE("pszFullPath (1): %S\n", pszFullPath); 1745 TRACE("pszPatternPart (1): %S\n", pszPatternPart); 1746 1747 /* 1748 * Restore the correct file name component in case the pattern contained 1749 * trailing dots that have been skipped by GetFullPathName(). 1750 */ 1751 1752 /* Find the last path separator in the original szPath */ 1753 pNextDir = _tcsrchr(pszPattern, _T('\\')); 1754 if (pNextDir) 1755 { 1756 /* Skip past the separator and look at the path */ 1757 ++pNextDir; 1758 } 1759 else 1760 { 1761 /* The pattern is the path we need to look at */ 1762 pNextDir = pszPattern; 1763 } 1764 1765 /* 1766 * When pszPatternPart == NULL this means that pszFullPath should be a 1767 * directory; however it might have happened that the original pattern 1768 * was specifying a dots-only directory, that has been stripped off by 1769 * GetFullPathName(). In both these cases we need to restore these as 1770 * they are part of the actual directory path; the exception being if 1771 * these are the special "." or ".." directories. 1772 */ 1773 if (pszPatternPart == NULL) 1774 { 1775 ASSERT(pszFullPath[_tcslen(pszFullPath)-1] == _T('\\')); 1776 1777 /* Anything NOT being "." or ".." (the special directories) must be fully restored */ 1778 if (*pNextDir && !IsDotDirectory(pNextDir)) 1779 { 1780 pszPatternPart = &pszFullPath[_tcslen(pszFullPath)]; 1781 _tcscpy(pszPatternPart, pNextDir); 1782 pszPatternPart = NULL; 1783 } 1784 } 1785 else if (_tcscmp(pNextDir, pszPatternPart) != 0) 1786 { 1787 /* 1788 * For example, pszPatternPart == "." or ".." and we do not need to 1789 * do anything for these, or pszPatternPart == "dir\\noextfile." and 1790 * we need to restore all the trailing points. 1791 */ 1792 TRACE("pszPatternPart: %S is DIFFERENT from file criterion: %S\n", pszPatternPart, pNextDir); 1793 1794 /* Anything NOT being "." or ".." (the special directories) must be fully restored */ 1795 if (*pNextDir && !IsDotDirectory(pNextDir)) 1796 { 1797 /* Restore the correct file criterion */ 1798 _tcscpy(pszPatternPart, pNextDir); 1799 } 1800 } 1801 1802 TRACE("pszFullPath (2): %S\n", pszFullPath); 1803 1804 /* 1805 * If no wildcard or file was specified and this is a directory, 1806 * display all files in it. 1807 */ 1808 if (pszPatternPart == NULL || IsExistingDirectory(pszFullPath)) 1809 { 1810 pszPatternPart = &pszFullPath[_tcslen(pszFullPath)]; 1811 if (pszPatternPart[-1] != _T('\\')) 1812 *pszPatternPart++ = _T('\\'); 1813 _tcscpy(pszPatternPart, _T("*")); 1814 } 1815 1816 TRACE("pszPatternPart (2): %S\n", pszPatternPart); 1817 1818 if (ppszPatternPart) 1819 *ppszPatternPart = pszPatternPart; 1820 } 1821 1822 /* 1823 * dir 1824 * 1825 * internal dir command 1826 */ 1827 INT 1828 CommandDir(LPTSTR rest) 1829 { 1830 TCHAR dircmd[MAX_PATH]; /* A variable to store the DIRCMD environment variable */ 1831 TCHAR prev_volume[MAX_PATH]; 1832 TCHAR szFullPath[MAX_PATH]; 1833 LPTSTR* params = NULL; 1834 LPTSTR pszFilePart; 1835 TCHAR cPathSep; 1836 INT entries = 0; 1837 UINT loop = 0; 1838 DIRSWITCHFLAGS stFlags; 1839 INT ret = 1; 1840 BOOL ChangedVolume; 1841 1842 /* Initialize Switch Flags < Default switches are set here! > */ 1843 stFlags.b4Digit = TRUE; 1844 stFlags.bBareFormat = FALSE; 1845 stFlags.bDataStreams = FALSE; 1846 stFlags.bLowerCase = FALSE; 1847 stFlags.bNewLongList = TRUE; 1848 stFlags.bPause = FALSE; 1849 stFlags.bRecursive = FALSE; 1850 stFlags.bShortName = FALSE; 1851 stFlags.bTSeparator = TRUE; 1852 stFlags.bUser = FALSE; 1853 stFlags.bWideList = FALSE; 1854 stFlags.bWideListColSort = FALSE; 1855 stFlags.stTimeField.eTimeField = TF_MODIFIEDDATE; 1856 stFlags.stAttribs.dwAttribMask = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM; 1857 stFlags.stAttribs.dwAttribVal = 0L; 1858 stFlags.stOrderBy.sCriteriaCount = 0; 1859 1860 nErrorLevel = 0; 1861 1862 /* Read the parameters from the DIRCMD environment variable */ 1863 if (GetEnvironmentVariable (_T("DIRCMD"), dircmd, ARRAYSIZE(dircmd))) 1864 { 1865 if (!DirReadParam(dircmd, ¶ms, &entries, &stFlags)) 1866 { 1867 nErrorLevel = 1; 1868 goto cleanup; 1869 } 1870 } 1871 1872 /* Read the parameters */ 1873 if (!DirReadParam(rest, ¶ms, &entries, &stFlags) || CheckCtrlBreak(BREAK_INPUT)) 1874 { 1875 nErrorLevel = 1; 1876 goto cleanup; 1877 } 1878 1879 /* Default to current directory */ 1880 if (entries == 0) 1881 { 1882 if (!add_entry(&entries, ¶ms, _T("*"))) 1883 { 1884 nErrorLevel = 1; 1885 goto cleanup; 1886 } 1887 } 1888 1889 prev_volume[0] = _T('\0'); 1890 1891 /* Reset paging state */ 1892 if (stFlags.bPause) 1893 ConOutPrintfPaging(TRUE, _T("")); 1894 1895 for (loop = 0; loop < (UINT)entries; loop++) 1896 { 1897 if (CheckCtrlBreak(BREAK_INPUT)) 1898 { 1899 nErrorLevel = 1; 1900 goto cleanup; 1901 } 1902 1903 recurse_dir_cnt = 0L; 1904 recurse_file_cnt = 0L; 1905 recurse_bytes = 0; 1906 1907 /* <Debug :> 1908 Uncomment this to show the final state of switch flags*/ 1909 { 1910 int i; 1911 TRACE("Attributes mask/value %x/%x\n",stFlags.stAttribs.dwAttribMask,stFlags.stAttribs.dwAttribVal); 1912 TRACE("(B) Bare format : %i\n", stFlags.bBareFormat); 1913 TRACE("(C) Thousand : %i\n", stFlags.bTSeparator); 1914 TRACE("(W) Wide list : %i\n", stFlags.bWideList); 1915 TRACE("(D) Wide list sort by column : %i\n", stFlags.bWideListColSort); 1916 TRACE("(L) Lowercase : %i\n", stFlags.bLowerCase); 1917 TRACE("(N) New : %i\n", stFlags.bNewLongList); 1918 TRACE("(O) Order : %i\n", stFlags.stOrderBy.sCriteriaCount); 1919 for (i =0;i<stFlags.stOrderBy.sCriteriaCount;i++) 1920 TRACE(" Order Criteria [%i]: %i (Reversed: %i)\n",i, stFlags.stOrderBy.eCriteria[i], stFlags.stOrderBy.bCriteriaRev[i]); 1921 TRACE("(P) Pause : %i\n", stFlags.bPause); 1922 TRACE("(Q) Owner : %i\n", stFlags.bUser); 1923 TRACE("(R) Data stream : %i\n", stFlags.bDataStreams); 1924 TRACE("(S) Recursive : %i\n", stFlags.bRecursive); 1925 TRACE("(T) Time field : %i\n", stFlags.stTimeField.eTimeField); 1926 TRACE("(X) Short names : %i\n", stFlags.bShortName); 1927 TRACE("Parameter : %s\n", debugstr_aw(params[loop])); 1928 } 1929 1930 /* Print the drive header if the volume changed */ 1931 ChangedVolume = TRUE; 1932 1933 if (!stFlags.bBareFormat && 1934 GetVolumePathName(params[loop], szFullPath, ARRAYSIZE(szFullPath))) 1935 { 1936 if (!_tcscmp(szFullPath, prev_volume)) 1937 ChangedVolume = FALSE; 1938 else 1939 _tcscpy(prev_volume, szFullPath); 1940 } 1941 1942 /* Resolve the pattern */ 1943 ResolvePattern(params[loop], ARRAYSIZE(szFullPath), szFullPath, &pszFilePart); 1944 1945 /* Print the header */ 1946 cPathSep = pszFilePart[-1]; 1947 pszFilePart[-1] = _T('\0'); /* Truncate to directory name only */ 1948 if (ChangedVolume && !stFlags.bBareFormat && 1949 !PrintDirectoryHeader(szFullPath, &stFlags)) 1950 { 1951 nErrorLevel = 1; 1952 goto cleanup; 1953 } 1954 pszFilePart[-1] = cPathSep; 1955 1956 /* Perform the actual directory listing */ 1957 if (DirList(szFullPath, pszFilePart, &stFlags) != 0) 1958 { 1959 nErrorLevel = 1; 1960 goto cleanup; 1961 } 1962 1963 /* Print the footer */ 1964 pszFilePart[-1] = _T('\0'); /* Truncate to directory name only */ 1965 PrintSummary(szFullPath, 1966 recurse_file_cnt, 1967 recurse_dir_cnt, 1968 recurse_bytes, 1969 &stFlags, 1970 TRUE); 1971 } 1972 1973 ret = 0; 1974 1975 cleanup: 1976 freep(params); 1977 1978 return ret; 1979 } 1980 1981 #endif 1982 1983 /* EOF */ 1984