pysh.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. # pysh.py - command processing for pysh.
  2. #
  3. # Copyright 2007 Patrick Mezard
  4. #
  5. # This software may be used and distributed according to the terms
  6. # of the GNU General Public License, incorporated herein by reference.
  7. import optparse
  8. import os
  9. import sys
  10. import interp
  11. SH_OPT = optparse.OptionParser(prog='pysh', usage="%prog [OPTIONS]", version='0.1')
  12. SH_OPT.add_option('-c', action='store_true', dest='command_string', default=None,
  13. help='A string that shall be interpreted by the shell as one or more commands')
  14. SH_OPT.add_option('--redirect-to', dest='redirect_to', default=None,
  15. help='Redirect script commands stdout and stderr to the specified file')
  16. # See utility_command in builtin.py about the reason for this flag.
  17. SH_OPT.add_option('--redirected', dest='redirected', action='store_true', default=False,
  18. help='Tell the interpreter that stdout and stderr are actually the same objects, which is really stdout')
  19. SH_OPT.add_option('--debug-parsing', action='store_true', dest='debug_parsing', default=False,
  20. help='Trace PLY execution')
  21. SH_OPT.add_option('--debug-tree', action='store_true', dest='debug_tree', default=False,
  22. help='Display the generated syntax tree.')
  23. SH_OPT.add_option('--debug-cmd', action='store_true', dest='debug_cmd', default=False,
  24. help='Trace command execution before parameters expansion and exit status.')
  25. SH_OPT.add_option('--debug-utility', action='store_true', dest='debug_utility', default=False,
  26. help='Trace utility calls, after parameters expansions')
  27. SH_OPT.add_option('--ast', action='store_true', dest='ast', default=False,
  28. help='Encoded commands to execute in a subprocess')
  29. SH_OPT.add_option('--profile', action='store_true', default=False,
  30. help='Profile pysh run')
  31. def split_args(args):
  32. # Separate shell arguments from command ones
  33. # Just stop at the first argument not starting with a dash. I know, this is completely broken,
  34. # it ignores files starting with a dash or may take option values for command file. This is not
  35. # supposed to happen for now
  36. command_index = len(args)
  37. for i,arg in enumerate(args):
  38. if not arg.startswith('-'):
  39. command_index = i
  40. break
  41. return args[:command_index], args[command_index:]
  42. def fixenv(env):
  43. path = env.get('PATH')
  44. if path is not None:
  45. parts = path.split(os.pathsep)
  46. # Remove Windows utilities from PATH, they are useless at best and
  47. # some of them (find) may be confused with other utilities.
  48. parts = [p for p in parts if 'system32' not in p.lower()]
  49. env['PATH'] = os.pathsep.join(parts)
  50. if env.get('HOME') is None:
  51. # Several utilities, including cvsps, cannot work without
  52. # a defined HOME directory.
  53. env['HOME'] = os.path.expanduser('~')
  54. return env
  55. def _sh(cwd, shargs, cmdargs, options, debugflags=None, env=None):
  56. if os.environ.get('PYSH_TEXT') != '1':
  57. import msvcrt
  58. for fp in (sys.stdin, sys.stdout, sys.stderr):
  59. msvcrt.setmode(fp.fileno(), os.O_BINARY)
  60. hgbin = os.environ.get('PYSH_HGTEXT') != '1'
  61. if debugflags is None:
  62. debugflags = []
  63. if options.debug_parsing: debugflags.append('debug-parsing')
  64. if options.debug_utility: debugflags.append('debug-utility')
  65. if options.debug_cmd: debugflags.append('debug-cmd')
  66. if options.debug_tree: debugflags.append('debug-tree')
  67. if env is None:
  68. env = fixenv(dict(os.environ))
  69. if cwd is None:
  70. cwd = os.getcwd()
  71. if not cmdargs:
  72. # Nothing to do
  73. return 0
  74. ast = None
  75. command_file = None
  76. if options.command_string:
  77. input = cmdargs[0]
  78. if not options.ast:
  79. input += '\n'
  80. else:
  81. args, input = interp.decodeargs(input), None
  82. env, ast = args
  83. cwd = env.get('PWD', cwd)
  84. else:
  85. command_file = cmdargs[0]
  86. arguments = cmdargs[1:]
  87. prefix = interp.resolve_shebang(command_file, ignoreshell=True)
  88. if prefix:
  89. input = ' '.join(prefix + [command_file] + arguments)
  90. else:
  91. # Read commands from file
  92. f = file(command_file)
  93. try:
  94. # Trailing newline to help the parser
  95. input = f.read() + '\n'
  96. finally:
  97. f.close()
  98. redirect = None
  99. try:
  100. if options.redirected:
  101. stdout = sys.stdout
  102. stderr = stdout
  103. elif options.redirect_to:
  104. redirect = open(options.redirect_to, 'wb')
  105. stdout = redirect
  106. stderr = redirect
  107. else:
  108. stdout = sys.stdout
  109. stderr = sys.stderr
  110. # TODO: set arguments to environment variables
  111. opts = interp.Options()
  112. opts.hgbinary = hgbin
  113. ip = interp.Interpreter(cwd, debugflags, stdout=stdout, stderr=stderr,
  114. opts=opts)
  115. try:
  116. # Export given environment in shell object
  117. for k,v in env.iteritems():
  118. ip.get_env().export(k,v)
  119. return ip.execute_script(input, ast, scriptpath=command_file)
  120. finally:
  121. ip.close()
  122. finally:
  123. if redirect is not None:
  124. redirect.close()
  125. def sh(cwd=None, args=None, debugflags=None, env=None):
  126. if args is None:
  127. args = sys.argv[1:]
  128. shargs, cmdargs = split_args(args)
  129. options, shargs = SH_OPT.parse_args(shargs)
  130. if options.profile:
  131. import lsprof
  132. p = lsprof.Profiler()
  133. p.enable(subcalls=True)
  134. try:
  135. return _sh(cwd, shargs, cmdargs, options, debugflags, env)
  136. finally:
  137. p.disable()
  138. stats = lsprof.Stats(p.getstats())
  139. stats.sort()
  140. stats.pprint(top=10, file=sys.stderr, climit=5)
  141. else:
  142. return _sh(cwd, shargs, cmdargs, options, debugflags, env)
  143. def main():
  144. sys.exit(sh())
  145. if __name__=='__main__':
  146. main()