git-repo/pager.py

127 lines
3.3 KiB
Python
Raw Normal View History

2008-10-21 14:00:00 +00:00
# Copyright (C) 2008 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import select
import subprocess
2008-10-21 14:00:00 +00:00
import sys
import platform_utils
2008-10-21 14:00:00 +00:00
active = False
pager_process = None
old_stdout = None
old_stderr = None
2008-10-21 14:00:00 +00:00
2008-10-21 14:00:00 +00:00
def RunPager(globalConfig):
if not os.isatty(0) or not os.isatty(1):
return
pager = _SelectPager(globalConfig)
if pager == "" or pager == "cat":
return
2008-10-21 14:00:00 +00:00
if platform_utils.isWindows():
_PipePager(pager)
else:
_ForkPager(pager)
def TerminatePager():
global pager_process, old_stdout, old_stderr
if pager_process:
sys.stdout.flush()
sys.stderr.flush()
pager_process.stdin.close()
pager_process.wait()
pager_process = None
# Restore initial stdout/err in case there is more output in this
# process after shutting down the pager process.
sys.stdout = old_stdout
sys.stderr = old_stderr
def _PipePager(pager):
global pager_process, old_stdout, old_stderr
assert pager_process is None, "Only one active pager process at a time"
# Create pager process, piping stdout/err into its stdin.
try:
pager_process = subprocess.Popen(
[pager], stdin=subprocess.PIPE, stdout=sys.stdout, stderr=sys.stderr
)
except FileNotFoundError:
sys.exit(f'fatal: cannot start pager "{pager}"')
old_stdout = sys.stdout
old_stderr = sys.stderr
sys.stdout = pager_process.stdin
sys.stderr = pager_process.stdin
def _ForkPager(pager):
global active
# This process turns into the pager; a child it forks will
# do the real processing and output back to the pager. This
# is necessary to keep the pager in control of the tty.
try:
r, w = os.pipe()
pid = os.fork()
if not pid:
os.dup2(w, 1)
os.dup2(w, 2)
os.close(r)
os.close(w)
active = True
return
os.dup2(r, 0)
os.close(r)
os.close(w)
_BecomePager(pager)
except Exception:
print("fatal: cannot start pager '%s'" % pager, file=sys.stderr)
sys.exit(255)
2008-10-21 14:00:00 +00:00
2008-10-21 14:00:00 +00:00
def _SelectPager(globalConfig):
try:
return os.environ["GIT_PAGER"]
except KeyError:
pass
2008-10-21 14:00:00 +00:00
pager = globalConfig.GetString("core.pager")
if pager:
return pager
2008-10-21 14:00:00 +00:00
try:
return os.environ["PAGER"]
except KeyError:
pass
2008-10-21 14:00:00 +00:00
return "less"
2008-10-21 14:00:00 +00:00
2008-10-21 14:00:00 +00:00
def _BecomePager(pager):
# Delaying execution of the pager until we have output
# ready works around a long-standing bug in popularly
# available versions of 'less', a better 'more'.
_a, _b, _c = select.select([0], [], [0])
os.environ["LESS"] = "FRSX"
try:
os.execvp(pager, [pager])
except OSError:
os.execv("/bin/sh", ["sh", "-c", pager])