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
30 KiB
877 lines
30 KiB
/*++
|
|
|
|
Copyright (c) 1991-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
tree.cxx
|
|
|
|
Abstract:
|
|
|
|
This module contains the implementation of the TREE class.
|
|
The TREE class implements a tree utility functionally compatible
|
|
with the DOS 5 tree utility.
|
|
This utility displays the directory structure of a path or drive.
|
|
|
|
Usage:
|
|
|
|
TREE [drive:][path] [/F] [/A] [/?]
|
|
|
|
/F Display the names of files in each directory.
|
|
|
|
/A Uses ASCII instead of extended characters.
|
|
|
|
/? Displays a help message.
|
|
|
|
|
|
Author:
|
|
|
|
Jaime F. Sasson - jaimes - 13-May-1991
|
|
|
|
Environment:
|
|
|
|
ULIB, User Mode
|
|
|
|
--*/
|
|
|
|
#include "ulib.hxx"
|
|
#include "arg.hxx"
|
|
#include "array.hxx"
|
|
#include "path.hxx"
|
|
#include "wstring.hxx"
|
|
#include "substrng.hxx"
|
|
#include "dir.hxx"
|
|
#include "filter.hxx"
|
|
#include "system.hxx"
|
|
#include "arrayit.hxx"
|
|
#include "smsg.hxx"
|
|
#include "stream.hxx"
|
|
#include "rtmsg.h"
|
|
#include "tree.hxx"
|
|
|
|
extern "C" {
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
}
|
|
|
|
|
|
#define UNICODE_SINGLE_LEFT_T 0x251c
|
|
#define UNICODE_SINGLE_BOTTOM_LEFT_CORNER 0x2514
|
|
#define UNICODE_SINGLE_BOTTOM_HORIZONTAL 0x2500
|
|
#define UNICODE_SINGLE_VERTICAL 0x2502
|
|
#define UNICODE_SPACE 0x0020
|
|
|
|
|
|
PSTREAM Get_Standard_Input_Stream();
|
|
PSTREAM Get_Standard_Output_Stream();
|
|
|
|
DEFINE_CONSTRUCTOR( TREE, PROGRAM );
|
|
|
|
|
|
BOOLEAN
|
|
TREE::Initialize(
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes a TREE class.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - Indicates if the initialization succeeded.
|
|
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
ARGUMENT_LEXEMIZER ArgLex;
|
|
ARRAY LexArray;
|
|
|
|
ARRAY ArgumentArray;
|
|
|
|
STRING_ARGUMENT ProgramNameArgument;
|
|
PATH_ARGUMENT DirectoryPathArgument;
|
|
PWSTRING DirectoryNameString;
|
|
PATH DirectoryNamePath;
|
|
PWSTRING InvalidArgument;
|
|
PCWSTRING InvalidPath;
|
|
STRING_ARGUMENT InvalidSwitch;
|
|
PATH AuxPath;
|
|
STRING_ARGUMENT ParamNotCorrectFile;
|
|
STRING_ARGUMENT ParamNotCorrectAscii;
|
|
STRING_ARGUMENT ParamNotCorrectHelp;
|
|
BOOLEAN FlagInvalidPath;
|
|
PATH AuxInvPath;
|
|
PWSTRING InvPathNoDrive;
|
|
PWSTRING Device;
|
|
PATH DevPth;
|
|
PFSN_DIRECTORY DevDir;
|
|
#ifdef FE_SB
|
|
PWCHAR BufferMiddleBranch;
|
|
PWCHAR BufferBottomBranch;
|
|
PWCHAR BufferConnectingBranch;
|
|
|
|
WCHAR BufferMiddleBranchDBCS[] = {
|
|
UNICODE_SINGLE_LEFT_T, // 2
|
|
UNICODE_SINGLE_BOTTOM_HORIZONTAL, // 2
|
|
UNICODE_NULL // 0
|
|
};
|
|
WCHAR BufferBottomBranchDBCS[] = {
|
|
UNICODE_SINGLE_BOTTOM_LEFT_CORNER, // 2
|
|
UNICODE_SINGLE_BOTTOM_HORIZONTAL, // 2
|
|
UNICODE_NULL // 0
|
|
};
|
|
|
|
WCHAR BufferConnectingBranchDBCS[] = {
|
|
UNICODE_SINGLE_VERTICAL, // 2
|
|
UNICODE_SPACE, // 1
|
|
UNICODE_SPACE, // 1
|
|
UNICODE_NULL // 0
|
|
};
|
|
|
|
WCHAR BufferMiddleBranchSBCS[] = {
|
|
UNICODE_SINGLE_LEFT_T,
|
|
UNICODE_SINGLE_BOTTOM_HORIZONTAL,
|
|
UNICODE_SINGLE_BOTTOM_HORIZONTAL,
|
|
UNICODE_SINGLE_BOTTOM_HORIZONTAL,
|
|
UNICODE_NULL
|
|
};
|
|
WCHAR BufferBottomBranchSBCS[] = {
|
|
UNICODE_SINGLE_BOTTOM_LEFT_CORNER,
|
|
UNICODE_SINGLE_BOTTOM_HORIZONTAL,
|
|
UNICODE_SINGLE_BOTTOM_HORIZONTAL,
|
|
UNICODE_SINGLE_BOTTOM_HORIZONTAL,
|
|
UNICODE_NULL
|
|
};
|
|
|
|
WCHAR BufferConnectingBranchSBCS[] = {
|
|
UNICODE_SINGLE_VERTICAL,
|
|
UNICODE_SPACE,
|
|
UNICODE_SPACE,
|
|
UNICODE_SPACE,
|
|
UNICODE_NULL
|
|
};
|
|
|
|
switch ( ::GetConsoleOutputCP() ) {
|
|
case 932:
|
|
case 936:
|
|
case 949:
|
|
case 950:
|
|
BufferMiddleBranch = BufferMiddleBranchDBCS;
|
|
BufferBottomBranch = BufferBottomBranchDBCS;
|
|
BufferConnectingBranch = BufferConnectingBranchDBCS;
|
|
break;
|
|
|
|
default:
|
|
|
|
BufferMiddleBranch = BufferMiddleBranchSBCS;
|
|
BufferBottomBranch = BufferBottomBranchSBCS;
|
|
BufferConnectingBranch = BufferConnectingBranchSBCS;
|
|
break;
|
|
}
|
|
#else
|
|
|
|
WCHAR BufferMiddleBranch[] = {
|
|
UNICODE_SINGLE_LEFT_T,
|
|
UNICODE_SINGLE_BOTTOM_HORIZONTAL,
|
|
UNICODE_SINGLE_BOTTOM_HORIZONTAL,
|
|
UNICODE_SINGLE_BOTTOM_HORIZONTAL,
|
|
UNICODE_NULL
|
|
};
|
|
WCHAR BufferBottomBranch[] = {
|
|
UNICODE_SINGLE_BOTTOM_LEFT_CORNER,
|
|
UNICODE_SINGLE_BOTTOM_HORIZONTAL,
|
|
UNICODE_SINGLE_BOTTOM_HORIZONTAL,
|
|
UNICODE_SINGLE_BOTTOM_HORIZONTAL,
|
|
UNICODE_NULL
|
|
};
|
|
|
|
WCHAR BufferConnectingBranch[] = {
|
|
UNICODE_SINGLE_VERTICAL,
|
|
UNICODE_SPACE,
|
|
UNICODE_SPACE,
|
|
UNICODE_SPACE,
|
|
UNICODE_NULL
|
|
};
|
|
#endif
|
|
|
|
_InitialDirectory = NULL;
|
|
FlagInvalidPath = FALSE;
|
|
_FlagAtLeastOneSubdir = TRUE;
|
|
_StandardOutput = Get_Standard_Output_Stream();
|
|
|
|
//
|
|
// Initialize MESSAGE class
|
|
//
|
|
_Message.Initialize( _StandardOutput, Get_Standard_Input_Stream() );
|
|
|
|
//
|
|
// Parse command line
|
|
//
|
|
if ( !LexArray.Initialize( ) ) {
|
|
DebugAbort( "LexArray.Initialize() failed \n" );
|
|
return( FALSE );
|
|
}
|
|
if ( !ArgLex.Initialize( &LexArray ) ) {
|
|
DebugAbort( "ArgLex.Initialize() failed \n" );
|
|
return( FALSE );
|
|
}
|
|
ArgLex.PutSwitches( "/" );
|
|
ArgLex.PutStartQuotes( "\"" );
|
|
ArgLex.PutEndQuotes( "\"" );
|
|
ArgLex.PutSeparators( " \t" );
|
|
ArgLex.SetCaseSensitive( FALSE );
|
|
|
|
if( !ArgLex.PrepareToParse() ) {
|
|
DebugAbort( "ArgLex.PrepareToParse() failed \n" );
|
|
return( FALSE );
|
|
}
|
|
if ( !ArgumentArray.Initialize() ) {
|
|
DebugAbort( "ArgumentArray.Initialize() failed \n" );
|
|
return( FALSE );
|
|
}
|
|
if( !ProgramNameArgument.Initialize("*") ||
|
|
!_FlagDisplayFiles.Initialize( "/F" ) ||
|
|
!_FlagUseAsciiCharacters.Initialize( "/A" ) ||
|
|
!_FlagDisplayHelp.Initialize( "/?" ) ||
|
|
!ParamNotCorrectFile.Initialize( "/F*" ) ||
|
|
!ParamNotCorrectAscii.Initialize( "/A*" ) ||
|
|
!ParamNotCorrectHelp.Initialize( "/?*" ) ||
|
|
!InvalidSwitch.Initialize( "/*" ) ||
|
|
!DirectoryPathArgument.Initialize( "*" ) ) {
|
|
DebugAbort( "Unable to initialize flag or string arguments \n" );
|
|
return( FALSE );
|
|
}
|
|
if( !ArgumentArray.Put( &ProgramNameArgument ) ||
|
|
!ArgumentArray.Put( &_FlagDisplayFiles ) ||
|
|
!ArgumentArray.Put( &_FlagUseAsciiCharacters ) ||
|
|
!ArgumentArray.Put( &_FlagDisplayHelp ) ||
|
|
!ArgumentArray.Put( &ParamNotCorrectFile ) ||
|
|
!ArgumentArray.Put( &ParamNotCorrectAscii ) ||
|
|
!ArgumentArray.Put( &ParamNotCorrectHelp ) ||
|
|
!ArgumentArray.Put( &InvalidSwitch ) ||
|
|
!ArgumentArray.Put( &DirectoryPathArgument ) ) {
|
|
DebugAbort( "ArgumentArray.Put() failed \n" );
|
|
return( FALSE );
|
|
}
|
|
if( !ArgLex.DoParsing( &ArgumentArray ) ) {
|
|
InvalidArgument = ArgLex.QueryInvalidArgument();
|
|
DebugPtrAssert( InvalidArgument );
|
|
_Message.Set( MSG_TREE_TOO_MANY_PARAMETERS );
|
|
_Message.Display( "%W", InvalidArgument );
|
|
return( FALSE );
|
|
}
|
|
if( InvalidSwitch.IsValueSet() ) {
|
|
InvalidArgument = InvalidSwitch.GetString();
|
|
DebugPtrAssert( InvalidArgument );
|
|
_Message.Set( MSG_TREE_INVALID_SWITCH );
|
|
_Message.Display( "%W", InvalidArgument );
|
|
return( FALSE );
|
|
}
|
|
if( ParamNotCorrectFile.IsValueSet() ) {
|
|
InvalidArgument = ParamNotCorrectFile.GetLexeme();
|
|
DebugPtrAssert( InvalidArgument );
|
|
_Message.Set( MSG_TREE_PARAMETER_NOT_CORRECT );
|
|
_Message.Display( "%W", InvalidArgument );
|
|
return( FALSE );
|
|
}
|
|
if( ParamNotCorrectAscii.IsValueSet() ) {
|
|
InvalidArgument = ParamNotCorrectAscii.GetLexeme();
|
|
DebugPtrAssert( InvalidArgument );
|
|
_Message.Set( MSG_TREE_PARAMETER_NOT_CORRECT );
|
|
_Message.Display( "%W", InvalidArgument );
|
|
return( FALSE );
|
|
}
|
|
if( ParamNotCorrectHelp.IsValueSet() ) {
|
|
InvalidArgument = ParamNotCorrectHelp.GetLexeme();
|
|
DebugPtrAssert( InvalidArgument );
|
|
_Message.Set( MSG_TREE_PARAMETER_NOT_CORRECT );
|
|
_Message.Display( "%W", InvalidArgument );
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
//
|
|
// Displays help message if /? was found in the command line
|
|
//
|
|
if( _FlagDisplayHelp.QueryFlag() ) {
|
|
_Message.Set( MSG_TREE_HELP_MESSAGE );
|
|
_Message.Display( " " );
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// Find initial directory
|
|
//
|
|
if( !DirectoryPathArgument.IsValueSet() ) {
|
|
//
|
|
// User did't specify a path, so assume current directory
|
|
//
|
|
_FlagPathSupplied = FALSE;
|
|
|
|
if (!DirectoryNamePath.Initialize((LPWSTR)L".", TRUE )) {
|
|
|
|
DebugAbort( "DirectoryNamePath.Initialize() failed \n" );
|
|
return( FALSE );
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// User specified a path
|
|
//
|
|
|
|
DirectoryNameString = DirectoryPathArgument.GetPath()->QueryFullPathString();
|
|
DebugPtrAssert( DirectoryNameString );
|
|
DirectoryNameString->Strupr();
|
|
|
|
if (!DirectoryNamePath.Initialize(DirectoryNameString, TRUE)) {
|
|
DebugAbort( "DirectoryNamePath.Initialize() failed \n" );
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// Save the path specified by the user as he/she typed it
|
|
// (but in upper case)
|
|
//
|
|
if( !AuxPath.Initialize( DirectoryNameString, FALSE ) ) {
|
|
DebugAbort( "AuxPath.Initialize() failed \n" );
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// Validate the device if one was supplied
|
|
// AuxPath represents the path as typed by the user, and
|
|
// it may or may not contain a device. If it doesn't contain
|
|
// a device, no verification is made. In this case and we are
|
|
// sure that the device is valid
|
|
//
|
|
if( ( Device = AuxPath.QueryDevice() ) != NULL ) {
|
|
if( !DevPth.Initialize( Device ) ) {
|
|
DebugAbort( "DevPth.Initialize() failed \n" );
|
|
return( FALSE );
|
|
}
|
|
if( !(DevDir = SYSTEM::QueryDirectory( &DevPth ) ) ) {
|
|
_Message.Set( MSG_TREE_INVALID_DRIVE );
|
|
_Message.Display( " " );
|
|
DELETE( Device );
|
|
return( FALSE );
|
|
}
|
|
DELETE( Device );
|
|
DELETE( DevDir );
|
|
}
|
|
|
|
//
|
|
// Find out if what the user specified is just a drive
|
|
//
|
|
if( AuxPath.IsDrive() ) {
|
|
_FlagPathSupplied = FALSE;
|
|
} else {
|
|
_FlagPathSupplied = TRUE;
|
|
}
|
|
}
|
|
|
|
if (NULL == (_InitialDirectory = SYSTEM::QueryDirectory( &DirectoryNamePath ))) {
|
|
|
|
InvalidPath = DirectoryNamePath.GetPathString();
|
|
FlagInvalidPath = TRUE;
|
|
_FlagAtLeastOneSubdir = FALSE;
|
|
}
|
|
|
|
//
|
|
// Initialize filter for directories
|
|
//
|
|
|
|
if( !_FsnFilterDirectory.Initialize() ) {
|
|
DELETE( _InitialDirectory );
|
|
DebugAbort( "_FsnFilterDirectory.Initialize() failed \n" );
|
|
return( FALSE );
|
|
}
|
|
if( !_FsnFilterDirectory.SetFileName( "*.*" ) ) {
|
|
DELETE( _InitialDirectory );
|
|
DebugAbort( "_FsnFilterDirectory.SetFilename() failed \n" );
|
|
return( FALSE );
|
|
}
|
|
if( !_FsnFilterDirectory.SetAttributes( FSN_ATTRIBUTE_DIRECTORY,
|
|
( FSN_ATTRIBUTE )0,
|
|
FSN_ATTRIBUTE_HIDDEN |
|
|
FSN_ATTRIBUTE_SYSTEM ) ) {
|
|
DELETE( _InitialDirectory );
|
|
DebugAbort( "_FsnFilterDirectory.SetAttributes() failed \n" );
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// Initilize filter for files
|
|
//
|
|
|
|
if( _FlagDisplayFiles.QueryFlag() ) {
|
|
if( !_FsnFilterFile.Initialize() ) {
|
|
DELETE( _InitialDirectory );
|
|
DebugAbort( "FsnFilter.Initialize() failed \n" );
|
|
return( FALSE );
|
|
}
|
|
if( !_FsnFilterFile.SetFileName( "*.*" ) ) {
|
|
DELETE( _InitialDirectory );
|
|
DebugAbort( "FsnFilter.SetFilename() failed \n" );
|
|
return( FALSE );
|
|
}
|
|
if( !_FsnFilterFile.SetAttributes( ( FSN_ATTRIBUTE )0,
|
|
( FSN_ATTRIBUTE )0,
|
|
FSN_ATTRIBUTE_DIRECTORY |
|
|
FSN_ATTRIBUTE_HIDDEN |
|
|
FSN_ATTRIBUTE_SYSTEM ) ) {
|
|
DELETE( _InitialDirectory );
|
|
DebugAbort( "FsnFilter.SetAttributes() failed \n" );
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Find out what kind of characters should be used to display the tree
|
|
// and initialize the basic strings
|
|
//
|
|
if( _FlagUseAsciiCharacters.QueryFlag() ) {
|
|
|
|
_StringForDirectory.Initialize( (LPWSTR)L"+---" );
|
|
_StringForLastDirectory.Initialize( (LPWSTR)L"\\---" );
|
|
_StringForFile.Initialize( (LPWSTR)L"| " );
|
|
|
|
} else {
|
|
|
|
_StringForDirectory.Initialize( BufferMiddleBranch ); // "ÃÄÄÄ"
|
|
_StringForLastDirectory.Initialize( BufferBottomBranch ); // "ÀÄÄÄ"
|
|
_StringForFile.Initialize( BufferConnectingBranch ); // "³ "
|
|
|
|
}
|
|
_StringForFileNoDirectory.Initialize( (LPWSTR)L" " );
|
|
|
|
if( !_EndOfLineString.Initialize( (LPWSTR)L"\r\n" ) ) {
|
|
DebugPrint( "_EndOfLineString.Initialize() failed" );
|
|
DELETE( _InitialDirectory );
|
|
return( FALSE );
|
|
}
|
|
|
|
_VolumeName = SYSTEM::QueryVolumeLabel( &DirectoryNamePath, &_SerialNumber );
|
|
if (NULL == _VolumeName) {
|
|
_Message.Set( MSG_TREE_INVALID_DRIVE );
|
|
_Message.Display( " " );
|
|
DELETE( Device );
|
|
return( FALSE );
|
|
}
|
|
DebugPtrAssert( _VolumeName );
|
|
_FlagAtLeastOneSubdir = FALSE;
|
|
if( FlagInvalidPath ) {
|
|
//
|
|
// If user specified an invalid path, then display the same
|
|
// messages that Dos 5 does.
|
|
//
|
|
// First displays the volume info.
|
|
//
|
|
|
|
DisplayVolumeInfo();
|
|
|
|
//
|
|
// Then display the path that is invalid. This path must be
|
|
// displayed in capital letters, must always contain a device,
|
|
// and must be displayed as a relative or absolute path depending
|
|
// on what the user specified.
|
|
//
|
|
Device = DirectoryNamePath.QueryDevice();
|
|
AuxInvPath.Initialize( DirectoryNameString, FALSE );
|
|
InvPathNoDrive = AuxInvPath.QueryDirsAndName();
|
|
_StandardOutput->WriteString( Device );
|
|
_StandardOutput->WriteString( InvPathNoDrive );
|
|
_StandardOutput->WriteByte( '\r' );
|
|
_StandardOutput->WriteByte( '\n' );
|
|
|
|
//
|
|
// Display the message "Invalid Path - <path>"
|
|
// The path in this case cannot contain a drive even if the
|
|
// user specified a drive.
|
|
//
|
|
_Message.Set( MSG_TREE_INVALID_PATH );
|
|
_Message.Display( "%W", InvPathNoDrive );
|
|
DELETE( Device );
|
|
DELETE( InvPathNoDrive );
|
|
return( FALSE );
|
|
}
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
VOID
|
|
TREE::Terminate(
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Deletes objects created during initialization.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
if( _InitialDirectory != NULL ) {
|
|
DELETE( _InitialDirectory );
|
|
}
|
|
if( !_FlagAtLeastOneSubdir ) {
|
|
_Message.Set( MSG_TREE_NO_SUBDIRECTORIES );
|
|
_Message.Display( "%s", "\r\n" );
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOLEAN
|
|
TREE::DisplayName (
|
|
IN PCFSNODE Fsn,
|
|
IN PCWSTRING String
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method writes to the standard output the name of a file or
|
|
directory, preceded by a string that represents pieces of the
|
|
tree structure (pieces of the tree branches).
|
|
|
|
Arguments:
|
|
|
|
Fsn - Pointer to a FSNODE that describes the file or directory whose
|
|
name is to be written.
|
|
|
|
String - Pointer to a WSTRING object that contains the string to
|
|
be written before the file or directory name.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - returns TRUE to indicate that the operation succeeded, or
|
|
FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
PWSTRING Name;
|
|
PCPATH Path;
|
|
PCWSTRING InitialDirectory;
|
|
DSTRING InitialDirectoryUpperCase;
|
|
PWSTRING Device;
|
|
DSTRING EndOfLinePrecededByDot;
|
|
|
|
DebugPtrAssert( Fsn );
|
|
|
|
Path = Fsn->GetPath();
|
|
DebugPtrAssert( Path );
|
|
if( String != NULL ) {
|
|
if( !_StandardOutput->WriteString( String, 0, String->QueryChCount() ) ) {
|
|
DebugAbort( "_StandardOutput->WriteString() failed \n" );
|
|
return( FALSE );
|
|
}
|
|
if( ( Name=Path->QueryName() ) == NULL ) {
|
|
DebugAbort( "Path->QueryName() failed \n" );
|
|
return( FALSE );
|
|
}
|
|
Name->Strcat( &_EndOfLineString );
|
|
|
|
if( !_StandardOutput->WriteString( Name, 0, Name->QueryChCount() ) ) {
|
|
DebugAbort( "_StandardOutput->WriteString() failed \n" );
|
|
DELETE( Name );
|
|
return( FALSE );
|
|
}
|
|
|
|
DELETE( Name );
|
|
} else {
|
|
if( _FlagPathSupplied ) {
|
|
InitialDirectory = Path->GetPathString();
|
|
DebugPtrAssert( InitialDirectory );
|
|
InitialDirectoryUpperCase.Initialize( InitialDirectory );
|
|
InitialDirectoryUpperCase.Strupr();
|
|
InitialDirectoryUpperCase.Strcat( &_EndOfLineString );
|
|
if( !_StandardOutput->WriteString( &InitialDirectoryUpperCase ) ) {
|
|
DebugAbort( "_StandardOutput->WriteString() failed \n" );
|
|
return( FALSE );
|
|
}
|
|
} else {
|
|
Device = PPATH(Path)->QueryDevice();
|
|
if (Device == NULL) {
|
|
DebugAbort("TREE: Path->QueryDevice() failed\n");
|
|
return FALSE;
|
|
}
|
|
DebugPtrAssert( Device );
|
|
EndOfLinePrecededByDot.Initialize( (LPWSTR)L".\r\n" );
|
|
Device->Strcat( &EndOfLinePrecededByDot );
|
|
if( !_StandardOutput->WriteString( Device, 0, TO_END ) ) {
|
|
DebugAbort( "_StandardOutput->WriteString() failed \n" );
|
|
DELETE( Device );
|
|
return( FALSE );
|
|
}
|
|
DELETE( Device );
|
|
}
|
|
}
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
VOID
|
|
TREE::DisplayVolumeInfo (
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method writes to the standard output the the volume name (if one
|
|
is found) and the serial number.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Display volume name
|
|
//
|
|
if( _VolumeName->QueryChCount() == 0 ) {
|
|
_Message.Set( MSG_TREE_DIR_LISTING_NO_VOLUME_NAME );
|
|
_Message.Display( " " );
|
|
} else {
|
|
_Message.Set( MSG_TREE_DIR_LISTING_WITH_VOLUME_NAME );
|
|
_Message.Display( "%W", _VolumeName );
|
|
}
|
|
|
|
//
|
|
// Display serial number
|
|
//
|
|
if( _SerialNumber.HighOrder32Bits != 0 ) {
|
|
_Message.Set( MSG_TREE_64_BIT_SERIAL_NUMBER );
|
|
_Message.Display( "%08X%04X%04X",
|
|
_SerialNumber.HighOrder32Bits,
|
|
_SerialNumber.LowOrder32Bits >> 16,
|
|
( _SerialNumber.LowOrder32Bits << 16 ) >> 16);
|
|
} else {
|
|
_Message.Set( MSG_TREE_32_BIT_SERIAL_NUMBER );
|
|
_Message.Display( "%04X%04X",
|
|
_SerialNumber.LowOrder32Bits >> 16,
|
|
( _SerialNumber.LowOrder32Bits << 16 ) >> 16 );
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
TREE::ExamineDirectory(
|
|
IN PCFSN_DIRECTORY Directory,
|
|
IN PCWSTRING String
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Displays all files and subdirectories in the directory whose
|
|
FSN_DIRECTORY was received as parameter.
|
|
|
|
|
|
Arguments:
|
|
|
|
Directory - Pointer to an FSN_DIRECTORY that describes the
|
|
directory to be examined
|
|
|
|
String - Pointer to a WSTRING that contains part of the string
|
|
to be written in the left side of each subdiretory name
|
|
and file name found in the directory being examined
|
|
(the one whose FSN_DIRECTORY was received as parameter)
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - Returns TRUE to indicate that the operation succeded.
|
|
Returns FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
PARRAY DirectoryArray;
|
|
PFSN_DIRECTORY FsnDirectory;
|
|
PARRAY_ITERATOR DirectoryArrayIterator;
|
|
PARRAY FileArray;
|
|
PARRAY_ITERATOR FileArrayIterator;
|
|
PFSNODE FsnFile;
|
|
#if 0
|
|
PWSTRING StringFile;
|
|
#endif
|
|
#if 0
|
|
PWSTRING StringDir;
|
|
PWSTRING StringLastDir;
|
|
PWSTRING StringSon;
|
|
PWSTRING StringLastSon;
|
|
#endif
|
|
DSTRING StringFile;
|
|
DSTRING StringDir;
|
|
DSTRING StringLastDir;
|
|
DSTRING StringSon;
|
|
DSTRING StringLastSon;
|
|
|
|
DebugPtrAssert( Directory );
|
|
|
|
//
|
|
// Build list of directories (ie., an array of PFSN_DIRECTORY of all
|
|
// sub-directories in the current directory
|
|
//
|
|
DirectoryArray = Directory->QueryFsnodeArray( &_FsnFilterDirectory );
|
|
DebugPtrAssert( DirectoryArray );
|
|
DirectoryArrayIterator =
|
|
( PARRAY_ITERATOR )( DirectoryArray->QueryIterator() );
|
|
DebugPtrAssert( DirectoryArrayIterator );
|
|
|
|
//
|
|
// If files are to be displayed (/F in the command line), then build
|
|
// an array of files (ie., an array of PFSNODEs of all files in the
|
|
// current directory)
|
|
//
|
|
if( _FlagDisplayFiles.QueryFlag() ) {
|
|
FileArray = Directory->QueryFsnodeArray( &_FsnFilterFile );
|
|
DebugPtrAssert( FileArray );
|
|
FileArrayIterator =
|
|
( PARRAY_ITERATOR )( FileArray->QueryIterator() );
|
|
DebugPtrAssert( FileArrayIterator );
|
|
|
|
//
|
|
// If array of files is not empty, then we need the strings
|
|
// to be displayed in the left side of file names. These strings
|
|
// will contain the pieces of the branches of the tree diagram.
|
|
// These strings for files are created here.
|
|
//
|
|
if( FileArray->QueryMemberCount() > 0 ) {
|
|
#if 0
|
|
StringFile = NEW( DSTRING );
|
|
DebugPtrAssert( StringFile );
|
|
#endif
|
|
if( DirectoryArray->QueryMemberCount() > 0 ) {
|
|
if( String == NULL ) {
|
|
StringFile.Initialize( &_StringForFile );
|
|
} else {
|
|
StringFile.Initialize( String );
|
|
StringFile.Strcat( &_StringForFile );
|
|
}
|
|
} else {
|
|
if( String == NULL ) {
|
|
StringFile.Initialize( &_StringForFileNoDirectory );
|
|
} else {
|
|
StringFile.Initialize( String );
|
|
StringFile.Strcat( &_StringForFileNoDirectory );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Display all file names
|
|
//
|
|
while( ( FsnFile = ( PFSNODE )( FileArrayIterator->GetNext( ) ) ) != NULL ) {
|
|
DisplayName( FsnFile, &StringFile );
|
|
DELETE( FsnFile );
|
|
}
|
|
|
|
//
|
|
// Display an empty line after the last file
|
|
//
|
|
_StandardOutput->WriteString( &StringFile, 0, StringFile.QueryChCount() );
|
|
_StandardOutput->WriteString( &_EndOfLineString, 0, _EndOfLineString.QueryChCount() );
|
|
|
|
}
|
|
DELETE( FileArrayIterator );
|
|
DELETE( FileArray );
|
|
}
|
|
|
|
//
|
|
// If list of directories is not empty
|
|
//
|
|
if( DirectoryArray->QueryMemberCount() > 0 ) {
|
|
_FlagAtLeastOneSubdir = TRUE;
|
|
//
|
|
// Build strings to be printed before directory name
|
|
//
|
|
if( String == NULL ) {
|
|
StringDir.Initialize( &_StringForDirectory );
|
|
StringLastDir.Initialize( &_StringForLastDirectory );
|
|
StringSon.Initialize( &_StringForFile );
|
|
StringLastSon.Initialize( &_StringForFileNoDirectory );
|
|
} else {
|
|
StringDir.Initialize( String );
|
|
StringDir.Strcat( &_StringForDirectory );
|
|
StringLastDir.Initialize( String );
|
|
StringLastDir.Strcat( &_StringForLastDirectory );
|
|
StringSon.Initialize( String );
|
|
StringSon.Strcat( &_StringForFile );
|
|
StringLastSon.Initialize( String );
|
|
StringLastSon.Strcat( &_StringForFileNoDirectory );
|
|
}
|
|
//
|
|
// Display name of all directories, and examine each one of them
|
|
//
|
|
while( ( FsnDirectory = ( PFSN_DIRECTORY )( DirectoryArrayIterator->GetNext( ) ) ) != NULL ) {
|
|
if( DirectoryArrayIterator->QueryCurrentIndex() != DirectoryArray->QueryMemberCount() - 1 ) {
|
|
DisplayName( ( PCFSNODE )FsnDirectory, &StringDir );
|
|
ExamineDirectory( FsnDirectory, &StringSon );
|
|
} else {
|
|
DisplayName( ( PCFSNODE )FsnDirectory, &StringLastDir );
|
|
ExamineDirectory( FsnDirectory, &StringLastSon );
|
|
}
|
|
DELETE( FsnDirectory );
|
|
}
|
|
}
|
|
DELETE( DirectoryArrayIterator );
|
|
DELETE( DirectoryArray );
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
|
|
ULONG __cdecl
|
|
main()
|
|
|
|
{
|
|
DEFINE_CLASS_DESCRIPTOR( TREE );
|
|
|
|
{
|
|
TREE Tree;
|
|
PCFSN_DIRECTORY Directory;
|
|
|
|
if( Tree.Initialize() ) {
|
|
Tree.DisplayVolumeInfo();
|
|
//
|
|
// Display directory name
|
|
//
|
|
Directory = Tree.GetInitialDirectory();
|
|
Tree.DisplayName( ( PCFSNODE )Directory, ( PCWSTRING )NULL );
|
|
Tree.ExamineDirectory( Directory, ( PCWSTRING )NULL );
|
|
}
|
|
Tree.Terminate();
|
|
}
|
|
return( 0 );
|
|
}
|