# HG changeset patch # User Jun Wu # Date 1465673149 -3600 # Node ID bb3d5c20eaf664cca201fc9273c1df2402f5c6fa # Parent e095b9e753f7c8e729d5624891d1ea67ed18344d chg: exec pager in child process Before this patch, chg uses the old pager behavior (pre 369741ef7253), which executes pager in the main process. The user will see the exit code of the pager, instead of the hg command. Like 369741ef7253, this patch fixes the behavior by executing the pager in the child process, and wait for it at the end of the main process. diff -r e095b9e753f7 -r bb3d5c20eaf6 contrib/chg/chg.c --- a/contrib/chg/chg.c Mon Jun 13 13:16:17 2016 +0100 +++ b/contrib/chg/chg.c Sat Jun 11 20:25:49 2016 +0100 @@ -417,21 +417,22 @@ abortmsgerrno("failed to set up signal handlers"); } -/* This implementation is based on hgext/pager.py (pre 369741ef7253) */ -static void setuppager(hgclient_t *hgc, const char *const args[], +/* This implementation is based on hgext/pager.py (post 369741ef7253) + * Return 0 if pager is not started, or pid of the pager */ +static pid_t setuppager(hgclient_t *hgc, const char *const args[], size_t argsize) { const char *pagercmd = hgc_getpager(hgc, args, argsize); if (!pagercmd) - return; + return 0; int pipefds[2]; if (pipe(pipefds) < 0) - return; + return 0; pid_t pid = fork(); if (pid < 0) goto error; - if (pid == 0) { + if (pid > 0) { close(pipefds[0]); if (dup2(pipefds[1], fileno(stdout)) < 0) goto error; @@ -441,7 +442,7 @@ } close(pipefds[1]); hgc_attachio(hgc); /* reattach to pager */ - return; + return pid; } else { dup2(pipefds[0], fileno(stdin)); close(pipefds[0]); @@ -451,13 +452,27 @@ if (r < 0) { abortmsgerrno("cannot start pager '%s'", pagercmd); } - return; + return 0; } error: close(pipefds[0]); close(pipefds[1]); abortmsgerrno("failed to prepare pager"); + return 0; +} + +static void waitpager(pid_t pid) +{ + /* close output streams to notify the pager its input ends */ + fclose(stdout); + fclose(stderr); + while (1) { + pid_t ret = waitpid(pid, NULL, 0); + if (ret == -1 && errno == EINTR) + continue; + break; + } } /* Run instructions sent from the server like unlink and set redirect path @@ -585,9 +600,12 @@ } setupsignalhandler(hgc_peerpid(hgc)); - setuppager(hgc, argv + 1, argc - 1); + pid_t pagerpid = setuppager(hgc, argv + 1, argc - 1); int exitcode = hgc_runcommand(hgc, argv + 1, argc - 1); hgc_close(hgc); freecmdserveropts(&opts); + if (pagerpid) + waitpager(pagerpid); + return exitcode; } diff -r e095b9e753f7 -r bb3d5c20eaf6 tests/test-pager.t --- a/tests/test-pager.t Mon Jun 13 13:16:17 2016 +0100 +++ b/tests/test-pager.t Sat Jun 11 20:25:49 2016 +0100 @@ -201,3 +201,24 @@ paged! '1\n' $ A=2 hg --config pager.attend-printa=yes printa paged! '2\n' + +Pager should not override the exit code of other commands + + $ cat >> $TESTTMP/fortytwo.py <<'EOF' + > from mercurial import cmdutil, commands + > cmdtable = {} + > command = cmdutil.command(cmdtable) + > @command('fortytwo', [], 'fortytwo', norepo=True) + > def fortytwo(ui, *opts): + > ui.write('42\n') + > return 42 + > EOF + + $ cat >> $HGRCPATH <<'EOF' + > [extensions] + > fortytwo = $TESTTMP/fortytwo.py + > EOF + + $ hg fortytwo --pager=on + paged! '42\n' + [42]