/*
Grep Dir v0.2
Descrizione: Grep dir cerca una o due stringhe (in tal caso effettuando una ricerca incrociata)
	     nella directory scelta e/o in tutte le sue sub-directory.
             Con il parametro -s infatti è possibile effettuare la ricerca solo ina una determinata dir

Autore:     __GiReX__
Linguaggio: C++
Sito:	    http://girex.altervista.org/

Compile with: $ g++ grep_dir2.cpp -o grep-dir
Or under Windows with Dev-Cpp

Thanks to: Style HdS619 for beta-testing and moral support! 

30/12/07 02.14
*/

#define log_file "grep_dir_log.txt"         // Modificare a proprio piacimento     

#ifdef WIN32				    // Direttive del pre-processore per cambiare lo slash 
#define slash "\\"			    // delle dir in base al SO
#else
#define slash "/"
#endif

#include <iostream>
#include <fstream>
#include <dirent.h>
using namespace std;

bool single_dir = false;

int first_change(char *dir);
int check_dir(char *d, char *match, char *match1);
int check_file(char *file, char *dir, char *match, char *match1);
void display(char *buffer, char *file, char *dir, int riga);
void usage(char *prog);

int main(int argc, char *argv[])
{
 int error = 1;   

   if(argc == 3)  {								// Controllo dei parametri
        
         if(!first_change(argv[1])) error = check_dir(argv[1], argv[2], NULL);
         
 } else
 
   if(argc == 4 && strcmp(argv[1], "-s"))  {
        
         if(!first_change(argv[1])) error = check_dir(argv[1], argv[2], argv[3]);
         
 } else
 
   if(argc == 4 && !strcmp(argv[1], "-s"))  {
        
         if(!first_change(argv[2])) 
    {            
            single_dir = true;
            error = check_dir(argv[2], argv[3], NULL);
    }
 
 } else
 
   if(argc == 5 && !strcmp(argv[1], "-s"))  {

         if(!first_change(argv[2])) 
    {       
            single_dir = true;
            error = check_dir(argv[2], argv[3], argv[4]);
    }
    
 } else {
       
       usage(argv[0]);
       return -1;
 }
   
   if(!error) cout << "File " << log_file << " creato con successo!\n";
  else cout << "Sono stati riscontrati degli errori durante l'esecuzione del programma!\n";

 return 0;
}
   
int first_change(char *dir)
{
    if(chdir(dir) != 0)  							   // Cambio directory di lavoro
    {								                   // in quella passata come argomento
        cout << endl << "Impossibile cambiare la direcory: " << dir << endl;
        return -1; 
    }    

  return 0;
}  
    
int check_dir(char *d, char *match, char *match1)				  // Funzione ricorsiva
{										  // essenziale per trovare le sub-dir
 DIR *dir;
 dirent *dinfo;  
  char current[FILENAME_MAX];
    
    getcwd(current, FILENAME_MAX);						  // Ottengo la directory corrente
    
    if((dir = opendir(current)) == 0)  {				          // Apro la directory
        cout << "Impossibile aprire la directory: " << d << endl;
    return -1; }

  
     while(dinfo = readdir(dir))  						  // Scorro la directory
   {
      if(!strcmp(dinfo->d_name, ".") || !strcmp(dinfo->d_name, "..") || 
	 !strcmp(dinfo->d_name, "grep_dir_log.txt")) continue;
        
        if(chdir(dinfo->d_name) != 0) check_file(dinfo->d_name, d, match, match1); // Se non è 1 dir, controllo il file
         else { 
          if(single_dir == true) { chdir(".."); continue; }			  // Parametro -s, non cambio dir
          
          strcpy(current, d);
          strcat(current, "/");
          strcat(current, dinfo->d_name);					  // Ottengo una nuova dir 
          
          check_dir(current, match, match1);               		          // La funzione richiama se stessa
										  // con dir differente
           if(chdir("..") != 0) {
                cout << "Impossibile spostarsi nella cartella superiore\n";	  // Torno nella dir superiore
         return -2;  } 
       }
     }
   closedir(dir);
 return 0;
}


int check_file(char *file, char *dir, char *match, char *match1)
{
  bool single_match = true;
   ifstream current(file, ios::in);
   char buffer[1024];

    if(!current.is_open()) {
        cout << "Errore nell'apertura del file: " << file << endl;
    return -1;  }   
   
     if(match1 == NULL) single_match = true;
     else single_match = false;
   
      for(int i = 1; current.good(); i++) 
      {
          current.getline(buffer, sizeof(buffer));				 // Scorro tutte le righe per trovare il 
              if(single_match == true && strstr(buffer, match) || 	         // match
                 single_match == false && strstr(buffer, match) && strstr(buffer, match1))
                     display(buffer, file, dir, i);				 // Trovato match, loggo file dir e riga
 
                 memset(buffer, 0, sizeof(buffer));				 // Pulisco buffer
           } 
        
      current.clear(); 
   current.close();
 return 0;
}
    


void display(char *buffer, char *file, char *dir, int riga)
{
  ofstream log(log_file, ios::out | ios::app);
    
    log  << "---------------------------------------------------------------\n"
         << "Nome file:\t"      << file     << endl
         << "Sub directory:\t"  << dir      << endl
         << "Riga numero:\t"    << riga     << endl
         << endl                << buffer   << endl 
         << "---------------------------------------------------------------\n\n";
                                
  log.close();
}

void usage(char *prog)
{
   cout << endl 
	<< "Grep Dir v0.2 by __GiReX__ search string(s) il all sub-directory\n"
        << "and display file, line and sub-dir where it match the string(s)\n\n"
        << "Usage: " << prog << " <directory> <string> [string2] \n"
        << "Usage: " << prog << " -s <directory> <string> [string2]\n\n"
	<< "Example: " << prog << " ~/source \"#include <stdio.h>\"\n"
        << "Example: " << prog << " -s  ~/php      WHERE   $id\n\n"
        << "-s : il parametro -s specifica che si intende lavorarare solo\n"
        << "nella directory specificata e non in tutte le sue sub-directory\n\n"
        << "[string2] : specificare la seconda stringa per effettuare una\n"
        << "ricerca incrociata e' opzionale\n\n"
        << "Se tra virgolette \"\" si possono anche passare come argomento intere frasi\n"
        << endl;
}

