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