//
//  pfind.c 
//
//      AUTHOR: Russell Lowke
//   CSCIE-215: Assignment Two
//
//       Usage: pfind [startDir] [fileName]
//              pfind [-d startDir] [-f fileName]
//
//        Args:  startDir   // the name of the directory being searched through
//               fileName   // the name of the file being searched for
//
// Description: mimics the basic functionality of the the Unix find
//              command, searching through the directory [startDir], and all
//              directories therein for files or directories named [fileName].  
//              When found the full path of the file or directory is
//              printed using printf.
//
//      Method: main reads args, passing parameters to recursive searchdir.
//              searchdir opens directory, searching for and printing any match
//              When a directory is found searchdir calls itself using that directory
//              as the startDir.
//
//       Notes: symbolic links are not followed (see -follow in Unix find),
//              mainly as it may result in infinite loops, lstat being
//              used instead of stat.
//




// include header
#include                "pfind.h"


// the bulk of main is a "Finite state machine" for reading arguments.
// I've developed this code with reusability in mind, 
// it being structured with flexibility foremost. Modes are used
// so users may enter a variety of arguments, such as a list of
// names and a list of locations, these delimited by an option indicator '-'

int main(int ac, char **av)
{
    int    i;                      // general purpose counter
    char   mode      = 'd',        // modes are: 'd' = get start directory
                                   //            'f' = get file name
           *startDir = NULL,       // pointer to directory to search
           *fileName = NULL;       // pointer to file Name to look for
    
           
    // "Finite state machine"

    // loop through arguments passed into main via ac and av
    // Start at av[1] as av[0] is the program name, in this case 'pfind'
    for ( i = 1; i < ac; i++ ) {
        
            if (av[i][0] == '-') {
                    
                    // change mode to whatever valid option is
                    // pased after "-",
                    switch (av[i][1]) {
                        case 'd': case 'f':
                            mode = av[i][1];
                            break;
                        default:
                            fatal("Unknown option");    // BAD option
                    }
            }
            else {
                    // read argument via whatever mode
                    switch (mode) {
                        case 'd':
                            startDir = av[i];
                            // next argument should be a fileName
                            // so change mode to "f"
                            mode = 'f';
                            break;
                        case 'f':
                            fileName = av[i];
                            break;
                    }
            }
    }
        
    // ensure startDir has been passed successfully.
    if (! startDir)                         
            fatal("Directory(-d) needs to be specified");
    
    // ensure fileName has been passed successfully
    if (! fileName)                         
            fatal("File name(-f) needs to be specified");
    
    searchdir( startDir, fileName );
    
    return (0);
}




//
//  searchdir
//
//     Search directory [dirName] looking for [fileName]. Matches are
//     printed using printf.  When a directory is found searchdir calls
//     itself passing that directory as the dirName. 
//
//     args:  dirName     --  path of directory to search.
//            fileName    --  name of the file being looked for
//
void searchdir( char *dirName, char *fileName )
{
    DIR           * dir_ptr;            // the directory
    struct dirent * direntp;            // each entry
    char          * pathName;           // store pathname
    
    
    // bad directory?
    if (( dir_ptr = opendir( dirName )) == NULL ){
            fprintf(stderr,"find: ");
            perror( dirName );
    }
    
    // mimick find, '.' fileName functionality, just printing a '.' 
    else if ( strcmp(fileName, "." ) == 0 )
            printf( ".\n" );
    
    // mimick find, '..' fileName functionality, skipping 
    else if ( strcmp(fileName, "..") == 0 )
            ;
                
    else {
            // get pointer from open directory
            while (( direntp = readdir( dir_ptr )) != NULL ) {
                    
	            // get memory for pathname string
	            pathName = malloc( 1 + strlen( dirName) + 
                                           strlen( direntp->d_name));
	            if (pathName == NULL)    // out of memory
		        fatal("Out of memory");
                    
                    // get path name
                    sprintf( pathName, "%s/%s", dirName, direntp->d_name );

                    // if filename matches, print
                    if ( strcmp(direntp->d_name, fileName) == 0 )
                            printf("%s\n", pathName );

                    // ignore '.' and '..' directories
                    if ( strcmp(direntp->d_name, "." ) != 0 &&
                         strcmp(direntp->d_name, "..") != 0)
                            
                            // if a directory, recursivly drill through...
                            if ( checkIfDir ( pathName ) )
                                     searchdir( pathName, fileName );
                    
                    // cleanup malloc
                    free(pathName);
            }
            closedir(dir_ptr);
    }
}


int checkIfDir( char *pathName )
{
        struct stat info;
        
        if ( lstat(pathName, &info) == -1 )    // cannot stat
            perror( pathName );
        
        // use S_ISREG(m), check for regular file
        // use S_ISLNK(m), check for symbolic link
        return(S_ISDIR(info.st_mode));
}
