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