Merge alpha100 branch back to main trunk
diff --git a/Lib/UserList.py b/Lib/UserList.py
index 9fbcc02..1c8a622 100644
--- a/Lib/UserList.py
+++ b/Lib/UserList.py
@@ -1,11 +1,9 @@
 # A more or less complete user-defined wrapper around list objects
 
 class UserList:
-	def __init__(self, *args):
-		if len(args) > 1: raise TypeError, 'too many args'
+	def __init__(self, list = None):
 		self.data = []
-		if args:
-			list = args[0]
+		if list is not None:
 			if type(list) == type(self.data):
 				self.data[:] = list
 			else:
diff --git a/Lib/aifc.py b/Lib/aifc.py
index aa1e56c..c1418be 100644
--- a/Lib/aifc.py
+++ b/Lib/aifc.py
@@ -179,7 +179,10 @@
 
 def _read_string(file):
 	length = ord(file.read(1))
-	data = file.read(length)
+	if length == 0:
+		data = ''
+	else:
+		data = file.read(length)
 	if length & 1 == 0:
 		dummy = file.read(1)
 	return data
diff --git a/Lib/audiodev.py b/Lib/audiodev.py
index d0dcfed..02d63f7 100644
--- a/Lib/audiodev.py
+++ b/Lib/audiodev.py
@@ -1,28 +1,40 @@
-import AL, SUNAUDIODEV
-
 error = 'audiodev.error'
 
 class Play_Audio_sgi:
 	# Private instance variables
 	access frameratelist, nchannelslist, sampwidthlist, oldparams, \
 		  params, config, inited_outrate, inited_width, \
-		  inited_nchannels, port, converter: private
+		  inited_nchannels, port, converter, classinited: private
 
-	frameratelist = [(48000, AL.RATE_48000),
-			 (44100, AL.RATE_44100),
-			 (32000, AL.RATE_32000),
-			 (22050, AL.RATE_22050),
-			 (16000, AL.RATE_16000),
-			 (11025, AL.RATE_11025),
-			 ( 8000,  AL.RATE_8000)]
-	nchannelslist = [(1, AL.MONO),
-			 (2, AL.STEREO)]
-	sampwidthlist = [(1, AL.SAMPLE_8),
-			 (2, AL.SAMPLE_16),
-			 (3, AL.SAMPLE_24)]
+	classinited = 0
+	frameratelist = nchannelslist = sampwidthlist = None
+
+	def initclass(self):
+		import AL
+		Play_Audio_sgi.frameratelist = [
+			  (48000, AL.RATE_48000),
+			  (44100, AL.RATE_44100),
+			  (32000, AL.RATE_32000),
+			  (22050, AL.RATE_22050),
+			  (16000, AL.RATE_16000),
+			  (11025, AL.RATE_11025),
+			  ( 8000,  AL.RATE_8000),
+			  ]
+		Play_Audio_sgi.nchannelslist = [
+			  (1, AL.MONO),
+			  (2, AL.STEREO),
+			  ]
+		Play_Audio_sgi.sampwidthlist = [
+			  (1, AL.SAMPLE_8),
+			  (2, AL.SAMPLE_16),
+			  (3, AL.SAMPLE_24),
+			  ]
+		Play_Audio_sgi.classinited = 1
 
 	def __init__(self):
-		import al
+		import al, AL
+		if not self.classinited:
+			self.initclass()
 		self.oldparams = []
 		self.params = [AL.OUTPUT_RATE, 0]
 		self.config = al.newconfig()
@@ -37,7 +49,7 @@
 		if self.port:
 			self.stop()
 		if self.oldparams:
-			import al
+			import al, AL
 			al.setparams(AL.DEFAULT_DEVICE, self.oldparams)
 			self.oldparams = []
 
@@ -54,7 +66,7 @@
 			self.port.closeport()
 			self.port = None
 		if self.oldparams:
-			import al
+			import al, AL
 			al.setparams(AL.DEFAULT_DEVICE, self.oldparams)
 			self.oldparams = []
 
@@ -75,6 +87,7 @@
 				break
 		else:
 			if width == 0:
+				import AL
 				self.inited_width = 0
 				self.config.setwidth(AL.SAMPLE_16)
 				self.converter = self.ulaw2lin
@@ -94,7 +107,7 @@
 		if not (self.inited_outrate and self.inited_nchannels):
 			raise error, 'params not specified'
 		if not self.port:
-			import al
+			import al, AL
 			self.port = al.openport('Python', 'w', self.config)
 			self.oldparams = self.params[:]
 			al.getparams(AL.DEFAULT_DEVICE, self.oldparams)
@@ -156,7 +169,7 @@
 		if not (self.inited_outrate and self.inited_width and self.inited_nchannels):
 			raise error, 'params not specified'
 		if not self.port:
-			import sunaudiodev
+			import sunaudiodev, SUNAUDIODEV
 			self.port = sunaudiodev.open('w')
 			info = self.port.getinfo()
 			info.o_sample_rate = self.outrate
diff --git a/Lib/bdb.py b/Lib/bdb.py
index 6b3eab9..9b50767 100644
--- a/Lib/bdb.py
+++ b/Lib/bdb.py
@@ -17,6 +17,8 @@
 		self.breaks = {}
 	
 	def reset(self):
+		import linecache
+		linecache.checkcache()
 		self.botframe = None
 		self.stopframe = None
 		self.returnframe = None
@@ -134,11 +136,35 @@
 		self.returnframe = frame
 		self.quitting = 0
 	
+	def set_trace(self):
+		# Start debugging from here
+		try:
+			1 + ''
+		except:
+			frame = sys.exc_traceback.tb_frame.f_back
+		self.reset()
+		while frame:
+			frame.f_trace = self.trace_dispatch
+			self.botframe = frame
+			frame = frame.f_back
+		self.set_step()
+		sys.settrace(self.trace_dispatch)
+
 	def set_continue(self):
 		# Don't stop except at breakpoints or when finished
 		self.stopframe = self.botframe
 		self.returnframe = None
 		self.quitting = 0
+		if not self.breaks:
+			# no breakpoints; run without debugger overhead
+			sys.settrace(None)
+			try:
+				1 + ''	# raise an exception
+			except:
+				frame = sys.exc_traceback.tb_frame.f_back
+			while frame and frame is not self.botframe:
+				del frame.f_trace
+				frame = frame.f_back
 	
 	def set_quit(self):
 		self.stopframe = self.botframe
@@ -177,7 +203,7 @@
 			return 'There are no breakpoints in that file!'
 		del self.breaks[filename]
 	
-	def clear_all_breaks(self, filename, lineno):
+	def clear_all_breaks(self):
 		if not self.breaks:
 			return 'There are no breakpoints!'
 		self.breaks = {}
@@ -217,11 +243,14 @@
 	# 
 	
 	def format_stack_entry(self, frame_lineno):
-		import codehack, linecache, repr, string
+		import linecache, repr, string
 		frame, lineno = frame_lineno
 		filename = frame.f_code.co_filename
 		s = filename + '(' + `lineno` + ')'
-		s = s + codehack.getcodename(frame.f_code)
+		if frame.f_code.co_name:
+		    s = s + frame.f_code.co_name
+		else:
+		    s = s + "<lambda>"
 		if frame.f_locals.has_key('__args__'):
 			args = frame.f_locals['__args__']
 			if args is not None:
@@ -269,17 +298,19 @@
 			sys.settrace(None)
 
 
+def set_trace():
+	Bdb().set_trace()
+
 # -------------------- testing --------------------
 
 class Tdb(Bdb):
 	def user_call(self, frame, args):
-		import codehack
-		name = codehack.getcodename(frame.f_code)
+		name = frame.f_code.co_name
 		if not name: name = '???'
 		print '+++ call', name, args
 	def user_line(self, frame):
-		import linecache, string, codehack
-		name = codehack.getcodename(frame.f_code)
+		import linecache, string
+		name = frame.f_code.co_name
 		if not name: name = '???'
 		fn = frame.f_code.co_filename
 		line = linecache.getline(fn, frame.f_lineno)
@@ -300,7 +331,5 @@
 	return a/2
 
 def test():
-	import linecache
-	linecache.checkcache()
 	t = Tdb()
 	t.run('import bdb; bdb.foo(10)')
diff --git a/Lib/calendar.py b/Lib/calendar.py
index 4dfcf0c..e9b1644 100644
--- a/Lib/calendar.py
+++ b/Lib/calendar.py
@@ -106,12 +106,7 @@
 	return str
 
 # Print a month's calendar
-def prmonth(year, month, *rest):
-	if rest[2:]: raise TypeError, 'too many args'
-	w = 0
-	l = 0
-	if rest[0:]: w = rest[0]
-	if rest[1:]: l = rest[1]
+def prmonth(year, month, w = 0, l = 0):
 	w = max(2, w)
 	l = max(1, l)
 	print _center(month_name[month] + ' ' + `year`, 7*(w+1) - 1),
diff --git a/Lib/cmd.py b/Lib/cmd.py
index 87ddcfa..85115bb 100644
--- a/Lib/cmd.py
+++ b/Lib/cmd.py
@@ -56,8 +56,36 @@
 		else:
 			import newdir
 			names = newdir.dir(self.__class__)
-			cmds = []
+			cmds_doc = []
+			cmds_undoc = []
+			help = {}
+			for name in names:
+				if name[:5] == 'help_':
+					help[name[5:]]=1
 			for name in names:
 				if name[:3] == 'do_':
-					cmds.append(name[3:])
-			print cmds
+					cmd=name[3:]
+					if help.has_key(cmd):
+						cmds_doc.append(cmd)
+						del help[cmd]
+					else:
+						cmds_undoc.append(cmd)
+			print 
+			self.print_topics("Documented commands (type help " \
+		                          "<topic>):",cmds_doc, 15, 80)
+			self.print_topics("Miscellaneous help topics:",
+		                          help.keys(), 15, 80)
+			self.print_topics("Undocumented commands:", 
+		                          cmds_undoc, 15, 80)
+
+	def print_topics(self, header, cmds, cmdlen, maxcol):
+		if cmds:
+			print header;
+			print "="*len(header)
+			(cmds_per_line,junk)=divmod(maxcol,cmdlen)
+			col=cmds_per_line
+			for cmd in cmds:
+				if col==0: print
+				print (("%-"+`cmdlen`+"s") % cmd),
+				col = (col+1) % cmds_per_line
+			print "\n"
diff --git a/Lib/codehack.py b/Lib/codehack.py
index d00d2bf..1f16814 100644
--- a/Lib/codehack.py
+++ b/Lib/codehack.py
@@ -7,6 +7,10 @@
 import os
 import linecache
 
+# XXX The functions getcodename() and getfuncname() are now obsolete
+# XXX as code and function objects now have a name attribute --
+# XXX co.co_name and f.func_name.
+
 # Extract the function or class name from a code object.
 # This is a bit of a hack, since a code object doesn't contain
 # the name directly.  So what do we do:
diff --git a/Lib/dis.py b/Lib/dis.py
index cd62500..846dc32 100644
--- a/Lib/dis.py
+++ b/Lib/dis.py
@@ -135,7 +135,8 @@
 def_op('RAISE_EXCEPTION', 81)
 def_op('LOAD_LOCALS', 82)
 def_op('RETURN_VALUE', 83)
-
+def_op('LOAD_GLOBALS', 84)
+def_op('EXEC_STMT', 85)
 def_op('BUILD_FUNCTION', 86)
 def_op('POP_BLOCK', 87)
 def_op('END_FINALLY', 88)
@@ -173,6 +174,7 @@
 
 name_op('LOAD_LOCAL', 115)	# Index in name list
 name_op('LOAD_GLOBAL', 116)	# Index in name list
+def_op('SET_FUNC_ARGS', 117)	# Argcount
 
 jrel_op('SETUP_LOOP', 120)	# Distance to target address
 jrel_op('SETUP_EXCEPT', 121)	# ""
diff --git a/Lib/ftplib.py b/Lib/ftplib.py
index c441f1f..d160303 100644
--- a/Lib/ftplib.py
+++ b/Lib/ftplib.py
@@ -9,10 +9,7 @@
 # >>> from ftplib import FTP
 # >>> ftp = FTP('ftp.cwi.nl') # connect to host, default port
 # >>> ftp.login() # default, i.e.: user anonymous, passwd user@hostname
-# >>> def handle_one_line(line): # callback for ftp.retrlines
-# ...     print line
-# ... 
-# >>> ftp.retrlines('LIST', handle_one_line) # list directory contents
+# >>> ftp.retrlines('LIST') # list directory contents
 # total 43
 # d--x--x--x   2 root     root         512 Jul  1 16:50 bin
 # d--x--x--x   2 root     root         512 Sep 16  1991 etc
@@ -20,7 +17,7 @@
 # drwxr-srwt  15 root     ftp        10240 Nov  5 20:43 pub
 # >>> ftp.quit()
 #
-# To download a file, use ftp.retrlines('RETR ' + filename, handle_one_line),
+# To download a file, use ftp.retrlines('RETR ' + filename),
 # or ftp.retrbinary() with slightly different arguments.
 # To upload a file, use ftp.storlines() or ftp.storbinary(), which have
 # an open file as argument.
@@ -30,9 +27,14 @@
 
 import os
 import sys
-import socket
 import string
 
+# Import SOCKS module if it exists, else standard socket module socket
+try:
+    import SOCKS; socket = SOCKS
+except ImportError:
+    import socket
+
 
 # Magic number from <socket.h>
 MSG_OOB = 0x1				# Process data out of band
@@ -59,14 +61,6 @@
 CRLF = '\r\n'
 
 
-# Next port to be used by makeport(), with PORT_OFFSET added
-# (This is now only used when the python interpreter doesn't support
-# the getsockname() method yet)
-nextport = 0
-PORT_OFFSET = 40000
-PORT_CYCLE = 1000
-
-
 # The class itself
 class FTP:
 
@@ -74,7 +68,7 @@
 	# Initialize host to localhost, port to standard ftp port
 	# Optional arguments are host (for connect()),
 	# and user, passwd, acct (for login())
-	def __init__(self, *args):
+	def __init__(self, host = '', user = '', passwd = '', acct = ''):
 		# Initialize the instance to something mostly harmless
 		self.debugging = 0
 		self.host = ''
@@ -82,18 +76,16 @@
 		self.sock = None
 		self.file = None
 		self.welcome = None
-		if args:
-			self.connect(args[0])
-			if args[1:]:
-				apply(self.login, args[1:])
+		if host:
+			self.connect(host)
+			if user: self.login(user, passwd, acct)
 
 	# Connect to host.  Arguments:
 	# - host: hostname to connect to (default previous host)
 	# - port: port to connect to (default previous port)
-	def connect(self, *args):
-		if args: self.host = args[0]
-		if args[1:]: self.port = args[1]
-		if args[2:]: raise TypeError, 'too many args'
+	def connect(self, host = '', port = 0):
+		if host: self.host = host
+		if port: self.port = port
 		self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 		self.sock.connect(self.host, self.port)
 		self.file = self.sock.makefile('r')
@@ -208,19 +200,8 @@
 	def makeport(self):
 		global nextport
 		sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-		try:
-			getsockname = sock.getsockname
-		except AttributeError:
-			if self.debugging > 1:
-				print '*** getsockname not supported',
-				print '-- using manual port assignment ***'
-			port = nextport + PORT_OFFSET
-			nextport = (nextport + 1) % PORT_CYCLE
-			sock.bind('', port)
-			getsockname = None
-		sock.listen(0) # Assigns the port if not explicitly bound
-		if getsockname:
-			host, port = getsockname()
+		sock.listen(1)
+		host, port = sock.getsockname()
 		resp = self.sendport(port)
 		return sock
 
@@ -235,13 +216,7 @@
 		return conn
 
 	# Login, default anonymous
-	def login(self, *args):
-		user = passwd = acct = ''
-		n = len(args)
-		if n > 3: raise TypeError, 'too many arguments'
-		if n > 0: user = args[0]
-		if n > 1: passwd = args[1]
-		if n > 2: acct = args[2]
+	def login(self, user = '', passwd = '', acct = ''):
 		if not user: user = 'anonymous'
 		if user == 'anonymous' and passwd in ('', '-'):
 			thishost = socket.gethostname()
@@ -278,11 +253,7 @@
 	# The callback function is called for each line, with trailing
 	# CRLF stripped.  This creates a new port for you.
 	# print_lines is the default callback 
-	def retrlines(self, cmd, *args):
-		callback = None
-		if args:
-			callback = args[0]
-			if args[1:]: raise TypeError, 'too many args'
+	def retrlines(self, cmd, callback = None):
 		if not callback: callback = print_line
 		resp = self.sendcmd('TYPE A')
 		conn = self.transfercmd(cmd)
@@ -423,30 +394,22 @@
 # Usage: ftp [-d] host [-l[dir]] [-d[dir]] [file] ...
 def test():
 	import marshal
-	global nextport
-	try:
-		nextport = marshal.load(open('.@nextport', 'r'))
-	except IOError:
-		pass
-	try:
-		debugging = 0
-		while sys.argv[1] == '-d':
-			debugging = debugging+1
-			del sys.argv[1]
-		host = sys.argv[1]
-		ftp = FTP(host)
-		ftp.set_debuglevel(debugging)
-		ftp.login()
-		for file in sys.argv[2:]:
-			if file[:2] == '-l':
-				ftp.dir(file[2:])
-			elif file[:2] == '-d':
-				cmd = 'CWD'
-				if file[2:]: cmd = cmd + ' ' + file[2:]
-				resp = ftp.sendcmd(cmd)
-			else:
-				ftp.retrbinary('RETR ' + file, \
-					       sys.stdout.write, 1024)
-		ftp.quit()
-	finally:
-		marshal.dump(nextport, open('.@nextport', 'w'))
+	debugging = 0
+	while sys.argv[1] == '-d':
+		debugging = debugging+1
+		del sys.argv[1]
+	host = sys.argv[1]
+	ftp = FTP(host)
+	ftp.set_debuglevel(debugging)
+	ftp.login()
+	for file in sys.argv[2:]:
+		if file[:2] == '-l':
+			ftp.dir(file[2:])
+		elif file[:2] == '-d':
+			cmd = 'CWD'
+			if file[2:]: cmd = cmd + ' ' + file[2:]
+			resp = ftp.sendcmd(cmd)
+		else:
+			ftp.retrbinary('RETR ' + file, \
+				       sys.stdout.write, 1024)
+	ftp.quit()
diff --git a/Lib/irix5/FCNTL.py b/Lib/irix5/FCNTL.py
index 1271b9e..12c60fa 100755
--- a/Lib/irix5/FCNTL.py
+++ b/Lib/irix5/FCNTL.py
@@ -1,35 +1,35 @@
-# These lines were generated by h2py.py (see demo/scripts)
-# from <sys/fcntl.h> on Irix 4.0.2.
-# The applicability on other systems is not clear.
-
+# Generated by h2py from /usr/include/sys/fcntl.h
 FNDELAY = 0x04
 FAPPEND = 0x08
 FSYNC = 0x10
-FRCACH = 0x20
-FASYNC = 0x40
-FNONBLK = 0x80
-FCREAT = 0x100
-FTRUNC = 0x200
-FEXCL = 0x400
-FNOCTTY = 0x800
+FNONBLOCK = 0x80
+FASYNC = 0x1000
+FNONBLK = FNONBLOCK
+FDIRECT = 0x8000
+FCREAT = 0x0100
+FTRUNC = 0x0200
+FEXCL = 0x0400
+FNOCTTY = 0x0800
 O_RDONLY = 0
 O_WRONLY = 1
 O_RDWR = 2
-O_ACCMODE = 0x3
-O_NDELAY = FNDELAY
-O_APPEND = FAPPEND
-O_SYNC = FSYNC
-O_NONBLOCK = FNONBLK
-O_CREAT = FCREAT
-O_TRUNC = FTRUNC
-O_EXCL = FEXCL
-O_NOCTTY = FNOCTTY
+O_NDELAY = 0x04
+O_APPEND = 0x08
+O_SYNC = 0x10
+O_NONBLOCK = 0x80
+O_DIRECT = 0x8000
+O_CREAT = 0x100
+O_TRUNC = 0x200
+O_EXCL = 0x400
+O_NOCTTY = 0x800
 F_DUPFD = 0
 F_GETFD = 1
 F_SETFD = 2
 F_GETFL = 3
 F_SETFL = 4
-F_GETLK = 5
+F_GETLK = 14
+F_O_GETLK = 5
+F_GETLK = 14
 F_SETLK = 6
 F_SETLKW = 7
 F_CHKFL = 8
@@ -37,13 +37,17 @@
 F_FREESP = 11
 F_SETBSDLK = 12
 F_SETBSDLKW = 13
-F_RGETLK = 20
-F_RSETLK = 21
+F_DIOINFO = 30
+F_RSETLK = 20
+F_RGETLK = 21
 F_RSETLKW = 22
-F_GETOWN = 10
-F_SETOWN = 11
+F_GETOWN = 23
+F_SETOWN = 24
+F_O_GETOWN = 10
+F_O_SETOWN = 11
 F_RDLCK = 01
 F_WRLCK = 02
 F_UNLCK = 03
-FD_CLOEXEC = 0x1
-FD_NODUP_FORK = 0x2
+O_ACCMODE = 3
+FD_CLOEXEC = 1
+FD_NODUP_FORK = 4
diff --git a/Lib/irix5/IN.py b/Lib/irix5/IN.py
index 78be3ef..325a021 100755
--- a/Lib/irix5/IN.py
+++ b/Lib/irix5/IN.py
@@ -1,8 +1,4 @@
-# Symbolic constants from <netinet/in.h>.
-# These constants are SGI specific!
-# See demo/scripts/h2py.py for a tool to help generate a version for
-# your system.
-
+# Generated by h2py from /usr/include/netinet/in.h
 IPPROTO_IP = 0
 IPPROTO_ICMP = 1
 IPPROTO_IGMP = 2
@@ -14,6 +10,8 @@
 IPPROTO_IDP = 22
 IPPROTO_TP = 29
 IPPROTO_XTP = 36
+IPPROTO_HELLO = 63
+IPPROTO_ND = 77
 IPPROTO_EON = 80
 IPPROTO_RAW = 255
 IPPROTO_MAX = 256
@@ -42,6 +40,11 @@
 INADDR_NONE = 0xffffffff
 IN_LOOPBACKNET = 127
 IP_OPTIONS = 1
+IP_MULTICAST_IF = 2
+IP_MULTICAST_TTL = 3
+IP_MULTICAST_LOOP = 4
+IP_ADD_MEMBERSHIP = 5
+IP_DROP_MEMBERSHIP = 6
 IP_HDRINCL = 7
 IP_TOS = 8
 IP_TTL = 9
@@ -49,11 +52,32 @@
 IP_RECVRETOPTS = 11
 IP_RECVDSTADDR = 12
 IP_RETOPTS = 13
-IP_MULTICAST_IF = 2
-IP_MULTICAST_TTL = 3
-IP_MULTICAST_LOOP = 4
-IP_ADD_MEMBERSHIP = 5
-IP_DROP_MEMBERSHIP = 6
+IP_OPTIONS = 1
+IP_HDRINCL = 2
+IP_TOS = 3
+IP_TTL = 4
+IP_RECVOPTS = 5
+IP_RECVRETOPTS = 6
+IP_RECVDSTADDR = 7
+IP_RETOPTS = 8
+IP_MULTICAST_IF = 20
+IP_MULTICAST_TTL = 21
+IP_MULTICAST_LOOP = 22
+IP_ADD_MEMBERSHIP = 23
+IP_DROP_MEMBERSHIP = 24
+IRIX4_IP_OPTIONS = 1
+IRIX4_IP_MULTICAST_IF = 2
+IRIX4_IP_MULTICAST_TTL = 3
+IRIX4_IP_MULTICAST_LOOP = 4
+IRIX4_IP_ADD_MEMBERSHIP = 5
+IRIX4_IP_DROP_MEMBERSHIP = 6
+IRIX4_IP_HDRINCL = 7
+IRIX4_IP_TOS = 8
+IRIX4_IP_TTL = 9
+IRIX4_IP_RECVOPTS = 10
+IRIX4_IP_RECVRETOPTS = 11
+IRIX4_IP_RECVDSTADDR = 12
+IRIX4_IP_RETOPTS = 13
 IP_DEFAULT_MULTICAST_TTL = 1
 IP_DEFAULT_MULTICAST_LOOP = 1
 IP_MAX_MEMBERSHIPS = 20
diff --git a/Lib/irix5/SOCKET.py b/Lib/irix5/SOCKET.py
index 8a15ef9..0ba0742 100755
--- a/Lib/irix5/SOCKET.py
+++ b/Lib/irix5/SOCKET.py
@@ -1,8 +1,23 @@
+# Generated by h2py from /usr/include/sys/socket.h
 SOCK_STREAM = 1
 SOCK_DGRAM = 2
 SOCK_RAW = 3
 SOCK_RDM = 4
 SOCK_SEQPACKET = 5
+NC_TPI_CLTS = 1
+NC_TPI_COTS = 2
+NC_TPI_COTS_ORD = 3
+NC_TPI_RAW = 4
+SOCK_DGRAM = NC_TPI_CLTS
+SOCK_STREAM = NC_TPI_COTS
+SOCK_RAW = NC_TPI_RAW
+SOCK_RDM = 5
+SOCK_SEQPACKET = 6
+IRIX4_SOCK_STREAM = 1
+IRIX4_SOCK_DGRAM = 2
+IRIX4_SOCK_RAW = 3
+IRIX4_SOCK_RDM = 4
+IRIX4_SOCK_SEQPACKET = 5
 SO_DEBUG = 0x0001
 SO_ACCEPTCONN = 0x0002
 SO_REUSEADDR = 0x0004
@@ -13,6 +28,9 @@
 SO_LINGER = 0x0080
 SO_OOBINLINE = 0x0100
 SO_REUSEPORT = 0x0200
+SO_ORDREL = 0x0200
+SO_IMASOCKET = 0x0400
+SO_CHAMELEON = 0x1000
 SO_SNDBUF = 0x1001
 SO_RCVBUF = 0x1002
 SO_SNDLOWAT = 0x1003
@@ -21,6 +39,7 @@
 SO_RCVTIMEO = 0x1006
 SO_ERROR = 0x1007
 SO_TYPE = 0x1008
+SO_PROTOTYPE = 0x1009
 SOL_SOCKET = 0xffff
 AF_UNSPEC = 0
 AF_UNIX = 1
@@ -30,7 +49,6 @@
 AF_CHAOS = 5
 AF_NS = 6
 AF_ISO = 7
-AF_OSI = AF_ISO
 AF_ECMA = 8
 AF_DATAKIT = 9
 AF_CCITT = 10
@@ -44,7 +62,14 @@
 AF_RAW = 18
 AF_LINK = 18
 pseudo_AF_XTP = 19
-AF_MAX = 20
+AF_NIT = 17
+AF_802 = 18
+AF_OSI = 19
+AF_X25 = 20
+AF_OSINET = 21
+AF_GOSIP = 22
+AF_SDL = 23
+AF_MAX = (AF_SDL+1)
 PF_UNSPEC = AF_UNSPEC
 PF_UNIX = AF_UNIX
 PF_INET = AF_INET
@@ -53,7 +78,6 @@
 PF_CHAOS = AF_CHAOS
 PF_NS = AF_NS
 PF_ISO = AF_ISO
-PF_OSI = AF_ISO
 PF_ECMA = AF_ECMA
 PF_DATAKIT = AF_DATAKIT
 PF_CCITT = AF_CCITT
@@ -67,11 +91,18 @@
 PF_LINK = AF_LINK
 PF_XTP = pseudo_AF_XTP
 PF_RAW = AF_RAW
+PF_NIT = AF_NIT
+PF_802 = AF_802
+PF_OSI = AF_OSI
+PF_X25 = AF_X25
+PF_OSINET = AF_OSINET
+PF_GOSIP = AF_GOSIP
 PF_MAX = AF_MAX
 SOMAXCONN = 5
 MSG_OOB = 0x1
 MSG_PEEK = 0x2
 MSG_DONTROUTE = 0x4
+MSG_EOR = 0x8
 MSG_BTAG = 0x40
 MSG_ETAG = 0x80
 MSG_MAXIOVLEN = 16
diff --git a/Lib/irix5/cddb.py b/Lib/irix5/cddb.py
index d7fdc96..0dee709 100755
--- a/Lib/irix5/cddb.py
+++ b/Lib/irix5/cddb.py
@@ -138,6 +138,19 @@
 					continue
 				self.track[trackno] = value
 		f.close()
+		for i in range(2, len(self.track)):
+			track = self.track[i]
+			# if track title starts with `,', use initial part
+			# of previous track's title
+			if track[0] == ',':
+				try:
+					off = string.index(self.track[i - 1],
+							   ',')
+				except string.index_error:
+					pass
+				else:
+					self.track[i] = self.track[i-1][:off] \
+							+ track
 
 	def write(self):
 		import posixpath
@@ -153,6 +166,17 @@
 		f.write('album.title:\t' + self.title + '\n')
 		f.write('album.artist:\t' + self.artist + '\n')
 		f.write('album.toc:\t' + self.toc + '\n')
+		prevpref = None
 		for i in range(1, len(self.track)):
-			f.write('track' + `i` + '.title:\t' + self.track[i] + '\n')
+			track = self.track[i]
+			try:
+				off = string.index(track, ',')
+			except string.index_error:
+				prevpref = None
+			else:
+				if prevpref and track[:off] == prevpref:
+					track = track[off:]
+				else:
+					prevpref = track[:off]
+			f.write('track' + `i` + '.title:\t' + track + '\n')
 		f.close()
diff --git a/Lib/irix5/flp.py b/Lib/irix5/flp.py
index c3f6f3b..bc4a8ab 100755
--- a/Lib/irix5/flp.py
+++ b/Lib/irix5/flp.py
@@ -432,10 +432,10 @@
 
 def test():
     import time
-    t0 = time.millitimer()
+    t0 = time.time()
     if len(sys.argv) == 2:
 	forms = parse_forms(sys.argv[1])
-	t1 = time.millitimer()
+	t1 = time.time()
 	print 'parse time:', 0.001*(t1-t0), 'sec.'
 	keys = forms.keys()
 	keys.sort()
@@ -443,8 +443,8 @@
 	    _printform(forms[i])
     elif len(sys.argv) == 3:
 	form = parse_form(sys.argv[1], sys.argv[2])
-	t1 = time.millitimer()
-	print 'parse time:', 0.001*(t1-t0), 'sec.'
+	t1 = time.time()
+	print 'parse time:', round(t1-t0, 3), 'sec.'
 	_printform(form)
     else:
 	print 'Usage: test fdfile [form]'
diff --git a/Lib/lib-old/codehack.py b/Lib/lib-old/codehack.py
index d00d2bf..1f16814 100644
--- a/Lib/lib-old/codehack.py
+++ b/Lib/lib-old/codehack.py
@@ -7,6 +7,10 @@
 import os
 import linecache
 
+# XXX The functions getcodename() and getfuncname() are now obsolete
+# XXX as code and function objects now have a name attribute --
+# XXX co.co_name and f.func_name.
+
 # Extract the function or class name from a code object.
 # This is a bit of a hack, since a code object doesn't contain
 # the name directly.  So what do we do:
diff --git a/Lib/lib-old/newdir.py b/Lib/lib-old/newdir.py
index 26a7df0..937c49e 100644
--- a/Lib/lib-old/newdir.py
+++ b/Lib/lib-old/newdir.py
@@ -65,11 +65,9 @@
 # Approximation of builtin dir(); but note that this lists the user's
 # variables by default, not the current local name space.
 
-def dir(*args):
-	if len(args) > 0:
-		if len(args) == 1:
-			args = args[0]
-		return listattrs(args)
+def dir(x = None):
+	if x is not None:
+		return listattrs(x)
 	else:
 		import __main__
 		return listattrs(__main__)
diff --git a/Lib/lib-old/packmail.py b/Lib/lib-old/packmail.py
index d612c88..13b1bdc 100644
--- a/Lib/lib-old/packmail.py
+++ b/Lib/lib-old/packmail.py
@@ -41,12 +41,28 @@
 # Pack all files from a directory
 def packall(outfp, dirname):
 	names = os.listdir(dirname)
+	try:
+	    names.remove('.')
+	except:
+	    pass
+	try:
+	    names.remove('..')
+	except:
+	    pass
 	names.sort()
 	packsome(outfp, dirname, names)
 
 # Pack all files from a directory that are not older than a give one
 def packnotolder(outfp, dirname, oldest):
 	names = os.listdir(dirname)
+	try:
+	    names.remove('.')
+	except:
+	    pass
+	try:
+	    names.remove('..')
+	except:
+	    pass
 	oldest = os.path.join(dirname, oldest)
 	st = os.stat(oldest)
 	mtime = st[ST_MTIME]
@@ -67,6 +83,14 @@
 	print 'packtree', dirname
 	outfp.write('mkdir ' + unixfix(dirname) + '\n')
 	names = os.listdir(dirname)
+	try:
+	    names.remove('.')
+	except:
+	    pass
+	try:
+	    names.remove('..')
+	except:
+	    pass
 	subdirs = []
 	for name in names:
 		fullname = os.path.join(dirname, name)
diff --git a/Lib/lib-stdwin/WindowSched.py b/Lib/lib-stdwin/WindowSched.py
index 56ca6f8..b2fbe76 100644
--- a/Lib/lib-stdwin/WindowSched.py
+++ b/Lib/lib-stdwin/WindowSched.py
@@ -1,5 +1,5 @@
 # Combine a real-time scheduling queue and stdwin event handling.
-# Uses the millisecond timer.
+# Keeps times in milliseconds.
 
 import stdwin, stdwinq
 from stdwinevents import WE_TIMER
@@ -19,11 +19,11 @@
 		mainloop.dispatch(event)
 		return
 	#
-	# Use millisleep for very short delays or if there are no windows
+	# Use sleep for very short delays or if there are no windows
 	#
 	if msecs < 100 or mainloop.countwindows() == 0:
 		if msecs > 0:
-			time.millisleep(msecs)
+			time.sleep(msecs * 0.001)
 		return
 	#
 	# Post a timer event on an arbitrary window and wait for it
@@ -35,7 +35,10 @@
 	if event[0] <> WE_TIMER:
 		mainloop.dispatch(event)
 
-q = sched.scheduler(time.millitimer, delayfunc)
+def millitimer():
+	return int(1000 * time.time())
+
+q = sched.scheduler(millitimer, delayfunc)
 
 # Export functions enter, enterabs and cancel just like a scheduler
 #
diff --git a/Lib/lib-stdwin/filewin.py b/Lib/lib-stdwin/filewin.py
index a03c3f7..df6aa7d 100644
--- a/Lib/lib-stdwin/filewin.py
+++ b/Lib/lib-stdwin/filewin.py
@@ -2,19 +2,19 @@
 # File windows, a subclass of textwin (which is a subclass of gwin)
 
 import textwin
-import builtin
+import __builtin__
 
 
 # FILE WINDOW
 
 def open_readonly(fn): # Open a file window
-	fp = builtin.open(fn, 'r')
+	fp = __builtin__.open(fn, 'r')
 	w = textwin.open_readonly(fn, fp.read())
 	w.fn = fn
 	return w
 
 def open(fn): # Open a file window
-	fp = builtin.open(fn, 'r')
+	fp = __builtin__.open(fn, 'r')
 	w = textwin.open(fn, fp.read())
 	w.fn = fn
 	return w
diff --git a/Lib/lib-stdwin/wdb.py b/Lib/lib-stdwin/wdb.py
index d5c28bb..4018ab1 100644
--- a/Lib/lib-stdwin/wdb.py
+++ b/Lib/lib-stdwin/wdb.py
@@ -241,7 +241,7 @@
 			stdwin.fleep()
 	
 	def draw(self, detail):
-		import linecache, codehack, string
+		import linecache, string
 		d = self.win.begindrawing()
 		try:
 			h, v = 0, 0
@@ -252,7 +252,7 @@
 				else:
 					s = '  '
 				s = s + fn + '(' + `lineno` + ')'
-				s = s + codehack.getcodename(f.f_code)
+				s = s + f.f_code.co_name
 				if f.f_locals.has_key('__args__'):
 					args = f.f_locals['__args__']
 					if args is not None:
@@ -286,6 +286,8 @@
 	try: apply(x.runcall, args)
 	finally: x.close()
 
+def set_trace():
+	Wdb().set_trace()
 
 # Post-Mortem interface
 
@@ -304,6 +306,4 @@
 TESTCMD = 'import x; x.main()'
 
 def test():
-	import linecache
-	linecache.checkcache()
 	run(TESTCMD)
diff --git a/Lib/mimetools.py b/Lib/mimetools.py
index 2a076f0..2844fa4 100644
--- a/Lib/mimetools.py
+++ b/Lib/mimetools.py
@@ -1,8 +1,10 @@
 # Various tools used by MIME-reading or MIME-writing programs.
 
 
-import string
+import os
 import rfc822
+import string
+import tempfile
 
 
 # A derived class of rfc822.Message that knows about MIME headers and
@@ -67,7 +69,7 @@
 	def getencoding(self):
 		if self.encodingheader == None:
 			return '7bit'
-		return self.encodingheader
+		return string.lower(self.encodingheader)
 
 	def gettype(self):
 		return self.type
@@ -110,3 +112,75 @@
 	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/multifile.py b/Lib/multifile.py
index 7a52ab6..71e0dd0 100644
--- a/Lib/multifile.py
+++ b/Lib/multifile.py
@@ -85,6 +85,17 @@
 			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:
diff --git a/Lib/newdir.py b/Lib/newdir.py
index 26a7df0..937c49e 100644
--- a/Lib/newdir.py
+++ b/Lib/newdir.py
@@ -65,11 +65,9 @@
 # Approximation of builtin dir(); but note that this lists the user's
 # variables by default, not the current local name space.
 
-def dir(*args):
-	if len(args) > 0:
-		if len(args) == 1:
-			args = args[0]
-		return listattrs(args)
+def dir(x = None):
+	if x is not None:
+		return listattrs(x)
 	else:
 		import __main__
 		return listattrs(__main__)
diff --git a/Lib/nntplib.py b/Lib/nntplib.py
index e7f0627..fb08b0c 100644
--- a/Lib/nntplib.py
+++ b/Lib/nntplib.py
@@ -60,10 +60,7 @@
 	# - host: hostname to connect to
 	# - port: port to connect to (default the standard NNTP port)
 
-	def __init__(self, host, *args):
-		if len(args) > 1: raise TypeError, 'too many args'
-		if args: port = args[0]
-		else: port = NNTP_PORT
+	def __init__(self, host, port = NNTP_PORT):
 		self.host = host
 		self.port = port
 		self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
diff --git a/Lib/ospath.py b/Lib/ospath.py
index 866d02e..1e66759 100644
--- a/Lib/ospath.py
+++ b/Lib/ospath.py
@@ -1,15 +1,3 @@
-# ospath.py is to {posix,mac}path.py what os.py is to modules {posix,mac}
-
-try:
-	import posix
-	name = 'posix'
-	del posix
-except ImportError:
-	import mac
-	name = 'mac'
-	del mac
-
-if name == 'posix':
-	from posixpath import *
-elif name == 'mac':
-	from macpath import *
+# ospath.py is obsolete
+import os
+exec 'from %s import *' % os.name
diff --git a/Lib/packmail.py b/Lib/packmail.py
index d612c88..13b1bdc 100644
--- a/Lib/packmail.py
+++ b/Lib/packmail.py
@@ -41,12 +41,28 @@
 # Pack all files from a directory
 def packall(outfp, dirname):
 	names = os.listdir(dirname)
+	try:
+	    names.remove('.')
+	except:
+	    pass
+	try:
+	    names.remove('..')
+	except:
+	    pass
 	names.sort()
 	packsome(outfp, dirname, names)
 
 # Pack all files from a directory that are not older than a give one
 def packnotolder(outfp, dirname, oldest):
 	names = os.listdir(dirname)
+	try:
+	    names.remove('.')
+	except:
+	    pass
+	try:
+	    names.remove('..')
+	except:
+	    pass
 	oldest = os.path.join(dirname, oldest)
 	st = os.stat(oldest)
 	mtime = st[ST_MTIME]
@@ -67,6 +83,14 @@
 	print 'packtree', dirname
 	outfp.write('mkdir ' + unixfix(dirname) + '\n')
 	names = os.listdir(dirname)
+	try:
+	    names.remove('.')
+	except:
+	    pass
+	try:
+	    names.remove('..')
+	except:
+	    pass
 	subdirs = []
 	for name in names:
 		fullname = os.path.join(dirname, name)
diff --git a/Lib/pdb.doc b/Lib/pdb.doc
index c92b572..43a91c2 100644
--- a/Lib/pdb.doc
+++ b/Lib/pdb.doc
@@ -61,9 +61,10 @@
 	Move the current frame one level up in the stack trace
 	(to a newer frame).
 
-b(reak) [lineno]
-	With a line number argument, set a break there in the current file.
-	Without argument, list all breaks.
+b(reak) [lineno | function]
+	With a line number argument, set a break there in the current
+	file.  With a function name, set a break at the entry of that
+	function.  Without argument, list all breaks.
 
 cl(ear) [lineno]
 	With a line number argument, clear that break in the current file.
diff --git a/Lib/pdb.py b/Lib/pdb.py
index 64451d5..a77dd29 100755
--- a/Lib/pdb.py
+++ b/Lib/pdb.py
@@ -74,17 +74,31 @@
 	# Return true to exit from the command loop 
 	
 	do_h = cmd.Cmd.do_help
-	
+
 	def do_break(self, arg):
 		if not arg:
 			print self.get_all_breaks() # XXX
 			return
-		try:
+		# Try line number as argument
+		try:	
 			lineno = int(eval(arg))
+			filename = self.curframe.f_code.co_filename
 		except:
-			print '*** Error in argument:', `arg`
-			return
-		filename = self.curframe.f_code.co_filename
+			# Try function name as the argument
+			import codehack
+			try:
+				func = eval(arg, self.curframe.f_globals,
+					    self.curframe.f_locals)
+				if hasattr(func, 'im_func'):
+					func = func.im_func
+				code = func.func_code
+			except:
+				print '*** Could not eval argument:', arg
+				return
+			lineno = codehack.getlineno(code)
+			filename = code.co_filename
+
+		# now set the break point
 		err = self.set_break(filename, lineno)
 		if err: print '***', err
 	do_b = do_break
@@ -159,10 +173,10 @@
 	do_q = do_quit
 	
 	def do_args(self, arg):
-		if self.curframe.f_locals.has_key('__return__'):
-			print `self.curframe.f_locals['__return__']`
+		if self.curframe.f_locals.has_key('__args__'):
+			print `self.curframe.f_locals['__args__']`
 		else:
-			print '*** Not arguments?!'
+			print '*** No arguments?!'
 	do_a = do_args
 	
 	def do_retval(self, arg):
@@ -229,7 +243,6 @@
 	do_l = do_list
 
 	def do_whatis(self, arg):
-		import codehack
 		try:
 			value = eval(arg, self.curframe.f_globals, \
 					self.curframe.f_locals)
@@ -241,13 +254,13 @@
 		try: code = value.func_code
 		except: pass
 		if code:
-			print 'Function', codehack.getcodename(code)
+			print 'Function', code.co_name
 			return
 		# Is it an instance method?
 		try: code = value.im_func.func_code
 		except: pass
 		if code:
-			print 'Method', codehack.getcodename(code)
+			print 'Method', code.co_name
 			return
 		# None of the above...
 		print type(value)
@@ -276,6 +289,137 @@
 		print self.format_stack_entry(frame_lineno)
 
 
+	# Help methods (derived from pdb.doc)
+
+	def help_help(self):
+		self.help_h()
+
+	def help_h(self):
+		print """h(elp)
+	Without argument, print the list of available commands.
+	With a command name as argument, print help about that command
+	"help pdb" pipes the full documentation file to the $PAGER
+	"help exec" gives help on the ! command"""
+
+	def help_where(self):
+		self.help_w()
+
+	def help_w(self):
+		print """w(here)
+	Print a stack trace, with the most recent frame at the bottom.
+	An arrow indicates the "current frame", which determines the
+	context of most commands."""
+
+	def help_down(self):
+		self.help_d()
+
+	def help_d(self):
+		print """d(own)
+	Move the current frame one level down in the stack trace
+	(to an older frame)."""
+
+	def help_up(self):
+		self.help_u()
+
+	def help_u(self):
+		print """u(p)
+	Move the current frame one level up in the stack trace
+	(to a newer frame)."""
+
+	def help_break(self):
+		self.help_b()
+
+	def help_b(self):
+		print """b(reak) [lineno | function]
+	With a line number argument, set a break there in the current
+	file.  With a function name, set a break at the entry of that
+	function.  Without argument, list all breaks."""
+
+	def help_clear(self):
+		self.help_cl()
+
+	def help_cl(self):
+		print """cl(ear) [lineno]
+	With a line number argument, clear that break in the current file.
+	Without argument, clear all breaks (but first ask confirmation)."""
+
+	def help_step(self):
+		self.help_s()
+
+	def help_s(self):
+		print """s(tep)
+	Execute the current line, stop at the first possible occasion
+	(either in a function that is called or in the current function)."""
+
+	def help_next(self):
+		self.help_n()
+
+	def help_n(self):
+		print """n(ext)
+	Continue execution until the next line in the current function
+	is reached or it returns."""
+
+	def help_return(self):
+		self.help_r()
+
+	def help_r(self):
+		print """r(eturn)
+	Continue execution until the current function returns."""
+
+	def help_continue(self):
+		self.help_c()
+
+	def help_cont(self):
+		self.help_c()
+
+	def help_c(self):
+		print """c(ont(inue))
+	Continue execution, only stop when a breakpoint is encountered."""
+
+	def help_list(self):
+		self.help_l()
+
+	def help_l(self):
+		print """l(ist) [first [,last]]
+	List source code for the current file.
+	Without arguments, list 11 lines around the current line
+	or continue the previous listing.
+	With one argument, list 11 lines starting at that line.
+	With two arguments, list the given range;
+	if the second argument is less than the first, it is a count."""
+
+	def help_args(self):
+		self.help_a()
+
+	def help_a(self):
+		print """a(rgs)
+	Print the argument list of the current function."""
+
+	def help_p(self):
+		print """p expression
+	Print the value of the expression."""
+
+	def help_exec(self):
+		print """(!) statement
+	Execute the (one-line) statement in the context of
+	the current stack frame.
+	The exclamation point can be omitted unless the first word
+	of the statement resembles a debugger command.
+	To assign to a global variable you must always prefix the
+	command with a 'global' command, e.g.:
+	(Pdb) global list_options; list_options = ['-l']
+	(Pdb)"""
+
+	def help_quit(self):
+		self.help_q()
+
+	def help_q(self):
+		print """q(uit)	Quit from the debugger.
+	The program being executed is aborted."""
+
+	def help_pdb(self):
+		help()
+
 # Simplified interface
 
 def run(statement):
@@ -287,6 +431,8 @@
 def runcall(*args):
 	apply(Pdb().runcall, args)
 
+def set_trace():
+	Pdb().set_trace()
 
 # Post-Mortem interface
 
@@ -306,8 +452,6 @@
 TESTCMD = 'import x; x.main()'
 
 def test():
-	import linecache
-	linecache.checkcache()
 	run(TESTCMD)
 
 # print help
diff --git a/Lib/plat-irix5/FCNTL.py b/Lib/plat-irix5/FCNTL.py
index 1271b9e..12c60fa 100755
--- a/Lib/plat-irix5/FCNTL.py
+++ b/Lib/plat-irix5/FCNTL.py
@@ -1,35 +1,35 @@
-# These lines were generated by h2py.py (see demo/scripts)
-# from <sys/fcntl.h> on Irix 4.0.2.
-# The applicability on other systems is not clear.
-
+# Generated by h2py from /usr/include/sys/fcntl.h
 FNDELAY = 0x04
 FAPPEND = 0x08
 FSYNC = 0x10
-FRCACH = 0x20
-FASYNC = 0x40
-FNONBLK = 0x80
-FCREAT = 0x100
-FTRUNC = 0x200
-FEXCL = 0x400
-FNOCTTY = 0x800
+FNONBLOCK = 0x80
+FASYNC = 0x1000
+FNONBLK = FNONBLOCK
+FDIRECT = 0x8000
+FCREAT = 0x0100
+FTRUNC = 0x0200
+FEXCL = 0x0400
+FNOCTTY = 0x0800
 O_RDONLY = 0
 O_WRONLY = 1
 O_RDWR = 2
-O_ACCMODE = 0x3
-O_NDELAY = FNDELAY
-O_APPEND = FAPPEND
-O_SYNC = FSYNC
-O_NONBLOCK = FNONBLK
-O_CREAT = FCREAT
-O_TRUNC = FTRUNC
-O_EXCL = FEXCL
-O_NOCTTY = FNOCTTY
+O_NDELAY = 0x04
+O_APPEND = 0x08
+O_SYNC = 0x10
+O_NONBLOCK = 0x80
+O_DIRECT = 0x8000
+O_CREAT = 0x100
+O_TRUNC = 0x200
+O_EXCL = 0x400
+O_NOCTTY = 0x800
 F_DUPFD = 0
 F_GETFD = 1
 F_SETFD = 2
 F_GETFL = 3
 F_SETFL = 4
-F_GETLK = 5
+F_GETLK = 14
+F_O_GETLK = 5
+F_GETLK = 14
 F_SETLK = 6
 F_SETLKW = 7
 F_CHKFL = 8
@@ -37,13 +37,17 @@
 F_FREESP = 11
 F_SETBSDLK = 12
 F_SETBSDLKW = 13
-F_RGETLK = 20
-F_RSETLK = 21
+F_DIOINFO = 30
+F_RSETLK = 20
+F_RGETLK = 21
 F_RSETLKW = 22
-F_GETOWN = 10
-F_SETOWN = 11
+F_GETOWN = 23
+F_SETOWN = 24
+F_O_GETOWN = 10
+F_O_SETOWN = 11
 F_RDLCK = 01
 F_WRLCK = 02
 F_UNLCK = 03
-FD_CLOEXEC = 0x1
-FD_NODUP_FORK = 0x2
+O_ACCMODE = 3
+FD_CLOEXEC = 1
+FD_NODUP_FORK = 4
diff --git a/Lib/plat-irix5/IN.py b/Lib/plat-irix5/IN.py
index 78be3ef..325a021 100755
--- a/Lib/plat-irix5/IN.py
+++ b/Lib/plat-irix5/IN.py
@@ -1,8 +1,4 @@
-# Symbolic constants from <netinet/in.h>.
-# These constants are SGI specific!
-# See demo/scripts/h2py.py for a tool to help generate a version for
-# your system.
-
+# Generated by h2py from /usr/include/netinet/in.h
 IPPROTO_IP = 0
 IPPROTO_ICMP = 1
 IPPROTO_IGMP = 2
@@ -14,6 +10,8 @@
 IPPROTO_IDP = 22
 IPPROTO_TP = 29
 IPPROTO_XTP = 36
+IPPROTO_HELLO = 63
+IPPROTO_ND = 77
 IPPROTO_EON = 80
 IPPROTO_RAW = 255
 IPPROTO_MAX = 256
@@ -42,6 +40,11 @@
 INADDR_NONE = 0xffffffff
 IN_LOOPBACKNET = 127
 IP_OPTIONS = 1
+IP_MULTICAST_IF = 2
+IP_MULTICAST_TTL = 3
+IP_MULTICAST_LOOP = 4
+IP_ADD_MEMBERSHIP = 5
+IP_DROP_MEMBERSHIP = 6
 IP_HDRINCL = 7
 IP_TOS = 8
 IP_TTL = 9
@@ -49,11 +52,32 @@
 IP_RECVRETOPTS = 11
 IP_RECVDSTADDR = 12
 IP_RETOPTS = 13
-IP_MULTICAST_IF = 2
-IP_MULTICAST_TTL = 3
-IP_MULTICAST_LOOP = 4
-IP_ADD_MEMBERSHIP = 5
-IP_DROP_MEMBERSHIP = 6
+IP_OPTIONS = 1
+IP_HDRINCL = 2
+IP_TOS = 3
+IP_TTL = 4
+IP_RECVOPTS = 5
+IP_RECVRETOPTS = 6
+IP_RECVDSTADDR = 7
+IP_RETOPTS = 8
+IP_MULTICAST_IF = 20
+IP_MULTICAST_TTL = 21
+IP_MULTICAST_LOOP = 22
+IP_ADD_MEMBERSHIP = 23
+IP_DROP_MEMBERSHIP = 24
+IRIX4_IP_OPTIONS = 1
+IRIX4_IP_MULTICAST_IF = 2
+IRIX4_IP_MULTICAST_TTL = 3
+IRIX4_IP_MULTICAST_LOOP = 4
+IRIX4_IP_ADD_MEMBERSHIP = 5
+IRIX4_IP_DROP_MEMBERSHIP = 6
+IRIX4_IP_HDRINCL = 7
+IRIX4_IP_TOS = 8
+IRIX4_IP_TTL = 9
+IRIX4_IP_RECVOPTS = 10
+IRIX4_IP_RECVRETOPTS = 11
+IRIX4_IP_RECVDSTADDR = 12
+IRIX4_IP_RETOPTS = 13
 IP_DEFAULT_MULTICAST_TTL = 1
 IP_DEFAULT_MULTICAST_LOOP = 1
 IP_MAX_MEMBERSHIPS = 20
diff --git a/Lib/plat-irix5/SOCKET.py b/Lib/plat-irix5/SOCKET.py
index 8a15ef9..0ba0742 100755
--- a/Lib/plat-irix5/SOCKET.py
+++ b/Lib/plat-irix5/SOCKET.py
@@ -1,8 +1,23 @@
+# Generated by h2py from /usr/include/sys/socket.h
 SOCK_STREAM = 1
 SOCK_DGRAM = 2
 SOCK_RAW = 3
 SOCK_RDM = 4
 SOCK_SEQPACKET = 5
+NC_TPI_CLTS = 1
+NC_TPI_COTS = 2
+NC_TPI_COTS_ORD = 3
+NC_TPI_RAW = 4
+SOCK_DGRAM = NC_TPI_CLTS
+SOCK_STREAM = NC_TPI_COTS
+SOCK_RAW = NC_TPI_RAW
+SOCK_RDM = 5
+SOCK_SEQPACKET = 6
+IRIX4_SOCK_STREAM = 1
+IRIX4_SOCK_DGRAM = 2
+IRIX4_SOCK_RAW = 3
+IRIX4_SOCK_RDM = 4
+IRIX4_SOCK_SEQPACKET = 5
 SO_DEBUG = 0x0001
 SO_ACCEPTCONN = 0x0002
 SO_REUSEADDR = 0x0004
@@ -13,6 +28,9 @@
 SO_LINGER = 0x0080
 SO_OOBINLINE = 0x0100
 SO_REUSEPORT = 0x0200
+SO_ORDREL = 0x0200
+SO_IMASOCKET = 0x0400
+SO_CHAMELEON = 0x1000
 SO_SNDBUF = 0x1001
 SO_RCVBUF = 0x1002
 SO_SNDLOWAT = 0x1003
@@ -21,6 +39,7 @@
 SO_RCVTIMEO = 0x1006
 SO_ERROR = 0x1007
 SO_TYPE = 0x1008
+SO_PROTOTYPE = 0x1009
 SOL_SOCKET = 0xffff
 AF_UNSPEC = 0
 AF_UNIX = 1
@@ -30,7 +49,6 @@
 AF_CHAOS = 5
 AF_NS = 6
 AF_ISO = 7
-AF_OSI = AF_ISO
 AF_ECMA = 8
 AF_DATAKIT = 9
 AF_CCITT = 10
@@ -44,7 +62,14 @@
 AF_RAW = 18
 AF_LINK = 18
 pseudo_AF_XTP = 19
-AF_MAX = 20
+AF_NIT = 17
+AF_802 = 18
+AF_OSI = 19
+AF_X25 = 20
+AF_OSINET = 21
+AF_GOSIP = 22
+AF_SDL = 23
+AF_MAX = (AF_SDL+1)
 PF_UNSPEC = AF_UNSPEC
 PF_UNIX = AF_UNIX
 PF_INET = AF_INET
@@ -53,7 +78,6 @@
 PF_CHAOS = AF_CHAOS
 PF_NS = AF_NS
 PF_ISO = AF_ISO
-PF_OSI = AF_ISO
 PF_ECMA = AF_ECMA
 PF_DATAKIT = AF_DATAKIT
 PF_CCITT = AF_CCITT
@@ -67,11 +91,18 @@
 PF_LINK = AF_LINK
 PF_XTP = pseudo_AF_XTP
 PF_RAW = AF_RAW
+PF_NIT = AF_NIT
+PF_802 = AF_802
+PF_OSI = AF_OSI
+PF_X25 = AF_X25
+PF_OSINET = AF_OSINET
+PF_GOSIP = AF_GOSIP
 PF_MAX = AF_MAX
 SOMAXCONN = 5
 MSG_OOB = 0x1
 MSG_PEEK = 0x2
 MSG_DONTROUTE = 0x4
+MSG_EOR = 0x8
 MSG_BTAG = 0x40
 MSG_ETAG = 0x80
 MSG_MAXIOVLEN = 16
diff --git a/Lib/plat-irix5/cddb.py b/Lib/plat-irix5/cddb.py
index d7fdc96..0dee709 100755
--- a/Lib/plat-irix5/cddb.py
+++ b/Lib/plat-irix5/cddb.py
@@ -138,6 +138,19 @@
 					continue
 				self.track[trackno] = value
 		f.close()
+		for i in range(2, len(self.track)):
+			track = self.track[i]
+			# if track title starts with `,', use initial part
+			# of previous track's title
+			if track[0] == ',':
+				try:
+					off = string.index(self.track[i - 1],
+							   ',')
+				except string.index_error:
+					pass
+				else:
+					self.track[i] = self.track[i-1][:off] \
+							+ track
 
 	def write(self):
 		import posixpath
@@ -153,6 +166,17 @@
 		f.write('album.title:\t' + self.title + '\n')
 		f.write('album.artist:\t' + self.artist + '\n')
 		f.write('album.toc:\t' + self.toc + '\n')
+		prevpref = None
 		for i in range(1, len(self.track)):
-			f.write('track' + `i` + '.title:\t' + self.track[i] + '\n')
+			track = self.track[i]
+			try:
+				off = string.index(track, ',')
+			except string.index_error:
+				prevpref = None
+			else:
+				if prevpref and track[:off] == prevpref:
+					track = track[off:]
+				else:
+					prevpref = track[:off]
+			f.write('track' + `i` + '.title:\t' + track + '\n')
 		f.close()
diff --git a/Lib/plat-irix5/flp.py b/Lib/plat-irix5/flp.py
index c3f6f3b..bc4a8ab 100755
--- a/Lib/plat-irix5/flp.py
+++ b/Lib/plat-irix5/flp.py
@@ -432,10 +432,10 @@
 
 def test():
     import time
-    t0 = time.millitimer()
+    t0 = time.time()
     if len(sys.argv) == 2:
 	forms = parse_forms(sys.argv[1])
-	t1 = time.millitimer()
+	t1 = time.time()
 	print 'parse time:', 0.001*(t1-t0), 'sec.'
 	keys = forms.keys()
 	keys.sort()
@@ -443,8 +443,8 @@
 	    _printform(forms[i])
     elif len(sys.argv) == 3:
 	form = parse_form(sys.argv[1], sys.argv[2])
-	t1 = time.millitimer()
-	print 'parse time:', 0.001*(t1-t0), 'sec.'
+	t1 = time.time()
+	print 'parse time:', round(t1-t0, 3), 'sec.'
 	_printform(form)
     else:
 	print 'Usage: test fdfile [form]'
diff --git a/Lib/plat-sunos4/FCNTL.py b/Lib/plat-sunos4/FCNTL.py
index 0ba5e67..1256d81 100755
--- a/Lib/plat-sunos4/FCNTL.py
+++ b/Lib/plat-sunos4/FCNTL.py
@@ -1,10 +1,10 @@
+# Generated by h2py from stdin
 _FOPEN = (-1)
 _FREAD = 0x0001
 _FWRITE = 0x0002
 _FNDELAY = 0x0004
 _FAPPEND = 0x0008
-_FMARK = 0x0010
-_FDEFER = 0x0020
+_FSETBLK = 0x0010
 _FASYNC = 0x0040
 _FSHLOCK = 0x0080
 _FEXLOCK = 0x0100
@@ -15,6 +15,8 @@
 _FSYNC = 0x2000
 _FNONBLOCK = 0x4000
 _FNOCTTY = 0x8000
+_FMARK = 0x10000
+_FDEFER = 0x20000
 O_RDONLY = 0
 O_WRONLY = 1
 O_RDWR = 2
@@ -25,7 +27,6 @@
 O_NONBLOCK = _FNONBLOCK
 O_NOCTTY = _FNOCTTY
 O_SYNC = _FSYNC
-O_ACCMODE = (O_RDONLY|O_WRONLY|O_RDWR)
 FAPPEND = _FAPPEND
 FSYNC = _FSYNC
 FASYNC = _FASYNC
@@ -36,6 +37,7 @@
 FWRITE = _FWRITE
 FMARK = _FMARK
 FDEFER = _FDEFER
+FSETBLK = _FSETBLK
 FSHLOCK = _FSHLOCK
 FEXLOCK = _FEXLOCK
 FOPEN = _FOPEN
@@ -62,3 +64,4 @@
 F_WRLCK = 2
 F_UNLCK = 3
 F_UNLKSYS = 4
+O_ACCMODE = (O_RDONLY|O_WRONLY|O_RDWR)
diff --git a/Lib/plat-sunos4/IN.py b/Lib/plat-sunos4/IN.py
index 05188af..a05a944 100755
--- a/Lib/plat-sunos4/IN.py
+++ b/Lib/plat-sunos4/IN.py
@@ -1,8 +1,4 @@
-# Symbolic constants from <netinet/in.h>.
-# These constants are SunOS specific!  (Possibly even SunOS 4.1.1)
-# See demo/scripts/h2py.py for a tool to help generate a version for
-# your system.
-
+# Generated by h2py from /usr/include/netinet/in.h
 IPPROTO_IP = 0
 IPPROTO_ICMP = 1
 IPPROTO_IGMP = 2
diff --git a/Lib/plat-sunos4/SOCKET.py b/Lib/plat-sunos4/SOCKET.py
index c1b8542..65ce4bc 100755
--- a/Lib/plat-sunos4/SOCKET.py
+++ b/Lib/plat-sunos4/SOCKET.py
@@ -1,3 +1,4 @@
+# Generated by h2py from /usr/include/sys/socket.h
 SOCK_STREAM = 1
 SOCK_DGRAM = 2
 SOCK_RAW = 3
diff --git a/Lib/plat-sunos4/WAIT.py b/Lib/plat-sunos4/WAIT.py
new file mode 100755
index 0000000..43612f0
--- /dev/null
+++ b/Lib/plat-sunos4/WAIT.py
@@ -0,0 +1,13 @@
+# Generated by h2py from /usr/include/sys/wait.h
+WUNTRACED = 0004
+WNOHANG = 0100
+WEXITED = 0001
+WTRAPPED = 0002
+WSTOPPED = WUNTRACED
+WCONTINUED = 0010
+WNOWAIT = 0200
+WOPTMASK = (WEXITED|WTRAPPED|WSTOPPED|WCONTINUED|WNOHANG|WNOWAIT)
+WSTOPFLG = 0177
+WCONTFLG = 0177777
+WCOREFLG = 0200
+WSIGMASK = 0177
diff --git a/Lib/plat-sunos4/regen b/Lib/plat-sunos4/regen
index f1482db..8b52d74 100755
--- a/Lib/plat-sunos4/regen
+++ b/Lib/plat-sunos4/regen
@@ -8,4 +8,5 @@
 h2py </usr/include/sys/fcntlcom.h >FCNTL.py
 echo "O_ACCMODE = (O_RDONLY|O_WRONLY|O_RDWR)" >>FCNTL.py
 h2py /usr/include/sys/socket.h
+h2py /usr/include/sys/wait.h
 h2py -i '(u_long)' /usr/include/netinet/in.h
diff --git a/Lib/posixpath.py b/Lib/posixpath.py
index 96116d1..6110f8e 100644
--- a/Lib/posixpath.py
+++ b/Lib/posixpath.py
@@ -130,7 +130,7 @@
 
 
 # Is a path a regular file?
-# This follows symbolic links, so both islink() and isdir() can be true
+# This follows symbolic links, so both islink() and isfile() can be true
 # for the same path.
 
 def isfile(path):
@@ -205,7 +205,7 @@
 	for name in names:
 		if name not in exceptions:
 			name = join(top, name)
-			if isdir(name):
+			if isdir(name) and not islink(name):
 				walk(name, func, arg)
 
 
@@ -239,29 +239,35 @@
 
 
 # Expand paths containing shell variable substitutions.
-# This is done by piping it through the shell.
-# Shell quoting characters (\ " ' `) are protected by a backslash.
-# NB: a future version may avoid starting a subprocess and do the
-# substitutions internally.  This may slightly change the syntax
-# for variables.
+# 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
-	q = ''
-	for c in path:
-		if c in ('\\', '"', '\'', '`'):
-			c = '\\' + c
-		q = q + c
-	d = '!'
-	if q == d:
-		d = '+'
-	p = posix.popen('cat <<' + d + '\n' + q + '\n' + d + '\n', 'r')
-	res = p.read()
-	del p
-	if res[-1:] == '\n':
-		res = res[:-1]
-	return res
+	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.
diff --git a/Lib/profile.doc b/Lib/profile.doc
index 753d159..bf5d8e3 100644
--- a/Lib/profile.doc
+++ b/Lib/profile.doc
@@ -1,74 +1,702 @@
-The Python Profiler
+profile.doc                     last updated 6/23/94 [by Guido]
 
-To use the profiler in its simplest form:
+ PROFILER DOCUMENTATION and (mini) USER'S MANUAL
 
-	>>> import profile
-	>>> profile.run(statement)
+Copyright 1994, by InfoSeek Corporation, all rights reserved.
+Written by James Roskind
 
-This will execute the statement and print statistics.  To get more
-information out of the profiler, use:
+Permission to use, copy, modify, and distribute this Python software
+and its associated documentation for any purpose (subject to the
+restriction in the following sentence) without fee is hereby granted,
+provided that the above copyright notice appears in all copies, and
+that both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of InfoSeek not be used in
+advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.  This permission is
+explicitly restricted to the copying and modification of the software
+to remain in Python, compiled Python, or other languages (such as C)
+wherein the modified or derived code is exclusively imported into a
+Python module.
 
-	>>> import profile
-	>>> profile.run(statement, dump_file)
+INFOSEEK CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL INFOSEEK CORPORATION BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-where dump_file is a string naming a file to which the (binary)
-profile statistics is to be dumped.  The binary format is a dump of a
-dictionary.  The key is the function name in the format described
-above; the value is a tuple consisting of, in order, number of calls,
-total time spent in the function, total time spent in the function and
-all functions called from it, a list of functions called by this
-function, and a list of functions that called this function.  The dump
-can be read back using the following code:
 
-	>>> import marshal
-	>>> f = open(dump_file, 'r')
-	>>> dict = marshal.load(f)
-	>>> f.close()
+The profiler was written after only programming in Python for 3 weeks.
+As a result, it is probably clumsy code, but I don't know for sure yet
+'cause I'm a beginner :-).  I did work hard to make the code run fast,
+so that profiling would be a reasonable thing to do.  I tried not to
+repeat code fragments, but I'm sure I did some stuff in really awkward
+ways at times.  Please send suggestions for improvements to:
+jar@infoseek.com.  I won't promise *any* support.  ...but I'd
+appreciate the feedback.
 
-An easier way of doing this is by using the class `Stats' which is
-also defined in profile:
 
-	>>> import profile
-	>>> s = profile.Stats().init(dump_file)
+SECTION HEADING LIST:
+  INTRODUCTION
+  HOW IS THIS profile DIFFERENT FROM THE OLD profile MODULE?
+  INSTANT USERS MANUAL
+  WHAT IS DETERMINISTIC PROFILING?
+  REFERENCE MANUAL			  
+    FUNCTION	profile.run(string, filename_opt)
+    CLASS	Stats(filename, ...)
+    METHOD	strip_dirs()
+    METHOD	add(filename, ...)
+    METHOD	sort_stats(key, ...)
+    METHOD	reverse_order()
+    METHOD	print_stats(restriction, ...)
+    METHOD	print_callers(restrictions, ...)
+    METHOD	print_callees(restrictions, ...)
+    METHOD	ignore()
+  LIMITATIONS
+  CALIBRATION
+  EXTENSIONS: Deriving Better Profilers
 
-The following methods are defined for instances of `Stats':
 
-	print_stats()	-- Print the statistics in a format similar to
-			   the format profile.run() uses.
-	print_callers()	-- For each function, print all functions
-			   which it calls.
-	print_callees()	-- For each function, print all functions from
-			   which it is called.
-	sort_stats(n)	-- Sort the statistics for subsequent
-			   printing.  The argument determines on which
-			   field the output should be sorted.
-			   Possibilities are
-				-1	function name
-				0	number of calls
-				1	total time spent in a function
-				2	total time spent in a function
-					plus all functions it called
-	strip_dirs()	-- Strip the directory names off of the file
-			   names which are part of the function names.
-			   This undoes the effect of sort_stats(), but
-			   a subsequent sort_stats() does work.
 
-The methods sort_stats and strip_dirs may change in the future.
+INTRODUCTION
 
-Output of profile.run(statement) and of the print_stats() method of
-the `Stats' class consists of the following fields.
+A "profiler" is a program that describes the run time performance of a
+program, providing a variety of statistics.  This documentation
+describes the profiler functionality provided in the modules
+"profile" and "pstats."  This profiler provides "deterministic
+profiling" of any Python programs.  It also provides a series of
+report generation tools to allow users to rapidly examine the results
+of a profile operation.
 
-	Number of times the function was called.
-	Total time spent in the function.
-	Mean time per function call (second field divided by first).
-	Total time spent in the function and all functions it called,
-		recursively.
-	Mean time time spent in the function and all functions it
-		called (fourth field divided by first).
-	Name of the function in the format
-		<file name>:<line number>(<function name>)
 
-The output of the print_callers and print_callees methods consists of
-the name of the function and the names of all function it called or
-was called from.  The latter names are followed by a parenthesised
-number which is the number of calls for this function.
+HOW IS THIS profile DIFFERENT FROM THE OLD profile MODULE?
+
+The big changes from standard profiling module are that you get more
+information, and you pay less CPU time.  It's not a trade-off, it's a
+trade-up.
+
+To be specific:
+
+ bugs removed: local stack frame is no longer molested, execution time
+      is now charged to correct functions, ....
+
+ accuracy increased: profiler execution time is no longer charged to
+      user's code, calibration for platform is supported, file reads
+      are not done *by* profiler *during* profiling (and charged to
+      user's code!), ...
+
+ speed increased: Overhead CPU cost was reduced by more than a factor of
+      two (perhaps a factor of five), lightweight profiler module is
+      all that must be loaded, and the report generating module
+      (pstats) is not needed during profiling. 
+
+ recursive functions support: cumulative times in recursive functions
+      are correctly calculated; recursive entries are counted; ...
+
+ large growth in report generating UI: distinct profiles runs can be added
+       together forming a comprehensive report; functions that import
+       statistics take arbitrary lists of files; sorting criteria is now
+       based on keywords (instead of 4 integer options); reports shows
+       what functions were profiled as well as what profile file was
+       referenced; output format has been improved, ...
+
+
+INSTANT USERS MANUAL
+
+This section is provided for users that "don't want to read the
+manual." It provides a very brief overview, and allows a user to
+rapidly perform profiling on an existing application.
+
+To profile an application with a main entry point of "foo()", you
+would add the following to your module:
+
+	import profile
+	profile.run("foo()")
+
+The above action would cause "foo()" to be run, and a series of
+informative lines (the profile) to be printed.  The above approach is
+most useful when working with the interpreter.  If you would like to
+save the results of a profile into a file for later examination, you
+can supply a file name as the second argument to the run() function:
+
+	import profile
+	profile.run("foo()", 'fooprof')
+
+When you wish to review the profile, you should use the methods in the
+pstats module.  Typically you would load the statistics data as
+follows:
+
+	import pstats
+	p = pstats.Stats('fooprof')
+
+The class "Stats" (the above code just created an instance of this
+class) has a variety of methods for manipulating and printing the data
+that was just read into "p".  When you ran profile.run() above, what
+was printed was the result of three method calls:
+
+	p.strip_dirs().sort_stats(-1).print_stats()
+
+The first method removed the extraneous path from all the module
+names. The second method sorted all the entries according to the
+standard module/line/name string that is printed (this is to comply
+with the semantics of the old profiler).  The third method printed out
+all the statistics.  You might try the following sort calls:
+
+	p.sort_stats('name')
+	p.print_stats()
+
+The first call will actually sort the list by function name, and the
+second call will print out the statistics.  The following are some
+interesting calls to experiment with:
+
+	p.sort_stats('cumulative').print_stats(10)
+
+This sorts the profile by cumulative time in a function, and then only
+prints the ten most significant lines.  If you want to understand what
+algorithms are taking time, the above line is what you would use.
+
+If you were looking to see what functions were looping a lot, and
+taking a lot of time, you would do:
+
+	p.sort_stats('time').print_stats(10)
+
+to sort according to time spent within each function, and then print
+the statistics for the top ten functions.
+
+You might also try:
+
+	p.sort_stats('file').print_stats('__init__')
+
+This will sort all the statistics by file name, and then print out
+statistics for only the class init methods ('cause they are spelled
+with "__init__" in them).  As one final example, you could try:
+
+	p.sort_stats('time', 'cum').print_stats(.5, 'init')
+
+This line sorts stats with a primary key of time, and a secondary key
+of cumulative time, and then prints out some of the statistics.  To be
+specific, the list is first culled down to 50% (re: .5) of its
+original size, then only lines containing "init" are maintained, and
+that sub-sub-list is printed.
+
+If you wondered what functions called the above functions, you could
+now (p is still sorted according to the last criteria) do:
+
+	p.print_callers(.5, 'init')
+
+and you would get a list of callers for each of the listed functions. 
+
+If you want more functionality, you're going to have to read the
+manual (or guess) what the following functions do:
+
+	p.print_callees()
+	p.add('fooprof')
+
+
+WHAT IS DETERMINISTIC PROFILING?
+
+"Deterministic profiling" is meant to reflect the fact that all
+"function call", "function return", and "exception" events are
+monitored, and precise timings are made for the intervals between
+these events (during which time the user's code is executing).  In
+contrast, "statistical profiling" (which is not done by this module)
+randomly samples the effective instruction pointer, and deduces where
+time is being spent.  The latter technique traditionally involves less
+overhead (as the code does not need to be instrumented), but provides
+only relative indications of where time is being spent.
+
+In Python, since there is an interpreter active during execution, the
+presence of instrumented code is not required to do deterministic
+profiling. Python automatically provides a hook (optional callback)
+for each event.  In addition, the interpreted nature of Python tends
+to add so much overhead to execution, that deterministic profiling
+tends to only add small processing overhead, in typical applications.
+The result is that deterministic profiling is not that expensive, but
+yet provides extensive run time statistics about the execution of a
+Python program.  
+
+Call count statistics can be used to identify bugs in code (surprising
+counts), and to identify possible inline-expansion points (high call
+counts).  Internal time statistics can be used to identify hot loops
+that should be carefully optimized.  Cumulative time statistics should
+be used to identify high level errors in the selection of algorithms.
+Note that the unusual handling of cumulative times in this profiler
+allows statistics for recursive implementations of algorithms to be
+directly compared to iterative implementations.
+
+
+REFERENCE MANUAL			  
+
+The primary entry point for the profiler is the global function
+profile.run().  It is typically used to create any profile
+information.  The reports are formatted and printed using methods for
+the class pstats.Stats.  The following is a description of all of
+these standard entry points and functions.  For a more in-depth view
+of some of the code, consider reading the later section on "Profiler
+Extensions," which includes discussion of how to derive "better"
+profilers from the classes presented, or reading the source code for
+these modules.
+
+
+FUNCTION	profile.run(string, filename_opt)
+
+This function takes a single argument that has can be passed to the
+"exec" statement, and an optional file name.  In all cases this
+routine attempts to "exec" its first argument, and gather profiling
+statistics from the execution. If no file name is present, then this
+function automatically prints a simple profiling report, sorted by the
+standard name string (file/line/function-name) that is presented in
+each line.  The following is a typical output from such a call:
+
+cut here----
+
+         main()
+         2706 function calls (2004 primitive calls) in 4.504 CPU seconds
+
+   Ordered by: standard name
+
+   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
+        2    0.006    0.003    0.953    0.477 pobject.py:75(save_objects)
+     43/3    0.533    0.012    0.749    0.250 pobject.py:99(evaluate)
+	...
+
+cut here----
+
+The first line indicates that this profile was generated by the call:
+profile.run('main()'), and hence the exec'ed string is 'main()'.  The
+second line indicates that 2706 calls were monitored.  Of those calls,
+2004 were "primitive."  We define "primitive" to mean that the call
+was not induced via recursion.  The next line: "Ordered by: standard
+name", indicates that the text string in the far right column was used
+to sort the output.  The column headings include:
+
+	"ncalls" for the number of calls, 
+	"tottime" for the total time spent in the given function
+		(and excluding time made in calls to sub-functions), 
+	"percall" is the quotient of "tottime" divided by "ncalls"
+	"cumtime" is the total time spent in this and all subfunctions
+		(i.e., from invocation till exit). This figure is
+		accurate *even* for recursive functions.
+	"percall" is the quotient of "cumtime" divided by primitive
+		calls
+	"filename:lineno(function)" provides the respective data of
+		each function
+
+When there are two numbers in the first column (e.g.: 43/3), then the
+latter is the number of primitive calls, and the former is the actual
+number of calls.  Note that when the function does not recurse, these
+two values are the same, and only the single figure is printed.
+
+
+CLASS	Stats(filename, ...)
+
+This class constructor creates an instance of a statistics object from
+a filename (or set of filenames).  Stats objects are manipulated by
+methods, in order to print useful reports.  
+
+The file selected by the above constructor must have been created by
+the corresponding version of profile.  To be specific, there is *NO*
+file compatibility guaranteed with future versions of this profiler,
+and there is no compatibility with files produced by other profilers
+(e.g., the standard system profiler).
+
+If several files are provided, all the statistics for identical
+functions will be coalesced, so that an overall view of several
+processes can be considered in a single report.  If additional files
+need to be combined with data in an existing Stats object, the add()
+method can be used.
+
+
+METHOD	strip_dirs()
+
+This method for the Stats class removes all leading path information
+from file names.  It is very useful in reducing the size of the
+printout to fit within (close to) 80 columns.  This method modifies
+the object, and the striped information is lost.  After performing a
+strip operation, the object is considered to have its entries in a
+"random" order, as it was just after object initialization and
+loading.  If strip_dir() causes two function names to be
+indistinguishable (i.e., they are on the same line of the same
+filename, and have the same function name), then the statistics for
+these two entries are accumulated into a single entry.
+
+
+METHOD	add(filename, ...)
+
+This methods of the Stats class accumulates additional profiling
+information into the current profiling object.  Its arguments should
+refer to filenames created my the corresponding version of
+profile.run().  Statistics for identically named (re: file, line,
+name) functions are automatically accumulated into single function
+statistics.
+
+
+METHOD	sort_stats(key, ...)
+
+This method modifies the Stats object by sorting it according to the
+supplied criteria.  The argument is typically a string identifying the
+basis of a sort (example: "time" or "name").
+
+When more than one key is provided, then additional keys are used as
+secondary criteria when the there is equality in all keys selected
+before them.  For example, sort_stats('name', 'file') will sort all
+the entries according to their function name, and resolve all ties
+(identical function names) by sorting by file name.
+
+Abbreviations can be used for any key names, as long as the
+abbreviation is unambiguous.  The following are the keys currently
+defined: 
+
+		Valid Arg       Meaning
+		  "calls"      call count
+		  "cumulative" cumulative time
+		  "file"       file name
+		  "module"     file name
+		  "pcalls"     primitive call count
+		  "line"       line number
+		  "name"       function name
+		  "nfl"        name/file/line
+		  "stdname"    standard name
+		  "time"       internal time
+
+Note that all sorts on statistics are in descending order (placing most
+time consuming items first), where as name, file, and line number
+searches are in ascending order (i.e., alphabetical). The subtle
+distinction between "nfl" and "stdname" is that the standard name is a
+sort of the name as printed, which means that the embedded line
+numbers get compared in an odd way.  For example, lines 3, 20, and 40
+would (if the file names were the same) appear in the string order
+"20" "3" and "40".  In contrast, "nfl" does a numeric compare of the
+line numbers.  In fact, sort_stats("nfl") is the same as
+sort_stats("name", "file", "line").
+
+For compatibility with the standard profiler, the numeric argument -1,
+0, 1, and 2 are permitted.  They are interpreted as "stdname",
+"calls", "time", and "cumulative" respectively.  If this old style
+format (numeric) is used, only one sort key (the numeric key) will be
+used, and additionally arguments will be silently ignored.
+
+
+METHOD	reverse_order()
+
+This method for the Stats class reverses the ordering of the basic
+list within the object.  This method is provided primarily for
+compatibility with the standard profiler.  Its utility is questionable
+now that ascending vs descending order is properly selected based on
+the sort key of choice.
+
+
+METHOD	print_stats(restriction, ...)
+
+This method for the Stats class prints out a report as described in
+the profile.run() definition.  
+
+The order of the printing is based on the last sort_stats() operation
+done on the object (subject to caveats in add() and strip_dirs()).
+
+The arguments provided (if any) can be used to limit the list down to
+the significant entries.  Initially, the list is taken to be the
+complete set of profiled functions.  Each restriction is either an
+integer (to select a count of lines), or a decimal fraction between
+0.0 and 1.0 inclusive (to select a percentage of lines), or a regular
+expression (to pattern match the standard name that is printed).  If
+several restrictions are provided, then they are applied sequentially.
+For example:
+
+	print_stats(.1, "foo:")
+
+would first limit the printing to first 10% of list, and then only 
+print functions that were part of filename ".*foo:".  In contrast, the
+command: 
+
+	print_stats("foo:", .1)
+
+would limit the list to all functions having file names ".*foo:", and
+then proceed to only print the first 10% of them.
+
+
+METHOD	print_callers(restrictions, ...)
+
+This method for the Stats class prints a list of all functions that
+called each function in the profiled database.  The ordering is
+identical to that provided by print_stats(), and the definition of the
+restricting argument is also identical.  For convenience, a number is
+shown in parentheses after each caller to show how many times this
+specific call was made.  A second non-parenthesized number is the
+cumulative time spent in the function at the right.
+
+
+METHOD	print_callees(restrictions, ...)
+
+This method for the Stats class prints a list of all function that
+were called by the indicated function.  Aside from this reversal of
+direction of calls (re: called vs was called by), the arguments and
+ordering are identical to the print_callers() method.
+
+
+METHOD	ignore()
+
+This method of the Stats class is used to dispose of the value
+returned by earlier methods.  All standard methods in this class
+return the instance that is being processed, so that the commands can
+be strung together.  For example:
+
+pstats.Stats('foofile').strip_dirs().sort_stats('cum').print_stats().ignore()
+
+would perform all the indicated functions, but it would not return
+the final reference to the Stats instance.
+
+
+
+	
+LIMITATIONS
+
+There are two fundamental limitations on this profiler.  The first is
+that it relies on the Python interpreter to dispatch "call", "return",
+and "exception" events.  Compiled C code does not get interpreted,
+and hence is "invisible" to the profiler.  All time spent in C code
+(including builtin functions) will be charged to the Python function
+that was invoked the C code.  IF the C code calls out to some native
+Python code, then those calls will be profiled properly.
+
+The second limitation has to do with accuracy of timing information.
+There is a fundamental problem with deterministic profilers involving
+accuracy.  The most obvious restriction is that the underlying "clock"
+is only ticking at a rate (typically) of about .001 seconds.  Hence no
+measurements will be more accurate that that underlying clock.  If
+enough measurements are taken, then the "error" will tend to average
+out. Unfortunately, removing this first error induces a second source
+of error...
+
+The second problem is that it "takes a while" from when an event is
+dispatched until the profiler's call to get the time actually *gets*
+the state of the clock.  Similarly, there is a certain lag when
+exiting the profiler event handler from the time that the clock's
+value was obtained (and then squirreled away), until the user's code
+is once again executing.  As a result, functions that are called many
+times, or call many functions, will typically accumulate this error.
+The error that accumulates in this fashion is typically less than the
+accuracy of the clock (i.e., less than one clock tick), but it *can*
+accumulate and become very significant.  This profiler provides a
+means of calibrating itself for a give platform so that this error can
+be probabilistically (i.e., on the average) removed.  After the
+profiler is calibrated, it will be more accurate (in a least square
+sense), but it will sometimes produce negative numbers (when call
+counts are exceptionally low, and the gods of probability work against
+you :-). )  Do *NOT* be alarmed by negative numbers in the profile.
+They should *only* appear if you have calibrated your profiler, and
+the results are actually better than without calibration.
+
+
+CALIBRATION
+
+The profiler class has a hard coded constant that is added to each
+event handling time to compensate for the overhead of calling the time
+function, and socking away the results.  The following procedure can
+be used to obtain this constant for a given platform (see discussion
+in LIMITATIONS above). 
+
+	import profile
+	pr = profile.Profile()
+	pr.calibrate(100)
+	pr.calibrate(100)
+	pr.calibrate(100)
+
+The argument to calibrate() is the number of times to try to do the
+sample calls to get the CPU times.  If your computer is *very* fast,
+you might have to do:
+
+	pr.calibrate(1000)
+
+or even:
+
+	pr.calibrate(10000)
+
+The object of this exercise is to get a fairly consistent result.
+When you have a consistent answer, you are ready to use that number in
+the source code.  For a Sun Sparcstation 1000 running Solaris 2.3, the
+magical number is about .00053.  If you have a choice, you are better
+off with a smaller constant, and your results will "less often" show
+up as negative in profile statistics.
+
+The following shows how the trace_dispatch() method in the Profile
+class should be modified to install the calibration constant on a Sun
+Sparcstation 1000:
+
+	def trace_dispatch(self, frame, event, arg):
+		t = self.timer()
+		t = t[0] + t[1] - self.t - .00053 # Calibration constant
+
+		if self.dispatch[event](frame,t):
+			t = self.timer()
+			self.t = t[0] + t[1]
+		else:
+			r = self.timer()
+			self.t = r[0] + r[1] - t # put back unrecorded delta
+		return
+
+Note that if there is no calibration constant, then the line
+containing the callibration constant should simply say:
+
+		t = t[0] + t[1] - self.t  # no calibration constant
+
+You can also achieve the same results using a derived class (and the
+profiler will actually run equally fast!!), but the above method is
+the simplest to use.  I could have made the profiler "self
+calibrating", but it would have made the initialization of the
+profiler class slower, and would have required some *very* fancy
+coding, or else the use of a variable where the constant .00053 was
+placed in the code shown.  This is a ****VERY**** critical performance
+section, and there is no reason to use a variable lookup at this
+point, when a constant can be used.
+
+
+EXTENSIONS: Deriving Better Profilers
+
+The Profile class of profile was written so that derived classes
+could be developed to extend the profiler.  Rather than describing all
+the details of such an effort, I'll just present the following two
+examples of derived classes that can be used to do profiling.  If the
+reader is an avid Python programmer, then it should be possible to use
+these as a model and create similar (and perchance better) profile
+classes. 
+
+If all you want to do is change how the timer is called, or which
+timer function is used, then the basic class has an option for that in
+the constructor for the class.  Consider passing the name of a
+function to call into the constructor:
+
+	pr = profile.Profile(your_time_func)
+
+The resulting profiler will call your time function instead of
+os.times().  The function should return either a single number, or a
+list of numbers (like what os.times() returns).  If the function
+returns a single time number, or the list of returned numbers has
+length 2, then you will get an especially fast version of the dispatch
+routine.  
+
+Be warned that you *should* calibrate the profiler class for the
+timer function that you choose.  For most machines, a timer that
+returns a lone integer value will provide the best results in terms of
+low overhead during profiling.  (os.times is *pretty* bad, 'cause it
+returns a tuple of floating point values, so all arithmetic is
+floating point in the profiler!).  If you want to be substitute a
+better timer in the cleanest fashion, you should derive a class, and
+simply put in the replacement dispatch method that better handles your timer
+call, along with the appropriate calibration constant :-).
+
+
+cut here------------------------------------------------------------------
+#****************************************************************************
+# OldProfile class documentation
+#****************************************************************************
+#
+# The following derived profiler simulates the old style profile, providing
+# errant results on recursive functions. The reason for the usefulness of this
+# profiler is that it runs faster (i.e., less overhead) than the old
+# profiler.  It still creates all the caller stats, and is quite
+# useful when there is *no* recursion in the user's code.  It is also
+# a lot more accurate than the old profiler, as it does not charge all
+# its overhead time to the user's code. 
+#****************************************************************************
+class OldProfile(Profile):
+	def trace_dispatch_exception(self, frame, t):
+		rt, rtt, rct, rfn, rframe, rcur = self.cur
+		if rcur and not rframe is frame:
+			return self.trace_dispatch_return(rframe, t)
+		return 0
+
+	def trace_dispatch_call(self, frame, t):
+		fn = `frame.f_code`
+		
+		self.cur = (t, 0, 0, fn, frame, self.cur)
+		if self.timings.has_key(fn):
+			tt, ct, callers = self.timings[fn]
+			self.timings[fn] = tt, ct, callers
+		else:
+			self.timings[fn] = 0, 0, {}
+		return 1
+
+	def trace_dispatch_return(self, frame, t):
+		rt, rtt, rct, rfn, frame, rcur = self.cur
+		rtt = rtt + t
+		sft = rtt + rct
+
+		pt, ptt, pct, pfn, pframe, pcur = rcur
+		self.cur = pt, ptt+rt, pct+sft, pfn, pframe, pcur
+
+		tt, ct, callers = self.timings[rfn]
+		if callers.has_key(pfn):
+			callers[pfn] = callers[pfn] + 1
+		else:
+			callers[pfn] = 1
+		self.timings[rfn] = tt+rtt, ct + sft, callers
+
+		return 1
+
+
+	def snapshot_stats(self):
+		self.stats = {}
+		for func in self.timings.keys():
+			tt, ct, callers = self.timings[func]
+			nor_func = self.func_normalize(func)
+			nor_callers = {}
+			nc = 0
+			for func_caller in callers.keys():
+				nor_callers[self.func_normalize(func_caller)]=\
+					  callers[func_caller]
+				nc = nc + callers[func_caller]
+			self.stats[nor_func] = nc, nc, tt, ct, nor_callers
+
+		
+
+#****************************************************************************
+# HotProfile class documentation
+#****************************************************************************
+#
+# This profiler is the fastest derived profile example.  It does not
+# calculate caller-callee relationships, and does not calculate cumulative
+# time under a function.  It only calculates time spent in a function, so
+# it runs very quickly (re: very low overhead).  In truth, the basic
+# profiler is so fast, that is probably not worth the savings to give
+# up the data, but this class still provides a nice example.
+#****************************************************************************
+class HotProfile(Profile):
+	def trace_dispatch_exception(self, frame, t):
+		rt, rtt, rfn, rframe, rcur = self.cur
+		if rcur and not rframe is frame:
+			return self.trace_dispatch_return(rframe, t)
+		return 0
+
+	def trace_dispatch_call(self, frame, t):
+		self.cur = (t, 0, frame, self.cur)
+		return 1
+
+	def trace_dispatch_return(self, frame, t):
+		rt, rtt, frame, rcur = self.cur
+
+		rfn = `frame.f_code`
+
+		pt, ptt, pframe, pcur = rcur
+		self.cur = pt, ptt+rt, pframe, pcur
+
+		if self.timings.has_key(rfn):
+			nc, tt = self.timings[rfn]
+			self.timings[rfn] = nc + 1, rt + rtt + tt
+		else:
+			self.timings[rfn] =      1, rt + rtt
+
+		return 1
+
+
+	def snapshot_stats(self):
+		self.stats = {}
+		for func in self.timings.keys():
+			nc, tt = self.timings[func]
+			nor_func = self.func_normalize(func)
+			self.stats[nor_func] = nc, nc, tt, 0, {}
+
+		
+
+cut here------------------------------------------------------------------
diff --git a/Lib/profile.py b/Lib/profile.py
index 502a4db..35ed63e 100755
--- a/Lib/profile.py
+++ b/Lib/profile.py
@@ -1,380 +1,80 @@
 #
-# Class for profiling python code.
-# Author: Sjoerd Mullender
-# Hacked somewhat by: Guido van Rossum
+# Class for profiling python code. rev 1.0  6/2/94
 #
-# See the accompanying document profile.doc for more information.
+# Based on prior profile module by Sjoerd Mullender...
+#   which was hacked somewhat by: Guido van Rossum
+#
+# See profile.doc for more information
+
+
+# Copyright 1994, by InfoSeek Corporation, all rights reserved.
+# Written by James Roskind
+# 
+# Permission to use, copy, modify, and distribute this Python software
+# and its associated documentation for any purpose (subject to the
+# restriction in the following sentence) without fee is hereby granted,
+# provided that the above copyright notice appears in all copies, and
+# that both that copyright notice and this permission notice appear in
+# supporting documentation, and that the name of InfoSeek not be used in
+# advertising or publicity pertaining to distribution of the software
+# without specific, written prior permission.  This permission is
+# explicitly restricted to the copying and modification of the software
+# to remain in Python, compiled Python, or other languages (such as C)
+# wherein the modified or derived code is exclusively imported into a
+# Python module.
+# 
+# INFOSEEK CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+# SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+# FITNESS. IN NO EVENT SHALL INFOSEEK CORPORATION BE LIABLE FOR ANY
+# SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+# RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+# CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
 
 import sys
-import codehack
 import os
+import time
 import string
-import fpformat
 import marshal
 
-class Profile:
 
-	def __init__(self):
-		self.timings = {}
-		self.debug = None
-		self.call_level = 0
-		self.profile_func = None
-		self.profiling = 0
-
-	def profile(self, funcname):
-		if not self.profile_func:
-			self.profile_func = {}
-		self.profile_func[funcname] = 1
-
-	def trace_dispatch(self, frame, event, arg):
-		if event == 'call':
-			funcname = codehack.getcodename(frame.f_code)
-			if self.profile_func and not self.profiling:
-				if self.profile_func.has_key(funcname):
-					return
-				self.profiling = 1
-			t = os.times()
-			t = t[0] + t[1]
-			if frame.f_locals.has_key('__key'):
-				key = frame.f_locals['__key']
-			else:
-				lineno = codehack.getlineno(frame.f_code)
-				filename = frame.f_code.co_filename
-				key = filename + ':' + `lineno` + '(' + funcname + ')'
-				frame.f_locals['__key'] = key
-			self.call_level = depth(frame)
-			self.cur_frame = frame
-			pframe = frame.f_back
-			if self.debug:
-				s0 = 'call: ' + key + ' depth: ' + `self.call_level` + ' time: ' + `t`
-			if pframe:
-				if pframe.f_locals.has_key('__key'):
-					pkey = pframe.f_locals['__key']
-				else:
-					pkey = pframe.f_code.co_filename + \
-						  ':' + \
-						  `codehack.getlineno(pframe.f_code)` \
-						  + '(' + \
-						  codehack.getcodename(pframe.f_code) \
-						  + ')'
-					pframe.f_locals['__key'] = pkey
-				if self.debug:
-					s1 = 'parent: ' + pkey
-				if pframe.f_locals.has_key('__start_time'):
-					st = pframe.f_locals['__start_time']
-					nc, tt, ct, callers, callees = \
-						self.timings[pkey]
-					if self.debug:
-						s1 = s1+' before: st='+`st`+' nc='+`nc`+' tt='+`tt`+' ct='+`ct`
-					if callers.has_key(key):
-						callers[key] = callers[key] + 1
-					else:
-						callers[key] = 1
-					if self.debug:
-						s1 = s1+' after: st='+`st`+' nc='+`nc`+' tt='+`tt+(t-st)`+' ct='+`ct`
-					self.timings[pkey] = nc, tt + (t - st), ct, callers, callees
-			if self.timings.has_key(key):
-				nc, tt, ct, callers, callees = self.timings[key]
-			else:
-				nc, tt, ct, callers, callees = 0, 0, 0, {}, {}
-			if self.debug:
-				s0 = s0+' before: nc='+`nc`+' tt='+`tt`+' ct='+`ct`
-				s0 = s0+' after: nc='+`nc+1`+' tt='+`tt`+' ct='+`ct`
-			if pframe:
-				if callees.has_key(pkey):
-					callees[pkey] = callees[pkey] + 1
-				else:
-					callees[pkey] = 1
-			self.timings[key] = nc + 1, tt, ct, callers, callees
-			frame.f_locals['__start_time'] = t
-			if self.debug:
-				print s0
-				print s1
-			return
-		if event == 'return':
-			if self.profile_func:
-				if not self.profiling:
-					return
-				if self.profile_func.has_key( \
-					codehack.getcodename(frame.f_code)):
-					self.profiling = 0
-			self.call_level = depth(frame)
-			self.cur_frame = frame
-			pframe = frame.f_back
-			if self.debug:
-				s0 = 'return: '
-			else:
-				s0 = None
-			self.handle_return(pframe, frame, s0)
-			return
-		if event == 'exception':
-			if self.profile_func and not self.profiling:
-				return
-			call_level = depth(frame)
-			if call_level < self.call_level:
-				if call_level <> self.call_level - 1:
-					print 'heh!',call_level,self.call_level
-				if self.debug:
-					s0 = 'exception: '
-				else:
-					s0 = None
-				self.handle_return(self.cur_frame, frame, s0)
-			self.call_level = call_level
-			self.cur_frame = frame
-			return
-		print 'profile.Profile.dispatch: unknown debugging event:',
-		print `event`
-		return
-
-	def handle_return(self, pframe, frame, s0):
-		t = os.times()
-		t = t[0] + t[1]
-		if frame.f_locals.has_key('__key'):
-			key = frame.f_locals['__key']
-		else:
-			funcname = codehack.getcodename(frame.f_code)
-			lineno = codehack.getlineno(frame.f_code)
-			filename = frame.f_code.co_filename
-			key = filename + ':' + `lineno` + '(' + funcname + ')'
-			frame.f_locals['__key'] = key
-		if self.debug:
-			s0 = s0 + key + ' depth: ' + `self.call_level` + ' time: ' + `t`
-		if pframe:
-			if pframe.f_locals.has_key('__key'):
-				pkey = pframe.f_locals['__key']
-			else:
-				funcname = codehack.getcodename(frame.f_code)
-				lineno = codehack.getlineno(frame.f_code)
-				filename = frame.f_code.co_filename
-				pkey = filename + ':' + `lineno` + '(' + funcname + ')'
-				pframe.f_locals['__key'] = pkey
-			if self.debug:
-				s1 = 'parent: '+pkey
-			if pframe.f_locals.has_key('__start_time') and \
-				  self.timings.has_key(pkey):
-				st = pframe.f_locals['__start_time']
-				nc, tt, ct, callers, callees = \
-					self.timings[pkey]
-				if self.debug:
-					s1 = s1+' before: st='+`st`+' nc='+`nc`+' tt='+`tt`+' ct='+`ct`
-					s1 = s1+' after: st='+`t`+' nc='+`nc`+' tt='+`tt`+' ct='+`ct+(t-st)`
-				self.timings[pkey] = \
-					nc, tt, ct + (t - st), callers, callees
-				pframe.f_locals['__start_time'] = t
-		if self.timings.has_key(key):
-			nc, tt, ct, callers, callees = self.timings[key]
-		else:
-			nc, tt, ct, callers, callees = 0, 0, 0, {}, {}
-		if frame.f_locals.has_key('__start_time'):
-			st = frame.f_locals['__start_time']
-		else:
-			st = t
-		if self.debug:
-			s0 = s0+' before: st='+`st`+' nc='+`nc`+' tt='+`tt`+' ct='+`ct`
-			s0 = s0+' after: nc='+`nc`+' tt='+`tt+(t-st)`+' ct='+`ct+(t-st)`
-			print s0
-			print s1
-		self.timings[key] = \
-			nc, tt + (t - st), ct + (t - st), callers, callees
-
-	def print_stats(self):
-		# Print in reverse order by ct
-		print_title()
-		list = []
-		for key in self.timings.keys():
-			nc, tt, ct, callers, callees = self.timings[key]
-			if nc == 0:
-				continue
-			list.append(ct, tt, nc, key)
-		list.sort()
-		list.reverse()
-		for ct, tt, nc, key in list:
-			print_line(nc, tt, ct, os.path.basename(key))
-
-	def dump_stats(self, file):
-		f = open(file, 'w')
-		marshal.dump(self.timings, f)
-		f.close()
-
-	# The following two methods can be called by clients to use
-	# a profiler to profile a statement, given as a string.
-	
-	def run(self, cmd):
-		import __main__
-		dict = __main__.__dict__
-		self.runctx(cmd, dict, dict)
-	
-	def runctx(self, cmd, globals, locals):
-		sys.setprofile(self.trace_dispatch)
-		try:
-			exec(cmd + '\n', globals, locals)
-		finally:
-			sys.setprofile(None)
-
-	# This method is more useful to profile a single function call.
-
-	def runcall(self, func, *args):
-		sys.setprofile(self.trace_dispatch)
-		try:
-			apply(func, args)
-		finally:
-			sys.setprofile(None)
+# Global variables
+func_norm_dict = {}
+func_norm_counter = 0
+pid_string = `os.getpid()`
 
 
-def depth(frame):
-	d = 0
-	while frame:
-		d = d + 1
-		frame = frame.f_back
-	return d
+# Optimized intermodule references
+ostimes = os.times
 
-class Stats:
-	def __init__(self, file):
-		f = open(file, 'r')
-		self.stats = marshal.load(f)
-		f.close()
-		self.stats_list = None
 
-	def print_stats(self):
-		print_title()
-		if self.stats_list:
-			for i in range(len(self.stats_list)):
-				nc, tt, ct, callers, callees, key = \
-					self.stats_list[i]
-				print_line(nc, tt, ct, key)
-		else:
-			for key in self.stats.keys():
-				nc, tt, ct, callers, callees = self.stats[key]
-				print_line(nc, tt, ct, key)
+# Sample timer for use with 
+#i_count = 0
+#def integer_timer():
+#	global i_count
+#	i_count = i_count + 1
+#	return i_count
+#itimes = integer_timer # replace with C coded timer returning integers
 
-	def print_callers(self):
-		if self.stats_list:
-			for i in range(len(self.stats_list)):
-				nc, tt, ct, callers, callees, key = \
-					self.stats_list[i]
-				print key,
-				for func in callers.keys():
-					print func+'('+`callers[func]`+')',
-				print
-		else:
-			for key in self.stats.keys():
-				nc, tt, ct, callers, callees = self.stats[key]
-				print key,
-				for func in callers.keys():
-					print func+'('+`callers[func]`+')',
-				print
+#**************************************************************************
+# The following are the static member functions for the profiler class
+# Note that an instance of Profile() is *not* needed to call them.
+#**************************************************************************
 
-	def print_callees(self):
-		if self.stats_list:
-			for i in range(len(self.stats_list)):
-				nc, tt, ct, callers, callees, key = \
-					self.stats_list[i]
-				print key,
-				for func in callees.keys():
-					print func+'('+`callees[func]`+')',
-				print
-		else:
-			for key in self.stats.keys():
-				nc, tt, ct, callers, callees = self.stats[key]
-				print key,
-				for func in callees.keys():
-					print func+'('+`callees[func]`+')',
-				print
-
-	def sort_stats(self, field):
-		stats_list = []
-		for key in self.stats.keys():
-			t = self.stats[key]
-			nt = ()
-			for i in range(len(t)):
-				if i == field:
-					nt = (t[i],) + nt[:]
-				else:
-					nt = nt[:] + (t[i],)
-			if field == -1:
-				nt = (key,) + nt
-			else:
-				nt = nt + (key,)
-			stats_list.append(nt)
-		stats_list.sort()
-		self.stats_list = []
-		for i in range(len(stats_list)):
-			t = stats_list[i]
-			if field == -1:
-				nt = t[1:] + t[0:1]
-			else:
-				nt = t[1:]
-				nt = nt[:field] + t[0:1] + nt[field:]
-			self.stats_list.append(nt)
-
-	def reverse_order(self):
-		self.stats_list.reverse()
-
-	def strip_dirs(self):
-		newstats = {}
-		for key in self.stats.keys():
-			nc, tt, ct, callers, callees = self.stats[key]
-			newkey = os.path.basename(key)
-			newcallers = {}
-			for c in callers.keys():
-				newcallers[os.path.basename(c)] = callers[c]
-			newcallees = {}
-			for c in callees.keys():
-				newcallees[os.path.basename(c)] = callees[c]
-			newstats[newkey] = nc, tt, ct, newcallers, newcallees
-		self.stats = newstats
-		self.stats_list = None
-
-def print_title():
-	print string.rjust('ncalls', 8),
-	print string.rjust('tottime', 8),
-	print string.rjust('percall', 8),
-	print string.rjust('cumtime', 8),
-	print string.rjust('percall', 8),
-	print 'filename:lineno(function)'
-
-def print_line(nc, tt, ct, key):
-	print string.rjust(`nc`, 8),
-	print f8(tt),
-	if nc == 0:
-		print ' '*8,
-	else:
-		print f8(tt/nc),
-	print f8(ct),
-	if nc == 0:
-		print ' '*8,
-	else:
-		print f8(ct/nc),
-	print key
-
-def f8(x):
-	return string.rjust(fpformat.fix(x, 3), 8)
 
 # simplified user interface
 def run(statement, *args):
 	prof = Profile()
 	try:
-		prof.run(statement)
+		prof = prof.run(statement)
 	except SystemExit:
 		pass
-	if len(args) == 0:
-		prof.print_stats()
-	else:
+	if args:
 		prof.dump_stats(args[0])
-
-# test command with debugging
-def debug():
-	prof = Profile()
-	prof.debug = 1
-	try:
-		prof.run('import x; x.main()')
-	except SystemExit:
-		pass
-	prof.print_stats()
-
-# test command
-def test():
-	run('import x; x.main()')
+	else:
+		return prof.print_stats()
 
 # print help
 def help():
@@ -387,3 +87,526 @@
 	else:
 		print 'Sorry, can\'t find the help file "profile.doc"',
 		print 'along the Python search path'
+
+
+#**************************************************************************
+# class Profile documentation:
+#**************************************************************************
+# self.cur is always a tuple.  Each such tuple corresponds to a stack
+# frame that is currently active (self.cur[-2]).  The following are the
+# definitions of its members.  We use this external "parallel stack" to
+# avoid contaminating the program that we are profiling. (old profiler
+# used to write into the frames local dictionary!!) Derived classes
+# can change the definition of some entries, as long as they leave
+# [-2:] intact.
+#
+# [ 0] = Time that needs to be charged to the parent frame's function.  It is
+#        used so that a function call will not have to access the timing data
+#        for the parents frame.
+# [ 1] = Total time spent in this frame's function, excluding time in
+#        subfunctions
+# [ 2] = Cumulative time spent in this frame's function, including time in
+#        all subfunctions to this frame.
+# [-3] = Name of the function that corresonds to this frame.  
+# [-2] = Actual frame that we correspond to (used to sync exception handling)
+# [-1] = Our parent 6-tuple (corresonds to frame.f_back)
+#**************************************************************************
+# Timing data for each function is stored as a 5-tuple in the dictionary
+# self.timings[].  The index is always the name stored in self.cur[4].
+# The following are the definitions of the members:
+#
+# [0] = The number of times this function was called, not counting direct
+#       or indirect recursion,
+# [1] = Number of times this function appears on the stack, minus one
+# [2] = Total time spent internal to this function
+# [3] = Cumulative time that this function was present on the stack.  In
+#       non-recursive functions, this is the total execution time from start
+#       to finish of each invocation of a function, including time spent in
+#       all subfunctions.
+# [5] = A dictionary indicating for each function name, the number of times
+#       it was called by us.
+#**************************************************************************
+# We produce function names via a repr() call on the f_code object during
+# profiling. This save a *lot* of CPU time.  This results in a string that
+# always looks like:
+#   <code object main at 87090, file "/a/lib/python-local/myfib.py", line 76>
+# After we "normalize it, it is a tuple of filename, line, function-name.
+# We wait till we are done profiling to do the normalization.
+# *IF* this repr format changes, then only the normalization routine should
+# need to be fixed.
+#**************************************************************************
+class Profile:
+
+	def __init__(self, *arg):
+		self.timings = {}
+		self.cur = None
+		self.cmd = ""
+
+		self.dispatch = {  \
+			  'call'     : self.trace_dispatch_call, \
+			  'return'   : self.trace_dispatch_return, \
+			  'exception': self.trace_dispatch_exception, \
+			  }
+
+		if not arg:
+			self.timer = os.times
+			self.dispatcher = self.trace_dispatch
+		else:
+			self.timer = arg[0]
+			t = self.timer() # test out timer function
+			try:
+				if len(t) == 2:
+					self.dispatcher = self.trace_dispatch
+				else:
+					self.dispatcher = self.trace_dispatch_r
+			except:
+				self.dispatcher = self.trace_dispatch_i
+		self.t = self.get_time()
+		self.simulate_call('profiler')
+
+
+	def get_time(self): # slow simulation of method to acquire time
+		t = self.timer()
+		if type(t) == type(()) or type(t) == type([]):
+			t = reduce(lambda x,y: x+y, t, 0)
+		return t
+		
+
+	# Heavily optimized dispatch routine for os.times() timer
+
+	def trace_dispatch(self, frame, event, arg):
+		t = self.timer()
+		t = t[0] + t[1] - self.t        # No Calibration constant
+		# t = t[0] + t[1] - self.t - .00053 # Calibration constant
+
+		if self.dispatch[event](frame,t):
+			t = self.timer()
+			self.t = t[0] + t[1]
+		else:
+			r = self.timer()
+			self.t = r[0] + r[1] - t # put back unrecorded delta
+		return
+
+
+
+	# Dispatch routine for best timer program (return = scalar integer)
+
+	def trace_dispatch_i(self, frame, event, arg):
+		t = self.timer() - self.t # - 1 # Integer calibration constant
+		if self.dispatch[event](frame,t):
+			self.t = self.timer()
+		else:
+			self.t = self.timer() - t  # put back unrecorded delta
+		return
+
+
+	# SLOW generic dispatch rountine for timer returning lists of numbers
+
+	def trace_dispatch_l(self, frame, event, arg):
+		t = self.get_time() - self.t
+
+		if self.dispatch[event](frame,t):
+			self.t = self.get_time()
+		else:
+			self.t = self.get_time()-t # put back unrecorded delta
+		return
+
+
+	def trace_dispatch_exception(self, frame, t):
+		rt, rtt, rct, rfn, rframe, rcur = self.cur
+		if (not rframe is frame) and rcur:
+			return self.trace_dispatch_return(rframe, t)
+		return 0
+
+
+	def trace_dispatch_call(self, frame, t):
+		fn = `frame.f_code` 
+
+		# The following should be about the best approach, but
+		# we would need a function that maps from id() back to
+		# the actual code object.  
+		#     fn = id(frame.f_code)
+		# Note we would really use our own function, which would
+		# return the code address, *and* bump the ref count.  We
+		# would then fix up the normalize function to do the
+		# actualy repr(fn) call.
+
+		# The following is an interesting alternative
+		# It doesn't do as good a job, and it doesn't run as
+		# fast 'cause repr() is written in C, and this is Python.
+		#fcode = frame.f_code
+		#code = fcode.co_code
+		#if ord(code[0]) == 127: #  == SET_LINENO
+		#	# see "opcode.h" in the Python source
+		#	fn = (fcode.co_filename, ord(code[1]) | \
+		#		  ord(code[2]) << 8, fcode.co_name)
+		#else:
+		#	fn = (fcode.co_filename, 0, fcode.co_name)
+
+		self.cur = (t, 0, 0, fn, frame, self.cur)
+		if self.timings.has_key(fn):
+			cc, ns, tt, ct, callers = self.timings[fn]
+			self.timings[fn] = cc, ns + 1, tt, ct, callers
+		else:
+			self.timings[fn] = 0, 0, 0, 0, {}
+		return 1
+
+	def trace_dispatch_return(self, frame, t):
+		# if not frame is self.cur[-2]: raise "Bad return", self.cur[3]
+
+		# Prefix "r" means part of the Returning or exiting frame
+		# Prefix "p" means part of the Previous or older frame
+
+		rt, rtt, rct, rfn, frame, rcur = self.cur
+		rtt = rtt + t
+		sft = rtt + rct
+
+		pt, ptt, pct, pfn, pframe, pcur = rcur
+		self.cur = pt, ptt+rt, pct+sft, pfn, pframe, pcur
+
+		cc, ns, tt, ct, callers = self.timings[rfn]
+		if not ns:
+			ct = ct + sft
+			cc = cc + 1
+		if callers.has_key(pfn):
+			callers[pfn] = callers[pfn] + 1  # hack: gather more
+			# stats such as the amount of time added to ct courtesy
+			# of this specific call, and the contribution to cc
+			# courtesy of this call.
+		else:
+			callers[pfn] = 1
+		self.timings[rfn] = cc, ns - 1, tt+rtt, ct, callers
+
+		return 1
+
+	# The next few function play with self.cmd. By carefully preloading
+	# our paralell stack, we can force the profiled result to include
+	# an arbitrary string as the name of the calling function.
+	# We use self.cmd as that string, and the resulting stats look
+	# very nice :-).
+
+	def set_cmd(self, cmd):
+		if self.cur[-1]: return   # already set
+		self.cmd = cmd
+		self.simulate_call(cmd)
+
+	class fake_code:
+		def __init__(self, filename, line, name):
+			self.co_filename = filename
+			self.co_line = line
+			self.co_name = name
+			self.co_code = '\0'  # anything but 127
+
+		def __repr__(self):
+			return (self.co_filename, self.co_line, self.co_name)
+
+	class fake_frame:
+		def __init__(self, code, prior):
+			self.f_code = code
+			self.f_back = prior
+			
+	def simulate_call(self, name):
+		code = self.fake_code('profile', 0, name)
+		if self.cur:
+			pframe = self.cur[-2]
+		else:
+			pframe = None
+		frame = self.fake_frame(code, pframe)
+		a = self.dispatch['call'](frame, 0)
+		return
+
+	# collect stats from pending stack, including getting final
+	# timings for self.cmd frame.
+	
+	def simulate_cmd_complete(self):   
+		t = self.get_time() - self.t
+		while self.cur[-1]:
+			# We *can* cause assertion errors here if
+			# dispatch_trace_return checks for a frame match!
+			a = self.dispatch['return'](self.cur[-2], t)
+			t = 0
+		self.t = self.get_time() - t
+
+	
+	def print_stats(self):
+		import pstats
+		pstats.Stats(self).strip_dirs().sort_stats(-1). \
+			  print_stats()
+
+	def dump_stats(self, file):
+		f = open(file, 'w')
+		self.create_stats()
+		marshal.dump(self.stats, f)
+		f.close()
+
+	def create_stats(self):
+		self.simulate_cmd_complete()
+		self.snapshot_stats()
+
+	def snapshot_stats(self):
+		self.stats = {}
+		for func in self.timings.keys():
+			cc, ns, tt, ct, callers = self.timings[func]
+			nor_func = self.func_normalize(func)
+			nor_callers = {}
+			nc = 0
+			for func_caller in callers.keys():
+				nor_callers[self.func_normalize(func_caller)]=\
+					  callers[func_caller]
+				nc = nc + callers[func_caller]
+			self.stats[nor_func] = cc, nc, tt, ct, nor_callers
+
+
+	# Override the following function if you can figure out
+	# a better name for the binary f_code entries.  I just normalize
+	# them sequentially in a dictionary.  It would be nice if we could
+	# *really* see the name of the underlying C code :-).  Sometimes
+	#  you can figure out what-is-what by looking at caller and callee
+	# lists (and knowing what your python code does).
+	
+	def func_normalize(self, func_name):
+		global func_norm_dict
+		global func_norm_counter
+		global func_sequence_num
+
+		if func_norm_dict.has_key(func_name):
+			return func_norm_dict[func_name]
+		if type(func_name) == type(""):
+			long_name = string.split(func_name)
+			file_name = long_name[6][1:-2]
+			func = long_name[2]
+			lineno = long_name[8][:-1]
+			if '?' == func:   # Until I find out how to may 'em...
+				file_name = 'python'
+				func_norm_counter = func_norm_counter + 1
+				func = pid_string + ".C." + `func_norm_counter`
+			result =  file_name ,  string.atoi(lineno) , func
+		else:
+			result = func_name
+		func_norm_dict[func_name] = result
+		return result
+
+
+	# The following two methods can be called by clients to use
+	# a profiler to profile a statement, given as a string.
+	
+	def run(self, cmd):
+		import __main__
+		dict = __main__.__dict__
+		self.runctx(cmd, dict, dict)
+		return self
+	
+	def runctx(self, cmd, globals, locals):
+		self.set_cmd(cmd)
+		sys.setprofile(self.trace_dispatch)
+		try:
+			exec(cmd, globals, locals)
+		finally:
+			sys.setprofile(None)
+
+	# This method is more useful to profile a single function call.
+	def runcall(self, func, *args):
+		self.set_cmd(func.__name__)
+		sys.setprofile(self.trace_dispatch)
+		try:
+			apply(func, args)
+		finally:
+			sys.setprofile(None)
+		return self
+
+
+        #******************************************************************
+	# The following calculates the overhead for using a profiler.  The
+	# problem is that it takes a fair amount of time for the profiler
+	# to stop the stopwatch (from the time it recieves an event).
+	# Similarly, there is a delay from the time that the profiler
+	# re-starts the stopwatch before the user's code really gets to
+	# continue.  The following code tries to measure the difference on
+	# a per-event basis. The result can the be placed in the
+	# Profile.dispatch_event() routine for the given platform.  Note
+	# that this difference is only significant if there are a lot of
+	# events, and relatively little user code per event.  For example,
+	# code with small functions will typically benefit from having the
+	# profiler calibrated for the current platform.  This *could* be
+	# done on the fly during init() time, but it is not worth the
+	# effort.  Also note that if too large a value specified, then
+	# execution time on some functions will actually appear as a
+	# negative number.  It is *normal* for some functions (with very
+	# low call counts) to have such negative stats, even if the
+	# calibration figure is "correct." 
+	#
+	# One alternative to profile-time calibration adjustments (i.e.,
+	# adding in the magic little delta during each event) is to track
+	# more carefully the number of events (and cumulatively, the number
+	# of events during sub functions) that are seen.  If this were
+	# done, then the arithmetic could be done after the fact (i.e., at
+	# display time).  Currintly, we track only call/return events.
+	# These values can be deduced by examining the callees and callers
+	# vectors for each functions.  Hence we *can* almost correct the
+	# internal time figure at print time (note that we currently don't
+	# track exception event processing counts).  Unfortunately, there
+	# is currently no similar information for cumulative sub-function
+	# time.  It would not be hard to "get all this info" at profiler
+	# time.  Specifically, we would have to extend the tuples to keep
+	# counts of this in each frame, and then extend the defs of timing
+	# tuples to include the significant two figures. I'm a bit fearful
+	# that this additional feature will slow the heavily optimized
+	# event/time ratio (i.e., the profiler would run slower, fur a very
+	# low "value added" feature.) 
+	#
+	# Plugging in the calibration constant doesn't slow down the
+	# profiler very much, and the accuracy goes way up.
+	#**************************************************************
+	
+        def calibrate(self, m):
+		n = m
+		s = self.timer()
+		while n:
+			self.simple()
+			n = n - 1
+		f = self.timer()
+		my_simple = f[0]+f[1]-s[0]-s[1]
+		#print "Simple =", my_simple,
+
+		n = m
+		s = self.timer()
+		while n:
+			self.instrumented()
+			n = n - 1
+		f = self.timer()
+		my_inst = f[0]+f[1]-s[0]-s[1]
+		# print "Instrumented =", my_inst
+		avg_cost = (my_inst - my_simple)/m
+		#print "Delta/call =", avg_cost, "(profiler fixup constant)"
+		return avg_cost
+
+	# simulate a program with no profiler activity
+        def simple(self):      
+		a = 1
+		pass
+
+	# simulate a program with call/return event processing
+        def instrumented(self):
+		a = 1
+		self.profiler_simulation(a, a, a)
+
+	# simulate an event processing activity (from user's perspective)
+	def profiler_simulation(self, x, y, z):  
+		t = self.timer()
+		t = t[0] + t[1]
+		self.ut = t
+
+
+
+#****************************************************************************
+# OldProfile class documentation
+#****************************************************************************
+#
+# The following derived profiler simulates the old style profile, providing
+# errant results on recursive functions. The reason for the usefulnes of this
+# profiler is that it runs faster (i.e., less overhead).  It still creates
+# all the caller stats, and is quite useful when there is *no* recursion
+# in the user's code.
+#
+# This code also shows how easy it is to create a modified profiler.
+#****************************************************************************
+class OldProfile(Profile):
+	def trace_dispatch_exception(self, frame, t):
+		rt, rtt, rct, rfn, rframe, rcur = self.cur
+		if rcur and not rframe is frame:
+			return self.trace_dispatch_return(rframe, t)
+		return 0
+
+	def trace_dispatch_call(self, frame, t):
+		fn = `frame.f_code`
+		
+		self.cur = (t, 0, 0, fn, frame, self.cur)
+		if self.timings.has_key(fn):
+			tt, ct, callers = self.timings[fn]
+			self.timings[fn] = tt, ct, callers
+		else:
+			self.timings[fn] = 0, 0, {}
+		return 1
+
+	def trace_dispatch_return(self, frame, t):
+		rt, rtt, rct, rfn, frame, rcur = self.cur
+		rtt = rtt + t
+		sft = rtt + rct
+
+		pt, ptt, pct, pfn, pframe, pcur = rcur
+		self.cur = pt, ptt+rt, pct+sft, pfn, pframe, pcur
+
+		tt, ct, callers = self.timings[rfn]
+		if callers.has_key(pfn):
+			callers[pfn] = callers[pfn] + 1
+		else:
+			callers[pfn] = 1
+		self.timings[rfn] = tt+rtt, ct + sft, callers
+
+		return 1
+
+
+	def snapshot_stats(self):
+		self.stats = {}
+		for func in self.timings.keys():
+			tt, ct, callers = self.timings[func]
+			nor_func = self.func_normalize(func)
+			nor_callers = {}
+			nc = 0
+			for func_caller in callers.keys():
+				nor_callers[self.func_normalize(func_caller)]=\
+					  callers[func_caller]
+				nc = nc + callers[func_caller]
+			self.stats[nor_func] = nc, nc, tt, ct, nor_callers
+
+		
+
+#****************************************************************************
+# HotProfile class documentation
+#****************************************************************************
+#
+# This profiler is the fastest derived profile example.  It does not
+# calculate caller-callee relationships, and does not calculate cumulative
+# time under a function.  It only calculates time spent in a function, so
+# it runs very quickly (re: very low overhead)
+#****************************************************************************
+class HotProfile(Profile):
+	def trace_dispatch_exception(self, frame, t):
+		rt, rtt, rfn, rframe, rcur = self.cur
+		if rcur and not rframe is frame:
+			return self.trace_dispatch_return(rframe, t)
+		return 0
+
+	def trace_dispatch_call(self, frame, t):
+		self.cur = (t, 0, frame, self.cur)
+		return 1
+
+	def trace_dispatch_return(self, frame, t):
+		rt, rtt, frame, rcur = self.cur
+
+		rfn = `frame.f_code`
+
+		pt, ptt, pframe, pcur = rcur
+		self.cur = pt, ptt+rt, pframe, pcur
+
+		if self.timings.has_key(rfn):
+			nc, tt = self.timings[rfn]
+			self.timings[rfn] = nc + 1, rt + rtt + tt
+		else:
+			self.timings[rfn] =      1, rt + rtt
+
+		return 1
+
+
+	def snapshot_stats(self):
+		self.stats = {}
+		for func in self.timings.keys():
+			nc, tt = self.timings[func]
+			nor_func = self.func_normalize(func)
+			self.stats[nor_func] = nc, nc, tt, 0, {}
+
+		
+
+#****************************************************************************
+def Stats(*args):
+	print 'Report generating functions are in the "pstats" module\a'
diff --git a/Lib/regexp.py b/Lib/regexp.py
index 6136814..4b5db73 100644
--- a/Lib/regexp.py
+++ b/Lib/regexp.py
@@ -10,13 +10,7 @@
 			self.prog = regex.compile(pat)
 		finally:
 			xxx = regex.set_syntax(save_syntax)
-	def match(self, *args):
-		if len(args) == 2:
-			str, offset = args
-		elif len(args) == 1:
-			str, offset = args[0], 0
-		else:
-			raise TypeError, 'wrong argument count'
+	def match(self, str, offset = 0):
 		if self.prog.search(str, offset) < 0:
 			return ()
 		regs = self.prog.regs
diff --git a/Lib/repr.py b/Lib/repr.py
index 50ee6c9..2f4a0bf 100644
--- a/Lib/repr.py
+++ b/Lib/repr.py
@@ -77,6 +77,19 @@
 			j = max(0, self.maxlong-3-i)
 			s = s[:i] + '...' + s[len(s)-j:]
 		return s
+	def repr_instance(self, x, level):
+		try:
+			s = `x`
+			# Bugs in x.__repr__() can cause arbitrary
+			# exceptions -- then make up something
+		except:
+			return '<' + x.__class__.__name__ + ' instance at ' + \
+				  hex(id(x))[2:] + '>'
+		if len(s) > self.maxstring:
+			i = max(0, (self.maxstring-3)/2)
+			j = max(0, self.maxstring-3-i)
+			s = s[:i] + '...' + s[len(s)-j:]
+		return s
 
 aRepr = Repr()
 repr = aRepr.repr
diff --git a/Lib/rfc822.py b/Lib/rfc822.py
index 39ab6a6..580102c 100644
--- a/Lib/rfc822.py
+++ b/Lib/rfc822.py
@@ -1,8 +1,8 @@
 # RFC-822 message manipulation class.
 #
 # XXX This is only a very rough sketch of a full RFC-822 parser;
-# additional methods are needed to parse addresses and dates, and to
-# tokenize lines according to various other syntax rules.
+# in particular the tokenizing of addresses does not adhere to all the
+# quoting rules.
 #
 # Directions for use:
 #
@@ -22,6 +22,17 @@
 # embedded whitespace (including newlines) exactly as they are
 # specified in the header, and leave the case of the text unchanged.
 #
+# For addresses and address lists there are functions
+#   realname, mailaddress = m.getaddr(name) and
+#   list = m.getaddrlist(name)
+# where the latter returns a list of (realname, mailaddr) tuples.
+#
+# There is also a method
+#   time = m.getdate(name)
+# which parses a Date-like field and returns a time-compatible tuple,
+# i.e. a tuple such as returned by time.localtime() or accepted by
+# time.mktime().
+#
 # See the class definition for lower level access methods.
 #
 # There are also some utility functions here.
@@ -29,6 +40,7 @@
 
 import regex
 import string
+import time
 
 
 class Message:
@@ -105,12 +117,13 @@
 
 	# Method to determine whether a line is a legal end of
 	# RFC-822 headers.  You may override this method if your
-	# application wants to bend the rules, e.g. to accept lines
-	# ending in '\r\n', to strip trailing whitespace, or to
-	# recognise MH template separators ('--------'). 
+	# application wants to bend the rules, e.g. to strip trailing
+	# whitespace, or to recognise MH template separators
+	# ('--------').  For convenience (e.g. for code reading from
+	# sockets) a line consisting of \r\n also matches.
 
 	def islast(self, line):
-		return line == '\n'
+		return line == '\n' or line == '\r\n'
 
 
 	# Look through the list of headers and find all lines matching
@@ -178,27 +191,94 @@
 		return string.strip(text)
 
 
-	# XXX The next step would be to define self.getaddr(name)
-	# and self.getaddrlist(name) which would parse a header
-	# consisting of a single mail address and a number of mail
-	# addresses, respectively.  Lower level functions would be
-	# parseaddr(string) and parseaddrlist(string).
+	# Retrieve a single address from a header as a tuple, e.g.
+	# ('Guido van Rossum', 'guido@cwi.nl').
 
-	# XXX Similar, there would be a function self.getdate(name) to
-	# return a date in canonical form (perhaps a number compatible
-	# to time.time()) and a function parsedate(string).
+	def getaddr(self, name):
+		data = self.getheader(name)
+		if not data:
+			return None, None
+		return parseaddr(data)
 
-	# XXX The inverses of the parse functions may also be useful.
+	# Retrieve a list of addresses from a header, where each
+	# address is a tuple as returned by getaddr().
 
+	def getaddrlist(self, name):
+		# XXX This function is not really correct.  The split
+		# on ',' might fail in the case of commas within
+		# quoted strings.
+		data = self.getheader(name)
+		if not data:
+			return []
+		data = string.splitfields(data, ',')
+		for i in range(len(data)):
+			data[i] = parseaddr(data[i])
+		return data
+
+	# Retrieve a date field from a header as a tuple compatible
+	# with time.mktime().
+
+	def getdate(self, name):
+		data = self.getheader(name)
+		if not data:
+			return None
+		return parsedate(data)
+
+
+	# Access as a dictionary (only finds first header of each type):
+
+	def __len__(self):
+		types = {}
+		for line in self.headers:
+			if line[0] in string.whitespace: continue
+			i = string.find(line, ':')
+			if i > 0:
+				name = string.lower(line[:i])
+				types[name] = None
+		return len(types)
+
+	def __getitem__(self, name):
+		value = self.getheader(name)
+		if value is None: raise KeyError, name
+		return value
+
+	def has_key(self, name):
+		value = self.getheader(name)
+		return value is not None
+
+	def keys(self):
+		types = {}
+		for line in self.headers:
+			if line[0] in string.whitespace: continue
+			i = string.find(line, ':')
+			if i > 0:
+				name = line[:i]
+				key = string.lower(name)
+				types[key] = name
+		return types.values()
+
+	def values(self):
+		values = []
+		for name in self.keys():
+			values.append(self[name])
+		return values
+
+	def items(self):
+		items = []
+		for name in self.keys():
+			items.append(name, self[name])
+		return items
 
 
 
 # Utility functions
 # -----------------
 
+# XXX Should fix these to be really conformant.
+# XXX The inverses of the parse functions may also be useful.
+
 
 # Remove quotes from a string.
-# XXX Should fix this to be really conformant.
 
 def unquote(str):
 	if len(str) > 1:
@@ -207,3 +287,107 @@
 		if str[0] == '<' and str[-1:] == '>':
 			return str[1:-1]
 	return str
+
+
+# Parse an address into (name, address) tuple
+
+def parseaddr(address):
+	# This is probably not perfect
+	address = string.strip(address)
+	# Case 1: part of the address is in <xx@xx> form.
+	pos = regex.search('<.*>', address)
+	if pos >= 0:
+		name = address[:pos]
+		address = address[pos:]
+		length = regex.match('<.*>', address)
+		name = name + address[length:]
+		address = address[:length]
+	else:
+		# Case 2: part of the address is in (comment) form
+		pos = regex.search('(.*)', address)
+		if pos >= 0:
+			name = address[pos:]
+			address = address[:pos]
+			length = regex.match('(.*)', name)
+			address = address + name[length:]
+			name = name[:length]
+		else:
+			# Case 3: neither. Only an address
+			name = ''
+	name = string.strip(name)
+	address = string.strip(address)
+	if address and address[0] == '<' and address[-1] == '>':
+		address = address[1:-1]
+	if name and name[0] == '(' and name[-1] == ')':
+		name = name[1:-1]
+	return name, address
+
+
+# Parse a date field
+
+_monthnames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
+	  'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
+
+def parsedate(data):
+	# XXX This completely ignores timezone matters at the moment...
+	data = string.split(data)
+	if data[0][-1] == ',':
+		# There's a dayname here. Skip it
+		del data[0]
+	if len(data) < 5:
+		return None
+	data = data[:5]
+	[dd, mm, yy, tm, tz] = data
+	if not mm in _monthnames:
+		return None
+	mm = _monthnames.index(mm)+1
+	tm = string.splitfields(tm, ':')
+	if len(tm) == 2:
+		[thh, tmm] = tm
+		tss = '0'
+	else:
+		[thh, tmm, tss] = tm
+	try:
+		yy = string.atoi(yy)
+		dd = string.atoi(dd)
+		thh = string.atoi(thh)
+		tmm = string.atoi(tmm)
+		tss = string.atoi(tss)
+	except string.atoi_error:
+		return None
+	tuple = (yy, mm, dd, thh, tmm, tss, 0, 0, 0)
+	return tuple
+
+
+# When used as script, run a small test program.
+# The first command line argument must be a filename containing one
+# message in RFC-822 format.
+
+if __name__ == '__main__':
+	import sys
+	file = '/ufs/guido/Mail/drafts/,1'
+	if sys.argv[1:]: file = sys.argv[1]
+	f = open(file, 'r')
+	m = Message(f)
+	print 'From:', m.getaddr('from')
+	print 'To:', m.getaddrlist('to')
+	print 'Subject:', m.getheader('subject')
+	print 'Date:', m.getheader('date')
+	date = m.getdate('date')
+	if date:
+		print 'ParsedDate:', time.asctime(date)
+	else:
+		print 'ParsedDate:', None
+	m.rewindbody()
+	n = 0
+	while f.readline():
+		n = n + 1
+	print 'Lines:', n
+	print '-'*70
+	print 'len =', len(m)
+	if m.has_key('Date'): print 'Date =', m['Date']
+	if m.has_key('X-Nonsense'): pass
+	print 'keys =', m.keys()
+	print 'values =', m.values()
+	print 'items =', m.items()
+	
diff --git a/Lib/sched.py b/Lib/sched.py
index c838bad..60b0a1b 100644
--- a/Lib/sched.py
+++ b/Lib/sched.py
@@ -6,12 +6,11 @@
 #
 # Each instance is parametrized with two functions, one that is
 # supposed to return the current time, one that is supposed to
-# implement a delay.  You can implement fine- or course-grained
-# real-time scheduling by substituting time and sleep or millitimer
-# and millisleep from the built-in module time, or you can implement
-# simulated time by writing your own functions.  This can also be
-# used to integrate scheduling with STDWIN events; the delay function
-# is allowed to modify the queue.  Time can be expressed as
+# implement a delay.  You can implement real-time scheduling by
+# substituting time and sleep from built-in module time, or you can
+# implement simulated time by writing your own functions.  This can
+# also be used to integrate scheduling with STDWIN events; the delay
+# function is allowed to modify the queue.  Time can be expressed as
 # integers or floating point numbers, as long as it is consistent.
 
 # Events are specified by tuples (time, priority, action, argument).
diff --git a/Lib/stdwin/WindowSched.py b/Lib/stdwin/WindowSched.py
index 56ca6f8..b2fbe76 100755
--- a/Lib/stdwin/WindowSched.py
+++ b/Lib/stdwin/WindowSched.py
@@ -1,5 +1,5 @@
 # Combine a real-time scheduling queue and stdwin event handling.
-# Uses the millisecond timer.
+# Keeps times in milliseconds.
 
 import stdwin, stdwinq
 from stdwinevents import WE_TIMER
@@ -19,11 +19,11 @@
 		mainloop.dispatch(event)
 		return
 	#
-	# Use millisleep for very short delays or if there are no windows
+	# Use sleep for very short delays or if there are no windows
 	#
 	if msecs < 100 or mainloop.countwindows() == 0:
 		if msecs > 0:
-			time.millisleep(msecs)
+			time.sleep(msecs * 0.001)
 		return
 	#
 	# Post a timer event on an arbitrary window and wait for it
@@ -35,7 +35,10 @@
 	if event[0] <> WE_TIMER:
 		mainloop.dispatch(event)
 
-q = sched.scheduler(time.millitimer, delayfunc)
+def millitimer():
+	return int(1000 * time.time())
+
+q = sched.scheduler(millitimer, delayfunc)
 
 # Export functions enter, enterabs and cancel just like a scheduler
 #
diff --git a/Lib/stdwin/filewin.py b/Lib/stdwin/filewin.py
index a03c3f7..df6aa7d 100755
--- a/Lib/stdwin/filewin.py
+++ b/Lib/stdwin/filewin.py
@@ -2,19 +2,19 @@
 # File windows, a subclass of textwin (which is a subclass of gwin)
 
 import textwin
-import builtin
+import __builtin__
 
 
 # FILE WINDOW
 
 def open_readonly(fn): # Open a file window
-	fp = builtin.open(fn, 'r')
+	fp = __builtin__.open(fn, 'r')
 	w = textwin.open_readonly(fn, fp.read())
 	w.fn = fn
 	return w
 
 def open(fn): # Open a file window
-	fp = builtin.open(fn, 'r')
+	fp = __builtin__.open(fn, 'r')
 	w = textwin.open(fn, fp.read())
 	w.fn = fn
 	return w
diff --git a/Lib/stdwin/wdb.py b/Lib/stdwin/wdb.py
index d5c28bb..4018ab1 100755
--- a/Lib/stdwin/wdb.py
+++ b/Lib/stdwin/wdb.py
@@ -241,7 +241,7 @@
 			stdwin.fleep()
 	
 	def draw(self, detail):
-		import linecache, codehack, string
+		import linecache, string
 		d = self.win.begindrawing()
 		try:
 			h, v = 0, 0
@@ -252,7 +252,7 @@
 				else:
 					s = '  '
 				s = s + fn + '(' + `lineno` + ')'
-				s = s + codehack.getcodename(f.f_code)
+				s = s + f.f_code.co_name
 				if f.f_locals.has_key('__args__'):
 					args = f.f_locals['__args__']
 					if args is not None:
@@ -286,6 +286,8 @@
 	try: apply(x.runcall, args)
 	finally: x.close()
 
+def set_trace():
+	Wdb().set_trace()
 
 # Post-Mortem interface
 
@@ -304,6 +306,4 @@
 TESTCMD = 'import x; x.main()'
 
 def test():
-	import linecache
-	linecache.checkcache()
 	run(TESTCMD)
diff --git a/Lib/string.py b/Lib/string.py
index afe5bec..a37cbf0 100644
--- a/Lib/string.py
+++ b/Lib/string.py
@@ -95,30 +95,18 @@
 
 # Find substring, raise exception if not found
 index_error = 'substring not found in string.index'
-def index(s, sub, *args):
-	if args:
-		if len(args) > 1:
-			raise TypeError, 'string.index(): too many args'
-		i = args[0]
-		if i < 0: i = i + len(s)
-	else:
-		i = 0
+def index(s, sub, i = 0):
+	if i < 0: i = i + len(s)
 	n = len(sub)
 	m = len(s) + 1 - n
 	while i < m:
 		if sub == s[i:i+n]: return i
 		i = i+1
-	raise index_error, (s, sub) + args
+	raise index_error, (s, sub, i)
 
 # Find last substring, raise exception if not found
-def rindex(s, sub, *args):
-	if args:
-		if len(args) > 1:
-			raise TypeError, 'string.rindex(): too many args'
-		i = args[0]
-		if i < 0: i = i + len(s)
-	else:
-		i = 0
+def rindex(s, sub, i = 0):
+	if i < 0: i = i + len(s)
 	n = len(sub)
 	m = len(s) + 1 - n
 	r = None
@@ -126,20 +114,35 @@
 		if sub == s[i:i+n]: r = i
 		i = i+1
 	if r is None:
-		raise index_error, (s, sub) + args
+		raise index_error, (s, sub, i)
+	return r
+
+# Count non-overlapping occurrences of substring
+def count(s, sub, i = 0):
+	if i < 0: i = i + len(s)
+	n = len(sub)
+	m = len(s) + 1 - n
+	if n == 0: return m-i
+	r = 0
+	while i < m:
+		if sub == s[i:i+n]:
+			r = r+1
+			i = i+n
+		else:
+			i = i+1
 	return r
 
 # Find substring, return -1 if not found
-def find(*args):
+def find(s, sub, i = 0):
 	try:
-		return apply(index, args)
+		return index(s, sub, i)
 	except index_error:
 		return -1
 
 # Find last substring, return -1 if not found
-def rfind(*args):
+def rfind(s, sub, i = 0):
 	try:
-		return apply(rindex, args)
+		return rindex(s, sub, i)
 	except index_error:
 		return -1
 
@@ -157,7 +160,7 @@
 	if regex.match('[0-9]*\(\.[0-9]*\)?\([eE][-+]?[0-9]+\)?', s) != len(s):
 		raise atof_error, str
 	try:
-		return eval(sign + s)
+		return float(eval(sign + s))
 	except SyntaxError:
 		raise atof_error, str
 
@@ -242,12 +245,36 @@
 # it redefines some string operations that are 100-1000 times faster.
 # It also defines values for whitespace, lowercase and uppercase
 # that match <ctype.h>'s definitions.
-# The manipulation with index_error is needed for compatibility.
 
 try:
 	from strop import *
 	letters = lowercase + uppercase
+except ImportError:
+	pass # Use the original, slow versions
+
+# If certain functions are found, redefine the corresponding exceptions
+# as ValueError
+
+try:
 	from strop import index
 	index_error = ValueError
 except ImportError:
 	pass # Use the original, slow versions
+
+try:
+	from strop import atoi
+	atoi_error = ValueError
+except ImportError:
+	pass # Use the original, slow versions
+
+try:
+	from strop import atof
+	atof_error = ValueError
+except ImportError:
+	pass # Use the original, slow versions
+
+try:
+	from strop import atol
+	atol_error = ValueError
+except ImportError:
+	pass # Use the original, slow versions
diff --git a/Lib/stringold.py b/Lib/stringold.py
index afe5bec..a37cbf0 100644
--- a/Lib/stringold.py
+++ b/Lib/stringold.py
@@ -95,30 +95,18 @@
 
 # Find substring, raise exception if not found
 index_error = 'substring not found in string.index'
-def index(s, sub, *args):
-	if args:
-		if len(args) > 1:
-			raise TypeError, 'string.index(): too many args'
-		i = args[0]
-		if i < 0: i = i + len(s)
-	else:
-		i = 0
+def index(s, sub, i = 0):
+	if i < 0: i = i + len(s)
 	n = len(sub)
 	m = len(s) + 1 - n
 	while i < m:
 		if sub == s[i:i+n]: return i
 		i = i+1
-	raise index_error, (s, sub) + args
+	raise index_error, (s, sub, i)
 
 # Find last substring, raise exception if not found
-def rindex(s, sub, *args):
-	if args:
-		if len(args) > 1:
-			raise TypeError, 'string.rindex(): too many args'
-		i = args[0]
-		if i < 0: i = i + len(s)
-	else:
-		i = 0
+def rindex(s, sub, i = 0):
+	if i < 0: i = i + len(s)
 	n = len(sub)
 	m = len(s) + 1 - n
 	r = None
@@ -126,20 +114,35 @@
 		if sub == s[i:i+n]: r = i
 		i = i+1
 	if r is None:
-		raise index_error, (s, sub) + args
+		raise index_error, (s, sub, i)
+	return r
+
+# Count non-overlapping occurrences of substring
+def count(s, sub, i = 0):
+	if i < 0: i = i + len(s)
+	n = len(sub)
+	m = len(s) + 1 - n
+	if n == 0: return m-i
+	r = 0
+	while i < m:
+		if sub == s[i:i+n]:
+			r = r+1
+			i = i+n
+		else:
+			i = i+1
 	return r
 
 # Find substring, return -1 if not found
-def find(*args):
+def find(s, sub, i = 0):
 	try:
-		return apply(index, args)
+		return index(s, sub, i)
 	except index_error:
 		return -1
 
 # Find last substring, return -1 if not found
-def rfind(*args):
+def rfind(s, sub, i = 0):
 	try:
-		return apply(rindex, args)
+		return rindex(s, sub, i)
 	except index_error:
 		return -1
 
@@ -157,7 +160,7 @@
 	if regex.match('[0-9]*\(\.[0-9]*\)?\([eE][-+]?[0-9]+\)?', s) != len(s):
 		raise atof_error, str
 	try:
-		return eval(sign + s)
+		return float(eval(sign + s))
 	except SyntaxError:
 		raise atof_error, str
 
@@ -242,12 +245,36 @@
 # it redefines some string operations that are 100-1000 times faster.
 # It also defines values for whitespace, lowercase and uppercase
 # that match <ctype.h>'s definitions.
-# The manipulation with index_error is needed for compatibility.
 
 try:
 	from strop import *
 	letters = lowercase + uppercase
+except ImportError:
+	pass # Use the original, slow versions
+
+# If certain functions are found, redefine the corresponding exceptions
+# as ValueError
+
+try:
 	from strop import index
 	index_error = ValueError
 except ImportError:
 	pass # Use the original, slow versions
+
+try:
+	from strop import atoi
+	atoi_error = ValueError
+except ImportError:
+	pass # Use the original, slow versions
+
+try:
+	from strop import atof
+	atof_error = ValueError
+except ImportError:
+	pass # Use the original, slow versions
+
+try:
+	from strop import atol
+	atol_error = ValueError
+except ImportError:
+	pass # Use the original, slow versions
diff --git a/Lib/sunos4/FCNTL.py b/Lib/sunos4/FCNTL.py
index 0ba5e67..1256d81 100755
--- a/Lib/sunos4/FCNTL.py
+++ b/Lib/sunos4/FCNTL.py
@@ -1,10 +1,10 @@
+# Generated by h2py from stdin
 _FOPEN = (-1)
 _FREAD = 0x0001
 _FWRITE = 0x0002
 _FNDELAY = 0x0004
 _FAPPEND = 0x0008
-_FMARK = 0x0010
-_FDEFER = 0x0020
+_FSETBLK = 0x0010
 _FASYNC = 0x0040
 _FSHLOCK = 0x0080
 _FEXLOCK = 0x0100
@@ -15,6 +15,8 @@
 _FSYNC = 0x2000
 _FNONBLOCK = 0x4000
 _FNOCTTY = 0x8000
+_FMARK = 0x10000
+_FDEFER = 0x20000
 O_RDONLY = 0
 O_WRONLY = 1
 O_RDWR = 2
@@ -25,7 +27,6 @@
 O_NONBLOCK = _FNONBLOCK
 O_NOCTTY = _FNOCTTY
 O_SYNC = _FSYNC
-O_ACCMODE = (O_RDONLY|O_WRONLY|O_RDWR)
 FAPPEND = _FAPPEND
 FSYNC = _FSYNC
 FASYNC = _FASYNC
@@ -36,6 +37,7 @@
 FWRITE = _FWRITE
 FMARK = _FMARK
 FDEFER = _FDEFER
+FSETBLK = _FSETBLK
 FSHLOCK = _FSHLOCK
 FEXLOCK = _FEXLOCK
 FOPEN = _FOPEN
@@ -62,3 +64,4 @@
 F_WRLCK = 2
 F_UNLCK = 3
 F_UNLKSYS = 4
+O_ACCMODE = (O_RDONLY|O_WRONLY|O_RDWR)
diff --git a/Lib/sunos4/IN.py b/Lib/sunos4/IN.py
index 05188af..a05a944 100755
--- a/Lib/sunos4/IN.py
+++ b/Lib/sunos4/IN.py
@@ -1,8 +1,4 @@
-# Symbolic constants from <netinet/in.h>.
-# These constants are SunOS specific!  (Possibly even SunOS 4.1.1)
-# See demo/scripts/h2py.py for a tool to help generate a version for
-# your system.
-
+# Generated by h2py from /usr/include/netinet/in.h
 IPPROTO_IP = 0
 IPPROTO_ICMP = 1
 IPPROTO_IGMP = 2
diff --git a/Lib/sunos4/SOCKET.py b/Lib/sunos4/SOCKET.py
index c1b8542..65ce4bc 100755
--- a/Lib/sunos4/SOCKET.py
+++ b/Lib/sunos4/SOCKET.py
@@ -1,3 +1,4 @@
+# Generated by h2py from /usr/include/sys/socket.h
 SOCK_STREAM = 1
 SOCK_DGRAM = 2
 SOCK_RAW = 3
diff --git a/Lib/sunos4/WAIT.py b/Lib/sunos4/WAIT.py
new file mode 100755
index 0000000..43612f0
--- /dev/null
+++ b/Lib/sunos4/WAIT.py
@@ -0,0 +1,13 @@
+# Generated by h2py from /usr/include/sys/wait.h
+WUNTRACED = 0004
+WNOHANG = 0100
+WEXITED = 0001
+WTRAPPED = 0002
+WSTOPPED = WUNTRACED
+WCONTINUED = 0010
+WNOWAIT = 0200
+WOPTMASK = (WEXITED|WTRAPPED|WSTOPPED|WCONTINUED|WNOHANG|WNOWAIT)
+WSTOPFLG = 0177
+WCONTFLG = 0177777
+WCOREFLG = 0200
+WSIGMASK = 0177
diff --git a/Lib/sunos4/regen b/Lib/sunos4/regen
index f1482db..8b52d74 100755
--- a/Lib/sunos4/regen
+++ b/Lib/sunos4/regen
@@ -8,4 +8,5 @@
 h2py </usr/include/sys/fcntlcom.h >FCNTL.py
 echo "O_ACCMODE = (O_RDONLY|O_WRONLY|O_RDWR)" >>FCNTL.py
 h2py /usr/include/sys/socket.h
+h2py /usr/include/sys/wait.h
 h2py -i '(u_long)' /usr/include/netinet/in.h
diff --git a/Lib/symbol.py b/Lib/symbol.py
index 1422f12..f3ec122 100755
--- a/Lib/symbol.py
+++ b/Lib/symbol.py
@@ -3,49 +3,49 @@
 single_input = 256
 file_input = 257
 eval_input = 258
-lambda_input = 259
-funcdef = 260
-parameters = 261
-varargslist = 262
-fpdef = 263
-fplist = 264
-stmt = 265
-simple_stmt = 266
-small_stmt = 267
-expr_stmt = 268
-print_stmt = 269
-del_stmt = 270
-pass_stmt = 271
-flow_stmt = 272
-break_stmt = 273
-continue_stmt = 274
-return_stmt = 275
-raise_stmt = 276
-import_stmt = 277
-global_stmt = 278
-access_stmt = 279
-accesstype = 280
-exec_stmt = 281
-compound_stmt = 282
-if_stmt = 283
-while_stmt = 284
-for_stmt = 285
-try_stmt = 286
-except_clause = 287
-suite = 288
-test = 289
-and_test = 290
-not_test = 291
-comparison = 292
-comp_op = 293
-expr = 294
-xor_expr = 295
-and_expr = 296
-shift_expr = 297
-arith_expr = 298
-term = 299
-factor = 300
-atom = 301
+funcdef = 259
+parameters = 260
+varargslist = 261
+fpdef = 262
+fplist = 263
+stmt = 264
+simple_stmt = 265
+small_stmt = 266
+expr_stmt = 267
+print_stmt = 268
+del_stmt = 269
+pass_stmt = 270
+flow_stmt = 271
+break_stmt = 272
+continue_stmt = 273
+return_stmt = 274
+raise_stmt = 275
+import_stmt = 276
+global_stmt = 277
+access_stmt = 278
+accesstype = 279
+exec_stmt = 280
+compound_stmt = 281
+if_stmt = 282
+while_stmt = 283
+for_stmt = 284
+try_stmt = 285
+except_clause = 286
+suite = 287
+test = 288
+and_test = 289
+not_test = 290
+comparison = 291
+comp_op = 292
+expr = 293
+xor_expr = 294
+and_expr = 295
+shift_expr = 296
+arith_expr = 297
+term = 298
+factor = 299
+atom = 300
+lambdef = 301
 trailer = 302
 subscript = 303
 exprlist = 304
diff --git a/Lib/test/test_b1.py b/Lib/test/test_b1.py
index 434b379..5eb4f09 100644
--- a/Lib/test/test_b1.py
+++ b/Lib/test/test_b1.py
@@ -97,6 +97,22 @@
 	raise TestFailed, 'filter (remove false values)'
 if filter(lambda x: x > 0, [1, -3, 9, 0, 2]) <> [1, 9, 2]:
 	raise TestFailed, 'filter (keep positives)'
+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]
+if filter(None, Squares(10)) != [1, 4, 9, 16, 25, 36, 49, 64, 81]:
+	raise TestFailed, 'filter(None, Squares(10))'
+if filter(lambda x: x%2, Squares(10)) != [1, 9, 25, 49, 81]:
+	raise TestFailed, 'filter(oddp, Squares(10))'
 
 print 'float'
 if float(3.14) <> 3.14: raise TestFailed, 'float(3.14)'
@@ -158,6 +174,14 @@
 	raise TestFailed, 'map(plus, [1, 3, 7], [4, 9, 2])'
 if map(plus, [1, 3, 7], [4, 9, 2], [1, 1, 0]) <> [1+4+1, 3+9+1, 7+2+0]:
 	raise TestFailed, 'map(plus, [1, 3, 7], [4, 9, 2], [1, 1, 0])'
+if map(None, Squares(10)) != [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]:
+	raise TestFailed, 'map(None, Squares(10))'
+if map(int, Squares(10)) != [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]:
+	raise TestFailed, 'map(int, Squares(10))'
+if map(None, Squares(3), Squares(2)) != [(0,0), (1,1), (4,None)]:
+	raise TestFailed, 'map(None: x, Squares(3), Squares(2))'
+if map(max, Squares(3), Squares(2)) != [0, 1, 4]:
+	raise TestFailed, 'map(None: x, Squares(3), Squares(2))'
 
 print 'max'
 if max('123123') <> '3': raise TestFailed, 'max(\'123123\')'
diff --git a/Lib/test/test_b2.py b/Lib/test/test_b2.py
index 7118b08..10c0bfb 100644
--- a/Lib/test/test_b2.py
+++ b/Lib/test/test_b2.py
@@ -121,6 +121,25 @@
 	raise TestFailed, 'reduce(): compute 7!'
 if reduce(lambda x, y: x*y, range(2,21), 1L) <> 2432902008176640000L:
 	raise TestFailed, 'reduce(): compute 20!, use long'
+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]
+if reduce(lambda x, y: x+y, Squares(10)) != 285:
+	raise TestFailed, 'reduce(<+>, Squares(10))'
+if reduce(lambda x, y: x+y, Squares(10), 0) != 285:
+	raise TestFailed, 'reduce(<+>, Squares(10), 0)'
+if reduce(lambda x, y: x+y, Squares(0), 0) != 0:
+	raise TestFailed, 'reduce(<+>, Squares(0), 0)'
+
 
 print 'reload'
 import string
diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py
index 0988574..67baf09 100644
--- a/Lib/test/test_grammar.py
+++ b/Lib/test/test_grammar.py
@@ -41,7 +41,7 @@
 		raise TestFailed, \
 			  'No OverflowError on huge integer literal ' + `s`
 elif eval('maxint == 9223372036854775807'):
-	if eval('9223372036854775807-1 != -01000000000000000000000'):
+	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'
@@ -91,9 +91,35 @@
 x = "doesn't \"shrink\" does it"
 y = 'doesn\'t "shrink" does it'
 assert(len(x) == 24 and x == y)
-x = "doesn \"shrink\" doesn't it"
-y = 'doesn "shrink" doesn\'t it'
-assert(len(x) == 25 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'
@@ -113,7 +139,8 @@
 print 'funcdef'
 ### 'def' NAME parameters ':' suite
 ### parameters: '(' [varargslist] ')'
-### varargslist: (fpdef ',')* '*' NAME | fpdef (',' fpdef)* [',']
+### varargslist: (fpdef ['=' test] ',')* '*' NAME
+###            | fpdef ['=' test] (',' fpdef ['=' test])* [',']
 ### fpdef: NAME | '(' fplist ')'
 ### fplist: fpdef (',' fpdef)* [',']
 def f1(): pass
@@ -126,6 +153,54 @@
 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
@@ -184,17 +259,11 @@
 except KeyboardInterrupt: pass
 
 print 'import_stmt' # 'import' NAME (',' NAME)* | 'from' NAME 'import' ('*' | NAME (',' NAME)*)
-[1]
 import sys
-[2]
 import time, math
-[3]
-from time import sleep
-[4]
+from time import time
 from sys import *
-[5]
 from math import sin, cos
-[6]
 
 print 'global_stmt' # 'global' NAME (',' NAME)*
 def f():
@@ -242,24 +311,41 @@
 else: pass
 
 print 'for_stmt' # 'for' exprlist 'in' exprlist ':' suite ['else' ':' suite]
-[1]
 for i in 1, 2, 3: pass
-[2]
 for i, j, k in (): pass
 else: pass
-[3]
+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' ':' suite (except_clause ':' suite)+ | 'try' ':' suite 'finally' ':' suite
+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
diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py
index 0a43de3..6a3f772 100644
--- a/Lib/test/test_types.py
+++ b/Lib/test/test_types.py
@@ -67,6 +67,10 @@
 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'
@@ -94,6 +98,8 @@
 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(())'
diff --git a/Lib/test/testall.out b/Lib/test/testall.out
index 46a728e..b921512 100644
--- a/Lib/test/testall.out
+++ b/Lib/test/testall.out
@@ -15,8 +15,6 @@
 funcdef
 simple_stmt
 expr_stmt
-1
-(1, 2, 3)
 print_stmt
 1 2 3
 1 2 3
@@ -29,20 +27,11 @@
 return_stmt
 raise_stmt
 import_stmt
-[1]
-[2]
-[3]
-[4]
-[5]
-[6]
 global_stmt
 exec_stmt
 if_stmt
 while_stmt
 for_stmt
-[1]
-[2]
-[3]
 try_stmt
 suite
 test
diff --git a/Lib/tzparse.py b/Lib/tzparse.py
index 26824ab..ef325e9 100644
--- a/Lib/tzparse.py
+++ b/Lib/tzparse.py
@@ -12,7 +12,7 @@
 	if tzprog == None:
 		import regex
 		tzprog = regex.compile(tzpat)
-	if not tzprog.match(tzstr):
+	if tzprog.match(tzstr) < 0:
 		raise ValueError, 'not the TZ syntax I understand'
 	regs = tzprog.regs
 	subs = []
@@ -78,5 +78,3 @@
 		x = localtime(t)
 		tm = x[:-1] + (0,)
 		print 'd =', d, 't =', t, '=', asctime(tm), x[-1]
-
-test()
diff --git a/Lib/whrandom.py b/Lib/whrandom.py
index c7881b5..670ca7a 100644
--- a/Lib/whrandom.py
+++ b/Lib/whrandom.py
@@ -35,31 +35,25 @@
 	# Without arguments, initialize from current time.
 	# With arguments (x, y, z), initialize from them.
 	#
-	def __init__(self, *xyz):
-		if not xyz:
+	def __init__(self, x = None, y = None, z = None):
+		if x is None:
 			# Initialize from current time
 			import time
 			t = int(time.time())
 			t, x = divmod(t, 256)
 			t, y = divmod(t, 256)
 			t, z = divmod(t, 256)
-		else:
-			# Initialize from arguments (x, y, z)
-			x, y, z = xyz
 		self.seed(x, y, z)
 	#
 	# Set the seed from (x, y, z).
 	# These must be integers in the range [0, 256).
 	#
-	def seed(self, *xyz):
-		if type(xyz) <> type(()) or len(xyz) <> 3:
-			raise TypeError, '3 seeds required'
-		x, y, z = xyz
+	def seed(self, x, y, z):
 		if not type(x) == type(y) == type(z) == type(0):
 			raise TypeError, 'seeds must be integers'
 		if not 0 <= x < 256 and 0 <= y < 256 and 0 <= z < 256:
 			raise ValueError, 'seeds must be in range(0, 256)'
-		self._seed = xyz
+		self._seed = (x, y, z)
 	#
 	# Get the next random number in the range [0.0, 1.0).
 	#