/* execute_command.c -- Execute a COMMAND structure. */ /* 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 #include #ifndef SONY #include #endif #include #include #include #ifndef SIGABRT #define SIGABRT SIGIOT #endif #include #include #include "shell.h" #include "y.tab.h" #include "builtins.h" #include "flags.h" #include "hash.h" #ifdef JOB_CONTROL #include "jobs.h" #endif #ifdef ALIAS #include "alias.h" #endif extern int breaking, continuing, loop_level; extern int errno, sys_nerr; extern char *sys_errlist[]; #ifdef SYSV extern int last_made_pid; #endif extern WORD_LIST *expand_words (), *expand_word (); /* The value returned by the last synchronous command. */ int last_command_exit_value = 0; /* The list of redirections to preform which will undo the redirections that I made in the shell. */ REDIRECT *redirection_undo_list = (REDIRECT *)NULL; /* Execute the command passed in COMMAND. COMMAND is exactly what read_command () places into GLOBAL_COMMAND. See "shell.h" for the details of the command structure. EXECUTION_SUCCESS or EXECUTION_FAILURE are the only possible return values. Executing a command with nothing in it returns success. */ execute_command (command) COMMAND *command; { /* Just do the command, but not asynchronously. */ return (execute_command_internal (command, 0, NO_PIPE, NO_PIPE)); } /* Returns 1 if TYPE is a shell control structure type. */ int shell_control_structure (type) enum command_type type; { switch (type) { case cm_for: case cm_case: case cm_while: case cm_until: case cm_if: case cm_group: return (1); default: return (0); } } execute_command_internal (command, asynchronous, pipe_in, pipe_out) COMMAND *command; int asynchronous; int pipe_in, pipe_out; { int exec_result; REDIRECT *my_undo_list = (REDIRECT *)NULL; if (!command || breaking || continuing) return (EXECUTION_SUCCESS); /* If a command was being explicitly run in a subshell, or if it is a shell control-structure, and it has a pipe, then we do the command in a subshell. */ if (command->subshell || (shell_control_structure (command->type) && (pipe_out != NO_PIPE || pipe_in != NO_PIPE))) { int paren_pid; /* Fork a subshell, turn off the subshell bit, turn off job control and call execute_command () on the command again. */ paren_pid = make_child (savestring (make_command_string (command)), asynchronous); if (paren_pid == 0) { extern int interactive, login_shell; command->subshell = 0; /* Don't fork again, we are already in a subshell. */ asynchronous = 0; /* Subshells are neither login nor interactive. */ login_shell = interactive = 0; #ifdef JOB_CONTROL /* Delete all traces that there were any jobs running. This is only for subshells. */ without_job_control (); #endif do_piping (pipe_in, pipe_out); if (command->redirects) if (!(do_redirections (command->redirects, 1, 0) == 0)) exit (EXECUTION_FAILURE); exit (execute_command_internal (command, asynchronous, NO_PIPE, NO_PIPE)); } else { close_pipes (pipe_in, pipe_out); /* If we are part of a pipeline, and not the end of the pipeline, then we should simply return and let the last command in the pipe be waited for. If we are not in a pipeline, or are the last command in the pipeline, then we wait for the subshell and return its exit status as usual. */ if (pipe_out != NO_PIPE) return (EXECUTION_SUCCESS); stop_pipeline (asynchronous, (COMMAND *)NULL); if (!asynchronous) return (last_command_exit_value = wait_for (paren_pid)); else { extern int interactive; if (interactive) describe_pid (paren_pid); return (EXECUTION_SUCCESS); } } } /* Handle WHILE FOR CASE etc. with redirections. (Also '&' input redirection.) */ do_redirections (command->redirects, 1, 1); my_undo_list = (REDIRECT *)copy_redirects (redirection_undo_list); switch (command->type) { case cm_for: exec_result = execute_for_command (command->value.For); break; case cm_case: exec_result = execute_case_command (command->value.Case); break; case cm_while: exec_result = execute_while_command (command->value.While); break; case cm_until: exec_result = execute_until_command (command->value.While); break; case cm_if: exec_result = execute_if_command (command->value.If); break; case cm_group: if (asynchronous) { command->subshell = 1; exec_result = execute_command_internal (command, 1, pipe_in, pipe_out); } else { exec_result = execute_command_internal (command->value.Group->command, asynchronous, pipe_in, pipe_out); } break; case cm_simple: { extern int last_asynchronous_pid, last_made_pid; int last_pid = last_made_pid; #ifdef JOB_CONTROL extern int already_making_children; #endif exec_result = execute_simple_command (command->value.Simple, pipe_in, pipe_out, asynchronous); /* If we forked to do the command, then we must wait_for() the child. */ #ifdef JOB_CONTROL if (already_making_children && pipe_out == NO_PIPE) #else if (pipe_out == NO_PIPE) #endif { if (last_pid != last_made_pid) { stop_pipeline (asynchronous, (COMMAND *)NULL); if (asynchronous) { extern int interactive; if (interactive) describe_pid (last_made_pid); } else exec_result = wait_for (last_made_pid); } } } break; case cm_connection: switch (command->value.Connection->connector) { /* Do the first command asynchronously. */ case '&': { COMMAND *tc = command->value.Connection->first; #ifndef JOB_CONTROL { REDIRECT *tr = make_redirection (0, r_inputa_direction, make_word ("/dev/null")); tr->next = tc->redirects; tc->redirects = tr; } #endif /* !JOB_CONTROL */ exec_result = execute_command_internal (tc, 1, pipe_in, pipe_out); if (command->value.Connection->second) exec_result = execute_command_internal (command->value.Connection->second, asynchronous, pipe_in, pipe_out); } break; case ';': /* Just call execute command on both of them. */ execute_command (command->value.Connection->first); exec_result = execute_command_internal (command->value.Connection->second, asynchronous, pipe_in, pipe_out); break; case '|': { /* Make a pipeline between the two commands. */ int fildes[2]; if (pipe (fildes) < 0) { report_error ("Pipe error %d", errno); exec_result = EXECUTION_FAILURE; } else { execute_command_internal (command->value.Connection->first, asynchronous, pipe_in, fildes[1]); exec_result = execute_command_internal (command->value.Connection->second, asynchronous, fildes[0], pipe_out); } } break; case AND_AND: /* Execute the first command. If the result of that is successful, then execute the second command, otherwise return. */ if (execute_command (command->value.Connection->first) != EXECUTION_FAILURE) exec_result = execute_command (command->value.Connection->second); else exec_result = EXECUTION_FAILURE; break; case OR_OR: /* Execute the first command. If the result of that is successfull, then return, otherwise execute the second command. */ if (execute_command (command->value.Connection->first) == EXECUTION_FAILURE) exec_result = execute_command (command->value.Connection->second); else exec_result = EXECUTION_SUCCESS; break; default: programming_error ("Bad connector `%d'!", command->value.Connection->connector); longjmp (top_level, DISCARD); break; } break; case cm_function_def: exec_result = intern_function (command->value.Function_def->name, command->value.Function_def->command); break; default: programming_error ("execute_command: Bad command type `%d'!", command->type); } if (my_undo_list) { do_redirections (my_undo_list, 1, 0); dispose_redirects (my_undo_list); } return (last_command_exit_value = exec_result); } /* Execute a FOR command. The syntax is: FOR word_desc IN word_list; DO command; DONE */ execute_for_command (for_command) FOR_COM *for_command; { /* I just noticed that the Bourne shell leaves word_desc bound to the last name in word_list after the FOR statement is done. This seems wrong to me; I thought that the variable binding should be lexically scoped, i.e. only would last the duration of the FOR command. This behaviour can be gotten by turning on the lexical_scoping switch. */ extern int breaking, continuing; register WORD_LIST *releaser, *list; WORD_DESC *temp = for_command->name; char *identifier; SHELL_VAR *old_value; /* Remember the old value of x. */ if (!check_identifier (temp)) return (EXECUTION_FAILURE); loop_level++; identifier = temp->word; list = releaser = expand_words (for_command->map_list, 0); if (lexical_scoping) old_value = copy_variable (find_variable (identifier)); while (list) { QUIT; bind_variable (identifier, list->word->word); execute_command (for_command->action); QUIT; if (breaking) { breaking--; break; } if (continuing) { continuing--; if (continuing) break; } list = list->next; } dispose_words (releaser); loop_level--; if (lexical_scoping) { if (!old_value) { makunbound (identifier); } else { SHELL_VAR *new_value; if (function_p (old_value)) bind_function (identifier, old_value->function); else bind_variable (identifier, old_value->value); new_value = find_variable (identifier); new_value->attributes = old_value->attributes; } dispose_variable (old_value); } return (EXECUTION_SUCCESS); } /* Execute a CASE command. The syntax is: CASE word_desc IN pattern_list ESAC. The pattern_list is a linked list of pattern clauses; each clause contains some patterns to compare word_desc against, and an associated command to execute. */ execute_case_command (case_command) CASE_COM *case_command; { extern dispose_words (); WORD_LIST *wlist = expand_word (case_command->word, 0); PATTERN_LIST *clauses = case_command->clauses; register WORD_LIST *list; char *word = (wlist) ? wlist->word->word : ""; add_unwind_protect (dispose_words, wlist); while (clauses) { QUIT; list = clauses->patterns; while (list) { WORD_LIST *es = expand_word (list->word, 0); char *pattern = (es) ? es->word->word : ""; if (glob_match (pattern, word, 0)) { dispose_words (es); execute_command (clauses->action); goto exit_command; } dispose_words (es); list = list->next; QUIT; } clauses = clauses->next; } exit_command: remove_unwind_protect (); dispose_words (wlist); return (EXECUTION_SUCCESS); } /* The WHILE command. Syntax: WHILE test DO action; DONE. Repeatedly execute action while executing test produces EXECUTION_SUCCESS. */ execute_while_command (while_command) WHILE_COM *while_command; { extern int breaking; extern int continuing; int commands_executed = 0; loop_level++; while (execute_command (while_command->test) == EXECUTION_SUCCESS) { QUIT; commands_executed = 1; execute_command (while_command->action); QUIT; if (breaking) { breaking--; break; } if (continuing) { continuing--; if (continuing) break; } } loop_level--; return ((commands_executed == 1) ? EXECUTION_SUCCESS : EXECUTION_FAILURE); } /* UNTIL is just like WHILE except that the test result is negated. */ execute_until_command (while_command) WHILE_COM *while_command; { extern int breaking; extern int continuing; int commands_executed = 0; loop_level++; while (execute_command (while_command->test) != EXECUTION_SUCCESS) { QUIT; commands_executed = 1; execute_command (while_command->action); QUIT; if (breaking) { breaking--; break; } if (continuing) { continuing--; if (continuing) break; } } loop_level--; return (commands_executed); } /* IF test THEN command [ELSE command]. IF also allows ELIF in the place of ELSE IF, but the parser makes *that* stupidity transparent. */ execute_if_command (if_command) IF_COM *if_command; { if (execute_command (if_command->test) == EXECUTION_SUCCESS) { QUIT; return (execute_command (if_command->true_case)); } else { QUIT; return (execute_command (if_command->false_case)); } } Function * find_shell_builtin (string) char *string; { int i = 0; while (shell_builtins[i].name) { if (shell_builtins[i].enabled && strcmp (shell_builtins[i].name, string) == 0) return (shell_builtins[i].function); i++; } return ((Function *)NULL); } /* The name of the command that is currently being executed. `test' needs this, for example. */ char *this_command_name; /* For catching RETURN in a function. */ int return_catch_flag = 0; int return_catch_value; jmp_buf return_catch; /* The meaty part of all the executions. We have to start hacking the real execution of commands here. Fork a process, set things up, execute the command. */ execute_simple_command (simple_command, pipe_in, pipe_out, async) SIMPLE_COM *simple_command; int pipe_in, pipe_out; { WORD_LIST *expand_words (); WORD_LIST *words; /* Remember what this command line looks like at invocation. */ extern int command_string_index; extern char *the_printed_command; char *command_line; int first_word_quoted; command_string_index = 0; print_simple_command (simple_command); command_line = (char *)alloca (1 + strlen (the_printed_command)); strcpy (command_line, the_printed_command); first_word_quoted = (simple_command->words? simple_command->words->word->quoted : 0); words = expand_words (simple_command->words); /* It is possible for WORDS not to have anything left in it. Perhaps all the words consisted of `$foo', and there was no variable `$foo'. */ if (words) { extern Function *last_shell_builtin, *this_shell_builtin; extern int ignore_function_references; Function *builtin; SHELL_VAR *var = find_variable (words->word->word); char *auto_resume_value; if (echo_command_at_execute) { extern char *string_list (), *indirection_level_string (); char *line = string_list (words); if (line && *line) fprintf (stderr, "%s%s\n", indirection_level_string (), line); if (line) free (line); } if (ignore_function_references) var = (SHELL_VAR *)NULL; QUIT; #ifdef JOB_CONTROL /* Is this command a job control related thing? */ if (words->word->word[0] == '%') { this_command_name = "fg"; return (fg_builtin (words)); } /* One other possiblilty. The user may want to resume an existing job. If they do, find out whether this word is a candidate for a running job. */ if ((auto_resume_value = get_string_value ("auto_resume")) && !first_word_quoted && !words->next && words->word->word[0] && !simple_command->redirects && pipe_in == NO_PIPE && pipe_out == NO_PIPE && !async) { char *word = words->word->word; register int i, wl = strlen (word), exact; exact = strcmp (auto_resume_value, "exact") == 0; for (i = job_slots - 1; i > -1; i--) { if (jobs[i]) { register PROCESS *p = jobs[i]->pipe; do { if ((exact && strcmp (p->command, word) == 0) || strncmp (p->command, word, wl) == 0) { dispose_words (words); return (start_job (i, 1)); } p = p->next; } while (p != jobs[i]->pipe); } } } #endif /* Not a running job. Do normal command processing. */ maybe_make_export_env (); QUIT; /* Remember the name of this command globally. */ this_command_name = words->word->word; /* This command could be a shell builtin or a user-defined function. If so, and we have pipes, then fork a subshell in here. Else, just do the command. */ if (var && function_p (var)) builtin = (Function *)NULL; else { builtin = find_shell_builtin (words->word->word); last_shell_builtin = this_shell_builtin; this_shell_builtin = builtin; } if (builtin || (var && function_p (var))) { if ((pipe_in != NO_PIPE) || (pipe_out != NO_PIPE) || async) { #ifdef JOB_CONTROL extern int job_control; int old_job_control = job_control; /* Turn off job control before we fork the subshell. */ set_job_control (0); #endif /* JOB_CONTROL */ if (make_child (savestring (command_line), async) == 0) { do_piping (pipe_in, pipe_out); if (do_redirections (simple_command->redirects, 1, 0) == 0) { if (builtin) exit ((*builtin) (words->next)); else { COMMAND *tc = (COMMAND *)copy_command (function_cell (var)); int result; extern int variable_context; remember_args (words->next, 1); #ifdef JOB_CONTROL stop_pipeline (async, (COMMAND *)NULL); #endif variable_context++; return_catch_flag++; result = execute_command (tc); dispose_command (tc); variable_context--; exit (result); } } else { exit (EXECUTION_FAILURE); } } else { close_pipes (pipe_in, pipe_out); #ifdef JOB_CONTROL set_job_control (old_job_control); #endif return (EXECUTION_SUCCESS); } } else { int result = EXECUTION_FAILURE; if (do_redirections (simple_command->redirects, 1, 1) == 0) { REDIRECT *saved_undo_list = redirection_undo_list; redirection_undo_list = (REDIRECT *)NULL; if (builtin) result = ((*builtin) (words->next)); else { int return_val; extern int dispose_command (), pop_context (); jmp_buf old_return_catch; COMMAND *tc; tc = (COMMAND *)copy_command (function_cell (var)); push_context (); begin_unwind_frame ("function_calling"); add_unwind_protect (pop_context, (char *)NULL); add_unwind_protect (dispose_command, (char *)tc); /* Note the second argument of "1", meaning that we discard the current value of "$*"! This is apparently the right thing. */ remember_args (words->next, 1); return_catch_flag++; bcopy ((char *)return_catch, (char *)old_return_catch, sizeof (jmp_buf)); return_val = setjmp (return_catch); if (return_val) result = return_catch_value; else result = execute_command (tc); run_unwind_frame ("function_calling"); return_catch_flag--; bcopy ((char *)old_return_catch, (char *)return_catch, sizeof (jmp_buf)); } redirection_undo_list = saved_undo_list; } do_redirections (redirection_undo_list, 1, 0); dispose_words (words); return (result); } } { /* Hopefully this command is defined in a disk file somewhere. 1) fork () 2) connect pipes 3) close file descriptors 3-NOFILE 4) look up the command 5) do redirections 6) execve () 7) If the execve failed, see if the file has executable mode set. If so, and it isn't a directory, then execute its contents as a shell script. Note that the filename hashing stuff has to take place up here, in the parent. This is probably why the Bourne style shells don't handle it, since that would require them to go through this gnarly hair, for no good reason. */ char **make_word_array (), *find_user_command (), *find_hashed_filename (); char *hashed_file, *command, **args; hashed_file = find_hashed_filename (words->word->word); if (hashed_file) command = savestring (hashed_file); else { command = find_user_command (words->word->word); if (command && !hashing_disabled) { extern int dot_found_in_search; if (!absolute_pathname (words->word->word)) remember_filename (words->word->word, command, dot_found_in_search); /* Increase the number of hits to 1. */ find_hashed_filename (words->word->word); } } /* We have to make the child before we check for the non-existance of COMMAND, since we want the error messages to be redirected. */ if (make_child (savestring (command_line), async) == 0) { do_piping (pipe_in, pipe_out); { register int i; for (i = 3; i < NOFILE; i++) close (i); } /* Execve expects the command name to be in args[0]. So we leave it there, in the same format that the user used to type it in. */ args = make_word_array (words); if (!command) { report_error ("%s: command not found", args[0]); exit (EXECUTION_FAILURE); } if (do_redirections (simple_command->redirects, 1, 0) == 0) { execve (command, args, export_env); /* If we get to this point, then start checking out the file. Maybe it is something we can hack ourselves. */ { struct stat finfo; extern int errno; if (errno != ENOEXEC) { if ((stat (command, &finfo) == 0) && ((finfo.st_mode & S_IFMT) == S_IFDIR)) report_error ("%s: is a directory", args[0]); else file_error (command); exit (EXECUTION_FAILURE); } else { /* This file is executable. If it begins with #!, then help out people with losing operating systems. Otherwise, check to see if it is a binary file by seeing if the first line (or upto 30 characters) are in the ASCII set. Execute the contents as shell commands. */ extern char *shell_name; int larry = array_len (args) + 1; int i, should_exec = 0; { int fd = open (command, O_RDONLY); if (fd != -1) { unsigned char sample[80]; int sample_len = read (fd, &sample[0], 80); /* Is this supposed to be an executable script? */ if (strncmp (sample, "#!", 2) == 0) { char *execname; int start; for (i = 2; whitespace (sample[i]) && i < 80; i++); start = i; for (; !whitespace (sample[i]) && sample[i] != '\n' && i < 80; i++); execname = (char *)xmalloc (1 + (i - start)); strncpy (execname, sample + start, i - start); execname[i - start] = '\0'; should_exec = 1; shell_name = execname; } else { if (sample_len != -1) { for (i = 0; i < sample_len; i++) { if (sample[i] == '\n') break; if (sample[i] > 128 || sample[i] < ' ') { if (sample[i] == '\t') continue; report_error ("%s: Cannot execute binary file", shell_name); exit (EXECUTION_FAILURE); } } } } close (fd); } } #ifdef JOB_CONTROL /* Forget about the way that job control was working. We are in a subshell. */ without_job_control (); #endif #ifdef ALIAS /* Forget about any aliases that we knew of. We are in a subshell. */ delete_all_aliases (); #endif /* Insert the name of this shell into the argument list. */ args = (char **)xrealloc (args, (1 + larry) * sizeof (char *)); for (i = larry - 1; i; i--) args[i] = args[i - 1]; args[0] = shell_name; args[1] = command; args[larry] = (char *)NULL; if (args[0][0] == '-') args[0]++; if (should_exec) { struct stat finfo; extern int errno; execve (shell_name, args, export_env); /* Oh, no! We couldn't even exec this! */ if ((stat (shell_name, &finfo) == 0) && ((finfo.st_mode & S_IFMT) == S_IFDIR)) report_error ("%s: is a directory", args[0]); else file_error (shell_name); exit (EXECUTION_FAILURE); } else exit (main (larry, args, export_env)); } } } else { exit (EXECUTION_FAILURE); } } else { /* Make sure that the pipes are closed in the parent. */ close_pipes (pipe_in, pipe_out); if (command) free (command); } } dispose_words (words); return (EXECUTION_SUCCESS); } else { /* Even if there aren't any command names, pretend to do the redirections that are specified. The user expects the side effects to take place. */ if (do_redirections (simple_command->redirects, 0, 0) == 0) return (EXECUTION_SUCCESS); else return (EXECUTION_FAILURE); } } close_pipes (in, out) int in, out; { if (in >= 0) close (in); if (out >= 0) close (out); } /* Redirect input and output to be from and to the specified pipes. NO_PIPE and REDIRECT_BOTH are handled correctly. */ do_piping (pipe_in, pipe_out) int pipe_in, pipe_out; { if (pipe_in != NO_PIPE) { dup2 (pipe_in, 0); close (pipe_in); } if (pipe_out != NO_PIPE) { dup2 (pipe_out, 1); close (pipe_out); if (pipe_out == REDIRECT_BOTH) dup2 (1, 2); } } /* Non-zero means don't overwrite existing files. */ int noclobber = 0; #define AMBIGUOUS_REDIRECT -1 #define NOCLOBBER_REDIRECT -2 /* Perform the redirections on LIST. If FOR_REAL, then actually make input and output file descriptors, otherwise just do whatever is neccessary for side effecting. INTERNAL says to remember how to undo the redirections later, if non-zero. */ do_redirections (list, for_real, internal) REDIRECT *list; int for_real, internal; { register int error; register REDIRECT *temp = list; if (internal && redirection_undo_list) { dispose_redirects (redirection_undo_list); redirection_undo_list = (REDIRECT *)NULL; } while (temp) { error = do_redirection (temp, for_real, internal); if (error) { if (error == AMBIGUOUS_REDIRECT) report_error ("%s: Ambiguous redirect", temp->redirectee.filename->word); else if (error == NOCLOBBER_REDIRECT) report_error ("%s: Cannot clobber existing file\n", temp->redirectee.filename->word); else report_error ("%s: %s", temp->redirectee.filename->word, sys_errlist[error]); return (error); } temp = temp->next; } return (0); } /* Expand the word in WORD returning a string. If WORD expands to multiple words (or no words), then return NULL. */ char * redirection_expand (word) WORD_DESC *word; { char *string_list (), *result; WORD_LIST *make_word_list (), *expand_words_no_vars (); WORD_LIST *tlist1, *tlist2; tlist1 = make_word_list (copy_word (word), (WORD_LIST *)NULL); tlist2 = expand_words_no_vars (tlist1); dispose_words (tlist1); result = string_list (tlist2); dispose_words (tlist2); return (result); } /* Do the specific redirection requested. Returns errno in case of error. If FOR_REAL is zero, then just do whatever is neccessary to produce the appropriate side effects. REMEMBERING, if non-zero, says to remember how to undo each redirection. */ do_redirection (redirect, for_real, remembering) REDIRECT *redirect; int for_real, remembering; { WORD_DESC *redirectee = redirect->redirectee.filename; int redirector = redirect->redirector; char *redirectee_word = 0; enum r_instruction ri = redirect->instruction; int fd; switch (ri) { case r_output_direction: case r_appending_to: case r_input_direction: case r_inputa_direction: case r_err_and_out: /* command &>filename */ if (!(redirectee_word = redirection_expand (redirectee))) return (AMBIGUOUS_REDIRECT); /* If we are in noclobber mode, you are not allowed to overwrite existing files. Check first. */ if (noclobber && (ri == r_output_direction || ri == r_appending_to || ri == r_err_and_out)) { struct stat buf; if (stat (redirectee_word, &buf) == 0) return (NOCLOBBER_REDIRECT); } fd = open (redirectee_word, redirect->flags, 0666); free (redirectee_word); if (fd < 0 ) return (errno); if (for_real) { struct stat buf; if (remembering) /* Only setup to undo it if the thing to undo is active. */ if (fstat (redirector, &buf) == 0) add_undo_redirect (redirector); if (fd != redirector && dup2 (fd, redirector) < 0) return (errno); } if (fd != redirector) close (fd); /* If we are hacking both stdout and stderr, do the stderr redirection here. */ if (redirect->instruction == r_err_and_out) { if (for_real) { if (remembering) add_undo_redirect (2); dup2 (1, 2); } } break; case r_reading_until: case r_deblank_reading_until: { /* REDIRECTEE is a pointer to a WORD_DESC containing the text of the new input. Place it in a temporary file. */ char *document = (char *)NULL; int document_index = 0; /* Expand the text if the word that was specified had no quoting. Note that the text that we expand is treated exactly as if it were surrounded by double-quotes. */ if (!redirectee) document = savestring (""); else { if (!redirectee->quoted) { WORD_LIST *temp_word_list = (WORD_LIST *)expand_string (redirectee->word, 1); document = (char *)string_list (temp_word_list); if (!document) document = savestring (""); dispose_words (temp_word_list); } else { document = redirectee->word; } document_index = strlen (document); { char filename[40]; int pid = getpid (); /* Make the filename for the temp file. */ sprintf (filename, "/tmp/t%d-sh", pid); fd = open (filename, O_TRUNC | O_WRONLY | O_CREAT, 0666); if (fd < 0) { if (!redirectee->quoted) free (document); return (errno); } write (fd, document, document_index); close (fd); if (!redirectee->quoted) free (document); /* Make the document really temporary. Also make it the input. */ fd = open (filename, O_RDONLY, 0666); if (unlink (filename) < 0 || fd < 0) return (errno); if (for_real) { if (remembering) add_undo_redirect (redirector); if (dup2 (fd, redirector) < 0) return (errno); } close (fd); } } } break; case r_duplicating: if (for_real) { if (remembering) add_undo_redirect (redirector); /* This is correct. 2>&1 means dup2 (1, 2); */ dup2 ((int)redirectee, redirector); } break; case r_close_this: if (for_real) { if (remembering) add_undo_redirect (redirector); close (redirector); } break; } return (0); } /* Remember the file descriptor associated with the slot FD, on REDIRECTION_UNDO_LIST. Note that the list will be reversed before it is executed. */ add_undo_redirect (fd) int fd; { int new_fd = dup (fd); REDIRECT *new_redirect, *closer; if (new_fd < 0) { file_error ("redirection error"); return (-1); } else { closer = make_redirection (new_fd, r_close_this, 0); new_redirect = make_redirection (fd, r_duplicating, new_fd); new_redirect->next = closer; closer->next = redirection_undo_list; redirection_undo_list = new_redirect; } return (0); } intern_function (name, function) WORD_DESC *name; COMMAND *function; { if (!check_identifier (name)) return (EXECUTION_FAILURE); bind_function (name->word, function); return (EXECUTION_SUCCESS); } /* Make sure that identifier is a valid shell identifier, i.e. does not contain a dollar sign, nor is quoted in any way. Nor does it consist of all digits. */ check_identifier (word) WORD_DESC *word; { if (word->dollar_present || word->quoted || all_digits (word->word)) { report_error ("`%s' is not a valid identifier", word->word); return (0); } else return (1); } all_digits (string) char *string; { while (*string) { if (!digit (*string)) return (0); else string++; } return (1); } #define u_mode_bits(x) (((x) & 0000700) >> 6) #define g_mode_bits(x) (((x) & 0000070) >> 3) #define o_mode_bits(x) (((x) & 0000007) >> 0) #define X_BIT(x) (x & 1) /* Non-zero if the last call to executable_file () found the file, but stated that it wasn't executable. */ int file_exists_p = 0; /* Return non-zero if FILE is an executable file, otherwise 0. Note that this function is the definition of what an executable file is; do not change this unless YOU know what an executable file is. */ executable_file (file) char *file; { struct stat finfo; int user_id; /* If the file doesn't exist, or is a directory, then we are not interested. */ file_exists_p = !stat (file, &finfo); if (!file_exists_p || (finfo.st_mode & S_IFDIR)) return (0); /* By definition, the only other criteria is that the file has an execute bit set that we can use. */ user_id = geteuid (); /* If we are the owner of the file, the owner execute bit applies. */ if (user_id == finfo.st_uid) return (X_BIT (u_mode_bits (finfo.st_mode))); /* If we are in the owning group, the group permissions apply. */ if (group_member (finfo.st_gid)) return (X_BIT (g_mode_bits (finfo.st_mode))); /* If `others' have execute permission to the file, then so do we, since we are also `others'. */ return (X_BIT (o_mode_bits (finfo.st_mode))); } #ifndef SYSV /* The number of groups (within 64) that this user is a member of. */ static int default_group_array_size = 0; static int ngroups = 0; static int *group_array = (int *)NULL; #endif /* Return non-zero if GID is one that we have in our groups list. */ group_member (gid) int gid; { #ifdef SYSV return ((gid == getgid ()) || (gid == geteuid ())); #else register int i; /* getgroups () returns the number of elements that it was able to place into the array. We simply continue to call getgroups () until the number of elements placed into the array is smaller than the physical size of the array. */ while (ngroups == default_group_array_size) { default_group_array_size += 64; if (!group_array) group_array = (int *)xmalloc (default_group_array_size * sizeof (int)); else group_array = (int *)xrealloc (group_array, default_group_array_size * sizeof (int)); ngroups = getgroups (default_group_array_size, group_array); } /* In case of error, the user loses. */ if (ngroups < 0) return (0); /* Search through the list looking for GID. */ for (i = 0; i < ngroups; i++) if (gid == group_array[i]) return (1); return (0); #endif /* SYSV */ } /* DOT_FOUND_IN_SEARCH becomes non-zero when find_user_command () encounters a `.' as the directory pathname while scanning the list of possible pathnames; i.e., if `.' comes before the directory containing the file of interest. */ int dot_found_in_search = 0; /* Locate the executable file referenced by NAME, searching along the contents of the shell PATH variable. Return a new string which is the full pathname to the file, or NULL if the file couldn't be found. If a file is found that isn't executable, and that is the only match, then return that. */ char * find_user_command (name) char *name; { char *find_user_command_internal (); return (find_user_command_internal (name, 1)); } /* Locate the file referenced by NAME, searching along the contents of the shell PATH variable. Return a new string which is the full pathname to the file, or NULL if the file couldn't be found. This returns the first file found. */ char * find_path_file (name) char *name; { char *find_user_command_internal (); return (find_user_command_internal (name, 0)); } char * find_user_command_internal (name, must_be_executable) char *name; int must_be_executable; { char *path_list; char *find_user_command_in_path (); path_list = get_string_value ("PATH"); if (!path_list) return (savestring (name)); return (find_user_command_in_path (name, path_list, must_be_executable)); } char * user_command_matches (name, must_be_executable, state) char *name; int must_be_executable; int state; { register int i; char *path_list; int path_index; char *path_element; char *match; static char **match_list = NULL; static int match_list_size = 0; static int match_index = 0; char *extract_colon_unit (); if (!state) { /* Create the list of matches. */ if (!match_list) { match_list = (char **) xmalloc ((match_list_size = 5) * sizeof(char *)); for (i = 0; i < match_list_size; i++) match_list[i] = 0; } /* Clear out the old match list. */ for (i = 0; i < match_list_size; i++) match_list[i] = NULL; /* We haven't found any files yet. */ match_index = 0; path_list = get_string_value ("PATH"); path_index = 0; while (path_element = extract_colon_unit (path_list, &path_index)) { char *find_user_command_in_path (); match = find_user_command_in_path (name, path_element, must_be_executable); free (path_element); if (!match) continue; if (match_index + 1 == match_list_size) match_list = (char **)xrealloc (match_list, ((match_list_size += 10) + 1) * sizeof (char *)); match_list[match_index++] = match; match_list[match_index] = (char *)NULL; } /* We haven't returned any strings yet. */ match_index = 0; } match = match_list[match_index]; if (match) match_index++; return(match); } /* This does the dirty work for find_path_file () and find_user_command (). */ char * find_user_command_in_path (name, path_list, must_be_executable) char *name; char *path_list; int must_be_executable; { extern char *extract_colon_unit (); extern int file_exists_p; char *full_path; char *path; int path_index = 0; /* The file name which we would try to execute, except that it isn't possible to execute it. This is the first file that matches the name that we are looking for while we are searching $PATH for a suitable one to execute. If we cannot find a suitable executable file, then we use this one. */ char *file_to_lose_on = (char *)NULL; /* We haven't started looking, so we certainly haven't seen a `.' as the directory path yet. */ dot_found_in_search = 0; if (absolute_pathname (name)) { full_path = (char *)xmalloc (1 + strlen (name)); strcpy (full_path, name); if (executable_file (full_path) || file_exists_p) { return (full_path); } else { free (full_path); return ((char *)NULL); } } while (path_list && path_list[path_index]) { path = extract_colon_unit (path_list, &path_index); if (!*path) { free (path); path = savestring ("."); /* by definition. */ } if (!disallow_filename_globbing && *path == '~') { char *tilde_expand (); char *t = tilde_expand (path); free (path); path = t; } /* Remember the location of "." in the path. */ if (strcmp (path, ".") == 0) dot_found_in_search = 1; full_path = (char *)xmalloc (2 + strlen (path) + strlen (name)); sprintf (full_path, "%s/%s", path, name); free (path); if (executable_file (full_path) || (!must_be_executable && file_exists_p)) { if (file_to_lose_on) free (file_to_lose_on); return (full_path); } else { if (file_exists_p && !file_to_lose_on) file_to_lose_on = full_path; else free (full_path); } } /* If we found a file with the right name, but not one that is executable, then return the one with the right name. */ if (file_to_lose_on) return (file_to_lose_on); else return (char *)NULL; } /* Given a string containing units of information separated by colons, return the next one pointed to by INDEX, or NULL if there are no more. Advance INDEX to the character after the colon. */ char * extract_colon_unit (string, index) char *string; int *index; { int i, start; i = *index; if ((i >= strlen (string)) || !string) return ((char *)NULL); if (string[i] == ':') i++; start = i; while (string[i] && string[i] != ':') i++; *index = i; if (i == start) { if (!string[i]) return ((char *)NULL); (*index)++; return (savestring ("")); } else { char *value; value = (char *)xmalloc (1 + (i - start)); strncpy (value, &string[start], (i - start)); value [i - start] = '\0'; return (value); } }