//parses an and-or statement, returning command struct //modifies global variable t CMD *andOr() { CMD *cmd = 0 /*output*/, *tmp = 0; //swap cmd = pipeline(); while (t && (t->type == SEP_AND || t->type == SEP_OR)) { if (!cmd) { DIE("Parse: null command\n"); freeCMD(cmd); return 0; } tmp = cmd; cmd = mallocCMD(); cmd->type = t->type; cmd->left = tmp; t = t->next; cmd->right = andOr(); if (!cmd->right) { DIE("Parse: null command\n"); freeCMD(cmd); return 0; } } return cmd; }
//parses a pipeline, returning command struct //modifies global variable t CMD *pipeline() { CMD *cmd = 0, *tmp = 0; cmd = stage(); while (t && ISPIPE(t->type)) { if (!cmd) { DIE("Parse: null command\n"); freeCMD(cmd); return 0; } if (cmd->toType != NONE) { DIE("Parse: two output redirects\n"); freeCMD(cmd); return 0; } tmp = cmd; cmd = mallocCMD(); cmd->type = t->type; cmd->left = tmp; t = t->next; cmd->right = pipeline(); if (!cmd->right) { DIE("Parse: null command\n"); freeCMD(cmd); return 0; } } return cmd; }
//handles redirect statements for stage and simple //input is a command struct, output is same struct with changed i/o CMD *redirect(CMD* cmd) { if (ISREDOUT(t->type)) { if (cmd->toType != NONE) { DIE("Parse: two output redirects\n"); freeCMD(cmd); return 0; } cmd->toType = t->type; t = t->next; if (!t || t->type != SIMPLE) { DIE("Parse: missing filename\n"); freeCMD(cmd); return 0; } cmd->toFile = malloc(strlen(t->text) + 1); strcpy(cmd->toFile, t->text); t = t->next; } else if (t->type == RED_IN || t->type == RED_HERE) { if (cmd->fromType != NONE) { DIE("Parse: two input redirects\n"); freeCMD(cmd); return 0; } cmd->fromType = t->type; t = t->next; if (!t || t->type != SIMPLE) { DIE("Parse: missing filename\n"); freeCMD(cmd); return 0; } if (cmd->fromType == RED_IN) { cmd->fromFile = malloc(strlen(t->text) + 1); strcpy(cmd->fromFile, t->text); } else { cmd->fromFile = hereDoc(t->text); } t = t->next; } return cmd; }
//parses a stage, returning a command struct //modifies global variable t CMD *stage() { CMD *tmp; CMD *cmd = 0; if (!t) return 0; tmp = mallocCMD(); while (t && (ISREDOUT(t->type) || ISREDIN(t->type))) tmp = redirect(tmp); if (!tmp) return 0; else if (!t) { DIE("Parse: null command\n"); return 0; } else if (t->type == PAR_LEFT) { cmd = mallocCMD(); cmd->type = SUBCMD; t = t->next; cmd->left = command(); if (!cmd->left) { DIE("Parse: null command\n"); freeCMD(cmd); return 0; } else if (!t || t->type != PAR_RIGHT) { DIE("Parse: unbalanced parens\n"); freeCMD(cmd); return 0; } else { t = t->next; while(t && cmd && (ISREDOUT(t->type) || ISREDIN(t->type))) cmd = redirect(cmd); } } else cmd = simple(); cmd = merge(cmd, tmp); freeCMD(tmp); return cmd; }
CMD *parseSimple(token **lstHead) { CMD *cmd = mallocCMD(); while((*lstHead) && ((*lstHead)->type == SIMPLE || IS_RED((*lstHead)->type))) { cmd->type = SIMPLE; if((*lstHead)->type == SIMPLE) { //not a redirection cmd->argv[cmd->argc] = copyText((*lstHead)->text); cmd->argc++; cmd->argv = realloc(cmd->argv,(cmd->argc+1)*sizeof(char*)); cmd->argv[cmd->argc] = NULL; *lstHead = (*lstHead)->next; } else { //redirection found cmd = parseRedirect(lstHead,cmd); if(cmd->type == ERROR) { //if error occurred in parseRedirect return cmd; } } } if(cmd && cmd->type == NONE) { //case where no SIMPLE was found freeCMD(cmd); return NULL; } return cmd; }
CMD* make_cmd(token_list** list_ref) { token_list* list = *list_ref; if (!list) return make_error_cmd("could not form command (no tokens)"); CMD* cmd_tree = make_andor(&list); if (cmd_tree->type == ERROR) return cmd_tree; int next_type = type(list); if (next_type == SEP_END || next_type == SEP_BG) { advance(&list); CMD* parent = mallocCMD(); parent->type = next_type; parent->left = cmd_tree; cmd_tree = parent; // See if it is possible to make a subcommand; if not, return to initial // state (because subcommand is optional following ; or & token_list* old_head = list; // Hold onto a copy in case sub command // returns an error CMD* sub_cmd = make_cmd(&list); if (sub_cmd->type == ERROR) { freeCMD(sub_cmd); sub_cmd = NULL; list = old_head; } cmd_tree->right = sub_cmd; } *list_ref = list; return cmd_tree; }
CMD* make_andor(token_list** list_ref) { token_list* list = *list_ref; if (!list) return make_error_cmd("could not form and-or (no tokens)"); CMD* andor_tree = make_pipeline(&list); if (andor_tree->type == ERROR) return andor_tree; int next_type = type(list); if (next_type == SEP_AND || next_type == SEP_OR) { advance(&list); CMD* parent = mallocCMD(); // TODO: make sure all initialized to NULL parent->type = next_type; parent->left = andor_tree; andor_tree = parent; CMD* sub_andor = make_andor(&list); if (sub_andor->type == ERROR) { freeCMD(andor_tree); return sub_andor; } andor_tree->right = sub_andor; } *list_ref = list; return andor_tree; }
CMD* make_pipeline(token_list** list_ref) { token_list* list = *list_ref; if (!list) return make_error_cmd("could not form pipeline (no tokens)"); CMD* pipeline_tree = make_stage(&list); if (pipeline_tree->type == ERROR) return pipeline_tree; int next_type = type(list); if (next_type == PIPE || next_type == PIPE_ERR) { advance(&list); CMD* parent = mallocCMD(); parent->type = next_type; parent->left = pipeline_tree; pipeline_tree = parent; CMD* sub_pipeline = make_pipeline(&list); if (sub_pipeline->type == ERROR) { freeCMD(pipeline_tree); return sub_pipeline; } pipeline_tree->right = sub_pipeline; } *list_ref = list; return pipeline_tree; }
/* The top level function of a recursive descent parser for the grammar defined * in parse.h. Parses a token list into a command structure. * * @list = list of tokens to parse * * Returns: a pointer to the resulting command structure; NULL if errors found */ CMD* parse(token_list* list) { CMD* parse_tree = make_cmd(&list); if (parse_tree->type == ERROR) { assert(parse_tree); ERROR(parse_tree->argv[0]); freeCMD(parse_tree); return NULL; } if (list) { ERROR("could not parse remaining tokens"); freeCMD(parse_tree); return NULL; } return parse_tree; }
CMD *parse(token *list) { CMD *cmd = parseCommand(&list); if(cmd->type == ERROR) { freeCMD(cmd); return NULL; } return cmd; }
CMD *errorCMD(CMD *toFree) { if(toFree) { freeCMD(toFree); } CMD *cmd = mallocCMD(); cmd->type = ERROR; return cmd; }
//takes the i/o commands from cont and puts them in cmd //returns 0 and sends an error if there is a conflict CMD *merge(CMD *cmd, CMD* cont) { if (!cmd || !cont) return 0; if (cont->toType != NONE) { if (cmd->toType != NONE) { DIE("Parse: two output redirects\n"); freeCMD(cmd); return 0; } else { cmd->toType = cont->toType; cmd->toFile = malloc(strlen(cont->toFile) + 1); strcpy(cmd->toFile, cont->toFile); } } if (cont->fromType != NONE) { if (cmd->fromType != NONE) { DIE("Parse: two input redirects\n"); freeCMD(cmd); return 0; } else { cmd->fromType = cont->fromType; cmd->fromFile = malloc(strlen(cont->fromFile) + 1); strcpy(cmd->fromFile, cont->fromFile); } } return cmd; }
//parses a command, returning command struct //modifies global variable t CMD *command() { CMD *cmd = 0 /*output*/, *tmp = 0; //swap variable cmd = andOr(); while (t && (t->type == SEP_END || t->type == SEP_BG)) { if (!cmd) { DIE("Parse: null command\n"); freeCMD(cmd); return 0; } tmp = cmd; cmd = mallocCMD(); cmd->type = t->type; cmd->left = tmp; t = t->next; cmd->right = command(); } return cmd; }
int main(int argc, char** argv) { int nCmd = 1; // Command number char *line; // Initial command line token *list; // Linked list of tokens CMD *cmd; // Parsed command for( ; ; free(line)) { // Prompt for command printf("(%d)$ ", nCmd); fflush(stdout); // Read line if((line = getLine(stdin)) == NULL) { break; // Break on end of file } // Lex line into tokens if((list = tokenize(line)) == NULL) { continue; } if ((cmd = parse(list)) != NULL) // Parsed command? { process(cmd); // Execute command freeCMD(cmd); // Free associated storage nCmd++; // Adjust prompt } freeList(list); // Free token list } return EXIT_SUCCESS; }
CMD* make_stage(token_list** list_ref) { token_list* list = *list_ref; if (!list) return make_error_cmd("could not form stage (no tokens)"); CMD* stage_tree = mallocCMD(); // Current type is NONE int next_type = NONE; // Hold the type of the head token of list bool redir_in = false; // Has there been a previous input redirection? bool redir_out = false; // Loop until hit a pipe or a separator; raise an error if multiple redirect // or both a command and subcommand while ((next_type = type(list)) != -1 && !ISSEP(next_type) && !ISPIPE(next_type) && next_type != PAR_RIGHT) { bool error = false; // Signals whether the current action results in an // error if (next_type == SIMPLE && stage_tree->type != SUBCMD) { int argc = stage_tree->argc + 1; char** argv = stage_tree->argv; argv = realloc(argv, (argc + 1) * sizeof(char*)); // +1 for trailing // NULL required // by freeCMD argv[argc - 1] = strdup(text(list)); argv[argc] = NULL; stage_tree->argc = argc; stage_tree->argv = argv; stage_tree->type = SIMPLE; } else if ((next_type == RED_IN || next_type == RED_HERE) && !redir_in) { error = make_redirect(stage_tree, &redir_in, next_type, &list); } else if (ISREDOUT(next_type) && !redir_out) { error = make_redirect(stage_tree, &redir_out, next_type, &list); } else if (next_type == PAR_LEFT && stage_tree->type == NONE) { // Can't have two subcommands in a stage advance(&list); // Advance past left parentheses CMD* subcmd_tree = make_cmd(&list); error = subcmd_tree->type == ERROR || type(list) != PAR_RIGHT; stage_tree->type = SUBCMD; stage_tree->left = subcmd_tree; } else { error = true; } if (error) { freeCMD(stage_tree); return make_error_cmd("Invalid stage"); } advance(&list); } // Make sure we didn't double-redirect if (ISPIPE(next_type) && redir_out) { freeCMD(stage_tree); return make_error_cmd("Double redirect - pipe and >"); } // Make sure there was actually an argument if (stage_tree->type == NONE) { freeCMD(stage_tree); return make_error_cmd("No arguments for simple command"); } assert(stage_tree->type != NONE); *list_ref = list; return stage_tree; }