660 lines
14 KiB
C++
660 lines
14 KiB
C++
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1991 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
find.cxx
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This utility allows the user to search for strings in a file
|
|||
|
It is functionaly compatible with DOS 5 find utility.
|
|||
|
|
|||
|
SYNTAX (Command line)
|
|||
|
|
|||
|
FIND [/?][/V][/C][/N][/I] "string" [[d:][path]filename[.ext]...]
|
|||
|
|
|||
|
where:
|
|||
|
|
|||
|
/? - Display this help
|
|||
|
/V - Display all lines NOT containing the string
|
|||
|
/C - Display only a count of lines containing string
|
|||
|
/N - Display number of line containing string
|
|||
|
/I - Ignore case
|
|||
|
|
|||
|
UTILITY FUNCTION:
|
|||
|
|
|||
|
Searches the specified file(s) looking for the string the user
|
|||
|
entered from the command line. If file name(s) are specifeied,
|
|||
|
those names are displayed, and if the string is found, then the
|
|||
|
entire line containing that string will be displayed. Optional
|
|||
|
parameters modify that behavior and are described above. String
|
|||
|
arguments have to be enclosed in double quotes. (Two double quotes
|
|||
|
if a double quote is to be included). Only one string argument is
|
|||
|
presently allowed. The maximum line size is determined by buffer
|
|||
|
size. Bigger lines will bomb the program. If no file name is given
|
|||
|
then it will asssume the input is coming from the standard Input.
|
|||
|
No errors are reported when reading from standard Input.
|
|||
|
|
|||
|
|
|||
|
EXIT:
|
|||
|
The program returns errorlevel:
|
|||
|
0 - OK, and some matches
|
|||
|
1 -
|
|||
|
2 - Some Error
|
|||
|
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Bruce Wilson (w-wilson) 08-May-1991
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
ULIB, User Mode
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
08-May-1991 w-wilson
|
|||
|
|
|||
|
created
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "ulib.hxx"
|
|||
|
#include "ulibcl.hxx"
|
|||
|
#include "error.hxx"
|
|||
|
#include "arg.hxx"
|
|||
|
#include "array.hxx"
|
|||
|
#include "path.hxx"
|
|||
|
#include "wstring.hxx"
|
|||
|
#include "substrng.hxx"
|
|||
|
#include "filestrm.hxx"
|
|||
|
#include "file.hxx"
|
|||
|
#include "system.hxx"
|
|||
|
#include "arrayit.hxx"
|
|||
|
#include "smsg.hxx"
|
|||
|
#include "rtmsg.h"
|
|||
|
#include "find.hxx"
|
|||
|
|
|||
|
|
|||
|
extern "C" {
|
|||
|
#include <stdio.h>
|
|||
|
#include <string.h>
|
|||
|
}
|
|||
|
|
|||
|
#define MAX_LINE_LEN 1024
|
|||
|
|
|||
|
|
|||
|
ERRSTACK* perrstk;
|
|||
|
static STR TmpBuf[MAX_LINE_LEN];
|
|||
|
|
|||
|
DEFINE_CONSTRUCTOR( FIND, PROGRAM );
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
FIND::Initialize(
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Initializes an FIND class.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
BOOLEAN - Indicates if the initialization succeeded.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
|
|||
|
{
|
|||
|
ARGUMENT_LEXEMIZER ArgLex;
|
|||
|
ARRAY LexArray;
|
|||
|
|
|||
|
ARRAY ArgumentArray;
|
|||
|
|
|||
|
STRING_ARGUMENT ProgramNameArgument;
|
|||
|
FLAG_ARGUMENT FlagCaseInsensitive;
|
|||
|
FLAG_ARGUMENT FlagNegativeSearch;
|
|||
|
FLAG_ARGUMENT FlagCountLines;
|
|||
|
FLAG_ARGUMENT FlagDisplayNumbers;
|
|||
|
FLAG_ARGUMENT FlagDisplayHelp;
|
|||
|
FLAG_ARGUMENT FlagInvalid;
|
|||
|
STRING_ARGUMENT StringPattern;
|
|||
|
|
|||
|
PROGRAM::Initialize();
|
|||
|
|
|||
|
_ErrorLevel = 0;
|
|||
|
|
|||
|
if( !SYSTEM::IsCorrectVersion() ) {
|
|||
|
DisplayMessage(MSG_FIND_INCORRECT_VERSION);
|
|||
|
// BUGBUG: (w-wilson) this seems stupid - shouldn't it be 1 or 2??
|
|||
|
_ErrorLevel = 0;
|
|||
|
return(FALSE);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// - init the array that will contain the command-line args
|
|||
|
//
|
|||
|
if ( !ArgumentArray.Initialize() ) {
|
|||
|
DbgAbort( "ArgumentArray.Initialize() failed \n" );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// - init the individual arguments
|
|||
|
//
|
|||
|
if( !ProgramNameArgument.Initialize("*")
|
|||
|
|| !FlagCaseInsensitive.Initialize( "/I" )
|
|||
|
|| !FlagNegativeSearch.Initialize( "/V" )
|
|||
|
|| !FlagCountLines.Initialize( "/C" )
|
|||
|
|| !FlagDisplayNumbers.Initialize( "/N" )
|
|||
|
|| !FlagDisplayHelp.Initialize( "/?" )
|
|||
|
|| !FlagInvalid.Initialize( "/*" )
|
|||
|
|| !StringPattern.Initialize( "\"*\"" )
|
|||
|
|| !_PathArguments.Initialize( "*", FALSE, TRUE ) ) {
|
|||
|
|
|||
|
DbgAbort( "Unable to initialize flag or string arguments \n" );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// - put the arguments in the array
|
|||
|
//
|
|||
|
if( !ArgumentArray.Put( &ProgramNameArgument )
|
|||
|
|| !ArgumentArray.Put( &FlagCaseInsensitive )
|
|||
|
|| !ArgumentArray.Put( &FlagNegativeSearch )
|
|||
|
|| !ArgumentArray.Put( &FlagCountLines )
|
|||
|
|| !ArgumentArray.Put( &FlagDisplayNumbers )
|
|||
|
|| !ArgumentArray.Put( &FlagDisplayHelp )
|
|||
|
|| !ArgumentArray.Put( &FlagInvalid )
|
|||
|
|| !ArgumentArray.Put( &StringPattern )
|
|||
|
|| !ArgumentArray.Put( &_PathArguments ) ) {
|
|||
|
|
|||
|
DbgAbort( "ArgumentArray.Put() failed \n" );
|
|||
|
}
|
|||
|
//
|
|||
|
// - init the lexemizer
|
|||
|
//
|
|||
|
if ( !LexArray.Initialize() ) {
|
|||
|
DbgAbort( "LexArray.Initialize() failed \n" );
|
|||
|
}
|
|||
|
if ( !ArgLex.Initialize( &LexArray ) ) {
|
|||
|
DbgAbort( "ArgLex.Initialize() failed \n" );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// - set up the defaults
|
|||
|
//
|
|||
|
ArgLex.PutSwitches( "/" );
|
|||
|
ArgLex.PutStartQuotes( "\"" );
|
|||
|
ArgLex.PutEndQuotes( "\"" );
|
|||
|
ArgLex.PutSeparators( " \"" );
|
|||
|
ArgLex.SetCaseSensitive( FALSE );
|
|||
|
if( !ArgLex.PrepareToParse() ) {
|
|||
|
|
|||
|
//
|
|||
|
// invalid format
|
|||
|
//
|
|||
|
DisplayMessage(MSG_FIND_INVALID_FORMAT);
|
|||
|
|
|||
|
_ErrorLevel = 2;
|
|||
|
return( FALSE );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// - now parse the command line. The args in the array will be set
|
|||
|
// if they are found on the command line.
|
|||
|
//
|
|||
|
if( !ArgLex.DoParsing( &ArgumentArray ) ) {
|
|||
|
if( FlagInvalid.QueryFlag() ) {
|
|||
|
//
|
|||
|
// invalid switch
|
|||
|
//
|
|||
|
DisplayMessage(MSG_FIND_INVALID_SWITCH);
|
|||
|
|
|||
|
} else {
|
|||
|
//
|
|||
|
// invalid format
|
|||
|
//
|
|||
|
DisplayMessage(MSG_FIND_INVALID_FORMAT);
|
|||
|
}
|
|||
|
_ErrorLevel = 2;
|
|||
|
return( FALSE );
|
|||
|
} else if ( _PathArguments.WildCardExpansionFailed() ) {
|
|||
|
|
|||
|
//
|
|||
|
// No files matched
|
|||
|
//
|
|||
|
DisplayMessage(MSG_FIND_FILE_NOT_FOUND, ERROR_MESSAGE, "%W", _PathArguments.GetLexemeThatFailed() );
|
|||
|
|
|||
|
_ErrorLevel = 2;
|
|||
|
return( FALSE );
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
// DbgPrint( "\nargs parsed ok\n" );
|
|||
|
}
|
|||
|
|
|||
|
if( FlagInvalid.QueryFlag() ) {
|
|||
|
//
|
|||
|
// invalid switch
|
|||
|
//
|
|||
|
DisplayMessage(MSG_FIND_INVALID_SWITCH, ERROR_MESSAGE );
|
|||
|
_ErrorLevel = 2;
|
|||
|
return( FALSE );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// - now do semantic checking/processing
|
|||
|
// - if they ask for help, do it right away and return
|
|||
|
// - set flags
|
|||
|
//
|
|||
|
if( FlagDisplayHelp.QueryFlag() ) {
|
|||
|
DisplayMessage(MSG_FIND_USAGE);
|
|||
|
_ErrorLevel = 0;
|
|||
|
return( FALSE );
|
|||
|
}
|
|||
|
|
|||
|
if( !StringPattern.IsValueSet() ) {
|
|||
|
DisplayMessage(MSG_FIND_INVALID_FORMAT);
|
|||
|
_ErrorLevel = 2;
|
|||
|
return( FALSE );
|
|||
|
} else {
|
|||
|
//
|
|||
|
// - keep a copy of the pattern string
|
|||
|
//
|
|||
|
|
|||
|
DbgAssert(StringPattern.GetString());
|
|||
|
_PatternString = *StringPattern.GetString();
|
|||
|
|
|||
|
_PatternString.QuerySTR( 0, TO_END, TmpBuf, sizeof( TmpBuf ));
|
|||
|
// DbgPrint("pattern is ");
|
|||
|
// DbgPrint(TmpBuf);
|
|||
|
// DbgPrint("\n");
|
|||
|
}
|
|||
|
|
|||
|
_CaseSensitive = (BOOLEAN)!FlagCaseInsensitive.QueryFlag();
|
|||
|
|
|||
|
_LinesContainingPattern = (BOOLEAN)!FlagNegativeSearch.QueryFlag();
|
|||
|
|
|||
|
_OutputLines = (BOOLEAN)!FlagCountLines.QueryFlag();
|
|||
|
|
|||
|
_OutputLineNumbers = (BOOLEAN)FlagDisplayNumbers.QueryFlag();
|
|||
|
|
|||
|
DbgAssert(sprintf(TmpBuf,
|
|||
|
"flags:caseSens %1d, +veSrch %1d, outLines %1d, line#'s %1d\n",
|
|||
|
_CaseSensitive, _LinesContainingPattern, _OutputLines,
|
|||
|
_OutputLineNumbers) > 0);
|
|||
|
// DbgPrint(TmpBuf);
|
|||
|
|
|||
|
return( TRUE );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
FIND::IsDos5CompatibleFileName(
|
|||
|
IN PCPATH Path
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Parses the path string and returns FALSE if DOS5 would reject
|
|||
|
the path.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Path - Supplies the path
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
BOOLEAN - Returns FALSE if DOS5 would reject the path,
|
|||
|
TRUE otherwise
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
PWSTRING String;
|
|||
|
|
|||
|
DbgPtrAssert( Path );
|
|||
|
|
|||
|
String = (PWSTRING)Path->GetPathString();
|
|||
|
|
|||
|
DbgPtrAssert( String );
|
|||
|
|
|||
|
if ( String->QueryChCount() > 0 ) {
|
|||
|
|
|||
|
if ( String->QueryChAt(0) == '\"' ) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return TRUE;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
ULONG
|
|||
|
FIND::SearchStream(
|
|||
|
PSTREAM StreamToSearch
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Does the search on an open file_stream.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Number of lines found/not found.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
ULONG LineCount;
|
|||
|
ULONG FoundCount;
|
|||
|
CHNUM PatternLen;
|
|||
|
CHNUM PositionInLine;
|
|||
|
CHNUM LastPosInLine;
|
|||
|
WSTRING CurrentLine;
|
|||
|
WSTRING Delim;
|
|||
|
USHORT CompareFlags;
|
|||
|
WCHAR Wchar;
|
|||
|
BOOLEAN Found;
|
|||
|
|
|||
|
LineCount = FoundCount = 0;
|
|||
|
PatternLen = _PatternString.QueryChCount();
|
|||
|
if( !Delim.Initialize( "\r\n" ) ) {
|
|||
|
DbgAbort( "Delim.Initialize() failed \n" );
|
|||
|
}
|
|||
|
if( !CurrentLine.Initialize("") ) {
|
|||
|
DbgAbort( "CurrentLine.Initialize() failed \n" );
|
|||
|
}
|
|||
|
CompareFlags = (_CaseSensitive) ? 0 : CF_IGNORECASE;
|
|||
|
|
|||
|
//
|
|||
|
// - for each line from stream
|
|||
|
// - do strstr to see if pattern string is in line
|
|||
|
// - if -ve search and not in line || +ve search and in line
|
|||
|
// - output line and number or inc counter appropriately
|
|||
|
//
|
|||
|
// DbgPrint("searching stream\n");
|
|||
|
while( !StreamToSearch->IsAtEnd() ) {
|
|||
|
|
|||
|
//
|
|||
|
// BUGBUG: the following can be replaced with ReadLine() when
|
|||
|
// it's ready
|
|||
|
//
|
|||
|
//if( !StreamToSearch->ReadString(&CurrentLine, &Delim) ) {
|
|||
|
// DbgAbort( "ReadString() failed \n" );
|
|||
|
//}
|
|||
|
//if( !StreamToSearch->IsAtEnd() ) {
|
|||
|
// if( !StreamToSearch->ReadChar( &Wchar ) ) {
|
|||
|
// DbgAbort( "ReadChar() failed \n" );
|
|||
|
// }
|
|||
|
// if( Wchar == ( WCHAR )'\r' ) {
|
|||
|
// if( !StreamToSearch->IsAtEnd() ) {
|
|||
|
// if( !StreamToSearch->ReadChar( &Wchar ) ) {
|
|||
|
// DbgAbort( "ReadChar() failed \n" );
|
|||
|
// }
|
|||
|
// }
|
|||
|
// }
|
|||
|
//}
|
|||
|
|
|||
|
if ( !StreamToSearch->ReadLine( &CurrentLine ) ) {
|
|||
|
DbgAbort( "ReadLine failed\n" );
|
|||
|
}
|
|||
|
|
|||
|
LineCount++;
|
|||
|
// DbgPrint(".");
|
|||
|
|
|||
|
//
|
|||
|
// - look for pattern string in the current line
|
|||
|
// - note: a 0-length pattern ("") never matches a line.
|
|||
|
// A 0-length pattern can produce output with the /v
|
|||
|
// switch.
|
|||
|
// - start at the end (saves a var)
|
|||
|
//
|
|||
|
|
|||
|
Found = FALSE;
|
|||
|
|
|||
|
if( PatternLen && CurrentLine.QueryChCount() >= PatternLen ) {
|
|||
|
|
|||
|
LastPosInLine = CurrentLine.QueryChCount() - PatternLen;
|
|||
|
|
|||
|
for( PositionInLine = 0;
|
|||
|
PositionInLine <= LastPosInLine; PositionInLine++ ) {
|
|||
|
|
|||
|
if( CurrentLine.StringCompare(PositionInLine, PatternLen,
|
|||
|
&_PatternString, 0, PatternLen,
|
|||
|
CompareFlags) == 0 ) {
|
|||
|
//
|
|||
|
// found a match so exit loop
|
|||
|
//
|
|||
|
Found = TRUE;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// - if either (search is +ve and found a match)
|
|||
|
// or (search is -ve and no match found)
|
|||
|
// then print line/line number based on options
|
|||
|
//
|
|||
|
if( (_LinesContainingPattern && Found)
|
|||
|
|| (!_LinesContainingPattern && !Found) ) {
|
|||
|
|
|||
|
FoundCount++;
|
|||
|
if( _OutputLines ) {
|
|||
|
if( _OutputLineNumbers ) {
|
|||
|
DisplayMessage( MSG_FIND_LINE_AND_NUMBER, NORMAL_MESSAGE, "%d%W", LineCount, &CurrentLine);
|
|||
|
} else {
|
|||
|
DisplayMessage( MSG_FIND_LINEONLY, NORMAL_MESSAGE, "%W", &CurrentLine);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
// DbgPrint("\n");
|
|||
|
return(FoundCount);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
FIND::SearchFiles(
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Does the search on the files specified on the command line.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
// STR nameBuf[ MAX_PATH ];
|
|||
|
PARRAY PathArray;
|
|||
|
PARRAY_ITERATOR PIterator;
|
|||
|
PPATH CurrentPath;
|
|||
|
PFSN_FILE CurrentFSNode = NULL;
|
|||
|
PFSN_DIRECTORY CurrentFSDir;
|
|||
|
PFILE_STREAM CurrentFile = NULL;
|
|||
|
WSTRING CurrentPathString;
|
|||
|
ULONG LinesFound;
|
|||
|
|
|||
|
|
|||
|
if( !_PathArguments.IsValueSet() ) {
|
|||
|
// DbgPrint( "no paths specified\n" );
|
|||
|
} else {
|
|||
|
// DbgPrint( "paths specified\n" );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// - if 0 paths on cmdline then open stdin
|
|||
|
// - if more than one path set OutputName flag
|
|||
|
//
|
|||
|
if( (_PathArguments.QueryPathCount() == 0) ) {
|
|||
|
// DbgPrint("PathCount == 0 so searching stdin\n");
|
|||
|
// use stdin
|
|||
|
LinesFound = SearchStream( Get_Standard_Input_Stream() );
|
|||
|
if( !_OutputLines ) {
|
|||
|
DisplayMessage(MSG_FIND_COUNT, NORMAL_MESSAGE, "%d", LinesFound);
|
|||
|
}
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
PathArray = _PathArguments.GetPathArray();
|
|||
|
PIterator = (PARRAY_ITERATOR)PathArray->QueryIterator();
|
|||
|
|
|||
|
//
|
|||
|
// - for each path specified on the command line
|
|||
|
// - open a stream for the path
|
|||
|
// - print filename if supposed to
|
|||
|
// - call SearchStream
|
|||
|
//
|
|||
|
while( (CurrentPath = (PPATH)PIterator->GetNext()) != NULL ) {
|
|||
|
CurrentPathString.Initialize( CurrentPath->GetPathString() );
|
|||
|
CurrentPathString.Strupr();
|
|||
|
|
|||
|
// ->QuerySTR( 0, TO_END, nameBuf, sizeof(nameBuf));
|
|||
|
// DbgAssert(sprintf(TmpBuf, "path is <%s>\n", nameBuf) > 0);
|
|||
|
// DbgPrint(TmpBuf);
|
|||
|
|
|||
|
// if the system object can return a FSN_DIRECTORY for this
|
|||
|
// path then the user is trying to 'find' on a dir so print
|
|||
|
// access denied and skip this file
|
|||
|
|
|||
|
if( CurrentFSDir = SYSTEM::QueryDirectory(CurrentPath) ) {
|
|||
|
|
|||
|
if (CurrentPath->IsDrive()) {
|
|||
|
|
|||
|
DisplayMessage(MSG_FIND_FILE_NOT_FOUND, ERROR_MESSAGE, "%W", &CurrentPathString);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
DisplayMessage( MSG_ACCESS_DENIED, ERROR_MESSAGE, "%W", &CurrentPathString);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
DELETE( CurrentFSDir );
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if( !(CurrentFSNode = SYSTEM::QueryFile(CurrentPath)) ||
|
|||
|
!(CurrentFile = CurrentFSNode->QueryStream(READ_ACCESS)) ) {
|
|||
|
|
|||
|
//
|
|||
|
// If the file name is "", DOS5 prints an invalid parameter
|
|||
|
// format message. There is no clean way to filter this
|
|||
|
// kind of stuff in the ULIB library, so we will have to
|
|||
|
// parse the path ourselves.
|
|||
|
//
|
|||
|
if ( IsDos5CompatibleFileName( CurrentPath ) ) {
|
|||
|
DisplayMessage(MSG_FIND_FILE_NOT_FOUND, ERROR_MESSAGE, "%W", &CurrentPathString);
|
|||
|
} else {
|
|||
|
DisplayMessage(MSG_FIND_INVALID_FORMAT, ERROR_MESSAGE );
|
|||
|
break;
|
|||
|
}
|
|||
|
DELETE( CurrentFile );
|
|||
|
DELETE( CurrentFSNode );
|
|||
|
CurrentFile = NULL;
|
|||
|
CurrentFSNode = NULL;
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if( _OutputLines ) {
|
|||
|
DisplayMessage( MSG_FIND_BANNER, NORMAL_MESSAGE, "%W", &CurrentPathString);
|
|||
|
}
|
|||
|
|
|||
|
LinesFound = SearchStream( CurrentFile );
|
|||
|
|
|||
|
if( !_OutputLines ) {
|
|||
|
DisplayMessage(MSG_FIND_COUNT_BANNER, NORMAL_MESSAGE, "%W%d", &CurrentPathString, LinesFound);
|
|||
|
}
|
|||
|
|
|||
|
DELETE( CurrentFSNode );
|
|||
|
DELETE( CurrentFile );
|
|||
|
CurrentFSNode = NULL;
|
|||
|
CurrentFile = NULL;
|
|||
|
}
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
FIND::Terminate(
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Deletes objects created during initialization.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
exit(_ErrorLevel);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
main()
|
|||
|
|
|||
|
{
|
|||
|
DEFINE_CLASS_DESCRIPTOR( FIND );
|
|||
|
|
|||
|
{
|
|||
|
FIND Find;
|
|||
|
|
|||
|
perrstk = NEW ERRSTACK;
|
|||
|
|
|||
|
if( Find.Initialize() ) {
|
|||
|
// DbgPrint("done init\n" );
|
|||
|
Find.SearchFiles();
|
|||
|
}
|
|||
|
Find.Terminate();
|
|||
|
}
|
|||
|
}
|