/*
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: $ gcc grep_dir2.c -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 "\\"			    
#else
#define slash "/"
#endif

#include <stdio.h>
#include <dirent.h>

typedef enum {false, true} bool;
bool single_dir = false;

int first_change(char *dir);
int check_dir(char *d, char *match, char *match1);
void getline(FILE *fd, char *buffer, int size);
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) printf("File %s creato con successo!\n", log_file);
  else printf("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 */
        printf("\nImpossibile cambiare la direcory: %s \n", dir);
        return -1; 
    }    

  return 0;
}  
    
int check_dir(char *d, char *match, char *match1)   /* Funzione ricorsiva, scorre tutte le sub-dir */
{										 
 DIR *dir;
 struct dirent *dinfo;  
 char current[FILENAME_MAX];
    
  getcwd(current, FILENAME_MAX);		/* Ottengo la directory corrente */
    
    if((dir = opendir(current)) == 0)  
    {
        printf("Impossibile aprire la directory: %s \n", dir);
        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); 
                                                           /* Non è una dir, scorro 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, camb. parametri */
										  
           if(chdir("..") != 0) 
           {		      /* Torno nella dir superiore */
                printf("Impossibile spostarsi nella cartella superiore\n");  
                return -2;  
           } 
        }
      }
   closedir(dir);
 return 0;
}

void getline(FILE *fd, char *buffer, int size)
{
 int i;

  memset(buffer, 0, strlen(buffer));
  for(i = 0; !feof(fd) && (buffer[i] = getc(fd)) != '\n' && i <= size; i++) { } 

/* Memorizzo riga su buffer */
}

int check_file(char *file, char *dir, char *match, char *match1)
{
 int i;
 bool single_match = true;
 char buffer[1024] = {0};
 
 FILE *current = fopen(file, "r");

    if(current == NULL)
    {
        printf("Errore nell'apertura del file: %s \n", file);
        return -1;  
    }   
   
     if(match1 == NULL) single_match = true;
     else single_match = false;
   
      for(i = 1; !feof(current); i++) 
      {
          getline(current, buffer, sizeof(buffer));	 /* Scorro tutte le righe per trovare il match */
              if(single_match == true && strstr(buffer, 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 */
           } 
        
      fflush(current);
   fclose(current);
 return 0;
}
    


void display(char *buffer, char *file, char *dir, int riga)
{
  FILE *log = fopen(log_file, "a");
    
   fprintf( log, 
            "---------------------------------------------------------------\n"
            "Nome file:\t %s \n"
            "Sub directory:\t %s \n"
            "Riga numero:\t %d \n"
            "\n%s \n"
            "---------------------------------------------------------------\n"
            "\n", file, dir, riga, buffer);
                                
  fclose(log);
}

void usage(char *prog)
{
  printf( "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: %s <directory> <string> [string2] \n"
          "Usage: %s -s <directory> <string> [string2]\n\n"
	  "Example: %s ~/source \"#include <stdio.h>\"\n"
          "Example: %s -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"
          "\n", prog, prog, prog, prog);
}
