/* CS 282 - Craig Kelley - March 9, 1995 */

/* Readcmd is program that displays a prompt and accepts input from the
 * the terminal.  If a <TAB> is encountered, it will attempt to complete the
 * string from an internal trie.  The list, in this case, contains the
 * the reserved words for the C language.  This version also recognizes the
 * backspace key to allow editing.  A <RETURN> will cancel the current
 * operation and allow the user to start anew.  A CTRL-D or a "QUIT" signal
 * will exit the program.  I would like to utilize the up/down arrows in
 * a future version to allow the user to scroll through the list with a
 * given prefix.  If an option is added at the command line, readcmd will
 * display some brief instructions.
 */
 
/* Included Headers */

#include <stdio.h>
#include <signal.h>
#include <ctype.h>
#include "trie.h"
#include "terminal.h"

/* Definitions */

#define CRCODE 10		/* Carriage Return code */
#define BACKKILL (0x7f)		/* Primary backspace */
#define ALTBAKILL '\b'		/* An alternate backspace */

/* Function Prototype */

void exit_gracefully();

int term;  /* our terminal identifier :: global so that the trap can use it*/

int main (int argc, char *argv[])		       
{

   char *list[] = {
      "auto", "break", "case", "char", "const", "continue", "default",
      "do", "double", "else", "enum", "extern", "float", "for", "goto",
      "if", "int", "long", "register", "return", "short", "signed",
      "sizeof", "static", "struct", "switch", "typedef", "union",
      "unsigned", "void", "volatile", "while"};
   node_ptr trie;		/* our trie */
   int i, start_over=1;		/* for loops and counting */
   char c;			/* our main character */
   char prefix[MAXLENGTH];	/* prefix of input line */

   if (argc > 1) {	     /* if there is an option, display instructions */
      printf("Type in some characters and press <TAB> to complete the line\n");
      printf("When you are finished, send <CONTOL-D>\n");
      return 0;
   }
   
   if ((term = terminal_cbreak()) < 0) {
      printf("Unable to setup terminal : %d", term);
      exit(-1);
   }

   signal(SIGINT, SIG_IGN);	/* ignore the interrupt */
   signal(SIGQUIT, exit_gracefully); /* trap the quit signal to exit */

   trie = new_node(0);		/* initialize the trie */
   for(i=0; i < 32; ++i) 	/* insert each item in the trie */
      insert_trie(trie,list[i]);
   
   while(start_over) {
      i = 0;			/* initialize our pointer */
      prefix[0] = 0;
      start_over = 0;		/* provide a way to start over */
      write(term, "] ", 2);	/* a prompt */

/* This is the main loop; it first tests whether or not it should exit
 * the loop (startover), in case the <RETURN> key was pressed.  Then it
 * reads a character from the keyboard and if it's printable, it adds it
 * to the prefix and writes it to the terminal; if it's not printable it
 * will let the switch trap the significant characters.
 */
      
      while((start_over == 0) && (read(term, &c, 1))) 
	 if (isprint(c) && (i < MAXLENGTH)){
	    prefix[i++] = c;	/* insert the char into our prefix */
	    prefix[i] = 0;	/* terminate the prefix */
	    write(term, &c, 1);
	    }   
	 
	 else			/* trap the non-printable char */
	    switch (c) {
	    case '\t' :		/* Tab? */
	       if (i) 		/* Make sure we actually have a prefix */
		  if (output_trie(trie, &prefix[0], &prefix[i])) {
		     write(term, &prefix[i], strlen(&prefix[i]));
		     i = i + (strlen(&prefix[i])); 
		     prefix[i+1] = 0;  /* terminate the string */
		  }
	       break;
	    case BACKKILL :	/* Backspace */
	    case ALTBAKILL :	/* in case you use the NeXT via ISUUX */
	       if (i > 0) {	/* Make sure there's something to erase */
		  prefix[i--] = 0;     /* erase the char from memory */
		  write(term,"\b \b", 3);   /* erase the screen */
	       }
	       break;
	    case CRCODE :	/* carriage return */
	       start_over = 1;	/* flag our loop to start over */
	       write(term, "\n", 1); /* get a new line */
	       break;
	    case ('d' & 0x01F) :  /* Control - D? */
	       free_trie(trie);
	       exit_gracefully();
	       break;
	    }
   }
   return 0;
} 

void exit_gracefully() {

   if (restore_terminal(term) > 0) {
      printf("\nreadcmd: quit successful\n");
      exit(0);
   }
   else {
      write(term, "ERROR : Couldn't close terminal", 31);
      exit(-1);
   }
}
       


