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