2006-02-28 22:26:21 +03: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 11:14:12 +04: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 09:03:28 +03:00
|
|
|
#include "sigchain.h"
|
2006-02-28 22:26:21 +03:00
|
|
|
|
2009-10-31 04:45:34 +03:00
|
|
|
#ifndef DEFAULT_PAGER
|
|
|
|
#define DEFAULT_PAGER "less"
|
|
|
|
#endif
|
|
|
|
|
2012-10-26 19:53:52 +04:00
|
|
|
struct pager_config {
|
|
|
|
const char *cmd;
|
|
|
|
int want;
|
|
|
|
char *value;
|
|
|
|
};
|
|
|
|
|
2006-02-28 22:26:21 +03:00
|
|
|
/*
|
2007-12-08 23:28:41 +03:00
|
|
|
* This is split up from the rest of git so that we can do
|
|
|
|
* something different on Windows.
|
2006-02-28 22:26:21 +03:00
|
|
|
*/
|
|
|
|
|
2009-12-30 13:53:57 +03:00
|
|
|
static const char *pager_argv[] = { 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 11:14:12 +04:00
|
|
|
static struct child_process pager_process;
|
|
|
|
|
2007-12-08 23:28:41 +03:00
|
|
|
static void wait_for_pager(void)
|
|
|
|
{
|
|
|
|
fflush(stdout);
|
|
|
|
fflush(stderr);
|
|
|
|
/* signal EOF to pager */
|
|
|
|
close(1);
|
|
|
|
close(2);
|
|
|
|
finish_command(&pager_process);
|
|
|
|
}
|
2006-02-28 22:26:21 +03: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 09:03:28 +03:00
|
|
|
static void wait_for_pager_signal(int signo)
|
|
|
|
{
|
|
|
|
wait_for_pager();
|
|
|
|
sigchain_pop(signo);
|
|
|
|
raise(signo);
|
|
|
|
}
|
|
|
|
|
2010-02-14 14:59:59 +03:00
|
|
|
const char *git_pager(int stdout_is_tty)
|
2006-02-28 22:26:21 +03:00
|
|
|
{
|
2009-10-31 04:41:27 +03:00
|
|
|
const char *pager;
|
2006-02-28 22:26:21 +03:00
|
|
|
|
2010-02-14 14:59:59 +03:00
|
|
|
if (!stdout_is_tty)
|
2009-10-31 04:41:27 +03:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
pager = getenv("GIT_PAGER");
|
2007-08-07 08:08:43 +04:00
|
|
|
if (!pager) {
|
|
|
|
if (!pager_program)
|
2008-05-14 21:46:53 +04:00
|
|
|
git_config(git_default_config, NULL);
|
2007-07-03 22:18:11 +04:00
|
|
|
pager = pager_program;
|
2007-08-07 08:08:43 +04:00
|
|
|
}
|
2006-07-31 17:27:00 +04:00
|
|
|
if (!pager)
|
|
|
|
pager = getenv("PAGER");
|
2006-04-16 06:44:25 +04:00
|
|
|
if (!pager)
|
2009-10-31 04:45:34 +03:00
|
|
|
pager = DEFAULT_PAGER;
|
pager: turn on "cat" optimization for DEFAULT_PAGER
If the user specifies a pager of "cat" (or the empty
string), whether it is in the environment or from config, we
automagically optimize it out to mean "no pager" and avoid
forking at all. We treat an empty pager variable similary.
However, we did not apply this optimization when
DEFAULT_PAGER was set to "cat" (or the empty string). There
is no reason to treat DEFAULT_PAGER any differently. The
optimization should not be user-visible (unless the user has
a bizarre "cat" in their PATH). And even if it is, we are
better off behaving consistently between the compile-time
default and the environment and config settings.
The stray "else" we are removing from this code was
introduced by 402461a (pager: do not fork a pager if PAGER
is set to empty., 2006-04-16). At that time, the line
directly above used:
if (!pager)
pager = "less";
as a fallback, meaning that it could not possibly trigger
the optimization. Later, a3d023d (Provide a build time
default-pager setting, 2009-10-30) turned that constant into
a build-time setting which could be anything, but didn't
loosen the "else" to let DEFAULT_PAGER use the optimization.
Noticed-by: Dale R. Worley <worley@alum.mit.edu>
Suggested-by: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-09-03 11:41:50 +04:00
|
|
|
if (!*pager || !strcmp(pager, "cat"))
|
2009-10-31 04:41:27 +03:00
|
|
|
pager = NULL;
|
|
|
|
|
|
|
|
return pager;
|
|
|
|
}
|
|
|
|
|
|
|
|
void setup_pager(void)
|
|
|
|
{
|
2010-02-14 14:59:59 +03:00
|
|
|
const char *pager = git_pager(isatty(1));
|
2009-10-31 04:41:27 +03:00
|
|
|
|
2012-04-13 14:54:34 +04:00
|
|
|
if (!pager || pager_in_use())
|
2006-04-16 06:44:25 +04:00
|
|
|
return;
|
|
|
|
|
2012-02-12 18:12:32 +04:00
|
|
|
/*
|
|
|
|
* force computing the width of the terminal before we redirect
|
|
|
|
* the standard output to the pager.
|
|
|
|
*/
|
|
|
|
(void) term_columns();
|
|
|
|
|
2011-08-18 09:02:29 +04:00
|
|
|
setenv("GIT_PAGER_IN_USE", "true", 1);
|
2006-06-07 03:58:40 +04:00
|
|
|
|
2007-12-08 23:28:41 +03:00
|
|
|
/* spawn the pager */
|
2009-12-30 13:53:57 +03:00
|
|
|
pager_argv[0] = pager;
|
|
|
|
pager_process.use_shell = 1;
|
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 11:14:12 +04:00
|
|
|
pager_process.argv = pager_argv;
|
|
|
|
pager_process.in = -1;
|
2009-09-11 21:45:07 +04:00
|
|
|
if (!getenv("LESS")) {
|
|
|
|
static const char *env[] = { "LESS=FRSX", NULL };
|
|
|
|
pager_process.env = env;
|
|
|
|
}
|
2007-12-08 23:28:41 +03:00
|
|
|
if (start_command(&pager_process))
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* original process continues, but writes to the pipe */
|
|
|
|
dup2(pager_process.in, 1);
|
2008-12-15 11:33:34 +03:00
|
|
|
if (isatty(2))
|
|
|
|
dup2(pager_process.in, 2);
|
2007-12-08 23:28:41 +03: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 09:03:28 +03:00
|
|
|
sigchain_push_common(wait_for_pager_signal);
|
2007-12-08 23:28:41 +03:00
|
|
|
atexit(wait_for_pager);
|
2006-02-28 22:26:21 +03:00
|
|
|
}
|
2007-12-11 09:27:33 +03:00
|
|
|
|
|
|
|
int pager_in_use(void)
|
|
|
|
{
|
|
|
|
const char *env;
|
|
|
|
env = getenv("GIT_PAGER_IN_USE");
|
|
|
|
return env ? git_config_bool("GIT_PAGER_IN_USE", env) : 0;
|
|
|
|
}
|
2012-02-12 18:12:32 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Return cached value (if set) or $COLUMNS environment variable (if
|
|
|
|
* set and positive) or ioctl(1, TIOCGWINSZ).ws_col (if positive),
|
|
|
|
* and default to 80 if all else fails.
|
|
|
|
*/
|
|
|
|
int term_columns(void)
|
|
|
|
{
|
|
|
|
static int term_columns_at_startup;
|
|
|
|
|
|
|
|
char *col_string;
|
|
|
|
int n_cols;
|
|
|
|
|
|
|
|
if (term_columns_at_startup)
|
|
|
|
return term_columns_at_startup;
|
|
|
|
|
|
|
|
term_columns_at_startup = 80;
|
|
|
|
|
|
|
|
col_string = getenv("COLUMNS");
|
|
|
|
if (col_string && (n_cols = atoi(col_string)) > 0)
|
|
|
|
term_columns_at_startup = n_cols;
|
|
|
|
#ifdef TIOCGWINSZ
|
|
|
|
else {
|
|
|
|
struct winsize ws;
|
|
|
|
if (!ioctl(1, TIOCGWINSZ, &ws) && ws.ws_col)
|
|
|
|
term_columns_at_startup = ws.ws_col;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return term_columns_at_startup;
|
|
|
|
}
|
2012-02-20 12:15:11 +04:00
|
|
|
|
2012-02-12 18:16:20 +04:00
|
|
|
/*
|
|
|
|
* How many columns do we need to show this number in decimal?
|
|
|
|
*/
|
|
|
|
int decimal_width(int number)
|
|
|
|
{
|
|
|
|
int i, width;
|
|
|
|
|
|
|
|
for (width = 1, i = 10; i <= number; width++)
|
|
|
|
i *= 10;
|
|
|
|
return width;
|
|
|
|
}
|
2012-10-26 19:53:52 +04:00
|
|
|
|
|
|
|
static int pager_command_config(const char *var, const char *value, void *data)
|
|
|
|
{
|
|
|
|
struct pager_config *c = data;
|
|
|
|
if (!prefixcmp(var, "pager.") && !strcmp(var + 6, c->cmd)) {
|
|
|
|
int b = git_config_maybe_bool(var, value);
|
|
|
|
if (b >= 0)
|
|
|
|
c->want = b;
|
|
|
|
else {
|
|
|
|
c->want = 1;
|
|
|
|
c->value = xstrdup(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */
|
|
|
|
int check_pager_config(const char *cmd)
|
|
|
|
{
|
|
|
|
struct pager_config c;
|
|
|
|
c.cmd = cmd;
|
|
|
|
c.want = -1;
|
|
|
|
c.value = NULL;
|
|
|
|
git_config(pager_command_config, &c);
|
|
|
|
if (c.value)
|
|
|
|
pager_program = c.value;
|
|
|
|
return c.want;
|
|
|
|
}
|