Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

877 lines
29 KiB

  1. /*++
  2. Copyright (c) 1991-2000 Microsoft Corporation
  3. Module Name:
  4. tree.cxx
  5. Abstract:
  6. This module contains the implementation of the TREE class.
  7. The TREE class implements a tree utility functionally compatible
  8. with the DOS 5 tree utility.
  9. This utility displays the directory structure of a path or drive.
  10. Usage:
  11. TREE [drive:][path] [/F] [/A] [/?]
  12. /F Display the names of files in each directory.
  13. /A Uses ASCII instead of extended characters.
  14. /? Displays a help message.
  15. Author:
  16. Jaime F. Sasson - jaimes - 13-May-1991
  17. Environment:
  18. ULIB, User Mode
  19. --*/
  20. #include "ulib.hxx"
  21. #include "arg.hxx"
  22. #include "array.hxx"
  23. #include "path.hxx"
  24. #include "wstring.hxx"
  25. #include "substrng.hxx"
  26. #include "dir.hxx"
  27. #include "filter.hxx"
  28. #include "system.hxx"
  29. #include "arrayit.hxx"
  30. #include "smsg.hxx"
  31. #include "stream.hxx"
  32. #include "rtmsg.h"
  33. #include "tree.hxx"
  34. extern "C" {
  35. #include <stdio.h>
  36. #include <string.h>
  37. }
  38. #define UNICODE_SINGLE_LEFT_T 0x251c
  39. #define UNICODE_SINGLE_BOTTOM_LEFT_CORNER 0x2514
  40. #define UNICODE_SINGLE_BOTTOM_HORIZONTAL 0x2500
  41. #define UNICODE_SINGLE_VERTICAL 0x2502
  42. #define UNICODE_SPACE 0x0020
  43. PSTREAM Get_Standard_Input_Stream();
  44. PSTREAM Get_Standard_Output_Stream();
  45. DEFINE_CONSTRUCTOR( TREE, PROGRAM );
  46. BOOLEAN
  47. TREE::Initialize(
  48. )
  49. /*++
  50. Routine Description:
  51. Initializes a TREE class.
  52. Arguments:
  53. None.
  54. Return Value:
  55. BOOLEAN - Indicates if the initialization succeeded.
  56. --*/
  57. {
  58. ARGUMENT_LEXEMIZER ArgLex;
  59. ARRAY LexArray;
  60. ARRAY ArgumentArray;
  61. STRING_ARGUMENT ProgramNameArgument;
  62. PATH_ARGUMENT DirectoryPathArgument;
  63. PWSTRING DirectoryNameString;
  64. PATH DirectoryNamePath;
  65. PWSTRING InvalidArgument;
  66. PCWSTRING InvalidPath;
  67. STRING_ARGUMENT InvalidSwitch;
  68. PATH AuxPath;
  69. STRING_ARGUMENT ParamNotCorrectFile;
  70. STRING_ARGUMENT ParamNotCorrectAscii;
  71. STRING_ARGUMENT ParamNotCorrectHelp;
  72. BOOLEAN FlagInvalidPath;
  73. PATH AuxInvPath;
  74. PWSTRING InvPathNoDrive;
  75. PWSTRING Device;
  76. PATH DevPth;
  77. PFSN_DIRECTORY DevDir;
  78. #ifdef FE_SB
  79. PWCHAR BufferMiddleBranch;
  80. PWCHAR BufferBottomBranch;
  81. PWCHAR BufferConnectingBranch;
  82. WCHAR BufferMiddleBranchDBCS[] = {
  83. UNICODE_SINGLE_LEFT_T, // 2
  84. UNICODE_SINGLE_BOTTOM_HORIZONTAL, // 2
  85. UNICODE_NULL // 0
  86. };
  87. WCHAR BufferBottomBranchDBCS[] = {
  88. UNICODE_SINGLE_BOTTOM_LEFT_CORNER, // 2
  89. UNICODE_SINGLE_BOTTOM_HORIZONTAL, // 2
  90. UNICODE_NULL // 0
  91. };
  92. WCHAR BufferConnectingBranchDBCS[] = {
  93. UNICODE_SINGLE_VERTICAL, // 2
  94. UNICODE_SPACE, // 1
  95. UNICODE_SPACE, // 1
  96. UNICODE_NULL // 0
  97. };
  98. WCHAR BufferMiddleBranchSBCS[] = {
  99. UNICODE_SINGLE_LEFT_T,
  100. UNICODE_SINGLE_BOTTOM_HORIZONTAL,
  101. UNICODE_SINGLE_BOTTOM_HORIZONTAL,
  102. UNICODE_SINGLE_BOTTOM_HORIZONTAL,
  103. UNICODE_NULL
  104. };
  105. WCHAR BufferBottomBranchSBCS[] = {
  106. UNICODE_SINGLE_BOTTOM_LEFT_CORNER,
  107. UNICODE_SINGLE_BOTTOM_HORIZONTAL,
  108. UNICODE_SINGLE_BOTTOM_HORIZONTAL,
  109. UNICODE_SINGLE_BOTTOM_HORIZONTAL,
  110. UNICODE_NULL
  111. };
  112. WCHAR BufferConnectingBranchSBCS[] = {
  113. UNICODE_SINGLE_VERTICAL,
  114. UNICODE_SPACE,
  115. UNICODE_SPACE,
  116. UNICODE_SPACE,
  117. UNICODE_NULL
  118. };
  119. switch ( ::GetConsoleOutputCP() ) {
  120. case 932:
  121. case 936:
  122. case 949:
  123. case 950:
  124. BufferMiddleBranch = BufferMiddleBranchDBCS;
  125. BufferBottomBranch = BufferBottomBranchDBCS;
  126. BufferConnectingBranch = BufferConnectingBranchDBCS;
  127. break;
  128. default:
  129. BufferMiddleBranch = BufferMiddleBranchSBCS;
  130. BufferBottomBranch = BufferBottomBranchSBCS;
  131. BufferConnectingBranch = BufferConnectingBranchSBCS;
  132. break;
  133. }
  134. #else
  135. WCHAR BufferMiddleBranch[] = {
  136. UNICODE_SINGLE_LEFT_T,
  137. UNICODE_SINGLE_BOTTOM_HORIZONTAL,
  138. UNICODE_SINGLE_BOTTOM_HORIZONTAL,
  139. UNICODE_SINGLE_BOTTOM_HORIZONTAL,
  140. UNICODE_NULL
  141. };
  142. WCHAR BufferBottomBranch[] = {
  143. UNICODE_SINGLE_BOTTOM_LEFT_CORNER,
  144. UNICODE_SINGLE_BOTTOM_HORIZONTAL,
  145. UNICODE_SINGLE_BOTTOM_HORIZONTAL,
  146. UNICODE_SINGLE_BOTTOM_HORIZONTAL,
  147. UNICODE_NULL
  148. };
  149. WCHAR BufferConnectingBranch[] = {
  150. UNICODE_SINGLE_VERTICAL,
  151. UNICODE_SPACE,
  152. UNICODE_SPACE,
  153. UNICODE_SPACE,
  154. UNICODE_NULL
  155. };
  156. #endif
  157. _InitialDirectory = NULL;
  158. FlagInvalidPath = FALSE;
  159. _FlagAtLeastOneSubdir = TRUE;
  160. _StandardOutput = Get_Standard_Output_Stream();
  161. //
  162. // Initialize MESSAGE class
  163. //
  164. _Message.Initialize( _StandardOutput, Get_Standard_Input_Stream() );
  165. //
  166. // Parse command line
  167. //
  168. if ( !LexArray.Initialize( ) ) {
  169. DebugAbort( "LexArray.Initialize() failed \n" );
  170. return( FALSE );
  171. }
  172. if ( !ArgLex.Initialize( &LexArray ) ) {
  173. DebugAbort( "ArgLex.Initialize() failed \n" );
  174. return( FALSE );
  175. }
  176. ArgLex.PutSwitches( "/" );
  177. ArgLex.PutStartQuotes( "\"" );
  178. ArgLex.PutEndQuotes( "\"" );
  179. ArgLex.PutSeparators( " \t" );
  180. ArgLex.SetCaseSensitive( FALSE );
  181. if( !ArgLex.PrepareToParse() ) {
  182. DebugAbort( "ArgLex.PrepareToParse() failed \n" );
  183. return( FALSE );
  184. }
  185. if ( !ArgumentArray.Initialize() ) {
  186. DebugAbort( "ArgumentArray.Initialize() failed \n" );
  187. return( FALSE );
  188. }
  189. if( !ProgramNameArgument.Initialize("*") ||
  190. !_FlagDisplayFiles.Initialize( "/F" ) ||
  191. !_FlagUseAsciiCharacters.Initialize( "/A" ) ||
  192. !_FlagDisplayHelp.Initialize( "/?" ) ||
  193. !ParamNotCorrectFile.Initialize( "/F*" ) ||
  194. !ParamNotCorrectAscii.Initialize( "/A*" ) ||
  195. !ParamNotCorrectHelp.Initialize( "/?*" ) ||
  196. !InvalidSwitch.Initialize( "/*" ) ||
  197. !DirectoryPathArgument.Initialize( "*" ) ) {
  198. DebugAbort( "Unable to initialize flag or string arguments \n" );
  199. return( FALSE );
  200. }
  201. if( !ArgumentArray.Put( &ProgramNameArgument ) ||
  202. !ArgumentArray.Put( &_FlagDisplayFiles ) ||
  203. !ArgumentArray.Put( &_FlagUseAsciiCharacters ) ||
  204. !ArgumentArray.Put( &_FlagDisplayHelp ) ||
  205. !ArgumentArray.Put( &ParamNotCorrectFile ) ||
  206. !ArgumentArray.Put( &ParamNotCorrectAscii ) ||
  207. !ArgumentArray.Put( &ParamNotCorrectHelp ) ||
  208. !ArgumentArray.Put( &InvalidSwitch ) ||
  209. !ArgumentArray.Put( &DirectoryPathArgument ) ) {
  210. DebugAbort( "ArgumentArray.Put() failed \n" );
  211. return( FALSE );
  212. }
  213. if( !ArgLex.DoParsing( &ArgumentArray ) ) {
  214. InvalidArgument = ArgLex.QueryInvalidArgument();
  215. DebugPtrAssert( InvalidArgument );
  216. _Message.Set( MSG_TREE_TOO_MANY_PARAMETERS );
  217. _Message.Display( "%W", InvalidArgument );
  218. return( FALSE );
  219. }
  220. if( InvalidSwitch.IsValueSet() ) {
  221. InvalidArgument = InvalidSwitch.GetString();
  222. DebugPtrAssert( InvalidArgument );
  223. _Message.Set( MSG_TREE_INVALID_SWITCH );
  224. _Message.Display( "%W", InvalidArgument );
  225. return( FALSE );
  226. }
  227. if( ParamNotCorrectFile.IsValueSet() ) {
  228. InvalidArgument = ParamNotCorrectFile.GetLexeme();
  229. DebugPtrAssert( InvalidArgument );
  230. _Message.Set( MSG_TREE_PARAMETER_NOT_CORRECT );
  231. _Message.Display( "%W", InvalidArgument );
  232. return( FALSE );
  233. }
  234. if( ParamNotCorrectAscii.IsValueSet() ) {
  235. InvalidArgument = ParamNotCorrectAscii.GetLexeme();
  236. DebugPtrAssert( InvalidArgument );
  237. _Message.Set( MSG_TREE_PARAMETER_NOT_CORRECT );
  238. _Message.Display( "%W", InvalidArgument );
  239. return( FALSE );
  240. }
  241. if( ParamNotCorrectHelp.IsValueSet() ) {
  242. InvalidArgument = ParamNotCorrectHelp.GetLexeme();
  243. DebugPtrAssert( InvalidArgument );
  244. _Message.Set( MSG_TREE_PARAMETER_NOT_CORRECT );
  245. _Message.Display( "%W", InvalidArgument );
  246. return( FALSE );
  247. }
  248. //
  249. // Displays help message if /? was found in the command line
  250. //
  251. if( _FlagDisplayHelp.QueryFlag() ) {
  252. _Message.Set( MSG_TREE_HELP_MESSAGE );
  253. _Message.Display( " " );
  254. return( FALSE );
  255. }
  256. //
  257. // Find initial directory
  258. //
  259. if( !DirectoryPathArgument.IsValueSet() ) {
  260. //
  261. // User did't specify a path, so assume current directory
  262. //
  263. _FlagPathSupplied = FALSE;
  264. if (!DirectoryNamePath.Initialize((LPWSTR)L".", TRUE )) {
  265. DebugAbort( "DirectoryNamePath.Initialize() failed \n" );
  266. return( FALSE );
  267. }
  268. } else {
  269. //
  270. // User specified a path
  271. //
  272. DirectoryNameString = DirectoryPathArgument.GetPath()->QueryFullPathString();
  273. DebugPtrAssert( DirectoryNameString );
  274. DirectoryNameString->Strupr();
  275. if (!DirectoryNamePath.Initialize(DirectoryNameString, TRUE)) {
  276. DebugAbort( "DirectoryNamePath.Initialize() failed \n" );
  277. return( FALSE );
  278. }
  279. //
  280. // Save the path specified by the user as he/she typed it
  281. // (but in upper case)
  282. //
  283. if( !AuxPath.Initialize( DirectoryNameString, FALSE ) ) {
  284. DebugAbort( "AuxPath.Initialize() failed \n" );
  285. return( FALSE );
  286. }
  287. //
  288. // Validate the device if one was supplied
  289. // AuxPath represents the path as typed by the user, and
  290. // it may or may not contain a device. If it doesn't contain
  291. // a device, no verification is made. In this case and we are
  292. // sure that the device is valid
  293. //
  294. if( ( Device = AuxPath.QueryDevice() ) != NULL ) {
  295. if( !DevPth.Initialize( Device ) ) {
  296. DebugAbort( "DevPth.Initialize() failed \n" );
  297. return( FALSE );
  298. }
  299. if( !(DevDir = SYSTEM::QueryDirectory( &DevPth ) ) ) {
  300. _Message.Set( MSG_TREE_INVALID_DRIVE );
  301. _Message.Display( " " );
  302. DELETE( Device );
  303. return( FALSE );
  304. }
  305. DELETE( Device );
  306. DELETE( DevDir );
  307. }
  308. //
  309. // Find out if what the user specified is just a drive
  310. //
  311. if( AuxPath.IsDrive() ) {
  312. _FlagPathSupplied = FALSE;
  313. } else {
  314. _FlagPathSupplied = TRUE;
  315. }
  316. }
  317. if (NULL == (_InitialDirectory = SYSTEM::QueryDirectory( &DirectoryNamePath ))) {
  318. InvalidPath = DirectoryNamePath.GetPathString();
  319. FlagInvalidPath = TRUE;
  320. _FlagAtLeastOneSubdir = FALSE;
  321. }
  322. //
  323. // Initialize filter for directories
  324. //
  325. if( !_FsnFilterDirectory.Initialize() ) {
  326. DELETE( _InitialDirectory );
  327. DebugAbort( "_FsnFilterDirectory.Initialize() failed \n" );
  328. return( FALSE );
  329. }
  330. if( !_FsnFilterDirectory.SetFileName( "*.*" ) ) {
  331. DELETE( _InitialDirectory );
  332. DebugAbort( "_FsnFilterDirectory.SetFilename() failed \n" );
  333. return( FALSE );
  334. }
  335. if( !_FsnFilterDirectory.SetAttributes( FSN_ATTRIBUTE_DIRECTORY,
  336. ( FSN_ATTRIBUTE )0,
  337. FSN_ATTRIBUTE_HIDDEN |
  338. FSN_ATTRIBUTE_SYSTEM ) ) {
  339. DELETE( _InitialDirectory );
  340. DebugAbort( "_FsnFilterDirectory.SetAttributes() failed \n" );
  341. return( FALSE );
  342. }
  343. //
  344. // Initilize filter for files
  345. //
  346. if( _FlagDisplayFiles.QueryFlag() ) {
  347. if( !_FsnFilterFile.Initialize() ) {
  348. DELETE( _InitialDirectory );
  349. DebugAbort( "FsnFilter.Initialize() failed \n" );
  350. return( FALSE );
  351. }
  352. if( !_FsnFilterFile.SetFileName( "*.*" ) ) {
  353. DELETE( _InitialDirectory );
  354. DebugAbort( "FsnFilter.SetFilename() failed \n" );
  355. return( FALSE );
  356. }
  357. if( !_FsnFilterFile.SetAttributes( ( FSN_ATTRIBUTE )0,
  358. ( FSN_ATTRIBUTE )0,
  359. FSN_ATTRIBUTE_DIRECTORY |
  360. FSN_ATTRIBUTE_HIDDEN |
  361. FSN_ATTRIBUTE_SYSTEM ) ) {
  362. DELETE( _InitialDirectory );
  363. DebugAbort( "FsnFilter.SetAttributes() failed \n" );
  364. return( FALSE );
  365. }
  366. }
  367. //
  368. // Find out what kind of characters should be used to display the tree
  369. // and initialize the basic strings
  370. //
  371. if( _FlagUseAsciiCharacters.QueryFlag() ) {
  372. _StringForDirectory.Initialize( (LPWSTR)L"+---" );
  373. _StringForLastDirectory.Initialize( (LPWSTR)L"\\---" );
  374. _StringForFile.Initialize( (LPWSTR)L"| " );
  375. } else {
  376. _StringForDirectory.Initialize( BufferMiddleBranch ); // "����"
  377. _StringForLastDirectory.Initialize( BufferBottomBranch ); // "����"
  378. _StringForFile.Initialize( BufferConnectingBranch ); // "� "
  379. }
  380. _StringForFileNoDirectory.Initialize( (LPWSTR)L" " );
  381. if( !_EndOfLineString.Initialize( (LPWSTR)L"\r\n" ) ) {
  382. DebugPrint( "_EndOfLineString.Initialize() failed" );
  383. DELETE( _InitialDirectory );
  384. return( FALSE );
  385. }
  386. _VolumeName = SYSTEM::QueryVolumeLabel( &DirectoryNamePath, &_SerialNumber );
  387. if (NULL == _VolumeName) {
  388. _Message.Set( MSG_TREE_INVALID_DRIVE );
  389. _Message.Display( " " );
  390. DELETE( Device );
  391. return( FALSE );
  392. }
  393. DebugPtrAssert( _VolumeName );
  394. _FlagAtLeastOneSubdir = FALSE;
  395. if( FlagInvalidPath ) {
  396. //
  397. // If user specified an invalid path, then display the same
  398. // messages that Dos 5 does.
  399. //
  400. // First displays the volume info.
  401. //
  402. DisplayVolumeInfo();
  403. //
  404. // Then display the path that is invalid. This path must be
  405. // displayed in capital letters, must always contain a device,
  406. // and must be displayed as a relative or absolute path depending
  407. // on what the user specified.
  408. //
  409. Device = DirectoryNamePath.QueryDevice();
  410. AuxInvPath.Initialize( DirectoryNameString, FALSE );
  411. InvPathNoDrive = AuxInvPath.QueryDirsAndName();
  412. _StandardOutput->WriteString( Device );
  413. _StandardOutput->WriteString( InvPathNoDrive );
  414. _StandardOutput->WriteByte( '\r' );
  415. _StandardOutput->WriteByte( '\n' );
  416. //
  417. // Display the message "Invalid Path - <path>"
  418. // The path in this case cannot contain a drive even if the
  419. // user specified a drive.
  420. //
  421. _Message.Set( MSG_TREE_INVALID_PATH );
  422. _Message.Display( "%W", InvPathNoDrive );
  423. DELETE( Device );
  424. DELETE( InvPathNoDrive );
  425. return( FALSE );
  426. }
  427. return( TRUE );
  428. }
  429. VOID
  430. TREE::Terminate(
  431. )
  432. /*++
  433. Routine Description:
  434. Deletes objects created during initialization.
  435. Arguments:
  436. None.
  437. Return Value:
  438. None.
  439. --*/
  440. {
  441. if( _InitialDirectory != NULL ) {
  442. DELETE( _InitialDirectory );
  443. }
  444. if( !_FlagAtLeastOneSubdir ) {
  445. _Message.Set( MSG_TREE_NO_SUBDIRECTORIES );
  446. _Message.Display( "%s", "\r\n" );
  447. }
  448. }
  449. BOOLEAN
  450. TREE::DisplayName (
  451. IN PCFSNODE Fsn,
  452. IN PCWSTRING String
  453. )
  454. /*++
  455. Routine Description:
  456. This method writes to the standard output the name of a file or
  457. directory, preceded by a string that represents pieces of the
  458. tree structure (pieces of the tree branches).
  459. Arguments:
  460. Fsn - Pointer to a FSNODE that describes the file or directory whose
  461. name is to be written.
  462. String - Pointer to a WSTRING object that contains the string to
  463. be written before the file or directory name.
  464. Return Value:
  465. BOOLEAN - returns TRUE to indicate that the operation succeeded, or
  466. FALSE otherwise.
  467. --*/
  468. {
  469. PWSTRING Name;
  470. PCPATH Path;
  471. PCWSTRING InitialDirectory;
  472. DSTRING InitialDirectoryUpperCase;
  473. PWSTRING Device;
  474. DSTRING EndOfLinePrecededByDot;
  475. DebugPtrAssert( Fsn );
  476. Path = Fsn->GetPath();
  477. DebugPtrAssert( Path );
  478. if( String != NULL ) {
  479. if( !_StandardOutput->WriteString( String, 0, String->QueryChCount() ) ) {
  480. DebugAbort( "_StandardOutput->WriteString() failed \n" );
  481. return( FALSE );
  482. }
  483. if( ( Name=Path->QueryName() ) == NULL ) {
  484. DebugAbort( "Path->QueryName() failed \n" );
  485. return( FALSE );
  486. }
  487. Name->Strcat( &_EndOfLineString );
  488. if( !_StandardOutput->WriteString( Name, 0, Name->QueryChCount() ) ) {
  489. DebugAbort( "_StandardOutput->WriteString() failed \n" );
  490. DELETE( Name );
  491. return( FALSE );
  492. }
  493. DELETE( Name );
  494. } else {
  495. if( _FlagPathSupplied ) {
  496. InitialDirectory = Path->GetPathString();
  497. DebugPtrAssert( InitialDirectory );
  498. InitialDirectoryUpperCase.Initialize( InitialDirectory );
  499. InitialDirectoryUpperCase.Strupr();
  500. InitialDirectoryUpperCase.Strcat( &_EndOfLineString );
  501. if( !_StandardOutput->WriteString( &InitialDirectoryUpperCase ) ) {
  502. DebugAbort( "_StandardOutput->WriteString() failed \n" );
  503. return( FALSE );
  504. }
  505. } else {
  506. Device = PPATH(Path)->QueryDevice();
  507. if (Device == NULL) {
  508. DebugAbort("TREE: Path->QueryDevice() failed\n");
  509. return FALSE;
  510. }
  511. DebugPtrAssert( Device );
  512. EndOfLinePrecededByDot.Initialize( (LPWSTR)L".\r\n" );
  513. Device->Strcat( &EndOfLinePrecededByDot );
  514. if( !_StandardOutput->WriteString( Device, 0, TO_END ) ) {
  515. DebugAbort( "_StandardOutput->WriteString() failed \n" );
  516. DELETE( Device );
  517. return( FALSE );
  518. }
  519. DELETE( Device );
  520. }
  521. }
  522. return( TRUE );
  523. }
  524. VOID
  525. TREE::DisplayVolumeInfo (
  526. )
  527. /*++
  528. Routine Description:
  529. This method writes to the standard output the the volume name (if one
  530. is found) and the serial number.
  531. Arguments:
  532. None.
  533. Return Value:
  534. None.
  535. --*/
  536. {
  537. //
  538. // Display volume name
  539. //
  540. if( _VolumeName->QueryChCount() == 0 ) {
  541. _Message.Set( MSG_TREE_DIR_LISTING_NO_VOLUME_NAME );
  542. _Message.Display( " " );
  543. } else {
  544. _Message.Set( MSG_TREE_DIR_LISTING_WITH_VOLUME_NAME );
  545. _Message.Display( "%W", _VolumeName );
  546. }
  547. //
  548. // Display serial number
  549. //
  550. if( _SerialNumber.HighOrder32Bits != 0 ) {
  551. _Message.Set( MSG_TREE_64_BIT_SERIAL_NUMBER );
  552. _Message.Display( "%08X%04X%04X",
  553. _SerialNumber.HighOrder32Bits,
  554. _SerialNumber.LowOrder32Bits >> 16,
  555. ( _SerialNumber.LowOrder32Bits << 16 ) >> 16);
  556. } else {
  557. _Message.Set( MSG_TREE_32_BIT_SERIAL_NUMBER );
  558. _Message.Display( "%04X%04X",
  559. _SerialNumber.LowOrder32Bits >> 16,
  560. ( _SerialNumber.LowOrder32Bits << 16 ) >> 16 );
  561. }
  562. }
  563. BOOLEAN
  564. TREE::ExamineDirectory(
  565. IN PCFSN_DIRECTORY Directory,
  566. IN PCWSTRING String
  567. )
  568. /*++
  569. Routine Description:
  570. Displays all files and subdirectories in the directory whose
  571. FSN_DIRECTORY was received as parameter.
  572. Arguments:
  573. Directory - Pointer to an FSN_DIRECTORY that describes the
  574. directory to be examined
  575. String - Pointer to a WSTRING that contains part of the string
  576. to be written in the left side of each subdiretory name
  577. and file name found in the directory being examined
  578. (the one whose FSN_DIRECTORY was received as parameter)
  579. Return Value:
  580. BOOLEAN - Returns TRUE to indicate that the operation succeded.
  581. Returns FALSE otherwise.
  582. --*/
  583. {
  584. PARRAY DirectoryArray;
  585. PFSN_DIRECTORY FsnDirectory;
  586. PARRAY_ITERATOR DirectoryArrayIterator;
  587. PARRAY FileArray;
  588. PARRAY_ITERATOR FileArrayIterator;
  589. PFSNODE FsnFile;
  590. #if 0
  591. PWSTRING StringFile;
  592. #endif
  593. #if 0
  594. PWSTRING StringDir;
  595. PWSTRING StringLastDir;
  596. PWSTRING StringSon;
  597. PWSTRING StringLastSon;
  598. #endif
  599. DSTRING StringFile;
  600. DSTRING StringDir;
  601. DSTRING StringLastDir;
  602. DSTRING StringSon;
  603. DSTRING StringLastSon;
  604. DebugPtrAssert( Directory );
  605. //
  606. // Build list of directories (ie., an array of PFSN_DIRECTORY of all
  607. // sub-directories in the current directory
  608. //
  609. DirectoryArray = Directory->QueryFsnodeArray( &_FsnFilterDirectory );
  610. DebugPtrAssert( DirectoryArray );
  611. DirectoryArrayIterator =
  612. ( PARRAY_ITERATOR )( DirectoryArray->QueryIterator() );
  613. DebugPtrAssert( DirectoryArrayIterator );
  614. //
  615. // If files are to be displayed (/F in the command line), then build
  616. // an array of files (ie., an array of PFSNODEs of all files in the
  617. // current directory)
  618. //
  619. if( _FlagDisplayFiles.QueryFlag() ) {
  620. FileArray = Directory->QueryFsnodeArray( &_FsnFilterFile );
  621. DebugPtrAssert( FileArray );
  622. FileArrayIterator =
  623. ( PARRAY_ITERATOR )( FileArray->QueryIterator() );
  624. DebugPtrAssert( FileArrayIterator );
  625. //
  626. // If array of files is not empty, then we need the strings
  627. // to be displayed in the left side of file names. These strings
  628. // will contain the pieces of the branches of the tree diagram.
  629. // These strings for files are created here.
  630. //
  631. if( FileArray->QueryMemberCount() > 0 ) {
  632. #if 0
  633. StringFile = NEW( DSTRING );
  634. DebugPtrAssert( StringFile );
  635. #endif
  636. if( DirectoryArray->QueryMemberCount() > 0 ) {
  637. if( String == NULL ) {
  638. StringFile.Initialize( &_StringForFile );
  639. } else {
  640. StringFile.Initialize( String );
  641. StringFile.Strcat( &_StringForFile );
  642. }
  643. } else {
  644. if( String == NULL ) {
  645. StringFile.Initialize( &_StringForFileNoDirectory );
  646. } else {
  647. StringFile.Initialize( String );
  648. StringFile.Strcat( &_StringForFileNoDirectory );
  649. }
  650. }
  651. //
  652. // Display all file names
  653. //
  654. while( ( FsnFile = ( PFSNODE )( FileArrayIterator->GetNext( ) ) ) != NULL ) {
  655. DisplayName( FsnFile, &StringFile );
  656. DELETE( FsnFile );
  657. }
  658. //
  659. // Display an empty line after the last file
  660. //
  661. _StandardOutput->WriteString( &StringFile, 0, StringFile.QueryChCount() );
  662. _StandardOutput->WriteString( &_EndOfLineString, 0, _EndOfLineString.QueryChCount() );
  663. }
  664. DELETE( FileArrayIterator );
  665. DELETE( FileArray );
  666. }
  667. //
  668. // If list of directories is not empty
  669. //
  670. if( DirectoryArray->QueryMemberCount() > 0 ) {
  671. _FlagAtLeastOneSubdir = TRUE;
  672. //
  673. // Build strings to be printed before directory name
  674. //
  675. if( String == NULL ) {
  676. StringDir.Initialize( &_StringForDirectory );
  677. StringLastDir.Initialize( &_StringForLastDirectory );
  678. StringSon.Initialize( &_StringForFile );
  679. StringLastSon.Initialize( &_StringForFileNoDirectory );
  680. } else {
  681. StringDir.Initialize( String );
  682. StringDir.Strcat( &_StringForDirectory );
  683. StringLastDir.Initialize( String );
  684. StringLastDir.Strcat( &_StringForLastDirectory );
  685. StringSon.Initialize( String );
  686. StringSon.Strcat( &_StringForFile );
  687. StringLastSon.Initialize( String );
  688. StringLastSon.Strcat( &_StringForFileNoDirectory );
  689. }
  690. //
  691. // Display name of all directories, and examine each one of them
  692. //
  693. while( ( FsnDirectory = ( PFSN_DIRECTORY )( DirectoryArrayIterator->GetNext( ) ) ) != NULL ) {
  694. if( DirectoryArrayIterator->QueryCurrentIndex() != DirectoryArray->QueryMemberCount() - 1 ) {
  695. DisplayName( ( PCFSNODE )FsnDirectory, &StringDir );
  696. ExamineDirectory( FsnDirectory, &StringSon );
  697. } else {
  698. DisplayName( ( PCFSNODE )FsnDirectory, &StringLastDir );
  699. ExamineDirectory( FsnDirectory, &StringLastSon );
  700. }
  701. DELETE( FsnDirectory );
  702. }
  703. }
  704. DELETE( DirectoryArrayIterator );
  705. DELETE( DirectoryArray );
  706. return( TRUE );
  707. }
  708. ULONG __cdecl
  709. main()
  710. {
  711. DEFINE_CLASS_DESCRIPTOR( TREE );
  712. {
  713. TREE Tree;
  714. PCFSN_DIRECTORY Directory;
  715. if( Tree.Initialize() ) {
  716. Tree.DisplayVolumeInfo();
  717. //
  718. // Display directory name
  719. //
  720. Directory = Tree.GetInitialDirectory();
  721. Tree.DisplayName( ( PCFSNODE )Directory, ( PCWSTRING )NULL );
  722. Tree.ExamineDirectory( Directory, ( PCWSTRING )NULL );
  723. }
  724. Tree.Terminate();
  725. }
  726. return( 0 );
  727. }