//GREP.CPP
//COPYRIGHT EMILE J. KEITH 2014//
#include <iostream>
#include <regex>
#include <cstdlib>
#include <exception>
#include <fstream>
#include <iomanip>
#include <boost/filesystem.hpp>//filesystem header file from boost library, cross platform compatible
using namespace std;
using namespace boost::filesystem;
int main(int argc, char *argv[])
{
if(argc<3)
{
cout<<"Not enough Arguments provided\n";
}
else
{
regex rootExpression("([A-Za-z]:[\\\\/]){1,1}");//This expression states the first char in the word must be any capital letter followed by a : and then a back or forward slash
regex expressionToMatch;
string concatinatedPath, extension, fileToSearch, fileName, fileExtension, currentLine;
bool iFlag=false, vFlag=false, lFlag=false, nFlag=false, cFlag=false, iterator=false, fullPathProvided=false, match=false;
/*iFlag is ignore case
vFlag prints non matching
lFlag prints just file names instead of actual matching string
nFlag prints line number in input file
cFlag just print number of matches and not the matches*/
unsigned short lineNumber, numberOfMatches=0;
unsigned long currentPosition, endOfFile;
path searchPath;//a class from the boost filesystem header
if(argc>=4 && argv[1][0]=='-' && argv[1][1]=='i')
iFlag=true;
else if(argc>=4 && argv[1][0]=='-' && argv[1][1]=='v')//Only one flag can be provided as an argument, therefore else if works perfectly for this situation
vFlag=true;
else if(argc>=4 && argv[1][0]=='-' && argv[1][1]=='l')
lFlag=true;
else if(argc>=4 && argv[1][0]=='-' && argv[1][1]=='n')
nFlag=true;
else if(argc>=4 && argv[1][0]=='-' && argv[1][1]=='c')
cFlag=true;
for(unsigned short count=argc-1;count>0;count--)
{
if(regex_search(string(argv[count]),rootExpression))//if the current argument matches the expression
{
fullPathProvided=true;
for(unsigned short counter=count; counter<argc; counter++)
{
concatinatedPath+=argv[counter];//concatinate it onto the string
if(concatinatedPath[concatinatedPath.length()-1]!='\\' && counter!=(argc-1))//And as long as the last char of the argument is not a \ and it is not the last argument
{
concatinatedPath+=" ";//add a space
}
}
break;//break the for loop
}
}
if(!fullPathProvided)//If a full search path was not provided, identified by the previous regex having no matches
concatinatedPath=argv[argc-1];//Use the last provided argument as the concatinated path
if((concatinatedPath.find_last_of('\\')+1==concatinatedPath.find_last_of('*'))||(concatinatedPath.find_last_of('/')+1==concatinatedPath.find_last_of('*')) || ((!fullPathProvided) && concatinatedPath.at(0)=='*' && concatinatedPath.at(1)=='.'))
{//if the char in the path after the last \ or / is *
iterator=true;
extension=concatinatedPath.substr(concatinatedPath.find_last_of('.'),string::npos);//take a substring begining from the last . and assign it to the extension
}
else
{
fileToSearch=concatinatedPath.substr(concatinatedPath.find_last_of('\\')+1,string::npos);//else everything after the last \ or / specifies a specific file to search
}
if(iFlag)//if the -i flag is active
expressionToMatch.assign(argv[2],regex::icase);//Assign the third argument to the regex object expressionToMatch and make it case insensitive
else
expressionToMatch.assign(argv[1]);//else assign the second argument to expressionToMatch
if(fullPathProvided)
searchPath.append(concatinatedPath);
else
searchPath.append(current_path().string()+"\\"+concatinatedPath);
if(exists(searchPath) || (iterator && exists(searchPath.parent_path())))//If the path exists, or iteretor is true and the root path for the searPath variable exists
{
if(iterator)//if iteratin
{
directory_iterator end_iter;//directory_iterator class, when initialized without a path, it is default set to indicate the end of a directory flag
if(searchPath.parent_path()==current_path())//If the searPath root is the same as current directoyr
{
for(directory_iterator dir_iter(searchPath.parent_path()); dir_iter!=end_iter; ++dir_iter)//this time the directory_iterator class instance was intitialized with a path vairiable, and is set to the beginning of the directory(LAYMAN'S TERMS)
{
path newPath=dir_iter->path();//new path currently holds the value of the path for the item dir_iter is currently positioned on
fileName=newPath.filename().string();//extracts the filename from the path, casts to a string then returns it
fileExtension=newPath.extension().string();
if(is_regular_file(newPath))
{
if(fileExtension==extension)
{
ifstream file((fileName+fileExtension));
try
{
if(file.bad())
throw runtime_error("could not open "+fileName+fileExtension+" for reading!\n");
else
{
file.seekg(0, ios::end);
endOfFile=file.tellg();//find out the position of the last charachter in file
file.seekg(0, ios::beg);//set reading pointer back to start of file
}
}
catch(runtime_error &ex)
{
cerr<<ex.what()<<endl;
}
lineNumber=1;
while(getline(file, currentLine))
{ //This is where the error occurs
system("PAUSE");
//cout<<"I am reading line "<<lineNumber<<" which is "<<currentLine;
currentPosition=file.tellg();
if(currentPosition==endOfFile)
{
if(cFlag)
cout<<numberOfMatches;
numberOfMatches=0;
lineNumber=1;
cout<<endl;
file.close();
break;
}
else
{
cout<<"almost at match\n";
if(regex_search(currentLine,expressionToMatch) || (!regex_search(currentLine, expressionToMatch) && vFlag))//The last condition says f there is no match and vFlag is true, execute this block of code. Since vFlag is print non-matching lines
{
cout<<"match\n";
numberOfMatches++;
if(!match && lFlag)//if no match has been made for this file, and lFlag active, which is to print file name
{
cout<<fileName<<endl;
numberOfMatches=0;
lineNumber=1;
break;//We know already that the file contains a match so, we need to read no further so break
}
else
{
if(nFlag)//If the nFlag is true which means to print line number
cout<<lineNumber<<". ";
cout<<currentLine<<'\n';
lineNumber++;
}
}
}
}
}
}
}
}
}
}
else
cout<<"That specified file could not be found"<<endl;
}
return 0;
}