Showing the changes to sys/src/cmd/python/Demo/scripts (23 of 3706 files). Show all
Unified · Split
| @@ -0,0 +1,23 @@ | ||
| 1 | +This directory contains a collection of executable Python scripts. | |
| 2 | + | |
| 3 | +See also the Tools/scripts directory! | |
| 4 | + | |
| 5 | +beer.py Print the classic 'bottles of beer' list. | |
| 6 | +eqfix.py Fix .py files to use the correct equality test operator | |
| 7 | +fact.py Factorize numbers | |
| 8 | +find-uname.py Search for Unicode characters using regexps. | |
| 9 | +from.py Summarize mailbox | |
| 10 | +ftpstats.py Summarize ftp daemon log file | |
| 11 | +lpwatch.py Watch BSD line printer queues | |
| 12 | +makedir.py Like mkdir -p | |
| 13 | +markov.py Markov chain simulation of words or characters | |
| 14 | +mboxconvvert.py Convert MH or MMDF mailboxes to unix mailbox format | |
| 15 | +mkrcs.py Fix symlinks named RCS into parallel tree | |
| 16 | +morse.py Produce morse code (audible or on AIFF file) | |
| 17 | +pi.py Print all digits of pi -- given enough time and memory | |
| 18 | +pp.py Emulate some Perl command line options | |
| 19 | +primes.py Print prime numbers | |
| 20 | +queens.py Dijkstra's solution to Wirth's "N Queens problem" | |
| 21 | +script.py Equivalent to BSD script(1) -- by Steen Lumholt | |
| 22 | +unbirthday.py Print unbirthday count | |
| 23 | +update.py Update a bunch of files according to a script. | |
| @@ -0,0 +1,14 @@ | ||
| 1 | +#! /usr/bin/env python | |
| 2 | +# By GvR, demystified after a version by Fredrik Lundh. | |
| 3 | +import sys | |
| 4 | +n = 100 | |
| 5 | +if sys.argv[1:]: n = int(sys.argv[1]) | |
| 6 | +def bottle(n): | |
| 7 | + if n == 0: return "no more bottles of beer" | |
| 8 | + if n == 1: return "one bottle of beer" | |
| 9 | + return str(n) + " bottles of beer" | |
| 10 | +for i in range(n): | |
| 11 | + print bottle(n-i), "on the wall," | |
| 12 | + print bottle(n-i) + "." | |
| 13 | + print "Take one down, pass it around," | |
| 14 | + print bottle(n-i-1), "on the wall." | |
| @@ -0,0 +1,198 @@ | ||
| 1 | +#! /usr/bin/env python | |
| 2 | + | |
| 3 | +# Fix Python source files to use the new equality test operator, i.e., | |
| 4 | +# if x = y: ... | |
| 5 | +# is changed to | |
| 6 | +# if x == y: ... | |
| 7 | +# The script correctly tokenizes the Python program to reliably | |
| 8 | +# distinguish between assignments and equality tests. | |
| 9 | +# | |
| 10 | +# Command line arguments are files or directories to be processed. | |
| 11 | +# Directories are searched recursively for files whose name looks | |
| 12 | +# like a python module. | |
| 13 | +# Symbolic links are always ignored (except as explicit directory | |
| 14 | +# arguments). Of course, the original file is kept as a back-up | |
| 15 | +# (with a "~" attached to its name). | |
| 16 | +# It complains about binaries (files containing null bytes) | |
| 17 | +# and about files that are ostensibly not Python files: if the first | |
| 18 | +# line starts with '#!' and does not contain the string 'python'. | |
| 19 | +# | |
| 20 | +# Changes made are reported to stdout in a diff-like format. | |
| 21 | +# | |
| 22 | +# Undoubtedly you can do this using find and sed or perl, but this is | |
| 23 | +# a nice example of Python code that recurses down a directory tree | |
| 24 | +# and uses regular expressions. Also note several subtleties like | |
| 25 | +# preserving the file's mode and avoiding to even write a temp file | |
| 26 | +# when no changes are needed for a file. | |
| 27 | +# | |
| 28 | +# NB: by changing only the function fixline() you can turn this | |
| 29 | +# into a program for a different change to Python programs... | |
| 30 | + | |
| 31 | +import sys | |
| 32 | +import re | |
| 33 | +import os | |
| 34 | +from stat import * | |
| 35 | +import string | |
| 36 | + | |
| 37 | +err = sys.stderr.write | |
| 38 | +dbg = err | |
| 39 | +rep = sys.stdout.write | |
| 40 | + | |
| 41 | +def main(): | |
| 42 | + bad = 0 | |
| 43 | + if not sys.argv[1:]: # No arguments | |
| 44 | + err('usage: ' + sys.argv[0] + ' file-or-directory ...\n') | |
| 45 | + sys.exit(2) | |
| 46 | + for arg in sys.argv[1:]: | |
| 47 | + if os.path.isdir(arg): | |
| 48 | + if recursedown(arg): bad = 1 | |
| 49 | + elif os.path.islink(arg): | |
| 50 | + err(arg + ': will not process symbolic links\n') | |
| 51 | + bad = 1 | |
| 52 | + else: | |
| 53 | + if fix(arg): bad = 1 | |
| 54 | + sys.exit(bad) | |
| 55 | + | |
| 56 | +ispythonprog = re.compile('^[a-zA-Z0-9_]+\.py$') | |
| 57 | +def ispython(name): | |
| 58 | + return ispythonprog.match(name) >= 0 | |
| 59 | + | |
| 60 | +def recursedown(dirname): | |
| 61 | + dbg('recursedown(%r)\n' % (dirname,)) | |
| 62 | + bad = 0 | |
| 63 | + try: | |
| 64 | + names = os.listdir(dirname) | |
| 65 | + except os.error, msg: | |
| 66 | + err('%s: cannot list directory: %r\n' % (dirname, msg)) | |
| 67 | + return 1 | |
| 68 | + names.sort() | |
| 69 | + subdirs = [] | |
| 70 | + for name in names: | |
| 71 | + if name in (os.curdir, os.pardir): continue | |
| 72 | + fullname = os.path.join(dirname, name) | |
| 73 | + if os.path.islink(fullname): pass | |
| 74 | + elif os.path.isdir(fullname): | |
| 75 | + subdirs.append(fullname) | |
| 76 | + elif ispython(name): | |
| 77 | + if fix(fullname): bad = 1 | |
| 78 | + for fullname in subdirs: | |
| 79 | + if recursedown(fullname): bad = 1 | |
| 80 | + return bad | |
| 81 | + | |
| 82 | +def fix(filename): | |
| 83 | +## dbg('fix(%r)\n' % (dirname,)) | |
| 84 | + try: | |
| 85 | + f = open(filename, 'r') | |
| 86 | + except IOError, msg: | |
| 87 | + err('%s: cannot open: %r\n' % (filename, msg)) | |
| 88 | + return 1 | |
| 89 | + head, tail = os.path.split(filename) | |
| 90 | + tempname = os.path.join(head, '@' + tail) | |
| 91 | + g = None | |
| 92 | + # If we find a match, we rewind the file and start over but | |
| 93 | + # now copy everything to a temp file. | |
| 94 | + lineno = 0 | |
| 95 | + while 1: | |
| 96 | + line = f.readline() | |
| 97 | + if not line: break | |
| 98 | + lineno = lineno + 1 | |
| 99 | + if g is None and '\0' in line: | |
| 100 | + # Check for binary files | |
| 101 | + err(filename + ': contains null bytes; not fixed\n') | |
| 102 | + f.close() | |
| 103 | + return 1 | |
| 104 | + if lineno == 1 and g is None and line[:2] == '#!': | |
| 105 | + # Check for non-Python scripts | |
| 106 | + words = string.split(line[2:]) | |
| 107 | + if words and re.search('[pP]ython', words[0]) < 0: | |
| 108 | + msg = filename + ': ' + words[0] | |
| 109 | + msg = msg + ' script; not fixed\n' | |
| 110 | + err(msg) | |
| 111 | + f.close() | |
| 112 | + return 1 | |
| 113 | + while line[-2:] == '\\\n': | |
| 114 | + nextline = f.readline() | |
| 115 | + if not nextline: break | |
| 116 | + line = line + nextline | |
| 117 | + lineno = lineno + 1 | |
| 118 | + newline = fixline(line) | |
| 119 | + if newline != line: | |
| 120 | + if g is None: | |
| 121 | + try: | |
| 122 | + g = open(tempname, 'w') | |
| 123 | + except IOError, msg: | |
| 124 | + f.close() | |
| 125 | + err('%s: cannot create: %r\n' % (tempname, msg)) | |
| 126 | + return 1 | |
| 127 | + f.seek(0) | |
| 128 | + lineno = 0 | |
| 129 | + rep(filename + ':\n') | |
| 130 | + continue # restart from the beginning | |
| 131 | + rep(repr(lineno) + '\n') | |
| 132 | + rep('< ' + line) | |
| 133 | + rep('> ' + newline) | |
| 134 | + if g is not None: | |
| 135 | + g.write(newline) | |
| 136 | + | |
| 137 | + # End of file | |
| 138 | + f.close() | |
| 139 | + if not g: return 0 # No changes | |
| 140 | + | |
| 141 | + # Finishing touch -- move files | |
| 142 | + | |
| 143 | + # First copy the file's mode to the temp file | |
| 144 | + try: | |
| 145 | + statbuf = os.stat(filename) | |
| 146 | + os.chmod(tempname, statbuf[ST_MODE] & 07777) | |
| 147 | + except os.error, msg: | |
| 148 | + err('%s: warning: chmod failed (%r)\n' % (tempname, msg)) | |
| 149 | + # Then make a backup of the original file as filename~ | |
| 150 | + try: | |
| 151 | + os.rename(filename, filename + '~') | |
| 152 | + except os.error, msg: | |
| 153 | + err('%s: warning: backup failed (%r)\n' % (filename, msg)) | |
| 154 | + # Now move the temp file to the original file | |
| 155 | + try: | |
| 156 | + os.rename(tempname, filename) | |
| 157 | + except os.error, msg: | |
| 158 | + err('%s: rename failed (%r)\n' % (filename, msg)) | |
| 159 | + return 1 | |
| 160 | + # Return succes | |
| 161 | + return 0 | |
| 162 | + | |
| 163 | + | |
| 164 | +from tokenize import tokenprog | |
| 165 | + | |
| 166 | +match = {'if':':', 'elif':':', 'while':':', 'return':'\n', \ | |
| 167 | + '(':')', '[':']', '{':'}', '`':'`'} | |
| 168 | + | |
| 169 | +def fixline(line): | |
| 170 | + # Quick check for easy case | |
| 171 | + if '=' not in line: return line | |
| 172 | + | |
| 173 | + i, n = 0, len(line) | |
| 174 | + stack = [] | |
| 175 | + while i < n: | |
| 176 | + j = tokenprog.match(line, i) | |
| 177 | + if j < 0: | |
| 178 | + # A bad token; forget about the rest of this line | |
| 179 | + print '(Syntax error:)' | |
| 180 | + print line, | |
| 181 | + return line | |
| 182 | + a, b = tokenprog.regs[3] # Location of the token proper | |
| 183 | + token = line[a:b] | |
| 184 | + i = i+j | |
| 185 | + if stack and token == stack[-1]: | |
| 186 | + del stack[-1] | |
| 187 | + elif match.has_key(token): | |
| 188 | + stack.append(match[token]) | |
| 189 | + elif token == '=' and stack: | |
| 190 | + line = line[:a] + '==' + line[b:] | |
| 191 | + i, n = a + len('=='), len(line) | |
| 192 | + elif token == '==' and not stack: | |
| 193 | + print '(Warning: \'==\' at top level:)' | |
| 194 | + print line, | |
| 195 | + return line | |
| 196 | + | |
| 197 | +if __name__ == "__main__": | |
| 198 | + main() | |
| @@ -0,0 +1,49 @@ | ||
| 1 | +#! /usr/bin/env python | |
| 2 | + | |
| 3 | +# Factorize numbers. | |
| 4 | +# The algorithm is not efficient, but easy to understand. | |
| 5 | +# If there are large factors, it will take forever to find them, | |
| 6 | +# because we try all odd numbers between 3 and sqrt(n)... | |
| 7 | + | |
| 8 | +import sys | |
| 9 | +from math import sqrt | |
| 10 | + | |
| 11 | +error = 'fact.error' # exception | |
| 12 | + | |
| 13 | +def fact(n): | |
| 14 | + if n < 1: raise error # fact() argument should be >= 1 | |
| 15 | + if n == 1: return [] # special case | |
| 16 | + res = [] | |
| 17 | + # Treat even factors special, so we can use i = i+2 later | |
| 18 | + while n%2 == 0: | |
| 19 | + res.append(2) | |
| 20 | + n = n/2 | |
| 21 | + # Try odd numbers up to sqrt(n) | |
| 22 | + limit = sqrt(float(n+1)) | |
| 23 | + i = 3 | |
| 24 | + while i <= limit: | |
| 25 | + if n%i == 0: | |
| 26 | + res.append(i) | |
| 27 | + n = n/i | |
| 28 | + limit = sqrt(n+1) | |
| 29 | + else: | |
| 30 | + i = i+2 | |
| 31 | + if n != 1: | |
| 32 | + res.append(n) | |
| 33 | + return res | |
| 34 | + | |
| 35 | +def main(): | |
| 36 | + if len(sys.argv) > 1: | |
| 37 | + for arg in sys.argv[1:]: | |
| 38 | + n = eval(arg) | |
| 39 | + print n, fact(n) | |
| 40 | + else: | |
| 41 | + try: | |
| 42 | + while 1: | |
| 43 | + n = input() | |
| 44 | + print n, fact(n) | |
| 45 | + except EOFError: | |
| 46 | + pass | |
| 47 | + | |
| 48 | +if __name__ == "__main__": | |
| 49 | + main() | |
| @@ -0,0 +1,40 @@ | ||
| 1 | +#!/usr/bin/env python | |
| 2 | + | |
| 3 | +""" | |
| 4 | +For each argument on the command line, look for it in the set of all Unicode | |
| 5 | +names. Arguments are treated as case-insensitive regular expressions, e.g.: | |
| 6 | + | |
| 7 | + % find-uname 'small letter a$' 'horizontal line' | |
| 8 | + *** small letter a$ matches *** | |
| 9 | + LATIN SMALL LETTER A (97) | |
| 10 | + COMBINING LATIN SMALL LETTER A (867) | |
| 11 | + CYRILLIC SMALL LETTER A (1072) | |
| 12 | + PARENTHESIZED LATIN SMALL LETTER A (9372) | |
| 13 | + CIRCLED LATIN SMALL LETTER A (9424) | |
| 14 | + FULLWIDTH LATIN SMALL LETTER A (65345) | |
| 15 | + *** horizontal line matches *** | |
| 16 | + HORIZONTAL LINE EXTENSION (9135) | |
| 17 | +""" | |
| 18 | + | |
| 19 | +import unicodedata | |
| 20 | +import sys | |
| 21 | +import re | |
| 22 | + | |
| 23 | +def main(args): | |
| 24 | + unicode_names= [] | |
| 25 | + for ix in range(sys.maxunicode+1): | |
| 26 | + try: | |
| 27 | + unicode_names.append( (ix, unicodedata.name(unichr(ix))) ) | |
| 28 | + except ValueError: # no name for the character | |
| 29 | + pass | |
| 30 | + for arg in args: | |
| 31 | + pat = re.compile(arg, re.I) | |
| 32 | + matches = [(x,y) for (x,y) in unicode_names | |
| 33 | + if pat.search(y) is not None] | |
| 34 | + if matches: | |
| 35 | + print "***", arg, "matches", "***" | |
| 36 | + for (x,y) in matches: | |
| 37 | + print "%s (%d)" % (y,x) | |
| 38 | + | |
| 39 | +if __name__ == "__main__": | |
| 40 | + main(sys.argv[1:]) | |
| @@ -0,0 +1,35 @@ | ||
| 1 | +#! /usr/bin/env python | |
| 2 | + | |
| 3 | +# Print From and Subject of messages in $MAIL. | |
| 4 | +# Extension to multiple mailboxes and other bells & whistles are left | |
| 5 | +# as exercises for the reader. | |
| 6 | + | |
| 7 | +import sys, os | |
| 8 | + | |
| 9 | +# Open mailbox file. Exits with exception when this fails. | |
| 10 | + | |
| 11 | +try: | |
| 12 | + mailbox = os.environ['MAIL'] | |
| 13 | +except (AttributeError, KeyError): | |
| 14 | + sys.stderr.write('No environment variable $MAIL\n') | |
| 15 | + sys.exit(2) | |
| 16 | + | |
| 17 | +try: | |
| 18 | + mail = open(mailbox) | |
| 19 | +except IOError: | |
| 20 | + sys.exit('Cannot open mailbox file: ' + mailbox) | |
| 21 | + | |
| 22 | +while 1: | |
| 23 | + line = mail.readline() | |
| 24 | + if not line: | |
| 25 | + break # EOF | |
| 26 | + if line.startswith('From '): | |
| 27 | + # Start of message found | |
| 28 | + print line[:-1], | |
| 29 | + while 1: | |
| 30 | + line = mail.readline() | |
| 31 | + if not line or line == '\n': | |
| 32 | + break | |
| 33 | + if line.startswith('Subject: '): | |
| 34 | + print repr(line[9:-1]), | |
| 35 | ||
| @@ -0,0 +1,145 @@ | ||
| 1 | +#! /usr/bin/env python | |
| 2 | + | |
| 3 | +# Extract statistics from ftp daemon log. | |
| 4 | + | |
| 5 | +# Usage: | |
| 6 | +# ftpstats [-m maxitems] [-s search] [file] | |
| 7 | +# -m maxitems: restrict number of items in "top-N" lists, default 25. | |
| 8 | +# -s string: restrict statistics to lines containing this string. | |
| 9 | +# Default file is /usr/adm/ftpd; a "-" means read standard input. | |
| 10 | + | |
| 11 | +# The script must be run on the host where the ftp daemon runs. | |
| 12 | +# (At CWI this is currently buizerd.) | |
| 13 | + | |
| 14 | +import os | |
| 15 | +import sys | |
| 16 | +import re | |
| 17 | +import string | |
| 18 | +import getopt | |
| 19 | + | |
| 20 | +pat = '^([a-zA-Z0-9 :]*)!(.*)!(.*)!([<>].*)!([0-9]+)!([0-9]+)$' | |
| 21 | +prog = re.compile(pat) | |
| 22 | + | |
| 23 | +def main(): | |
| 24 | + maxitems = 25 | |
| 25 | + search = None | |
| 26 | + try: | |
| 27 | + opts, args = getopt.getopt(sys.argv[1:], 'm:s:') | |
| 28 | + except getopt.error, msg: | |
| 29 | + print msg | |
| 30 | + print 'usage: ftpstats [-m maxitems] [file]' | |
| 31 | + sys.exit(2) | |
| 32 | + for o, a in opts: | |
| 33 | + if o == '-m': | |
| 34 | + maxitems = string.atoi(a) | |
| 35 | + if o == '-s': | |
| 36 | + search = a | |
| 37 | + file = '/usr/adm/ftpd' | |
| 38 | + if args: file = args[0] | |
| 39 | + if file == '-': | |
| 40 | + f = sys.stdin | |
| 41 | + else: | |
| 42 | + try: | |
| 43 | + f = open(file, 'r') | |
| 44 | + except IOError, msg: | |
| 45 | + print file, ':', msg | |
| 46 | + sys.exit(1) | |
| 47 | + bydate = {} | |
| 48 | + bytime = {} | |
| 49 | + byfile = {} | |
| 50 | + bydir = {} | |
| 51 | + byhost = {} | |
| 52 | + byuser = {} | |
| 53 | + bytype = {} | |
| 54 | + lineno = 0 | |
| 55 | + try: | |
| 56 | + while 1: | |
| 57 | + line = f.readline() | |
| 58 | + if not line: break | |
| 59 | + lineno = lineno + 1 | |
| 60 | + if search and string.find(line, search) < 0: | |
| 61 | + continue | |
| 62 | + if prog.match(line) < 0: | |
| 63 | + print 'Bad line', lineno, ':', repr(line) | |
| 64 | + continue | |
| 65 | + items = prog.group(1, 2, 3, 4, 5, 6) | |
| 66 | + (logtime, loguser, loghost, logfile, logbytes, | |
| 67 | + logxxx2) = items | |
| 68 | +## print logtime | |
| 69 | +## print '-->', loguser | |
| 70 | +## print '--> -->', loghost | |
| 71 | +## print '--> --> -->', logfile | |
| 72 | +## print '--> --> --> -->', logbytes | |
| 73 | +## print '--> --> --> --> -->', logxxx2 | |
| 74 | +## for i in logtime, loghost, logbytes, logxxx2: | |
| 75 | +## if '!' in i: print '???', i | |
| 76 | + add(bydate, logtime[-4:] + ' ' + logtime[:6], items) | |
| 77 | + add(bytime, logtime[7:9] + ':00-59', items) | |
| 78 | + direction, logfile = logfile[0], logfile[1:] | |
| 79 | + # The real path probably starts at the last //... | |
| 80 | + while 1: | |
| 81 | + i = string.find(logfile, '//') | |
| 82 | + if i < 0: break | |
| 83 | + logfile = logfile[i+1:] | |
| 84 | + add(byfile, logfile + ' ' + direction, items) | |
| 85 | + logdir = os.path.dirname(logfile) | |
| 86 | +## logdir = os.path.normpath(logdir) + '/.' | |
| 87 | + while 1: | |
| 88 | + add(bydir, logdir + ' ' + direction, items) | |
| 89 | + dirhead = os.path.dirname(logdir) | |
| 90 | + if dirhead == logdir: break | |
| 91 | + logdir = dirhead | |
| 92 | + add(byhost, loghost, items) | |
| 93 | + add(byuser, loguser, items) | |
| 94 | + add(bytype, direction, items) | |
| 95 | + except KeyboardInterrupt: | |
| 96 | + print 'Interrupted at line', lineno | |
| 97 | + show(bytype, 'by transfer direction', maxitems) | |
| 98 | + show(bydir, 'by directory', maxitems) | |
| 99 | + show(byfile, 'by file', maxitems) | |
| 100 | + show(byhost, 'by host', maxitems) | |
| 101 | + show(byuser, 'by user', maxitems) | |
| 102 | + showbar(bydate, 'by date') | |
| 103 | + showbar(bytime, 'by time of day') | |
| 104 | + | |
| 105 | +def showbar(dict, title): | |
| 106 | + n = len(title) | |
| 107 | + print '='*((70-n)/2), title, '='*((71-n)/2) | |
| 108 | + list = [] | |
| 109 | + keys = dict.keys() | |
| 110 | + keys.sort() | |
| 111 | + for key in keys: | |
| 112 | + n = len(str(key)) | |
| 113 | + list.append((len(dict[key]), key)) | |
| 114 | + maxkeylength = 0 | |
| 115 | + maxcount = 0 | |
| 116 | + for count, key in list: | |
| 117 | + maxkeylength = max(maxkeylength, len(key)) | |
| 118 | + maxcount = max(maxcount, count) | |
| 119 | + maxbarlength = 72 - maxkeylength - 7 | |
| 120 | + for count, key in list: | |
| 121 | + barlength = int(round(maxbarlength*float(count)/maxcount)) | |
| 122 | + bar = '*'*barlength | |
| 123 | + print '%5d %-*s %s' % (count, maxkeylength, key, bar) | |
| 124 | + | |
| 125 | +def show(dict, title, maxitems): | |
| 126 | + if len(dict) > maxitems: | |
| 127 | + title = title + ' (first %d)'%maxitems | |
| 128 | + n = len(title) | |
| 129 | + print '='*((70-n)/2), title, '='*((71-n)/2) | |
| 130 | + list = [] | |
| 131 | + keys = dict.keys() | |
| 132 | + for key in keys: | |
| 133 | + list.append((-len(dict[key]), key)) | |
| 134 | + list.sort() | |
| 135 | + for count, key in list[:maxitems]: | |
| 136 | + print '%5d %s' % (-count, key) | |
| 137 | + | |
| 138 | +def add(dict, key, item): | |
| 139 | + if dict.has_key(key): | |
| 140 | + dict[key].append(item) | |
| 141 | + else: | |
| 142 | + dict[key] = [item] | |
| 143 | + | |
| 144 | +if __name__ == "__main__": | |
| 145 | + main() | |
| @@ -0,0 +1,110 @@ | ||
| 1 | +#! /usr/bin/env python | |
| 2 | + | |
| 3 | +# Watch line printer queue(s). | |
| 4 | +# Intended for BSD 4.3 lpq. | |
| 5 | + | |
| 6 | +import posix | |
| 7 | +import sys | |
| 8 | +import time | |
| 9 | +import string | |
| 10 | + | |
| 11 | +DEF_PRINTER = 'psc' | |
| 12 | +DEF_DELAY = 10 | |
| 13 | + | |
| 14 | +def main(): | |
| 15 | + delay = DEF_DELAY # XXX Use getopt() later | |
| 16 | + try: | |
| 17 | + thisuser = posix.environ['LOGNAME'] | |
| 18 | + except: | |
| 19 | + thisuser = posix.environ['USER'] | |
| 20 | + printers = sys.argv[1:] | |
| 21 | + if printers: | |
| 22 | + # Strip '-P' from printer names just in case | |
| 23 | + # the user specified it... | |
| 24 | + for i in range(len(printers)): | |
| 25 | + if printers[i][:2] == '-P': | |
| 26 | + printers[i] = printers[i][2:] | |
| 27 | + else: | |
| 28 | + if posix.environ.has_key('PRINTER'): | |
| 29 | + printers = [posix.environ['PRINTER']] | |
| 30 | + else: | |
| 31 | + printers = [DEF_PRINTER] | |
| 32 | + # | |
| 33 | + clearhome = posix.popen('clear', 'r').read() | |
| 34 | + # | |
| 35 | + while 1: | |
| 36 | + text = clearhome | |
| 37 | + for name in printers: | |
| 38 | + text = text + makestatus(name, thisuser) + '\n' | |
| 39 | + print text | |
| 40 | + time.sleep(delay) | |
| 41 | + | |
| 42 | +def makestatus(name, thisuser): | |
| 43 | + pipe = posix.popen('lpq -P' + name + ' 2>&1', 'r') | |
| 44 | + lines = [] | |
| 45 | + users = {} | |
| 46 | + aheadbytes = 0 | |
| 47 | + aheadjobs = 0 | |
| 48 | + userseen = 0 | |
| 49 | + totalbytes = 0 | |
| 50 | + totaljobs = 0 | |
| 51 | + while 1: | |
| 52 | + line = pipe.readline() | |
| 53 | + if not line: break | |
| 54 | + fields = string.split(line) | |
| 55 | + n = len(fields) | |
| 56 | + if len(fields) >= 6 and fields[n-1] == 'bytes': | |
| 57 | + rank = fields[0] | |
| 58 | + user = fields[1] | |
| 59 | + job = fields[2] | |
| 60 | + files = fields[3:-2] | |
| 61 | + bytes = eval(fields[n-2]) | |
| 62 | + if user == thisuser: | |
| 63 | + userseen = 1 | |
| 64 | + elif not userseen: | |
| 65 | + aheadbytes = aheadbytes + bytes | |
| 66 | + aheadjobs = aheadjobs + 1 | |
| 67 | + totalbytes = totalbytes + bytes | |
| 68 | + totaljobs = totaljobs + 1 | |
| 69 | + if users.has_key(user): | |
| 70 | + ujobs, ubytes = users[user] | |
| 71 | + else: | |
| 72 | + ujobs, ubytes = 0, 0 | |
| 73 | + ujobs = ujobs + 1 | |
| 74 | + ubytes = ubytes + bytes | |
| 75 | + users[user] = ujobs, ubytes | |
| 76 | + else: | |
| 77 | + if fields and fields[0] <> 'Rank': | |
| 78 | + line = string.strip(line) | |
| 79 | + if line == 'no entries': | |
| 80 | + line = name + ': idle' | |
| 81 | + elif line[-22:] == ' is ready and printing': | |
| 82 | + line = name | |
| 83 | + lines.append(line) | |
| 84 | + # | |
| 85 | + if totaljobs: | |
| 86 | + line = '%d K' % ((totalbytes+1023)/1024) | |
| 87 | + if totaljobs <> len(users): | |
| 88 | + line = line + ' (%d jobs)' % totaljobs | |
| 89 | + if len(users) == 1: | |
| 90 | + line = line + ' for %s' % (users.keys()[0],) | |
| 91 | + else: | |
| 92 | + line = line + ' for %d users' % len(users) | |
| 93 | + if userseen: | |
| 94 | + if aheadjobs == 0: | |
| 95 | + line = line + ' (%s first)' % thisuser | |
| 96 | + else: | |
| 97 | + line = line + ' (%d K before %s)' % ( | |
| 98 | + (aheadbytes+1023)/1024, thisuser) | |
| 99 | + lines.append(line) | |
| 100 | + # | |
| 101 | + sts = pipe.close() | |
| 102 | + if sts: | |
| 103 | + lines.append('lpq exit status %r' % (sts,)) | |
| 104 | + return string.joinfields(lines, ': ') | |
| 105 | + | |
| 106 | +if __name__ == "__main__": | |
| 107 | + try: | |
| 108 | + main() | |
| 109 | + except KeyboardInterrupt: | |
| 110 | + pass | |
| @@ -0,0 +1,21 @@ | ||
| 1 | +#! /usr/bin/env python | |
| 2 | + | |
| 3 | +# Like mkdir, but also make intermediate directories if necessary. | |
| 4 | +# It is not an error if the given directory already exists (as long | |
| 5 | +# as it is a directory). | |
| 6 | +# Errors are not treated specially -- you just get a Python exception. | |
| 7 | + | |
| 8 | +import sys, os | |
| 9 | + | |
| 10 | +def main(): | |
| 11 | + for p in sys.argv[1:]: | |
| 12 | + makedirs(p) | |
| 13 | + | |
| 14 | +def makedirs(p): | |
| 15 | + if p and not os.path.isdir(p): | |
| 16 | + head, tail = os.path.split(p) | |
| 17 | + makedirs(head) | |
| 18 | + os.mkdir(p, 0777) | |
| 19 | + | |
| 20 | +if __name__ == "__main__": | |
| 21 | + main() | |
| @@ -0,0 +1,117 @@ | ||
| 1 | +#! /usr/bin/env python | |
| 2 | + | |
| 3 | +class Markov: | |
| 4 | + def __init__(self, histsize, choice): | |
| 5 | + self.histsize = histsize | |
| 6 | + self.choice = choice | |
| 7 | + self.trans = {} | |
| 8 | + def add(self, state, next): | |
| 9 | + if not self.trans.has_key(state): | |
| 10 | + self.trans[state] = [next] | |
| 11 | + else: | |
| 12 | + self.trans[state].append(next) | |
| 13 | + def put(self, seq): | |
| 14 | + n = self.histsize | |
| 15 | + add = self.add | |
| 16 | + add(None, seq[:0]) | |
| 17 | + for i in range(len(seq)): | |
| 18 | + add(seq[max(0, i-n):i], seq[i:i+1]) | |
| 19 | + add(seq[len(seq)-n:], None) | |
| 20 | + def get(self): | |
| 21 | + choice = self.choice | |
| 22 | + trans = self.trans | |
| 23 | + n = self.histsize | |
| 24 | + seq = choice(trans[None]) | |
| 25 | + while 1: | |
| 26 | + subseq = seq[max(0, len(seq)-n):] | |
| 27 | + options = trans[subseq] | |
| 28 | + next = choice(options) | |
| 29 | + if not next: break | |
| 30 | + seq = seq + next | |
| 31 | + return seq | |
| 32 | + | |
| 33 | +def test(): | |
| 34 | + import sys, string, random, getopt | |
| 35 | + args = sys.argv[1:] | |
| 36 | + try: | |
| 37 | + opts, args = getopt.getopt(args, '0123456789cdw') | |
| 38 | + except getopt.error: | |
| 39 | + print 'Usage: markov [-#] [-cddqw] [file] ...' | |
| 40 | + print 'Options:' | |
| 41 | + print '-#: 1-digit history size (default 2)' | |
| 42 | + print '-c: characters (default)' | |
| 43 | + print '-w: words' | |
| 44 | + print '-d: more debugging output' | |
| 45 | + print '-q: no debugging output' | |
| 46 | + print 'Input files (default stdin) are split in paragraphs' | |
| 47 | + print 'separated blank lines and each paragraph is split' | |
| 48 | + print 'in words by whitespace, then reconcatenated with' | |
| 49 | + print 'exactly one space separating words.' | |
| 50 | + print 'Output consists of paragraphs separated by blank' | |
| 51 | + print 'lines, where lines are no longer than 72 characters.' | |
| 52 | + histsize = 2 | |
| 53 | + do_words = 0 | |
| 54 | + debug = 1 | |
| 55 | + for o, a in opts: | |
| 56 | + if '-0' <= o <= '-9': histsize = eval(o[1:]) | |
| 57 | + if o == '-c': do_words = 0 | |
| 58 | + if o == '-d': debug = debug + 1 | |
| 59 | + if o == '-q': debug = 0 | |
| 60 | + if o == '-w': do_words = 1 | |
| 61 | + if not args: args = ['-'] | |
| 62 | + m = Markov(histsize, random.choice) | |
| 63 | + try: | |
| 64 | + for filename in args: | |
| 65 | + if filename == '-': | |
| 66 | + f = sys.stdin | |
| 67 | + if f.isatty(): | |
| 68 | + print 'Sorry, need stdin from file' | |
| 69 | + continue | |
| 70 | + else: | |
| 71 | + f = open(filename, 'r') | |
| 72 | + if debug: print 'processing', filename, '...' | |
| 73 | + text = f.read() | |
| 74 | + f.close() | |
| 75 | + paralist = string.splitfields(text, '\n\n') | |
| 76 | + for para in paralist: | |
| 77 | + if debug > 1: print 'feeding ...' | |
| 78 | + words = string.split(para) | |
| 79 | + if words: | |
| 80 | + if do_words: data = tuple(words) | |
| 81 | + else: data = string.joinfields(words, ' ') | |
| 82 | + m.put(data) | |
| 83 | + except KeyboardInterrupt: | |
| 84 | + print 'Interrupted -- continue with data read so far' | |
| 85 | + if not m.trans: | |
| 86 | + print 'No valid input files' | |
| 87 | + return | |
| 88 | + if debug: print 'done.' | |
| 89 | + if debug > 1: | |
| 90 | + for key in m.trans.keys(): | |
| 91 | + if key is None or len(key) < histsize: | |
| 92 | + print repr(key), m.trans[key] | |
| 93 | + if histsize == 0: print repr(''), m.trans[''] | |
| 94 | ||
| 95 | + while 1: | |
| 96 | + data = m.get() | |
| 97 | + if do_words: words = data | |
| 98 | + else: words = string.split(data) | |
| 99 | + n = 0 | |
| 100 | + limit = 72 | |
| 101 | + for w in words: | |
| 102 | + if n + len(w) > limit: | |
| 103 | ||
| 104 | + n = 0 | |
| 105 | + print w, | |
| 106 | + n = n + len(w) + 1 | |
| 107 | ||
| 108 | ||
| 109 | + | |
| 110 | +def tuple(list): | |
| 111 | + if len(list) == 0: return () | |
| 112 | + if len(list) == 1: return (list[0],) | |
| 113 | + i = len(list)/2 | |
| 114 | + return tuple(list[:i]) + tuple(list[i:]) | |
| 115 | + | |
| 116 | +if __name__ == "__main__": | |
| 117 | + test() | |
| @@ -0,0 +1,124 @@ | ||
| 1 | +#! /usr/bin/env python | |
| 2 | + | |
| 3 | +# Convert MH directories (1 message per file) or MMDF mailboxes (4x^A | |
| 4 | +# delimited) to unix mailbox (From ... delimited) on stdout. | |
| 5 | +# If -f is given, files contain one message per file (e.g. MH messages) | |
| 6 | + | |
| 7 | +import rfc822 | |
| 8 | +import sys | |
| 9 | +import time | |
| 10 | +import os | |
| 11 | +import stat | |
| 12 | +import getopt | |
| 13 | +import re | |
| 14 | + | |
| 15 | +def main(): | |
| 16 | + dofile = mmdf | |
| 17 | + try: | |
| 18 | + opts, args = getopt.getopt(sys.argv[1:], 'f') | |
| 19 | + except getopt.error, msg: | |
| 20 | + sys.stderr.write('%s\n' % msg) | |
| 21 | + sys.exit(2) | |
| 22 | + for o, a in opts: | |
| 23 | + if o == '-f': | |
| 24 | + dofile = message | |
| 25 | + if not args: | |
| 26 | + args = ['-'] | |
| 27 | + sts = 0 | |
| 28 | + for arg in args: | |
| 29 | + if arg == '-' or arg == '': | |
| 30 | + sts = dofile(sys.stdin) or sts | |
| 31 | + elif os.path.isdir(arg): | |
| 32 | + sts = mh(arg) or sts | |
| 33 | + elif os.path.isfile(arg): | |
| 34 | + try: | |
| 35 | + f = open(arg) | |
| 36 | + except IOError, msg: | |
| 37 | + sys.stderr.write('%s: %s\n' % (arg, msg)) | |
| 38 | + sts = 1 | |
| 39 | + continue | |
| 40 | + sts = dofile(f) or sts | |
| 41 | + f.close() | |
| 42 | + else: | |
| 43 | + sys.stderr.write('%s: not found\n' % arg) | |
| 44 | + sts = 1 | |
| 45 | + if sts: | |
| 46 | + sys.exit(sts) | |
| 47 | + | |
| 48 | +numeric = re.compile('[1-9][0-9]*') | |
| 49 | + | |
| 50 | +def mh(dir): | |
| 51 | + sts = 0 | |
| 52 | + msgs = os.listdir(dir) | |
| 53 | + for msg in msgs: | |
| 54 | + if numeric.match(msg) != len(msg): | |
| 55 | + continue | |
| 56 | + fn = os.path.join(dir, msg) | |
| 57 | + try: | |
| 58 | + f = open(fn) | |
| 59 | + except IOError, msg: | |
| 60 | + sys.stderr.write('%s: %s\n' % (fn, msg)) | |
| 61 | + sts = 1 | |
| 62 | + continue | |
| 63 | + sts = message(f) or sts | |
| 64 | + return sts | |
| 65 | + | |
| 66 | +def mmdf(f): | |
| 67 | + sts = 0 | |
| 68 | + while 1: | |
| 69 | + line = f.readline() | |
| 70 | + if not line: | |
| 71 | + break | |
| 72 | + if line == '\1\1\1\1\n': | |
| 73 | + sts = message(f, line) or sts | |
| 74 | + else: | |
| 75 | + sys.stderr.write( | |
| 76 | + 'Bad line in MMFD mailbox: %r\n' % (line,)) | |
| 77 | + return sts | |
| 78 | + | |
| 79 | +counter = 0 # for generating unique Message-ID headers | |
| 80 | + | |
| 81 | +def message(f, delimiter = ''): | |
| 82 | + sts = 0 | |
| 83 | + # Parse RFC822 header | |
| 84 | + m = rfc822.Message(f) | |
| 85 | + # Write unix header line | |
| 86 | + fullname, email = m.getaddr('From') | |
| 87 | + tt = m.getdate('Date') | |
| 88 | + if tt: | |
| 89 | + t = time.mktime(tt) | |
| 90 | + else: | |
| 91 | + sys.stderr.write( | |
| 92 | + 'Unparseable date: %r\n' % (m.getheader('Date'),)) | |
| 93 | + t = os.fstat(f.fileno())[stat.ST_MTIME] | |
| 94 | + print 'From', email, time.ctime(t) | |
| 95 | + # Copy RFC822 header | |
| 96 | + for line in m.headers: | |
| 97 | + print line, | |
| 98 | + # Invent Message-ID header if none is present | |
| 99 | + if not m.has_key('message-id'): | |
| 100 | + global counter | |
| 101 | + counter = counter + 1 | |
| 102 | + msgid = "<%s.%d>" % (hex(t), counter) | |
| 103 | + sys.stderr.write("Adding Message-ID %s (From %s)\n" % | |
| 104 | + (msgid, email)) | |
| 105 | + print "Message-ID:", msgid | |
| 106 | ||
| 107 | + # Copy body | |
| 108 | + while 1: | |
| 109 | + line = f.readline() | |
| 110 | + if line == delimiter: | |
| 111 | + break | |
| 112 | + if not line: | |
| 113 | + sys.stderr.write('Unexpected EOF in message\n') | |
| 114 | + sts = 1 | |
| 115 | + break | |
| 116 | + if line[:5] == 'From ': | |
| 117 | + line = '>' + line | |
| 118 | + print line, | |
| 119 | + # Print trailing newline | |
| 120 | ||
| 121 | + return sts | |
| 122 | + | |
| 123 | +if __name__ == "__main__": | |
| 124 | + main() | |
| @@ -0,0 +1,61 @@ | ||
| 1 | +#! /usr/bin/env python | |
| 2 | + | |
| 3 | +# A rather specialized script to make sure that a symbolic link named | |
| 4 | +# RCS exists pointing to a real RCS directory in a parallel tree | |
| 5 | +# referenced as RCStree in an ancestor directory. | |
| 6 | +# (I use this because I like my RCS files to reside on a physically | |
| 7 | +# different machine). | |
| 8 | + | |
| 9 | +import os | |
| 10 | + | |
| 11 | +def main(): | |
| 12 | + rcstree = 'RCStree' | |
| 13 | + rcs = 'RCS' | |
| 14 | + if os.path.islink(rcs): | |
| 15 | + print '%r is a symlink to %r' % (rcs, os.readlink(rcs)) | |
| 16 | + return | |
| 17 | + if os.path.isdir(rcs): | |
| 18 | + print '%r is an ordinary directory' % (rcs,) | |
| 19 | + return | |
| 20 | + if os.path.exists(rcs): | |
| 21 | + print '%r is a file?!?!' % (rcs,) | |
| 22 | + return | |
| 23 | + # | |
| 24 | + p = os.getcwd() | |
| 25 | + up = '' | |
| 26 | + down = '' | |
| 27 | + # Invariants: | |
| 28 | + # (1) join(p, down) is the current directory | |
| 29 | + # (2) up is the same directory as p | |
| 30 | + # Ergo: | |
| 31 | + # (3) join(up, down) is the current directory | |
| 32 | + #print 'p =', repr(p) | |
| 33 | + while not os.path.isdir(os.path.join(p, rcstree)): | |
| 34 | + head, tail = os.path.split(p) | |
| 35 | + #print 'head = %r; tail = %r' % (head, tail) | |
| 36 | + if not tail: | |
| 37 | + print 'Sorry, no ancestor dir contains %r' % (rcstree,) | |
| 38 | + return | |
| 39 | + p = head | |
| 40 | + up = os.path.join(os.pardir, up) | |
| 41 | + down = os.path.join(tail, down) | |
| 42 | + #print 'p = %r; up = %r; down = %r' % (p, up, down) | |
| 43 | + there = os.path.join(up, rcstree) | |
| 44 | + there = os.path.join(there, down) | |
| 45 | + there = os.path.join(there, rcs) | |
| 46 | + if os.path.isdir(there): | |
| 47 | + print '%r already exists' % (there, ) | |
| 48 | + else: | |
| 49 | + print 'making %r' % (there,) | |
| 50 | + makedirs(there) | |
| 51 | + print 'making symlink %r -> %r' % (rcs, there) | |
| 52 | + os.symlink(there, rcs) | |
| 53 | + | |
| 54 | +def makedirs(p): | |
| 55 | + if not os.path.isdir(p): | |
| 56 | + head, tail = os.path.split(p) | |
| 57 | + makedirs(head) | |
| 58 | + os.mkdir(p, 0777) | |
| 59 | + | |
| 60 | +if __name__ == "__main__": | |
| 61 | + main() | |
| @@ -0,0 +1,149 @@ | ||
| 1 | +# DAH should be three DOTs. | |
| 2 | +# Space between DOTs and DAHs should be one DOT. | |
| 3 | +# Space between two letters should be one DAH. | |
| 4 | +# Space between two words should be DOT DAH DAH. | |
| 5 | + | |
| 6 | +import sys, math, audiodev | |
| 7 | + | |
| 8 | +DOT = 30 | |
| 9 | +DAH = 3 * DOT | |
| 10 | +OCTAVE = 2 # 1 == 441 Hz, 2 == 882 Hz, ... | |
| 11 | + | |
| 12 | +morsetab = { | |
| 13 | + 'A': '.-', 'a': '.-', | |
| 14 | + 'B': '-...', 'b': '-...', | |
| 15 | + 'C': '-.-.', 'c': '-.-.', | |
| 16 | + 'D': '-..', 'd': '-..', | |
| 17 | + 'E': '.', 'e': '.', | |
| 18 | + 'F': '..-.', 'f': '..-.', | |
| 19 | + 'G': '--.', 'g': '--.', | |
| 20 | + 'H': '....', 'h': '....', | |
| 21 | + 'I': '..', 'i': '..', | |
| 22 | + 'J': '.---', 'j': '.---', | |
| 23 | + 'K': '-.-', 'k': '-.-', | |
| 24 | + 'L': '.-..', 'l': '.-..', | |
| 25 | + 'M': '--', 'm': '--', | |
| 26 | + 'N': '-.', 'n': '-.', | |
| 27 | + 'O': '---', 'o': '---', | |
| 28 | + 'P': '.--.', 'p': '.--.', | |
| 29 | + 'Q': '--.-', 'q': '--.-', | |
| 30 | + 'R': '.-.', 'r': '.-.', | |
| 31 | + 'S': '...', 's': '...', | |
| 32 | + 'T': '-', 't': '-', | |
| 33 | + 'U': '..-', 'u': '..-', | |
| 34 | + 'V': '...-', 'v': '...-', | |
| 35 | + 'W': '.--', 'w': '.--', | |
| 36 | + 'X': '-..-', 'x': '-..-', | |
| 37 | + 'Y': '-.--', 'y': '-.--', | |
| 38 | + 'Z': '--..', 'z': '--..', | |
| 39 | + '0': '-----', | |
| 40 | + '1': '.----', | |
| 41 | + '2': '..---', | |
| 42 | + '3': '...--', | |
| 43 | + '4': '....-', | |
| 44 | + '5': '.....', | |
| 45 | + '6': '-....', | |
| 46 | + '7': '--...', | |
| 47 | + '8': '---..', | |
| 48 | + '9': '----.', | |
| 49 | + ',': '--..--', | |
| 50 | + '.': '.-.-.-', | |
| 51 | + '?': '..--..', | |
| 52 | + ';': '-.-.-.', | |
| 53 | + ':': '---...', | |
| 54 | + "'": '.----.', | |
| 55 | + '-': '-....-', | |
| 56 | + '/': '-..-.', | |
| 57 | + '(': '-.--.-', | |
| 58 | + ')': '-.--.-', | |
| 59 | + '_': '..--.-', | |
| 60 | + ' ': ' ' | |
| 61 | +} | |
| 62 | + | |
| 63 | +# If we play at 44.1 kHz (which we do), then if we produce one sine | |
| 64 | +# wave in 100 samples, we get a tone of 441 Hz. If we produce two | |
| 65 | +# sine waves in these 100 samples, we get a tone of 882 Hz. 882 Hz | |
| 66 | +# appears to be a nice one for playing morse code. | |
| 67 | +def mkwave(octave): | |
| 68 | + global sinewave, nowave | |
| 69 | + sinewave = '' | |
| 70 | + for i in range(100): | |
| 71 | + val = int(math.sin(math.pi * float(i) * octave / 50.0) * 30000) | |
| 72 | + sinewave = sinewave + chr((val >> 8) & 255) + chr(val & 255) | |
| 73 | + nowave = '\0' * 200 | |
| 74 | + | |
| 75 | +mkwave(OCTAVE) | |
| 76 | + | |
| 77 | +def main(): | |
| 78 | + import getopt, string | |
| 79 | + try: | |
| 80 | + opts, args = getopt.getopt(sys.argv[1:], 'o:p:') | |
| 81 | + except getopt.error: | |
| 82 | + sys.stderr.write('Usage ' + sys.argv[0] + | |
| 83 | + ' [ -o outfile ] [ args ] ...\n') | |
| 84 | + sys.exit(1) | |
| 85 | + dev = None | |
| 86 | + for o, a in opts: | |
| 87 | + if o == '-o': | |
| 88 | + import aifc | |
| 89 | + dev = aifc.open(a, 'w') | |
| 90 | + dev.setframerate(44100) | |
| 91 | + dev.setsampwidth(2) | |
| 92 | + dev.setnchannels(1) | |
| 93 | + if o == '-p': | |
| 94 | + mkwave(string.atoi(a)) | |
| 95 | + if not dev: | |
| 96 | + import audiodev | |
| 97 | + dev = audiodev.AudioDev() | |
| 98 | + dev.setoutrate(44100) | |
| 99 | + dev.setsampwidth(2) | |
| 100 | + dev.setnchannels(1) | |
| 101 | + dev.close = dev.stop | |
| 102 | + dev.writeframesraw = dev.writeframes | |
| 103 | + if args: | |
| 104 | + line = string.join(args) | |
| 105 | + else: | |
| 106 | + line = sys.stdin.readline() | |
| 107 | + while line: | |
| 108 | + mline = morse(line) | |
| 109 | + play(mline, dev) | |
| 110 | + if hasattr(dev, 'wait'): | |
| 111 | + dev.wait() | |
| 112 | + if not args: | |
| 113 | + line = sys.stdin.readline() | |
| 114 | + else: | |
| 115 | + line = '' | |
| 116 | + dev.close() | |
| 117 | + | |
| 118 | +# Convert a string to morse code with \001 between the characters in | |
| 119 | +# the string. | |
| 120 | +def morse(line): | |
| 121 | + res = '' | |
| 122 | + for c in line: | |
| 123 | + try: | |
| 124 | + res = res + morsetab[c] + '\001' | |
| 125 | + except KeyError: | |
| 126 | + pass | |
| 127 | + return res | |
| 128 | + | |
| 129 | +# Play a line of morse code. | |
| 130 | +def play(line, dev): | |
| 131 | + for c in line: | |
| 132 | + if c == '.': | |
| 133 | + sine(dev, DOT) | |
| 134 | + elif c == '-': | |
| 135 | + sine(dev, DAH) | |
| 136 | + else: # space | |
| 137 | + pause(dev, DAH + DOT) | |
| 138 | + pause(dev, DOT) | |
| 139 | + | |
| 140 | +def sine(dev, length): | |
| 141 | + for i in range(length): | |
| 142 | + dev.writeframesraw(sinewave) | |
| 143 | + | |
| 144 | +def pause(dev, length): | |
| 145 | + for i in range(length): | |
| 146 | + dev.writeframesraw(nowave) | |
| 147 | + | |
| 148 | +if __name__ == '__main__' or sys.argv[0] == __name__: | |
| 149 | + main() | |
| @@ -0,0 +1,59 @@ | ||
| 1 | + NEWSLIST | |
| 2 | + ======== | |
| 3 | + A program to assist HTTP browsing of newsgroups | |
| 4 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| 5 | + | |
| 6 | +WWW browsers such as NCSA Mosaic allow the user to read newsgroup | |
| 7 | +articles by specifying the group name in a URL eg 'news:comp.answers'. | |
| 8 | + | |
| 9 | +To browse through many groups, though, (and there are several thousand | |
| 10 | +of them) you really need a page or pages containing links to all the | |
| 11 | +groups. There are some good ones out there, for example, | |
| 12 | + | |
| 13 | + http://info.cern.ch/hypertext/DataSources/News/Groups/Overview.html | |
| 14 | + | |
| 15 | +is the standard one at CERN, but it only shows the groups available there, | |
| 16 | +which may be rather different from those available on your machine. | |
| 17 | + | |
| 18 | +Newslist is a program which creates a hierarchy of pages for you based | |
| 19 | +on the groups available from YOUR server. It is written in python - a | |
| 20 | +splendid interpreted object-oriented language which I suggest you get | |
| 21 | +right now from the directory /pub/python at ftp.cwi.nl, if you haven't | |
| 22 | +already got it. | |
| 23 | + | |
| 24 | +You should be able to see some sample output by looking at: | |
| 25 | + http://pelican.cl.cam.ac.uk/newspage/root.html | |
| 26 | + | |
| 27 | +Descriptions of newsgroups can be added from a file with one group | |
| 28 | +per line. eg: | |
| 29 | + | |
| 30 | + alt.foo Articles about foo | |
| 31 | + comp.bar Programming in 'bar' and related languages | |
| 32 | + | |
| 33 | +A suitable list detailing most groups can be found at ftp.uu.net in | |
| 34 | +/uunet-info/newsgroups.gz. | |
| 35 | + | |
| 36 | +Make sure you read the information at the beginning of the program source and | |
| 37 | +configure the variables before running. | |
| 38 | + | |
| 39 | +In addition to python, you need: | |
| 40 | + | |
| 41 | + An NNTP-based news feed. | |
| 42 | + A directory in which to put the pages. | |
| 43 | + | |
| 44 | +The programming is not very beautiful, but it works! It comes with no | |
| 45 | +warranty, express or implied, but with the hope that some others may | |
| 46 | +find it useful. | |
| 47 | + | |
| 48 | +Comments, improvements & suggestions welcomed. | |
| 49 | +Quentin Stafford-Fraser | |
| 50 | + | |
| 51 | + ---------------------------------------------------------------------- | |
| 52 | + Quentin Stafford-Fraser | |
| 53 | + http://pelican.cl.cam.ac.uk/people/qs101/me.html | |
| 54 | + | |
| 55 | + Cambridge University Computer Lab Rank Xerox Cambridge EuroPARC | |
| 56 | + qs101@cl.cam.ac.uk fraser@europarc.xerox.com | |
| 57 | + Tel: +44 223 334411 Tel: +44 223 341521 | |
| 58 | + Fax: +44 223 334679 Fax: +44 223 341510 | |
| 59 | + ---------------------------------------------------------------------- | |
| @@ -0,0 +1,366 @@ | ||
| 1 | +#! /usr/bin/env python | |
| 2 | +####################################################################### | |
| 3 | +# Newslist $Revision: 37320 $ | |
| 4 | +# | |
| 5 | +# Syntax: | |
| 6 | +# newslist [ -a ] | |
| 7 | +# | |
| 8 | +# This is a program to create a directory full of HTML pages | |
| 9 | +# which between them contain links to all the newsgroups available | |
| 10 | +# on your server. | |
| 11 | +# | |
| 12 | +# The -a option causes a complete list of all groups to be read from | |
| 13 | +# the server rather than just the ones which have appeared since last | |
| 14 | +# execution. This recreates the local list from scratch. Use this on | |
| 15 | +# the first invocation of the program, and from time to time thereafter. | |
| 16 | +# When new groups are first created they may appear on your server as | |
| 17 | +# empty groups. By default, empty groups are ignored by the -a option. | |
| 18 | +# However, these new groups will not be created again, and so will not | |
| 19 | +# appear in the server's list of 'new groups' at a later date. Hence it | |
| 20 | +# won't appear until you do a '-a' after some articles have appeared. | |
| 21 | +# | |
| 22 | +# I should really keep a list of ignored empty groups and re-check them | |
| 23 | +# for articles on every run, but I haven't got around to it yet. | |
| 24 | +# | |
| 25 | +# This assumes an NNTP news feed. | |
| 26 | +# | |
| 27 | +# Feel free to copy, distribute and modify this code for | |
| 28 | +# non-commercial use. If you make any useful modifications, let me | |
| 29 | +# know! | |
| 30 | +# | |
| 31 | +# (c) Quentin Stafford-Fraser 1994 | |
| 32 | +# fraser@europarc.xerox.com qs101@cl.cam.ac.uk | |
| 33 | +# # | |
| 34 | +####################################################################### | |
| 35 | +import sys,nntplib, string, marshal, time, os, posix, string | |
| 36 | + | |
| 37 | +####################################################################### | |
| 38 | +# Check these variables before running! # | |
| 39 | + | |
| 40 | +# Top directory. | |
| 41 | +# Filenames which don't start with / are taken as being relative to this. | |
| 42 | +topdir='/anfs/qsbigdisc/web/html/newspage' | |
| 43 | + | |
| 44 | +# The name of your NNTP host | |
| 45 | +# eg. | |
| 46 | +# newshost = 'nntp-serv.cl.cam.ac.uk' | |
| 47 | +# or use following to get the name from the NNTPSERVER environment | |
| 48 | +# variable: | |
| 49 | +# newshost = posix.environ['NNTPSERVER'] | |
| 50 | +newshost = 'nntp-serv.cl.cam.ac.uk' | |
| 51 | + | |
| 52 | +# The filename for a local cache of the newsgroup list | |
| 53 | +treefile = 'grouptree' | |
| 54 | + | |
| 55 | +# The filename for descriptions of newsgroups | |
| 56 | +# I found a suitable one at ftp.uu.net in /uunet-info/newgroups.gz | |
| 57 | +# You can set this to '' if you don't wish to use one. | |
| 58 | +descfile = 'newsgroups' | |
| 59 | + | |
| 60 | +# The directory in which HTML pages should be created | |
| 61 | +# eg. | |
| 62 | +# pagedir = '/usr/local/lib/html/newspage' | |
| 63 | +# pagedir = 'pages' | |
| 64 | +pagedir = topdir | |
| 65 | + | |
| 66 | +# The html prefix which will refer to this directory | |
| 67 | +# eg. | |
| 68 | +# httppref = '/newspage/', | |
| 69 | +# or leave blank for relative links between pages: (Recommended) | |
| 70 | +# httppref = '' | |
| 71 | +httppref = '' | |
| 72 | + | |
| 73 | +# The name of the 'root' news page in this directory. | |
| 74 | +# A .html suffix will be added. | |
| 75 | +rootpage = 'root' | |
| 76 | + | |
| 77 | +# Set skipempty to 0 if you wish to see links to empty groups as well. | |
| 78 | +# Only affects the -a option. | |
| 79 | +skipempty = 1 | |
| 80 | + | |
| 81 | +# pagelinkicon can contain html to put an icon after links to | |
| 82 | +# further pages. This helps to make important links stand out. | |
| 83 | +# Set to '' if not wanted, or '...' is quite a good one. | |
| 84 | +pagelinkicon='... <img src="http://pelican.cl.cam.ac.uk/icons/page.xbm"> ' | |
| 85 | + | |
| 86 | +# --------------------------------------------------------------------- | |
| 87 | +# Less important personal preferences: | |
| 88 | + | |
| 89 | +# Sublistsize controls the maximum number of items the will appear as | |
| 90 | +# an indented sub-list before the whole thing is moved onto a different | |
| 91 | +# page. The smaller this is, the more pages you will have, but the | |
| 92 | +# shorter each will be. | |
| 93 | +sublistsize = 4 | |
| 94 | + | |
| 95 | +# That should be all. # | |
| 96 | +####################################################################### | |
| 97 | + | |
| 98 | +for dir in os.curdir, os.environ['HOME']: | |
| 99 | + rcfile = os.path.join(dir, '.newslistrc.py') | |
| 100 | + if os.path.exists(rcfile): | |
| 101 | + print rcfile | |
| 102 | + execfile(rcfile) | |
| 103 | + break | |
| 104 | + | |
| 105 | +from nntplib import NNTP | |
| 106 | +from stat import * | |
| 107 | + | |
| 108 | +rcsrev = '$Revision: 37320 $' | |
| 109 | +rcsrev = string.join(filter(lambda s: '$' not in s, string.split(rcsrev))) | |
| 110 | +desc = {} | |
| 111 | + | |
| 112 | +# Make (possibly) relative filenames into absolute ones | |
| 113 | +treefile = os.path.join(topdir,treefile) | |
| 114 | +descfile = os.path.join(topdir,descfile) | |
| 115 | +page = os.path.join(topdir,pagedir) | |
| 116 | + | |
| 117 | +# First the bits for creating trees --------------------------- | |
| 118 | + | |
| 119 | +# Addtotree creates/augments a tree from a list of group names | |
| 120 | +def addtotree(tree, groups): | |
| 121 | + print 'Updating tree...' | |
| 122 | + for i in groups: | |
| 123 | + parts = string.splitfields(i,'.') | |
| 124 | + makeleaf(tree, parts) | |
| 125 | + | |
| 126 | +# Makeleaf makes a leaf and the branch leading to it if necessary | |
| 127 | +def makeleaf(tree,path): | |
| 128 | + j = path[0] | |
| 129 | + l = len(path) | |
| 130 | + | |
| 131 | + if not tree.has_key(j): | |
| 132 | + tree[j] = {} | |
| 133 | + if l == 1: | |
| 134 | + tree[j]['.'] = '.' | |
| 135 | + if l > 1: | |
| 136 | + makeleaf(tree[j],path[1:]) | |
| 137 | + | |
| 138 | +# Then the bits for outputting trees as pages ---------------- | |
| 139 | + | |
| 140 | +# Createpage creates an HTML file named <root>.html containing links | |
| 141 | +# to those groups beginning with <root>. | |
| 142 | + | |
| 143 | +def createpage(root, tree, p): | |
| 144 | + filename = os.path.join(pagedir,root+'.html') | |
| 145 | + if root == rootpage: | |
| 146 | + detail = '' | |
| 147 | + else: | |
| 148 | + detail = ' under ' + root | |
| 149 | + f = open(filename,'w') | |
| 150 | + # f.write('Content-Type: text/html\n') | |
| 151 | + f.write('<TITLE>Newsgroups available' + detail + '</TITLE>\n') | |
| 152 | + f.write('<H1>Newsgroups available' + detail +'</H1>\n') | |
| 153 | + f.write('<A HREF="'+httppref+rootpage+'.html">Back to top level</A><P>\n') | |
| 154 | + printtree(f,tree,0,p) | |
| 155 | + f.write('<I>This page automatically created by \'newslist\' v. '+rcsrev+'.') | |
| 156 | + f.write(time.ctime(time.time()) + '</I><P>') | |
| 157 | + f.close() | |
| 158 | + | |
| 159 | +# Printtree prints the groups as a bulleted list. Groups with | |
| 160 | +# more than <sublistsize> subgroups will be put on a separate page. | |
| 161 | +# Other sets of subgroups are just indented. | |
| 162 | + | |
| 163 | +def printtree(f, tree, indent, p): | |
| 164 | + global desc | |
| 165 | + l = len(tree) | |
| 166 | + | |
| 167 | + if l > sublistsize and indent>0: | |
| 168 | + # Create a new page and a link to it | |
| 169 | + f.write('<LI><B><A HREF="'+httppref+p[1:]+'.html">') | |
| 170 | + f.write(p[1:]+'.*') | |
| 171 | + f.write('</A></B>'+pagelinkicon+'\n') | |
| 172 | + createpage(p[1:], tree, p) | |
| 173 | + return | |
| 174 | + | |
| 175 | + kl = tree.keys() | |
| 176 | + | |
| 177 | + if l > 1: | |
| 178 | + kl.sort() | |
| 179 | + if indent > 0: | |
| 180 | + # Create a sub-list | |
| 181 | + f.write('<LI>'+p[1:]+'\n<UL>') | |
| 182 | + else: | |
| 183 | + # Create a main list | |
| 184 | + f.write('<UL>') | |
| 185 | + indent = indent + 1 | |
| 186 | + | |
| 187 | + for i in kl: | |
| 188 | + if i == '.': | |
| 189 | + # Output a newsgroup | |
| 190 | + f.write('<LI><A HREF="news:' + p[1:] + '">'+ p[1:] + '</A> ') | |
| 191 | + if desc.has_key(p[1:]): | |
| 192 | + f.write(' <I>'+desc[p[1:]]+'</I>\n') | |
| 193 | + else: | |
| 194 | + f.write('\n') | |
| 195 | + else: | |
| 196 | + # Output a hierarchy | |
| 197 | + printtree(f,tree[i], indent, p+'.'+i) | |
| 198 | + | |
| 199 | + if l > 1: | |
| 200 | + f.write('\n</UL>') | |
| 201 | + | |
| 202 | +# Reading descriptions file --------------------------------------- | |
| 203 | + | |
| 204 | +# This returns an array mapping group name to its description | |
| 205 | + | |
| 206 | +def readdesc(descfile): | |
| 207 | + global desc | |
| 208 | + | |
| 209 | + desc = {} | |
| 210 | + | |
| 211 | + if descfile == '': | |
| 212 | + return | |
| 213 | + | |
| 214 | + try: | |
| 215 | + d = open(descfile, 'r') | |
| 216 | + print 'Reading descriptions...' | |
| 217 | + except (IOError): | |
| 218 | + print 'Failed to open description file ' + descfile | |
| 219 | + return | |
| 220 | + l = d.readline() | |
| 221 | + while l != '': | |
| 222 | + bits = string.split(l) | |
| 223 | + try: | |
| 224 | + grp = bits[0] | |
| 225 | + dsc = string.join(bits[1:]) | |
| 226 | + if len(dsc)>1: | |
| 227 | + desc[grp] = dsc | |
| 228 | + except (IndexError): | |
| 229 | + pass | |
| 230 | + l = d.readline() | |
| 231 | + | |
| 232 | +# Check that ouput directory exists, ------------------------------ | |
| 233 | +# and offer to create it if not | |
| 234 | + | |
| 235 | +def checkopdir(pagedir): | |
| 236 | + if not os.path.isdir(pagedir): | |
| 237 | + print 'Directory '+pagedir+' does not exist.' | |
| 238 | + print 'Shall I create it for you? (y/n)' | |
| 239 | + if sys.stdin.readline()[0] == 'y': | |
| 240 | + try: | |
| 241 | + os.mkdir(pagedir,0777) | |
| 242 | + except: | |
| 243 | + print 'Sorry - failed!' | |
| 244 | + sys.exit(1) | |
| 245 | + else: | |
| 246 | + print 'OK. Exiting.' | |
| 247 | + sys.exit(1) | |
| 248 | + | |
| 249 | +# Read and write current local tree ---------------------------------- | |
| 250 | + | |
| 251 | +def readlocallist(treefile): | |
| 252 | + print 'Reading current local group list...' | |
| 253 | + tree = {} | |
| 254 | + try: | |
| 255 | + treetime = time.localtime(os.stat(treefile)[ST_MTIME]) | |
| 256 | + except: | |
| 257 | + print '\n*** Failed to open local group cache '+treefile | |
| 258 | + print 'If this is the first time you have run newslist, then' | |
| 259 | + print 'use the -a option to create it.' | |
| 260 | + sys.exit(1) | |
| 261 | + treedate = '%02d%02d%02d' % (treetime[0] % 100 ,treetime[1], treetime[2]) | |
| 262 | + try: | |
| 263 | + dump = open(treefile,'r') | |
| 264 | + tree = marshal.load(dump) | |
| 265 | + dump.close() | |
| 266 | + except (IOError): | |
| 267 | + print 'Cannot open local group list ' + treefile | |
| 268 | + return (tree, treedate) | |
| 269 | + | |
| 270 | +def writelocallist(treefile, tree): | |
| 271 | + try: | |
| 272 | + dump = open(treefile,'w') | |
| 273 | + groups = marshal.dump(tree,dump) | |
| 274 | + dump.close() | |
| 275 | + print 'Saved list to '+treefile+'\n' | |
| 276 | + except: | |
| 277 | + print 'Sorry - failed to write to local group cache '+treefile | |
| 278 | + print 'Does it (or its directory) have the correct permissions?' | |
| 279 | + sys.exit(1) | |
| 280 | + | |
| 281 | +# Return list of all groups on server ----------------------------- | |
| 282 | + | |
| 283 | +def getallgroups(server): | |
| 284 | + print 'Getting list of all groups...' | |
| 285 | + treedate='010101' | |
| 286 | + info = server.list()[1] | |
| 287 | + groups = [] | |
| 288 | + print 'Processing...' | |
| 289 | + if skipempty: | |
| 290 | + print '\nIgnoring following empty groups:' | |
| 291 | + for i in info: | |
| 292 | + grpname = string.split(i[0])[0] | |
| 293 | + if skipempty and string.atoi(i[1]) < string.atoi(i[2]): | |
| 294 | + print grpname+' ', | |
| 295 | + else: | |
| 296 | + groups.append(grpname) | |
| 297 | + print '\n' | |
| 298 | + if skipempty: | |
| 299 | + print '(End of empty groups)' | |
| 300 | + return groups | |
| 301 | + | |
| 302 | +# Return list of new groups on server ----------------------------- | |
| 303 | + | |
| 304 | +def getnewgroups(server, treedate): | |
| 305 | + print 'Getting list of new groups since start of '+treedate+'...', | |
| 306 | + info = server.newgroups(treedate,'000001')[1] | |
| 307 | + print 'got %d.' % len(info) | |
| 308 | + print 'Processing...', | |
| 309 | + groups = [] | |
| 310 | + for i in info: | |
| 311 | + grpname = string.split(i)[0] | |
| 312 | + groups.append(grpname) | |
| 313 | + print 'Done' | |
| 314 | + return groups | |
| 315 | + | |
| 316 | +# Now the main program -------------------------------------------- | |
| 317 | + | |
| 318 | +def main(): | |
| 319 | + global desc | |
| 320 | + | |
| 321 | + tree={} | |
| 322 | + | |
| 323 | + # Check that the output directory exists | |
| 324 | + checkopdir(pagedir); | |
| 325 | + | |
| 326 | + try: | |
| 327 | + print 'Connecting to '+newshost+'...' | |
| 328 | + if sys.version[0] == '0': | |
| 329 | + s = NNTP.init(newshost) | |
| 330 | + else: | |
| 331 | + s = NNTP(newshost) | |
| 332 | + connected = 1 | |
| 333 | + except (nntplib.error_temp, nntplib.error_perm), x: | |
| 334 | + print 'Error connecting to host:', x | |
| 335 | + print 'I\'ll try to use just the local list.' | |
| 336 | + connected = 0 | |
| 337 | + | |
| 338 | + # If -a is specified, read the full list of groups from server | |
| 339 | + if connected and len(sys.argv) > 1 and sys.argv[1] == '-a': | |
| 340 | + | |
| 341 | + groups = getallgroups(s) | |
| 342 | + | |
| 343 | + # Otherwise just read the local file and then add | |
| 344 | + # groups created since local file last modified. | |
| 345 | + else: | |
| 346 | + | |
| 347 | + (tree, treedate) = readlocallist(treefile) | |
| 348 | + if connected: | |
| 349 | + groups = getnewgroups(s, treedate) | |
| 350 | + | |
| 351 | + if connected: | |
| 352 | + addtotree(tree, groups) | |
| 353 | + writelocallist(treefile,tree) | |
| 354 | + | |
| 355 | + # Read group descriptions | |
| 356 | + readdesc(descfile) | |
| 357 | + | |
| 358 | + print 'Creating pages...' | |
| 359 | + createpage(rootpage, tree, '') | |
| 360 | + print 'Done' | |
| 361 | + | |
| 362 | +if __name__ == "__main__": | |
| 363 | + main() | |
| 364 | + | |
| 365 | +# That's all folks | |
| 366 | +###################################################################### | |
| @@ -0,0 +1,34 @@ | ||
| 1 | +#! /usr/bin/env python | |
| 2 | + | |
| 3 | +# Print digits of pi forever. | |
| 4 | +# | |
| 5 | +# The algorithm, using Python's 'long' integers ("bignums"), works | |
| 6 | +# with continued fractions, and was conceived by Lambert Meertens. | |
| 7 | +# | |
| 8 | +# See also the ABC Programmer's Handbook, by Geurts, Meertens & Pemberton, | |
| 9 | +# published by Prentice-Hall (UK) Ltd., 1990. | |
| 10 | + | |
| 11 | +import sys | |
| 12 | + | |
| 13 | +def main(): | |
| 14 | + k, a, b, a1, b1 = 2L, 4L, 1L, 12L, 4L | |
| 15 | + while 1: | |
| 16 | + # Next approximation | |
| 17 | + p, q, k = k*k, 2L*k+1L, k+1L | |
| 18 | + a, b, a1, b1 = a1, b1, p*a+q*a1, p*b+q*b1 | |
| 19 | + # Print common digits | |
| 20 | + d, d1 = a/b, a1/b1 | |
| 21 | + while d == d1: | |
| 22 | + output(d) | |
| 23 | + a, a1 = 10L*(a%b), 10L*(a1%b1) | |
| 24 | + d, d1 = a/b, a1/b1 | |
| 25 | + | |
| 26 | +def output(d): | |
| 27 | + # Use write() to avoid spaces between the digits | |
| 28 | + # Use str() to avoid the 'L' | |
| 29 | + sys.stdout.write(str(d)) | |
| 30 | + # Flush so the output is seen immediately | |
| 31 | + sys.stdout.flush() | |
| 32 | + | |
| 33 | +if __name__ == "__main__": | |
| 34 | + main() | |
| @@ -0,0 +1,130 @@ | ||
| 1 | +#! /usr/bin/env python | |
| 2 | + | |
| 3 | +# Emulate some Perl command line options. | |
| 4 | +# Usage: pp [-a] [-c] [-d] [-e scriptline] [-F fieldsep] [-n] [-p] [file] ... | |
| 5 | +# Where the options mean the following: | |
| 6 | +# -a : together with -n or -p, splits each line into list F | |
| 7 | +# -c : check syntax only, do not execute any code | |
| 8 | +# -d : run the script under the debugger, pdb | |
| 9 | +# -e scriptline : gives one line of the Python script; may be repeated | |
| 10 | +# -F fieldsep : sets the field separator for the -a option [not in Perl] | |
| 11 | +# -n : runs the script for each line of input | |
| 12 | +# -p : prints the line after the script has run | |
| 13 | +# When no script lines have been passed, the first file argument | |
| 14 | +# contains the script. With -n or -p, the remaining arguments are | |
| 15 | +# read as input to the script, line by line. If a file is '-' | |
| 16 | +# or missing, standard input is read. | |
| 17 | + | |
| 18 | +# XXX To do: | |
| 19 | +# - add -i extension option (change files in place) | |
| 20 | +# - make a single loop over the files and lines (changes effect of 'break')? | |
| 21 | +# - add an option to specify the record separator | |
| 22 | +# - except for -n/-p, run directly from the file if at all possible | |
| 23 | + | |
| 24 | +import sys | |
| 25 | +import string | |
| 26 | +import getopt | |
| 27 | + | |
| 28 | +FS = '' | |
| 29 | +SCRIPT = [] | |
| 30 | +AFLAG = 0 | |
| 31 | +CFLAG = 0 | |
| 32 | +DFLAG = 0 | |
| 33 | +NFLAG = 0 | |
| 34 | +PFLAG = 0 | |
| 35 | + | |
| 36 | +try: | |
| 37 | + optlist, ARGS = getopt.getopt(sys.argv[1:], 'acde:F:np') | |
| 38 | +except getopt.error, msg: | |
| 39 | + sys.stderr.write(sys.argv[0] + ': ' + msg + '\n') | |
| 40 | + sys.exit(2) | |
| 41 | + | |
| 42 | +for option, optarg in optlist: | |
| 43 | + if option == '-a': | |
| 44 | + AFLAG = 1 | |
| 45 | + elif option == '-c': | |
| 46 | + CFLAG = 1 | |
| 47 | + elif option == '-d': | |
| 48 | + DFLAG = 1 | |
| 49 | + elif option == '-e': | |
| 50 | + for line in string.splitfields(optarg, '\n'): | |
| 51 | + SCRIPT.append(line) | |
| 52 | + elif option == '-F': | |
| 53 | + FS = optarg | |
| 54 | + elif option == '-n': | |
| 55 | + NFLAG = 1 | |
| 56 | + PFLAG = 0 | |
| 57 | + elif option == '-p': | |
| 58 | + NFLAG = 1 | |
| 59 | + PFLAG = 1 | |
| 60 | + else: | |
| 61 | + print option, 'not recognized???' | |
| 62 | + | |
| 63 | +if not ARGS: ARGS.append('-') | |
| 64 | + | |
| 65 | +if not SCRIPT: | |
| 66 | + if ARGS[0] == '-': | |
| 67 | + fp = sys.stdin | |
| 68 | + else: | |
| 69 | + fp = open(ARGS[0], 'r') | |
| 70 | + while 1: | |
| 71 | + line = fp.readline() | |
| 72 | + if not line: break | |
| 73 | + SCRIPT.append(line[:-1]) | |
| 74 | + del fp | |
| 75 | + del ARGS[0] | |
| 76 | + if not ARGS: ARGS.append('-') | |
| 77 | + | |
| 78 | +if CFLAG: | |
| 79 | + prologue = ['if 0:'] | |
| 80 | + epilogue = [] | |
| 81 | +elif NFLAG: | |
| 82 | + # Note that it is on purpose that AFLAG and PFLAG are | |
| 83 | + # tested dynamically each time through the loop | |
| 84 | + prologue = [ \ | |
| 85 | + 'LINECOUNT = 0', \ | |
| 86 | + 'for FILE in ARGS:', \ | |
| 87 | + ' \tif FILE == \'-\':', \ | |
| 88 | + ' \t \tFP = sys.stdin', \ | |
| 89 | + ' \telse:', \ | |
| 90 | + ' \t \tFP = open(FILE, \'r\')', \ | |
| 91 | + ' \tLINENO = 0', \ | |
| 92 | + ' \twhile 1:', \ | |
| 93 | + ' \t \tLINE = FP.readline()', \ | |
| 94 | + ' \t \tif not LINE: break', \ | |
| 95 | + ' \t \tLINENO = LINENO + 1', \ | |
| 96 | + ' \t \tLINECOUNT = LINECOUNT + 1', \ | |
| 97 | + ' \t \tL = LINE[:-1]', \ | |
| 98 | + ' \t \taflag = AFLAG', \ | |
| 99 | + ' \t \tif aflag:', \ | |
| 100 | + ' \t \t \tif FS: F = string.splitfields(L, FS)', \ | |
| 101 | + ' \t \t \telse: F = string.split(L)' \ | |
| 102 | + ] | |
| 103 | + epilogue = [ \ | |
| 104 | + ' \t \tif not PFLAG: continue', \ | |
| 105 | + ' \t \tif aflag:', \ | |
| 106 | + ' \t \t \tif FS: print string.joinfields(F, FS)', \ | |
| 107 | + ' \t \t \telse: print string.join(F)', \ | |
| 108 | + ' \t \telse: print L', \ | |
| 109 | + ] | |
| 110 | +else: | |
| 111 | + prologue = ['if 1:'] | |
| 112 | + epilogue = [] | |
| 113 | + | |
| 114 | +# Note that we indent using tabs only, so that any indentation style | |
| 115 | +# used in 'command' will come out right after re-indentation. | |
| 116 | + | |
| 117 | +program = string.joinfields(prologue, '\n') + '\n' | |
| 118 | +for line in SCRIPT: | |
| 119 | + program = program + (' \t \t' + line + '\n') | |
| 120 | +program = program + (string.joinfields(epilogue, '\n') + '\n') | |
| 121 | + | |
| 122 | +import tempfile | |
| 123 | +fp = tempfile.NamedTemporaryFile() | |
| 124 | +fp.write(program) | |
| 125 | +fp.flush() | |
| 126 | +if DFLAG: | |
| 127 | + import pdb | |
| 128 | + pdb.run('execfile(%r)' % (tfn,)) | |
| 129 | +else: | |
| 130 | + execfile(tfn) | |
| @@ -0,0 +1,27 @@ | ||
| 1 | +#! /usr/bin/env python | |
| 2 | + | |
| 3 | +# Print prime numbers in a given range | |
| 4 | + | |
| 5 | +def main(): | |
| 6 | + import sys | |
| 7 | + min, max = 2, 0x7fffffff | |
| 8 | + if sys.argv[1:]: | |
| 9 | + min = int(eval(sys.argv[1])) | |
| 10 | + if sys.argv[2:]: | |
| 11 | + max = int(eval(sys.argv[2])) | |
| 12 | + primes(min, max) | |
| 13 | + | |
| 14 | +def primes(min, max): | |
| 15 | + if 2 >= min: print 2 | |
| 16 | + primes = [2] | |
| 17 | + i = 3 | |
| 18 | + while i <= max: | |
| 19 | + for p in primes: | |
| 20 | + if i%p == 0 or p*p > i: break | |
| 21 | + if i%p <> 0: | |
| 22 | + primes.append(i) | |
| 23 | + if i >= min: print i | |
| 24 | + i = i+2 | |
| 25 | + | |
| 26 | +if __name__ == "__main__": | |
| 27 | + main() | |
| @@ -0,0 +1,85 @@ | ||
| 1 | +#! /usr/bin/env python | |
| 2 | + | |
| 3 | +"""N queens problem. | |
| 4 | + | |
| 5 | +The (well-known) problem is due to Niklaus Wirth. | |
| 6 | + | |
| 7 | +This solution is inspired by Dijkstra (Structured Programming). It is | |
| 8 | +a classic recursive backtracking approach. | |
| 9 | + | |
| 10 | +""" | |
| 11 | + | |
| 12 | +N = 8 # Default; command line overrides | |
| 13 | + | |
| 14 | +class Queens: | |
| 15 | + | |
| 16 | + def __init__(self, n=N): | |
| 17 | + self.n = n | |
| 18 | + self.reset() | |
| 19 | + | |
| 20 | + def reset(self): | |
| 21 | + n = self.n | |
| 22 | + self.y = [None]*n # Where is the queen in column x | |
| 23 | + self.row = [0]*n # Is row[y] safe? | |
| 24 | + self.up = [0] * (2*n-1) # Is upward diagonal[x-y] safe? | |
| 25 | + self.down = [0] * (2*n-1) # Is downward diagonal[x+y] safe? | |
| 26 | + self.nfound = 0 # Instrumentation | |
| 27 | + | |
| 28 | + def solve(self, x=0): # Recursive solver | |
| 29 | + for y in range(self.n): | |
| 30 | + if self.safe(x, y): | |
| 31 | + self.place(x, y) | |
| 32 | + if x+1 == self.n: | |
| 33 | + self.display() | |
| 34 | + else: | |
| 35 | + self.solve(x+1) | |
| 36 | + self.remove(x, y) | |
| 37 | + | |
| 38 | + def safe(self, x, y): | |
| 39 | + return not self.row[y] and not self.up[x-y] and not self.down[x+y] | |
| 40 | + | |
| 41 | + def place(self, x, y): | |
| 42 | + self.y[x] = y | |
| 43 | + self.row[y] = 1 | |
| 44 | + self.up[x-y] = 1 | |
| 45 | + self.down[x+y] = 1 | |
| 46 | + | |
| 47 | + def remove(self, x, y): | |
| 48 | + self.y[x] = None | |
| 49 | + self.row[y] = 0 | |
| 50 | + self.up[x-y] = 0 | |
| 51 | + self.down[x+y] = 0 | |
| 52 | + | |
| 53 | + silent = 0 # If set, count solutions only | |
| 54 | + | |
| 55 | + def display(self): | |
| 56 | + self.nfound = self.nfound + 1 | |
| 57 | + if self.silent: | |
| 58 | + return | |
| 59 | + print '+-' + '--'*self.n + '+' | |
| 60 | + for y in range(self.n-1, -1, -1): | |
| 61 | + print '|', | |
| 62 | + for x in range(self.n): | |
| 63 | + if self.y[x] == y: | |
| 64 | + print "Q", | |
| 65 | + else: | |
| 66 | + print ".", | |
| 67 | + print '|' | |
| 68 | + print '+-' + '--'*self.n + '+' | |
| 69 | + | |
| 70 | +def main(): | |
| 71 | + import sys | |
| 72 | + silent = 0 | |
| 73 | + n = N | |
| 74 | + if sys.argv[1:2] == ['-n']: | |
| 75 | + silent = 1 | |
| 76 | + del sys.argv[1] | |
| 77 | + if sys.argv[1:]: | |
| 78 | + n = int(sys.argv[1]) | |
| 79 | + q = Queens(n) | |
| 80 | + q.silent = silent | |
| 81 | + q.solve() | |
| 82 | + print "Found", q.nfound, "solutions." | |
| 83 | + | |
| 84 | +if __name__ == "__main__": | |
| 85 | + main() | |
| @@ -0,0 +1,33 @@ | ||
| 1 | +#! /usr/bin/env python | |
| 2 | +# script.py -- Make typescript of terminal session. | |
| 3 | +# Usage: | |
| 4 | +# -a Append to typescript. | |
| 5 | +# -p Use Python as shell. | |
| 6 | +# Author: Steen Lumholt. | |
| 7 | + | |
| 8 | + | |
| 9 | +import os, time, sys | |
| 10 | +import pty | |
| 11 | + | |
| 12 | +def read(fd): | |
| 13 | + data = os.read(fd, 1024) | |
| 14 | + file.write(data) | |
| 15 | + return data | |
| 16 | + | |
| 17 | +shell = 'sh' | |
| 18 | +filename = 'typescript' | |
| 19 | +mode = 'w' | |
| 20 | +if os.environ.has_key('SHELL'): | |
| 21 | + shell = os.environ['SHELL'] | |
| 22 | +if '-a' in sys.argv: | |
| 23 | + mode = 'a' | |
| 24 | +if '-p' in sys.argv: | |
| 25 | + shell = 'python' | |
| 26 | + | |
| 27 | +file = open(filename, mode) | |
| 28 | + | |
| 29 | +sys.stdout.write('Script started, file is %s\n' % filename) | |
| 30 | +file.write('Script started on %s\n' % time.ctime(time.time())) | |
| 31 | +pty.spawn(shell, read) | |
| 32 | +file.write('Script done on %s\n' % time.ctime(time.time())) | |
| 33 | +sys.stdout.write('Script done, file is %s\n' % filename) | |
| @@ -0,0 +1,107 @@ | ||
| 1 | +#! /usr/bin/env python | |
| 2 | + | |
| 3 | +# Calculate your unbirthday count (see Alice in Wonderland). | |
| 4 | +# This is defined as the number of days from your birth until today | |
| 5 | +# that weren't your birthday. (The day you were born is not counted). | |
| 6 | +# Leap years make it interesting. | |
| 7 | + | |
| 8 | +import sys | |
| 9 | +import time | |
| 10 | +import calendar | |
| 11 | + | |
| 12 | +def main(): | |
| 13 | + # Note that the range checks below also check for bad types, | |
| 14 | + # e.g. 3.14 or (). However syntactically invalid replies | |
| 15 | + # will raise an exception. | |
| 16 | + if sys.argv[1:]: | |
| 17 | + year = int(sys.argv[1]) | |
| 18 | + else: | |
| 19 | + year = int(raw_input('In which year were you born? ')) | |
| 20 | + if 0<=year<100: | |
| 21 | + print "I'll assume that by", year, | |
| 22 | + year = year + 1900 | |
| 23 | + print 'you mean', year, 'and not the early Christian era' | |
| 24 | + elif not (1850<=year<=2002): | |
| 25 | + print "It's hard to believe you were born in", year | |
| 26 | + return | |
| 27 | + # | |
| 28 | + if sys.argv[2:]: | |
| 29 | + month = int(sys.argv[2]) | |
| 30 | + else: | |
| 31 | + month = int(raw_input('And in which month? (1-12) ')) | |
| 32 | + if not (1<=month<=12): | |
| 33 | + print 'There is no month numbered', month | |
| 34 | + return | |
| 35 | + # | |
| 36 | + if sys.argv[3:]: | |
| 37 | + day = int(sys.argv[3]) | |
| 38 | + else: | |
| 39 | + day = int(raw_input('And on what day of that month? (1-31) ')) | |
| 40 | + if month == 2 and calendar.isleap(year): | |
| 41 | + maxday = 29 | |
| 42 | + else: | |
| 43 | + maxday = calendar.mdays[month] | |
| 44 | + if not (1<=day<=maxday): | |
| 45 | + print 'There are no', day, 'days in that month!' | |
| 46 | + return | |
| 47 | + # | |
| 48 | + bdaytuple = (year, month, day) | |
| 49 | + bdaydate = mkdate(bdaytuple) | |
| 50 | + print 'You were born on', format(bdaytuple) | |
| 51 | + # | |
| 52 | + todaytuple = time.localtime()[:3] | |
| 53 | + todaydate = mkdate(todaytuple) | |
| 54 | + print 'Today is', format(todaytuple) | |
| 55 | + # | |
| 56 | + if bdaytuple > todaytuple: | |
| 57 | + print 'You are a time traveler. Go back to the future!' | |
| 58 | + return | |
| 59 | + # | |
| 60 | + if bdaytuple == todaytuple: | |
| 61 | + print 'You were born today. Have a nice life!' | |
| 62 | + return | |
| 63 | + # | |
| 64 | + days = todaydate - bdaydate | |
| 65 | + print 'You have lived', days, 'days' | |
| 66 | + # | |
| 67 | + age = 0 | |
| 68 | + for y in range(year, todaytuple[0] + 1): | |
| 69 | + if bdaytuple < (y, month, day) <= todaytuple: | |
| 70 | + age = age + 1 | |
| 71 | + # | |
| 72 | + print 'You are', age, 'years old' | |
| 73 | + # | |
| 74 | + if todaytuple[1:] == bdaytuple[1:]: | |
| 75 | + print 'Congratulations! Today is your', nth(age), 'birthday' | |
| 76 | + print 'Yesterday was your', | |
| 77 | + else: | |
| 78 | + print 'Today is your', | |
| 79 | + print nth(days - age), 'unbirthday' | |
| 80 | + | |
| 81 | +def format((year, month, day)): | |
| 82 | + return '%d %s %d' % (day, calendar.month_name[month], year) | |
| 83 | + | |
| 84 | +def nth(n): | |
| 85 | + if n == 1: return '1st' | |
| 86 | + if n == 2: return '2nd' | |
| 87 | + if n == 3: return '3rd' | |
| 88 | + return '%dth' % n | |
| 89 | + | |
| 90 | +def mkdate((year, month, day)): | |
| 91 | + # Januari 1st, in 0 A.D. is arbitrarily defined to be day 1, | |
| 92 | + # even though that day never actually existed and the calendar | |
| 93 | + # was different then... | |
| 94 | + days = year*365 # years, roughly | |
| 95 | + days = days + (year+3)/4 # plus leap years, roughly | |
| 96 | + days = days - (year+99)/100 # minus non-leap years every century | |
| 97 | + days = days + (year+399)/400 # plus leap years every 4 centirues | |
| 98 | + for i in range(1, month): | |
| 99 | + if i == 2 and calendar.isleap(year): | |
| 100 | + days = days + 29 | |
| 101 | + else: | |
| 102 | + days = days + calendar.mdays[i] | |
| 103 | + days = days + day | |
| 104 | + return days | |
| 105 | + | |
| 106 | +if __name__ == "__main__": | |
| 107 | + main() | |
| @@ -0,0 +1,92 @@ | ||
| 1 | +#! /usr/bin/env python | |
| 2 | + | |
| 3 | +# Update a bunch of files according to a script. | |
| 4 | +# The input file contains lines of the form <filename>:<lineno>:<text>, | |
| 5 | +# meaning that the given line of the given file is to be replaced | |
| 6 | +# by the given text. This is useful for performing global substitutions | |
| 7 | +# on grep output: | |
| 8 | + | |
| 9 | +import os | |
| 10 | +import sys | |
| 11 | +import re | |
| 12 | + | |
| 13 | +pat = '^([^: \t\n]+):([1-9][0-9]*):' | |
| 14 | +prog = re.compile(pat) | |
| 15 | + | |
| 16 | +class FileObj: | |
| 17 | + def __init__(self, filename): | |
| 18 | + self.filename = filename | |
| 19 | + self.changed = 0 | |
| 20 | + try: | |
| 21 | + self.lines = open(filename, 'r').readlines() | |
| 22 | + except IOError, msg: | |
| 23 | + print '*** Can\'t open "%s":' % filename, msg | |
| 24 | + self.lines = None | |
| 25 | + return | |
| 26 | + print 'diffing', self.filename | |
| 27 | + | |
| 28 | + def finish(self): | |
| 29 | + if not self.changed: | |
| 30 | + print 'no changes to', self.filename | |
| 31 | + return | |
| 32 | + try: | |
| 33 | + os.rename(self.filename, self.filename + '~') | |
| 34 | + fp = open(self.filename, 'w') | |
| 35 | + except (os.error, IOError), msg: | |
| 36 | + print '*** Can\'t rewrite "%s":' % self.filename, msg | |
| 37 | + return | |
| 38 | + print 'writing', self.filename | |
| 39 | + for line in self.lines: | |
| 40 | + fp.write(line) | |
| 41 | + fp.close() | |
| 42 | + self.changed = 0 | |
| 43 | + | |
| 44 | + def process(self, lineno, rest): | |
| 45 | + if self.lines is None: | |
| 46 | + print '(not processed): %s:%s:%s' % ( | |
| 47 | + self.filename, lineno, rest), | |
| 48 | + return | |
| 49 | + i = eval(lineno) - 1 | |
| 50 | + if not 0 <= i < len(self.lines): | |
| 51 | + print '*** Line number out of range: %s:%s:%s' % ( | |
| 52 | + self.filename, lineno, rest), | |
| 53 | + return | |
| 54 | + if self.lines[i] == rest: | |
| 55 | + print '(no change): %s:%s:%s' % ( | |
| 56 | + self.filename, lineno, rest), | |
| 57 | + return | |
| 58 | + if not self.changed: | |
| 59 | + self.changed = 1 | |
| 60 | + print '%sc%s' % (lineno, lineno) | |
| 61 | + print '<', self.lines[i], | |
| 62 | + print '---' | |
| 63 | + self.lines[i] = rest | |
| 64 | + print '>', self.lines[i], | |
| 65 | + | |
| 66 | +def main(): | |
| 67 | + if sys.argv[1:]: | |
| 68 | + try: | |
| 69 | + fp = open(sys.argv[1], 'r') | |
| 70 | + except IOError, msg: | |
| 71 | + print 'Can\'t open "%s":' % sys.argv[1], msg | |
| 72 | + sys.exit(1) | |
| 73 | + else: | |
| 74 | + fp = sys.stdin | |
| 75 | + curfile = None | |
| 76 | + while 1: | |
| 77 | + line = fp.readline() | |
| 78 | + if not line: | |
| 79 | + if curfile: curfile.finish() | |
| 80 | + break | |
| 81 | + n = prog.match(line) | |
| 82 | + if n < 0: | |
| 83 | + print 'Funny line:', line, | |
| 84 | + continue | |
| 85 | + filename, lineno = prog.group(1, 2) | |
| 86 | + if not curfile or filename <> curfile.filename: | |
| 87 | + if curfile: curfile.finish() | |
| 88 | + curfile = FileObj(filename) | |
| 89 | + curfile.process(lineno, line[n:]) | |
| 90 | + | |
| 91 | +if __name__ == "__main__": | |
| 92 | + main() | |
| @@ -0,0 +1,2 @@ | ||
| 1 | +# This is here so I can use 'wh' instead of 'which' in '~/bin/generic_python' | |
| 2 | +import which | |