aboutsummaryrefslogtreecommitdiffstats
path: root/make_cmd.c
diff options
context:
space:
mode:
authorJari Aalto <jari.aalto@cante.net>1996-08-26 18:22:31 +0000
committerJari Aalto <jari.aalto@cante.net>2009-09-12 16:46:49 +0000
commit726f63884db0132f01745f1fb4465e6621088ccf (patch)
tree6c2f7765a890a97e0e513cb539df43283a8f7c4d /make_cmd.c
downloadandroid_external_bash-726f63884db0132f01745f1fb4465e6621088ccf.tar.gz
android_external_bash-726f63884db0132f01745f1fb4465e6621088ccf.tar.bz2
android_external_bash-726f63884db0132f01745f1fb4465e6621088ccf.zip
Imported from ../bash-1.14.7.tar.gz.
Diffstat (limited to 'make_cmd.c')
-rw-r--r--make_cmd.c612
1 files changed, 612 insertions, 0 deletions
diff --git a/make_cmd.c b/make_cmd.c
new file mode 100644
index 0000000..85eb3d5
--- /dev/null
+++ b/make_cmd.c
@@ -0,0 +1,612 @@
+/* make_cmd.c --
+ Functions for making instances of the various parser constructs. */
+
+/* Copyright (C) 1989 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 1, 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; see the file COPYING. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include "bashtypes.h"
+#include <sys/file.h>
+#include "filecntl.h"
+#include "bashansi.h"
+#include "config.h"
+#include "command.h"
+#include "general.h"
+#include "error.h"
+#include "flags.h"
+#include "make_cmd.h"
+#include "subst.h"
+#include "input.h"
+#include "externs.h"
+
+#if defined (JOB_CONTROL)
+#include "jobs.h"
+#endif
+
+extern int line_number, current_command_line_count;
+extern int disallow_filename_globbing;
+
+WORD_DESC *
+make_word (string)
+ char *string;
+{
+ WORD_DESC *temp;
+
+ temp = (WORD_DESC *)xmalloc (sizeof (WORD_DESC));
+ temp->word = savestring (string);
+ temp->quoted = temp->dollar_present = temp->assignment = 0;
+
+ while (*string)
+ {
+ if (*string == '$') temp->dollar_present = 1;
+
+#ifdef OLDCODE
+ if (member (*string, "'`\\\""))
+ {
+ temp->quoted = 1;
+ if (*string == '\\')
+ string++;
+ }
+#else
+ switch (*string)
+ {
+ case '\\':
+ string++;
+ /*FALLTHROUGH*/
+ case '\'':
+ case '`':
+ case '"':
+ temp->quoted = 1;
+ break;
+ }
+#endif
+
+ if (*string)
+ (string++);
+ }
+ return (temp);
+}
+
+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, link)
+ WORD_DESC *word;
+ WORD_LIST *link;
+{
+ WORD_LIST *temp;
+
+ temp = (WORD_LIST *)xmalloc (sizeof (WORD_LIST));
+ temp->word = word;
+ temp->next = link;
+ return (temp);
+}
+
+WORD_LIST *
+add_string_to_list (string, list)
+ char *string;
+ WORD_LIST *list;
+{
+ WORD_LIST *temp = (WORD_LIST *)xmalloc (sizeof (WORD_LIST));
+ temp->word = make_word (string);
+ temp->next = list;
+ return (temp);
+}
+
+#if 0
+WORD_DESC *
+coerce_to_word (number)
+ int number;
+{
+ char string[24];
+
+ sprintf (string, "%d", number);
+ return (make_word (string));
+}
+#endif
+
+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 = 0;
+ 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));
+}
+
+COMMAND *
+make_for_command (name, map_list, action)
+ WORD_DESC *name;
+ WORD_LIST *map_list;
+ COMMAND *action;
+{
+ FOR_COM *temp = (FOR_COM *)xmalloc (sizeof (FOR_COM));
+
+ temp->flags = 0;
+ temp->name = name;
+ temp->map_list = map_list;
+ temp->action = action;
+ return (make_command (cm_for, (SIMPLE_COM *)temp));
+}
+
+#if defined (SELECT_COMMAND)
+COMMAND *
+make_select_command (name, map_list, action)
+ WORD_DESC *name;
+ WORD_LIST *map_list;
+ COMMAND *action;
+{
+ SELECT_COM *temp = (SELECT_COM *)xmalloc (sizeof (SELECT_COM));
+
+ temp->flags = 0;
+ temp->name = name;
+ temp->map_list = map_list;
+ temp->action = action;
+ return (make_command (cm_select, (SIMPLE_COM *)temp));
+}
+#endif
+
+COMMAND *
+make_group_command (command)
+ COMMAND *command;
+{
+ GROUP_COM *temp = (GROUP_COM *)xmalloc (sizeof (GROUP_COM));
+
+ temp->command = command;
+ return (make_command (cm_group, (SIMPLE_COM *)temp));
+}
+
+COMMAND *
+make_case_command (word, clauses)
+ WORD_DESC *word;
+ PATTERN_LIST *clauses;
+{
+ CASE_COM *temp;
+
+ temp = (CASE_COM *)xmalloc (sizeof (CASE_COM));
+ temp->flags = 0;
+ 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;
+ 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 (test, action, which)
+ COMMAND *test, *action;
+ enum command_type which;
+{
+ 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 (test, action, cm_while));
+}
+
+COMMAND *
+make_until_command (test, action)
+ COMMAND *test, *action;
+{
+ return (make_until_or_while (test, action, cm_until));
+}
+
+COMMAND *
+make_bare_simple_command ()
+{
+ COMMAND *command;
+ SIMPLE_COM *temp = (SIMPLE_COM *)xmalloc (sizeof (SIMPLE_COM));
+
+ temp->flags = 0;
+ temp->line = line_number;
+ temp->words = (WORD_LIST *)NULL;
+ temp->redirects = (REDIRECT *)NULL;
+ command = (COMMAND *)xmalloc (sizeof (COMMAND));
+ command->type = cm_simple;
+ command->redirects = (REDIRECT *)NULL;
+ command->flags = 0;
+ command->value.Simple = temp;
+ 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)
+ command = make_bare_simple_command ();
+
+ if (element.word)
+ {
+ WORD_LIST *tw = (WORD_LIST *)xmalloc (sizeof (WORD_LIST));
+ tw->word = element.word;
+ tw->next = command->value.Simple->words;
+ command->value.Simple->words = tw;
+ }
+ else
+ {
+ 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);
+}
+
+#define POSIX_HERE_DOCUMENTS
+void
+make_here_document (temp)
+ REDIRECT *temp;
+{
+ int kill_leading = 0;
+
+ switch (temp->instruction)
+ {
+ /* 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. */
+
+ case r_deblank_reading_until: /* <<-foo */
+ kill_leading++;
+ /* FALLTHROUGH */
+ case r_reading_until: /* <<foo */
+ {
+ char *redir_word;
+ int redir_len;
+ char *full_line;
+ char *document = (char *)NULL;
+ int document_index = 0, document_size = 0;
+
+#if !defined (POSIX_HERE_DOCUMENTS)
+ /* Because of Bourne shell semantics, we turn off globbing, but
+ only for this style of redirection. I feel a little ill. */
+ {
+ int old_value = disallow_filename_globbing;
+ disallow_filename_globbing = 1;
+
+ redir_word = redirection_expand (temp->redirectee.filename);
+
+ disallow_filename_globbing = old_value;
+ }
+#else /* POSIX_HERE_DOCUMENTS */
+ /* Quote removal is the only expansion performed on the delimiter
+ for here documents, making it an extremely special case. I
+ still feel ill. */
+ redir_word = string_quote_removal (temp->redirectee.filename->word, 0);
+#endif /* POSIX_HERE_DOCUMENTS */
+
+ /* 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 = savestring ("");
+ 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. */
+ while (full_line = read_secondary_line
+ (temp->redirectee.filename->quoted == 0))
+ {
+ register char *line = full_line;
+ int len;
+
+ line_number++;
+
+ 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)
+ 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)
+ : 1000; /* XXX */
+ document = 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;
+ }
+
+ document_done:
+ if (document)
+ document[document_index] = '\0';
+ else
+ document = savestring ("");
+ 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)
+ int source;
+ enum r_instruction instruction;
+ REDIRECTEE dest_and_filename;
+{
+ REDIRECT *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->next = (REDIRECT *)NULL;
+
+ switch (instruction)
+ {
+
+ case r_output_direction: /* >foo */
+ case r_output_force: /* >| foo */
+ temp->flags = O_TRUNC | O_WRONLY | O_CREAT;
+ break;
+
+ case r_input_direction: /* <foo */
+ case r_inputa_direction: /* foo & makes this. */
+ temp->flags = O_RDONLY;
+ break;
+
+ case r_appending_to: /* >>foo */
+ temp->flags = O_APPEND | O_WRONLY | O_CREAT;
+ break;
+
+ case r_deblank_reading_until: /* <<-foo */
+ case r_reading_until: /* << foo */
+ break;
+
+ case r_duplicating_input: /* 1<&2 */
+ case r_duplicating_output: /* 1>&2 */
+ case r_close_this: /* <&- */
+ case r_duplicating_input_word: /* 1<&$foo */
+ case r_duplicating_output_word: /* 1>&$foo */
+ break;
+
+ case r_err_and_out: /* command &>filename */
+ temp->flags = O_TRUNC | O_WRONLY | O_CREAT;
+ break;
+
+ case r_input_output:
+ temp->flags = O_RDWR | O_CREAT;
+ break;
+
+ default:
+ programming_error ("Redirection instruction from yyparse () '%d' is\n\
+out of range in make_redirection ().", instruction);
+ abort ();
+ break;
+ }
+ return (temp);
+}
+
+COMMAND *
+make_function_def (name, command)
+ WORD_DESC *name;
+ COMMAND *command;
+{
+ FUNCTION_DEF *temp;
+
+ temp = (FUNCTION_DEF *)xmalloc (sizeof (FUNCTION_DEF));
+ temp->command = command;
+ temp->name = name;
+ command->line = line_number - current_command_line_count + 1;
+ return (make_command (cm_function_def, (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)
+ {
+ programming_error
+ ("clean_simple_command () got a command with type %d.", command->type);
+ }
+ else
+ {
+ command->value.Simple->words =
+ REVERSE_LIST (command->value.Simple->words, WORD_LIST *);
+ command->value.Simple->redirects =
+ REVERSE_LIST (command->value.Simple->redirects, REDIRECT *);
+ }
+
+ return (command);
+}
+
+/* Cons up a new array of words. The words are taken from LIST,
+ which is a WORD_LIST *. Absolutely everything is malloc'ed,
+ so you should free everything in this array when you are done.
+ The array is NULL terminated. */
+char **
+make_word_array (list)
+ WORD_LIST *list;
+{
+ int count = list_length (list);
+ char **array = (char **)xmalloc ((1 + count) * sizeof (char *));
+
+ for (count = 0; list; count++)
+ {
+ array[count] = xmalloc (1 + strlen (list->word->word));
+ strcpy (array[count], list->word->word);
+ list = list->next;
+ }
+ array[count] = (char *)NULL;
+ return (array);
+}
+
+/* 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;
+}