/* ParseCommandLine2.h */
#include <stdio.h>
#include <strings.h> /* for strdup */

/* the states */
#define COMMAND 1
#define ARG 2
#define INFILE 3
#define OUTFILE 4
#define UNDEF 5

struct Command {
  char *command;
  char *args[11];
  int numargs;
};

struct CommandData {
  struct Command TheCommands[20];  /* the commands to be
          executed.  TheCommands[0] is the first command
          to be executed.  Its output is piped to
          TheCommands[1], etc. */
  int numcommands; /* the number of commands in the above array */
  char *infile;   /* the file for input redirection, NULL if none */
  char *outfile;  /* the file for output redirection, NULL if none */
  int  background;  /* 0 if process is to run in foreground, 1 if in background */
};


int IsAlphaNum(char c)  /* is this a valid char for a pathname or filename? */
{
  if (c >= 'A' && c <= 'Z') return 1;
  if (c >= 'a' && c <= 'z') return 1;
  if (c >= '0' && c <= '9') return 1;
  if (c == '.' || c == '_' || c == '/' || c == '-') return 1;
  return 0;
}


/************** COPYWORD **************
  This function copies a word (token) to the appropriate field
  of the struct CommandData data, and resets state. It
  returns 1 if successful, 0 if it detects an error.
  This is called from the function ParseCommandLine
**************************************************/
int CopyWord(char *token, struct CommandData *data, int *state)
{
    switch (*state) {
        case COMMAND: 
             data->TheCommands[data->numcommands].command = strdup(token);
             data->TheCommands[data->numcommands].numargs = 0;
             *state = ARG;
             return 1;
        case ARG: 
             data->TheCommands[data->numcommands].args[data->TheCommands[data->numcommands].numargs] = strdup(token);
             /* this is one of the ugliest lines of code that I have ever written */
             data->TheCommands[data->numcommands].numargs++;
             if (data->TheCommands[data->numcommands].numargs > 10) {
		 fprintf(stderr,"Error, too many arguments\n");
                 return 0;
	      }
              return 1;
        case INFILE: 
             if (data->infile != NULL) {
                 fprintf(stderr,"Error, stdin redirected twice\n");
                 return 0;
	     }
	     data->infile = strdup(token);
             *state = UNDEF;
             return 1;
        case OUTFILE: 
             if (data->outfile != NULL) {
                 fprintf(stderr,"Error, stdout is redirected twice\n");
                 return 0;
	     }
	     data->outfile = strdup(token);
             *state = UNDEF;
             return 1; 
         case UNDEF:
             fprintf(stderr,"Error, bad syntax on the command line\n");
             return 0;
    };
    /* we should never get here */
    fprintf(stderr,"Error in CopyWord\n");
    return 0;
}

/********* PARSECOMMANDLINE ***********************************
   This function parses the commandline line, and populates
   the struct CommandData data.  

   Returns 1 if it successfully parsed the command line,
   Returns 0 if it found an error 

   Logic - it passes through the while loop once for each character
   in the line.
   It copies string names into token, and when it gets to the end,
   it calls CopyWord to copy token to the appropriate field in data based on the
   state.   White spaces are ignored, the < and > characters change
   the state, & sets the background flag to 1.
*************************************************************/
int ParseCommandLine(char *line, struct CommandData *data)
{
    int i, j;
    char token[256];
    int inaword;
    int state;

    i=0;
    inaword = 0;
    state = COMMAND;
    data->numcommands = 0;
    data->TheCommands[0].command=NULL;
    data->infile = data->outfile = NULL;
    data->background = 0;

    while (line[i] != '\0' && line[i] != '\n') {
        if (IsAlphaNum(line[i])) {
            if (inaword) {
	        token[j++]=line[i];
            }
            else { /* starting a new word */
	        j = 0;
                token[j++] = line[i];
                inaword = 1;
            }
        }
        else {  /* not an alphanumeric character */
            if (inaword) { /* we have found the end of a token */
	        token[j]='\0';
                inaword = 0;
                if (CopyWord(token,data,&state)==0)
	             return 0;
	     }
                     
            switch (line[i]) {
                case ' ': 
                    break;
	        case '>': 
                    if (state == OUTFILE || state == INFILE || state == COMMAND) {
                        fprintf(stderr, "Error, bad syntax on the command line\n");
                        return 0;
	            }
                    state = OUTFILE;
	            break;
                case '<': 
                    if (state == OUTFILE || state == INFILE || state == COMMAND) {
                        fprintf(stderr, "Error, bad syntax on the command line\n");
                        return 0;
	            }
                    state = INFILE;
	            break;
	        case '|':
                  if (state == COMMAND || state == INFILE || state == OUTFILE) {
                        fprintf(stderr, "Error, bad syntax on the command line\n");
                        return 0;
	            }
		  data->numcommands++;
                  state = COMMAND;
                  data->TheCommands[data->numcommands].command = NULL;
                  data->TheCommands[data->numcommands].numargs = 0;
                  break;
                   
                case '&': 
                    if (state == OUTFILE || state == INFILE || state == COMMAND) {
                        fprintf(stderr, "Error, bad syntax on the command line\n");    
                        return 0;
	            }
                    data->background = 1;
                    break;
                default : 
                    fprintf(stderr,"Error, invalid character on command line %c\n",line[i]);
                    return 0;
            }; /* end of switch */
        } /* end of else */
        i++;
    } /* end of while loop */
    /* we have gotten to the end of the line, there may still be a
       string that has not been processed */

    if (inaword) {
        token[j]='\0';
        if (CopyWord(token,data,&state)==0) {
            return 0;
	}
    }

    /* do some final error checking */
    if (state == INFILE && data->infile == NULL) {
        fprintf(stderr,"Error, < but no filename\n");
        return 0;
    }
    if (state == OUTFILE && data->outfile == NULL) {
        fprintf(stderr,"Error, > but no filename\n");
        return 0;
    }
    if (state == COMMAND) {
      fprintf(stderr,"Error, bad syntax on command line\n");
      return 0;
    }
      
    if (data->TheCommands[0].command == NULL) {
        fprintf(stderr, "Error, no command\n");
        return 0;
    }
    data->numcommands++;
    return 1;
}





       
  
