pager: fork and exec pager as parent process stable
authorBrodie Rao <brodie@bitheap.org>
Mon, 03 May 2010 14:00:34 -0500
branchstable
changeset 11182 3c368a1c962d
parent 11171 3b3261f6d9ba
child 11186 a890cc501501
child 11187 db2897926d14
pager: fork and exec pager as parent process With the pager as the child process instead of the parent process, the termination of the parent Mercurial process can cause the terminal to return before the pager exits. Inverting the relationship prevents that issue. Platforms without fork() will continue to use util.popen().
hgext/color.py
hgext/pager.py
--- a/hgext/color.py	Thu May 13 11:30:50 2010 -0500
+++ b/hgext/color.py	Mon May 03 14:00:34 2010 -0500
@@ -333,11 +333,14 @@
 
 def _setupcmd(ui, cmd, table, func, effectsmap):
     '''patch in command to command table and load effect map'''
+    # check isatty() before anything else changes it (like pager)
+    isatty = sys.__stdout__.isatty()
+
     def nocolor(orig, *args, **opts):
 
         if (opts['no_color'] or opts['color'] == 'never' or
             (opts['color'] == 'auto' and (os.environ.get('TERM') == 'dumb'
-                                          or not sys.__stdout__.isatty()))):
+                                          or not isatty))):
             del opts['no_color']
             del opts['color']
             return orig(*args, **opts)
--- a/hgext/pager.py	Thu May 13 11:30:50 2010 -0500
+++ b/hgext/pager.py	Mon May 03 14:00:34 2010 -0500
@@ -49,9 +49,27 @@
 specify them in the global .hgrc
 '''
 
-import sys, os, signal
+import sys, os, signal, shlex
 from mercurial import dispatch, util, extensions
 
+def _runpager(p):
+    if not hasattr(os, 'fork'):
+        sys.stderr = sys.stdout = util.popen(p, 'wb')
+        return
+    fdin, fdout = os.pipe()
+    pid = os.fork()
+    if pid == 0:
+        os.close(fdin)
+        os.dup2(fdout, sys.stdout.fileno())
+        os.dup2(fdout, sys.stderr.fileno())
+        os.close(fdout)
+        return
+    os.dup2(fdin, sys.stdin.fileno())
+    os.close(fdin)
+    os.close(fdout)
+    args = shlex.split(p)
+    os.execvp(args[0], args)
+
 def uisetup(ui):
     def pagecmd(orig, ui, options, cmd, cmdfunc):
         p = ui.config("pager", "pager", os.environ.get("PAGER"))
@@ -60,7 +78,7 @@
             if (cmd in attend or
                 (cmd not in ui.configlist('pager', 'ignore') and not attend)):
                 ui.setconfig('ui', 'interactive', False)
-                sys.stderr = sys.stdout = util.popen(p, "wb")
+                _runpager(p)
                 if ui.configbool('pager', 'quiet'):
                     signal.signal(signal.SIGPIPE, signal.SIG_DFL)
         return orig(ui, options, cmd, cmdfunc)