| /* make_cmd.c -- Functions for making instances of the various |
| parser constructs. */ |
| |
| /* Copyright (C) 1989-2009 Free Software Foundation, Inc. |
| |
| This file is part of GNU Bash, the Bourne Again SHell. |
| |
| Bash is free software: you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| Bash is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with Bash. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include "config.h" |
| |
| #include <stdio.h> |
| #include "bashtypes.h" |
| #if !defined (_MINIX) && defined (HAVE_SYS_FILE_H) |
| # include <sys/file.h> |
| #endif |
| #include "filecntl.h" |
| #include "bashansi.h" |
| #if defined (HAVE_UNISTD_H) |
| # include <unistd.h> |
| #endif |
| |
| #include "bashintl.h" |
| |
| #include "parser.h" |
| #include "syntax.h" |
| #include "command.h" |
| #include "general.h" |
| #include "error.h" |
| #include "flags.h" |
| #include "make_cmd.h" |
| #include "dispose_cmd.h" |
| #include "variables.h" |
| #include "subst.h" |
| #include "input.h" |
| #include "ocache.h" |
| #include "externs.h" |
| |
| #if defined (JOB_CONTROL) |
| #include "jobs.h" |
| #endif |
| |
| #include "shmbutil.h" |
| |
| extern int line_number, current_command_line_count, parser_state; |
| extern int last_command_exit_value; |
| |
| /* Object caching */ |
| sh_obj_cache_t wdcache = {0, 0, 0}; |
| sh_obj_cache_t wlcache = {0, 0, 0}; |
| |
| #define WDCACHESIZE 60 |
| #define WLCACHESIZE 60 |
| |
| static COMMAND *make_for_or_select __P((enum command_type, WORD_DESC *, WORD_LIST *, COMMAND *, int)); |
| #if defined (ARITH_FOR_COMMAND) |
| static WORD_LIST *make_arith_for_expr __P((char *)); |
| #endif |
| static COMMAND *make_until_or_while __P((enum command_type, COMMAND *, COMMAND *)); |
| |
| void |
| cmd_init () |
| { |
| ocache_create (wdcache, WORD_DESC, WDCACHESIZE); |
| ocache_create (wlcache, WORD_LIST, WLCACHESIZE); |
| } |
| |
| WORD_DESC * |
| alloc_word_desc () |
| { |
| WORD_DESC *temp; |
| |
| ocache_alloc (wdcache, WORD_DESC, temp); |
| temp->flags = 0; |
| temp->word = 0; |
| return temp; |
| } |
| |
| WORD_DESC * |
| make_bare_word (string) |
| const char *string; |
| { |
| WORD_DESC *temp; |
| |
| temp = alloc_word_desc (); |
| |
| if (*string) |
| temp->word = savestring (string); |
| else |
| { |
| temp->word = (char *)xmalloc (1); |
| temp->word[0] = '\0'; |
| } |
| |
| return (temp); |
| } |
| |
| WORD_DESC * |
| make_word_flags (w, string) |
| WORD_DESC *w; |
| const char *string; |
| { |
| register int i; |
| size_t slen; |
| DECLARE_MBSTATE; |
| |
| i = 0; |
| slen = strlen (string); |
| while (i < slen) |
| { |
| switch (string[i]) |
| { |
| case '$': |
| w->flags |= W_HASDOLLAR; |
| break; |
| case '\\': |
| break; /* continue the loop */ |
| case '\'': |
| case '`': |
| case '"': |
| w->flags |= W_QUOTED; |
| break; |
| } |
| |
| ADVANCE_CHAR (string, slen, i); |
| } |
| |
| return (w); |
| } |
| |
| WORD_DESC * |
| make_word (string) |
| const char *string; |
| { |
| WORD_DESC *temp; |
| |
| temp = make_bare_word (string); |
| return (make_word_flags (temp, string)); |
| } |
| |
| WORD_DESC * |
| make_word_from_token (token) |
| int token; |
| { |
| char tokenizer[2]; |
| |
| tokenizer[0] = token; |
| tokenizer[1] = '\0'; |
| |
| return (make_word (tokenizer)); |
| } |
| |
| WORD_LIST * |
| make_word_list (word, wlink) |
| WORD_DESC *word; |
| WORD_LIST *wlink; |
| { |
| WORD_LIST *temp; |
| |
| ocache_alloc (wlcache, WORD_LIST, temp); |
| |
| temp->word = word; |
| temp->next = wlink; |
| return (temp); |
| } |
| |
| COMMAND * |
| make_command (type, pointer) |
| enum command_type type; |
| SIMPLE_COM *pointer; |
| { |
| COMMAND *temp; |
| |
| temp = (COMMAND *)xmalloc (sizeof (COMMAND)); |
| temp->type = type; |
| temp->value.Simple = pointer; |
| temp->value.Simple->flags = temp->flags = 0; |
| temp->redirects = (REDIRECT *)NULL; |
| return (temp); |
| } |
| |
| COMMAND * |
| command_connect (com1, com2, connector) |
| COMMAND *com1, *com2; |
| int connector; |
| { |
| CONNECTION *temp; |
| |
| temp = (CONNECTION *)xmalloc (sizeof (CONNECTION)); |
| temp->connector = connector; |
| temp->first = com1; |
| temp->second = com2; |
| return (make_command (cm_connection, (SIMPLE_COM *)temp)); |
| } |
| |
| static COMMAND * |
| make_for_or_select (type, name, map_list, action, lineno) |
| enum command_type type; |
| WORD_DESC *name; |
| WORD_LIST *map_list; |
| COMMAND *action; |
| int lineno; |
| { |
| FOR_COM *temp; |
| |
| temp = (FOR_COM *)xmalloc (sizeof (FOR_COM)); |
| temp->flags = 0; |
| temp->name = name; |
| temp->line = lineno; |
| temp->map_list = map_list; |
| temp->action = action; |
| return (make_command (type, (SIMPLE_COM *)temp)); |
| } |
| |
| COMMAND * |
| make_for_command (name, map_list, action, lineno) |
| WORD_DESC *name; |
| WORD_LIST *map_list; |
| COMMAND *action; |
| int lineno; |
| { |
| return (make_for_or_select (cm_for, name, map_list, action, lineno)); |
| } |
| |
| COMMAND * |
| make_select_command (name, map_list, action, lineno) |
| WORD_DESC *name; |
| WORD_LIST *map_list; |
| COMMAND *action; |
| int lineno; |
| { |
| #if defined (SELECT_COMMAND) |
| return (make_for_or_select (cm_select, name, map_list, action, lineno)); |
| #else |
| last_command_exit_value = 2; |
| return ((COMMAND *)NULL); |
| #endif |
| } |
| |
| #if defined (ARITH_FOR_COMMAND) |
| static WORD_LIST * |
| make_arith_for_expr (s) |
| char *s; |
| { |
| WORD_LIST *result; |
| WORD_DESC *wd; |
| |
| if (s == 0 || *s == '\0') |
| return ((WORD_LIST *)NULL); |
| wd = make_word (s); |
| wd->flags |= W_NOGLOB|W_NOSPLIT|W_QUOTED|W_DQUOTE; /* no word splitting or globbing */ |
| result = make_word_list (wd, (WORD_LIST *)NULL); |
| return result; |
| } |
| #endif |
| |
| /* Note that this function calls dispose_words on EXPRS, since it doesn't |
| use the word list directly. We free it here rather than at the caller |
| because no other function in this file requires that the caller free |
| any arguments. */ |
| COMMAND * |
| make_arith_for_command (exprs, action, lineno) |
| WORD_LIST *exprs; |
| COMMAND *action; |
| int lineno; |
| { |
| #if defined (ARITH_FOR_COMMAND) |
| ARITH_FOR_COM *temp; |
| WORD_LIST *init, *test, *step; |
| char *s, *t, *start; |
| int nsemi; |
| |
| init = test = step = (WORD_LIST *)NULL; |
| /* Parse the string into the three component sub-expressions. */ |
| start = t = s = exprs->word->word; |
| for (nsemi = 0; ;) |
| { |
| /* skip whitespace at the start of each sub-expression. */ |
| while (whitespace (*s)) |
| s++; |
| start = s; |
| /* skip to the semicolon or EOS */ |
| while (*s && *s != ';') |
| s++; |
| |
| t = (s > start) ? substring (start, 0, s - start) : (char *)NULL; |
| |
| nsemi++; |
| switch (nsemi) |
| { |
| case 1: |
| init = make_arith_for_expr (t); |
| break; |
| case 2: |
| test = make_arith_for_expr (t); |
| break; |
| case 3: |
| step = make_arith_for_expr (t); |
| break; |
| } |
| |
| FREE (t); |
| if (*s == '\0') |
| break; |
| s++; /* skip over semicolon */ |
| } |
| |
| if (nsemi != 3) |
| { |
| if (nsemi < 3) |
| parser_error (lineno, _("syntax error: arithmetic expression required")); |
| else |
| parser_error (lineno, _("syntax error: `;' unexpected")); |
| parser_error (lineno, _("syntax error: `((%s))'"), exprs->word->word); |
| last_command_exit_value = 2; |
| return ((COMMAND *)NULL); |
| } |
| |
| temp = (ARITH_FOR_COM *)xmalloc (sizeof (ARITH_FOR_COM)); |
| temp->flags = 0; |
| temp->line = lineno; |
| temp->init = init ? init : make_arith_for_expr ("1"); |
| temp->test = test ? test : make_arith_for_expr ("1"); |
| temp->step = step ? step : make_arith_for_expr ("1"); |
| temp->action = action; |
| |
| dispose_words (exprs); |
| return (make_command (cm_arith_for, (SIMPLE_COM *)temp)); |
| #else |
| dispose_words (exprs); |
| last_command_exit_value = 2; |
| return ((COMMAND *)NULL); |
| #endif /* ARITH_FOR_COMMAND */ |
| } |
| |
| COMMAND * |
| make_group_command (command) |
| COMMAND *command; |
| { |
| GROUP_COM *temp; |
| |
| temp = (GROUP_COM *)xmalloc (sizeof (GROUP_COM)); |
| temp->command = command; |
| return (make_command (cm_group, (SIMPLE_COM *)temp)); |
| } |
| |
| COMMAND * |
| make_case_command (word, clauses, lineno) |
| WORD_DESC *word; |
| PATTERN_LIST *clauses; |
| int lineno; |
| { |
| CASE_COM *temp; |
| |
| temp = (CASE_COM *)xmalloc (sizeof (CASE_COM)); |
| temp->flags = 0; |
| temp->line = lineno; |
| temp->word = word; |
| temp->clauses = REVERSE_LIST (clauses, PATTERN_LIST *); |
| return (make_command (cm_case, (SIMPLE_COM *)temp)); |
| } |
| |
| PATTERN_LIST * |
| make_pattern_list (patterns, action) |
| WORD_LIST *patterns; |
| COMMAND *action; |
| { |
| PATTERN_LIST *temp; |
| |
| temp = (PATTERN_LIST *)xmalloc (sizeof (PATTERN_LIST)); |
| temp->patterns = REVERSE_LIST (patterns, WORD_LIST *); |
| temp->action = action; |
| temp->next = NULL; |
| temp->flags = 0; |
| return (temp); |
| } |
| |
| COMMAND * |
| make_if_command (test, true_case, false_case) |
| COMMAND *test, *true_case, *false_case; |
| { |
| IF_COM *temp; |
| |
| temp = (IF_COM *)xmalloc (sizeof (IF_COM)); |
| temp->flags = 0; |
| temp->test = test; |
| temp->true_case = true_case; |
| temp->false_case = false_case; |
| return (make_command (cm_if, (SIMPLE_COM *)temp)); |
| } |
| |
| static COMMAND * |
| make_until_or_while (which, test, action) |
| enum command_type which; |
| COMMAND *test, *action; |
| { |
| WHILE_COM *temp; |
| |
| temp = (WHILE_COM *)xmalloc (sizeof (WHILE_COM)); |
| temp->flags = 0; |
| temp->test = test; |
| temp->action = action; |
| return (make_command (which, (SIMPLE_COM *)temp)); |
| } |
| |
| COMMAND * |
| make_while_command (test, action) |
| COMMAND *test, *action; |
| { |
| return (make_until_or_while (cm_while, test, action)); |
| } |
| |
| COMMAND * |
| make_until_command (test, action) |
| COMMAND *test, *action; |
| { |
| return (make_until_or_while (cm_until, test, action)); |
| } |
| |
| COMMAND * |
| make_arith_command (exp) |
| WORD_LIST *exp; |
| { |
| #if defined (DPAREN_ARITHMETIC) |
| COMMAND *command; |
| ARITH_COM *temp; |
| |
| command = (COMMAND *)xmalloc (sizeof (COMMAND)); |
| command->value.Arith = temp = (ARITH_COM *)xmalloc (sizeof (ARITH_COM)); |
| |
| temp->flags = 0; |
| temp->line = line_number; |
| temp->exp = exp; |
| |
| command->type = cm_arith; |
| command->redirects = (REDIRECT *)NULL; |
| command->flags = 0; |
| |
| return (command); |
| #else |
| last_command_exit_value = 2; |
| return ((COMMAND *)NULL); |
| #endif |
| } |
| |
| #if defined (COND_COMMAND) |
| struct cond_com * |
| make_cond_node (type, op, left, right) |
| int type; |
| WORD_DESC *op; |
| struct cond_com *left, *right; |
| { |
| COND_COM *temp; |
| |
| temp = (COND_COM *)xmalloc (sizeof (COND_COM)); |
| temp->flags = 0; |
| temp->line = line_number; |
| temp->type = type; |
| temp->op = op; |
| temp->left = left; |
| temp->right = right; |
| |
| return (temp); |
| } |
| #endif |
| |
| COMMAND * |
| make_cond_command (cond_node) |
| COND_COM *cond_node; |
| { |
| #if defined (COND_COMMAND) |
| COMMAND *command; |
| |
| command = (COMMAND *)xmalloc (sizeof (COMMAND)); |
| command->value.Cond = cond_node; |
| |
| command->type = cm_cond; |
| command->redirects = (REDIRECT *)NULL; |
| command->flags = 0; |
| command->line = cond_node ? cond_node->line : 0; |
| |
| return (command); |
| #else |
| last_command_exit_value = 2; |
| return ((COMMAND *)NULL); |
| #endif |
| } |
| |
| COMMAND * |
| make_bare_simple_command () |
| { |
| COMMAND *command; |
| SIMPLE_COM *temp; |
| |
| command = (COMMAND *)xmalloc (sizeof (COMMAND)); |
| command->value.Simple = temp = (SIMPLE_COM *)xmalloc (sizeof (SIMPLE_COM)); |
| |
| temp->flags = 0; |
| temp->line = line_number; |
| temp->words = (WORD_LIST *)NULL; |
| temp->redirects = (REDIRECT *)NULL; |
| |
| command->type = cm_simple; |
| command->redirects = (REDIRECT *)NULL; |
| command->flags = 0; |
| |
| return (command); |
| } |
| |
| /* Return a command which is the connection of the word or redirection |
| in ELEMENT, and the command * or NULL in COMMAND. */ |
| COMMAND * |
| make_simple_command (element, command) |
| ELEMENT element; |
| COMMAND *command; |
| { |
| /* If we are starting from scratch, then make the initial command |
| structure. Also note that we have to fill in all the slots, since |
| malloc doesn't return zeroed space. */ |
| if (command == 0) |
| { |
| command = make_bare_simple_command (); |
| parser_state |= PST_REDIRLIST; |
| } |
| |
| if (element.word) |
| { |
| command->value.Simple->words = make_word_list (element.word, command->value.Simple->words); |
| parser_state &= ~PST_REDIRLIST; |
| } |
| else if (element.redirect) |
| { |
| REDIRECT *r = element.redirect; |
| /* Due to the way <> is implemented, there may be more than a single |
| redirection in element.redirect. We just follow the chain as far |
| as it goes, and hook onto the end. */ |
| while (r->next) |
| r = r->next; |
| r->next = command->value.Simple->redirects; |
| command->value.Simple->redirects = element.redirect; |
| } |
| |
| return (command); |
| } |
| |
| /* Because we are Bourne compatible, we read the input for this |
| << or <<- redirection now, from wherever input is coming from. |
| We store the input read into a WORD_DESC. Replace the text of |
| the redirectee.word with the new input text. If <<- is on, |
| then remove leading TABS from each line. */ |
| void |
| make_here_document (temp, lineno) |
| REDIRECT *temp; |
| int lineno; |
| { |
| int kill_leading, redir_len; |
| char *redir_word, *document, *full_line; |
| int document_index, document_size, delim_unquoted; |
| |
| if (temp->instruction != r_deblank_reading_until && |
| temp->instruction != r_reading_until) |
| { |
| internal_error (_("make_here_document: bad instruction type %d"), temp->instruction); |
| return; |
| } |
| |
| kill_leading = temp->instruction == r_deblank_reading_until; |
| |
| document = (char *)NULL; |
| document_index = document_size = 0; |
| |
| /* Quote removal is the only expansion performed on the delimiter |
| for here documents, making it an extremely special case. */ |
| redir_word = string_quote_removal (temp->redirectee.filename->word, 0); |
| |
| /* redirection_expand will return NULL if the expansion results in |
| multiple words or no words. Check for that here, and just abort |
| this here document if it does. */ |
| if (redir_word) |
| redir_len = strlen (redir_word); |
| else |
| { |
| temp->here_doc_eof = (char *)xmalloc (1); |
| temp->here_doc_eof[0] = '\0'; |
| goto document_done; |
| } |
| |
| free (temp->redirectee.filename->word); |
| temp->here_doc_eof = redir_word; |
| |
| /* Read lines from wherever lines are coming from. |
| For each line read, if kill_leading, then kill the |
| leading tab characters. |
| If the line matches redir_word exactly, then we have |
| manufactured the document. Otherwise, add the line to the |
| list of lines in the document. */ |
| |
| /* If the here-document delimiter was quoted, the lines should |
| be read verbatim from the input. If it was not quoted, we |
| need to perform backslash-quoted newline removal. */ |
| delim_unquoted = (temp->redirectee.filename->flags & W_QUOTED) == 0; |
| while (full_line = read_secondary_line (delim_unquoted)) |
| { |
| register char *line; |
| int len; |
| |
| line = full_line; |
| line_number++; |
| |
| /* If set -v is in effect, echo the line read. read_secondary_line/ |
| read_a_line leaves the newline at the end, so don't print another. */ |
| if (echo_input_at_read) |
| fprintf (stderr, "%s", line); |
| |
| if (kill_leading && *line) |
| { |
| /* Hack: To be compatible with some Bourne shells, we |
| check the word before stripping the whitespace. This |
| is a hack, though. */ |
| if (STREQN (line, redir_word, redir_len) && line[redir_len] == '\n') |
| goto document_done; |
| |
| while (*line == '\t') |
| line++; |
| } |
| |
| if (*line == 0) |
| continue; |
| |
| if (STREQN (line, redir_word, redir_len) && line[redir_len] == '\n') |
| goto document_done; |
| |
| len = strlen (line); |
| if (len + document_index >= document_size) |
| { |
| document_size = document_size ? 2 * (document_size + len) : len + 2; |
| document = (char *)xrealloc (document, document_size); |
| } |
| |
| /* len is guaranteed to be > 0 because of the check for line |
| being an empty string before the call to strlen. */ |
| FASTCOPY (line, document + document_index, len); |
| document_index += len; |
| } |
| |
| if (full_line == 0) |
| internal_warning (_("here-document at line %d delimited by end-of-file (wanted `%s')"), lineno, redir_word); |
| |
| document_done: |
| if (document) |
| document[document_index] = '\0'; |
| else |
| { |
| document = (char *)xmalloc (1); |
| document[0] = '\0'; |
| } |
| temp->redirectee.filename->word = document; |
| } |
| |
| /* Generate a REDIRECT from SOURCE, DEST, and INSTRUCTION. |
| INSTRUCTION is the instruction type, SOURCE is a file descriptor, |
| and DEST is a file descriptor or a WORD_DESC *. */ |
| REDIRECT * |
| make_redirection (source, instruction, dest_and_filename, flags) |
| REDIRECTEE source; |
| enum r_instruction instruction; |
| REDIRECTEE dest_and_filename; |
| int flags; |
| { |
| REDIRECT *temp; |
| WORD_DESC *w; |
| int wlen; |
| intmax_t lfd; |
| |
| temp = (REDIRECT *)xmalloc (sizeof (REDIRECT)); |
| |
| /* First do the common cases. */ |
| temp->redirector = source; |
| temp->redirectee = dest_and_filename; |
| temp->instruction = instruction; |
| temp->flags = 0; |
| temp->rflags = flags; |
| temp->next = (REDIRECT *)NULL; |
| |
| switch (instruction) |
| { |
| |
| case r_output_direction: /* >foo */ |
| case r_output_force: /* >| foo */ |
| case r_err_and_out: /* &>filename */ |
| temp->flags = O_TRUNC | O_WRONLY | O_CREAT; |
| break; |
| |
| case r_appending_to: /* >>foo */ |
| case r_append_err_and_out: /* &>> filename */ |
| temp->flags = O_APPEND | O_WRONLY | O_CREAT; |
| break; |
| |
| case r_input_direction: /* <foo */ |
| case r_inputa_direction: /* foo & makes this. */ |
| temp->flags = O_RDONLY; |
| break; |
| |
| case r_input_output: /* <>foo */ |
| temp->flags = O_RDWR | O_CREAT; |
| break; |
| |
| case r_deblank_reading_until: /* <<-foo */ |
| case r_reading_until: /* << foo */ |
| case r_reading_string: /* <<< foo */ |
| case r_close_this: /* <&- */ |
| case r_duplicating_input: /* 1<&2 */ |
| case r_duplicating_output: /* 1>&2 */ |
| break; |
| |
| /* the parser doesn't pass these. */ |
| case r_move_input: /* 1<&2- */ |
| case r_move_output: /* 1>&2- */ |
| case r_move_input_word: /* 1<&$foo- */ |
| case r_move_output_word: /* 1>&$foo- */ |
| break; |
| |
| /* The way the lexer works we have to do this here. */ |
| case r_duplicating_input_word: /* 1<&$foo */ |
| case r_duplicating_output_word: /* 1>&$foo */ |
| w = dest_and_filename.filename; |
| wlen = strlen (w->word) - 1; |
| if (w->word[wlen] == '-') /* Yuck */ |
| { |
| w->word[wlen] = '\0'; |
| if (all_digits (w->word) && legal_number (w->word, &lfd) && lfd == (int)lfd) |
| { |
| dispose_word (w); |
| temp->instruction = (instruction == r_duplicating_input_word) ? r_move_input : r_move_output; |
| temp->redirectee.dest = lfd; |
| } |
| else |
| temp->instruction = (instruction == r_duplicating_input_word) ? r_move_input_word : r_move_output_word; |
| } |
| |
| break; |
| |
| default: |
| programming_error (_("make_redirection: redirection instruction `%d' out of range"), instruction); |
| abort (); |
| break; |
| } |
| return (temp); |
| } |
| |
| COMMAND * |
| make_function_def (name, command, lineno, lstart) |
| WORD_DESC *name; |
| COMMAND *command; |
| int lineno, lstart; |
| { |
| FUNCTION_DEF *temp; |
| #if defined (ARRAY_VARS) |
| SHELL_VAR *bash_source_v; |
| ARRAY *bash_source_a; |
| #endif |
| |
| temp = (FUNCTION_DEF *)xmalloc (sizeof (FUNCTION_DEF)); |
| temp->command = command; |
| temp->name = name; |
| temp->line = lineno; |
| temp->flags = 0; |
| command->line = lstart; |
| |
| /* Information used primarily for debugging. */ |
| temp->source_file = 0; |
| #if defined (ARRAY_VARS) |
| GET_ARRAY_FROM_VAR ("BASH_SOURCE", bash_source_v, bash_source_a); |
| if (bash_source_a && array_num_elements (bash_source_a) > 0) |
| temp->source_file = array_reference (bash_source_a, 0); |
| #endif |
| #if defined (DEBUGGER) |
| bind_function_def (name->word, temp); |
| #endif |
| |
| temp->source_file = 0; |
| return (make_command (cm_function_def, (SIMPLE_COM *)temp)); |
| } |
| |
| COMMAND * |
| make_subshell_command (command) |
| COMMAND *command; |
| { |
| SUBSHELL_COM *temp; |
| |
| temp = (SUBSHELL_COM *)xmalloc (sizeof (SUBSHELL_COM)); |
| temp->command = command; |
| temp->flags = CMD_WANT_SUBSHELL; |
| return (make_command (cm_subshell, (SIMPLE_COM *)temp)); |
| } |
| |
| COMMAND * |
| make_coproc_command (name, command) |
| char *name; |
| COMMAND *command; |
| { |
| COPROC_COM *temp; |
| |
| temp = (COPROC_COM *)xmalloc (sizeof (COPROC_COM)); |
| temp->name = savestring (name); |
| temp->command = command; |
| temp->flags = CMD_WANT_SUBSHELL|CMD_COPROC_SUBSHELL; |
| return (make_command (cm_coproc, (SIMPLE_COM *)temp)); |
| } |
| |
| /* Reverse the word list and redirection list in the simple command |
| has just been parsed. It seems simpler to do this here the one |
| time then by any other method that I can think of. */ |
| COMMAND * |
| clean_simple_command (command) |
| COMMAND *command; |
| { |
| if (command->type != cm_simple) |
| command_error ("clean_simple_command", CMDERR_BADTYPE, command->type, 0); |
| else |
| { |
| command->value.Simple->words = |
| REVERSE_LIST (command->value.Simple->words, WORD_LIST *); |
| command->value.Simple->redirects = |
| REVERSE_LIST (command->value.Simple->redirects, REDIRECT *); |
| } |
| |
| parser_state &= ~PST_REDIRLIST; |
| return (command); |
| } |
| |
| /* The Yacc grammar productions have a problem, in that they take a |
| list followed by an ampersand (`&') and do a simple command connection, |
| making the entire list effectively asynchronous, instead of just |
| the last command. This means that when the list is executed, all |
| the commands have stdin set to /dev/null when job control is not |
| active, instead of just the last. This is wrong, and needs fixing |
| up. This function takes the `&' and applies it to the last command |
| in the list. This is done only for lists connected by `;'; it makes |
| `;' bind `tighter' than `&'. */ |
| COMMAND * |
| connect_async_list (command, command2, connector) |
| COMMAND *command, *command2; |
| int connector; |
| { |
| COMMAND *t, *t1, *t2; |
| |
| t1 = command; |
| t = command->value.Connection->second; |
| |
| if (!t || (command->flags & CMD_WANT_SUBSHELL) || |
| command->value.Connection->connector != ';') |
| { |
| t = command_connect (command, command2, connector); |
| return t; |
| } |
| |
| /* This is just defensive programming. The Yacc precedence rules |
| will generally hand this function a command where t points directly |
| to the command we want (e.g. given a ; b ; c ; d &, t1 will point |
| to the `a ; b ; c' list and t will be the `d'). We only want to do |
| this if the list is not being executed as a unit in the background |
| with `( ... )', so we have to check for CMD_WANT_SUBSHELL. That's |
| the only way to tell. */ |
| while (((t->flags & CMD_WANT_SUBSHELL) == 0) && t->type == cm_connection && |
| t->value.Connection->connector == ';') |
| { |
| t1 = t; |
| t = t->value.Connection->second; |
| } |
| /* Now we have t pointing to the last command in the list, and |
| t1->value.Connection->second == t. */ |
| t2 = command_connect (t, command2, connector); |
| t1->value.Connection->second = t2; |
| return command; |
| } |