#
# comments start with a # sign
#
# each item in the make file consists of
# three things:  a product, its ingredients and how to make it
#
# gcc -o test test.c -lcurses


CC = gcc -g -Wall
        
wsng: wsng.o varlib.o socklib.o
	$(CC) wsng.o socklib.o varlib.o -o wsng
        
wsng.o: wsng.c wsng.h
	$(CC) -c wsng.c

varlib.o: varlib.c varlib.h
	$(CC) -c varlib.c
        
socklib.o: socklib.c socklib.h
	$(CC) -c socklib.c


_______

/*//////
//
// header for wsng.c package
//
*/

#ifndef _WSNG_H_
#define _WSNG_H_

#include	<stdio.h>
#include	<stdlib.h>
#include	<strings.h>
#include	<string.h>
#include	<netdb.h>
#include	<errno.h>
#include	<unistd.h>
#include	<sys/types.h>
#include	<sys/stat.h>
#include	<sys/param.h>
#include	<sys/time.h>
#include	<sys/wait.h>
#include        <time.h>
#include	<signal.h>
#include        <dirent.h>
#include        <unistd.h>
#include	"socklib.h"
#include	"varlib.h"

#define	oops(m,x)	{ perror(m); exit(x); }

/*
// prototypes
*/

void    initAlarm();
void    initVarTable();
void    initExtTable();
int	initSocket(int, char *a[], char [], int *);
int     getLine(char *buffer, FILE *input_stream);
void    set_ticker( int n_msecs );
void    killZombies();
void	read_til_crnl(FILE *);
void    extractQuery(char *item);
void	process_rq( char *rq, FILE *fp);
char	*modify_argument(char *arg, int len);
void    header( FILE *fp, char *content_type, char *resultStr );
void	cannot_do(FILE *fp);
void    do_403(FILE *fp, int cmdType);
void	do_404(char *item, FILE *fp, int cmdType);
void	do_cat(char *f, FILE *fpsock, int cmdType);
void	do_exec( char *prog, FILE *fp, int cmdType);
void	do_ls(char *dir, FILE *fp, int cmdType);
char    *readTable(char *str);
int	isadir(char *f);
int	not_exist(char *f);
int     usrRead(char *f);
int	ends_in_cgi(char *f);
char 	*file_type(char *f);
char    *rfc822_time(time_t thetime);
void    fatal(char *errStr);

struct fileTypes {char extension[BUFSIZ]; 
                  char fileType[BUFSIZ]; };

#endif /* _WSNG_ */


________


/*
//
//  wsng.c ( adapted from Bruce's ws.c ) - a web server
//
//      AUTHOR: Russell Lowke
//   CSCIE-215: Assignment Six
//
//       Usage: wsng [portnumber [rootdir] ]
//
// Description: Enhance Bruces ws.c web server so it will:
//                 1. Return the date, server name & version
//                   see head() function.
//                 2. Support the HEAD method command 
//                   see process_rq() and do() functions.
//                 3. reads filetype and extensions from external file
//                   see initExtTable() and  readTable() functions.
//                 4. Produce directory listings with links
//                   see do_ls() function.
//                 5. look for index.html
//                   see do_ls() function.
//                 6. Handle an extra error
//                   see process_rq() and do_403() functions.
//                 7. Cleanup Zombies
//                   see initAlarm() and killZombies() functions.
//                 8. handle QUERY_STRING
//                   see initVarTable() and extractQuery() functions.         
//
//      Method: Main sends calls to setup the alarm, the variable table, read
//              from the external text file the filetypes, and to initalize
//              the socket. Main then sits in a loop reading incoming socket
//              requests, which are sent to process_rq to be delt with.
//              Alarm is set to periodically trigger killZombies(), which
//              cleans up any dead(finished) processes.  
//              process_rq() forks to a new process, then breaks the call into
//              [cmd] and [item] parts.  The [cmd] should be either a GET or
//              HEAD while [item] contains the bulk of the call.
//              extractQuery() is called on [item] to extract any query
//              variables that may have been sent at the end of [item], these
//              being delimited by a '?'.  [item] is determined to be either
//              a directory, a cgi, or a file format, and sent to the
//              appropriate do() to be processed.
//              ...for directories:
//                 do_ls() will chdir into the directory, then search for an
//              index.html.  If no index.html is found then html is generated to
//              describe the contents of the directory, with links to each item,
//              including item size and last modified dates.
//              ...for cgi:
//                 do_exec() is called, which exec's the cgi
//              ...for files:
//                 do_cat() is called, which checks through the table
//              of extension names v filetypes.  When the right filetype is
//              known this information is passed to head() to be passed at
//              the beginning of the reply, then the rest of the file follows,
//              reading via getc() and sending via putc().
//              ...for errors.  Three important errors are handled.
//               501 - unimplemented command.
//                      basically a syntax error
//               403 - Permissions won't allow.(q 6, "add one more error case")
//                      uses stat(f,&info) and (S_IRUSR & info.st_mode) to
//                      check the mode of the file and see if permissionas
//                      are set to allow a read
//               404 - item not found.
//                      item requested is not found
//
//
//         General functions are...
//                main - Calls four initialization functions for setting
//                       the alarm, the var table, the .ext table, and to get a 
//                       socket. The Main loop then waits for socket requests.
//                       When a request is recieved via socket it goes to
//                       process_rq for processing
//           initAlarm - initialization for alarm, sets it to call killZombies
//                       every 40 seconds or so (ZOMBIEFIX)
//        initVarTable - initialization of environment table using varlib.c
//                       Adds all the external values from [environ]
//                       into the var table.  Decalres REQUEST_METHOD
//                       and QUERY_STRING as vars to be exported to environ
//          initSocket - Changes to the root dir, makes a server socket,
//                       gets the hostname.
//        initExtTable - An external text file called "txtTable" contains a
//                       list of all the extension (.ext) names and their
//                       corresponding file types (text/html).
//                       readTextFile reads this file and tabulates
//                       the data into a struct called mFileTypes.
//          set_ticker - Called by initAlarm
//                       arranges for the interval timer to issue
//                       SIGALRM's at regular intervals
//         killZombies - Called periodically via alarm ( see set_ticker() )
//                       uses waitpid with a WNOHANG option. This will return a 
//                       positive pid for all zombies,
//                       and clean them in the process.
//        extractQuery - Str [item] might contain a '?', after which a
//                       query string is passed as typically used for cgi
//                       scripts. extractQuery() finds the '?' delimeter and,
//                       if found sets the environment var QUERY_STRING =
//                       whatever is after the '?'
//          process_rq - Process all requests.
//                       Request is handled in a new child process (fork)
//                       the parent simply returns and looks for the next 
//                       request. The child must exit, so the exit(0); 
//                       calls are very important here. Scanf is used to
//                       break the request into command [cmd] and argument [arg]
//                       the arg is further passed through modify_argument,
//                       to verify path, and extractQuery, to extract cgi?query
//                       information at end of the request.
//                       A flag called cmdType stores if cmd is of type
//                       HEAD or GET. Finallly, [item] is passed to one of
//                       various "do" functions that handle the request.
//     modify_argument - For security, remove all ".." components in paths
//                       and cleanup, if arg is "/" convert to "."
//              header - Use by all the "do" functions
//                       Sends a standard header to fp as the beginning
//                       of a reply.
//           cannot_do - do_501 unimplemented HTTP command
//              do_403 - Permisions won't allow, so block
//              do_404 - Item requested is not found
//              do_cat - sends back contents after a header
//             do_exec - exec a program/cgi and send it down the pipe
//               do_ls - Looks for index.html in directory, if found, 
//                       sends a do_cat of the index. Otherwise do_ls creates 
//                       a menu of links of all the items in the 
//                       directory by generating html.
//                       The size and last modified date
//                       of each item is displayed also.
//           readTable - looks through the table [mFileTypes] for .ext [str]
//             usrRead - Tests if file [f] is user readable
//                       determined by the st_mode permissions
//                       i.e. chmod foo +r
//         rfc822_time - return a time string suitable for web servers
//                       such as:  Sun, 06 Nov 1994 08:49:37 GMT
//                       uses gmtime() to get struct
//                       then uses asctime to translate to English
//                       then rearranges using sprintf
//
//       Notes:          For question 6 "add one more error case" I added
//                       do_403(), which checks the permissions on a file
//                       to see if it may be read.
*/

#include	"wsng.h"
extern char **environ;


#define	PORTNUM         9666
#define	SERVER_ROOT	"/home/l/o/lowke/public_html/wsng"
#define TXTFILENAME     "txtTable"
#define	HEAD            0
#define	GET             1
#define	POST            2
#define MAXSTR          512
#define ZOMBIEFIX       1000
    
char	myhost[MAXHOSTNAMELEN];
int	myport;
char	*full_hostname();
struct fileTypes mFileTypes[MAXSTR];

/*
//  main
//
//     Calls four initialization functions for setting
//     the alarm, the var table, the .ext table, and to get a socket.
//     The Main loop then waits for socket requests.
//     When a request is recieved via socket it goes to process_rq for
//     processing
//
//     args:  ac         --  argument count
//            **arg      --  pointer to arguments
*/
int main(int ac, char *av[])
{
    int 	sock, fd;
    FILE	*fpin, *fpout;
    char	request[BUFSIZ];
    
    /*
    //   set up
    */
    initAlarm();                                      /* alarm                */
    initVarTable();                                   /* environ var table    */
    initExtTable();                                   /* .ext table           */
    sock = initSocket(ac, av, myhost, &myport);       /* get server socket    */
    printf("ws1 started.  host=%s port=%d\n",         /* print sign on        */
            myhost, myport);
    
    /* 
    // loop forever
    */
    while(1){                                                    
        fd = accept( sock, NULL, NULL );              /* take a call          */
        if ( fd == -1 )
            ;                      /* ...error, (probably system call alarm)  */
        else
        {
            fpin  = fdopen(fd, "r" );
            fpout = fdopen(fd, "w" );

            fgets(request, BUFSIZ, fpin);             /* read request         */
            printf("got a call: request = %s", request);
            read_til_crnl(fpin);

            process_rq(request, fpout);               /* do what client asks  */

            fclose(fpin);
            fclose(fpout);
        }
    }
    
    return 0;
}


/*
//  initAlarm
//
//     initialization for alarm, sets it to
//     call killZombies every 40 seconds or so (ZOMBIEFIX)
//
*/
void initAlarm()
{
    signal(SIGALRM, killZombies );      /* set alarm to trigger killZombies...*/
    set_ticker( ZOMBIEFIX );            /* every ZOMBIEFIX milliseconds.      */
}


/*
//  initVarTable
//
//     initialization of environment table using varlib.c
//     Adds all the external values from [environ] into the var table
//     Decalres REQUEST_METHOD and QUERY_STRING 
//     as vars to be exported to environ
*/
void initVarTable()
{
    if ( VLenviron2table(environ) == 0 )/* copy externals to var table        */
        exit(1);
    VLexport("REQUEST_METHOD");         /* externalize REQUEST_METHOD         */
    VLexport("QUERY_STRING");           /* externalize QUERY_STRING           */
}


/*
//  initSocket
//
//     Changes to the root dir, makes a server socket,
//     gets the hostname.
//
//     args: *ac         --  argument count (from main)
//           *av         --  argument values (from main)
//           *host       --  container for the host name
//           *portnump   --  container for the port number
//
//     returns: the server socket
*/
int initSocket(int ac, char *av[],char host[], int *portnump)
{
    int	sock;                                /* socket int                    */
    int	portnum = PORTNUM;
    char *rootdir = SERVER_ROOT;

    if ( ac > 1 )
        portnum = atoi(av[1]);               /* ensure as integer             */
    if ( ac > 2 )
        rootdir = av[2];

    if ( chdir(rootdir) == -1 )              /* change the directory          */
        oops(rootdir,3);

    sock = make_server_socket( portnum );    /* get the socket int            */
    if ( sock == -1 ) 
        oops("making socket",2);
    strcpy(myhost, full_hostname());
    *portnump = portnum;
    return sock;
}


/*
//  initExtTable
//
//     An external text file called "txtTable" contains a
//     list of all the extension (.ext) names and their
//     corresponding file types (text/html).
//     readTextFile reads this file and tabulates the data into
//     a struct called mFileTypes.
//
//     Note: this function could be improved by
//           finding the size of the text file and initializing
//           the mFileTypes[] array to the right size.
*/
void initExtTable()
{
    int i;                                          /* general purpose int    */
    char        txtline[MAXSTR];                    /* str to hold txt line   */
    FILE	*txtFile;                           /* ptr for txt file       */

    if ( ! (txtFile = fopen(TXTFILENAME, "r")))     /* open the text file     */
            perror( TXTFILENAME );                  /* ...couldn't open       */
    while ( getLine( txtline, txtFile ) ) {
        if ( sscanf(txtline, "%s%s",
                    mFileTypes[i].extension,
                    mFileTypes[i].fileType) != 2 )  /* parse txtline          */
            fatal("text file format bad");
        i++;             
    }
    strcpy(mFileTypes[i].extension, "\0");          /* conclude table         */
    fclose(txtFile);
}


/*
//  getLine
//
//     Used by initExtTable to verify lines of data.
//
//     args: *buffer         --  buffer to put string into
//           *input_stream   --  the input stream
//
//     returns: TRUE if line OK, FALSE if line not OK.
*/
int getLine( char *buffer, FILE *input_stream )
{
    if ( fgets(buffer, MAXSTR, input_stream) )     /* get line                */
    {
        buffer[strlen(buffer) - 1] = '\0';         /* remove nl               */
        return TRUE;                               /* say ok                  */
    }
    return FALSE;                                  /* no more                 */
}


/*
//  set_ticker (adapted from bruce's alarmlib.c)
//
//     Called by initAlarm
//     arranges for the interval timer to issue
//     SIGALRM's at regular intervals
//
//     args:  n_msecs      --  in milliseconds, converted into micro seoncds
*/
void set_ticker( int n_msecs )
{
    struct itimerval new_timeset, old_timeset;
    long	n_sec, n_usecs;

    n_sec = n_msecs / 1000;
    n_usecs = ( n_msecs % 1000 ) * 1000L;

    new_timeset.it_interval.tv_sec  = n_sec;       /* set reload              */
    new_timeset.it_interval.tv_usec = n_usecs;	   /* new ticker value        */
    new_timeset.it_value.tv_sec     = n_sec;       /* store this              */
    new_timeset.it_value.tv_usec    = n_usecs;     /* and this                */

    if ( setitimer( ITIMER_REAL, &new_timeset, &old_timeset ) != 0 ){
        printf("Error with timer..errno=%d\n", errno );
        exit(1);
    }
}


/*
//  killZombies
//
//     Called periodically via alarm ( see set_ticker() )
//     uses waitpid with a WNOHANG option. This will return a
//     positive pid for all zombies, and clean them up in the process.
*/
void killZombies()
{
   // waitpid(-1,0,WNOHANG) loops through cleaning dead processes,
   // WNOHANG option allows it NOT to wait for process, but proceed.
   while ( waitpid(-1,0,WNOHANG) > 0)
       ;
}


/*
//  read_til_crnl
//
//     skip over all request info until a CRNL is seen
//
//     args: *fp         --  file pointer
*/
void read_til_crnl(FILE *fp)
{
    char buf[BUFSIZ];
    while( fgets(buf,BUFSIZ,fp) != NULL && strcmp(buf,"\r\n") != 0 )
        ;
}


/*
//  extractQuery
//
//     Str [item] might contain a '?', after which a query string is passed
//     as typically used for cgi scripts.
//     extractQuery() finds the '?' delimeter and, if found
//     sets the environment var QUERY_STRING = whatever is after the '?'
//
//     args: *item         --  symbol to use for prompt
*/
void extractQuery(char *item)
{
    char *qStr;                                 /* query string pointer       */

    for (qStr = item; *qStr ; qStr++) {         /* parse out the query string */
        if (*qStr == '?') {
            *qStr++ = '\0';
            break;
        }
    }
    
    if (*qStr) {                                /* if query string found      */             
        VLstore ("QUERY_STRING", qStr);         /* save it to var table       */
        VLstore ("REQUEST_METHOD", "GET");
    }
    else {
        VLstore ("QUERY_STRING", "");           /* ...else clear QUERY_STRING */
        VLstore ("REQUEST_METHOD", "");
    }
    environ = VLtable2environ();                /* externalize                */
}


/*
//  process_rq
//
//     Process all requests.
//     Request is handled in a new child process (fork)
//     the parent simply returns and looks for the next request.
//     The child must exit, so the exit(0); calls are very important here.
//     Scanf is used to break the request into command [cmd] and argument [arg]
//     the arg is further passed through modify_argument, to verify path, and
//     extractQuery, to extract cgi?query information at end of the request.
//     A flag called cmdType stores if cmd is of type HEAD or GET.
//     Finallly, [item] is passed to one of various "do" functions that handle
//     the request.
//
//     args: *rq         --  the request
//           *fp         --  file pointer
*/
void process_rq( char *rq, FILE *fp)
{
    char	cmd[BUFSIZ], arg[BUFSIZ];
    char	*item, *modify_argument();
    int         cmdType;
    
    if ( sscanf(rq, "%s%s", cmd, arg) != 2 )    /* parses rq into cmd & arg   */
    return;                                     /* ...bad rq, return          */

    item = modify_argument(arg, BUFSIZ);        /* see pg 51 - ensure no ".." */
    
    extractQuery(item);                         /* extract query from item str*/
    
    if ( fork() != 0 )                          /* create a new process       */
        return;                                 /* return if parent           */
    
                                                /* determine command          */
    if ( strcmp(cmd, "HEAD") == 0) 
        cmdType = HEAD;
    else if ( strcmp(cmd, "GET") == 0)
        cmdType = GET;
    else {
        cannot_do(fp);                          /*      501 Not Implemented   */
        exit(0);
    }

                                                /* deal with item             */
    if ( not_exist( item ) )
        do_404(item, fp, cmdType );             /*      404 Not Found         */
    else if ( isadir( item ) )
        do_ls(item, fp, cmdType );              /* ls,  200 OK                */
    else if ( ends_in_cgi( item ) )
        do_exec(item, fp, cmdType );            /* cgi, 200 OK                */
    else if ( ! usrRead(item) )
        do_403(fp, cmdType );                   /*      403 Forbidden         */
    else
        do_cat(item, fp, cmdType );             /* cat, 200 OK                */
    
    exit(0);
}


/*
//  modify_argument
//
//     For security, remove all ".." components in paths
//          and cleanup - if arg is "/" convert to "."
//
//     args: *arg         --  argument string being modified
//           *len         --  length of arg
//
//     returns: pointer to modified string
*/
char *modify_argument(char *arg, int len)
{
    char *nexttoken;
    char *copy = malloc(len);

    if ( copy == NULL )
        oops("memory error", 1);

    /* remove all ".." components from path */
    /* by tokeninzing on "/" and rebuilding */
    /* the string without the ".." items    */

    *copy = '\0';

    nexttoken = strtok(arg, "/");
    while( nexttoken != NULL )
    {
        if ( strcmp(nexttoken,"..") != 0 )
        {
            if ( *copy )
                strcat(copy, "/");
                strcat(copy, nexttoken);
            }
        nexttoken = strtok(NULL, "/");
	}
    strcpy(arg, copy);
    free(copy);

    /* the array is now cleaned up */
    
    /* handle a special case       */
    if ( strcmp(arg,"") == 0 )
        strcpy(arg, ".");
    return arg;
}


/*
//  header
//
//     Use by all the "do" functions
//     Sends a standard header to fp as the beginning of a reply.
//
//     args: *fp              --  file pointer
//           *content_type    --  filetype string from mFileTypes table
//           *resultStr       --  status result
*/
void header( FILE *fp, char *content_type, char *resultStr )
{
    time_t      timeval;                             /* struct for the time   */
    
    fprintf(fp, "HTTP/1.0 %s\r\n", resultStr);       /* status line           */

    time(&timeval);
    fprintf(fp, "Date: %s", ctime(&timeval));        /* print date time       */
    fprintf(fp, "Server: Russ/1.0.0\r\n");           /* print server name     */
    
    if ( content_type )
        fprintf(fp, "Content-type: %s\r\n", content_type ); /* file type      */
}


/*
//  cannot_do
//
//     do_501 unimplemented HTTP command
//
//     args: *fp         --  file pointer
*/
void cannot_do(FILE *fp)
{
    header(fp, "text/plain", "501 Not Implemented");         /* header        */

    fprintf(fp,"\r\n");
    fprintf(fp, "That command is not yet implemented\r\n");  /* msg           */
    fclose(fp);                                              /* close         */
}


/*
//  do_403
//
//     Permisions won't allow, so block
//
//     args: *fp         --  file pointer
//           *cmdType    --  type of cmd,  GET or HEAD
*/
void do_403(FILE *fp, int cmdType)
{
    header(fp, "text/plain", "403 Forbidden");               /* header        */

    if ( cmdType == GET ) {
        fprintf(fp,"\r\n");
        fprintf(fp, "Not authorized to view item requested\r\n");
    }
    fclose(fp);
}


/*
//  do_404
//
//     Item requested is not found
//
//     args: *item       --  item requested
//           *fp         --  file pointer
//           *cmdType    --  type of cmd,  GET or HEAD
*/
void do_404(char *item, FILE *fp, int cmdType)
{
    header(fp, "text/plain", "404 Not Found");               /* header        */

    if ( cmdType == GET ) {
        fprintf(fp,"\r\n");
        fprintf(fp, "The item you requested: %s\r\nis not found\r\n", item);
    }
    fclose(fp);
}


/*
//  do_cat
//
//     sends back contents after a header
//
//     args: *f         --  filename
//           *fpsock    --  socket pointer
//           *cmdType   --  type of cmd,  GET or HEAD
*/
void do_cat(char *f, FILE *fpsock, int cmdType)
{
    char	*extension = file_type(f);
    char	*content;
    FILE	*fpfile;
    int	c;    
    
    /*
    //  read the filetype associated with file .ext from table
    */
    content = readTable(extension);                      /* try read .ext     */
    if (! content)
        content = readTable("DEFAULT");                  /* try read default  */
    if (! content)
        content = "text/plain";                          /* ...use text plain */

    fpfile = fopen( f , "r");
    if ( fpfile != NULL )
    {
        header( fpsock, content, "200 OK" );             /* header            */
        if ( cmdType == GET ) {
            fprintf(fpsock, "\r\n");
            while( (c = getc(fpfile) ) != EOF )          /* send the file     */
                putc(c, fpsock);
        }
        fclose(fpfile);                                  /* cleanup           */
        fclose(fpsock);        
    }
}


/*
//  do_exec
//
//     exec a program/cgi and send it down the pipe
//
//     args: *prog       --  prog name to exec
//           *fp         --  file pointer
//           *cmdType    --  type of cmd,  GET or HEAD
*/
void do_exec( char *prog, FILE *fp, int cmdType)
{
    int	fd = fileno(fp);

    header(fp, NULL, "200 OK");
    if ( cmdType == GET ) {
        fflush(fp);
        dup2(fd, 1);                                /* get plumbing ready     */
        dup2(fd, 2);
        fclose(fp);
        execl(prog,prog,NULL);                      /* exec the file
                                                      (this should auto exit) */
        perror(prog);                               /*...error if get to here */
    }
    else
       fclose(fp); 
}


/*
//  do_ls
//
//     Looks for index.html in directory, if found, 
//     sends a do_cat of the index. Otherwise do_ls creates 
//     a menu of links of all the items in the 
//     directory by generating html.
//     The size and last modified date
//     of each item is displayed also.
//
//     args: *dir         --  symbol to use for prompt
//           *fp         --  string buffer
//           *cmdType   --  type of cmd,  GET or HEAD
*/
void do_ls(char *dir, FILE *fp, int cmdType)
{    
    DIR           *dir_ptr;                            /* the directory       */
    struct dirent *direntp;                            /* each entry          */
    struct stat   info;
    char          *displayAs, dirSym;
    int           paddingSize;
    
    if (( dir_ptr = opendir( dir )) == NULL ){         /* bad directory?      */
            fprintf(stderr,"Couldn't open directory");
            perror( dir );
    }
    
    if ( chdir(dir) == -1 )                            /* change to directory */
        oops(dir, 3);

    while ((direntp = readdir( dir_ptr )) != NULL) {   /* find index.html     */
        if ( strcmp(direntp->d_name, "index.html" ) == 0) {
             do_cat(direntp->d_name, fp, cmdType );    /* switch to exec      */            
             exit(0);                                  /* exit after exec     */
        }
    }
    rewinddir(dir_ptr);                                /* ...need to rewind   */
     
    header(fp, "text/html", "200 OK");
    fprintf(fp,"\r\n");
    fflush(fp);                                        /* flush fp buffer     */

    if ( cmdType == GET ) {
        
        fprintf(fp,"<html>\n");
        fprintf(fp,"<head><title>Index of...</title>\n");
        fprintf(fp,"</head>\n");
        fprintf(fp,"<body bgcolor = white>\n");
        fprintf(fp,"<center> Index of %s </center>\n", dir);
        fprintf(fp,"<hr>\n");
        fprintf(fp,"<pre>\n");    /* use preformatted mode, <BR>'s not needed */
        fprintf(fp,"%-20s %-35s %-20s\n", "NAME", "LAST MODIFIED", "SIZE");

        
        /*
        // read each item in directory
        */
        while (( direntp = readdir( dir_ptr )) != NULL ) {  
        
            if ( stat(direntp->d_name, &info) == -1 ) {      /* can't stat    */
                fprintf(fp,"could not get info on file %s\n", direntp->d_name);
                continue;
            }
            
            displayAs = direntp->d_name;               /* name display var    */
            
            if ( strcmp(direntp->d_name, "." ) == 0)   /* ignore '.' directory*/
                continue;
            if ( strcmp(direntp->d_name, ".." ) == 0)  /* fix '..' directory  */
                displayAs = "Parent Directory";
            
            paddingSize = 20-strlen(displayAs);        /* calc padding        */
            if ( isadir( direntp->d_name ) ) {         /* test for directory  */
                paddingSize--;
                dirSym = '/';
            }
            else
                dirSym = '\0';
            
            fprintf(fp,"<A HREF='%s%c'>%s%c</A>%*s %-35s %d bytes\n",
                direntp->d_name, dirSym,
                displayAs, dirSym, paddingSize, " ",
                rfc822_time(info.st_mtime), (int)info.st_size);
        }
        closedir(dir_ptr);                             /* close directory     */
        
        fprintf(fp,"</pre>\n");
        fprintf(fp,"</body></html>\n");                /* close html          */
        
        fclose(fp);        
    }
    else
        fclose(fp);
}


/*
//  readTable
//
//     looks through the table [mFileTypes] for extension [str]
//
//     args: *str    -- str (usually ".ext") being looked for
//
//     returns:  string of filetype for extension [str]
*/
char *readTable(char *str)
{
    int i;
    
    /* loop through table of extensions */
    for ( i = 0; strcmp(mFileTypes[i].extension, "\0") ; i++ ) {  
        if ( ! strcmp(mFileTypes[i].extension, str) )  /* find the ext        */
            return mFileTypes[i].fileType;             /* return fileType     */
    }
    return NULL;                                       /* str not found       */
}


/*
//  isadir
//
//     Test if pathname [p] is a directory
//
//     args: *f         --  pathname to test
//
//     returns: TRUE if pathname [p] is a directory
*/
int isadir(char *f)
{
    struct stat info;
    return ( stat(f, &info) != -1 && S_ISDIR(info.st_mode) );
}


/*
//  not_exist
//
//     read next line from input_stream.
//
//     args: *f         --  filename
//
//     returns: FALSE on EOF
*/
int not_exist(char *f)
{
    struct stat info;
    return( stat(f,&info) == -1 && errno == ENOENT );
}


/*
//  usrRead
//
//     Tests if file [f] is user readable
//     determined by the st_mode stat permissions, 
//     stat(f,&info) and (S_IRUSR & info.st_mode)
//     checks the mode of the file and see if permissionas
//     are set to allow a read
//     i.e. chmod foo +r
//
//     args: *f         --  filename
//
//     returns: TRUE if the file permissions respond as readable 
*/
int usrRead(char *f)
{
    struct stat info;
        
    return( stat(f,&info) != -1 && (S_IRUSR & info.st_mode));
}


/*
//  ends_in_cgi
//
//     test if [f] ends with cgi
//
//     args: *f         --  filename
//
//     returns: TRUE if [f] ends in cgi
*/
int ends_in_cgi(char *f)
{
    return ( strcmp( file_type(f), "cgi" ) == 0 );
}


/*
//  rfc822_time
//
//     return a time string suitable for web servers
//     such as:  Sun, 06 Nov 1994 08:49:37 GMT
//     uses gmtime() to get struct
//     then uses asctime to translate to English
//     then rearranges using sprintf
//
//     args: *thetime         --  a time_t value
//
//     returns: a pointer to a static buffer string
*/
char *rfc822_time(time_t thetime)
{
        struct  tm *t;
        char    *str;
        int     d;
        static  char retval[36];

        t = gmtime( &thetime );                       /* break into parts     */
        str = asctime( t );                           /* create string        */
        d = atoi( str + 8 );
        sprintf(retval,"%.3s, %02d %.3s %.4s %.8s GMT", 
                        str ,   d, str+4, str+20, str+11 );
        return retval;
}


/*
//  file_type
//
//     return extension of file [f]
//
//     args: *f         --  filename
//
//     returns: extension on filename [f]
*/
char *file_type(char *f)
{
    char	*cp;                                  /* char ptr             */
    if ( (cp = strrchr(f, '.' )) != NULL )            /* look for '.'         */
        return cp+1;
    return "";
}


/*
//  full_hostname
//
// NOTE: this returns a ptr to a static buffer that is
//       overwritten with each call. ( you know what to do.)
//
//     returns: returns full `official' hostname for current machine
*/
char *full_hostname()
{
    struct	hostent		*hp;
    char	hname[MAXHOSTNAMELEN];
    static  char fullname[MAXHOSTNAMELEN];

    if ( gethostname(hname,MAXHOSTNAMELEN) == -1 )         /* get rel name    */
    {
        perror("gethostname");
        exit(1);
    }
    hp = gethostbyname( hname );		      /* get info about host  */
    if ( hp == NULL )			              /*   or die             */
		return NULL;
    strcpy( fullname, hp->h_name );		      /* store foo.bar.com    */
    return fullname;			              /* and return it        */
}


/*
//  fatal
//
//     Fatal error message to stderr then exit (never returns)
//
//     args:  errStr      --  error message to display
*/
void fatal(char *errStr)
{
    fprintf(stderr, "%s\n", errStr);
    exit(1);
}


______



/*
 *	socklib.h
 *
 *	This file contains functions used lots when writing internet
 *	client/server programs.  The two main functions here are:
 *
 *	make_server_socket( portnum )	returns a server socket
 *					or -1 if error
 *
 *	connect_to_server(char *hostname, int portnum)
 *					returns a connected socket
 *					or -1 if error
 */ 

int make_server_socket( int );
int connect_to_server( char *, int );




______



#include	<stdio.h>
#include	<strings.h>
#include	<sys/types.h>
#include	<sys/socket.h>
#include	<netinet/in.h>
#include	<netdb.h>
#include	<unistd.h>

/*
//	socklib.c
//
//	This file contains functions used lots when writing internet
//	client/server programs.  The two main functions here are:
//
//	make_server_socket( portnum )	returns a server socket
//					or -1 if error
//
//	connect_to_server(char *hostname, int portnum)
//					returns a connected socket
//					or -1 if error
*/ 

int make_server_socket( int portnum )
{
        struct  sockaddr_in   saddr;   /* build our address here */
	struct	hostent		*hp;   /* this is part of our    */
	char	hostname[256];         /* address 	         */
	int	sock_id;	       /* line id, file desc     */

       /*
       //      step 1: build our network address
       //               domain is internet, hostname is local host,
       //               port is some arbitrary number
       */

       gethostname( hostname , 256 );          /* where am I ?         */
       hp = gethostbyname( hostname );         /* get info about host  */
       if ( hp == NULL )
		return -1;

       bzero( (void *)&saddr, sizeof(saddr) ); /* zero struct          */
                                               /* fill in hostaddr     */
       bcopy( (void *)hp->h_addr, (void *)&saddr.sin_addr, hp->h_length);
       saddr.sin_family = AF_INET ;            /* fill in socket type  */
       saddr.sin_port = htons(portnum);        /* fill in socket port  */

       /*
       //      step 2: ask kernel for a socket, then bind address
       */

       sock_id = socket( AF_INET, SOCK_STREAM, 0 );    /* get a socket */
       if ( sock_id == -1 ) return -1;
						       /* give it      */
       if ( bind(sock_id, (struct sockaddr *) &saddr, sizeof(saddr)) != 0 )
                return -1;				/* an address	*/

       /*
       //      step 3: tell kernel we want to listen for calls
       */

       if ( listen(sock_id, 1) != 0 ) return -1;

	return sock_id;
}

int connect_to_server( char *hostname, int portnum )
{
	struct sockaddr_in  servadd;        /* the number to call */
	struct hostent      *hp;            /* used to get number */
	int    sock_id;			    /* returned to caller */

       /*
       //      build the network address of where we want to call
       */

       hp = gethostbyname( hostname );
       if ( hp == NULL ) return -1;

       bzero( (void *)&servadd, sizeof( servadd ) ); /* zero the address */
       servadd.sin_family = AF_INET ;          /* fill in socket type  */
                                               /* and machine address  */
       bcopy( (void *)hp->h_addr, (void *)&servadd.sin_addr, hp->h_length);
       servadd.sin_port = htons(portnum);      /* host to num short    */

       /*
       //        make the connection
       */

       sock_id = socket( AF_INET, SOCK_STREAM, 0 );    /* get a line   */
       if ( sock_id == -1 ) return -1;          	/* or fail      */
                                                       /* now dial     */
       if( connect(sock_id,(struct sockaddr *)&servadd,sizeof(servadd)) !=0 )
               return -1;

       /*
       //      we're connected to that number, return the socket
       */

	return sock_id ;
}


________


/*//////
//
// header for varlib.c package
//
*/

#ifndef _VARLIB_H_
#define _VARLIB_H_

#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>

int	VLenviron2table(char **);
int	VLexport(char *);
char	*VLlookup(char *);
void	VLset();
int	VLstore( char *, char * );
char	**VLtable2environ();

#endif /* _VARLIB_ */



________


/*/////////
//
//  varlib.c 
//
//      AUTHOR: Bruce's varlib.c modified by Russell Lowke
//   CSCIE-215: Assignment Five
//
//       Usage: a simple storage system to store name=value pairs
//              with facility to mark items as part of the environment
//
//   interface: VLstore( name, value )    returns 1 for 0k, 0 for no
//              VLlookup( name )          returns string or NULL if not there
//              VLset()                   prints out current table
//              VLexport( name )          adds name to list of env vars
//              VLtable2environ()         copy from table to environ
//              VLenviron2table()         copy from environ to table
//
//     details: the table is stored as an array of structs that
//	        contain a flag for `global' and a single string of
//	        the form name=value.  This allows EZ addition to the
//              environment.  It makes searching pretty easy, as
//              long as you search for "name=" 
//
*/

#include	"varlib.h"

#define	MAXVARS	200		/* a linked list would be nicer */

struct var
	{
	char *str;		/* name=val string	*/
	int  global;		/* a boolean		*/
	};

/* local vars: the table */
static struct var tab[MAXVARS];

/* local functions */
static char *new_string( char *, char *);
static struct var *find_item(char *, int);


/*
//  VLstore
//
//     traverse list, if found, replace it, else add at end
//     since there is no delete, a blank one is a free one
//     return 0 if trouble, 1 if ok
//
//     args:  *name         --  var name to store
//            *val          --  value to set
*/
int VLstore( char *name, char *val )
{
    struct var *itemp;
    char	*s;
        
    if ( (itemp = find_item(name,1)) == NULL )      /* find spot to put it    */
        return 0;

    if ( itemp->str )                               /* chuck old value        */
        free(itemp->str);

    if ( ( s = new_string( name, val )) == NULL )   /* new name=val           */
        return 0;				    /* no memory              */
    
    itemp->str = s;                                 /* store it               */
    return 1;
}


/*
//  new_string
//
//     returns new string of form name=value or NULL on error
//
//     args:  *name        --  name of variable
//            *val         --  value of variable
*/
char *new_string( char *name, char *val )
{
    char	*retval;

    retval = malloc( strlen(name) + strlen(val) + 2 );
    if ( retval != NULL )
        sprintf(retval, "%s=%s", name, val );
    return retval;
}


/*
//  VLlookup
//
//     returns value of var or empty string if not there
//
//     args:  *name         --  name of var to look up
*/
char *VLlookup( char *name )
{
    struct var *itemp;

    if ( (itemp = find_item(name,0)) != NULL )
        return itemp->str + 1 + strlen(name);
    return "";
}


/*
//  VLexport
//
//     marks a var for export, adds it if not there
//     returns 0 for no, 1 for ok
//
//     args:  *name     --  name of var to export
*/
int VLexport( char *name )
{
	struct var *itemp;

	if ( (itemp = find_item(name,0)) != NULL ){
		itemp->global = 1;
		return 1;
	}
	if ( VLstore(name, "") == 1 )
		VLexport(name);
	return 0;
}


/*
//  find_item
//
//     searches table for an item
//     returns ptr to struct or NULL if not found
//     OR if (first_blank) then ptr to first blank one
//
//     args:  *name         --  variable name to look up
//            first_blank   --  ptr to first blank one
*/
static struct var *find_item( char *name , int first_blank )
{
    int   i;
    int   len = strlen(name);
    char *s;

    for( i = 0 ; i<MAXVARS && tab[i].str != NULL ; i++ )
    {
        s = tab[i].str;
        if ( strncmp(s,name,len) == 0 && s[len] == '=' ){
            return &tab[i];
        }
    }
    if ( i < MAXVARS && first_blank )
        return &tab[i];
    return NULL;
}


/*
//  VLset
//
//     performs the shell's  `set'  command
//     Lists the contents of the variable table, marking each
//     exported variable with the symbol  '[E]' 
*/
void VLset()
{
	int	i;
	for(i = 0 ; i<MAXVARS && tab[i].str != NULL ; i++ )
	{
		if ( tab[i].global )
			printf("[E] %s\n", tab[i].str);
		else
			printf("    %s\n", tab[i].str);
	}
}


/*
//  VLenviron2table
//
//     initialize the variable table by loading array of strings
//     return 1 for ok, 0 for not ok
//
//     args:  *env[]         --  environment list
*/
int VLenviron2table(char *env[])
{
	int     i;
	char	*newstring;

	for(i = 0 ; env[i] != NULL ; i++ )
	{
		if ( i == MAXVARS )
			return 0;
		newstring = malloc(1+strlen(env[i]));
		if ( newstring == NULL )
			return 0;
		strcpy(newstring, env[i]);
		tab[i].str = newstring;
		tab[i].global = 1;
	}
	while( i < MAXVARS ){		/* I know we don't need this	*/
		tab[i].str = NULL ;	/* static globals are nulled	*/
		tab[i++].global = 0;	/* by default			*/
	}
	return 1;
}


/*
//  VLtable2environ
//
//     build an array of pointers suitable for making a new environment
//     note, you need to free() this when done to avoid memory leaks
*/
char **VLtable2environ()
{
	int	i,			/* index			*/
		j,			/* another index		*/
		n = 0;			/* counter			*/
	char	**envtab;		/* array of pointers		*/

	/*
	// first, count the number of global variables
	*/

	for( i = 0 ; i<MAXVARS && tab[i].str != NULL ; i++ )
		if ( tab[i].global == 1 )
			n++;

	/* then, allocate space for that many variables	*/
	envtab = (char **) malloc( (n+1) * sizeof(char *) );
	if ( envtab == NULL )
		return NULL;

	/* then, load the array with pointers		*/
	for(i = 0, j = 0 ; i<MAXVARS && tab[i].str != NULL ; i++ )
		if ( tab[i].global == 1 )
			envtab[j++] = tab[i].str;
	envtab[j] = NULL;
	return envtab;
}


________



// myLib.c
//
// Useful general purpose functions 

#include        <string.h>
#include        <ctype.h>

#define	BUFLEN	512
#define	YES	1
#define	NO	0


//
//  Fatal error message to stderr then exit (never returns)
//
void fatal(char *errStr)
{
    fprintf(stderr, "%s\n", errStr);
    exit(1);
}

//
//  Random generator
//
//   // set random seed
//   srand(getpid());

/*
// Random generator
*/
double randomInt(int min, int max)
{
    int range = max - min;
    
    return rand()%range + min;
}


//
// Init all values in array to '\0'
//
void initArray(char mArray[], int arraySize)
{
    int i;
    for (i = 0;  i <= arraySize-1 ; i++)
            mArray[i] = '\0';
}


//
// Trim leading and trailing whitespace (including '\n')
//
void trimWhiteSpace(char source[])
{    
    int  i;
    char target[BUFLEN];
    
    // strip trailing whitespace
    for (i = strlen(source)-1; isspace((int)source[i]) && i >= 0 ; i-- ) {
            source[i] = '\0';
    }

    // count leading whitespace
    for (i = 0; isspace((int)source[i]) ; i++) {
    }

    // copy into target
    strcpy(target, &source[i]);

    // copy back to source
    strcpy(source, target);
}


//
// Read chars into field line[BUFLEN]
// until reaching item delimiter (itemDel[]), return delimiter reached
//
char readChars(int itemDel[], FILE *fp, char line[BUFLEN])
{    
    char mChar;               // my char from stream
    int  i, 
    endStr    = NO,
    charCount = 0;
    
    // empty target String
    initArray(line, BUFLEN);

    do {
            // get a char
            mChar = fgetc(fp);

            // check for delimiter
            for (i = 0; itemDel[i]; i++)
                    if (mChar == itemDel[i] || mChar == EOF )
                            endStr = YES;

            // append char to target string
            if ( ! endStr)
                    line[charCount] = mChar;
        
            charCount++;

    } while (! endStr);

    // conclude target string
    line[charCount] = '\0';

    return mChar;
}


//
// Print contents of array (useful for debugging)
// if arraySize = -1 then array terminated with '\0' (string)
//
void printCharArray(char mArray[], int arraySize) {
    
    int i;

    printf("[");

    // if string. (p.s. could use strlen)
    if (arraySize == -1) {
            for (arraySize = 0; mArray[arraySize] != '\0'; arraySize++) {
            }
    }
        
    for (i = 0; i <= arraySize ; i++) {
            if (mArray[i] == '\0')
                    printf(" 0 ");
            else
                    printf("%c", mArray[i]);
                
            if (i < arraySize)
                    printf(",");
        }
        
        printf("]");
}


//
// Parse mString into an array of separate fields
// accepts an array of item delimiters to determine breaks
//
int parse_line( int  itemDel[],
                char mString[],
                char fields[BUFLEN][BUFLEN])  
{
    int i       = 0,  // general purpose counter
    endFlag     = 0,  // flag for indicating record is finished
    readAt      = -1, // reading position in source array
    currentFld  = 0,  // item in fields[] array
    destination = 0;  // destination in current field                
        
    do {
            // loop through mString
            readAt++;
                
            // loop through each possible delimiter
            for (i = 0; itemDel[i] != NULL && ! endFlag ; i++) {

                    // check for delimeter or the end of string
                    if ( mString[readAt] == itemDel[i] ||
                         mString[readAt] == NULL )
                            endFlag = 1;    // flag that record as ended
            }

            if ( ! endFlag ) {
            
                    // copy the char
                    fields[currentFld][destination] = mString[readAt];
                    destination++;
            }
            else {

                    // end record, terminate this field
                    fields[currentFld][destination] = '\0';    

                    // check if field is empty before incrementing
                    if ( fields[currentFld][0] != '\0')
                            currentFld++;

                    // reset for new field
                    destination = 0;
                    endFlag = 0;
            }
            
            // error check: array limits
            if (readAt      == BUFLEN ||
                currentFld  == BUFLEN ||
                destination == BUFLEN )
                    break;

        } while ( mString[readAt] != NULL );
        
        // return number of fields found in line
        return currentFld;
}

// -- Using sprintf
// sprintf(myString, "%s%c%s", strA, myChar, strB);

// -- Using strstr
// if (strstr(line, "<delim")) {
//      cp = strstr(line, "value=");
//      valueChar = cp[6];
// }

//  readers = atoi(optarg);    atoi()   asci to string


int splitline(char *cmdline, int *argcp, char *argv[], int max)
{
    int   i      = 0,
          retval = FALSE;
    char  mode   = ' ',
         *cmdp   = cmdline ;

    while ( i<=max ){			        /* still room?          */
						/* skip leading space   */
        while ( *cmdp == ' ' || *cmdp == '\t' )
            cmdp++;
					
        if ( *cmdp == '\0' )                    /* at end of string?    */
            break;

        if ( *cmdp == '\"' ) {                  /* start of a quote?    */
            cmdp++;                             /* bump pointer         */
            mode = '\"';                        /* change to quote mode */
        }
						/* record string        */
        argv[i++] = cmdp ;		        /* and bump counter     */
		
                                                /* move to end of word or quote */
        while ( *++cmdp && *cmdp != mode && *cmdp != '\t' )
            ;

        mode = ' ';                             /* revert mode to space */

        if ( *cmdp != '\0' )		        /* past end of word     */
            *cmdp++ = '\0';                     /* terminate string     */

    }

    if ( i > max )
        printf("Too many args\n");
    else if ( i > 0 ){
        argv[i] = NULL ;                        /* mark end of array    */
       *argcp   = i;                            /* and store argc       */
        retval  = TRUE ;                        /* say ok               */
    }
    
    return retval ;
}

_____
