##// END OF EJS Templates
pager: drop python 2.4 hack around subprocess...
Pierre-Yves David -
r25201:59d79415 default
parent child Browse files
Show More
@@ -1,179 +1,145
1 # pager.py - display output using a pager
1 # pager.py - display output using a pager
2 #
2 #
3 # Copyright 2008 David Soria Parra <dsp@php.net>
3 # Copyright 2008 David Soria Parra <dsp@php.net>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7 #
7 #
8 # To load the extension, add it to your configuration file:
8 # To load the extension, add it to your configuration file:
9 #
9 #
10 # [extension]
10 # [extension]
11 # pager =
11 # pager =
12 #
12 #
13 # Run "hg help pager" to get info on configuration.
13 # Run "hg help pager" to get info on configuration.
14
14
15 '''browse command output with an external pager
15 '''browse command output with an external pager
16
16
17 To set the pager that should be used, set the application variable::
17 To set the pager that should be used, set the application variable::
18
18
19 [pager]
19 [pager]
20 pager = less -FRX
20 pager = less -FRX
21
21
22 If no pager is set, the pager extensions uses the environment variable
22 If no pager is set, the pager extensions uses the environment variable
23 $PAGER. If neither pager.pager, nor $PAGER is set, no pager is used.
23 $PAGER. If neither pager.pager, nor $PAGER is set, no pager is used.
24
24
25 You can disable the pager for certain commands by adding them to the
25 You can disable the pager for certain commands by adding them to the
26 pager.ignore list::
26 pager.ignore list::
27
27
28 [pager]
28 [pager]
29 ignore = version, help, update
29 ignore = version, help, update
30
30
31 You can also enable the pager only for certain commands using
31 You can also enable the pager only for certain commands using
32 pager.attend. Below is the default list of commands to be paged::
32 pager.attend. Below is the default list of commands to be paged::
33
33
34 [pager]
34 [pager]
35 attend = annotate, cat, diff, export, glog, log, qdiff
35 attend = annotate, cat, diff, export, glog, log, qdiff
36
36
37 Setting pager.attend to an empty value will cause all commands to be
37 Setting pager.attend to an empty value will cause all commands to be
38 paged.
38 paged.
39
39
40 If pager.attend is present, pager.ignore will be ignored.
40 If pager.attend is present, pager.ignore will be ignored.
41
41
42 Lastly, you can enable and disable paging for individual commands with
42 Lastly, you can enable and disable paging for individual commands with
43 the attend-<command> option. This setting takes precedence over
43 the attend-<command> option. This setting takes precedence over
44 existing attend and ignore options and defaults::
44 existing attend and ignore options and defaults::
45
45
46 [pager]
46 [pager]
47 attend-cat = false
47 attend-cat = false
48
48
49 To ignore global commands like :hg:`version` or :hg:`help`, you have
49 To ignore global commands like :hg:`version` or :hg:`help`, you have
50 to specify them in your user configuration file.
50 to specify them in your user configuration file.
51
51
52 The --pager=... option can also be used to control when the pager is
52 The --pager=... option can also be used to control when the pager is
53 used. Use a boolean value like yes, no, on, off, or use auto for
53 used. Use a boolean value like yes, no, on, off, or use auto for
54 normal behavior.
54 normal behavior.
55
55
56 '''
56 '''
57
57
58 import atexit, sys, os, signal, subprocess, errno, shlex
58 import atexit, sys, os, signal, subprocess
59 from mercurial import commands, dispatch, util, extensions, cmdutil
59 from mercurial import commands, dispatch, util, extensions, cmdutil
60 from mercurial.i18n import _
60 from mercurial.i18n import _
61
61
62 # Note for extension authors: ONLY specify testedwith = 'internal' for
62 # Note for extension authors: ONLY specify testedwith = 'internal' for
63 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
63 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
64 # be specifying the version(s) of Mercurial they are tested with, or
64 # be specifying the version(s) of Mercurial they are tested with, or
65 # leave the attribute unspecified.
65 # leave the attribute unspecified.
66 testedwith = 'internal'
66 testedwith = 'internal'
67
67
68 def _pagerfork(ui, p):
69 if not util.safehasattr(os, 'fork'):
70 sys.stdout = util.popen(p, 'wb')
71 if ui._isatty(sys.stderr):
72 sys.stderr = sys.stdout
73 return
74 fdin, fdout = os.pipe()
75 pid = os.fork()
76 if pid == 0:
77 os.close(fdin)
78 os.dup2(fdout, sys.stdout.fileno())
79 if ui._isatty(sys.stderr):
80 os.dup2(fdout, sys.stderr.fileno())
81 os.close(fdout)
82 return
83 os.dup2(fdin, sys.stdin.fileno())
84 os.close(fdin)
85 os.close(fdout)
86 try:
87 os.execvp('/bin/sh', ['/bin/sh', '-c', p])
88 except OSError, e:
89 if e.errno == errno.ENOENT:
90 # no /bin/sh, try executing the pager directly
91 args = shlex.split(p)
92 os.execvp(args[0], args)
93 else:
94 raise
95
96 def _pagersubprocess(ui, p):
68 def _pagersubprocess(ui, p):
97 pager = subprocess.Popen(p, shell=True, bufsize=-1,
69 pager = subprocess.Popen(p, shell=True, bufsize=-1,
98 close_fds=util.closefds, stdin=subprocess.PIPE,
70 close_fds=util.closefds, stdin=subprocess.PIPE,
99 stdout=sys.stdout, stderr=sys.stderr)
71 stdout=sys.stdout, stderr=sys.stderr)
100
72
101 stdout = os.dup(sys.stdout.fileno())
73 stdout = os.dup(sys.stdout.fileno())
102 stderr = os.dup(sys.stderr.fileno())
74 stderr = os.dup(sys.stderr.fileno())
103 os.dup2(pager.stdin.fileno(), sys.stdout.fileno())
75 os.dup2(pager.stdin.fileno(), sys.stdout.fileno())
104 if ui._isatty(sys.stderr):
76 if ui._isatty(sys.stderr):
105 os.dup2(pager.stdin.fileno(), sys.stderr.fileno())
77 os.dup2(pager.stdin.fileno(), sys.stderr.fileno())
106
78
107 @atexit.register
79 @atexit.register
108 def killpager():
80 def killpager():
109 if util.safehasattr(signal, "SIGINT"):
81 if util.safehasattr(signal, "SIGINT"):
110 signal.signal(signal.SIGINT, signal.SIG_IGN)
82 signal.signal(signal.SIGINT, signal.SIG_IGN)
111 pager.stdin.close()
83 pager.stdin.close()
112 os.dup2(stdout, sys.stdout.fileno())
84 os.dup2(stdout, sys.stdout.fileno())
113 os.dup2(stderr, sys.stderr.fileno())
85 os.dup2(stderr, sys.stderr.fileno())
114 pager.wait()
86 pager.wait()
115
87
116 def _runpager(ui, p):
88 def _runpager(ui, p):
117 # The subprocess module shipped with Python <= 2.4 is buggy (issue3533).
89 _pagersubprocess(ui, p)
118 # The compat version is buggy on Windows (issue3225), but has been shipping
119 # with hg for a long time. Preserve existing functionality.
120 if sys.version_info >= (2, 5):
121 _pagersubprocess(ui, p)
122 else:
123 _pagerfork(ui, p)
124
90
125 def uisetup(ui):
91 def uisetup(ui):
126 if '--debugger' in sys.argv or not ui.formatted():
92 if '--debugger' in sys.argv or not ui.formatted():
127 return
93 return
128
94
129 def pagecmd(orig, ui, options, cmd, cmdfunc):
95 def pagecmd(orig, ui, options, cmd, cmdfunc):
130 p = ui.config("pager", "pager", os.environ.get("PAGER"))
96 p = ui.config("pager", "pager", os.environ.get("PAGER"))
131 usepager = False
97 usepager = False
132 always = util.parsebool(options['pager'])
98 always = util.parsebool(options['pager'])
133 auto = options['pager'] == 'auto'
99 auto = options['pager'] == 'auto'
134
100
135 if not p:
101 if not p:
136 pass
102 pass
137 elif always:
103 elif always:
138 usepager = True
104 usepager = True
139 elif not auto:
105 elif not auto:
140 usepager = False
106 usepager = False
141 else:
107 else:
142 attend = ui.configlist('pager', 'attend', attended)
108 attend = ui.configlist('pager', 'attend', attended)
143 ignore = ui.configlist('pager', 'ignore')
109 ignore = ui.configlist('pager', 'ignore')
144 cmds, _ = cmdutil.findcmd(cmd, commands.table)
110 cmds, _ = cmdutil.findcmd(cmd, commands.table)
145
111
146 for cmd in cmds:
112 for cmd in cmds:
147 var = 'attend-%s' % cmd
113 var = 'attend-%s' % cmd
148 if ui.config('pager', var):
114 if ui.config('pager', var):
149 usepager = ui.configbool('pager', var)
115 usepager = ui.configbool('pager', var)
150 break
116 break
151 if (cmd in attend or
117 if (cmd in attend or
152 (cmd not in ignore and not attend)):
118 (cmd not in ignore and not attend)):
153 usepager = True
119 usepager = True
154 break
120 break
155
121
156 setattr(ui, 'pageractive', usepager)
122 setattr(ui, 'pageractive', usepager)
157
123
158 if usepager:
124 if usepager:
159 ui.setconfig('ui', 'formatted', ui.formatted(), 'pager')
125 ui.setconfig('ui', 'formatted', ui.formatted(), 'pager')
160 ui.setconfig('ui', 'interactive', False, 'pager')
126 ui.setconfig('ui', 'interactive', False, 'pager')
161 if util.safehasattr(signal, "SIGPIPE"):
127 if util.safehasattr(signal, "SIGPIPE"):
162 signal.signal(signal.SIGPIPE, signal.SIG_DFL)
128 signal.signal(signal.SIGPIPE, signal.SIG_DFL)
163 _runpager(ui, p)
129 _runpager(ui, p)
164 return orig(ui, options, cmd, cmdfunc)
130 return orig(ui, options, cmd, cmdfunc)
165
131
166 # Wrap dispatch._runcommand after color is loaded so color can see
132 # Wrap dispatch._runcommand after color is loaded so color can see
167 # ui.pageractive. Otherwise, if we loaded first, color's wrapped
133 # ui.pageractive. Otherwise, if we loaded first, color's wrapped
168 # dispatch._runcommand would run without having access to ui.pageractive.
134 # dispatch._runcommand would run without having access to ui.pageractive.
169 def afterloaded(loaded):
135 def afterloaded(loaded):
170 extensions.wrapfunction(dispatch, '_runcommand', pagecmd)
136 extensions.wrapfunction(dispatch, '_runcommand', pagecmd)
171 extensions.afterloaded('color', afterloaded)
137 extensions.afterloaded('color', afterloaded)
172
138
173 def extsetup(ui):
139 def extsetup(ui):
174 commands.globalopts.append(
140 commands.globalopts.append(
175 ('', 'pager', 'auto',
141 ('', 'pager', 'auto',
176 _("when to paginate (boolean, always, auto, or never)"),
142 _("when to paginate (boolean, always, auto, or never)"),
177 _('TYPE')))
143 _('TYPE')))
178
144
179 attended = ['annotate', 'cat', 'diff', 'export', 'glog', 'log', 'qdiff']
145 attended = ['annotate', 'cat', 'diff', 'export', 'glog', 'log', 'qdiff']
General Comments 0
You need to be logged in to leave comments. Login now