pager.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. #
  2. # Copyright (C) 2008 The Android Open Source Project
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. from __future__ import print_function
  16. import os
  17. import select
  18. import subprocess
  19. import sys
  20. import platform_utils
  21. active = False
  22. pager_process = None
  23. old_stdout = None
  24. old_stderr = None
  25. def RunPager(globalConfig):
  26. if not os.isatty(0) or not os.isatty(1):
  27. return
  28. pager = _SelectPager(globalConfig)
  29. if pager == '' or pager == 'cat':
  30. return
  31. if platform_utils.isWindows():
  32. _PipePager(pager);
  33. else:
  34. _ForkPager(pager)
  35. def TerminatePager():
  36. global pager_process, old_stdout, old_stderr
  37. if pager_process:
  38. sys.stdout.flush()
  39. sys.stderr.flush()
  40. pager_process.stdin.close()
  41. pager_process.wait();
  42. pager_process = None
  43. # Restore initial stdout/err in case there is more output in this process
  44. # after shutting down the pager process
  45. sys.stdout = old_stdout
  46. sys.stderr = old_stderr
  47. def _PipePager(pager):
  48. global pager_process, old_stdout, old_stderr
  49. assert pager_process is None, "Only one active pager process at a time"
  50. # Create pager process, piping stdout/err into its stdin
  51. pager_process = subprocess.Popen([pager], stdin=subprocess.PIPE, stdout=sys.stdout, stderr=sys.stderr)
  52. old_stdout = sys.stdout
  53. old_stderr = sys.stderr
  54. sys.stdout = pager_process.stdin
  55. sys.stderr = pager_process.stdin
  56. def _ForkPager(pager):
  57. global active
  58. # This process turns into the pager; a child it forks will
  59. # do the real processing and output back to the pager. This
  60. # is necessary to keep the pager in control of the tty.
  61. #
  62. try:
  63. r, w = os.pipe()
  64. pid = os.fork()
  65. if not pid:
  66. os.dup2(w, 1)
  67. os.dup2(w, 2)
  68. os.close(r)
  69. os.close(w)
  70. active = True
  71. return
  72. os.dup2(r, 0)
  73. os.close(r)
  74. os.close(w)
  75. _BecomePager(pager)
  76. except Exception:
  77. print("fatal: cannot start pager '%s'" % pager, file=sys.stderr)
  78. sys.exit(255)
  79. def _SelectPager(globalConfig):
  80. try:
  81. return os.environ['GIT_PAGER']
  82. except KeyError:
  83. pass
  84. pager = globalConfig.GetString('core.pager')
  85. if pager:
  86. return pager
  87. try:
  88. return os.environ['PAGER']
  89. except KeyError:
  90. pass
  91. return 'less'
  92. def _BecomePager(pager):
  93. # Delaying execution of the pager until we have output
  94. # ready works around a long-standing bug in popularly
  95. # available versions of 'less', a better 'more'.
  96. #
  97. _a, _b, _c = select.select([0], [], [0])
  98. os.environ['LESS'] = 'FRSX'
  99. try:
  100. os.execvp(pager, [pager])
  101. except OSError:
  102. os.execv('/bin/sh', ['sh', '-c', pager])