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
|
|
|
|
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;
|
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: 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(int in_signal)
|
2007-12-09 04:28:41 +08:00
|
|
|
{
|
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
|
|
|
if (!in_signal) {
|
|
|
|
fflush(stdout);
|
|
|
|
fflush(stderr);
|
|
|
|
}
|
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
|
|
|
if (in_signal)
|
|
|
|
finish_command_in_signal(&pager_process);
|
|
|
|
else
|
|
|
|
finish_command(&pager_process);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void wait_for_pager_atexit(void)
|
|
|
|
{
|
|
|
|
wait_for_pager(0);
|
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)
|
|
|
|
{
|
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
|
|
|
wait_for_pager(1);
|
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);
|
|
|
|
}
|
|
|
|
|
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)
|
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)
|
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;
|
|
|
|
}
|
|
|
|
|
2016-02-17 06:34:44 +08:00
|
|
|
void prepare_pager_args(struct child_process *pager_process, const char *pager)
|
|
|
|
{
|
|
|
|
argv_array_push(&pager_process->args, pager);
|
|
|
|
pager_process->use_shell = 1;
|
|
|
|
if (!getenv("LESS"))
|
|
|
|
argv_array_push(&pager_process->env_array, "LESS=FRX");
|
|
|
|
if (!getenv("LV"))
|
|
|
|
argv_array_push(&pager_process->env_array, "LV=-c");
|
|
|
|
}
|
|
|
|
|
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
|
|
|
/*
|
|
|
|
* force computing the width of the terminal before we redirect
|
|
|
|
* the standard output to the pager.
|
|
|
|
*/
|
|
|
|
(void) term_columns();
|
|
|
|
|
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;
|
2015-07-04 01:18:45 +08:00
|
|
|
argv_array_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)
|
|
|
|
{
|
|
|
|
const char *env;
|
|
|
|
env = getenv("GIT_PAGER_IN_USE");
|
|
|
|
return env ? git_config_bool("GIT_PAGER_IN_USE", env) : 0;
|
|
|
|
}
|
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
|
|
|
|
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
|
|
|
|
2014-08-08 00:21:23 +08:00
|
|
|
/* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */
|
|
|
|
int check_pager_config(const char *cmd)
|
2012-10-26 23:53:52 +08:00
|
|
|
{
|
2014-08-08 00:21:23 +08:00
|
|
|
int want = -1;
|
|
|
|
struct strbuf key = STRBUF_INIT;
|
|
|
|
const char *value = NULL;
|
|
|
|
strbuf_addf(&key, "pager.%s", cmd);
|
config: silence warnings for command names with invalid keys
When we are running the git command "foo", we may have to
look up the config keys "pager.foo" and "alias.foo". These
config schemes are mis-designed, as the command names can be
anything, but the config syntax has some restrictions. For
example:
$ git foo_bar
error: invalid key: pager.foo_bar
error: invalid key: alias.foo_bar
git: 'foo_bar' is not a git command. See 'git --help'.
You cannot name an alias with an underscore. And if you have
an external command with one, you cannot configure its
pager.
In the long run, we may develop a different config scheme
for these features. But in the near term (and because we'll
need to support the existing scheme indefinitely), we should
at least squelch the error messages shown above.
These errors come from git_config_parse_key. Ideally we
would pass a "quiet" flag to the config machinery, but there
are many layers between the pager code and the key parsing.
Passing a flag through all of those would be an invasive
change.
Instead, let's provide a config function to report on
whether a key is syntactically valid, and have the pager and
alias code skip lookup for bogus keys. We can build this
easily around the existing git_config_parse_key, with two
minor modifications:
1. We now handle a NULL store_key, to validate but not
write out the normalized key.
2. We accept a "quiet" flag to avoid writing to stderr.
This doesn't need to be a full-blown public "flags"
field, because we can make the existing implementation
a static helper function, keeping the mess contained
inside config.c.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-08-24 14:11:33 +08:00
|
|
|
if (git_config_key_is_valid(key.buf) &&
|
|
|
|
!git_config_get_value(key.buf, &value)) {
|
2014-08-08 00:21:23 +08:00
|
|
|
int b = git_config_maybe_bool(key.buf, value);
|
2012-10-26 23:53:52 +08:00
|
|
|
if (b >= 0)
|
2014-08-08 00:21:23 +08:00
|
|
|
want = b;
|
2012-10-26 23:53:52 +08:00
|
|
|
else {
|
2014-08-08 00:21:23 +08:00
|
|
|
want = 1;
|
|
|
|
pager_program = xstrdup(value);
|
2012-10-26 23:53:52 +08:00
|
|
|
}
|
|
|
|
}
|
2014-08-08 00:21:23 +08:00
|
|
|
strbuf_release(&key);
|
|
|
|
return want;
|
2012-10-26 23:53:52 +08:00
|
|
|
}
|