/*
 * procline.c
 * File Revision 1
 * Commandline processing for smallsh.
 * (c) 1999 Addison Wesley Longman, Inc.
 * (c) 2000 Jacob Lundberg, jacob@chaos2.org
 */


/*
 * 2000.11.05	Acquire initial code
 *		Adjust style
 */


#include <ctype.h>
#include "smallsh.h"


/* Program buffers and work pointers */
static char inpbuf[MAXBUF], tokbuf[2 * MAXBUF], *ptr = inpbuf, *tok = tokbuf;
static char special[] = {' ', '\t', '<', '>', '&', ';', '\n', '\0' };


void doprompt(char *p) {
/*
 * doprompt()
 * Push the prompt on out to the console.
 */

   pid_t pid;
   int status;

   /* Check for information on backgrounded processes. */
   while((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
      if(WIFEXITED(status))
         printf("background pid %li is done: exit value %i\n", pid, WEXITSTATUS(status));
      else if(WIFSIGNALED(status))
         printf("background pid %li is done: terminated by signal %i\n", pid, WTERMSIG(status));
      else if(WIFSTOPPED(status))
         printf("background pid %li has been stopped by signal %i\n", pid, WSTOPSIG(status));
   }

   /* Display the prompt itself. */
   printf("%s", p);

   /* Make sure it gets out now. */
   fflush(stdout);
   return;

}


int userin(char *p) {
/*
 * userin()
 * Print the prompt and read a line.
 */

   char c;
   int count;

   /* Initialization for later routines. */
   ptr = inpbuf;
   tok = tokbuf;

   /* Display the prompt. */
   doprompt(p);

   count = 0;
   while(true) {
      c = getchar();
      if(c == EOF) {
	 return(EOF);
      }

      if(count < MAXBUF)
	 inpbuf[count++] = c;

      if(c == '\n' && count < MAXBUF) {
	 inpbuf[count] = '\0';
	 return(count);
      }

      /* If line too long, restart. */
      if(c == '\n') {
	 count = 0;
	 printf("smallsh: input line too long\n");
         doprompt(p);
      }
   }
}


int inarg(char c) {
/*
 * inarg()
 * Determine if a character can be part of an ordinary argument or special character.
 */

   char *wrk;

   for(wrk = special; *wrk; ++wrk) {
      if(c == *wrk)
	 return(false);
   }

   return(true);

}


int gettok(char **outptr) {
/*
 * gettok()
 * Tokenize the string reading process.
 */

   int type;

   /* set the outptr string to tok */
   *outptr = tok;

   /* strip white space from the buffer containing the tokens */
   while(*ptr == '\t' || *ptr == ' ')
      ++ptr;

   *tok++ = *ptr;

   /* set the variable depending on the token in the buffer */
   switch(*ptr++) {
   case '\n':
      type = SH_SYM_EOL;
      break;
   case '<':
      type = SH_SYM_LT;
      break;
   case '>':
      type = SH_SYM_GT;
      break;
   case '&':
      type = SH_SYM_AMP;
      break;
   case ';':
      type = SH_SYM_SC;
      break;
   default:
      type = SH_SYM_ARG;
      /* keep reading valid ordinary characters */
      while(inarg(*ptr))
      *tok++ = *ptr++;
   }
   *tok++ = '\0';

   return(type);

}


void procline(void) {
/*
 * procline()
 * Parse a command line using gettok(), and construct an argument list.
 * Call docommand() when it reaches a semicolon or newline. The command
 * must have alredy been read by userin before calling this function.
 */

   char *arg[MAXARG + 1];       /* pointer array for runcommand */
   sh_task_whence type;         /* SH_TASK_FORE or SH_TASK_BACK */
   int narg = 0;                /* number of arguments so far */  
   int toktype;                 /* type of token in command */
   char *oup = NULL;            /* output redirection file */
   char *inp = NULL;            /* input redirection file */

   while(true) {
      /* Take action according to token type. */
      toktype = gettok(&arg[narg]);
      type = SH_TASK_FORE;
      switch(toktype) {
      case SH_SYM_ARG:
         if(narg < MAXARG)
            ++narg;
         break;
      case SH_SYM_AMP:
         type = SH_TASK_BACK;
      case SH_SYM_EOL:
      case SH_SYM_SC:
         if(narg != 0) {
            arg[narg] = NULL;
            runcommand(arg, type, inp, oup);
         }
         if(toktype == SH_SYM_EOL)
            return;
         narg = 0;
         break;
      case SH_SYM_LT:
         gettok(&inp);
         break;
      case SH_SYM_GT:
         gettok(&oup);
         break;
      }
   }

}

