The Usual
diff --git a/Lib/dos-8x3/cgihttps.py b/Lib/dos-8x3/cgihttps.py
index 6a259a3..ba1e76b 100755
--- a/Lib/dos-8x3/cgihttps.py
+++ b/Lib/dos-8x3/cgihttps.py
@@ -3,28 +3,31 @@
 This module builds on SimpleHTTPServer by implementing GET and POST
 requests to cgi-bin scripts.
 
-If the os.fork() function is not present, this module will not work;
-SystemError will be raised instead.
+If the os.fork() function is not present (e.g. on Windows),
+os.popen2() is used as a fallback, with slightly altered semantics; if
+that function is not present either (e.g. on Macintosh), only Python
+scripts are supported, and they are executed by the current process.
+
+In all cases, the implementation is intentionally naive -- all
+requests are executed sychronously.
+
+SECURITY WARNING: DON'T USE THIS CODE UNLESS YOU ARE INSIDE A FIREWALL
+-- it may execute arbitrary Python code or external programs.
 
 """
 
 
-__version__ = "0.3"
+__version__ = "0.4"
 
 
 import os
+import sys
 import string
 import urllib
 import BaseHTTPServer
 import SimpleHTTPServer
 
 
-try:
-    os.fork
-except AttributeError:
-    raise SystemError, __name__ + " requires os.fork()"
-
-
 class CGIHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
 
     """Complete HTTP server with GET, HEAD and POST commands.
@@ -35,6 +38,10 @@
 
     """
 
+    # Determine platform specifics
+    have_fork = hasattr(os, 'fork')
+    have_popen2 = hasattr(os, 'popen2')
+
     # Make rfile unbuffered -- we need to read one line and then pass
     # the rest to a subprocess, so we can't use buffered input.
     rbufsize = 0
@@ -59,9 +66,9 @@
             return SimpleHTTPServer.SimpleHTTPRequestHandler.send_head(self)
 
     def is_cgi(self):
-        """test whether PATH corresponds to a CGI script.
+        """Test whether self.path corresponds to a CGI script.
 
-        Return a tuple (dir, rest) if PATH requires running a
+        Return a tuple (dir, rest) if self.path requires running a
         CGI script, None if not.  Note that rest begins with a
         slash if it is not empty.
 
@@ -83,6 +90,15 @@
 
     cgi_directories = ['/cgi-bin', '/htbin']
 
+    def is_executable(self, path):
+        """Test whether argument path is an executable file."""
+        return executable(path)
+
+    def is_python(self, path):
+        """Test whether argument path is a Python script."""
+        head, tail = os.path.splitext(path)
+        return tail.lower() in (".py", ".pyw")
+
     def run_cgi(self):
         """Execute a CGI script."""
         dir, rest = self.cgi_info
@@ -105,79 +121,152 @@
             self.send_error(403, "CGI script is not a plain file (%s)" %
                             `scriptname`)
             return
-        if not executable(scriptfile):
-            self.send_error(403, "CGI script is not executable (%s)" %
-                            `scriptname`)
-            return
-        nobody = nobody_uid()
-        self.send_response(200, "Script output follows")
-        self.wfile.flush() # Always flush before forking
-        pid = os.fork()
-        if pid != 0:
-            # Parent
-            pid, sts = os.waitpid(pid, 0)
-            if sts:
-                self.log_error("CGI script exit status x%x" % sts)
-            return
-        # Child
-        try:
-            # Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html
-            # XXX Much of the following could be prepared ahead of time!
-            env = {}
-            env['SERVER_SOFTWARE'] = self.version_string()
-            env['SERVER_NAME'] = self.server.server_name
-            env['GATEWAY_INTERFACE'] = 'CGI/1.1'
-            env['SERVER_PROTOCOL'] = self.protocol_version
-            env['SERVER_PORT'] = str(self.server.server_port)
-            env['REQUEST_METHOD'] = self.command
-            uqrest = urllib.unquote(rest)
-            env['PATH_INFO'] = uqrest
-            env['PATH_TRANSLATED'] = self.translate_path(uqrest)
-            env['SCRIPT_NAME'] = scriptname
-            if query:
-                env['QUERY_STRING'] = query
-            host = self.address_string()
-            if host != self.client_address[0]:
-                env['REMOTE_HOST'] = host
-            env['REMOTE_ADDR'] = self.client_address[0]
-            # AUTH_TYPE
-            # REMOTE_USER
-            # REMOTE_IDENT
-            if self.headers.typeheader is None:
-                env['CONTENT_TYPE'] = self.headers.type
+        ispy = self.is_python(scriptname)
+        if not ispy:
+            if not (self.have_fork or self.have_popen2):
+                self.send_error(403, "CGI script is not a Python script (%s)" %
+                                `scriptname`)
+                return
+            if not self.is_executable(scriptfile):
+                self.send_error(403, "CGI script is not executable (%s)" %
+                                `scriptname`)
+                return
+
+        # Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html
+        # XXX Much of the following could be prepared ahead of time!
+        env = {}
+        env['SERVER_SOFTWARE'] = self.version_string()
+        env['SERVER_NAME'] = self.server.server_name
+        env['GATEWAY_INTERFACE'] = 'CGI/1.1'
+        env['SERVER_PROTOCOL'] = self.protocol_version
+        env['SERVER_PORT'] = str(self.server.server_port)
+        env['REQUEST_METHOD'] = self.command
+        uqrest = urllib.unquote(rest)
+        env['PATH_INFO'] = uqrest
+        env['PATH_TRANSLATED'] = self.translate_path(uqrest)
+        env['SCRIPT_NAME'] = scriptname
+        if query:
+            env['QUERY_STRING'] = query
+        host = self.address_string()
+        if host != self.client_address[0]:
+            env['REMOTE_HOST'] = host
+        env['REMOTE_ADDR'] = self.client_address[0]
+        # XXX AUTH_TYPE
+        # XXX REMOTE_USER
+        # XXX REMOTE_IDENT
+        if self.headers.typeheader is None:
+            env['CONTENT_TYPE'] = self.headers.type
+        else:
+            env['CONTENT_TYPE'] = self.headers.typeheader
+        length = self.headers.getheader('content-length')
+        if length:
+            env['CONTENT_LENGTH'] = length
+        accept = []
+        for line in self.headers.getallmatchingheaders('accept'):
+            if line[:1] in string.whitespace:
+                accept.append(string.strip(line))
             else:
-                env['CONTENT_TYPE'] = self.headers.typeheader
-            length = self.headers.getheader('content-length')
-            if length:
-                env['CONTENT_LENGTH'] = length
-            accept = []
-            for line in self.headers.getallmatchingheaders('accept'):
-                if line[:1] in string.whitespace:
-                    accept.append(string.strip(line))
-                else:
-                    accept = accept + string.split(line[7:], ',')
-            env['HTTP_ACCEPT'] = string.joinfields(accept, ',')
-            ua = self.headers.getheader('user-agent')
-            if ua:
-                env['HTTP_USER_AGENT'] = ua
-            co = filter(None, self.headers.getheaders('cookie'))
-            if co:
-                env['HTTP_COOKIE'] = string.join(co, ', ')
-            # XXX Other HTTP_* headers
-            decoded_query = string.replace(query, '+', ' ')
+                accept = accept + string.split(line[7:], ',')
+        env['HTTP_ACCEPT'] = string.joinfields(accept, ',')
+        ua = self.headers.getheader('user-agent')
+        if ua:
+            env['HTTP_USER_AGENT'] = ua
+        co = filter(None, self.headers.getheaders('cookie'))
+        if co:
+            env['HTTP_COOKIE'] = string.join(co, ', ')
+        # XXX Other HTTP_* headers
+        if not self.have_fork:
+            # Since we're setting the env in the parent, provide empty
+            # values to override previously set values
+            for k in ('QUERY_STRING', 'REMOTE_HOST', 'CONTENT_LENGTH',
+                      'HTTP_USER_AGENT', 'HTTP_COOKIE'):
+                env.setdefault(k, "")
+
+        self.send_response(200, "Script output follows")
+
+        decoded_query = string.replace(query, '+', ' ')
+
+        if self.have_fork:
+            # Unix -- fork as we should
+            args = [script]
+            if '=' not in decoded_query:
+                args.append(decoded_query)
+            nobody = nobody_uid()
+            self.wfile.flush() # Always flush before forking
+            pid = os.fork()
+            if pid != 0:
+                # Parent
+                pid, sts = os.waitpid(pid, 0)
+                if sts:
+                    self.log_error("CGI script exit status %#x", sts)
+                return
+            # Child
             try:
-                os.setuid(nobody)
-            except os.error:
-                pass
-            os.dup2(self.rfile.fileno(), 0)
-            os.dup2(self.wfile.fileno(), 1)
-            print scriptfile, script, decoded_query
-            os.execve(scriptfile,
-                      [script, decoded_query],
-                      env)
-        except:
-            self.server.handle_error(self.request, self.client_address)
-            os._exit(127)
+                try:
+                    os.setuid(nobody)
+                except os.error:
+                    pass
+                os.dup2(self.rfile.fileno(), 0)
+                os.dup2(self.wfile.fileno(), 1)
+                os.execve(scriptfile, args, env)
+            except:
+                self.server.handle_error(self.request, self.client_address)
+                os._exit(127)
+
+        elif self.have_popen2:
+            # Windows -- use popen2 to create a subprocess
+            import shutil
+            os.environ.update(env)
+            cmdline = scriptfile
+            if self.is_python(scriptfile):
+                interp = sys.executable
+                if interp.lower().endswith("w.exe"):
+                    # On Windows, use python.exe, not python.exe
+                    interp = interp[:-5] = interp[-4:]
+                cmdline = "%s %s" % (interp, cmdline)
+            if '=' not in query and '"' not in query:
+                cmdline = '%s "%s"' % (cmdline, query)
+            self.log_error("command: %s", cmdline)
+            try:
+                nbytes = int(length)
+            except:
+                nbytes = 0
+            fi, fo = os.popen2(cmdline)
+            if self.command.lower() == "post" and nbytes > 0:
+                data = self.rfile.read(nbytes)
+                fi.write(data)
+            fi.close()
+            shutil.copyfileobj(fo, self.wfile)
+            sts = fo.close()
+            if sts:
+                self.log_error("CGI script exit status %#x", sts)
+            else:
+                self.log_error("CGI script exited OK")
+
+        else:
+            # Other O.S. -- execute script in this process
+            os.environ.update(env)
+            save_argv = sys.argv
+            save_stdin = sys.stdin
+            save_stdout = sys.stdout
+            save_stderr = sys.stderr
+            try:
+                try:
+                    sys.argv = [scriptfile]
+                    if '=' not in decoded_query:
+                        sys.argv.append(decoded_query)
+                    sys.stdout = self.wfile
+                    sys.stdin = self.rfile
+                    execfile(scriptfile, {"__name__": "__main__"})
+                finally:
+                    sys.argv = save_argv
+                    sys.stdin = save_stdin
+                    sys.stdout = save_stdout
+                    sys.stderr = save_stderr
+            except SystemExit, sts:
+                self.log_error("CGI script exit status %s", str(sts))
+            else:
+                self.log_error("CGI script exited OK")
 
 
 nobody = None
@@ -187,7 +276,10 @@
     global nobody
     if nobody:
         return nobody
-    import pwd
+    try:
+        import pwd
+    except ImportError:
+        return -1
     try:
         nobody = pwd.getpwnam('nobody')[2]
     except KeyError:
diff --git a/Lib/dos-8x3/configpa.py b/Lib/dos-8x3/configpa.py
index e7636b9..16d18d2 100644
--- a/Lib/dos-8x3/configpa.py
+++ b/Lib/dos-8x3/configpa.py
@@ -440,7 +440,7 @@
                         # allow empty values
                         if optval == '""':
                             optval = ''
-                        cursect[optname] = optval
+                        cursect[self.optionxform(optname)] = optval
                     else:
                         # a non-fatal parsing error occurred.  set up the
                         # exception but keep going. the exception will be
diff --git a/Lib/dos-8x3/posixfil.py b/Lib/dos-8x3/posixfil.py
index d358dc4..23f2c85 100755
--- a/Lib/dos-8x3/posixfil.py
+++ b/Lib/dos-8x3/posixfil.py
@@ -67,9 +67,6 @@
                 (self.states[file.closed], file.name, file.mode, \
                  hex(id(self))[2:])
 
-    def __del__(self):
-        self._file_.close()
-
     #
     # Initialization routines
     #
diff --git a/Lib/dos-8x3/py_compi.py b/Lib/dos-8x3/py_compi.py
index c54d61b..b453109 100755
--- a/Lib/dos-8x3/py_compi.py
+++ b/Lib/dos-8x3/py_compi.py
@@ -48,6 +48,11 @@
     except AttributeError:
         timestamp = long(os.stat(file)[8])
     codestring = f.read()
+    # If parsing from a string, line breaks are \n (see parsetok.c:tok_nextc)
+    # Replace will return original string if pattern is not found, so
+    # we don't need to check whether it is found first.
+    codestring = codestring.replace("\r\n","\n")
+    codestring = codestring.replace("\r","\n")
     f.close()
     if codestring and codestring[-1] != '\n':
         codestring = codestring + '\n'
diff --git a/Lib/dos-8x3/sre_pars.py b/Lib/dos-8x3/sre_pars.py
index 55de24c..f4741c9 100644
--- a/Lib/dos-8x3/sre_pars.py
+++ b/Lib/dos-8x3/sre_pars.py
@@ -634,7 +634,7 @@
                 while 1:
                     group = _group(this, pattern.groups+1)
                     if group:
-                        if (not s.next or
+                        if (s.next not in DIGITS or
                             not _group(this + s.next, pattern.groups+1)):
                             code = MARK, int(group)
                             break
diff --git a/Lib/dos-8x3/stringio.py b/Lib/dos-8x3/stringio.py
index 8efd7d8..02eb7c8 100755
--- a/Lib/dos-8x3/stringio.py
+++ b/Lib/dos-8x3/stringio.py
@@ -91,11 +91,15 @@
 		r = self.buf[self.pos:newpos]
 		self.pos = newpos
 		return r
-	def readlines(self):
+	def readlines(self, sizehint = 0):
+		total = 0
 		lines = []
 		line = self.readline()
 		while line:
 			lines.append(line)
+			total += len(line)
+			if 0 < sizehint <= total:
+				break
 			line = self.readline()
 		return lines
 	def write(self, s):
diff --git a/Lib/dos-8x3/test_arr.py b/Lib/dos-8x3/test_arr.py
index d3fe7e9..fb451a9 100644
--- a/Lib/dos-8x3/test_arr.py
+++ b/Lib/dos-8x3/test_arr.py
@@ -122,9 +122,14 @@
             a.pop()
             a.pop()
             a.pop()
-            a.pop()
+            x = a.pop()
+            if x != 'e':
+                raise TestFailed, "array(%s) pop-test" % `type`
             if a != array.array(type, "acd"):
             	raise TestFailed, "array(%s) pop-test" % `type`
+            a.reverse()
+            if a != array.array(type, "dca"):
+            	raise TestFailed, "array(%s) reverse-test" % `type`
         else:
             a = array.array(type, [1, 2, 3, 4, 5])
             a[:-1] = a
@@ -155,9 +160,14 @@
             a.pop()
             a.pop()
             a.pop()
-            a.pop()
+            x = a.pop()
+            if x != 5:
+                raise TestFailed, "array(%s) pop-test" % `type`
             if a != array.array(type, [1, 3, 4]):
             	raise TestFailed, "array(%s) pop-test" % `type`
+            a.reverse()
+            if a != array.array(type, [4, 3, 1]):
+            	raise TestFailed, "array(%s) reverse-test" % `type`
 
         # test that overflow exceptions are raised as expected for assignment
         # to array of specific integral types
diff --git a/Lib/dos-8x3/test_cpi.py b/Lib/dos-8x3/test_cpi.py
index f5e920f..f2aa0fe 100644
--- a/Lib/dos-8x3/test_cpi.py
+++ b/Lib/dos-8x3/test_cpi.py
@@ -1,107 +1,5 @@
 # Test the cPickle module
 
-DATA = """(lp0
-I0
-aL1L
-aF2.0
-ac__builtin__
-complex
-p1
-(F3.0
-F0.0
-tp2
-Rp3
-a(S'abc'
-p4
-g4
-(i__main__
-C
-p5
-(dp6
-S'foo'
-p7
-I1
-sS'bar'
-p8
-I2
-sbg5
-tp9
-ag9
-aI5
-a.
-"""
-
-BINDATA = ']q\000(K\000L1L\012G@\000\000\000\000\000\000\000c__builtin__\012complex\012q\001(G@\010\000\000\000\000\000\000G\000\000\000\000\000\000\000\000tq\002Rq\003(U\003abcq\004h\004(c__main__\012C\012q\005oq\006}q\007(U\003fooq\010K\001U\003barq\011K\002ubh\006tq\012h\012K\005e.'
-
 import cPickle
-
-class C:
-    def __cmp__(self, other):
-        return cmp(self.__dict__, other.__dict__)
-
-import __main__
-__main__.C = C
-
-def dotest():
-    c = C()
-    c.foo = 1
-    c.bar = 2
-    x = [0, 1L, 2.0, 3.0+0j]
-    y = ('abc', 'abc', c, c)
-    x.append(y)
-    x.append(y)
-    x.append(5)
-    print "dumps()"
-    s = cPickle.dumps(x)
-    print "loads()"
-    x2 = cPickle.loads(s)
-    if x2 == x: print "ok"
-    else: print "bad"
-    print "loads() DATA"
-    x2 = cPickle.loads(DATA)
-    if x2 == x: print "ok"
-    else: print "bad"
-    print "dumps() binary"
-    s = cPickle.dumps(x, 1)
-    print "loads() binary"
-    x2 = cPickle.loads(s)
-    if x2 == x: print "ok"
-    else: print "bad"
-    print "loads() BINDATA"
-    x2 = cPickle.loads(BINDATA)
-    if x2 == x: print "ok"
-    else: print "bad"
-
-    # Test protection against closed files
-    import tempfile, os
-    fn = tempfile.mktemp()
-    f = open(fn, "w")
-    f.close()
-    try:
-        cPickle.dump(123, f)
-    except ValueError:
-        pass
-    else:
-        print "dump to closed file should raise ValueError"
-    f = open(fn, "r")
-    f.close()
-    try:
-        cPickle.load(f)
-    except ValueError:
-        pass
-    else:
-        print "load from closed file should raise ValueError"
-    os.remove(fn)
-
-    # Test specific bad cases
-    for i in range(10):
-        try:
-            x = cPickle.loads('garyp')
-        except cPickle.BadPickleGet, y:
-            del y
-        else:
-            print "unexpected success!"
-            break
-    
-
-dotest()
+import test_pickle
+test_pickle.dotest(cPickle)
diff --git a/Lib/dos-8x3/test_exc.py b/Lib/dos-8x3/test_exc.py
index 7ee203c..076f470 100755
--- a/Lib/dos-8x3/test_exc.py
+++ b/Lib/dos-8x3/test_exc.py
@@ -86,6 +86,55 @@
 try: exec '/\n'
 except SyntaxError: pass
 
+# make sure the right exception message is raised for each of these
+# code fragments:
+
+def ckmsg(src, msg):
+    try:
+        compile(src, '<fragment>', 'exec')
+    except SyntaxError, e:
+        print e.msg
+        if e.msg == msg:
+            print "ok"
+        else:
+            print "expected:", msg
+    else:
+        print "failed to get expected SyntaxError"
+
+s = '''\
+while 1:
+    try:
+        continue
+    except:
+        pass
+'''
+ckmsg(s, "'continue' not supported inside 'try' clause")
+s = '''\
+while 1:
+    try:
+        continue
+    finally:
+        pass
+'''
+ckmsg(s, "'continue' not supported inside 'try' clause")
+s = '''\
+while 1:
+    try:
+        if 1:
+            continue
+    finally:
+        pass
+'''
+ckmsg(s, "'continue' not supported inside 'try' clause")
+s = '''\
+try:
+    continue
+except:
+    pass
+'''
+ckmsg(s, "'continue' not properly in loop")
+ckmsg("continue\n", "'continue' not properly in loop")
+
 r(IndentationError)
 
 r(TabError)
diff --git a/Lib/dos-8x3/test_fcn.py b/Lib/dos-8x3/test_fcn.py
index 3c1be88..e1ed88b 100644
--- a/Lib/dos-8x3/test_fcn.py
+++ b/Lib/dos-8x3/test_fcn.py
@@ -21,7 +21,7 @@
                     'bsdos2', 'bsdos3', 'bsdos4',
                     'openbsd', 'openbsd2'):
     lockdata = struct.pack('lxxxxlxxxxlhh', 0, 0, 0, FCNTL.F_WRLCK, 0)
-elif sys.platform in ['aix3', 'aix4']:
+elif sys.platform in ['aix3', 'aix4', 'hp-uxB']:
     lockdata = struct.pack('hhlllii', FCNTL.F_WRLCK, 0, 0, 0, 0, 0, 0)
 else:
     lockdata = struct.pack('hhllhh', FCNTL.F_WRLCK, 0, 0, 0, 0, 0)
diff --git a/Lib/dos-8x3/test_gra.py b/Lib/dos-8x3/test_gra.py
index ef7c09b..68cae81 100755
--- a/Lib/dos-8x3/test_gra.py
+++ b/Lib/dos-8x3/test_gra.py
@@ -355,6 +355,13 @@
 	del z
 	exec 'z=1+1'
 	if z <> 2: raise TestFailed, 'exec \'z=1+1\''
+	z = None
+	del z
+	exec u'z=1+1\n'
+	if z <> 2: raise TestFailed, 'exec u\'z=1+1\'\\n'
+	del z
+	exec u'z=1+1'
+	if z <> 2: raise TestFailed, 'exec u\'z=1+1\''
 f()
 g = {}
 exec 'z = 1' in g
diff --git a/Lib/dos-8x3/test_min.py b/Lib/dos-8x3/test_min.py
index 73c8ac4..8a63535 100644
--- a/Lib/dos-8x3/test_min.py
+++ b/Lib/dos-8x3/test_min.py
@@ -5,6 +5,7 @@
 import os.path
 import sys
 import traceback
+from test_support import verbose
 
 if __name__ == "__main__":
     base = sys.argv[0]
@@ -13,15 +14,28 @@
 tstfile = os.path.join(os.path.dirname(base), "test.xml")
 del base
 
+def confirm( test, testname="Test" ):
+    if test: 
+        print "Passed " + testname
+    else: 
+        print "Failed " + testname
+        raise Exception
+
 Node._debug=1
 
+def testParseFromFile():
+    from StringIO import StringIO
+    dom=parse( StringIO( open( tstfile ).read() ) )
+    dom.unlink()
+    confirm(isinstance(dom,Document))
+
 def testGetElementsByTagName( ):
     dom=parse( tstfile )
-    assert dom.getElementsByTagName( "LI" )==\
-            dom.documentElement.getElementsByTagName( "LI" )
+    confirm( dom.getElementsByTagName( "LI" )==\
+            dom.documentElement.getElementsByTagName( "LI" ) )
     dom.unlink()
     dom=None
-    assert( len( Node.allnodes ))==0
+    confirm (len( Node.allnodes )==0)
 
 def testInsertBefore( ):
     dom=parse( tstfile )
@@ -32,44 +46,44 @@
     #docel.insertBefore( dom.createProcessingInstruction("a", "b"),
     #                        docel.childNodes[0])
 
-    #assert docel.childNodes[0].target=="a"
-    #assert docel.childNodes[2].target=="a"
+    #confirm( docel.childNodes[0].tet=="a" )
+    #confirm( docel.childNodes[2].tet=="a" )
     dom.unlink()
     del dom
     del docel
-    assert( len( Node.allnodes ))==0
+    confirm( len( Node.allnodes )==0)
 
 def testAppendChild():
     dom=parse( tstfile )
     dom.documentElement.appendChild( dom.createComment( u"Hello" ))
-    assert dom.documentElement.childNodes[-1].nodeName=="#comment"
-    assert dom.documentElement.childNodes[-1].data=="Hello"
+    confirm( dom.documentElement.childNodes[-1].nodeName=="#comment" )
+    confirm( dom.documentElement.childNodes[-1].data=="Hello" )
     dom.unlink()
     dom=None
-    assert( len( Node.allnodes ))==0
+    confirm( len( Node.allnodes )==0 )
 
 def testNonZero():
     dom=parse( tstfile )
-    assert dom # should not be zero
+    confirm( dom )# should not be zero
     dom.appendChild( dom.createComment( "foo" ) )
-    assert not dom.childNodes[-1].childNodes
+    confirm( not dom.childNodes[-1].childNodes )
     dom.unlink()
     dom=None
-    assert( len( Node.allnodes ))==0
+    confirm( len( Node.allnodes )==0 )
 
 def testUnlink():
     dom=parse( tstfile )
     dom.unlink()
     dom=None
-    assert( len( Node.allnodes ))==0
+    confirm( len( Node.allnodes )==0 )
 
 def testElement():
     dom=Document()
     dom.appendChild( dom.createElement( "abc" ) )
-    assert dom.documentElement
+    confirm( dom.documentElement )
     dom.unlink()
     dom=None
-    assert( len( Node.allnodes ))==0
+    confirm( len( Node.allnodes )==0 )
 
 def testAAA():
     dom=parseString( "<abc/>" )
@@ -91,20 +105,20 @@
     child=dom.appendChild( dom.createElement( "abc" ) )
 
     child.setAttribute( "def", "ghi" )
-    assert child.getAttribute( "def" )=="ghi"
-    assert child.attributes["def"].value=="ghi"
+    confirm( child.getAttribute( "def" )=="ghi" )
+    confirm( child.attributes["def"].value=="ghi" )
 
     child.setAttribute( "jkl", "mno" )
-    assert child.getAttribute( "jkl" )=="mno"
-    assert child.attributes["jkl"].value=="mno"
+    confirm( child.getAttribute( "jkl" )=="mno" )
+    confirm( child.attributes["jkl"].value=="mno" )
 
-    assert len( child.attributes )==2
+    confirm( len( child.attributes )==2 )
 
     child.setAttribute( "def", "newval" )
-    assert child.getAttribute( "def" )=="newval"
-    assert child.attributes["def"].value=="newval"
+    confirm( child.getAttribute( "def" )=="newval" )
+    confirm( child.attributes["def"].value=="newval" )
 
-    assert len( child.attributes )==2
+    confirm( len( child.attributes )==2 )
 
     dom.unlink()
     dom=None
@@ -114,22 +128,22 @@
     dom=Document()
     child=dom.appendChild( dom.createElement( "abc" ) )
 
-    assert len( child.attributes)==0
+    confirm( len( child.attributes)==0 )
     child.setAttribute( "def", "ghi" )
-    assert len( child.attributes)==1
+    confirm( len( child.attributes)==1 )
     del child.attributes["def"]
-    assert len( child.attributes)==0
+    confirm( len( child.attributes)==0 )
     dom.unlink()
-    assert( len( Node.allnodes ))==0
+    confirm( len( Node.allnodes )==0 )
 
 def testRemoveAttr():
     dom=Document()
     child=dom.appendChild( dom.createElement( "abc" ) )
 
     child.setAttribute( "def", "ghi" )
-    assert len( child.attributes)==1
+    confirm( len( child.attributes)==1 )
     child.removeAttribute("def" )
-    assert len( child.attributes)==0
+    confirm( len( child.attributes)==0 )
 
     dom.unlink()
 
@@ -140,9 +154,9 @@
     child.setAttributeNS( "http://www.w3.org", "xmlns:python", 
                                             "http://www.python.org" )
     child.setAttributeNS( "http://www.python.org", "python:abcattr", "foo" )
-    assert len( child.attributes )==2
+    confirm( len( child.attributes )==2 )
     child.removeAttributeNS( "http://www.python.org", "abcattr" )
-    assert len( child.attributes )==1
+    confirm( len( child.attributes )==1 )
 
     dom.unlink()
     dom=None
@@ -151,31 +165,31 @@
     dom=Document()
     child=dom.appendChild( dom.createElement( "foo" ) )
     child.setAttribute( "spam", "jam" )
-    assert len( child.attributes )==1
+    confirm( len( child.attributes )==1 )
     node=child.getAttributeNode( "spam" )
     child.removeAttributeNode( node )
-    assert len( child.attributes )==0
+    confirm( len( child.attributes )==0 )
 
     dom.unlink()
     dom=None
-    assert len( Node.allnodes )==0
+    confirm( len( Node.allnodes )==0 )
 
 def testChangeAttr():
     dom=parseString( "<abc/>" )
     el=dom.documentElement
     el.setAttribute( "spam", "jam" )
-    assert len( el.attributes )==1
+    confirm( len( el.attributes )==1 )
     el.setAttribute( "spam", "bam" )
-    assert len( el.attributes )==1
+    confirm( len( el.attributes )==1 )
     el.attributes["spam"]="ham"
-    assert len( el.attributes )==1
+    confirm( len( el.attributes )==1 )
     el.setAttribute( "spam2", "bam" )
-    assert len( el.attributes )==2
+    confirm( len( el.attributes )==2 )
     el.attributes[ "spam2"]= "bam2"
-    assert len( el.attributes )==2
+    confirm( len( el.attributes )==2 )
     dom.unlink()
     dom=None
-    assert len( Node.allnodes )==0
+    confirm( len( Node.allnodes )==0 )
 
 def testGetAttrList():
     pass
@@ -199,7 +213,7 @@
     el=dom.appendChild( dom.createElement( "abc" ) )
     string1=repr( el )
     string2=str( el )
-    assert string1==string2
+    confirm( string1==string2 )
     dom.unlink()
 
 # commented out until Fredrick's fix is checked in
@@ -208,25 +222,25 @@
     el=dom.appendChild( dom.createElement( u"abc" ) )
     string1=repr( el )
     string2=str( el )
-    assert string1==string2
+    confirm( string1==string2 )
     dom.unlink()
 
 # commented out until Fredrick's fix is checked in
 def _testElementReprAndStrUnicodeNS():
     dom=Document()
     el=dom.appendChild(
-	 dom.createElementNS( u"http://www.slashdot.org", u"slash:abc" ))
+        dom.createElementNS( u"http://www.slashdot.org", u"slash:abc" ))
     string1=repr( el )
     string2=str( el )
-    assert string1==string2
-    assert string1.find("slash:abc" )!=-1
+    confirm( string1==string2 )
+    confirm( string1.find("slash:abc" )!=-1 )
     dom.unlink()
 
 def testAttributeRepr():
     dom=Document()
     el=dom.appendChild( dom.createElement( u"abc" ) )
     node=el.setAttribute( "abc", "def" )
-    assert str( node ) == repr( node )
+    confirm( str( node ) == repr( node ) )
     dom.unlink()
 
 def testTextNodeRepr(): pass
@@ -312,6 +326,9 @@
 
 names=globals().keys()
 names.sort()
+
+works=1
+
 for name in names:
     if name.startswith( "test" ):
         func=globals()[name]
@@ -320,12 +337,24 @@
             print "Test Succeeded", name
             if len( Node.allnodes ):
                 print "Garbage left over:"
-                print Node.allnodes.items()[0:10]
+                if verbose:
+                    print Node.allnodes.items()[0:10]
+                else:
+                    # Don't print specific nodes if repeatable results
+                    # are needed
+                    print len(Node.allnodes)
             Node.allnodes={}
         except Exception, e :
+            works=0
             print "Test Failed: ", name
             apply( traceback.print_exception, sys.exc_info() )
             print `e`
             Node.allnodes={}
-            raise
 
+if works:
+    print "All tests succeeded"
+else:
+    print "\n\n\n\n************ Check for failures!"
+
+Node.debug = None # Delete debug output collected in a StringIO object
+Node._debug = 0   # And reset debug mode
diff --git a/Lib/dos-8x3/test_pic.py b/Lib/dos-8x3/test_pic.py
index 8fb534d..3c81fdd 100644
--- a/Lib/dos-8x3/test_pic.py
+++ b/Lib/dos-8x3/test_pic.py
@@ -1,5 +1,6 @@
 # Test the pickle module
 
+# break into multiple strings to please font-lock-mode
 DATA = """(lp0
 I0
 aL1L
@@ -7,17 +8,20 @@
 ac__builtin__
 complex
 p1
-(F3.0
+""" \
+"""(F3.0
 F0.0
 tp2
 Rp3
 a(S'abc'
 p4
 g4
-(i__main__
+""" \
+"""(i__main__
 C
 p5
-(dp6
+""" \
+"""(dp6
 S'foo'
 p7
 I1
@@ -33,8 +37,6 @@
 
 BINDATA = ']q\000(K\000L1L\012G@\000\000\000\000\000\000\000c__builtin__\012complex\012q\001(G@\010\000\000\000\000\000\000G\000\000\000\000\000\000\000\000tq\002Rq\003(U\003abcq\004h\004(c__main__\012C\012q\005oq\006}q\007(U\003fooq\010K\001U\003barq\011K\002ubh\006tq\012h\012K\005e.'
 
-import pickle
-
 class C:
     def __cmp__(self, other):
         return cmp(self.__dict__, other.__dict__)
@@ -42,7 +44,7 @@
 import __main__
 __main__.C = C
 
-def dotest():
+def dotest(pickle):
     c = C()
     c.foo = 1
     c.bar = 2
@@ -51,6 +53,8 @@
     x.append(y)
     x.append(y)
     x.append(5)
+    r = []
+    r.append(r)
     print "dumps()"
     s = pickle.dumps(x)
     print "loads()"
@@ -71,5 +75,69 @@
     x2 = pickle.loads(BINDATA)
     if x2 == x: print "ok"
     else: print "bad"
+    s = pickle.dumps(r)
+    print "dumps() RECURSIVE"
+    x2 = pickle.loads(s)
+    if x2 == r: print "ok"
+    else: print "bad"
+    # don't create cyclic garbage
+    del x2[0]
+    del r[0]
 
-dotest()
+    # Test protection against closed files
+    import tempfile, os
+    fn = tempfile.mktemp()
+    f = open(fn, "w")
+    f.close()
+    try:
+        pickle.dump(123, f)
+    except ValueError:
+        pass
+    else:
+        print "dump to closed file should raise ValueError"
+    f = open(fn, "r")
+    f.close()
+    try:
+        pickle.load(f)
+    except ValueError:
+        pass
+    else:
+        print "load from closed file should raise ValueError"
+    os.remove(fn)
+
+    # Test specific bad cases
+    for i in range(10):
+        try:
+            x = pickle.loads('garyp')
+        except KeyError, y:
+            # pickle
+            del y
+        except pickle.BadPickleGet, y:
+            # cPickle
+            del y
+        else:
+            print "unexpected success!"
+            break
+
+    # Test insecure strings
+    insecure = ["abc", "2 + 2", # not quoted
+                "'abc' + 'def'", # not a single quoted string
+                "'abc", # quote is not closed
+                "'abc\"", # open quote and close quote don't match
+                "'abc'   ?", # junk after close quote
+                # some tests of the quoting rules
+                "'abc\"\''",
+                "'\\\\a\'\'\'\\\'\\\\\''",
+                ]
+    for s in insecure:
+        buf = "S" + s + "\012p0\012."
+        try:
+            x = pickle.loads(buf)
+        except ValueError:
+            pass
+        else:
+            print "accepted insecure string: %s" % repr(buf)
+        
+
+import pickle
+dotest(pickle)
diff --git a/Lib/dos-8x3/test_pye.py b/Lib/dos-8x3/test_pye.py
index d6bd84b..a119987 100644
--- a/Lib/dos-8x3/test_pye.py
+++ b/Lib/dos-8x3/test_pye.py
@@ -3,10 +3,7 @@
 # XXX TypeErrors on calling handlers, or on bad return values from a
 # handler, are obscure and unhelpful.
         
-import sys, string
-import os
-
-import pyexpat
+from xml.parsers import expat
                 
 class Outputter:
     def StartElementHandler(self, name, attrs):
@@ -16,7 +13,7 @@
         print 'End element:\n\t', repr(name)
 
     def CharacterDataHandler(self, data):
-        data = string.strip(data)
+        data = data.strip()
         if data:
             print 'Character data:'
             print '\t', repr(data)
@@ -63,29 +60,37 @@
         pass
 
 
+def confirm(ok):
+    if ok:
+        print "OK."
+    else:
+        print "Not OK."
+
 out = Outputter()
-parser = pyexpat.ParserCreate(namespace_separator='!')
+parser = expat.ParserCreate(namespace_separator='!')
 
 # Test getting/setting returns_unicode
-parser.returns_unicode = 0 ; assert parser.returns_unicode == 0
-parser.returns_unicode = 1 ; assert parser.returns_unicode == 1
-parser.returns_unicode = 2 ; assert parser.returns_unicode == 1
-parser.returns_unicode = 0 ; assert parser.returns_unicode == 0
+parser.returns_unicode = 0; confirm(parser.returns_unicode == 0)
+parser.returns_unicode = 1; confirm(parser.returns_unicode == 1)
+parser.returns_unicode = 2; confirm(parser.returns_unicode == 1)
+parser.returns_unicode = 0; confirm(parser.returns_unicode == 0)
 
-HANDLER_NAMES = ['StartElementHandler', 'EndElementHandler',
-             'CharacterDataHandler', 'ProcessingInstructionHandler',
-             'UnparsedEntityDeclHandler', 'NotationDeclHandler',
-             'StartNamespaceDeclHandler', 'EndNamespaceDeclHandler',
-             'CommentHandler', 'StartCdataSectionHandler',
-             'EndCdataSectionHandler',
-             'DefaultHandler', 'DefaultHandlerExpand',
-             #'NotStandaloneHandler',
-             'ExternalEntityRefHandler'
-             ]
+HANDLER_NAMES = [
+    'StartElementHandler', 'EndElementHandler',
+    'CharacterDataHandler', 'ProcessingInstructionHandler',
+    'UnparsedEntityDeclHandler', 'NotationDeclHandler',
+    'StartNamespaceDeclHandler', 'EndNamespaceDeclHandler',
+    'CommentHandler', 'StartCdataSectionHandler',
+    'EndCdataSectionHandler',
+    'DefaultHandler', 'DefaultHandlerExpand',
+    #'NotStandaloneHandler',
+    'ExternalEntityRefHandler'
+    ]
 for name in HANDLER_NAMES:
-    setattr(parser, name, getattr(out, name) )
+    setattr(parser, name, getattr(out, name))
 
-data = """<?xml version="1.0" encoding="iso-8859-1" standalone="no"?>
+data = '''\
+<?xml version="1.0" encoding="iso-8859-1" standalone="no"?>
 <?xml-stylesheet href="stylesheet.css"?>
 <!-- comment data -->
 <!DOCTYPE quotations SYSTEM "quotations.dtd" [
@@ -104,45 +109,44 @@
 <sub2><![CDATA[contents of CDATA section]]></sub2>
 &external_entity;
 </root>
-"""
+'''
 
 # Produce UTF-8 output
 parser.returns_unicode = 0
 try:
     parser.Parse(data, 1)
-except pyexpat.error:
-    print '** Error', parser.ErrorCode, pyexpat.ErrorString( parser.ErrorCode)
+except expat.error:
+    print '** Error', parser.ErrorCode, expat.ErrorString(parser.ErrorCode)
     print '** Line', parser.ErrorLineNumber
     print '** Column', parser.ErrorColumnNumber
     print '** Byte', parser.ErrorByteIndex
 
 # Try the parse again, this time producing Unicode output
-parser = pyexpat.ParserCreate(namespace_separator='!')
+parser = expat.ParserCreate(namespace_separator='!')
 parser.returns_unicode = 1
 
 for name in HANDLER_NAMES:
-    setattr(parser, name, getattr(out, name) )
+    setattr(parser, name, getattr(out, name))
 try:
     parser.Parse(data, 1)
-except pyexpat.error:
-    print '** Error', parser.ErrorCode, pyexpat.ErrorString( parser.ErrorCode)
+except expat.error:
+    print '** Error', parser.ErrorCode, expat.ErrorString(parser.ErrorCode)
     print '** Line', parser.ErrorLineNumber
     print '** Column', parser.ErrorColumnNumber
     print '** Byte', parser.ErrorByteIndex
 
 # Try parsing a file
-parser = pyexpat.ParserCreate(namespace_separator='!')
+parser = expat.ParserCreate(namespace_separator='!')
 parser.returns_unicode = 1
 
 for name in HANDLER_NAMES:
-    setattr(parser, name, getattr(out, name) )
+    setattr(parser, name, getattr(out, name))
 import StringIO
 file = StringIO.StringIO(data)
 try:
     parser.ParseFile(file)
-except pyexpat.error:
-    print '** Error', parser.ErrorCode, pyexpat.ErrorString( parser.ErrorCode)
+except expat.error:
+    print '** Error', parser.ErrorCode, expat.ErrorString(parser.ErrorCode)
     print '** Line', parser.ErrorLineNumber
     print '** Column', parser.ErrorColumnNumber
     print '** Byte', parser.ErrorByteIndex
-
diff --git a/Lib/dos-8x3/test_rfc.py b/Lib/dos-8x3/test_rfc.py
index 0d4c66f..36e7a51 100644
--- a/Lib/dos-8x3/test_rfc.py
+++ b/Lib/dos-8x3/test_rfc.py
@@ -120,3 +120,7 @@
 
 test''', [('', 'goit@lip.com')])
 
+
+test('''To: guido@[132.151.1.21]
+
+foo''', [('', 'guido@[132.151.1.21]')])
diff --git a/Lib/dos-8x3/test_str.py b/Lib/dos-8x3/test_str.py
index c713d05..6e321e9 100644
--- a/Lib/dos-8x3/test_str.py
+++ b/Lib/dos-8x3/test_str.py
@@ -1,134 +1,15 @@
-#! /usr/bin/env python
+# Tests StringIO and cStringIO
 
-# Sanity checker for time.strftime
+import string
 
-import time, calendar, sys, string, os, re
-from test_support import verbose
+def do_test(module):
+    s = (string.letters+'\n')*5
+    f = module.StringIO(s)
+    print f.read(10)
+    print f.readline()
+    print len(f.readlines(60))
 
-def main():
-    global verbose
-    now = time.time()
-    strftest(now)
-    verbose = 0
-    # Try a bunch of dates and times,  chosen to vary through time of
-    # day and daylight saving time
-    for j in range(-5, 5):
-        for i in range(25):
-            strftest(now + (i + j*100)*23*3603)
-
-def strftest(now):
-    if verbose:
-        print "strftime test for", time.ctime(now)
-    nowsecs = str(long(now))[:-1]
-    gmt = time.gmtime(now)
-    now = time.localtime(now)
-
-    if now[3] < 12: ampm='AM'
-    else: ampm='PM'
-
-    jan1 = time.localtime(time.mktime((now[0], 1, 1) + (0,)*6))
-
-    try:
-        if now[8]: tz = time.tzname[1]
-        else: tz = time.tzname[0]
-    except AttributeError:
-        tz = ''
-
-    if now[3] > 12: clock12 = now[3] - 12
-    elif now[3] > 0: clock12 = now[3]
-    else: clock12 = 12
-
-    expectations = (
-        ('%a', calendar.day_abbr[now[6]], 'abbreviated weekday name'),
-        ('%A', calendar.day_name[now[6]], 'full weekday name'),
-        ('%b', calendar.month_abbr[now[1]], 'abbreviated month name'),
-        ('%B', calendar.month_name[now[1]], 'full month name'),
-        # %c see below
-        ('%d', '%02d' % now[2], 'day of month as number (00-31)'),
-        ('%H', '%02d' % now[3], 'hour (00-23)'),
-        ('%I', '%02d' % clock12, 'hour (01-12)'),
-        ('%j', '%03d' % now[7], 'julian day (001-366)'),
-        ('%m', '%02d' % now[1], 'month as number (01-12)'),
-        ('%M', '%02d' % now[4], 'minute, (00-59)'),
-        ('%p', ampm, 'AM or PM as appropriate'),
-        ('%S', '%02d' % now[5], 'seconds of current time (00-60)'),
-        ('%U', '%02d' % ((now[7] + jan1[6])/7),
-         'week number of the year (Sun 1st)'),
-        ('%w', '0?%d' % ((1+now[6]) % 7), 'weekday as a number (Sun 1st)'),
-        ('%W', '%02d' % ((now[7] + (jan1[6] - 1)%7)/7),
-         'week number of the year (Mon 1st)'),
-        # %x see below
-        ('%X', '%02d:%02d:%02d' % (now[3], now[4], now[5]), '%H:%M:%S'),
-        ('%y', '%02d' % (now[0]%100), 'year without century'),
-        ('%Y', '%d' % now[0], 'year with century'),
-        # %Z see below
-        ('%%', '%', 'single percent sign'),
-        )
-
-    nonstandard_expectations = (
-        # These are standard but don't have predictable output
-        ('%c', fixasctime(time.asctime(now)), 'near-asctime() format'),
-        ('%x', '%02d/%02d/%02d' % (now[1], now[2], (now[0]%100)),
-         '%m/%d/%y %H:%M:%S'),
-        ('%Z', '%s' % tz, 'time zone name'),
-
-        # These are some platform specific extensions
-        ('%D', '%02d/%02d/%02d' % (now[1], now[2], (now[0]%100)), 'mm/dd/yy'),
-        ('%e', '%2d' % now[2], 'day of month as number, blank padded ( 0-31)'),
-        ('%h', calendar.month_abbr[now[1]], 'abbreviated month name'),
-        ('%k', '%2d' % now[3], 'hour, blank padded ( 0-23)'),
-        ('%n', '\n', 'newline character'),
-        ('%r', '%02d:%02d:%02d %s' % (clock12, now[4], now[5], ampm),
-         '%I:%M:%S %p'),
-        ('%R', '%02d:%02d' % (now[3], now[4]), '%H:%M'),
-        ('%s', nowsecs, 'seconds since the Epoch in UCT'),
-        ('%t', '\t', 'tab character'),
-        ('%T', '%02d:%02d:%02d' % (now[3], now[4], now[5]), '%H:%M:%S'),
-        ('%3y', '%03d' % (now[0]%100),
-         'year without century rendered using fieldwidth'),
-        )
-
-    if verbose:
-        print "Strftime test, platform: %s, Python version: %s" % \
-              (sys.platform, string.split(sys.version)[0])
-
-    for e in expectations:
-        try:
-            result = time.strftime(e[0], now)
-        except ValueError, error:
-            print "Standard '%s' format gave error:" % e[0], error
-            continue
-        if re.match(e[1], result): continue
-        if not result or result[0] == '%':
-            print "Does not support standard '%s' format (%s)" % (e[0], e[2])
-        else:
-            print "Conflict for %s (%s):" % (e[0], e[2])
-            print "  Expected %s, but got %s" % (e[1], result)
-
-    for e in nonstandard_expectations:
-        try:
-            result = time.strftime(e[0], now)
-        except ValueError, result:
-            if verbose:
-                print "Error for nonstandard '%s' format (%s): %s" % \
-                      (e[0], e[2], str(result))
-            continue
-        if re.match(e[1], result):
-            if verbose:
-                print "Supports nonstandard '%s' format (%s)" % (e[0], e[2])
-        elif not result or result[0] == '%':
-            if verbose:
-                print "Does not appear to support '%s' format (%s)" % (e[0],
-                                                                       e[2])
-        else:
-            if verbose:
-                print "Conflict for nonstandard '%s' format (%s):" % (e[0],
-                                                                      e[2])
-                print "  Expected %s, but got %s" % (e[1], result)
-
-def fixasctime(s):
-    if s[8] == ' ':
-        s = s[:8] + '0' + s[9:]
-    return s
-
-main()
+# Don't bother testing cStringIO without
+import StringIO, cStringIO
+do_test(StringIO)
+do_test(cStringIO)
diff --git a/Lib/dos-8x3/test_url.py b/Lib/dos-8x3/test_url.py
index e69de29..484acea 100644
--- a/Lib/dos-8x3/test_url.py
+++ b/Lib/dos-8x3/test_url.py
@@ -0,0 +1,32 @@
+# Minimal test of the quote function
+import urllib
+
+chars = 'abcdefghijklmnopqrstuvwxyz'\
+        '\337\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356' \
+        '\357\360\361\362\363\364\365\366\370\371\372\373\374\375\376\377' \
+        'ABCDEFGHIJKLMNOPQRSTUVWXYZ' \
+        '\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317' \
+        '\320\321\322\323\324\325\326\330\331\332\333\334\335\336'
+
+expected = 'abcdefghijklmnopqrstuvwxyz%df%e0%e1%e2%e3%e4%e5%e6%e7%e8%e9%ea%eb%ec%ed%ee%ef%f0%f1%f2%f3%f4%f5%f6%f8%f9%fa%fb%fc%fd%fe%ffABCDEFGHIJKLMNOPQRSTUVWXYZ%c0%c1%c2%c3%c4%c5%c6%c7%c8%c9%ca%cb%cc%cd%ce%cf%d0%d1%d2%d3%d4%d5%d6%d8%d9%da%db%dc%dd%de'
+
+test = urllib.quote(chars)
+assert test == expected, "urllib.quote problem"
+test2 = urllib.unquote(expected)
+assert test2 == chars
+
+in1 = "abc/def"
+out1_1 = "abc/def"
+out1_2 = "abc%2fdef"
+
+assert urllib.quote(in1) == out1_1, "urllib.quote problem"
+assert urllib.quote(in1, '') == out1_2, "urllib.quote problem"
+
+in2 = "abc?def"
+out2_1 = "abc%3fdef"
+out2_2 = "abc?def"
+
+assert urllib.quote(in2) == out2_1, "urllib.quote problem"
+assert urllib.quote(in2, '?') == out2_2, "urllib.quote problem"
+
+
diff --git a/Lib/dos-8x3/test_win.py b/Lib/dos-8x3/test_win.py
index 7b4fa15..18ce7a7 100644
--- a/Lib/dos-8x3/test_win.py
+++ b/Lib/dos-8x3/test_win.py
@@ -1,7 +1,147 @@
-# Ridiculously simple test of the winsound module for Windows.
+# Test the windows specific win32reg module.
+# Only win32reg functions not hit here: FlushKey, LoadKey and SaveKey
 
-import winsound
-for i in range(100, 2000, 100):
-    winsound.Beep(i, 75)
-print "Hopefully you heard some sounds increasing in frequency!"
+from _winreg import *
+import os, sys
+
+test_key_name = "SOFTWARE\\Python Registry Test Key - Delete Me"
+
+test_data = [
+    ("Int Value",     45,                                      REG_DWORD),
+    ("String Val",    "A string value",                        REG_SZ,),
+    (u"Unicode Val",  u"A Unicode value",                      REG_SZ,),
+    ("StringExpand",  "The path is %path%",                    REG_EXPAND_SZ),
+    ("UnicodeExpand", u"The path is %path%",                   REG_EXPAND_SZ),
+    ("Multi-string",  ["Lots", "of", "string", "values"],      REG_MULTI_SZ),
+    ("Multi-unicode", [u"Lots", u"of", u"unicode", u"values"], REG_MULTI_SZ),
+    ("Multi-mixed",   [u"Unicode", u"and", "string", "values"],REG_MULTI_SZ),
+    ("Raw Data",      ("binary"+chr(0)+"data"),                REG_BINARY),
+]
+
+def WriteTestData(root_key):
+    # Set the default value for this key.
+    SetValue(root_key, test_key_name, REG_SZ, "Default value")
+    key = CreateKey(root_key, test_key_name)
+    # Create a sub-key
+    sub_key = CreateKey(key, "sub_key")
+    # Give the sub-key some named values
+
+    for value_name, value_data, value_type in test_data:
+        SetValueEx(sub_key, value_name, 0, value_type, value_data)
+
+    # Check we wrote as many items as we thought.
+    nkeys, nvalues, since_mod = QueryInfoKey(key)
+    assert nkeys==1, "Not the correct number of sub keys"
+    assert nvalues==1, "Not the correct number of values"
+    nkeys, nvalues, since_mod = QueryInfoKey(sub_key)
+    assert nkeys==0, "Not the correct number of sub keys"
+    assert nvalues==len(test_data), "Not the correct number of values"
+    # Close this key this way...
+    # (but before we do, copy the key as an integer - this allows
+    # us to test that the key really gets closed).
+    int_sub_key = int(sub_key)
+    CloseKey(sub_key)
+    try:
+        QueryInfoKey(int_sub_key)
+        raise RuntimeError, "It appears the CloseKey() function does not close the actual key!"
+    except EnvironmentError:
+        pass
+    # ... and close that key that way :-)
+    int_key = int(key)
+    key.Close()
+    try:
+        QueryInfoKey(int_key)
+        raise RuntimeError, "It appears the key.Close() function does not close the actual key!"
+    except EnvironmentError:
+        pass
+
+def ReadTestData(root_key):
+    # Check we can get default value for this key.
+    val = QueryValue(root_key, test_key_name)
+    assert val=="Default value", "Registry didn't give back the correct value"
+
+    key = OpenKey(root_key, test_key_name)
+    # Read the sub-keys
+    sub_key = OpenKey(key, "sub_key")
+    # Check I can enumerate over the values.
+    index = 0
+    while 1:
+        try:
+            data = EnumValue(sub_key, index)
+        except EnvironmentError:
+            break
+        assert data in test_data, "Didn't read back the correct test data"
+        index = index + 1
+    assert index==len(test_data), "Didn't read the correct number of items"
+    # Check I can directly access each item
+    for value_name, value_data, value_type in test_data:
+        read_val, read_typ = QueryValueEx(sub_key, value_name)
+        assert read_val==value_data and read_typ == value_type, \
+               "Could not directly read the value"
+    sub_key.Close()
+    # Enumerate our main key.
+    read_val = EnumKey(key, 0)
+    assert read_val == "sub_key", "Read subkey value wrong"
+    try:
+        EnumKey(key, 1)
+        assert 0, "Was able to get a second key when I only have one!"
+    except EnvironmentError:
+        pass
+
+    key.Close()
+
+def DeleteTestData(root_key):
+    key = OpenKey(root_key, test_key_name, 0, KEY_ALL_ACCESS)
+    sub_key = OpenKey(key, "sub_key", 0, KEY_ALL_ACCESS)
+    # It is not necessary to delete the values before deleting
+    # the key (although subkeys must not exist).  We delete them
+    # manually just to prove we can :-)
+    for value_name, value_data, value_type in test_data:
+        DeleteValue(sub_key, value_name)
+
+    nkeys, nvalues, since_mod = QueryInfoKey(sub_key)
+    assert nkeys==0 and nvalues==0, "subkey not empty before delete"
+    sub_key.Close()
+    DeleteKey(key, "sub_key")
+
+    try:
+        # Shouldnt be able to delete it twice!
+        DeleteKey(key, "sub_key")
+        assert 0, "Deleting the key twice succeeded"
+    except EnvironmentError:
+        pass
+    key.Close()
+    DeleteKey(root_key, test_key_name)
+    # Opening should now fail!
+    try:
+        key = OpenKey(root_key, test_key_name)
+        assert 0, "Could open the non-existent key"
+    except WindowsError: # Use this error name this time
+        pass
+
+def TestAll(root_key):
+    WriteTestData(root_key)
+    ReadTestData(root_key)
+    DeleteTestData(root_key)
+
+# Test on my local machine.
+TestAll(HKEY_CURRENT_USER)
+print "Local registry tests worked"
+try:
+    remote_name = sys.argv[sys.argv.index("--remote")+1]
+except (IndexError, ValueError):
+    remote_name = None
+
+if remote_name is not None:
+    try:
+        remote_key = ConnectRegistry(remote_name, HKEY_CURRENT_USER)
+    except EnvironmentError, exc:
+        print "Could not connect to the remote machine -", exc.strerror
+        remote_key = None
+    if remote_key is not None:
+        TestAll(remote_key)
+        print "Remote registry tests worked"
+else:
+    print "Remote registry calls can be tested using",
+    print "'test_winreg.py --remote \\\\machine_name'"
 
diff --git a/Lib/dos-8x3/userlist.py b/Lib/dos-8x3/userlist.py
index 1146060..aab5119 100755
--- a/Lib/dos-8x3/userlist.py
+++ b/Lib/dos-8x3/userlist.py
@@ -17,6 +17,7 @@
             return cmp(self.data, other.data)
         else:
             return cmp(self.data, other)
+    def __contains__(self, item): return item in self.data
     def __len__(self): return len(self.data)
     def __getitem__(self, i): return self.data[i]
     def __setitem__(self, i, item): self.data[i] = item
diff --git a/Lib/dos-8x3/webbrows.py b/Lib/dos-8x3/webbrows.py
index 66cdbff..a8b0e8b 100644
--- a/Lib/dos-8x3/webbrows.py
+++ b/Lib/dos-8x3/webbrows.py
@@ -183,7 +183,7 @@
 
 class WindowsDefault:
     def open(self, url, new=0):
-        self.junk = os.popen("start " + url)
+        os.startfile(url)
 
     def open_new(self, url):
         self.open(url)