//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 *parseCommand(token **lstHead) { CMD *cmd = parseAndOr(lstHead); if(cmd->type == ERROR) { //propagate an error return cmd; } //check to make sure that end of linked list not reached if(cmd && *lstHead && ((*lstHead)->type == SEP_END || (*lstHead)->type == SEP_BG)) { CMD *andorCMD = cmd; cmd = mallocCMD(); cmd->type = (*lstHead)->type; cmd->left = andorCMD; *lstHead = (*lstHead)->next; if(*lstHead) { cmd->right = parseCommand(lstHead); if(cmd->right->type == ERROR) { //propagate along an error return cmd->right; } } } 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; }
//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 SIMPLE statement, returning command struct //modifies global variable t CMD *simple() { CMD *cmd; cmd = mallocCMD(); cmd->type = SIMPLE; while (t && cmd != NULL) { if (t->type == SIMPLE) { (cmd->argc)++; cmd->argv = realloc(cmd->argv, sizeof(char*) * (cmd->argc + 1)); cmd->argv[cmd->argc] = 0; cmd->argv[cmd->argc - 1] = malloc(strlen(t->text) + 1); strcpy(cmd->argv[cmd->argc - 1], t->text); t = t->next; } else if (ISREDOUT(t->type) || ISREDIN(t->type)) cmd = redirect(cmd); else break; } if (!cmd || !cmd->argc) return 0; else 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; }
/* Makes a command with type ERROR as an error-handling mechanism * * Usage notes: Memory must be freed with freeCMD(). Error message is stored in * argv[0], for use by error handler in outputting messages. * * @message = error message */ CMD* make_error_cmd(char* message) { CMD* error_cmd = mallocCMD(); error_cmd->type = ERROR; error_cmd->argv = realloc(error_cmd->argv, 2 * sizeof(char*)); error_cmd->argv[0] = strdup(message); error_cmd->argv[1] = NULL; return error_cmd; }
CMD *errorCMD(CMD *toFree) { if(toFree) { freeCMD(toFree); } CMD *cmd = mallocCMD(); cmd->type = ERROR; return cmd; }
CMD *parsePipeline(token **lstHead) { CMD *cmd = parseStage(lstHead); if(cmd->type == ERROR) { //propagate an error return cmd; } //check to make sure that end of linked list not reached and cmd not NULL! if(cmd && *lstHead && ISPIPE((*lstHead)->type)) { CMD *stageCMD = cmd; cmd = mallocCMD(); cmd->type = (*lstHead)->type; cmd->left = stageCMD; *lstHead = (*lstHead)->next; if(*lstHead == NULL) { //error..incomplete pipeline! fprintf(stderr,"%s\n","Error: Incomplete pipeline command"); return errorCMD(cmd); } cmd->right = parsePipeline(lstHead); if(cmd->right == NULL) { //case of no right argument for pipe fprintf(stderr,"%s\n","Error: Incomplete pipeline command"); return errorCMD(cmd); } if(cmd->right->type == ERROR) { //propagate along an error return cmd->right; } if(stageCMD->toFile != NULL || cmd->right->fromFile != NULL) { //case of multiple redirections involving a pipeline fprintf(stderr,"%s\n","Error: Multiple redirections"); return errorCMD(cmd); } } return cmd; }
CMD *parseAndOr(token **lstHead) { CMD *cmd = parsePipeline(lstHead); if(cmd->type == ERROR) { //propagate an error return cmd; } //check to make sure that end of linked list not reached if(cmd && *lstHead && ((*lstHead)->type == SEP_AND || (*lstHead)->type == SEP_OR)) { CMD *pipelineCMD = cmd; cmd = mallocCMD(); cmd->type = (*lstHead)->type; cmd->left = pipelineCMD; *lstHead = (*lstHead)->next; if(*lstHead == NULL) { //error..incomplete and/or fprintf(stderr,"%s\n","Error: Incomplete and/or command"); return errorCMD(cmd); } cmd->right = parseAndOr(lstHead); if(cmd->right == NULL) { //case of no right arguments for and/or fprintf(stderr,"%s\n","Error: Incomplete and/or command"); return errorCMD(cmd); } if(cmd->right->type == ERROR) { //propagate along an error return cmd->right; } } 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; }
CMD *parseStage(token **lstHead) { CMD *cmd = parseSimple(lstHead); if(cmd && cmd->type == ERROR) { //propagate along an error return cmd; } /*if(*lstHead && (*lstHead)->type == PAR_RIGHT) { //right parenthesis before left...error! fprintf(stderr,"%s\n","Error: Right parenthesis before left"); return errorCMD(cmd); }*/ if(*lstHead && (*lstHead)->type == PAR_LEFT) { if(cmd) { //error..subcommand after command fprintf(stderr,"%s\n","Error: Subcommand after command"); return errorCMD(cmd); } *lstHead = (*lstHead)->next; cmd = mallocCMD(); if(*lstHead == NULL) { //error..invalid subcommand fprintf(stderr,"%s\n","Error: Invalid subcommand"); return errorCMD(cmd); } cmd->type = SUBCMD; cmd->left = parseCommand(lstHead); if(cmd->left->type == ERROR) { //propagate error return cmd->left; } if(*lstHead == NULL || ((*lstHead)->type != PAR_RIGHT && !IS_RED((*lstHead)->type))) { //error...parens incorrectly nested fprintf(stderr,"%s\n","Error: Incorrect parenthesis nesting"); return errorCMD(cmd); } *lstHead = (*lstHead)->next; if(*lstHead && IS_RED((*lstHead)->type)) { //redirection found cmd = parseRedirect(lstHead,cmd); if(cmd->type == ERROR) { //if error occurred in parseRedirect return cmd; } } } return cmd; }
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; }