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