Script started on Fri Mar 15 22:30:27 2002
ws11% cat README

README for CSCI-e215

Russell J Lowke

Assignment 3: "sttyl"

______

files: sttyl.h         // header file for sttyl
       sttyl.c         // sttyl main .c code
       myLib.c         // my c lib of useful stuff
       Makefile        // makefile to compile sttyl
       Plan            // general plan for sttyl
       typescript      // typescript of sttyl running
       README          // this file

______

Notes: sttyl seems to compile and run fine...

ws11% cat Makefile

#
# comments start with a # sign
#
# each item in the make file consists of
# three things:  a product, its ingredients and how to make it
#

CC = gcc -g -Wall

sttyl: sttyl.o myLib.o
        $(CC) sttyl.o myLib.o -o sttyl

sttyl.o: sttyl.c sttyl.h
        $(CC) -c sttyl.c
        
myLib.o: myLib.c
        $(CC) -c myLib.c

run:
        ./pfind . Plan

test:
        ~lib215/hw/pfind/test.pfind



ws11% make

gcc -g -Wall -c sttyl.c
gcc -g -Wall sttyl.o myLib.o -o sttyl
ws11% ./sttyl

speed 9600 baud; 
intr = ^C; erase = ^?; kill = ^U; start = ^Q; stop = ^S; 
icrnl onlcr echo echoe -olcuc tabs icano icanon isig hupcl cread brkint -inpck iexten 
ws11% ./sttyl -tabs

ws11% ./sttyl

speed 9600 baud; 
intr = ^C; erase = ^?; kill = ^U; start = ^Q; stop = ^S; 
icrnl onlcr echo echoe -olcuc -tabs icano icanon isig hupcl cread brkint -inpck iexten 
ws11% ./sttyl tabs

ws11% ./sttyl erase ^K[K[K""^"K""

ws11% ./sttyl

speed 9600 baud; 
intr = ^C; erase = ^K; kill = ^U; start = ^Q; stop = ^S; 
icrnl onlcr echo echoe -olcuc tabs icano icanon isig hupcl cread brkint -inpck iexten 
ws11% ./sttyl erase "^?"

ws11% ./sttyl

speed 9600 baud; 
intr = ^C; erase = ^?; kill = ^U; start = ^Q; stop = ^S; 
icrnl onlcr echo echoe -olcuc tabs icano icanon isig hupcl cread brkint -inpck iexten 
ws11% cat sttyl.h

//
// pfind.h
//


///////
//
// defines
//

//
// YES, and NO for clarity in function returns
//
#define YES     1
#define NO      0

//
// Flag types for identifying items in flagTable
//
#define INPUTFLAG    1
#define OUTPUTFLAG   2
#define CONTROLFLAG  3
#define LOCALFLAG    4

//
// Error handling for fatal errors
//
#define oops(errStr)  { perror(errStr); exit(1); }


///////
//
// Includes
//
#include    <stdio.h>
#include    <sys/types.h>
#include    <sys/stat.h>
#include    <dirent.h>
#include    <stdlib.h>
#include    <string.h>
#include    <termios.h>


///////
//
// structs
//
struct flagTable {char *attribute; 
                  int   theValue; 
                  int   flagType; 
                  char *decription; };
struct keyTable  {char *attribute; 
                  int   theValue; };

///////
//
//  predefines
//
//     predefine all functions for smooth compile
//
int        flagIndex( char           *myString );
int         keyIndex( char           *myString );
void         setFlag( struct termios *info, 
                      int             myIndex,
                      int             switchIs );
int         getAscii( char           *str    );
void displaySettings( struct termios *info     );
void        showbaud( int             thespeed );
void       printFlag( struct termios *ttyp,
                      char           *flagName,
                      int             flagType,
                      int             theFlag  );
void      printASCII( int             myAscii  );

// from myLib.c
void           fatal( char           *errStr   );
ws11% cat sttyl.c

///////////
//
//  sttyl.c 
//
//      AUTHOR: Russell Lowke
//   CSCIE-215: Assignment Three
//
//
//       Usage: sttyl [-]icrnl        // map CR to NL (ala CRMOD)
//                    [-]onlcr        // map NL to CR-NL (ala CRMOD)
//                    [-]echo         // enable echoing
//                    [-]echoe        // visually erase chars
//                    [-]olcuc        // map lowercase to uppercase on output
//                    [-]tabs         // expand tabs on output
//                    [-]icano        // canonicalize input lines
//                    [-]icanon       // canonicalize input lines
//                    [-]isig         // enable signals INTR, QUIT, [D]SUSP
//                    [-]hupcl        // hang up on last close
//                    [-]cread        // enable receiver
//                    [-]brkint       // map BREAK to SIGINTR
//                    [-]inpck        // disable checking of parity errors
//                    [-]iexten       // enable FLUSHO and LNEXT
//
//                    intr  CHAR      // CHAR will send an interrupt signal
//                    erase CHAR      // CHAR will erase last character typed
//                    kill  CHAR      // CHAR will erase the current line
//                    start CHAR      // CHAR will restart output after stopping
//                    stop  CHAR      // CHAR will stop the output
//
//
// Description: Mimics the basic functionality of the Unix stty,
//              Prints or changes terminal characteristics.
//
//      Method: Reads args sent to main,
//              looks for each arg in data tables for flag args and Key args
//              gets information about arg from table
//              set flags OFF (&= ~) or  ON (|=) depending on arg
//              convert CHARs passed to ascii, reading '^' as ctrl and "^?" as del
//              set key arg to CHAR when valid
//              exit with "Unknown mode" for any invalid arg
//              if no args sent, print values of all vars in tables 
//              
//
//       Notes: sttyl only works properly in sh
//              '^' prefix means a ctrl CHAR
//              CHAR for delete is passed as "^?"
//              CHAR commands are best passed in quotes, ie  "^?"
//


// include header
#include                "sttyl.h"

//
//  tables

//
//  mFlagArgs    
//
//     struct of type flagTable
//     Holds the attribute, value, flag type and description,
//     for every argument flag accepted by sttyl
//
struct flagTable mFlagArgs[] = {
    {"icrnl" , ICRNL , INPUTFLAG  , "map CR to NL (ala CRMOD)"             },
    {"onlcr" , ONLCR , OUTPUTFLAG , "map NL to CR-NL (ala CRMOD)"          },
    {"echo"  , ECHO  , LOCALFLAG  , "enable echoing"                       },
    {"echoe" , ECHOE , LOCALFLAG  , "visually erase chars"                 },
    {"olcuc" , OLCUC , OUTPUTFLAG , "map lowercase to uppercase on output" },
    {"tabs"  , TAB3  , OUTPUTFLAG , "expand tabs on output"                },
    {"icano" , ICANON, LOCALFLAG  , "canonicalize input lines"             },
    {"icanon", ICANON, LOCALFLAG  , "canonicalize input lines"             },
    {"isig"  , ISIG  , LOCALFLAG  , "enable signals INTR, QUIT, [D]SUSP"   },
    {"hupcl" , HUPCL , CONTROLFLAG, "hang up on last close"                },
    {"cread" , CREAD , CONTROLFLAG, "enable receiver"                      },
    {"brkint", BRKINT, INPUTFLAG  , "map BREAK to SIGINTR"                 },
    {"inpck" , INPCK , INPUTFLAG  , "disable checking of parity errors"    },
    {"iexten", IEXTEN, LOCALFLAG  , "enable FLUSHO and LNEXT"              },
    { NULL   , 0     , 0          ,  NULL                                  }};


//
//  mKeyArgs
//
//     struct of type keyTable
//     Holds the attribute and value,
//     for every modifable key argument accepted by sttyl 
//
struct keyTable mKeyArgs[] = {
    {"intr"  , VINTR },
    {"erase" , VERASE},
    {"kill"  , VKILL },
    {"start" , VSTART},
    {"stop"  , VSTOP },
    { NULL   , 0     }};


//
//  main
//
//     loop through args passed into main via ac and av
//     record if arg has prefix '-'
//     check if arg is in flag table, if so set flag
//     check if arg is in key table, if so set key to next arg
//     
    
int main(int ac, char **av)
{
    struct termios info;                     // struct to hold termios info
    int    i,                                // general purpose counter
           switchIs,                         // indicates if flag is,
                                             // turning OFF (0)  or ON (1)
           item;                             // position of item in flagTable,
                                             //                  or keyTable
    char   ascii,                            // modified ascii of CHAR key
          *argName;                          // pointer to argument name
    
        
    if (tcgetattr(0, &info) < 0)             // load terminal arributes to info
        oops("Couldn't get termios");        // ...error
    

    for ( i = 1; i < ac; i++ ) {             // loop through args given to main
        
        ascii    =  0;                       // cleanup ascii and item vars
        
        switchIs = (av[i][0] == '-');        // switchIs to 0, or 1 
                                             //   depending on '-' operator
        
        argName  = &av[i][switchIs];         // pointer to argument name,
                                             //   excluding '-'
        
                                             // if argName has a flagIndex
        if ((item = flagIndex(argName)) != -1)
            setFlag(&info, item, switchIs);  // set flag [item] to [switchIs]
        
                                             // if argName has a keyIndex
                                             // and there is another argument
        else if ((item = keyIndex(argName)) != -1 
                    && i+1 < ac ) {
            
            ascii = getAscii(av[++i]);       // increment i and get ascii

            
            if (ascii)                       // if valid ascii
                info.c_cc[mKeyArgs[item].theValue] = ascii;   // set char key
            else
                fatal("Invalid argument");   // ...error
        } 
        else
            fatal("Unknown mode");           // ...error
    }
    
    tcsetattr(0, TCSANOW, &info);            // update termios

    if (ac == 1) 
        displaySettings(&info);              // if 1 arg then print
    
    return (0);
}



///////
//
//  flagIndex
//
//     return the index of attribute [str] in table mFlagArgs
//
//     args:  str          -- name of attribute being looked for
//
int flagIndex(char *str)
{
    int i;                                  // general purpose counter
    
    for ( i = 0; mFlagArgs[i].theValue ; i++ ) {        // loop through table
        if ( strcmp(mFlagArgs[i].attribute, str) == 0 )
            return (i);                                 // str found
    }

    return -1;                              // str not found
}


///////
//
//  keyIndex
//
//     return the index of attribute [str] in table mKeyArgs
//
//     args:  str          -- name of attribute being looked for
//
int keyIndex(char *str)
{
    int i;                                  // general purpose counter
    
    for ( i = 0; mKeyArgs[i].theValue ; i++ ) {        // loop through table
        if ( strcmp(mKeyArgs[i].attribute, str) == 0 )
            return (i);                                // str found
    }

    return -1;                              // str not found
}


///////
//
//  setFlag
//
//     set a flag in the [*info] struct to ON or OFF
//     two case blocks for switches,
//     flag type and arg value is read from table mFlagArgs
//
//     args: *info         --  pointer to struct being modifed.
//            item         --  index of arg in mFlagArgs table
//            switchIs     --  switch arg to OFF (0)  or ON (1)
//
void setFlag(struct termios *info, int item, int switchIs)
{
    if (switchIs) {
        
        // Switch flag OFF
        switch (mFlagArgs[item].flagType) {
            case INPUTFLAG:
                info->c_iflag &= ~mFlagArgs[item].theValue;
                break;
            case OUTPUTFLAG:
                info->c_oflag &= ~mFlagArgs[item].theValue;
                break;
            case CONTROLFLAG:
                info->c_cflag &= ~mFlagArgs[item].theValue;
                break;
            case LOCALFLAG:
                info->c_lflag &= ~mFlagArgs[item].theValue;
                break;
        }
    }
    else {
        
        // Switch flag ON
        switch (mFlagArgs[item].flagType) {
            case INPUTFLAG:
                info->c_iflag |= mFlagArgs[item].theValue;
                break;
            case OUTPUTFLAG:
                info->c_oflag |= mFlagArgs[item].theValue;
                break;
            case CONTROLFLAG:
                info->c_cflag |= mFlagArgs[item].theValue;
                break;
            case LOCALFLAG:
                info->c_lflag |= mFlagArgs[item].theValue;
                break;
            }
    }
}

    
///////
//
//  getAscii
//
//     return the char in [*str] as ascii
//     cntrl chars are identified by the prefix '^'
//     [*str] should never be more than 2 chars long
//     if [*str] is invalid or can't be read, return 0
//
//     args: *str     --  pointer to str to convert.
//
int getAscii(char *str)
{   
    int ascii = 0;
    
    if ( strlen(str) == 1 )                    // if arg length is 1
        ascii = str[0];                        // ...get ascii from 1st char
                
    else if ( strlen(str) == 2
                && str[0] == '^' ) {           // if arg length is 2 and '^'
                
        ascii = str[1];                        // ...get ascii from 2nd char
                
        if (ascii == '?')                      // convert "^?" to backspace
            ascii = 127;
        else {
            
            if (ascii >= 'a') 
                ascii = ascii - 'a' + 1;       // lower case to cntrl ascii
            else
                ascii = ascii - 'A' + 1;       // upper case to cntrl ascii

            if (ascii < 0 || ascii > 31)       // keep within ctrl chars
                ascii = 0;                     // ...bad value
        }
    }
    
    return ascii;
}


///////
//
//  displaySettings
//
//     print the showbaud and print the values of
//     all the args in both the mKeyArgs table
//                      and the mFlagArgs table
//
//     args: *info        --  pointer to struct being printed.
//
void displaySettings(struct termios *info)
{    
    int i;  
        
    showbaud( cfgetospeed( info ));                    // get and show baud rate
    printf("\n");                                      // new line

    // loop through all the values in mKeyArgs
    for ( i = 0; mKeyArgs[i].theValue ; i++ ) {
        printf("%s = ", mKeyArgs[i].attribute);        // print arg
        printASCII(info->c_cc[mKeyArgs[i].theValue]);  // print the key
    }
    printf("\n");                                      // new line
   
    // loop through all the values in mFlagArgs
    for ( i = 0; mFlagArgs[i].theValue ; i++ ) {
        printFlag( info, mFlagArgs[i].attribute,       // print arg
                         mFlagArgs[i].theValue,
                         mFlagArgs[i].flagType);
    }
    printf("\n");                                      // new line
}


///////
//
//  showbaud
//
//     prints the speed in english
//
//     args:  thespeed   --  value from cfgetospeed.
//
void showbaud( int thespeed )
{
        printf("speed ");

        // convert thespeed symbol
        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; ");
}


///////
//
//  printFlag
//
//     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: *info         --  pointer to struct being printed.
//           *attribute    --  attribute to print
//            theValue     --  value of attribute
//            flagType     --  flag type of attribute
//
void printFlag( struct termios *info,
                char           *attribute,
                int             theValue, 
                int             flagType)
{
    int infoValue;             // value of attribute stored in {*info]

    // get infoValue according to flagType
    switch (flagType) {
        case INPUTFLAG:
            infoValue = info->c_iflag;
            break;
        case OUTPUTFLAG:
            infoValue = info->c_oflag;
            break;
        case CONTROLFLAG:
            infoValue = info->c_cflag;
            break;
        case LOCALFLAG:
            infoValue = info->c_lflag;
            break;
    }

    // print attribute
    if (infoValue & theValue)
            printf("%s ", attribute);                  // attribute ON
    else
            printf("-%s ", attribute);                 // attribute OFF
}


///////
//
//  printASCII
//
//     print asci value as a char, using prefix '^' for ctrl chars
//     and "^?" for the delete char
//
//     args:  ascii        --  ascii value of char to print.
//
void printASCII(int ascii)
{
    if (ascii == 127)                                  
        printf("^?; ");                                // is delete
    else if (ascii > 0 || ascii < 32)                  
        printf("^%c; ", ascii -1 + 'A');               // is ctrl char
    else
        printf("%c; ", ascii);                         // is regular char
}


/////////
//
/* All the flags in the termios.h file
//  formatted for
// 
//  struct flaginfo { int   fl_value; char  *fl_name; };

//
// Input flags - software input processing
//
struct flaginfo inputFlags[] = {
                  {IGNBRK    , "ignore BREAK condition"},
                  {BRKINT    , "map BREAK to SIGINTR"},
                  {IGNPAR    , "ignore (discard) parity errors"},
                  {PARMRK    , "mark parity and framing errors"},
                  {INPCK     , "disable checking of parity errors"},
                  {ISTRIP    , "strip 8th bit off chars"},
                  {INLCR     , "map NL into CR"},
                  {IGNCR     , "ignore CR"},
                  {ICRNL     , "map CR to NL (ala CRMOD)"},
                  {IXON      , "enable output flow control"},
                  {IXOFF     , "enable input flow control"},
                  {IXANY     , "any char will restart after stop"},
                  {IUCLC     , "DUMMY VALUE Map upper to lower case on input"},
                  {IFLOW     , "enable output flow control"},
                  {ITANDEM   , "enable input flow control"},
                  {IMAXBEL   , "ring bell on input queue full"},
                  {0         ,  NULL }};
//
// Output flags - software output processing
//
struct flaginfo outputFlags[] = {
                     {OPOST     , "enable following output processing"},
                     {ONLCR     , "map NL to CR-NL (ala CRMOD)"},
                     {OLCUC     , "Map lower case to upper on output"},
                     {OCRNL     , "Map CR to NL on output"},
                     {ONOCR     , "No CR output at column 0"},
                     {ONLRET    , "NL performs CR function"},
                     {OFILL     , "Use fill characters for delay"},
                     {OFDEL     , "fill is DEL, else NUL"},
                     {NLDLY     , "\\n delay"},
                     {NL0       , "NL0"},
                     {NL1       , "NL1 tty 37"},
                     {NL2       , "NL2 vt05"},
                     {NL3       , "NL3"},
                     {TABDLY    , "horizontal tab delay"},
                     {TAB0      , "TAB0"},
                     {TAB1      , "TAB1 tty 37"},
                     {TAB2      , "TAB2"},
                     {TAB3      , "TAB3 expand tabs on output"},
                     {CRDLY     , "\\r delay"},
                     {CR0       , "CR0"},
                     {CR1       , "CR1 tn 300"},
                     {CR2       , "CR2 tty 37"},
                     {CR3       , "CR3 concept 100"},
                     {FFDLY     , "Form feed delay"},
                     {FF0       , "FF0"},
                     {FF1       , "FF1"},
                     {BSDLY     , "\\b delay"},
                     {BS0       , "BS0"},
                     {BS1       , "BS1"},
                     {VTDLY     , "vertical tab delay"},
                     {VT0       , "VT0"},
                     {VT1       , "VT1 tty 37"},
                     {ONLCRNL   , "ONLCRNL"},
                     {OXTABS    , "expand tabs to spaces"},
                     {ONOEOT    , "discard EOT's (^D) on output)"},
                     {0         ,  NULL }};                  
//
// Control flags - hardware control of terminal
//
struct flaginfo cntrFlags[] = {
                     {CSIZE     , "character size mask"},
                     {CSTOPB    , "send 2 stop bits"},
                     {CREAD     , "enable receiver"},
                     {PARENB    , "parity enable"},
                     {PARODD    , "odd parity, else even"},
                     {HUPCL     , "hang up on last close"},
                     {CLOCAL    , "ignore modem status lines"},
                     {CRTSCTS   , "RTS/CTS flow control"},
                     {0         ,  NULL }};

                     //{CS5       , "5 bits (pseudo)"},
                     //{CS6       , "6 bits"},
                     //{CS7       , "7 bits"},
                     //{CS8       , "8 bits"},

// 
// "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.
//
struct flaginfo localFlags[] = {
                     {ECHOE     , "visually erase chars"},
                     {ECHOK     , "echo NL after line kill"},
                     {ECHO      , "enable echoing"},
                     {ECHONL    , "echo NL even if ECHO is off"},
                     {ISIG      , "enable signals INTR, QUIT, [D]SUSP"},
                     {ICANON    , "canonicalize input lines"},
                     {IEXTEN    , "enable FLUSHO and LNEXT"},
                     {NOFLSH    , "don't flush after interrupt"},
                     {TOSTOP    , "stop background jobs from output"},
                     {XCASE     , "Cononical upper/lower presentation"},
                     {ECHOKE    , "visual erase for line kill"},
                     {ECHOPRT   , "visual erase mode for hardcopy"},
                     {ECHOCTL   , "echo control chars as ^(Char)"},
                     {ALTWERASE , "use alternate WERASE algorithm"},
                     {MDMBUF    , "flow control output via Carrier"},
                     {FLUSHO    , "output being flushed (state)"},
                     {NOHANG    , "this should go away"},
                     {PENDIN    , "retype pending input (state)"},
                     {NOKERNINFO, "Disable printing kernel info"},
                     {0         , NULL }};

*/
ws11% cat Plan

DESIGN OUTLINE for CSCI-e215 Assignment 3
Russell J Lowke

     Project: sttyl

       Usage: sttyl [-]icrnl        // map CR to NL (ala CRMOD)
                    [-]onlcr        // map NL to CR-NL (ala CRMOD)
                    [-]echo         // enable echoing
                    [-]echoe        // visually erase chars
                    [-]olcuc        // map lowercase to uppercase on output
                    [-]tabs         // expand tabs on output
                    [-]icano        // canonicalize input lines
                    [-]icanon       // canonicalize input lines
                    [-]isig         // enable signals INTR, QUIT, [D]SUSP
                    [-]hupcl        // hang up on last close
                    [-]cread        // enable receiver
                    [-]brkint       // map BREAK to SIGINTR
                    [-]inpck        // disable checking of parity errors
                    [-]iexten       // enable FLUSHO and LNEXT

                    intr  CHAR      // CHAR will send an interrupt signal
                    erase CHAR      // CHAR will erase last character typed
                    kill  CHAR      // CHAR will erase the current line
                    start CHAR      // CHAR will restart output after stopping
                    stop  CHAR      // CHAR will stop the output


 Description: Mimics the basic functionality of the Unix stty,
              Prints or changes terminal characteristics.

      Method: Reads args sent to main,
              looks for each arg in data tables for flag args and Key args
              gets information about arg from table
              set flags OFF (&= ~) or  ON (|=) depending on arg
              convert CHARs passed to ascii, reading '^' as ctrl and "^?" as del
              set key arg to CHAR when valid
              exit with "Unknown mode" for any invalid arg
              if no args sent, print values of all vars in tables 
              

       Notes: sttyl only works properly in sh
              '^' prefix means a ctrl CHAR
              CHAR for delete is passed as "^?"
              CHAR commands are best passed in quotes, ie  "^?"


ws11% ~lib215/hw/stty/test.stty

   0. Looking at output : tf should examine this 
 
speed 9600 baud; 
intr = ^C; erase = ^H; kill = ^U; start = ^Q; stop = ^S; 
icrnl onlcr echo echoe -olcuc -tabs icano icanon isig -hupcl cread brkint inpck iexten 
 
   I. testing erase and kill chars  
	a. Simple chars .. OK
 
        b. Control Chars typed as Caret-LetterOK
 
        c. Typical one-char control chars.. OK
 
        d. No argument given to erase..Unknown mode
OK
        Note: your version exited with 1
 
  II. Checking each flag separately .. 
                icrnl ..  OK 

		onlcr ..  OK 
		echo ..  OK 

		tabs ..  OK 

                echoe ..  OK 

		olcuc ..  OK 

		icanon ..  OK 

		isig ..  OK 

 
 III. Checking all flag pairs
 icrnl:OK onlcr:OK echo:OK tabs:OK echoe:OK olcuc:OK ICANON:OK isig:OK 
        Checking results .. 
all tests worked correctly
resetting tty
ws11% exit

exit

script done on Fri Mar 15 22:33:43 2002
