888 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			888 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* 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;
 | |
| }
 | 
