//
//  sttyl.c 
//
//      AUTHOR: Russell Lowke
//   CSCIE-215: Assignment Three
//
// **** NEED TO FIX COMMENTS
//
//       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                "sttyl.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");
    */

    displaySettings();
     
    return (0);
}


void displaySettings()
{
    struct termios info;
    
    // load terminal attributes into term
    if (tcgetattr(0, &info) < 0)
        oops("tcgetattr error");
    
    // info.c_cc[VMIN] = 1         // get 1 char at a time
    // info.c_lflag &= ~ECHO;  // echo OFF
    // info.c_lflag |= ECHO;  // echo ON
    //  tcsetattr(0, TCSANOW, &info);

    if ( info.c_lflag & ECHO)
        printf(" echo is on, since its bit is 1 \n");
    else
        printf(" echo if OFF, since its bit is 0 \n");

    printf("echo is:%d \n",info.c_lflag & ECHO);
    

    showSomeFlags( &info );

    // size = info.c_cflag & CSIZE;
    // printf("size %d; \n", size);
    printf("_____\n");

    // output
    showbaud( cfgetospeed( &info ));  // get and show baud rate
    printf("evenp ");
    printf("hupcl ");
    printf("cread ");
    printf("\n");
    
    printf("intr = ");
    printASCII(info.c_cc[VINTR]);

    printf("erase = ");
    printASCII(info.c_cc[VERASE]);  
    
    printf("kill = ");
    printASCII(info.c_cc[VKILL]);    

    printf("start = ");
    printASCII(info.c_cc[VSTART]);  

    printf("stop = ");
    printASCII(info.c_cc[VSTOP]);  
    printf("\n");
    
    printf("brkint -inpck icrni -ixany onicr tabs");
    printf("iexten echo -echoe -echok");
    printf("\n");
 
}


//
//  Given an ASCII code print the readible symbol
//  can't I just use  atoi(myAscii)l; 
//
void printASCII(int myAscii)
{
    int asciiConv;

    // if a Ctrl char then
    asciiConv = myAscii -1 + 'A';
    
    // special case for 0 and 127  ^?
    
    printf("^%c; ", asciiConv);

}


//
// convert a string (i.e. "^C" or "C" or "c") to ASCII value
//
int strToAscii(char *myString)
{
    int myAscii = 0;
    
    return myAscii;
}


//
//      prints the speed in english
//
void showbaud( int thespeed )
{
        printf("speed ");
        switch ( thespeed ){
                case B300:      printf("300");        break;
                case B600:      printf("600");        break;
                case B1200:     printf("1200");       break;
                case B1800:     printf("1800");       break;
                case B2400:     printf("2400");       break;
                case B4800:     printf("4800");       break;
                case B9600:     printf("9600");       break;
                default:        printf("Fast");       break;
        }
        printf(" baud; ");
}

struct flaginfo input_flags[] = {
                     {IGNBRK, "Ignore the break condition"      },
                     {BRKINT, "Signal interrupt on break"       },
                     {IGNPAR, "Ignore chars with parity errors" },
                     {PARMRK, "Mark parity errors"              },
                     {INPCK , "Enable input parity check"       },
                     {ISTRIP, "Strip character"                 },
                     {INLCR , "Map NL to CR on input"           },
                     {IGNCR , "Ignore CR"                       },
                     {ICRNL , "Map CR to NL on input"           },
                     {IXON  , "Enable start/stop output control"},
                     {IXOFF , "Enable start/stop input control" },
                     {0     ,  NULL }};

struct flaginfo local_flags[] = {
                     {ISIG  , "Enable signals"                  },
                     {ICANON, "Canonical input (erase and kill)"},
                     {ECHO  , "Enable echo"                     },
                     {ECHOE , "Echo ERASE as BS-SPACE-BS"       },
                     {ECHOK , "Echo KILL by starting new line"  },
                     {0     , NULL }};
                    


/*


//
// Input flags - software input processing
//
#define IGNBRK          0x00000001      // ignore BREAK condition
#define BRKINT          0x00000002      // map BREAK to SIGINTR
#define IGNPAR          0x00000004      // ignore (discard) parity errors
#define PARMRK          0x00000008      // mark parity and framing errors
#define INPCK           0x00000010      // disable checking of parity errors
#define ISTRIP          0x00000020      // strip 8th bit off chars
#define INLCR           0x00000040      // map NL into CR
#define IGNCR           0x00000080      // ignore CR
#define ICRNL           0x00000100      // map CR to NL (ala CRMOD)
#define IXON            0x00000200      // enable output flow control
#define IXOFF           0x00000400      // enable input flow control
#define IXANY           0x00000800      // any char will restart after stop
#define IUCLC           0x00001000      // DUMMY VALUE Map upper to lower
                                        // case on input
#define IFLOW           IXON            // enable output flow control
#define ITANDEM         IXOFF           // enable input flow control
#define IMAXBEL         0x00002000      // ring bell on input queue full

//
// Output flags - software output processing
//
#define OPOST           0x00000001      // enable following output processing
#define ONLCR           0x00000002      // map NL to CR-NL (ala CRMOD)
#define OLCUC           0x00000004      // Map lower case to upper on output
#define OCRNL           0x00000008      // Map CR to NL on output
#define ONOCR           0x00000010      // No CR output at column 0
#define ONLRET          0x00000020      // NL performs CR function
#define OFILL           0x00000040      // Use fill characters for delay
#define OFDEL           0x00000080      // fill is DEL, else NUL
#define         NLDLY           0x00000300      // \n delay
#define                 NL0     0x00000000
#define                 NL1     0x00000100      // tty 37
#define                 NL2     0x00000200      // vt05
#define                 NL3     0x00000300
#define         TABDLY          0x00000c00      // horizontal tab delay
#define                 TAB0    0x00000000
#define                 TAB1    0x00000400      // tty 37
#define                 TAB2    0x00000800
#define                 TAB3    0x00000C00      // expand tabs on output
#define         CRDLY           0x00003000      // \r delay
#define                 CR0     0x00000000
#define                 CR1     0x00001000      // tn 300
#define                 CR2     0x00002000      // tty 37
#define                 CR3     0x00003000      // concept 100
#define         FFDLY           0x00004000      // Form feed delay
#define                 FF0     0x00000000
#define                 FF1     0x00004000
#define         BSDLY           0x00008000      // \b delay
#define                 BS0     0x00000000
#define                 BS1     0x00008000
#define         VTDLY           0x00010000      // vertical tab delay
#define                 VT0     0x00000000
#define                 VT1     0x00010000      // tty 37
#define ONLCRNL         ONLCR
#define OXTABS          0x00040000      // expand tabs to spaces
#define ONOEOT          0x00080000      // discard EOT's (^D) on output)

                     
//
// Control flags - hardware control of terminal
//
#define CSIZE           0x00000300      // character size mask
#define     CS5             0x00000000      // 5 bits (pseudo)
#define     CS6             0x00000100      // 6 bits
#define     CS7             0x00000200      // 7 bits
#define     CS8             0x00000300      // 8 bits
#define CSTOPB          0x00000400      // send 2 stop bits
#define CREAD           0x00000800      // enable receiver
#define PARENB          0x00001000      // parity enable
#define PARODD          0x00002000      // odd parity, else even
#define HUPCL           0x00004000      // hang up on last close
#define CLOCAL          0x00008000      // ignore modem status lines
#define CRTSCTS         0x00010000      // RTS/CTS flow control

// 
// "Local" flags - dumping ground for other state
//
// Warning: some flags in this structure begin with
// the letter "I" and look like they belong in the
// input flag.
//
#define ECHOE           0x00000002      // visually erase chars
#define ECHOK           0x00000004      // echo NL after line kill
#define ECHO            0x00000008      // enable echoing
#define ECHONL          0x00000010      // echo NL even if ECHO is off
#define ISIG            0x00000080      // enable signals INTR, QUIT, [D]SUSP
#define ICANON          0x00000100      // canonicalize input lines
#define IEXTEN          0x00000400      // enable FLUSHO and LNEXT
#define NOFLSH          0x80000000      // don't flush after interrupt
#define TOSTOP          0x00400000      // stop background jobs from output
#define XCASE           0x00004000      // Cononical upper/lower presentation
#define ECHOKE          0x00000001      // visual erase for line kill
#define ECHOPRT         0x00000020      // visual erase mode for hardcopy
#define ECHOCTL         0x00000040      // echo control chars as ^(Char)
#define ALTWERASE       0x00000200      // use alternate WERASE algorithm
#define MDMBUF          0x00100000      // flow control output via Carrier
#define FLUSHO          0x00800000      // output being flushed (state)
#define NOHANG          0x01000000      // this should go away
#define PENDIN          0x20000000      // retype pending input (state)
#define NOKERNINFO      0x40000000      // Disable printing kernel info
*/




                     
void showSomeFlags( struct termios *ttyp )
//
//      show the values of two of the flag sets_: c_iflag and c_lflag
//      adding c_oflag and c_cflag is pretty routine - just add new
//      tables above and a bit more code below.
//
{
        showFlagSet( ttyp->c_iflag, input_flags );
        showFlagSet( ttyp->c_lflag, local_flags );
}

void showFlagSet( int thevalue, struct flaginfo thebitnames[] )
//
// check each bit pattern and display descriptive title
//
{
        int i;
        
        for ( i = 0; thebitnames[i].fl_value ; i++ ) {
                printf( "  %s is ", thebitnames[i].fl_name);
                if ( thevalue & thebitnames[i].fl_value )
                        printf("ON\n");
                else
                        printf("OFF\n");
        }
}















//////////////////////////////////////////////////
//
//  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));
}
