summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2002-11-25 23:33:49 (GMT)
committerSteven Knight <knight@baldmt.com>2002-11-25 23:33:49 (GMT)
commitdd30a312b7c59abd25d41c3d332df57801abf66b (patch)
tree850fbde140c46672ee388dea2fe3fc0dd4ed525f /src
parent22c249b07f1831b86aca87ba1728a0cf19884b80 (diff)
downloadSCons-dd30a312b7c59abd25d41c3d332df57801abf66b.zip
SCons-dd30a312b7c59abd25d41c3d332df57801abf66b.tar.gz
SCons-dd30a312b7c59abd25d41c3d332df57801abf66b.tar.bz2
Make the shell pickable via a construction variable. (Anthony Roach)
Diffstat (limited to 'src')
-rw-r--r--src/CHANGES.txt3
-rw-r--r--src/RELEASE.txt41
-rw-r--r--src/engine/SCons/Action.py182
-rw-r--r--src/engine/SCons/ActionTests.py36
-rw-r--r--src/engine/SCons/BuilderTests.py6
-rw-r--r--src/engine/SCons/Platform/PlatformTests.py17
-rw-r--r--src/engine/SCons/Platform/cygwin.py16
-rw-r--r--src/engine/SCons/Platform/posix.py58
-rw-r--r--src/engine/SCons/Platform/win32.py55
9 files changed, 185 insertions, 229 deletions
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index 0c6325f..825d657 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -122,6 +122,9 @@ RELEASE 0.09 -
- Allow the File() and Dir() methods to take a path-name string as
the starting directory, in addition to a Dir object.
+ - Allow the command handler to be selected via the SPAWN, SHELL
+ and ESCAPE construction variables.
+
From sam th:
- Dynamically check for the existence of utilities with which to
diff --git a/src/RELEASE.txt b/src/RELEASE.txt
index 0dab6ce..5f8c89a 100644
--- a/src/RELEASE.txt
+++ b/src/RELEASE.txt
@@ -25,6 +25,13 @@ RELEASE 0.08 - Mon, 15 Jul 2002 12:08:51 -0500
This is the eighth alpha release of SCons. Please consult the
CHANGES.txt file for a list of specific changes since last release.
+ Please note the following important changes since release 0.08:
+
+ - The SetCommandHandler() function has been superceded
+ by the SPAWN, SHELL and ESCAPE construction variables.
+
+ XXX
+
Please note the following important changes since release 0.07:
- Builder objects no longer automatically split target and source
@@ -66,35 +73,6 @@ RELEASE 0.08 - Mon, 15 Jul 2002 12:08:51 -0500
- The prefix, suffix, and src_suffix keyword arguments to the
Builder() function may no longer be callable functions.
- Please note the following important changes since release 0.06:
-
- - The functionality of the -U option has changed. It now works
- exactly like the -u option (searches up the directory tree for an
- SConstruct file) but, when no targets are specified on the command
- line, it will build all targets that are defined in any SConscript
- files in the current directory.
-
- The previous functionality of this option is now available in the
- -D option: when no targets are specified on the command line,
- SCons will build *all* Default() targets, not just those at or
- below the current directory,
-
- - The default Fortran compilation command on Windows systems now
- uses Windows conventions (/Fo) instead of UNIX conventions (-o).
-
- - The $SOURCE construction variable is now a synonym for
- ${SOURCES[0]}. This will affect you if you previously set $SOURCE
- explicitly in a construction environment.
-
- - Scanner functions now take three or four arguments. The target
- Node is now passed in as the third argument; the fourth argument
- is an optional SCons.Node.FS.FS object. You will need to update
- the interfaces of any local Scanner functions you have defined.
-
- - Command generator functions now take a fourth argument,
- for_signature. You will need to add this argument to any
- generator functions you have defined.
-
Owing to an extensive test suite, the SCons team believes that this
release is of sufficient quality that you can use it for real work,
despite the "alpha" label.
@@ -160,11 +138,6 @@ RELEASE 0.08 - Mon, 15 Jul 2002 12:08:51 -0500
site is currently out of date. Take what you read there with a
grain of salt.
- - SCons does not yet support file names with quotes (" or ') in the
- file name when an external command is used to create or process a
- file. Results will be unpredictable based on the interaction with
- the shell used to execute the external command.
-
- If a file is specified to be built in multiple ways, the last
processed builder specification overwrites all other builders,
without any warning.
diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py
index 8347195..534b813 100644
--- a/src/engine/SCons/Action.py
+++ b/src/engine/SCons/Action.py
@@ -40,6 +40,7 @@ import UserDict
import SCons.Util
import SCons.Errors
+
print_actions = 1;
execute_actions = 1;
@@ -56,168 +57,11 @@ def rfile(n):
except AttributeError:
return n
-if os.name == 'posix':
-
- def defaultEscape(arg):
- "escape shell special characters"
- slash = '\\'
- special = '"$'
-
- arg = string.replace(arg, slash, slash+slash)
- for c in special:
- arg = string.replace(arg, c, slash+c)
-
- return '"' + arg + '"'
-
- # If the env command exists, then we can use os.system()
- # to spawn commands, otherwise we fall back on os.fork()/os.exec().
- # os.system() is prefered because it seems to work better with
- # threads (i.e. -j) and is more efficient than forking Python.
- if SCons.Util.WhereIs('env'):
- def defaultSpawn(cmd, args, env):
- if env:
- s = 'env -i '
- for key in env.keys():
- s = s + '%s=%s '%(key, defaultEscape(env[key]))
- s = s + 'sh -c '
- s = s + defaultEscape(string.join(args))
- else:
- s = string.join(args)
-
- return os.system(s) >> 8
- else:
- def defaultSpawn(cmd, args, env):
- pid = os.fork()
- if not pid:
- # Child process.
- exitval = 127
- args = ['sh', '-c', string.join(args)]
- try:
- os.execvpe('sh', args, env)
- except OSError, e:
- exitval = exitvalmap[e[0]]
- sys.stderr.write("scons: %s: %s\n" % (cmd, e[1]))
- os._exit(exitval)
- else:
- # Parent process.
- pid, stat = os.waitpid(pid, 0)
- ret = stat >> 8
- return ret
-
-elif os.name == 'nt':
-
- def pathsearch(cmd, env):
- # In order to deal with the fact that 1.5.2 doesn't have
- # os.spawnvpe(), roll our own PATH search.
- if os.path.isabs(cmd):
- if not os.path.exists(cmd):
- exts = env['PATHEXT']
- if not SCons.Util.is_List(exts):
- exts = string.split(exts, os.pathsep)
- for e in exts:
- f = cmd + e
- if os.path.exists(f):
- return f
- else:
- return cmd
- else:
- path = env['PATH']
- if not SCons.Util.is_List(path):
- path = string.split(path, os.pathsep)
- exts = env['PATHEXT']
- if not SCons.Util.is_List(exts):
- exts = string.split(exts, os.pathsep)
- pairs = []
- for dir in path:
- for e in exts:
- pairs.append((dir, e))
- for dir, ext in pairs:
- f = os.path.join(dir, cmd)
- if not ext is None:
- f = f + ext
- if os.path.exists(f):
- return f
- return None
-
- # Attempt to find cmd.exe (for WinNT/2k/XP) or
- # command.com for Win9x
-
- cmd_interp = ''
- # First see if we can look in the registry...
- if SCons.Util.can_read_reg:
- try:
- # Look for Windows NT system root
- k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE,
- 'Software\\Microsoft\\Windows NT\\CurrentVersion')
- val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot')
- cmd_interp = os.path.join(val, 'System32\\cmd.exe')
- except SCons.Util.RegError:
- try:
- # Okay, try the Windows 9x system root
- k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE,
- 'Software\\Microsoft\\Windows\\CurrentVersion')
- val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot')
- cmd_interp = os.path.join(val, 'command.com')
- except:
- pass
- if not cmd_interp:
- cmd_interp = pathsearch('cmd', os.environ)
- if not cmd_interp:
- cmd_interp = pathsearch('command', os.environ)
-
- # The upshot of all this is that, if you are using Python 1.5.2,
- # you had better have cmd or command.com in your PATH when you run
- # scons.
-
- def defaultSpawn(cmd, args, env):
- if not cmd_interp:
- sys.stderr.write("scons: Could not find command interpreter, is it in your PATH?\n")
- return 127
- else:
- try:
- args = [cmd_interp, '/C', escape_cmd(string.join(args)) ]
- ret = os.spawnve(os.P_WAIT, cmd_interp, args, env)
- except OSError, e:
- ret = exitvalmap[e[0]]
- sys.stderr.write("scons: %s: %s\n" % (cmd, e[1]))
- return ret
-
- # Windows does not allow special characters in file names
- # anyway, so no need for an escape function, we will just quote
- # the arg.
- defaultEscape = lambda x: '"' + x + '"'
-else:
- def defaultSpawn(cmd, args, env):
- sys.stderr.write("scons: Unknown os '%s', cannot spawn command interpreter.\n" % os.name)
- sys.stderr.write("scons: Set your command handler with SetCommandHandler().\n")
- return 127
-
-spawn = defaultSpawn
-escape_cmd = defaultEscape
-
def SetCommandHandler(func, escape = lambda x: x):
- """Sets the command handler and escape function for the
- system. All command actions are passed through
- the command handler, which should be a function that accepts
- 3 arguments: a string command, a list of arguments (the first
- of which is the command itself), and a dictionary representing
- the execution environment. The function should then pass
- the string to a suitable command interpreter.
-
- The escape function should take a string and return the same
- string with all special characters escaped such that the command
- interpreter will interpret the string literally."""
- global spawn, escape_cmd
- spawn = func
- escape_cmd = escape
-
-def GetCommandHandler():
- global spawn
- return spawn
+ raise SCons.Errors.UserError("SetCommandHandler() is no longer supported, use the SPAWN and ESCAPE construction variables.")
-def GetEscapeHandler():
- global escape_cmd
- return escape_cmd
+def GetCommandHandler():
+ raise SCons.Errors.UserError("GetCommandHandler() is no longer supported, use the SPAWN construction variable.")
class CommandGenerator:
"""
@@ -347,6 +191,20 @@ class CommandAction(ActionBase):
self.cmd_list = cmd
def execute(self, target, source, env):
+ escape = env.get('ESCAPE', lambda x: x)
+
+ import SCons.Errors
+
+ if env.has_key('SHELL'):
+ shell = env['SHELL']
+ else:
+ raise SCons.Errors.UserError('Missing SHELL construction variable.')
+
+ if env.has_key('SPAWN'):
+ spawn = env['SPAWN']
+ else:
+ raise SCons.Errors.UserError('Missing SPAWN construction variable.')
+
dict = self.subst_dict(target, source, env)
import SCons.Util
cmd_list = SCons.Util.scons_subst_list(self.cmd_list, dict, {}, _rm)
@@ -365,9 +223,9 @@ class CommandAction(ActionBase):
ENV = default_ENV
# Escape the command line for the command
# interpreter we are using
- map(lambda x: x.escape(escape_cmd), cmd_line)
+ map(lambda x, e=escape: x.escape(e), cmd_line)
cmd_line = map(str, cmd_line)
- ret = spawn(cmd_line[0], cmd_line, ENV)
+ ret = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
if ret:
return ret
return 0
diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py
index 1ef71f0..86b84b9 100644
--- a/src/engine/SCons/ActionTests.py
+++ b/src/engine/SCons/ActionTests.py
@@ -36,6 +36,7 @@ import unittest
import SCons.Action
import TestCmd
+import SCons.Errors
import UserDict
@@ -157,8 +158,9 @@ class CommandActionTestCase(unittest.TestCase):
def __init__(self):
self.executed = 0
t=Test()
- def func(cmd, args, env, test=t):
+ def func(sh, escape, cmd, args, env, test=t):
test.executed = args
+ test.shell = sh
return 0
def escape_func(cmd):
return '**' + cmd + '**'
@@ -170,17 +172,25 @@ class CommandActionTestCase(unittest.TestCase):
return self.data
def is_literal(self):
return 1
+
+ try:
+ SCons.Action.SetCommandHandler(func)
+ except SCons.Errors.UserError:
+ pass
+ else:
+ assert 0, "should have gotten user error"
- SCons.Action.SetCommandHandler(func)
- assert SCons.Action.spawn is func
a = SCons.Action.CommandAction(["xyzzy"])
- a.execute([],[],Environment({}))
+ a.execute([],[],Environment({'SPAWN':func}))
assert t.executed == [ 'xyzzy' ]
- SCons.Action.SetCommandHandler(func,escape_func)
- assert SCons.Action.GetEscapeHandler() == escape_func
+ a = SCons.Action.CommandAction(["xyzzy"])
+ a.execute([],[],Environment({'SPAWN':func, 'SHELL':'fake shell'}))
+ assert t.executed == [ 'xyzzy' ]
+ assert t.shell == 'fake shell'
+
a = SCons.Action.CommandAction([ LiteralStr("xyzzy") ])
- a.execute([],[],Environment({ }))
+ a.execute([],[],Environment({'SPAWN':func, 'ESCAPE':escape_func}))
assert t.executed == [ '**xyzzy**' ], t.executed
def test_get_raw_contents(self):
@@ -241,21 +251,17 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
self.dummy=dummy
def f2(target, source, env, for_signature, f=func_action):
return f
- def ch(cmd, args, env, self=self):
+ def ch(sh, escape, cmd, args, env, self=self):
self.cmd.append(cmd)
self.args.append(args)
a = SCons.Action.CommandGeneratorAction(f)
self.dummy = 0
- old_hdl = SCons.Action.GetCommandHandler()
self.cmd = []
self.args = []
- try:
- SCons.Action.SetCommandHandler(ch)
- a.execute([],[],env=Environment({ 'FOO' : 'foo baz\nbar ack',
- 'dummy' : 1}))
- finally:
- SCons.Action.SetCommandHandler(old_hdl)
+ a.execute([],[],env=Environment({ 'FOO' : 'foo baz\nbar ack',
+ 'dummy' : 1,
+ 'SPAWN':ch}))
assert self.dummy == 1, self.dummy
assert self.cmd == ['foo', 'bar'], self.cmd
assert self.args == [[ 'foo', 'baz' ], [ 'bar', 'ack' ]], self.args
diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py
index 1917405..b087d7b 100644
--- a/src/engine/SCons/BuilderTests.py
+++ b/src/engine/SCons/BuilderTests.py
@@ -40,6 +40,7 @@ import SCons.Builder
import SCons.Errors
import SCons.Node.FS
import SCons.Warnings
+import SCons.Environment
# Initial setup of the common environment for all tests,
# a temporary working directory containing a
@@ -70,9 +71,14 @@ show_string = None
env_scanner = None
count = 0
+scons_env = SCons.Environment.Environment()
+
class Environment:
def __init__(self, **kw):
self.d = {}
+ self.d['SHELL'] = scons_env['SHELL']
+ self.d['SPAWN'] = scons_env['SPAWN']
+ self.d['ESCAPE'] = scons_env['ESCAPE']
for k, v in kw.items():
self.d[k] = v
def subst(self, s):
diff --git a/src/engine/SCons/Platform/PlatformTests.py b/src/engine/SCons/Platform/PlatformTests.py
index b4b67bb..ffbee67 100644
--- a/src/engine/SCons/Platform/PlatformTests.py
+++ b/src/engine/SCons/Platform/PlatformTests.py
@@ -28,34 +28,41 @@ import unittest
import SCons.Errors
import SCons.Platform
+import UserDict
+
+class Environment(UserDict.UserDict):
+ def Detect(self, cmd):
+ return cmd
class PlatformTestCase(unittest.TestCase):
def test_Platform(self):
"""Test the Platform() function"""
p = SCons.Platform.Platform('cygwin')
assert str(p) == 'cygwin', p
- env = {}
+ env = Environment()
p(env)
assert env['PROGSUFFIX'] == '.exe', env
assert env['LIBSUFFIX'] == '.a', env
+ assert env['SHELL'] == 'sh', env
p = SCons.Platform.Platform('os2')
assert str(p) == 'os2', p
- env = {}
+ env = Environment()
p(env)
assert env['PROGSUFFIX'] == '.exe', env
assert env['LIBSUFFIX'] == '.lib', env
p = SCons.Platform.Platform('posix')
assert str(p) == 'posix', p
- env = {}
+ env = Environment()
p(env)
assert env['PROGSUFFIX'] == '', env
assert env['LIBSUFFIX'] == '.a', env
+ assert env['SHELL'] == 'sh', env
p = SCons.Platform.Platform('win32')
assert str(p) == 'win32', p
- env = {}
+ env = Environment()
p(env)
assert env['PROGSUFFIX'] == '.exe', env
assert env['LIBSUFFIX'] == '.lib', env
@@ -68,7 +75,7 @@ class PlatformTestCase(unittest.TestCase):
else:
raise
- env = {}
+ env = Environment()
SCons.Platform.Platform()(env)
assert env != {}, env
diff --git a/src/engine/SCons/Platform/cygwin.py b/src/engine/SCons/Platform/cygwin.py
index 47b64d6..fe5a818 100644
--- a/src/engine/SCons/Platform/cygwin.py
+++ b/src/engine/SCons/Platform/cygwin.py
@@ -33,20 +33,10 @@ selection method.
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import SCons.Util
+import posix
def generate(env):
- if not env.has_key('ENV'):
- env['ENV'] = {}
- env['ENV']['PATH'] = '/usr/local/bin:/bin:/usr/bin'
- env['OBJPREFIX'] = ''
- env['OBJSUFFIX'] = '.o'
- env['SHOBJPREFIX'] = '$OBJPREFIX'
- env['SHOBJSUFFIX'] = '$OBJSUFFIX'
+ posix.generate(env)
+
env['PROGPREFIX'] = ''
env['PROGSUFFIX'] = '.exe'
- env['LIBPREFIX'] = 'lib'
- env['LIBSUFFIX'] = '.a'
- env['SHLIBPREFIX'] = '$LIBPREFIX'
- env['SHLIBSUFFIX'] = '.so'
- env['LIBPREFIXES'] = '$LIBPREFIX'
- env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ]
diff --git a/src/engine/SCons/Platform/posix.py b/src/engine/SCons/Platform/posix.py
index 551265c..c7ec9d1 100644
--- a/src/engine/SCons/Platform/posix.py
+++ b/src/engine/SCons/Platform/posix.py
@@ -33,8 +33,63 @@ selection method.
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import SCons.Util
+import string
+import os
+import sys
+import os.path
+def escape(arg):
+ "escape shell special characters"
+ slash = '\\'
+ special = '"$'
+
+ arg = string.replace(arg, slash, slash+slash)
+ for c in special:
+ arg = string.replace(arg, c, slash+c)
+
+ return '"' + arg + '"'
+
+def env_spawn(sh, escape, cmd, args, env):
+ if env:
+ s = 'env -i '
+ for key in env.keys():
+ s = s + '%s=%s '%(key, escape(env[key]))
+ s = s + sh + ' -c '
+ s = s + escape(string.join(args))
+ else:
+ s = string.join(args)
+
+ return os.system(s) >> 8
+
+def fork_spawn(sh, escape, cmd, args, env):
+ pid = os.fork()
+ if not pid:
+ # Child process.
+ exitval = 127
+ args = [sh, '-c', string.join(args)]
+ try:
+ os.execvpe(sh, args, env)
+ except OSError, e:
+ exitval = exitvalmap[e[0]]
+ sys.stderr.write("scons: %s: %s\n" % (cmd, e[1]))
+ os._exit(exitval)
+ else:
+ # Parent process.
+ pid, stat = os.waitpid(pid, 0)
+ ret = stat >> 8
+ return ret
+
def generate(env):
+
+ # If the env command exists, then we can use os.system()
+ # to spawn commands, otherwise we fall back on os.fork()/os.exec().
+ # os.system() is prefered because it seems to work better with
+ # threads (i.e. -j) and is more efficient than forking Python.
+ if env.Detect('env'):
+ spawn = env_spawn
+ else:
+ spawn = fork_spawn
+
if not env.has_key('ENV'):
env['ENV'] = {}
env['ENV']['PATH'] = '/usr/local/bin:/bin:/usr/bin'
@@ -50,3 +105,6 @@ def generate(env):
env['SHLIBSUFFIX'] = '.so'
env['LIBPREFIXES'] = '$LIBPREFIX'
env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ]
+ env['SPAWN'] = spawn
+ env['SHELL'] = 'sh'
+ env['ESCAPE'] = escape
diff --git a/src/engine/SCons/Platform/win32.py b/src/engine/SCons/Platform/win32.py
index 5ad9ff1..b54aefe 100644
--- a/src/engine/SCons/Platform/win32.py
+++ b/src/engine/SCons/Platform/win32.py
@@ -33,8 +33,60 @@ selection method.
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import SCons.Util
+import os
+import os.path
+import string
+import sys
+
+# The upshot of all this is that, if you are using Python 1.5.2,
+# you had better have cmd or command.com in your PATH when you run
+# scons.
+
+def spawn(sh, escape, cmd, args, env):
+ if not sh:
+ sys.stderr.write("scons: Could not find command interpreter, is it in your PATH?\n")
+ return 127
+ else:
+ try:
+ args = [sh, '/C', escape(string.join(args)) ]
+ ret = os.spawnve(os.P_WAIT, sh, args, env)
+ except OSError, e:
+ ret = exitvalmap[e[0]]
+ sys.stderr.write("scons: %s: %s\n" % (cmd, e[1]))
+ return ret
+
+# Windows does not allow special characters in file names
+# anyway, so no need for an escape function, we will just quote
+# the arg.
+escape = lambda x: '"' + x + '"'
def generate(env):
+
+ # Attempt to find cmd.exe (for WinNT/2k/XP) or
+ # command.com for Win9x
+ cmd_interp = ''
+ # First see if we can look in the registry...
+ if SCons.Util.can_read_reg:
+ try:
+ # Look for Windows NT system root
+ k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE,
+ 'Software\\Microsoft\\Windows NT\\CurrentVersion')
+ val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot')
+ cmd_interp = os.path.join(val, 'System32\\cmd.exe')
+ except SCons.Util.RegError:
+ try:
+ # Okay, try the Windows 9x system root
+ k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE,
+ 'Software\\Microsoft\\Windows\\CurrentVersion')
+ val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot')
+ cmd_interp = os.path.join(val, 'command.com')
+ except:
+ pass
+ if not cmd_interp:
+ cmd_interp = env.Detect('cmd')
+ if not cmd_interp:
+ cmd_interp = env.Detect('command')
+
if not env.has_key('ENV'):
env['ENV'] = {}
env['ENV']['PATHEXT'] = '.COM;.EXE;.BAT;.CMD'
@@ -50,3 +102,6 @@ def generate(env):
env['SHLIBSUFFIX'] = '.dll'
env['LIBPREFIXES'] = [ '$LIBPREFIX', '$SHLIBPREFIX' ]
env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ]
+ env['SPAWN'] = spawn
+ env['SHELL'] = cmd_interp
+ env['ESCAPE'] = escape