2006-03-01 03:26:21 +08:00
|
|
|
#include "cache.h"
|
2017-06-15 02:07:36 +08:00
|
|
|
#include "config.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"
|
2018-05-21 02:40:06 +08:00
|
|
|
#include "alias.h"
|
2006-03-01 03:26:21 +08:00
|
|
|
|
2009-10-31 09:45:34 +08:00
|
|
|
#ifndef DEFAULT_PAGER
|
|
|
|
#define DEFAULT_PAGER "less"
|
|
|
|
#endif
|
|
|
|
|
2014-08-20 03:09:35 +08:00
|
|
|
static struct child_process pager_process = CHILD_PROCESS_INIT;
|
2016-09-13 11:23:48 +08:00
|
|
|
static const char *pager_program;
|
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
|
|
|
|
2021-02-02 09:59:57 +08:00
|
|
|
static void close_pager_fds(void)
|
2007-12-09 04:28:41 +08:00
|
|
|
{
|
|
|
|
/* signal EOF to pager */
|
|
|
|
close(1);
|
|
|
|
close(2);
|
pager: don't use unsafe functions in signal handlers
Since the commit a3da8821208d (pager: do wait_for_pager on signal
death), we call wait_for_pager() in the pager's signal handler. The
recent bug report revealed that this causes a deadlock in glibc at
aborting "git log" [*1*]. When this happens, git process is left
unterminated, and it can't be killed by SIGTERM but only by SIGKILL.
The problem is that wait_for_pager() function does more than waiting
for pager process's termination, but it does cleanups and printing
errors. Unfortunately, the functions that may be used in a signal
handler are very limited [*2*]. Particularly, malloc(), free() and the
variants can't be used in a signal handler because they take a mutex
internally in glibc. This was the cause of the deadlock above. Other
than the direct calls of malloc/free, many functions calling
malloc/free can't be used. strerror() is such one, either.
Also the usage of fflush() and printf() in a signal handler is bad,
although it seems working so far. In a safer side, we should avoid
them, too.
This patch tries to reduce the calls of such functions in signal
handlers. wait_for_signal() takes a flag and avoids the unsafe
calls. Also, finish_command_in_signal() is introduced for the
same reason. There the free() calls are removed, and only waits for
the children without whining at errors.
[*1*] https://bugzilla.opensuse.org/show_bug.cgi?id=942297
[*2*] http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04_03
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Reviewed-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-09-04 17:35:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void wait_for_pager_atexit(void)
|
|
|
|
{
|
2021-02-02 09:59:57 +08:00
|
|
|
fflush(stdout);
|
|
|
|
fflush(stderr);
|
|
|
|
close_pager_fds();
|
|
|
|
finish_command(&pager_process);
|
2007-12-09 04:28:41 +08:00
|
|
|
}
|
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)
|
|
|
|
{
|
2021-02-02 09:59:57 +08:00
|
|
|
close_pager_fds();
|
|
|
|
finish_command_in_signal(&pager_process);
|
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_pop(signo);
|
|
|
|
raise(signo);
|
|
|
|
}
|
|
|
|
|
pager: stop loading git_default_config()
In git_pager(), we really only care about getting the value
of core.pager. But to do so, we use the git_default_config()
callback, which loads many other values. Ordinarily it
isn't a big deal to load this config an extra time, as it
simply overwrites the values from the previous run. But it's
a bad idea here, for two reasons:
1. The pager setup may be called very early in the
program, before we have found the git repository. As a
result, we may fail to read the correct repo-level
config file. This is a problem for core.pager, too,
but we should at least try to minimize the pollution to
other configured values.
2. Because we call setup_pager() from git.c, basically
every builtin command _may_ end up reading this config
and getting an implicit git_default_config() setup.
Which doesn't sound like a terrible thing, except that
we don't do it consistently; it triggers only when
stdout is a tty. So if a command forgets to load the
default config itself (but depends on it anyway), it
may appear to work, and then mysteriously fail when the
pager is not in use.
We can improve this by loading _just_ the core.pager config
from git_pager().
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-09-13 11:23:44 +08:00
|
|
|
static int core_pager_config(const char *var, const char *value, void *data)
|
|
|
|
{
|
|
|
|
if (!strcmp(var, "core.pager"))
|
|
|
|
return git_config_string(&pager_program, var, value);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-02-14 19:59:59 +08:00
|
|
|
const char *git_pager(int stdout_is_tty)
|
2006-03-01 03:26:21 +08:00
|
|
|
{
|
2009-10-31 09:41:27 +08:00
|
|
|
const char *pager;
|
2006-03-01 03:26:21 +08:00
|
|
|
|
2010-02-14 19:59:59 +08:00
|
|
|
if (!stdout_is_tty)
|
2009-10-31 09:41:27 +08:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
pager = getenv("GIT_PAGER");
|
2007-08-07 12:08:43 +08:00
|
|
|
if (!pager) {
|
|
|
|
if (!pager_program)
|
2016-09-13 11:23:56 +08:00
|
|
|
read_early_config(core_pager_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)
|
2009-10-31 09:45:34 +08: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 15:41:50 +08:00
|
|
|
if (!*pager || !strcmp(pager, "cat"))
|
2009-10-31 09:41:27 +08:00
|
|
|
pager = NULL;
|
|
|
|
|
|
|
|
return pager;
|
|
|
|
}
|
|
|
|
|
2020-07-29 04:25:12 +08:00
|
|
|
static void setup_pager_env(struct strvec *env)
|
2016-08-04 19:40:25 +08:00
|
|
|
{
|
|
|
|
const char **argv;
|
|
|
|
int i;
|
|
|
|
char *pager_env = xstrdup(PAGER_ENV);
|
|
|
|
int n = split_cmdline(pager_env, &argv);
|
|
|
|
|
|
|
|
if (n < 0)
|
|
|
|
die("malformed build-time PAGER_ENV: %s",
|
|
|
|
split_cmdline_strerror(n));
|
|
|
|
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
char *cp = strchr(argv[i], '=');
|
|
|
|
|
|
|
|
if (!cp)
|
|
|
|
die("malformed build-time PAGER_ENV");
|
|
|
|
|
|
|
|
*cp = '\0';
|
|
|
|
if (!getenv(argv[i])) {
|
|
|
|
*cp = '=';
|
2020-07-29 04:25:12 +08:00
|
|
|
strvec_push(env, argv[i]);
|
2016-08-04 19:40:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
free(pager_env);
|
|
|
|
free(argv);
|
|
|
|
}
|
|
|
|
|
2016-02-17 06:34:44 +08:00
|
|
|
void prepare_pager_args(struct child_process *pager_process, const char *pager)
|
|
|
|
{
|
2020-07-29 04:25:12 +08:00
|
|
|
strvec_push(&pager_process->args, pager);
|
2016-02-17 06:34:44 +08:00
|
|
|
pager_process->use_shell = 1;
|
2016-08-04 19:40:25 +08:00
|
|
|
setup_pager_env(&pager_process->env_array);
|
2019-02-23 06:25:04 +08:00
|
|
|
pager_process->trace2_child_class = "pager";
|
2016-02-17 06:34:44 +08:00
|
|
|
}
|
|
|
|
|
2009-10-31 09:41:27 +08:00
|
|
|
void setup_pager(void)
|
|
|
|
{
|
2010-02-14 19:59:59 +08:00
|
|
|
const char *pager = git_pager(isatty(1));
|
2009-10-31 09:41:27 +08:00
|
|
|
|
2014-04-22 04:46:22 +08:00
|
|
|
if (!pager)
|
2006-04-16 10:44:25 +08:00
|
|
|
return;
|
|
|
|
|
2012-02-12 22:12:32 +08:00
|
|
|
/*
|
pager: set COLUMNS to term_columns()
After we invoke the pager, our stdout goes to a pipe, not the
terminal, meaning we can no longer use an ioctl to get the
terminal width. For that reason, ad6c3739a3 (pager: find out
the terminal width before spawning the pager, 2012-02-12)
started caching the terminal width.
But that cache is only an in-process variable. Any programs
we spawn will also not be able to run that ioctl, but won't
have access to our cache. They'll end up falling back to our
80-column default.
You can see the problem with:
git tag --column=row
Since git-tag spawns a pager these days, its spawned
git-column helper will see neither the terminal on stdout
nor a useful COLUMNS value (assuming you do not export it
from your shell already). And you'll end up with 80-column
output in the pager, regardless of your terminal size.
We can fix this by setting COLUMNS right before spawning the
pager. That fixes this case, as well as any more complicated
ones (e.g., a paged program spawns another script which then
generates columnized output).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-05-11 17:25:16 +08:00
|
|
|
* After we redirect standard output, we won't be able to use an ioctl
|
|
|
|
* to get the terminal size. Let's grab it now, and then set $COLUMNS
|
|
|
|
* to communicate it to any sub-processes.
|
2012-02-12 22:12:32 +08:00
|
|
|
*/
|
pager: set COLUMNS to term_columns()
After we invoke the pager, our stdout goes to a pipe, not the
terminal, meaning we can no longer use an ioctl to get the
terminal width. For that reason, ad6c3739a3 (pager: find out
the terminal width before spawning the pager, 2012-02-12)
started caching the terminal width.
But that cache is only an in-process variable. Any programs
we spawn will also not be able to run that ioctl, but won't
have access to our cache. They'll end up falling back to our
80-column default.
You can see the problem with:
git tag --column=row
Since git-tag spawns a pager these days, its spawned
git-column helper will see neither the terminal on stdout
nor a useful COLUMNS value (assuming you do not export it
from your shell already). And you'll end up with 80-column
output in the pager, regardless of your terminal size.
We can fix this by setting COLUMNS right before spawning the
pager. That fixes this case, as well as any more complicated
ones (e.g., a paged program spawns another script which then
generates columnized output).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-05-11 17:25:16 +08:00
|
|
|
{
|
|
|
|
char buf[64];
|
|
|
|
xsnprintf(buf, sizeof(buf), "%d", term_columns());
|
|
|
|
setenv("COLUMNS", buf, 0);
|
|
|
|
}
|
2012-02-12 22:12:32 +08:00
|
|
|
|
2011-08-18 13:02:29 +08:00
|
|
|
setenv("GIT_PAGER_IN_USE", "true", 1);
|
2006-06-07 07:58:40 +08:00
|
|
|
|
2007-12-09 04:28:41 +08:00
|
|
|
/* spawn the pager */
|
2016-02-17 06:34:44 +08:00
|
|
|
prepare_pager_args(&pager_process, 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.in = -1;
|
2020-07-29 04:25:12 +08:00
|
|
|
strvec_push(&pager_process.env_array, "GIT_PAGER_IN_USE");
|
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);
|
pager: don't use unsafe functions in signal handlers
Since the commit a3da8821208d (pager: do wait_for_pager on signal
death), we call wait_for_pager() in the pager's signal handler. The
recent bug report revealed that this causes a deadlock in glibc at
aborting "git log" [*1*]. When this happens, git process is left
unterminated, and it can't be killed by SIGTERM but only by SIGKILL.
The problem is that wait_for_pager() function does more than waiting
for pager process's termination, but it does cleanups and printing
errors. Unfortunately, the functions that may be used in a signal
handler are very limited [*2*]. Particularly, malloc(), free() and the
variants can't be used in a signal handler because they take a mutex
internally in glibc. This was the cause of the deadlock above. Other
than the direct calls of malloc/free, many functions calling
malloc/free can't be used. strerror() is such one, either.
Also the usage of fflush() and printf() in a signal handler is bad,
although it seems working so far. In a safer side, we should avoid
them, too.
This patch tries to reduce the calls of such functions in signal
handlers. wait_for_signal() takes a flag and avoids the unsafe
calls. Also, finish_command_in_signal() is introduced for the
same reason. There the free() calls are removed, and only waits for
the children without whining at errors.
[*1*] https://bugzilla.opensuse.org/show_bug.cgi?id=942297
[*2*] http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04_03
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Reviewed-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-09-04 17:35:57 +08:00
|
|
|
atexit(wait_for_pager_atexit);
|
2006-03-01 03:26:21 +08:00
|
|
|
}
|
2007-12-11 14:27:33 +08:00
|
|
|
|
|
|
|
int pager_in_use(void)
|
|
|
|
{
|
2017-03-25 02:59:12 +08:00
|
|
|
return git_env_bool("GIT_PAGER_IN_USE", 0);
|
2007-12-11 14:27:33 +08:00
|
|
|
}
|
2012-02-12 22:12:32 +08: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 16:15:11 +08:00
|
|
|
|
pager: add a helper function to clear the last line in the terminal
There are a couple of places where we want to clear the last line on
the terminal, e.g. when a progress bar line is overwritten by a
shorter line, then the end of that progress line would remain visible,
unless we cover it up.
In 'progress.c' we did this by always appending a fixed number of
space characters to the next line (even if it was not shorter than the
previous), but as it turned out that fixed number was not quite large
enough, see the fix in 9f1fd84e15 (progress: clear previous progress
update dynamically, 2019-04-12). From then on we've been keeping
track of the length of the last displayed progress line and appending
the appropriate number of space characters to the next line, if
necessary, but, alas, this approach turned out to be error prone, see
the fix in 1aed1a5f25 (progress: avoid empty line when breaking the
progress line, 2019-05-19). The next patch in this series is about to
fix a case where we don't clear the last line, and on occasion do end
up with such garbage at the end of the line. It would be great if we
could do that without the need to deal with that without meticulously
computing the necessary number of space characters.
So add a helper function to clear the last line on the terminal using
an ANSI escape sequence, which has the advantage to clear the whole
line no matter how wide it is, even after the terminal width changed.
Such an escape sequence is not available on dumb terminals, though, so
in that case fall back to simply print a whole terminal width (as
reported by term_columns()) worth of space characters.
In 'editor.c' launch_specified_editor() already used this ANSI escape
sequence, so replace it with a call to this function.
Signed-off-by: SZEDER Gábor <szeder.dev@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-06-25 02:13:16 +08:00
|
|
|
/*
|
|
|
|
* Clear the entire line, leave cursor in first column.
|
|
|
|
*/
|
|
|
|
void term_clear_line(void)
|
|
|
|
{
|
|
|
|
if (is_terminal_dumb())
|
|
|
|
/*
|
|
|
|
* Fall back to print a terminal width worth of space
|
|
|
|
* characters (hoping that the terminal is still as wide
|
|
|
|
* as it was upon the first call to term_columns()).
|
|
|
|
*/
|
|
|
|
fprintf(stderr, "\r%*s\r", term_columns(), "");
|
|
|
|
else
|
|
|
|
/*
|
|
|
|
* On non-dumb terminals use an escape sequence to clear
|
|
|
|
* the whole line, no matter how wide the terminal.
|
|
|
|
*/
|
|
|
|
fputs("\r\033[K", stderr);
|
|
|
|
}
|
|
|
|
|
2012-02-12 22:16:20 +08:00
|
|
|
/*
|
|
|
|
* How many columns do we need to show this number in decimal?
|
|
|
|
*/
|
decimal_width: avoid integer overflow
The decimal_width function originally appeared in blame.c as
"lineno_width", and was designed for calculating the
print-width of small-ish integer values (line numbers in
text files). In ec7ff5b, it was made into a reusable
function, and in dc801e7, we started using it to align
diffstats.
Binary files in a diffstat show byte counts rather than line
numbers, meaning they can be quite large (e.g., consider
adding or removing a 2GB file). decimal_width is not up to
the challenge for two reasons:
1. It takes the value as an "int", whereas large files may
easily surpass this. The value may be truncated, in
which case we will produce an incorrect value.
2. It counts "up" by repeatedly multiplying another
integer by 10 until it surpasses the value. This can
cause an infinite loop when the value is close to the
largest representable integer.
For example, consider using a 32-bit signed integer,
and a value of 2,140,000,000 (just shy of 2^31-1).
We will count up and eventually see that 1,000,000,000
is smaller than our value. The next step would be to
multiply by 10 and see that 10,000,000,000 is too
large, ending the loop. But we can't represent that
value, and we have signed overflow.
This is technically undefined behavior, but a common
behavior is to lose the high bits, in which case our
iterator will certainly be less than the number. So
we'll keep multiplying, overflow again, and so on.
This patch changes the argument to a uintmax_t (the same
type we use to store the diffstat information for binary
filese), and counts "down" by repeatedly dividing our value
by 10.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-02-05 16:14:19 +08:00
|
|
|
int decimal_width(uintmax_t number)
|
2012-02-12 22:16:20 +08:00
|
|
|
{
|
decimal_width: avoid integer overflow
The decimal_width function originally appeared in blame.c as
"lineno_width", and was designed for calculating the
print-width of small-ish integer values (line numbers in
text files). In ec7ff5b, it was made into a reusable
function, and in dc801e7, we started using it to align
diffstats.
Binary files in a diffstat show byte counts rather than line
numbers, meaning they can be quite large (e.g., consider
adding or removing a 2GB file). decimal_width is not up to
the challenge for two reasons:
1. It takes the value as an "int", whereas large files may
easily surpass this. The value may be truncated, in
which case we will produce an incorrect value.
2. It counts "up" by repeatedly multiplying another
integer by 10 until it surpasses the value. This can
cause an infinite loop when the value is close to the
largest representable integer.
For example, consider using a 32-bit signed integer,
and a value of 2,140,000,000 (just shy of 2^31-1).
We will count up and eventually see that 1,000,000,000
is smaller than our value. The next step would be to
multiply by 10 and see that 10,000,000,000 is too
large, ending the loop. But we can't represent that
value, and we have signed overflow.
This is technically undefined behavior, but a common
behavior is to lose the high bits, in which case our
iterator will certainly be less than the number. So
we'll keep multiplying, overflow again, and so on.
This patch changes the argument to a uintmax_t (the same
type we use to store the diffstat information for binary
filese), and counts "down" by repeatedly dividing our value
by 10.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-02-05 16:14:19 +08:00
|
|
|
int width;
|
2012-02-12 22:16:20 +08:00
|
|
|
|
decimal_width: avoid integer overflow
The decimal_width function originally appeared in blame.c as
"lineno_width", and was designed for calculating the
print-width of small-ish integer values (line numbers in
text files). In ec7ff5b, it was made into a reusable
function, and in dc801e7, we started using it to align
diffstats.
Binary files in a diffstat show byte counts rather than line
numbers, meaning they can be quite large (e.g., consider
adding or removing a 2GB file). decimal_width is not up to
the challenge for two reasons:
1. It takes the value as an "int", whereas large files may
easily surpass this. The value may be truncated, in
which case we will produce an incorrect value.
2. It counts "up" by repeatedly multiplying another
integer by 10 until it surpasses the value. This can
cause an infinite loop when the value is close to the
largest representable integer.
For example, consider using a 32-bit signed integer,
and a value of 2,140,000,000 (just shy of 2^31-1).
We will count up and eventually see that 1,000,000,000
is smaller than our value. The next step would be to
multiply by 10 and see that 10,000,000,000 is too
large, ending the loop. But we can't represent that
value, and we have signed overflow.
This is technically undefined behavior, but a common
behavior is to lose the high bits, in which case our
iterator will certainly be less than the number. So
we'll keep multiplying, overflow again, and so on.
This patch changes the argument to a uintmax_t (the same
type we use to store the diffstat information for binary
filese), and counts "down" by repeatedly dividing our value
by 10.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-02-05 16:14:19 +08:00
|
|
|
for (width = 1; number >= 10; width++)
|
|
|
|
number /= 10;
|
2012-02-12 22:16:20 +08:00
|
|
|
return width;
|
|
|
|
}
|
2012-10-26 23:53:52 +08:00
|
|
|
|
2016-09-13 11:23:52 +08:00
|
|
|
struct pager_command_config_data {
|
|
|
|
const char *cmd;
|
|
|
|
int want;
|
|
|
|
char *value;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int pager_command_config(const char *var, const char *value, void *vdata)
|
2012-10-26 23:53:52 +08:00
|
|
|
{
|
2016-09-13 11:23:52 +08:00
|
|
|
struct pager_command_config_data *data = vdata;
|
|
|
|
const char *cmd;
|
|
|
|
|
|
|
|
if (skip_prefix(var, "pager.", &cmd) && !strcmp(cmd, data->cmd)) {
|
2017-08-08 02:20:49 +08:00
|
|
|
int b = git_parse_maybe_bool(value);
|
2012-10-26 23:53:52 +08:00
|
|
|
if (b >= 0)
|
2016-09-13 11:23:52 +08:00
|
|
|
data->want = b;
|
2012-10-26 23:53:52 +08:00
|
|
|
else {
|
2016-09-13 11:23:52 +08:00
|
|
|
data->want = 1;
|
|
|
|
data->value = xstrdup(value);
|
2012-10-26 23:53:52 +08:00
|
|
|
}
|
|
|
|
}
|
2016-09-13 11:23:52 +08:00
|
|
|
|
|
|
|
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_command_config_data data;
|
|
|
|
|
|
|
|
data.cmd = cmd;
|
|
|
|
data.want = -1;
|
|
|
|
data.value = NULL;
|
|
|
|
|
2016-09-13 11:23:56 +08:00
|
|
|
read_early_config(pager_command_config, &data);
|
2016-09-13 11:23:52 +08:00
|
|
|
|
|
|
|
if (data.value)
|
|
|
|
pager_program = data.value;
|
|
|
|
return data.want;
|
2012-10-26 23:53:52 +08:00
|
|
|
}
|