blob: 352923d92b3cb625a3c1de7d6da3424122f1d979 [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
David Pursehouse3cda50a2020-02-13 13:17:03 +090059 pager_process = subprocess.Popen([pager], stdin=subprocess.PIPE, stdout=sys.stdout,
60 stderr=sys.stderr)
Renaud Paquaye8595e92016-11-01 15:51:59 -070061 old_stdout = sys.stdout
62 old_stderr = sys.stderr
63 sys.stdout = pager_process.stdin
64 sys.stderr = pager_process.stdin
65
David Pursehouse819827a2020-02-12 15:20:19 +090066
Renaud Paquaye8595e92016-11-01 15:51:59 -070067def _ForkPager(pager):
68 global active
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070069 # This process turns into the pager; a child it forks will
70 # do the real processing and output back to the pager. This
71 # is necessary to keep the pager in control of the tty.
72 #
73 try:
74 r, w = os.pipe()
75 pid = os.fork()
76 if not pid:
77 os.dup2(w, 1)
78 os.dup2(w, 2)
79 os.close(r)
80 os.close(w)
81 active = True
82 return
83
84 os.dup2(r, 0)
85 os.close(r)
86 os.close(w)
87
88 _BecomePager(pager)
89 except Exception:
Sarah Owenscecd1d82012-11-01 22:59:27 -070090 print("fatal: cannot start pager '%s'" % pager, file=sys.stderr)
David Pursehouse01f443d2012-10-03 19:11:28 +090091 sys.exit(255)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070092
David Pursehouse819827a2020-02-12 15:20:19 +090093
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070094def _SelectPager(globalConfig):
95 try:
96 return os.environ['GIT_PAGER']
97 except KeyError:
98 pass
99
100 pager = globalConfig.GetString('core.pager')
101 if pager:
102 return pager
103
104 try:
105 return os.environ['PAGER']
106 except KeyError:
107 pass
108
109 return 'less'
110
David Pursehouse819827a2020-02-12 15:20:19 +0900111
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700112def _BecomePager(pager):
113 # Delaying execution of the pager until we have output
114 # ready works around a long-standing bug in popularly
115 # available versions of 'less', a better 'more'.
116 #
David Pursehouse8a68ff92012-09-24 12:15:13 +0900117 _a, _b, _c = select.select([0], [], [0])
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700118
119 os.environ['LESS'] = 'FRSX'
120
121 try:
122 os.execvp(pager, [pager])
David Pursehouse8a68ff92012-09-24 12:15:13 +0900123 except OSError:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700124 os.execv('/bin/sh', ['sh', '-c', pager])