]> granicus.if.org Git - python/commitdiff
Fuck. For PC support, this must be in the distribution.
authorGuido van Rossum <guido@python.org>
Mon, 22 Jul 1996 15:23:25 +0000 (15:23 +0000)
committerGuido van Rossum <guido@python.org>
Mon, 22 Jul 1996 15:23:25 +0000 (15:23 +0000)
84 files changed:
Lib/dos-8x3/arrayio.py [new file with mode: 0755]
Lib/dos-8x3/ast.py [new file with mode: 0755]
Lib/dos-8x3/basehttp.py [new file with mode: 0755]
Lib/dos-8x3/bastion.py [new file with mode: 0755]
Lib/dos-8x3/cgihttps.py [new file with mode: 0755]
Lib/dos-8x3/compilea.py [new file with mode: 0755]
Lib/dos-8x3/complex.py [new file with mode: 0755]
Lib/dos-8x3/formatte.py [new file with mode: 0755]
Lib/dos-8x3/gopherli.py [new file with mode: 0755]
Lib/dos-8x3/htmlenti.py [new file with mode: 0755]
Lib/dos-8x3/importal.py [new file with mode: 0755]
Lib/dos-8x3/linecach.py [new file with mode: 0755]
Lib/dos-8x3/macurl2p.py [new file with mode: 0755]
Lib/dos-8x3/mimetool.py [new file with mode: 0755]
Lib/dos-8x3/multifil.py [new file with mode: 0755]
Lib/dos-8x3/nturl2pa.py [new file with mode: 0755]
Lib/dos-8x3/para.py [new file with mode: 0755]
Lib/dos-8x3/posixfil.py [new file with mode: 0755]
Lib/dos-8x3/posixpat.py [new file with mode: 0755]
Lib/dos-8x3/py_compi.py [new file with mode: 0755]
Lib/dos-8x3/queue.py [new file with mode: 0755]
Lib/dos-8x3/regex_sy.py [new file with mode: 0755]
Lib/dos-8x3/simpleht.py [new file with mode: 0755]
Lib/dos-8x3/socketse.py [new file with mode: 0755]
Lib/dos-8x3/statcach.py [new file with mode: 0755]
Lib/dos-8x3/stringio.py [new file with mode: 0755]
Lib/dos-8x3/test_aud.py [new file with mode: 0755]
Lib/dos-8x3/test_bui.py [new file with mode: 0755]
Lib/dos-8x3/test_exc.py [new file with mode: 0755]
Lib/dos-8x3/test_gra.py [new file with mode: 0755]
Lib/dos-8x3/test_opc.py [new file with mode: 0755]
Lib/dos-8x3/test_ope.py [new file with mode: 0755]
Lib/dos-8x3/test_rgb.py [new file with mode: 0755]
Lib/dos-8x3/test_sel.py [new file with mode: 0755]
Lib/dos-8x3/test_sig.py [new file with mode: 0755]
Lib/dos-8x3/test_sup.py [new file with mode: 0755]
Lib/dos-8x3/test_thr.py [new file with mode: 0755]
Lib/dos-8x3/test_typ.py [new file with mode: 0755]
Lib/dos-8x3/tracebac.py [new file with mode: 0755]
Lib/dos-8x3/userdict.py [new file with mode: 0755]
Lib/dos-8x3/userlist.py [new file with mode: 0755]
Lib/dos-8x3/whatsoun.py [new file with mode: 0755]
Lib/dos_8x3/arrayio.py [new file with mode: 0755]
Lib/dos_8x3/ast.py [new file with mode: 0755]
Lib/dos_8x3/basehttp.py [new file with mode: 0755]
Lib/dos_8x3/bastion.py [new file with mode: 0755]
Lib/dos_8x3/cgihttps.py [new file with mode: 0755]
Lib/dos_8x3/compilea.py [new file with mode: 0755]
Lib/dos_8x3/complex.py [new file with mode: 0755]
Lib/dos_8x3/formatte.py [new file with mode: 0755]
Lib/dos_8x3/gopherli.py [new file with mode: 0755]
Lib/dos_8x3/htmlenti.py [new file with mode: 0755]
Lib/dos_8x3/importal.py [new file with mode: 0755]
Lib/dos_8x3/linecach.py [new file with mode: 0755]
Lib/dos_8x3/macurl2p.py [new file with mode: 0755]
Lib/dos_8x3/mimetool.py [new file with mode: 0755]
Lib/dos_8x3/multifil.py [new file with mode: 0755]
Lib/dos_8x3/nturl2pa.py [new file with mode: 0755]
Lib/dos_8x3/para.py [new file with mode: 0755]
Lib/dos_8x3/posixfil.py [new file with mode: 0755]
Lib/dos_8x3/posixpat.py [new file with mode: 0755]
Lib/dos_8x3/py_compi.py [new file with mode: 0755]
Lib/dos_8x3/queue.py [new file with mode: 0755]
Lib/dos_8x3/regex_sy.py [new file with mode: 0755]
Lib/dos_8x3/simpleht.py [new file with mode: 0755]
Lib/dos_8x3/socketse.py [new file with mode: 0755]
Lib/dos_8x3/statcach.py [new file with mode: 0755]
Lib/dos_8x3/stringio.py [new file with mode: 0755]
Lib/dos_8x3/test_aud.py [new file with mode: 0755]
Lib/dos_8x3/test_bui.py [new file with mode: 0755]
Lib/dos_8x3/test_exc.py [new file with mode: 0755]
Lib/dos_8x3/test_gra.py [new file with mode: 0755]
Lib/dos_8x3/test_opc.py [new file with mode: 0755]
Lib/dos_8x3/test_ope.py [new file with mode: 0755]
Lib/dos_8x3/test_rgb.py [new file with mode: 0755]
Lib/dos_8x3/test_sel.py [new file with mode: 0755]
Lib/dos_8x3/test_sig.py [new file with mode: 0755]
Lib/dos_8x3/test_sup.py [new file with mode: 0755]
Lib/dos_8x3/test_thr.py [new file with mode: 0755]
Lib/dos_8x3/test_typ.py [new file with mode: 0755]
Lib/dos_8x3/tracebac.py [new file with mode: 0755]
Lib/dos_8x3/userdict.py [new file with mode: 0755]
Lib/dos_8x3/userlist.py [new file with mode: 0755]
Lib/dos_8x3/whatsoun.py [new file with mode: 0755]

diff --git a/Lib/dos-8x3/arrayio.py b/Lib/dos-8x3/arrayio.py
new file mode 100755 (executable)
index 0000000..5b06f92
--- /dev/null
@@ -0,0 +1,142 @@
+"""File-like objects that read/write an array buffer.
+
+This implements (nearly) all stdio methods.
+
+f = ArrayIO()       # ready for writing
+f = ArrayIO(buf)    # ready for reading
+f.close()           # explicitly release resources held
+flag = f.isatty()   # always false
+pos = f.tell()      # get current position
+f.seek(pos)         # set current position
+f.seek(pos, mode)   # mode 0: absolute; 1: relative; 2: relative to EOF
+buf = f.read()      # read until EOF
+buf = f.read(n)     # read up to n bytes
+buf = f.readline()  # read until end of line ('\n') or EOF
+list = f.readlines()# list of f.readline() results until EOF
+f.write(buf)        # write at current position
+f.writelines(list)  # for line in list: f.write(line)
+f.getvalue()        # return whole file's contents as a string
+
+Notes:
+- This is very similar to StringIO.  StringIO is faster for reading,
+  but ArrayIO is faster for writing.
+- ArrayIO uses an array object internally, but all its interfaces
+  accept and return strings.
+- Using a real file is often faster (but less convenient).
+- fileno() is left unimplemented so that code which uses it triggers
+  an exception early.
+- Seeking far beyond EOF and then writing will insert real null
+  bytes that occupy space in the buffer.
+- There's a simple test set (see end of this file).
+"""
+
+import string
+from array import array
+
+class ArrayIO:
+       def __init__(self, buf = ''):
+               self.buf = array('c', buf)
+               self.pos = 0
+               self.closed = 0
+               self.softspace = 0
+       def close(self):
+               if not self.closed:
+                       self.closed = 1
+                       del self.buf, self.pos
+       def isatty(self):
+               return 0
+       def seek(self, pos, mode = 0):
+               if mode == 1:
+                       pos = pos + self.pos
+               elif mode == 2:
+                       pos = pos + len(self.buf)
+               self.pos = max(0, pos)
+       def tell(self):
+               return self.pos
+       def read(self, n = -1):
+               if n < 0:
+                       newpos = len(self.buf)
+               else:
+                       newpos = min(self.pos+n, len(self.buf))
+               r = self.buf[self.pos:newpos].tostring()
+               self.pos = newpos
+               return r
+       def readline(self):
+               i = string.find(self.buf[self.pos:].tostring(), '\n')
+               if i < 0:
+                       newpos = len(self.buf)
+               else:
+                       newpos = self.pos+i+1
+               r = self.buf[self.pos:newpos].tostring()
+               self.pos = newpos
+               return r
+       def readlines(self):
+               lines = string.splitfields(self.read(), '\n')
+               if not lines:
+                       return lines
+               for i in range(len(lines)-1):
+                       lines[i] = lines[i] + '\n'
+               if not lines[-1]:
+                       del lines[-1]
+               return lines
+       def write(self, s):
+               if not s: return
+               a = array('c', s)
+               n = self.pos - len(self.buf)
+               if n > 0:
+                       self.buf[len(self.buf):] = array('c', '\0')*n
+               newpos = self.pos + len(a)
+               self.buf[self.pos:newpos] = a
+               self.pos = newpos
+       def writelines(self, list):
+               self.write(string.joinfields(list, ''))
+       def flush(self):
+               pass
+       def getvalue(self):
+               return self.buf.tostring()
+
+
+# A little test suite
+
+def test():
+       import sys
+       if sys.argv[1:]:
+               file = sys.argv[1]
+       else:
+               file = '/etc/passwd'
+       lines = open(file, 'r').readlines()
+       text = open(file, 'r').read()
+       f = ArrayIO()
+       for line in lines[:-2]:
+               f.write(line)
+       f.writelines(lines[-2:])
+       if f.getvalue() != text:
+               raise RuntimeError, 'write failed'
+       length = f.tell()
+       print 'File length =', length
+       f.seek(len(lines[0]))
+       f.write(lines[1])
+       f.seek(0)
+       print 'First line =', `f.readline()`
+       here = f.tell()
+       line = f.readline()
+       print 'Second line =', `line`
+       f.seek(-len(line), 1)
+       line2 = f.read(len(line))
+       if line != line2:
+               raise RuntimeError, 'bad result after seek back'
+       f.seek(len(line2), 1)
+       list = f.readlines()
+       line = list[-1]
+       f.seek(f.tell() - len(line))
+       line2 = f.read()
+       if line != line2:
+               raise RuntimeError, 'bad result after seek back from EOF'
+       print 'Read', len(list), 'more lines'
+       print 'File length =', f.tell()
+       if f.tell() != length:
+               raise RuntimeError, 'bad length'
+       f.close()
+
+if __name__ == '__main__':
+       test()
diff --git a/Lib/dos-8x3/ast.py b/Lib/dos-8x3/ast.py
new file mode 100755 (executable)
index 0000000..6f92bee
--- /dev/null
@@ -0,0 +1,224 @@
+"""Object-oriented interface to the parser module.
+
+This module exports three classes which together provide an interface
+to the parser module.  Together, the three classes represent two ways
+to create parsed representations of Python source and the two starting
+data types (source text and tuple representations).  Each class
+provides interfaces which are identical other than the constructors.
+The constructors are described in detail in the documentation for each
+class and the remaining, shared portion of the interface is documented
+below.  Briefly, the three classes provided are:
+
+AST
+    Defines the primary interface to the AST objects and supports creation
+    from the tuple representation of the parse tree.
+
+ExpressionAST
+    Supports creation of expression constructs from source text.
+
+SuiteAST
+    Supports creation of statement suites from source text.
+
+FileSuiteAST
+    Convenience subclass of the `SuiteAST' class; loads source text of the
+    suite from an external file.
+
+Aside from the constructors, several methods are provided to allow
+access to the various interpretations of the parse tree and to check
+conditions of the construct represented by the parse tree.
+
+ast()
+    Returns the corresponding `parser.ASTType' object.
+
+code()
+    Returns the compiled code object.
+
+filename()
+    Returns the name of the associated source file, if known.
+
+isExpression()
+    Returns true value if parse tree represents an expression, or a false
+    value otherwise.
+
+isSuite()
+    Returns true value if parse tree represents a suite of statements, or
+    a false value otherwise.
+
+text()
+    Returns the source text, or None if not available.
+
+tuple()
+    Returns the tuple representing the parse tree.
+"""
+
+__version__ = '$Revision$'
+__copyright__ = """Copyright (c) 1995, 1996 by Fred L. Drake, Jr.
+
+This software may be used and distributed freely for any purpose provided
+that this notice is included unchanged on any and all copies.  The author
+does not warrant or guarantee this software in any way.
+"""
+
+class AST:
+    """Base class for Abstract Syntax Tree objects.
+
+    Creates an Abstract Syntax Tree based on the tuple representation
+    of the parse tree.  The parse tree can represent either an
+    expression or a suite; which will be recognized automatically.
+    This base class provides all of the query methods for subclass
+    objects defined in this module.
+    """
+    _p = __import__('parser')          # import internally to avoid
+                                       # namespace pollution at the
+                                       # top level
+    _text = None
+    _code = None
+    _ast  = None
+    _type = 'unknown'
+    _tupl = None
+
+    def __init__(self, tuple):
+       """Create an `AST' instance from a tuple-tree representation.
+
+       tuple
+           The tuple tree to convert.
+
+       The tuple-tree may represent either an expression or a suite; the
+       type will be determined automatically.
+       """
+       if type(tuple) is not type(()):
+           raise TypeError, 'Base AST class requires tuple parameter.'
+
+       self._tupl = tuple
+       self._ast  = self._p.tuple2ast(tuple)
+       self._type = (self._p.isexpr(self._ast) and 'expression') or 'suite'
+
+    def tuple(self):
+       """Returns the tuple representing the parse tree.
+       """
+       if self._tupl is None:
+           self._tupl = self._p.ast2tuple(self._ast)
+       return self._tupl
+
+    def code(self):
+       """Returns the compiled code object.
+
+       The code object returned by this method may be passed to the
+       exec statement if `AST.isSuite()' is true or to the eval()
+       function if `AST.isExpression()' is true.  All the usual rules
+       regarding execution of code objects apply.
+       """
+       if not self._code:
+           self._code = self._p.compileast(self._ast)
+       return self._code
+
+    def ast(self):
+       """Returns the corresponding `parser.ASTType' object.
+       """
+       return self._ast
+
+    def filename(self):
+       """Returns the name of the source file if known, or None.
+       """
+       return None
+
+    def text(self):
+       """Returns the source text, or None if not available.
+
+       If the instance is of class `AST', None is returned since no
+       source text is available.  If of class `ExpressionAST' or
+       `SuiteAST', the source text passed to the constructor is
+       returned.
+       """
+       return self._text
+
+    def isSuite(self):
+       """Determine if `AST' instance represents a suite of statements.
+       """
+       return self._type == 'suite'
+
+    def isExpression(self):
+       """Determine if `AST' instance represents an expression.
+       """
+       return self._type == 'expression'
+
+
+
+class SuiteAST(AST):
+    """Statement suite parse tree representation.
+
+    This subclass of the `AST' base class represents statement suites
+    parsed from the source text of a Python suite.  If the source text
+    does not represent a parsable suite of statements, the appropriate
+    exception is raised by the parser.
+    """
+    _type = 'suite'
+
+    def __init__(self, text):
+       """Initialize a `SuiteAST' from source text.
+
+       text
+           Source text to parse.
+       """
+       if type(text) is not type(''):
+           raise TypeError, 'SuiteAST requires source text parameter.'
+       self._text = text
+       self._ast  = self._p.suite(text)
+
+    def isSuite(self):
+       return 1
+
+    def isExpression(self):
+       return 0
+
+
+class FileSuiteAST(SuiteAST):
+    """Representation of a python source file syntax tree.
+
+    This provides a convenience wrapper around the `SuiteAST' class to
+    load the source text from an external file.
+    """
+    def __init__(self, fileName):
+       """Initialize a `SuiteAST' from a source file.
+
+       fileName
+           Name of the external source file.
+       """
+       self._fileName = fileName
+       SuiteAST.__init__(self, open(fileName).read())
+
+    def filename(self):
+       return self._fileName
+
+
+
+class ExpressionAST(AST):
+    """Expression parse tree representation.
+
+    This subclass of the `AST' base class represents expression
+    constructs parsed from the source text of a Python expression.  If
+    the source text does not represent a parsable expression, the
+    appropriate exception is raised by the Python parser.
+    """
+    _type = 'expression'
+
+    def __init__(self, text):
+       """Initialize an expression AST from source text.
+
+       text
+           Source text to parse.
+       """
+       if type(text) is not type(''):
+           raise TypeError, 'ExpressionAST requires source text parameter.'
+       self._text = text
+       self._ast  = self._p.expr(text)
+
+    def isSuite(self):
+       return 0
+
+    def isExpression(self):
+       return 1
+
+
+#
+#  end of file
diff --git a/Lib/dos-8x3/basehttp.py b/Lib/dos-8x3/basehttp.py
new file mode 100755 (executable)
index 0000000..281ddf6
--- /dev/null
@@ -0,0 +1,482 @@
+"""HTTP server base class.
+
+Note: the class in this module doesn't implement any HTTP request; see
+SimpleHTTPServer for simple implementations of GET, HEAD and POST
+(including CGI scripts).
+
+Contents:
+
+- BaseHTTPRequestHandler: HTTP request handler base class
+- test: test function
+
+XXX To do:
+
+- send server version
+- log requests even later (to capture byte count)
+- log user-agent header and other interesting goodies
+- send error log to separate file
+- are request names really case sensitive?
+
+"""
+
+
+# See also:
+#
+# HTTP Working Group                                        T. Berners-Lee
+# INTERNET-DRAFT                                            R. T. Fielding
+# <draft-ietf-http-v10-spec-00.txt>                     H. Frystyk Nielsen
+# Expires September 8, 1995                                  March 8, 1995
+#
+# URL: http://www.ics.uci.edu/pub/ietf/http/draft-ietf-http-v10-spec-00.txt
+
+
+# Log files
+# ---------
+# 
+# Here's a quote from the NCSA httpd docs about log file format.
+# 
+# | The logfile format is as follows. Each line consists of: 
+# | 
+# | host rfc931 authuser [DD/Mon/YYYY:hh:mm:ss] "request" ddd bbbb 
+# | 
+# |        host: Either the DNS name or the IP number of the remote client 
+# |        rfc931: Any information returned by identd for this person,
+# |                - otherwise. 
+# |        authuser: If user sent a userid for authentication, the user name,
+# |                  - otherwise. 
+# |        DD: Day 
+# |        Mon: Month (calendar name) 
+# |        YYYY: Year 
+# |        hh: hour (24-hour format, the machine's timezone) 
+# |        mm: minutes 
+# |        ss: seconds 
+# |        request: The first line of the HTTP request as sent by the client. 
+# |        ddd: the status code returned by the server, - if not available. 
+# |        bbbb: the total number of bytes sent,
+# |              *not including the HTTP/1.0 header*, - if not available 
+# | 
+# | You can determine the name of the file accessed through request.
+# 
+# (Actually, the latter is only true if you know the server configuration
+# at the time the request was made!)
+
+
+__version__ = "0.2"
+
+
+import sys
+import time
+import socket # For gethostbyaddr()
+import string
+import rfc822
+import mimetools
+import SocketServer
+
+# Default error message
+DEFAULT_ERROR_MESSAGE = """\
+<head>
+<title>Error response</title>
+</head>
+<body>
+<h1>Error response</h1>
+<p>Error code %(code)d.
+<p>Message: %(message)s.
+<p>Error code explanation: %(code)s = %(explain)s.
+</body>
+"""
+
+
+class HTTPServer(SocketServer.TCPServer):
+
+    def server_bind(self):
+       """Override server_bind to store the server name."""
+       SocketServer.TCPServer.server_bind(self)
+       host, port = self.socket.getsockname()
+       if not host or host == '0.0.0.0':
+           host = socket.gethostname()
+       hostname, hostnames, hostaddrs = socket.gethostbyaddr(host)
+       if '.' not in hostname:
+           for host in hostnames:
+               if '.' in host:
+                   hostname = host
+                   break
+       self.server_name = hostname
+       self.server_port = port
+
+
+class BaseHTTPRequestHandler(SocketServer.StreamRequestHandler):
+
+    """HTTP request handler base class.
+
+    The following explanation of HTTP serves to guide you through the
+    code as well as to expose any misunderstandings I may have about
+    HTTP (so you don't need to read the code to figure out I'm wrong
+    :-).
+
+    HTTP (HyperText Transfer Protocol) is an extensible protocol on
+    top of a reliable stream transport (e.g. TCP/IP).  The protocol
+    recognizes three parts to a request:
+
+    1. One line identifying the request type and path
+    2. An optional set of RFC-822-style headers
+    3. An optional data part
+
+    The headers and data are separated by a blank line.
+
+    The first line of the request has the form
+
+    <command> <path> <version>
+
+    where <command> is a (case-sensitive) keyword such as GET or POST,
+    <path> is a string containing path information for the request,
+    and <version> should be the string "HTTP/1.0".  <path> is encoded
+    using the URL encoding scheme (using %xx to signify the ASCII
+    character with hex code xx).
+
+    The protocol is vague about whether lines are separated by LF
+    characters or by CRLF pairs -- for compatibility with the widest
+    range of clients, both should be accepted.  Similarly, whitespace
+    in the request line should be treated sensibly (allowing multiple
+    spaces between components and allowing trailing whitespace).
+
+    Similarly, for output, lines ought to be separated by CRLF pairs
+    but most clients grok LF characters just fine.
+
+    If the first line of the request has the form
+
+    <command> <path>
+
+    (i.e. <version> is left out) then this is assumed to be an HTTP
+    0.9 request; this form has no optional headers and data part and
+    the reply consists of just the data.
+
+    The reply form of the HTTP 1.0 protocol again has three parts:
+
+    1. One line giving the response code
+    2. An optional set of RFC-822-style headers
+    3. The data
+
+    Again, the headers and data are separated by a blank line.
+
+    The response code line has the form
+
+    <version> <responsecode> <responsestring>
+
+    where <version> is the protocol version (always "HTTP/1.0"),
+    <responsecode> is a 3-digit response code indicating success or
+    failure of the request, and <responsestring> is an optional
+    human-readable string explaining what the response code means.
+
+    This server parses the request and the headers, and then calls a
+    function specific to the request type (<command>).  Specifically,
+    a request SPAM will be handled by a method handle_SPAM().  If no
+    such method exists the server sends an error response to the
+    client.  If it exists, it is called with no arguments:
+
+    do_SPAM()
+
+    Note that the request name is case sensitive (i.e. SPAM and spam
+    are different requests).
+
+    The various request details are stored in instance variables:
+
+    - client_address is the client IP address in the form (host,
+    port);
+
+    - command, path and version are the broken-down request line;
+
+    - headers is an instance of mimetools.Message (or a derived
+    class) containing the header information;
+
+    - rfile is a file object open for reading positioned at the
+    start of the optional input data part;
+
+    - wfile is a file object open for writing.
+
+    IT IS IMPORTANT TO ADHERE TO THE PROTOCOL FOR WRITING!
+
+    The first thing to be written must be the response line.  Then
+    follow 0 or more header lines, then a blank line, and then the
+    actual data (if any).  The meaning of the header lines depends on
+    the command executed by the server; in most cases, when data is
+    returned, there should be at least one header line of the form
+
+    Content-type: <type>/<subtype>
+
+    where <type> and <subtype> should be registered MIME types,
+    e.g. "text/html" or "text/plain".
+
+    """
+
+    # The Python system version, truncated to its first component.
+    sys_version = "Python/" + string.split(sys.version)[0]
+
+    # The server software version.  You may want to override this.
+    # The format is multiple whitespace-separated strings,
+    # where each string is of the form name[/version].
+    server_version = "BaseHTTP/" + __version__
+
+    def handle(self):
+       """Handle a single HTTP request.
+
+       You normally don't need to override this method; see the class
+       __doc__ string for information on how to handle specific HTTP
+       commands such as GET and POST.
+
+       """
+
+       self.raw_requestline = self.rfile.readline()
+       self.request_version = version = "HTTP/0.9" # Default
+       requestline = self.raw_requestline
+       if requestline[-2:] == '\r\n':
+           requestline = requestline[:-2]
+       elif requestline[-1:] == '\n':
+           requestline = requestline[:-1]
+       self.requestline = requestline
+       words = string.split(requestline)
+       if len(words) == 3:
+           [command, path, version] = words
+           if version != self.protocol_version:
+               self.send_error(400, "Bad request version (%s)" % `version`)
+               return
+       elif len(words) == 2:
+           [command, path] = words
+           if command != 'GET':
+               self.send_error(400,
+                               "Bad HTTP/0.9 request type (%s)" % `command`)
+               return
+       else:
+           self.send_error(400, "Bad request syntax (%s)" % `requestline`)
+           return
+       self.command, self.path, self.request_version = command, path, version
+       self.headers = self.MessageClass(self.rfile, 0)
+       mname = 'do_' + command
+       if not hasattr(self, mname):
+           self.send_error(501, "Unsupported method (%s)" % `mname`)
+           return
+       method = getattr(self, mname)
+       method()
+
+    def send_error(self, code, message=None):
+       """Send and log an error reply.
+
+       Arguments are the error code, and a detailed message.
+       The detailed message defaults to the short entry matching the
+       response code.
+
+       This sends an error response (so it must be called before any
+       output has been generated), logs the error, and finally sends
+       a piece of HTML explaining the error to the user.
+
+       """
+
+       try:
+           short, long = self.responses[code]
+       except KeyError:
+           short, long = '???', '???'
+       if not message:
+           message = short
+       explain = long
+       self.log_error("code %d, message %s", code, message)
+       self.send_response(code, message)
+       self.end_headers()
+       self.wfile.write(self.error_message_format %
+                        {'code': code,
+                         'message': message,
+                         'explain': explain})
+
+    error_message_format = DEFAULT_ERROR_MESSAGE
+
+    def send_response(self, code, message=None):
+       """Send the response header and log the response code.
+
+       Also send two standard headers with the server software
+       version and the current date.
+
+       """
+       self.log_request(code)
+       if message is None:
+           if self.responses.has_key(code):
+               message = self.responses[code][1]
+           else:
+               message = ''
+       if self.request_version != 'HTTP/0.9':
+           self.wfile.write("%s %s %s\r\n" %
+                            (self.protocol_version, str(code), message))
+       self.send_header('Server', self.version_string())
+       self.send_header('Date', self.date_time_string())
+
+    def send_header(self, keyword, value):
+       """Send a MIME header."""
+       if self.request_version != 'HTTP/0.9':
+           self.wfile.write("%s: %s\r\n" % (keyword, value))
+
+    def end_headers(self):
+       """Send the blank line ending the MIME headers."""
+       if self.request_version != 'HTTP/0.9':
+           self.wfile.write("\r\n")
+
+    def log_request(self, code='-', size='-'):
+       """Log an accepted request.
+
+       This is called by send_reponse().
+
+       """
+
+       self.log_message('"%s" %s %s',
+                        self.requestline, str(code), str(size))
+
+    def log_error(self, *args):
+       """Log an error.
+
+       This is called when a request cannot be fulfilled.  By
+       default it passes the message on to log_message().
+
+       Arguments are the same as for log_message().
+
+       XXX This should go to the separate error log.
+
+       """
+
+       apply(self.log_message, args)
+
+    def log_message(self, format, *args):
+       """Log an arbitrary message.
+
+       This is used by all other logging functions.  Override
+       it if you have specific logging wishes.
+
+       The first argument, FORMAT, is a format string for the
+       message to be logged.  If the format string contains
+       any % escapes requiring parameters, they should be
+       specified as subsequent arguments (it's just like
+       printf!).
+
+       The client host and current date/time are prefixed to
+       every message.
+
+       """
+
+       sys.stderr.write("%s - - [%s] %s\n" %
+                        (self.address_string(),
+                         self.log_date_time_string(),
+                         format%args))
+
+    def version_string(self):
+       """Return the server software version string."""
+       return self.server_version + ' ' + self.sys_version
+
+    def date_time_string(self):
+       """Return the current date and time formatted for a message header."""
+       now = time.time()
+       year, month, day, hh, mm, ss, wd, y, z = time.gmtime(now)
+       s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (
+               self.weekdayname[wd],
+               day, self.monthname[month], year,
+               hh, mm, ss)
+       return s
+
+    def log_date_time_string(self):
+       """Return the current time formatted for logging."""
+       now = time.time()
+       year, month, day, hh, mm, ss, x, y, z = time.localtime(now)
+       s = "%02d/%3s/%04d %02d:%02d:%02d" % (
+               day, self.monthname[month], year, hh, mm, ss)
+       return s
+
+    weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
+
+    monthname = [None,
+                'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
+                'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
+
+    def address_string(self):
+       """Return the client address formatted for logging.
+
+       This version looks up the full hostname using gethostbyaddr(),
+       and tries to find a name that contains at least one dot.
+
+       """
+
+       (host, port) = self.client_address
+       try:
+           name, names, addresses = socket.gethostbyaddr(host)
+       except socket.error, msg:
+           return host
+       names.insert(0, name)
+       for name in names:
+           if '.' in name: return name
+       return names[0]
+
+
+    # Essentially static class variables
+
+    # The version of the HTTP protocol we support.
+    # Don't override unless you know what you're doing (hint: incoming
+    # requests are required to have exactly this version string).
+    protocol_version = "HTTP/1.0"
+
+    # The Message-like class used to parse headers
+    MessageClass = mimetools.Message
+
+    # Table mapping response codes to messages; entries have the
+    # form {code: (shortmessage, longmessage)}.
+    # See http://www.w3.org/hypertext/WWW/Protocols/HTTP/HTRESP.html
+    responses = {
+       200: ('OK', 'Request fulfilled, document follows'),
+       201: ('Created', 'Document created, URL follows'),
+       202: ('Accepted',
+             'Request accepted, processing continues off-line'),
+       203: ('Partial information', 'Request fulfilled from cache'),
+       204: ('No response', 'Request fulfilled, nothing follows'),
+       
+       301: ('Moved', 'Object moved permanently -- see URI list'),
+       302: ('Found', 'Object moved temporarily -- see URI list'),
+       303: ('Method', 'Object moved -- see Method and URL list'),
+       304: ('Not modified',
+             'Document has not changed singe given time'),
+       
+       400: ('Bad request',
+             'Bad request syntax or unsupported method'),
+       401: ('Unauthorized',
+             'No permission -- see authorization schemes'),
+       402: ('Payment required',
+             'No payment -- see charging schemes'),
+       403: ('Forbidden',
+             'Request forbidden -- authorization will not help'),
+       404: ('Not found', 'Nothing matches the given URI'),
+       
+       500: ('Internal error', 'Server got itself in trouble'),
+       501: ('Not implemented',
+             'Server does not support this operation'),
+       502: ('Service temporarily overloaded',
+             'The server cannot process the request due to a high load'),
+       503: ('Gateway timeout',
+             'The gateway server did not receive a timely response'),
+       
+       }
+
+
+def test(HandlerClass = BaseHTTPRequestHandler,
+        ServerClass = HTTPServer):
+    """Test the HTTP request handler class.
+
+    This runs an HTTP server on port 8000 (or the first command line
+    argument).
+
+    """
+
+    if sys.argv[1:]:
+       port = string.atoi(sys.argv[1])
+    else:
+       port = 8000
+    server_address = ('', port)
+
+    httpd = ServerClass(server_address, HandlerClass)
+
+    print "Serving HTTP on port", port, "..."
+    httpd.serve_forever()
+
+
+if __name__ == '__main__':
+    test()
diff --git a/Lib/dos-8x3/bastion.py b/Lib/dos-8x3/bastion.py
new file mode 100755 (executable)
index 0000000..7ddd93e
--- /dev/null
@@ -0,0 +1,162 @@
+"""Bastionification utility.
+
+A bastion (for another object -- the 'original') is an object that has
+the same methods as the original but does not give access to its
+instance variables.  Bastions have a number of uses, but the most
+obvious one is to provide code executing in restricted mode with a
+safe interface to an object implemented in unrestricted mode.
+
+The bastionification routine has an optional second argument which is
+a filter function.  Only those methods for which the filter method
+(called with the method name as argument) returns true are accessible.
+The default filter method returns true unless the method name begins
+with an underscore.
+
+There are a number of possible implementations of bastions.  We use a
+'lazy' approach where the bastion's __getattr__() discipline does all
+the work for a particular method the first time it is used.  This is
+usually fastest, especially if the user doesn't call all available
+methods.  The retrieved methods are stored as instance variables of
+the bastion, so the overhead is only occurred on the first use of each
+method.
+
+Detail: the bastion class has a __repr__() discipline which includes
+the repr() of the original object.  This is precomputed when the
+bastion is created.
+
+"""
+
+__version__ = '$Revision$'
+# $Source$
+
+
+from types import MethodType
+
+
+class BastionClass:
+
+    """Helper class used by the Bastion() function.
+
+    You could subclass this and pass the subclass as the bastionclass
+    argument to the Bastion() function, as long as the constructor has
+    the same signature (a get() function and a name for the object).
+
+    """
+
+    def __init__(self, get, name):
+       """Constructor.
+
+       Arguments:
+
+       get - a function that gets the attribute value (by name)
+       name - a human-readable name for the original object
+              (suggestion: use repr(object))
+
+       """
+       self._get_ = get
+       self._name_ = name
+
+    def __repr__(self):
+       """Return a representation string.
+
+       This includes the name passed in to the constructor, so that
+       if you print the bastion during debugging, at least you have
+       some idea of what it is.
+
+       """
+       return "<Bastion for %s>" % self._name_
+
+    def __getattr__(self, name):
+       """Get an as-yet undefined attribute value.
+
+       This calls the get() function that was passed to the
+       constructor.  The result is stored as an instance variable so
+       that the next time the same attribute is requested,
+       __getattr__() won't be invoked.
+
+       If the get() function raises an exception, this is simply
+       passed on -- exceptions are not cached.
+
+       """
+       attribute = self._get_(name)
+       self.__dict__[name] = attribute
+       return attribute
+
+
+def Bastion(object, filter = lambda name: name[:1] != '_',
+           name=None, bastionclass=BastionClass):
+    """Create a bastion for an object, using an optional filter.
+
+    See the Bastion module's documentation for background.
+
+    Arguments:
+
+    object - the original object
+    filter - a predicate that decides whether a function name is OK;
+             by default all names are OK that don't start with '_'
+    name - the name of the object; default repr(object)
+    bastionclass - class used to create the bastion; default BastionClass
+
+    """
+
+    # Note: we define *two* ad-hoc functions here, get1 and get2.
+    # Both are intended to be called in the same way: get(name).
+    # It is clear that the real work (getting the attribute
+    # from the object and calling the filter) is done in get1.
+    # Why can't we pass get1 to the bastion?  Because the user
+    # would be able to override the filter argument!  With get2,
+    # overriding the default argument is no security loophole:
+    # all it does is call it.
+    # Also notice that we can't place the object and filter as
+    # instance variables on the bastion object itself, since
+    # the user has full access to all instance variables!
+
+    def get1(name, object=object, filter=filter):
+       """Internal function for Bastion().  See source comments."""
+       if filter(name):
+           attribute = getattr(object, name)
+           if type(attribute) == MethodType:
+               return attribute
+       raise AttributeError, name
+
+    def get2(name, get1=get1):
+       """Internal function for Bastion().  See source comments."""
+       return get1(name)
+
+    if name is None:
+       name = `object`
+    return bastionclass(get2, name)
+
+
+def _test():
+    """Test the Bastion() function."""
+    class Original:
+       def __init__(self):
+           self.sum = 0
+       def add(self, n):
+           self._add(n)
+       def _add(self, n):
+           self.sum = self.sum + n
+       def total(self):
+           return self.sum
+    o = Original()
+    b = Bastion(o)
+    b.add(81)
+    b.add(18)
+    print "b.total() =", b.total()
+    try:
+       print "b.sum =", b.sum,
+    except:
+       print "inaccessible"
+    else:
+       print "accessible"
+    try:
+       print "b._add =", b._add,
+    except:
+       print "inaccessible"
+    else:
+       print "accessible"
+
+
+if __name__ == '__main__':
+    _test()
diff --git a/Lib/dos-8x3/cgihttps.py b/Lib/dos-8x3/cgihttps.py
new file mode 100755 (executable)
index 0000000..837f7c2
--- /dev/null
@@ -0,0 +1,203 @@
+"""CGI-savvy HTTP Server.
+
+This module builds on SimpleHTTPServer by implementing GET and POST
+requests to cgi-bin scripts.
+
+"""
+
+
+__version__ = "0.3"
+
+
+import os
+import sys
+import time
+import socket
+import string
+import urllib
+import BaseHTTPServer
+import SimpleHTTPServer
+
+
+class CGIHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
+
+    """Complete HTTP server with GET, HEAD and POST commands.
+
+    GET and HEAD also support running CGI scripts.
+
+    The POST command is *only* implemented for CGI scripts.
+
+    """
+
+    def do_POST(self):
+       """Serve a POST request.
+
+       This is only implemented for CGI scripts.
+
+       """
+
+       if self.is_cgi():
+           self.run_cgi()
+       else:
+           self.send_error(501, "Can only POST to CGI scripts")
+
+    def send_head(self):
+       """Version of send_head that support CGI scripts"""
+       if self.is_cgi():
+           return self.run_cgi()
+       else:
+           return SimpleHTTPServer.SimpleHTTPRequestHandler.send_head(self)
+
+    def is_cgi(self):
+       """test whether PATH corresponds to a CGI script.
+
+       Return a tuple (dir, rest) if PATH requires running a
+       CGI script, None if not.  Note that rest begins with a
+       slash if it is not empty.
+
+       The default implementation tests whether the path
+       begins with one of the strings in the list
+       self.cgi_directories (and the next character is a '/'
+       or the end of the string).
+
+       """
+
+       path = self.path
+
+       for x in self.cgi_directories:
+           i = len(x)
+           if path[:i] == x and (not path[i:] or path[i] == '/'):
+               self.cgi_info = path[:i], path[i+1:]
+               return 1
+       return 0
+
+    cgi_directories = ['/cgi-bin', '/htbin']
+
+    def run_cgi(self):
+       """Execute a CGI script."""
+       dir, rest = self.cgi_info
+       i = string.rfind(rest, '?')
+       if i >= 0:
+           rest, query = rest[:i], rest[i+1:]
+       else:
+           query = ''
+       i = string.find(rest, '/')
+       if i >= 0:
+           script, rest = rest[:i], rest[i:]
+       else:
+           script, rest = rest, ''
+       scriptname = dir + '/' + script
+       scriptfile = self.translate_path(scriptname)
+       if not os.path.exists(scriptfile):
+           self.send_error(404, "No such CGI script (%s)" % `scriptname`)
+           return
+       if not os.path.isfile(scriptfile):
+           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
+           env['CONTENT_TYPE'] = self.headers.type
+           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
+           # XXX Other HTTP_* headers
+           import regsub
+           decoded_query = regsub.gsub('+', ' ', query)
+           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)
+
+
+nobody = None
+
+def nobody_uid():
+    """Internal routine to get nobody's uid"""
+    global nobody
+    if nobody:
+       return nobody
+    import pwd
+    try:
+       nobody = pwd.getpwnam('nobody')[2]
+    except pwd.error:
+       nobody = 1 + max(map(lambda x: x[2], pwd.getpwall()))
+    return nobody
+
+
+def executable(path):
+    """Test for executable file."""
+    try:
+       st = os.stat(path)
+    except os.error:
+       return 0
+    return st[0] & 0111 != 0
+
+
+def test(HandlerClass = CGIHTTPRequestHandler,
+        ServerClass = BaseHTTPServer.HTTPServer):
+    import sys
+    if sys.argv[1:2] == ['-r']:
+       db = MyArchive()
+       db.regenindices()
+       return
+    SimpleHTTPServer.test(HandlerClass, ServerClass)
+
+
+if __name__ == '__main__':
+    test()
diff --git a/Lib/dos-8x3/compilea.py b/Lib/dos-8x3/compilea.py
new file mode 100755 (executable)
index 0000000..3120284
--- /dev/null
@@ -0,0 +1,70 @@
+# Routines to force "compilation" of all .py files in a directory
+# tree or on sys.path.  By default recursion is pruned at a depth of
+# 10 and the current directory, if it occurs in sys.path, is skipped.
+# When called as a script, compiles argument directories, or sys.path
+# if no arguments.
+# After a similar module by Sjoerd Mullender.
+
+import os
+import sys
+import py_compile
+
+def compile_dir(dir, maxlevels = 10):
+       print 'Listing', dir, '...'
+       try:
+               names = os.listdir(dir)
+       except os.error:
+               print "Can't list", dir
+               names = []
+       names.sort()
+       for name in names:
+               fullname = os.path.join(dir, name)
+               if os.path.isfile(fullname):
+                       head, tail = name[:-3], name[-3:]
+                       if tail == '.py':
+                               print 'Compiling', fullname, '...'
+                               try:
+                                       py_compile.compile(fullname)
+                               except KeyboardInterrupt:
+                                       del names[:]
+                                       print '\n[interrupt]'
+                                       break
+                               except:
+                                       if type(sys.exc_type) == type(''):
+                                               exc_type_name = sys.exc_type
+                                       else: exc_type_name = sys.exc_type.__name__
+                                       print 'Sorry:', exc_type_name + ':',
+                                       print sys.exc_value
+               elif maxlevels > 0 and \
+                    name != os.curdir and name != os.pardir and \
+                    os.path.isdir(fullname) and \
+                    not os.path.islink(fullname):
+                       compile_dir(fullname, maxlevels - 1)
+
+def compile_path(skip_curdir = 1):
+       for dir in sys.path:
+               if dir == os.curdir and skip_curdir:
+                       print 'Skipping current directory'
+               else:
+                       compile_dir(dir, 0)
+
+def main():
+       import getopt
+       try:
+               opts, args = getopt.getopt(sys.argv[1:], 'l')
+       except getopt.error, msg:
+               print msg
+               print "usage: compileall [-l] [directory ...]"
+               print "-l: don't recurse down"
+               print "if no arguments, -l sys.path is assumed"
+       maxlevels = 10
+       for o, a in opts:
+               if o == '-l': maxlevels = 0
+       if args:
+               for dir in sys.argv[1:]:
+                       compile_dir(dir, maxlevels)
+       else:
+               compile_path()
+
+if __name__ == '__main__':
+       main()
diff --git a/Lib/dos-8x3/complex.py b/Lib/dos-8x3/complex.py
new file mode 100755 (executable)
index 0000000..f4892f3
--- /dev/null
@@ -0,0 +1,275 @@
+# Complex numbers
+# ---------------
+
+# This module represents complex numbers as instances of the class Complex.
+# A Complex instance z has two data attribues, z.re (the real part) and z.im
+# (the imaginary part).  In fact, z.re and z.im can have any value -- all
+# arithmetic operators work regardless of the type of z.re and z.im (as long
+# as they support numerical operations).
+#
+# The following functions exist (Complex is actually a class):
+# Complex([re [,im]) -> creates a complex number from a real and an imaginary part
+# IsComplex(z) -> true iff z is a complex number (== has .re and .im attributes)
+# Polar([r [,phi [,fullcircle]]]) ->
+#      the complex number z for which r == z.radius() and phi == z.angle(fullcircle)
+#      (r and phi default to 0)
+#
+# Complex numbers have the following methods:
+# z.abs() -> absolute value of z
+# z.radius() == z.abs()
+# z.angle([fullcircle]) -> angle from positive X axis; fullcircle gives units
+# z.phi([fullcircle]) == z.angle(fullcircle)
+#
+# These standard functions and unary operators accept complex arguments:
+# abs(z)
+# -z
+# +z
+# not z
+# repr(z) == `z`
+# str(z)
+# hash(z) -> a combination of hash(z.re) and hash(z.im) such that if z.im is zero
+#            the result equals hash(z.re)
+# Note that hex(z) and oct(z) are not defined.
+#
+# These conversions accept complex arguments only if their imaginary part is zero:
+# int(z)
+# long(z)
+# float(z)
+#
+# The following operators accept two complex numbers, or one complex number
+# and one real number (int, long or float):
+# z1 + z2
+# z1 - z2
+# z1 * z2
+# z1 / z2
+# pow(z1, z2)
+# cmp(z1, z2)
+# Note that z1 % z2 and divmod(z1, z2) are not defined,
+# nor are shift and mask operations.
+#
+# The standard module math does not support complex numbers.
+# (I suppose it would be easy to implement a cmath module.)
+#
+# Idea:
+# add a class Polar(r, phi) and mixed-mode arithmetic which
+# chooses the most appropriate type for the result:
+# Complex for +,-,cmp
+# Polar   for *,/,pow
+
+
+import types, math
+
+if not hasattr(math, 'hypot'):
+       def hypot(x, y):
+               # XXX I know there's a way to compute this without possibly causing
+               # overflow, but I can't remember what it is right now...
+               return math.sqrt(x*x + y*y)
+       math.hypot = hypot
+
+twopi = math.pi*2.0
+halfpi = math.pi/2.0
+
+def IsComplex(obj):
+       return hasattr(obj, 're') and hasattr(obj, 'im')
+
+def Polar(r = 0, phi = 0, fullcircle = twopi):
+       phi = phi * (twopi / fullcircle)
+       return Complex(math.cos(phi)*r, math.sin(phi)*r)
+
+class Complex:
+
+       def __init__(self, re=0, im=0):
+               if IsComplex(re):
+                       im = im + re.im
+                       re = re.re
+               if IsComplex(im):
+                       re = re - im.im
+                       im = im.re
+               self.re = re
+               self.im = im
+
+       def __setattr__(self, name, value):
+               if hasattr(self, name):
+                       raise TypeError, "Complex numbers have set-once attributes"
+               self.__dict__[name] = value
+
+       def __repr__(self):
+               if not self.im:
+                       return 'Complex(%s)' % `self.re`
+               else:
+                       return 'Complex(%s, %s)' % (`self.re`, `self.im`)
+
+       def __str__(self):
+               if not self.im:
+                       return `self.re`
+               else:
+                       return 'Complex(%s, %s)' % (`self.re`, `self.im`)
+
+       def __coerce__(self, other):
+               if IsComplex(other):
+                       return self, other
+               return self, Complex(other)     # May fail
+
+       def __cmp__(self, other):
+               return cmp(self.re, other.re) or cmp(self.im, other.im)
+
+       def __hash__(self):
+               if not self.im: return hash(self.re)
+               mod = sys.maxint + 1L
+               return int((hash(self.re) + 2L*hash(self.im) + mod) % (2L*mod) - mod)
+
+       def __neg__(self):
+               return Complex(-self.re, -self.im)
+
+       def __pos__(self):
+               return self
+
+       def __abs__(self):
+               return math.hypot(self.re, self.im)
+               ##return math.sqrt(self.re*self.re + self.im*self.im)
+
+
+       def __int__(self):
+               if self.im:
+                       raise ValueError, "can't convert Complex with nonzero im to int"
+               return int(self.re)
+
+       def __long__(self):
+               if self.im:
+                       raise ValueError, "can't convert Complex with nonzero im to long"
+               return long(self.re)
+
+       def __float__(self):
+               if self.im:
+                       raise ValueError, "can't convert Complex with nonzero im to float"
+               return float(self.re)
+
+       def __nonzero__(self):
+               return not (self.re == self.im == 0)
+
+       abs = radius = __abs__
+
+       def angle(self, fullcircle = twopi):
+               return (fullcircle/twopi) * ((halfpi - math.atan2(self.re, self.im)) % twopi)
+
+       phi = angle
+
+       def __add__(self, other):
+               return Complex(self.re + other.re, self.im + other.im)
+
+       __radd__ = __add__
+
+       def __sub__(self, other):
+               return Complex(self.re - other.re, self.im - other.im)
+
+       def __rsub__(self, other):
+               return Complex(other.re - self.re, other.im - self.im)
+
+       def __mul__(self, other):
+               return Complex(self.re*other.re - self.im*other.im,
+                              self.re*other.im + self.im*other.re)
+
+       __rmul__ = __mul__
+
+       def __div__(self, other):
+               # Deviating from the general principle of not forcing re or im
+               # to be floats, we cast to float here, otherwise division
+               # of Complex numbers with integer re and im parts would use
+               # the (truncating) integer division
+               d = float(other.re*other.re + other.im*other.im)
+               if not d: raise ZeroDivisionError, 'Complex division'
+               return Complex((self.re*other.re + self.im*other.im) / d,
+                              (self.im*other.re - self.re*other.im) / d)
+
+       def __rdiv__(self, other):
+               return other / self
+
+       def __pow__(self, n, z=None):
+               if z is not None:
+                       raise TypeError, 'Complex does not support ternary pow()'
+               if IsComplex(n):
+                       if n.im: raise TypeError, 'Complex to the Complex power'
+                       n = n.re
+               r = pow(self.abs(), n)
+               phi = n*self.angle()
+               return Complex(math.cos(phi)*r, math.sin(phi)*r)
+       
+       def __rpow__(self, base):
+               return pow(base, self)
+
+
+# Everything below this point is part of the test suite
+
+def checkop(expr, a, b, value, fuzz = 1e-6):
+       import sys
+       print '       ', a, 'and', b,
+       try:
+               result = eval(expr)
+       except:
+               result = sys.exc_type
+       print '->', result
+       if (type(result) == type('') or type(value) == type('')):
+               ok = result == value
+       else:
+               ok = abs(result - value) <= fuzz
+       if not ok:
+               print '!!\t!!\t!! should be', value, 'diff', abs(result - value)
+
+
+def test():
+       testsuite = {
+               'a+b': [
+                       (1, 10, 11),
+                       (1, Complex(0,10), Complex(1,10)),
+                       (Complex(0,10), 1, Complex(1,10)),
+                       (Complex(0,10), Complex(1), Complex(1,10)),
+                       (Complex(1), Complex(0,10), Complex(1,10)),
+               ],
+               'a-b': [
+                       (1, 10, -9),
+                       (1, Complex(0,10), Complex(1,-10)),
+                       (Complex(0,10), 1, Complex(-1,10)),
+                       (Complex(0,10), Complex(1), Complex(-1,10)),
+                       (Complex(1), Complex(0,10), Complex(1,-10)),
+               ],
+               'a*b': [
+                       (1, 10, 10),
+                       (1, Complex(0,10), Complex(0, 10)),
+                       (Complex(0,10), 1, Complex(0,10)),
+                       (Complex(0,10), Complex(1), Complex(0,10)),
+                       (Complex(1), Complex(0,10), Complex(0,10)),
+               ],
+               'a/b': [
+                       (1., 10, 0.1),
+                       (1, Complex(0,10), Complex(0, -0.1)),
+                       (Complex(0, 10), 1, Complex(0, 10)),
+                       (Complex(0, 10), Complex(1), Complex(0, 10)),
+                       (Complex(1), Complex(0,10), Complex(0, -0.1)),
+               ],
+               'pow(a,b)': [
+                       (1, 10, 1),
+                       (1, Complex(0,10), 'TypeError'),
+                       (Complex(0,10), 1, Complex(0,10)),
+                       (Complex(0,10), Complex(1), Complex(0,10)),
+                       (Complex(1), Complex(0,10), 'TypeError'),
+                       (2, Complex(4,0), 16),
+               ],
+               'cmp(a,b)': [
+                       (1, 10, -1),
+                       (1, Complex(0,10), 1),
+                       (Complex(0,10), 1, -1),
+                       (Complex(0,10), Complex(1), -1),
+                       (Complex(1), Complex(0,10), 1),
+               ],
+       }
+       exprs = testsuite.keys()
+       exprs.sort()
+       for expr in exprs:
+               print expr + ':'
+               t = (expr,)
+               for item in testsuite[expr]:
+                       apply(checkop, t+item)
+       
+
+if __name__ == '__main__':
+       test()
diff --git a/Lib/dos-8x3/formatte.py b/Lib/dos-8x3/formatte.py
new file mode 100755 (executable)
index 0000000..0266379
--- /dev/null
@@ -0,0 +1,399 @@
+import regex
+import regsub
+import string
+import sys
+from types import StringType
+
+
+AS_IS = None
+
+
+class NullFormatter:
+
+    def __init__(self): pass
+    def end_paragraph(self, blankline): pass
+    def add_line_break(self): pass
+    def add_hor_rule(self, abswidth=None, percentwidth=1.0,
+                    height=None, align=None): pass
+    def add_label_data(self, format, counter): pass
+    def add_flowing_data(self, data): pass
+    def add_literal_data(self, data): pass
+    def flush_softspace(self): pass
+    def push_alignment(self, align): pass
+    def pop_alignment(self): pass
+    def push_font(self, x): pass
+    def pop_font(self): pass
+    def push_margin(self, margin): pass
+    def pop_margin(self): pass
+    def set_spacing(self, spacing): pass
+    def push_style(self, *styles): pass
+    def pop_style(self, n=1): pass
+    def assert_line_data(self, flag=1): pass
+
+
+class AbstractFormatter:
+
+    def __init__(self, writer):
+       self.writer = writer            # Output device
+       self.align = None               # Current alignment
+       self.align_stack = []           # Alignment stack
+       self.font_stack = []            # Font state
+       self.margin_stack = []          # Margin state
+       self.spacing = None             # Vertical spacing state
+       self.style_stack = []           # Other state, e.g. color
+       self.nospace = 1                # Should leading space be suppressed
+       self.softspace = 0              # Should a space be inserted
+       self.para_end = 1               # Just ended a paragraph
+       self.parskip = 0                # Skipped space between paragraphs?
+       self.hard_break = 1             # Have a hard break
+       self.have_label = 0
+
+    def end_paragraph(self, blankline):
+       if not self.hard_break:
+           self.writer.send_line_break()
+           self.have_label = 0
+       if self.parskip < blankline and not self.have_label:
+           self.writer.send_paragraph(blankline - self.parskip)
+           self.parskip = blankline
+           self.have_label = 0
+       self.hard_break = self.nospace = self.para_end = 1
+       self.softspace = 0
+
+    def add_line_break(self):
+       if not (self.hard_break or self.para_end):
+           self.writer.send_line_break()
+           self.have_label = self.parskip = 0
+       self.hard_break = self.nospace = 1
+       self.softspace = 0
+
+    def add_hor_rule(self, *args, **kw):
+       if not self.hard_break:
+           self.writer.send_line_break()
+       apply(self.writer.send_hor_rule, args, kw)
+       self.hard_break = self.nospace = 1
+       self.have_label = self.para_end = self.softspace = self.parskip = 0
+
+    def add_label_data(self, format, counter, blankline = None):
+       if self.have_label or not self.hard_break:
+           self.writer.send_line_break()
+       if not self.para_end:
+           self.writer.send_paragraph((blankline and 1) or 0)
+       if type(format) is StringType:
+           self.writer.send_label_data(self.format_counter(format, counter))
+       else:
+           self.writer.send_label_data(format)
+       self.nospace = self.have_label = self.hard_break = self.para_end = 1
+       self.softspace = self.parskip = 0
+
+    def format_counter(self, format, counter):
+        label = ''
+        for c in format:
+            try:
+                if c == '1':
+                   label = label + ('%d' % counter)
+                elif c in 'aA':
+                   if counter > 0:
+                       label = label + self.format_letter(c, counter)
+                elif c in 'iI':
+                   if counter > 0:
+                       label = label + self.format_roman(c, counter)
+               else:
+                   label = label + c
+            except:
+                label = label + c
+        return label
+
+    def format_letter(self, case, counter):
+       label = ''
+       while counter > 0:
+           counter, x = divmod(counter-1, 26)
+           s = chr(ord(case) + x)
+           label = s + label
+       return label
+
+    def format_roman(self, case, counter):
+        ones = ['i', 'x', 'c', 'm']
+        fives = ['v', 'l', 'd']
+        label, index = '', 0
+       # This will die of IndexError when counter is too big
+        while counter > 0:
+            counter, x = divmod(counter, 10)
+            if x == 9:
+                label = ones[index] + ones[index+1] + label
+            elif x == 4:
+                label = ones[index] + fives[index] + label
+            else:
+                if x >= 5:
+                    s = fives[index]
+                    x = x-5
+                else:
+                    s = ''
+                s = s + ones[index]*x
+               label = s + label
+            index = index + 1
+        if case == 'I':
+           return string.upper(label)
+        return label
+
+    def add_flowing_data(self, data,
+                        # These are only here to load them into locals:
+                        whitespace = string.whitespace,
+                        join = string.join, split = string.split):
+       if not data: return
+       # The following looks a bit convoluted but is a great improvement over
+       # data = regsub.gsub('[' + string.whitespace + ']+', ' ', data)
+       prespace = data[:1] in whitespace
+       postspace = data[-1:] in whitespace
+       data = join(split(data))
+       if self.nospace and not data:
+           return
+       elif prespace or self.softspace:
+           if not data:
+               if not self.nospace:
+                   self.softspace = 1
+                   self.parskip = 0
+               return
+           if not self.nospace:
+               data = ' ' + data
+       self.hard_break = self.nospace = self.para_end = \
+                         self.parskip = self.have_label = 0
+       self.softspace = postspace
+       self.writer.send_flowing_data(data)
+
+    def add_literal_data(self, data):
+       if not data: return
+       #  Caller is expected to cause flush_softspace() if needed.
+       self.hard_break = data[-1:] == '\n'
+       self.nospace = self.para_end = self.softspace = \
+                      self.parskip = self.have_label = 0
+       self.writer.send_literal_data(data)
+
+    def flush_softspace(self):
+       if self.softspace:
+           self.hard_break = self.nospace = self.para_end = self.parskip = \
+                             self.have_label = self.softspace = 0
+           self.writer.send_flowing_data(' ')
+
+    def push_alignment(self, align):
+       if align and align != self.align:
+           self.writer.new_alignment(align)
+           self.align = align
+           self.align_stack.append(align)
+       else:
+           self.align_stack.append(self.align)
+
+    def pop_alignment(self):
+       if self.align_stack:
+           del self.align_stack[-1]
+       if self.align_stack:
+           self.align = align = self.align_stack[-1]
+           self.writer.new_alignment(align)
+       else:
+           self.align = None
+           self.writer.new_alignment(None)
+
+    def push_font(self, (size, i, b, tt)):
+       if self.softspace:
+           self.hard_break = self.nospace = self.para_end = self.softspace = 0
+           self.writer.send_flowing_data(' ')
+       if self.font_stack:
+           csize, ci, cb, ctt = self.font_stack[-1]
+           if size is AS_IS: size = csize
+           if i is AS_IS: i = ci
+           if b is AS_IS: b = cb
+           if tt is AS_IS: tt = ctt
+       font = (size, i, b, tt)
+       self.font_stack.append(font)
+       self.writer.new_font(font)
+
+    def pop_font(self):
+       if self.softspace:
+           self.hard_break = self.nospace = self.para_end = self.softspace = 0
+           self.writer.send_flowing_data(' ')
+       if self.font_stack:
+           del self.font_stack[-1]
+       if self.font_stack:
+           font = self.font_stack[-1]
+       else:
+           font = None
+       self.writer.new_font(font)
+
+    def push_margin(self, margin):
+       self.margin_stack.append(margin)
+       fstack = filter(None, self.margin_stack)
+       if not margin and fstack:
+           margin = fstack[-1]
+       self.writer.new_margin(margin, len(fstack))
+
+    def pop_margin(self):
+       if self.margin_stack:
+           del self.margin_stack[-1]
+       fstack = filter(None, self.margin_stack)
+       if fstack:
+           margin = fstack[-1]
+       else:
+           margin = None
+       self.writer.new_margin(margin, len(fstack))
+
+    def set_spacing(self, spacing):
+       self.spacing = spacing
+       self.writer.new_spacing(spacing)
+
+    def push_style(self, *styles):
+       if self.softspace:
+           self.hard_break = self.nospace = self.para_end = self.softspace = 0
+           self.writer.send_flowing_data(' ')
+       for style in styles:
+           self.style_stack.append(style)
+       self.writer.new_styles(tuple(self.style_stack))
+
+    def pop_style(self, n=1):
+       if self.softspace:
+           self.hard_break = self.nospace = self.para_end = self.softspace = 0
+           self.writer.send_flowing_data(' ')
+       del self.style_stack[-n:]
+       self.writer.new_styles(tuple(self.style_stack))
+
+    def assert_line_data(self, flag=1):
+       self.nospace = self.hard_break = not flag
+       self.para_end = self.have_label = 0
+
+
+class NullWriter:
+    """Minimal writer interface to use in testing.
+    """
+    def __init__(self): pass
+    def new_alignment(self, align): pass
+    def new_font(self, font): pass
+    def new_margin(self, margin, level): pass
+    def new_spacing(self, spacing): pass
+    def new_styles(self, styles): pass
+    def send_paragraph(self, blankline): pass
+    def send_line_break(self): pass
+    def send_hor_rule(self, *args, **kw): pass
+    def send_label_data(self, data): pass
+    def send_flowing_data(self, data): pass
+    def send_literal_data(self, data): pass
+
+
+class AbstractWriter(NullWriter):
+
+    def __init__(self):
+       pass
+
+    def new_alignment(self, align):
+       print "new_alignment(%s)" % `align`
+
+    def new_font(self, font):
+       print "new_font(%s)" % `font`
+
+    def new_margin(self, margin, level):
+       print "new_margin(%s, %d)" % (`margin`, level)
+
+    def new_spacing(self, spacing):
+       print "new_spacing(%s)" % `spacing`
+
+    def new_styles(self, styles):
+       print "new_styles(%s)" % `styles`
+
+    def send_paragraph(self, blankline):
+       print "send_paragraph(%s)" % `blankline`
+
+    def send_line_break(self):
+       print "send_line_break()"
+
+    def send_hor_rule(self, *args, **kw):
+       print "send_hor_rule()"
+
+    def send_label_data(self, data):
+       print "send_label_data(%s)" % `data`
+
+    def send_flowing_data(self, data):
+       print "send_flowing_data(%s)" % `data`
+
+    def send_literal_data(self, data):
+       print "send_literal_data(%s)" % `data`
+
+
+class DumbWriter(NullWriter):
+
+    def __init__(self, file=None, maxcol=72):
+       self.file = file or sys.stdout
+       self.maxcol = maxcol
+       NullWriter.__init__(self)
+       self.reset()
+
+    def reset(self):
+       self.col = 0
+       self.atbreak = 0
+
+    def send_paragraph(self, blankline):
+       self.file.write('\n' + '\n'*blankline)
+       self.col = 0
+       self.atbreak = 0
+
+    def send_line_break(self):
+       self.file.write('\n')
+       self.col = 0
+       self.atbreak = 0
+
+    def send_hor_rule(self, *args, **kw):
+       self.file.write('\n')
+       self.file.write('-'*self.maxcol)
+       self.file.write('\n')
+       self.col = 0
+       self.atbreak = 0
+
+    def send_literal_data(self, data):
+       self.file.write(data)
+       i = string.rfind(data, '\n')
+       if i >= 0:
+           self.col = 0
+           data = data[i+1:]
+       data = string.expandtabs(data)
+       self.col = self.col + len(data)
+       self.atbreak = 0
+
+    def send_flowing_data(self, data):
+       if not data: return
+       atbreak = self.atbreak or data[0] in string.whitespace
+       col = self.col
+       maxcol = self.maxcol
+       write = self.file.write
+       for word in string.split(data):
+           if atbreak:
+               if col + len(word) >= maxcol:
+                   write('\n')
+                   col = 0
+               else:
+                   write(' ')
+                   col = col + 1
+           write(word)
+           col = col + len(word)
+           atbreak = 1
+       self.col = col
+       self.atbreak = data[-1] in string.whitespace
+
+
+def test(file = None):
+    w = DumbWriter()
+    f = AbstractFormatter(w)
+    if file:
+       fp = open(file)
+    elif sys.argv[1:]:
+       fp = open(sys.argv[1])
+    else:
+       fp = sys.stdin
+    while 1:
+       line = fp.readline()
+       if not line:
+           break
+       if line == '\n':
+           f.end_paragraph(1)
+       else:
+           f.add_flowing_data(line)
+    f.end_paragraph(0)
+
+
+if __name__ == '__main__':
+    test()
diff --git a/Lib/dos-8x3/gopherli.py b/Lib/dos-8x3/gopherli.py
new file mode 100755 (executable)
index 0000000..cf06e95
--- /dev/null
@@ -0,0 +1,191 @@
+# Gopher protocol client interface
+
+import string
+
+# Default selector, host and port
+DEF_SELECTOR = '1/'
+DEF_HOST     = 'gopher.micro.umn.edu'
+DEF_PORT     = 70
+
+# Recognized file types
+A_TEXT       = '0'
+A_MENU       = '1'
+A_CSO        = '2'
+A_ERROR      = '3'
+A_MACBINHEX  = '4'
+A_PCBINHEX   = '5'
+A_UUENCODED  = '6'
+A_INDEX      = '7'
+A_TELNET     = '8'
+A_BINARY     = '9'
+A_DUPLICATE  = '+'
+A_SOUND      = 's'
+A_EVENT      = 'e'
+A_CALENDAR   = 'c'
+A_HTML       = 'h'
+A_TN3270     = 'T'
+A_MIME       = 'M'
+A_IMAGE      = 'I'
+A_WHOIS      = 'w'
+A_QUERY      = 'q'
+A_GIF        = 'g'
+A_HTML       = 'h'                     # HTML file
+A_WWW        = 'w'                     # WWW address
+A_PLUS_IMAGE = ':'
+A_PLUS_MOVIE = ';'
+A_PLUS_SOUND = '<'
+
+
+# Function mapping all file types to strings; unknown types become TYPE='x'
+_names = dir()
+_type_to_name_map = None
+def type_to_name(gtype):
+       global _type_to_name_map
+       if not _type_to_name_map:
+               for name in _names:
+                       if name[:2] == 'A_':
+                               _type_to_name_map[eval(name)] = name[2:]
+       if _type_to_name_map.has_key(gtype):
+               return _type_to_name_map[gtype]
+       return 'TYPE=' + `gtype`
+
+# Names for characters and strings
+CRLF = '\r\n'
+TAB = '\t'
+
+# Send a selector to a given host and port, return a file with the reply
+def send_selector(selector, host, port = 0):
+       import socket
+       import string
+       if not port:
+               i = string.find(host, ':')
+               if i >= 0:
+                       host, port = host[:i], string.atoi(host[i+1:])
+       if not port:
+               port = DEF_PORT
+       elif type(port) == type(''):
+               port = string.atoi(port)
+       s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+       s.connect(host, port)
+       s.send(selector + CRLF)
+       s.shutdown(1)
+       return s.makefile('rb')
+
+# Send a selector and a query string
+def send_query(selector, query, host, port = 0):
+       return send_selector(selector + '\t' + query, host, port)
+
+# The following functions interpret the data returned by the gopher
+# server according to the expected type, e.g. textfile or directory
+
+# Get a directory in the form of a list of entries
+def get_directory(f):
+       import string
+       list = []
+       while 1:
+               line = f.readline()
+               if not line:
+                       print '(Unexpected EOF from server)'
+                       break
+               if line[-2:] == CRLF:
+                       line = line[:-2]
+               elif line[-1:] in CRLF:
+                       line = line[:-1]
+               if line == '.':
+                       break
+               if not line:
+                       print '(Empty line from server)'
+                       continue
+               gtype = line[0]
+               parts = string.splitfields(line[1:], TAB)
+               if len(parts) < 4:
+                       print '(Bad line from server:', `line`, ')'
+                       continue
+               if len(parts) > 4:
+                       if parts[4:] != ['+']:
+                           print '(Extra info from server:', parts[4:], ')'
+               else:
+                       parts.append('')
+               parts.insert(0, gtype)
+               list.append(parts)
+       return list
+
+# Get a text file as a list of lines, with trailing CRLF stripped
+def get_textfile(f):
+       list = []
+       get_alt_textfile(f, list.append)
+       return list
+
+# Get a text file and pass each line to a function, with trailing CRLF stripped
+def get_alt_textfile(f, func):
+       while 1:
+               line = f.readline()
+               if not line:
+                       print '(Unexpected EOF from server)'
+                       break
+               if line[-2:] == CRLF:
+                       line = line[:-2]
+               elif line[-1:] in CRLF:
+                       line = line[:-1]
+               if line == '.':
+                       break
+               if line[:2] == '..':
+                       line = line[1:]
+               func(line)
+
+# Get a binary file as one solid data block
+def get_binary(f):
+       data = f.read()
+       return data
+
+# Get a binary file and pass each block to a function
+def get_alt_binary(f, func, blocksize):
+       while 1:
+               data = f.read(blocksize)
+               if not data:
+                       break
+               func(data)
+
+# Trivial test program
+def test():
+       import sys
+       import getopt
+       opts, args = getopt.getopt(sys.argv[1:], '')
+       selector = DEF_SELECTOR
+       type = selector[0]
+       host = DEF_HOST
+       port = DEF_PORT
+       if args:
+               host = args[0]
+               args = args[1:]
+       if args:
+               type = args[0]
+               args = args[1:]
+               if len(type) > 1:
+                       type, selector = type[0], type
+               else:
+                       selector = ''
+                       if args:
+                               selector = args[0]
+                               args = args[1:]
+               query = ''
+               if args:
+                       query = args[0]
+                       args = args[1:]
+       if type == A_INDEX:
+               f = send_query(selector, query, host)
+       else:
+               f = send_selector(selector, host)
+       if type == A_TEXT:
+               list = get_textfile(f)
+               for item in list: print item
+       elif type in (A_MENU, A_INDEX):
+               list = get_directory(f)
+               for item in list: print item
+       else:
+               data = get_binary(f)
+               print 'binary data:', len(data), 'bytes:', `data[:100]`[:40]
+
+# Run the test when run as script
+if __name__ == '__main__':
+       test()
diff --git a/Lib/dos-8x3/htmlenti.py b/Lib/dos-8x3/htmlenti.py
new file mode 100755 (executable)
index 0000000..55aefaa
--- /dev/null
@@ -0,0 +1,105 @@
+# Proposed entity definitions for HTML, taken from
+# http://www.w3.org/hypertext/WWW/MarkUp/html-spec/html-spec_14.html
+
+entitydefs = {
+    'lt':       '<',
+    'gt':       '>',
+    'amp':      '&',
+    'quot':    '"',
+    'nbsp':    chr(160),       # no-break space
+    'iexcl':   chr(161),       # inverted exclamation mark
+    'cent':    chr(162),       # cent sign
+    'pound':   chr(163),       # pound sterling sign
+    'curren':  chr(164),       # general currency sign
+    'yen':     chr(165),       # yen sign
+    'brvbar':  chr(166),       # broken (vertical) bar
+    'sect':    chr(167),       # section sign
+    'uml':     chr(168),       # umlaut (dieresis)
+    'copy':    chr(169),       # copyright sign
+    'ordf':    chr(170),       # ordinal indicator, feminine
+    'laquo':   chr(171),       # angle quotation mark, left
+    'not':     chr(172),       # not sign
+    'shy':     chr(173),       # soft hyphen
+    'reg':     chr(174),       # registered sign
+    'macr':    chr(175),       # macron
+    'deg':     chr(176),       # degree sign
+    'plusmn':  chr(177),       # plus-or-minus sign
+    'sup2':    chr(178),       # superscript two
+    'sup3':    chr(179),       # superscript three
+    'acute':   chr(180),       # acute accent
+    'micro':   chr(181),       # micro sign
+    'para':    chr(182),       # pilcrow (paragraph sign)
+    'middot':  chr(183),       # middle dot
+    'cedil':   chr(184),       # cedilla
+    'sup1':    chr(185),       # superscript one
+    'ordm':    chr(186),       # ordinal indicator, masculine
+    'raquo':   chr(187),       # angle quotation mark, right
+    'frac14':  chr(188),       # fraction one-quarter
+    'frac12':  chr(189),       # fraction one-half
+    'frac34':  chr(190),       # fraction three-quarters
+    'iquest':  chr(191),       # inverted question mark
+    'Agrave':  chr(192),       # capital A, grave accent
+    'Aacute':  chr(193),       # capital A, acute accent
+    'Acirc':   chr(194),       # capital A, circumflex accent
+    'Atilde':  chr(195),       # capital A, tilde
+    'Auml':    chr(196),       # capital A, dieresis or umlaut mark
+    'Aring':   chr(197),       # capital A, ring
+    'AElig':   chr(198),       # capital AE diphthong (ligature)
+    'Ccedil':  chr(199),       # capital C, cedilla
+    'Egrave':  chr(200),       # capital E, grave accent
+    'Eacute':  chr(201),       # capital E, acute accent
+    'Ecirc':   chr(202),       # capital E, circumflex accent
+    'Euml':    chr(203),       # capital E, dieresis or umlaut mark
+    'Igrave':  chr(204),       # capital I, grave accent
+    'Iacute':  chr(205),       # capital I, acute accent
+    'Icirc':   chr(206),       # capital I, circumflex accent
+    'Iuml':    chr(207),       # capital I, dieresis or umlaut mark
+    'ETH':     chr(208),       # capital Eth, Icelandic
+    'Ntilde':  chr(209),       # capital N, tilde
+    'Ograve':  chr(210),       # capital O, grave accent
+    'Oacute':  chr(211),       # capital O, acute accent
+    'Ocirc':   chr(212),       # capital O, circumflex accent
+    'Otilde':  chr(213),       # capital O, tilde
+    'Ouml':    chr(214),       # capital O, dieresis or umlaut mark
+    'times':   chr(215),       # multiply sign
+    'Oslash':  chr(216),       # capital O, slash
+    'Ugrave':  chr(217),       # capital U, grave accent
+    'Uacute':  chr(218),       # capital U, acute accent
+    'Ucirc':   chr(219),       # capital U, circumflex accent
+    'Uuml':    chr(220),       # capital U, dieresis or umlaut mark
+    'Yacute':  chr(221),       # capital Y, acute accent
+    'THORN':   chr(222),       # capital THORN, Icelandic
+    'szlig':   chr(223),       # small sharp s, German (sz ligature)
+    'agrave':  chr(224),       # small a, grave accent
+    'aacute':  chr(225),       # small a, acute accent
+    'acirc':   chr(226),       # small a, circumflex accent
+    'atilde':  chr(227),       # small a, tilde
+    'auml':    chr(228),       # small a, dieresis or umlaut mark
+    'aring':   chr(229),       # small a, ring
+    'aelig':   chr(230),       # small ae diphthong (ligature)
+    'ccedil':  chr(231),       # small c, cedilla
+    'egrave':  chr(232),       # small e, grave accent
+    'eacute':  chr(233),       # small e, acute accent
+    'ecirc':   chr(234),       # small e, circumflex accent
+    'euml':    chr(235),       # small e, dieresis or umlaut mark
+    'igrave':  chr(236),       # small i, grave accent
+    'iacute':  chr(237),       # small i, acute accent
+    'icirc':   chr(238),       # small i, circumflex accent
+    'iuml':    chr(239),       # small i, dieresis or umlaut mark
+    'eth':     chr(240),       # small eth, Icelandic
+    'ntilde':  chr(241),       # small n, tilde
+    'ograve':  chr(242),       # small o, grave accent
+    'oacute':  chr(243),       # small o, acute accent
+    'ocirc':   chr(244),       # small o, circumflex accent
+    'otilde':  chr(245),       # small o, tilde
+    'ouml':    chr(246),       # small o, dieresis or umlaut mark
+    'divide':  chr(247),       # divide sign
+    'oslash':  chr(248),       # small o, slash
+    'ugrave':  chr(249),       # small u, grave accent
+    'uacute':  chr(250),       # small u, acute accent
+    'ucirc':   chr(251),       # small u, circumflex accent
+    'uuml':    chr(252),       # small u, dieresis or umlaut mark
+    'yacute':  chr(253),       # small y, acute accent
+    'thorn':   chr(254),       # small thorn, Icelandic
+    'yuml':    chr(255),       # small y, dieresis or umlaut mark
+}
diff --git a/Lib/dos-8x3/importal.py b/Lib/dos-8x3/importal.py
new file mode 100755 (executable)
index 0000000..780862c
--- /dev/null
@@ -0,0 +1,36 @@
+# THIS IS OBSOLETE -- USE MODULE 'compileall' INSTEAD!
+
+# Utility module to import all modules in the path, in the hope
+# that this will update their ".pyc" files.
+
+import os
+import sys
+
+# Sabotage 'gl' and 'stdwin' to prevent windows popping up...
+for m in 'gl', 'stdwin', 'fl', 'fm':
+       sys.modules[m] = sys
+
+exceptions = ['importall']
+
+for dir in sys.path:
+       print 'Listing', dir
+       try:
+               names = os.listdir(dir)
+       except os.error:
+               print 'Can\'t list', dir
+               names = []
+       names.sort()
+       for name in names:
+               head, tail = name[:-3], name[-3:]
+               if tail == '.py' and head not in exceptions:
+                       s = 'import ' + head
+                       print s
+                       try:
+                               exec s + '\n'
+                       except KeyboardInterrupt:
+                               del names[:]
+                               print '\n[interrupt]'
+                               break
+                       except:
+                               print 'Sorry:', sys.exc_type + ':',
+                               print sys.exc_value
diff --git a/Lib/dos-8x3/linecach.py b/Lib/dos-8x3/linecach.py
new file mode 100755 (executable)
index 0000000..7de373f
--- /dev/null
@@ -0,0 +1,90 @@
+# Cache lines from files.
+# This is intended to read lines from modules imported -- hence if a filename
+# is not found, it will look down the module search path for a file by
+# that name.
+
+import sys
+import os
+from stat import *
+
+def getline(filename, lineno):
+       lines = getlines(filename)
+       if 1 <= lineno <= len(lines):
+               return lines[lineno-1]
+       else:
+               return ''
+
+
+# The cache
+
+cache = {} # The cache
+
+
+# Clear the cache entirely
+
+def clearcache():
+       global cache
+       cache = {}
+
+
+# Get the lines for a file from the cache.
+# Update the cache if it doesn't contain an entry for this file already.
+
+def getlines(filename):
+       if cache.has_key(filename):
+               return cache[filename][2]
+       else:
+               return updatecache(filename)
+
+
+# Discard cache entries that are out of date.
+# (This is not checked upon each call!)
+
+def checkcache():
+       for filename in cache.keys():
+               size, mtime, lines, fullname = cache[filename]
+               try:
+                       stat = os.stat(fullname)
+               except os.error:
+                       del cache[filename]
+                       continue
+               if size <> stat[ST_SIZE] or mtime <> stat[ST_MTIME]:
+                       del cache[filename]
+
+
+# Update a cache entry and return its list of lines.
+# If something's wrong, print a message, discard the cache entry,
+# and return an empty list.
+
+def updatecache(filename):
+       if cache.has_key(filename):
+               del cache[filename]
+       if not filename or filename[0] + filename[-1] == '<>':
+               return []
+       fullname = filename
+       try:
+               stat = os.stat(fullname)
+       except os.error, msg:
+               # Try looking through the module search path
+               basename = os.path.split(filename)[1]
+               for dirname in sys.path:
+                       fullname = os.path.join(dirname, basename)
+                       try:
+                               stat = os.stat(fullname)
+                               break
+                       except os.error:
+                               pass
+               else:
+                       # No luck
+##                     print '*** Cannot stat', filename, ':', msg
+                       return []
+       try:
+               fp = open(fullname, 'r')
+               lines = fp.readlines()
+               fp.close()
+       except IOError, msg:
+##             print '*** Cannot open', fullname, ':', msg
+               return []
+       size, mtime = stat[ST_SIZE], stat[ST_MTIME]
+       cache[filename] = size, mtime, lines, fullname
+       return lines
diff --git a/Lib/dos-8x3/macurl2p.py b/Lib/dos-8x3/macurl2p.py
new file mode 100755 (executable)
index 0000000..65afe69
--- /dev/null
@@ -0,0 +1,76 @@
+"""Mac specific module for conversion between pathnames and URLs."""
+
+import string
+import urllib
+import os
+
+def url2pathname(pathname):
+    "Convert /-delimited pathname to mac pathname"
+    #
+    # XXXX The .. handling should be fixed...
+    #
+    tp = urllib.splittype(pathname)[0]
+    if tp and tp <> 'file':
+       raise RuntimeError, 'Cannot convert non-local URL to pathname'
+    components = string.split(pathname, '/')
+    # Remove . and embedded ..
+    i = 0
+    while i < len(components):
+       if components[i] == '.':
+           del components[i]
+       elif components[i] == '..' and i > 0 and \
+                                 components[i-1] not in ('', '..'):
+           del components[i-1:i+1]
+           i = i-1
+       elif components[i] == '' and i > 0 and components[i-1] <> '':
+           del components[i]
+       else:
+           i = i+1
+    if not components[0]:
+       # Absolute unix path, don't start with colon
+       return string.join(components[1:], ':')
+    else:
+       # relative unix path, start with colon. First replace
+       # leading .. by empty strings (giving ::file)
+       i = 0
+       while i < len(components) and components[i] == '..':
+           components[i] = ''
+           i = i + 1
+       return ':' + string.join(components, ':')
+
+def pathname2url(pathname):
+    "convert mac pathname to /-delimited pathname"
+    if '/' in pathname:
+       raise RuntimeError, "Cannot convert pathname containing slashes"
+    components = string.split(pathname, ':')
+    # Replace empty string ('::') by .. (will result in '/../' later)
+    for i in range(1, len(components)):
+       if components[i] == '':
+           components[i] = '..'
+    # Truncate names longer than 31 bytes
+    components = map(lambda x: x[:31], components)
+
+    if os.path.isabs(pathname):
+       return '/' + string.join(components, '/')
+    else:
+       return string.join(components, '/')
+
+def test():
+    for url in ["index.html",
+               "bar/index.html",
+               "/foo/bar/index.html",
+               "/foo/bar/",
+               "/"]:
+       print `url`, '->', `url2pathname(url)`
+    for path in ["drive:",
+                "drive:dir:",
+                "drive:dir:file",
+                "drive:file",
+                "file",
+                ":file",
+                ":dir:",
+                ":dir:file"]:
+       print `path`, '->', `pathname2url(path)`
+
+if __name__ == '__main__':
+    test()
diff --git a/Lib/dos-8x3/mimetool.py b/Lib/dos-8x3/mimetool.py
new file mode 100755 (executable)
index 0000000..da33a77
--- /dev/null
@@ -0,0 +1,187 @@
+# Various tools used by MIME-reading or MIME-writing programs.
+
+
+import os
+import rfc822
+import string
+import tempfile
+
+
+# A derived class of rfc822.Message that knows about MIME headers and
+# contains some hooks for decoding encoded and multipart messages.
+
+class Message(rfc822.Message):
+
+       def __init__(self, fp, seekable = 1):
+               rfc822.Message.__init__(self, fp, seekable)
+               self.encodingheader = \
+                       self.getheader('content-transfer-encoding')
+               self.typeheader = \
+                       self.getheader('content-type')
+               self.parsetype()
+               self.parseplist()
+
+       def parsetype(self):
+               str = self.typeheader
+               if str == None:
+                       str = 'text/plain'
+               if ';' in str:
+                       i = string.index(str, ';')
+                       self.plisttext = str[i:]
+                       str = str[:i]
+               else:
+                       self.plisttext = ''
+               fields = string.splitfields(str, '/')
+               for i in range(len(fields)):
+                       fields[i] = string.lower(string.strip(fields[i]))
+               self.type = string.joinfields(fields, '/')
+               self.maintype = fields[0]
+               self.subtype = string.joinfields(fields[1:], '/')
+
+       def parseplist(self):
+               str = self.plisttext
+               self.plist = []
+               while str[:1] == ';':
+                       str = str[1:]
+                       if ';' in str:
+                               # XXX Should parse quotes!
+                               end = string.index(str, ';')
+                       else:
+                               end = len(str)
+                       f = str[:end]
+                       if '=' in f:
+                               i = string.index(f, '=')
+                               f = string.lower(string.strip(f[:i])) + \
+                                       '=' + string.strip(f[i+1:])
+                       self.plist.append(string.strip(f))
+                       str = str[end:]
+
+       def getplist(self):
+               return self.plist
+
+       def getparam(self, name):
+               name = string.lower(name) + '='
+               n = len(name)
+               for p in self.plist:
+                       if p[:n] == name:
+                               return rfc822.unquote(p[n:])
+               return None
+
+       def getencoding(self):
+               if self.encodingheader == None:
+                       return '7bit'
+               return string.lower(self.encodingheader)
+
+       def gettype(self):
+               return self.type
+
+       def getmaintype(self):
+               return self.maintype
+
+       def getsubtype(self):
+               return self.subtype
+
+
+
+
+# Utility functions
+# -----------------
+
+
+# Return a random string usable as a multipart boundary.
+# The method used is so that it is *very* unlikely that the same
+# string of characters will every occur again in the Universe,
+# so the caller needn't check the data it is packing for the
+# occurrence of the boundary.
+#
+# The boundary contains dots so you have to quote it in the header.
+
+_prefix = None
+
+def choose_boundary():
+       global _prefix
+       import time
+       import rand
+       if _prefix == None:
+               import socket
+               import os
+               hostid = socket.gethostbyname(socket.gethostname())
+               uid = `os.getuid()`
+               pid = `os.getpid()`
+               seed = `rand.rand()`
+               _prefix = hostid + '.' + uid + '.' + pid
+       timestamp = `int(time.time())`
+       seed = `rand.rand()`
+       return _prefix + '.' + timestamp + '.' + seed
+
+
+# Subroutines for decoding some common content-transfer-types
+
+# XXX This requires that uudecode and mmencode are in $PATH
+
+def decode(input, output, encoding):
+       if decodetab.has_key(encoding):
+               pipethrough(input, decodetab[encoding], output)
+       else:
+               raise ValueError, \
+                     'unknown Content-Transfer-Encoding: %s' % encoding
+
+def encode(input, output, encoding):
+       if encodetab.has_key(encoding):
+               pipethrough(input, encodetab[encoding], output)
+       else:
+               raise ValueError, \
+                     'unknown Content-Transfer-Encoding: %s' % encoding
+
+uudecode_pipe = '''(
+TEMP=/tmp/@uu.$$
+sed "s%^begin [0-7][0-7]* .*%begin 600 $TEMP%" | uudecode
+cat $TEMP
+rm $TEMP
+)'''
+
+decodetab = {
+       'uuencode':             uudecode_pipe,
+       'x-uuencode':           uudecode_pipe,
+       'quoted-printable':     'mmencode -u -q',
+       'base64':               'mmencode -u -b',
+}
+
+encodetab = {
+       'x-uuencode':           'uuencode tempfile',
+       'uuencode':             'uuencode tempfile',
+       'quoted-printable':     'mmencode -q',
+       'base64':               'mmencode -b',
+}
+
+def pipeto(input, command):
+       pipe = os.popen(command, 'w')
+       copyliteral(input, pipe)
+       pipe.close()
+
+def pipethrough(input, command, output):
+       tempname = tempfile.mktemp()
+       try:
+               temp = open(tempname, 'w')
+       except IOError:
+               print '*** Cannot create temp file', `tempname`
+               return
+       copyliteral(input, temp)
+       temp.close()
+       pipe = os.popen(command + ' <' + tempname, 'r')
+       copybinary(pipe, output)
+       pipe.close()
+       os.unlink(tempname)
+
+def copyliteral(input, output):
+       while 1:
+               line = input.readline()
+               if not line: break
+               output.write(line)
+
+def copybinary(input, output):
+       BUFSIZE = 8192
+       while 1:
+               line = input.read(BUFSIZE)
+               if not line: break
+               output.write(line)
diff --git a/Lib/dos-8x3/multifil.py b/Lib/dos-8x3/multifil.py
new file mode 100755 (executable)
index 0000000..71e0dd0
--- /dev/null
@@ -0,0 +1,128 @@
+# A class that makes each part of a multipart message "feel" like an
+# ordinary file, as long as you use fp.readline().  Allows recursive
+# use, for nested multipart messages.  Probably best used together
+# with module mimetools.
+#
+# Suggested use:
+#
+# real_fp = open(...)
+# fp = MultiFile(real_fp)
+#
+# "read some lines from fp"
+# fp.push(separator)
+# while 1:
+#      "read lines from fp until it returns an empty string" (A)
+#      if not fp.next(): break
+# fp.pop()
+# "read remaining lines from fp until it returns an empty string"
+#
+# The latter sequence may be used recursively at (A).
+# It is also allowed to use multiple push()...pop() sequences.
+# Note that if a nested multipart message is terminated by a separator
+# for an outer message, this is not reported, even though it is really
+# illegal input.
+
+import sys
+import string
+
+err = sys.stderr.write
+
+Error = 'multifile.Error'
+
+class MultiFile:
+       #
+       def __init__(self, fp):
+               self.fp = fp
+               self.stack = [] # Grows down
+               self.level = 0
+               self.last = 0
+               self.start = self.fp.tell()
+               self.posstack = [] # Grows down
+       #
+       def tell(self):
+               if self.level > 0:
+                       return self.lastpos
+               return self.fp.tell() - self.start
+       #
+       def seek(self, pos):
+               if not 0 <= pos <= self.tell() or \
+                               self.level > 0 and pos > self.lastpos:
+                       raise Error, 'bad MultiFile.seek() call'
+               self.fp.seek(pos + self.start)
+               self.level = 0
+               self.last = 0
+       #
+       def readline(self):
+               if self.level > 0: return ''
+               line = self.fp.readline()
+               if not line:
+                       self.level = len(self.stack)
+                       self.last = (self.level > 0)
+                       if self.last:
+                               err('*** Sudden EOF in MultiFile.readline()\n')
+                       return ''
+               if line[:2] <> '--': return line
+               n = len(line)
+               k = n
+               while k > 0 and line[k-1] in string.whitespace: k = k-1
+               mark = line[2:k]
+               if mark[-2:] == '--': mark1 = mark[:-2]
+               else: mark1 = None
+               for i in range(len(self.stack)):
+                       sep = self.stack[i]
+                       if sep == mark:
+                               self.last = 0
+                               break
+                       elif mark1 <> None and sep == mark1:
+                               self.last = 1
+                               break
+               else:
+                       return line
+               # Get here after break out of loop
+               self.lastpos = self.tell() - len(line)
+               self.level = i+1
+               if self.level > 1:
+                       err('*** Missing endmarker in MultiFile.readline()\n')
+               return ''
+       #
+       def readlines(self):
+               list = []
+               while 1:
+                       line = self.readline()
+                       if not line: break
+                       list.append(line)
+               return list
+       #
+       def read(self): # Note: no size argument -- read until EOF only!
+               return string.joinfields(self.readlines(), '')
+       #
+       def next(self):
+               while self.readline(): pass
+               if self.level > 1 or self.last:
+                       return 0
+               self.level = 0
+               self.last = 0
+               self.start = self.fp.tell()
+               return 1
+       #
+       def push(self, sep):
+               if self.level > 0:
+                       raise Error, 'bad MultiFile.push() call'
+               self.stack.insert(0, sep)
+               self.posstack.insert(0, self.start)
+               self.start = self.fp.tell()
+       #
+       def pop(self):
+               if self.stack == []:
+                       raise Error, 'bad MultiFile.pop() call'
+               if self.level <= 1:
+                       self.last = 0
+               else:
+                       abslastpos = self.lastpos + self.start
+               self.level = max(0, self.level - 1)
+               del self.stack[0]
+               self.start = self.posstack[0]
+               del self.posstack[0]
+               if self.level > 0:
+                       self.lastpos = abslastpos - self.start
+       #
diff --git a/Lib/dos-8x3/nturl2pa.py b/Lib/dos-8x3/nturl2pa.py
new file mode 100755 (executable)
index 0000000..17f01f5
--- /dev/null
@@ -0,0 +1,52 @@
+#
+# nturl2path convert a NT pathname to a file URL and 
+# vice versa  
+
+def url2pathname(url):
+       """ Convert a URL to a DOS path...
+       Currently only works for absolute paths
+
+               ///C|/foo/bar/spam.foo
+
+                       becomes
+
+               C:\foo\bar\spam.foo
+       """
+       import string
+       comp = string.splitfields(url, '|')
+       if len(comp) != 2 or comp[0][-1] not in string.letters:
+               error = 'Bad URL: ' + url
+               raise IOError, error
+       drive = string.upper(comp[0][-1])
+       components = string.splitfields(comp[1], '/')
+       path = drive + ':'
+       for  comp in components:
+               if comp:
+                       path = path + '\\' + comp
+       return path
+
+def pathname2url(p):
+
+       """ Convert a DOS path name to a file url...
+       Currently only works for absolute paths
+
+               C:\foo\bar\spam.foo
+
+                       becomes
+
+               ///C|/foo/bar/spam.foo
+       """
+
+       import string
+       comp = string.splitfields(p, ':')
+       if len(comp) != 2 or len(comp[0]) > 1:
+               error = 'Bad path: ' + p
+               raise IOError, error
+
+       drive = string.upper(comp[0])
+       components = string.splitfields(comp[1], '\\')
+       path = '///' + drive + '|'
+       for comp in components:
+               if comp:
+                       path = path + '/' + comp
+       return path
diff --git a/Lib/dos-8x3/para.py b/Lib/dos-8x3/para.py
new file mode 100755 (executable)
index 0000000..c9b3bdd
--- /dev/null
@@ -0,0 +1,409 @@
+# Text formatting abstractions
+# Note -- this module is obsolete, it's too slow anyway
+
+
+# Oft-used type object
+Int = type(0)
+
+
+# Represent a paragraph.  This is a list of words with associated
+# font and size information, plus indents and justification for the
+# entire paragraph.
+# Once the words have been added to a paragraph, it can be laid out
+# for different line widths.  Once laid out, it can be rendered at
+# different screen locations.  Once rendered, it can be queried
+# for mouse hits, and parts of the text can be highlighted
+class Para:
+       #
+       def __init__(self):
+               self.words = [] # The words
+               self.just = 'l' # Justification: 'l', 'r', 'lr' or 'c'
+               self.indent_left = self.indent_right = self.indent_hang = 0
+               # Final lay-out parameters, may change
+               self.left = self.top = self.right = self.bottom = \
+                       self.width = self.height = self.lines = None
+       #
+       # Add a word, computing size information for it.
+       # Words may also be added manually by appending to self.words
+       # Each word should be a 7-tuple:
+       # (font, text, width, space, stretch, ascent, descent)
+       def addword(self, d, font, text, space, stretch):
+               if font <> None:
+                       d.setfont(font)
+               width = d.textwidth(text)
+               ascent = d.baseline()
+               descent = d.lineheight() - ascent
+               spw = d.textwidth(' ')
+               space = space * spw
+               stretch = stretch * spw
+               tuple = (font, text, width, space, stretch, ascent, descent)
+               self.words.append(tuple)
+       #
+       # Hooks to begin and end anchors -- insert numbers in the word list!
+       def bgn_anchor(self, id):
+               self.words.append(id)
+       #
+       def end_anchor(self, id):
+               self.words.append(0)
+       #
+       # Return the total length (width) of the text added so far, in pixels
+       def getlength(self):
+               total = 0
+               for word in self.words:
+                       if type(word) <> Int:
+                               total = total + word[2] + word[3]
+               return total
+       #
+       # Tab to a given position (relative to the current left indent):
+       # remove all stretch, add fixed space up to the new indent.
+       # If the current position is already beying the tab stop,
+       # don't add any new space (but still remove the stretch)
+       def tabto(self, tab):
+               total = 0
+               as, de = 1, 0
+               for i in range(len(self.words)):
+                       word = self.words[i]
+                       if type(word) == Int: continue
+                       fo, te, wi, sp, st, as, de = word
+                       self.words[i] = fo, te, wi, sp, 0, as, de
+                       total = total + wi + sp
+               if total < tab:
+                       self.words.append(None, '', 0, tab-total, 0, as, de)
+       #
+       # Make a hanging tag: tab to hang, increment indent_left by hang,
+       # and reset indent_hang to -hang
+       def makehangingtag(self, hang):
+               self.tabto(hang)
+               self.indent_left = self.indent_left + hang
+               self.indent_hang = -hang
+       #
+       # Decide where the line breaks will be given some screen width
+       def layout(self, linewidth):
+               self.width = linewidth
+               height = 0
+               self.lines = lines = []
+               avail1 = self.width - self.indent_left - self.indent_right
+               avail = avail1 - self.indent_hang
+               words = self.words
+               i = 0
+               n = len(words)
+               lastfont = None
+               while i < n:
+                       firstfont = lastfont
+                       charcount = 0
+                       width = 0
+                       stretch = 0
+                       ascent = 0
+                       descent = 0
+                       lsp = 0
+                       j = i
+                       while i < n:
+                               word = words[i]
+                               if type(word) == Int:
+                                       if word > 0 and width >= avail:
+                                               break
+                                       i = i+1
+                                       continue
+                               fo, te, wi, sp, st, as, de = word
+                               if width + wi > avail and width > 0 and wi > 0:
+                                       break
+                               if fo <> None:
+                                       lastfont = fo
+                                       if width == 0:
+                                               firstfont = fo
+                               charcount = charcount + len(te) + (sp > 0)
+                               width = width + wi + sp
+                               lsp = sp
+                               stretch = stretch + st
+                               lst = st
+                               ascent = max(ascent, as)
+                               descent = max(descent, de)
+                               i = i+1
+                       while i > j and type(words[i-1]) == Int and \
+                               words[i-1] > 0: i = i-1
+                       width = width - lsp
+                       if i < n:
+                               stretch = stretch - lst
+                       else:
+                               stretch = 0
+                       tuple = i-j, firstfont, charcount, width, stretch, \
+                               ascent, descent
+                       lines.append(tuple)
+                       height = height + ascent + descent
+                       avail = avail1
+               self.height = height
+       #
+       # Call a function for all words in a line
+       def visit(self, wordfunc, anchorfunc):
+               avail1 = self.width - self.indent_left - self.indent_right
+               avail = avail1 - self.indent_hang
+               v = self.top
+               i = 0
+               for tuple in self.lines:
+                       wordcount, firstfont, charcount, width, stretch, \
+                               ascent, descent = tuple
+                       h = self.left + self.indent_left
+                       if i == 0: h = h + self.indent_hang
+                       extra = 0
+                       if self.just == 'r': h = h + avail - width
+                       elif self.just == 'c': h = h + (avail - width) / 2
+                       elif self.just == 'lr' and stretch > 0:
+                               extra = avail - width
+                       v2 = v + ascent + descent
+                       for j in range(i, i+wordcount):
+                               word = self.words[j]
+                               if type(word) == Int:
+                                       ok = anchorfunc(self, tuple, word, \
+                                                       h, v)
+                                       if ok <> None: return ok
+                                       continue
+                               fo, te, wi, sp, st, as, de = word
+                               if extra > 0 and stretch > 0:
+                                       ex = extra * st / stretch
+                                       extra = extra - ex
+                                       stretch = stretch - st
+                               else:
+                                       ex = 0
+                               h2 = h + wi + sp + ex
+                               ok = wordfunc(self, tuple, word, h, v, \
+                                       h2, v2, (j==i), (j==i+wordcount-1))
+                               if ok <> None: return ok
+                               h = h2
+                       v = v2
+                       i = i + wordcount
+                       avail = avail1
+       #
+       # Render a paragraph in "drawing object" d, using the rectangle
+       # given by (left, top, right) with an unspecified bottom.
+       # Return the computed bottom of the text.
+       def render(self, d, left, top, right):
+               if self.width <> right-left:
+                       self.layout(right-left)
+               self.left = left
+               self.top = top
+               self.right = right
+               self.bottom = self.top + self.height
+               self.anchorid = 0
+               try:
+                       self.d = d
+                       self.visit(self.__class__._renderword, \
+                                  self.__class__._renderanchor)
+               finally:
+                       self.d = None
+               return self.bottom
+       #
+       def _renderword(self, tuple, word, h, v, h2, v2, isfirst, islast):
+               if word[0] <> None: self.d.setfont(word[0])
+               baseline = v + tuple[5]
+               self.d.text((h, baseline - word[5]), word[1])
+               if self.anchorid > 0:
+                       self.d.line((h, baseline+2), (h2, baseline+2))
+       #
+       def _renderanchor(self, tuple, word, h, v):
+               self.anchorid = word
+       #
+       # Return which anchor(s) was hit by the mouse
+       def hitcheck(self, mouseh, mousev):
+               self.mouseh = mouseh
+               self.mousev = mousev
+               self.anchorid = 0
+               self.hits = []
+               self.visit(self.__class__._hitcheckword, \
+                          self.__class__._hitcheckanchor)
+               return self.hits
+       #
+       def _hitcheckword(self, tuple, word, h, v, h2, v2, isfirst, islast):
+               if self.anchorid > 0 and h <= self.mouseh <= h2 and \
+                       v <= self.mousev <= v2:
+                       self.hits.append(self.anchorid)
+       #
+       def _hitcheckanchor(self, tuple, word, h, v):
+               self.anchorid = word
+       #
+       # Return whether the given anchor id is present
+       def hasanchor(self, id):
+               return id in self.words or -id in self.words
+       #
+       # Extract the raw text from the word list, substituting one space
+       # for non-empty inter-word space, and terminating with '\n'
+       def extract(self):
+               text = ''
+               for w in self.words:
+                       if type(w) <> Int:
+                               word = w[1]
+                               if w[3]: word = word + ' '
+                               text = text + word
+               return text + '\n'
+       #
+       # Return which character position was hit by the mouse, as
+       # an offset in the entire text as returned by extract().
+       # Return None if the mouse was not in this paragraph
+       def whereis(self, d, mouseh, mousev):
+               if mousev < self.top or mousev > self.bottom:
+                       return None
+               self.mouseh = mouseh
+               self.mousev = mousev
+               self.lastfont = None
+               self.charcount = 0
+               try:
+                       self.d = d
+                       return self.visit(self.__class__._whereisword, \
+                                         self.__class__._whereisanchor)
+               finally:
+                       self.d = None
+       #
+       def _whereisword(self, tuple, word, h1, v1, h2, v2, isfirst, islast):
+               fo, te, wi, sp, st, as, de = word
+               if fo <> None: self.lastfont = fo
+               h = h1
+               if isfirst: h1 = 0
+               if islast: h2 = 999999
+               if not (v1 <= self.mousev <= v2 and h1 <= self.mouseh <= h2):
+                       self.charcount = self.charcount + len(te) + (sp > 0)
+                       return
+               if self.lastfont <> None:
+                       self.d.setfont(self.lastfont)
+               cc = 0
+               for c in te:
+                       cw = self.d.textwidth(c)
+                       if self.mouseh <= h + cw/2:
+                               return self.charcount + cc
+                       cc = cc+1
+                       h = h+cw
+               self.charcount = self.charcount + cc
+               if self.mouseh <= (h+h2) / 2:
+                       return self.charcount
+               else:
+                       return self.charcount + 1
+       #
+       def _whereisanchor(self, tuple, word, h, v):
+               pass
+       #
+       # Return screen position corresponding to position in paragraph.
+       # Return tuple (h, vtop, vbaseline, vbottom).
+       # This is more or less the inverse of whereis()
+       def screenpos(self, d, pos):
+               if pos < 0:
+                       ascent, descent = self.lines[0][5:7]
+                       return self.left, self.top, self.top + ascent, \
+                               self.top + ascent + descent
+               self.pos = pos
+               self.lastfont = None
+               try:
+                       self.d = d
+                       ok = self.visit(self.__class__._screenposword, \
+                                       self.__class__._screenposanchor)
+               finally:
+                       self.d = None
+               if ok == None:
+                       ascent, descent = self.lines[-1][5:7]
+                       ok = self.right, self.bottom - ascent - descent, \
+                               self.bottom - descent, self.bottom
+               return ok
+       #
+       def _screenposword(self, tuple, word, h1, v1, h2, v2, isfirst, islast):
+               fo, te, wi, sp, st, as, de = word
+               if fo <> None: self.lastfont = fo
+               cc = len(te) + (sp > 0)
+               if self.pos > cc:
+                       self.pos = self.pos - cc
+                       return
+               if self.pos < cc:
+                       self.d.setfont(self.lastfont)
+                       h = h1 + self.d.textwidth(te[:self.pos])
+               else:
+                       h = h2
+               ascent, descent = tuple[5:7]
+               return h, v1, v1+ascent, v2
+       #
+       def _screenposanchor(self, tuple, word, h, v):
+               pass
+       #
+       # Invert the stretch of text between pos1 and pos2.
+       # If pos1 is None, the beginning is implied;
+       # if pos2 is None, the end is implied.
+       # Undoes its own effect when called again with the same arguments
+       def invert(self, d, pos1, pos2):
+               if pos1 == None:
+                       pos1 = self.left, self.top, self.top, self.top
+               else:
+                       pos1 = self.screenpos(d, pos1)
+               if pos2 == None:
+                       pos2 = self.right, self.bottom,self.bottom,self.bottom
+               else:
+                       pos2 = self.screenpos(d, pos2)
+               h1, top1, baseline1, bottom1 = pos1
+               h2, top2, baseline2, bottom2 = pos2
+               if bottom1 <= top2:
+                       d.invert((h1, top1), (self.right, bottom1))
+                       h1 = self.left
+                       if bottom1 < top2:
+                               d.invert((h1, bottom1), (self.right, top2))
+                       top1, bottom1 = top2, bottom2
+               d.invert((h1, top1), (h2, bottom2))
+
+
+# Test class Para
+# XXX This was last used on the Mac, hence the weird fonts...
+def test():
+       import stdwin
+       from stdwinevents import *
+       words = 'The', 'quick', 'brown', 'fox', 'jumps', 'over', \
+               'the', 'lazy', 'dog.'
+       paralist = []
+       for just in 'l', 'r', 'lr', 'c':
+               p = Para()
+               p.just = just
+               p.addword(stdwin, ('New York', 'p', 12), words[0], 1, 1)
+               for word in words[1:-1]:
+                       p.addword(stdwin, None, word, 1, 1)
+               p.addword(stdwin, None, words[-1], 2, 4)
+               p.addword(stdwin, ('New York', 'b', 18), 'Bye!', 0, 0)
+               p.addword(stdwin, ('New York', 'p', 10), 'Bye!', 0, 0)
+               paralist.append(p)
+       window = stdwin.open('Para.test()')
+       start = stop = selpara = None
+       while 1:
+               etype, win, detail = stdwin.getevent()
+               if etype == WE_CLOSE:
+                       break
+               if etype == WE_SIZE:
+                       window.change((0, 0), (1000, 1000))
+               if etype == WE_DRAW:
+                       width, height = window.getwinsize()
+                       d = None
+                       try:
+                               d = window.begindrawing()
+                               d.cliprect(detail)
+                               d.erase(detail)
+                               v = 0
+                               for p in paralist:
+                                       v = p.render(d, 0, v, width)
+                                       if p == selpara and \
+                                          start <> None and stop <> None:
+                                               p.invert(d, start, stop)
+                       finally:
+                               if d: d.close()
+               if etype == WE_MOUSE_DOWN:
+                       if selpara and start <> None and stop <> None:
+                               d = window.begindrawing()
+                               selpara.invert(d, start, stop)
+                               d.close()
+                       start = stop = selpara = None
+                       mouseh, mousev = detail[0]
+                       for p in paralist:
+                               start = p.whereis(stdwin, mouseh, mousev)
+                               if start <> None:
+                                       selpara = p
+                                       break
+               if etype == WE_MOUSE_UP and start <> None and selpara:
+                       mouseh, mousev = detail[0]
+                       stop = selpara.whereis(stdwin, mouseh, mousev)
+                       if stop == None: start = selpara = None
+                       else:
+                               if start > stop:
+                                       start, stop = stop, start
+                               d = window.begindrawing()
+                               selpara.invert(d, start, stop)
+                               d.close()
+       window.close()
diff --git a/Lib/dos-8x3/posixfil.py b/Lib/dos-8x3/posixfil.py
new file mode 100755 (executable)
index 0000000..459cd52
--- /dev/null
@@ -0,0 +1,207 @@
+#
+# Start of posixfile.py
+#
+
+#
+# Extended file operations
+#
+# f = posixfile.open(filename, [mode, [bufsize]])
+#      will create a new posixfile object
+#
+# f = posixfile.fileopen(fileobject)
+#      will create a posixfile object from a builtin file object
+#
+# f.file()
+#      will return the original builtin file object
+#
+# f.dup()
+#      will return a new file object based on a new filedescriptor
+#
+# f.dup2(fd)
+#      will return a new file object based on the given filedescriptor
+#
+# f.flags(mode)
+#      will turn on the associated flag (merge)
+#      mode can contain the following characters:
+#
+#   (character representing a flag)
+#      a       append only flag
+#      c       close on exec flag
+#      n       no delay flag
+#      s       synchronization flag
+#   (modifiers)
+#      !       turn flags 'off' instead of default 'on'
+#      =       copy flags 'as is' instead of default 'merge'
+#      ?       return a string in which the characters represent the flags
+#              that are set
+#
+#      note: - the '!' and '=' modifiers are mutually exclusive.
+#            - the '?' modifier will return the status of the flags after they
+#              have been changed by other characters in the mode string
+#
+# f.lock(mode [, len [, start [, whence]]])
+#      will (un)lock a region
+#      mode can contain the following characters:
+#
+#   (character representing type of lock)
+#      u       unlock
+#      r       read lock
+#      w       write lock
+#   (modifiers)
+#      |       wait until the lock can be granted
+#      ?       return the first lock conflicting with the requested lock
+#              or 'None' if there is no conflict. The lock returned is in the
+#              format (mode, len, start, whence, pid) where mode is a
+#              character representing the type of lock ('r' or 'w')
+#
+#      note: - the '?' modifier prevents a region from being locked; it is
+#              query only
+#
+
+class _posixfile_:
+    states = ['open', 'closed']
+
+    #
+    # Internal routines
+    #
+    def __repr__(self):
+       file = self._file_
+       return "<%s posixfile '%s', mode '%s' at %s>" % \
+               (self.states[file.closed], file.name, file.mode, \
+                hex(id(self))[2:])
+
+    def __del__(self):
+       self._file_.close()
+
+    #
+    # Initialization routines
+    #
+    def open(self, name, mode='r', bufsize=-1):
+       import __builtin__
+       return self.fileopen(__builtin__.open(name, mode, bufsize))
+
+    def fileopen(self, file):
+       if `type(file)` != "<type 'file'>":
+           raise TypeError, 'posixfile.fileopen() arg must be file object'
+       self._file_  = file
+       # Copy basic file methods
+       for method in file.__methods__:
+           setattr(self, method, getattr(file, method))
+       return self
+
+    #
+    # New methods
+    #
+    def file(self):
+       return self._file_
+
+    def dup(self):
+       import posix
+
+       try: ignore = posix.fdopen
+       except: raise AttributeError, 'dup() method unavailable'
+
+       return posix.fdopen(posix.dup(self._file_.fileno()), self._file_.mode)
+
+    def dup2(self, fd):
+       import posix
+
+       try: ignore = posix.fdopen
+       except: raise AttributeError, 'dup() method unavailable'
+
+       posix.dup2(self._file_.fileno(), fd)
+       return posix.fdopen(fd, self._file_.mode)
+
+    def flags(self, *which):
+       import fcntl, FCNTL
+
+       if which:
+           if len(which) > 1:
+               raise TypeError, 'Too many arguments'
+           which = which[0]
+       else: which = '?'
+
+       l_flags = 0
+       if 'n' in which: l_flags = l_flags | FCNTL.O_NDELAY
+       if 'a' in which: l_flags = l_flags | FCNTL.O_APPEND
+       if 's' in which: l_flags = l_flags | FCNTL.O_SYNC
+
+       file = self._file_
+
+       if '=' not in which:
+           cur_fl = fcntl.fcntl(file.fileno(), FCNTL.F_GETFL, 0)
+           if '!' in which: l_flags = cur_fl & ~ l_flags
+           else: l_flags = cur_fl | l_flags
+
+       l_flags = fcntl.fcntl(file.fileno(), FCNTL.F_SETFL, l_flags)
+
+       if 'c' in which:        
+           arg = ('!' not in which)    # 0 is don't, 1 is do close on exec
+           l_flags = fcntl.fcntl(file.fileno(), FCNTL.F_SETFD, arg)
+
+       if '?' in which:
+           which = ''                  # Return current flags
+           l_flags = fcntl.fcntl(file.fileno(), FCNTL.F_GETFL, 0)
+           if FCNTL.O_APPEND & l_flags: which = which + 'a'
+           if fcntl.fcntl(file.fileno(), FCNTL.F_GETFD, 0) & 1:
+               which = which + 'c'
+           if FCNTL.O_NDELAY & l_flags: which = which + 'n'
+           if FCNTL.O_SYNC & l_flags: which = which + 's'
+           return which
+       
+    def lock(self, how, *args):
+       import struct, fcntl, FCNTL
+
+       if 'w' in how: l_type = FCNTL.F_WRLCK
+       elif 'r' in how: l_type = FCNTL.F_RDLCK
+       elif 'u' in how: l_type = FCNTL.F_UNLCK
+       else: raise TypeError, 'no type of lock specified'
+
+       if '|' in how: cmd = FCNTL.F_SETLKW
+       elif '?' in how: cmd = FCNTL.F_GETLK
+       else: cmd = FCNTL.F_SETLK
+
+       l_whence = 0
+       l_start = 0
+       l_len = 0
+
+       if len(args) == 1:
+           l_len = args[0]
+       elif len(args) == 2:
+           l_len, l_start = args
+       elif len(args) == 3:
+           l_len, l_start, l_whence = args
+       elif len(args) > 3:
+           raise TypeError, 'too many arguments'
+
+       flock = struct.pack('hhllhh', l_type, l_whence, l_start, l_len, 0, 0)
+       flock = fcntl.fcntl(self._file_.fileno(), cmd, flock)
+
+       if '?' in how:
+           l_type, l_whence, l_start, l_len, l_sysid, l_pid = \
+               struct.unpack('hhllhh', flock)
+           if l_type != FCNTL.F_UNLCK:
+               if l_type == FCNTL.F_RDLCK:
+                   return 'r', l_len, l_start, l_whence, l_pid
+               else:
+                   return 'w', l_len, l_start, l_whence, l_pid
+
+#
+# Public routine to obtain a posixfile object
+#
+def open(name, mode='r', bufsize=-1):
+    return _posixfile_().open(name, mode, bufsize)
+
+def fileopen(file):
+    return _posixfile_().fileopen(file)
+
+#
+# Constants
+#
+SEEK_SET = 0
+SEEK_CUR = 1
+SEEK_END = 2
+
+#
+# End of posixfile.py
+#
diff --git a/Lib/dos-8x3/posixpat.py b/Lib/dos-8x3/posixpat.py
new file mode 100755 (executable)
index 0000000..d284834
--- /dev/null
@@ -0,0 +1,307 @@
+# Module 'posixpath' -- common operations on POSIX pathnames
+
+import posix
+import stat
+
+
+# Normalize the case of a pathname.  Trivial in Posix, string.lower on Mac.
+# On MS-DOS this may also turn slashes into backslashes; however, other
+# normalizations (such as optimizing '../' away) are not allowed
+# (another function should be defined to do that).
+
+def normcase(s):
+       return s
+
+
+# Return wheter a path is absolute.
+# Trivial in Posix, harder on the Mac or MS-DOS.
+
+def isabs(s):
+       return s[:1] == '/'
+
+
+# Join two pathnames.
+# Ignore the first part if the second part is absolute.
+# Insert a '/' unless the first part is empty or already ends in '/'.
+
+def join(a, b):
+       if b[:1] == '/': return b
+       if a == '' or a[-1:] == '/': return a + b
+       # Note: join('x', '') returns 'x/'; is this what we want?
+       return a + '/' + b
+
+
+# Split a path in head (everything up to the last '/') and tail (the
+# rest).  If the path ends in '/', tail will be empty.  If there is no
+# '/' in the path, head  will be empty.
+# Trailing '/'es are stripped from head unless it is the root.
+
+def split(p):
+       import string
+       i = string.rfind(p, '/') + 1
+       head, tail = p[:i], p[i:]
+       if head and head <> '/'*len(head):
+               while head[-1] == '/':
+                       head = head[:-1]
+       return head, tail
+
+
+# Split a path in root and extension.
+# The extension is everything starting at the first dot in the last
+# pathname component; the root is everything before that.
+# It is always true that root + ext == p.
+
+def splitext(p):
+       root, ext = '', ''
+       for c in p:
+               if c == '/':
+                       root, ext = root + ext + c, ''
+               elif c == '.':
+                       if ext:
+                               root, ext = root + ext, c
+                       else:
+                               ext = c
+               elif ext:
+                       ext = ext + c
+               else:
+                       root = root + c
+       return root, ext
+
+
+# Split a pathname into a drive specification and the rest of the
+# path.  Useful on DOS/Windows/NT; on Unix, the drive is always empty.
+
+def splitdrive(p):
+       return '', p
+
+
+# Return the tail (basename) part of a path.
+
+def basename(p):
+       return split(p)[1]
+
+
+# Return the head (dirname) part of a path.
+
+def dirname(p):
+       return split(p)[0]
+
+
+# Return the longest prefix of all list elements.
+
+def commonprefix(m):
+       if not m: return ''
+       prefix = m[0]
+       for item in m:
+               for i in range(len(prefix)):
+                       if prefix[:i+1] <> item[:i+1]:
+                               prefix = prefix[:i]
+                               if i == 0: return ''
+                               break
+       return prefix
+
+
+# Is a path a symbolic link?
+# This will always return false on systems where posix.lstat doesn't exist.
+
+def islink(path):
+       try:
+               st = posix.lstat(path)
+       except (posix.error, AttributeError):
+               return 0
+       return stat.S_ISLNK(st[stat.ST_MODE])
+
+
+# Does a path exist?
+# This is false for dangling symbolic links.
+
+def exists(path):
+       try:
+               st = posix.stat(path)
+       except posix.error:
+               return 0
+       return 1
+
+
+# Is a path a posix directory?
+# This follows symbolic links, so both islink() and isdir() can be true
+# for the same path.
+
+def isdir(path):
+       try:
+               st = posix.stat(path)
+       except posix.error:
+               return 0
+       return stat.S_ISDIR(st[stat.ST_MODE])
+
+
+# Is a path a regular file?
+# This follows symbolic links, so both islink() and isfile() can be true
+# for the same path.
+
+def isfile(path):
+       try:
+               st = posix.stat(path)
+       except posix.error:
+               return 0
+       return stat.S_ISREG(st[stat.ST_MODE])
+
+
+# Are two filenames really pointing to the same file?
+
+def samefile(f1, f2):
+       s1 = posix.stat(f1)
+       s2 = posix.stat(f2)
+       return samestat(s1, s2)
+
+
+# Are two open files really referencing the same file?
+# (Not necessarily the same file descriptor!)
+# XXX Oops, posix.fstat() doesn't exist yet!
+
+def sameopenfile(fp1, fp2):
+       s1 = posix.fstat(fp1)
+       s2 = posix.fstat(fp2)
+       return samestat(s1, s2)
+
+
+# Are two stat buffers (obtained from stat, fstat or lstat)
+# describing the same file?
+
+def samestat(s1, s2):
+       return s1[stat.ST_INO] == s2[stat.ST_INO] and \
+               s1[stat.ST_DEV] == s2[stat.ST_DEV]
+
+
+# Is a path a mount point?
+# (Does this work for all UNIXes?  Is it even guaranteed to work by POSIX?)
+
+def ismount(path):
+       try:
+               s1 = posix.stat(path)
+               s2 = posix.stat(join(path, '..'))
+       except posix.error:
+               return 0 # It doesn't exist -- so not a mount point :-)
+       dev1 = s1[stat.ST_DEV]
+       dev2 = s2[stat.ST_DEV]
+       if dev1 != dev2:
+               return 1                # path/.. on a different device as path
+       ino1 = s1[stat.ST_INO]
+       ino2 = s2[stat.ST_INO]
+       if ino1 == ino2:
+               return 1                # path/.. is the same i-node as path
+       return 0
+
+
+# Directory tree walk.
+# For each directory under top (including top itself, but excluding
+# '.' and '..'), func(arg, dirname, filenames) is called, where
+# dirname is the name of the directory and filenames is the list
+# files files (and subdirectories etc.) in the directory.
+# The func may modify the filenames list, to implement a filter,
+# or to impose a different order of visiting.
+
+def walk(top, func, arg):
+       try:
+               names = posix.listdir(top)
+       except posix.error:
+               return
+       func(arg, top, names)
+       exceptions = ('.', '..')
+       for name in names:
+               if name not in exceptions:
+                       name = join(top, name)
+                       if isdir(name) and not islink(name):
+                               walk(name, func, arg)
+
+
+# Expand paths beginning with '~' or '~user'.
+# '~' means $HOME; '~user' means that user's home directory.
+# If the path doesn't begin with '~', or if the user or $HOME is unknown,
+# the path is returned unchanged (leaving error reporting to whatever
+# function is called with the expanded path as argument).
+# See also module 'glob' for expansion of *, ? and [...] in pathnames.
+# (A function should also be defined to do full *sh-style environment
+# variable expansion.)
+
+def expanduser(path):
+       if path[:1] <> '~':
+               return path
+       i, n = 1, len(path)
+       while i < n and path[i] <> '/':
+               i = i+1
+       if i == 1:
+               if not posix.environ.has_key('HOME'):
+                       return path
+               userhome = posix.environ['HOME']
+       else:
+               import pwd
+               try:
+                       pwent = pwd.getpwnam(path[1:i])
+               except KeyError:
+                       return path
+               userhome = pwent[5]
+       if userhome[-1:] == '/': i = i+1
+       return userhome + path[i:]
+
+
+# Expand paths containing shell variable substitutions.
+# This expands the forms $variable and ${variable} only.
+# Non-existant variables are left unchanged.
+
+_varprog = None
+
+def expandvars(path):
+       global _varprog
+       if '$' not in path:
+               return path
+       if not _varprog:
+               import regex
+               _varprog = regex.compile('$\([a-zA-Z0-9_]+\|{[^}]*}\)')
+       i = 0
+       while 1:
+               i = _varprog.search(path, i)
+               if i < 0:
+                       break
+               name = _varprog.group(1)
+               j = i + len(_varprog.group(0))
+               if name[:1] == '{' and name[-1:] == '}':
+                       name = name[1:-1]
+               if posix.environ.has_key(name):
+                       tail = path[j:]
+                       path = path[:i] + posix.environ[name]
+                       i = len(path)
+                       path = path + tail
+               else:
+                       i = j
+       return path
+
+
+# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
+# It should be understood that this may change the meaning of the path
+# if it contains symbolic links!
+
+def normpath(path):
+       import string
+       # Treat initial slashes specially
+       slashes = ''
+       while path[:1] == '/':
+               slashes = slashes + '/'
+               path = path[1:]
+       comps = string.splitfields(path, '/')
+       i = 0
+       while i < len(comps):
+               if comps[i] == '.':
+                       del comps[i]
+               elif comps[i] == '..' and i > 0 and \
+                                         comps[i-1] not in ('', '..'):
+                       del comps[i-1:i+1]
+                       i = i-1
+               elif comps[i] == '' and i > 0 and comps[i-1] <> '':
+                       del comps[i]
+               else:
+                       i = i+1
+       # If the path is now empty, substitute '.'
+       if not comps and not slashes:
+               comps.append('.')
+       return slashes + string.joinfields(comps, '/')
diff --git a/Lib/dos-8x3/py_compi.py b/Lib/dos-8x3/py_compi.py
new file mode 100755 (executable)
index 0000000..2e68ba8
--- /dev/null
@@ -0,0 +1,31 @@
+# Routine to "compile" a .py file to a .pyc file.
+# This has intimate knowledge of how Python/import.c does it.
+# By Sjoerd Mullender (I forced him to write it :-).
+
+import imp
+MAGIC = imp.get_magic()
+
+def wr_long(f, x):
+       f.write(chr( x        & 0xff))
+       f.write(chr((x >> 8)  & 0xff))
+       f.write(chr((x >> 16) & 0xff))
+       f.write(chr((x >> 24) & 0xff))
+
+def compile(file, cfile = None):
+       import os, marshal, __builtin__
+       f = open(file)
+       codestring = f.read()
+       f.close()
+       timestamp = os.stat(file)[8]
+       codeobject = __builtin__.compile(codestring, file, 'exec')
+       if not cfile:
+               cfile = file + 'c'
+       fc = open(cfile, 'wb')
+       fc.write(MAGIC)
+       wr_long(fc, timestamp)
+       marshal.dump(codeobject, fc)
+       fc.close()
+       if os.name == 'mac':
+               import macfs
+               macfs.FSSpec(cfile).SetCreatorType('Pyth', 'PYC ')
+               macfs.FSSpec(file).SetCreatorType('Pyth', 'TEXT')
diff --git a/Lib/dos-8x3/queue.py b/Lib/dos-8x3/queue.py
new file mode 100755 (executable)
index 0000000..5125fd5
--- /dev/null
@@ -0,0 +1,121 @@
+# A multi-producer, multi-consumer queue.
+
+Empty = 'Queue.Empty' # Exception raised by get_nowait()
+
+class Queue:
+
+       # Initialize a queue object with a given maximum size
+       # (If maxsize is <= 0, the maximum size is infinite)
+       def __init__(self, maxsize):
+               import thread
+               self._init(maxsize)
+               self.mutex = thread.allocate_lock()
+               self.esema = thread.allocate_lock()
+               self.esema.acquire_lock()
+               self.fsema = thread.allocate_lock()
+
+       # Get an approximation of the queue size (not reliable!)
+       def qsize(self):
+               self.mutex.acquire_lock()
+               n = self._qsize()
+               self.mutex.release_lock()
+               return n
+
+       # Check if the queue is empty (not reliable!)
+       def empty(self):
+               self.mutex.acquire_lock()
+               n = self._empty()
+               self.mutex.release_lock()
+               return n
+
+       # Check if the queue is full (not reliable!)
+       def full(self):
+               self.mutex.acquire_lock()
+               n = self._full()
+               self.mutex.release_lock()
+               return n
+
+       # Put a new item into the queue
+       def put(self, item):
+               self.fsema.acquire_lock()
+               self.mutex.acquire_lock()
+               was_empty = self._empty()
+               self._put(item)
+               if was_empty:
+                       self.esema.release_lock()
+               if not self._full():
+                       self.fsema.release_lock()
+               self.mutex.release_lock()
+
+       # Get an item from the queue,
+       # blocking if necessary until one is available
+       def get(self):
+               self.esema.acquire_lock()
+               self.mutex.acquire_lock()
+               was_full = self._full()
+               item = self._get()
+               if was_full:
+                       self.fsema.release_lock()
+               if not self._empty():
+                       self.esema.release_lock()
+               self.mutex.release_lock()
+               return item
+
+       # Get an item from the queue if one is immediately available,
+       # raise Empty if the queue is empty or temporarily unavailable
+       def get_nowait(self):
+               locked = self.esema.acquire_lock(0)
+               self.mutex.acquire_lock()
+               if self._empty():
+                       # The queue is empyt -- we can't have esema
+                       self.mutex.release_lock()
+                       raise Empty
+               if not locked:
+                       locked = self.esema.acquire_lock(0)
+                       if not locked:
+                               # Somebody else has esema
+                               # but we have mutex --
+                               # go out of their way
+                               self.mutex.release_lock()
+                               raise Empty
+               was_full = self._full()
+               item = self._get()
+               if was_full:
+                       self.fsema.release_lock()
+               if not self._empty():
+                       self.esema.release_lock()
+               self.mutex.release_lock()
+               return item
+
+       # XXX Need to define put_nowait() as well.
+               
+
+       # Override these methods to implement other queue organizations
+       # (e.g. stack or priority queue).
+       # These will only be called with appropriate locks held
+
+       # Initialize the queue representation
+       def _init(self, maxsize):
+               self.maxsize = maxsize
+               self.queue = []
+
+       def _qsize(self):
+               return len(self.queue)
+
+       # Check wheter the queue is empty
+       def _empty(self):
+               return not self.queue
+
+       # Check whether the queue is full
+       def _full(self):
+               return self.maxsize > 0 and len(self.queue) == self.maxsize
+
+       # Put a new item in the queue
+       def _put(self, item):
+               self.queue.append(item)
+
+       # Get an item from the queue
+       def _get(self):
+               item = self.queue[0]
+               del self.queue[0]
+               return item
diff --git a/Lib/dos-8x3/regex_sy.py b/Lib/dos-8x3/regex_sy.py
new file mode 100755 (executable)
index 0000000..bb80f4e
--- /dev/null
@@ -0,0 +1,41 @@
+# These bits are passed to regex.set_syntax() to choose among
+# alternative regexp syntaxes.
+
+# 1 means plain parentheses serve as grouping, and backslash
+#   parentheses are needed for literal searching.
+# 0 means backslash-parentheses are grouping, and plain parentheses
+#   are for literal searching.
+RE_NO_BK_PARENS = 1
+
+# 1 means plain | serves as the "or"-operator, and \| is a literal.
+# 0 means \| serves as the "or"-operator, and | is a literal.
+RE_NO_BK_VBAR = 2
+
+# 0 means plain + or ? serves as an operator, and \+, \? are literals.
+# 1 means \+, \? are operators and plain +, ? are literals.
+RE_BK_PLUS_QM = 4
+
+# 1 means | binds tighter than ^ or $.
+# 0 means the contrary.
+RE_TIGHT_VBAR = 8
+
+# 1 means treat \n as an _OR operator
+# 0 means treat it as a normal character
+RE_NEWLINE_OR = 16
+
+# 0 means that a special characters (such as *, ^, and $) always have
+#   their special meaning regardless of the surrounding context.
+# 1 means that special characters may act as normal characters in some
+#   contexts.  Specifically, this applies to:
+#      ^ - only special at the beginning, or after ( or |
+#      $ - only special at the end, or before ) or |
+#      *, +, ? - only special when not after the beginning, (, or |
+RE_CONTEXT_INDEP_OPS = 32
+
+# Now define combinations of bits for the standard possibilities.
+RE_SYNTAX_AWK = (RE_NO_BK_PARENS | RE_NO_BK_VBAR | RE_CONTEXT_INDEP_OPS)
+RE_SYNTAX_EGREP = (RE_SYNTAX_AWK | RE_NEWLINE_OR)
+RE_SYNTAX_GREP = (RE_BK_PLUS_QM | RE_NEWLINE_OR)
+RE_SYNTAX_EMACS = 0
+
+# (Python's obsolete "regexp" module used a syntax similar to awk.)
diff --git a/Lib/dos-8x3/simpleht.py b/Lib/dos-8x3/simpleht.py
new file mode 100755 (executable)
index 0000000..dd3107a
--- /dev/null
@@ -0,0 +1,168 @@
+"""Simple HTTP Server.
+
+This module builds on BaseHTTPServer by implementing the standard GET
+and HEAD requests in a fairly straightforward manner.
+
+"""
+
+
+__version__ = "0.3"
+
+
+import os
+import pwd
+import sys
+import time
+import socket
+import string
+import posixpath
+import SocketServer
+import BaseHTTPServer
+
+
+def nobody_uid():
+    """Internal routine to get nobody's uid"""
+    try:
+       nobody = pwd.getpwnam('nobody')[2]
+    except pwd.error:
+       nobody = 1 + max(map(lambda x: x[2], pwd.getpwall()))
+    return nobody
+
+nobody = nobody_uid()
+
+
+class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+
+    """Simple HTTP request handler with GET and HEAD commands.
+
+    This serves files from the current directory and any of its
+    subdirectories.  It assumes that all files are plain text files
+    unless they have the extension ".html" in which case it assumes
+    they are HTML files.
+
+    The GET and HEAD requests are identical except that the HEAD
+    request omits the actual contents of the file.
+
+    """
+
+    server_version = "SimpleHTTP/" + __version__
+
+    def do_GET(self):
+       """Serve a GET request."""
+       f = self.send_head()
+       if f:
+           self.copyfile(f, self.wfile)
+           f.close()
+
+    def do_HEAD(self):
+       """Serve a HEAD request."""
+       f = self.send_head()
+       if f:
+           f.close()
+
+    def send_head(self):
+       """Common code for GET and HEAD commands.
+
+       This sends the response code and MIME headers.
+
+       Return value is either a file object (which has to be copied
+       to the outputfile by the caller unless the command was HEAD,
+       and must be closed by the caller under all circumstances), or
+       None, in which case the caller has nothing further to do.
+
+       """
+       path = self.translate_path(self.path)
+       if os.path.isdir(path):
+           self.send_error(403, "Directory listing not supported")
+           return None
+       try:
+           f = open(path)
+       except IOError:
+           self.send_error(404, "File not found")
+           return None
+       self.send_response(200)
+       self.send_header("Content-type", self.guess_type(path))
+       self.end_headers()
+       return f
+
+    def translate_path(self, path):
+       """Translate a /-separated PATH to the local filename syntax.
+
+       Components that mean special things to the local file system
+       (e.g. drive or directory names) are ignored.  (XXX They should
+       probably be diagnosed.)
+
+       """
+       path = posixpath.normpath(path)
+       words = string.splitfields(path, '/')
+       words = filter(None, words)
+       path = os.getcwd()
+       for word in words:
+           drive, word = os.path.splitdrive(word)
+           head, word = os.path.split(word)
+           if word in (os.curdir, os.pardir): continue
+           path = os.path.join(path, word)
+       return path
+
+    def copyfile(self, source, outputfile):
+       """Copy all data between two file objects.
+
+       The SOURCE argument is a file object open for reading
+       (or anything with a read() method) and the DESTINATION
+       argument is a file object open for writing (or
+       anything with a write() method).
+
+       The only reason for overriding this would be to change
+       the block size or perhaps to replace newlines by CRLF
+       -- note however that this the default server uses this
+       to copy binary data as well.
+
+       """
+
+       BLOCKSIZE = 8192
+       while 1:
+           data = source.read(BLOCKSIZE)
+           if not data: break
+           outputfile.write(data)
+
+    def guess_type(self, path):
+       """Guess the type of a file.
+
+       Argument is a PATH (a filename).
+
+       Return value is a string of the form type/subtype,
+       usable for a MIME Content-type header.
+
+       The default implementation looks the file's extension
+       up in the table self.extensions_map, using text/plain
+       as a default; however it would be permissible (if
+       slow) to look inside the data to make a better guess.
+
+       """
+
+       base, ext = posixpath.splitext(path)
+       if self.extensions_map.has_key(ext):
+           return self.extensions_map[ext]
+       ext = string.lower(ext)
+       if self.extensions_map.has_key(ext):
+           return self.extensions_map[ext]
+       else:
+           return self.extensions_map['']
+
+    extensions_map = {
+           '': 'text/plain',   # Default, *must* be present
+           '.html': 'text/html',
+           '.htm': 'text/html',
+           '.gif': 'image/gif',
+           '.jpg': 'image/jpeg',
+           '.jpeg': 'image/jpeg',
+           }
+
+
+def test(HandlerClass = SimpleHTTPRequestHandler,
+        ServerClass = SocketServer.TCPServer):
+    BaseHTTPServer.test(HandlerClass, ServerClass)
+
+
+if __name__ == '__main__':
+    test()
diff --git a/Lib/dos-8x3/socketse.py b/Lib/dos-8x3/socketse.py
new file mode 100755 (executable)
index 0000000..9a646ab
--- /dev/null
@@ -0,0 +1,413 @@
+"""Generic socket server classes.
+
+This module tries to capture the various aspects of defining a server:
+
+- address family:
+       - AF_INET: IP (Internet Protocol) sockets (default)
+       - AF_UNIX: Unix domain sockets
+       - others, e.g. AF_DECNET are conceivable (see <socket.h>
+- socket type:
+       - SOCK_STREAM (reliable stream, e.g. TCP)
+       - SOCK_DGRAM (datagrams, e.g. UDP)
+- client address verification before further looking at the request
+       (This is actually a hook for any processing that needs to look
+        at the request before anything else, e.g. logging)
+- how to handle multiple requests:
+       - synchronous (one request is handled at a time)
+       - forking (each request is handled by a new process)
+       - threading (each request is handled by a new thread)
+
+The classes in this module favor the server type that is simplest to
+write: a synchronous TCP/IP server.  This is bad class design, but
+save some typing.  (There's also the issue that a deep class hierarchy
+slows down method lookups.)
+
+There are four classes in an inheritance diagram that represent
+synchronous servers of four types:
+
+       +-----------+        +------------------+
+       | TCPServer |------->| UnixStreamServer |
+       +-----------+        +------------------+
+             |
+             v
+       +-----------+        +--------------------+
+       | UDPServer |------->| UnixDatagramServer |
+       +-----------+        +--------------------+
+
+(Note that UnixDatagramServer derives from UDPServer, not from
+UnixStreamServer -- the only difference between an IP and a Unix
+stream server is the address family, which is simply repeated in both
+unix server classes.)
+
+Forking and threading versions of each type of server can be created
+using the ForkingServer and ThreadingServer mix-in classes.  For
+instance, a threading UDP server class is created as follows:
+
+       class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
+
+(The Mix-in class must come first, since it overrides a method defined
+in UDPServer!)
+
+To implement a service, you must derive a class from
+BaseRequestHandler and redefine its handle() method.  You can then run
+various versions of the service by combining one of the server classes
+with your request handler class.
+
+The request handler class must be different for datagram or stream
+services.  This can be hidden by using the mix-in request handler
+classes StreamRequestHandler or DatagramRequestHandler.
+
+Of course, you still have to use your head!
+
+For instance, it makes no sense to use a forking server if the service
+contains state in memory that can be modified by requests (since the
+modifications in the child process would never reach the initial state
+kept in the parent process and passed to each child).  In this case,
+you can use a threading server, but you will probably have to use
+locks to avoid two requests that come in nearly simultaneous to apply
+conflicting changes to the server state.
+
+On the other hand, if you are building e.g. an HTTP server, where all
+data is stored externally (e.g. in the file system), a synchronous
+class will essentially render the service "deaf" while one request is
+being handled -- which may be for a very long time if a client is slow
+to reqd all the data it has requested.  Here a threading or forking
+server is appropriate.
+
+In some cases, it may be appropriate to process part of a request
+synchronously, but to finish processing in a forked child depending on
+the request data.  This can be implemented by using a synchronous
+server and doing an explicit fork in the request handler class's
+handle() method.
+
+Another approach to handling multiple simultaneous requests in an
+environment that supports neither threads nor fork (or where these are
+too expensive or inappropriate for the service) is to maintain an
+explicit table of partially finished requests and to use select() to
+decide which request to work on next (or whether to handle a new
+incoming request).  This is particularly important for stream services
+where each client can potentially be connected for a long time (if
+threads or subprocesses can't be used).
+
+Future work:
+- Standard classes for Sun RPC (which uses either UDP or TCP)
+- Standard mix-in classes to implement various authentication
+  and encryption schemes
+- Standard framework for select-based multiplexing
+
+XXX Open problems:
+- What to do with out-of-band data?
+
+"""
+
+
+__version__ = "0.2"
+
+
+import socket
+import sys
+import os
+
+
+class TCPServer:
+
+    """Base class for various socket-based server classes.
+
+    Defaults to synchronous IP stream (i.e., TCP).
+
+    Methods for the caller:
+
+    - __init__(server_address, RequestHandlerClass)
+    - serve_forever()
+    - handle_request() # if you don't use serve_forever()
+    - fileno() -> int  # for select()
+
+    Methods that may be overridden:
+
+    - server_bind()
+    - server_activate()
+    - get_request() -> request, client_address
+    - verify_request(request, client_address)
+    - process_request(request, client_address)
+    - handle_error()
+
+    Methods for derived classes:
+
+    - finish_request(request, client_address)
+
+    Class variables that may be overridden by derived classes or
+    instances:
+
+    - address_family
+    - socket_type
+    - request_queue_size (only for stream sockets)
+
+    Instance variables:
+
+    - server_address
+    - RequestHandlerClass
+    - socket
+
+    """
+
+    address_family = socket.AF_INET
+
+    socket_type = socket.SOCK_STREAM
+
+    request_queue_size = 5
+
+    def __init__(self, server_address, RequestHandlerClass):
+       """Constructor.  May be extended, do not override."""
+       self.server_address = server_address
+       self.RequestHandlerClass = RequestHandlerClass
+       self.socket = socket.socket(self.address_family,
+                                   self.socket_type)
+       self.server_bind()
+       self.server_activate()
+
+    def server_bind(self):
+       """Called by constructor to bind the socket.
+
+       May be overridden.
+
+       """
+       self.socket.bind(self.server_address)
+
+    def server_activate(self):
+       """Called by constructor to activate the server.
+
+       May be overridden.
+
+       """
+       self.socket.listen(self.request_queue_size)
+
+    def fileno(self):
+       """Return socket file number.
+
+       Interface required by select().
+
+       """
+       return self.socket.fileno()
+
+    def serve_forever(self):
+       """Handle one request at a time until doomsday."""
+       while 1:
+           self.handle_request()
+
+    # The distinction between handling, getting, processing and
+    # finishing a request is fairly arbitrary.  Remember:
+    #
+    # - handle_request() is the top-level call.  It calls
+    #   get_request(), verify_request() and process_request()
+    # - get_request() is different for stream or datagram sockets
+    # - process_request() is the place that may fork a new process
+    #   or create a new thread to finish the request
+    # - finish_request() instantiates the request handler class;
+    #   this constructor will handle the request all by itself
+
+    def handle_request(self):
+       """Handle one request, possibly blocking."""
+       request, client_address = self.get_request()
+       if self.verify_request(request, client_address):
+           try:
+               self.process_request(request, client_address)
+           except:
+               self.handle_error(request, client_address)
+
+    def get_request(self):
+       """Get the request and client address from the socket.
+
+       May be overridden.
+
+       """
+       return self.socket.accept()
+
+    def verify_request(self, request, client_address):
+       """Verify the request.  May be overridden.
+
+       Return true if we should proceed with this request.
+
+       """
+       return 1
+
+    def process_request(self, request, client_address):
+       """Call finish_request.
+
+       Overridden by ForkingMixIn and ThreadingMixIn.
+
+       """
+       self.finish_request(request, client_address)
+
+    def finish_request(self, request, client_address):
+       """Finish one request by instantiating RequestHandlerClass."""
+       self.RequestHandlerClass(request, client_address, self)
+
+    def handle_error(self, request, client_address):
+       """Handle an error gracefully.  May be overridden.
+
+       The default is to print a traceback and continue.
+
+       """
+       exc, value, tb = sys.exc_type, sys.exc_value, sys.exc_traceback
+       print '-'*40
+       print 'Exception happened during processing of request from',
+       print client_address
+       import traceback
+       traceback.print_exception(exc, value, tb)
+       print '-'*40
+
+
+class UDPServer(TCPServer):
+
+    """UDP server class."""
+
+    socket_type = socket.SOCK_DGRAM
+
+    max_packet_size = 8192
+
+    def get_request(self):
+       return self.socket.recvfrom(max_packet_size)
+
+
+if hasattr(socket, 'AF_UNIX'):
+
+    class UnixStreamServer(TCPServer):
+
+       address_family = socket.AF_UNIX
+
+
+    class UnixDatagramServer(UDPServer):
+
+       address_family = socket.AF_UNIX
+
+
+class ForkingMixIn:
+
+    """Mix-in class to handle each request in a new process."""
+
+    active_children = None
+
+    def collect_children(self):
+       """Internal routine to wait for died children."""
+       while self.active_children:
+           pid, status = os.waitpid(0, os.WNOHANG)
+           if not pid: break
+           self.active_children.remove(pid)
+
+    def process_request(self, request, client_address):
+       """Fork a new subprocess to process the request."""
+       self.collect_children()
+       pid = os.fork()
+       if pid:
+           # Parent process
+           if self.active_children is None:
+               self.active_children = []
+           self.active_children.append(pid)
+           return
+       else:
+           # Child process.
+           # This must never return, hence os._exit()!
+           try:
+               self.finish_request(request, client_address)
+               os._exit(0)
+           except:
+               try:
+                   self.handle_error(request,
+                                     client_address)
+               finally:
+                   os._exit(1)
+
+
+class ThreadingMixIn:
+
+    """Mix-in class to handle each request in a new thread."""
+
+    def process_request(self, request, client_address):
+       """Start a new thread to process the request."""
+       import thread
+       thread.start_new_thread(self.finish_request,
+                               (request, client_address))
+
+
+class ForkingUDPServer(ForkingMixIn, UDPServer): pass
+class ForkingTCPServer(ForkingMixIn, TCPServer): pass
+
+class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
+class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
+
+
+class BaseRequestHandler:
+
+    """Base class for request handler classes.
+
+    This class is instantiated for each request to be handled.  The
+    constructor sets the instance variables request, client_address
+    and server, and then calls the handle() method.  To implement a
+    specific service, all you need to do is to derive a class which
+    defines a handle() method.
+
+    The handle() method can find the request as self.request, the
+    client address as self.client_request, and the server (in case it
+    needs access to per-server information) as self.server.  Since a
+    separate instance is created for each request, the handle() method
+    can define arbitrary other instance variariables.
+
+    """
+
+    def __init__(self, request, client_address, server):
+       self.request = request
+       self.client_address = client_address
+       self.server = server
+       try:
+           self.setup()
+           self.handle()
+           self.finish()
+       finally:
+           sys.exc_traceback = None    # Help garbage collection
+
+    def setup(self):
+       pass
+
+    def __del__(self):
+       pass
+
+    def handle(self):
+       pass
+
+    def finish(self):
+       pass
+
+
+# The following two classes make it possible to use the same service
+# class for stream or datagram servers.
+# Each class sets up these instance variables:
+# - rfile: a file object from which receives the request is read
+# - wfile: a file object to which the reply is written
+# When the handle() method returns, wfile is flushed properly
+
+
+class StreamRequestHandler(BaseRequestHandler):
+
+    """Define self.rfile and self.wfile for stream sockets."""
+
+    def setup(self):
+       self.connection = self.request
+       self.rfile = self.connection.makefile('rb')
+       self.wfile = self.connection.makefile('wb', 0)
+
+    def finish(self):
+       self.wfile.flush()
+
+
+class DatagramRequestHandler(BaseRequestHandler):
+
+    """Define self.rfile and self.wfile for datagram sockets."""
+
+    def setup(self):
+       import StringIO
+       self.packet, self.socket = self.request
+       self.rfile = StringIO.StringIO(self.packet)
+       self.wfile = StringIO.StringIO(self.packet)
+
+    def finish(self):
+       self.socket.send(self.wfile.getvalue())
diff --git a/Lib/dos-8x3/statcach.py b/Lib/dos-8x3/statcach.py
new file mode 100755 (executable)
index 0000000..770aef0
--- /dev/null
@@ -0,0 +1,82 @@
+# Module 'statcache'
+#
+# Maintain a cache of file stats.
+# There are functions to reset the cache or to selectively remove items.
+
+import os
+from stat import *
+
+# The cache.
+# Keys are pathnames, values are `os.stat' outcomes.
+#
+cache = {}
+
+
+# Stat a file, possibly out of the cache.
+#
+def stat(path):
+       if cache.has_key(path):
+               return cache[path]
+       cache[path] = ret = os.stat(path)
+       return ret
+
+
+# Reset the cache completely.
+#
+def reset():
+       global cache
+       cache = {}
+
+
+# Remove a given item from the cache, if it exists.
+#
+def forget(path):
+       if cache.has_key(path):
+               del cache[path]
+
+
+# Remove all pathnames with a given prefix.
+#
+def forget_prefix(prefix):
+       n = len(prefix)
+       for path in cache.keys():
+               if path[:n] == prefix:
+                       del cache[path]
+
+
+# Forget about a directory and all entries in it, but not about
+# entries in subdirectories.
+#
+def forget_dir(prefix):
+       if prefix[-1:] == '/' and prefix <> '/':
+               prefix = prefix[:-1]
+       forget(prefix)
+       if prefix[-1:] <> '/':
+               prefix = prefix + '/'
+       n = len(prefix)
+       for path in cache.keys():
+               if path[:n] == prefix:
+                       rest = path[n:]
+                       if rest[-1:] == '/': rest = rest[:-1]
+                       if '/' not in rest:
+                               del cache[path]
+
+
+# Remove all pathnames except with a given prefix.
+# Normally used with prefix = '/' after a chdir().
+#
+def forget_except_prefix(prefix):
+       n = len(prefix)
+       for path in cache.keys():
+               if path[:n] <> prefix:
+                       del cache[path]
+
+
+# Check for directory.
+#
+def isdir(path):
+       try:
+               st = stat(path)
+       except os.error:
+               return 0
+       return S_ISDIR(st[ST_MODE])
diff --git a/Lib/dos-8x3/stringio.py b/Lib/dos-8x3/stringio.py
new file mode 100755 (executable)
index 0000000..bbd9428
--- /dev/null
@@ -0,0 +1,156 @@
+# class StringIO implements  file-like objects that read/write a
+# string buffer (a.k.a. "memory files").
+#
+# This implements (nearly) all stdio methods.
+#
+# f = StringIO()      # ready for writing
+# f = StringIO(buf)   # ready for reading
+# f.close()           # explicitly release resources held
+# flag = f.isatty()   # always false
+# pos = f.tell()      # get current position
+# f.seek(pos)         # set current position
+# f.seek(pos, mode)   # mode 0: absolute; 1: relative; 2: relative to EOF
+# buf = f.read()      # read until EOF
+# buf = f.read(n)     # read up to n bytes
+# buf = f.readline()  # read until end of line ('\n') or EOF
+# list = f.readlines()# list of f.readline() results until EOF
+# f.write(buf)        # write at current position
+# f.writelines(list)  # for line in list: f.write(line)
+# f.getvalue()        # return whole file's contents as a string
+#
+# Notes:
+# - Using a real file is often faster (but less convenient).
+# - fileno() is left unimplemented so that code which uses it triggers
+#   an exception early.
+# - Seeking far beyond EOF and then writing will insert real null
+#   bytes that occupy space in the buffer.
+# - There's a simple test set (see end of this file).
+
+import string
+
+class StringIO:
+       def __init__(self, buf = ''):
+               self.buf = buf
+               self.len = len(buf)
+               self.buflist = []
+               self.pos = 0
+               self.closed = 0
+               self.softspace = 0
+       def close(self):
+               if not self.closed:
+                       self.closed = 1
+                       del self.buf, self.pos
+       def isatty(self):
+               return 0
+       def seek(self, pos, mode = 0):
+               if self.buflist:
+                       self.buf = self.buf + string.joinfields(self.buflist, '')
+                       self.buflist = []
+               if mode == 1:
+                       pos = pos + self.pos
+               elif mode == 2:
+                       pos = pos + self.len
+               self.pos = max(0, pos)
+       def tell(self):
+               return self.pos
+       def read(self, n = -1):
+               if self.buflist:
+                       self.buf = self.buf + string.joinfields(self.buflist, '')
+                       self.buflist = []
+               if n < 0:
+                       newpos = self.len
+               else:
+                       newpos = min(self.pos+n, self.len)
+               r = self.buf[self.pos:newpos]
+               self.pos = newpos
+               return r
+       def readline(self):
+               if self.buflist:
+                       self.buf = self.buf + string.joinfields(self.buflist, '')
+                       self.buflist = []
+               i = string.find(self.buf, '\n', self.pos)
+               if i < 0:
+                       newpos = self.len
+               else:
+                       newpos = i+1
+               r = self.buf[self.pos:newpos]
+               self.pos = newpos
+               return r
+       def readlines(self):
+               lines = []
+               line = self.readline()
+               while line:
+                       lines.append(line)
+                       line = self.readline()
+               return lines
+       def write(self, s):
+               if not s: return
+               if self.pos > self.len:
+                       self.buflist.append('\0'*(self.pos - self.len))
+                       self.len = self.pos
+               newpos = self.pos + len(s)
+               if self.pos < self.len:
+                       if self.buflist:
+                               self.buf = self.buf + string.joinfields(self.buflist, '')
+                               self.buflist = []
+                       self.buflist = [self.buf[:self.pos], s, self.buf[newpos:]]
+                       self.buf = ''
+               else:
+                       self.buflist.append(s)
+                       self.len = newpos
+               self.pos = newpos
+       def writelines(self, list):
+               self.write(string.joinfields(list, ''))
+       def flush(self):
+               pass
+       def getvalue(self):
+               if self.buflist:
+                       self.buf = self.buf + string.joinfields(self.buflist, '')
+                       self.buflist = []
+               return self.buf
+
+
+# A little test suite
+
+def test():
+       import sys
+       if sys.argv[1:]:
+               file = sys.argv[1]
+       else:
+               file = '/etc/passwd'
+       lines = open(file, 'r').readlines()
+       text = open(file, 'r').read()
+       f = StringIO()
+       for line in lines[:-2]:
+               f.write(line)
+       f.writelines(lines[-2:])
+       if f.getvalue() != text:
+               raise RuntimeError, 'write failed'
+       length = f.tell()
+       print 'File length =', length
+       f.seek(len(lines[0]))
+       f.write(lines[1])
+       f.seek(0)
+       print 'First line =', `f.readline()`
+       here = f.tell()
+       line = f.readline()
+       print 'Second line =', `line`
+       f.seek(-len(line), 1)
+       line2 = f.read(len(line))
+       if line != line2:
+               raise RuntimeError, 'bad result after seek back'
+       f.seek(len(line2), 1)
+       list = f.readlines()
+       line = list[-1]
+       f.seek(f.tell() - len(line))
+       line2 = f.read()
+       if line != line2:
+               raise RuntimeError, 'bad result after seek back from EOF'
+       print 'Read', len(list), 'more lines'
+       print 'File length =', f.tell()
+       if f.tell() != length:
+               raise RuntimeError, 'bad length'
+       f.close()
+
+if __name__ == '__main__':
+       test()
diff --git a/Lib/dos-8x3/test_aud.py b/Lib/dos-8x3/test_aud.py
new file mode 100755 (executable)
index 0000000..3acf1bf
--- /dev/null
@@ -0,0 +1,202 @@
+# Test audioop.
+import audioop
+
+def gendata1():
+       return '\0\1\2'
+
+def gendata2():
+       if audioop.getsample('\0\1', 2, 0) == 1:
+               return '\0\0\0\1\0\2'
+       else:
+               return '\0\0\1\0\2\0'
+
+def gendata4():
+       if audioop.getsample('\0\0\0\1', 4, 0) == 1:
+               return '\0\0\0\0\0\0\0\1\0\0\0\2'
+       else:
+               return '\0\0\0\0\1\0\0\0\2\0\0\0'
+
+def testmax(data):
+       if audioop.max(data[0], 1) <> 2 or \
+                 audioop.max(data[1], 2) <> 2 or \
+                 audioop.max(data[2], 4) <> 2:
+               return 0
+       return 1
+
+def testmaxpp(data):
+       if audioop.maxpp(data[0], 1) <> 0 or \
+                 audioop.maxpp(data[1], 2) <> 0 or \
+                 audioop.maxpp(data[2], 4) <> 0:
+               return 0
+       return 1
+
+def testavg(data):
+       if audioop.avg(data[0], 1) <> 1 or \
+                 audioop.avg(data[1], 2) <> 1 or \
+                 audioop.avg(data[2], 4) <> 1:
+               return 0
+       return 1
+
+def testavgpp(data):
+       if audioop.avgpp(data[0], 1) <> 0 or \
+                 audioop.avgpp(data[1], 2) <> 0 or \
+                 audioop.avgpp(data[2], 4) <> 0:
+               return 0
+       return 1
+
+def testrms(data):
+       if audioop.rms(data[0], 1) <> 1 or \
+                 audioop.rms(data[1], 2) <> 1 or \
+                 audioop.rms(data[2], 4) <> 1:
+               return 0
+       return 1
+
+def testcross(data):
+       if audioop.cross(data[0], 1) <> 0 or \
+                 audioop.cross(data[1], 2) <> 0 or \
+                 audioop.cross(data[2], 4) <> 0:
+               return 0
+       return 1
+
+def testadd(data):
+       data2 = []
+       for d in data:
+               str = ''
+               for s in d:
+                       str = str + chr(ord(s)*2)
+               data2.append(str)
+       if audioop.add(data[0], data[0], 1) <> data2[0] or \
+                 audioop.add(data[1], data[1], 2) <> data2[1] or \
+                 audioop.add(data[2], data[2], 4) <> data2[2]:
+               return 0
+       return 1
+
+def testbias(data):
+       # Note: this test assumes that avg() works
+       d1 = audioop.bias(data[0], 1, 100)
+       d2 = audioop.bias(data[1], 2, 100)
+       d4 = audioop.bias(data[2], 4, 100)
+       if audioop.avg(d1, 1) <> 101 or \
+                 audioop.avg(d2, 2) <> 101 or \
+                 audioop.avg(d4, 4) <> 101:
+               return 0
+       return 1
+
+def testlin2lin(data):
+       # too simple: we test only the size
+       for d1 in data:
+               for d2 in data:
+                       got = len(d1)/3
+                       wtd = len(d2)/3
+                       if len(audioop.lin2lin(d1, got, wtd)) <> len(d2):
+                               return 0
+       return 1
+
+def testadpcm2lin(data):
+       # Very cursory test
+       if audioop.adpcm2lin('\0\0', 1, None) <> ('\0\0\0\0', (0,0)):
+               return 0
+       return 1
+
+def testlin2adpcm(data):
+       # Very cursory test
+       if audioop.lin2adpcm('\0\0\0\0', 1, None) <> ('\0\0', (0,0)):
+               return 0
+       return 1
+
+def testlin2ulaw(data):
+       if audioop.lin2ulaw(data[0], 1) <> '\377\347\333' or \
+                 audioop.lin2ulaw(data[1], 2) <> '\377\377\377' or \
+                 audioop.lin2ulaw(data[2], 4) <> '\377\377\377':
+               return 0
+       return 1
+
+def testulaw2lin(data):
+       # Cursory
+       d = audioop.lin2ulaw(data[0], 1)
+       if audioop.ulaw2lin(d, 1) <> data[0]:
+               return 0
+       return 1
+
+def testmul(data):
+       data2 = []
+       for d in data:
+               str = ''
+               for s in d:
+                       str = str + chr(ord(s)*2)
+               data2.append(str)
+       if audioop.mul(data[0], 1, 2) <> data2[0] or \
+                 audioop.mul(data[1],2, 2) <> data2[1] or \
+                 audioop.mul(data[2], 4, 2) <> data2[2]:
+               return 0
+       return 1
+
+def testreverse(data):
+       if audioop.reverse(data[0], 1) <> '\2\1\0':
+               return 0
+       return 1
+
+def testtomono(data):
+       data2 = ''
+       for d in data[0]:
+               data2 = data2 + d + d
+       if audioop.tomono(data2, 1, 0.5, 0.5) <> data[0]:
+               return 0
+       return 1
+
+def testtostereo(data):
+       data2 = ''
+       for d in data[0]:
+               data2 = data2 + d + d
+       if audioop.tostereo(data[0], 1, 1, 1) <> data2:
+               return 0
+       return 1
+
+def testfindfactor(data):
+       if audioop.findfactor(data[1], data[1]) <> 1.0:
+               return 0
+       return 1
+
+def testfindfit(data):
+       if audioop.findfit(data[1], data[1]) <> (0, 1.0):
+               return 0
+       return 1
+
+def testfindmax(data):
+       if audioop.findmax(data[1], 1) <> 2:
+               return 0
+       return 1
+
+def testgetsample(data):
+       for i in range(3):
+               if audioop.getsample(data[0], 1, i) <> i or \
+                         audioop.getsample(data[1], 2, i) <> i or \
+                         audioop.getsample(data[2], 4, i) <> i:
+                       return 0
+       return 1
+
+def testone(name, data):
+       try:
+               func = eval('test'+name)
+       except NameError:
+               print 'No test found for audioop.'+name+'()'
+               return
+       try:
+               rv = func(data)
+       except 'xx':
+               print 'Test FAILED for audioop.'+name+'() (with an exception)'
+               return
+       if not rv:
+               print 'Test FAILED for audioop.'+name+'()'
+
+def testall():
+       data = [gendata1(), gendata2(), gendata4()]
+       names = dir(audioop)
+       # We know there is a routine 'add'
+       routines = []
+       for n in names:
+               if type(eval('audioop.'+n)) == type(audioop.add):
+                       routines.append(n)
+       for n in routines:
+               testone(n, data)
+testall()
diff --git a/Lib/dos-8x3/test_bui.py b/Lib/dos-8x3/test_bui.py
new file mode 100755 (executable)
index 0000000..33fef8d
--- /dev/null
@@ -0,0 +1,13 @@
+# Python test set -- part 4, built-in functions
+
+from test_support import *
+
+print '4. Built-in functions'
+
+print 'test_b1'
+unload('test_b1')
+import test_b1
+
+print 'test_b2'
+unload('test_b2')
+import test_b2
diff --git a/Lib/dos-8x3/test_exc.py b/Lib/dos-8x3/test_exc.py
new file mode 100755 (executable)
index 0000000..4fbee3e
--- /dev/null
@@ -0,0 +1,92 @@
+# Python test set -- part 5, built-in exceptions
+
+from test_support import *
+
+print '5. Built-in exceptions'
+# XXX This is not really enough, each *operation* should be tested!
+
+def r(name): print name
+
+r(AttributeError)
+import sys
+try: x = sys.undefined_attribute
+except AttributeError: pass
+
+r(EOFError)
+import sys
+fp = open(TESTFN, 'w')
+fp.close()
+fp = open(TESTFN, 'r')
+savestdin = sys.stdin
+try:
+       try:
+               sys.stdin = fp
+               x = raw_input()
+       except EOFError:
+               pass
+finally:
+       sys.stdin = savestdin
+       fp.close()
+
+r(IOError)
+try: open('this file does not exist', 'r')
+except IOError: pass
+
+r(ImportError)
+try: import undefined_module
+except ImportError: pass
+
+r(IndexError)
+x = []
+try: a = x[10]
+except IndexError: pass
+
+r(KeyError)
+x = {}
+try: a = x['key']
+except KeyError: pass
+
+r(KeyboardInterrupt)
+print '(not testable in a script)'
+
+r(MemoryError)
+print '(not safe to test)'
+
+r(NameError)
+try: x = undefined_variable
+except NameError: pass
+
+r(OverflowError)
+x = 1
+try:
+       while 1: x = x+x
+except OverflowError: pass
+
+r(RuntimeError)
+print '(not used any more?)'
+
+r(SyntaxError)
+try: exec '/\n'
+except SyntaxError: pass
+
+r(SystemError)
+print '(hard to reproduce)'
+
+r(SystemExit)
+import sys
+try: sys.exit(0)
+except SystemExit: pass
+
+r(TypeError)
+try: [] + ()
+except TypeError: pass
+
+r(ValueError)
+try: x = chr(10000)
+except ValueError: pass
+
+r(ZeroDivisionError)
+try: x = 1/0
+except ZeroDivisionError: pass
+
+unlink(TESTFN)
diff --git a/Lib/dos-8x3/test_gra.py b/Lib/dos-8x3/test_gra.py
new file mode 100755 (executable)
index 0000000..b9607ac
--- /dev/null
@@ -0,0 +1,513 @@
+# Python test set -- part 1, grammar.
+# This just tests whether the parser accepts them all.
+
+from test_support import *
+
+print '1. Parser'
+
+print '1.1 Tokens'
+
+print '1.1.1 Backslashes'
+
+# Backslash means line continuation:
+x = 1 \
++ 1
+if x <> 2: raise TestFailed, 'backslash for line continuation'
+
+# Backslash does not means continuation in comments :\
+x = 0
+if x <> 0: raise TestFailed, 'backslash ending comment'
+
+print '1.1.2 Numeric literals'
+
+print '1.1.2.1 Plain integers'
+if 0xff <> 255: raise TestFailed, 'hex int'
+if 0377 <> 255: raise TestFailed, 'octal int'
+if  2147483647   != 017777777777: raise TestFailed, 'large positive int'
+try:
+       from sys import maxint
+except ImportError:
+       maxint = 2147483647
+if maxint == 2147483647:
+       if -2147483647-1 != 020000000000: raise TestFailed, 'max negative int'
+       # XXX -2147483648
+       if 037777777777 != -1: raise TestFailed, 'oct -1'
+       if 0xffffffff != -1: raise TestFailed, 'hex -1'
+       for s in '2147483648', '040000000000', '0x100000000':
+               try:
+                       x = eval(s)
+               except OverflowError:
+                       continue
+##             raise TestFailed, \
+               print \
+                         'No OverflowError on huge integer literal ' + `s`
+elif eval('maxint == 9223372036854775807'):
+       if eval('-9223372036854775807-1 != 01000000000000000000000'):
+               raise TestFailed, 'max negative int'
+       if eval('01777777777777777777777') != -1: raise TestFailed, 'oct -1'
+       if eval('0xffffffffffffffff') != -1: raise TestFailed, 'hex -1'
+       for s in '9223372036854775808', '02000000000000000000000', \
+                '0x10000000000000000':
+               try:
+                       x = eval(s)
+               except OverflowError:
+                       continue
+               raise TestFailed, \
+                         'No OverflowError on huge integer literal ' + `s`
+else:
+       print 'Weird maxint value', maxint
+
+print '1.1.2.2 Long integers'
+x = 0L
+x = 0l
+x = 0xffffffffffffffffL
+x = 0xffffffffffffffffl
+x = 077777777777777777L
+x = 077777777777777777l
+x = 123456789012345678901234567890L
+x = 123456789012345678901234567890l
+
+print '1.1.2.3 Floating point'
+x = 3.14
+x = 314.
+x = 0.314
+# XXX x = 000.314
+x = .314
+x = 3e14
+x = 3E14
+x = 3e-14
+x = 3e+14
+x = 3.e14
+x = .3e14
+x = 3.1e4
+
+print '1.1.3 String literals'
+
+def assert(s):
+       if not s: raise TestFailed, 'see traceback'
+
+x = ''; y = ""; assert(len(x) == 0 and x == y)
+x = '\''; y = "'"; assert(len(x) == 1 and x == y and ord(x) == 39)
+x = '"'; y = "\""; assert(len(x) == 1 and x == y and ord(x) == 34)
+x = "doesn't \"shrink\" does it"
+y = 'doesn\'t "shrink" does it'
+assert(len(x) == 24 and x == y)
+x = "does \"shrink\" doesn't it"
+y = 'does "shrink" doesn\'t it'
+assert(len(x) == 24 and x == y)
+x = """
+The "quick"
+brown fox
+jumps over
+the 'lazy' dog.
+"""
+y = '\nThe "quick"\nbrown fox\njumps over\nthe \'lazy\' dog.\n'
+assert(x == y)
+y = '''
+The "quick"
+brown fox
+jumps over
+the 'lazy' dog.
+'''; assert(x == y)
+y = "\n\
+The \"quick\"\n\
+brown fox\n\
+jumps over\n\
+the 'lazy' dog.\n\
+"; assert(x == y)
+y = '\n\
+The \"quick\"\n\
+brown fox\n\
+jumps over\n\
+the \'lazy\' dog.\n\
+'; assert(x == y)
+
+
+print '1.2 Grammar'
+
+print 'single_input' # NEWLINE | simple_stmt | compound_stmt NEWLINE
+# XXX can't test in a script -- this rule is only used when interactive
+
+print 'file_input' # (NEWLINE | stmt)* ENDMARKER
+# Being tested as this very moment this very module
+
+print 'expr_input' # testlist NEWLINE
+# XXX Hard to test -- used only in calls to input()
+
+print 'eval_input' # testlist ENDMARKER
+x = eval('1, 0 or 1')
+
+print 'funcdef'
+### 'def' NAME parameters ':' suite
+### parameters: '(' [varargslist] ')'
+### varargslist: (fpdef ['=' test] ',')* '*' NAME
+###            | fpdef ['=' test] (',' fpdef ['=' test])* [',']
+### fpdef: NAME | '(' fplist ')'
+### fplist: fpdef (',' fpdef)* [',']
+def f1(): pass
+def f2(one_argument): pass
+def f3(two, arguments): pass
+def f4(two, (compound, (argument, list))): pass
+def a1(one_arg,): pass
+def a2(two, args,): pass
+def v0(*rest): pass
+def v1(a, *rest): pass
+def v2(a, b, *rest): pass
+def v3(a, (b, c), *rest): pass
+def d01(a=1): pass
+d01()
+d01(1)
+def d11(a, b=1): pass
+d11(1)
+d11(1, 2)
+def d21(a, b, c=1): pass
+d21(1, 2)
+d21(1, 2, 3)
+def d02(a=1, b=2): pass
+d02()
+d02(1)
+d02(1, 2)
+def d12(a, b=1, c=2): pass
+d12(1)
+d12(1, 2)
+d12(1, 2, 3)
+def d22(a, b, c=1, d=2): pass
+d22(1, 2)
+d22(1, 2, 3)
+d22(1, 2, 3, 4)
+def d01v(a=1, *rest): pass
+d01v()
+d01v(1)
+d01v(1, 2)
+def d11v(a, b=1, *rest): pass
+d11v(1)
+d11v(1, 2)
+d11v(1, 2, 3)
+def d21v(a, b, c=1, *rest): pass
+d21v(1, 2)
+d21v(1, 2, 3)
+d21v(1, 2, 3, 4)
+def d02v(a=1, b=2, *rest): pass
+d02v()
+d02v(1)
+d02v(1, 2)
+d02v(1, 2, 3)
+def d12v(a, b=1, c=2, *rest): pass
+d12v(1)
+d12v(1, 2)
+d12v(1, 2, 3)
+d12v(1, 2, 3, 4)
+def d22v(a, b, c=1, d=2, *rest): pass
+d22v(1, 2)
+d22v(1, 2, 3)
+d22v(1, 2, 3, 4)
+d22v(1, 2, 3, 4, 5)
+
+### stmt: simple_stmt | compound_stmt
+# Tested below
+
+### simple_stmt: small_stmt (';' small_stmt)* [';']
+print 'simple_stmt'
+x = 1; pass; del x
+
+### small_stmt: expr_stmt | print_stmt  | pass_stmt | del_stmt | flow_stmt | import_stmt | global_stmt | access_stmt | exec_stmt
+# Tested below
+
+print 'expr_stmt' # (exprlist '=')* exprlist
+1
+1, 2, 3
+x = 1
+x = 1, 2, 3
+x = y = z = 1, 2, 3
+x, y, z = 1, 2, 3
+abc = a, b, c = x, y, z = xyz = 1, 2, (3, 4)
+# NB these variables are deleted below
+
+print 'print_stmt' # 'print' (test ',')* [test]
+print 1, 2, 3
+print 1, 2, 3,
+print
+print 0 or 1, 0 or 1,
+print 0 or 1
+
+print 'del_stmt' # 'del' exprlist
+del abc
+del x, y, (z, xyz)
+
+print 'pass_stmt' # 'pass'
+pass
+
+print 'flow_stmt' # break_stmt | continue_stmt | return_stmt | raise_stmt
+# Tested below
+
+print 'break_stmt' # 'break'
+while 1: break
+
+print 'continue_stmt' # 'continue'
+i = 1
+while i: i = 0; continue
+
+print 'return_stmt' # 'return' [testlist]
+def g1(): return
+def g2(): return 1
+g1()
+x = g2()
+
+print 'raise_stmt' # 'raise' test [',' test]
+try: raise RuntimeError, 'just testing'
+except RuntimeError: pass
+try: raise KeyboardInterrupt
+except KeyboardInterrupt: pass
+
+print 'import_stmt' # 'import' NAME (',' NAME)* | 'from' NAME 'import' ('*' | NAME (',' NAME)*)
+import sys
+import time, sys
+from time import time
+from sys import *
+from sys import path, argv
+
+print 'global_stmt' # 'global' NAME (',' NAME)*
+def f():
+       global a
+       global a, b
+       global one, two, three, four, five, six, seven, eight, nine, ten
+
+print 'exec_stmt' # 'exec' expr ['in' expr [',' expr]]
+def f():
+       z = None
+       del z
+       exec 'z=1+1\n'
+       if z <> 2: raise TestFailed, 'exec \'z=1+1\'\\n'
+       del z
+       exec 'z=1+1'
+       if z <> 2: raise TestFailed, 'exec \'z=1+1\''
+f()
+g = {}
+exec 'z = 1' in g
+if g.has_key('__builtins__'): del g['__builtins__']
+if g <> {'z': 1}: raise TestFailed, 'exec \'z = 1\' in g'
+g = {}
+l = {}
+exec 'global a; a = 1; b = 2' in g, l
+if g.has_key('__builtins__'): del g['__builtins__']
+if l.has_key('__builtins__'): del l['__builtins__']
+if (g, l) <> ({'a':1}, {'b':2}): raise TestFailed, 'exec ... in g, l'
+
+
+### compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | funcdef | classdef
+# Tested below
+
+print 'if_stmt' # 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
+if 1: pass
+if 1: pass
+else: pass
+if 0: pass
+elif 0: pass
+if 0: pass
+elif 0: pass
+elif 0: pass
+elif 0: pass
+else: pass
+
+print 'while_stmt' # 'while' test ':' suite ['else' ':' suite]
+while 0: pass
+while 0: pass
+else: pass
+
+print 'for_stmt' # 'for' exprlist 'in' exprlist ':' suite ['else' ':' suite]
+for i in 1, 2, 3: pass
+for i, j, k in (): pass
+else: pass
+class Squares:
+       def __init__(self, max):
+               self.max = max
+               self.sofar = []
+       def __len__(self): return len(self.sofar)
+       def __getitem__(self, i):
+               if not 0 <= i < self.max: raise IndexError
+               n = len(self.sofar)
+               while n <= i:
+                       self.sofar.append(n*n)
+                       n = n+1
+               return self.sofar[i]
+n = 0
+for x in Squares(10): n = n+x
+if n != 285: raise TestFailed, 'for over growing sequence'
+
+print 'try_stmt'
+### try_stmt: 'try' ':' suite (except_clause ':' suite)+ ['else' ':' suite]
+###         | 'try' ':' suite 'finally' ':' suite
+### except_clause: 'except' [expr [',' expr]]
+try:
+       1/0
+except ZeroDivisionError:
+       pass
+else:
+       pass
+try: 1/0
+except EOFError: pass
+except TypeError, msg: pass
+except RuntimeError, msg: pass
+except: pass
+else: pass
+try: 1/0
+except (EOFError, TypeError, ZeroDivisionError): pass
+try: 1/0
+except (EOFError, TypeError, ZeroDivisionError), msg: pass
+try: pass
+finally: pass
+
+print 'suite' # simple_stmt | NEWLINE INDENT NEWLINE* (stmt NEWLINE*)+ DEDENT
+if 1: pass
+if 1:
+       pass
+if 1:
+       #
+       #
+       #
+       pass
+       pass
+       #
+       pass
+       #
+
+print 'test'
+### and_test ('or' and_test)*
+### and_test: not_test ('and' not_test)*
+### not_test: 'not' not_test | comparison
+if not 1: pass
+if 1 and 1: pass
+if 1 or 1: pass
+if not not not 1: pass
+if not 1 and 1 and 1: pass
+if 1 and 1 or 1 and 1 and 1 or not 1 and 1: pass
+
+print 'comparison'
+### comparison: expr (comp_op expr)*
+### comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not'
+if 1: pass
+x = (1 == 1)
+if 1 == 1: pass
+if 1 != 1: pass
+if 1 <> 1: pass
+if 1 < 1: pass
+if 1 > 1: pass
+if 1 <= 1: pass
+if 1 >= 1: pass
+if 1 is 1: pass
+if 1 is not 1: pass
+if 1 in (): pass
+if 1 not in (): pass
+if 1 < 1 > 1 == 1 >= 1 <= 1 <> 1 != 1 in 1 not in 1 is 1 is not 1: pass
+
+print 'binary mask ops'
+x = 1 & 1
+x = 1 ^ 1
+x = 1 | 1
+
+print 'shift ops'
+x = 1 << 1
+x = 1 >> 1
+x = 1 << 1 >> 1
+
+print 'additive ops'
+x = 1
+x = 1 + 1
+x = 1 - 1 - 1
+x = 1 - 1 + 1 - 1 + 1
+
+print 'multiplicative ops'
+x = 1 * 1
+x = 1 / 1
+x = 1 % 1
+x = 1 / 1 * 1 % 1
+
+print 'unary ops'
+x = +1
+x = -1
+x = ~1
+x = ~1 ^ 1 & 1 | 1 & 1 ^ -1
+x = -1*1/1 + 1*1 - ---1*1
+
+print 'selectors'
+### trailer: '(' [testlist] ')' | '[' subscript ']' | '.' NAME
+### subscript: expr | [expr] ':' [expr]
+f1()
+f2(1)
+f2(1,)
+f3(1, 2)
+f3(1, 2,)
+f4(1, (2, (3, 4)))
+v0()
+v0(1)
+v0(1,)
+v0(1,2)
+v0(1,2,3,4,5,6,7,8,9,0)
+v1(1)
+v1(1,)
+v1(1,2)
+v1(1,2,3)
+v1(1,2,3,4,5,6,7,8,9,0)
+v2(1,2)
+v2(1,2,3)
+v2(1,2,3,4)
+v2(1,2,3,4,5,6,7,8,9,0)
+v3(1,(2,3))
+v3(1,(2,3),4)
+v3(1,(2,3),4,5,6,7,8,9,0)
+import sys, time
+c = sys.path[0]
+x = time.time()
+x = sys.modules['time'].time()
+a = '01234'
+c = a[0]
+c = a[-1]
+s = a[0:5]
+s = a[:5]
+s = a[0:]
+s = a[:]
+s = a[-5:]
+s = a[:-1]
+s = a[-4:-3]
+
+print 'atoms'
+### atom: '(' [testlist] ')' | '[' [testlist] ']' | '{' [dictmaker] '}' | '`' testlist '`' | NAME | NUMBER | STRING
+### dictmaker: test ':' test (',' test ':' test)* [',']
+
+x = (1)
+x = (1 or 2 or 3)
+x = (1 or 2 or 3, 2, 3)
+
+x = []
+x = [1]
+x = [1 or 2 or 3]
+x = [1 or 2 or 3, 2, 3]
+x = []
+
+x = {}
+x = {'one': 1}
+x = {'one': 1,}
+x = {'one' or 'two': 1 or 2}
+x = {'one': 1, 'two': 2}
+x = {'one': 1, 'two': 2,}
+x = {'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5, 'six': 6}
+
+x = `x`
+x = `1 or 2 or 3`
+x = x
+x = 'x'
+x = 123
+
+### exprlist: expr (',' expr)* [',']
+### testlist: test (',' test)* [',']
+# These have been exercised enough above
+
+print 'classdef' # 'class' NAME ['(' testlist ')'] ':' suite
+class B: pass
+class C1(B): pass
+class C2(B): pass
+class D(C1, C2, B): pass
+class C:
+       def meth1(self): pass
+       def meth2(self, arg): pass
+       def meth3(self, a1, a2): pass
diff --git a/Lib/dos-8x3/test_opc.py b/Lib/dos-8x3/test_opc.py
new file mode 100755 (executable)
index 0000000..b1e944b
--- /dev/null
@@ -0,0 +1,59 @@
+# Python test set -- part 2, opcodes
+
+from test_support import *
+
+
+print '2. Opcodes'
+print 'XXX Not yet fully implemented'
+
+print '2.1 try inside for loop'
+n = 0
+for i in range(10):
+       n = n+i
+       try: 1/0
+       except NameError: pass
+       except ZeroDivisionError: pass
+       except TypeError: pass
+       try: pass
+       except: pass
+       try: pass
+       finally: pass
+       n = n+i
+if n <> 90:
+       raise TestFailed, 'try inside for'
+
+
+print '2.2 raise class exceptions'
+
+class AClass: pass
+class BClass(AClass): pass
+class CClass: pass
+
+try: raise AClass()
+except: pass
+
+try: raise AClass()
+except AClass: pass
+
+try: raise BClass()
+except AClass: pass
+
+try: raise BClass()
+except CClass: raise TestFailed
+except: pass
+
+a = AClass()
+b = BClass()
+
+try: raise AClass, b
+except BClass, v: raise TestFailed
+except AClass, v:
+       if v != b: raise TestFailed
+
+
+try: raise b
+except AClass, v:
+       if v != b: raise TestFailed
+
+try:  raise BClass, a
+except TypeError: pass
diff --git a/Lib/dos-8x3/test_ope.py b/Lib/dos-8x3/test_ope.py
new file mode 100755 (executable)
index 0000000..1a75065
--- /dev/null
@@ -0,0 +1,5 @@
+# Python test set -- part 3, built-in operations.
+
+
+print '3. Operations'
+print 'XXX Not yet implemented'
diff --git a/Lib/dos-8x3/test_rgb.py b/Lib/dos-8x3/test_rgb.py
new file mode 100755 (executable)
index 0000000..cdadc66
--- /dev/null
@@ -0,0 +1,49 @@
+# Testing rgbimg module
+
+import rgbimg, os
+
+error = 'test_rgbimg.error'
+
+print 'RGBimg test suite:'
+
+def findfile(file):
+       if os.path.isabs(file): return file
+       import sys
+       for dn in sys.path:
+               fn = os.path.join(dn, file)
+               if os.path.exists(fn): return fn
+       return file
+
+def testimg(rgb_file, raw_file):
+       rgb_file = findfile(rgb_file)
+       raw_file = findfile(raw_file)
+       width, height = rgbimg.sizeofimage(rgb_file)
+       rgb = rgbimg.longimagedata(rgb_file)
+       if len(rgb) != width * height * 4:
+               raise error, 'bad image length'
+       raw = open(raw_file, 'r').read()
+       if rgb != raw:
+               raise error, 'images don\'t match for '+rgb_file+' and '+raw_file
+       for depth in [1, 3, 4]:
+               rgbimg.longstoimage(rgb, width, height, depth, '@.rgb')
+       os.unlink('@.rgb')
+
+ttob = rgbimg.ttob(0)
+if ttob != 0:
+       raise error, 'ttob should start out as zero'
+
+testimg('test.rgb', 'test.rawimg')
+
+ttob = rgbimg.ttob(1)
+if ttob != 0:
+       raise error, 'ttob should be zero'
+
+testimg('test.rgb', 'test.rawimg.rev')
+
+ttob = rgbimg.ttob(0)
+if ttob != 1:
+       raise error, 'ttob should be one'
+
+ttob = rgbimg.ttob(0)
+if ttob != 0:
+       raise error, 'ttob should be zero'
diff --git a/Lib/dos-8x3/test_sel.py b/Lib/dos-8x3/test_sel.py
new file mode 100755 (executable)
index 0000000..f185308
--- /dev/null
@@ -0,0 +1,23 @@
+# Testing select module
+
+def test():
+       import select
+       import os
+       cmd = 'for i in 0 1 2 3 4 5 6 7 8 9; do date; sleep 3; done'
+       p = os.popen(cmd, 'r')
+       for tout in (0, 1, 2, 4, 8, 16) + (None,)*10:
+               print 'timeout =', tout
+               rfd, wfd, xfd = select.select([p], [], [], tout)
+               print rfd, wfd, xfd
+               if (rfd, wfd, xfd) == ([], [], []):
+                       continue
+               if (rfd, wfd, xfd) == ([p], [], []):
+                       line = p.readline()
+                       print `line`
+                       if not line:
+                               print 'EOF'
+                               break
+                       continue
+               print 'Heh?'
+
+test()
diff --git a/Lib/dos-8x3/test_sig.py b/Lib/dos-8x3/test_sig.py
new file mode 100755 (executable)
index 0000000..bfcf517
--- /dev/null
@@ -0,0 +1,50 @@
+# Test the signal module
+
+import signal
+import os
+
+
+pid = os.getpid()
+
+# Shell script that will send us asynchronous signals
+script = """
+(
+       set -x
+       sleep 2
+       kill -5 %(pid)d
+       sleep 2
+       kill -2 %(pid)d
+       sleep 2
+       kill -3 %(pid)d
+) &
+""" % vars()
+
+def handlerA(*args):
+       print "handlerA", args
+
+HandlerBCalled = "HandlerBCalled"      # Exception
+
+def handlerB(*args):
+       print "handlerB", args
+       raise HandlerBCalled, args
+
+signal.alarm(20)                       # Entire test lasts at most 20 sec.
+signal.signal(5, handlerA)
+signal.signal(2, handlerB)
+signal.signal(3, signal.SIG_IGN)
+signal.signal(signal.SIGALRM, signal.default_int_handler)
+
+os.system(script)
+
+print "starting pause() loop..."
+
+try:
+       while 1:
+               print "call pause()..."
+               try:
+                       signal.pause()
+                       print "pause() returned"
+               except HandlerBCalled:
+                       print "HandlerBCalled exception caught"
+except KeyboardInterrupt:
+       print "KeyboardInterrupt (assume the alarm() went off)"
diff --git a/Lib/dos-8x3/test_sup.py b/Lib/dos-8x3/test_sup.py
new file mode 100755 (executable)
index 0000000..7a76664
--- /dev/null
@@ -0,0 +1,41 @@
+# Python test set -- supporting definitions.
+
+TestFailed = 'test_support -- test failed'     # Exception
+
+def unload(name):
+       import sys
+       try:
+               del sys.modules[name]
+       except KeyError:
+               pass
+
+def forget(modname):
+       unload(modname)
+       import sys, os
+       for dirname in sys.path:
+               try:
+                       os.unlink(os.path.join(dirname, modname + '.pyc'))
+               except os.error:
+                       pass
+
+FUZZ = 1e-6
+
+def fcmp(x, y): # fuzzy comparison function
+       if type(x) == type(0.0) or type(y) == type(0.0):
+               try:
+                       x, y = coerce(x, y)
+                       fuzz = (abs(x) + abs(y)) * FUZZ
+                       if abs(x-y) <= fuzz:
+                               return 0
+               except:
+                       pass
+       elif type(x) == type(y) and type(x) in (type(()), type([])):
+               for i in range(min(len(x), len(y))):
+                       outcome = fcmp(x[i], y[i])
+                       if outcome <> 0:
+                               return outcome
+               return cmp(len(x), len(y))
+       return cmp(x, y)
+
+TESTFN = '@test' # Filename used for testing
+from os import unlink
diff --git a/Lib/dos-8x3/test_thr.py b/Lib/dos-8x3/test_thr.py
new file mode 100755 (executable)
index 0000000..4e0eb70
--- /dev/null
@@ -0,0 +1,106 @@
+# Very rudimentary test of thread module
+
+# Create a bunch of threads, let each do some work, wait until all are done
+
+import whrandom
+import thread
+import time
+
+mutex = thread.allocate_lock()
+whmutex = thread.allocate_lock() # for calls to whrandom
+running = 0
+done = thread.allocate_lock()
+done.acquire()
+
+numtasks = 10
+
+def task(ident):
+       global running
+       whmutex.acquire()
+       delay = whrandom.random() * numtasks
+       whmutex.release()
+       print 'task', ident, 'will run for', delay, 'sec'
+       time.sleep(delay)
+       print 'task', ident, 'done'
+       mutex.acquire()
+       running = running - 1
+       if running == 0:
+               done.release()
+       mutex.release()
+
+next_ident = 0
+def newtask():
+       global next_ident, running
+       mutex.acquire()
+       next_ident = next_ident + 1
+       print 'creating task', next_ident
+       thread.start_new_thread(task, (next_ident,))
+       running = running + 1
+       mutex.release()
+
+for i in range(numtasks):
+       newtask()
+
+print 'waiting for all tasks to complete'
+done.acquire()
+print 'all tasks done'
+
+class barrier:
+       def __init__(self, n):
+               self.n = n
+               self.waiting = 0
+               self.checkin  = thread.allocate_lock()
+               self.checkout = thread.allocate_lock()
+               self.checkout.acquire()
+
+       def enter(self):
+               checkin, checkout = self.checkin, self.checkout
+
+               checkin.acquire()
+               self.waiting = self.waiting + 1
+               if self.waiting == self.n:
+                       self.waiting = self.n - 1
+                       checkout.release()
+                       return
+               checkin.release()
+
+               checkout.acquire()
+               self.waiting = self.waiting - 1
+               if self.waiting == 0:
+                       checkin.release()
+                       return
+               checkout.release()
+
+numtrips = 3
+def task2(ident):
+       global running
+       for i in range(numtrips):
+               if ident == 0:
+                       # give it a good chance to enter the next
+                       # barrier before the others are all out
+                       # of the current one
+                       delay = 0.001
+               else:
+                       whmutex.acquire()
+                       delay = whrandom.random() * numtasks
+                       whmutex.release()
+               print 'task', ident, 'will run for', delay, 'sec'
+               time.sleep(delay)
+               print 'task', ident, 'entering barrier', i
+               bar.enter()
+               print 'task', ident, 'leaving barrier', i
+       mutex.acquire()
+       running = running - 1
+       if running == 0:
+               done.release()
+       mutex.release()
+
+print '\n*** Barrier Test ***'
+if done.acquire(0):
+       raise ValueError, "'done' should have remained acquired"
+bar = barrier(numtasks)
+running = numtasks
+for i in range(numtasks):
+       thread.start_new_thread(task2, (i,))
+done.acquire()
+print 'all tasks done'
diff --git a/Lib/dos-8x3/test_typ.py b/Lib/dos-8x3/test_typ.py
new file mode 100755 (executable)
index 0000000..51c76dc
--- /dev/null
@@ -0,0 +1,182 @@
+# Python test set -- part 6, built-in types
+
+from test_support import *
+
+print '6. Built-in types'
+
+print '6.1 Truth value testing'
+if None: raise TestFailed, 'None is true instead of false'
+if 0: raise TestFailed, '0 is true instead of false'
+if 0L: raise TestFailed, '0L is true instead of false'
+if 0.0: raise TestFailed, '0.0 is true instead of false'
+if '': raise TestFailed, '\'\' is true instead of false'
+if (): raise TestFailed, '() is true instead of false'
+if []: raise TestFailed, '[] is true instead of false'
+if {}: raise TestFailed, '{} is true instead of false'
+if not 1: raise TestFailed, '1 is false instead of true'
+if not 1L: raise TestFailed, '1L is false instead of true'
+if not 1.0: raise TestFailed, '1.0 is false instead of true'
+if not 'x': raise TestFailed, '\'x\' is false instead of true'
+if not (1, 1): raise TestFailed, '(1, 1) is false instead of true'
+if not [1]: raise TestFailed, '[1] is false instead of true'
+if not {'x': 1}: raise TestFailed, '{\'x\': 1} is false instead of true'
+def f(): pass
+class C: pass
+import sys
+x = C()
+if not f: raise TestFailed, 'f is false instead of true'
+if not C: raise TestFailed, 'C is false instead of true'
+if not sys: raise TestFailed, 'sys is false instead of true'
+if not x: raise TestFailed, 'x is false instead of true'
+
+print '6.2 Boolean operations'
+if 0 or 0: raise TestFailed, '0 or 0 is true instead of false'
+if 1 and 1: pass
+else: raise TestFailed, '1 and 1 is false instead of false'
+if not 1: raise TestFailed, 'not 1 is true instead of false'
+
+print '6.3 Comparisons'
+if 0 < 1 <= 1 == 1 >= 1 > 0 <> 1: pass
+else: raise TestFailed, 'int comparisons failed'
+if 0L < 1L <= 1L == 1L >= 1L > 0L <> 1L: pass
+else: raise TestFailed, 'long int comparisons failed'
+if 0.0 < 1.0 <= 1.0 == 1.0 >= 1.0 > 0.0 <> 1.0: pass
+else: raise TestFailed, 'float comparisons failed'
+if '' < 'a' <= 'a' == 'a' < 'abc' < 'abd' < 'b': pass
+else: raise TestFailed, 'string comparisons failed'
+if 0 in [0] and 0 not in [1]: pass
+else: raise TestFailed, 'membership test failed'
+if None is None and [] is not []: pass
+else: raise TestFailed, 'identity test failed'
+
+print '6.4 Numeric types (mostly conversions)'
+if 0 <> 0L or 0 <> 0.0 or 0L <> 0.0: raise TestFailed, 'mixed comparisons'
+if 1 <> 1L or 1 <> 1.0 or 1L <> 1.0: raise TestFailed, 'mixed comparisons'
+if -1 <> -1L or -1 <> -1.0 or -1L <> -1.0:
+       raise TestFailed, 'int/long/float value not equal'
+if int(1.9) == 1 == int(1.1) and int(-1.1) == -1 == int(-1.9): pass
+else: raise TestFailed, 'int() does not round properly'
+if long(1.9) == 1L == long(1.1) and long(-1.1) == -1L == long(-1.9): pass
+else: raise TestFailed, 'long() does not round properly'
+if float(1) == 1.0 and float(-1) == -1.0 and float(0) == 0.0: pass
+else: raise TestFailed, 'float() does not work properly'
+print '6.4.1 32-bit integers'
+if 12 + 24 <> 36: raise TestFailed, 'int op'
+if 12 + (-24) <> -12: raise TestFailed, 'int op'
+if (-12) + 24 <> 12: raise TestFailed, 'int op'
+if (-12) + (-24) <> -36: raise TestFailed, 'int op'
+if not 12 < 24: raise TestFailed, 'int op'
+if not -24 < -12: raise TestFailed, 'int op'
+# Test for a particular bug in integer multiply
+xsize, ysize, zsize = 238, 356, 4
+if not (xsize*ysize*zsize == zsize*xsize*ysize == 338912):
+       raise TestFailed, 'int mul commutativity'
+print '6.4.2 Long integers'
+if 12L + 24L <> 36L: raise TestFailed, 'long op'
+if 12L + (-24L) <> -12L: raise TestFailed, 'long op'
+if (-12L) + 24L <> 12L: raise TestFailed, 'long op'
+if (-12L) + (-24L) <> -36L: raise TestFailed, 'long op'
+if not 12L < 24L: raise TestFailed, 'long op'
+if not -24L < -12L: raise TestFailed, 'long op'
+print '6.4.3 Floating point numbers'
+if 12.0 + 24.0 <> 36.0: raise TestFailed, 'float op'
+if 12.0 + (-24.0) <> -12.0: raise TestFailed, 'float op'
+if (-12.0) + 24.0 <> 12.0: raise TestFailed, 'float op'
+if (-12.0) + (-24.0) <> -36.0: raise TestFailed, 'float op'
+if not 12.0 < 24.0: raise TestFailed, 'float op'
+if not -24.0 < -12.0: raise TestFailed, 'float op'
+
+print '6.5 Sequence types'
+
+print '6.5.1 Strings'
+if len('') <> 0: raise TestFailed, 'len(\'\')'
+if len('a') <> 1: raise TestFailed, 'len(\'a\')'
+if len('abcdef') <> 6: raise TestFailed, 'len(\'abcdef\')'
+if 'xyz' + 'abcde' <> 'xyzabcde': raise TestFailed, 'string concatenation'
+if 'xyz'*3 <> 'xyzxyzxyz': raise TestFailed, 'string repetition *3'
+if 0*'abcde' <> '': raise TestFailed, 'string repetition 0*'
+if min('abc') <> 'a' or max('abc') <> 'c': raise TestFailed, 'min/max string'
+if 'a' in 'abc' and 'b' in 'abc' and 'c' in 'abc' and 'd' not in 'abc': pass
+else: raise TestFailed, 'in/not in string'
+x = 'x'*103
+if '%s!'%x != x+'!': raise TestFailed, 'nasty string formatting bug'
+
+print '6.5.2 Tuples'
+if len(()) <> 0: raise TestFailed, 'len(())'
+if len((1,)) <> 1: raise TestFailed, 'len((1,))'
+if len((1,2,3,4,5,6)) <> 6: raise TestFailed, 'len((1,2,3,4,5,6))'
+if (1,2)+(3,4) <> (1,2,3,4): raise TestFailed, 'tuple concatenation'
+if (1,2)*3 <> (1,2,1,2,1,2): raise TestFailed, 'tuple repetition *3'
+if 0*(1,2,3) <> (): raise TestFailed, 'tuple repetition 0*'
+if min((1,2)) <> 1 or max((1,2)) <> 2: raise TestFailed, 'min/max tuple'
+if 0 in (0,1,2) and 1 in (0,1,2) and 2 in (0,1,2) and 3 not in (0,1,2): pass
+else: raise TestFailed, 'in/not in tuple'
+
+print '6.5.3 Lists'
+if len([]) <> 0: raise TestFailed, 'len([])'
+if len([1,]) <> 1: raise TestFailed, 'len([1,])'
+if len([1,2,3,4,5,6]) <> 6: raise TestFailed, 'len([1,2,3,4,5,6])'
+if [1,2]+[3,4] <> [1,2,3,4]: raise TestFailed, 'list concatenation'
+if [1,2]*3 <> [1,2,1,2,1,2]: raise TestFailed, 'list repetition *3'
+if 0*[1,2,3] <> []: raise TestFailed, 'list repetition 0*'
+if min([1,2]) <> 1 or max([1,2]) <> 2: raise TestFailed, 'min/max list'
+if 0 in [0,1,2] and 1 in [0,1,2] and 2 in [0,1,2] and 3 not in [0,1,2]: pass
+else: raise TestFailed, 'in/not in list'
+
+print '6.5.3a Additional list operations'
+a = [0,1,2,3,4]
+a[0] = 5
+a[1] = 6
+a[2] = 7
+if a <> [5,6,7,3,4]: raise TestFailed, 'list item assignment [0], [1], [2]'
+a[-2] = 8
+a[-1] = 9
+if a <> [5,6,7,8,9]: raise TestFailed, 'list item assignment [-2], [-1]'
+a[:2] = [0,4]
+a[-3:] = []
+a[1:1] = [1,2,3]
+if a <> [0,1,2,3,4]: raise TestFailed, 'list slice assignment'
+del a[1:4]
+if a <> [0,4]: raise TestFailed, 'list slice deletion'
+del a[0]
+if a <> [4]: raise TestFailed, 'list item deletion [0]'
+del a[-1]
+if a <> []: raise TestFailed, 'list item deletion [-1]'
+a.append(0)
+a.append(1)
+a.append(2)
+if a <> [0,1,2]: raise TestFailed, 'list append'
+a.insert(0, -2)
+a.insert(1, -1)
+a.insert(2,0)
+if a <> [-2,-1,0,0,1,2]: raise TestFailed, 'list insert'
+if a.count(0) <> 2: raise TestFailed, ' list count'
+if a.index(0) <> 2: raise TestFailed, 'list index'
+a.remove(0)
+if a <> [-2,-1,0,1,2]: raise TestFailed, 'list remove'
+a.reverse()
+if a <> [2,1,0,-1,-2]: raise TestFailed, 'list reverse'
+a.sort()
+if a <> [-2,-1,0,1,2]: raise TestFailed, 'list sort'
+def revcmp(a, b): return cmp(b, a)
+a.sort(revcmp)
+if a <> [2,1,0,-1,-2]: raise TestFailed, 'list sort with cmp func'
+
+print '6.6 Mappings == Dictionaries'
+d = {}
+if d.keys() <> []: raise TestFailed, '{}.keys()'
+if d.has_key('a') <> 0: raise TestFailed, '{}.has_key(\'a\')'
+if len(d) <> 0: raise TestFailed, 'len({})'
+d = {'a': 1, 'b': 2}
+if len(d) <> 2: raise TestFailed, 'len(dict)'
+k = d.keys()
+k.sort()
+if k <> ['a', 'b']: raise TestFailed, 'dict keys()'
+if d.has_key('a') and d.has_key('b') and not d.has_key('c'): pass
+else: raise TestFailed, 'dict keys()'
+if d['a'] <> 1 or d['b'] <> 2: raise TestFailed, 'dict item'
+d['c'] = 3
+d['a'] = 4
+if d['c'] <> 3 or d['a'] <> 4: raise TestFailed, 'dict item assignment'
+del d['b']
+if d <> {'a': 4, 'c': 3}: raise TestFailed, 'dict item deletion'
diff --git a/Lib/dos-8x3/tracebac.py b/Lib/dos-8x3/tracebac.py
new file mode 100755 (executable)
index 0000000..8d2dfdc
--- /dev/null
@@ -0,0 +1,125 @@
+# Format and print Python stack traces
+
+import linecache
+import string
+import sys
+import types
+
+def _print(file, str='', terminator='\n'):
+       file.write(str+terminator)
+       
+
+def print_tb(tb, limit=None, file=None):
+       if not file:
+               file = sys.stderr
+       if limit is None:
+               if hasattr(sys, 'tracebacklimit'):
+                       limit = sys.tracebacklimit
+       n = 0
+       while tb is not None and (limit is None or n < limit):
+               f = tb.tb_frame
+               lineno = tb.tb_lineno
+               co = f.f_code
+               filename = co.co_filename
+               name = co.co_name
+               _print(file,
+                      '  File "%s", line %d, in %s' % (filename,lineno,name))
+               line = linecache.getline(filename, lineno)
+               if line: _print(file, '    ' + string.strip(line))
+               tb = tb.tb_next
+               n = n+1
+
+def format_tb(tb, limit = None):
+       list = []
+       for filename, lineno, name, line in extract_tb(tb, limit):
+               item = '  File "%s", line %d, in %s\n' % (filename,lineno,name)
+               if line:
+                       item = item + '    %s\n' % string.strip(line)
+               list.append(item)
+       return list
+
+def extract_tb(tb, limit = None):
+       if limit is None:
+               if hasattr(sys, 'tracebacklimit'):
+                       limit = sys.tracebacklimit
+       list = []
+       n = 0
+       while tb is not None and (limit is None or n < limit):
+               f = tb.tb_frame
+               lineno = tb.tb_lineno
+               co = f.f_code
+               filename = co.co_filename
+               name = co.co_name
+               line = linecache.getline(filename, lineno)
+               if line: line = string.strip(line)
+               else: line = None
+               list.append(filename, lineno, name, line)
+               tb = tb.tb_next
+               n = n+1
+       return list
+
+
+def print_exception(etype, value, tb, limit=None, file=None):
+       if not file:
+               file = sys.stderr
+       if tb:
+               _print(file, 'Traceback (innermost last):')
+               print_tb(tb, limit, file)
+       lines = format_exception_only(etype, value)
+       for line in lines[:-1]:
+               _print(file, line, ' ')
+       _print(file, lines[-1], '')
+
+def format_exception(etype, value, tb, limit = None):
+       if tb:
+               list = ['Traceback (innermost last):\n']
+               list = list + format_tb(tb, limit)
+       list = list + format_exception_only(etype, value)
+       return list
+
+def format_exception_only(etype, value):
+       list = []
+       if type(etype) == types.ClassType:
+               stype = etype.__name__
+       else:
+               stype = etype
+       if value is None:
+               list.append(str(stype) + '\n')
+       else:
+               if etype is SyntaxError:
+                       try:
+                               msg, (filename, lineno, offset, line) = value
+                       except:
+                               pass
+                       else:
+                               if not filename: filename = "<string>"
+                               list.append('  File "%s", line %d\n' %
+                                           (filename, lineno))
+                               i = 0
+                               while i < len(line) and \
+                                     line[i] in string.whitespace:
+                                       i = i+1
+                               list.append('    %s\n' % string.strip(line))
+                               s = '    '
+                               for c in line[i:offset-1]:
+                                       if c in string.whitespace:
+                                               s = s + c
+                                       else:
+                                               s = s + ' '
+                               list.append('%s^\n' % s)
+                               value = msg
+               list.append('%s: %s\n' % (str(stype), str(value)))
+       return list
+
+
+def print_exc(limit=None, file=None):
+       if not file:
+               file = sys.stderr
+       print_exception(sys.exc_type, sys.exc_value, sys.exc_traceback,
+                       limit, file)
+
+def print_last(limit=None, file=None):
+       if not file:
+               file = sys.stderr
+       print_exception(sys.last_type, sys.last_value, sys.last_traceback,
+                       limit, file)
diff --git a/Lib/dos-8x3/userdict.py b/Lib/dos-8x3/userdict.py
new file mode 100755 (executable)
index 0000000..f6b2f82
--- /dev/null
@@ -0,0 +1,18 @@
+# A more or less complete user-defined wrapper around dictionary objects
+
+class UserDict:
+       def __init__(self): self.data = {}
+       def __repr__(self): return repr(self.data)
+       def __cmp__(self, dict):
+               if type(dict) == type(self.data):
+                       return cmp(self.data, dict)
+               else:
+                       return cmp(self.data, dict.data)
+       def __len__(self): return len(self.data)
+       def __getitem__(self, key): return self.data[key]
+       def __setitem__(self, key, item): self.data[key] = item
+       def __delitem__(self, key): del self.data[key]
+       def keys(self): return self.data.keys()
+       def items(self): return self.data.items()
+       def values(self): return self.data.values()
+       def has_key(self, key): return self.data.has_key(key)
diff --git a/Lib/dos-8x3/userlist.py b/Lib/dos-8x3/userlist.py
new file mode 100755 (executable)
index 0000000..1f19ad9
--- /dev/null
@@ -0,0 +1,50 @@
+# A more or less complete user-defined wrapper around list objects
+
+class UserList:
+       def __init__(self, list = None):
+               self.data = []
+               if list is not None:
+                       if type(list) == type(self.data):
+                               self.data[:] = list
+                       else:
+                               self.data[:] = list.data[:]
+       def __repr__(self): return repr(self.data)
+       def __cmp__(self, list):
+               if type(list) == type(self.data):
+                       return cmp(self.data, list)
+               else:
+                       return cmp(self.data, list.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
+       def __delitem__(self, i): del self.data[i]
+       def __getslice__(self, i, j):
+               userlist = UserList()
+               userlist.data[:] = self.data[i:j]
+               return userlist
+       def __setslice__(self, i, j, list):
+               if type(list) == type(self.data):
+                       self.data[i:j] = list
+               else:
+                       self.data[i:j] = list.data
+       def __delslice__(self, i, j): del self.data[i:j]
+       def __add__(self, list):
+               if type(list) == type(self.data):
+                       return self.__class__(self.data + list)
+               else:
+                       return self.__class__(self.data + list.data)
+       def __radd__(self, list):
+               if type(list) == type(self.data):
+                       return self.__class__(list + self.data)
+               else:
+                       return self.__class__(list.data + self.data)
+       def __mul__(self, n):
+               return self.__class__(self.data*n)
+       __rmul__ = __mul__
+       def append(self, item): self.data.append(item)
+       def insert(self, i, item): self.data.insert(i, item)
+       def remove(self, item): self.data.remove(item)
+       def count(self, item): return self.data.count(item)
+       def index(self, item): return self.data.index(item)
+       def reverse(self): self.data.reverse()
+       def sort(self, *args): apply(self.data.sort, args)
diff --git a/Lib/dos-8x3/whatsoun.py b/Lib/dos-8x3/whatsoun.py
new file mode 100755 (executable)
index 0000000..b7b349c
--- /dev/null
@@ -0,0 +1,270 @@
+# This module contains several routines that help recognizing sound
+# files.
+#
+# Function whathdr() recognizes various types of sound file headers.
+# It understands almost all headers that SOX can decode.
+#
+# The return tuple contains the following items, in this order:
+# - file type (as SOX understands it)
+# - sampling rate (0 if unknown or hard to decode)
+# - number of channels (0 if unknown or hard to decode)
+# - number of frames in the file (-1 if unknown or hard to decode)
+# - number of bits/sample, or 'U' for U-LAW, or 'A' for A-LAW
+#
+# If the file doesn't have a recognizable type, it returns None.
+# If the file can't be opened, IOError is raised.
+#
+# To compute the total time, divide the number of frames by the
+# sampling rate (a frame contains a sample for each channel).
+#
+# Function whatraw() calls the "whatsound" program and interprets its
+# output.  You'll have to guess the sampling rate by listening though!
+#
+# Function what() calls whathdr() and if it doesn't recognize the file
+# then calls whatraw().
+#
+# Finally, the function test() is a simple main program that calls
+# what() for all files mentioned on the argument list.  For directory
+# arguments it calls what() for all files in that directory.  Default
+# argument is "." (testing all files in the current directory).  The
+# option -r tells it to recurse down directories found inside
+# explicitly given directories.
+#
+# The file structure is top-down except that the test program and its
+# subroutine come last.
+
+
+#------------------------------------------------------#
+# Guess the type of any sound file, raw or with header #
+#------------------------------------------------------#
+
+def what(filename):
+       res = whathdr(filename)
+       if not res:
+               res = whatraw(filename)
+       return res
+
+
+#-----------------------------#
+# Guess the type of raw sound #
+#-----------------------------#
+
+def whatraw(filename):
+       # Assume it's always 1 channel, byte-sized samples
+       # Don't assume anything about the rate
+       import os
+       from stat import ST_SIZE
+       # XXX "whatsound" should be part of the distribution somehow...
+       cmd = 'whatsound ' + filename + ' 2>/dev/null'
+       cmd = 'PATH=$PATH:/ufs/guido/bin/sgi\n' + cmd
+       pipe = os.popen(cmd, 'r')
+       data = pipe.read()
+       sts = pipe.close()
+       if sts:
+               return None
+       if data[:13] == '-t raw -b -s ':
+               type = 'sb'
+               sample_size = 8
+       elif data[:13] == '-t raw -b -u ':
+               type = 'ub'
+               sample_size = 8
+       elif data[:13] == '-t raw -b -U ':
+               type = 'ul'
+               sample_size = 'U'
+       else:
+               return None
+       try:
+               frame_count = os.stat(filename)[ST_SIZE]
+       except IOError:
+               frame_count = -1
+       return type, 0, 1, frame_count, sample_size
+
+
+#-------------------------#
+# Recognize sound headers #
+#-------------------------#
+
+def whathdr(filename):
+       f = open(filename, 'r')
+       h = f.read(512)
+       for tf in tests:
+               res = tf(h, f)
+               if res:
+                       return res
+       return None
+
+
+#-----------------------------------#
+# Subroutines per sound header type #
+#-----------------------------------#
+
+tests = []
+
+def test_aifc(h, f):
+       import aifc
+       if h[:4] <> 'FORM':
+               return None
+       if h[8:12] == 'AIFC':
+               fmt = 'aifc'
+       elif h[8:12] == 'AIFF':
+               fmt = 'aiff'
+       else:
+               return None
+       f.seek(0)
+       try:
+               a = aifc.openfp(f, 'r')
+       except (EOFError, aifc.Error):
+               return None
+       return (fmt, a.getframerate(), a.getnchannels(), \
+               a.getnframes(), 8*a.getsampwidth())
+
+tests.append(test_aifc)
+
+
+def test_au(h, f):
+       if h[:4] == '.snd':
+               f = get_long_be
+       elif h[:4] in ('\0ds.', 'dns.'):
+               f = get_long_le
+       else:
+               return None
+       type = 'au'
+       hdr_size = f(h[4:8])
+       data_size = f(h[8:12])
+       encoding = f(h[12:16])
+       rate = f(h[16:20])
+       nchannels = f(h[20:24])
+       sample_size = 1 # default
+       if encoding == 1:
+               sample_bits = 'U'
+       elif encoding == 2:
+               sample_bits = 8
+       elif encoding == 3:
+               sample_bits = 16
+               sample_size = 2
+       else:
+               sample_bits = '?'
+       frame_size = sample_size * nchannels
+       return type, rate, nchannels, data_size/frame_size, sample_bits
+
+tests.append(test_au)
+
+
+def test_hcom(h, f):
+       if h[65:69] <> 'FSSD' or h[128:132] <> 'HCOM':
+               return None
+       divisor = get_long_be(h[128+16:128+20])
+       return 'hcom', 22050/divisor, 1, -1, 8
+
+tests.append(test_hcom)
+
+
+def test_voc(h, f):
+       if h[:20] <> 'Creative Voice File\032':
+               return None
+       sbseek = get_short_le(h[20:22])
+       rate = 0
+       if 0 <= sbseek < 500 and h[sbseek] == '\1':
+               ratecode = ord(h[sbseek+4])
+               rate = int(1000000.0 / (256 - ratecode))
+       return 'voc', rate, 1, -1, 8
+
+tests.append(test_voc)
+
+
+def test_wav(h, f):
+       # 'RIFF' <len> 'WAVE' 'fmt ' <len>
+       if h[:4] <> 'RIFF' or h[8:12] <> 'WAVE' or h[12:16] <> 'fmt ':
+               return None
+       style = get_short_le(h[20:22])
+       nchannels = get_short_le(h[22:24])
+       rate = get_long_le(h[24:28])
+       sample_bits = get_short_le(h[34:36])
+       return 'wav', rate, nchannels, -1, sample_bits
+
+tests.append(test_wav)
+
+
+def test_8svx(h, f):
+       if h[:4] <> 'FORM' or h[8:12] <> '8SVX':
+               return None
+       # Should decode it to get #channels -- assume always 1
+       return '8svx', 0, 1, 0, 8
+
+tests.append(test_8svx)
+
+
+def test_sndt(h, f):
+       if h[:5] == 'SOUND':
+               nsamples = get_long_le(h[8:12])
+               rate = get_short_le(h[20:22])
+               return 'sndt', rate, 1, nsamples, 8
+
+tests.append(test_sndt)
+
+
+def test_sndr(h, f):
+       if h[:2] == '\0\0':
+               rate = get_short_le(h[2:4])
+               if 4000 <= rate <= 25000:
+                       return 'sndr', rate, 1, -1, 8
+
+tests.append(test_sndr)
+
+
+#---------------------------------------------#
+# Subroutines to extract numbers from strings #
+#---------------------------------------------#
+
+def get_long_be(s):
+       return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3])
+
+def get_long_le(s):
+       return (ord(s[3])<<24) | (ord(s[2])<<16) | (ord(s[1])<<8) | ord(s[0])
+
+def get_short_be(s):
+       return (ord(s[0])<<8) | ord(s[1])
+
+def get_short_le(s):
+       return (ord(s[1])<<8) | ord(s[0])
+
+
+#--------------------#
+# Small test program #
+#--------------------#
+
+def test():
+       import sys
+       recursive = 0
+       if sys.argv[1:] and sys.argv[1] == '-r':
+               del sys.argv[1:2]
+               recursive = 1
+       try:
+               if sys.argv[1:]:
+                       testall(sys.argv[1:], recursive, 1)
+               else:
+                       testall(['.'], recursive, 1)
+       except KeyboardInterrupt:
+               sys.stderr.write('\n[Interrupted]\n')
+               sys.exit(1)
+
+def testall(list, recursive, toplevel):
+       import sys
+       import os
+       for filename in list:
+               if os.path.isdir(filename):
+                       print filename + '/:',
+                       if recursive or toplevel:
+                               print 'recursing down:'
+                               import glob
+                               names = glob.glob(os.path.join(filename, '*'))
+                               testall(names, recursive, 0)
+                       else:
+                               print '*** directory (use -r) ***'
+               else:
+                       print filename + ':',
+                       sys.stdout.flush()
+                       try:
+                               print what(filename)
+                       except IOError:
+                               print '*** not found ***'
diff --git a/Lib/dos_8x3/arrayio.py b/Lib/dos_8x3/arrayio.py
new file mode 100755 (executable)
index 0000000..5b06f92
--- /dev/null
@@ -0,0 +1,142 @@
+"""File-like objects that read/write an array buffer.
+
+This implements (nearly) all stdio methods.
+
+f = ArrayIO()       # ready for writing
+f = ArrayIO(buf)    # ready for reading
+f.close()           # explicitly release resources held
+flag = f.isatty()   # always false
+pos = f.tell()      # get current position
+f.seek(pos)         # set current position
+f.seek(pos, mode)   # mode 0: absolute; 1: relative; 2: relative to EOF
+buf = f.read()      # read until EOF
+buf = f.read(n)     # read up to n bytes
+buf = f.readline()  # read until end of line ('\n') or EOF
+list = f.readlines()# list of f.readline() results until EOF
+f.write(buf)        # write at current position
+f.writelines(list)  # for line in list: f.write(line)
+f.getvalue()        # return whole file's contents as a string
+
+Notes:
+- This is very similar to StringIO.  StringIO is faster for reading,
+  but ArrayIO is faster for writing.
+- ArrayIO uses an array object internally, but all its interfaces
+  accept and return strings.
+- Using a real file is often faster (but less convenient).
+- fileno() is left unimplemented so that code which uses it triggers
+  an exception early.
+- Seeking far beyond EOF and then writing will insert real null
+  bytes that occupy space in the buffer.
+- There's a simple test set (see end of this file).
+"""
+
+import string
+from array import array
+
+class ArrayIO:
+       def __init__(self, buf = ''):
+               self.buf = array('c', buf)
+               self.pos = 0
+               self.closed = 0
+               self.softspace = 0
+       def close(self):
+               if not self.closed:
+                       self.closed = 1
+                       del self.buf, self.pos
+       def isatty(self):
+               return 0
+       def seek(self, pos, mode = 0):
+               if mode == 1:
+                       pos = pos + self.pos
+               elif mode == 2:
+                       pos = pos + len(self.buf)
+               self.pos = max(0, pos)
+       def tell(self):
+               return self.pos
+       def read(self, n = -1):
+               if n < 0:
+                       newpos = len(self.buf)
+               else:
+                       newpos = min(self.pos+n, len(self.buf))
+               r = self.buf[self.pos:newpos].tostring()
+               self.pos = newpos
+               return r
+       def readline(self):
+               i = string.find(self.buf[self.pos:].tostring(), '\n')
+               if i < 0:
+                       newpos = len(self.buf)
+               else:
+                       newpos = self.pos+i+1
+               r = self.buf[self.pos:newpos].tostring()
+               self.pos = newpos
+               return r
+       def readlines(self):
+               lines = string.splitfields(self.read(), '\n')
+               if not lines:
+                       return lines
+               for i in range(len(lines)-1):
+                       lines[i] = lines[i] + '\n'
+               if not lines[-1]:
+                       del lines[-1]
+               return lines
+       def write(self, s):
+               if not s: return
+               a = array('c', s)
+               n = self.pos - len(self.buf)
+               if n > 0:
+                       self.buf[len(self.buf):] = array('c', '\0')*n
+               newpos = self.pos + len(a)
+               self.buf[self.pos:newpos] = a
+               self.pos = newpos
+       def writelines(self, list):
+               self.write(string.joinfields(list, ''))
+       def flush(self):
+               pass
+       def getvalue(self):
+               return self.buf.tostring()
+
+
+# A little test suite
+
+def test():
+       import sys
+       if sys.argv[1:]:
+               file = sys.argv[1]
+       else:
+               file = '/etc/passwd'
+       lines = open(file, 'r').readlines()
+       text = open(file, 'r').read()
+       f = ArrayIO()
+       for line in lines[:-2]:
+               f.write(line)
+       f.writelines(lines[-2:])
+       if f.getvalue() != text:
+               raise RuntimeError, 'write failed'
+       length = f.tell()
+       print 'File length =', length
+       f.seek(len(lines[0]))
+       f.write(lines[1])
+       f.seek(0)
+       print 'First line =', `f.readline()`
+       here = f.tell()
+       line = f.readline()
+       print 'Second line =', `line`
+       f.seek(-len(line), 1)
+       line2 = f.read(len(line))
+       if line != line2:
+               raise RuntimeError, 'bad result after seek back'
+       f.seek(len(line2), 1)
+       list = f.readlines()
+       line = list[-1]
+       f.seek(f.tell() - len(line))
+       line2 = f.read()
+       if line != line2:
+               raise RuntimeError, 'bad result after seek back from EOF'
+       print 'Read', len(list), 'more lines'
+       print 'File length =', f.tell()
+       if f.tell() != length:
+               raise RuntimeError, 'bad length'
+       f.close()
+
+if __name__ == '__main__':
+       test()
diff --git a/Lib/dos_8x3/ast.py b/Lib/dos_8x3/ast.py
new file mode 100755 (executable)
index 0000000..6f92bee
--- /dev/null
@@ -0,0 +1,224 @@
+"""Object-oriented interface to the parser module.
+
+This module exports three classes which together provide an interface
+to the parser module.  Together, the three classes represent two ways
+to create parsed representations of Python source and the two starting
+data types (source text and tuple representations).  Each class
+provides interfaces which are identical other than the constructors.
+The constructors are described in detail in the documentation for each
+class and the remaining, shared portion of the interface is documented
+below.  Briefly, the three classes provided are:
+
+AST
+    Defines the primary interface to the AST objects and supports creation
+    from the tuple representation of the parse tree.
+
+ExpressionAST
+    Supports creation of expression constructs from source text.
+
+SuiteAST
+    Supports creation of statement suites from source text.
+
+FileSuiteAST
+    Convenience subclass of the `SuiteAST' class; loads source text of the
+    suite from an external file.
+
+Aside from the constructors, several methods are provided to allow
+access to the various interpretations of the parse tree and to check
+conditions of the construct represented by the parse tree.
+
+ast()
+    Returns the corresponding `parser.ASTType' object.
+
+code()
+    Returns the compiled code object.
+
+filename()
+    Returns the name of the associated source file, if known.
+
+isExpression()
+    Returns true value if parse tree represents an expression, or a false
+    value otherwise.
+
+isSuite()
+    Returns true value if parse tree represents a suite of statements, or
+    a false value otherwise.
+
+text()
+    Returns the source text, or None if not available.
+
+tuple()
+    Returns the tuple representing the parse tree.
+"""
+
+__version__ = '$Revision$'
+__copyright__ = """Copyright (c) 1995, 1996 by Fred L. Drake, Jr.
+
+This software may be used and distributed freely for any purpose provided
+that this notice is included unchanged on any and all copies.  The author
+does not warrant or guarantee this software in any way.
+"""
+
+class AST:
+    """Base class for Abstract Syntax Tree objects.
+
+    Creates an Abstract Syntax Tree based on the tuple representation
+    of the parse tree.  The parse tree can represent either an
+    expression or a suite; which will be recognized automatically.
+    This base class provides all of the query methods for subclass
+    objects defined in this module.
+    """
+    _p = __import__('parser')          # import internally to avoid
+                                       # namespace pollution at the
+                                       # top level
+    _text = None
+    _code = None
+    _ast  = None
+    _type = 'unknown'
+    _tupl = None
+
+    def __init__(self, tuple):
+       """Create an `AST' instance from a tuple-tree representation.
+
+       tuple
+           The tuple tree to convert.
+
+       The tuple-tree may represent either an expression or a suite; the
+       type will be determined automatically.
+       """
+       if type(tuple) is not type(()):
+           raise TypeError, 'Base AST class requires tuple parameter.'
+
+       self._tupl = tuple
+       self._ast  = self._p.tuple2ast(tuple)
+       self._type = (self._p.isexpr(self._ast) and 'expression') or 'suite'
+
+    def tuple(self):
+       """Returns the tuple representing the parse tree.
+       """
+       if self._tupl is None:
+           self._tupl = self._p.ast2tuple(self._ast)
+       return self._tupl
+
+    def code(self):
+       """Returns the compiled code object.
+
+       The code object returned by this method may be passed to the
+       exec statement if `AST.isSuite()' is true or to the eval()
+       function if `AST.isExpression()' is true.  All the usual rules
+       regarding execution of code objects apply.
+       """
+       if not self._code:
+           self._code = self._p.compileast(self._ast)
+       return self._code
+
+    def ast(self):
+       """Returns the corresponding `parser.ASTType' object.
+       """
+       return self._ast
+
+    def filename(self):
+       """Returns the name of the source file if known, or None.
+       """
+       return None
+
+    def text(self):
+       """Returns the source text, or None if not available.
+
+       If the instance is of class `AST', None is returned since no
+       source text is available.  If of class `ExpressionAST' or
+       `SuiteAST', the source text passed to the constructor is
+       returned.
+       """
+       return self._text
+
+    def isSuite(self):
+       """Determine if `AST' instance represents a suite of statements.
+       """
+       return self._type == 'suite'
+
+    def isExpression(self):
+       """Determine if `AST' instance represents an expression.
+       """
+       return self._type == 'expression'
+
+
+
+class SuiteAST(AST):
+    """Statement suite parse tree representation.
+
+    This subclass of the `AST' base class represents statement suites
+    parsed from the source text of a Python suite.  If the source text
+    does not represent a parsable suite of statements, the appropriate
+    exception is raised by the parser.
+    """
+    _type = 'suite'
+
+    def __init__(self, text):
+       """Initialize a `SuiteAST' from source text.
+
+       text
+           Source text to parse.
+       """
+       if type(text) is not type(''):
+           raise TypeError, 'SuiteAST requires source text parameter.'
+       self._text = text
+       self._ast  = self._p.suite(text)
+
+    def isSuite(self):
+       return 1
+
+    def isExpression(self):
+       return 0
+
+
+class FileSuiteAST(SuiteAST):
+    """Representation of a python source file syntax tree.
+
+    This provides a convenience wrapper around the `SuiteAST' class to
+    load the source text from an external file.
+    """
+    def __init__(self, fileName):
+       """Initialize a `SuiteAST' from a source file.
+
+       fileName
+           Name of the external source file.
+       """
+       self._fileName = fileName
+       SuiteAST.__init__(self, open(fileName).read())
+
+    def filename(self):
+       return self._fileName
+
+
+
+class ExpressionAST(AST):
+    """Expression parse tree representation.
+
+    This subclass of the `AST' base class represents expression
+    constructs parsed from the source text of a Python expression.  If
+    the source text does not represent a parsable expression, the
+    appropriate exception is raised by the Python parser.
+    """
+    _type = 'expression'
+
+    def __init__(self, text):
+       """Initialize an expression AST from source text.
+
+       text
+           Source text to parse.
+       """
+       if type(text) is not type(''):
+           raise TypeError, 'ExpressionAST requires source text parameter.'
+       self._text = text
+       self._ast  = self._p.expr(text)
+
+    def isSuite(self):
+       return 0
+
+    def isExpression(self):
+       return 1
+
+
+#
+#  end of file
diff --git a/Lib/dos_8x3/basehttp.py b/Lib/dos_8x3/basehttp.py
new file mode 100755 (executable)
index 0000000..281ddf6
--- /dev/null
@@ -0,0 +1,482 @@
+"""HTTP server base class.
+
+Note: the class in this module doesn't implement any HTTP request; see
+SimpleHTTPServer for simple implementations of GET, HEAD and POST
+(including CGI scripts).
+
+Contents:
+
+- BaseHTTPRequestHandler: HTTP request handler base class
+- test: test function
+
+XXX To do:
+
+- send server version
+- log requests even later (to capture byte count)
+- log user-agent header and other interesting goodies
+- send error log to separate file
+- are request names really case sensitive?
+
+"""
+
+
+# See also:
+#
+# HTTP Working Group                                        T. Berners-Lee
+# INTERNET-DRAFT                                            R. T. Fielding
+# <draft-ietf-http-v10-spec-00.txt>                     H. Frystyk Nielsen
+# Expires September 8, 1995                                  March 8, 1995
+#
+# URL: http://www.ics.uci.edu/pub/ietf/http/draft-ietf-http-v10-spec-00.txt
+
+
+# Log files
+# ---------
+# 
+# Here's a quote from the NCSA httpd docs about log file format.
+# 
+# | The logfile format is as follows. Each line consists of: 
+# | 
+# | host rfc931 authuser [DD/Mon/YYYY:hh:mm:ss] "request" ddd bbbb 
+# | 
+# |        host: Either the DNS name or the IP number of the remote client 
+# |        rfc931: Any information returned by identd for this person,
+# |                - otherwise. 
+# |        authuser: If user sent a userid for authentication, the user name,
+# |                  - otherwise. 
+# |        DD: Day 
+# |        Mon: Month (calendar name) 
+# |        YYYY: Year 
+# |        hh: hour (24-hour format, the machine's timezone) 
+# |        mm: minutes 
+# |        ss: seconds 
+# |        request: The first line of the HTTP request as sent by the client. 
+# |        ddd: the status code returned by the server, - if not available. 
+# |        bbbb: the total number of bytes sent,
+# |              *not including the HTTP/1.0 header*, - if not available 
+# | 
+# | You can determine the name of the file accessed through request.
+# 
+# (Actually, the latter is only true if you know the server configuration
+# at the time the request was made!)
+
+
+__version__ = "0.2"
+
+
+import sys
+import time
+import socket # For gethostbyaddr()
+import string
+import rfc822
+import mimetools
+import SocketServer
+
+# Default error message
+DEFAULT_ERROR_MESSAGE = """\
+<head>
+<title>Error response</title>
+</head>
+<body>
+<h1>Error response</h1>
+<p>Error code %(code)d.
+<p>Message: %(message)s.
+<p>Error code explanation: %(code)s = %(explain)s.
+</body>
+"""
+
+
+class HTTPServer(SocketServer.TCPServer):
+
+    def server_bind(self):
+       """Override server_bind to store the server name."""
+       SocketServer.TCPServer.server_bind(self)
+       host, port = self.socket.getsockname()
+       if not host or host == '0.0.0.0':
+           host = socket.gethostname()
+       hostname, hostnames, hostaddrs = socket.gethostbyaddr(host)
+       if '.' not in hostname:
+           for host in hostnames:
+               if '.' in host:
+                   hostname = host
+                   break
+       self.server_name = hostname
+       self.server_port = port
+
+
+class BaseHTTPRequestHandler(SocketServer.StreamRequestHandler):
+
+    """HTTP request handler base class.
+
+    The following explanation of HTTP serves to guide you through the
+    code as well as to expose any misunderstandings I may have about
+    HTTP (so you don't need to read the code to figure out I'm wrong
+    :-).
+
+    HTTP (HyperText Transfer Protocol) is an extensible protocol on
+    top of a reliable stream transport (e.g. TCP/IP).  The protocol
+    recognizes three parts to a request:
+
+    1. One line identifying the request type and path
+    2. An optional set of RFC-822-style headers
+    3. An optional data part
+
+    The headers and data are separated by a blank line.
+
+    The first line of the request has the form
+
+    <command> <path> <version>
+
+    where <command> is a (case-sensitive) keyword such as GET or POST,
+    <path> is a string containing path information for the request,
+    and <version> should be the string "HTTP/1.0".  <path> is encoded
+    using the URL encoding scheme (using %xx to signify the ASCII
+    character with hex code xx).
+
+    The protocol is vague about whether lines are separated by LF
+    characters or by CRLF pairs -- for compatibility with the widest
+    range of clients, both should be accepted.  Similarly, whitespace
+    in the request line should be treated sensibly (allowing multiple
+    spaces between components and allowing trailing whitespace).
+
+    Similarly, for output, lines ought to be separated by CRLF pairs
+    but most clients grok LF characters just fine.
+
+    If the first line of the request has the form
+
+    <command> <path>
+
+    (i.e. <version> is left out) then this is assumed to be an HTTP
+    0.9 request; this form has no optional headers and data part and
+    the reply consists of just the data.
+
+    The reply form of the HTTP 1.0 protocol again has three parts:
+
+    1. One line giving the response code
+    2. An optional set of RFC-822-style headers
+    3. The data
+
+    Again, the headers and data are separated by a blank line.
+
+    The response code line has the form
+
+    <version> <responsecode> <responsestring>
+
+    where <version> is the protocol version (always "HTTP/1.0"),
+    <responsecode> is a 3-digit response code indicating success or
+    failure of the request, and <responsestring> is an optional
+    human-readable string explaining what the response code means.
+
+    This server parses the request and the headers, and then calls a
+    function specific to the request type (<command>).  Specifically,
+    a request SPAM will be handled by a method handle_SPAM().  If no
+    such method exists the server sends an error response to the
+    client.  If it exists, it is called with no arguments:
+
+    do_SPAM()
+
+    Note that the request name is case sensitive (i.e. SPAM and spam
+    are different requests).
+
+    The various request details are stored in instance variables:
+
+    - client_address is the client IP address in the form (host,
+    port);
+
+    - command, path and version are the broken-down request line;
+
+    - headers is an instance of mimetools.Message (or a derived
+    class) containing the header information;
+
+    - rfile is a file object open for reading positioned at the
+    start of the optional input data part;
+
+    - wfile is a file object open for writing.
+
+    IT IS IMPORTANT TO ADHERE TO THE PROTOCOL FOR WRITING!
+
+    The first thing to be written must be the response line.  Then
+    follow 0 or more header lines, then a blank line, and then the
+    actual data (if any).  The meaning of the header lines depends on
+    the command executed by the server; in most cases, when data is
+    returned, there should be at least one header line of the form
+
+    Content-type: <type>/<subtype>
+
+    where <type> and <subtype> should be registered MIME types,
+    e.g. "text/html" or "text/plain".
+
+    """
+
+    # The Python system version, truncated to its first component.
+    sys_version = "Python/" + string.split(sys.version)[0]
+
+    # The server software version.  You may want to override this.
+    # The format is multiple whitespace-separated strings,
+    # where each string is of the form name[/version].
+    server_version = "BaseHTTP/" + __version__
+
+    def handle(self):
+       """Handle a single HTTP request.
+
+       You normally don't need to override this method; see the class
+       __doc__ string for information on how to handle specific HTTP
+       commands such as GET and POST.
+
+       """
+
+       self.raw_requestline = self.rfile.readline()
+       self.request_version = version = "HTTP/0.9" # Default
+       requestline = self.raw_requestline
+       if requestline[-2:] == '\r\n':
+           requestline = requestline[:-2]
+       elif requestline[-1:] == '\n':
+           requestline = requestline[:-1]
+       self.requestline = requestline
+       words = string.split(requestline)
+       if len(words) == 3:
+           [command, path, version] = words
+           if version != self.protocol_version:
+               self.send_error(400, "Bad request version (%s)" % `version`)
+               return
+       elif len(words) == 2:
+           [command, path] = words
+           if command != 'GET':
+               self.send_error(400,
+                               "Bad HTTP/0.9 request type (%s)" % `command`)
+               return
+       else:
+           self.send_error(400, "Bad request syntax (%s)" % `requestline`)
+           return
+       self.command, self.path, self.request_version = command, path, version
+       self.headers = self.MessageClass(self.rfile, 0)
+       mname = 'do_' + command
+       if not hasattr(self, mname):
+           self.send_error(501, "Unsupported method (%s)" % `mname`)
+           return
+       method = getattr(self, mname)
+       method()
+
+    def send_error(self, code, message=None):
+       """Send and log an error reply.
+
+       Arguments are the error code, and a detailed message.
+       The detailed message defaults to the short entry matching the
+       response code.
+
+       This sends an error response (so it must be called before any
+       output has been generated), logs the error, and finally sends
+       a piece of HTML explaining the error to the user.
+
+       """
+
+       try:
+           short, long = self.responses[code]
+       except KeyError:
+           short, long = '???', '???'
+       if not message:
+           message = short
+       explain = long
+       self.log_error("code %d, message %s", code, message)
+       self.send_response(code, message)
+       self.end_headers()
+       self.wfile.write(self.error_message_format %
+                        {'code': code,
+                         'message': message,
+                         'explain': explain})
+
+    error_message_format = DEFAULT_ERROR_MESSAGE
+
+    def send_response(self, code, message=None):
+       """Send the response header and log the response code.
+
+       Also send two standard headers with the server software
+       version and the current date.
+
+       """
+       self.log_request(code)
+       if message is None:
+           if self.responses.has_key(code):
+               message = self.responses[code][1]
+           else:
+               message = ''
+       if self.request_version != 'HTTP/0.9':
+           self.wfile.write("%s %s %s\r\n" %
+                            (self.protocol_version, str(code), message))
+       self.send_header('Server', self.version_string())
+       self.send_header('Date', self.date_time_string())
+
+    def send_header(self, keyword, value):
+       """Send a MIME header."""
+       if self.request_version != 'HTTP/0.9':
+           self.wfile.write("%s: %s\r\n" % (keyword, value))
+
+    def end_headers(self):
+       """Send the blank line ending the MIME headers."""
+       if self.request_version != 'HTTP/0.9':
+           self.wfile.write("\r\n")
+
+    def log_request(self, code='-', size='-'):
+       """Log an accepted request.
+
+       This is called by send_reponse().
+
+       """
+
+       self.log_message('"%s" %s %s',
+                        self.requestline, str(code), str(size))
+
+    def log_error(self, *args):
+       """Log an error.
+
+       This is called when a request cannot be fulfilled.  By
+       default it passes the message on to log_message().
+
+       Arguments are the same as for log_message().
+
+       XXX This should go to the separate error log.
+
+       """
+
+       apply(self.log_message, args)
+
+    def log_message(self, format, *args):
+       """Log an arbitrary message.
+
+       This is used by all other logging functions.  Override
+       it if you have specific logging wishes.
+
+       The first argument, FORMAT, is a format string for the
+       message to be logged.  If the format string contains
+       any % escapes requiring parameters, they should be
+       specified as subsequent arguments (it's just like
+       printf!).
+
+       The client host and current date/time are prefixed to
+       every message.
+
+       """
+
+       sys.stderr.write("%s - - [%s] %s\n" %
+                        (self.address_string(),
+                         self.log_date_time_string(),
+                         format%args))
+
+    def version_string(self):
+       """Return the server software version string."""
+       return self.server_version + ' ' + self.sys_version
+
+    def date_time_string(self):
+       """Return the current date and time formatted for a message header."""
+       now = time.time()
+       year, month, day, hh, mm, ss, wd, y, z = time.gmtime(now)
+       s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (
+               self.weekdayname[wd],
+               day, self.monthname[month], year,
+               hh, mm, ss)
+       return s
+
+    def log_date_time_string(self):
+       """Return the current time formatted for logging."""
+       now = time.time()
+       year, month, day, hh, mm, ss, x, y, z = time.localtime(now)
+       s = "%02d/%3s/%04d %02d:%02d:%02d" % (
+               day, self.monthname[month], year, hh, mm, ss)
+       return s
+
+    weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
+
+    monthname = [None,
+                'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
+                'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
+
+    def address_string(self):
+       """Return the client address formatted for logging.
+
+       This version looks up the full hostname using gethostbyaddr(),
+       and tries to find a name that contains at least one dot.
+
+       """
+
+       (host, port) = self.client_address
+       try:
+           name, names, addresses = socket.gethostbyaddr(host)
+       except socket.error, msg:
+           return host
+       names.insert(0, name)
+       for name in names:
+           if '.' in name: return name
+       return names[0]
+
+
+    # Essentially static class variables
+
+    # The version of the HTTP protocol we support.
+    # Don't override unless you know what you're doing (hint: incoming
+    # requests are required to have exactly this version string).
+    protocol_version = "HTTP/1.0"
+
+    # The Message-like class used to parse headers
+    MessageClass = mimetools.Message
+
+    # Table mapping response codes to messages; entries have the
+    # form {code: (shortmessage, longmessage)}.
+    # See http://www.w3.org/hypertext/WWW/Protocols/HTTP/HTRESP.html
+    responses = {
+       200: ('OK', 'Request fulfilled, document follows'),
+       201: ('Created', 'Document created, URL follows'),
+       202: ('Accepted',
+             'Request accepted, processing continues off-line'),
+       203: ('Partial information', 'Request fulfilled from cache'),
+       204: ('No response', 'Request fulfilled, nothing follows'),
+       
+       301: ('Moved', 'Object moved permanently -- see URI list'),
+       302: ('Found', 'Object moved temporarily -- see URI list'),
+       303: ('Method', 'Object moved -- see Method and URL list'),
+       304: ('Not modified',
+             'Document has not changed singe given time'),
+       
+       400: ('Bad request',
+             'Bad request syntax or unsupported method'),
+       401: ('Unauthorized',
+             'No permission -- see authorization schemes'),
+       402: ('Payment required',
+             'No payment -- see charging schemes'),
+       403: ('Forbidden',
+             'Request forbidden -- authorization will not help'),
+       404: ('Not found', 'Nothing matches the given URI'),
+       
+       500: ('Internal error', 'Server got itself in trouble'),
+       501: ('Not implemented',
+             'Server does not support this operation'),
+       502: ('Service temporarily overloaded',
+             'The server cannot process the request due to a high load'),
+       503: ('Gateway timeout',
+             'The gateway server did not receive a timely response'),
+       
+       }
+
+
+def test(HandlerClass = BaseHTTPRequestHandler,
+        ServerClass = HTTPServer):
+    """Test the HTTP request handler class.
+
+    This runs an HTTP server on port 8000 (or the first command line
+    argument).
+
+    """
+
+    if sys.argv[1:]:
+       port = string.atoi(sys.argv[1])
+    else:
+       port = 8000
+    server_address = ('', port)
+
+    httpd = ServerClass(server_address, HandlerClass)
+
+    print "Serving HTTP on port", port, "..."
+    httpd.serve_forever()
+
+
+if __name__ == '__main__':
+    test()
diff --git a/Lib/dos_8x3/bastion.py b/Lib/dos_8x3/bastion.py
new file mode 100755 (executable)
index 0000000..7ddd93e
--- /dev/null
@@ -0,0 +1,162 @@
+"""Bastionification utility.
+
+A bastion (for another object -- the 'original') is an object that has
+the same methods as the original but does not give access to its
+instance variables.  Bastions have a number of uses, but the most
+obvious one is to provide code executing in restricted mode with a
+safe interface to an object implemented in unrestricted mode.
+
+The bastionification routine has an optional second argument which is
+a filter function.  Only those methods for which the filter method
+(called with the method name as argument) returns true are accessible.
+The default filter method returns true unless the method name begins
+with an underscore.
+
+There are a number of possible implementations of bastions.  We use a
+'lazy' approach where the bastion's __getattr__() discipline does all
+the work for a particular method the first time it is used.  This is
+usually fastest, especially if the user doesn't call all available
+methods.  The retrieved methods are stored as instance variables of
+the bastion, so the overhead is only occurred on the first use of each
+method.
+
+Detail: the bastion class has a __repr__() discipline which includes
+the repr() of the original object.  This is precomputed when the
+bastion is created.
+
+"""
+
+__version__ = '$Revision$'
+# $Source$
+
+
+from types import MethodType
+
+
+class BastionClass:
+
+    """Helper class used by the Bastion() function.
+
+    You could subclass this and pass the subclass as the bastionclass
+    argument to the Bastion() function, as long as the constructor has
+    the same signature (a get() function and a name for the object).
+
+    """
+
+    def __init__(self, get, name):
+       """Constructor.
+
+       Arguments:
+
+       get - a function that gets the attribute value (by name)
+       name - a human-readable name for the original object
+              (suggestion: use repr(object))
+
+       """
+       self._get_ = get
+       self._name_ = name
+
+    def __repr__(self):
+       """Return a representation string.
+
+       This includes the name passed in to the constructor, so that
+       if you print the bastion during debugging, at least you have
+       some idea of what it is.
+
+       """
+       return "<Bastion for %s>" % self._name_
+
+    def __getattr__(self, name):
+       """Get an as-yet undefined attribute value.
+
+       This calls the get() function that was passed to the
+       constructor.  The result is stored as an instance variable so
+       that the next time the same attribute is requested,
+       __getattr__() won't be invoked.
+
+       If the get() function raises an exception, this is simply
+       passed on -- exceptions are not cached.
+
+       """
+       attribute = self._get_(name)
+       self.__dict__[name] = attribute
+       return attribute
+
+
+def Bastion(object, filter = lambda name: name[:1] != '_',
+           name=None, bastionclass=BastionClass):
+    """Create a bastion for an object, using an optional filter.
+
+    See the Bastion module's documentation for background.
+
+    Arguments:
+
+    object - the original object
+    filter - a predicate that decides whether a function name is OK;
+             by default all names are OK that don't start with '_'
+    name - the name of the object; default repr(object)
+    bastionclass - class used to create the bastion; default BastionClass
+
+    """
+
+    # Note: we define *two* ad-hoc functions here, get1 and get2.
+    # Both are intended to be called in the same way: get(name).
+    # It is clear that the real work (getting the attribute
+    # from the object and calling the filter) is done in get1.
+    # Why can't we pass get1 to the bastion?  Because the user
+    # would be able to override the filter argument!  With get2,
+    # overriding the default argument is no security loophole:
+    # all it does is call it.
+    # Also notice that we can't place the object and filter as
+    # instance variables on the bastion object itself, since
+    # the user has full access to all instance variables!
+
+    def get1(name, object=object, filter=filter):
+       """Internal function for Bastion().  See source comments."""
+       if filter(name):
+           attribute = getattr(object, name)
+           if type(attribute) == MethodType:
+               return attribute
+       raise AttributeError, name
+
+    def get2(name, get1=get1):
+       """Internal function for Bastion().  See source comments."""
+       return get1(name)
+
+    if name is None:
+       name = `object`
+    return bastionclass(get2, name)
+
+
+def _test():
+    """Test the Bastion() function."""
+    class Original:
+       def __init__(self):
+           self.sum = 0
+       def add(self, n):
+           self._add(n)
+       def _add(self, n):
+           self.sum = self.sum + n
+       def total(self):
+           return self.sum
+    o = Original()
+    b = Bastion(o)
+    b.add(81)
+    b.add(18)
+    print "b.total() =", b.total()
+    try:
+       print "b.sum =", b.sum,
+    except:
+       print "inaccessible"
+    else:
+       print "accessible"
+    try:
+       print "b._add =", b._add,
+    except:
+       print "inaccessible"
+    else:
+       print "accessible"
+
+
+if __name__ == '__main__':
+    _test()
diff --git a/Lib/dos_8x3/cgihttps.py b/Lib/dos_8x3/cgihttps.py
new file mode 100755 (executable)
index 0000000..837f7c2
--- /dev/null
@@ -0,0 +1,203 @@
+"""CGI-savvy HTTP Server.
+
+This module builds on SimpleHTTPServer by implementing GET and POST
+requests to cgi-bin scripts.
+
+"""
+
+
+__version__ = "0.3"
+
+
+import os
+import sys
+import time
+import socket
+import string
+import urllib
+import BaseHTTPServer
+import SimpleHTTPServer
+
+
+class CGIHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
+
+    """Complete HTTP server with GET, HEAD and POST commands.
+
+    GET and HEAD also support running CGI scripts.
+
+    The POST command is *only* implemented for CGI scripts.
+
+    """
+
+    def do_POST(self):
+       """Serve a POST request.
+
+       This is only implemented for CGI scripts.
+
+       """
+
+       if self.is_cgi():
+           self.run_cgi()
+       else:
+           self.send_error(501, "Can only POST to CGI scripts")
+
+    def send_head(self):
+       """Version of send_head that support CGI scripts"""
+       if self.is_cgi():
+           return self.run_cgi()
+       else:
+           return SimpleHTTPServer.SimpleHTTPRequestHandler.send_head(self)
+
+    def is_cgi(self):
+       """test whether PATH corresponds to a CGI script.
+
+       Return a tuple (dir, rest) if PATH requires running a
+       CGI script, None if not.  Note that rest begins with a
+       slash if it is not empty.
+
+       The default implementation tests whether the path
+       begins with one of the strings in the list
+       self.cgi_directories (and the next character is a '/'
+       or the end of the string).
+
+       """
+
+       path = self.path
+
+       for x in self.cgi_directories:
+           i = len(x)
+           if path[:i] == x and (not path[i:] or path[i] == '/'):
+               self.cgi_info = path[:i], path[i+1:]
+               return 1
+       return 0
+
+    cgi_directories = ['/cgi-bin', '/htbin']
+
+    def run_cgi(self):
+       """Execute a CGI script."""
+       dir, rest = self.cgi_info
+       i = string.rfind(rest, '?')
+       if i >= 0:
+           rest, query = rest[:i], rest[i+1:]
+       else:
+           query = ''
+       i = string.find(rest, '/')
+       if i >= 0:
+           script, rest = rest[:i], rest[i:]
+       else:
+           script, rest = rest, ''
+       scriptname = dir + '/' + script
+       scriptfile = self.translate_path(scriptname)
+       if not os.path.exists(scriptfile):
+           self.send_error(404, "No such CGI script (%s)" % `scriptname`)
+           return
+       if not os.path.isfile(scriptfile):
+           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
+           env['CONTENT_TYPE'] = self.headers.type
+           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
+           # XXX Other HTTP_* headers
+           import regsub
+           decoded_query = regsub.gsub('+', ' ', query)
+           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)
+
+
+nobody = None
+
+def nobody_uid():
+    """Internal routine to get nobody's uid"""
+    global nobody
+    if nobody:
+       return nobody
+    import pwd
+    try:
+       nobody = pwd.getpwnam('nobody')[2]
+    except pwd.error:
+       nobody = 1 + max(map(lambda x: x[2], pwd.getpwall()))
+    return nobody
+
+
+def executable(path):
+    """Test for executable file."""
+    try:
+       st = os.stat(path)
+    except os.error:
+       return 0
+    return st[0] & 0111 != 0
+
+
+def test(HandlerClass = CGIHTTPRequestHandler,
+        ServerClass = BaseHTTPServer.HTTPServer):
+    import sys
+    if sys.argv[1:2] == ['-r']:
+       db = MyArchive()
+       db.regenindices()
+       return
+    SimpleHTTPServer.test(HandlerClass, ServerClass)
+
+
+if __name__ == '__main__':
+    test()
diff --git a/Lib/dos_8x3/compilea.py b/Lib/dos_8x3/compilea.py
new file mode 100755 (executable)
index 0000000..3120284
--- /dev/null
@@ -0,0 +1,70 @@
+# Routines to force "compilation" of all .py files in a directory
+# tree or on sys.path.  By default recursion is pruned at a depth of
+# 10 and the current directory, if it occurs in sys.path, is skipped.
+# When called as a script, compiles argument directories, or sys.path
+# if no arguments.
+# After a similar module by Sjoerd Mullender.
+
+import os
+import sys
+import py_compile
+
+def compile_dir(dir, maxlevels = 10):
+       print 'Listing', dir, '...'
+       try:
+               names = os.listdir(dir)
+       except os.error:
+               print "Can't list", dir
+               names = []
+       names.sort()
+       for name in names:
+               fullname = os.path.join(dir, name)
+               if os.path.isfile(fullname):
+                       head, tail = name[:-3], name[-3:]
+                       if tail == '.py':
+                               print 'Compiling', fullname, '...'
+                               try:
+                                       py_compile.compile(fullname)
+                               except KeyboardInterrupt:
+                                       del names[:]
+                                       print '\n[interrupt]'
+                                       break
+                               except:
+                                       if type(sys.exc_type) == type(''):
+                                               exc_type_name = sys.exc_type
+                                       else: exc_type_name = sys.exc_type.__name__
+                                       print 'Sorry:', exc_type_name + ':',
+                                       print sys.exc_value
+               elif maxlevels > 0 and \
+                    name != os.curdir and name != os.pardir and \
+                    os.path.isdir(fullname) and \
+                    not os.path.islink(fullname):
+                       compile_dir(fullname, maxlevels - 1)
+
+def compile_path(skip_curdir = 1):
+       for dir in sys.path:
+               if dir == os.curdir and skip_curdir:
+                       print 'Skipping current directory'
+               else:
+                       compile_dir(dir, 0)
+
+def main():
+       import getopt
+       try:
+               opts, args = getopt.getopt(sys.argv[1:], 'l')
+       except getopt.error, msg:
+               print msg
+               print "usage: compileall [-l] [directory ...]"
+               print "-l: don't recurse down"
+               print "if no arguments, -l sys.path is assumed"
+       maxlevels = 10
+       for o, a in opts:
+               if o == '-l': maxlevels = 0
+       if args:
+               for dir in sys.argv[1:]:
+                       compile_dir(dir, maxlevels)
+       else:
+               compile_path()
+
+if __name__ == '__main__':
+       main()
diff --git a/Lib/dos_8x3/complex.py b/Lib/dos_8x3/complex.py
new file mode 100755 (executable)
index 0000000..f4892f3
--- /dev/null
@@ -0,0 +1,275 @@
+# Complex numbers
+# ---------------
+
+# This module represents complex numbers as instances of the class Complex.
+# A Complex instance z has two data attribues, z.re (the real part) and z.im
+# (the imaginary part).  In fact, z.re and z.im can have any value -- all
+# arithmetic operators work regardless of the type of z.re and z.im (as long
+# as they support numerical operations).
+#
+# The following functions exist (Complex is actually a class):
+# Complex([re [,im]) -> creates a complex number from a real and an imaginary part
+# IsComplex(z) -> true iff z is a complex number (== has .re and .im attributes)
+# Polar([r [,phi [,fullcircle]]]) ->
+#      the complex number z for which r == z.radius() and phi == z.angle(fullcircle)
+#      (r and phi default to 0)
+#
+# Complex numbers have the following methods:
+# z.abs() -> absolute value of z
+# z.radius() == z.abs()
+# z.angle([fullcircle]) -> angle from positive X axis; fullcircle gives units
+# z.phi([fullcircle]) == z.angle(fullcircle)
+#
+# These standard functions and unary operators accept complex arguments:
+# abs(z)
+# -z
+# +z
+# not z
+# repr(z) == `z`
+# str(z)
+# hash(z) -> a combination of hash(z.re) and hash(z.im) such that if z.im is zero
+#            the result equals hash(z.re)
+# Note that hex(z) and oct(z) are not defined.
+#
+# These conversions accept complex arguments only if their imaginary part is zero:
+# int(z)
+# long(z)
+# float(z)
+#
+# The following operators accept two complex numbers, or one complex number
+# and one real number (int, long or float):
+# z1 + z2
+# z1 - z2
+# z1 * z2
+# z1 / z2
+# pow(z1, z2)
+# cmp(z1, z2)
+# Note that z1 % z2 and divmod(z1, z2) are not defined,
+# nor are shift and mask operations.
+#
+# The standard module math does not support complex numbers.
+# (I suppose it would be easy to implement a cmath module.)
+#
+# Idea:
+# add a class Polar(r, phi) and mixed-mode arithmetic which
+# chooses the most appropriate type for the result:
+# Complex for +,-,cmp
+# Polar   for *,/,pow
+
+
+import types, math
+
+if not hasattr(math, 'hypot'):
+       def hypot(x, y):
+               # XXX I know there's a way to compute this without possibly causing
+               # overflow, but I can't remember what it is right now...
+               return math.sqrt(x*x + y*y)
+       math.hypot = hypot
+
+twopi = math.pi*2.0
+halfpi = math.pi/2.0
+
+def IsComplex(obj):
+       return hasattr(obj, 're') and hasattr(obj, 'im')
+
+def Polar(r = 0, phi = 0, fullcircle = twopi):
+       phi = phi * (twopi / fullcircle)
+       return Complex(math.cos(phi)*r, math.sin(phi)*r)
+
+class Complex:
+
+       def __init__(self, re=0, im=0):
+               if IsComplex(re):
+                       im = im + re.im
+                       re = re.re
+               if IsComplex(im):
+                       re = re - im.im
+                       im = im.re
+               self.re = re
+               self.im = im
+
+       def __setattr__(self, name, value):
+               if hasattr(self, name):
+                       raise TypeError, "Complex numbers have set-once attributes"
+               self.__dict__[name] = value
+
+       def __repr__(self):
+               if not self.im:
+                       return 'Complex(%s)' % `self.re`
+               else:
+                       return 'Complex(%s, %s)' % (`self.re`, `self.im`)
+
+       def __str__(self):
+               if not self.im:
+                       return `self.re`
+               else:
+                       return 'Complex(%s, %s)' % (`self.re`, `self.im`)
+
+       def __coerce__(self, other):
+               if IsComplex(other):
+                       return self, other
+               return self, Complex(other)     # May fail
+
+       def __cmp__(self, other):
+               return cmp(self.re, other.re) or cmp(self.im, other.im)
+
+       def __hash__(self):
+               if not self.im: return hash(self.re)
+               mod = sys.maxint + 1L
+               return int((hash(self.re) + 2L*hash(self.im) + mod) % (2L*mod) - mod)
+
+       def __neg__(self):
+               return Complex(-self.re, -self.im)
+
+       def __pos__(self):
+               return self
+
+       def __abs__(self):
+               return math.hypot(self.re, self.im)
+               ##return math.sqrt(self.re*self.re + self.im*self.im)
+
+
+       def __int__(self):
+               if self.im:
+                       raise ValueError, "can't convert Complex with nonzero im to int"
+               return int(self.re)
+
+       def __long__(self):
+               if self.im:
+                       raise ValueError, "can't convert Complex with nonzero im to long"
+               return long(self.re)
+
+       def __float__(self):
+               if self.im:
+                       raise ValueError, "can't convert Complex with nonzero im to float"
+               return float(self.re)
+
+       def __nonzero__(self):
+               return not (self.re == self.im == 0)
+
+       abs = radius = __abs__
+
+       def angle(self, fullcircle = twopi):
+               return (fullcircle/twopi) * ((halfpi - math.atan2(self.re, self.im)) % twopi)
+
+       phi = angle
+
+       def __add__(self, other):
+               return Complex(self.re + other.re, self.im + other.im)
+
+       __radd__ = __add__
+
+       def __sub__(self, other):
+               return Complex(self.re - other.re, self.im - other.im)
+
+       def __rsub__(self, other):
+               return Complex(other.re - self.re, other.im - self.im)
+
+       def __mul__(self, other):
+               return Complex(self.re*other.re - self.im*other.im,
+                              self.re*other.im + self.im*other.re)
+
+       __rmul__ = __mul__
+
+       def __div__(self, other):
+               # Deviating from the general principle of not forcing re or im
+               # to be floats, we cast to float here, otherwise division
+               # of Complex numbers with integer re and im parts would use
+               # the (truncating) integer division
+               d = float(other.re*other.re + other.im*other.im)
+               if not d: raise ZeroDivisionError, 'Complex division'
+               return Complex((self.re*other.re + self.im*other.im) / d,
+                              (self.im*other.re - self.re*other.im) / d)
+
+       def __rdiv__(self, other):
+               return other / self
+
+       def __pow__(self, n, z=None):
+               if z is not None:
+                       raise TypeError, 'Complex does not support ternary pow()'
+               if IsComplex(n):
+                       if n.im: raise TypeError, 'Complex to the Complex power'
+                       n = n.re
+               r = pow(self.abs(), n)
+               phi = n*self.angle()
+               return Complex(math.cos(phi)*r, math.sin(phi)*r)
+       
+       def __rpow__(self, base):
+               return pow(base, self)
+
+
+# Everything below this point is part of the test suite
+
+def checkop(expr, a, b, value, fuzz = 1e-6):
+       import sys
+       print '       ', a, 'and', b,
+       try:
+               result = eval(expr)
+       except:
+               result = sys.exc_type
+       print '->', result
+       if (type(result) == type('') or type(value) == type('')):
+               ok = result == value
+       else:
+               ok = abs(result - value) <= fuzz
+       if not ok:
+               print '!!\t!!\t!! should be', value, 'diff', abs(result - value)
+
+
+def test():
+       testsuite = {
+               'a+b': [
+                       (1, 10, 11),
+                       (1, Complex(0,10), Complex(1,10)),
+                       (Complex(0,10), 1, Complex(1,10)),
+                       (Complex(0,10), Complex(1), Complex(1,10)),
+                       (Complex(1), Complex(0,10), Complex(1,10)),
+               ],
+               'a-b': [
+                       (1, 10, -9),
+                       (1, Complex(0,10), Complex(1,-10)),
+                       (Complex(0,10), 1, Complex(-1,10)),
+                       (Complex(0,10), Complex(1), Complex(-1,10)),
+                       (Complex(1), Complex(0,10), Complex(1,-10)),
+               ],
+               'a*b': [
+                       (1, 10, 10),
+                       (1, Complex(0,10), Complex(0, 10)),
+                       (Complex(0,10), 1, Complex(0,10)),
+                       (Complex(0,10), Complex(1), Complex(0,10)),
+                       (Complex(1), Complex(0,10), Complex(0,10)),
+               ],
+               'a/b': [
+                       (1., 10, 0.1),
+                       (1, Complex(0,10), Complex(0, -0.1)),
+                       (Complex(0, 10), 1, Complex(0, 10)),
+                       (Complex(0, 10), Complex(1), Complex(0, 10)),
+                       (Complex(1), Complex(0,10), Complex(0, -0.1)),
+               ],
+               'pow(a,b)': [
+                       (1, 10, 1),
+                       (1, Complex(0,10), 'TypeError'),
+                       (Complex(0,10), 1, Complex(0,10)),
+                       (Complex(0,10), Complex(1), Complex(0,10)),
+                       (Complex(1), Complex(0,10), 'TypeError'),
+                       (2, Complex(4,0), 16),
+               ],
+               'cmp(a,b)': [
+                       (1, 10, -1),
+                       (1, Complex(0,10), 1),
+                       (Complex(0,10), 1, -1),
+                       (Complex(0,10), Complex(1), -1),
+                       (Complex(1), Complex(0,10), 1),
+               ],
+       }
+       exprs = testsuite.keys()
+       exprs.sort()
+       for expr in exprs:
+               print expr + ':'
+               t = (expr,)
+               for item in testsuite[expr]:
+                       apply(checkop, t+item)
+       
+
+if __name__ == '__main__':
+       test()
diff --git a/Lib/dos_8x3/formatte.py b/Lib/dos_8x3/formatte.py
new file mode 100755 (executable)
index 0000000..0266379
--- /dev/null
@@ -0,0 +1,399 @@
+import regex
+import regsub
+import string
+import sys
+from types import StringType
+
+
+AS_IS = None
+
+
+class NullFormatter:
+
+    def __init__(self): pass
+    def end_paragraph(self, blankline): pass
+    def add_line_break(self): pass
+    def add_hor_rule(self, abswidth=None, percentwidth=1.0,
+                    height=None, align=None): pass
+    def add_label_data(self, format, counter): pass
+    def add_flowing_data(self, data): pass
+    def add_literal_data(self, data): pass
+    def flush_softspace(self): pass
+    def push_alignment(self, align): pass
+    def pop_alignment(self): pass
+    def push_font(self, x): pass
+    def pop_font(self): pass
+    def push_margin(self, margin): pass
+    def pop_margin(self): pass
+    def set_spacing(self, spacing): pass
+    def push_style(self, *styles): pass
+    def pop_style(self, n=1): pass
+    def assert_line_data(self, flag=1): pass
+
+
+class AbstractFormatter:
+
+    def __init__(self, writer):
+       self.writer = writer            # Output device
+       self.align = None               # Current alignment
+       self.align_stack = []           # Alignment stack
+       self.font_stack = []            # Font state
+       self.margin_stack = []          # Margin state
+       self.spacing = None             # Vertical spacing state
+       self.style_stack = []           # Other state, e.g. color
+       self.nospace = 1                # Should leading space be suppressed
+       self.softspace = 0              # Should a space be inserted
+       self.para_end = 1               # Just ended a paragraph
+       self.parskip = 0                # Skipped space between paragraphs?
+       self.hard_break = 1             # Have a hard break
+       self.have_label = 0
+
+    def end_paragraph(self, blankline):
+       if not self.hard_break:
+           self.writer.send_line_break()
+           self.have_label = 0
+       if self.parskip < blankline and not self.have_label:
+           self.writer.send_paragraph(blankline - self.parskip)
+           self.parskip = blankline
+           self.have_label = 0
+       self.hard_break = self.nospace = self.para_end = 1
+       self.softspace = 0
+
+    def add_line_break(self):
+       if not (self.hard_break or self.para_end):
+           self.writer.send_line_break()
+           self.have_label = self.parskip = 0
+       self.hard_break = self.nospace = 1
+       self.softspace = 0
+
+    def add_hor_rule(self, *args, **kw):
+       if not self.hard_break:
+           self.writer.send_line_break()
+       apply(self.writer.send_hor_rule, args, kw)
+       self.hard_break = self.nospace = 1
+       self.have_label = self.para_end = self.softspace = self.parskip = 0
+
+    def add_label_data(self, format, counter, blankline = None):
+       if self.have_label or not self.hard_break:
+           self.writer.send_line_break()
+       if not self.para_end:
+           self.writer.send_paragraph((blankline and 1) or 0)
+       if type(format) is StringType:
+           self.writer.send_label_data(self.format_counter(format, counter))
+       else:
+           self.writer.send_label_data(format)
+       self.nospace = self.have_label = self.hard_break = self.para_end = 1
+       self.softspace = self.parskip = 0
+
+    def format_counter(self, format, counter):
+        label = ''
+        for c in format:
+            try:
+                if c == '1':
+                   label = label + ('%d' % counter)
+                elif c in 'aA':
+                   if counter > 0:
+                       label = label + self.format_letter(c, counter)
+                elif c in 'iI':
+                   if counter > 0:
+                       label = label + self.format_roman(c, counter)
+               else:
+                   label = label + c
+            except:
+                label = label + c
+        return label
+
+    def format_letter(self, case, counter):
+       label = ''
+       while counter > 0:
+           counter, x = divmod(counter-1, 26)
+           s = chr(ord(case) + x)
+           label = s + label
+       return label
+
+    def format_roman(self, case, counter):
+        ones = ['i', 'x', 'c', 'm']
+        fives = ['v', 'l', 'd']
+        label, index = '', 0
+       # This will die of IndexError when counter is too big
+        while counter > 0:
+            counter, x = divmod(counter, 10)
+            if x == 9:
+                label = ones[index] + ones[index+1] + label
+            elif x == 4:
+                label = ones[index] + fives[index] + label
+            else:
+                if x >= 5:
+                    s = fives[index]
+                    x = x-5
+                else:
+                    s = ''
+                s = s + ones[index]*x
+               label = s + label
+            index = index + 1
+        if case == 'I':
+           return string.upper(label)
+        return label
+
+    def add_flowing_data(self, data,
+                        # These are only here to load them into locals:
+                        whitespace = string.whitespace,
+                        join = string.join, split = string.split):
+       if not data: return
+       # The following looks a bit convoluted but is a great improvement over
+       # data = regsub.gsub('[' + string.whitespace + ']+', ' ', data)
+       prespace = data[:1] in whitespace
+       postspace = data[-1:] in whitespace
+       data = join(split(data))
+       if self.nospace and not data:
+           return
+       elif prespace or self.softspace:
+           if not data:
+               if not self.nospace:
+                   self.softspace = 1
+                   self.parskip = 0
+               return
+           if not self.nospace:
+               data = ' ' + data
+       self.hard_break = self.nospace = self.para_end = \
+                         self.parskip = self.have_label = 0
+       self.softspace = postspace
+       self.writer.send_flowing_data(data)
+
+    def add_literal_data(self, data):
+       if not data: return
+       #  Caller is expected to cause flush_softspace() if needed.
+       self.hard_break = data[-1:] == '\n'
+       self.nospace = self.para_end = self.softspace = \
+                      self.parskip = self.have_label = 0
+       self.writer.send_literal_data(data)
+
+    def flush_softspace(self):
+       if self.softspace:
+           self.hard_break = self.nospace = self.para_end = self.parskip = \
+                             self.have_label = self.softspace = 0
+           self.writer.send_flowing_data(' ')
+
+    def push_alignment(self, align):
+       if align and align != self.align:
+           self.writer.new_alignment(align)
+           self.align = align
+           self.align_stack.append(align)
+       else:
+           self.align_stack.append(self.align)
+
+    def pop_alignment(self):
+       if self.align_stack:
+           del self.align_stack[-1]
+       if self.align_stack:
+           self.align = align = self.align_stack[-1]
+           self.writer.new_alignment(align)
+       else:
+           self.align = None
+           self.writer.new_alignment(None)
+
+    def push_font(self, (size, i, b, tt)):
+       if self.softspace:
+           self.hard_break = self.nospace = self.para_end = self.softspace = 0
+           self.writer.send_flowing_data(' ')
+       if self.font_stack:
+           csize, ci, cb, ctt = self.font_stack[-1]
+           if size is AS_IS: size = csize
+           if i is AS_IS: i = ci
+           if b is AS_IS: b = cb
+           if tt is AS_IS: tt = ctt
+       font = (size, i, b, tt)
+       self.font_stack.append(font)
+       self.writer.new_font(font)
+
+    def pop_font(self):
+       if self.softspace:
+           self.hard_break = self.nospace = self.para_end = self.softspace = 0
+           self.writer.send_flowing_data(' ')
+       if self.font_stack:
+           del self.font_stack[-1]
+       if self.font_stack:
+           font = self.font_stack[-1]
+       else:
+           font = None
+       self.writer.new_font(font)
+
+    def push_margin(self, margin):
+       self.margin_stack.append(margin)
+       fstack = filter(None, self.margin_stack)
+       if not margin and fstack:
+           margin = fstack[-1]
+       self.writer.new_margin(margin, len(fstack))
+
+    def pop_margin(self):
+       if self.margin_stack:
+           del self.margin_stack[-1]
+       fstack = filter(None, self.margin_stack)
+       if fstack:
+           margin = fstack[-1]
+       else:
+           margin = None
+       self.writer.new_margin(margin, len(fstack))
+
+    def set_spacing(self, spacing):
+       self.spacing = spacing
+       self.writer.new_spacing(spacing)
+
+    def push_style(self, *styles):
+       if self.softspace:
+           self.hard_break = self.nospace = self.para_end = self.softspace = 0
+           self.writer.send_flowing_data(' ')
+       for style in styles:
+           self.style_stack.append(style)
+       self.writer.new_styles(tuple(self.style_stack))
+
+    def pop_style(self, n=1):
+       if self.softspace:
+           self.hard_break = self.nospace = self.para_end = self.softspace = 0
+           self.writer.send_flowing_data(' ')
+       del self.style_stack[-n:]
+       self.writer.new_styles(tuple(self.style_stack))
+
+    def assert_line_data(self, flag=1):
+       self.nospace = self.hard_break = not flag
+       self.para_end = self.have_label = 0
+
+
+class NullWriter:
+    """Minimal writer interface to use in testing.
+    """
+    def __init__(self): pass
+    def new_alignment(self, align): pass
+    def new_font(self, font): pass
+    def new_margin(self, margin, level): pass
+    def new_spacing(self, spacing): pass
+    def new_styles(self, styles): pass
+    def send_paragraph(self, blankline): pass
+    def send_line_break(self): pass
+    def send_hor_rule(self, *args, **kw): pass
+    def send_label_data(self, data): pass
+    def send_flowing_data(self, data): pass
+    def send_literal_data(self, data): pass
+
+
+class AbstractWriter(NullWriter):
+
+    def __init__(self):
+       pass
+
+    def new_alignment(self, align):
+       print "new_alignment(%s)" % `align`
+
+    def new_font(self, font):
+       print "new_font(%s)" % `font`
+
+    def new_margin(self, margin, level):
+       print "new_margin(%s, %d)" % (`margin`, level)
+
+    def new_spacing(self, spacing):
+       print "new_spacing(%s)" % `spacing`
+
+    def new_styles(self, styles):
+       print "new_styles(%s)" % `styles`
+
+    def send_paragraph(self, blankline):
+       print "send_paragraph(%s)" % `blankline`
+
+    def send_line_break(self):
+       print "send_line_break()"
+
+    def send_hor_rule(self, *args, **kw):
+       print "send_hor_rule()"
+
+    def send_label_data(self, data):
+       print "send_label_data(%s)" % `data`
+
+    def send_flowing_data(self, data):
+       print "send_flowing_data(%s)" % `data`
+
+    def send_literal_data(self, data):
+       print "send_literal_data(%s)" % `data`
+
+
+class DumbWriter(NullWriter):
+
+    def __init__(self, file=None, maxcol=72):
+       self.file = file or sys.stdout
+       self.maxcol = maxcol
+       NullWriter.__init__(self)
+       self.reset()
+
+    def reset(self):
+       self.col = 0
+       self.atbreak = 0
+
+    def send_paragraph(self, blankline):
+       self.file.write('\n' + '\n'*blankline)
+       self.col = 0
+       self.atbreak = 0
+
+    def send_line_break(self):
+       self.file.write('\n')
+       self.col = 0
+       self.atbreak = 0
+
+    def send_hor_rule(self, *args, **kw):
+       self.file.write('\n')
+       self.file.write('-'*self.maxcol)
+       self.file.write('\n')
+       self.col = 0
+       self.atbreak = 0
+
+    def send_literal_data(self, data):
+       self.file.write(data)
+       i = string.rfind(data, '\n')
+       if i >= 0:
+           self.col = 0
+           data = data[i+1:]
+       data = string.expandtabs(data)
+       self.col = self.col + len(data)
+       self.atbreak = 0
+
+    def send_flowing_data(self, data):
+       if not data: return
+       atbreak = self.atbreak or data[0] in string.whitespace
+       col = self.col
+       maxcol = self.maxcol
+       write = self.file.write
+       for word in string.split(data):
+           if atbreak:
+               if col + len(word) >= maxcol:
+                   write('\n')
+                   col = 0
+               else:
+                   write(' ')
+                   col = col + 1
+           write(word)
+           col = col + len(word)
+           atbreak = 1
+       self.col = col
+       self.atbreak = data[-1] in string.whitespace
+
+
+def test(file = None):
+    w = DumbWriter()
+    f = AbstractFormatter(w)
+    if file:
+       fp = open(file)
+    elif sys.argv[1:]:
+       fp = open(sys.argv[1])
+    else:
+       fp = sys.stdin
+    while 1:
+       line = fp.readline()
+       if not line:
+           break
+       if line == '\n':
+           f.end_paragraph(1)
+       else:
+           f.add_flowing_data(line)
+    f.end_paragraph(0)
+
+
+if __name__ == '__main__':
+    test()
diff --git a/Lib/dos_8x3/gopherli.py b/Lib/dos_8x3/gopherli.py
new file mode 100755 (executable)
index 0000000..cf06e95
--- /dev/null
@@ -0,0 +1,191 @@
+# Gopher protocol client interface
+
+import string
+
+# Default selector, host and port
+DEF_SELECTOR = '1/'
+DEF_HOST     = 'gopher.micro.umn.edu'
+DEF_PORT     = 70
+
+# Recognized file types
+A_TEXT       = '0'
+A_MENU       = '1'
+A_CSO        = '2'
+A_ERROR      = '3'
+A_MACBINHEX  = '4'
+A_PCBINHEX   = '5'
+A_UUENCODED  = '6'
+A_INDEX      = '7'
+A_TELNET     = '8'
+A_BINARY     = '9'
+A_DUPLICATE  = '+'
+A_SOUND      = 's'
+A_EVENT      = 'e'
+A_CALENDAR   = 'c'
+A_HTML       = 'h'
+A_TN3270     = 'T'
+A_MIME       = 'M'
+A_IMAGE      = 'I'
+A_WHOIS      = 'w'
+A_QUERY      = 'q'
+A_GIF        = 'g'
+A_HTML       = 'h'                     # HTML file
+A_WWW        = 'w'                     # WWW address
+A_PLUS_IMAGE = ':'
+A_PLUS_MOVIE = ';'
+A_PLUS_SOUND = '<'
+
+
+# Function mapping all file types to strings; unknown types become TYPE='x'
+_names = dir()
+_type_to_name_map = None
+def type_to_name(gtype):
+       global _type_to_name_map
+       if not _type_to_name_map:
+               for name in _names:
+                       if name[:2] == 'A_':
+                               _type_to_name_map[eval(name)] = name[2:]
+       if _type_to_name_map.has_key(gtype):
+               return _type_to_name_map[gtype]
+       return 'TYPE=' + `gtype`
+
+# Names for characters and strings
+CRLF = '\r\n'
+TAB = '\t'
+
+# Send a selector to a given host and port, return a file with the reply
+def send_selector(selector, host, port = 0):
+       import socket
+       import string
+       if not port:
+               i = string.find(host, ':')
+               if i >= 0:
+                       host, port = host[:i], string.atoi(host[i+1:])
+       if not port:
+               port = DEF_PORT
+       elif type(port) == type(''):
+               port = string.atoi(port)
+       s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+       s.connect(host, port)
+       s.send(selector + CRLF)
+       s.shutdown(1)
+       return s.makefile('rb')
+
+# Send a selector and a query string
+def send_query(selector, query, host, port = 0):
+       return send_selector(selector + '\t' + query, host, port)
+
+# The following functions interpret the data returned by the gopher
+# server according to the expected type, e.g. textfile or directory
+
+# Get a directory in the form of a list of entries
+def get_directory(f):
+       import string
+       list = []
+       while 1:
+               line = f.readline()
+               if not line:
+                       print '(Unexpected EOF from server)'
+                       break
+               if line[-2:] == CRLF:
+                       line = line[:-2]
+               elif line[-1:] in CRLF:
+                       line = line[:-1]
+               if line == '.':
+                       break
+               if not line:
+                       print '(Empty line from server)'
+                       continue
+               gtype = line[0]
+               parts = string.splitfields(line[1:], TAB)
+               if len(parts) < 4:
+                       print '(Bad line from server:', `line`, ')'
+                       continue
+               if len(parts) > 4:
+                       if parts[4:] != ['+']:
+                           print '(Extra info from server:', parts[4:], ')'
+               else:
+                       parts.append('')
+               parts.insert(0, gtype)
+               list.append(parts)
+       return list
+
+# Get a text file as a list of lines, with trailing CRLF stripped
+def get_textfile(f):
+       list = []
+       get_alt_textfile(f, list.append)
+       return list
+
+# Get a text file and pass each line to a function, with trailing CRLF stripped
+def get_alt_textfile(f, func):
+       while 1:
+               line = f.readline()
+               if not line:
+                       print '(Unexpected EOF from server)'
+                       break
+               if line[-2:] == CRLF:
+                       line = line[:-2]
+               elif line[-1:] in CRLF:
+                       line = line[:-1]
+               if line == '.':
+                       break
+               if line[:2] == '..':
+                       line = line[1:]
+               func(line)
+
+# Get a binary file as one solid data block
+def get_binary(f):
+       data = f.read()
+       return data
+
+# Get a binary file and pass each block to a function
+def get_alt_binary(f, func, blocksize):
+       while 1:
+               data = f.read(blocksize)
+               if not data:
+                       break
+               func(data)
+
+# Trivial test program
+def test():
+       import sys
+       import getopt
+       opts, args = getopt.getopt(sys.argv[1:], '')
+       selector = DEF_SELECTOR
+       type = selector[0]
+       host = DEF_HOST
+       port = DEF_PORT
+       if args:
+               host = args[0]
+               args = args[1:]
+       if args:
+               type = args[0]
+               args = args[1:]
+               if len(type) > 1:
+                       type, selector = type[0], type
+               else:
+                       selector = ''
+                       if args:
+                               selector = args[0]
+                               args = args[1:]
+               query = ''
+               if args:
+                       query = args[0]
+                       args = args[1:]
+       if type == A_INDEX:
+               f = send_query(selector, query, host)
+       else:
+               f = send_selector(selector, host)
+       if type == A_TEXT:
+               list = get_textfile(f)
+               for item in list: print item
+       elif type in (A_MENU, A_INDEX):
+               list = get_directory(f)
+               for item in list: print item
+       else:
+               data = get_binary(f)
+               print 'binary data:', len(data), 'bytes:', `data[:100]`[:40]
+
+# Run the test when run as script
+if __name__ == '__main__':
+       test()
diff --git a/Lib/dos_8x3/htmlenti.py b/Lib/dos_8x3/htmlenti.py
new file mode 100755 (executable)
index 0000000..55aefaa
--- /dev/null
@@ -0,0 +1,105 @@
+# Proposed entity definitions for HTML, taken from
+# http://www.w3.org/hypertext/WWW/MarkUp/html-spec/html-spec_14.html
+
+entitydefs = {
+    'lt':       '<',
+    'gt':       '>',
+    'amp':      '&',
+    'quot':    '"',
+    'nbsp':    chr(160),       # no-break space
+    'iexcl':   chr(161),       # inverted exclamation mark
+    'cent':    chr(162),       # cent sign
+    'pound':   chr(163),       # pound sterling sign
+    'curren':  chr(164),       # general currency sign
+    'yen':     chr(165),       # yen sign
+    'brvbar':  chr(166),       # broken (vertical) bar
+    'sect':    chr(167),       # section sign
+    'uml':     chr(168),       # umlaut (dieresis)
+    'copy':    chr(169),       # copyright sign
+    'ordf':    chr(170),       # ordinal indicator, feminine
+    'laquo':   chr(171),       # angle quotation mark, left
+    'not':     chr(172),       # not sign
+    'shy':     chr(173),       # soft hyphen
+    'reg':     chr(174),       # registered sign
+    'macr':    chr(175),       # macron
+    'deg':     chr(176),       # degree sign
+    'plusmn':  chr(177),       # plus-or-minus sign
+    'sup2':    chr(178),       # superscript two
+    'sup3':    chr(179),       # superscript three
+    'acute':   chr(180),       # acute accent
+    'micro':   chr(181),       # micro sign
+    'para':    chr(182),       # pilcrow (paragraph sign)
+    'middot':  chr(183),       # middle dot
+    'cedil':   chr(184),       # cedilla
+    'sup1':    chr(185),       # superscript one
+    'ordm':    chr(186),       # ordinal indicator, masculine
+    'raquo':   chr(187),       # angle quotation mark, right
+    'frac14':  chr(188),       # fraction one-quarter
+    'frac12':  chr(189),       # fraction one-half
+    'frac34':  chr(190),       # fraction three-quarters
+    'iquest':  chr(191),       # inverted question mark
+    'Agrave':  chr(192),       # capital A, grave accent
+    'Aacute':  chr(193),       # capital A, acute accent
+    'Acirc':   chr(194),       # capital A, circumflex accent
+    'Atilde':  chr(195),       # capital A, tilde
+    'Auml':    chr(196),       # capital A, dieresis or umlaut mark
+    'Aring':   chr(197),       # capital A, ring
+    'AElig':   chr(198),       # capital AE diphthong (ligature)
+    'Ccedil':  chr(199),       # capital C, cedilla
+    'Egrave':  chr(200),       # capital E, grave accent
+    'Eacute':  chr(201),       # capital E, acute accent
+    'Ecirc':   chr(202),       # capital E, circumflex accent
+    'Euml':    chr(203),       # capital E, dieresis or umlaut mark
+    'Igrave':  chr(204),       # capital I, grave accent
+    'Iacute':  chr(205),       # capital I, acute accent
+    'Icirc':   chr(206),       # capital I, circumflex accent
+    'Iuml':    chr(207),       # capital I, dieresis or umlaut mark
+    'ETH':     chr(208),       # capital Eth, Icelandic
+    'Ntilde':  chr(209),       # capital N, tilde
+    'Ograve':  chr(210),       # capital O, grave accent
+    'Oacute':  chr(211),       # capital O, acute accent
+    'Ocirc':   chr(212),       # capital O, circumflex accent
+    'Otilde':  chr(213),       # capital O, tilde
+    'Ouml':    chr(214),       # capital O, dieresis or umlaut mark
+    'times':   chr(215),       # multiply sign
+    'Oslash':  chr(216),       # capital O, slash
+    'Ugrave':  chr(217),       # capital U, grave accent
+    'Uacute':  chr(218),       # capital U, acute accent
+    'Ucirc':   chr(219),       # capital U, circumflex accent
+    'Uuml':    chr(220),       # capital U, dieresis or umlaut mark
+    'Yacute':  chr(221),       # capital Y, acute accent
+    'THORN':   chr(222),       # capital THORN, Icelandic
+    'szlig':   chr(223),       # small sharp s, German (sz ligature)
+    'agrave':  chr(224),       # small a, grave accent
+    'aacute':  chr(225),       # small a, acute accent
+    'acirc':   chr(226),       # small a, circumflex accent
+    'atilde':  chr(227),       # small a, tilde
+    'auml':    chr(228),       # small a, dieresis or umlaut mark
+    'aring':   chr(229),       # small a, ring
+    'aelig':   chr(230),       # small ae diphthong (ligature)
+    'ccedil':  chr(231),       # small c, cedilla
+    'egrave':  chr(232),       # small e, grave accent
+    'eacute':  chr(233),       # small e, acute accent
+    'ecirc':   chr(234),       # small e, circumflex accent
+    'euml':    chr(235),       # small e, dieresis or umlaut mark
+    'igrave':  chr(236),       # small i, grave accent
+    'iacute':  chr(237),       # small i, acute accent
+    'icirc':   chr(238),       # small i, circumflex accent
+    'iuml':    chr(239),       # small i, dieresis or umlaut mark
+    'eth':     chr(240),       # small eth, Icelandic
+    'ntilde':  chr(241),       # small n, tilde
+    'ograve':  chr(242),       # small o, grave accent
+    'oacute':  chr(243),       # small o, acute accent
+    'ocirc':   chr(244),       # small o, circumflex accent
+    'otilde':  chr(245),       # small o, tilde
+    'ouml':    chr(246),       # small o, dieresis or umlaut mark
+    'divide':  chr(247),       # divide sign
+    'oslash':  chr(248),       # small o, slash
+    'ugrave':  chr(249),       # small u, grave accent
+    'uacute':  chr(250),       # small u, acute accent
+    'ucirc':   chr(251),       # small u, circumflex accent
+    'uuml':    chr(252),       # small u, dieresis or umlaut mark
+    'yacute':  chr(253),       # small y, acute accent
+    'thorn':   chr(254),       # small thorn, Icelandic
+    'yuml':    chr(255),       # small y, dieresis or umlaut mark
+}
diff --git a/Lib/dos_8x3/importal.py b/Lib/dos_8x3/importal.py
new file mode 100755 (executable)
index 0000000..780862c
--- /dev/null
@@ -0,0 +1,36 @@
+# THIS IS OBSOLETE -- USE MODULE 'compileall' INSTEAD!
+
+# Utility module to import all modules in the path, in the hope
+# that this will update their ".pyc" files.
+
+import os
+import sys
+
+# Sabotage 'gl' and 'stdwin' to prevent windows popping up...
+for m in 'gl', 'stdwin', 'fl', 'fm':
+       sys.modules[m] = sys
+
+exceptions = ['importall']
+
+for dir in sys.path:
+       print 'Listing', dir
+       try:
+               names = os.listdir(dir)
+       except os.error:
+               print 'Can\'t list', dir
+               names = []
+       names.sort()
+       for name in names:
+               head, tail = name[:-3], name[-3:]
+               if tail == '.py' and head not in exceptions:
+                       s = 'import ' + head
+                       print s
+                       try:
+                               exec s + '\n'
+                       except KeyboardInterrupt:
+                               del names[:]
+                               print '\n[interrupt]'
+                               break
+                       except:
+                               print 'Sorry:', sys.exc_type + ':',
+                               print sys.exc_value
diff --git a/Lib/dos_8x3/linecach.py b/Lib/dos_8x3/linecach.py
new file mode 100755 (executable)
index 0000000..7de373f
--- /dev/null
@@ -0,0 +1,90 @@
+# Cache lines from files.
+# This is intended to read lines from modules imported -- hence if a filename
+# is not found, it will look down the module search path for a file by
+# that name.
+
+import sys
+import os
+from stat import *
+
+def getline(filename, lineno):
+       lines = getlines(filename)
+       if 1 <= lineno <= len(lines):
+               return lines[lineno-1]
+       else:
+               return ''
+
+
+# The cache
+
+cache = {} # The cache
+
+
+# Clear the cache entirely
+
+def clearcache():
+       global cache
+       cache = {}
+
+
+# Get the lines for a file from the cache.
+# Update the cache if it doesn't contain an entry for this file already.
+
+def getlines(filename):
+       if cache.has_key(filename):
+               return cache[filename][2]
+       else:
+               return updatecache(filename)
+
+
+# Discard cache entries that are out of date.
+# (This is not checked upon each call!)
+
+def checkcache():
+       for filename in cache.keys():
+               size, mtime, lines, fullname = cache[filename]
+               try:
+                       stat = os.stat(fullname)
+               except os.error:
+                       del cache[filename]
+                       continue
+               if size <> stat[ST_SIZE] or mtime <> stat[ST_MTIME]:
+                       del cache[filename]
+
+
+# Update a cache entry and return its list of lines.
+# If something's wrong, print a message, discard the cache entry,
+# and return an empty list.
+
+def updatecache(filename):
+       if cache.has_key(filename):
+               del cache[filename]
+       if not filename or filename[0] + filename[-1] == '<>':
+               return []
+       fullname = filename
+       try:
+               stat = os.stat(fullname)
+       except os.error, msg:
+               # Try looking through the module search path
+               basename = os.path.split(filename)[1]
+               for dirname in sys.path:
+                       fullname = os.path.join(dirname, basename)
+                       try:
+                               stat = os.stat(fullname)
+                               break
+                       except os.error:
+                               pass
+               else:
+                       # No luck
+##                     print '*** Cannot stat', filename, ':', msg
+                       return []
+       try:
+               fp = open(fullname, 'r')
+               lines = fp.readlines()
+               fp.close()
+       except IOError, msg:
+##             print '*** Cannot open', fullname, ':', msg
+               return []
+       size, mtime = stat[ST_SIZE], stat[ST_MTIME]
+       cache[filename] = size, mtime, lines, fullname
+       return lines
diff --git a/Lib/dos_8x3/macurl2p.py b/Lib/dos_8x3/macurl2p.py
new file mode 100755 (executable)
index 0000000..65afe69
--- /dev/null
@@ -0,0 +1,76 @@
+"""Mac specific module for conversion between pathnames and URLs."""
+
+import string
+import urllib
+import os
+
+def url2pathname(pathname):
+    "Convert /-delimited pathname to mac pathname"
+    #
+    # XXXX The .. handling should be fixed...
+    #
+    tp = urllib.splittype(pathname)[0]
+    if tp and tp <> 'file':
+       raise RuntimeError, 'Cannot convert non-local URL to pathname'
+    components = string.split(pathname, '/')
+    # Remove . and embedded ..
+    i = 0
+    while i < len(components):
+       if components[i] == '.':
+           del components[i]
+       elif components[i] == '..' and i > 0 and \
+                                 components[i-1] not in ('', '..'):
+           del components[i-1:i+1]
+           i = i-1
+       elif components[i] == '' and i > 0 and components[i-1] <> '':
+           del components[i]
+       else:
+           i = i+1
+    if not components[0]:
+       # Absolute unix path, don't start with colon
+       return string.join(components[1:], ':')
+    else:
+       # relative unix path, start with colon. First replace
+       # leading .. by empty strings (giving ::file)
+       i = 0
+       while i < len(components) and components[i] == '..':
+           components[i] = ''
+           i = i + 1
+       return ':' + string.join(components, ':')
+
+def pathname2url(pathname):
+    "convert mac pathname to /-delimited pathname"
+    if '/' in pathname:
+       raise RuntimeError, "Cannot convert pathname containing slashes"
+    components = string.split(pathname, ':')
+    # Replace empty string ('::') by .. (will result in '/../' later)
+    for i in range(1, len(components)):
+       if components[i] == '':
+           components[i] = '..'
+    # Truncate names longer than 31 bytes
+    components = map(lambda x: x[:31], components)
+
+    if os.path.isabs(pathname):
+       return '/' + string.join(components, '/')
+    else:
+       return string.join(components, '/')
+
+def test():
+    for url in ["index.html",
+               "bar/index.html",
+               "/foo/bar/index.html",
+               "/foo/bar/",
+               "/"]:
+       print `url`, '->', `url2pathname(url)`
+    for path in ["drive:",
+                "drive:dir:",
+                "drive:dir:file",
+                "drive:file",
+                "file",
+                ":file",
+                ":dir:",
+                ":dir:file"]:
+       print `path`, '->', `pathname2url(path)`
+
+if __name__ == '__main__':
+    test()
diff --git a/Lib/dos_8x3/mimetool.py b/Lib/dos_8x3/mimetool.py
new file mode 100755 (executable)
index 0000000..da33a77
--- /dev/null
@@ -0,0 +1,187 @@
+# Various tools used by MIME-reading or MIME-writing programs.
+
+
+import os
+import rfc822
+import string
+import tempfile
+
+
+# A derived class of rfc822.Message that knows about MIME headers and
+# contains some hooks for decoding encoded and multipart messages.
+
+class Message(rfc822.Message):
+
+       def __init__(self, fp, seekable = 1):
+               rfc822.Message.__init__(self, fp, seekable)
+               self.encodingheader = \
+                       self.getheader('content-transfer-encoding')
+               self.typeheader = \
+                       self.getheader('content-type')
+               self.parsetype()
+               self.parseplist()
+
+       def parsetype(self):
+               str = self.typeheader
+               if str == None:
+                       str = 'text/plain'
+               if ';' in str:
+                       i = string.index(str, ';')
+                       self.plisttext = str[i:]
+                       str = str[:i]
+               else:
+                       self.plisttext = ''
+               fields = string.splitfields(str, '/')
+               for i in range(len(fields)):
+                       fields[i] = string.lower(string.strip(fields[i]))
+               self.type = string.joinfields(fields, '/')
+               self.maintype = fields[0]
+               self.subtype = string.joinfields(fields[1:], '/')
+
+       def parseplist(self):
+               str = self.plisttext
+               self.plist = []
+               while str[:1] == ';':
+                       str = str[1:]
+                       if ';' in str:
+                               # XXX Should parse quotes!
+                               end = string.index(str, ';')
+                       else:
+                               end = len(str)
+                       f = str[:end]
+                       if '=' in f:
+                               i = string.index(f, '=')
+                               f = string.lower(string.strip(f[:i])) + \
+                                       '=' + string.strip(f[i+1:])
+                       self.plist.append(string.strip(f))
+                       str = str[end:]
+
+       def getplist(self):
+               return self.plist
+
+       def getparam(self, name):
+               name = string.lower(name) + '='
+               n = len(name)
+               for p in self.plist:
+                       if p[:n] == name:
+                               return rfc822.unquote(p[n:])
+               return None
+
+       def getencoding(self):
+               if self.encodingheader == None:
+                       return '7bit'
+               return string.lower(self.encodingheader)
+
+       def gettype(self):
+               return self.type
+
+       def getmaintype(self):
+               return self.maintype
+
+       def getsubtype(self):
+               return self.subtype
+
+
+
+
+# Utility functions
+# -----------------
+
+
+# Return a random string usable as a multipart boundary.
+# The method used is so that it is *very* unlikely that the same
+# string of characters will every occur again in the Universe,
+# so the caller needn't check the data it is packing for the
+# occurrence of the boundary.
+#
+# The boundary contains dots so you have to quote it in the header.
+
+_prefix = None
+
+def choose_boundary():
+       global _prefix
+       import time
+       import rand
+       if _prefix == None:
+               import socket
+               import os
+               hostid = socket.gethostbyname(socket.gethostname())
+               uid = `os.getuid()`
+               pid = `os.getpid()`
+               seed = `rand.rand()`
+               _prefix = hostid + '.' + uid + '.' + pid
+       timestamp = `int(time.time())`
+       seed = `rand.rand()`
+       return _prefix + '.' + timestamp + '.' + seed
+
+
+# Subroutines for decoding some common content-transfer-types
+
+# XXX This requires that uudecode and mmencode are in $PATH
+
+def decode(input, output, encoding):
+       if decodetab.has_key(encoding):
+               pipethrough(input, decodetab[encoding], output)
+       else:
+               raise ValueError, \
+                     'unknown Content-Transfer-Encoding: %s' % encoding
+
+def encode(input, output, encoding):
+       if encodetab.has_key(encoding):
+               pipethrough(input, encodetab[encoding], output)
+       else:
+               raise ValueError, \
+                     'unknown Content-Transfer-Encoding: %s' % encoding
+
+uudecode_pipe = '''(
+TEMP=/tmp/@uu.$$
+sed "s%^begin [0-7][0-7]* .*%begin 600 $TEMP%" | uudecode
+cat $TEMP
+rm $TEMP
+)'''
+
+decodetab = {
+       'uuencode':             uudecode_pipe,
+       'x-uuencode':           uudecode_pipe,
+       'quoted-printable':     'mmencode -u -q',
+       'base64':               'mmencode -u -b',
+}
+
+encodetab = {
+       'x-uuencode':           'uuencode tempfile',
+       'uuencode':             'uuencode tempfile',
+       'quoted-printable':     'mmencode -q',
+       'base64':               'mmencode -b',
+}
+
+def pipeto(input, command):
+       pipe = os.popen(command, 'w')
+       copyliteral(input, pipe)
+       pipe.close()
+
+def pipethrough(input, command, output):
+       tempname = tempfile.mktemp()
+       try:
+               temp = open(tempname, 'w')
+       except IOError:
+               print '*** Cannot create temp file', `tempname`
+               return
+       copyliteral(input, temp)
+       temp.close()
+       pipe = os.popen(command + ' <' + tempname, 'r')
+       copybinary(pipe, output)
+       pipe.close()
+       os.unlink(tempname)
+
+def copyliteral(input, output):
+       while 1:
+               line = input.readline()
+               if not line: break
+               output.write(line)
+
+def copybinary(input, output):
+       BUFSIZE = 8192
+       while 1:
+               line = input.read(BUFSIZE)
+               if not line: break
+               output.write(line)
diff --git a/Lib/dos_8x3/multifil.py b/Lib/dos_8x3/multifil.py
new file mode 100755 (executable)
index 0000000..71e0dd0
--- /dev/null
@@ -0,0 +1,128 @@
+# A class that makes each part of a multipart message "feel" like an
+# ordinary file, as long as you use fp.readline().  Allows recursive
+# use, for nested multipart messages.  Probably best used together
+# with module mimetools.
+#
+# Suggested use:
+#
+# real_fp = open(...)
+# fp = MultiFile(real_fp)
+#
+# "read some lines from fp"
+# fp.push(separator)
+# while 1:
+#      "read lines from fp until it returns an empty string" (A)
+#      if not fp.next(): break
+# fp.pop()
+# "read remaining lines from fp until it returns an empty string"
+#
+# The latter sequence may be used recursively at (A).
+# It is also allowed to use multiple push()...pop() sequences.
+# Note that if a nested multipart message is terminated by a separator
+# for an outer message, this is not reported, even though it is really
+# illegal input.
+
+import sys
+import string
+
+err = sys.stderr.write
+
+Error = 'multifile.Error'
+
+class MultiFile:
+       #
+       def __init__(self, fp):
+               self.fp = fp
+               self.stack = [] # Grows down
+               self.level = 0
+               self.last = 0
+               self.start = self.fp.tell()
+               self.posstack = [] # Grows down
+       #
+       def tell(self):
+               if self.level > 0:
+                       return self.lastpos
+               return self.fp.tell() - self.start
+       #
+       def seek(self, pos):
+               if not 0 <= pos <= self.tell() or \
+                               self.level > 0 and pos > self.lastpos:
+                       raise Error, 'bad MultiFile.seek() call'
+               self.fp.seek(pos + self.start)
+               self.level = 0
+               self.last = 0
+       #
+       def readline(self):
+               if self.level > 0: return ''
+               line = self.fp.readline()
+               if not line:
+                       self.level = len(self.stack)
+                       self.last = (self.level > 0)
+                       if self.last:
+                               err('*** Sudden EOF in MultiFile.readline()\n')
+                       return ''
+               if line[:2] <> '--': return line
+               n = len(line)
+               k = n
+               while k > 0 and line[k-1] in string.whitespace: k = k-1
+               mark = line[2:k]
+               if mark[-2:] == '--': mark1 = mark[:-2]
+               else: mark1 = None
+               for i in range(len(self.stack)):
+                       sep = self.stack[i]
+                       if sep == mark:
+                               self.last = 0
+                               break
+                       elif mark1 <> None and sep == mark1:
+                               self.last = 1
+                               break
+               else:
+                       return line
+               # Get here after break out of loop
+               self.lastpos = self.tell() - len(line)
+               self.level = i+1
+               if self.level > 1:
+                       err('*** Missing endmarker in MultiFile.readline()\n')
+               return ''
+       #
+       def readlines(self):
+               list = []
+               while 1:
+                       line = self.readline()
+                       if not line: break
+                       list.append(line)
+               return list
+       #
+       def read(self): # Note: no size argument -- read until EOF only!
+               return string.joinfields(self.readlines(), '')
+       #
+       def next(self):
+               while self.readline(): pass
+               if self.level > 1 or self.last:
+                       return 0
+               self.level = 0
+               self.last = 0
+               self.start = self.fp.tell()
+               return 1
+       #
+       def push(self, sep):
+               if self.level > 0:
+                       raise Error, 'bad MultiFile.push() call'
+               self.stack.insert(0, sep)
+               self.posstack.insert(0, self.start)
+               self.start = self.fp.tell()
+       #
+       def pop(self):
+               if self.stack == []:
+                       raise Error, 'bad MultiFile.pop() call'
+               if self.level <= 1:
+                       self.last = 0
+               else:
+                       abslastpos = self.lastpos + self.start
+               self.level = max(0, self.level - 1)
+               del self.stack[0]
+               self.start = self.posstack[0]
+               del self.posstack[0]
+               if self.level > 0:
+                       self.lastpos = abslastpos - self.start
+       #
diff --git a/Lib/dos_8x3/nturl2pa.py b/Lib/dos_8x3/nturl2pa.py
new file mode 100755 (executable)
index 0000000..17f01f5
--- /dev/null
@@ -0,0 +1,52 @@
+#
+# nturl2path convert a NT pathname to a file URL and 
+# vice versa  
+
+def url2pathname(url):
+       """ Convert a URL to a DOS path...
+       Currently only works for absolute paths
+
+               ///C|/foo/bar/spam.foo
+
+                       becomes
+
+               C:\foo\bar\spam.foo
+       """
+       import string
+       comp = string.splitfields(url, '|')
+       if len(comp) != 2 or comp[0][-1] not in string.letters:
+               error = 'Bad URL: ' + url
+               raise IOError, error
+       drive = string.upper(comp[0][-1])
+       components = string.splitfields(comp[1], '/')
+       path = drive + ':'
+       for  comp in components:
+               if comp:
+                       path = path + '\\' + comp
+       return path
+
+def pathname2url(p):
+
+       """ Convert a DOS path name to a file url...
+       Currently only works for absolute paths
+
+               C:\foo\bar\spam.foo
+
+                       becomes
+
+               ///C|/foo/bar/spam.foo
+       """
+
+       import string
+       comp = string.splitfields(p, ':')
+       if len(comp) != 2 or len(comp[0]) > 1:
+               error = 'Bad path: ' + p
+               raise IOError, error
+
+       drive = string.upper(comp[0])
+       components = string.splitfields(comp[1], '\\')
+       path = '///' + drive + '|'
+       for comp in components:
+               if comp:
+                       path = path + '/' + comp
+       return path
diff --git a/Lib/dos_8x3/para.py b/Lib/dos_8x3/para.py
new file mode 100755 (executable)
index 0000000..c9b3bdd
--- /dev/null
@@ -0,0 +1,409 @@
+# Text formatting abstractions
+# Note -- this module is obsolete, it's too slow anyway
+
+
+# Oft-used type object
+Int = type(0)
+
+
+# Represent a paragraph.  This is a list of words with associated
+# font and size information, plus indents and justification for the
+# entire paragraph.
+# Once the words have been added to a paragraph, it can be laid out
+# for different line widths.  Once laid out, it can be rendered at
+# different screen locations.  Once rendered, it can be queried
+# for mouse hits, and parts of the text can be highlighted
+class Para:
+       #
+       def __init__(self):
+               self.words = [] # The words
+               self.just = 'l' # Justification: 'l', 'r', 'lr' or 'c'
+               self.indent_left = self.indent_right = self.indent_hang = 0
+               # Final lay-out parameters, may change
+               self.left = self.top = self.right = self.bottom = \
+                       self.width = self.height = self.lines = None
+       #
+       # Add a word, computing size information for it.
+       # Words may also be added manually by appending to self.words
+       # Each word should be a 7-tuple:
+       # (font, text, width, space, stretch, ascent, descent)
+       def addword(self, d, font, text, space, stretch):
+               if font <> None:
+                       d.setfont(font)
+               width = d.textwidth(text)
+               ascent = d.baseline()
+               descent = d.lineheight() - ascent
+               spw = d.textwidth(' ')
+               space = space * spw
+               stretch = stretch * spw
+               tuple = (font, text, width, space, stretch, ascent, descent)
+               self.words.append(tuple)
+       #
+       # Hooks to begin and end anchors -- insert numbers in the word list!
+       def bgn_anchor(self, id):
+               self.words.append(id)
+       #
+       def end_anchor(self, id):
+               self.words.append(0)
+       #
+       # Return the total length (width) of the text added so far, in pixels
+       def getlength(self):
+               total = 0
+               for word in self.words:
+                       if type(word) <> Int:
+                               total = total + word[2] + word[3]
+               return total
+       #
+       # Tab to a given position (relative to the current left indent):
+       # remove all stretch, add fixed space up to the new indent.
+       # If the current position is already beying the tab stop,
+       # don't add any new space (but still remove the stretch)
+       def tabto(self, tab):
+               total = 0
+               as, de = 1, 0
+               for i in range(len(self.words)):
+                       word = self.words[i]
+                       if type(word) == Int: continue
+                       fo, te, wi, sp, st, as, de = word
+                       self.words[i] = fo, te, wi, sp, 0, as, de
+                       total = total + wi + sp
+               if total < tab:
+                       self.words.append(None, '', 0, tab-total, 0, as, de)
+       #
+       # Make a hanging tag: tab to hang, increment indent_left by hang,
+       # and reset indent_hang to -hang
+       def makehangingtag(self, hang):
+               self.tabto(hang)
+               self.indent_left = self.indent_left + hang
+               self.indent_hang = -hang
+       #
+       # Decide where the line breaks will be given some screen width
+       def layout(self, linewidth):
+               self.width = linewidth
+               height = 0
+               self.lines = lines = []
+               avail1 = self.width - self.indent_left - self.indent_right
+               avail = avail1 - self.indent_hang
+               words = self.words
+               i = 0
+               n = len(words)
+               lastfont = None
+               while i < n:
+                       firstfont = lastfont
+                       charcount = 0
+                       width = 0
+                       stretch = 0
+                       ascent = 0
+                       descent = 0
+                       lsp = 0
+                       j = i
+                       while i < n:
+                               word = words[i]
+                               if type(word) == Int:
+                                       if word > 0 and width >= avail:
+                                               break
+                                       i = i+1
+                                       continue
+                               fo, te, wi, sp, st, as, de = word
+                               if width + wi > avail and width > 0 and wi > 0:
+                                       break
+                               if fo <> None:
+                                       lastfont = fo
+                                       if width == 0:
+                                               firstfont = fo
+                               charcount = charcount + len(te) + (sp > 0)
+                               width = width + wi + sp
+                               lsp = sp
+                               stretch = stretch + st
+                               lst = st
+                               ascent = max(ascent, as)
+                               descent = max(descent, de)
+                               i = i+1
+                       while i > j and type(words[i-1]) == Int and \
+                               words[i-1] > 0: i = i-1
+                       width = width - lsp
+                       if i < n:
+                               stretch = stretch - lst
+                       else:
+                               stretch = 0
+                       tuple = i-j, firstfont, charcount, width, stretch, \
+                               ascent, descent
+                       lines.append(tuple)
+                       height = height + ascent + descent
+                       avail = avail1
+               self.height = height
+       #
+       # Call a function for all words in a line
+       def visit(self, wordfunc, anchorfunc):
+               avail1 = self.width - self.indent_left - self.indent_right
+               avail = avail1 - self.indent_hang
+               v = self.top
+               i = 0
+               for tuple in self.lines:
+                       wordcount, firstfont, charcount, width, stretch, \
+                               ascent, descent = tuple
+                       h = self.left + self.indent_left
+                       if i == 0: h = h + self.indent_hang
+                       extra = 0
+                       if self.just == 'r': h = h + avail - width
+                       elif self.just == 'c': h = h + (avail - width) / 2
+                       elif self.just == 'lr' and stretch > 0:
+                               extra = avail - width
+                       v2 = v + ascent + descent
+                       for j in range(i, i+wordcount):
+                               word = self.words[j]
+                               if type(word) == Int:
+                                       ok = anchorfunc(self, tuple, word, \
+                                                       h, v)
+                                       if ok <> None: return ok
+                                       continue
+                               fo, te, wi, sp, st, as, de = word
+                               if extra > 0 and stretch > 0:
+                                       ex = extra * st / stretch
+                                       extra = extra - ex
+                                       stretch = stretch - st
+                               else:
+                                       ex = 0
+                               h2 = h + wi + sp + ex
+                               ok = wordfunc(self, tuple, word, h, v, \
+                                       h2, v2, (j==i), (j==i+wordcount-1))
+                               if ok <> None: return ok
+                               h = h2
+                       v = v2
+                       i = i + wordcount
+                       avail = avail1
+       #
+       # Render a paragraph in "drawing object" d, using the rectangle
+       # given by (left, top, right) with an unspecified bottom.
+       # Return the computed bottom of the text.
+       def render(self, d, left, top, right):
+               if self.width <> right-left:
+                       self.layout(right-left)
+               self.left = left
+               self.top = top
+               self.right = right
+               self.bottom = self.top + self.height
+               self.anchorid = 0
+               try:
+                       self.d = d
+                       self.visit(self.__class__._renderword, \
+                                  self.__class__._renderanchor)
+               finally:
+                       self.d = None
+               return self.bottom
+       #
+       def _renderword(self, tuple, word, h, v, h2, v2, isfirst, islast):
+               if word[0] <> None: self.d.setfont(word[0])
+               baseline = v + tuple[5]
+               self.d.text((h, baseline - word[5]), word[1])
+               if self.anchorid > 0:
+                       self.d.line((h, baseline+2), (h2, baseline+2))
+       #
+       def _renderanchor(self, tuple, word, h, v):
+               self.anchorid = word
+       #
+       # Return which anchor(s) was hit by the mouse
+       def hitcheck(self, mouseh, mousev):
+               self.mouseh = mouseh
+               self.mousev = mousev
+               self.anchorid = 0
+               self.hits = []
+               self.visit(self.__class__._hitcheckword, \
+                          self.__class__._hitcheckanchor)
+               return self.hits
+       #
+       def _hitcheckword(self, tuple, word, h, v, h2, v2, isfirst, islast):
+               if self.anchorid > 0 and h <= self.mouseh <= h2 and \
+                       v <= self.mousev <= v2:
+                       self.hits.append(self.anchorid)
+       #
+       def _hitcheckanchor(self, tuple, word, h, v):
+               self.anchorid = word
+       #
+       # Return whether the given anchor id is present
+       def hasanchor(self, id):
+               return id in self.words or -id in self.words
+       #
+       # Extract the raw text from the word list, substituting one space
+       # for non-empty inter-word space, and terminating with '\n'
+       def extract(self):
+               text = ''
+               for w in self.words:
+                       if type(w) <> Int:
+                               word = w[1]
+                               if w[3]: word = word + ' '
+                               text = text + word
+               return text + '\n'
+       #
+       # Return which character position was hit by the mouse, as
+       # an offset in the entire text as returned by extract().
+       # Return None if the mouse was not in this paragraph
+       def whereis(self, d, mouseh, mousev):
+               if mousev < self.top or mousev > self.bottom:
+                       return None
+               self.mouseh = mouseh
+               self.mousev = mousev
+               self.lastfont = None
+               self.charcount = 0
+               try:
+                       self.d = d
+                       return self.visit(self.__class__._whereisword, \
+                                         self.__class__._whereisanchor)
+               finally:
+                       self.d = None
+       #
+       def _whereisword(self, tuple, word, h1, v1, h2, v2, isfirst, islast):
+               fo, te, wi, sp, st, as, de = word
+               if fo <> None: self.lastfont = fo
+               h = h1
+               if isfirst: h1 = 0
+               if islast: h2 = 999999
+               if not (v1 <= self.mousev <= v2 and h1 <= self.mouseh <= h2):
+                       self.charcount = self.charcount + len(te) + (sp > 0)
+                       return
+               if self.lastfont <> None:
+                       self.d.setfont(self.lastfont)
+               cc = 0
+               for c in te:
+                       cw = self.d.textwidth(c)
+                       if self.mouseh <= h + cw/2:
+                               return self.charcount + cc
+                       cc = cc+1
+                       h = h+cw
+               self.charcount = self.charcount + cc
+               if self.mouseh <= (h+h2) / 2:
+                       return self.charcount
+               else:
+                       return self.charcount + 1
+       #
+       def _whereisanchor(self, tuple, word, h, v):
+               pass
+       #
+       # Return screen position corresponding to position in paragraph.
+       # Return tuple (h, vtop, vbaseline, vbottom).
+       # This is more or less the inverse of whereis()
+       def screenpos(self, d, pos):
+               if pos < 0:
+                       ascent, descent = self.lines[0][5:7]
+                       return self.left, self.top, self.top + ascent, \
+                               self.top + ascent + descent
+               self.pos = pos
+               self.lastfont = None
+               try:
+                       self.d = d
+                       ok = self.visit(self.__class__._screenposword, \
+                                       self.__class__._screenposanchor)
+               finally:
+                       self.d = None
+               if ok == None:
+                       ascent, descent = self.lines[-1][5:7]
+                       ok = self.right, self.bottom - ascent - descent, \
+                               self.bottom - descent, self.bottom
+               return ok
+       #
+       def _screenposword(self, tuple, word, h1, v1, h2, v2, isfirst, islast):
+               fo, te, wi, sp, st, as, de = word
+               if fo <> None: self.lastfont = fo
+               cc = len(te) + (sp > 0)
+               if self.pos > cc:
+                       self.pos = self.pos - cc
+                       return
+               if self.pos < cc:
+                       self.d.setfont(self.lastfont)
+                       h = h1 + self.d.textwidth(te[:self.pos])
+               else:
+                       h = h2
+               ascent, descent = tuple[5:7]
+               return h, v1, v1+ascent, v2
+       #
+       def _screenposanchor(self, tuple, word, h, v):
+               pass
+       #
+       # Invert the stretch of text between pos1 and pos2.
+       # If pos1 is None, the beginning is implied;
+       # if pos2 is None, the end is implied.
+       # Undoes its own effect when called again with the same arguments
+       def invert(self, d, pos1, pos2):
+               if pos1 == None:
+                       pos1 = self.left, self.top, self.top, self.top
+               else:
+                       pos1 = self.screenpos(d, pos1)
+               if pos2 == None:
+                       pos2 = self.right, self.bottom,self.bottom,self.bottom
+               else:
+                       pos2 = self.screenpos(d, pos2)
+               h1, top1, baseline1, bottom1 = pos1
+               h2, top2, baseline2, bottom2 = pos2
+               if bottom1 <= top2:
+                       d.invert((h1, top1), (self.right, bottom1))
+                       h1 = self.left
+                       if bottom1 < top2:
+                               d.invert((h1, bottom1), (self.right, top2))
+                       top1, bottom1 = top2, bottom2
+               d.invert((h1, top1), (h2, bottom2))
+
+
+# Test class Para
+# XXX This was last used on the Mac, hence the weird fonts...
+def test():
+       import stdwin
+       from stdwinevents import *
+       words = 'The', 'quick', 'brown', 'fox', 'jumps', 'over', \
+               'the', 'lazy', 'dog.'
+       paralist = []
+       for just in 'l', 'r', 'lr', 'c':
+               p = Para()
+               p.just = just
+               p.addword(stdwin, ('New York', 'p', 12), words[0], 1, 1)
+               for word in words[1:-1]:
+                       p.addword(stdwin, None, word, 1, 1)
+               p.addword(stdwin, None, words[-1], 2, 4)
+               p.addword(stdwin, ('New York', 'b', 18), 'Bye!', 0, 0)
+               p.addword(stdwin, ('New York', 'p', 10), 'Bye!', 0, 0)
+               paralist.append(p)
+       window = stdwin.open('Para.test()')
+       start = stop = selpara = None
+       while 1:
+               etype, win, detail = stdwin.getevent()
+               if etype == WE_CLOSE:
+                       break
+               if etype == WE_SIZE:
+                       window.change((0, 0), (1000, 1000))
+               if etype == WE_DRAW:
+                       width, height = window.getwinsize()
+                       d = None
+                       try:
+                               d = window.begindrawing()
+                               d.cliprect(detail)
+                               d.erase(detail)
+                               v = 0
+                               for p in paralist:
+                                       v = p.render(d, 0, v, width)
+                                       if p == selpara and \
+                                          start <> None and stop <> None:
+                                               p.invert(d, start, stop)
+                       finally:
+                               if d: d.close()
+               if etype == WE_MOUSE_DOWN:
+                       if selpara and start <> None and stop <> None:
+                               d = window.begindrawing()
+                               selpara.invert(d, start, stop)
+                               d.close()
+                       start = stop = selpara = None
+                       mouseh, mousev = detail[0]
+                       for p in paralist:
+                               start = p.whereis(stdwin, mouseh, mousev)
+                               if start <> None:
+                                       selpara = p
+                                       break
+               if etype == WE_MOUSE_UP and start <> None and selpara:
+                       mouseh, mousev = detail[0]
+                       stop = selpara.whereis(stdwin, mouseh, mousev)
+                       if stop == None: start = selpara = None
+                       else:
+                               if start > stop:
+                                       start, stop = stop, start
+                               d = window.begindrawing()
+                               selpara.invert(d, start, stop)
+                               d.close()
+       window.close()
diff --git a/Lib/dos_8x3/posixfil.py b/Lib/dos_8x3/posixfil.py
new file mode 100755 (executable)
index 0000000..459cd52
--- /dev/null
@@ -0,0 +1,207 @@
+#
+# Start of posixfile.py
+#
+
+#
+# Extended file operations
+#
+# f = posixfile.open(filename, [mode, [bufsize]])
+#      will create a new posixfile object
+#
+# f = posixfile.fileopen(fileobject)
+#      will create a posixfile object from a builtin file object
+#
+# f.file()
+#      will return the original builtin file object
+#
+# f.dup()
+#      will return a new file object based on a new filedescriptor
+#
+# f.dup2(fd)
+#      will return a new file object based on the given filedescriptor
+#
+# f.flags(mode)
+#      will turn on the associated flag (merge)
+#      mode can contain the following characters:
+#
+#   (character representing a flag)
+#      a       append only flag
+#      c       close on exec flag
+#      n       no delay flag
+#      s       synchronization flag
+#   (modifiers)
+#      !       turn flags 'off' instead of default 'on'
+#      =       copy flags 'as is' instead of default 'merge'
+#      ?       return a string in which the characters represent the flags
+#              that are set
+#
+#      note: - the '!' and '=' modifiers are mutually exclusive.
+#            - the '?' modifier will return the status of the flags after they
+#              have been changed by other characters in the mode string
+#
+# f.lock(mode [, len [, start [, whence]]])
+#      will (un)lock a region
+#      mode can contain the following characters:
+#
+#   (character representing type of lock)
+#      u       unlock
+#      r       read lock
+#      w       write lock
+#   (modifiers)
+#      |       wait until the lock can be granted
+#      ?       return the first lock conflicting with the requested lock
+#              or 'None' if there is no conflict. The lock returned is in the
+#              format (mode, len, start, whence, pid) where mode is a
+#              character representing the type of lock ('r' or 'w')
+#
+#      note: - the '?' modifier prevents a region from being locked; it is
+#              query only
+#
+
+class _posixfile_:
+    states = ['open', 'closed']
+
+    #
+    # Internal routines
+    #
+    def __repr__(self):
+       file = self._file_
+       return "<%s posixfile '%s', mode '%s' at %s>" % \
+               (self.states[file.closed], file.name, file.mode, \
+                hex(id(self))[2:])
+
+    def __del__(self):
+       self._file_.close()
+
+    #
+    # Initialization routines
+    #
+    def open(self, name, mode='r', bufsize=-1):
+       import __builtin__
+       return self.fileopen(__builtin__.open(name, mode, bufsize))
+
+    def fileopen(self, file):
+       if `type(file)` != "<type 'file'>":
+           raise TypeError, 'posixfile.fileopen() arg must be file object'
+       self._file_  = file
+       # Copy basic file methods
+       for method in file.__methods__:
+           setattr(self, method, getattr(file, method))
+       return self
+
+    #
+    # New methods
+    #
+    def file(self):
+       return self._file_
+
+    def dup(self):
+       import posix
+
+       try: ignore = posix.fdopen
+       except: raise AttributeError, 'dup() method unavailable'
+
+       return posix.fdopen(posix.dup(self._file_.fileno()), self._file_.mode)
+
+    def dup2(self, fd):
+       import posix
+
+       try: ignore = posix.fdopen
+       except: raise AttributeError, 'dup() method unavailable'
+
+       posix.dup2(self._file_.fileno(), fd)
+       return posix.fdopen(fd, self._file_.mode)
+
+    def flags(self, *which):
+       import fcntl, FCNTL
+
+       if which:
+           if len(which) > 1:
+               raise TypeError, 'Too many arguments'
+           which = which[0]
+       else: which = '?'
+
+       l_flags = 0
+       if 'n' in which: l_flags = l_flags | FCNTL.O_NDELAY
+       if 'a' in which: l_flags = l_flags | FCNTL.O_APPEND
+       if 's' in which: l_flags = l_flags | FCNTL.O_SYNC
+
+       file = self._file_
+
+       if '=' not in which:
+           cur_fl = fcntl.fcntl(file.fileno(), FCNTL.F_GETFL, 0)
+           if '!' in which: l_flags = cur_fl & ~ l_flags
+           else: l_flags = cur_fl | l_flags
+
+       l_flags = fcntl.fcntl(file.fileno(), FCNTL.F_SETFL, l_flags)
+
+       if 'c' in which:        
+           arg = ('!' not in which)    # 0 is don't, 1 is do close on exec
+           l_flags = fcntl.fcntl(file.fileno(), FCNTL.F_SETFD, arg)
+
+       if '?' in which:
+           which = ''                  # Return current flags
+           l_flags = fcntl.fcntl(file.fileno(), FCNTL.F_GETFL, 0)
+           if FCNTL.O_APPEND & l_flags: which = which + 'a'
+           if fcntl.fcntl(file.fileno(), FCNTL.F_GETFD, 0) & 1:
+               which = which + 'c'
+           if FCNTL.O_NDELAY & l_flags: which = which + 'n'
+           if FCNTL.O_SYNC & l_flags: which = which + 's'
+           return which
+       
+    def lock(self, how, *args):
+       import struct, fcntl, FCNTL
+
+       if 'w' in how: l_type = FCNTL.F_WRLCK
+       elif 'r' in how: l_type = FCNTL.F_RDLCK
+       elif 'u' in how: l_type = FCNTL.F_UNLCK
+       else: raise TypeError, 'no type of lock specified'
+
+       if '|' in how: cmd = FCNTL.F_SETLKW
+       elif '?' in how: cmd = FCNTL.F_GETLK
+       else: cmd = FCNTL.F_SETLK
+
+       l_whence = 0
+       l_start = 0
+       l_len = 0
+
+       if len(args) == 1:
+           l_len = args[0]
+       elif len(args) == 2:
+           l_len, l_start = args
+       elif len(args) == 3:
+           l_len, l_start, l_whence = args
+       elif len(args) > 3:
+           raise TypeError, 'too many arguments'
+
+       flock = struct.pack('hhllhh', l_type, l_whence, l_start, l_len, 0, 0)
+       flock = fcntl.fcntl(self._file_.fileno(), cmd, flock)
+
+       if '?' in how:
+           l_type, l_whence, l_start, l_len, l_sysid, l_pid = \
+               struct.unpack('hhllhh', flock)
+           if l_type != FCNTL.F_UNLCK:
+               if l_type == FCNTL.F_RDLCK:
+                   return 'r', l_len, l_start, l_whence, l_pid
+               else:
+                   return 'w', l_len, l_start, l_whence, l_pid
+
+#
+# Public routine to obtain a posixfile object
+#
+def open(name, mode='r', bufsize=-1):
+    return _posixfile_().open(name, mode, bufsize)
+
+def fileopen(file):
+    return _posixfile_().fileopen(file)
+
+#
+# Constants
+#
+SEEK_SET = 0
+SEEK_CUR = 1
+SEEK_END = 2
+
+#
+# End of posixfile.py
+#
diff --git a/Lib/dos_8x3/posixpat.py b/Lib/dos_8x3/posixpat.py
new file mode 100755 (executable)
index 0000000..d284834
--- /dev/null
@@ -0,0 +1,307 @@
+# Module 'posixpath' -- common operations on POSIX pathnames
+
+import posix
+import stat
+
+
+# Normalize the case of a pathname.  Trivial in Posix, string.lower on Mac.
+# On MS-DOS this may also turn slashes into backslashes; however, other
+# normalizations (such as optimizing '../' away) are not allowed
+# (another function should be defined to do that).
+
+def normcase(s):
+       return s
+
+
+# Return wheter a path is absolute.
+# Trivial in Posix, harder on the Mac or MS-DOS.
+
+def isabs(s):
+       return s[:1] == '/'
+
+
+# Join two pathnames.
+# Ignore the first part if the second part is absolute.
+# Insert a '/' unless the first part is empty or already ends in '/'.
+
+def join(a, b):
+       if b[:1] == '/': return b
+       if a == '' or a[-1:] == '/': return a + b
+       # Note: join('x', '') returns 'x/'; is this what we want?
+       return a + '/' + b
+
+
+# Split a path in head (everything up to the last '/') and tail (the
+# rest).  If the path ends in '/', tail will be empty.  If there is no
+# '/' in the path, head  will be empty.
+# Trailing '/'es are stripped from head unless it is the root.
+
+def split(p):
+       import string
+       i = string.rfind(p, '/') + 1
+       head, tail = p[:i], p[i:]
+       if head and head <> '/'*len(head):
+               while head[-1] == '/':
+                       head = head[:-1]
+       return head, tail
+
+
+# Split a path in root and extension.
+# The extension is everything starting at the first dot in the last
+# pathname component; the root is everything before that.
+# It is always true that root + ext == p.
+
+def splitext(p):
+       root, ext = '', ''
+       for c in p:
+               if c == '/':
+                       root, ext = root + ext + c, ''
+               elif c == '.':
+                       if ext:
+                               root, ext = root + ext, c
+                       else:
+                               ext = c
+               elif ext:
+                       ext = ext + c
+               else:
+                       root = root + c
+       return root, ext
+
+
+# Split a pathname into a drive specification and the rest of the
+# path.  Useful on DOS/Windows/NT; on Unix, the drive is always empty.
+
+def splitdrive(p):
+       return '', p
+
+
+# Return the tail (basename) part of a path.
+
+def basename(p):
+       return split(p)[1]
+
+
+# Return the head (dirname) part of a path.
+
+def dirname(p):
+       return split(p)[0]
+
+
+# Return the longest prefix of all list elements.
+
+def commonprefix(m):
+       if not m: return ''
+       prefix = m[0]
+       for item in m:
+               for i in range(len(prefix)):
+                       if prefix[:i+1] <> item[:i+1]:
+                               prefix = prefix[:i]
+                               if i == 0: return ''
+                               break
+       return prefix
+
+
+# Is a path a symbolic link?
+# This will always return false on systems where posix.lstat doesn't exist.
+
+def islink(path):
+       try:
+               st = posix.lstat(path)
+       except (posix.error, AttributeError):
+               return 0
+       return stat.S_ISLNK(st[stat.ST_MODE])
+
+
+# Does a path exist?
+# This is false for dangling symbolic links.
+
+def exists(path):
+       try:
+               st = posix.stat(path)
+       except posix.error:
+               return 0
+       return 1
+
+
+# Is a path a posix directory?
+# This follows symbolic links, so both islink() and isdir() can be true
+# for the same path.
+
+def isdir(path):
+       try:
+               st = posix.stat(path)
+       except posix.error:
+               return 0
+       return stat.S_ISDIR(st[stat.ST_MODE])
+
+
+# Is a path a regular file?
+# This follows symbolic links, so both islink() and isfile() can be true
+# for the same path.
+
+def isfile(path):
+       try:
+               st = posix.stat(path)
+       except posix.error:
+               return 0
+       return stat.S_ISREG(st[stat.ST_MODE])
+
+
+# Are two filenames really pointing to the same file?
+
+def samefile(f1, f2):
+       s1 = posix.stat(f1)
+       s2 = posix.stat(f2)
+       return samestat(s1, s2)
+
+
+# Are two open files really referencing the same file?
+# (Not necessarily the same file descriptor!)
+# XXX Oops, posix.fstat() doesn't exist yet!
+
+def sameopenfile(fp1, fp2):
+       s1 = posix.fstat(fp1)
+       s2 = posix.fstat(fp2)
+       return samestat(s1, s2)
+
+
+# Are two stat buffers (obtained from stat, fstat or lstat)
+# describing the same file?
+
+def samestat(s1, s2):
+       return s1[stat.ST_INO] == s2[stat.ST_INO] and \
+               s1[stat.ST_DEV] == s2[stat.ST_DEV]
+
+
+# Is a path a mount point?
+# (Does this work for all UNIXes?  Is it even guaranteed to work by POSIX?)
+
+def ismount(path):
+       try:
+               s1 = posix.stat(path)
+               s2 = posix.stat(join(path, '..'))
+       except posix.error:
+               return 0 # It doesn't exist -- so not a mount point :-)
+       dev1 = s1[stat.ST_DEV]
+       dev2 = s2[stat.ST_DEV]
+       if dev1 != dev2:
+               return 1                # path/.. on a different device as path
+       ino1 = s1[stat.ST_INO]
+       ino2 = s2[stat.ST_INO]
+       if ino1 == ino2:
+               return 1                # path/.. is the same i-node as path
+       return 0
+
+
+# Directory tree walk.
+# For each directory under top (including top itself, but excluding
+# '.' and '..'), func(arg, dirname, filenames) is called, where
+# dirname is the name of the directory and filenames is the list
+# files files (and subdirectories etc.) in the directory.
+# The func may modify the filenames list, to implement a filter,
+# or to impose a different order of visiting.
+
+def walk(top, func, arg):
+       try:
+               names = posix.listdir(top)
+       except posix.error:
+               return
+       func(arg, top, names)
+       exceptions = ('.', '..')
+       for name in names:
+               if name not in exceptions:
+                       name = join(top, name)
+                       if isdir(name) and not islink(name):
+                               walk(name, func, arg)
+
+
+# Expand paths beginning with '~' or '~user'.
+# '~' means $HOME; '~user' means that user's home directory.
+# If the path doesn't begin with '~', or if the user or $HOME is unknown,
+# the path is returned unchanged (leaving error reporting to whatever
+# function is called with the expanded path as argument).
+# See also module 'glob' for expansion of *, ? and [...] in pathnames.
+# (A function should also be defined to do full *sh-style environment
+# variable expansion.)
+
+def expanduser(path):
+       if path[:1] <> '~':
+               return path
+       i, n = 1, len(path)
+       while i < n and path[i] <> '/':
+               i = i+1
+       if i == 1:
+               if not posix.environ.has_key('HOME'):
+                       return path
+               userhome = posix.environ['HOME']
+       else:
+               import pwd
+               try:
+                       pwent = pwd.getpwnam(path[1:i])
+               except KeyError:
+                       return path
+               userhome = pwent[5]
+       if userhome[-1:] == '/': i = i+1
+       return userhome + path[i:]
+
+
+# Expand paths containing shell variable substitutions.
+# This expands the forms $variable and ${variable} only.
+# Non-existant variables are left unchanged.
+
+_varprog = None
+
+def expandvars(path):
+       global _varprog
+       if '$' not in path:
+               return path
+       if not _varprog:
+               import regex
+               _varprog = regex.compile('$\([a-zA-Z0-9_]+\|{[^}]*}\)')
+       i = 0
+       while 1:
+               i = _varprog.search(path, i)
+               if i < 0:
+                       break
+               name = _varprog.group(1)
+               j = i + len(_varprog.group(0))
+               if name[:1] == '{' and name[-1:] == '}':
+                       name = name[1:-1]
+               if posix.environ.has_key(name):
+                       tail = path[j:]
+                       path = path[:i] + posix.environ[name]
+                       i = len(path)
+                       path = path + tail
+               else:
+                       i = j
+       return path
+
+
+# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
+# It should be understood that this may change the meaning of the path
+# if it contains symbolic links!
+
+def normpath(path):
+       import string
+       # Treat initial slashes specially
+       slashes = ''
+       while path[:1] == '/':
+               slashes = slashes + '/'
+               path = path[1:]
+       comps = string.splitfields(path, '/')
+       i = 0
+       while i < len(comps):
+               if comps[i] == '.':
+                       del comps[i]
+               elif comps[i] == '..' and i > 0 and \
+                                         comps[i-1] not in ('', '..'):
+                       del comps[i-1:i+1]
+                       i = i-1
+               elif comps[i] == '' and i > 0 and comps[i-1] <> '':
+                       del comps[i]
+               else:
+                       i = i+1
+       # If the path is now empty, substitute '.'
+       if not comps and not slashes:
+               comps.append('.')
+       return slashes + string.joinfields(comps, '/')
diff --git a/Lib/dos_8x3/py_compi.py b/Lib/dos_8x3/py_compi.py
new file mode 100755 (executable)
index 0000000..2e68ba8
--- /dev/null
@@ -0,0 +1,31 @@
+# Routine to "compile" a .py file to a .pyc file.
+# This has intimate knowledge of how Python/import.c does it.
+# By Sjoerd Mullender (I forced him to write it :-).
+
+import imp
+MAGIC = imp.get_magic()
+
+def wr_long(f, x):
+       f.write(chr( x        & 0xff))
+       f.write(chr((x >> 8)  & 0xff))
+       f.write(chr((x >> 16) & 0xff))
+       f.write(chr((x >> 24) & 0xff))
+
+def compile(file, cfile = None):
+       import os, marshal, __builtin__
+       f = open(file)
+       codestring = f.read()
+       f.close()
+       timestamp = os.stat(file)[8]
+       codeobject = __builtin__.compile(codestring, file, 'exec')
+       if not cfile:
+               cfile = file + 'c'
+       fc = open(cfile, 'wb')
+       fc.write(MAGIC)
+       wr_long(fc, timestamp)
+       marshal.dump(codeobject, fc)
+       fc.close()
+       if os.name == 'mac':
+               import macfs
+               macfs.FSSpec(cfile).SetCreatorType('Pyth', 'PYC ')
+               macfs.FSSpec(file).SetCreatorType('Pyth', 'TEXT')
diff --git a/Lib/dos_8x3/queue.py b/Lib/dos_8x3/queue.py
new file mode 100755 (executable)
index 0000000..5125fd5
--- /dev/null
@@ -0,0 +1,121 @@
+# A multi-producer, multi-consumer queue.
+
+Empty = 'Queue.Empty' # Exception raised by get_nowait()
+
+class Queue:
+
+       # Initialize a queue object with a given maximum size
+       # (If maxsize is <= 0, the maximum size is infinite)
+       def __init__(self, maxsize):
+               import thread
+               self._init(maxsize)
+               self.mutex = thread.allocate_lock()
+               self.esema = thread.allocate_lock()
+               self.esema.acquire_lock()
+               self.fsema = thread.allocate_lock()
+
+       # Get an approximation of the queue size (not reliable!)
+       def qsize(self):
+               self.mutex.acquire_lock()
+               n = self._qsize()
+               self.mutex.release_lock()
+               return n
+
+       # Check if the queue is empty (not reliable!)
+       def empty(self):
+               self.mutex.acquire_lock()
+               n = self._empty()
+               self.mutex.release_lock()
+               return n
+
+       # Check if the queue is full (not reliable!)
+       def full(self):
+               self.mutex.acquire_lock()
+               n = self._full()
+               self.mutex.release_lock()
+               return n
+
+       # Put a new item into the queue
+       def put(self, item):
+               self.fsema.acquire_lock()
+               self.mutex.acquire_lock()
+               was_empty = self._empty()
+               self._put(item)
+               if was_empty:
+                       self.esema.release_lock()
+               if not self._full():
+                       self.fsema.release_lock()
+               self.mutex.release_lock()
+
+       # Get an item from the queue,
+       # blocking if necessary until one is available
+       def get(self):
+               self.esema.acquire_lock()
+               self.mutex.acquire_lock()
+               was_full = self._full()
+               item = self._get()
+               if was_full:
+                       self.fsema.release_lock()
+               if not self._empty():
+                       self.esema.release_lock()
+               self.mutex.release_lock()
+               return item
+
+       # Get an item from the queue if one is immediately available,
+       # raise Empty if the queue is empty or temporarily unavailable
+       def get_nowait(self):
+               locked = self.esema.acquire_lock(0)
+               self.mutex.acquire_lock()
+               if self._empty():
+                       # The queue is empyt -- we can't have esema
+                       self.mutex.release_lock()
+                       raise Empty
+               if not locked:
+                       locked = self.esema.acquire_lock(0)
+                       if not locked:
+                               # Somebody else has esema
+                               # but we have mutex --
+                               # go out of their way
+                               self.mutex.release_lock()
+                               raise Empty
+               was_full = self._full()
+               item = self._get()
+               if was_full:
+                       self.fsema.release_lock()
+               if not self._empty():
+                       self.esema.release_lock()
+               self.mutex.release_lock()
+               return item
+
+       # XXX Need to define put_nowait() as well.
+               
+
+       # Override these methods to implement other queue organizations
+       # (e.g. stack or priority queue).
+       # These will only be called with appropriate locks held
+
+       # Initialize the queue representation
+       def _init(self, maxsize):
+               self.maxsize = maxsize
+               self.queue = []
+
+       def _qsize(self):
+               return len(self.queue)
+
+       # Check wheter the queue is empty
+       def _empty(self):
+               return not self.queue
+
+       # Check whether the queue is full
+       def _full(self):
+               return self.maxsize > 0 and len(self.queue) == self.maxsize
+
+       # Put a new item in the queue
+       def _put(self, item):
+               self.queue.append(item)
+
+       # Get an item from the queue
+       def _get(self):
+               item = self.queue[0]
+               del self.queue[0]
+               return item
diff --git a/Lib/dos_8x3/regex_sy.py b/Lib/dos_8x3/regex_sy.py
new file mode 100755 (executable)
index 0000000..bb80f4e
--- /dev/null
@@ -0,0 +1,41 @@
+# These bits are passed to regex.set_syntax() to choose among
+# alternative regexp syntaxes.
+
+# 1 means plain parentheses serve as grouping, and backslash
+#   parentheses are needed for literal searching.
+# 0 means backslash-parentheses are grouping, and plain parentheses
+#   are for literal searching.
+RE_NO_BK_PARENS = 1
+
+# 1 means plain | serves as the "or"-operator, and \| is a literal.
+# 0 means \| serves as the "or"-operator, and | is a literal.
+RE_NO_BK_VBAR = 2
+
+# 0 means plain + or ? serves as an operator, and \+, \? are literals.
+# 1 means \+, \? are operators and plain +, ? are literals.
+RE_BK_PLUS_QM = 4
+
+# 1 means | binds tighter than ^ or $.
+# 0 means the contrary.
+RE_TIGHT_VBAR = 8
+
+# 1 means treat \n as an _OR operator
+# 0 means treat it as a normal character
+RE_NEWLINE_OR = 16
+
+# 0 means that a special characters (such as *, ^, and $) always have
+#   their special meaning regardless of the surrounding context.
+# 1 means that special characters may act as normal characters in some
+#   contexts.  Specifically, this applies to:
+#      ^ - only special at the beginning, or after ( or |
+#      $ - only special at the end, or before ) or |
+#      *, +, ? - only special when not after the beginning, (, or |
+RE_CONTEXT_INDEP_OPS = 32
+
+# Now define combinations of bits for the standard possibilities.
+RE_SYNTAX_AWK = (RE_NO_BK_PARENS | RE_NO_BK_VBAR | RE_CONTEXT_INDEP_OPS)
+RE_SYNTAX_EGREP = (RE_SYNTAX_AWK | RE_NEWLINE_OR)
+RE_SYNTAX_GREP = (RE_BK_PLUS_QM | RE_NEWLINE_OR)
+RE_SYNTAX_EMACS = 0
+
+# (Python's obsolete "regexp" module used a syntax similar to awk.)
diff --git a/Lib/dos_8x3/simpleht.py b/Lib/dos_8x3/simpleht.py
new file mode 100755 (executable)
index 0000000..dd3107a
--- /dev/null
@@ -0,0 +1,168 @@
+"""Simple HTTP Server.
+
+This module builds on BaseHTTPServer by implementing the standard GET
+and HEAD requests in a fairly straightforward manner.
+
+"""
+
+
+__version__ = "0.3"
+
+
+import os
+import pwd
+import sys
+import time
+import socket
+import string
+import posixpath
+import SocketServer
+import BaseHTTPServer
+
+
+def nobody_uid():
+    """Internal routine to get nobody's uid"""
+    try:
+       nobody = pwd.getpwnam('nobody')[2]
+    except pwd.error:
+       nobody = 1 + max(map(lambda x: x[2], pwd.getpwall()))
+    return nobody
+
+nobody = nobody_uid()
+
+
+class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+
+    """Simple HTTP request handler with GET and HEAD commands.
+
+    This serves files from the current directory and any of its
+    subdirectories.  It assumes that all files are plain text files
+    unless they have the extension ".html" in which case it assumes
+    they are HTML files.
+
+    The GET and HEAD requests are identical except that the HEAD
+    request omits the actual contents of the file.
+
+    """
+
+    server_version = "SimpleHTTP/" + __version__
+
+    def do_GET(self):
+       """Serve a GET request."""
+       f = self.send_head()
+       if f:
+           self.copyfile(f, self.wfile)
+           f.close()
+
+    def do_HEAD(self):
+       """Serve a HEAD request."""
+       f = self.send_head()
+       if f:
+           f.close()
+
+    def send_head(self):
+       """Common code for GET and HEAD commands.
+
+       This sends the response code and MIME headers.
+
+       Return value is either a file object (which has to be copied
+       to the outputfile by the caller unless the command was HEAD,
+       and must be closed by the caller under all circumstances), or
+       None, in which case the caller has nothing further to do.
+
+       """
+       path = self.translate_path(self.path)
+       if os.path.isdir(path):
+           self.send_error(403, "Directory listing not supported")
+           return None
+       try:
+           f = open(path)
+       except IOError:
+           self.send_error(404, "File not found")
+           return None
+       self.send_response(200)
+       self.send_header("Content-type", self.guess_type(path))
+       self.end_headers()
+       return f
+
+    def translate_path(self, path):
+       """Translate a /-separated PATH to the local filename syntax.
+
+       Components that mean special things to the local file system
+       (e.g. drive or directory names) are ignored.  (XXX They should
+       probably be diagnosed.)
+
+       """
+       path = posixpath.normpath(path)
+       words = string.splitfields(path, '/')
+       words = filter(None, words)
+       path = os.getcwd()
+       for word in words:
+           drive, word = os.path.splitdrive(word)
+           head, word = os.path.split(word)
+           if word in (os.curdir, os.pardir): continue
+           path = os.path.join(path, word)
+       return path
+
+    def copyfile(self, source, outputfile):
+       """Copy all data between two file objects.
+
+       The SOURCE argument is a file object open for reading
+       (or anything with a read() method) and the DESTINATION
+       argument is a file object open for writing (or
+       anything with a write() method).
+
+       The only reason for overriding this would be to change
+       the block size or perhaps to replace newlines by CRLF
+       -- note however that this the default server uses this
+       to copy binary data as well.
+
+       """
+
+       BLOCKSIZE = 8192
+       while 1:
+           data = source.read(BLOCKSIZE)
+           if not data: break
+           outputfile.write(data)
+
+    def guess_type(self, path):
+       """Guess the type of a file.
+
+       Argument is a PATH (a filename).
+
+       Return value is a string of the form type/subtype,
+       usable for a MIME Content-type header.
+
+       The default implementation looks the file's extension
+       up in the table self.extensions_map, using text/plain
+       as a default; however it would be permissible (if
+       slow) to look inside the data to make a better guess.
+
+       """
+
+       base, ext = posixpath.splitext(path)
+       if self.extensions_map.has_key(ext):
+           return self.extensions_map[ext]
+       ext = string.lower(ext)
+       if self.extensions_map.has_key(ext):
+           return self.extensions_map[ext]
+       else:
+           return self.extensions_map['']
+
+    extensions_map = {
+           '': 'text/plain',   # Default, *must* be present
+           '.html': 'text/html',
+           '.htm': 'text/html',
+           '.gif': 'image/gif',
+           '.jpg': 'image/jpeg',
+           '.jpeg': 'image/jpeg',
+           }
+
+
+def test(HandlerClass = SimpleHTTPRequestHandler,
+        ServerClass = SocketServer.TCPServer):
+    BaseHTTPServer.test(HandlerClass, ServerClass)
+
+
+if __name__ == '__main__':
+    test()
diff --git a/Lib/dos_8x3/socketse.py b/Lib/dos_8x3/socketse.py
new file mode 100755 (executable)
index 0000000..9a646ab
--- /dev/null
@@ -0,0 +1,413 @@
+"""Generic socket server classes.
+
+This module tries to capture the various aspects of defining a server:
+
+- address family:
+       - AF_INET: IP (Internet Protocol) sockets (default)
+       - AF_UNIX: Unix domain sockets
+       - others, e.g. AF_DECNET are conceivable (see <socket.h>
+- socket type:
+       - SOCK_STREAM (reliable stream, e.g. TCP)
+       - SOCK_DGRAM (datagrams, e.g. UDP)
+- client address verification before further looking at the request
+       (This is actually a hook for any processing that needs to look
+        at the request before anything else, e.g. logging)
+- how to handle multiple requests:
+       - synchronous (one request is handled at a time)
+       - forking (each request is handled by a new process)
+       - threading (each request is handled by a new thread)
+
+The classes in this module favor the server type that is simplest to
+write: a synchronous TCP/IP server.  This is bad class design, but
+save some typing.  (There's also the issue that a deep class hierarchy
+slows down method lookups.)
+
+There are four classes in an inheritance diagram that represent
+synchronous servers of four types:
+
+       +-----------+        +------------------+
+       | TCPServer |------->| UnixStreamServer |
+       +-----------+        +------------------+
+             |
+             v
+       +-----------+        +--------------------+
+       | UDPServer |------->| UnixDatagramServer |
+       +-----------+        +--------------------+
+
+(Note that UnixDatagramServer derives from UDPServer, not from
+UnixStreamServer -- the only difference between an IP and a Unix
+stream server is the address family, which is simply repeated in both
+unix server classes.)
+
+Forking and threading versions of each type of server can be created
+using the ForkingServer and ThreadingServer mix-in classes.  For
+instance, a threading UDP server class is created as follows:
+
+       class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
+
+(The Mix-in class must come first, since it overrides a method defined
+in UDPServer!)
+
+To implement a service, you must derive a class from
+BaseRequestHandler and redefine its handle() method.  You can then run
+various versions of the service by combining one of the server classes
+with your request handler class.
+
+The request handler class must be different for datagram or stream
+services.  This can be hidden by using the mix-in request handler
+classes StreamRequestHandler or DatagramRequestHandler.
+
+Of course, you still have to use your head!
+
+For instance, it makes no sense to use a forking server if the service
+contains state in memory that can be modified by requests (since the
+modifications in the child process would never reach the initial state
+kept in the parent process and passed to each child).  In this case,
+you can use a threading server, but you will probably have to use
+locks to avoid two requests that come in nearly simultaneous to apply
+conflicting changes to the server state.
+
+On the other hand, if you are building e.g. an HTTP server, where all
+data is stored externally (e.g. in the file system), a synchronous
+class will essentially render the service "deaf" while one request is
+being handled -- which may be for a very long time if a client is slow
+to reqd all the data it has requested.  Here a threading or forking
+server is appropriate.
+
+In some cases, it may be appropriate to process part of a request
+synchronously, but to finish processing in a forked child depending on
+the request data.  This can be implemented by using a synchronous
+server and doing an explicit fork in the request handler class's
+handle() method.
+
+Another approach to handling multiple simultaneous requests in an
+environment that supports neither threads nor fork (or where these are
+too expensive or inappropriate for the service) is to maintain an
+explicit table of partially finished requests and to use select() to
+decide which request to work on next (or whether to handle a new
+incoming request).  This is particularly important for stream services
+where each client can potentially be connected for a long time (if
+threads or subprocesses can't be used).
+
+Future work:
+- Standard classes for Sun RPC (which uses either UDP or TCP)
+- Standard mix-in classes to implement various authentication
+  and encryption schemes
+- Standard framework for select-based multiplexing
+
+XXX Open problems:
+- What to do with out-of-band data?
+
+"""
+
+
+__version__ = "0.2"
+
+
+import socket
+import sys
+import os
+
+
+class TCPServer:
+
+    """Base class for various socket-based server classes.
+
+    Defaults to synchronous IP stream (i.e., TCP).
+
+    Methods for the caller:
+
+    - __init__(server_address, RequestHandlerClass)
+    - serve_forever()
+    - handle_request() # if you don't use serve_forever()
+    - fileno() -> int  # for select()
+
+    Methods that may be overridden:
+
+    - server_bind()
+    - server_activate()
+    - get_request() -> request, client_address
+    - verify_request(request, client_address)
+    - process_request(request, client_address)
+    - handle_error()
+
+    Methods for derived classes:
+
+    - finish_request(request, client_address)
+
+    Class variables that may be overridden by derived classes or
+    instances:
+
+    - address_family
+    - socket_type
+    - request_queue_size (only for stream sockets)
+
+    Instance variables:
+
+    - server_address
+    - RequestHandlerClass
+    - socket
+
+    """
+
+    address_family = socket.AF_INET
+
+    socket_type = socket.SOCK_STREAM
+
+    request_queue_size = 5
+
+    def __init__(self, server_address, RequestHandlerClass):
+       """Constructor.  May be extended, do not override."""
+       self.server_address = server_address
+       self.RequestHandlerClass = RequestHandlerClass
+       self.socket = socket.socket(self.address_family,
+                                   self.socket_type)
+       self.server_bind()
+       self.server_activate()
+
+    def server_bind(self):
+       """Called by constructor to bind the socket.
+
+       May be overridden.
+
+       """
+       self.socket.bind(self.server_address)
+
+    def server_activate(self):
+       """Called by constructor to activate the server.
+
+       May be overridden.
+
+       """
+       self.socket.listen(self.request_queue_size)
+
+    def fileno(self):
+       """Return socket file number.
+
+       Interface required by select().
+
+       """
+       return self.socket.fileno()
+
+    def serve_forever(self):
+       """Handle one request at a time until doomsday."""
+       while 1:
+           self.handle_request()
+
+    # The distinction between handling, getting, processing and
+    # finishing a request is fairly arbitrary.  Remember:
+    #
+    # - handle_request() is the top-level call.  It calls
+    #   get_request(), verify_request() and process_request()
+    # - get_request() is different for stream or datagram sockets
+    # - process_request() is the place that may fork a new process
+    #   or create a new thread to finish the request
+    # - finish_request() instantiates the request handler class;
+    #   this constructor will handle the request all by itself
+
+    def handle_request(self):
+       """Handle one request, possibly blocking."""
+       request, client_address = self.get_request()
+       if self.verify_request(request, client_address):
+           try:
+               self.process_request(request, client_address)
+           except:
+               self.handle_error(request, client_address)
+
+    def get_request(self):
+       """Get the request and client address from the socket.
+
+       May be overridden.
+
+       """
+       return self.socket.accept()
+
+    def verify_request(self, request, client_address):
+       """Verify the request.  May be overridden.
+
+       Return true if we should proceed with this request.
+
+       """
+       return 1
+
+    def process_request(self, request, client_address):
+       """Call finish_request.
+
+       Overridden by ForkingMixIn and ThreadingMixIn.
+
+       """
+       self.finish_request(request, client_address)
+
+    def finish_request(self, request, client_address):
+       """Finish one request by instantiating RequestHandlerClass."""
+       self.RequestHandlerClass(request, client_address, self)
+
+    def handle_error(self, request, client_address):
+       """Handle an error gracefully.  May be overridden.
+
+       The default is to print a traceback and continue.
+
+       """
+       exc, value, tb = sys.exc_type, sys.exc_value, sys.exc_traceback
+       print '-'*40
+       print 'Exception happened during processing of request from',
+       print client_address
+       import traceback
+       traceback.print_exception(exc, value, tb)
+       print '-'*40
+
+
+class UDPServer(TCPServer):
+
+    """UDP server class."""
+
+    socket_type = socket.SOCK_DGRAM
+
+    max_packet_size = 8192
+
+    def get_request(self):
+       return self.socket.recvfrom(max_packet_size)
+
+
+if hasattr(socket, 'AF_UNIX'):
+
+    class UnixStreamServer(TCPServer):
+
+       address_family = socket.AF_UNIX
+
+
+    class UnixDatagramServer(UDPServer):
+
+       address_family = socket.AF_UNIX
+
+
+class ForkingMixIn:
+
+    """Mix-in class to handle each request in a new process."""
+
+    active_children = None
+
+    def collect_children(self):
+       """Internal routine to wait for died children."""
+       while self.active_children:
+           pid, status = os.waitpid(0, os.WNOHANG)
+           if not pid: break
+           self.active_children.remove(pid)
+
+    def process_request(self, request, client_address):
+       """Fork a new subprocess to process the request."""
+       self.collect_children()
+       pid = os.fork()
+       if pid:
+           # Parent process
+           if self.active_children is None:
+               self.active_children = []
+           self.active_children.append(pid)
+           return
+       else:
+           # Child process.
+           # This must never return, hence os._exit()!
+           try:
+               self.finish_request(request, client_address)
+               os._exit(0)
+           except:
+               try:
+                   self.handle_error(request,
+                                     client_address)
+               finally:
+                   os._exit(1)
+
+
+class ThreadingMixIn:
+
+    """Mix-in class to handle each request in a new thread."""
+
+    def process_request(self, request, client_address):
+       """Start a new thread to process the request."""
+       import thread
+       thread.start_new_thread(self.finish_request,
+                               (request, client_address))
+
+
+class ForkingUDPServer(ForkingMixIn, UDPServer): pass
+class ForkingTCPServer(ForkingMixIn, TCPServer): pass
+
+class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
+class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
+
+
+class BaseRequestHandler:
+
+    """Base class for request handler classes.
+
+    This class is instantiated for each request to be handled.  The
+    constructor sets the instance variables request, client_address
+    and server, and then calls the handle() method.  To implement a
+    specific service, all you need to do is to derive a class which
+    defines a handle() method.
+
+    The handle() method can find the request as self.request, the
+    client address as self.client_request, and the server (in case it
+    needs access to per-server information) as self.server.  Since a
+    separate instance is created for each request, the handle() method
+    can define arbitrary other instance variariables.
+
+    """
+
+    def __init__(self, request, client_address, server):
+       self.request = request
+       self.client_address = client_address
+       self.server = server
+       try:
+           self.setup()
+           self.handle()
+           self.finish()
+       finally:
+           sys.exc_traceback = None    # Help garbage collection
+
+    def setup(self):
+       pass
+
+    def __del__(self):
+       pass
+
+    def handle(self):
+       pass
+
+    def finish(self):
+       pass
+
+
+# The following two classes make it possible to use the same service
+# class for stream or datagram servers.
+# Each class sets up these instance variables:
+# - rfile: a file object from which receives the request is read
+# - wfile: a file object to which the reply is written
+# When the handle() method returns, wfile is flushed properly
+
+
+class StreamRequestHandler(BaseRequestHandler):
+
+    """Define self.rfile and self.wfile for stream sockets."""
+
+    def setup(self):
+       self.connection = self.request
+       self.rfile = self.connection.makefile('rb')
+       self.wfile = self.connection.makefile('wb', 0)
+
+    def finish(self):
+       self.wfile.flush()
+
+
+class DatagramRequestHandler(BaseRequestHandler):
+
+    """Define self.rfile and self.wfile for datagram sockets."""
+
+    def setup(self):
+       import StringIO
+       self.packet, self.socket = self.request
+       self.rfile = StringIO.StringIO(self.packet)
+       self.wfile = StringIO.StringIO(self.packet)
+
+    def finish(self):
+       self.socket.send(self.wfile.getvalue())
diff --git a/Lib/dos_8x3/statcach.py b/Lib/dos_8x3/statcach.py
new file mode 100755 (executable)
index 0000000..770aef0
--- /dev/null
@@ -0,0 +1,82 @@
+# Module 'statcache'
+#
+# Maintain a cache of file stats.
+# There are functions to reset the cache or to selectively remove items.
+
+import os
+from stat import *
+
+# The cache.
+# Keys are pathnames, values are `os.stat' outcomes.
+#
+cache = {}
+
+
+# Stat a file, possibly out of the cache.
+#
+def stat(path):
+       if cache.has_key(path):
+               return cache[path]
+       cache[path] = ret = os.stat(path)
+       return ret
+
+
+# Reset the cache completely.
+#
+def reset():
+       global cache
+       cache = {}
+
+
+# Remove a given item from the cache, if it exists.
+#
+def forget(path):
+       if cache.has_key(path):
+               del cache[path]
+
+
+# Remove all pathnames with a given prefix.
+#
+def forget_prefix(prefix):
+       n = len(prefix)
+       for path in cache.keys():
+               if path[:n] == prefix:
+                       del cache[path]
+
+
+# Forget about a directory and all entries in it, but not about
+# entries in subdirectories.
+#
+def forget_dir(prefix):
+       if prefix[-1:] == '/' and prefix <> '/':
+               prefix = prefix[:-1]
+       forget(prefix)
+       if prefix[-1:] <> '/':
+               prefix = prefix + '/'
+       n = len(prefix)
+       for path in cache.keys():
+               if path[:n] == prefix:
+                       rest = path[n:]
+                       if rest[-1:] == '/': rest = rest[:-1]
+                       if '/' not in rest:
+                               del cache[path]
+
+
+# Remove all pathnames except with a given prefix.
+# Normally used with prefix = '/' after a chdir().
+#
+def forget_except_prefix(prefix):
+       n = len(prefix)
+       for path in cache.keys():
+               if path[:n] <> prefix:
+                       del cache[path]
+
+
+# Check for directory.
+#
+def isdir(path):
+       try:
+               st = stat(path)
+       except os.error:
+               return 0
+       return S_ISDIR(st[ST_MODE])
diff --git a/Lib/dos_8x3/stringio.py b/Lib/dos_8x3/stringio.py
new file mode 100755 (executable)
index 0000000..bbd9428
--- /dev/null
@@ -0,0 +1,156 @@
+# class StringIO implements  file-like objects that read/write a
+# string buffer (a.k.a. "memory files").
+#
+# This implements (nearly) all stdio methods.
+#
+# f = StringIO()      # ready for writing
+# f = StringIO(buf)   # ready for reading
+# f.close()           # explicitly release resources held
+# flag = f.isatty()   # always false
+# pos = f.tell()      # get current position
+# f.seek(pos)         # set current position
+# f.seek(pos, mode)   # mode 0: absolute; 1: relative; 2: relative to EOF
+# buf = f.read()      # read until EOF
+# buf = f.read(n)     # read up to n bytes
+# buf = f.readline()  # read until end of line ('\n') or EOF
+# list = f.readlines()# list of f.readline() results until EOF
+# f.write(buf)        # write at current position
+# f.writelines(list)  # for line in list: f.write(line)
+# f.getvalue()        # return whole file's contents as a string
+#
+# Notes:
+# - Using a real file is often faster (but less convenient).
+# - fileno() is left unimplemented so that code which uses it triggers
+#   an exception early.
+# - Seeking far beyond EOF and then writing will insert real null
+#   bytes that occupy space in the buffer.
+# - There's a simple test set (see end of this file).
+
+import string
+
+class StringIO:
+       def __init__(self, buf = ''):
+               self.buf = buf
+               self.len = len(buf)
+               self.buflist = []
+               self.pos = 0
+               self.closed = 0
+               self.softspace = 0
+       def close(self):
+               if not self.closed:
+                       self.closed = 1
+                       del self.buf, self.pos
+       def isatty(self):
+               return 0
+       def seek(self, pos, mode = 0):
+               if self.buflist:
+                       self.buf = self.buf + string.joinfields(self.buflist, '')
+                       self.buflist = []
+               if mode == 1:
+                       pos = pos + self.pos
+               elif mode == 2:
+                       pos = pos + self.len
+               self.pos = max(0, pos)
+       def tell(self):
+               return self.pos
+       def read(self, n = -1):
+               if self.buflist:
+                       self.buf = self.buf + string.joinfields(self.buflist, '')
+                       self.buflist = []
+               if n < 0:
+                       newpos = self.len
+               else:
+                       newpos = min(self.pos+n, self.len)
+               r = self.buf[self.pos:newpos]
+               self.pos = newpos
+               return r
+       def readline(self):
+               if self.buflist:
+                       self.buf = self.buf + string.joinfields(self.buflist, '')
+                       self.buflist = []
+               i = string.find(self.buf, '\n', self.pos)
+               if i < 0:
+                       newpos = self.len
+               else:
+                       newpos = i+1
+               r = self.buf[self.pos:newpos]
+               self.pos = newpos
+               return r
+       def readlines(self):
+               lines = []
+               line = self.readline()
+               while line:
+                       lines.append(line)
+                       line = self.readline()
+               return lines
+       def write(self, s):
+               if not s: return
+               if self.pos > self.len:
+                       self.buflist.append('\0'*(self.pos - self.len))
+                       self.len = self.pos
+               newpos = self.pos + len(s)
+               if self.pos < self.len:
+                       if self.buflist:
+                               self.buf = self.buf + string.joinfields(self.buflist, '')
+                               self.buflist = []
+                       self.buflist = [self.buf[:self.pos], s, self.buf[newpos:]]
+                       self.buf = ''
+               else:
+                       self.buflist.append(s)
+                       self.len = newpos
+               self.pos = newpos
+       def writelines(self, list):
+               self.write(string.joinfields(list, ''))
+       def flush(self):
+               pass
+       def getvalue(self):
+               if self.buflist:
+                       self.buf = self.buf + string.joinfields(self.buflist, '')
+                       self.buflist = []
+               return self.buf
+
+
+# A little test suite
+
+def test():
+       import sys
+       if sys.argv[1:]:
+               file = sys.argv[1]
+       else:
+               file = '/etc/passwd'
+       lines = open(file, 'r').readlines()
+       text = open(file, 'r').read()
+       f = StringIO()
+       for line in lines[:-2]:
+               f.write(line)
+       f.writelines(lines[-2:])
+       if f.getvalue() != text:
+               raise RuntimeError, 'write failed'
+       length = f.tell()
+       print 'File length =', length
+       f.seek(len(lines[0]))
+       f.write(lines[1])
+       f.seek(0)
+       print 'First line =', `f.readline()`
+       here = f.tell()
+       line = f.readline()
+       print 'Second line =', `line`
+       f.seek(-len(line), 1)
+       line2 = f.read(len(line))
+       if line != line2:
+               raise RuntimeError, 'bad result after seek back'
+       f.seek(len(line2), 1)
+       list = f.readlines()
+       line = list[-1]
+       f.seek(f.tell() - len(line))
+       line2 = f.read()
+       if line != line2:
+               raise RuntimeError, 'bad result after seek back from EOF'
+       print 'Read', len(list), 'more lines'
+       print 'File length =', f.tell()
+       if f.tell() != length:
+               raise RuntimeError, 'bad length'
+       f.close()
+
+if __name__ == '__main__':
+       test()
diff --git a/Lib/dos_8x3/test_aud.py b/Lib/dos_8x3/test_aud.py
new file mode 100755 (executable)
index 0000000..3acf1bf
--- /dev/null
@@ -0,0 +1,202 @@
+# Test audioop.
+import audioop
+
+def gendata1():
+       return '\0\1\2'
+
+def gendata2():
+       if audioop.getsample('\0\1', 2, 0) == 1:
+               return '\0\0\0\1\0\2'
+       else:
+               return '\0\0\1\0\2\0'
+
+def gendata4():
+       if audioop.getsample('\0\0\0\1', 4, 0) == 1:
+               return '\0\0\0\0\0\0\0\1\0\0\0\2'
+       else:
+               return '\0\0\0\0\1\0\0\0\2\0\0\0'
+
+def testmax(data):
+       if audioop.max(data[0], 1) <> 2 or \
+                 audioop.max(data[1], 2) <> 2 or \
+                 audioop.max(data[2], 4) <> 2:
+               return 0
+       return 1
+
+def testmaxpp(data):
+       if audioop.maxpp(data[0], 1) <> 0 or \
+                 audioop.maxpp(data[1], 2) <> 0 or \
+                 audioop.maxpp(data[2], 4) <> 0:
+               return 0
+       return 1
+
+def testavg(data):
+       if audioop.avg(data[0], 1) <> 1 or \
+                 audioop.avg(data[1], 2) <> 1 or \
+                 audioop.avg(data[2], 4) <> 1:
+               return 0
+       return 1
+
+def testavgpp(data):
+       if audioop.avgpp(data[0], 1) <> 0 or \
+                 audioop.avgpp(data[1], 2) <> 0 or \
+                 audioop.avgpp(data[2], 4) <> 0:
+               return 0
+       return 1
+
+def testrms(data):
+       if audioop.rms(data[0], 1) <> 1 or \
+                 audioop.rms(data[1], 2) <> 1 or \
+                 audioop.rms(data[2], 4) <> 1:
+               return 0
+       return 1
+
+def testcross(data):
+       if audioop.cross(data[0], 1) <> 0 or \
+                 audioop.cross(data[1], 2) <> 0 or \
+                 audioop.cross(data[2], 4) <> 0:
+               return 0
+       return 1
+
+def testadd(data):
+       data2 = []
+       for d in data:
+               str = ''
+               for s in d:
+                       str = str + chr(ord(s)*2)
+               data2.append(str)
+       if audioop.add(data[0], data[0], 1) <> data2[0] or \
+                 audioop.add(data[1], data[1], 2) <> data2[1] or \
+                 audioop.add(data[2], data[2], 4) <> data2[2]:
+               return 0
+       return 1
+
+def testbias(data):
+       # Note: this test assumes that avg() works
+       d1 = audioop.bias(data[0], 1, 100)
+       d2 = audioop.bias(data[1], 2, 100)
+       d4 = audioop.bias(data[2], 4, 100)
+       if audioop.avg(d1, 1) <> 101 or \
+                 audioop.avg(d2, 2) <> 101 or \
+                 audioop.avg(d4, 4) <> 101:
+               return 0
+       return 1
+
+def testlin2lin(data):
+       # too simple: we test only the size
+       for d1 in data:
+               for d2 in data:
+                       got = len(d1)/3
+                       wtd = len(d2)/3
+                       if len(audioop.lin2lin(d1, got, wtd)) <> len(d2):
+                               return 0
+       return 1
+
+def testadpcm2lin(data):
+       # Very cursory test
+       if audioop.adpcm2lin('\0\0', 1, None) <> ('\0\0\0\0', (0,0)):
+               return 0
+       return 1
+
+def testlin2adpcm(data):
+       # Very cursory test
+       if audioop.lin2adpcm('\0\0\0\0', 1, None) <> ('\0\0', (0,0)):
+               return 0
+       return 1
+
+def testlin2ulaw(data):
+       if audioop.lin2ulaw(data[0], 1) <> '\377\347\333' or \
+                 audioop.lin2ulaw(data[1], 2) <> '\377\377\377' or \
+                 audioop.lin2ulaw(data[2], 4) <> '\377\377\377':
+               return 0
+       return 1
+
+def testulaw2lin(data):
+       # Cursory
+       d = audioop.lin2ulaw(data[0], 1)
+       if audioop.ulaw2lin(d, 1) <> data[0]:
+               return 0
+       return 1
+
+def testmul(data):
+       data2 = []
+       for d in data:
+               str = ''
+               for s in d:
+                       str = str + chr(ord(s)*2)
+               data2.append(str)
+       if audioop.mul(data[0], 1, 2) <> data2[0] or \
+                 audioop.mul(data[1],2, 2) <> data2[1] or \
+                 audioop.mul(data[2], 4, 2) <> data2[2]:
+               return 0
+       return 1
+
+def testreverse(data):
+       if audioop.reverse(data[0], 1) <> '\2\1\0':
+               return 0
+       return 1
+
+def testtomono(data):
+       data2 = ''
+       for d in data[0]:
+               data2 = data2 + d + d
+       if audioop.tomono(data2, 1, 0.5, 0.5) <> data[0]:
+               return 0
+       return 1
+
+def testtostereo(data):
+       data2 = ''
+       for d in data[0]:
+               data2 = data2 + d + d
+       if audioop.tostereo(data[0], 1, 1, 1) <> data2:
+               return 0
+       return 1
+
+def testfindfactor(data):
+       if audioop.findfactor(data[1], data[1]) <> 1.0:
+               return 0
+       return 1
+
+def testfindfit(data):
+       if audioop.findfit(data[1], data[1]) <> (0, 1.0):
+               return 0
+       return 1
+
+def testfindmax(data):
+       if audioop.findmax(data[1], 1) <> 2:
+               return 0
+       return 1
+
+def testgetsample(data):
+       for i in range(3):
+               if audioop.getsample(data[0], 1, i) <> i or \
+                         audioop.getsample(data[1], 2, i) <> i or \
+                         audioop.getsample(data[2], 4, i) <> i:
+                       return 0
+       return 1
+
+def testone(name, data):
+       try:
+               func = eval('test'+name)
+       except NameError:
+               print 'No test found for audioop.'+name+'()'
+               return
+       try:
+               rv = func(data)
+       except 'xx':
+               print 'Test FAILED for audioop.'+name+'() (with an exception)'
+               return
+       if not rv:
+               print 'Test FAILED for audioop.'+name+'()'
+
+def testall():
+       data = [gendata1(), gendata2(), gendata4()]
+       names = dir(audioop)
+       # We know there is a routine 'add'
+       routines = []
+       for n in names:
+               if type(eval('audioop.'+n)) == type(audioop.add):
+                       routines.append(n)
+       for n in routines:
+               testone(n, data)
+testall()
diff --git a/Lib/dos_8x3/test_bui.py b/Lib/dos_8x3/test_bui.py
new file mode 100755 (executable)
index 0000000..33fef8d
--- /dev/null
@@ -0,0 +1,13 @@
+# Python test set -- part 4, built-in functions
+
+from test_support import *
+
+print '4. Built-in functions'
+
+print 'test_b1'
+unload('test_b1')
+import test_b1
+
+print 'test_b2'
+unload('test_b2')
+import test_b2
diff --git a/Lib/dos_8x3/test_exc.py b/Lib/dos_8x3/test_exc.py
new file mode 100755 (executable)
index 0000000..4fbee3e
--- /dev/null
@@ -0,0 +1,92 @@
+# Python test set -- part 5, built-in exceptions
+
+from test_support import *
+
+print '5. Built-in exceptions'
+# XXX This is not really enough, each *operation* should be tested!
+
+def r(name): print name
+
+r(AttributeError)
+import sys
+try: x = sys.undefined_attribute
+except AttributeError: pass
+
+r(EOFError)
+import sys
+fp = open(TESTFN, 'w')
+fp.close()
+fp = open(TESTFN, 'r')
+savestdin = sys.stdin
+try:
+       try:
+               sys.stdin = fp
+               x = raw_input()
+       except EOFError:
+               pass
+finally:
+       sys.stdin = savestdin
+       fp.close()
+
+r(IOError)
+try: open('this file does not exist', 'r')
+except IOError: pass
+
+r(ImportError)
+try: import undefined_module
+except ImportError: pass
+
+r(IndexError)
+x = []
+try: a = x[10]
+except IndexError: pass
+
+r(KeyError)
+x = {}
+try: a = x['key']
+except KeyError: pass
+
+r(KeyboardInterrupt)
+print '(not testable in a script)'
+
+r(MemoryError)
+print '(not safe to test)'
+
+r(NameError)
+try: x = undefined_variable
+except NameError: pass
+
+r(OverflowError)
+x = 1
+try:
+       while 1: x = x+x
+except OverflowError: pass
+
+r(RuntimeError)
+print '(not used any more?)'
+
+r(SyntaxError)
+try: exec '/\n'
+except SyntaxError: pass
+
+r(SystemError)
+print '(hard to reproduce)'
+
+r(SystemExit)
+import sys
+try: sys.exit(0)
+except SystemExit: pass
+
+r(TypeError)
+try: [] + ()
+except TypeError: pass
+
+r(ValueError)
+try: x = chr(10000)
+except ValueError: pass
+
+r(ZeroDivisionError)
+try: x = 1/0
+except ZeroDivisionError: pass
+
+unlink(TESTFN)
diff --git a/Lib/dos_8x3/test_gra.py b/Lib/dos_8x3/test_gra.py
new file mode 100755 (executable)
index 0000000..b9607ac
--- /dev/null
@@ -0,0 +1,513 @@
+# Python test set -- part 1, grammar.
+# This just tests whether the parser accepts them all.
+
+from test_support import *
+
+print '1. Parser'
+
+print '1.1 Tokens'
+
+print '1.1.1 Backslashes'
+
+# Backslash means line continuation:
+x = 1 \
++ 1
+if x <> 2: raise TestFailed, 'backslash for line continuation'
+
+# Backslash does not means continuation in comments :\
+x = 0
+if x <> 0: raise TestFailed, 'backslash ending comment'
+
+print '1.1.2 Numeric literals'
+
+print '1.1.2.1 Plain integers'
+if 0xff <> 255: raise TestFailed, 'hex int'
+if 0377 <> 255: raise TestFailed, 'octal int'
+if  2147483647   != 017777777777: raise TestFailed, 'large positive int'
+try:
+       from sys import maxint
+except ImportError:
+       maxint = 2147483647
+if maxint == 2147483647:
+       if -2147483647-1 != 020000000000: raise TestFailed, 'max negative int'
+       # XXX -2147483648
+       if 037777777777 != -1: raise TestFailed, 'oct -1'
+       if 0xffffffff != -1: raise TestFailed, 'hex -1'
+       for s in '2147483648', '040000000000', '0x100000000':
+               try:
+                       x = eval(s)
+               except OverflowError:
+                       continue
+##             raise TestFailed, \
+               print \
+                         'No OverflowError on huge integer literal ' + `s`
+elif eval('maxint == 9223372036854775807'):
+       if eval('-9223372036854775807-1 != 01000000000000000000000'):
+               raise TestFailed, 'max negative int'
+       if eval('01777777777777777777777') != -1: raise TestFailed, 'oct -1'
+       if eval('0xffffffffffffffff') != -1: raise TestFailed, 'hex -1'
+       for s in '9223372036854775808', '02000000000000000000000', \
+                '0x10000000000000000':
+               try:
+                       x = eval(s)
+               except OverflowError:
+                       continue
+               raise TestFailed, \
+                         'No OverflowError on huge integer literal ' + `s`
+else:
+       print 'Weird maxint value', maxint
+
+print '1.1.2.2 Long integers'
+x = 0L
+x = 0l
+x = 0xffffffffffffffffL
+x = 0xffffffffffffffffl
+x = 077777777777777777L
+x = 077777777777777777l
+x = 123456789012345678901234567890L
+x = 123456789012345678901234567890l
+
+print '1.1.2.3 Floating point'
+x = 3.14
+x = 314.
+x = 0.314
+# XXX x = 000.314
+x = .314
+x = 3e14
+x = 3E14
+x = 3e-14
+x = 3e+14
+x = 3.e14
+x = .3e14
+x = 3.1e4
+
+print '1.1.3 String literals'
+
+def assert(s):
+       if not s: raise TestFailed, 'see traceback'
+
+x = ''; y = ""; assert(len(x) == 0 and x == y)
+x = '\''; y = "'"; assert(len(x) == 1 and x == y and ord(x) == 39)
+x = '"'; y = "\""; assert(len(x) == 1 and x == y and ord(x) == 34)
+x = "doesn't \"shrink\" does it"
+y = 'doesn\'t "shrink" does it'
+assert(len(x) == 24 and x == y)
+x = "does \"shrink\" doesn't it"
+y = 'does "shrink" doesn\'t it'
+assert(len(x) == 24 and x == y)
+x = """
+The "quick"
+brown fox
+jumps over
+the 'lazy' dog.
+"""
+y = '\nThe "quick"\nbrown fox\njumps over\nthe \'lazy\' dog.\n'
+assert(x == y)
+y = '''
+The "quick"
+brown fox
+jumps over
+the 'lazy' dog.
+'''; assert(x == y)
+y = "\n\
+The \"quick\"\n\
+brown fox\n\
+jumps over\n\
+the 'lazy' dog.\n\
+"; assert(x == y)
+y = '\n\
+The \"quick\"\n\
+brown fox\n\
+jumps over\n\
+the \'lazy\' dog.\n\
+'; assert(x == y)
+
+
+print '1.2 Grammar'
+
+print 'single_input' # NEWLINE | simple_stmt | compound_stmt NEWLINE
+# XXX can't test in a script -- this rule is only used when interactive
+
+print 'file_input' # (NEWLINE | stmt)* ENDMARKER
+# Being tested as this very moment this very module
+
+print 'expr_input' # testlist NEWLINE
+# XXX Hard to test -- used only in calls to input()
+
+print 'eval_input' # testlist ENDMARKER
+x = eval('1, 0 or 1')
+
+print 'funcdef'
+### 'def' NAME parameters ':' suite
+### parameters: '(' [varargslist] ')'
+### varargslist: (fpdef ['=' test] ',')* '*' NAME
+###            | fpdef ['=' test] (',' fpdef ['=' test])* [',']
+### fpdef: NAME | '(' fplist ')'
+### fplist: fpdef (',' fpdef)* [',']
+def f1(): pass
+def f2(one_argument): pass
+def f3(two, arguments): pass
+def f4(two, (compound, (argument, list))): pass
+def a1(one_arg,): pass
+def a2(two, args,): pass
+def v0(*rest): pass
+def v1(a, *rest): pass
+def v2(a, b, *rest): pass
+def v3(a, (b, c), *rest): pass
+def d01(a=1): pass
+d01()
+d01(1)
+def d11(a, b=1): pass
+d11(1)
+d11(1, 2)
+def d21(a, b, c=1): pass
+d21(1, 2)
+d21(1, 2, 3)
+def d02(a=1, b=2): pass
+d02()
+d02(1)
+d02(1, 2)
+def d12(a, b=1, c=2): pass
+d12(1)
+d12(1, 2)
+d12(1, 2, 3)
+def d22(a, b, c=1, d=2): pass
+d22(1, 2)
+d22(1, 2, 3)
+d22(1, 2, 3, 4)
+def d01v(a=1, *rest): pass
+d01v()
+d01v(1)
+d01v(1, 2)
+def d11v(a, b=1, *rest): pass
+d11v(1)
+d11v(1, 2)
+d11v(1, 2, 3)
+def d21v(a, b, c=1, *rest): pass
+d21v(1, 2)
+d21v(1, 2, 3)
+d21v(1, 2, 3, 4)
+def d02v(a=1, b=2, *rest): pass
+d02v()
+d02v(1)
+d02v(1, 2)
+d02v(1, 2, 3)
+def d12v(a, b=1, c=2, *rest): pass
+d12v(1)
+d12v(1, 2)
+d12v(1, 2, 3)
+d12v(1, 2, 3, 4)
+def d22v(a, b, c=1, d=2, *rest): pass
+d22v(1, 2)
+d22v(1, 2, 3)
+d22v(1, 2, 3, 4)
+d22v(1, 2, 3, 4, 5)
+
+### stmt: simple_stmt | compound_stmt
+# Tested below
+
+### simple_stmt: small_stmt (';' small_stmt)* [';']
+print 'simple_stmt'
+x = 1; pass; del x
+
+### small_stmt: expr_stmt | print_stmt  | pass_stmt | del_stmt | flow_stmt | import_stmt | global_stmt | access_stmt | exec_stmt
+# Tested below
+
+print 'expr_stmt' # (exprlist '=')* exprlist
+1
+1, 2, 3
+x = 1
+x = 1, 2, 3
+x = y = z = 1, 2, 3
+x, y, z = 1, 2, 3
+abc = a, b, c = x, y, z = xyz = 1, 2, (3, 4)
+# NB these variables are deleted below
+
+print 'print_stmt' # 'print' (test ',')* [test]
+print 1, 2, 3
+print 1, 2, 3,
+print
+print 0 or 1, 0 or 1,
+print 0 or 1
+
+print 'del_stmt' # 'del' exprlist
+del abc
+del x, y, (z, xyz)
+
+print 'pass_stmt' # 'pass'
+pass
+
+print 'flow_stmt' # break_stmt | continue_stmt | return_stmt | raise_stmt
+# Tested below
+
+print 'break_stmt' # 'break'
+while 1: break
+
+print 'continue_stmt' # 'continue'
+i = 1
+while i: i = 0; continue
+
+print 'return_stmt' # 'return' [testlist]
+def g1(): return
+def g2(): return 1
+g1()
+x = g2()
+
+print 'raise_stmt' # 'raise' test [',' test]
+try: raise RuntimeError, 'just testing'
+except RuntimeError: pass
+try: raise KeyboardInterrupt
+except KeyboardInterrupt: pass
+
+print 'import_stmt' # 'import' NAME (',' NAME)* | 'from' NAME 'import' ('*' | NAME (',' NAME)*)
+import sys
+import time, sys
+from time import time
+from sys import *
+from sys import path, argv
+
+print 'global_stmt' # 'global' NAME (',' NAME)*
+def f():
+       global a
+       global a, b
+       global one, two, three, four, five, six, seven, eight, nine, ten
+
+print 'exec_stmt' # 'exec' expr ['in' expr [',' expr]]
+def f():
+       z = None
+       del z
+       exec 'z=1+1\n'
+       if z <> 2: raise TestFailed, 'exec \'z=1+1\'\\n'
+       del z
+       exec 'z=1+1'
+       if z <> 2: raise TestFailed, 'exec \'z=1+1\''
+f()
+g = {}
+exec 'z = 1' in g
+if g.has_key('__builtins__'): del g['__builtins__']
+if g <> {'z': 1}: raise TestFailed, 'exec \'z = 1\' in g'
+g = {}
+l = {}
+exec 'global a; a = 1; b = 2' in g, l
+if g.has_key('__builtins__'): del g['__builtins__']
+if l.has_key('__builtins__'): del l['__builtins__']
+if (g, l) <> ({'a':1}, {'b':2}): raise TestFailed, 'exec ... in g, l'
+
+
+### compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | funcdef | classdef
+# Tested below
+
+print 'if_stmt' # 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
+if 1: pass
+if 1: pass
+else: pass
+if 0: pass
+elif 0: pass
+if 0: pass
+elif 0: pass
+elif 0: pass
+elif 0: pass
+else: pass
+
+print 'while_stmt' # 'while' test ':' suite ['else' ':' suite]
+while 0: pass
+while 0: pass
+else: pass
+
+print 'for_stmt' # 'for' exprlist 'in' exprlist ':' suite ['else' ':' suite]
+for i in 1, 2, 3: pass
+for i, j, k in (): pass
+else: pass
+class Squares:
+       def __init__(self, max):
+               self.max = max
+               self.sofar = []
+       def __len__(self): return len(self.sofar)
+       def __getitem__(self, i):
+               if not 0 <= i < self.max: raise IndexError
+               n = len(self.sofar)
+               while n <= i:
+                       self.sofar.append(n*n)
+                       n = n+1
+               return self.sofar[i]
+n = 0
+for x in Squares(10): n = n+x
+if n != 285: raise TestFailed, 'for over growing sequence'
+
+print 'try_stmt'
+### try_stmt: 'try' ':' suite (except_clause ':' suite)+ ['else' ':' suite]
+###         | 'try' ':' suite 'finally' ':' suite
+### except_clause: 'except' [expr [',' expr]]
+try:
+       1/0
+except ZeroDivisionError:
+       pass
+else:
+       pass
+try: 1/0
+except EOFError: pass
+except TypeError, msg: pass
+except RuntimeError, msg: pass
+except: pass
+else: pass
+try: 1/0
+except (EOFError, TypeError, ZeroDivisionError): pass
+try: 1/0
+except (EOFError, TypeError, ZeroDivisionError), msg: pass
+try: pass
+finally: pass
+
+print 'suite' # simple_stmt | NEWLINE INDENT NEWLINE* (stmt NEWLINE*)+ DEDENT
+if 1: pass
+if 1:
+       pass
+if 1:
+       #
+       #
+       #
+       pass
+       pass
+       #
+       pass
+       #
+
+print 'test'
+### and_test ('or' and_test)*
+### and_test: not_test ('and' not_test)*
+### not_test: 'not' not_test | comparison
+if not 1: pass
+if 1 and 1: pass
+if 1 or 1: pass
+if not not not 1: pass
+if not 1 and 1 and 1: pass
+if 1 and 1 or 1 and 1 and 1 or not 1 and 1: pass
+
+print 'comparison'
+### comparison: expr (comp_op expr)*
+### comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not'
+if 1: pass
+x = (1 == 1)
+if 1 == 1: pass
+if 1 != 1: pass
+if 1 <> 1: pass
+if 1 < 1: pass
+if 1 > 1: pass
+if 1 <= 1: pass
+if 1 >= 1: pass
+if 1 is 1: pass
+if 1 is not 1: pass
+if 1 in (): pass
+if 1 not in (): pass
+if 1 < 1 > 1 == 1 >= 1 <= 1 <> 1 != 1 in 1 not in 1 is 1 is not 1: pass
+
+print 'binary mask ops'
+x = 1 & 1
+x = 1 ^ 1
+x = 1 | 1
+
+print 'shift ops'
+x = 1 << 1
+x = 1 >> 1
+x = 1 << 1 >> 1
+
+print 'additive ops'
+x = 1
+x = 1 + 1
+x = 1 - 1 - 1
+x = 1 - 1 + 1 - 1 + 1
+
+print 'multiplicative ops'
+x = 1 * 1
+x = 1 / 1
+x = 1 % 1
+x = 1 / 1 * 1 % 1
+
+print 'unary ops'
+x = +1
+x = -1
+x = ~1
+x = ~1 ^ 1 & 1 | 1 & 1 ^ -1
+x = -1*1/1 + 1*1 - ---1*1
+
+print 'selectors'
+### trailer: '(' [testlist] ')' | '[' subscript ']' | '.' NAME
+### subscript: expr | [expr] ':' [expr]
+f1()
+f2(1)
+f2(1,)
+f3(1, 2)
+f3(1, 2,)
+f4(1, (2, (3, 4)))
+v0()
+v0(1)
+v0(1,)
+v0(1,2)
+v0(1,2,3,4,5,6,7,8,9,0)
+v1(1)
+v1(1,)
+v1(1,2)
+v1(1,2,3)
+v1(1,2,3,4,5,6,7,8,9,0)
+v2(1,2)
+v2(1,2,3)
+v2(1,2,3,4)
+v2(1,2,3,4,5,6,7,8,9,0)
+v3(1,(2,3))
+v3(1,(2,3),4)
+v3(1,(2,3),4,5,6,7,8,9,0)
+import sys, time
+c = sys.path[0]
+x = time.time()
+x = sys.modules['time'].time()
+a = '01234'
+c = a[0]
+c = a[-1]
+s = a[0:5]
+s = a[:5]
+s = a[0:]
+s = a[:]
+s = a[-5:]
+s = a[:-1]
+s = a[-4:-3]
+
+print 'atoms'
+### atom: '(' [testlist] ')' | '[' [testlist] ']' | '{' [dictmaker] '}' | '`' testlist '`' | NAME | NUMBER | STRING
+### dictmaker: test ':' test (',' test ':' test)* [',']
+
+x = (1)
+x = (1 or 2 or 3)
+x = (1 or 2 or 3, 2, 3)
+
+x = []
+x = [1]
+x = [1 or 2 or 3]
+x = [1 or 2 or 3, 2, 3]
+x = []
+
+x = {}
+x = {'one': 1}
+x = {'one': 1,}
+x = {'one' or 'two': 1 or 2}
+x = {'one': 1, 'two': 2}
+x = {'one': 1, 'two': 2,}
+x = {'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5, 'six': 6}
+
+x = `x`
+x = `1 or 2 or 3`
+x = x
+x = 'x'
+x = 123
+
+### exprlist: expr (',' expr)* [',']
+### testlist: test (',' test)* [',']
+# These have been exercised enough above
+
+print 'classdef' # 'class' NAME ['(' testlist ')'] ':' suite
+class B: pass
+class C1(B): pass
+class C2(B): pass
+class D(C1, C2, B): pass
+class C:
+       def meth1(self): pass
+       def meth2(self, arg): pass
+       def meth3(self, a1, a2): pass
diff --git a/Lib/dos_8x3/test_opc.py b/Lib/dos_8x3/test_opc.py
new file mode 100755 (executable)
index 0000000..b1e944b
--- /dev/null
@@ -0,0 +1,59 @@
+# Python test set -- part 2, opcodes
+
+from test_support import *
+
+
+print '2. Opcodes'
+print 'XXX Not yet fully implemented'
+
+print '2.1 try inside for loop'
+n = 0
+for i in range(10):
+       n = n+i
+       try: 1/0
+       except NameError: pass
+       except ZeroDivisionError: pass
+       except TypeError: pass
+       try: pass
+       except: pass
+       try: pass
+       finally: pass
+       n = n+i
+if n <> 90:
+       raise TestFailed, 'try inside for'
+
+
+print '2.2 raise class exceptions'
+
+class AClass: pass
+class BClass(AClass): pass
+class CClass: pass
+
+try: raise AClass()
+except: pass
+
+try: raise AClass()
+except AClass: pass
+
+try: raise BClass()
+except AClass: pass
+
+try: raise BClass()
+except CClass: raise TestFailed
+except: pass
+
+a = AClass()
+b = BClass()
+
+try: raise AClass, b
+except BClass, v: raise TestFailed
+except AClass, v:
+       if v != b: raise TestFailed
+
+
+try: raise b
+except AClass, v:
+       if v != b: raise TestFailed
+
+try:  raise BClass, a
+except TypeError: pass
diff --git a/Lib/dos_8x3/test_ope.py b/Lib/dos_8x3/test_ope.py
new file mode 100755 (executable)
index 0000000..1a75065
--- /dev/null
@@ -0,0 +1,5 @@
+# Python test set -- part 3, built-in operations.
+
+
+print '3. Operations'
+print 'XXX Not yet implemented'
diff --git a/Lib/dos_8x3/test_rgb.py b/Lib/dos_8x3/test_rgb.py
new file mode 100755 (executable)
index 0000000..cdadc66
--- /dev/null
@@ -0,0 +1,49 @@
+# Testing rgbimg module
+
+import rgbimg, os
+
+error = 'test_rgbimg.error'
+
+print 'RGBimg test suite:'
+
+def findfile(file):
+       if os.path.isabs(file): return file
+       import sys
+       for dn in sys.path:
+               fn = os.path.join(dn, file)
+               if os.path.exists(fn): return fn
+       return file
+
+def testimg(rgb_file, raw_file):
+       rgb_file = findfile(rgb_file)
+       raw_file = findfile(raw_file)
+       width, height = rgbimg.sizeofimage(rgb_file)
+       rgb = rgbimg.longimagedata(rgb_file)
+       if len(rgb) != width * height * 4:
+               raise error, 'bad image length'
+       raw = open(raw_file, 'r').read()
+       if rgb != raw:
+               raise error, 'images don\'t match for '+rgb_file+' and '+raw_file
+       for depth in [1, 3, 4]:
+               rgbimg.longstoimage(rgb, width, height, depth, '@.rgb')
+       os.unlink('@.rgb')
+
+ttob = rgbimg.ttob(0)
+if ttob != 0:
+       raise error, 'ttob should start out as zero'
+
+testimg('test.rgb', 'test.rawimg')
+
+ttob = rgbimg.ttob(1)
+if ttob != 0:
+       raise error, 'ttob should be zero'
+
+testimg('test.rgb', 'test.rawimg.rev')
+
+ttob = rgbimg.ttob(0)
+if ttob != 1:
+       raise error, 'ttob should be one'
+
+ttob = rgbimg.ttob(0)
+if ttob != 0:
+       raise error, 'ttob should be zero'
diff --git a/Lib/dos_8x3/test_sel.py b/Lib/dos_8x3/test_sel.py
new file mode 100755 (executable)
index 0000000..f185308
--- /dev/null
@@ -0,0 +1,23 @@
+# Testing select module
+
+def test():
+       import select
+       import os
+       cmd = 'for i in 0 1 2 3 4 5 6 7 8 9; do date; sleep 3; done'
+       p = os.popen(cmd, 'r')
+       for tout in (0, 1, 2, 4, 8, 16) + (None,)*10:
+               print 'timeout =', tout
+               rfd, wfd, xfd = select.select([p], [], [], tout)
+               print rfd, wfd, xfd
+               if (rfd, wfd, xfd) == ([], [], []):
+                       continue
+               if (rfd, wfd, xfd) == ([p], [], []):
+                       line = p.readline()
+                       print `line`
+                       if not line:
+                               print 'EOF'
+                               break
+                       continue
+               print 'Heh?'
+
+test()
diff --git a/Lib/dos_8x3/test_sig.py b/Lib/dos_8x3/test_sig.py
new file mode 100755 (executable)
index 0000000..bfcf517
--- /dev/null
@@ -0,0 +1,50 @@
+# Test the signal module
+
+import signal
+import os
+
+
+pid = os.getpid()
+
+# Shell script that will send us asynchronous signals
+script = """
+(
+       set -x
+       sleep 2
+       kill -5 %(pid)d
+       sleep 2
+       kill -2 %(pid)d
+       sleep 2
+       kill -3 %(pid)d
+) &
+""" % vars()
+
+def handlerA(*args):
+       print "handlerA", args
+
+HandlerBCalled = "HandlerBCalled"      # Exception
+
+def handlerB(*args):
+       print "handlerB", args
+       raise HandlerBCalled, args
+
+signal.alarm(20)                       # Entire test lasts at most 20 sec.
+signal.signal(5, handlerA)
+signal.signal(2, handlerB)
+signal.signal(3, signal.SIG_IGN)
+signal.signal(signal.SIGALRM, signal.default_int_handler)
+
+os.system(script)
+
+print "starting pause() loop..."
+
+try:
+       while 1:
+               print "call pause()..."
+               try:
+                       signal.pause()
+                       print "pause() returned"
+               except HandlerBCalled:
+                       print "HandlerBCalled exception caught"
+except KeyboardInterrupt:
+       print "KeyboardInterrupt (assume the alarm() went off)"
diff --git a/Lib/dos_8x3/test_sup.py b/Lib/dos_8x3/test_sup.py
new file mode 100755 (executable)
index 0000000..7a76664
--- /dev/null
@@ -0,0 +1,41 @@
+# Python test set -- supporting definitions.
+
+TestFailed = 'test_support -- test failed'     # Exception
+
+def unload(name):
+       import sys
+       try:
+               del sys.modules[name]
+       except KeyError:
+               pass
+
+def forget(modname):
+       unload(modname)
+       import sys, os
+       for dirname in sys.path:
+               try:
+                       os.unlink(os.path.join(dirname, modname + '.pyc'))
+               except os.error:
+                       pass
+
+FUZZ = 1e-6
+
+def fcmp(x, y): # fuzzy comparison function
+       if type(x) == type(0.0) or type(y) == type(0.0):
+               try:
+                       x, y = coerce(x, y)
+                       fuzz = (abs(x) + abs(y)) * FUZZ
+                       if abs(x-y) <= fuzz:
+                               return 0
+               except:
+                       pass
+       elif type(x) == type(y) and type(x) in (type(()), type([])):
+               for i in range(min(len(x), len(y))):
+                       outcome = fcmp(x[i], y[i])
+                       if outcome <> 0:
+                               return outcome
+               return cmp(len(x), len(y))
+       return cmp(x, y)
+
+TESTFN = '@test' # Filename used for testing
+from os import unlink
diff --git a/Lib/dos_8x3/test_thr.py b/Lib/dos_8x3/test_thr.py
new file mode 100755 (executable)
index 0000000..4e0eb70
--- /dev/null
@@ -0,0 +1,106 @@
+# Very rudimentary test of thread module
+
+# Create a bunch of threads, let each do some work, wait until all are done
+
+import whrandom
+import thread
+import time
+
+mutex = thread.allocate_lock()
+whmutex = thread.allocate_lock() # for calls to whrandom
+running = 0
+done = thread.allocate_lock()
+done.acquire()
+
+numtasks = 10
+
+def task(ident):
+       global running
+       whmutex.acquire()
+       delay = whrandom.random() * numtasks
+       whmutex.release()
+       print 'task', ident, 'will run for', delay, 'sec'
+       time.sleep(delay)
+       print 'task', ident, 'done'
+       mutex.acquire()
+       running = running - 1
+       if running == 0:
+               done.release()
+       mutex.release()
+
+next_ident = 0
+def newtask():
+       global next_ident, running
+       mutex.acquire()
+       next_ident = next_ident + 1
+       print 'creating task', next_ident
+       thread.start_new_thread(task, (next_ident,))
+       running = running + 1
+       mutex.release()
+
+for i in range(numtasks):
+       newtask()
+
+print 'waiting for all tasks to complete'
+done.acquire()
+print 'all tasks done'
+
+class barrier:
+       def __init__(self, n):
+               self.n = n
+               self.waiting = 0
+               self.checkin  = thread.allocate_lock()
+               self.checkout = thread.allocate_lock()
+               self.checkout.acquire()
+
+       def enter(self):
+               checkin, checkout = self.checkin, self.checkout
+
+               checkin.acquire()
+               self.waiting = self.waiting + 1
+               if self.waiting == self.n:
+                       self.waiting = self.n - 1
+                       checkout.release()
+                       return
+               checkin.release()
+
+               checkout.acquire()
+               self.waiting = self.waiting - 1
+               if self.waiting == 0:
+                       checkin.release()
+                       return
+               checkout.release()
+
+numtrips = 3
+def task2(ident):
+       global running
+       for i in range(numtrips):
+               if ident == 0:
+                       # give it a good chance to enter the next
+                       # barrier before the others are all out
+                       # of the current one
+                       delay = 0.001
+               else:
+                       whmutex.acquire()
+                       delay = whrandom.random() * numtasks
+                       whmutex.release()
+               print 'task', ident, 'will run for', delay, 'sec'
+               time.sleep(delay)
+               print 'task', ident, 'entering barrier', i
+               bar.enter()
+               print 'task', ident, 'leaving barrier', i
+       mutex.acquire()
+       running = running - 1
+       if running == 0:
+               done.release()
+       mutex.release()
+
+print '\n*** Barrier Test ***'
+if done.acquire(0):
+       raise ValueError, "'done' should have remained acquired"
+bar = barrier(numtasks)
+running = numtasks
+for i in range(numtasks):
+       thread.start_new_thread(task2, (i,))
+done.acquire()
+print 'all tasks done'
diff --git a/Lib/dos_8x3/test_typ.py b/Lib/dos_8x3/test_typ.py
new file mode 100755 (executable)
index 0000000..51c76dc
--- /dev/null
@@ -0,0 +1,182 @@
+# Python test set -- part 6, built-in types
+
+from test_support import *
+
+print '6. Built-in types'
+
+print '6.1 Truth value testing'
+if None: raise TestFailed, 'None is true instead of false'
+if 0: raise TestFailed, '0 is true instead of false'
+if 0L: raise TestFailed, '0L is true instead of false'
+if 0.0: raise TestFailed, '0.0 is true instead of false'
+if '': raise TestFailed, '\'\' is true instead of false'
+if (): raise TestFailed, '() is true instead of false'
+if []: raise TestFailed, '[] is true instead of false'
+if {}: raise TestFailed, '{} is true instead of false'
+if not 1: raise TestFailed, '1 is false instead of true'
+if not 1L: raise TestFailed, '1L is false instead of true'
+if not 1.0: raise TestFailed, '1.0 is false instead of true'
+if not 'x': raise TestFailed, '\'x\' is false instead of true'
+if not (1, 1): raise TestFailed, '(1, 1) is false instead of true'
+if not [1]: raise TestFailed, '[1] is false instead of true'
+if not {'x': 1}: raise TestFailed, '{\'x\': 1} is false instead of true'
+def f(): pass
+class C: pass
+import sys
+x = C()
+if not f: raise TestFailed, 'f is false instead of true'
+if not C: raise TestFailed, 'C is false instead of true'
+if not sys: raise TestFailed, 'sys is false instead of true'
+if not x: raise TestFailed, 'x is false instead of true'
+
+print '6.2 Boolean operations'
+if 0 or 0: raise TestFailed, '0 or 0 is true instead of false'
+if 1 and 1: pass
+else: raise TestFailed, '1 and 1 is false instead of false'
+if not 1: raise TestFailed, 'not 1 is true instead of false'
+
+print '6.3 Comparisons'
+if 0 < 1 <= 1 == 1 >= 1 > 0 <> 1: pass
+else: raise TestFailed, 'int comparisons failed'
+if 0L < 1L <= 1L == 1L >= 1L > 0L <> 1L: pass
+else: raise TestFailed, 'long int comparisons failed'
+if 0.0 < 1.0 <= 1.0 == 1.0 >= 1.0 > 0.0 <> 1.0: pass
+else: raise TestFailed, 'float comparisons failed'
+if '' < 'a' <= 'a' == 'a' < 'abc' < 'abd' < 'b': pass
+else: raise TestFailed, 'string comparisons failed'
+if 0 in [0] and 0 not in [1]: pass
+else: raise TestFailed, 'membership test failed'
+if None is None and [] is not []: pass
+else: raise TestFailed, 'identity test failed'
+
+print '6.4 Numeric types (mostly conversions)'
+if 0 <> 0L or 0 <> 0.0 or 0L <> 0.0: raise TestFailed, 'mixed comparisons'
+if 1 <> 1L or 1 <> 1.0 or 1L <> 1.0: raise TestFailed, 'mixed comparisons'
+if -1 <> -1L or -1 <> -1.0 or -1L <> -1.0:
+       raise TestFailed, 'int/long/float value not equal'
+if int(1.9) == 1 == int(1.1) and int(-1.1) == -1 == int(-1.9): pass
+else: raise TestFailed, 'int() does not round properly'
+if long(1.9) == 1L == long(1.1) and long(-1.1) == -1L == long(-1.9): pass
+else: raise TestFailed, 'long() does not round properly'
+if float(1) == 1.0 and float(-1) == -1.0 and float(0) == 0.0: pass
+else: raise TestFailed, 'float() does not work properly'
+print '6.4.1 32-bit integers'
+if 12 + 24 <> 36: raise TestFailed, 'int op'
+if 12 + (-24) <> -12: raise TestFailed, 'int op'
+if (-12) + 24 <> 12: raise TestFailed, 'int op'
+if (-12) + (-24) <> -36: raise TestFailed, 'int op'
+if not 12 < 24: raise TestFailed, 'int op'
+if not -24 < -12: raise TestFailed, 'int op'
+# Test for a particular bug in integer multiply
+xsize, ysize, zsize = 238, 356, 4
+if not (xsize*ysize*zsize == zsize*xsize*ysize == 338912):
+       raise TestFailed, 'int mul commutativity'
+print '6.4.2 Long integers'
+if 12L + 24L <> 36L: raise TestFailed, 'long op'
+if 12L + (-24L) <> -12L: raise TestFailed, 'long op'
+if (-12L) + 24L <> 12L: raise TestFailed, 'long op'
+if (-12L) + (-24L) <> -36L: raise TestFailed, 'long op'
+if not 12L < 24L: raise TestFailed, 'long op'
+if not -24L < -12L: raise TestFailed, 'long op'
+print '6.4.3 Floating point numbers'
+if 12.0 + 24.0 <> 36.0: raise TestFailed, 'float op'
+if 12.0 + (-24.0) <> -12.0: raise TestFailed, 'float op'
+if (-12.0) + 24.0 <> 12.0: raise TestFailed, 'float op'
+if (-12.0) + (-24.0) <> -36.0: raise TestFailed, 'float op'
+if not 12.0 < 24.0: raise TestFailed, 'float op'
+if not -24.0 < -12.0: raise TestFailed, 'float op'
+
+print '6.5 Sequence types'
+
+print '6.5.1 Strings'
+if len('') <> 0: raise TestFailed, 'len(\'\')'
+if len('a') <> 1: raise TestFailed, 'len(\'a\')'
+if len('abcdef') <> 6: raise TestFailed, 'len(\'abcdef\')'
+if 'xyz' + 'abcde' <> 'xyzabcde': raise TestFailed, 'string concatenation'
+if 'xyz'*3 <> 'xyzxyzxyz': raise TestFailed, 'string repetition *3'
+if 0*'abcde' <> '': raise TestFailed, 'string repetition 0*'
+if min('abc') <> 'a' or max('abc') <> 'c': raise TestFailed, 'min/max string'
+if 'a' in 'abc' and 'b' in 'abc' and 'c' in 'abc' and 'd' not in 'abc': pass
+else: raise TestFailed, 'in/not in string'
+x = 'x'*103
+if '%s!'%x != x+'!': raise TestFailed, 'nasty string formatting bug'
+
+print '6.5.2 Tuples'
+if len(()) <> 0: raise TestFailed, 'len(())'
+if len((1,)) <> 1: raise TestFailed, 'len((1,))'
+if len((1,2,3,4,5,6)) <> 6: raise TestFailed, 'len((1,2,3,4,5,6))'
+if (1,2)+(3,4) <> (1,2,3,4): raise TestFailed, 'tuple concatenation'
+if (1,2)*3 <> (1,2,1,2,1,2): raise TestFailed, 'tuple repetition *3'
+if 0*(1,2,3) <> (): raise TestFailed, 'tuple repetition 0*'
+if min((1,2)) <> 1 or max((1,2)) <> 2: raise TestFailed, 'min/max tuple'
+if 0 in (0,1,2) and 1 in (0,1,2) and 2 in (0,1,2) and 3 not in (0,1,2): pass
+else: raise TestFailed, 'in/not in tuple'
+
+print '6.5.3 Lists'
+if len([]) <> 0: raise TestFailed, 'len([])'
+if len([1,]) <> 1: raise TestFailed, 'len([1,])'
+if len([1,2,3,4,5,6]) <> 6: raise TestFailed, 'len([1,2,3,4,5,6])'
+if [1,2]+[3,4] <> [1,2,3,4]: raise TestFailed, 'list concatenation'
+if [1,2]*3 <> [1,2,1,2,1,2]: raise TestFailed, 'list repetition *3'
+if 0*[1,2,3] <> []: raise TestFailed, 'list repetition 0*'
+if min([1,2]) <> 1 or max([1,2]) <> 2: raise TestFailed, 'min/max list'
+if 0 in [0,1,2] and 1 in [0,1,2] and 2 in [0,1,2] and 3 not in [0,1,2]: pass
+else: raise TestFailed, 'in/not in list'
+
+print '6.5.3a Additional list operations'
+a = [0,1,2,3,4]
+a[0] = 5
+a[1] = 6
+a[2] = 7
+if a <> [5,6,7,3,4]: raise TestFailed, 'list item assignment [0], [1], [2]'
+a[-2] = 8
+a[-1] = 9
+if a <> [5,6,7,8,9]: raise TestFailed, 'list item assignment [-2], [-1]'
+a[:2] = [0,4]
+a[-3:] = []
+a[1:1] = [1,2,3]
+if a <> [0,1,2,3,4]: raise TestFailed, 'list slice assignment'
+del a[1:4]
+if a <> [0,4]: raise TestFailed, 'list slice deletion'
+del a[0]
+if a <> [4]: raise TestFailed, 'list item deletion [0]'
+del a[-1]
+if a <> []: raise TestFailed, 'list item deletion [-1]'
+a.append(0)
+a.append(1)
+a.append(2)
+if a <> [0,1,2]: raise TestFailed, 'list append'
+a.insert(0, -2)
+a.insert(1, -1)
+a.insert(2,0)
+if a <> [-2,-1,0,0,1,2]: raise TestFailed, 'list insert'
+if a.count(0) <> 2: raise TestFailed, ' list count'
+if a.index(0) <> 2: raise TestFailed, 'list index'
+a.remove(0)
+if a <> [-2,-1,0,1,2]: raise TestFailed, 'list remove'
+a.reverse()
+if a <> [2,1,0,-1,-2]: raise TestFailed, 'list reverse'
+a.sort()
+if a <> [-2,-1,0,1,2]: raise TestFailed, 'list sort'
+def revcmp(a, b): return cmp(b, a)
+a.sort(revcmp)
+if a <> [2,1,0,-1,-2]: raise TestFailed, 'list sort with cmp func'
+
+print '6.6 Mappings == Dictionaries'
+d = {}
+if d.keys() <> []: raise TestFailed, '{}.keys()'
+if d.has_key('a') <> 0: raise TestFailed, '{}.has_key(\'a\')'
+if len(d) <> 0: raise TestFailed, 'len({})'
+d = {'a': 1, 'b': 2}
+if len(d) <> 2: raise TestFailed, 'len(dict)'
+k = d.keys()
+k.sort()
+if k <> ['a', 'b']: raise TestFailed, 'dict keys()'
+if d.has_key('a') and d.has_key('b') and not d.has_key('c'): pass
+else: raise TestFailed, 'dict keys()'
+if d['a'] <> 1 or d['b'] <> 2: raise TestFailed, 'dict item'
+d['c'] = 3
+d['a'] = 4
+if d['c'] <> 3 or d['a'] <> 4: raise TestFailed, 'dict item assignment'
+del d['b']
+if d <> {'a': 4, 'c': 3}: raise TestFailed, 'dict item deletion'
diff --git a/Lib/dos_8x3/tracebac.py b/Lib/dos_8x3/tracebac.py
new file mode 100755 (executable)
index 0000000..8d2dfdc
--- /dev/null
@@ -0,0 +1,125 @@
+# Format and print Python stack traces
+
+import linecache
+import string
+import sys
+import types
+
+def _print(file, str='', terminator='\n'):
+       file.write(str+terminator)
+       
+
+def print_tb(tb, limit=None, file=None):
+       if not file:
+               file = sys.stderr
+       if limit is None:
+               if hasattr(sys, 'tracebacklimit'):
+                       limit = sys.tracebacklimit
+       n = 0
+       while tb is not None and (limit is None or n < limit):
+               f = tb.tb_frame
+               lineno = tb.tb_lineno
+               co = f.f_code
+               filename = co.co_filename
+               name = co.co_name
+               _print(file,
+                      '  File "%s", line %d, in %s' % (filename,lineno,name))
+               line = linecache.getline(filename, lineno)
+               if line: _print(file, '    ' + string.strip(line))
+               tb = tb.tb_next
+               n = n+1
+
+def format_tb(tb, limit = None):
+       list = []
+       for filename, lineno, name, line in extract_tb(tb, limit):
+               item = '  File "%s", line %d, in %s\n' % (filename,lineno,name)
+               if line:
+                       item = item + '    %s\n' % string.strip(line)
+               list.append(item)
+       return list
+
+def extract_tb(tb, limit = None):
+       if limit is None:
+               if hasattr(sys, 'tracebacklimit'):
+                       limit = sys.tracebacklimit
+       list = []
+       n = 0
+       while tb is not None and (limit is None or n < limit):
+               f = tb.tb_frame
+               lineno = tb.tb_lineno
+               co = f.f_code
+               filename = co.co_filename
+               name = co.co_name
+               line = linecache.getline(filename, lineno)
+               if line: line = string.strip(line)
+               else: line = None
+               list.append(filename, lineno, name, line)
+               tb = tb.tb_next
+               n = n+1
+       return list
+
+
+def print_exception(etype, value, tb, limit=None, file=None):
+       if not file:
+               file = sys.stderr
+       if tb:
+               _print(file, 'Traceback (innermost last):')
+               print_tb(tb, limit, file)
+       lines = format_exception_only(etype, value)
+       for line in lines[:-1]:
+               _print(file, line, ' ')
+       _print(file, lines[-1], '')
+
+def format_exception(etype, value, tb, limit = None):
+       if tb:
+               list = ['Traceback (innermost last):\n']
+               list = list + format_tb(tb, limit)
+       list = list + format_exception_only(etype, value)
+       return list
+
+def format_exception_only(etype, value):
+       list = []
+       if type(etype) == types.ClassType:
+               stype = etype.__name__
+       else:
+               stype = etype
+       if value is None:
+               list.append(str(stype) + '\n')
+       else:
+               if etype is SyntaxError:
+                       try:
+                               msg, (filename, lineno, offset, line) = value
+                       except:
+                               pass
+                       else:
+                               if not filename: filename = "<string>"
+                               list.append('  File "%s", line %d\n' %
+                                           (filename, lineno))
+                               i = 0
+                               while i < len(line) and \
+                                     line[i] in string.whitespace:
+                                       i = i+1
+                               list.append('    %s\n' % string.strip(line))
+                               s = '    '
+                               for c in line[i:offset-1]:
+                                       if c in string.whitespace:
+                                               s = s + c
+                                       else:
+                                               s = s + ' '
+                               list.append('%s^\n' % s)
+                               value = msg
+               list.append('%s: %s\n' % (str(stype), str(value)))
+       return list
+
+
+def print_exc(limit=None, file=None):
+       if not file:
+               file = sys.stderr
+       print_exception(sys.exc_type, sys.exc_value, sys.exc_traceback,
+                       limit, file)
+
+def print_last(limit=None, file=None):
+       if not file:
+               file = sys.stderr
+       print_exception(sys.last_type, sys.last_value, sys.last_traceback,
+                       limit, file)
diff --git a/Lib/dos_8x3/userdict.py b/Lib/dos_8x3/userdict.py
new file mode 100755 (executable)
index 0000000..f6b2f82
--- /dev/null
@@ -0,0 +1,18 @@
+# A more or less complete user-defined wrapper around dictionary objects
+
+class UserDict:
+       def __init__(self): self.data = {}
+       def __repr__(self): return repr(self.data)
+       def __cmp__(self, dict):
+               if type(dict) == type(self.data):
+                       return cmp(self.data, dict)
+               else:
+                       return cmp(self.data, dict.data)
+       def __len__(self): return len(self.data)
+       def __getitem__(self, key): return self.data[key]
+       def __setitem__(self, key, item): self.data[key] = item
+       def __delitem__(self, key): del self.data[key]
+       def keys(self): return self.data.keys()
+       def items(self): return self.data.items()
+       def values(self): return self.data.values()
+       def has_key(self, key): return self.data.has_key(key)
diff --git a/Lib/dos_8x3/userlist.py b/Lib/dos_8x3/userlist.py
new file mode 100755 (executable)
index 0000000..1f19ad9
--- /dev/null
@@ -0,0 +1,50 @@
+# A more or less complete user-defined wrapper around list objects
+
+class UserList:
+       def __init__(self, list = None):
+               self.data = []
+               if list is not None:
+                       if type(list) == type(self.data):
+                               self.data[:] = list
+                       else:
+                               self.data[:] = list.data[:]
+       def __repr__(self): return repr(self.data)
+       def __cmp__(self, list):
+               if type(list) == type(self.data):
+                       return cmp(self.data, list)
+               else:
+                       return cmp(self.data, list.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
+       def __delitem__(self, i): del self.data[i]
+       def __getslice__(self, i, j):
+               userlist = UserList()
+               userlist.data[:] = self.data[i:j]
+               return userlist
+       def __setslice__(self, i, j, list):
+               if type(list) == type(self.data):
+                       self.data[i:j] = list
+               else:
+                       self.data[i:j] = list.data
+       def __delslice__(self, i, j): del self.data[i:j]
+       def __add__(self, list):
+               if type(list) == type(self.data):
+                       return self.__class__(self.data + list)
+               else:
+                       return self.__class__(self.data + list.data)
+       def __radd__(self, list):
+               if type(list) == type(self.data):
+                       return self.__class__(list + self.data)
+               else:
+                       return self.__class__(list.data + self.data)
+       def __mul__(self, n):
+               return self.__class__(self.data*n)
+       __rmul__ = __mul__
+       def append(self, item): self.data.append(item)
+       def insert(self, i, item): self.data.insert(i, item)
+       def remove(self, item): self.data.remove(item)
+       def count(self, item): return self.data.count(item)
+       def index(self, item): return self.data.index(item)
+       def reverse(self): self.data.reverse()
+       def sort(self, *args): apply(self.data.sort, args)
diff --git a/Lib/dos_8x3/whatsoun.py b/Lib/dos_8x3/whatsoun.py
new file mode 100755 (executable)
index 0000000..b7b349c
--- /dev/null
@@ -0,0 +1,270 @@
+# This module contains several routines that help recognizing sound
+# files.
+#
+# Function whathdr() recognizes various types of sound file headers.
+# It understands almost all headers that SOX can decode.
+#
+# The return tuple contains the following items, in this order:
+# - file type (as SOX understands it)
+# - sampling rate (0 if unknown or hard to decode)
+# - number of channels (0 if unknown or hard to decode)
+# - number of frames in the file (-1 if unknown or hard to decode)
+# - number of bits/sample, or 'U' for U-LAW, or 'A' for A-LAW
+#
+# If the file doesn't have a recognizable type, it returns None.
+# If the file can't be opened, IOError is raised.
+#
+# To compute the total time, divide the number of frames by the
+# sampling rate (a frame contains a sample for each channel).
+#
+# Function whatraw() calls the "whatsound" program and interprets its
+# output.  You'll have to guess the sampling rate by listening though!
+#
+# Function what() calls whathdr() and if it doesn't recognize the file
+# then calls whatraw().
+#
+# Finally, the function test() is a simple main program that calls
+# what() for all files mentioned on the argument list.  For directory
+# arguments it calls what() for all files in that directory.  Default
+# argument is "." (testing all files in the current directory).  The
+# option -r tells it to recurse down directories found inside
+# explicitly given directories.
+#
+# The file structure is top-down except that the test program and its
+# subroutine come last.
+
+
+#------------------------------------------------------#
+# Guess the type of any sound file, raw or with header #
+#------------------------------------------------------#
+
+def what(filename):
+       res = whathdr(filename)
+       if not res:
+               res = whatraw(filename)
+       return res
+
+
+#-----------------------------#
+# Guess the type of raw sound #
+#-----------------------------#
+
+def whatraw(filename):
+       # Assume it's always 1 channel, byte-sized samples
+       # Don't assume anything about the rate
+       import os
+       from stat import ST_SIZE
+       # XXX "whatsound" should be part of the distribution somehow...
+       cmd = 'whatsound ' + filename + ' 2>/dev/null'
+       cmd = 'PATH=$PATH:/ufs/guido/bin/sgi\n' + cmd
+       pipe = os.popen(cmd, 'r')
+       data = pipe.read()
+       sts = pipe.close()
+       if sts:
+               return None
+       if data[:13] == '-t raw -b -s ':
+               type = 'sb'
+               sample_size = 8
+       elif data[:13] == '-t raw -b -u ':
+               type = 'ub'
+               sample_size = 8
+       elif data[:13] == '-t raw -b -U ':
+               type = 'ul'
+               sample_size = 'U'
+       else:
+               return None
+       try:
+               frame_count = os.stat(filename)[ST_SIZE]
+       except IOError:
+               frame_count = -1
+       return type, 0, 1, frame_count, sample_size
+
+
+#-------------------------#
+# Recognize sound headers #
+#-------------------------#
+
+def whathdr(filename):
+       f = open(filename, 'r')
+       h = f.read(512)
+       for tf in tests:
+               res = tf(h, f)
+               if res:
+                       return res
+       return None
+
+
+#-----------------------------------#
+# Subroutines per sound header type #
+#-----------------------------------#
+
+tests = []
+
+def test_aifc(h, f):
+       import aifc
+       if h[:4] <> 'FORM':
+               return None
+       if h[8:12] == 'AIFC':
+               fmt = 'aifc'
+       elif h[8:12] == 'AIFF':
+               fmt = 'aiff'
+       else:
+               return None
+       f.seek(0)
+       try:
+               a = aifc.openfp(f, 'r')
+       except (EOFError, aifc.Error):
+               return None
+       return (fmt, a.getframerate(), a.getnchannels(), \
+               a.getnframes(), 8*a.getsampwidth())
+
+tests.append(test_aifc)
+
+
+def test_au(h, f):
+       if h[:4] == '.snd':
+               f = get_long_be
+       elif h[:4] in ('\0ds.', 'dns.'):
+               f = get_long_le
+       else:
+               return None
+       type = 'au'
+       hdr_size = f(h[4:8])
+       data_size = f(h[8:12])
+       encoding = f(h[12:16])
+       rate = f(h[16:20])
+       nchannels = f(h[20:24])
+       sample_size = 1 # default
+       if encoding == 1:
+               sample_bits = 'U'
+       elif encoding == 2:
+               sample_bits = 8
+       elif encoding == 3:
+               sample_bits = 16
+               sample_size = 2
+       else:
+               sample_bits = '?'
+       frame_size = sample_size * nchannels
+       return type, rate, nchannels, data_size/frame_size, sample_bits
+
+tests.append(test_au)
+
+
+def test_hcom(h, f):
+       if h[65:69] <> 'FSSD' or h[128:132] <> 'HCOM':
+               return None
+       divisor = get_long_be(h[128+16:128+20])
+       return 'hcom', 22050/divisor, 1, -1, 8
+
+tests.append(test_hcom)
+
+
+def test_voc(h, f):
+       if h[:20] <> 'Creative Voice File\032':
+               return None
+       sbseek = get_short_le(h[20:22])
+       rate = 0
+       if 0 <= sbseek < 500 and h[sbseek] == '\1':
+               ratecode = ord(h[sbseek+4])
+               rate = int(1000000.0 / (256 - ratecode))
+       return 'voc', rate, 1, -1, 8
+
+tests.append(test_voc)
+
+
+def test_wav(h, f):
+       # 'RIFF' <len> 'WAVE' 'fmt ' <len>
+       if h[:4] <> 'RIFF' or h[8:12] <> 'WAVE' or h[12:16] <> 'fmt ':
+               return None
+       style = get_short_le(h[20:22])
+       nchannels = get_short_le(h[22:24])
+       rate = get_long_le(h[24:28])
+       sample_bits = get_short_le(h[34:36])
+       return 'wav', rate, nchannels, -1, sample_bits
+
+tests.append(test_wav)
+
+
+def test_8svx(h, f):
+       if h[:4] <> 'FORM' or h[8:12] <> '8SVX':
+               return None
+       # Should decode it to get #channels -- assume always 1
+       return '8svx', 0, 1, 0, 8
+
+tests.append(test_8svx)
+
+
+def test_sndt(h, f):
+       if h[:5] == 'SOUND':
+               nsamples = get_long_le(h[8:12])
+               rate = get_short_le(h[20:22])
+               return 'sndt', rate, 1, nsamples, 8
+
+tests.append(test_sndt)
+
+
+def test_sndr(h, f):
+       if h[:2] == '\0\0':
+               rate = get_short_le(h[2:4])
+               if 4000 <= rate <= 25000:
+                       return 'sndr', rate, 1, -1, 8
+
+tests.append(test_sndr)
+
+
+#---------------------------------------------#
+# Subroutines to extract numbers from strings #
+#---------------------------------------------#
+
+def get_long_be(s):
+       return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3])
+
+def get_long_le(s):
+       return (ord(s[3])<<24) | (ord(s[2])<<16) | (ord(s[1])<<8) | ord(s[0])
+
+def get_short_be(s):
+       return (ord(s[0])<<8) | ord(s[1])
+
+def get_short_le(s):
+       return (ord(s[1])<<8) | ord(s[0])
+
+
+#--------------------#
+# Small test program #
+#--------------------#
+
+def test():
+       import sys
+       recursive = 0
+       if sys.argv[1:] and sys.argv[1] == '-r':
+               del sys.argv[1:2]
+               recursive = 1
+       try:
+               if sys.argv[1:]:
+                       testall(sys.argv[1:], recursive, 1)
+               else:
+                       testall(['.'], recursive, 1)
+       except KeyboardInterrupt:
+               sys.stderr.write('\n[Interrupted]\n')
+               sys.exit(1)
+
+def testall(list, recursive, toplevel):
+       import sys
+       import os
+       for filename in list:
+               if os.path.isdir(filename):
+                       print filename + '/:',
+                       if recursive or toplevel:
+                               print 'recursing down:'
+                               import glob
+                               names = glob.glob(os.path.join(filename, '*'))
+                               testall(names, recursive, 0)
+                       else:
+                               print '*** directory (use -r) ***'
+               else:
+                       print filename + ':',
+                       sys.stdout.flush()
+                       try:
+                               print what(filename)
+                       except IOError:
+                               print '*** not found ***'