diff options
author | cinap_lenrek <cinap_lenrek@localhost> | 2011-05-03 11:25:13 +0000 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@localhost> | 2011-05-03 11:25:13 +0000 |
commit | 458120dd40db6b4df55a4e96b650e16798ef06a0 (patch) | |
tree | 8f82685be24fef97e715c6f5ca4c68d34d5074ee /sys/src/cmd/python/Demo/scripts | |
parent | 3a742c699f6806c1145aea5149bf15de15a0afd7 (diff) |
add hg and python
Diffstat (limited to 'sys/src/cmd/python/Demo/scripts')
23 files changed, 2021 insertions, 0 deletions
diff --git a/sys/src/cmd/python/Demo/scripts/README b/sys/src/cmd/python/Demo/scripts/README new file mode 100644 index 000000000..d8434e8d4 --- /dev/null +++ b/sys/src/cmd/python/Demo/scripts/README @@ -0,0 +1,23 @@ +This directory contains a collection of executable Python scripts. + +See also the Tools/scripts directory! + +beer.py Print the classic 'bottles of beer' list. +eqfix.py Fix .py files to use the correct equality test operator +fact.py Factorize numbers +find-uname.py Search for Unicode characters using regexps. +from.py Summarize mailbox +ftpstats.py Summarize ftp daemon log file +lpwatch.py Watch BSD line printer queues +makedir.py Like mkdir -p +markov.py Markov chain simulation of words or characters +mboxconvvert.py Convert MH or MMDF mailboxes to unix mailbox format +mkrcs.py Fix symlinks named RCS into parallel tree +morse.py Produce morse code (audible or on AIFF file) +pi.py Print all digits of pi -- given enough time and memory +pp.py Emulate some Perl command line options +primes.py Print prime numbers +queens.py Dijkstra's solution to Wirth's "N Queens problem" +script.py Equivalent to BSD script(1) -- by Steen Lumholt +unbirthday.py Print unbirthday count +update.py Update a bunch of files according to a script. diff --git a/sys/src/cmd/python/Demo/scripts/beer.py b/sys/src/cmd/python/Demo/scripts/beer.py new file mode 100644 index 000000000..1b9ac8bad --- /dev/null +++ b/sys/src/cmd/python/Demo/scripts/beer.py @@ -0,0 +1,14 @@ +#! /usr/bin/env python +# By GvR, demystified after a version by Fredrik Lundh. +import sys +n = 100 +if sys.argv[1:]: n = int(sys.argv[1]) +def bottle(n): + if n == 0: return "no more bottles of beer" + if n == 1: return "one bottle of beer" + return str(n) + " bottles of beer" +for i in range(n): + print bottle(n-i), "on the wall," + print bottle(n-i) + "." + print "Take one down, pass it around," + print bottle(n-i-1), "on the wall." diff --git a/sys/src/cmd/python/Demo/scripts/eqfix.py b/sys/src/cmd/python/Demo/scripts/eqfix.py new file mode 100755 index 000000000..35c43aa04 --- /dev/null +++ b/sys/src/cmd/python/Demo/scripts/eqfix.py @@ -0,0 +1,198 @@ +#! /usr/bin/env python + +# Fix Python source files to use the new equality test operator, i.e., +# if x = y: ... +# is changed to +# if x == y: ... +# The script correctly tokenizes the Python program to reliably +# distinguish between assignments and equality tests. +# +# Command line arguments are files or directories to be processed. +# Directories are searched recursively for files whose name looks +# like a python module. +# Symbolic links are always ignored (except as explicit directory +# arguments). Of course, the original file is kept as a back-up +# (with a "~" attached to its name). +# It complains about binaries (files containing null bytes) +# and about files that are ostensibly not Python files: if the first +# line starts with '#!' and does not contain the string 'python'. +# +# Changes made are reported to stdout in a diff-like format. +# +# Undoubtedly you can do this using find and sed or perl, but this is +# a nice example of Python code that recurses down a directory tree +# and uses regular expressions. Also note several subtleties like +# preserving the file's mode and avoiding to even write a temp file +# when no changes are needed for a file. +# +# NB: by changing only the function fixline() you can turn this +# into a program for a different change to Python programs... + +import sys +import re +import os +from stat import * +import string + +err = sys.stderr.write +dbg = err +rep = sys.stdout.write + +def main(): + bad = 0 + if not sys.argv[1:]: # No arguments + err('usage: ' + sys.argv[0] + ' file-or-directory ...\n') + sys.exit(2) + for arg in sys.argv[1:]: + if os.path.isdir(arg): + if recursedown(arg): bad = 1 + elif os.path.islink(arg): + err(arg + ': will not process symbolic links\n') + bad = 1 + else: + if fix(arg): bad = 1 + sys.exit(bad) + +ispythonprog = re.compile('^[a-zA-Z0-9_]+\.py$') +def ispython(name): + return ispythonprog.match(name) >= 0 + +def recursedown(dirname): + dbg('recursedown(%r)\n' % (dirname,)) + bad = 0 + try: + names = os.listdir(dirname) + except os.error, msg: + err('%s: cannot list directory: %r\n' % (dirname, msg)) + return 1 + names.sort() + subdirs = [] + for name in names: + if name in (os.curdir, os.pardir): continue + fullname = os.path.join(dirname, name) + if os.path.islink(fullname): pass + elif os.path.isdir(fullname): + subdirs.append(fullname) + elif ispython(name): + if fix(fullname): bad = 1 + for fullname in subdirs: + if recursedown(fullname): bad = 1 + return bad + +def fix(filename): +## dbg('fix(%r)\n' % (dirname,)) + try: + f = open(filename, 'r') + except IOError, msg: + err('%s: cannot open: %r\n' % (filename, msg)) + return 1 + head, tail = os.path.split(filename) + tempname = os.path.join(head, '@' + tail) + g = None + # If we find a match, we rewind the file and start over but + # now copy everything to a temp file. + lineno = 0 + while 1: + line = f.readline() + if not line: break + lineno = lineno + 1 + if g is None and '\0' in line: + # Check for binary files + err(filename + ': contains null bytes; not fixed\n') + f.close() + return 1 + if lineno == 1 and g is None and line[:2] == '#!': + # Check for non-Python scripts + words = string.split(line[2:]) + if words and re.search('[pP]ython', words[0]) < 0: + msg = filename + ': ' + words[0] + msg = msg + ' script; not fixed\n' + err(msg) + f.close() + return 1 + while line[-2:] == '\\\n': + nextline = f.readline() + if not nextline: break + line = line + nextline + lineno = lineno + 1 + newline = fixline(line) + if newline != line: + if g is None: + try: + g = open(tempname, 'w') + except IOError, msg: + f.close() + err('%s: cannot create: %r\n' % (tempname, msg)) + return 1 + f.seek(0) + lineno = 0 + rep(filename + ':\n') + continue # restart from the beginning + rep(repr(lineno) + '\n') + rep('< ' + line) + rep('> ' + newline) + if g is not None: + g.write(newline) + + # End of file + f.close() + if not g: return 0 # No changes + + # Finishing touch -- move files + + # First copy the file's mode to the temp file + try: + statbuf = os.stat(filename) + os.chmod(tempname, statbuf[ST_MODE] & 07777) + except os.error, msg: + err('%s: warning: chmod failed (%r)\n' % (tempname, msg)) + # Then make a backup of the original file as filename~ + try: + os.rename(filename, filename + '~') + except os.error, msg: + err('%s: warning: backup failed (%r)\n' % (filename, msg)) + # Now move the temp file to the original file + try: + os.rename(tempname, filename) + except os.error, msg: + err('%s: rename failed (%r)\n' % (filename, msg)) + return 1 + # Return succes + return 0 + + +from tokenize import tokenprog + +match = {'if':':', 'elif':':', 'while':':', 'return':'\n', \ + '(':')', '[':']', '{':'}', '`':'`'} + +def fixline(line): + # Quick check for easy case + if '=' not in line: return line + + i, n = 0, len(line) + stack = [] + while i < n: + j = tokenprog.match(line, i) + if j < 0: + # A bad token; forget about the rest of this line + print '(Syntax error:)' + print line, + return line + a, b = tokenprog.regs[3] # Location of the token proper + token = line[a:b] + i = i+j + if stack and token == stack[-1]: + del stack[-1] + elif match.has_key(token): + stack.append(match[token]) + elif token == '=' and stack: + line = line[:a] + '==' + line[b:] + i, n = a + len('=='), len(line) + elif token == '==' and not stack: + print '(Warning: \'==\' at top level:)' + print line, + return line + +if __name__ == "__main__": + main() diff --git a/sys/src/cmd/python/Demo/scripts/fact.py b/sys/src/cmd/python/Demo/scripts/fact.py new file mode 100755 index 000000000..03cab8bb8 --- /dev/null +++ b/sys/src/cmd/python/Demo/scripts/fact.py @@ -0,0 +1,49 @@ +#! /usr/bin/env python + +# Factorize numbers. +# The algorithm is not efficient, but easy to understand. +# If there are large factors, it will take forever to find them, +# because we try all odd numbers between 3 and sqrt(n)... + +import sys +from math import sqrt + +error = 'fact.error' # exception + +def fact(n): + if n < 1: raise error # fact() argument should be >= 1 + if n == 1: return [] # special case + res = [] + # Treat even factors special, so we can use i = i+2 later + while n%2 == 0: + res.append(2) + n = n/2 + # Try odd numbers up to sqrt(n) + limit = sqrt(float(n+1)) + i = 3 + while i <= limit: + if n%i == 0: + res.append(i) + n = n/i + limit = sqrt(n+1) + else: + i = i+2 + if n != 1: + res.append(n) + return res + +def main(): + if len(sys.argv) > 1: + for arg in sys.argv[1:]: + n = eval(arg) + print n, fact(n) + else: + try: + while 1: + n = input() + print n, fact(n) + except EOFError: + pass + +if __name__ == "__main__": + main() diff --git a/sys/src/cmd/python/Demo/scripts/find-uname.py b/sys/src/cmd/python/Demo/scripts/find-uname.py new file mode 100644 index 000000000..b76b9f0fe --- /dev/null +++ b/sys/src/cmd/python/Demo/scripts/find-uname.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python + +""" +For each argument on the command line, look for it in the set of all Unicode +names. Arguments are treated as case-insensitive regular expressions, e.g.: + + % find-uname 'small letter a$' 'horizontal line' + *** small letter a$ matches *** + LATIN SMALL LETTER A (97) + COMBINING LATIN SMALL LETTER A (867) + CYRILLIC SMALL LETTER A (1072) + PARENTHESIZED LATIN SMALL LETTER A (9372) + CIRCLED LATIN SMALL LETTER A (9424) + FULLWIDTH LATIN SMALL LETTER A (65345) + *** horizontal line matches *** + HORIZONTAL LINE EXTENSION (9135) +""" + +import unicodedata +import sys +import re + +def main(args): + unicode_names= [] + for ix in range(sys.maxunicode+1): + try: + unicode_names.append( (ix, unicodedata.name(unichr(ix))) ) + except ValueError: # no name for the character + pass + for arg in args: + pat = re.compile(arg, re.I) + matches = [(x,y) for (x,y) in unicode_names + if pat.search(y) is not None] + if matches: + print "***", arg, "matches", "***" + for (x,y) in matches: + print "%s (%d)" % (y,x) + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/sys/src/cmd/python/Demo/scripts/from.py b/sys/src/cmd/python/Demo/scripts/from.py new file mode 100755 index 000000000..3c04fcd49 --- /dev/null +++ b/sys/src/cmd/python/Demo/scripts/from.py @@ -0,0 +1,35 @@ +#! /usr/bin/env python + +# Print From and Subject of messages in $MAIL. +# Extension to multiple mailboxes and other bells & whistles are left +# as exercises for the reader. + +import sys, os + +# Open mailbox file. Exits with exception when this fails. + +try: + mailbox = os.environ['MAIL'] +except (AttributeError, KeyError): + sys.stderr.write('No environment variable $MAIL\n') + sys.exit(2) + +try: + mail = open(mailbox) +except IOError: + sys.exit('Cannot open mailbox file: ' + mailbox) + +while 1: + line = mail.readline() + if not line: + break # EOF + if line.startswith('From '): + # Start of message found + print line[:-1], + while 1: + line = mail.readline() + if not line or line == '\n': + break + if line.startswith('Subject: '): + print repr(line[9:-1]), + print diff --git a/sys/src/cmd/python/Demo/scripts/ftpstats.py b/sys/src/cmd/python/Demo/scripts/ftpstats.py new file mode 100755 index 000000000..5c1599e66 --- /dev/null +++ b/sys/src/cmd/python/Demo/scripts/ftpstats.py @@ -0,0 +1,145 @@ +#! /usr/bin/env python + +# Extract statistics from ftp daemon log. + +# Usage: +# ftpstats [-m maxitems] [-s search] [file] +# -m maxitems: restrict number of items in "top-N" lists, default 25. +# -s string: restrict statistics to lines containing this string. +# Default file is /usr/adm/ftpd; a "-" means read standard input. + +# The script must be run on the host where the ftp daemon runs. +# (At CWI this is currently buizerd.) + +import os +import sys +import re +import string +import getopt + +pat = '^([a-zA-Z0-9 :]*)!(.*)!(.*)!([<>].*)!([0-9]+)!([0-9]+)$' +prog = re.compile(pat) + +def main(): + maxitems = 25 + search = None + try: + opts, args = getopt.getopt(sys.argv[1:], 'm:s:') + except getopt.error, msg: + print msg + print 'usage: ftpstats [-m maxitems] [file]' + sys.exit(2) + for o, a in opts: + if o == '-m': + maxitems = string.atoi(a) + if o == '-s': + search = a + file = '/usr/adm/ftpd' + if args: file = args[0] + if file == '-': + f = sys.stdin + else: + try: + f = open(file, 'r') + except IOError, msg: + print file, ':', msg + sys.exit(1) + bydate = {} + bytime = {} + byfile = {} + bydir = {} + byhost = {} + byuser = {} + bytype = {} + lineno = 0 + try: + while 1: + line = f.readline() + if not line: break + lineno = lineno + 1 + if search and string.find(line, search) < 0: + continue + if prog.match(line) < 0: + print 'Bad line', lineno, ':', repr(line) + continue + items = prog.group(1, 2, 3, 4, 5, 6) + (logtime, loguser, loghost, logfile, logbytes, + logxxx2) = items +## print logtime +## print '-->', loguser +## print '--> -->', loghost +## print '--> --> -->', logfile +## print '--> --> --> -->', logbytes +## print '--> --> --> --> -->', logxxx2 +## for i in logtime, loghost, logbytes, logxxx2: +## if '!' in i: print '???', i + add(bydate, logtime[-4:] + ' ' + logtime[:6], items) + add(bytime, logtime[7:9] + ':00-59', items) + direction, logfile = logfile[0], logfile[1:] + # The real path probably starts at the last //... + while 1: + i = string.find(logfile, '//') + if i < 0: break + logfile = logfile[i+1:] + add(byfile, logfile + ' ' + direction, items) + logdir = os.path.dirname(logfile) +## logdir = os.path.normpath(logdir) + '/.' + while 1: + add(bydir, logdir + ' ' + direction, items) + dirhead = os.path.dirname(logdir) + if dirhead == logdir: break + logdir = dirhead + add(byhost, loghost, items) + add(byuser, loguser, items) + add(bytype, direction, items) + except KeyboardInterrupt: + print 'Interrupted at line', lineno + show(bytype, 'by transfer direction', maxitems) + show(bydir, 'by directory', maxitems) + show(byfile, 'by file', maxitems) + show(byhost, 'by host', maxitems) + show(byuser, 'by user', maxitems) + showbar(bydate, 'by date') + showbar(bytime, 'by time of day') + +def showbar(dict, title): + n = len(title) + print '='*((70-n)/2), title, '='*((71-n)/2) + list = [] + keys = dict.keys() + keys.sort() + for key in keys: + n = len(str(key)) + list.append((len(dict[key]), key)) + maxkeylength = 0 + maxcount = 0 + for count, key in list: + maxkeylength = max(maxkeylength, len(key)) + maxcount = max(maxcount, count) + maxbarlength = 72 - maxkeylength - 7 + for count, key in list: + barlength = int(round(maxbarlength*float(count)/maxcount)) + bar = '*'*barlength + print '%5d %-*s %s' % (count, maxkeylength, key, bar) + +def show(dict, title, maxitems): + if len(dict) > maxitems: + title = title + ' (first %d)'%maxitems + n = len(title) + print '='*((70-n)/2), title, '='*((71-n)/2) + list = [] + keys = dict.keys() + for key in keys: + list.append((-len(dict[key]), key)) + list.sort() + for count, key in list[:maxitems]: + print '%5d %s' % (-count, key) + +def add(dict, key, item): + if dict.has_key(key): + dict[key].append(item) + else: + dict[key] = [item] + +if __name__ == "__main__": + main() diff --git a/sys/src/cmd/python/Demo/scripts/lpwatch.py b/sys/src/cmd/python/Demo/scripts/lpwatch.py new file mode 100755 index 000000000..8887dee7d --- /dev/null +++ b/sys/src/cmd/python/Demo/scripts/lpwatch.py @@ -0,0 +1,110 @@ +#! /usr/bin/env python + +# Watch line printer queue(s). +# Intended for BSD 4.3 lpq. + +import posix +import sys +import time +import string + +DEF_PRINTER = 'psc' +DEF_DELAY = 10 + +def main(): + delay = DEF_DELAY # XXX Use getopt() later + try: + thisuser = posix.environ['LOGNAME'] + except: + thisuser = posix.environ['USER'] + printers = sys.argv[1:] + if printers: + # Strip '-P' from printer names just in case + # the user specified it... + for i in range(len(printers)): + if printers[i][:2] == '-P': + printers[i] = printers[i][2:] + else: + if posix.environ.has_key('PRINTER'): + printers = [posix.environ['PRINTER']] + else: + printers = [DEF_PRINTER] + # + clearhome = posix.popen('clear', 'r').read() + # + while 1: + text = clearhome + for name in printers: + text = text + makestatus(name, thisuser) + '\n' + print text + time.sleep(delay) + +def makestatus(name, thisuser): + pipe = posix.popen('lpq -P' + name + ' 2>&1', 'r') + lines = [] + users = {} + aheadbytes = 0 + aheadjobs = 0 + userseen = 0 + totalbytes = 0 + totaljobs = 0 + while 1: + line = pipe.readline() + if not line: break + fields = string.split(line) + n = len(fields) + if len(fields) >= 6 and fields[n-1] == 'bytes': + rank = fields[0] + user = fields[1] + job = fields[2] + files = fields[3:-2] + bytes = eval(fields[n-2]) + if user == thisuser: + userseen = 1 + elif not userseen: + aheadbytes = aheadbytes + bytes + aheadjobs = aheadjobs + 1 + totalbytes = totalbytes + bytes + totaljobs = totaljobs + 1 + if users.has_key(user): + ujobs, ubytes = users[user] + else: + ujobs, ubytes = 0, 0 + ujobs = ujobs + 1 + ubytes = ubytes + bytes + users[user] = ujobs, ubytes + else: + if fields and fields[0] <> 'Rank': + line = string.strip(line) + if line == 'no entries': + line = name + ': idle' + elif line[-22:] == ' is ready and printing': + line = name + lines.append(line) + # + if totaljobs: + line = '%d K' % ((totalbytes+1023)/1024) + if totaljobs <> len(users): + line = line + ' (%d jobs)' % totaljobs + if len(users) == 1: + line = line + ' for %s' % (users.keys()[0],) + else: + line = line + ' for %d users' % len(users) + if userseen: + if aheadjobs == 0: + line = line + ' (%s first)' % thisuser + else: + line = line + ' (%d K before %s)' % ( + (aheadbytes+1023)/1024, thisuser) + lines.append(line) + # + sts = pipe.close() + if sts: + lines.append('lpq exit status %r' % (sts,)) + return string.joinfields(lines, ': ') + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass diff --git a/sys/src/cmd/python/Demo/scripts/makedir.py b/sys/src/cmd/python/Demo/scripts/makedir.py new file mode 100755 index 000000000..f70facd08 --- /dev/null +++ b/sys/src/cmd/python/Demo/scripts/makedir.py @@ -0,0 +1,21 @@ +#! /usr/bin/env python + +# Like mkdir, but also make intermediate directories if necessary. +# It is not an error if the given directory already exists (as long +# as it is a directory). +# Errors are not treated specially -- you just get a Python exception. + +import sys, os + +def main(): + for p in sys.argv[1:]: + makedirs(p) + +def makedirs(p): + if p and not os.path.isdir(p): + head, tail = os.path.split(p) + makedirs(head) + os.mkdir(p, 0777) + +if __name__ == "__main__": + main() diff --git a/sys/src/cmd/python/Demo/scripts/markov.py b/sys/src/cmd/python/Demo/scripts/markov.py new file mode 100755 index 000000000..bddec5693 --- /dev/null +++ b/sys/src/cmd/python/Demo/scripts/markov.py @@ -0,0 +1,117 @@ +#! /usr/bin/env python + +class Markov: + def __init__(self, histsize, choice): + self.histsize = histsize + self.choice = choice + self.trans = {} + def add(self, state, next): + if not self.trans.has_key(state): + self.trans[state] = [next] + else: + self.trans[state].append(next) + def put(self, seq): + n = self.histsize + add = self.add + add(None, seq[:0]) + for i in range(len(seq)): + add(seq[max(0, i-n):i], seq[i:i+1]) + add(seq[len(seq)-n:], None) + def get(self): + choice = self.choice + trans = self.trans + n = self.histsize + seq = choice(trans[None]) + while 1: + subseq = seq[max(0, len(seq)-n):] + options = trans[subseq] + next = choice(options) + if not next: break + seq = seq + next + return seq + +def test(): + import sys, string, random, getopt + args = sys.argv[1:] + try: + opts, args = getopt.getopt(args, '0123456789cdw') + except getopt.error: + print 'Usage: markov [-#] [-cddqw] [file] ...' + print 'Options:' + print '-#: 1-digit history size (default 2)' + print '-c: characters (default)' + print '-w: words' + print '-d: more debugging output' + print '-q: no debugging output' + print 'Input files (default stdin) are split in paragraphs' + print 'separated blank lines and each paragraph is split' + print 'in words by whitespace, then reconcatenated with' + print 'exactly one space separating words.' + print 'Output consists of paragraphs separated by blank' + print 'lines, where lines are no longer than 72 characters.' + histsize = 2 + do_words = 0 + debug = 1 + for o, a in opts: + if '-0' <= o <= '-9': histsize = eval(o[1:]) + if o == '-c': do_words = 0 + if o == '-d': debug = debug + 1 + if o == '-q': debug = 0 + if o == '-w': do_words = 1 + if not args: args = ['-'] + m = Markov(histsize, random.choice) + try: + for filename in args: + if filename == '-': + f = sys.stdin + if f.isatty(): + print 'Sorry, need stdin from file' + continue + else: + f = open(filename, 'r') + if debug: print 'processing', filename, '...' + text = f.read() + f.close() + paralist = string.splitfields(text, '\n\n') + for para in paralist: + if debug > 1: print 'feeding ...' + words = string.split(para) + if words: + if do_words: data = tuple(words) + else: data = string.joinfields(words, ' ') + m.put(data) + except KeyboardInterrupt: + print 'Interrupted -- continue with data read so far' + if not m.trans: + print 'No valid input files' + return + if debug: print 'done.' + if debug > 1: + for key in m.trans.keys(): + if key is None or len(key) < histsize: + print repr(key), m.trans[key] + if histsize == 0: print repr(''), m.trans[''] + print + while 1: + data = m.get() + if do_words: words = data + else: words = string.split(data) + n = 0 + limit = 72 + for w in words: + if n + len(w) > limit: + print + n = 0 + print w, + n = n + len(w) + 1 + print + print + +def tuple(list): + if len(list) == 0: return () + if len(list) == 1: return (list[0],) + i = len(list)/2 + return tuple(list[:i]) + tuple(list[i:]) + +if __name__ == "__main__": + test() diff --git a/sys/src/cmd/python/Demo/scripts/mboxconvert.py b/sys/src/cmd/python/Demo/scripts/mboxconvert.py new file mode 100755 index 000000000..8c462f3b1 --- /dev/null +++ b/sys/src/cmd/python/Demo/scripts/mboxconvert.py @@ -0,0 +1,124 @@ +#! /usr/bin/env python + +# Convert MH directories (1 message per file) or MMDF mailboxes (4x^A +# delimited) to unix mailbox (From ... delimited) on stdout. +# If -f is given, files contain one message per file (e.g. MH messages) + +import rfc822 +import sys +import time +import os +import stat +import getopt +import re + +def main(): + dofile = mmdf + try: + opts, args = getopt.getopt(sys.argv[1:], 'f') + except getopt.error, msg: + sys.stderr.write('%s\n' % msg) + sys.exit(2) + for o, a in opts: + if o == '-f': + dofile = message + if not args: + args = ['-'] + sts = 0 + for arg in args: + if arg == '-' or arg == '': + sts = dofile(sys.stdin) or sts + elif os.path.isdir(arg): + sts = mh(arg) or sts + elif os.path.isfile(arg): + try: + f = open(arg) + except IOError, msg: + sys.stderr.write('%s: %s\n' % (arg, msg)) + sts = 1 + continue + sts = dofile(f) or sts + f.close() + else: + sys.stderr.write('%s: not found\n' % arg) + sts = 1 + if sts: + sys.exit(sts) + +numeric = re.compile('[1-9][0-9]*') + +def mh(dir): + sts = 0 + msgs = os.listdir(dir) + for msg in msgs: + if numeric.match(msg) != len(msg): + continue + fn = os.path.join(dir, msg) + try: + f = open(fn) + except IOError, msg: + sys.stderr.write('%s: %s\n' % (fn, msg)) + sts = 1 + continue + sts = message(f) or sts + return sts + +def mmdf(f): + sts = 0 + while 1: + line = f.readline() + if not line: + break + if line == '\1\1\1\1\n': + sts = message(f, line) or sts + else: + sys.stderr.write( + 'Bad line in MMFD mailbox: %r\n' % (line,)) + return sts + +counter = 0 # for generating unique Message-ID headers + +def message(f, delimiter = ''): + sts = 0 + # Parse RFC822 header + m = rfc822.Message(f) + # Write unix header line + fullname, email = m.getaddr('From') + tt = m.getdate('Date') + if tt: + t = time.mktime(tt) + else: + sys.stderr.write( + 'Unparseable date: %r\n' % (m.getheader('Date'),)) + t = os.fstat(f.fileno())[stat.ST_MTIME] + print 'From', email, time.ctime(t) + # Copy RFC822 header + for line in m.headers: + print line, + # Invent Message-ID header if none is present + if not m.has_key('message-id'): + global counter + counter = counter + 1 + msgid = "<%s.%d>" % (hex(t), counter) + sys.stderr.write("Adding Message-ID %s (From %s)\n" % + (msgid, email)) + print "Message-ID:", msgid + print + # Copy body + while 1: + line = f.readline() + if line == delimiter: + break + if not line: + sys.stderr.write('Unexpected EOF in message\n') + sts = 1 + break + if line[:5] == 'From ': + line = '>' + line + print line, + # Print trailing newline + print + return sts + +if __name__ == "__main__": + main() diff --git a/sys/src/cmd/python/Demo/scripts/mkrcs.py b/sys/src/cmd/python/Demo/scripts/mkrcs.py new file mode 100755 index 000000000..cacdda0a5 --- /dev/null +++ b/sys/src/cmd/python/Demo/scripts/mkrcs.py @@ -0,0 +1,61 @@ +#! /usr/bin/env python + +# A rather specialized script to make sure that a symbolic link named +# RCS exists pointing to a real RCS directory in a parallel tree +# referenced as RCStree in an ancestor directory. +# (I use this because I like my RCS files to reside on a physically +# different machine). + +import os + +def main(): + rcstree = 'RCStree' + rcs = 'RCS' + if os.path.islink(rcs): + print '%r is a symlink to %r' % (rcs, os.readlink(rcs)) + return + if os.path.isdir(rcs): + print '%r is an ordinary directory' % (rcs,) + return + if os.path.exists(rcs): + print '%r is a file?!?!' % (rcs,) + return + # + p = os.getcwd() + up = '' + down = '' + # Invariants: + # (1) join(p, down) is the current directory + # (2) up is the same directory as p + # Ergo: + # (3) join(up, down) is the current directory + #print 'p =', repr(p) + while not os.path.isdir(os.path.join(p, rcstree)): + head, tail = os.path.split(p) + #print 'head = %r; tail = %r' % (head, tail) + if not tail: + print 'Sorry, no ancestor dir contains %r' % (rcstree,) + return + p = head + up = os.path.join(os.pardir, up) + down = os.path.join(tail, down) + #print 'p = %r; up = %r; down = %r' % (p, up, down) + there = os.path.join(up, rcstree) + there = os.path.join(there, down) + there = os.path.join(there, rcs) + if os.path.isdir(there): + print '%r already exists' % (there, ) + else: + print 'making %r' % (there,) + makedirs(there) + print 'making symlink %r -> %r' % (rcs, there) + os.symlink(there, rcs) + +def makedirs(p): + if not os.path.isdir(p): + head, tail = os.path.split(p) + makedirs(head) + os.mkdir(p, 0777) + +if __name__ == "__main__": + main() diff --git a/sys/src/cmd/python/Demo/scripts/morse.py b/sys/src/cmd/python/Demo/scripts/morse.py new file mode 100755 index 000000000..3da49da9e --- /dev/null +++ b/sys/src/cmd/python/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/sys/src/cmd/python/Demo/scripts/newslist.doc b/sys/src/cmd/python/Demo/scripts/newslist.doc new file mode 100755 index 000000000..87fd9ba27 --- /dev/null +++ b/sys/src/cmd/python/Demo/scripts/newslist.doc @@ -0,0 +1,59 @@ + NEWSLIST + ======== + A program to assist HTTP browsing of newsgroups + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +WWW browsers such as NCSA Mosaic allow the user to read newsgroup +articles by specifying the group name in a URL eg 'news:comp.answers'. + +To browse through many groups, though, (and there are several thousand +of them) you really need a page or pages containing links to all the +groups. There are some good ones out there, for example, + + http://info.cern.ch/hypertext/DataSources/News/Groups/Overview.html + +is the standard one at CERN, but it only shows the groups available there, +which may be rather different from those available on your machine. + +Newslist is a program which creates a hierarchy of pages for you based +on the groups available from YOUR server. It is written in python - a +splendid interpreted object-oriented language which I suggest you get +right now from the directory /pub/python at ftp.cwi.nl, if you haven't +already got it. + +You should be able to see some sample output by looking at: + http://pelican.cl.cam.ac.uk/newspage/root.html + +Descriptions of newsgroups can be added from a file with one group +per line. eg: + + alt.foo Articles about foo + comp.bar Programming in 'bar' and related languages + +A suitable list detailing most groups can be found at ftp.uu.net in +/uunet-info/newsgroups.gz. + +Make sure you read the information at the beginning of the program source and +configure the variables before running. + +In addition to python, you need: + + An NNTP-based news feed. + A directory in which to put the pages. + +The programming is not very beautiful, but it works! It comes with no +warranty, express or implied, but with the hope that some others may +find it useful. + +Comments, improvements & suggestions welcomed. +Quentin Stafford-Fraser + + ---------------------------------------------------------------------- + Quentin Stafford-Fraser + http://pelican.cl.cam.ac.uk/people/qs101/me.html + + Cambridge University Computer Lab Rank Xerox Cambridge EuroPARC + qs101@cl.cam.ac.uk fraser@europarc.xerox.com + Tel: +44 223 334411 Tel: +44 223 341521 + Fax: +44 223 334679 Fax: +44 223 341510 + ---------------------------------------------------------------------- diff --git a/sys/src/cmd/python/Demo/scripts/newslist.py b/sys/src/cmd/python/Demo/scripts/newslist.py new file mode 100755 index 000000000..35ba48a48 --- /dev/null +++ b/sys/src/cmd/python/Demo/scripts/newslist.py @@ -0,0 +1,366 @@ +#! /usr/bin/env python +####################################################################### +# Newslist $Revision: 37320 $ +# +# Syntax: +# newslist [ -a ] +# +# This is a program to create a directory full of HTML pages +# which between them contain links to all the newsgroups available +# on your server. +# +# The -a option causes a complete list of all groups to be read from +# the server rather than just the ones which have appeared since last +# execution. This recreates the local list from scratch. Use this on +# the first invocation of the program, and from time to time thereafter. +# When new groups are first created they may appear on your server as +# empty groups. By default, empty groups are ignored by the -a option. +# However, these new groups will not be created again, and so will not +# appear in the server's list of 'new groups' at a later date. Hence it +# won't appear until you do a '-a' after some articles have appeared. +# +# I should really keep a list of ignored empty groups and re-check them +# for articles on every run, but I haven't got around to it yet. +# +# This assumes an NNTP news feed. +# +# Feel free to copy, distribute and modify this code for +# non-commercial use. If you make any useful modifications, let me +# know! +# +# (c) Quentin Stafford-Fraser 1994 +# fraser@europarc.xerox.com qs101@cl.cam.ac.uk +# # +####################################################################### +import sys,nntplib, string, marshal, time, os, posix, string + +####################################################################### +# Check these variables before running! # + +# Top directory. +# Filenames which don't start with / are taken as being relative to this. +topdir='/anfs/qsbigdisc/web/html/newspage' + +# The name of your NNTP host +# eg. +# newshost = 'nntp-serv.cl.cam.ac.uk' +# or use following to get the name from the NNTPSERVER environment +# variable: +# newshost = posix.environ['NNTPSERVER'] +newshost = 'nntp-serv.cl.cam.ac.uk' + +# The filename for a local cache of the newsgroup list +treefile = 'grouptree' + +# The filename for descriptions of newsgroups +# I found a suitable one at ftp.uu.net in /uunet-info/newgroups.gz +# You can set this to '' if you don't wish to use one. +descfile = 'newsgroups' + +# The directory in which HTML pages should be created +# eg. +# pagedir = '/usr/local/lib/html/newspage' +# pagedir = 'pages' +pagedir = topdir + +# The html prefix which will refer to this directory +# eg. +# httppref = '/newspage/', +# or leave blank for relative links between pages: (Recommended) +# httppref = '' +httppref = '' + +# The name of the 'root' news page in this directory. +# A .html suffix will be added. +rootpage = 'root' + +# Set skipempty to 0 if you wish to see links to empty groups as well. +# Only affects the -a option. +skipempty = 1 + +# pagelinkicon can contain html to put an icon after links to +# further pages. This helps to make important links stand out. +# Set to '' if not wanted, or '...' is quite a good one. +pagelinkicon='... <img src="http://pelican.cl.cam.ac.uk/icons/page.xbm"> ' + +# --------------------------------------------------------------------- +# Less important personal preferences: + +# Sublistsize controls the maximum number of items the will appear as +# an indented sub-list before the whole thing is moved onto a different +# page. The smaller this is, the more pages you will have, but the +# shorter each will be. +sublistsize = 4 + +# That should be all. # +####################################################################### + +for dir in os.curdir, os.environ['HOME']: + rcfile = os.path.join(dir, '.newslistrc.py') + if os.path.exists(rcfile): + print rcfile + execfile(rcfile) + break + +from nntplib import NNTP +from stat import * + +rcsrev = '$Revision: 37320 $' +rcsrev = string.join(filter(lambda s: '$' not in s, string.split(rcsrev))) +desc = {} + +# Make (possibly) relative filenames into absolute ones +treefile = os.path.join(topdir,treefile) +descfile = os.path.join(topdir,descfile) +page = os.path.join(topdir,pagedir) + +# First the bits for creating trees --------------------------- + +# Addtotree creates/augments a tree from a list of group names +def addtotree(tree, groups): + print 'Updating tree...' + for i in groups: + parts = string.splitfields(i,'.') + makeleaf(tree, parts) + +# Makeleaf makes a leaf and the branch leading to it if necessary +def makeleaf(tree,path): + j = path[0] + l = len(path) + + if not tree.has_key(j): + tree[j] = {} + if l == 1: + tree[j]['.'] = '.' + if l > 1: + makeleaf(tree[j],path[1:]) + +# Then the bits for outputting trees as pages ---------------- + +# Createpage creates an HTML file named <root>.html containing links +# to those groups beginning with <root>. + +def createpage(root, tree, p): + filename = os.path.join(pagedir,root+'.html') + if root == rootpage: + detail = '' + else: + detail = ' under ' + root + f = open(filename,'w') + # f.write('Content-Type: text/html\n') + f.write('<TITLE>Newsgroups available' + detail + '</TITLE>\n') + f.write('<H1>Newsgroups available' + detail +'</H1>\n') + f.write('<A HREF="'+httppref+rootpage+'.html">Back to top level</A><P>\n') + printtree(f,tree,0,p) + f.write('<I>This page automatically created by \'newslist\' v. '+rcsrev+'.') + f.write(time.ctime(time.time()) + '</I><P>') + f.close() + +# Printtree prints the groups as a bulleted list. Groups with +# more than <sublistsize> subgroups will be put on a separate page. +# Other sets of subgroups are just indented. + +def printtree(f, tree, indent, p): + global desc + l = len(tree) + + if l > sublistsize and indent>0: + # Create a new page and a link to it + f.write('<LI><B><A HREF="'+httppref+p[1:]+'.html">') + f.write(p[1:]+'.*') + f.write('</A></B>'+pagelinkicon+'\n') + createpage(p[1:], tree, p) + return + + kl = tree.keys() + + if l > 1: + kl.sort() + if indent > 0: + # Create a sub-list + f.write('<LI>'+p[1:]+'\n<UL>') + else: + # Create a main list + f.write('<UL>') + indent = indent + 1 + + for i in kl: + if i == '.': + # Output a newsgroup + f.write('<LI><A HREF="news:' + p[1:] + '">'+ p[1:] + '</A> ') + if desc.has_key(p[1:]): + f.write(' <I>'+desc[p[1:]]+'</I>\n') + else: + f.write('\n') + else: + # Output a hierarchy + printtree(f,tree[i], indent, p+'.'+i) + + if l > 1: + f.write('\n</UL>') + +# Reading descriptions file --------------------------------------- + +# This returns an array mapping group name to its description + +def readdesc(descfile): + global desc + + desc = {} + + if descfile == '': + return + + try: + d = open(descfile, 'r') + print 'Reading descriptions...' + except (IOError): + print 'Failed to open description file ' + descfile + return + l = d.readline() + while l != '': + bits = string.split(l) + try: + grp = bits[0] + dsc = string.join(bits[1:]) + if len(dsc)>1: + desc[grp] = dsc + except (IndexError): + pass + l = d.readline() + +# Check that ouput directory exists, ------------------------------ +# and offer to create it if not + +def checkopdir(pagedir): + if not os.path.isdir(pagedir): + print 'Directory '+pagedir+' does not exist.' + print 'Shall I create it for you? (y/n)' + if sys.stdin.readline()[0] == 'y': + try: + os.mkdir(pagedir,0777) + except: + print 'Sorry - failed!' + sys.exit(1) + else: + print 'OK. Exiting.' + sys.exit(1) + +# Read and write current local tree ---------------------------------- + +def readlocallist(treefile): + print 'Reading current local group list...' + tree = {} + try: + treetime = time.localtime(os.stat(treefile)[ST_MTIME]) + except: + print '\n*** Failed to open local group cache '+treefile + print 'If this is the first time you have run newslist, then' + print 'use the -a option to create it.' + sys.exit(1) + treedate = '%02d%02d%02d' % (treetime[0] % 100 ,treetime[1], treetime[2]) + try: + dump = open(treefile,'r') + tree = marshal.load(dump) + dump.close() + except (IOError): + print 'Cannot open local group list ' + treefile + return (tree, treedate) + +def writelocallist(treefile, tree): + try: + dump = open(treefile,'w') + groups = marshal.dump(tree,dump) + dump.close() + print 'Saved list to '+treefile+'\n' + except: + print 'Sorry - failed to write to local group cache '+treefile + print 'Does it (or its directory) have the correct permissions?' + sys.exit(1) + +# Return list of all groups on server ----------------------------- + +def getallgroups(server): + print 'Getting list of all groups...' + treedate='010101' + info = server.list()[1] + groups = [] + print 'Processing...' + if skipempty: + print '\nIgnoring following empty groups:' + for i in info: + grpname = string.split(i[0])[0] + if skipempty and string.atoi(i[1]) < string.atoi(i[2]): + print grpname+' ', + else: + groups.append(grpname) + print '\n' + if skipempty: + print '(End of empty groups)' + return groups + +# Return list of new groups on server ----------------------------- + +def getnewgroups(server, treedate): + print 'Getting list of new groups since start of '+treedate+'...', + info = server.newgroups(treedate,'000001')[1] + print 'got %d.' % len(info) + print 'Processing...', + groups = [] + for i in info: + grpname = string.split(i)[0] + groups.append(grpname) + print 'Done' + return groups + +# Now the main program -------------------------------------------- + +def main(): + global desc + + tree={} + + # Check that the output directory exists + checkopdir(pagedir); + + try: + print 'Connecting to '+newshost+'...' + if sys.version[0] == '0': + s = NNTP.init(newshost) + else: + s = NNTP(newshost) + connected = 1 + except (nntplib.error_temp, nntplib.error_perm), x: + print 'Error connecting to host:', x + print 'I\'ll try to use just the local list.' + connected = 0 + + # If -a is specified, read the full list of groups from server + if connected and len(sys.argv) > 1 and sys.argv[1] == '-a': + + groups = getallgroups(s) + + # Otherwise just read the local file and then add + # groups created since local file last modified. + else: + + (tree, treedate) = readlocallist(treefile) + if connected: + groups = getnewgroups(s, treedate) + + if connected: + addtotree(tree, groups) + writelocallist(treefile,tree) + + # Read group descriptions + readdesc(descfile) + + print 'Creating pages...' + createpage(rootpage, tree, '') + print 'Done' + +if __name__ == "__main__": + main() + +# That's all folks +###################################################################### diff --git a/sys/src/cmd/python/Demo/scripts/pi.py b/sys/src/cmd/python/Demo/scripts/pi.py new file mode 100755 index 000000000..9b242451a --- /dev/null +++ b/sys/src/cmd/python/Demo/scripts/pi.py @@ -0,0 +1,34 @@ +#! /usr/bin/env python + +# Print digits of pi forever. +# +# The algorithm, using Python's 'long' integers ("bignums"), works +# with continued fractions, and was conceived by Lambert Meertens. +# +# See also the ABC Programmer's Handbook, by Geurts, Meertens & Pemberton, +# published by Prentice-Hall (UK) Ltd., 1990. + +import sys + +def main(): + k, a, b, a1, b1 = 2L, 4L, 1L, 12L, 4L + while 1: + # Next approximation + p, q, k = k*k, 2L*k+1L, k+1L + a, b, a1, b1 = a1, b1, p*a+q*a1, p*b+q*b1 + # Print common digits + d, d1 = a/b, a1/b1 + while d == d1: + output(d) + a, a1 = 10L*(a%b), 10L*(a1%b1) + d, d1 = a/b, a1/b1 + +def output(d): + # Use write() to avoid spaces between the digits + # Use str() to avoid the 'L' + sys.stdout.write(str(d)) + # Flush so the output is seen immediately + sys.stdout.flush() + +if __name__ == "__main__": + main() diff --git a/sys/src/cmd/python/Demo/scripts/pp.py b/sys/src/cmd/python/Demo/scripts/pp.py new file mode 100755 index 000000000..0491fa9bf --- /dev/null +++ b/sys/src/cmd/python/Demo/scripts/pp.py @@ -0,0 +1,130 @@ +#! /usr/bin/env python + +# Emulate some Perl command line options. +# Usage: pp [-a] [-c] [-d] [-e scriptline] [-F fieldsep] [-n] [-p] [file] ... +# Where the options mean the following: +# -a : together with -n or -p, splits each line into list F +# -c : check syntax only, do not execute any code +# -d : run the script under the debugger, pdb +# -e scriptline : gives one line of the Python script; may be repeated +# -F fieldsep : sets the field separator for the -a option [not in Perl] +# -n : runs the script for each line of input +# -p : prints the line after the script has run +# When no script lines have been passed, the first file argument +# contains the script. With -n or -p, the remaining arguments are +# read as input to the script, line by line. If a file is '-' +# or missing, standard input is read. + +# XXX To do: +# - add -i extension option (change files in place) +# - make a single loop over the files and lines (changes effect of 'break')? +# - add an option to specify the record separator +# - except for -n/-p, run directly from the file if at all possible + +import sys +import string +import getopt + +FS = '' +SCRIPT = [] +AFLAG = 0 +CFLAG = 0 +DFLAG = 0 +NFLAG = 0 +PFLAG = 0 + +try: + optlist, ARGS = getopt.getopt(sys.argv[1:], 'acde:F:np') +except getopt.error, msg: + sys.stderr.write(sys.argv[0] + ': ' + msg + '\n') + sys.exit(2) + +for option, optarg in optlist: + if option == '-a': + AFLAG = 1 + elif option == '-c': + CFLAG = 1 + elif option == '-d': + DFLAG = 1 + elif option == '-e': + for line in string.splitfields(optarg, '\n'): + SCRIPT.append(line) + elif option == '-F': + FS = optarg + elif option == '-n': + NFLAG = 1 + PFLAG = 0 + elif option == '-p': + NFLAG = 1 + PFLAG = 1 + else: + print option, 'not recognized???' + +if not ARGS: ARGS.append('-') + +if not SCRIPT: + if ARGS[0] == '-': + fp = sys.stdin + else: + fp = open(ARGS[0], 'r') + while 1: + line = fp.readline() + if not line: break + SCRIPT.append(line[:-1]) + del fp + del ARGS[0] + if not ARGS: ARGS.append('-') + +if CFLAG: + prologue = ['if 0:'] + epilogue = [] +elif NFLAG: + # Note that it is on purpose that AFLAG and PFLAG are + # tested dynamically each time through the loop + prologue = [ \ + 'LINECOUNT = 0', \ + 'for FILE in ARGS:', \ + ' \tif FILE == \'-\':', \ + ' \t \tFP = sys.stdin', \ + ' \telse:', \ + ' \t \tFP = open(FILE, \'r\')', \ + ' \tLINENO = 0', \ + ' \twhile 1:', \ + ' \t \tLINE = FP.readline()', \ + ' \t \tif not LINE: break', \ + ' \t \tLINENO = LINENO + 1', \ + ' \t \tLINECOUNT = LINECOUNT + 1', \ + ' \t \tL = LINE[:-1]', \ + ' \t \taflag = AFLAG', \ + ' \t \tif aflag:', \ + ' \t \t \tif FS: F = string.splitfields(L, FS)', \ + ' \t \t \telse: F = string.split(L)' \ + ] + epilogue = [ \ + ' \t \tif not PFLAG: continue', \ + ' \t \tif aflag:', \ + ' \t \t \tif FS: print string.joinfields(F, FS)', \ + ' \t \t \telse: print string.join(F)', \ + ' \t \telse: print L', \ + ] +else: + prologue = ['if 1:'] + epilogue = [] + +# Note that we indent using tabs only, so that any indentation style +# used in 'command' will come out right after re-indentation. + +program = string.joinfields(prologue, '\n') + '\n' +for line in SCRIPT: + program = program + (' \t \t' + line + '\n') +program = program + (string.joinfields(epilogue, '\n') + '\n') + +import tempfile +fp = tempfile.NamedTemporaryFile() +fp.write(program) +fp.flush() +if DFLAG: + import pdb + pdb.run('execfile(%r)' % (tfn,)) +else: + execfile(tfn) diff --git a/sys/src/cmd/python/Demo/scripts/primes.py b/sys/src/cmd/python/Demo/scripts/primes.py new file mode 100755 index 000000000..5935a3c84 --- /dev/null +++ b/sys/src/cmd/python/Demo/scripts/primes.py @@ -0,0 +1,27 @@ +#! /usr/bin/env python + +# Print prime numbers in a given range + +def main(): + import sys + min, max = 2, 0x7fffffff + if sys.argv[1:]: + min = int(eval(sys.argv[1])) + if sys.argv[2:]: + max = int(eval(sys.argv[2])) + primes(min, max) + +def primes(min, max): + if 2 >= min: print 2 + primes = [2] + i = 3 + while i <= max: + for p in primes: + if i%p == 0 or p*p > i: break + if i%p <> 0: + primes.append(i) + if i >= min: print i + i = i+2 + +if __name__ == "__main__": + main() diff --git a/sys/src/cmd/python/Demo/scripts/queens.py b/sys/src/cmd/python/Demo/scripts/queens.py new file mode 100755 index 000000000..74756be7d --- /dev/null +++ b/sys/src/cmd/python/Demo/scripts/queens.py @@ -0,0 +1,85 @@ +#! /usr/bin/env python + +"""N queens problem. + +The (well-known) problem is due to Niklaus Wirth. + +This solution is inspired by Dijkstra (Structured Programming). It is +a classic recursive backtracking approach. + +""" + +N = 8 # Default; command line overrides + +class Queens: + + def __init__(self, n=N): + self.n = n + self.reset() + + def reset(self): + n = self.n + self.y = [None]*n # Where is the queen in column x + self.row = [0]*n # Is row[y] safe? + self.up = [0] * (2*n-1) # Is upward diagonal[x-y] safe? + self.down = [0] * (2*n-1) # Is downward diagonal[x+y] safe? + self.nfound = 0 # Instrumentation + + def solve(self, x=0): # Recursive solver + for y in range(self.n): + if self.safe(x, y): + self.place(x, y) + if x+1 == self.n: + self.display() + else: + self.solve(x+1) + self.remove(x, y) + + def safe(self, x, y): + return not self.row[y] and not self.up[x-y] and not self.down[x+y] + + def place(self, x, y): + self.y[x] = y + self.row[y] = 1 + self.up[x-y] = 1 + self.down[x+y] = 1 + + def remove(self, x, y): + self.y[x] = None + self.row[y] = 0 + self.up[x-y] = 0 + self.down[x+y] = 0 + + silent = 0 # If set, count solutions only + + def display(self): + self.nfound = self.nfound + 1 + if self.silent: + return + print '+-' + '--'*self.n + '+' + for y in range(self.n-1, -1, -1): + print '|', + for x in range(self.n): + if self.y[x] == y: + print "Q", + else: + print ".", + print '|' + print '+-' + '--'*self.n + '+' + +def main(): + import sys + silent = 0 + n = N + if sys.argv[1:2] == ['-n']: + silent = 1 + del sys.argv[1] + if sys.argv[1:]: + n = int(sys.argv[1]) + q = Queens(n) + q.silent = silent + q.solve() + print "Found", q.nfound, "solutions." + +if __name__ == "__main__": + main() diff --git a/sys/src/cmd/python/Demo/scripts/script.py b/sys/src/cmd/python/Demo/scripts/script.py new file mode 100755 index 000000000..6eaa7aec2 --- /dev/null +++ b/sys/src/cmd/python/Demo/scripts/script.py @@ -0,0 +1,33 @@ +#! /usr/bin/env 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/sys/src/cmd/python/Demo/scripts/unbirthday.py b/sys/src/cmd/python/Demo/scripts/unbirthday.py new file mode 100755 index 000000000..2d0b8e5f4 --- /dev/null +++ b/sys/src/cmd/python/Demo/scripts/unbirthday.py @@ -0,0 +1,107 @@ +#! /usr/bin/env python + +# Calculate your unbirthday count (see Alice in Wonderland). +# This is defined as the number of days from your birth until today +# that weren't your birthday. (The day you were born is not counted). +# Leap years make it interesting. + +import sys +import time +import calendar + +def main(): + # Note that the range checks below also check for bad types, + # e.g. 3.14 or (). However syntactically invalid replies + # will raise an exception. + if sys.argv[1:]: + year = int(sys.argv[1]) + else: + year = int(raw_input('In which year were you born? ')) + if 0<=year<100: + print "I'll assume that by", year, + year = year + 1900 + print 'you mean', year, 'and not the early Christian era' + elif not (1850<=year<=2002): + print "It's hard to believe you were born in", year + return + # + if sys.argv[2:]: + month = int(sys.argv[2]) + else: + month = int(raw_input('And in which month? (1-12) ')) + if not (1<=month<=12): + print 'There is no month numbered', month + return + # + if sys.argv[3:]: + day = int(sys.argv[3]) + else: + day = int(raw_input('And on what day of that month? (1-31) ')) + if month == 2 and calendar.isleap(year): + maxday = 29 + else: + maxday = calendar.mdays[month] + if not (1<=day<=maxday): + print 'There are no', day, 'days in that month!' + return + # + bdaytuple = (year, month, day) + bdaydate = mkdate(bdaytuple) + print 'You were born on', format(bdaytuple) + # + todaytuple = time.localtime()[:3] + todaydate = mkdate(todaytuple) + print 'Today is', format(todaytuple) + # + if bdaytuple > todaytuple: + print 'You are a time traveler. Go back to the future!' + return + # + if bdaytuple == todaytuple: + print 'You were born today. Have a nice life!' + return + # + days = todaydate - bdaydate + print 'You have lived', days, 'days' + # + age = 0 + for y in range(year, todaytuple[0] + 1): + if bdaytuple < (y, month, day) <= todaytuple: + age = age + 1 + # + print 'You are', age, 'years old' + # + if todaytuple[1:] == bdaytuple[1:]: + print 'Congratulations! Today is your', nth(age), 'birthday' + print 'Yesterday was your', + else: + print 'Today is your', + print nth(days - age), 'unbirthday' + +def format((year, month, day)): + return '%d %s %d' % (day, calendar.month_name[month], year) + +def nth(n): + if n == 1: return '1st' + if n == 2: return '2nd' + if n == 3: return '3rd' + return '%dth' % n + +def mkdate((year, month, day)): + # Januari 1st, in 0 A.D. is arbitrarily defined to be day 1, + # even though that day never actually existed and the calendar + # was different then... + days = year*365 # years, roughly + days = days + (year+3)/4 # plus leap years, roughly + days = days - (year+99)/100 # minus non-leap years every century + days = days + (year+399)/400 # plus leap years every 4 centirues + for i in range(1, month): + if i == 2 and calendar.isleap(year): + days = days + 29 + else: + days = days + calendar.mdays[i] + days = days + day + return days + +if __name__ == "__main__": + main() diff --git a/sys/src/cmd/python/Demo/scripts/update.py b/sys/src/cmd/python/Demo/scripts/update.py new file mode 100755 index 000000000..c9360260e --- /dev/null +++ b/sys/src/cmd/python/Demo/scripts/update.py @@ -0,0 +1,92 @@ +#! /usr/bin/env python + +# Update a bunch of files according to a script. +# The input file contains lines of the form <filename>:<lineno>:<text>, +# meaning that the given line of the given file is to be replaced +# by the given text. This is useful for performing global substitutions +# on grep output: + +import os +import sys +import re + +pat = '^([^: \t\n]+):([1-9][0-9]*):' +prog = re.compile(pat) + +class FileObj: + def __init__(self, filename): + self.filename = filename + self.changed = 0 + try: + self.lines = open(filename, 'r').readlines() + except IOError, msg: + print '*** Can\'t open "%s":' % filename, msg + self.lines = None + return + print 'diffing', self.filename + + def finish(self): + if not self.changed: + print 'no changes to', self.filename + return + try: + os.rename(self.filename, self.filename + '~') + fp = open(self.filename, 'w') + except (os.error, IOError), msg: + print '*** Can\'t rewrite "%s":' % self.filename, msg + return + print 'writing', self.filename + for line in self.lines: + fp.write(line) + fp.close() + self.changed = 0 + + def process(self, lineno, rest): + if self.lines is None: + print '(not processed): %s:%s:%s' % ( + self.filename, lineno, rest), + return + i = eval(lineno) - 1 + if not 0 <= i < len(self.lines): + print '*** Line number out of range: %s:%s:%s' % ( + self.filename, lineno, rest), + return + if self.lines[i] == rest: + print '(no change): %s:%s:%s' % ( + self.filename, lineno, rest), + return + if not self.changed: + self.changed = 1 + print '%sc%s' % (lineno, lineno) + print '<', self.lines[i], + print '---' + self.lines[i] = rest + print '>', self.lines[i], + +def main(): + if sys.argv[1:]: + try: + fp = open(sys.argv[1], 'r') + except IOError, msg: + print 'Can\'t open "%s":' % sys.argv[1], msg + sys.exit(1) + else: + fp = sys.stdin + curfile = None + while 1: + line = fp.readline() + if not line: + if curfile: curfile.finish() + break + n = prog.match(line) + if n < 0: + print 'Funny line:', line, + continue + filename, lineno = prog.group(1, 2) + if not curfile or filename <> curfile.filename: + if curfile: curfile.finish() + curfile = FileObj(filename) + curfile.process(lineno, line[n:]) + +if __name__ == "__main__": + main() diff --git a/sys/src/cmd/python/Demo/scripts/wh.py b/sys/src/cmd/python/Demo/scripts/wh.py new file mode 100755 index 000000000..b9b09efa6 --- /dev/null +++ b/sys/src/cmd/python/Demo/scripts/wh.py @@ -0,0 +1,2 @@ +# This is here so I can use 'wh' instead of 'which' in '~/bin/generic_python' +import which |