blob: 438597ef0dce50a580e0f773f32707b203a03743 [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001# Copyright (C) 2008 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15import os
16import select
Renaud Paquaye8595e92016-11-01 15:51:59 -070017import subprocess
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070018import sys
19
Renaud Paquaye8595e92016-11-01 15:51:59 -070020import platform_utils
21
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070022active = False
Renaud Paquaye8595e92016-11-01 15:51:59 -070023pager_process = None
24old_stdout = None
25old_stderr = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070026
David Pursehouse819827a2020-02-12 15:20:19 +090027
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070028def RunPager(globalConfig):
Shawn O. Pearce8f82a4f2009-04-01 07:24:22 -070029 if not os.isatty(0) or not os.isatty(1):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070030 return
31 pager = _SelectPager(globalConfig)
32 if pager == '' or pager == 'cat':
33 return
34
Renaud Paquaye8595e92016-11-01 15:51:59 -070035 if platform_utils.isWindows():
David Pursehouse03ae9922020-02-12 14:14:57 +090036 _PipePager(pager)
Renaud Paquaye8595e92016-11-01 15:51:59 -070037 else:
38 _ForkPager(pager)
39
David Pursehouse819827a2020-02-12 15:20:19 +090040
Renaud Paquaye8595e92016-11-01 15:51:59 -070041def TerminatePager():
42 global pager_process, old_stdout, old_stderr
43 if pager_process:
44 sys.stdout.flush()
45 sys.stderr.flush()
46 pager_process.stdin.close()
David Pursehouse03ae9922020-02-12 14:14:57 +090047 pager_process.wait()
Renaud Paquaye8595e92016-11-01 15:51:59 -070048 pager_process = None
49 # Restore initial stdout/err in case there is more output in this process
50 # after shutting down the pager process
51 sys.stdout = old_stdout
52 sys.stderr = old_stderr
53
David Pursehouse819827a2020-02-12 15:20:19 +090054
Renaud Paquaye8595e92016-11-01 15:51:59 -070055def _PipePager(pager):
56 global pager_process, old_stdout, old_stderr
57 assert pager_process is None, "Only one active pager process at a time"
58 # Create pager process, piping stdout/err into its stdin
Mike Frysinger3c2d8072022-08-18 07:45:43 -040059 try:
60 pager_process = subprocess.Popen([pager], stdin=subprocess.PIPE, stdout=sys.stdout,
61 stderr=sys.stderr)
62 except FileNotFoundError:
63 sys.exit(f'fatal: cannot start pager "{pager}"')
Renaud Paquaye8595e92016-11-01 15:51:59 -070064 old_stdout = sys.stdout
65 old_stderr = sys.stderr
66 sys.stdout = pager_process.stdin
67 sys.stderr = pager_process.stdin
68
David Pursehouse819827a2020-02-12 15:20:19 +090069
Renaud Paquaye8595e92016-11-01 15:51:59 -070070def _ForkPager(pager):
71 global active
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070072 # This process turns into the pager; a child it forks will
73 # do the real processing and output back to the pager. This
74 # is necessary to keep the pager in control of the tty.
75 #
76 try:
77 r, w = os.pipe()
78 pid = os.fork()
79 if not pid:
80 os.dup2(w, 1)
81 os.dup2(w, 2)
82 os.close(r)
83 os.close(w)
84 active = True
85 return
86
87 os.dup2(r, 0)
88 os.close(r)
89 os.close(w)
90
91 _BecomePager(pager)
92 except Exception:
Sarah Owenscecd1d82012-11-01 22:59:27 -070093 print("fatal: cannot start pager '%s'" % pager, file=sys.stderr)
David Pursehouse01f443d2012-10-03 19:11:28 +090094 sys.exit(255)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070095
David Pursehouse819827a2020-02-12 15:20:19 +090096
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070097def _SelectPager(globalConfig):
98 try:
99 return os.environ['GIT_PAGER']
100 except KeyError:
101 pass
102
103 pager = globalConfig.GetString('core.pager')
104 if pager:
105 return pager
106
107 try:
108 return os.environ['PAGER']
109 except KeyError:
110 pass
111
112 return 'less'
113
David Pursehouse819827a2020-02-12 15:20:19 +0900114
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700115def _BecomePager(pager):
116 # Delaying execution of the pager until we have output
117 # ready works around a long-standing bug in popularly
118 # available versions of 'less', a better 'more'.
119 #
David Pursehouse8a68ff92012-09-24 12:15:13 +0900120 _a, _b, _c = select.select([0], [], [0])
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700121
122 os.environ['LESS'] = 'FRSX'
123
124 try:
125 os.execvp(pager, [pager])
David Pursehouse8a68ff92012-09-24 12:15:13 +0900126 except OSError:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700127 os.execv('/bin/sh', ['sh', '-c', pager])