• R/O
  • SSH
  • HTTPS

yash: 提交


Commit MetaInfo

修訂4123 (tree)
時間2020-11-02 23:08:15
作者magicant

Log Message

Rewrite exec_commands

Change Summary

差異

--- yash/branches/exec/exec.c (revision 4122)
+++ yash/branches/exec/exec.c (revision 4123)
@@ -144,7 +144,9 @@
144144 static void exec_funcdef(const command_T *c, bool finally_exit)
145145 __attribute__((nonnull));
146146
147-static void exec_commands(command_T *c, exec_T type);
147+static void exec_commands(command_T *cs, exec_T type);
148+static inline size_t number_of_commands_in_pipeline(const command_T *c)
149+ __attribute__((nonnull,pure,warn_unused_result));
148150 static bool is_errexit_condition(void)
149151 __attribute__((pure));
150152 static bool is_errreturn_condition(void)
@@ -314,6 +316,8 @@
314316 suppresserrexit |= suppress;
315317 suppresserrreturn |= suppress;
316318
319+ // TODO doing_job_control_now and any_trap_set should be checked in
320+ // exec_commands
317321 bool self = finally_exit && !doing_job_control_now
318322 && !p->next && !p->pl_neg && !any_trap_set;
319323 exec_commands(p->pl_commands, self ? E_SELF : E_NORMAL);
@@ -575,104 +579,113 @@
575579 }
576580
577581 /* Executes the commands in a pipeline. */
578-void exec_commands(command_T *c, exec_T type)
582+void exec_commands(command_T *const cs, exec_T type)
579583 {
580- // TODO Probably should do this in exec_one_command
581- /* increment the reference count of `c' to prevent `c' from being freed
582- * during execution. */
583- c = comsdup(c);
584-
585- if (c->next == NULL) {
586- exec_one_command(c, type);
584+ if (cs->next == NULL) {
585+ exec_one_command(cs, type);
587586 goto done;
588587 }
589588
590- // TODO rewrite below
591- size_t count;
592- pid_t pgid;
593- command_T *cc;
594- job_T *job;
595- process_T *ps, *pp;
596- pipeinfo_T pinfo = PIPEINFO_INIT;
589+ size_t count = number_of_commands_in_pipeline(cs);
590+ assert(count >= 2);
597591
598- /* count the number of the commands */
599- count = 0;
600- for (cc = c; cc != NULL; cc = cc->next)
601- count++;
602- assert(count > 0);
603-
604- if (type == E_SELF && shopt_pipefail && count > 1)
592+ if (type == E_SELF && shopt_pipefail) {
593+ // need to check the exit status of all the commands, not only the last
605594 type = E_NORMAL;
595+ }
606596
607- job = xmallocs(sizeof *job, count, sizeof *job->j_procs);
608- ps = job->j_procs;
597+ /* fork a child process for each command in the pipeline */
598+ pid_t pgid = 0;
599+ pipeinfo_T pipe = PIPEINFO_INIT;
600+ job_T *job = xmallocs(sizeof *job, count, sizeof *job->j_procs);
601+ command_T *c;
602+ process_T *p;
603+ for (c = cs, p = job->j_procs; c != NULL; c = c->next, p++) {
604+ bool is_last = c->next == NULL;
605+ next_pipe(&pipe, !is_last);
609606
610- /* execute the commands */
611- pgid = 0, cc = c, pp = ps;
612- do {
613- pid_t pid;
607+ if (type == E_SELF && is_last)
608+ goto exec_one_command; /* skip forking */
614609
615- next_pipe(&pinfo, cc->next != NULL);
616- pid = exec_process(cc,
617- (type == E_SELF && cc->next != NULL) ? E_NORMAL : type,
618- &pinfo,
619- pgid);
620- pp->pr_pid = pid;
621- if (pid != 0) {
622- pp->pr_status = JS_RUNNING;
623- pp->pr_statuscode = 0;
610+ sigtype_T sigtype = (type == E_ASYNC) ? t_quitint : 0;
611+ pid_t pid = fork_and_reset(pgid, type == E_NORMAL, sigtype);
612+ if (pid == 0) {
613+exec_one_command: /* child process */
614+ free(job);
615+ connect_pipes(&pipe);
616+ exec_one_command(c, E_SELF);
617+ assert(false);
618+ } else if (pid >= 0) {
619+ /* parent process: fork succeeded */
620+ if (pgid == 0)
621+ pgid = pid;
622+ p->pr_pid = pid;
623+ p->pr_status = JS_RUNNING;
624+ // p->pr_statuscode = ?; // The process is still running.
625+ p->pr_name = NULL; // The actual name is given later.
624626 } else {
625- pp->pr_status = JS_DONE;
626- pp->pr_statuscode = laststatus;
627+ /* parent process: fork failed */
628+ p->pr_pid = 0;
629+ p->pr_status = JS_DONE;
630+ p->pr_statuscode = Exit_NOEXEC;
631+ p->pr_name = NULL;
627632 }
628- pp->pr_name = NULL; /* name is given later */
629- if (pgid == 0)
630- pgid = pid;
631- cc = cc->next, pp++;
632- } while (cc != NULL);
633- assert(type != E_SELF); /* `exec_process' doesn't return for E_SELF */
634- assert(pinfo.pi_tonextfds[PIPE_IN] < 0);
635- assert(pinfo.pi_tonextfds[PIPE_OUT] < 0);
636- if (pinfo.pi_fromprevfd >= 0)
637- xclose(pinfo.pi_fromprevfd); /* close leftover pipe */
633+ }
638634
639- if (pgid == 0) {
640- /* nothing more to do if we didn't fork */
641- free(job);
642- } else {
643- job->j_pgid = doing_job_control_now ? pgid : 0;
644- job->j_status = JS_RUNNING;
645- job->j_statuschanged = true;
646- job->j_legacy = false;
647- job->j_nonotify = false;
648- job->j_pcount = count;
649- set_active_job(job);
650- if (type == E_NORMAL) {
635+ assert(pipe.pi_tonextfds[PIPE_IN] < 0);
636+ assert(pipe.pi_tonextfds[PIPE_OUT] < 0);
637+ if (pipe.pi_fromprevfd >= 0)
638+ xclose(pipe.pi_fromprevfd); /* close the leftover pipe */
639+
640+ /* establish the job and wait for it */
641+ job->j_pgid = doing_job_control_now ? pgid : 0;
642+ job->j_status = JS_RUNNING;
643+ job->j_statuschanged = true;
644+ job->j_legacy = false;
645+ job->j_nonotify = false;
646+ job->j_pcount = count;
647+ set_active_job(job);
648+ switch (type) {
649+ case E_NORMAL:
651650 wait_for_job(ACTIVE_JOBNO, doing_job_control_now, false, false);
652651 if (doing_job_control_now)
653652 put_foreground(shell_pgid);
654653 laststatus = calc_status_of_job(job);
655- } else {
654+ break;
655+ case E_ASYNC:
656656 assert(type == E_ASYNC);
657+ // TODO laststatus should be Exit_NOEXEC if fork failed
657658 laststatus = Exit_SUCCESS;
658- lastasyncpid = ps[count - 1].pr_pid;
659- }
660- if (job->j_status != JS_DONE) {
661- for (cc = c, pp = ps; cc != NULL; cc = cc->next, pp++)
662- pp->pr_name = command_to_wcs(cc, false);
663- add_job(type == E_NORMAL || shopt_curasync);
664- } else {
665- notify_signaled_job(ACTIVE_JOBNO);
666- remove_job(ACTIVE_JOBNO);
667- }
659+ lastasyncpid = job->j_procs[count - 1].pr_pid;
660+ break;
661+ case E_SELF:
662+ assert(false);
668663 }
669664
665+ if (job->j_status == JS_DONE) {
666+ notify_signaled_job(ACTIVE_JOBNO);
667+ remove_job(ACTIVE_JOBNO);
668+ } else {
669+ /* name the job processes */
670+ for (c = cs, p = job->j_procs; c != NULL; c = c->next, p++)
671+ p->pr_name = command_to_wcs(c, false);
672+
673+ /* remember the suspended job */
674+ add_job(type == E_NORMAL || shopt_curasync);
675+ }
676+
670677 done:
671678 handle_signals();
672679
673- apply_errexit_errreturn(c);
680+ apply_errexit_errreturn(cs);
681+}
674682
675- comsfree(c);
683+size_t number_of_commands_in_pipeline(const command_T *c)
684+{
685+ size_t count = 1;
686+ while ((c = c->next) != NULL)
687+ count++;
688+ return count;
676689 }
677690
678691 /* Returns true if the shell should exit because of the `errexit' option. */
Show on old repository browser