/* parse a compound list * * The term compound-list is derived from the grammar in 3.10; it is * equivalent to a sequence of lists, separated by <newline>s, that * can be preceded or followed by an arbitrary number of <newline>s. * ----------------------------------------------------------------------- */ union node *parse_compound_list(struct parser *p) { union node *list; union node **nptr; tree_init(list, nptr); /* skip arbitrary newlines */ while(parse_gettok(p, P_DEFAULT) & T_NL); p->pushback++; for(;;) { /* try to parse a list */ *nptr = parse_list(p); /* skip arbitrary newlines */ while(p->tok & T_NL) parse_gettok(p, P_DEFAULT); p->pushback++; /* no more lists */ if(*nptr == NULL) break; /* parse_list already returns a list, so we must skip over it to get &lastnode->next */ while(*nptr) tree_next(nptr); } return list; }
/* 3.9.4.1 parse a grouping compound * * The format for grouping commands is a s follows: * * (compound-list) Execute compound-list in a subshell environment; * Variable assignments and built-in commands that * affect the environment shall not remain in effect * after the list finishes. * * { compound-list;} Execute compound-list in the current process * environment. * * ----------------------------------------------------------------------- */ union node *parse_grouping(struct parser *p) { enum tok_flag tok; union node **rptr; union node *grouping; union node *compound_list; /* return NULL on empty compound */ grouping = NULL; if(!(tok = parse_expect(p, P_DEFAULT, T_BEGIN|T_LP, NULL))) return NULL; /* parse compound content and create a compound node if there are commands */ if((compound_list = parse_compound_list(p))) { grouping = tree_newnode(tok == T_BEGIN ? N_CMDLIST : N_SUBSHELL); grouping->ngrp.cmds = compound_list; } /* expect the appropriate ending token */ if(!parse_expect(p, P_DEFAULT, tok << 1, grouping)) return NULL; if(grouping) { tree_init(grouping->ngrp.rdir, rptr); /* now any redirections may follow */ while(parse_gettok(p, P_DEFAULT) & T_REDIR) tree_move(p->tree, rptr); p->pushback++; } return grouping; }
/* A list is a sequence of one or more AND-OR-lists separated by the * operators * * ; & * * and optionally terminated by * * ; & <newline> * * ----------------------------------------------------------------------- */ union node *parse_list(struct parser *p) { union node *list; union node **nptr; enum tok_flag tok; /* keep looking for and-or lists */ tree_init(list, nptr); while((*nptr = parse_and_or(p))) { tok = parse_gettok(p, P_DEFAULT); /* <newline> terminates the list and eats the token */ if(tok & T_NL) return list; /* there must be & or ; after the and-or list, otherwise the list will be terminated */ if(!(tok & (T_SEMI | T_BGND))) { p->pushback++; break; } /* & causes async exec of preceding and-or list */ if(tok & T_BGND) (*nptr)->nlist.bgnd = 1; /* now check for another and-or list */ tree_next(nptr); } return list; }
static int parse_expect(struct parser *p, int tempflags, int toks) { int n, i, f; if (parse_gettok(p, tempflags) & toks) return p->tok; if (p->tok) { for (i = 0, n = p->tok; n > 1; i++, n >>= 1); fprintf(stderr, "unexpected token '%s', expecting '", tokens[i].name); } else {
/* parse a compound- or a simple-command * (pipeline and lists are done outside this) * ----------------------------------------------------------------------- */ union node *parse_command(struct parser *p, int tempflags) { enum tok_flag tok; union node *command; union node **rptr; tok = parse_gettok(p, tempflags); switch(tok) { /* T_FOR begins an iteration statement */ case T_FOR: command = parse_for(p); break; /* T_IF begins a case match statement */ case T_CASE: command = parse_case(p); break; /* T_IF begins a conditional statement */ case T_IF: command = parse_if(p); break; /* T_WHILE/T_UNTIL begin a for-loop statement */ case T_WHILE: case T_UNTIL: command = parse_loop(p); break; /* T_LP/T_BEGIN start a grouping compound */ case T_LP: case T_BEGIN: p->pushback++; command = parse_grouping(p); break; /* handle simple commands */ case T_NAME: case T_WORD: case T_REDIR: case T_ASSIGN: p->pushback++; command = parse_simple_command(p); break; /* it wasn't a compound command, return now */ default: p->pushback++; return NULL; } if(command) { /* they all can have redirections, so parse these now */ rptr = &command->ncmd.rdir; /* * in the case of a simple command there are maybe already * redirections in the list (because in a simple command they * can appear between arguments), so skip to the end of the list. */ while(*rptr) tree_next(rptr); while(parse_gettok(p, P_DEFAULT) & T_REDIR) tree_move(p->tree, rptr); } p->pushback++; return command; }
/* main loop, parse lines into trees and execute them * ----------------------------------------------------------------------- */ void sh_loop(void) { struct parser p; union node *list; stralloc cmd; /* if we're in interactive mode some additional stuff is to be initialized */ if(source->mode & SOURCE_IACTIVE) history_load(); stralloc_init(&cmd); parse_init(&p, P_DEFAULT); while(!(parse_gettok(&p, P_DEFAULT) & T_EOF)) { p.pushback++; parse_lineno = source->line; var_setvint("LINENO", parse_lineno, V_DEFAULT); /* launch the parser to get a complete command */ if((list = parse_list(&p))) { struct eval e; if(source->mode & SOURCE_IACTIVE) { tree_printlist(list, &cmd, NULL); stralloc_catc(&cmd, '\n'); stralloc_nul(&cmd); history_set(cmd.s); cmd.s = NULL; history_advance(); } #ifdef DEBUG /* debug_list(list, 0); buffer_putnlflush(fd_err->w);*/ #endif /* DEBUG */ eval_push(&e, E_JCTL); eval_tree(&e, list, E_ROOT|E_LIST); sh->exitcode = eval_pop(&e); stralloc_zero(&cmd); tree_free(list); } else if(!(p.tok & (T_NL | T_SEMI | T_BGND))) { /* we have a parse error */ if(p.tok != T_EOF) parse_error(&p, 0); /* exit if not interactive */ if(!(source->mode & SOURCE_IACTIVE)) sh_exit(1); /* ..otherwise discard the input buffer */ source_flush(); p.pushback = 0; } if(p.tok & (T_NL|T_SEMI|T_BGND)) p.pushback = 0; /* reset prompt */ prompt_number = 0; } }