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() |