Get rid of freeze (now its own directory).
Added some new demos.  Fixed a few others.
diff --git a/Demo/scripts/README b/Demo/scripts/README
index 5b5e88d..e7443da 100644
--- a/Demo/scripts/README
+++ b/Demo/scripts/README
@@ -21,7 +21,6 @@
 fixps.py		Fix Python scripts' first line (if #!)
 fixcid.py		Massive identifier substitution on C source files
 fixheader.py		Add some cpp magic to a C include file
-freeze.py		Convert a Python script into a free-standing binary
 from.py			Summarize mailbox
 ftpstats.py		Summarize ftp daemon log file
 ifdef.py		Remove #if(n)def groups from C sources
@@ -31,6 +30,7 @@
 markov.py		Markov chain simulation of words or characters
 mboxconvvert.py		Convert MH or MMDF mailboxes to unix mailbox format
 methfix.py		Fix old method syntax def f(self, (a1, ..., aN)):
+morse.py		Produce morse code (audible or on AIFF file)
 mkreal.py		Turn a symbolic link into a real file or directory
 mpzpi.py		test mpz -- print digits of pi (compare pi.py)
 objgraph.py		Print object graph from nm output on a library
@@ -41,7 +41,9 @@
 pp.py			Emulate some Perl command line options
 primes.py		Print prime numbers
 ptags.py		Create vi tags file for Python modules
+script.py		Equivalent to BSD script(1) -- by Steen Lumholt
 suff.py			Sort a list of files by suffix
+sum5.py			Print md5 checksums of files
 unbirthday.py		Print unbirthday count
 which.py		Find a program in $PATH
 xxci.py			Wrapper for rcsdiff and ci
diff --git a/Demo/scripts/morse.py b/Demo/scripts/morse.py
new file mode 100755
index 0000000..2cea83e
--- /dev/null
+++ b/Demo/scripts/morse.py
@@ -0,0 +1,149 @@
+# DAH should be three DOTs.
+# Space between DOTs and DAHs should be one DOT.
+# Space between two letters should be one DAH.
+# Space between two words should be DOT DAH DAH.
+
+import sys, math, audiodev
+
+DOT = 30
+DAH = 3 * DOT
+OCTAVE = 2				# 1 == 441 Hz, 2 == 882 Hz, ...
+
+morsetab = {
+	'A': '.-',		'a': '.-',
+	'B': '-...',		'b': '-...',
+	'C': '-.-.',		'c': '-.-.',
+	'D': '-..',		'd': '-..',
+	'E': '.',		'e': '.',
+	'F': '..-.',		'f': '..-.',
+	'G': '--.',		'g': '--.',
+	'H': '....',		'h': '....',
+	'I': '..',		'i': '..',
+	'J': '.---',		'j': '.---',
+	'K': '-.-',		'k': '-.-',
+	'L': '.-..',		'l': '.-..',
+	'M': '--',		'm': '--',
+	'N': '-.',		'n': '-.',
+	'O': '---',		'o': '---',
+	'P': '.--.',		'p': '.--.',
+	'Q': '--.-',		'q': '--.-',
+	'R': '.-.',		'r': '.-.',
+	'S': '...',		's': '...',
+	'T': '-',		't': '-',
+	'U': '..-',		'u': '..-',
+	'V': '...-',		'v': '...-',
+	'W': '.--',		'w': '.--',
+	'X': '-..-',		'x': '-..-',
+	'Y': '-.--',		'y': '-.--',
+	'Z': '--..',		'z': '--..',
+	'0': '-----',
+	'1': '.----',
+	'2': '..---',
+	'3': '...--',
+	'4': '....-',
+	'5': '.....',
+	'6': '-....',
+	'7': '--...',
+	'8': '---..',
+	'9': '----.',
+	',': '--..--',
+	'.': '.-.-.-',
+	'?': '..--..',
+	';': '-.-.-.',
+	':': '---...',
+	"'": '.----.',
+	'-': '-....-',
+	'/': '-..-.',
+	'(': '-.--.-',
+	')': '-.--.-',
+	'_': '..--.-',
+	' ': ' '
+}
+
+# If we play at 44.1 kHz (which we do), then if we produce one sine
+# wave in 100 samples, we get a tone of 441 Hz.  If we produce two
+# sine waves in these 100 samples, we get a tone of 882 Hz.  882 Hz
+# appears to be a nice one for playing morse code.
+def mkwave(octave):
+	global sinewave, nowave
+	sinewave = ''
+	for i in range(100):
+		val = int(math.sin(math.pi * float(i) * octave / 50.0) * 30000)
+		sinewave = sinewave + chr((val >> 8) & 255) + chr(val & 255)
+	nowave = '\0' * 200
+
+mkwave(OCTAVE)
+
+def main():
+	import getopt, string
+	try:
+		opts, args = getopt.getopt(sys.argv[1:], 'o:p:')
+	except getopt.error:
+		sys.stderr.write('Usage ' + sys.argv[0] +
+				 ' [ -o outfile ] [ args ] ...\n')
+		sys.exit(1)
+	dev = None
+	for o, a in opts:
+		if o == '-o':
+			import aifc
+			dev = aifc.open(a, 'w')
+			dev.setframerate(44100)
+			dev.setsampwidth(2)
+			dev.setnchannels(1)
+		if o == '-p':
+			mkwave(string.atoi(a))
+	if not dev:
+		import audiodev
+		dev = audiodev.AudioDev()
+		dev.setoutrate(44100)
+		dev.setsampwidth(2)
+		dev.setnchannels(1)
+		dev.close = dev.stop
+		dev.writeframesraw = dev.writeframes
+	if args:
+		line = string.join(args)
+	else:
+		line = sys.stdin.readline()
+	while line:
+		mline = morse(line)
+		play(mline, dev)
+		if hasattr(dev, 'wait'):
+			dev.wait()
+		if not args:
+			line = sys.stdin.readline()
+		else:
+			line = ''
+	dev.close()
+
+# Convert a string to morse code with \001 between the characters in
+# the string.
+def morse(line):
+	res = ''
+	for c in line:
+		try:
+			res = res + morsetab[c] + '\001'
+		except KeyError:
+			pass
+	return res
+
+# Play a line of morse code.
+def play(line, dev):
+	for c in line:
+		if c == '.':
+			sine(dev, DOT)
+		elif c == '-':
+			sine(dev, DAH)
+		else:			# space
+			pause(dev, DAH + DOT)
+		pause(dev, DOT)
+
+def sine(dev, length):
+	for i in range(length):
+		dev.writeframesraw(sinewave)
+
+def pause(dev, length):
+	for i in range(length):
+		dev.writeframesraw(nowave)
+
+if __name__ == '__main__' or sys.argv[0] == __name__:
+	main()
diff --git a/Demo/scripts/script.py b/Demo/scripts/script.py
new file mode 100755
index 0000000..04e7ecb
--- /dev/null
+++ b/Demo/scripts/script.py
@@ -0,0 +1,33 @@
+#! /usr/local/bin/python
+# script.py -- Make typescript of terminal session.
+# Usage:
+#	-a	Append to typescript.
+#	-p	Use Python as shell.
+# Author: Steen Lumholt.
+
+
+import os, time, sys
+import pty
+
+def read(fd):
+	data = os.read(fd, 1024)
+	file.write(data)
+	return data
+
+shell = 'sh'
+filename = 'typescript'
+mode = 'w'
+if os.environ.has_key('SHELL'):
+	shell = os.environ['SHELL']
+if '-a' in sys.argv:
+	mode = 'a'
+if '-p' in sys.argv:
+	shell = 'python'
+
+file = open(filename, mode)
+
+sys.stdout.write('Script started, file is %s\n' % filename)
+file.write('Script started on %s\n' % time.ctime(time.time()))
+pty.spawn(shell, read)
+file.write('Script done on %s\n' % time.ctime(time.time()))
+sys.stdout.write('Script done, file is %s\n' % filename)
diff --git a/Tools/scripts/fixcid.py b/Tools/scripts/fixcid.py
index ce3f1f3..3b37bb8 100755
--- a/Tools/scripts/fixcid.py
+++ b/Tools/scripts/fixcid.py
@@ -194,7 +194,7 @@
 
 # Tokenizing ANSI C (partly)
 
-Identifier = '[a-zA-Z_][a-zA-Z0-9_]+'
+Identifier = '\(struct \)?[a-zA-Z_][a-zA-Z0-9_]+'
 String = '"\([^\n\\"]\|\\\\.\)*"'
 Char = '\'\([^\n\\\']\|\\\\.\)*\''
 CommentStart = '/\*'
@@ -246,6 +246,7 @@
 			if Program is InsideCommentProgram:
 				if not Docomments:
 					print 'Found in comment:', found
+					i = i + n
 					continue
 				if NotInComment.has_key(found):
 ##					print 'Ignored in comment:',
@@ -290,7 +291,9 @@
 			i = -1		# Happens to delete trailing \n
 		words = string.split(line[:i])
 		if not words: continue
-		if len(words) <> 2:
+		if len(words) == 3 and words[0] == 'struct':
+			words[:2] == [words[0] + ' ' + words[1]]
+		elif len(words) <> 2:
 			err(substfile + ':' + `lineno` +
 				  ': warning: bad line: ' + line)
 			continue
diff --git a/Tools/scripts/h2py.py b/Tools/scripts/h2py.py
index 3d7a851..db0dbd8 100755
--- a/Tools/scripts/h2py.py
+++ b/Tools/scripts/h2py.py
@@ -1,32 +1,43 @@
 #! /usr/local/bin/python
 
-# Read #define's from stdin and translate to Python code on stdout.
-# Very primitive: non-#define's are ignored, as is anything that isn't
-# valid Python as it stands.
+# Read #define's and translate to Python code.
+# Handle #include statements.
+# Handle #define macros with one argument.
+# Anything that isn't recognized or doesn't translate into valid
+# Python is ignored.
+
+# Without filename arguments, acts as a filter.
 # If one or more filenames are given, output is written to corresponding
 # filenames in the local directory, translated to all uppercase, with
 # the extension replaced by ".py".
+
 # By passing one or more options of the form "-i regular_expression"
 # you can specify additional strings to be ignored.  This is useful
 # e.g. to ignore casts to u_long: simply specify "-i '(u_long)'".
 
 # XXX To do:
 # - turn trailing C comments into Python comments
-# - turn C string quotes into Python comments
 # - turn C Boolean operators "&& || !" into Python "and or not"
 # - what to do about #if(def)?
-# - what to do about #include?
-# - what to do about macros with parameters?
-# - reject definitions with semicolons in them
+# - what to do about macros with multiple parameters?
 
-import sys, regex, string, getopt, os
+import sys, regex, regsub, string, getopt, os
 
 p_define = regex.compile('^#[\t ]*define[\t ]+\([a-zA-Z0-9_]+\)[\t ]+')
 
+p_macro = regex.compile(
+  '^#[\t ]*define[\t ]+\([a-zA-Z0-9_]+\)(\([_a-zA-Z][_a-zA-Z0-9]*\))[\t ]+')
+
+p_include = regex.compile('^#[\t ]*include[\t ]+<\([a-zA-Z0-9_/\.]+\)')
+
 p_comment = regex.compile('/\*\([^*]+\|\*+[^/]\)*\(\*+/\)?')
 
 ignores = [p_comment]
 
+p_char = regex.compile("'\(\\\\.[^\\\\]*\|[^\\\\]\)'")
+
+filedict = {}
+
 def main():
 	opts, args = getopt.getopt(sys.argv[1:], 'i:')
 	for o, a in opts:
@@ -47,40 +58,65 @@
 			outfile = outfile + '.py'
 			outfp = open(outfile, 'w')
 			outfp.write('# Generated by h2py from %s\n' % filename)
+			filedict = {}
+			if filename[:13] == '/usr/include/':
+				filedict[filename[13:]] = None
 			process(fp, outfp)
 			outfp.close()
 			fp.close()
 
-def process(fp, outfp):
-	env = {}
+def process(fp, outfp, env = {}):
 	lineno = 0
 	while 1:
 		line = fp.readline()
 		if not line: break
 		lineno = lineno + 1
-		# gobble up continuation lines
-		while line[-2:] == '\\\n':
-			nextline = fp.readline()
-			if not nextline: break
-			lineno = lineno + 1
-			line = line + nextline
 		n = p_define.match(line)
 		if n >= 0:
+			# gobble up continuation lines
+			while line[-2:] == '\\\n':
+				nextline = fp.readline()
+				if not nextline: break
+				lineno = lineno + 1
+				line = line + nextline
 			name = p_define.group(1)
 			body = line[n:]
 			# replace ignored patterns by spaces
 			for p in ignores:
-				while p.search(body) >= 0:
-					a, b = p.regs[0]
-					body = body[:a] + ' ' + body[b:]
+				body = regsub.gsub(p, ' ', body)
+			# replace char literals by ord(...)
+			body = regsub.gsub(p_char, 'ord(\\0)', body)
 			stmt = '%s = %s\n' % (name, string.strip(body))
 			ok = 0
 			try:
 				exec stmt in env
-				ok = 1
 			except:
 				sys.stderr.write('Skipping: %s' % stmt)
-			if ok:
+			else:
 				outfp.write(stmt)
-
+		n =p_macro.match(line)
+		if n >= 0:
+			macro, arg = p_macro.group(1, 2)
+			body = line[n:]
+			for p in ignores:
+				body = regsub.gsub(p, ' ', body)
+			body = regsub.gsub(p_char, 'ord(\\0)', body)
+			stmt = 'def %s(%s): return %s\n' % (macro, arg, body)
+			try:
+				exec stmt in env
+			except:
+				sys.stderr.write('Skipping: %s' % stmt)
+			else:
+				outfp.write(stmt)
+		if p_include.match(line) >= 0:
+			regs = p_include.regs
+			a, b = regs[1]
+			filename = line[a:b]
+			if not filedict.has_key(filename):
+				filedict[filename] = None
+				outfp.write(
+					'\n# Included from %s\n' % filename)
+				inclfp = open('/usr/include/' + filename, 'r')
+				process(inclfp, outfp, env)
 main()
+
diff --git a/Tools/scripts/sum5.py b/Tools/scripts/sum5.py
new file mode 100755
index 0000000..fdb83fa
--- /dev/null
+++ b/Tools/scripts/sum5.py
@@ -0,0 +1,97 @@
+#! /usr/local/bin/python
+
+# print md5 checksum for files
+
+bufsize = 8096
+fnfilter = None
+rmode = 'r'
+
+usage = """
+usage: sum5 [-b] [-t] [-l] [-s bufsize] [file ...]
+-b        : read files in binary mode
+-t        : read files in text mode (default)
+-l        : print last pathname component only
+-s bufsize: read buffer size (default %d)
+file ...  : files to sum; '-' or no files means stdin
+""" % bufsize
+
+import sys
+import string
+import os
+import md5
+import regsub
+
+StringType = type('')
+FileType = type(sys.stdin)
+
+def sum(*files):
+	sts = 0
+	if files and type(files[-1]) == FileType:
+		out, files = files[-1], files[:-1]
+	else:
+		out = sys.stdout
+	if len(files) == 1 and type(files[0]) != StringType:
+		files = files[0]
+	for f in files:
+		if type(f) == StringType:
+			if f == '-':
+				sts = printsumfp(sys.stdin, '<stdin>', out) or sts
+			else:
+				sts = printsum(f, out) or sts
+		else:
+			sts = sum(f, out) or sts
+	return sts
+
+def printsum(file, out = sys.stdout):
+	try:
+		fp = open(file, rmode)
+	except IOError, msg:
+		sys.stderr.write('%s: Can\'t open: %s\n' % (file, msg))
+		return 1
+	if fnfilter:
+		file = fnfilter(file)
+	sts = printsumfp(fp, file, out)
+	fp.close()
+	return sts
+
+def printsumfp(fp, file, out = sys.stdout):
+	m = md5.md5()
+	try:
+		while 1:
+			data = fp.read(bufsize)
+			if not data: break
+			m.update(data)
+	except IOError, msg:
+		sys.stderr.write('%s: I/O error: %s\n' % (file, msg))
+		return 1
+	out.write('%s %s\n' % (hexify(m.digest()), file))
+	return 0
+
+def hexify(s):
+	res = ''
+	for c in s:
+		res = res + '%02x' % ord(c)
+	return res
+
+def main(args = sys.argv[1:], out = sys.stdout):
+	global fnfilter, rmode, bufsize
+	import getopt
+	try:
+		opts, args = getopt.getopt(args, 'blts:')
+	except getopt.error, msg:
+		sys.stderr.write('%s: %s\n%s' % (sys.argv[0], msg, usage))
+		return 2
+	for o, a in opts:
+		if o == '-l':
+			fnfilter = os.path.basename
+		if o == '-b':
+			rmode = 'rb'
+		if o == '-t':
+			rmode = 'r'
+		if o == '-s':
+			bufsize = string.atoi(a)
+	if not args: args = ['-']
+	return sum(args, out)
+
+if __name__ == '__main__' or __name__ == sys.argv[0]:
+	sys.exit(main(sys.argv[1:], sys.stdout))