/************************************************************************
*
* Purpose:
* Author: M J Leslie
* Date: 26-Oct-98
*
************************************************************************/
/* Known problems.
Serious memory leaks
not enough comments
Has problems with vi swp files on Linux (so does normal grep).
*/
extern "C"
{
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
}
#include <iostream.h>
// DEBUG 1 == Debugging information is required.
// DEBUG 0 == Debugging info is suppresed.
#define DEBUG 0
class List
{
public:
List()
{
Reset();
}
~List()
{
}
void Add(int Number)
{
Add(Number, ':');
}
void Add(int Number, char Prefix)
{
// Are we adding the first item in the list?
if ( pFirstItem == 0 )
{
// Yes.
pFirstItem = new IntList;
pCurrentItem = pFirstItem;
}
else
{
// No. Add the number to the end of the list.
pCurrentItem->pNext = new IntList;
pCurrentItem = pCurrentItem->pNext;
}
pCurrentItem->Item = Number;
pCurrentItem->Prefix = Prefix;
pCurrentItem->pNext = 0;
}
void PointToFirstEntry(void)
{
pCurrentItem = pFirstItem;
}
void Reset(void)
{
// This is going to cause seriuos memory leaks!
pFirstItem = 0;
pCurrentItem = 0;
}
int GetCurrentEntry(void)
{
// Check we have a valid Item.
if (pCurrentItem)
{
return (pCurrentItem->Item);
}
else
{
// No Item. This could happen when the list is empty.
return (0);
}
}
int GetNextEntry(void)
{
// Is there another entry in the list?
if ( pCurrentItem->pNext != 0 )
{
// Yes. Give it to the caller.
pCurrentItem = pCurrentItem->pNext;
return (pCurrentItem->Item);
}
else
{
// No.
return (0);
}
}
char GetCurrentPrefix(void)
{
// pCurrentItem = pCurrentItem->pNext;
return (pCurrentItem->Prefix);
}
char GetNextPrefix(void)
{
pCurrentItem = pCurrentItem->pNext;
return (pCurrentItem->Prefix);
}
private:
struct IntList
{
int Item;
char Prefix;
IntList *pNext;
};
IntList *pFirstItem;
IntList *pCurrentItem;
};
class grep
{
public:
// enum { LL = 512 } LineLength ;
// .......................................................................
grep() // Constructor.
{
OFF = 0;
ON = 1;
LineNumbering = ON;
LinesBefore = 2;
LinesAfter = 2;
LinesInFile = 0;
pFileName = 0;
SearchString = '\0';
InsensitiveSearch = OFF;
}
// .......................................................................
~grep () // Distructor.
{
delete[] pFileName;
}
// .......................................................................
void BuildMatrix(void)
{
int Line = 0;
int StartPos = 0;
int EndPos = 0;
int NextPos = 0;
Debug("BuildMatrix");
FoundLines.PointToFirstEntry();
Line = FoundLines.GetCurrentEntry();
Debug(Line);
// Loop until we run out of line numbers.
while ( Line > 0 )
{
// Find the first line to display.
StartPos = Line - LinesBefore;
if ( StartPos <= EndPos )
{
StartPos = EndPos + 1;
}
while ( StartPos < Line )
{
LineMatrix.Add(StartPos, ':');
Debug(StartPos, 's');
StartPos++;
}
LineMatrix.Add(Line, '*');
Debug(Line, '*');
EndPos = Line + LinesAfter;
if ( EndPos > LinesInFile )
{
EndPos = LinesInFile;
}
NextPos = FoundLines.GetNextEntry();
if ( NextPos > 0 && EndPos >= NextPos )
{
EndPos = NextPos - 1;
}
Line++;
while ( Line <= EndPos )
{
LineMatrix.Add(Line, ':');
Debug(Line, 'e');
Line++;
}
Line = NextPos;
Debug(Line);
}
}
// .......................................................................
void CaseInsensitive(void)
{
InsensitiveSearch = ON;
}
// .......................................................................
void Diagnostic(void)
{
cout << endl << "Extra lines before the found line: " << LinesBefore << endl;
if ( InsensitiveSearch )
{
cout << "Search is: Case Insensitive." << endl;
}
else
{
cout << "Search is: Case Sensitive." << endl;
}
}
// .......................................................................
int Exists(void)
{
// Have we got the file name?
if ( pFileName == 0 )
{
// No.
return (0);
}
else
{
// Yes. Check the file exists.
struct stat stat_p; /* 'stat_p' is a pointer to a structure
* of type 'stat'. */
/* Get stats for file and place them in
* the structure. */
if ( -1 == stat (pFileName, &stat_p) )
{
// File name not found.
return (0);
}
if(!S_ISREG(stat_p.st_mode))
{
// This is not a regular file. It may be a directory.
// Ignore it.
return(0);
}
// File name is OK.
return (1);
}
}
// .......................................................................
void Name(char *pFN)
{
Debug("Name", pFN);
pFileName = new char[strlen(pFN)+1];
strcpy(pFileName, pFN);
}
// .......................................................................
void NoLineNumbers(void)
{
LineNumbering = OFF;
}
// .......................................................................
void ProcessFile(void)
{
int Line;
int PreviousLine= 0;
char Prefix;
int CurrentLine = 0;
char Buffer[512];
int LineCount = 0;
FILE *fp;
Debug("ProcessFile");
cout << endl << "Search String is: " << SearchString << endl;
cout << "File Name is: " << pFileName << endl;
fp = fopen (pFileName, "r");
LineMatrix.PointToFirstEntry();
Line = LineMatrix.GetCurrentEntry();
Prefix = LineMatrix.GetCurrentPrefix();
while ( Line > 0 )
{
do
{
fgets(Buffer, 512, fp);
LineCount ++;
} while ( LineCount < Line );
// Put in a seperator between blocks.
if ( PreviousLine+1 < Line )
{
Seperator();
}
if ( LineNumbering )
{
cout << Line << Prefix << " " << Buffer;
}
else
{
cout << Prefix << " " << Buffer;
}
PreviousLine = Line;
Line = LineMatrix.GetNextEntry();
Prefix = LineMatrix.GetCurrentPrefix();
}
Seperator();
fclose(fp);
}
// .......................................................................
void PutSearchString(char *String)
{
Debug("PutSearchString");
Debug(String);
SearchString = new char[strlen(String)+1];
strcpy(SearchString, String);
if ( InsensitiveSearch )
{
// Yes. Convert the buffer to uppercase.
Uppercase(SearchString);
}
}
// .......................................................................
void Reset(void)
{
FoundLines.Reset();
LineMatrix.Reset();
}
// .......................................................................
int ScanFile(void)
{
FILE *fp;
char Buffer[512];
int LineCounter = 0;
int Found = 0; // Number of lines that matched the search string.
fp = fopen(pFileName, "r");
Debug ("ScanFile");
while ( !feof(fp) )
{
LineCounter++;
fgets(Buffer, 512, fp);
// Check the Buffer was big enough.
if (strlen(Buffer) == 512 -1 )
{
cout << __FILE__
<< ": Warning! Lines in "
<< pFileName
<< " exceed the max line length of " << 512 << endl;
}
// Are we doing a case insensitive search?
if ( InsensitiveSearch )
{
// Yes. Convert the buffer to uppercase.
Uppercase(Buffer);
}
if ( strstr(Buffer, SearchString) )
{
FoundLines.Add(LineCounter);
Found++;
}
}
fclose (fp);
LinesInFile = LineCounter;
return(Found);
}
// .......................................................................
void SetLines(int Lines)
{
SetLinesBefore(Lines);
SetLinesAfter(Lines);
}
// .......................................................................
void SetLinesBefore(int Lines)
{
LinesBefore = Lines;
}
// .......................................................................
void SetLinesAfter(int Lines)
{
LinesAfter = Lines;
}
private:
// Debugging methods.
void Debug(char *Message)
{
if ( DEBUG )
{
cout << "Debug: " << Message << endl;
}
}
// .......................................................................
void Debug(int Value)
{
if ( DEBUG )
{
cout << "Debug: " << Value << endl;
}
}
// .......................................................................
void Debug(int Value, char Char)
{
if ( DEBUG )
{
cout << "Debug: " << Char << " " << Value << endl;
}
}
// .......................................................................
void Debug(char * Str, int Value)
{
if ( DEBUG )
{
cout << "Debug: " << Str << " " << Value << endl;
}
}
// .......................................................................
void Debug(char * Str1, char * Str2)
{
if ( DEBUG )
{
cout << "Debug: " << Str1 << " " << Str2 << endl;
}
}
// .......................................................................
// Seperate blocks of data.
void Seperator(void)
{
cout << "------" << endl;
}
// .......................................................................
void Uppercase(char *String)
{
int Offset = 0;
while ( String[Offset] != (char)NULL )
{
String[Offset] = toupper(String[Offset]);
Offset++;
}
}
int LineNumbering; // ON = Line numbering required.
int LinesBefore;
int LinesAfter;
int LinesInFile; // Number of lines in the file.
int InsensitiveSearch; // ON = Case insensitive search required.
char *pFileName; // File to be searched.
char *SearchString;
List FoundLines; // List of lines that contain the search string.
List LineMatrix; // List of lines that will be displayed.
// Handy variables to set boolean variables.
int OFF;
int ON;
};
// Private functions.
void Debug(char *Message);
void Help(void);
void ProcessTheCommandLine(
int argc,
char **Argv);
//
// Start point in the program.
//
main(
int Argc,
char **Argv)
{
ProcessTheCommandLine(Argc, Argv);
}
// .......................................................................
void Help(void)
{
cout << endl
<< " mgrep (Martins grep or Multi line grep) is an alternative to grep. " << endl
<< " The purpose is the same as grep in that it searches files " << endl
<< " looking for a supplied string. The difference is in the output, " << endl
<< " aswell as showing the line that contains the string, it also " << endl
<< " shows the lines around the found line." << endl;
cout << endl
<< " When reading from stdin, mgrep works a little differently to grep." << endl
<< " grep will scan the data on stdin looking for the search string." << endl
<< " mgrep assumes it is being passed file names, therefore it attempts " << endl
<< " to open the files and search the contents. " << endl;
cout << endl
<< " Please note that mgrep is slower than grep, this is because it " << endl
<< " performs two passes over the files being searched. " << endl;
cout << endl
<< "Syntax:" << endl << endl;
// cout << " mgrep [ -d -h -l n -b n -a n -s] string [filenames]" << endl;
cout << " mgrep [ -d -h -l n -b n -a n ] string [filenames]" << endl;
cout << "" << endl;
cout << " -h --------- Display this help." << endl;
cout << " -d --------- Basic diagnostic information is printed." << endl;
cout << " -i --------- Case insensitive search." << endl;
cout << " -n --------- No line numbering required." << endl;
cout << " -l n ------- Number of lines to show either side of the found line." << endl;
cout << " Default is 2." << endl;
cout << " -b n ------- Number of lines to show before the found line." << endl;
cout << " Default is 2." << endl;
cout << " -a n ------- Number of lines to show after the found line." << endl;
cout << " Default is 2." << endl;
cout << " -s --------- Look for the list of files on STDIN." << endl;
cout << " string ----- The string to search for." << endl;
cout << " filenames -- List of files to search." << endl << endl;
}
// .......................................................................
void Debug(char *Message)
{
if ( DEBUG )
{
cout << "Debug: " << Message << endl;
}
}
//
// Read the command line and act on its contents.
//
void ProcessTheCommandLine(
int Argc,
char **Argv)
{
grep File;
int Flag = 'x';
int LookAtStdin = 0;
int FirstFile = 1;
int DiagnosticRequired = 0;
Debug("ProcessTheCommandLine");
// Process the command line.
for ( int Inc = 1; Inc < Argc; Inc++ )
{
// Have we got a flag?
if ( *(Argv[Inc]) == '-' )
{
// Yes.
Flag = *((Argv[Inc])+1);
switch ( Flag )
{
case 'd': // Diagnostics requested
DiagnosticRequired = 1;
break;
case 'h': // Help requested
Help();
exit(0);
break;
case 'i': // Case insensitive search
File.CaseInsensitive();
break;
case 'l': // Number of lines to show either side of the found line.
Inc++;
File.SetLines(atoi(Argv[Inc]));
break;
case 'n':
File.NoLineNumbers();
break;
case 'b':
Inc++;
File.SetLinesBefore(atoi(Argv[Inc]));
break;
case 'a':
Inc++;
File.SetLinesAfter(atoi(Argv[Inc]));
break;
case 's':
LookAtStdin = 1;
default:
cout << Argv[Inc] << " is an invalid flag" << endl;
}
}
else
{
// This is not a flag, it has to be
// the search string or a file name.
if ( FirstFile )
{
// Assume the first word following
// the flags is the search string.
File.PutSearchString(Argv[Inc]);
if ( DiagnosticRequired )
{
File.Diagnostic();
}
FirstFile = 0;
}
else
{
File.Name(Argv[Inc]);
// ... If the file exists,
// ... find and display the lines that contain
// ... the search string.
if ( !File.Exists() )
{
// cout << "File " << Argv[Inc] << " not found. " << endl;
}
else
{
int Found = 0;
Debug("File Found");
Found = File.ScanFile();
// Only proceed with this file if we found matching records.
if (Found > 0)
{
File.BuildMatrix();
File.ProcessFile();
}
File.Reset();
}
}
}
}
// Should we scan stdin for file names?
// This is a bodge because I cant figure out how to do a non blocking
// read on sdtin.
if (LookAtStdin)
{
// Yes.
char FileName[256];
// check we have a search string before looking in STDIN for filenames.
if (FirstFile)
{
cout << __FILE__ << ": Search string not Supplied. " << endl;
Help();
return;
}
// Disable buffering on stdin. This mean that stdin can be
// read without special cmd line flags and fgets does not
// lock up the program.
char Buffer[BUFSIZ];
// setvbuf(stdin, Buffer);
// Now check STDIN for file names.
while (fgets(FileName, 256, stdin))
{
FileName[strlen(FileName)-1] = '\0';
// cout << "stdin loop" << FileName << endl;
File.Name(FileName);
// ... If the file exists,
// ... find and display the lines that contain
// ... the search string.
if ( !File.Exists() )
{
// cout << "File " << FileName << " not found. " << endl;
}
else
{
int Found = 0;
Debug("File Found");
Found = File.ScanFile();
// Only proceed with this file if we found matching records.
if (Found > 0)
{
File.BuildMatrix();
File.ProcessFile();
}
File.Reset();
}
}
}
}
syntax highlighted by Code2HTML, v. 0.9.1