All of lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH 0/4] fixes for pysh
  2018-12-04  2:05 [PATCH 0/4] fixes for pysh Robert Yang
@ 2018-12-04  1:59 ` Robert Yang
  2018-12-04  2:06 ` [PATCH 1/4] pysh: Remove unused modules Robert Yang
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Robert Yang @ 2018-12-04  1:59 UTC (permalink / raw)
  To: bitbake-devel

I Received a message from bitbake list:

     [PATCH 1/4] pysh: Remove unused modules

Is being held until the list moderator can review it for approval.

The reason it is being held:

     Message body is too big: 93560 bytes with a limit of 80 KB


Here is the stat of the patch:

  bitbake/lib/bb/pysh/builtin.py        |  710 
---------------------------------------------------
  bitbake/lib/bb/pysh/interp.py         | 1367 
---------------------------------------------------------------------------------------------------
  bitbake/lib/bb/pysh/lsprof.py         |  116 ---------
  bitbake/lib/bb/pysh/pysh.py           |  167 ------------
  bitbake/lib/bb/pysh/subprocess_fix.py |   77 ------
  5 files changed, 2437 deletions(-)


// Robert

On 12/4/18 10:05 AM, Robert Yang wrote:
> Hi RP,
> 
> The pysh's code looks a little out of date, and the author declared that pysh
> is no longer maintained:
> http://pysh.sourceforge.net/
> 
> These are fixes for it.
> 
> // Robert
> 
> The following changes since commit 016e3dda2d6acc9b457079d11b5346a5352a431d:
> 
>    btrfs-tools: update to 4.19 (2018-12-03 12:20:01 +0000)
> 
> are available in the git repository at:
> 
>    git://git.pokylinux.org/poky-contrib rbt/pysh
>    http://git.pokylinux.org/cgit.cgi//log/?h=rbt/pysh
> 
> Robert Yang (4):
>    pysh: Remove unused modules
>    pysh/sherrors.py: Remove unused classes
>    pysh/pyshlex.py: Set is not importable
>    bitbake: pysh: Improve error handling for shell code
> 
>   bitbake/lib/bb/codeparser.py          |    5 +-
>   bitbake/lib/bb/pysh/builtin.py        |  710 -----------------
>   bitbake/lib/bb/pysh/interp.py         | 1367 ---------------------------------
>   bitbake/lib/bb/pysh/lsprof.py         |  116 ---
>   bitbake/lib/bb/pysh/pysh.py           |  167 ----
>   bitbake/lib/bb/pysh/pyshlex.py        |    5 -
>   bitbake/lib/bb/pysh/pyshyacc.py       |   17 +-
>   bitbake/lib/bb/pysh/sherrors.py       |   26 -
>   bitbake/lib/bb/pysh/subprocess_fix.py |   77 --
>   9 files changed, 13 insertions(+), 2477 deletions(-)
>   delete mode 100644 bitbake/lib/bb/pysh/builtin.py
>   delete mode 100644 bitbake/lib/bb/pysh/interp.py
>   delete mode 100644 bitbake/lib/bb/pysh/lsprof.py
>   delete mode 100644 bitbake/lib/bb/pysh/pysh.py
>   delete mode 100644 bitbake/lib/bb/pysh/subprocess_fix.py
> 


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 0/4] fixes for pysh
@ 2018-12-04  2:05 Robert Yang
  2018-12-04  1:59 ` Robert Yang
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Robert Yang @ 2018-12-04  2:05 UTC (permalink / raw)
  To: bitbake-devel

Hi RP,

The pysh's code looks a little out of date, and the author declared that pysh
is no longer maintained:
http://pysh.sourceforge.net/

These are fixes for it.

// Robert

The following changes since commit 016e3dda2d6acc9b457079d11b5346a5352a431d:

  btrfs-tools: update to 4.19 (2018-12-03 12:20:01 +0000)

are available in the git repository at:

  git://git.pokylinux.org/poky-contrib rbt/pysh
  http://git.pokylinux.org/cgit.cgi//log/?h=rbt/pysh

Robert Yang (4):
  pysh: Remove unused modules
  pysh/sherrors.py: Remove unused classes
  pysh/pyshlex.py: Set is not importable
  bitbake: pysh: Improve error handling for shell code

 bitbake/lib/bb/codeparser.py          |    5 +-
 bitbake/lib/bb/pysh/builtin.py        |  710 -----------------
 bitbake/lib/bb/pysh/interp.py         | 1367 ---------------------------------
 bitbake/lib/bb/pysh/lsprof.py         |  116 ---
 bitbake/lib/bb/pysh/pysh.py           |  167 ----
 bitbake/lib/bb/pysh/pyshlex.py        |    5 -
 bitbake/lib/bb/pysh/pyshyacc.py       |   17 +-
 bitbake/lib/bb/pysh/sherrors.py       |   26 -
 bitbake/lib/bb/pysh/subprocess_fix.py |   77 --
 9 files changed, 13 insertions(+), 2477 deletions(-)
 delete mode 100644 bitbake/lib/bb/pysh/builtin.py
 delete mode 100644 bitbake/lib/bb/pysh/interp.py
 delete mode 100644 bitbake/lib/bb/pysh/lsprof.py
 delete mode 100644 bitbake/lib/bb/pysh/pysh.py
 delete mode 100644 bitbake/lib/bb/pysh/subprocess_fix.py

-- 
2.7.4



^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/4] pysh: Remove unused modules
  2018-12-04  2:05 [PATCH 0/4] fixes for pysh Robert Yang
  2018-12-04  1:59 ` Robert Yang
@ 2018-12-04  2:06 ` Robert Yang
  2018-12-04  2:06 ` [PATCH 2/4] pysh/sherrors.py: Remove unused classes Robert Yang
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Robert Yang @ 2018-12-04  2:06 UTC (permalink / raw)
  To: bitbake-devel

We don't use these modules in bitbake, so remove them.

And pysh's author declared that pysh is no longer maintained:
http://pysh.sourceforge.net/

Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
---
 bitbake/lib/bb/pysh/builtin.py        |  710 -----------------
 bitbake/lib/bb/pysh/interp.py         | 1367 ---------------------------------
 bitbake/lib/bb/pysh/lsprof.py         |  116 ---
 bitbake/lib/bb/pysh/pysh.py           |  167 ----
 bitbake/lib/bb/pysh/subprocess_fix.py |   77 --
 5 files changed, 2437 deletions(-)
 delete mode 100644 bitbake/lib/bb/pysh/builtin.py
 delete mode 100644 bitbake/lib/bb/pysh/interp.py
 delete mode 100644 bitbake/lib/bb/pysh/lsprof.py
 delete mode 100644 bitbake/lib/bb/pysh/pysh.py
 delete mode 100644 bitbake/lib/bb/pysh/subprocess_fix.py

diff --git a/bitbake/lib/bb/pysh/builtin.py b/bitbake/lib/bb/pysh/builtin.py
deleted file mode 100644
index a8814dc..0000000
--- a/bitbake/lib/bb/pysh/builtin.py
+++ /dev/null
@@ -1,710 +0,0 @@
-# builtin.py - builtins and utilities definitions for pysh.
-#
-# Copyright 2007 Patrick Mezard
-#
-# This software may be used and distributed according to the terms
-# of the GNU General Public License, incorporated herein by reference.
-
-"""Builtin and internal utilities implementations.
-
-- Beware not to use python interpreter environment as if it were the shell
-environment. For instance, commands working directory must be explicitely handled
-through env['PWD'] instead of relying on python working directory.
-"""
-import errno
-import optparse
-import os
-import re
-import subprocess
-import sys
-import time
-
-def has_subprocess_bug():
-    return getattr(subprocess, 'list2cmdline') and \
-       (    subprocess.list2cmdline(['']) == '' or \
-            subprocess.list2cmdline(['foo|bar']) == 'foo|bar')
-            
-# Detect python bug 1634343: "subprocess swallows empty arguments under win32"
-# <http://sourceforge.net/tracker/index.php?func=detail&aid=1634343&group_id=5470&atid=105470>
-# Also detect: "[ 1710802 ] subprocess must escape redirection characters under win32"
-# <http://sourceforge.net/tracker/index.php?func=detail&aid=1710802&group_id=5470&atid=105470>
-if has_subprocess_bug():
-    import subprocess_fix
-    subprocess.list2cmdline = subprocess_fix.list2cmdline
-
-from sherrors import *
-
-class NonExitingParser(optparse.OptionParser):
-    """OptionParser default behaviour upon error is to print the error message and
-    exit. Raise a utility error instead.
-    """
-    def error(self, msg):
-        raise UtilityError(msg)
-
-#-------------------------------------------------------------------------------  
-# set special builtin
-#-------------------------------------------------------------------------------  
-OPT_SET = NonExitingParser(usage="set - set or unset options and positional parameters")
-OPT_SET.add_option( '-f', action='store_true', dest='has_f', default=False,
-    help='The shell shall disable pathname expansion.')
-OPT_SET.add_option('-e', action='store_true', dest='has_e', default=False,
-    help="""When this option is on, if a simple command fails for any of the \
-    reasons listed in Consequences of Shell Errors or returns an exit status \
-    value >0, and is not part of the compound list following a while, until, \
-    or if keyword, and is not a part of an AND or OR list, and is not a \
-    pipeline preceded by the ! reserved word, then the shell shall immediately \
-    exit.""")
-OPT_SET.add_option('-x', action='store_true', dest='has_x', default=False,
-    help="""The shell shall write to standard error a trace for each command \
-    after it expands the command and before it executes it. It is unspecified \
-    whether the command that turns tracing off is traced.""")
-
-def builtin_set(name, args, interp, env, stdin, stdout, stderr, debugflags):
-    if 'debug-utility' in debugflags:
-        print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
-
-    option, args = OPT_SET.parse_args(args)
-    env = interp.get_env()
-    
-    if option.has_f:
-        env.set_opt('-f')
-    if option.has_e:
-        env.set_opt('-e')
-    if option.has_x:
-        env.set_opt('-x')
-    return 0
-    
-#-------------------------------------------------------------------------------  
-# shift special builtin
-#-------------------------------------------------------------------------------  
-def builtin_shift(name, args, interp, env, stdin, stdout, stderr, debugflags):
-    if 'debug-utility' in debugflags:
-        print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
-        
-    params = interp.get_env().get_positional_args()
-    if args:
-        try:
-            n = int(args[0])
-            if n > len(params):
-                raise ValueError()
-        except ValueError:
-            return 1
-    else:
-        n = 1
-        
-    params[:n] = []
-    interp.get_env().set_positional_args(params)
-    return 0
-    
-#-------------------------------------------------------------------------------  
-# export special builtin
-#-------------------------------------------------------------------------------  
-OPT_EXPORT = NonExitingParser(usage="set - set or unset options and positional parameters")
-OPT_EXPORT.add_option('-p', action='store_true', dest='has_p', default=False)
-
-def builtin_export(name, args, interp, env, stdin, stdout, stderr, debugflags):
-    if 'debug-utility' in debugflags:
-        print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
-        
-    option, args = OPT_EXPORT.parse_args(args)
-    if option.has_p:
-        raise NotImplementedError()
-    
-    for arg in args:
-        try:
-            name, value = arg.split('=', 1)
-        except ValueError:
-            name, value = arg, None
-        env = interp.get_env().export(name, value)
-    
-    return 0
-    
-#-------------------------------------------------------------------------------  
-# return special builtin
-#-------------------------------------------------------------------------------  
-def builtin_return(name, args, interp, env, stdin, stdout, stderr, debugflags):
-    if 'debug-utility' in debugflags:
-        print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
-    res = 0
-    if args:
-        try:
-            res = int(args[0])
-        except ValueError:
-            res = 0
-        if not 0<=res<=255:
-            res = 0
-            
-    # BUG: should be last executed command exit code        
-    raise ReturnSignal(res)
-
-#-------------------------------------------------------------------------------  
-# trap special builtin
-#-------------------------------------------------------------------------------  
-def builtin_trap(name, args, interp, env, stdin, stdout, stderr, debugflags):
-    if 'debug-utility' in debugflags:
-        print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
-    if len(args) < 2:
-        stderr.write('trap: usage: trap [[arg] signal_spec ...]\n')
-        return 2
-
-    action = args[0]
-    for sig in args[1:]:
-        try:
-            env.traps[sig] = action
-        except Exception as e:
-            stderr.write('trap: %s\n' % str(e))
-    return 0
-
-#-------------------------------------------------------------------------------  
-# unset special builtin
-#-------------------------------------------------------------------------------
-OPT_UNSET = NonExitingParser("unset - unset values and attributes of variables and functions")
-OPT_UNSET.add_option( '-f', action='store_true', dest='has_f', default=False)
-OPT_UNSET.add_option( '-v', action='store_true', dest='has_v', default=False)
-
-def builtin_unset(name, args, interp, env, stdin, stdout, stderr, debugflags):
-    if 'debug-utility' in debugflags:
-        print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
-        
-    option, args = OPT_UNSET.parse_args(args)
-    
-    status = 0
-    env = interp.get_env()
-    for arg in args:    
-        try:
-            if option.has_f:
-                env.remove_function(arg)
-            else:
-                del env[arg]
-        except KeyError:
-            pass
-        except VarAssignmentError:
-            status = 1
-            
-    return status
-
-#-------------------------------------------------------------------------------  
-# wait special builtin
-#-------------------------------------------------------------------------------  
-def builtin_wait(name, args, interp, env, stdin, stdout, stderr, debugflags):
-    if 'debug-utility' in debugflags:
-        print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
-
-    return interp.wait([int(arg) for arg in args])
-
-#-------------------------------------------------------------------------------  
-# cat utility
-#-------------------------------------------------------------------------------
-def utility_cat(name, args, interp, env, stdin, stdout, stderr, debugflags):
-    if 'debug-utility' in debugflags:
-        print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
-
-    if not args:
-        args = ['-']
-
-    status = 0
-    for arg in args:
-        if arg == '-':
-            data = stdin.read()
-        else:
-            path = os.path.join(env['PWD'], arg)
-            try:
-                f = file(path, 'rb')
-                try:
-                    data = f.read()
-                finally:
-                    f.close()
-            except IOError as e:
-                if e.errno != errno.ENOENT:
-                    raise
-                status = 1
-                continue
-        stdout.write(data)
-        stdout.flush()
-    return status
-    
-#-------------------------------------------------------------------------------  
-# cd utility
-#-------------------------------------------------------------------------------  
-OPT_CD = NonExitingParser("cd - change the working directory")
-
-def utility_cd(name, args, interp, env, stdin, stdout, stderr, debugflags):
-    if 'debug-utility' in debugflags:
-        print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
-
-    option, args = OPT_CD.parse_args(args)
-    env = interp.get_env()
-    
-    directory = None
-    printdir = False
-    if not args:
-        home = env.get('HOME')
-        if home:
-            # Unspecified, do nothing
-            return 0
-        else:
-            directory = home
-    elif len(args)==1:
-        directory = args[0]
-        if directory=='-':
-            if 'OLDPWD' not in env:
-                raise UtilityError("OLDPWD not set")
-            printdir = True
-            directory = env['OLDPWD']
-    else:
-        raise UtilityError("too many arguments")
-            
-    curpath = None
-    # Absolute directories will be handled correctly by the os.path.join call.
-    if not directory.startswith('.') and not directory.startswith('..'):
-        cdpaths = env.get('CDPATH', '.').split(';')
-        for cdpath in cdpaths:
-            p = os.path.join(cdpath, directory)
-            if os.path.isdir(p):
-                curpath = p
-                break
-    
-    if curpath is None:
-        curpath = directory
-    curpath = os.path.join(env['PWD'], directory)
-
-    env['OLDPWD'] = env['PWD']
-    env['PWD'] = curpath
-    if printdir:
-        stdout.write('%s\n' % curpath)
-    return 0
-
-#-------------------------------------------------------------------------------  
-# colon utility
-#-------------------------------------------------------------------------------  
-def utility_colon(name, args, interp, env, stdin, stdout, stderr, debugflags):
-    if 'debug-utility' in debugflags:
-        print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
-    return 0
-    
-#-------------------------------------------------------------------------------  
-# echo utility
-#-------------------------------------------------------------------------------  
-def utility_echo(name, args, interp, env, stdin, stdout, stderr, debugflags):
-    if 'debug-utility' in debugflags:
-        print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
-        
-    # Echo only takes arguments, no options. Use printf if you need fancy stuff.
-    output = ' '.join(args) + '\n'
-    stdout.write(output)
-    stdout.flush()
-    return 0
-    
-#-------------------------------------------------------------------------------  
-# egrep utility
-#-------------------------------------------------------------------------------
-# egrep is usually a shell script.
-# Unfortunately, pysh does not support shell scripts *with arguments* right now,
-# so the redirection is implemented here, assuming grep is available.
-def utility_egrep(name, args, interp, env, stdin, stdout, stderr, debugflags):
-    if 'debug-utility' in debugflags:
-        print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
-        
-    return run_command('grep', ['-E'] + args, interp, env, stdin, stdout, 
-        stderr, debugflags)
-    
-#-------------------------------------------------------------------------------  
-# env utility
-#-------------------------------------------------------------------------------  
-def utility_env(name, args, interp, env, stdin, stdout, stderr, debugflags):
-    if 'debug-utility' in debugflags:
-        print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
-    
-    if args and args[0]=='-i':
-        raise NotImplementedError('env: -i option is not implemented')
-    
-    i = 0
-    for arg in args:
-        if '=' not in arg:
-            break
-        # Update the current environment
-        name, value = arg.split('=', 1)
-        env[name] = value
-        i += 1
-        
-    if args[i:]:
-        # Find then execute the specified interpreter
-        utility = env.find_in_path(args[i])
-        if not utility:
-            return 127
-        args[i:i+1] = utility
-        name = args[i]
-        args = args[i+1:]
-        try:
-            return run_command(name, args, interp, env, stdin, stdout, stderr, 
-                debugflags)
-        except UtilityError:
-            stderr.write('env: failed to execute %s' % ' '.join([name]+args))
-            return 126            
-    else:
-        for pair in env.get_variables().iteritems():
-            stdout.write('%s=%s\n' % pair)
-    return 0
-    
-#-------------------------------------------------------------------------------  
-# exit utility
-#-------------------------------------------------------------------------------
-def utility_exit(name, args, interp, env, stdin, stdout, stderr, debugflags):
-    if 'debug-utility' in debugflags:
-        print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
-        
-    res = None
-    if args:
-        try:
-            res = int(args[0])
-        except ValueError:
-            res = None
-        if not 0<=res<=255:
-            res = None
-            
-    if res is None:
-        # BUG: should be last executed command exit code
-        res = 0
-        
-    raise ExitSignal(res)
-
-#-------------------------------------------------------------------------------  
-# fgrep utility
-#-------------------------------------------------------------------------------
-# see egrep
-def utility_fgrep(name, args, interp, env, stdin, stdout, stderr, debugflags):
-    if 'debug-utility' in debugflags:
-        print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
-        
-    return run_command('grep', ['-F'] + args, interp, env, stdin, stdout, 
-        stderr, debugflags)
-
-#-------------------------------------------------------------------------------  
-# gunzip utility
-#-------------------------------------------------------------------------------
-# see egrep
-def utility_gunzip(name, args, interp, env, stdin, stdout, stderr, debugflags):
-    if 'debug-utility' in debugflags:
-        print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
-        
-    return run_command('gzip', ['-d'] + args, interp, env, stdin, stdout, 
-        stderr, debugflags)
-    
-#-------------------------------------------------------------------------------  
-# kill utility
-#-------------------------------------------------------------------------------
-def utility_kill(name, args, interp, env, stdin, stdout, stderr, debugflags):
-    if 'debug-utility' in debugflags:
-        print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
-        
-    for arg in args:
-        pid = int(arg)
-        status = subprocess.call(['pskill', '/T', str(pid)],
-                                shell=True,
-                                stdout=subprocess.PIPE,
-                                stderr=subprocess.PIPE)
-        # pskill is asynchronous, hence the stupid polling loop
-        while 1:
-            p = subprocess.Popen(['pslist', str(pid)],
-                                shell=True,
-                                stdout=subprocess.PIPE,
-                                stderr=subprocess.STDOUT)
-            output = p.communicate()[0]
-            if ('process %d was not' % pid) in output:
-                break
-            time.sleep(1)
-    return status
-    
-#-------------------------------------------------------------------------------  
-# mkdir utility
-#-------------------------------------------------------------------------------
-OPT_MKDIR = NonExitingParser("mkdir - make directories.")
-OPT_MKDIR.add_option('-p', action='store_true', dest='has_p', default=False)
-
-def utility_mkdir(name, args, interp, env, stdin, stdout, stderr, debugflags):
-    if 'debug-utility' in debugflags:
-        print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
-        
-    # TODO: implement umask
-    # TODO: implement proper utility error report
-    option, args = OPT_MKDIR.parse_args(args)
-    for arg in args:
-        path = os.path.join(env['PWD'], arg)
-        if option.has_p:
-            try:
-                os.makedirs(path)
-            except IOError as e:
-                if e.errno != errno.EEXIST:
-                    raise
-        else:               
-            os.mkdir(path)
-    return 0
-
-#-------------------------------------------------------------------------------  
-# netstat utility
-#-------------------------------------------------------------------------------
-def utility_netstat(name, args, interp, env, stdin, stdout, stderr, debugflags):
-    # Do you really expect me to implement netstat ?
-    # This empty form is enough for Mercurial tests since it's
-    # supposed to generate nothing upon success. Faking this test
-    # is not a big deal either.
-    if 'debug-utility' in debugflags:
-        print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
-    return 0
-    
-#-------------------------------------------------------------------------------  
-# pwd utility
-#-------------------------------------------------------------------------------  
-OPT_PWD = NonExitingParser("pwd - return working directory name")
-OPT_PWD.add_option('-L', action='store_true', dest='has_L', default=True,
-    help="""If the PWD environment variable contains an absolute pathname of \
-    the current directory that does not contain the filenames dot or dot-dot, \
-    pwd shall write this pathname to standard output. Otherwise, the -L option \
-    shall behave as the -P option.""")
-OPT_PWD.add_option('-P', action='store_true', dest='has_L', default=False,
-    help="""The absolute pathname written shall not contain filenames that, in \
-    the context of the pathname, refer to files of type symbolic link.""")
-
-def utility_pwd(name, args, interp, env, stdin, stdout, stderr, debugflags):
-    if 'debug-utility' in debugflags:
-        print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
-
-    option, args = OPT_PWD.parse_args(args)        
-    stdout.write('%s\n' % env['PWD'])
-    return 0
-    
-#-------------------------------------------------------------------------------  
-# printf utility
-#-------------------------------------------------------------------------------
-RE_UNESCAPE = re.compile(r'(\\x[a-zA-Z0-9]{2}|\\[0-7]{1,3}|\\.)')
-
-def utility_printf(name, args, interp, env, stdin, stdout, stderr, debugflags):
-    if 'debug-utility' in debugflags:
-        print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
-        
-    def replace(m):
-        assert m.group()
-        g = m.group()[1:]
-        if g.startswith('x'):
-            return chr(int(g[1:], 16))
-        if len(g) <= 3 and len([c for c in g if c in '01234567']) == len(g):
-            # Yay, an octal number
-            return chr(int(g, 8))
-        return {
-            'a': '\a',
-            'b': '\b',
-            'f': '\f',
-            'n': '\n',
-            'r': '\r',
-            't': '\t',
-            'v': '\v',
-            '\\': '\\',
-        }.get(g)
-        
-    # Convert escape sequences
-    format = re.sub(RE_UNESCAPE, replace, args[0])
-    stdout.write(format % tuple(args[1:]))
-    return 0
-    
-#-------------------------------------------------------------------------------  
-# true utility
-#-------------------------------------------------------------------------------
-def utility_true(name, args, interp, env, stdin, stdout, stderr, debugflags):
-    if 'debug-utility' in debugflags:
-        print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
-    return 0
-
-#-------------------------------------------------------------------------------  
-# sed utility
-#-------------------------------------------------------------------------------
-RE_SED = re.compile(r'^s(.).*\1[a-zA-Z]*$')
-
-# cygwin sed fails with some expressions when they do not end with a single space.
-# see unit tests for details. Interestingly, the same expressions works perfectly
-# in cygwin shell.
-def utility_sed(name, args, interp, env, stdin, stdout, stderr, debugflags):
-    if 'debug-utility' in debugflags:
-        print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
-        
-    # Scan pattern arguments and append a space if necessary
-    for i in range(len(args)):
-        if not RE_SED.search(args[i]):
-            continue
-        args[i] = args[i] + ' '
-
-    return run_command(name, args, interp, env, stdin, stdout, 
-        stderr, debugflags)
-
-#-------------------------------------------------------------------------------  
-# sleep utility
-#-------------------------------------------------------------------------------
-def utility_sleep(name, args, interp, env, stdin, stdout, stderr, debugflags):
-    if 'debug-utility' in debugflags:
-        print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
-    time.sleep(int(args[0]))
-    return 0
-    
-#-------------------------------------------------------------------------------  
-# sort utility
-#-------------------------------------------------------------------------------
-OPT_SORT = NonExitingParser("sort - sort, merge, or sequence check text files")
-
-def utility_sort(name, args, interp, env, stdin, stdout, stderr, debugflags):
-
-    def sort(path):
-        if path == '-':
-            lines = stdin.readlines()
-        else:
-            try:
-                f = file(path)
-                try:
-                    lines = f.readlines()
-                finally:
-                    f.close()
-            except IOError as e:
-                stderr.write(str(e) + '\n')
-                return 1
-        
-        if lines and lines[-1][-1]!='\n':
-            lines[-1] = lines[-1] + '\n'
-        return lines
-    
-    if 'debug-utility' in debugflags:
-        print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
-        
-    option, args = OPT_SORT.parse_args(args)
-    alllines = []
-    
-    if len(args)<=0:
-        args += ['-']
-        
-    # Load all files lines
-    curdir = os.getcwd()
-    try:
-        os.chdir(env['PWD'])
-        for path in args:
-            alllines += sort(path)
-    finally:
-        os.chdir(curdir)
-            
-    alllines.sort()
-    for line in alllines:
-        stdout.write(line)
-    return 0
-    
-#-------------------------------------------------------------------------------
-# hg utility
-#-------------------------------------------------------------------------------
-
-hgcommands = [
-    'add',
-    'addremove',
-    'commit', 'ci',
-    'debugrename',
-    'debugwalk',
-    'falabala', # Dummy command used in a mercurial test
-    'incoming',
-    'locate',
-    'pull',
-    'push',
-    'qinit',
-    'remove', 'rm',
-    'rename', 'mv',
-    'revert',    
-    'showconfig',
-    'status', 'st',
-    'strip',
-    ]
-
-def rewriteslashes(name, args):
-    # Several hg commands output file paths, rewrite the separators
-    if len(args) > 1 and name.lower().endswith('python') \
-       and args[0].endswith('hg'):
-        for cmd in hgcommands:
-            if cmd in args[1:]:
-                return True
-            
-    # svn output contains many paths with OS specific separators.
-    # Normalize these to unix paths.
-    base = os.path.basename(name)
-    if base.startswith('svn'):
-        return True
-    
-    return False
-
-def rewritehg(output):
-    if not output:
-        return output
-    # Rewrite os specific messages
-    output = output.replace(': The system cannot find the file specified',
-                            ': No such file or directory')
-    output = re.sub(': Access is denied.*$', ': Permission denied', output)
-    output = output.replace(': No connection could be made because the target machine actively refused it',
-                            ': Connection refused')
-    return output
-                            
-
-def run_command(name, args, interp, env, stdin, stdout,
-                stderr, debugflags):
-    # Execute the command
-    if 'debug-utility' in debugflags:
-        print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
-
-    hgbin = interp.options().hgbinary
-    ishg = hgbin and ('hg' in name or args and 'hg' in args[0])
-    unixoutput = 'cygwin' in name or ishg
-    
-    exec_env = env.get_variables()        
-    try:
-        # BUG: comparing file descriptor is clearly not a reliable way to tell
-        # whether they point on the same underlying object. But in pysh limited
-        # scope this is usually right, we do not expect complicated redirections
-        # besides usual 2>&1.
-        # Still there is one case we have but cannot deal with is when stdout
-        # and stderr are redirected *by pysh caller*. This the reason for the
-        # --redirect pysh() option.
-        # Now, we want to know they are the same because we sometimes need to 
-        # transform the command output, mostly remove CR-LF to ensure that
-        # command output is unix-like. Cygwin utilies are a special case because
-        # they explicitely set their output streams to binary mode, so we have
-        # nothing to do. For all others commands, we have to guess whether they
-        # are sending text data, in which case the transformation must be done.
-        # Again, the NUL character test is unreliable but should be enough for
-        # hg tests.
-        redirected = stdout.fileno()==stderr.fileno()
-        if not redirected:
-            p = subprocess.Popen([name] + args, cwd=env['PWD'], env=exec_env, 
-                    stdin=stdin, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-        else:
-            p = subprocess.Popen([name] + args, cwd=env['PWD'], env=exec_env, 
-                    stdin=stdin, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-        out, err = p.communicate()
-    except WindowsError as e:
-        raise UtilityError(str(e))
-
-    if not unixoutput:
-        def encode(s):
-            if '\0' in s:
-                return s
-            return s.replace('\r\n', '\n')
-    else:
-        encode = lambda s: s
-
-    if rewriteslashes(name, args):
-        encode1_ = encode
-        def encode(s):
-            s = encode1_(s)
-            s = s.replace('\\\\', '\\')
-            s = s.replace('\\', '/')
-            return s
-
-    if ishg:
-        encode2_ = encode
-        def encode(s):
-            return rewritehg(encode2_(s))
-    
-    stdout.write(encode(out))
-    if not redirected:
-        stderr.write(encode(err))
-    return p.returncode
-            
diff --git a/bitbake/lib/bb/pysh/interp.py b/bitbake/lib/bb/pysh/interp.py
deleted file mode 100644
index d14ecf3..0000000
--- a/bitbake/lib/bb/pysh/interp.py
+++ /dev/null
@@ -1,1367 +0,0 @@
-# interp.py - shell interpreter for pysh.
-#
-# Copyright 2007 Patrick Mezard
-#
-# This software may be used and distributed according to the terms
-# of the GNU General Public License, incorporated herein by reference.
-
-"""Implement the shell interpreter.
-
-Most references are made to "The Open Group Base Specifications Issue 6".
-<http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html>
-"""
-# TODO: document the fact input streams must implement fileno() so Popen will work correctly.
-# it requires non-stdin stream to be implemented as files. Still to be tested...
-# DOC: pathsep is used in PATH instead of ':'. Clearly, there are path syntax issues here.
-# TODO: stop command execution upon error.
-# TODO: sort out the filename/io_number mess. It should be possible to use filenames only.
-# TODO: review subshell implementation
-# TODO: test environment cloning for non-special builtins
-# TODO: set -x should not rebuild commands from tokens, assignments/redirections are lost
-# TODO: unit test for variable assignment
-# TODO: test error management wrt error type/utility type
-# TODO: test for binary output everywhere
-# BUG: debug-parsing does not pass log file to PLY. Maybe a PLY upgrade is necessary.
-import base64
-import cPickle as pickle
-import errno
-import glob
-import os
-import re
-import subprocess
-import sys
-import tempfile
-
-try:
-    s = set()
-    del s
-except NameError:
-    from Set import Set as set
-
-import builtin
-from sherrors import *
-import pyshlex
-import pyshyacc
-
-def mappend(func, *args, **kargs):
-    """Like map but assume func returns a list. Returned lists are merged into
-    a single one.
-    """
-    return reduce(lambda a,b: a+b, map(func, *args, **kargs), [])
-
-class FileWrapper:
-    """File object wrapper to ease debugging.
-    
-    Allow mode checking and implement file duplication through a simple 
-    reference counting scheme. Not sure the latter is really useful since
-    only real file descriptors can be used.
-    """
-    def __init__(self, mode, file, close=True):
-        if mode not in ('r', 'w', 'a'):
-            raise IOError('invalid mode: %s' % mode)
-        self._mode = mode
-        self._close = close
-        if isinstance(file, FileWrapper):
-            if file._refcount[0] <= 0:
-                raise IOError(0, 'Error')
-            self._refcount = file._refcount
-            self._refcount[0] += 1
-            self._file = file._file
-        else:
-            self._refcount = [1]
-            self._file = file
-        
-    def dup(self):
-        return FileWrapper(self._mode, self, self._close)
-        
-    def fileno(self):
-        """fileno() should be only necessary for input streams."""
-        return self._file.fileno()
-        
-    def read(self, size=-1):
-        if self._mode!='r':
-            raise IOError(0, 'Error')
-        return self._file.read(size)
-        
-    def readlines(self, *args, **kwargs):
-        return self._file.readlines(*args, **kwargs)
-        
-    def write(self, s):
-        if self._mode not in ('w', 'a'):
-            raise IOError(0, 'Error')
-        return self._file.write(s)
-        
-    def flush(self):
-        self._file.flush()
-        
-    def close(self):        
-        if not self._refcount:
-            return
-        assert  self._refcount[0] > 0
-        
-        self._refcount[0] -= 1    
-        if self._refcount[0] == 0:
-            self._mode = 'c'
-            if self._close:
-                self._file.close()
-        self._refcount = None
-                
-    def mode(self):
-        return self._mode
-
-    def __getattr__(self, name):
-        if name == 'name':
-            self.name = getattr(self._file, name)
-            return self.name
-        else:
-            raise AttributeError(name)
-
-    def __del__(self):
-        self.close()
-               
-               
-def win32_open_devnull(mode):
-    return open('NUL', mode)
-    
-        
-class Redirections:
-    """Stores open files and their mapping to pseudo-sh file descriptor.
-    """
-    # BUG: redirections are not handled correctly: 1>&3 2>&3 3>&4 does 
-    # not make 1 to redirect to 4
-    def __init__(self, stdin=None, stdout=None, stderr=None):
-        self._descriptors = {}
-        if stdin is not None:
-            self._add_descriptor(0, stdin)
-        if stdout is not None:
-            self._add_descriptor(1, stdout)
-        if stderr is not None:
-            self._add_descriptor(2, stderr)
-            
-    def add_here_document(self, interp, name, content, io_number=None):
-        if io_number is None:
-            io_number = 0
-            
-        if name==pyshlex.unquote_wordtree(name):
-            content = interp.expand_here_document(('TOKEN', content))
-    
-        # Write document content in a temporary file
-        tmp = tempfile.TemporaryFile()
-        try:
-            tmp.write(content)
-            tmp.flush()
-            tmp.seek(0)
-            self._add_descriptor(io_number, FileWrapper('r', tmp))
-        except:
-            tmp.close()
-            raise                
-        
-    def add(self, interp, op, filename, io_number=None):
-        if op not in ('<', '>', '>|', '>>', '>&'):
-            # TODO: add descriptor duplication and here_documents      
-            raise RedirectionError('Unsupported redirection operator "%s"' % op)
-            
-        if io_number is not None:
-            io_number = int(io_number)
-            
-        if (op == '>&' and filename.isdigit()) or filename=='-':
-            # No expansion for file descriptors, quote them if you want a filename
-            fullname = filename
-        else:
-            if filename.startswith('/'):
-                # TODO: win32 kludge
-                if filename=='/dev/null':
-                    fullname = 'NUL'
-                else:
-                    # TODO: handle absolute pathnames, they are unlikely to exist on the
-                    # current platform (win32 for instance).
-                    raise NotImplementedError()
-            else:
-                fullname = interp.expand_redirection(('TOKEN', filename))
-                if not fullname:
-                    raise RedirectionError('%s: ambiguous redirect' % filename)
-                # Build absolute path based on PWD
-                fullname = os.path.join(interp.get_env()['PWD'], fullname)
-            
-        if op=='<':
-            return self._add_input_redirection(interp, fullname, io_number)
-        elif op in ('>', '>|'):
-            clobber = ('>|'==op)
-            return self._add_output_redirection(interp, fullname, io_number, clobber)
-        elif op=='>>':
-            return self._add_output_appending(interp, fullname, io_number)
-        elif op=='>&':
-            return self._dup_output_descriptor(fullname, io_number)
-        
-    def close(self):
-        if self._descriptors is not None:
-            for desc in self._descriptors.itervalues():
-                desc.flush()
-                desc.close()
-            self._descriptors = None
-            
-    def stdin(self):
-        return self._descriptors[0]
-          
-    def stdout(self):
-        return self._descriptors[1] 
-        
-    def stderr(self):
-        return self._descriptors[2] 
-            
-    def clone(self):
-        clone = Redirections()
-        for desc, fileobj in self._descriptors.iteritems():
-            clone._descriptors[desc] = fileobj.dup()
-        return clone
-           
-    def _add_output_redirection(self, interp, filename, io_number, clobber):    
-        if io_number is None:
-            # io_number default to standard output
-            io_number = 1
-        
-        if not clobber and interp.get_env().has_opt('-C') and os.path.isfile(filename):
-            # File already exist in no-clobber mode, bail out
-            raise RedirectionError('File "%s" already exists' % filename)
-            
-        # Open and register
-        self._add_file_descriptor(io_number, filename, 'w')
-        
-    def _add_output_appending(self, interp, filename, io_number):    
-        if io_number is None:
-            io_number = 1
-        self._add_file_descriptor(io_number, filename, 'a')
-            
-    def _add_input_redirection(self, interp, filename, io_number):
-        if io_number is None:
-            io_number = 0
-        self._add_file_descriptor(io_number, filename, 'r')
-        
-    def _add_file_descriptor(self, io_number, filename, mode):    
-        try:            
-            if filename.startswith('/'):
-                if filename=='/dev/null':
-                    f = win32_open_devnull(mode+'b')
-                else:
-                    # TODO: handle absolute pathnames, they are unlikely to exist on the
-                    # current platform (win32 for instance).
-                    raise NotImplementedError('cannot open absolute path %s' % repr(filename))
-            else:
-                f = file(filename, mode+'b')
-        except IOError as e:
-            raise RedirectionError(str(e))
-            
-        wrapper = None
-        try:
-            wrapper = FileWrapper(mode, f)
-            f = None
-            self._add_descriptor(io_number, wrapper)
-        except:
-            if f: f.close()
-            if wrapper: wrapper.close()
-            raise
-            
-    def _dup_output_descriptor(self, source_fd, dest_fd):
-        if source_fd is None:
-            source_fd = 1
-        self._dup_file_descriptor(source_fd, dest_fd, 'w')
-            
-    def _dup_file_descriptor(self, source_fd, dest_fd, mode):
-        source_fd = int(source_fd)
-        if source_fd not in self._descriptors:
-            raise RedirectionError('"%s" is not a valid file descriptor' % str(source_fd))
-        source = self._descriptors[source_fd]
-        
-        if source.mode()!=mode:
-            raise RedirectionError('Descriptor %s cannot be duplicated in mode "%s"' % (str(source), mode))
-        
-        if dest_fd=='-':
-            # Close the source descriptor
-            del self._descriptors[source_fd]
-            source.close()
-        else:
-            dest_fd = int(dest_fd)
-            if dest_fd not in self._descriptors:
-                raise RedirectionError('Cannot replace file descriptor %s' % str(dest_fd))
-                
-            dest = self._descriptors[dest_fd]
-            if dest.mode()!=mode:
-                raise RedirectionError('Descriptor %s cannot be cannot be redirected in mode "%s"' % (str(dest), mode))
-            
-            self._descriptors[dest_fd] = source.dup()
-            dest.close()        
-            
-    def _add_descriptor(self, io_number, file):
-        io_number = int(io_number)
-        
-        if io_number in self._descriptors:
-            # Close the current descriptor
-            d = self._descriptors[io_number]
-            del self._descriptors[io_number]
-            d.close()
-            
-        self._descriptors[io_number] = file
-
-    def __str__(self):
-        names = [('%d=%r' % (k, getattr(v, 'name', None))) for k,v
-                 in self._descriptors.iteritems()]
-        names = ','.join(names)
-        return 'Redirections(%s)' % names
-
-    def __del__(self):
-        self.close()
-    
-def cygwin_to_windows_path(path):
-    """Turn /cygdrive/c/foo into c:/foo, or return path if it
-    is not a cygwin path.
-    """
-    if not path.startswith('/cygdrive/'):
-        return path
-    path = path[len('/cygdrive/'):]
-    path = path[:1] + ':' + path[1:]
-    return path
-    
-def win32_to_unix_path(path):
-    if path is not None:
-        path = path.replace('\\', '/')
-    return path    
-    
-_RE_SHEBANG = re.compile(r'^\#!\s?([^\s]+)(?:\s([^\s]+))?')
-_SHEBANG_CMDS = {
-    '/usr/bin/env': 'env',
-    '/bin/sh': 'pysh',
-    'python': 'python',
-}
-    
-def resolve_shebang(path, ignoreshell=False):
-    """Return a list of arguments as shebang interpreter call or an empty list
-    if path does not refer to an executable script.
-    See <http://www.opengroup.org/austin/docs/austin_51r2.txt>.
-    
-    ignoreshell - set to True to ignore sh shebangs. Return an empty list instead.
-    """
-    try:
-        f = file(path)
-        try:
-            # At most 80 characters in the first line
-            header = f.read(80).splitlines()[0]
-        finally:
-            f.close()
-            
-        m = _RE_SHEBANG.search(header)
-        if not m:
-            return []
-        cmd, arg = m.group(1,2)
-        if os.path.isfile(cmd):
-            # Keep this one, the hg script for instance contains a weird windows
-            # shebang referencing the current python install.
-            cmdfile = os.path.basename(cmd).lower()
-            if cmdfile == 'python.exe':
-                cmd = 'python'
-            pass
-        elif cmd not in _SHEBANG_CMDS:
-            raise CommandNotFound('Unknown interpreter "%s" referenced in '\
-                'shebang' % header)
-        cmd = _SHEBANG_CMDS.get(cmd)
-        if cmd is None or (ignoreshell and cmd == 'pysh'):
-            return []
-        if arg is None:
-            return [cmd, win32_to_unix_path(path)]
-        return [cmd, arg, win32_to_unix_path(path)]
-    except IOError as e:
-        if  e.errno!=errno.ENOENT and \
-            (e.errno!=errno.EPERM and not os.path.isdir(path)): # Opening a directory raises EPERM
-            raise
-        return []
-        
-def win32_find_in_path(name, path):
-    if isinstance(path, str):
-        path = path.split(os.pathsep)
-        
-    exts = os.environ.get('PATHEXT', '').lower().split(os.pathsep)
-    for p in path:
-        p_name = os.path.join(p, name)
-        
-        prefix = resolve_shebang(p_name)
-        if prefix:
-            return prefix
-            
-        for ext in exts:    
-            p_name_ext = p_name + ext
-            if os.path.exists(p_name_ext):
-                return [win32_to_unix_path(p_name_ext)]
-    return []
-
-class Traps(dict):
-    def __setitem__(self, key, value):
-        if key not in ('EXIT',):
-            raise NotImplementedError()
-        super(Traps, self).__setitem__(key, value)
-
-# IFS white spaces character class
-_IFS_WHITESPACES = (' ', '\t', '\n')
-
-class Environment:
-    """Environment holds environment variables, export table, function 
-    definitions and whatever is defined in 2.12 "Shell Execution Environment",
-    redirection excepted.
-    """
-    def __init__(self, pwd):
-        self._opt = set()       #Shell options
-        
-        self._functions = {}        
-        self._env = {'?': '0', '#': '0'}
-        self._exported = set([
-            'HOME', 'IFS', 'PATH'
-        ])
-        
-        # Set environment vars with side-effects
-        self._ifs_ws = None     # Set of IFS whitespace characters
-        self._ifs_re = None     # Regular expression used to split between words using IFS classes
-        self['IFS'] = ''.join(_IFS_WHITESPACES) #Default environment values
-        self['PWD'] = pwd
-        self.traps = Traps()
-        
-    def clone(self, subshell=False):
-        env = Environment(self['PWD'])
-        env._opt = set(self._opt)
-        for k,v in self.get_variables().iteritems():
-            if k in self._exported:
-                env.export(k,v)
-            elif subshell:
-                env[k] = v
-                
-        if subshell:
-            env._functions = dict(self._functions)
-            
-        return env        
-        
-    def __getitem__(self, key):
-        if key in ('@', '*', '-', '$'):
-            raise NotImplementedError('%s is not implemented' % repr(key))
-        return self._env[key]
-        
-    def get(self, key, defval=None):
-        try:
-            return self[key]
-        except KeyError:
-            return defval
-        
-    def __setitem__(self, key, value):
-        if key=='IFS':
-            # Update the whitespace/non-whitespace classes
-            self._update_ifs(value)
-        elif key=='PWD':
-            pwd = os.path.abspath(value)
-            if not os.path.isdir(pwd):
-                raise VarAssignmentError('Invalid directory %s' % value)
-            value = pwd
-        elif key in ('?', '!'):
-            value = str(int(value))
-        self._env[key] = value
-        
-    def __delitem__(self, key):
-        if key in ('IFS', 'PWD', '?'):
-            raise VarAssignmentError('%s cannot be unset' % key)
-        del self._env[key]
-
-    def __contains__(self, item):
-        return item in self._env
-        
-    def set_positional_args(self, args):
-        """Set the content of 'args' as positional argument from 1 to len(args).
-        Return previous argument as a list of strings.
-        """
-        # Save and remove previous arguments
-        prevargs = []        
-        for i in range(int(self._env['#'])):
-            i = str(i+1)
-            prevargs.append(self._env[i])
-            del self._env[i]
-        self._env['#'] = '0'
-                
-        #Set new ones
-        for i,arg in enumerate(args):
-            self._env[str(i+1)] = str(arg)
-        self._env['#'] = str(len(args))
-        
-        return prevargs
-        
-    def get_positional_args(self):
-        return [self._env[str(i+1)] for i in range(int(self._env['#']))]
-        
-    def get_variables(self):
-        return dict(self._env)
-        
-    def export(self, key, value=None):
-        if value is not None:
-            self[key] = value
-        self._exported.add(key)
-        
-    def get_exported(self):
-        return [(k,self._env.get(k)) for k in self._exported]
-            
-    def split_fields(self, word):
-        if not self._ifs_ws or not word:
-            return [word]
-        return re.split(self._ifs_re, word)
-   
-    def _update_ifs(self, value):
-        """Update the split_fields related variables when IFS character set is
-        changed.
-        """
-        # TODO: handle NULL IFS
-        
-        # Separate characters in whitespace and non-whitespace
-        chars = set(value)
-        ws = [c for c in chars if c in _IFS_WHITESPACES]
-        nws = [c for c in chars if c not in _IFS_WHITESPACES]
-        
-        # Keep whitespaces in a string for left and right stripping
-        self._ifs_ws = ''.join(ws)
-        
-        # Build a regexp to split fields
-        trailing = '[' + ''.join([re.escape(c) for c in ws]) + ']'
-        if nws:
-            # First, the single non-whitespace occurence.
-            nws = '[' + ''.join([re.escape(c) for c in nws]) + ']'
-            nws = '(?:' + trailing + '*' + nws + trailing + '*' + '|' + trailing + '+)'
-        else:
-            # Then mix all parts with quantifiers
-            nws = trailing + '+'
-        self._ifs_re = re.compile(nws)
-       
-    def has_opt(self, opt, val=None):
-        return (opt, val) in self._opt
-        
-    def set_opt(self, opt, val=None):
-        self._opt.add((opt, val))
-        
-    def find_in_path(self, name, pwd=False):
-        path = self._env.get('PATH', '').split(os.pathsep)
-        if pwd:
-            path[:0] = [self['PWD']]
-        if os.name == 'nt':
-            return win32_find_in_path(name, self._env.get('PATH', ''))
-        else:
-            raise NotImplementedError()
-            
-    def define_function(self, name, body):
-        if not is_name(name):
-            raise ShellSyntaxError('%s is not a valid function name' % repr(name))
-        self._functions[name] = body
-        
-    def remove_function(self, name):
-        del self._functions[name]
-        
-    def is_function(self, name):
-        return name in self._functions
-        
-    def get_function(self, name):
-        return self._functions.get(name)
-        
-       
-name_charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'
-name_charset = dict(zip(name_charset,name_charset))
-           
-def match_name(s):
-    """Return the length in characters of the longest prefix made of name
-    allowed characters in s.
-    """
-    for i,c in enumerate(s):
-        if c not in name_charset:
-            return s[:i]
-    return s
-    
-def is_name(s):
-    return len([c for c in s if c not in name_charset])<=0
-    
-def is_special_param(c):
-    return len(c)==1 and c in ('@','*','#','?','-','$','!','0')
-    
-def utility_not_implemented(name, *args, **kwargs):
-    raise NotImplementedError('%s utility is not implemented' % name)
-    
-
-class Utility:
-    """Define utilities properties:
-    func -- utility callable. See builtin module for utility samples.
-    is_special -- see XCU 2.8.
-    """
-    def __init__(self, func, is_special=0):
-        self.func = func
-        self.is_special = bool(is_special)
-
-
-def encodeargs(args):
-    def encodearg(s):
-        lines = base64.encodestring(s)
-        lines = [l.splitlines()[0] for l in lines]
-        return ''.join(lines)
-
-    s = pickle.dumps(args)
-    return encodearg(s)
-
-def decodeargs(s):
-    s = base64.decodestring(s)
-    return pickle.loads(s)
-    
-
-class GlobError(Exception):
-    pass
-
-class Options:
-    def __init__(self):
-        # True if Mercurial operates with binary streams
-        self.hgbinary = True
-
-class Interpreter:
-    # Implementation is very basic: the execute() method just makes a DFS on the
-    # AST and execute nodes one by one. Nodes are tuple (name,obj) where name
-    # is a string identifier and obj the AST element returned by the parser.
-    #
-    # Handler are named after the node identifiers.
-    # TODO: check node names and remove the switch in execute with some
-    # dynamic getattr() call to find node handlers.
-    """Shell interpreter.
-    
-    The following debugging flags can be passed:
-    debug-parsing - enable PLY debugging.
-    debug-tree - print the generated AST.
-    debug-cmd - trace command execution before word expansion, plus exit status.
-    debug-utility - trace utility execution.
-    """
-    
-    # List supported commands.
-    COMMANDS = {
-        'cat':       Utility(builtin.utility_cat,),
-        'cd':       Utility(builtin.utility_cd,),
-        ':':        Utility(builtin.utility_colon,),
-        'echo':     Utility(builtin.utility_echo),
-        'env':      Utility(builtin.utility_env),
-        'exit':     Utility(builtin.utility_exit),
-        'export':   Utility(builtin.builtin_export,     is_special=1),
-        'egrep':    Utility(builtin.utility_egrep),
-        'fgrep':    Utility(builtin.utility_fgrep),
-        'gunzip':   Utility(builtin.utility_gunzip),
-        'kill':     Utility(builtin.utility_kill),
-        'mkdir':    Utility(builtin.utility_mkdir),
-        'netstat':  Utility(builtin.utility_netstat),
-        'printf':   Utility(builtin.utility_printf),
-        'pwd':      Utility(builtin.utility_pwd),
-        'return':   Utility(builtin.builtin_return,     is_special=1),
-        'sed':      Utility(builtin.utility_sed,),
-        'set':      Utility(builtin.builtin_set,),
-        'shift':    Utility(builtin.builtin_shift,),
-        'sleep':    Utility(builtin.utility_sleep,),
-        'sort':     Utility(builtin.utility_sort,),
-        'trap':     Utility(builtin.builtin_trap,       is_special=1),
-        'true':     Utility(builtin.utility_true),
-        'unset':    Utility(builtin.builtin_unset,      is_special=1),
-        'wait':     Utility(builtin.builtin_wait,       is_special=1),
-    }
-    
-    def __init__(self, pwd, debugflags = [], env=None, redirs=None, stdin=None,
-                 stdout=None, stderr=None, opts=Options()):
-        self._env = env
-        if self._env is None:
-            self._env = Environment(pwd)
-        self._children = {}
-            
-        self._redirs = redirs
-        self._close_redirs = False
-        
-        if self._redirs is None:
-            if stdin is None:
-                stdin = sys.stdin
-            if stdout is None:
-                stdout = sys.stdout
-            if stderr is None:
-                stderr = sys.stderr
-            stdin = FileWrapper('r', stdin, False)
-            stdout = FileWrapper('w', stdout, False)
-            stderr = FileWrapper('w', stderr, False)
-            self._redirs = Redirections(stdin, stdout, stderr)
-            self._close_redirs = True
-            
-        self._debugflags = list(debugflags)
-        self._logfile = sys.stderr
-        self._options = opts
-        
-    def close(self):
-        """Must be called when the interpreter is no longer used."""
-        script = self._env.traps.get('EXIT')
-        if script:
-            try:
-                self.execute_script(script=script)
-            except:
-                pass
-
-        if self._redirs is not None and self._close_redirs:
-            self._redirs.close()
-            self._redirs = None
-            
-    def log(self, s):
-        self._logfile.write(s)
-        self._logfile.flush()
-            
-    def __getitem__(self, key):
-        return self._env[key]
-        
-    def __setitem__(self, key, value):
-        self._env[key] = value
-
-    def options(self):
-        return self._options
-
-    def redirect(self, redirs, ios):
-        def add_redir(io):
-            if isinstance(io, pyshyacc.IORedirect):
-                redirs.add(self, io.op, io.filename, io.io_number)
-            else:
-                redirs.add_here_document(self, io.name, io.content, io.io_number)
-                    
-        map(add_redir, ios)
-        return redirs
-            
-    def execute_script(self, script=None, ast=None, sourced=False,
-                       scriptpath=None):
-        """If script is not None, parse the input. Otherwise takes the supplied
-        AST. Then execute the AST.
-        Return the script exit status.
-        """
-        try:
-            if scriptpath is not None:
-                self._env['0'] = os.path.abspath(scriptpath)
-
-            if script is not None:
-                debug_parsing = ('debug-parsing' in self._debugflags)    
-                cmds, script = pyshyacc.parse(script, True, debug_parsing)
-                if 'debug-tree' in self._debugflags:
-                    pyshyacc.print_commands(cmds, self._logfile)
-                    self._logfile.flush()
-            else:
-                cmds, script = ast, ''                
-                
-            status = 0
-            for cmd in cmds:
-                try:
-                    status = self.execute(cmd)
-                except ExitSignal as e:
-                    if sourced:
-                        raise
-                    status = int(e.args[0])
-                    return status
-                except ShellError:
-                    self._env['?'] = 1
-                    raise
-                if 'debug-utility' in self._debugflags or 'debug-cmd' in self._debugflags:
-                    self.log('returncode ' + str(status)+ '\n')
-            return status
-        except CommandNotFound as e:
-            print >>self._redirs.stderr, str(e)
-            self._redirs.stderr.flush()
-            # Command not found by non-interactive shell
-            # return 127
-            raise
-        except RedirectionError as e:
-            # TODO: should be handled depending on the utility status
-            print >>self._redirs.stderr, str(e)
-            self._redirs.stderr.flush()
-            # Command not found by non-interactive shell
-            # return 127
-            raise
-
-    def dotcommand(self, env, args):
-        if len(args) < 1:
-            raise ShellError('. expects at least one argument')
-        path = args[0]
-        if '/' not in path:
-            found = env.find_in_path(args[0], True)
-            if found:
-                path = found[0]
-        script = file(path).read()
-        return self.execute_script(script=script, sourced=True)
-
-    def execute(self, token, redirs=None):
-        """Execute and AST subtree with supplied redirections overriding default
-        interpreter ones.
-        Return the exit status.
-        """
-        if not token:
-            return 0
-            
-        if redirs is None:
-            redirs = self._redirs
-            
-        if isinstance(token, list):
-            # Commands sequence
-            res = 0
-            for t in token:
-                res = self.execute(t, redirs)
-            return res
-
-        type, value = token
-        status = 0
-        if type=='simple_command':
-            redirs_copy = redirs.clone()
-            try:
-                # TODO: define and handle command return values
-                # TODO: implement set -e
-                status = self._execute_simple_command(value, redirs_copy)
-            finally:
-                redirs_copy.close()
-        elif type=='pipeline':
-            status = self._execute_pipeline(value, redirs)
-        elif type=='and_or':
-            status = self._execute_and_or(value, redirs)
-        elif type=='for_clause':
-            status = self._execute_for_clause(value, redirs)
-        elif type=='while_clause':
-            status = self._execute_while_clause(value, redirs)
-        elif type=='function_definition':
-            status = self._execute_function_definition(value, redirs)
-        elif type=='brace_group':
-            status = self._execute_brace_group(value, redirs)
-        elif type=='if_clause':
-            status = self._execute_if_clause(value, redirs)
-        elif type=='subshell':
-            status = self.subshell(ast=value.cmds, redirs=redirs)
-        elif type=='async':
-            status = self._asynclist(value)
-        elif type=='redirect_list':
-            redirs_copy = self.redirect(redirs.clone(), value.redirs)
-            try:
-                status = self.execute(value.cmd, redirs_copy)
-            finally:
-                redirs_copy.close()
-        else:
-            raise NotImplementedError('Unsupported token type ' + type)
-
-        if status < 0:
-            status = 255
-        return status
-            
-    def _execute_if_clause(self, if_clause, redirs):
-        cond_status = self.execute(if_clause.cond, redirs)
-        if cond_status==0:
-            return self.execute(if_clause.if_cmds, redirs)
-        else:
-            return self.execute(if_clause.else_cmds, redirs)
-      
-    def _execute_brace_group(self, group, redirs):
-        status = 0
-        for cmd in group.cmds:
-            status = self.execute(cmd, redirs)
-        return status
-            
-    def _execute_function_definition(self, fundef, redirs):
-        self._env.define_function(fundef.name, fundef.body)
-        return 0
-            
-    def _execute_while_clause(self, while_clause, redirs):
-        status = 0
-        while 1:
-            cond_status = 0
-            for cond in while_clause.condition:
-                cond_status = self.execute(cond, redirs)
-                
-            if cond_status:
-                break
-                
-            for cmd in while_clause.cmds:
-                status = self.execute(cmd, redirs)
-                
-        return status
-            
-    def _execute_for_clause(self, for_clause, redirs):
-        if not is_name(for_clause.name):
-            raise ShellSyntaxError('%s is not a valid name' % repr(for_clause.name))
-        items = mappend(self.expand_token, for_clause.items)
-        
-        status = 0    
-        for item in items:
-            self._env[for_clause.name] = item
-            for cmd in for_clause.cmds:
-                status = self.execute(cmd, redirs)
-        return status
-            
-    def _execute_and_or(self, or_and, redirs):
-        res = self.execute(or_and.left, redirs)        
-        if (or_and.op=='&&' and res==0) or (or_and.op!='&&' and res!=0):
-            res = self.execute(or_and.right, redirs)
-        return res
-            
-    def _execute_pipeline(self, pipeline, redirs):            
-        if len(pipeline.commands)==1:
-            status = self.execute(pipeline.commands[0], redirs)
-        else:
-            # Execute all commands one after the other
-            status = 0
-            inpath, outpath = None, None
-            try:
-                # Commands inputs and outputs cannot really be plugged as done
-                # by a real shell. Run commands sequentially and chain their
-                # input/output throught temporary files.
-                tmpfd, inpath = tempfile.mkstemp()
-                os.close(tmpfd)
-                tmpfd, outpath = tempfile.mkstemp()
-                os.close(tmpfd)
-                
-                inpath = win32_to_unix_path(inpath)
-                outpath = win32_to_unix_path(outpath)
-                
-                for i, cmd in enumerate(pipeline.commands):
-                    call_redirs = redirs.clone()
-                    try:
-                        if i!=0:
-                            call_redirs.add(self, '<', inpath)
-                        if i!=len(pipeline.commands)-1:
-                            call_redirs.add(self, '>', outpath)
-                        
-                        status = self.execute(cmd, call_redirs)
-                        
-                        # Chain inputs/outputs
-                        inpath, outpath = outpath, inpath
-                    finally:
-                        call_redirs.close()            
-            finally:
-                if inpath: os.remove(inpath)
-                if outpath: os.remove(outpath)
-        
-        if pipeline.reverse_status:
-            status = int(not status)
-        self._env['?'] = status
-        return status
-        
-    def _execute_function(self, name, args, interp, env, stdin, stdout, stderr, *others):
-        assert interp is self
-        
-        func = env.get_function(name)
-        #Set positional parameters
-        prevargs = None
-        try:
-            prevargs = env.set_positional_args(args)
-            try:
-                redirs = Redirections(stdin.dup(), stdout.dup(), stderr.dup())
-                try:
-                    status = self.execute(func, redirs)
-                finally:
-                    redirs.close()
-            except ReturnSignal as e:
-                status = int(e.args[0])
-                env['?'] = status
-            return status
-        finally:
-            #Reset positional parameters
-            if prevargs is not None:
-                env.set_positional_args(prevargs)
-                
-    def _execute_simple_command(self, token, redirs):
-        """Can raise ReturnSignal when return builtin is called, ExitSignal when
-        exit is called, and other shell exceptions upon builtin failures.
-        """
-        debug_command = 'debug-cmd' in self._debugflags
-        if debug_command:
-            self.log('word' + repr(token.words) + '\n')
-            self.log('assigns' + repr(token.assigns) + '\n')
-            self.log('redirs' + repr(token.redirs) + '\n')
-        
-        is_special = None
-        env = self._env
-        
-        try:
-            # Word expansion
-            args = []
-            for word in token.words:                
-                args += self.expand_token(word)
-                if is_special is None and args:
-                    is_special = env.is_function(args[0]) or \
-                        (args[0] in self.COMMANDS and self.COMMANDS[args[0]].is_special)
-                        
-            if debug_command:
-                self.log('_execute_simple_command' + str(args) + '\n')
-                
-            if not args:
-                # Redirections happen is a subshell
-                redirs = redirs.clone()
-            elif not is_special:
-                env = self._env.clone()
-            
-            # Redirections
-            self.redirect(redirs, token.redirs)
-                
-            # Variables assignments
-            res = 0
-            for type,(k,v) in token.assigns:
-                status, expanded = self.expand_variable((k,v))
-                if status is not None:
-                    res = status
-                if args:
-                    env.export(k, expanded)
-                else:
-                    env[k] = expanded
-                
-            if args and args[0] in ('.', 'source'):
-                res = self.dotcommand(env, args[1:])
-            elif args:
-                if args[0] in self.COMMANDS:
-                    command = self.COMMANDS[args[0]]
-                elif env.is_function(args[0]):
-                    command = Utility(self._execute_function, is_special=True)
-                else:
-                    if not '/' in args[0].replace('\\', '/'):
-                        cmd = env.find_in_path(args[0])
-                        if not cmd:
-                            # TODO: test error code on unknown command => 127
-                            raise CommandNotFound('Unknown command: "%s"' % args[0])
-                    else:
-                        # Handle commands like '/cygdrive/c/foo.bat'
-                        cmd = cygwin_to_windows_path(args[0])
-                        if not os.path.exists(cmd):
-                            raise CommandNotFound('%s: No such file or directory' % args[0])
-                        shebang = resolve_shebang(cmd)
-                        if shebang:
-                            cmd = shebang
-                        else:
-                            cmd = [cmd]
-                    args[0:1] = cmd
-                    command = Utility(builtin.run_command)
-                
-                # Command execution
-                if 'debug-cmd' in self._debugflags:
-                    self.log('redirections ' + str(redirs) + '\n')
-                    
-                res = command.func(args[0], args[1:], self, env,
-                                   redirs.stdin(), redirs.stdout(), 
-                                   redirs.stderr(), self._debugflags)
-            
-            if self._env.has_opt('-x'):
-                # Trace command execution in shell environment
-                # BUG: would be hard to reproduce a real shell behaviour since
-                # the AST is not annotated with source lines/tokens.
-                self._redirs.stdout().write(' '.join(args))
-                
-        except ReturnSignal:
-            raise
-        except ShellError as e:
-            if is_special or isinstance(e, (ExitSignal,
-                                            ShellSyntaxError, ExpansionError)):
-                raise e
-            self._redirs.stderr().write(str(e)+'\n')
-            return 1
-
-        return res
-
-    def expand_token(self, word):
-        """Expand a word as specified in [2.6 Word Expansions]. Return the list
-        of expanded words.
-        """
-        status, wtrees = self._expand_word(word)
-        return map(pyshlex.wordtree_as_string, wtrees)
-        
-    def expand_variable(self, word):
-        """Return a status code (or None if no command expansion occurred)
-        and a single word.
-        """
-        status, wtrees = self._expand_word(word, pathname=False, split=False)
-        words = map(pyshlex.wordtree_as_string, wtrees)
-        assert len(words)==1
-        return status, words[0]
-        
-    def expand_here_document(self, word):
-        """Return the expanded document as a single word. The here document is 
-        assumed to be unquoted.
-        """
-        status, wtrees = self._expand_word(word, pathname=False,
-                                           split=False, here_document=True)
-        words = map(pyshlex.wordtree_as_string, wtrees)
-        assert len(words)==1
-        return words[0]
-        
-    def expand_redirection(self, word):
-        """Return a single word."""
-        return self.expand_variable(word)[1]
-        
-    def get_env(self):
-        return self._env
-        
-    def _expand_word(self, token, pathname=True, split=True, here_document=False):
-        wtree = pyshlex.make_wordtree(token[1], here_document=here_document)
-        
-        # TODO: implement tilde expansion
-        def expand(wtree):
-            """Return a pseudo wordtree: the tree or its subelements can be empty
-            lists when no value result from the expansion.
-            """
-            status = None
-            for part in wtree:
-                if not isinstance(part, list):
-                    continue
-                if part[0]in ("'", '\\'):
-                    continue
-                elif part[0] in ('`', '$('):
-                    status, result = self._expand_command(part)
-                    part[:] = result
-                elif part[0] in ('$', '${'):
-                    part[:] = self._expand_parameter(part, wtree[0]=='"', split)
-                elif part[0] in ('', '"'):
-                    status, result = expand(part)
-                    part[:] = result
-                else:
-                    raise NotImplementedError('%s expansion is not implemented'
-                                              % part[0])
-            # [] is returned when an expansion result in no-field,
-            # like an empty $@
-            wtree = [p for p in wtree if p != []]
-            if len(wtree) < 3:
-                return status, []
-            return status, wtree
-        
-        status, wtree = expand(wtree)
-        if len(wtree) == 0:
-            return status, wtree
-        wtree = pyshlex.normalize_wordtree(wtree)
-        
-        if split:
-            wtrees = self._split_fields(wtree)
-        else:
-            wtrees = [wtree]
-        
-        if pathname:
-            wtrees = mappend(self._expand_pathname, wtrees)
-        
-        wtrees = map(self._remove_quotes, wtrees)
-        return status, wtrees
-        
-    def _expand_command(self, wtree):
-        # BUG: there is something to do with backslashes and quoted
-        # characters here
-        command = pyshlex.wordtree_as_string(wtree[1:-1])
-        status, output = self.subshell_output(command)
-        return status, ['', output, '']
-        
-    def _expand_parameter(self, wtree, quoted=False, split=False):
-        """Return a valid wtree or an empty list when no parameter results."""
-        # Get the parameter name
-        # TODO: implement weird expansion rules with ':'
-        name = pyshlex.wordtree_as_string(wtree[1:-1])
-        if not is_name(name) and not is_special_param(name):
-            raise ExpansionError('Bad substitution "%s"' % name)
-        # TODO: implement special parameters
-        if name in ('@', '*'):
-            args = self._env.get_positional_args()
-            if len(args) == 0:
-                return []
-            if len(args)<2:
-                return ['', ''.join(args), '']
-                
-            sep = self._env.get('IFS', '')[:1]
-            if split and quoted and name=='@':
-                # Introduce a new token to tell the caller that these parameters
-                # cause a split as specified in 2.5.2
-                return ['@'] + args + ['']
-            else:
-                return ['', sep.join(args), '']                
-        
-        return ['', self._env.get(name, ''), '']
-        
-    def _split_fields(self, wtree):
-        def is_empty(split):
-            return split==['', '', '']
-            
-        def split_positional(quoted):
-            # Return a list of wtree split according positional parameters rules.
-            # All remaining '@' groups are removed.
-            assert quoted[0]=='"'
-            
-            splits = [[]]
-            for part in quoted:
-                if not isinstance(part, list) or part[0]!='@':
-                    splits[-1].append(part)
-                else:
-                    # Empty or single argument list were dealt with already
-                    assert len(part)>3
-                    # First argument must join with the beginning part of the original word
-                    splits[-1].append(part[1])
-                    # Create double-quotes expressions for every argument after the first
-                    for arg in part[2:-1]:
-                        splits[-1].append('"')
-                        splits.append(['"', arg])
-            return splits
-        
-        # At this point, all expansions but pathnames have occured. Only quoted
-        # and positional sequences remain. Thus, all candidates for field splitting 
-        # are in the tree root, or are positional splits ('@') and lie in root
-        # children.
-        if not wtree or wtree[0] not in ('', '"'):
-            # The whole token is quoted or empty, nothing to split
-            return [wtree]
-            
-        if wtree[0]=='"':
-            wtree = ['', wtree, '']
-        
-        result = [['', '']]
-        for part in wtree[1:-1]:
-            if isinstance(part, list):
-                if part[0]=='"':
-                    splits = split_positional(part)
-                    if len(splits)<=1:
-                        result[-1] += [part, '']
-                    else:
-                        # Terminate the current split
-                        result[-1] += [splits[0], '']
-                        result += splits[1:-1]
-                        # Create a new split
-                        result += [['', splits[-1], '']]
-                else:
-                    result[-1] += [part, '']
-            else:
-                splits = self._env.split_fields(part)
-                if len(splits)<=1:
-                    # No split
-                    result[-1][-1] += part
-                else:
-                    # Terminate the current resulting part and create a new one
-                    result[-1][-1] += splits[0]
-                    result[-1].append('')
-                    result += [['', r, ''] for r in splits[1:-1]]
-                    result += [['', splits[-1]]]
-        result[-1].append('')
-        
-        # Leading and trailing empty groups come from leading/trailing blanks
-        if result and is_empty(result[-1]):
-            result[-1:] = []
-        if result and is_empty(result[0]):
-            result[:1] = []
-        return result
-        
-    def _expand_pathname(self, wtree):
-        """See [2.6.6 Pathname Expansion]."""
-        if self._env.has_opt('-f'):
-            return [wtree]
-        
-        # All expansions have been performed, only quoted sequences should remain
-        # in the tree. Generate the pattern by folding the tree, escaping special
-        # characters when appear quoted
-        special_chars = '*?[]'
-        
-        def make_pattern(wtree):
-            subpattern = []
-            for part in wtree[1:-1]:
-                if isinstance(part, list):
-                    part = make_pattern(part)
-                elif wtree[0]!='':
-                    for c in part:
-                        # Meta-characters cannot be quoted
-                        if c in special_chars:
-                            raise GlobError()
-                subpattern.append(part)
-            return ''.join(subpattern)
-            
-        def pwd_glob(pattern):
-            cwd = os.getcwd()
-            os.chdir(self._env['PWD'])
-            try:
-                return glob.glob(pattern) 
-            finally:
-                os.chdir(cwd)    
-            
-        #TODO: check working directory issues here wrt relative patterns
-        try:
-            pattern = make_pattern(wtree)
-            paths = pwd_glob(pattern)
-        except GlobError:
-            # BUG: Meta-characters were found in quoted sequences. The should 
-            # have been used literally but this is unsupported in current glob module.
-            # Instead we consider the whole tree must be used literally and
-            # therefore there is no point in globbing. This is wrong when meta
-            # characters are mixed with quoted meta in the same pattern like:
-            # < foo*"py*" >
-            paths = []
-            
-        if not paths:
-            return [wtree]
-        return [['', path, ''] for path in paths]
-        
-    def _remove_quotes(self, wtree):
-        """See [2.6.7 Quote Removal]."""
-        
-        def unquote(wtree):
-            unquoted = []
-            for part in wtree[1:-1]:
-                if isinstance(part, list):
-                    part = unquote(part)
-                unquoted.append(part)
-            return ''.join(unquoted)
-            
-        return ['', unquote(wtree), '']
-        
-    def subshell(self, script=None, ast=None, redirs=None):
-        """Execute the script or AST in a subshell, with inherited redirections
-        if redirs is not None.
-        """
-        if redirs:
-            sub_redirs = redirs
-        else:
-            sub_redirs = redirs.clone()
-        
-        subshell = None    
-        try:
-            subshell = Interpreter(None, self._debugflags, self._env.clone(True),
-                                   sub_redirs, opts=self._options)
-            return subshell.execute_script(script, ast)
-        finally:
-            if not redirs: sub_redirs.close()
-            if subshell: subshell.close()
-        
-    def subshell_output(self, script):
-        """Execute the script in a subshell and return the captured output."""        
-        # Create temporary file to capture subshell output
-        tmpfd, tmppath = tempfile.mkstemp()
-        try:
-            tmpfile = os.fdopen(tmpfd, 'wb')
-            stdout = FileWrapper('w', tmpfile)
-            
-            redirs = Redirections(self._redirs.stdin().dup(),
-                                  stdout,
-                                  self._redirs.stderr().dup())            
-            try:
-                status = self.subshell(script=script, redirs=redirs)
-            finally:
-                redirs.close()
-                redirs = None
-            
-            # Extract subshell standard output
-            tmpfile = open(tmppath, 'rb')
-            try:
-                output = tmpfile.read()
-                return status, output.rstrip('\n')
-            finally:
-                tmpfile.close()
-        finally:
-            os.remove(tmppath)
-
-    def _asynclist(self, cmd):
-        args = (self._env.get_variables(), cmd)
-        arg = encodeargs(args)
-        assert len(args) < 30*1024
-        cmd = ['pysh.bat', '--ast', '-c', arg]
-        p = subprocess.Popen(cmd, cwd=self._env['PWD'])
-        self._children[p.pid] = p
-        self._env['!'] = p.pid
-        return 0
-
-    def wait(self, pids=None):
-        if not pids:
-            pids = self._children.keys()
-
-        status = 127
-        for pid in pids:
-            if pid not in self._children:
-                continue
-            p = self._children.pop(pid)
-            status = p.wait()
-
-        return status
-
diff --git a/bitbake/lib/bb/pysh/lsprof.py b/bitbake/lib/bb/pysh/lsprof.py
deleted file mode 100644
index b1831c2..0000000
--- a/bitbake/lib/bb/pysh/lsprof.py
+++ /dev/null
@@ -1,116 +0,0 @@
-#! /usr/bin/env python
-
-import sys
-from _lsprof import Profiler, profiler_entry
-
-__all__ = ['profile', 'Stats']
-
-def profile(f, *args, **kwds):
-    """XXX docstring"""
-    p = Profiler()
-    p.enable(subcalls=True, builtins=True)
-    try:
-        f(*args, **kwds)
-    finally:
-        p.disable()
-    return Stats(p.getstats())
-
-
-class Stats(object):
-    """XXX docstring"""
-
-    def __init__(self, data):
-        self.data = data
-
-    def sort(self, crit="inlinetime"):
-        """XXX docstring"""
-        if crit not in profiler_entry.__dict__:
-            raise ValueError("Can't sort by %s" % crit)
-        self.data.sort(lambda b, a: cmp(getattr(a, crit),
-                                        getattr(b, crit)))
-        for e in self.data:
-            if e.calls:
-                e.calls.sort(lambda b, a: cmp(getattr(a, crit),
-                                              getattr(b, crit)))
-
-    def pprint(self, top=None, file=None, limit=None, climit=None):
-        """XXX docstring"""
-        if file is None:
-            file = sys.stdout
-        d = self.data
-        if top is not None:
-            d = d[:top]
-        cols = "% 12s %12s %11.4f %11.4f   %s\n"
-        hcols = "% 12s %12s %12s %12s %s\n"
-        cols2 = "+%12s %12s %11.4f %11.4f +  %s\n"
-        file.write(hcols % ("CallCount", "Recursive", "Total(ms)",
-                            "Inline(ms)", "module:lineno(function)"))
-        count = 0
-        for e in d:
-            file.write(cols % (e.callcount, e.reccallcount, e.totaltime,
-                               e.inlinetime, label(e.code)))
-            count += 1
-            if limit is not None and count == limit:
-                return
-            ccount = 0
-            if e.calls:
-                for se in e.calls:
-                    file.write(cols % ("+%s" % se.callcount, se.reccallcount,
-                                       se.totaltime, se.inlinetime,
-                                       "+%s" % label(se.code)))
-                    count += 1
-                    ccount += 1
-                    if limit is not None and count == limit:
-                        return
-                    if climit is not None and ccount == climit:
-                        break
-
-    def freeze(self):
-        """Replace all references to code objects with string
-        descriptions; this makes it possible to pickle the instance."""
-
-        # this code is probably rather ickier than it needs to be!
-        for i in range(len(self.data)):
-            e = self.data[i]
-            if not isinstance(e.code, str):
-                self.data[i] = type(e)((label(e.code),) + e[1:])
-            if e.calls:
-                for j in range(len(e.calls)):
-                    se = e.calls[j]
-                    if not isinstance(se.code, str):
-                        e.calls[j] = type(se)((label(se.code),) + se[1:])
-
-_fn2mod = {}
-
-def label(code):
-    if isinstance(code, str):
-        return code
-    try:
-        mname = _fn2mod[code.co_filename]
-    except KeyError:
-        for k, v in sys.modules.items():
-            if v is None:
-                continue
-            if not hasattr(v, '__file__'):
-                continue
-            if not isinstance(v.__file__, str):
-                continue
-            if v.__file__.startswith(code.co_filename):
-                mname = _fn2mod[code.co_filename] = k
-                break
-        else:
-            mname = _fn2mod[code.co_filename] = '<%s>'%code.co_filename
-
-    return '%s:%d(%s)' % (mname, code.co_firstlineno, code.co_name)
-
-
-if __name__ == '__main__':
-    import os
-    sys.argv = sys.argv[1:]
-    if not sys.argv:
-        print >> sys.stderr, "usage: lsprof.py <script> <arguments...>"
-        sys.exit(2)
-    sys.path.insert(0, os.path.abspath(os.path.dirname(sys.argv[0])))
-    stats = profile(execfile, sys.argv[0], globals(), locals())
-    stats.sort()
-    stats.pprint()
diff --git a/bitbake/lib/bb/pysh/pysh.py b/bitbake/lib/bb/pysh/pysh.py
deleted file mode 100644
index b4e6145..0000000
--- a/bitbake/lib/bb/pysh/pysh.py
+++ /dev/null
@@ -1,167 +0,0 @@
-# pysh.py - command processing for pysh.
-#
-# Copyright 2007 Patrick Mezard
-#
-# This software may be used and distributed according to the terms
-# of the GNU General Public License, incorporated herein by reference.
-
-import optparse
-import os
-import sys
-
-import interp
-
-SH_OPT = optparse.OptionParser(prog='pysh', usage="%prog [OPTIONS]", version='0.1')
-SH_OPT.add_option('-c', action='store_true', dest='command_string', default=None, 
-    help='A string that shall be interpreted by the shell as one or more commands')
-SH_OPT.add_option('--redirect-to', dest='redirect_to', default=None, 
-    help='Redirect script commands stdout and stderr to the specified file')
-# See utility_command in builtin.py about the reason for this flag.
-SH_OPT.add_option('--redirected', dest='redirected', action='store_true', default=False, 
-    help='Tell the interpreter that stdout and stderr are actually the same objects, which is really stdout')
-SH_OPT.add_option('--debug-parsing', action='store_true', dest='debug_parsing', default=False, 
-    help='Trace PLY execution')
-SH_OPT.add_option('--debug-tree', action='store_true', dest='debug_tree', default=False, 
-    help='Display the generated syntax tree.')
-SH_OPT.add_option('--debug-cmd', action='store_true', dest='debug_cmd', default=False, 
-    help='Trace command execution before parameters expansion and exit status.')
-SH_OPT.add_option('--debug-utility', action='store_true', dest='debug_utility', default=False, 
-    help='Trace utility calls, after parameters expansions')
-SH_OPT.add_option('--ast', action='store_true', dest='ast', default=False,
-    help='Encoded commands to execute in a subprocess')
-SH_OPT.add_option('--profile', action='store_true', default=False,
-    help='Profile pysh run')
-    
-    
-def split_args(args):
-    # Separate shell arguments from command ones
-    # Just stop at the first argument not starting with a dash. I know, this is completely broken,
-    # it ignores files starting with a dash or may take option values for command file. This is not
-    # supposed to happen for now
-    command_index = len(args)
-    for i,arg in enumerate(args):
-        if not arg.startswith('-'):
-            command_index = i
-            break
-            
-    return args[:command_index], args[command_index:]
-
-
-def fixenv(env):
-    path = env.get('PATH')
-    if path is not None:
-        parts = path.split(os.pathsep)
-        # Remove Windows utilities from PATH, they are useless at best and
-        # some of them (find) may be confused with other utilities.
-        parts = [p for p in parts if 'system32' not in p.lower()]
-        env['PATH'] = os.pathsep.join(parts)
-    if env.get('HOME') is None:
-        # Several utilities, including cvsps, cannot work without
-        # a defined HOME directory.
-        env['HOME'] = os.path.expanduser('~')
-    return env
-
-def _sh(cwd, shargs, cmdargs, options, debugflags=None, env=None):
-    if os.environ.get('PYSH_TEXT') != '1':
-        import msvcrt
-        for fp in (sys.stdin, sys.stdout, sys.stderr):
-            msvcrt.setmode(fp.fileno(), os.O_BINARY)
-
-    hgbin = os.environ.get('PYSH_HGTEXT') != '1'
-    
-    if debugflags is None:
-        debugflags = []
-        if options.debug_parsing:    debugflags.append('debug-parsing')
-        if options.debug_utility:    debugflags.append('debug-utility')
-        if options.debug_cmd:        debugflags.append('debug-cmd')
-        if options.debug_tree:       debugflags.append('debug-tree')
-    
-    if env is None:
-        env = fixenv(dict(os.environ))
-    if cwd is None:
-        cwd = os.getcwd()
-
-    if not cmdargs:
-        # Nothing to do
-        return 0
-
-    ast = None
-    command_file = None
-    if options.command_string:
-        input = cmdargs[0]
-        if not options.ast:
-            input += '\n'
-        else:
-            args, input = interp.decodeargs(input), None
-            env, ast = args
-            cwd = env.get('PWD', cwd)
-    else:
-        command_file = cmdargs[0]
-        arguments = cmdargs[1:]
-
-        prefix = interp.resolve_shebang(command_file, ignoreshell=True)
-        if prefix:
-            input = ' '.join(prefix + [command_file] + arguments)
-        else:
-            # Read commands from file
-            f = file(command_file)
-            try:
-                # Trailing newline to help the parser
-                input = f.read() + '\n'
-            finally:
-                f.close()
-    
-    redirect = None
-    try:
-        if options.redirected:
-            stdout = sys.stdout
-            stderr = stdout
-        elif options.redirect_to:
-            redirect = open(options.redirect_to, 'wb')
-            stdout = redirect
-            stderr = redirect
-        else:
-            stdout = sys.stdout
-            stderr = sys.stderr
-            
-        # TODO: set arguments to environment variables
-        opts = interp.Options()
-        opts.hgbinary = hgbin
-        ip = interp.Interpreter(cwd, debugflags, stdout=stdout, stderr=stderr,
-                                opts=opts)
-        try:
-            # Export given environment in shell object
-            for k,v in env.iteritems():
-                ip.get_env().export(k,v)
-            return ip.execute_script(input, ast, scriptpath=command_file)
-        finally:
-            ip.close()
-    finally:
-        if redirect is not None:
-            redirect.close()
-
-def sh(cwd=None, args=None, debugflags=None, env=None):
-    if args is None:
-        args = sys.argv[1:]
-    shargs, cmdargs = split_args(args)
-    options, shargs = SH_OPT.parse_args(shargs)
-
-    if options.profile:
-        import lsprof
-        p = lsprof.Profiler()
-        p.enable(subcalls=True)
-        try:
-            return _sh(cwd, shargs, cmdargs, options, debugflags, env)
-        finally:
-            p.disable()
-            stats = lsprof.Stats(p.getstats())
-            stats.sort()
-            stats.pprint(top=10, file=sys.stderr, climit=5)
-    else:
-        return _sh(cwd, shargs, cmdargs, options, debugflags, env)
-            
-def main():
-    sys.exit(sh())
-
-if __name__=='__main__':
-    main()
diff --git a/bitbake/lib/bb/pysh/subprocess_fix.py b/bitbake/lib/bb/pysh/subprocess_fix.py
deleted file mode 100644
index 46eca22..0000000
--- a/bitbake/lib/bb/pysh/subprocess_fix.py
+++ /dev/null
@@ -1,77 +0,0 @@
-# subprocess - Subprocesses with accessible I/O streams
-#
-# For more information about this module, see PEP 324.
-#
-# This module should remain compatible with Python 2.2, see PEP 291.
-#
-# Copyright (c) 2003-2005 by Peter Astrand <astrand@lysator.liu.se>
-#
-# Licensed to PSF under a Contributor Agreement.
-# See http://www.python.org/2.4/license for licensing details.
-
-def list2cmdline(seq):
-    """
-    Translate a sequence of arguments into a command line
-    string, using the same rules as the MS C runtime:
-
-    1) Arguments are delimited by white space, which is either a
-       space or a tab.
-
-    2) A string surrounded by double quotation marks is
-       interpreted as a single argument, regardless of white space
-       contained within.  A quoted string can be embedded in an
-       argument.
-
-    3) A double quotation mark preceded by a backslash is
-       interpreted as a literal double quotation mark.
-
-    4) Backslashes are interpreted literally, unless they
-       immediately precede a double quotation mark.
-
-    5) If backslashes immediately precede a double quotation mark,
-       every pair of backslashes is interpreted as a literal
-       backslash.  If the number of backslashes is odd, the last
-       backslash escapes the next double quotation mark as
-       described in rule 3.
-    """
-
-    # See
-    # http://msdn.microsoft.com/library/en-us/vccelng/htm/progs_12.asp
-    result = []
-    needquote = False
-    for arg in seq:
-        bs_buf = []
-
-        # Add a space to separate this argument from the others
-        if result:
-            result.append(' ')
-
-        needquote = (" " in arg) or ("\t" in arg) or ("|" in arg) or arg == ""
-        if needquote:
-            result.append('"')
-
-        for c in arg:
-            if c == '\\':
-                # Don't know if we need to double yet.
-                bs_buf.append(c)
-            elif c == '"':
-                # Double backspaces.
-                result.append('\\' * len(bs_buf)*2)
-                bs_buf = []
-                result.append('\\"')
-            else:
-                # Normal char
-                if bs_buf:
-                    result.extend(bs_buf)
-                    bs_buf = []
-                result.append(c)
-
-        # Add remaining backspaces, if any.
-        if bs_buf:
-            result.extend(bs_buf)
-
-        if needquote:
-            result.extend(bs_buf)
-            result.append('"')
-            
-    return ''.join(result)
-- 
2.7.4



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/4] pysh/sherrors.py: Remove unused classes
  2018-12-04  2:05 [PATCH 0/4] fixes for pysh Robert Yang
  2018-12-04  1:59 ` Robert Yang
  2018-12-04  2:06 ` [PATCH 1/4] pysh: Remove unused modules Robert Yang
@ 2018-12-04  2:06 ` Robert Yang
  2018-12-04  2:06 ` [PATCH 3/4] pysh/pyshlex.py: Set is not importable Robert Yang
  2018-12-04  2:06 ` [PATCH 4/4] bitbake: pysh: Improve error handling for shell code Robert Yang
  4 siblings, 0 replies; 6+ messages in thread
From: Robert Yang @ 2018-12-04  2:06 UTC (permalink / raw)
  To: bitbake-devel

The only two that we need are ShellSyntaxError and ShellError, others are not
used, so remove them.

Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
---
 bitbake/lib/bb/pysh/sherrors.py | 26 --------------------------
 1 file changed, 26 deletions(-)

diff --git a/bitbake/lib/bb/pysh/sherrors.py b/bitbake/lib/bb/pysh/sherrors.py
index 49d0533..3fe8e47 100644
--- a/bitbake/lib/bb/pysh/sherrors.py
+++ b/bitbake/lib/bb/pysh/sherrors.py
@@ -13,29 +13,3 @@ class ShellError(Exception):
 
 class ShellSyntaxError(ShellError):
     pass
-    
-class UtilityError(ShellError):
-    """Raised upon utility syntax error (option or operand error)."""
-    pass
-   
-class ExpansionError(ShellError):
-    pass
-     
-class CommandNotFound(ShellError):
-    """Specified command was not found."""
-    pass
-    
-class RedirectionError(ShellError):
-    pass
-    
-class VarAssignmentError(ShellError):
-    """Variable assignment error."""
-    pass
-    
-class ExitSignal(ShellError):
-    """Exit signal."""
-    pass
-    
-class ReturnSignal(ShellError):
-    """Exit signal."""
-    pass
-- 
2.7.4



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 3/4] pysh/pyshlex.py: Set is not importable
  2018-12-04  2:05 [PATCH 0/4] fixes for pysh Robert Yang
                   ` (2 preceding siblings ...)
  2018-12-04  2:06 ` [PATCH 2/4] pysh/sherrors.py: Remove unused classes Robert Yang
@ 2018-12-04  2:06 ` Robert Yang
  2018-12-04  2:06 ` [PATCH 4/4] bitbake: pysh: Improve error handling for shell code Robert Yang
  4 siblings, 0 replies; 6+ messages in thread
From: Robert Yang @ 2018-12-04  2:06 UTC (permalink / raw)
  To: bitbake-devel

>>> from Set import Set
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named 'Set'

So remove related code.

Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
---
 bitbake/lib/bb/pysh/pyshlex.py | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/bitbake/lib/bb/pysh/pyshlex.py b/bitbake/lib/bb/pysh/pyshlex.py
index fbf094b..a42c294 100644
--- a/bitbake/lib/bb/pysh/pyshlex.py
+++ b/bitbake/lib/bb/pysh/pyshlex.py
@@ -13,11 +13,6 @@
 # PLY in pull mode. It was designed to work incrementally and it would not be
 # that hard to enable pull mode.
 import re
-try:
-    s = set()
-    del s
-except NameError:
-    from Set import Set as set
 
 from ply import lex
 from bb.pysh.sherrors import *
-- 
2.7.4



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 4/4] bitbake: pysh: Improve error handling for shell code
  2018-12-04  2:05 [PATCH 0/4] fixes for pysh Robert Yang
                   ` (3 preceding siblings ...)
  2018-12-04  2:06 ` [PATCH 3/4] pysh/pyshlex.py: Set is not importable Robert Yang
@ 2018-12-04  2:06 ` Robert Yang
  4 siblings, 0 replies; 6+ messages in thread
From: Robert Yang @ 2018-12-04  2:06 UTC (permalink / raw)
  To: bitbake-devel

The p_error() is used for printing errors when parse shell code, but it can't
the EOF error correctly

- Add the following lines to quilt.inc
  do_configure_prepend () {
      find ${s} -name "*.in" -exec sed -i -e "1s,^#\!.*@perl@ -w$,#\! @perl@\nuse warnings;," {} \;
      if [ hello ]; then
  }

- Before the patch:
  $ rm -fr cache/ tmp/cache/; bitbake -p
  [snip]
  WARNING: /path/to/quilt/quilt-native_0.65.bb: Error during finalise of /path/to/quilt/quilt-native_0.65.bb
  [snip]
  bb.pysh.sherrors.ShellSyntaxError: None
  followed by:

  We can see that this isn't easy to debug, let p_error() check wheter it is EOF
  and print appropriate errors can improve the error message. And don't let
  codeparser.py except pyshlex.NeedMore (in fact, it never worked since p_error()
  only raise ShellSyntaxError), but make it print the last 5 lines which might be
  useful for debuging.

- After the patch
  $ rm -fr cache/ tmp/cache/; bitbake -p
  [snip]
  ERROR: /path/to/quilt/quilt_0.65.bb: Error during parse shell code, the last 5 lines are:
      find /path/to/quilt/0.65-r0/quilt-0.65 -name "*.in" -exec sed -i -e "1s,^#\!.*@PERL@ -w$,#\! @PERL@\nuse warnings;," {} \;
      if [ hello ]; then
      autotools_do_configure
      sed -e 's,^COMPAT_SYMLINKS.*:=.*,COMPAT_SYMLINKS    :=,' -i /path/to/quilt/0.65-r0/quilt-0.65/Makefile
  [snip]
    File "/path/to/bb/pysh/pyshyacc.py", line 649, in p_error(p=None):
               w('Unexpected EOF')
      >    raise sherrors.ShellSyntaxError(''.join(msg))

  bb.pysh.sherrors.ShellSyntaxError: Unexpected EOF

Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
---
 bitbake/lib/bb/codeparser.py    |  5 +++--
 bitbake/lib/bb/pysh/pyshyacc.py | 17 ++++++++++-------
 2 files changed, 13 insertions(+), 9 deletions(-)

diff --git a/bitbake/lib/bb/codeparser.py b/bitbake/lib/bb/codeparser.py
index ddd1b97..3f8ac1d 100644
--- a/bitbake/lib/bb/codeparser.py
+++ b/bitbake/lib/bb/codeparser.py
@@ -368,8 +368,9 @@ class ShellParser():
     def _parse_shell(self, value):
         try:
             tokens, _ = pyshyacc.parse(value, eof=True, debug=False)
-        except pyshlex.NeedMore:
-            raise sherrors.ShellSyntaxError("Unexpected EOF")
+        except Exception:
+            bb.error('Error during parse shell code, the last 5 lines are:\n%s' % '\n'.join(value.split('\n')[-5:]))
+            raise
 
         self.process_tokens(tokens)
 
diff --git a/bitbake/lib/bb/pysh/pyshyacc.py b/bitbake/lib/bb/pysh/pyshyacc.py
index ba4cefd..de565dc 100644
--- a/bitbake/lib/bb/pysh/pyshyacc.py
+++ b/bitbake/lib/bb/pysh/pyshyacc.py
@@ -636,13 +636,16 @@ def p_empty(p):
 def p_error(p):
     msg = []
     w = msg.append
-    w('%r\n' % p)
-    w('followed by:\n')
-    for i in range(5):
-        n = yacc.token()
-        if not n:
-            break
-        w('  %r\n' % n)
+    if p:
+        w('%r\n' % p)
+        w('followed by:\n')
+        for i in range(5):
+            n = yacc.token()
+            if not n:
+                break
+            w('  %r\n' % n)
+    else:
+        w('Unexpected EOF')
     raise sherrors.ShellSyntaxError(''.join(msg))
 
 # Build the parser
-- 
2.7.4



^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2018-12-04  1:54 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2018-12-04  2:05 [PATCH 0/4] fixes for pysh Robert Yang
2018-12-04  1:59 ` Robert Yang
2018-12-04  2:06 ` [PATCH 1/4] pysh: Remove unused modules Robert Yang
2018-12-04  2:06 ` [PATCH 2/4] pysh/sherrors.py: Remove unused classes Robert Yang
2018-12-04  2:06 ` [PATCH 3/4] pysh/pyshlex.py: Set is not importable Robert Yang
2018-12-04  2:06 ` [PATCH 4/4] bitbake: pysh: Improve error handling for shell code Robert Yang

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.