/* * command_line_parse(parsestate, in_parens) * * Parses a command line from 'input' into a linked list of command_t * structures. The head of the linked list is returned, or NULL is * returned on error. * If 'in_parens != 0', then command_line_parse() is being called recursively * from command_parse(). A right parenthesis should end the "command line". * But at the top-level command line, when 'in_parens == 0', a right * parenthesis is an error. */ command_t * command_line_parse(parsestate_t *parsestate, int in_parens) { command_t *prev_cmd = NULL; command_t *head = NULL; command_t *cmd; token_t token; int r; // This loop has to deal with command syntax in a smart way. // Here's a non exhaustive list of the behavior it should implement // when 'in_parens == 0'. // COMMAND => OK // COMMAND ; => OK // COMMAND && COMMAND => OK // COMMAND && => error (can't end with &&) // COMMAND ) => error (but OK if "in_parens") while (1) { // Parse the next command. cmd = command_parse(parsestate); if (!cmd) // Empty commands are errors. goto error; // Link the command up to the command line. if (prev_cmd) prev_cmd->next = cmd; else head = cmd; prev_cmd = cmd; // EXERCISE: Fetch the next token to see how to connect this // command with the next command. React to errors with // 'goto error'. The ";" and "&" tokens may require special // handling, since unlike other special tokens, they can end // the command line. parse_gettoken(parsestate, &token); if (token.type == TOK_NORMAL) printf("parse token error"); cmd->controlop = token.type; /* Your code here */ switch (token.type) { case TOK_SEMICOLON: case TOK_AMPERSAND: parse_gettoken(parsestate, &token); if (token.type != TOK_END) parse_ungettoken(parsestate); else goto done; break; case TOK_CLOSE_PAREN: if (in_parens == PARENS_IN) { cmd->controlop = CMD_END; goto done; } else goto error; break; case TOK_END: goto done; default: ; } } done: // EXERCISE: Check that the command line ends properly. /* if (in_parens == PARENS_OUT) { if (strlen(parsestate->position) != 0) { printf("strlen %d\n", strlen(parsestate->position)); perror("line end error"); exit(1); } }*/ /* Your code here */ return head; error: command_free(head); return NULL; }
command_t * command_parse(parsestate_t *parsestate) { int i = 0; command_t *cmd = command_alloc(); if (!cmd) { printf("cmd alloc error\n"); return NULL; } while (1) { // EXERCISE: Read the next token from 'parsestate'. // Normal tokens go in the cmd->argv[] array. // Redirection file names go into cmd->redirect_filename[]. // Open parenthesis tokens indicate a subshell command. // Other tokens complete the current command // and are not actually part of it; // use parse_ungettoken() to save those tokens for later. // There are a couple errors you should check. // First, be careful about overflow on normal tokens. // Each command_t only has space for MAXTOKENS tokens in // 'argv'. If there are too many tokens, reject the whole // command. // Second, redirection tokens (<, >, 2>) must be followed by // TOK_NORMAL tokens containing file names. // Third, a parenthesized subcommand can't be part of the // same command as other normal tokens. For example, // "echo ( echo foo )" and "( echo foo ) echo" are both errors. // (You should figure out exactly how to check for this kind // of error. Try interacting with the actual 'bash' shell // for some ideas.) // 'goto error' when you encounter one of these errors, // which frees the current command and returns NULL. // Hint: An open parenthesis should recursively call // command_line_parse(). The command_t structure has a slot // you can use for parens; figure out how to use it! if (i > MAXTOKENS) goto error; token_t token; parse_gettoken(parsestate, &token); switch (token.type) { case TOK_NORMAL: //printf("%s len: %d", token.buffer, strlen(token.buffer)); if (cmd->subshell != NULL) { parse_ungettoken(parsestate); goto done; } if (strlen(token.buffer) != 0) { cmd->argv[i] = strdup(token.buffer); i++; } break; case TOK_LESS_THAN: //printf("input red\n"); parse_gettoken(parsestate, &token); if (token.type != TOK_NORMAL) goto error; cmd->redirect_filename[0] = strdup(token.buffer); break; case TOK_GREATER_THAN: //printf("output red\n"); parse_gettoken(parsestate, &token); if (token.type != TOK_NORMAL) goto error; cmd->redirect_filename[1] = strdup(token.buffer); break; case TOK_2_GREATER_THAN: //printf("stderr red\n"); parse_gettoken(parsestate, &token); if (token.type != TOK_NORMAL) goto error; cmd->redirect_filename[2] = strdup(token.buffer); break; case TOK_OPEN_PAREN: cmd->subshell = command_line_parse(parsestate, PARENS_IN); //goto done; break; case TOK_CLOSE_PAREN: default: parse_ungettoken(parsestate); goto done; } } done: // NULL-terminate the argv list cmd->argv[i] = 0; // EXERCISE: Make sure you return the right return value! if (i == 0 && cmd->subshell == NULL) { /* Empty command */ command_free(cmd); return NULL; } else return cmd; error: command_free(cmd); return NULL; }
/* * cmd_line_parse(parsestate, in_parens) * * Parses a command line from 'input' into a linked list of command_t * structures. The head of the linked list is returned, or NULL is * returned on error. * If 'in_parens != 0', then cmd_line_parse() is being called recursively * from cmd_parse(). A right parenthesis should end the "command line". * But at the top-level command line, when 'in_parens == 0', a right * parenthesis is an error. */ command_t * cmd_line_parse(parsestate_t *parsestate, int in_parens) { command_t *prev_cmd = NULL; command_t *head = NULL; command_t *cmd; token_t token; int r; // This loop has to deal with command syntax in a smart way. // Here's a nonexhaustive list of the behavior it should implement // when 'in_parens == 0'. // COMMAND => OK // COMMAND ; => OK // COMMAND && COMMAND => OK // COMMAND && => error (can't end with &&) // COMMAND ) => error (but OK if "in_parens") while (1) { // Parse the next command. cmd = cmd_parse(parsestate); if (!cmd) // Empty commands are errors. goto error; if (prev_cmd) prev_cmd->next = cmd; else head = cmd; prev_cmd = cmd; // Fetch the next token to see how to connect this // command with the next command. React to errors with // 'goto error'. The ";" and "&" tokens require special // handling, since unlike other special tokens, they can end // the command line. parse_gettoken(parsestate, &token); switch (token.type) { case TOK_DOUBLEAMP: case TOK_DOUBLEPIPE: case TOK_PIPE: cmd->controlop = token.type; break; case TOK_SEMICOLON: case TOK_AMPERSAND: cmd->controlop = token.type; parse_gettoken(parsestate, &token); if (token.type == TOK_END || token.type == TOK_CLOSE_PAREN) goto ender; parse_ungettoken(parsestate); break; ender: case TOK_END: case TOK_CLOSE_PAREN: if ((token.type == TOK_END) == (in_parens != 0)) goto error; goto done; default: goto error; } } done: // Check that the command line ends properly. if (prev_cmd && prev_cmd->controlop != CMD_END && prev_cmd->controlop != CMD_SEMICOLON && prev_cmd->controlop != CMD_BACKGROUND) goto error; return head; error: cmd_free(head); return NULL; }
command_t * command_parse(parsestate_t *parsestate) { int i = 0; command_t *cmd = command_alloc(); if (!cmd) return NULL; int flag_in=0; int flag_out=0; int flag_err=0; while (1) { // EXERCISE: Read the next token from 'parsestate'. // Normal tokens go in the cmd->argv[] array. // Redirection file names go into cmd->redirect_filename[]. // Open parenthesis tokens indicate a subshell command. // Other tokens complete the current command // and are not actually part of it; // use parse_ungettoken() to save those tokens for later. // There are a couple errors you should check. // First, be careful about overflow on normal tokens. // Each command_t only has space for MAXTOKENS tokens in // 'argv'. If there are too many tokens, reject the whole // command. // Second, redirection tokens (<, >, 2>) must be followed by // TOK_NORMAL tokens containing file names. // Third, a parenthesized subcommand can't be part of the // same command as other normal tokens. For example, // "echo ( echo foo )" and "( echo foo ) echo" are both errors. // (You should figure out exactly how to check for this kind // of error. Try interacting with the actual 'bash' shell // for some ideas.) // 'goto error' when you encounter one of these errors, // which frees the current command and returns NULL. // Hint: An open parenthesis should recursively call // command_line_parse(). The command_t structure has a slot // you can use for parens; figure out how to use it! token_t token; memset(&token, 0, sizeof(token)); parse_gettoken(parsestate, &token); switch (token.type) { case TOK_NORMAL: if(flag_out){ flag_out=0; cmd->redirect_filename[1]=strdup(token.buffer); break; } else if(flag_in){ flag_in=0; cmd->redirect_filename[0]=strdup(token.buffer); break; } else if(flag_err){ flag_err=0; cmd->redirect_filename[2]=strdup(token.buffer); break; } if(i==MAXTOKENS) goto error; cmd->argv[i] = strdup(token.buffer); i++; break; case TOK_PIPE: cmd->controlop=CMD_PIPE; parse_ungettoken(parsestate); goto done; case TOK_DOUBLEPIPE: cmd->controlop=CMD_OR; parse_ungettoken(parsestate); goto done; case TOK_AMPERSAND: cmd->controlop=CMD_BACKGROUND; parse_ungettoken(parsestate); goto done; case TOK_DOUBLEAMP: cmd->controlop=CMD_AND; parse_ungettoken(parsestate); goto done; case TOK_SEMICOLON: cmd->controlop=CMD_SEMICOLON; parse_ungettoken(parsestate); goto done; case TOK_OPEN_PAREN: cmd->subshell=command_line_parse(parsestate,1); break; case TOK_CLOSE_PAREN: parse_ungettoken(parsestate); goto done; case TOK_END: parse_ungettoken(parsestate); cmd->controlop=CMD_END; goto done; case TOK_ERROR: goto error; case TOK_GREATER_THAN: flag_out=1; break; case TOK_LESS_THAN: flag_in=1; break; case TOK_2_GREATER_THAN: flag_err=1; break; default: parse_ungettoken(parsestate); goto done; } } done: // NULL-terminate the argv list cmd->argv[i] = 0; // EXERCISE: Make sure you return the right return value! if (i == 0 && cmd->subshell==NULL) { /* Empty command */ command_free(cmd); return NULL; } else return cmd; error: command_free(cmd); return NULL; }
command_t * cmd_parse(parsestate_t *parsestate) { int i = 0; command_t *cmd = cmd_alloc(); if (!cmd) return NULL; while (1) { // Read the next token from 'parsestate'. token_t token; parse_gettoken(parsestate, &token); // Normal tokens go in the cmd->argv[] array. // Redirection file names go into cmd->redirect_filename[]. // Open parenthesis tokens indicate a subshell command. // Other tokens complete the current command // and are not actually part of it; // we use parse_ungettoken() to save those tokens for later. switch (token.type) { case TOK_NORMAL: // Overflow on normal tokens if (i >= MAXTOKENS || cmd->subshell) goto error; cmd->argv[i] = strdup(token.buffer); i++; break; case TOK_LESS_THAN: case TOK_GREATER_THAN: case TOK_2_GREATER_THAN: { int fd = token.type - TOK_LESS_THAN; // Can't redirect nothing if (i == 0 && !cmd->subshell) goto error; // Redirection tokens (<, >, 2>) must be followed by // TOK_NORMAL tokens containing file names. parse_gettoken(parsestate, &token); if (token.type != TOK_NORMAL) goto error; free(cmd->redirect_filename[fd]); cmd->redirect_filename[fd] = strdup(token.buffer); break; } case TOK_OPEN_PAREN: // EXERCISE: Handle parentheses. // NOTE the following: // --Parentheses in the shell do not act like // mathematical parentheses. // --In particular, a parenthesized subcommand cannot // be part of the same command as other normal tokens. // For example, "echo ( echo foo )", // "( echo foo ) echo", and // "(echo foo) (echo foo)" // // are all syntax errors. (You should figure out // exactly how to check for this kind of error. Try // interacting with the actual 'bash' shell for some // ideas.) // // Some hints for the code: // --An open parenthesis should recursively call // cmd_line_parse(). The command_t structure has a slot // you can use. // --'goto error' when you encounter an error, // which frees the current command and returns NULL. // --You will need to adjust the logic in the "done:" // block, below. Otherwise, a subshell will be regarded as // an error (why?) // --You don't have to write a lot of code here, but // you will have to understand how the pieces that you // have been given fit together. (It may be helpful to // look over cmdparse.h again.) /* Your code here. */ cmd->subshell = cmd_line_parse(parsestate, 1); if(!cmd->subshell){ goto error; } break; default: parse_ungettoken(parsestate); goto done; } } done: // NULL-terminate the argv list cmd->argv[i] = 0; if (i == 0 && !cmd->subshell) { /* Empty command */ cmd_free(cmd); return NULL; } else return cmd; error: cmd_free(cmd); return NULL; }