/*********************************************************************
 * Author: B. Alex Bridges                                           *
 * File: lab09.c                                                     *
 * Class: CPSC-202 Lab Section 3                                     *
 * Project: Lab 9 - On-line Test Administration System               *
 * Purpose: This program will administer a test via the computer.    *
  *********************************************************************/

/* INCLUDES */
#include <ctype.h>
#include <fcntl.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

/* CONSTANTS */
#define c_MaxLngth 255+1
#define c_Timeout 15

/* GLOBAL VARIABLES */
sigjmp_buf sj_env;

/* FUNCTION PROTOTYPE(S) */
void f_sigAlrm(int int_sig);
int f_EchoLine(int int_fd);


/*********************************************************************
 * MAIN FUNCTION                                                     *
 *********************************************************************/
/*************************************************************************
 * Purpose: To administer a test via the computer.                       *
 * Input: --COMMAND-LINE ARGUMENTS--                                     *
 *        1. 'BASE_NAME' = The base name for the exam's test and         *
 *                         answer key.                                   *
 * Output: --RETURN VALUES--                                             *
 *         => COMPLETED SUCCESSFULLY = 0                                 *
 *         => ERROR = -1                                                 *
 * Called Routines: f_EchoLine                                           *
 * Assumptions: The last typed character is the user's final answer.     *
 *************************************************************************/
int main(int argc, char *argv[]) /* SETS UP COMMAND-LINE ARGUMENTS */
{
	/* --LOCAL VARIABLES-- */
	char str_I_test[c_MaxLngth];/* THE TEST INPUT FILENAME */
	char str_I_key[c_MaxLngth];	/* THE ANSWER KEY INPUT FILENAME */
	int fd_I_test = 0;			/* open (str_I_test) RESULT */
	int fd_I_key = 0;			/* open (str_I_key) RESULT */
	int f_Done = 0;				/* DONE FLAG ~ 0 = FALSE; 1 = TRUE */
	char charA_Read[1];			/* CURRENT READ CHARACTER */
	char str_Line[c_MaxLngth];	/* LINE OF READ TEXT */
	int int_i = 0;				/* ARRAY INDEX */
	int int_Qstns = 0;			/* THE NUMBER OF QUESTIONS */
	int int_n = 0;				/* LOOP COUNTER */
	char char_RAns;				/* THE REAL ANSWER */
	char char_UAns;				/* USER'S FINAL ANSWER */
	int int_CAns = 0;			/* THE NUMBER OF CORRECT ANSWERS */
	float fp_Grade = 0.0;		/* THE USER'S GRADE IN PERCENT */

	/* --FUNCTION-- */
	/* HEADER */
	system("clear");
	printf("B. Alex Bridges\n"
	       "CPSC-202, Winter 1998\n"
	       "The On-line Test Administration System Program\n"
	       "\n");

	/* CHECKING FOR REQUIRED ARGUMENTS */
	if(argc < 2)
	{
		printf("\aERROR: The following command-line argument must be included:\n"
		       " => 1. 'BASE_NAME' = The base name for the exam's test and answer key.\n");
		exit(-1); /* ERROR */
	} /* if */

	/* BUILD UP OF INPUT FILENAMES */
	/* => TEST */
	str_I_test[0] = '\0';
	strcpy(str_I_test, argv[1]);
	strcat(str_I_test, ".test");
	str_I_test[strlen(str_I_test)] = '\0';
	/* => KEY */
	str_I_key[0] = '\0';
	strcpy(str_I_key, argv[1]);
	strcat(str_I_key, ".key");
	str_I_key[strlen(str_I_key)] = '\0';

	/* OPENING INPUT FILES */
	/* => TEST */
	if( ( fd_I_test = open(str_I_test, O_RDONLY) ) < 0 ) /* OPENS INPUT FILE FOR READING */
	{
		perror("ERROR: open Exam's Test");
		exit(-1);
	} /* if */
	fprintf(stderr, "--Test Input File Opened--\n"); /* DEBUG */
	/* => ANSWER KEY */
	if( ( fd_I_key = open(str_I_key, O_RDONLY) ) < 0 ) /* OPENS INPUT FILE FOR READING */
	{
		perror("ERROR: open Exam's Answer Key");
		exit(-1);
	} /* if */

	/* NUMBER OF QUESTIONS */
	/* => READ CHARACTERS TO BUILD UP LINE OF TEXT 'TILL DONE OR ERROR */
	str_Line[0] = '\0';
	while(f_Done == 0)
	{
		if( read(fd_I_test, charA_Read, 1) < 0 )
		{
			perror("ERROR: read Exam's Test");
			exit(-1);
		} /* if */
		else if(charA_Read[0] == '\n')
		{
			str_Line[int_i] = '\0';
			f_Done = 1;
		} /* if */
		else
			str_Line[int_i++] = charA_Read[0];
	} /* while */
	int_Qstns = atoi(str_Line);

	/* SETS UP THE SIGNAL HANDLER */
	if( sigset( SIGALRM, f_sigAlrm ) == SIG_ERR )
		perror("ERROR: sigset SIGALRM");

	/* ADMINISTRING THE TEST */
	for(int_n = 0; int_n < int_Qstns; int_n++)
	{
		/* => CORRECT ANSWER */
		if( read(fd_I_key, charA_Read, 2) < 0 )
		{
			perror("ERROR: read Exam's Answer Key");
			exit(-1);
		} /* if */
		else
			char_RAns = charA_Read[0];

		/* => TEST QUESTION */
		printf( "%d. ", (int_n + 1) );
		if( f_EchoLine(fd_I_test) == -1)
			exit(-1);
		printf("\n"); /* CARRIAGE RETURN */

		/* => MULTIPLE CHOICE ANSWER - A */
		printf("A) ");
		if( f_EchoLine(fd_I_test) == -1)
			exit(-1);
		printf("\n");

		/* => MULTIPLE CHOICE ANSWER - B */
		printf("B) ");
		if( f_EchoLine(fd_I_test) == -1)
			exit(-1);
		printf("\n"); /* CARRIAGE RETURN */

		/* => MULTIPLE CHOICE ANSWER - C */
		printf("C) ");
		if( f_EchoLine(fd_I_test) == -1)
			exit(-1);
		printf("\n"); /* CARRIAGE RETURN */

		/* => MULTIPLE CHOICE ANSWER - D */
		printf("D) ");
		if( f_EchoLine(fd_I_test) == -1)
			exit(-1);
		printf("\n" /* CARRIAGE RETURN */
		       "\n"); /* BLANK LINE */

		/* => USER'S ANSWER */
		if( sigsetjmp(sj_env, 1) == 0)
		{
			printf("Please enter answer: ");
			alarm(c_Timeout);
			while( ( charA_Read[0] = getchar() ) != '\n' )
				char_UAns=charA_Read[0];
			alarm(0);
		} /* if */
		else
			printf("WARNING: Input process' timeout of %d seconds has expired.\n", c_Timeout);

		/* => CHECKING ANSWER */
		if( tolower(char_RAns) == tolower(char_UAns) )
			int_CAns++;

		/* => USER NOTIFICATION */
		printf("\n"
		       " Question Results:\n"
		       "  => Your answer was '%c'.  The correct answer is '%c'.\n"
		       "\n", char_UAns, char_RAns);
	} /* for */

	/*  TEST RESULTS */
	fp_Grade = ( ( (float) int_CAns / (float) int_Qstns ) * 100);
	printf( "Test Results:\n"
	        "=> Number correct %d out of %d for a(n) %3.2f percent.\n", int_CAns, int_Qstns, fp_Grade);

	/* CLOSING FILES */
	/* => TEST */
	if( close(fd_I_test) == -1 )
	{
		perror("ERROR: close Exam's Test");
		exit(-1);
	} /* if */
	/* => ANSWER KEY */
	if( close(fd_I_key) == -1 )
	{
		perror("ERROR: close Exam's Answer Key");
		exit(-1);
	} /* if */

	return(0);
} /* main */

/*********************************************************************
 * ADDITIONAL FUNCTIONS                                              *
 *********************************************************************/
/*************************************************************************
 * Purpose: To echo a line of text from a file.                          *
 * Input: int_fd = The file's file descriptor.                           *
 * Output: --RETURN VALUES--                                             *
 *         => COMPLETED SUCCESSFULLY  = 0                                *
 *         => ERROR = -1                                                 *
 * Called Routines: N/A                                                  *
 * Assumptions: The file has already been opened.                        *
 *************************************************************************/
int f_EchoLine(int int_fd)
{
	/* --LOCAL VARIABLES-- */
	int f_Done = 0;				/* DONE FLAG ~ 0 = FALSE; 1 = TRUE */
	char charA_Read[1];			/* CURRENT READ CHARACTER */
	int int_Read = 0;			/* THE NUMBER OF READ CHARACTERS */
	char str_Line[c_MaxLngth];	/* LINE OF READ TEXT */
	int int_i = 0;				/* ARRAY INDEX */

	/* READ CHARACTERS TO BUILD UP LINE OF TEXT 'TILL DONE OR ERROR */
	str_Line[0] = '\0';
	while(f_Done == 0)
	{
		if( ( int_Read = read(int_fd, charA_Read, 1) ) < 0 )
		{
			perror("ERROR: read File");
			return(-1);
		} /* if */
		else if( ( int_Read == 0 ) || ( charA_Read[0] == '\n' ) )
		{
			str_Line[int_i] = '\0';
			f_Done = 1;
		} /* if */
		else
			str_Line[int_i++] = charA_Read[0];
	} /* while */

	/* ECHO LINE OF TEXT */
	printf("%s", str_Line);

	return(0);
} /* f_EchoLine */

/*************************************************************************
 * Purpose: To go to sigsetjmp.                                          *
 * Input: int_sig = A signal                                             *
 * Output: N/A                                                           *
 * Called Routines: N/A                                                  *
 * Assumptions: N/A                                                      *
 *************************************************************************/
void f_sigAlrm(int int_sig)
{
	siglongjmp(sj_env, 1);
} /* f_EchoLine */
