git/pager.c

124 lines
2.2 KiB
C
Raw Normal View History

#include "cache.h"
/*
* This is split up from the rest of git so that we can do
* something different on Windows.
*/
static int spawned_pager;
#ifndef __MINGW32__
static void run_pager(const char *pager)
{
/*
* 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);
execlp(pager, pager, NULL);
execl("/bin/sh", "sh", "-c", pager, NULL);
}
#else
#include "run-command.h"
static const char *pager_argv[] = { "sh", "-c", NULL, NULL };
static struct child_process pager_process = {
.argv = pager_argv,
.in = -1
};
static void wait_for_pager(void)
{
fflush(stdout);
fflush(stderr);
/* signal EOF to pager */
close(1);
close(2);
finish_command(&pager_process);
}
#endif
void setup_pager(void)
{
#ifndef __MINGW32__
pid_t pid;
int fd[2];
#endif
const char *pager = getenv("GIT_PAGER");
if (!isatty(1))
return;
if (!pager) {
if (!pager_program)
git_config(git_default_config, NULL);
pager = pager_program;
}
if (!pager)
pager = getenv("PAGER");
if (!pager)
pager = "less";
else if (!*pager || !strcmp(pager, "cat"))
return;
spawned_pager = 1; /* means we are emitting to terminal */
#ifndef __MINGW32__
if (pipe(fd) < 0)
return;
pid = fork();
if (pid < 0) {
close(fd[0]);
close(fd[1]);
return;
}
/* return in the child */
if (!pid) {
dup2(fd[1], 1);
sending errors to stdout under $PAGER If you do this (and you are not an Emacs user who uses PAGER=cat in your *shell* buffer): $ git init Initialized empty Git repository in .git/ $ echo hello world >foo $ H=$(git hash-object -w foo) $ git tag -a foo-tag -m "Tags $H" $H $ echo $H 3b18e512dba79e4c8300dd08aeb37f8e728b8dad $ rm -f .git/objects/3b/18e5* $ git show foo-tag tag foo-tag Tagger: Junio C Hamano <gitster@pobox.com> Date: Sat Feb 16 10:43:23 2008 -0800 Tags 3b18e512dba79e4c8300dd08aeb37f8e728b8dad you do not get any indication of error. If you are careful, you would notice that no contents from the tagged object is displayed, but that is about it. If you run the "show" command without pager, however, you will see the error: $ git --no-pager show foo-tag tag foo-tag Tagger: Junio C Hamano <gitster@pobox.com> Date: Sat Feb 16 10:43:23 2008 -0800 Tags 3b18e512dba79e4c8300dd08aeb37f8e728b8dad error: Could not read object 3b18e512dba79e4c8300dd08aeb37f8e728b8dad Because we spawn the pager as the foreground process and feed its input via pipe from the real command, we cannot affect the exit status the shell sees from git command when the pager is in use (I think there is not much gain we can have by working it around, though). But at least it may make sense to show the error message to the user sitting in front of the pager. [jc: Edgar Toernig suggested a much nicer implementation than what I originally posted, which I took.] Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-02-16 19:15:41 +00:00
dup2(fd[1], 2);
close(fd[0]);
close(fd[1]);
return;
}
/* The original process turns into the PAGER */
dup2(fd[0], 0);
close(fd[0]);
close(fd[1]);
setenv("LESS", "FRSX", 0);
run_pager(pager);
die("unable to execute pager '%s'", pager);
exit(255);
#else
/* spawn the pager */
pager_argv[2] = pager;
if (start_command(&pager_process))
return;
/* original process continues, but writes to the pipe */
dup2(pager_process.in, 1);
if (isatty(2))
dup2(pager_process.in, 2);
close(pager_process.in);
/* this makes sure that the parent terminates after the pager */
atexit(wait_for_pager);
#endif
}
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;
}