pager.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. # 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. import os
  15. import select
  16. import subprocess
  17. import sys
  18. import platform_utils
  19. active = False
  20. pager_process = None
  21. old_stdout = None
  22. old_stderr = None
  23. def RunPager(globalConfig):
  24. if not os.isatty(0) or not os.isatty(1):
  25. return
  26. pager = _SelectPager(globalConfig)
  27. if pager == '' or pager == 'cat':
  28. return
  29. if platform_utils.isWindows():
  30. _PipePager(pager)
  31. else:
  32. _ForkPager(pager)
  33. def TerminatePager():
  34. global pager_process, old_stdout, old_stderr
  35. if pager_process:
  36. sys.stdout.flush()
  37. sys.stderr.flush()
  38. pager_process.stdin.close()
  39. pager_process.wait()
  40. pager_process = None
  41. # Restore initial stdout/err in case there is more output in this process
  42. # after shutting down the pager process
  43. sys.stdout = old_stdout
  44. sys.stderr = old_stderr
  45. def _PipePager(pager):
  46. global pager_process, old_stdout, old_stderr
  47. assert pager_process is None, "Only one active pager process at a time"
  48. # Create pager process, piping stdout/err into its stdin
  49. pager_process = subprocess.Popen([pager], stdin=subprocess.PIPE, stdout=sys.stdout,
  50. stderr=sys.stderr)
  51. old_stdout = sys.stdout
  52. old_stderr = sys.stderr
  53. sys.stdout = pager_process.stdin
  54. sys.stderr = pager_process.stdin
  55. def _ForkPager(pager):
  56. global active
  57. # This process turns into the pager; a child it forks will
  58. # do the real processing and output back to the pager. This
  59. # is necessary to keep the pager in control of the tty.
  60. #
  61. try:
  62. r, w = os.pipe()
  63. pid = os.fork()
  64. if not pid:
  65. os.dup2(w, 1)
  66. os.dup2(w, 2)
  67. os.close(r)
  68. os.close(w)
  69. active = True
  70. return
  71. os.dup2(r, 0)
  72. os.close(r)
  73. os.close(w)
  74. _BecomePager(pager)
  75. except Exception:
  76. print("fatal: cannot start pager '%s'" % pager, file=sys.stderr)
  77. sys.exit(255)
  78. def _SelectPager(globalConfig):
  79. try:
  80. return os.environ['GIT_PAGER']
  81. except KeyError:
  82. pass
  83. pager = globalConfig.GetString('core.pager')
  84. if pager:
  85. return pager
  86. try:
  87. return os.environ['PAGER']
  88. except KeyError:
  89. pass
  90. return 'less'
  91. def _BecomePager(pager):
  92. # Delaying execution of the pager until we have output
  93. # ready works around a long-standing bug in popularly
  94. # available versions of 'less', a better 'more'.
  95. #
  96. _a, _b, _c = select.select([0], [], [0])
  97. os.environ['LESS'] = 'FRSX'
  98. try:
  99. os.execvp(pager, [pager])
  100. except OSError:
  101. os.execv('/bin/sh', ['sh', '-c', pager])