2006-03-01 03:26:21 +08:00
|
|
|
#include "cache.h"
|
spawn pager via run_command interface
This has two important effects:
1. The pager is now the _child_ process, instead of the
parent. This means that whatever spawned git (e.g., the
shell) will see the exit code of the git process, and
not the pager.
2. The mingw and regular code are now unified, which makes
the setup_pager function much simpler.
There are two caveats:
1. We used to call execlp directly on the pager, followed
by trying to exec it via the shall. We now just use the
shell (which is what mingw has always done). This may
have different results for pager names which contain
shell metacharacters.
It is also slightly less efficient because we
unnecessarily run the shell; however, pager spawning is
by definition an interactive task, so it shouldn't be
a huge problem.
2. The git process will remain in memory while the user
looks through the pager. This is potentially wasteful.
We could get around this by turning the parent into a
meta-process which spawns _both_ git and the pager,
collects the exit status from git, waits for both to
end, and then exits with git's exit code.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-07-22 15:14:12 +08:00
|
|
|
#include "run-command.h"
|
pager: do wait_for_pager on signal death
Since ea27a18 (spawn pager via run_command interface), the
original git process actually does git work, and the pager
is a child process (actually, on Windows it has always been
that way, since Windows lacks fork). After spawning the
pager, we register an atexit() handler that waits for the
pager to finish.
Unfortunately, that handler does not always run. In
particular, if git is killed by a signal, then we exit
immediately. The calling shell then thinks that git is done;
however, the pager is still trying to run and impact the
terminal. The result can be seen by running a long git
process with a pager (e.g., "git log -p") and hitting ^C.
Depending on your config, you should see the shell prompt,
but pressing a key causes the pager to do any terminal
de-initialization sequence.
This patch just intercepts any death-dealing signals and
waits for the pager before dying. Under typical less
configuration, that means hitting ^C will cause git to stop
generating output, but the pager will keep running.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-01-22 14:03:28 +08:00
|
|
|
#include "sigchain.h"
|
2006-03-01 03:26:21 +08:00
|
|
|
|
|
|
|
/*
|
2007-12-09 04:28:41 +08:00
|
|
|
* This is split up from the rest of git so that we can do
|
|
|
|
* something different on Windows.
|
2006-03-01 03:26:21 +08:00
|
|
|
*/
|
|
|
|
|
2007-12-11 14:27:33 +08:00
|
|
|
static int spawned_pager;
|
|
|
|
|
2009-09-16 16:20:22 +08:00
|
|
|
#ifndef WIN32
|
spawn pager via run_command interface
This has two important effects:
1. The pager is now the _child_ process, instead of the
parent. This means that whatever spawned git (e.g., the
shell) will see the exit code of the git process, and
not the pager.
2. The mingw and regular code are now unified, which makes
the setup_pager function much simpler.
There are two caveats:
1. We used to call execlp directly on the pager, followed
by trying to exec it via the shall. We now just use the
shell (which is what mingw has always done). This may
have different results for pager names which contain
shell metacharacters.
It is also slightly less efficient because we
unnecessarily run the shell; however, pager spawning is
by definition an interactive task, so it shouldn't be
a huge problem.
2. The git process will remain in memory while the user
looks through the pager. This is potentially wasteful.
We could get around this by turning the parent into a
meta-process which spawns _both_ git and the pager,
collects the exit status from git, waits for both to
end, and then exits with git's exit code.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-07-22 15:14:12 +08:00
|
|
|
static void pager_preexec(void)
|
2006-03-01 03:26:21 +08:00
|
|
|
{
|
2007-01-25 03:21:10 +08:00
|
|
|
/*
|
|
|
|
* Work around bug in "less" by not starting it until we
|
|
|
|
* have real input
|
|
|
|
*/
|
|
|
|
fd_set in;
|
|
|
|
|
|
|
|
FD_ZERO(&in);
|
|
|
|
FD_SET(0, &in);
|
|
|
|
select(1, &in, NULL, &in, NULL);
|
2006-03-01 03:26:21 +08:00
|
|
|
}
|
spawn pager via run_command interface
This has two important effects:
1. The pager is now the _child_ process, instead of the
parent. This means that whatever spawned git (e.g., the
shell) will see the exit code of the git process, and
not the pager.
2. The mingw and regular code are now unified, which makes
the setup_pager function much simpler.
There are two caveats:
1. We used to call execlp directly on the pager, followed
by trying to exec it via the shall. We now just use the
shell (which is what mingw has always done). This may
have different results for pager names which contain
shell metacharacters.
It is also slightly less efficient because we
unnecessarily run the shell; however, pager spawning is
by definition an interactive task, so it shouldn't be
a huge problem.
2. The git process will remain in memory while the user
looks through the pager. This is potentially wasteful.
We could get around this by turning the parent into a
meta-process which spawns _both_ git and the pager,
collects the exit status from git, waits for both to
end, and then exits with git's exit code.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-07-22 15:14:12 +08:00
|
|
|
#endif
|
2007-12-09 04:28:41 +08:00
|
|
|
|
|
|
|
static const char *pager_argv[] = { "sh", "-c", NULL, NULL };
|
spawn pager via run_command interface
This has two important effects:
1. The pager is now the _child_ process, instead of the
parent. This means that whatever spawned git (e.g., the
shell) will see the exit code of the git process, and
not the pager.
2. The mingw and regular code are now unified, which makes
the setup_pager function much simpler.
There are two caveats:
1. We used to call execlp directly on the pager, followed
by trying to exec it via the shall. We now just use the
shell (which is what mingw has always done). This may
have different results for pager names which contain
shell metacharacters.
It is also slightly less efficient because we
unnecessarily run the shell; however, pager spawning is
by definition an interactive task, so it shouldn't be
a huge problem.
2. The git process will remain in memory while the user
looks through the pager. This is potentially wasteful.
We could get around this by turning the parent into a
meta-process which spawns _both_ git and the pager,
collects the exit status from git, waits for both to
end, and then exits with git's exit code.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-07-22 15:14:12 +08:00
|
|
|
static struct child_process pager_process;
|
|
|
|
|
2007-12-09 04:28:41 +08:00
|
|
|
static void wait_for_pager(void)
|
|
|
|
{
|
|
|
|
fflush(stdout);
|
|
|
|
fflush(stderr);
|
|
|
|
/* signal EOF to pager */
|
|
|
|
close(1);
|
|
|
|
close(2);
|
|
|
|
finish_command(&pager_process);
|
|
|
|
}
|
2006-03-01 03:26:21 +08:00
|
|
|
|
pager: do wait_for_pager on signal death
Since ea27a18 (spawn pager via run_command interface), the
original git process actually does git work, and the pager
is a child process (actually, on Windows it has always been
that way, since Windows lacks fork). After spawning the
pager, we register an atexit() handler that waits for the
pager to finish.
Unfortunately, that handler does not always run. In
particular, if git is killed by a signal, then we exit
immediately. The calling shell then thinks that git is done;
however, the pager is still trying to run and impact the
terminal. The result can be seen by running a long git
process with a pager (e.g., "git log -p") and hitting ^C.
Depending on your config, you should see the shell prompt,
but pressing a key causes the pager to do any terminal
de-initialization sequence.
This patch just intercepts any death-dealing signals and
waits for the pager before dying. Under typical less
configuration, that means hitting ^C will cause git to stop
generating output, but the pager will keep running.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-01-22 14:03:28 +08:00
|
|
|
static void wait_for_pager_signal(int signo)
|
|
|
|
{
|
|
|
|
wait_for_pager();
|
|
|
|
sigchain_pop(signo);
|
|
|
|
raise(signo);
|
|
|
|
}
|
|
|
|
|
2006-03-01 03:26:21 +08:00
|
|
|
void setup_pager(void)
|
|
|
|
{
|
2006-07-31 21:27:00 +08:00
|
|
|
const char *pager = getenv("GIT_PAGER");
|
2006-03-01 03:26:21 +08:00
|
|
|
|
|
|
|
if (!isatty(1))
|
|
|
|
return;
|
2007-08-07 12:08:43 +08:00
|
|
|
if (!pager) {
|
|
|
|
if (!pager_program)
|
2008-05-15 01:46:53 +08:00
|
|
|
git_config(git_default_config, NULL);
|
2007-07-04 02:18:11 +08:00
|
|
|
pager = pager_program;
|
2007-08-07 12:08:43 +08:00
|
|
|
}
|
2006-07-31 21:27:00 +08:00
|
|
|
if (!pager)
|
|
|
|
pager = getenv("PAGER");
|
2006-04-16 10:44:25 +08:00
|
|
|
if (!pager)
|
|
|
|
pager = "less";
|
2006-04-16 16:46:08 +08:00
|
|
|
else if (!*pager || !strcmp(pager, "cat"))
|
2006-04-16 10:44:25 +08:00
|
|
|
return;
|
|
|
|
|
2007-12-11 14:27:33 +08:00
|
|
|
spawned_pager = 1; /* means we are emitting to terminal */
|
2006-06-07 07:58:40 +08:00
|
|
|
|
2007-12-09 04:28:41 +08:00
|
|
|
/* spawn the pager */
|
|
|
|
pager_argv[2] = pager;
|
spawn pager via run_command interface
This has two important effects:
1. The pager is now the _child_ process, instead of the
parent. This means that whatever spawned git (e.g., the
shell) will see the exit code of the git process, and
not the pager.
2. The mingw and regular code are now unified, which makes
the setup_pager function much simpler.
There are two caveats:
1. We used to call execlp directly on the pager, followed
by trying to exec it via the shall. We now just use the
shell (which is what mingw has always done). This may
have different results for pager names which contain
shell metacharacters.
It is also slightly less efficient because we
unnecessarily run the shell; however, pager spawning is
by definition an interactive task, so it shouldn't be
a huge problem.
2. The git process will remain in memory while the user
looks through the pager. This is potentially wasteful.
We could get around this by turning the parent into a
meta-process which spawns _both_ git and the pager,
collects the exit status from git, waits for both to
end, and then exits with git's exit code.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-07-22 15:14:12 +08:00
|
|
|
pager_process.argv = pager_argv;
|
|
|
|
pager_process.in = -1;
|
2009-09-12 01:45:07 +08:00
|
|
|
if (!getenv("LESS")) {
|
|
|
|
static const char *env[] = { "LESS=FRSX", NULL };
|
|
|
|
pager_process.env = env;
|
|
|
|
}
|
2009-09-16 16:20:22 +08:00
|
|
|
#ifndef WIN32
|
spawn pager via run_command interface
This has two important effects:
1. The pager is now the _child_ process, instead of the
parent. This means that whatever spawned git (e.g., the
shell) will see the exit code of the git process, and
not the pager.
2. The mingw and regular code are now unified, which makes
the setup_pager function much simpler.
There are two caveats:
1. We used to call execlp directly on the pager, followed
by trying to exec it via the shall. We now just use the
shell (which is what mingw has always done). This may
have different results for pager names which contain
shell metacharacters.
It is also slightly less efficient because we
unnecessarily run the shell; however, pager spawning is
by definition an interactive task, so it shouldn't be
a huge problem.
2. The git process will remain in memory while the user
looks through the pager. This is potentially wasteful.
We could get around this by turning the parent into a
meta-process which spawns _both_ git and the pager,
collects the exit status from git, waits for both to
end, and then exits with git's exit code.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-07-22 15:14:12 +08:00
|
|
|
pager_process.preexec_cb = pager_preexec;
|
|
|
|
#endif
|
2007-12-09 04:28:41 +08:00
|
|
|
if (start_command(&pager_process))
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* original process continues, but writes to the pipe */
|
|
|
|
dup2(pager_process.in, 1);
|
2008-12-15 16:33:34 +08:00
|
|
|
if (isatty(2))
|
|
|
|
dup2(pager_process.in, 2);
|
2007-12-09 04:28:41 +08:00
|
|
|
close(pager_process.in);
|
|
|
|
|
|
|
|
/* this makes sure that the parent terminates after the pager */
|
pager: do wait_for_pager on signal death
Since ea27a18 (spawn pager via run_command interface), the
original git process actually does git work, and the pager
is a child process (actually, on Windows it has always been
that way, since Windows lacks fork). After spawning the
pager, we register an atexit() handler that waits for the
pager to finish.
Unfortunately, that handler does not always run. In
particular, if git is killed by a signal, then we exit
immediately. The calling shell then thinks that git is done;
however, the pager is still trying to run and impact the
terminal. The result can be seen by running a long git
process with a pager (e.g., "git log -p") and hitting ^C.
Depending on your config, you should see the shell prompt,
but pressing a key causes the pager to do any terminal
de-initialization sequence.
This patch just intercepts any death-dealing signals and
waits for the pager before dying. Under typical less
configuration, that means hitting ^C will cause git to stop
generating output, but the pager will keep running.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-01-22 14:03:28 +08:00
|
|
|
sigchain_push_common(wait_for_pager_signal);
|
2007-12-09 04:28:41 +08:00
|
|
|
atexit(wait_for_pager);
|
2006-03-01 03:26:21 +08:00
|
|
|
}
|
2007-12-11 14:27:33 +08:00
|
|
|
|
|
|
|
int pager_in_use(void)
|
|
|
|
{
|
|
|
|
const char *env;
|
|
|
|
|
|
|
|
if (spawned_pager)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
env = getenv("GIT_PAGER_IN_USE");
|
|
|
|
return env ? git_config_bool("GIT_PAGER_IN_USE", env) : 0;
|
|
|
|
}
|