Merge branch exec (#40855)
@@ -113,6 +113,12 @@ | ||
113 | 113 | #define ci_builtin value.builtin |
114 | 114 | #define ci_function value.function |
115 | 115 | |
116 | +/* result of `fork_and_wait' */ | |
117 | +typedef struct fork_and_wait_T { | |
118 | + pid_t cpid; /* child process ID */ | |
119 | + wchar_t **namep; /* where to place the job name */ | |
120 | +} fork_and_wait_T; | |
121 | + | |
116 | 122 | typedef enum exception_T { |
117 | 123 | E_NONE, |
118 | 124 | E_CONTINUE, |
@@ -144,7 +150,9 @@ | ||
144 | 150 | static void exec_funcdef(const command_T *c, bool finally_exit) |
145 | 151 | __attribute__((nonnull)); |
146 | 152 | |
147 | -static void exec_commands(command_T *c, exec_T type); | |
153 | +static void exec_commands(command_T *cs, exec_T type); | |
154 | +static inline size_t number_of_commands_in_pipeline(const command_T *c) | |
155 | + __attribute__((nonnull,pure,warn_unused_result)); | |
148 | 156 | static bool is_errexit_condition(void) |
149 | 157 | __attribute__((pure)); |
150 | 158 | static bool is_errreturn_condition(void) |
@@ -154,12 +162,16 @@ | ||
154 | 162 | __attribute__((pure)); |
155 | 163 | static inline void next_pipe(pipeinfo_T *pi, bool next) |
156 | 164 | __attribute__((nonnull)); |
157 | -static pid_t exec_process( | |
158 | - command_T *restrict c, exec_T type, pipeinfo_T *restrict pi, pid_t pgid) | |
165 | +static void exec_one_command(command_T *c, bool finally_exit) | |
159 | 166 | __attribute__((nonnull)); |
167 | +static void exec_simple_command(const command_T *c, bool finally_exit) | |
168 | + __attribute__((nonnull)); | |
169 | +// TODO Reconsider order of functions around here | |
160 | 170 | static inline void connect_pipes(pipeinfo_T *pi) |
161 | 171 | __attribute__((nonnull)); |
162 | 172 | static void become_child(sigtype_T sigtype); |
173 | +static fork_and_wait_T fork_and_wait(sigtype_T sigtype) | |
174 | + __attribute__((warn_unused_result)); | |
163 | 175 | static void search_command( |
164 | 176 | const char *restrict name, const wchar_t *restrict wname, |
165 | 177 | commandinfo_T *restrict ci, enum srchcmdtype_T type) |
@@ -170,9 +182,9 @@ | ||
170 | 182 | __attribute__((nonnull)); |
171 | 183 | static void exec_nonsimple_command(command_T *c, bool finally_exit) |
172 | 184 | __attribute__((nonnull)); |
173 | -static void exec_simple_command(const commandinfo_T *ci, | |
185 | +static wchar_t **invoke_simple_command(const commandinfo_T *ci, | |
174 | 186 | int argc, char *argv0, void **argv, bool finally_exit) |
175 | - __attribute__((nonnull)); | |
187 | + __attribute__((nonnull,warn_unused_result)); | |
176 | 188 | static void exec_external_program( |
177 | 189 | const char *path, int argc, char *argv0, void **argv, char **envs) |
178 | 190 | __attribute__((nonnull)); |
@@ -311,6 +323,8 @@ | ||
311 | 323 | suppresserrexit |= suppress; |
312 | 324 | suppresserrreturn |= suppress; |
313 | 325 | |
326 | + // TODO doing_job_control_now and any_trap_set should be checked in | |
327 | + // exec_commands | |
314 | 328 | bool self = finally_exit && !doing_job_control_now |
315 | 329 | && !p->next && !p->pl_neg && !any_trap_set; |
316 | 330 | exec_commands(p->pl_commands, self ? E_SELF : E_NORMAL); |
@@ -572,96 +586,115 @@ | ||
572 | 586 | } |
573 | 587 | |
574 | 588 | /* Executes the commands in a pipeline. */ |
575 | -void exec_commands(command_T *c, exec_T type) | |
589 | +void exec_commands(command_T *const cs, exec_T type) | |
576 | 590 | { |
577 | - size_t count; | |
578 | - pid_t pgid; | |
579 | - command_T *cc; | |
580 | - job_T *job; | |
581 | - process_T *ps, *pp; | |
582 | - pipeinfo_T pinfo = PIPEINFO_INIT; | |
591 | + if (cs->next == NULL && type != E_ASYNC) { | |
592 | + exec_one_command(cs, type == E_SELF); | |
593 | + goto done; | |
594 | + } | |
583 | 595 | |
584 | - /* increment the reference count of `c' to prevent `c' from being freed | |
585 | - * during execution. */ | |
586 | - c = comsdup(c); | |
587 | - | |
588 | - /* count the number of the commands */ | |
589 | - count = 0; | |
590 | - for (cc = c; cc != NULL; cc = cc->next) | |
591 | - count++; | |
596 | + size_t count = number_of_commands_in_pipeline(cs); | |
592 | 597 | assert(count > 0); |
593 | 598 | |
594 | - if (type == E_SELF && shopt_pipefail && count > 1) | |
599 | + if (type == E_SELF && shopt_pipefail) { | |
600 | + // need to check the exit status of all the commands, not only the last | |
595 | 601 | type = E_NORMAL; |
602 | + } | |
596 | 603 | |
597 | - job = xmallocs(sizeof *job, count, sizeof *job->j_procs); | |
598 | - ps = job->j_procs; | |
604 | + /* fork a child process for each command in the pipeline */ | |
605 | + pid_t pgid = 0; | |
606 | + pipeinfo_T pipe = PIPEINFO_INIT; | |
607 | + job_T *job = xmallocs(sizeof *job, count, sizeof *job->j_procs); | |
608 | + command_T *c; | |
609 | + process_T *p; | |
610 | + for (c = cs, p = job->j_procs; c != NULL; c = c->next, p++) { | |
611 | + bool is_last = c->next == NULL; | |
612 | + next_pipe(&pipe, !is_last); | |
599 | 613 | |
600 | - /* execute the commands */ | |
601 | - pgid = 0, cc = c, pp = ps; | |
602 | - do { | |
603 | - pid_t pid; | |
614 | + if (type == E_SELF && is_last) | |
615 | + goto exec_one_command; /* skip forking */ | |
604 | 616 | |
605 | - next_pipe(&pinfo, cc->next != NULL); | |
606 | - pid = exec_process(cc, | |
607 | - (type == E_SELF && cc->next != NULL) ? E_NORMAL : type, | |
608 | - &pinfo, | |
609 | - pgid); | |
610 | - pp->pr_pid = pid; | |
611 | - if (pid != 0) { | |
612 | - pp->pr_status = JS_RUNNING; | |
613 | - pp->pr_statuscode = 0; | |
617 | + sigtype_T sigtype = (type == E_ASYNC) ? t_quitint : 0; | |
618 | + pid_t pid = fork_and_reset(pgid, type == E_NORMAL, sigtype); | |
619 | + if (pid == 0) { | |
620 | +exec_one_command: /* child process */ | |
621 | + free(job); | |
622 | + connect_pipes(&pipe); | |
623 | + if (type == E_ASYNC && pipe.pi_fromprevfd < 0) | |
624 | + maybe_redirect_stdin_to_devnull(); | |
625 | + exec_one_command(c, true); | |
626 | + assert(false); | |
627 | + } else if (pid >= 0) { | |
628 | + /* parent process: fork succeeded */ | |
629 | + if (pgid == 0) | |
630 | + pgid = pid; | |
631 | + p->pr_pid = pid; | |
632 | + p->pr_status = JS_RUNNING; | |
633 | + // p->pr_statuscode = ?; // The process is still running. | |
634 | + p->pr_name = NULL; // The actual name is given later. | |
614 | 635 | } else { |
615 | - pp->pr_status = JS_DONE; | |
616 | - pp->pr_statuscode = laststatus; | |
636 | + /* parent process: fork failed */ | |
637 | + p->pr_pid = 0; | |
638 | + p->pr_status = JS_DONE; | |
639 | + p->pr_statuscode = Exit_NOEXEC; | |
640 | + p->pr_name = NULL; | |
617 | 641 | } |
618 | - pp->pr_name = NULL; /* name is given later */ | |
619 | - if (pgid == 0) | |
620 | - pgid = pid; | |
621 | - cc = cc->next, pp++; | |
622 | - } while (cc != NULL); | |
623 | - assert(type != E_SELF); /* `exec_process' doesn't return for E_SELF */ | |
624 | - assert(pinfo.pi_tonextfds[PIPE_IN] < 0); | |
625 | - assert(pinfo.pi_tonextfds[PIPE_OUT] < 0); | |
626 | - if (pinfo.pi_fromprevfd >= 0) | |
627 | - xclose(pinfo.pi_fromprevfd); /* close leftover pipe */ | |
642 | + } | |
628 | 643 | |
629 | - if (pgid == 0) { | |
630 | - /* nothing more to do if we didn't fork */ | |
631 | - free(job); | |
632 | - } else { | |
633 | - job->j_pgid = doing_job_control_now ? pgid : 0; | |
634 | - job->j_status = JS_RUNNING; | |
635 | - job->j_statuschanged = true; | |
636 | - job->j_legacy = false; | |
637 | - job->j_nonotify = false; | |
638 | - job->j_pcount = count; | |
639 | - set_active_job(job); | |
640 | - if (type == E_NORMAL) { | |
644 | + assert(pipe.pi_tonextfds[PIPE_IN] < 0); | |
645 | + assert(pipe.pi_tonextfds[PIPE_OUT] < 0); | |
646 | + if (pipe.pi_fromprevfd >= 0) | |
647 | + xclose(pipe.pi_fromprevfd); /* close the leftover pipe */ | |
648 | + | |
649 | + /* establish the job and wait for it */ | |
650 | + job->j_pgid = doing_job_control_now ? pgid : 0; | |
651 | + job->j_status = JS_RUNNING; | |
652 | + job->j_statuschanged = true; | |
653 | + job->j_legacy = false; | |
654 | + job->j_nonotify = false; | |
655 | + job->j_pcount = count; | |
656 | + set_active_job(job); | |
657 | + switch (type) { | |
658 | + case E_NORMAL: | |
641 | 659 | wait_for_job(ACTIVE_JOBNO, doing_job_control_now, false, false); |
642 | 660 | if (doing_job_control_now) |
643 | 661 | put_foreground(shell_pgid); |
644 | 662 | laststatus = calc_status_of_job(job); |
645 | - } else { | |
663 | + break; | |
664 | + case E_ASYNC: | |
646 | 665 | assert(type == E_ASYNC); |
666 | + // TODO laststatus should be Exit_NOEXEC if fork failed | |
647 | 667 | laststatus = Exit_SUCCESS; |
648 | - lastasyncpid = ps[count - 1].pr_pid; | |
649 | - } | |
650 | - if (job->j_status != JS_DONE) { | |
651 | - for (cc = c, pp = ps; cc != NULL; cc = cc->next, pp++) | |
652 | - pp->pr_name = command_to_wcs(cc, false); | |
653 | - add_job(type == E_NORMAL || shopt_curasync); | |
654 | - } else { | |
655 | - notify_signaled_job(ACTIVE_JOBNO); | |
656 | - remove_job(ACTIVE_JOBNO); | |
657 | - } | |
668 | + lastasyncpid = job->j_procs[count - 1].pr_pid; | |
669 | + break; | |
670 | + case E_SELF: | |
671 | + assert(false); | |
658 | 672 | } |
659 | 673 | |
674 | + if (job->j_status == JS_DONE) { | |
675 | + notify_signaled_job(ACTIVE_JOBNO); | |
676 | + remove_job(ACTIVE_JOBNO); | |
677 | + } else { | |
678 | + /* name the job processes */ | |
679 | + for (c = cs, p = job->j_procs; c != NULL; c = c->next, p++) | |
680 | + p->pr_name = command_to_wcs(c, false); | |
681 | + | |
682 | + /* remember the suspended job */ | |
683 | + add_job(type == E_NORMAL || shopt_curasync); | |
684 | + } | |
685 | + | |
686 | +done: | |
660 | 687 | handle_signals(); |
661 | 688 | |
662 | - apply_errexit_errreturn(c); | |
689 | + apply_errexit_errreturn(cs); | |
690 | +} | |
663 | 691 | |
664 | - comsfree(c); | |
692 | +size_t number_of_commands_in_pipeline(const command_T *c) | |
693 | +{ | |
694 | + size_t count = 1; | |
695 | + while ((c = c->next) != NULL) | |
696 | + count++; | |
697 | + return count; | |
665 | 698 | } |
666 | 699 | |
667 | 700 | /* Returns true if the shell should exit because of the `errexit' option. */ |
@@ -779,67 +812,57 @@ | ||
779 | 812 | xerror(errno, Ngt("cannot open a pipe")); |
780 | 813 | } |
781 | 814 | |
782 | -/* Executes the command. | |
783 | - * If job control is active, the child process's process group ID is set to | |
784 | - * `pgid'. If `pgid' is 0, the child process's process ID is used as the process | |
785 | - * group ID. | |
786 | - * If the child process forked successfully, its process ID is returned. | |
787 | - * If the command was executed without forking, `laststatus' is set to the exit | |
788 | - * status of the command and 0 is returned. | |
789 | - * if `type' is E_SELF, this function never returns. */ | |
790 | -pid_t exec_process( | |
791 | - command_T *restrict c, exec_T type, pipeinfo_T *restrict pi, pid_t pgid) | |
815 | +/* Executes the command. */ | |
816 | +void exec_one_command(command_T *c, bool finally_exit) | |
792 | 817 | { |
793 | - bool finally_exit; /* never return? */ | |
794 | - int argc; | |
795 | - void **argv = NULL; | |
796 | - char *argv0 = NULL; | |
797 | - pid_t cpid = 0; | |
818 | + /* prevent the command data from being freed in case the command is part of | |
819 | + * a function that is unset during execution. */ | |
820 | + c = comsdup(c); | |
798 | 821 | |
799 | 822 | update_lineno(c->c_lineno); |
800 | 823 | |
801 | - finally_exit = (type == E_SELF); | |
802 | - if (finally_exit) { | |
803 | - if (c->c_type == CT_SUBSHELL) | |
804 | - /* No command follows this subshell command, so we can execute the | |
805 | - * subshell directly in this process. */ | |
806 | - become_child(0); | |
824 | + if (c->c_type == CT_SIMPLE) { | |
825 | + exec_simple_command(c, finally_exit); | |
807 | 826 | } else { |
808 | - /* fork first if `type' is E_ASYNC, the command type is subshell, | |
809 | - * or there is a pipe. */ | |
810 | - if (type == E_ASYNC || c->c_type == CT_SUBSHELL | |
811 | - || pi->pi_fromprevfd >= 0 || pi->pi_tonextfds[PIPE_OUT] >= 0) { | |
812 | - sigtype_T sigtype = (type == E_ASYNC) ? t_quitint : 0; | |
813 | - cpid = fork_and_reset(pgid, type == E_NORMAL, sigtype); | |
814 | - if (cpid != 0) | |
815 | - goto done; | |
816 | - finally_exit = true; | |
827 | + savefd_T *savefd; | |
828 | + if (open_redirections(c->c_redirs, &savefd)) { | |
829 | + exec_nonsimple_command(c, finally_exit && savefd == NULL); | |
830 | + undo_redirections(savefd); | |
831 | + } else { | |
832 | + laststatus = Exit_REDIRERR; | |
833 | + apply_errexit_errreturn(NULL); | |
817 | 834 | } |
818 | 835 | } |
819 | 836 | |
837 | + comsfree(c); | |
838 | + | |
839 | + if (finally_exit) | |
840 | + exit_shell(); | |
841 | +} | |
842 | + | |
843 | +/* Executes the simple command. */ | |
844 | +void exec_simple_command(const command_T *c, bool finally_exit) | |
845 | +{ | |
820 | 846 | lastcmdsubstatus = Exit_SUCCESS; |
821 | 847 | |
822 | - /* connect pipes and close leftovers */ | |
823 | - connect_pipes(pi); | |
848 | + /* expand the command words */ | |
849 | + int argc; | |
850 | + void **argv; | |
851 | + if (!expand_line(c->c_words, &argc, &argv)) { | |
852 | + laststatus = Exit_EXPERROR; | |
853 | + goto done; | |
854 | + } | |
855 | + if (is_interrupted()) | |
856 | + goto done1; | |
824 | 857 | |
825 | - if (c->c_type == CT_SIMPLE) { | |
826 | - if (!expand_line(c->c_words, &argc, &argv)) { | |
827 | - laststatus = Exit_EXPERROR; | |
828 | - goto done; | |
829 | - } | |
830 | - if (is_interrupted()) { | |
831 | - plfree(argv, free); | |
832 | - goto done; | |
833 | - } | |
834 | - if (argc == 0) { | |
835 | - argv0 = NULL; | |
836 | - } else { | |
837 | - argv0 = malloc_wcstombs(argv[0]); | |
838 | - if (argv0 == NULL) | |
839 | - argv0 = xstrdup(""); | |
840 | - } | |
858 | + char *argv0; // a multi-byte version of argv[0] | |
859 | + if (argc == 0) { | |
860 | + argv0 = NULL; | |
861 | + } else { | |
862 | + argv0 = malloc_wcstombs(argv[0]); | |
863 | + if (argv0 == NULL) | |
864 | + argv0 = xstrdup(""); | |
841 | 865 | } |
842 | - /* `argc' and `argv' are used only for `CT_SIMPLE'. */ | |
843 | 866 | |
844 | 867 | /* open redirections */ |
845 | 868 | savefd_T *savefd; |
@@ -847,24 +870,12 @@ | ||
847 | 870 | /* On redirection error, the command is not executed. */ |
848 | 871 | laststatus = Exit_REDIRERR; |
849 | 872 | apply_errexit_errreturn(NULL); |
850 | - if (posixly_correct && !is_interactive_now && c->c_type == CT_SIMPLE && | |
873 | + if (posixly_correct && !is_interactive_now && | |
851 | 874 | argc > 0 && is_special_builtin(argv0)) |
852 | 875 | finally_exit = true; |
853 | 876 | goto done2; |
854 | 877 | } |
855 | - | |
856 | - if (type == E_ASYNC && pi->pi_fromprevfd < 0) | |
857 | - maybe_redirect_stdin_to_devnull(); | |
858 | 878 | |
859 | - if (c->c_type != CT_SIMPLE) { | |
860 | - if (c->c_type == CT_SUBSHELL) { | |
861 | - clear_savefd(savefd); | |
862 | - savefd = NULL; | |
863 | - } | |
864 | - exec_nonsimple_command(c, finally_exit && savefd == NULL); | |
865 | - goto done1; | |
866 | - } | |
867 | - | |
868 | 879 | last_assign = c->c_assigns; |
869 | 880 | if (argc == 0) { |
870 | 881 | /* if there is no command word, just perform assignments */ |
@@ -879,14 +890,13 @@ | ||
879 | 890 | goto done2; |
880 | 891 | } |
881 | 892 | |
893 | + /* check if the command is a special built-in or function */ | |
882 | 894 | commandinfo_T cmdinfo; |
883 | - bool temp; | |
884 | - | |
885 | - /* check if the command is a special built-in or a function and determine | |
886 | - * whether we have to open a temporary environment. */ | |
887 | 895 | search_command(argv0, argv[0], &cmdinfo, SCT_BUILTIN | SCT_FUNCTION); |
888 | 896 | special_builtin_executed = (cmdinfo.type == CT_SPECIALBUILTIN); |
889 | - temp = c->c_assigns != NULL && !special_builtin_executed; | |
897 | + | |
898 | + /* open a temporary variable environment */ | |
899 | + bool temp = c->c_assigns != NULL && !special_builtin_executed; | |
890 | 900 | if (temp) |
891 | 901 | open_new_environment(true); |
892 | 902 |
@@ -915,29 +925,11 @@ | ||
915 | 925 | } |
916 | 926 | } |
917 | 927 | |
918 | - /* create a child process to execute the external command */ | |
919 | - if (cmdinfo.type == CT_EXTERNALPROGRAM && !finally_exit) { | |
920 | - assert(type == E_NORMAL); | |
921 | - cpid = fork_and_reset(pgid, true, t_leave); | |
922 | - if (cpid != 0) | |
923 | - goto done3; | |
924 | - finally_exit = true; | |
925 | - } | |
926 | - | |
927 | 928 | /* execute! */ |
928 | - bool finally_exit2; | |
929 | - switch (cmdinfo.type) { | |
930 | - case CT_EXTERNALPROGRAM: | |
931 | - finally_exit2 = true; | |
932 | - break; | |
933 | - case CT_FUNCTION: | |
934 | - finally_exit2 = (finally_exit && /* !temp && */ savefd == NULL); | |
935 | - break; | |
936 | - default: | |
937 | - finally_exit2 = false; | |
938 | - break; | |
939 | - } | |
940 | - exec_simple_command(&cmdinfo, argc, argv0, argv, finally_exit2); | |
929 | + wchar_t **namep = invoke_simple_command(&cmdinfo, argc, argv0, argv, | |
930 | + finally_exit && /* !temp && */ savefd == NULL); | |
931 | + if (namep != NULL) | |
932 | + *namep = command_to_wcs(c, false); | |
941 | 933 | |
942 | 934 | /* Redirections are not undone after a successful "exec" command: |
943 | 935 | * remove the saved data of file descriptors. */ |
@@ -947,22 +939,18 @@ | ||
947 | 939 | } |
948 | 940 | exec_builtin_executed = false; |
949 | 941 | |
942 | + /* cleanup */ | |
950 | 943 | done3: |
951 | 944 | if (temp) |
952 | 945 | close_current_environment(); |
953 | 946 | done2: |
954 | - if (c->c_type == CT_SIMPLE) | |
955 | - plfree(argv, free), free(argv0); | |
947 | + undo_redirections(savefd); | |
948 | + free(argv0); | |
956 | 949 | done1: |
957 | - undo_redirections(savefd); | |
950 | + plfree(argv, free); | |
958 | 951 | done: |
959 | - if (cpid < 0) { | |
960 | - laststatus = Exit_NOEXEC; | |
961 | - cpid = 0; | |
962 | - } | |
963 | 952 | if (finally_exit) |
964 | 953 | exit_shell(); |
965 | - return cpid; | |
966 | 954 | } |
967 | 955 | |
968 | 956 | /* Connects the pipe(s) and closes the pipes left. */ |
@@ -1064,6 +1052,35 @@ | ||
1064 | 1052 | exitstatus = -1; |
1065 | 1053 | } |
1066 | 1054 | |
1055 | +/* Forks a new child process and wait for it to finish. | |
1056 | + * `sigtype' is passed to `fork_and_reset'. | |
1057 | + * In the parent process, this function updates `laststatus' to the exit status | |
1058 | + * of the child. | |
1059 | + * In the child process, this function does not wait for anything. | |
1060 | + * Returns a pair of the return value of `fork' and a pointer to a pointer to | |
1061 | + * a wide string. If the pointer is non-null, the caller must assign it a | |
1062 | + * pointer to a newly malloced wide string that is the job name of the child. */ | |
1063 | +fork_and_wait_T fork_and_wait(sigtype_T sigtype) | |
1064 | +{ | |
1065 | + fork_and_wait_T result; | |
1066 | + result.cpid = fork_and_reset(0, true, sigtype); | |
1067 | + if (result.cpid < 0) { | |
1068 | + /* Fork failed. */ | |
1069 | + laststatus = Exit_NOEXEC; | |
1070 | + result.namep = NULL; | |
1071 | + } if (result.cpid > 0) { | |
1072 | + /* parent process */ | |
1073 | + result.namep = wait_for_child( | |
1074 | + result.cpid, | |
1075 | + doing_job_control_now ? result.cpid : 0, | |
1076 | + doing_job_control_now); | |
1077 | + } else { | |
1078 | + /* child process */ | |
1079 | + result.namep = NULL; | |
1080 | + } | |
1081 | + return result; | |
1082 | +} | |
1083 | + | |
1067 | 1084 | /* Searches for a command. |
1068 | 1085 | * The result is assigned to `*ci'. |
1069 | 1086 | * `name' and `wname' must contain the same string value. |
@@ -1191,9 +1208,7 @@ | ||
1191 | 1208 | } |
1192 | 1209 | |
1193 | 1210 | /* Executes the specified command whose type is not `CT_SIMPLE'. |
1194 | - * The redirections for the command is not performed in this function. | |
1195 | - * For CT_SUBSHELL, this function must be called in an already-forked subshell. | |
1196 | - */ | |
1211 | + * The redirections for the command is not performed in this function. */ | |
1197 | 1212 | void exec_nonsimple_command(command_T *c, bool finally_exit) |
1198 | 1213 | { |
1199 | 1214 | switch (c->c_type) { |
@@ -1200,6 +1215,21 @@ | ||
1200 | 1215 | case CT_SIMPLE: |
1201 | 1216 | assert(false); |
1202 | 1217 | case CT_SUBSHELL: |
1218 | + if (finally_exit) { | |
1219 | + /* This is the last command to execute in the current shell, hence | |
1220 | + * no need to make a new child. */ | |
1221 | + become_child(0); | |
1222 | + } else { | |
1223 | + /* make a child process to execute the command */ | |
1224 | + fork_and_wait_T faw = fork_and_wait(0); | |
1225 | + if (faw.cpid != 0) { | |
1226 | + if (faw.namep != NULL) | |
1227 | + *faw.namep = command_to_wcs(c, false); | |
1228 | + break; | |
1229 | + } | |
1230 | + finally_exit = true; | |
1231 | + } | |
1232 | + // falls thru! | |
1203 | 1233 | case CT_GROUP: |
1204 | 1234 | exec_and_or_lists(c->c_subcmds, finally_exit); |
1205 | 1235 | break; |
@@ -1228,14 +1258,16 @@ | ||
1228 | 1258 | } |
1229 | 1259 | } |
1230 | 1260 | |
1231 | -/* Executes the simple command. */ | |
1261 | +/* Invokes the simple command. */ | |
1232 | 1262 | /* `argv0' is the multibyte version of `argv[0]' */ |
1233 | -void exec_simple_command( | |
1263 | +wchar_t **invoke_simple_command( | |
1234 | 1264 | const commandinfo_T *ci, int argc, char *argv0, void **argv, |
1235 | 1265 | bool finally_exit) |
1236 | 1266 | { |
1237 | 1267 | assert(plcount(argv) == (size_t) argc); |
1238 | 1268 | |
1269 | + fork_and_wait_T faw = { 0, NULL }; | |
1270 | + | |
1239 | 1271 | switch (ci->type) { |
1240 | 1272 | case CT_NONE: |
1241 | 1273 | xerror(0, Ngt("no such command `%s'"), argv0); |
@@ -1243,19 +1275,9 @@ | ||
1243 | 1275 | break; |
1244 | 1276 | case CT_EXTERNALPROGRAM: |
1245 | 1277 | if (!finally_exit) { |
1246 | - pid_t cpid = fork_and_reset(0, true, t_leave); | |
1247 | - if (cpid < 0) { | |
1248 | - laststatus = Exit_NOEXEC; | |
1278 | + faw = fork_and_wait(t_leave); | |
1279 | + if (faw.cpid != 0) | |
1249 | 1280 | break; |
1250 | - } else if (cpid > 0) { | |
1251 | - wchar_t **namep = wait_for_child( | |
1252 | - cpid, | |
1253 | - doing_job_control_now ? cpid : 0, | |
1254 | - doing_job_control_now); | |
1255 | - if (namep != NULL) | |
1256 | - *namep = joinwcsarray(argv, L" "); | |
1257 | - break; | |
1258 | - } | |
1259 | 1281 | finally_exit = true; |
1260 | 1282 | } |
1261 | 1283 | exec_external_program(ci->ci_path, argc, argv0, argv, environ); |
@@ -1278,6 +1300,7 @@ | ||
1278 | 1300 | } |
1279 | 1301 | if (finally_exit) |
1280 | 1302 | exit_shell(); |
1303 | + return faw.namep; | |
1281 | 1304 | } |
1282 | 1305 | |
1283 | 1306 | /* Executes the external program. |
@@ -2321,7 +2344,11 @@ | ||
2321 | 2344 | } |
2322 | 2345 | |
2323 | 2346 | search_command(argv0, argv[0], &ci, type); |
2324 | - exec_simple_command(&ci, argc, argv0, argv, false); | |
2347 | + | |
2348 | + wchar_t **namep = invoke_simple_command(&ci, argc, argv0, argv, false); | |
2349 | + if (namep != NULL) | |
2350 | + *namep = joinwcsarray(argv, L" "); | |
2351 | + | |
2325 | 2352 | free(argv0); |
2326 | 2353 | return laststatus; |
2327 | 2354 | } |