/**************************************************************/
  /*                                                            */
  /*      UNIX 1 / WS 92/93       Gruppe  ux803                 */
  /*      3. Uebung - Aufgabe 2 - parser.c                      */
  /*                                                            */
  /*      Vorname     Name        Matrikelnr.                   */
  /*     ---------   -------     -------------                  */
  /*      Dietmar     Dierks        125761                      */
  /*      Roman       Czyborra      127221                      */
  /*      Torsten     Buller        117894                      */
  /*      Gerasimos   Paliatsaras   140956                      */
  /*                                                            */
  /**************************************************************/

#include <stdio.h>
#include "parser.h"

/* clean-up routine:
 * -----------------
 */

void forget (cmd)
     struct kommando *cmd;
{
  static char **p;
  
  if (!cmd) return;
  if (cmd -> num_tok1) { for (p = cmd -> token_1; *p; ++ p) free (*p);
			 free (cmd -> token_1); }
  if (cmd -> num_tok2) { for (p = cmd -> token_2; *p; ++ p) free (*p);
			 free (cmd -> token_2); }
  if (cmd -> inp_tok) free (cmd -> inp_tok);
  if (cmd -> out_tok) free (cmd -> out_tok);
  if (cmd -> next)    forget (cmd -> next);
  cfree (cmd);
}


/* variables made global for auxiliary routines:
 * ---------------------------------------------
 */
static char
  *toread, /* points to the next char to read in *buf */
  ***tok;  /* points to the currently built token vector */
int
  *num;    /* points to length of *tok */


/* auxiliary routines:
 * -------------------
 */

static int isblank (c)
     char c;
{
  return (strchr(" \t\v\f\r\n", c));
}

static int isfunc (c)
     char c;
{
  return (strchr ("<>|&;", c));
}

static int istok (c)
     char c;
{
  return (! strchr(" <>|&;\t\v\f\r\n",c));
}


static int blktok ()
{
  static char chr;

  while ((chr = *toread) && isblank (chr))
    ++ toread;

  return (chr && ! isfunc (chr));
} /* blktok () skips blanks and looks for a token
   * its return value says if there is a character 
   * after some or no blanks that is not a func
   */

static char *token()
{
  static char chr, *ptr;
  int len;

  ptr = toread;
  for (len = 0; (chr = * ptr++) && istok (chr); ++len);

  ptr = (char *) malloc ((len + 1) * sizeof (char));
  if (ptr)
    {
      strncpy (ptr, toread, len);
      toread += len;
      ptr [len] = '\0';
    }
  return ptr;

}/* token() duplicates one token into the heap
  * toread must point to a real token whenever token() is called
  */

static struct kommando * new ()
{
  static struct kommando * n;

  n = (struct kommando *) calloc (1, sizeof (struct kommando));
  if (n) 
    num = & n->num_tok1, 
    tok = & n->token_1;
  return n;

} /* new() generates and initializes a new struct kommando */


static int invalid0c(s)
     struct kommando *s;
{
  if (s->num_tok1) return (s->is_pipe && !s->num_tok2);
  else return (s->is_pipe || s->inp_tok || s->out_tok);
} /* checks, if *s is an invalid null command */


/* main routine:
 * =============
 */

struct kommando * parseline (buf)
     char *buf;
{
  static char
    chr,
    * error,
    AMBINP [] = "ambiguous input redirection",
    AMBOUT [] = "ambiguous output redirection",
    MISINP [] = "missing name for input",
    MISOUT [] = "missing name for output",
    MULPIP [] = "can't handle multiple pipe",
    NULCOM [] = "invalid null command",
    MALERR [] = "out of memory";
  
  static struct kommando *result, *cmd;
  
  error = NULL;
  
  result = cmd = new ();
  if (!cmd)
    error = MALERR;
  toread = buf;
  
  while (!error && (chr = *toread))
    if (istok (chr))
      {
	if (*num)
	  *tok = (char **) realloc (*tok, (*num + 2) * sizeof (char *));
	else
	  *tok = (char **) malloc (2 * sizeof (char *));
	if (*tok)
	  (*tok) [*num] = token (),
	  (*tok) [++*num] = NULL;
	else
	  error = MALERR;
      }
    else
      {
	++ toread;
	switch (chr)
	  {
	  case '<':
	    if (cmd->inp_tok || cmd->is_pipe) 
	      error = AMBINP;
	    else if (!blktok ()) 
	      error = MISINP;
	    else
	      cmd->inp_tok = token ();
	    break;	    
	  case '>':
	    if (cmd->out_tok)
	      error = AMBOUT;
	    else if (!blktok ())
	      error = MISOUT;
	    else
	      cmd->out_tok = token ();
	    break;
	  case '|':
	    if (cmd->is_pipe++)
	      error = MULPIP;
	    else if (cmd->out_tok)
	      error = AMBOUT;
	    else if (!*num)
	      error = NULCOM;
	    else
	      num = & cmd->num_tok2,
	      tok = & cmd->token_2;
	    break;
	  case '&':
	    cmd -> is_ampersand = 1;
	    /* flow through */
	  case ';':
	    if (invalid0c (cmd))
	      error = NULCOM;
	    else
	      {
		cmd = cmd -> next = new ();
		if (! cmd)
		  error = MALERR;
	      }
	  }
      }      
  
  if (!error && invalid0c (cmd))
    error = NULCOM;
  if (error) 
    {
      fprintf (stderr, "%s\n", error);
      forget(result);
      return NULL;
    }
  else
    return result;
}