Showing the changes to sys/src/cmd/python/Tools/scripts (63 of 3706 files). Show all
Unified · Split
| @@ -0,0 +1,66 @@ | |||
| 1 | This directory contains a collection of executable Python scripts that | ||
| 2 | are useful while building, extending or managing Python. Some (e.g., | ||
| 3 | dutree or lll) are also generally useful UNIX tools. | ||
| 4 | |||
| 5 | See also the Demo/scripts directory! | ||
| 6 | |||
| 7 | byext.py Print lines/words/chars stats of files by extension | ||
| 8 | byteyears.py Print product of a file's size and age | ||
| 9 | checkappend.py Search for multi-argument .append() calls | ||
| 10 | checkpyc.py Check presence and validity of ".pyc" files | ||
| 11 | classfix.py Convert old class syntax to new | ||
| 12 | cleanfuture.py Fix reduntant Python __future__ statements | ||
| 13 | combinerefs.py A helper for analyzing PYTHONDUMPREFS output. | ||
| 14 | copytime.py Copy one file's atime and mtime to another | ||
| 15 | crlf.py Change CRLF line endings to LF (Windows to Unix) | ||
| 16 | cvsfiles.py Print a list of files that are under CVS | ||
| 17 | db2pickle.py Dump a database file to a pickle | ||
| 18 | diff.py Print file diffs in context, unified, or ndiff formats | ||
| 19 | dutree.py Format du(1) output as a tree sorted by size | ||
| 20 | eptags.py Create Emacs TAGS file for Python modules | ||
| 21 | finddiv.py A grep-like tool that looks for division operators. | ||
| 22 | findlinksto.py Recursively find symbolic links to a given path prefix | ||
| 23 | findnocoding.py Find source files which need an encoding declaration | ||
| 24 | fixcid.py Massive identifier substitution on C source files | ||
| 25 | fixdiv.py Tool to fix division operators. | ||
| 26 | fixheader.py Add some cpp magic to a C include file | ||
| 27 | fixnotice.py Fix the copyright notice in source files | ||
| 28 | fixps.py Fix Python scripts' first line (if #!) | ||
| 29 | ftpmirror.py FTP mirror script | ||
| 30 | google.py Open a webbrowser with Google. | ||
| 31 | gprof2html.py Transform gprof(1) output into useful HTML. | ||
| 32 | h2py.py Translate #define's into Python assignments | ||
| 33 | hotshotmain.py Main program to run script under control of hotshot | ||
| 34 | idle Main program to start IDLE | ||
| 35 | ifdef.py Remove #if(n)def groups from C sources | ||
| 36 | lfcr.py Change LF line endings to CRLF (Unix to Windows) | ||
| 37 | linktree.py Make a copy of a tree with links to original files | ||
| 38 | lll.py Find and list symbolic links in current directory | ||
| 39 | logmerge.py Consolidate CVS/RCS logs read from stdin | ||
| 40 | mailerdaemon.py parse error messages from mailer daemons (Sjoerd&Jack) | ||
| 41 | md5sum.py Print MD5 checksums of argument files. | ||
| 42 | methfix.py Fix old method syntax def f(self, (a1, ..., aN)): | ||
| 43 | mkreal.py Turn a symbolic link into a real file or directory | ||
| 44 | ndiff.py Intelligent diff between text files (Tim Peters) | ||
| 45 | nm2def.py Create a template for PC/python_nt.def (Marc Lemburg) | ||
| 46 | objgraph.py Print object graph from nm output on a library | ||
| 47 | parseentities.py Utility for parsing HTML entity definitions | ||
| 48 | pathfix.py Change #!/usr/local/bin/python into something else | ||
| 49 | pdeps.py Print dependencies between Python modules | ||
| 50 | pickle2db.py Load a pickle generated by db2pickle.py to a database | ||
| 51 | pindent.py Indent Python code, giving block-closing comments | ||
| 52 | ptags.py Create vi tags file for Python modules | ||
| 53 | pydoc Python documentation browser. | ||
| 54 | pysource.py Find Python source files | ||
| 55 | redemo.py Basic regular expression demonstration facility | ||
| 56 | reindent.py Change .py files to use 4-space indents. | ||
| 57 | rgrep.py Reverse grep through a file (useful for big logfiles) | ||
| 58 | setup.py Install all scripts listed here. | ||
| 59 | suff.py Sort a list of files by suffix | ||
| 60 | svneol.py Sets svn:eol-style on all files in directory. | ||
| 61 | texcheck.py Validate Python LaTeX formatting (Raymond Hettinger) | ||
| 62 | texi2html.py Convert GNU texinfo files into HTML | ||
| 63 | treesync.py Synchronize source trees (very ideosyncratic) | ||
| 64 | untabify.py Replace tabs with spaces in argument files | ||
| 65 | which.py Find a program in $PATH | ||
| 66 | xxci.py Wrapper for rcsdiff and ci | ||
| @@ -0,0 +1,131 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | """Show file statistics by extension.""" | ||
| 4 | |||
| 5 | import os | ||
| 6 | import sys | ||
| 7 | |||
| 8 | class Stats: | ||
| 9 | |||
| 10 | def __init__(self): | ||
| 11 | self.stats = {} | ||
| 12 | |||
| 13 | def statargs(self, args): | ||
| 14 | for arg in args: | ||
| 15 | if os.path.isdir(arg): | ||
| 16 | self.statdir(arg) | ||
| 17 | elif os.path.isfile(arg): | ||
| 18 | self.statfile(arg) | ||
| 19 | else: | ||
| 20 | sys.stderr.write("Can't find %s\n" % arg) | ||
| 21 | self.addstats("<???>", "unknown", 1) | ||
| 22 | |||
| 23 | def statdir(self, dir): | ||
| 24 | self.addstats("<dir>", "dirs", 1) | ||
| 25 | try: | ||
| 26 | names = os.listdir(dir) | ||
| 27 | except os.error, err: | ||
| 28 | sys.stderr.write("Can't list %s: %s\n" % (dir, err)) | ||
| 29 | self.addstats("<dir>", "unlistable", 1) | ||
| 30 | return | ||
| 31 | names.sort() | ||
| 32 | for name in names: | ||
| 33 | if name.startswith(".#"): | ||
| 34 | continue # Skip CVS temp files | ||
| 35 | if name.endswith("~"): | ||
| 36 | continue# Skip Emacs backup files | ||
| 37 | full = os.path.join(dir, name) | ||
| 38 | if os.path.islink(full): | ||
| 39 | self.addstats("<lnk>", "links", 1) | ||
| 40 | elif os.path.isdir(full): | ||
| 41 | self.statdir(full) | ||
| 42 | else: | ||
| 43 | self.statfile(full) | ||
| 44 | |||
| 45 | def statfile(self, filename): | ||
| 46 | head, ext = os.path.splitext(filename) | ||
| 47 | head, base = os.path.split(filename) | ||
| 48 | if ext == base: | ||
| 49 | ext = "" # E.g. .cvsignore is deemed not to have an extension | ||
| 50 | ext = os.path.normcase(ext) | ||
| 51 | if not ext: | ||
| 52 | ext = "<none>" | ||
| 53 | self.addstats(ext, "files", 1) | ||
| 54 | try: | ||
| 55 | f = open(filename, "rb") | ||
| 56 | except IOError, err: | ||
| 57 | sys.stderr.write("Can't open %s: %s\n" % (filename, err)) | ||
| 58 | self.addstats(ext, "unopenable", 1) | ||
| 59 | return | ||
| 60 | data = f.read() | ||
| 61 | f.close() | ||
| 62 | self.addstats(ext, "bytes", len(data)) | ||
| 63 | if '\0' in data: | ||
| 64 | self.addstats(ext, "binary", 1) | ||
| 65 | return | ||
| 66 | if not data: | ||
| 67 | self.addstats(ext, "empty", 1) | ||
| 68 | #self.addstats(ext, "chars", len(data)) | ||
| 69 | lines = data.splitlines() | ||
| 70 | self.addstats(ext, "lines", len(lines)) | ||
| 71 | del lines | ||
| 72 | words = data.split() | ||
| 73 | self.addstats(ext, "words", len(words)) | ||
| 74 | |||
| 75 | def addstats(self, ext, key, n): | ||
| 76 | d = self.stats.setdefault(ext, {}) | ||
| 77 | d[key] = d.get(key, 0) + n | ||
| 78 | |||
| 79 | def report(self): | ||
| 80 | exts = self.stats.keys() | ||
| 81 | exts.sort() | ||
| 82 | # Get the column keys | ||
| 83 | columns = {} | ||
| 84 | for ext in exts: | ||
| 85 | columns.update(self.stats[ext]) | ||
| 86 | cols = columns.keys() | ||
| 87 | cols.sort() | ||
| 88 | colwidth = {} | ||
| 89 | colwidth["ext"] = max([len(ext) for ext in exts]) | ||
| 90 | minwidth = 6 | ||
| 91 | self.stats["TOTAL"] = {} | ||
| 92 | for col in cols: | ||
| 93 | total = 0 | ||
| 94 | cw = max(minwidth, len(col)) | ||
| 95 | for ext in exts: | ||
| 96 | value = self.stats[ext].get(col) | ||
| 97 | if value is None: | ||
| 98 | w = 0 | ||
| 99 | else: | ||
| 100 | w = len("%d" % value) | ||
| 101 | total += value | ||
| 102 | cw = max(cw, w) | ||
| 103 | cw = max(cw, len(str(total))) | ||
| 104 | colwidth[col] = cw | ||
| 105 | self.stats["TOTAL"][col] = total | ||
| 106 | exts.append("TOTAL") | ||
| 107 | for ext in exts: | ||
| 108 | self.stats[ext]["ext"] = ext | ||
| 109 | cols.insert(0, "ext") | ||
| 110 | def printheader(): | ||
| 111 | for col in cols: | ||
| 112 | print "%*s" % (colwidth[col], col), | ||
| 113 | |||
| 114 | printheader() | ||
| 115 | for ext in exts: | ||
| 116 | for col in cols: | ||
| 117 | value = self.stats[ext].get(col, "") | ||
| 118 | print "%*s" % (colwidth[col], value), | ||
| 119 | |||
| 120 | printheader() # Another header at the bottom | ||
| 121 | |||
| 122 | def main(): | ||
| 123 | args = sys.argv[1:] | ||
| 124 | if not args: | ||
| 125 | args = [os.curdir] | ||
| 126 | s = Stats() | ||
| 127 | s.statargs(args) | ||
| 128 | s.report() | ||
| 129 | |||
| 130 | if __name__ == "__main__": | ||
| 131 | main() | ||
| @@ -0,0 +1,61 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Print the product of age and size of each file, in suitable units. | ||
| 4 | # | ||
| 5 | # Usage: byteyears [ -a | -m | -c ] file ... | ||
| 6 | # | ||
| 7 | # Options -[amc] select atime, mtime (default) or ctime as age. | ||
| 8 | |||
| 9 | import sys, os, time | ||
| 10 | from stat import * | ||
| 11 | |||
| 12 | def main(): | ||
| 13 | |||
| 14 | # Use lstat() to stat files if it exists, else stat() | ||
| 15 | try: | ||
| 16 | statfunc = os.lstat | ||
| 17 | except AttributeError: | ||
| 18 | statfunc = os.stat | ||
| 19 | |||
| 20 | # Parse options | ||
| 21 | if sys.argv[1] == '-m': | ||
| 22 | itime = ST_MTIME | ||
| 23 | del sys.argv[1] | ||
| 24 | elif sys.argv[1] == '-c': | ||
| 25 | itime = ST_CTIME | ||
| 26 | del sys.argv[1] | ||
| 27 | elif sys.argv[1] == '-a': | ||
| 28 | itime = ST_CTIME | ||
| 29 | del sys.argv[1] | ||
| 30 | else: | ||
| 31 | itime = ST_MTIME | ||
| 32 | |||
| 33 | secs_per_year = 365.0 * 24.0 * 3600.0 # Scale factor | ||
| 34 | now = time.time() # Current time, for age computations | ||
| 35 | status = 0 # Exit status, set to 1 on errors | ||
| 36 | |||
| 37 | # Compute max file name length | ||
| 38 | maxlen = 1 | ||
| 39 | for filename in sys.argv[1:]: | ||
| 40 | maxlen = max(maxlen, len(filename)) | ||
| 41 | |||
| 42 | # Process each argument in turn | ||
| 43 | for filename in sys.argv[1:]: | ||
| 44 | try: | ||
| 45 | st = statfunc(filename) | ||
| 46 | except os.error, msg: | ||
| 47 | sys.stderr.write("can't stat %r: %r\n" % (filename, msg)) | ||
| 48 | status = 1 | ||
| 49 | st = () | ||
| 50 | if st: | ||
| 51 | anytime = st[itime] | ||
| 52 | size = st[ST_SIZE] | ||
| 53 | age = now - anytime | ||
| 54 | byteyears = float(size) * float(age) / secs_per_year | ||
| 55 | print filename.ljust(maxlen), | ||
| 56 | print repr(int(byteyears)).rjust(8) | ||
| 57 | |||
| 58 | sys.exit(status) | ||
| 59 | |||
| 60 | if __name__ == '__main__': | ||
| 61 | main() | ||
| @@ -0,0 +1,167 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Released to the public domain, by Tim Peters, 28 February 2000. | ||
| 4 | |||
| 5 | """checkappend.py -- search for multi-argument .append() calls. | ||
| 6 | |||
| 7 | Usage: specify one or more file or directory paths: | ||
| 8 | checkappend [-v] file_or_dir [file_or_dir] ... | ||
| 9 | |||
| 10 | Each file_or_dir is checked for multi-argument .append() calls. When | ||
| 11 | a directory, all .py files in the directory, and recursively in its | ||
| 12 | subdirectories, are checked. | ||
| 13 | |||
| 14 | Use -v for status msgs. Use -vv for more status msgs. | ||
| 15 | |||
| 16 | In the absence of -v, the only output is pairs of the form | ||
| 17 | |||
| 18 | filename(linenumber): | ||
| 19 | line containing the suspicious append | ||
| 20 | |||
| 21 | Note that this finds multi-argument append calls regardless of whether | ||
| 22 | they're attached to list objects. If a module defines a class with an | ||
| 23 | append method that takes more than one argument, calls to that method | ||
| 24 | will be listed. | ||
| 25 | |||
| 26 | Note that this will not find multi-argument list.append calls made via a | ||
| 27 | bound method object. For example, this is not caught: | ||
| 28 | |||
| 29 | somelist = [] | ||
| 30 | push = somelist.append | ||
| 31 | push(1, 2, 3) | ||
| 32 | """ | ||
| 33 | |||
| 34 | __version__ = 1, 0, 0 | ||
| 35 | |||
| 36 | import os | ||
| 37 | import sys | ||
| 38 | import getopt | ||
| 39 | import tokenize | ||
| 40 | |||
| 41 | verbose = 0 | ||
| 42 | |||
| 43 | def errprint(*args): | ||
| 44 | msg = ' '.join(args) | ||
| 45 | sys.stderr.write(msg) | ||
| 46 | sys.stderr.write("\n") | ||
| 47 | |||
| 48 | def main(): | ||
| 49 | args = sys.argv[1:] | ||
| 50 | global verbose | ||
| 51 | try: | ||
| 52 | opts, args = getopt.getopt(sys.argv[1:], "v") | ||
| 53 | except getopt.error, msg: | ||
| 54 | errprint(str(msg) + "\n\n" + __doc__) | ||
| 55 | return | ||
| 56 | for opt, optarg in opts: | ||
| 57 | if opt == '-v': | ||
| 58 | verbose = verbose + 1 | ||
| 59 | if not args: | ||
| 60 | errprint(__doc__) | ||
| 61 | return | ||
| 62 | for arg in args: | ||
| 63 | check(arg) | ||
| 64 | |||
| 65 | def check(file): | ||
| 66 | if os.path.isdir(file) and not os.path.islink(file): | ||
| 67 | if verbose: | ||
| 68 | print "%r: listing directory" % (file,) | ||
| 69 | names = os.listdir(file) | ||
| 70 | for name in names: | ||
| 71 | fullname = os.path.join(file, name) | ||
| 72 | if ((os.path.isdir(fullname) and | ||
| 73 | not os.path.islink(fullname)) | ||
| 74 | or os.path.normcase(name[-3:]) == ".py"): | ||
| 75 | check(fullname) | ||
| 76 | return | ||
| 77 | |||
| 78 | try: | ||
| 79 | f = open(file) | ||
| 80 | except IOError, msg: | ||
| 81 | errprint("%r: I/O Error: %s" % (file, msg)) | ||
| 82 | return | ||
| 83 | |||
| 84 | if verbose > 1: | ||
| 85 | print "checking %r ..." % (file,) | ||
| 86 | |||
| 87 | ok = AppendChecker(file, f).run() | ||
| 88 | if verbose and ok: | ||
| 89 | print "%r: Clean bill of health." % (file,) | ||
| 90 | |||
| 91 | [FIND_DOT, | ||
| 92 | FIND_APPEND, | ||
| 93 | FIND_LPAREN, | ||
| 94 | FIND_COMMA, | ||
| 95 | FIND_STMT] = range(5) | ||
| 96 | |||
| 97 | class AppendChecker: | ||
| 98 | def __init__(self, fname, file): | ||
| 99 | self.fname = fname | ||
| 100 | self.file = file | ||
| 101 | self.state = FIND_DOT | ||
| 102 | self.nerrors = 0 | ||
| 103 | |||
| 104 | def run(self): | ||
| 105 | try: | ||
| 106 | tokenize.tokenize(self.file.readline, self.tokeneater) | ||
| 107 | except tokenize.TokenError, msg: | ||
| 108 | errprint("%r: Token Error: %s" % (self.fname, msg)) | ||
| 109 | self.nerrors = self.nerrors + 1 | ||
| 110 | return self.nerrors == 0 | ||
| 111 | |||
| 112 | def tokeneater(self, type, token, start, end, line, | ||
| 113 | NEWLINE=tokenize.NEWLINE, | ||
| 114 | JUNK=(tokenize.COMMENT, tokenize.NL), | ||
| 115 | OP=tokenize.OP, | ||
| 116 | NAME=tokenize.NAME): | ||
| 117 | |||
| 118 | state = self.state | ||
| 119 | |||
| 120 | if type in JUNK: | ||
| 121 | pass | ||
| 122 | |||
| 123 | elif state is FIND_DOT: | ||
| 124 | if type is OP and token == ".": | ||
| 125 | state = FIND_APPEND | ||
| 126 | |||
| 127 | elif state is FIND_APPEND: | ||
| 128 | if type is NAME and token == "append": | ||
| 129 | self.line = line | ||
| 130 | self.lineno = start[0] | ||
| 131 | state = FIND_LPAREN | ||
| 132 | else: | ||
| 133 | state = FIND_DOT | ||
| 134 | |||
| 135 | elif state is FIND_LPAREN: | ||
| 136 | if type is OP and token == "(": | ||
| 137 | self.level = 1 | ||
| 138 | state = FIND_COMMA | ||
| 139 | else: | ||
| 140 | state = FIND_DOT | ||
| 141 | |||
| 142 | elif state is FIND_COMMA: | ||
| 143 | if type is OP: | ||
| 144 | if token in ("(", "{", "["): | ||
| 145 | self.level = self.level + 1 | ||
| 146 | elif token in (")", "}", "]"): | ||
| 147 | self.level = self.level - 1 | ||
| 148 | if self.level == 0: | ||
| 149 | state = FIND_DOT | ||
| 150 | elif token == "," and self.level == 1: | ||
| 151 | self.nerrors = self.nerrors + 1 | ||
| 152 | print "%s(%d):\n%s" % (self.fname, self.lineno, | ||
| 153 | self.line) | ||
| 154 | # don't gripe about this stmt again | ||
| 155 | state = FIND_STMT | ||
| 156 | |||
| 157 | elif state is FIND_STMT: | ||
| 158 | if type is NEWLINE: | ||
| 159 | state = FIND_DOT | ||
| 160 | |||
| 161 | else: | ||
| 162 | raise SystemError("unknown internal state '%r'" % (state,)) | ||
| 163 | |||
| 164 | self.state = state | ||
| 165 | |||
| 166 | if __name__ == '__main__': | ||
| 167 | main() | ||
| @@ -0,0 +1,66 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | # Check that all ".pyc" files exist and are up-to-date | ||
| 3 | # Uses module 'os' | ||
| 4 | |||
| 5 | import sys | ||
| 6 | import os | ||
| 7 | from stat import ST_MTIME | ||
| 8 | import imp | ||
| 9 | |||
| 10 | def main(): | ||
| 11 | silent = 0 | ||
| 12 | verbose = 0 | ||
| 13 | if sys.argv[1:]: | ||
| 14 | if sys.argv[1] == '-v': | ||
| 15 | verbose = 1 | ||
| 16 | elif sys.argv[1] == '-s': | ||
| 17 | silent = 1 | ||
| 18 | MAGIC = imp.get_magic() | ||
| 19 | if not silent: | ||
| 20 | print 'Using MAGIC word', repr(MAGIC) | ||
| 21 | for dirname in sys.path: | ||
| 22 | try: | ||
| 23 | names = os.listdir(dirname) | ||
| 24 | except os.error: | ||
| 25 | print 'Cannot list directory', repr(dirname) | ||
| 26 | continue | ||
| 27 | if not silent: | ||
| 28 | print 'Checking ', repr(dirname), '...' | ||
| 29 | names.sort() | ||
| 30 | for name in names: | ||
| 31 | if name[-3:] == '.py': | ||
| 32 | name = os.path.join(dirname, name) | ||
| 33 | try: | ||
| 34 | st = os.stat(name) | ||
| 35 | except os.error: | ||
| 36 | print 'Cannot stat', repr(name) | ||
| 37 | continue | ||
| 38 | if verbose: | ||
| 39 | print 'Check', repr(name), '...' | ||
| 40 | name_c = name + 'c' | ||
| 41 | try: | ||
| 42 | f = open(name_c, 'r') | ||
| 43 | except IOError: | ||
| 44 | print 'Cannot open', repr(name_c) | ||
| 45 | continue | ||
| 46 | magic_str = f.read(4) | ||
| 47 | mtime_str = f.read(4) | ||
| 48 | f.close() | ||
| 49 | if magic_str <> MAGIC: | ||
| 50 | print 'Bad MAGIC word in ".pyc" file', | ||
| 51 | print repr(name_c) | ||
| 52 | continue | ||
| 53 | mtime = get_long(mtime_str) | ||
| 54 | if mtime == 0 or mtime == -1: | ||
| 55 | print 'Bad ".pyc" file', repr(name_c) | ||
| 56 | elif mtime <> st[ST_MTIME]: | ||
| 57 | print 'Out-of-date ".pyc" file', | ||
| 58 | print repr(name_c) | ||
| 59 | |||
| 60 | def get_long(s): | ||
| 61 | if len(s) <> 4: | ||
| 62 | return -1 | ||
| 63 | return ord(s[0]) + (ord(s[1])<<8) + (ord(s[2])<<16) + (ord(s[3])<<24) | ||
| 64 | |||
| 65 | if __name__ == '__main__': | ||
| 66 | main() | ||
| @@ -0,0 +1,190 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # This script is obsolete -- it is kept for historical purposes only. | ||
| 4 | # | ||
| 5 | # Fix Python source files to use the new class definition syntax, i.e., | ||
| 6 | # the syntax used in Python versions before 0.9.8: | ||
| 7 | # class C() = base(), base(), ...: ... | ||
| 8 | # is changed to the current syntax: | ||
| 9 | # class C(base, base, ...): ... | ||
| 10 | # | ||
| 11 | # The script uses heuristics to find class definitions that usually | ||
| 12 | # work but occasionally can fail; carefully check the output! | ||
| 13 | # | ||
| 14 | # Command line arguments are files or directories to be processed. | ||
| 15 | # Directories are searched recursively for files whose name looks | ||
| 16 | # like a python module. | ||
| 17 | # Symbolic links are always ignored (except as explicit directory | ||
| 18 | # arguments). Of course, the original file is kept as a back-up | ||
| 19 | # (with a "~" attached to its name). | ||
| 20 | # | ||
| 21 | # Changes made are reported to stdout in a diff-like format. | ||
| 22 | # | ||
| 23 | # Undoubtedly you can do this using find and sed or perl, but this is | ||
| 24 | # a nice example of Python code that recurses down a directory tree | ||
| 25 | # and uses regular expressions. Also note several subtleties like | ||
| 26 | # preserving the file's mode and avoiding to even write a temp file | ||
| 27 | # when no changes are needed for a file. | ||
| 28 | # | ||
| 29 | # NB: by changing only the function fixline() you can turn this | ||
| 30 | # into a program for a different change to Python programs... | ||
| 31 | |||
| 32 | import sys | ||
| 33 | import re | ||
| 34 | import os | ||
| 35 | from stat import * | ||
| 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' % (filename,)) | ||
| 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 | while line[-2:] == '\\\n': | ||
| 100 | nextline = f.readline() | ||
| 101 | if not nextline: break | ||
| 102 | line = line + nextline | ||
| 103 | lineno = lineno + 1 | ||
| 104 | newline = fixline(line) | ||
| 105 | if newline != line: | ||
| 106 | if g is None: | ||
| 107 | try: | ||
| 108 | g = open(tempname, 'w') | ||
| 109 | except IOError, msg: | ||
| 110 | f.close() | ||
| 111 | err('%s: cannot create: %r\n' % (tempname, msg)) | ||
| 112 | return 1 | ||
| 113 | f.seek(0) | ||
| 114 | lineno = 0 | ||
| 115 | rep(filename + ':\n') | ||
| 116 | continue # restart from the beginning | ||
| 117 | rep(repr(lineno) + '\n') | ||
| 118 | rep('< ' + line) | ||
| 119 | rep('> ' + newline) | ||
| 120 | if g is not None: | ||
| 121 | g.write(newline) | ||
| 122 | |||
| 123 | # End of file | ||
| 124 | f.close() | ||
| 125 | if not g: return 0 # No changes | ||
| 126 | |||
| 127 | # Finishing touch -- move files | ||
| 128 | |||
| 129 | # First copy the file's mode to the temp file | ||
| 130 | try: | ||
| 131 | statbuf = os.stat(filename) | ||
| 132 | os.chmod(tempname, statbuf[ST_MODE] & 07777) | ||
| 133 | except os.error, msg: | ||
| 134 | err('%s: warning: chmod failed (%r)\n' % (tempname, msg)) | ||
| 135 | # Then make a backup of the original file as filename~ | ||
| 136 | try: | ||
| 137 | os.rename(filename, filename + '~') | ||
| 138 | except os.error, msg: | ||
| 139 | err('%s: warning: backup failed (%r)\n' % (filename, msg)) | ||
| 140 | # Now move the temp file to the original file | ||
| 141 | try: | ||
| 142 | os.rename(tempname, filename) | ||
| 143 | except os.error, msg: | ||
| 144 | err('%s: rename failed (%r)\n' % (filename, msg)) | ||
| 145 | return 1 | ||
| 146 | # Return succes | ||
| 147 | return 0 | ||
| 148 | |||
| 149 | # This expression doesn't catch *all* class definition headers, | ||
| 150 | # but it's pretty darn close. | ||
| 151 | classexpr = '^([ \t]*class +[a-zA-Z0-9_]+) *( *) *((=.*)?):' | ||
| 152 | classprog = re.compile(classexpr) | ||
| 153 | |||
| 154 | # Expressions for finding base class expressions. | ||
| 155 | baseexpr = '^ *(.*) *( *) *$' | ||
| 156 | baseprog = re.compile(baseexpr) | ||
| 157 | |||
| 158 | def fixline(line): | ||
| 159 | if classprog.match(line) < 0: # No 'class' keyword -- no change | ||
| 160 | return line | ||
| 161 | |||
| 162 | (a0, b0), (a1, b1), (a2, b2) = classprog.regs[:3] | ||
| 163 | # a0, b0 = Whole match (up to ':') | ||
| 164 | # a1, b1 = First subexpression (up to classname) | ||
| 165 | # a2, b2 = Second subexpression (=.*) | ||
| 166 | head = line[:b1] | ||
| 167 | tail = line[b0:] # Unmatched rest of line | ||
| 168 | |||
| 169 | if a2 == b2: # No base classes -- easy case | ||
| 170 | return head + ':' + tail | ||
| 171 | |||
| 172 | # Get rid of leading '=' | ||
| 173 | basepart = line[a2+1:b2] | ||
| 174 | |||
| 175 | # Extract list of base expressions | ||
| 176 | bases = basepart.split(',') | ||
| 177 | |||
| 178 | # Strip trailing '()' from each base expression | ||
| 179 | for i in range(len(bases)): | ||
| 180 | if baseprog.match(bases[i]) >= 0: | ||
| 181 | x1, y1 = baseprog.regs[1] | ||
| 182 | bases[i] = bases[i][x1:y1] | ||
| 183 | |||
| 184 | # Join the bases back again and build the new line | ||
| 185 | basepart = ', '.join(bases) | ||
| 186 | |||
| 187 | return head + '(' + basepart + '):' + tail | ||
| 188 | |||
| 189 | if __name__ == '__main__': | ||
| 190 | main() | ||
| @@ -0,0 +1,276 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | """cleanfuture [-d][-r][-v] path ... | ||
| 4 | |||
| 5 | -d Dry run. Analyze, but don't make any changes to, files. | ||
| 6 | -r Recurse. Search for all .py files in subdirectories too. | ||
| 7 | -v Verbose. Print informative msgs. | ||
| 8 | |||
| 9 | Search Python (.py) files for future statements, and remove the features | ||
| 10 | from such statements that are already mandatory in the version of Python | ||
| 11 | you're using. | ||
| 12 | |||
| 13 | Pass one or more file and/or directory paths. When a directory path, all | ||
| 14 | .py files within the directory will be examined, and, if the -r option is | ||
| 15 | given, likewise recursively for subdirectories. | ||
| 16 | |||
| 17 | Overwrites files in place, renaming the originals with a .bak extension. If | ||
| 18 | cleanfuture finds nothing to change, the file is left alone. If cleanfuture | ||
| 19 | does change a file, the changed file is a fixed-point (i.e., running | ||
| 20 | cleanfuture on the resulting .py file won't change it again, at least not | ||
| 21 | until you try it again with a later Python release). | ||
| 22 | |||
| 23 | Limitations: You can do these things, but this tool won't help you then: | ||
| 24 | |||
| 25 | + A future statement cannot be mixed with any other statement on the same | ||
| 26 | physical line (separated by semicolon). | ||
| 27 | |||
| 28 | + A future statement cannot contain an "as" clause. | ||
| 29 | |||
| 30 | Example: Assuming you're using Python 2.2, if a file containing | ||
| 31 | |||
| 32 | from __future__ import nested_scopes, generators | ||
| 33 | |||
| 34 | is analyzed by cleanfuture, the line is rewritten to | ||
| 35 | |||
| 36 | from __future__ import generators | ||
| 37 | |||
| 38 | because nested_scopes is no longer optional in 2.2 but generators is. | ||
| 39 | """ | ||
| 40 | |||
| 41 | import __future__ | ||
| 42 | import tokenize | ||
| 43 | import os | ||
| 44 | import sys | ||
| 45 | |||
| 46 | dryrun = 0 | ||
| 47 | recurse = 0 | ||
| 48 | verbose = 0 | ||
| 49 | |||
| 50 | def errprint(*args): | ||
| 51 | strings = map(str, args) | ||
| 52 | msg = ' '.join(strings) | ||
| 53 | if msg[-1:] != '\n': | ||
| 54 | msg += '\n' | ||
| 55 | sys.stderr.write(msg) | ||
| 56 | |||
| 57 | def main(): | ||
| 58 | import getopt | ||
| 59 | global verbose, recurse, dryrun | ||
| 60 | try: | ||
| 61 | opts, args = getopt.getopt(sys.argv[1:], "drv") | ||
| 62 | except getopt.error, msg: | ||
| 63 | errprint(msg) | ||
| 64 | return | ||
| 65 | for o, a in opts: | ||
| 66 | if o == '-d': | ||
| 67 | dryrun += 1 | ||
| 68 | elif o == '-r': | ||
| 69 | recurse += 1 | ||
| 70 | elif o == '-v': | ||
| 71 | verbose += 1 | ||
| 72 | if not args: | ||
| 73 | errprint("Usage:", __doc__) | ||
| 74 | return | ||
| 75 | for arg in args: | ||
| 76 | check(arg) | ||
| 77 | |||
| 78 | def check(file): | ||
| 79 | if os.path.isdir(file) and not os.path.islink(file): | ||
| 80 | if verbose: | ||
| 81 | print "listing directory", file | ||
| 82 | names = os.listdir(file) | ||
| 83 | for name in names: | ||
| 84 | fullname = os.path.join(file, name) | ||
| 85 | if ((recurse and os.path.isdir(fullname) and | ||
| 86 | not os.path.islink(fullname)) | ||
| 87 | or name.lower().endswith(".py")): | ||
| 88 | check(fullname) | ||
| 89 | return | ||
| 90 | |||
| 91 | if verbose: | ||
| 92 | print "checking", file, "...", | ||
| 93 | try: | ||
| 94 | f = open(file) | ||
| 95 | except IOError, msg: | ||
| 96 | errprint("%r: I/O Error: %s" % (file, str(msg))) | ||
| 97 | return | ||
| 98 | |||
| 99 | ff = FutureFinder(f, file) | ||
| 100 | changed = ff.run() | ||
| 101 | if changed: | ||
| 102 | ff.gettherest() | ||
| 103 | f.close() | ||
| 104 | if changed: | ||
| 105 | if verbose: | ||
| 106 | print "changed." | ||
| 107 | if dryrun: | ||
| 108 | print "But this is a dry run, so leaving it alone." | ||
| 109 | for s, e, line in changed: | ||
| 110 | print "%r lines %d-%d" % (file, s+1, e+1) | ||
| 111 | for i in range(s, e+1): | ||
| 112 | print ff.lines[i], | ||
| 113 | if line is None: | ||
| 114 | print "-- deleted" | ||
| 115 | else: | ||
| 116 | print "-- change to:" | ||
| 117 | print line, | ||
| 118 | if not dryrun: | ||
| 119 | bak = file + ".bak" | ||
| 120 | if os.path.exists(bak): | ||
| 121 | os.remove(bak) | ||
| 122 | os.rename(file, bak) | ||
| 123 | if verbose: | ||
| 124 | print "renamed", file, "to", bak | ||
| 125 | g = open(file, "w") | ||
| 126 | ff.write(g) | ||
| 127 | g.close() | ||
| 128 | if verbose: | ||
| 129 | print "wrote new", file | ||
| 130 | else: | ||
| 131 | if verbose: | ||
| 132 | print "unchanged." | ||
| 133 | |||
| 134 | class FutureFinder: | ||
| 135 | |||
| 136 | def __init__(self, f, fname): | ||
| 137 | self.f = f | ||
| 138 | self.fname = fname | ||
| 139 | self.ateof = 0 | ||
| 140 | self.lines = [] # raw file lines | ||
| 141 | |||
| 142 | # List of (start_index, end_index, new_line) triples. | ||
| 143 | self.changed = [] | ||
| 144 | |||
| 145 | # Line-getter for tokenize. | ||
| 146 | def getline(self): | ||
| 147 | if self.ateof: | ||
| 148 | return "" | ||
| 149 | line = self.f.readline() | ||
| 150 | if line == "": | ||
| 151 | self.ateof = 1 | ||
| 152 | else: | ||
| 153 | self.lines.append(line) | ||
| 154 | return line | ||
| 155 | |||
| 156 | def run(self): | ||
| 157 | STRING = tokenize.STRING | ||
| 158 | NL = tokenize.NL | ||
| 159 | NEWLINE = tokenize.NEWLINE | ||
| 160 | COMMENT = tokenize.COMMENT | ||
| 161 | NAME = tokenize.NAME | ||
| 162 | OP = tokenize.OP | ||
| 163 | |||
| 164 | changed = self.changed | ||
| 165 | get = tokenize.generate_tokens(self.getline).next | ||
| 166 | type, token, (srow, scol), (erow, ecol), line = get() | ||
| 167 | |||
| 168 | # Chew up initial comments and blank lines (if any). | ||
| 169 | while type in (COMMENT, NL, NEWLINE): | ||
| 170 | type, token, (srow, scol), (erow, ecol), line = get() | ||
| 171 | |||
| 172 | # Chew up docstring (if any -- and it may be implicitly catenated!). | ||
| 173 | while type is STRING: | ||
| 174 | type, token, (srow, scol), (erow, ecol), line = get() | ||
| 175 | |||
| 176 | # Analyze the future stmts. | ||
| 177 | while 1: | ||
| 178 | # Chew up comments and blank lines (if any). | ||
| 179 | while type in (COMMENT, NL, NEWLINE): | ||
| 180 | type, token, (srow, scol), (erow, ecol), line = get() | ||
| 181 | |||
| 182 | if not (type is NAME and token == "from"): | ||
| 183 | break | ||
| 184 | startline = srow - 1 # tokenize is one-based | ||
| 185 | type, token, (srow, scol), (erow, ecol), line = get() | ||
| 186 | |||
| 187 | if not (type is NAME and token == "__future__"): | ||
| 188 | break | ||
| 189 | type, token, (srow, scol), (erow, ecol), line = get() | ||
| 190 | |||
| 191 | if not (type is NAME and token == "import"): | ||
| 192 | break | ||
| 193 | type, token, (srow, scol), (erow, ecol), line = get() | ||
| 194 | |||
| 195 | # Get the list of features. | ||
| 196 | features = [] | ||
| 197 | while type is NAME: | ||
| 198 | features.append(token) | ||
| 199 | type, token, (srow, scol), (erow, ecol), line = get() | ||
| 200 | |||
| 201 | if not (type is OP and token == ','): | ||
| 202 | break | ||
| 203 | type, token, (srow, scol), (erow, ecol), line = get() | ||
| 204 | |||
| 205 | # A trailing comment? | ||
| 206 | comment = None | ||
| 207 | if type is COMMENT: | ||
| 208 | comment = token | ||
| 209 | type, token, (srow, scol), (erow, ecol), line = get() | ||
| 210 | |||
| 211 | if type is not NEWLINE: | ||
| 212 | errprint("Skipping file %r; can't parse line %d:\n%s" % | ||
| 213 | (self.fname, srow, line)) | ||
| 214 | return [] | ||
| 215 | |||
| 216 | endline = srow - 1 | ||
| 217 | |||
| 218 | # Check for obsolete features. | ||
| 219 | okfeatures = [] | ||
| 220 | for f in features: | ||
| 221 | object = getattr(__future__, f, None) | ||
| 222 | if object is None: | ||
| 223 | # A feature we don't know about yet -- leave it in. | ||
| 224 | # They'll get a compile-time error when they compile | ||
| 225 | # this program, but that's not our job to sort out. | ||
| 226 | okfeatures.append(f) | ||
| 227 | else: | ||
| 228 | released = object.getMandatoryRelease() | ||
| 229 | if released is None or released <= sys.version_info: | ||
| 230 | # Withdrawn or obsolete. | ||
| 231 | pass | ||
| 232 | else: | ||
| 233 | okfeatures.append(f) | ||
| 234 | |||
| 235 | # Rewrite the line if at least one future-feature is obsolete. | ||
| 236 | if len(okfeatures) < len(features): | ||
| 237 | if len(okfeatures) == 0: | ||
| 238 | line = None | ||
| 239 | else: | ||
| 240 | line = "from __future__ import " | ||
| 241 | line += ', '.join(okfeatures) | ||
| 242 | if comment is not None: | ||
| 243 | line += ' ' + comment | ||
| 244 | line += '\n' | ||
| 245 | changed.append((startline, endline, line)) | ||
| 246 | |||
| 247 | # Loop back for more future statements. | ||
| 248 | |||
| 249 | return changed | ||
| 250 | |||
| 251 | def gettherest(self): | ||
| 252 | if self.ateof: | ||
| 253 | self.therest = '' | ||
| 254 | else: | ||
| 255 | self.therest = self.f.read() | ||
| 256 | |||
| 257 | def write(self, f): | ||
| 258 | changed = self.changed | ||
| 259 | assert changed | ||
| 260 | # Prevent calling this again. | ||
| 261 | self.changed = [] | ||
| 262 | # Apply changes in reverse order. | ||
| 263 | changed.reverse() | ||
| 264 | for s, e, line in changed: | ||
| 265 | if line is None: | ||
| 266 | # pure deletion | ||
| 267 | del self.lines[s:e+1] | ||
| 268 | else: | ||
| 269 | self.lines[s:e+1] = [line] | ||
| 270 | f.writelines(self.lines) | ||
| 271 | # Copy over the remainder of the file. | ||
| 272 | if self.therest: | ||
| 273 | f.write(self.therest) | ||
| 274 | |||
| 275 | if __name__ == '__main__': | ||
| 276 | main() | ||
| @@ -0,0 +1,127 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | """ | ||
| 4 | combinerefs path | ||
| 5 | |||
| 6 | A helper for analyzing PYTHONDUMPREFS output. | ||
| 7 | |||
| 8 | When the PYTHONDUMPREFS envar is set in a debug build, at Python shutdown | ||
| 9 | time Py_Finalize() prints the list of all live objects twice: first it | ||
| 10 | prints the repr() of each object while the interpreter is still fully intact. | ||
| 11 | After cleaning up everything it can, it prints all remaining live objects | ||
| 12 | again, but the second time just prints their addresses, refcounts, and type | ||
| 13 | names (because the interpreter has been torn down, calling repr methods at | ||
| 14 | this point can get into infinite loops or blow up). | ||
| 15 | |||
| 16 | Save all this output into a file, then run this script passing the path to | ||
| 17 | that file. The script finds both output chunks, combines them, then prints | ||
| 18 | a line of output for each object still alive at the end: | ||
| 19 | |||
| 20 | address refcnt typename repr | ||
| 21 | |||
| 22 | address is the address of the object, in whatever format the platform C | ||
| 23 | produces for a %p format code. | ||
| 24 | |||
| 25 | refcnt is of the form | ||
| 26 | |||
| 27 | "[" ref "]" | ||
| 28 | |||
| 29 | when the object's refcount is the same in both PYTHONDUMPREFS output blocks, | ||
| 30 | or | ||
| 31 | |||
| 32 | "[" ref_before "->" ref_after "]" | ||
| 33 | |||
| 34 | if the refcount changed. | ||
| 35 | |||
| 36 | typename is object->ob_type->tp_name, extracted from the second PYTHONDUMPREFS | ||
| 37 | output block. | ||
| 38 | |||
| 39 | repr is repr(object), extracted from the first PYTHONDUMPREFS output block. | ||
| 40 | CAUTION: If object is a container type, it may not actually contain all the | ||
| 41 | objects shown in the repr: the repr was captured from the first output block, | ||
| 42 | and some of the containees may have been released since then. For example, | ||
| 43 | it's common for the line showing the dict of interned strings to display | ||
| 44 | strings that no longer exist at the end of Py_Finalize; this can be recognized | ||
| 45 | (albeit painfully) because such containees don't have a line of their own. | ||
| 46 | |||
| 47 | The objects are listed in allocation order, with most-recently allocated | ||
| 48 | printed first, and the first object allocated printed last. | ||
| 49 | |||
| 50 | |||
| 51 | Simple examples: | ||
| 52 | |||
| 53 | 00857060 [14] str '__len__' | ||
| 54 | |||
| 55 | The str object '__len__' is alive at shutdown time, and both PYTHONDUMPREFS | ||
| 56 | output blocks said there were 14 references to it. This is probably due to | ||
| 57 | C modules that intern the string "__len__" and keep a reference to it in a | ||
| 58 | file static. | ||
| 59 | |||
| 60 | 00857038 [46->5] tuple () | ||
| 61 | |||
| 62 | 46-5 = 41 references to the empty tuple were removed by the cleanup actions | ||
| 63 | between the times PYTHONDUMPREFS produced output. | ||
| 64 | |||
| 65 | 00858028 [1025->1456] str '<dummy key>' | ||
| 66 | |||
| 67 | The string '<dummy key>', which is used in dictobject.c to overwrite a real | ||
| 68 | key that gets deleted, grew several hundred references during cleanup. It | ||
| 69 | suggests that stuff did get removed from dicts by cleanup, but that the dicts | ||
| 70 | themselves are staying alive for some reason. """ | ||
| 71 | |||
| 72 | import re | ||
| 73 | import sys | ||
| 74 | |||
| 75 | # Generate lines from fileiter. If whilematch is true, continue reading | ||
| 76 | # while the regexp object pat matches line. If whilematch is false, lines | ||
| 77 | # are read so long as pat doesn't match them. In any case, the first line | ||
| 78 | # that doesn't match pat (when whilematch is true), or that does match pat | ||
| 79 | # (when whilematch is false), is lost, and fileiter will resume at the line | ||
| 80 | # following it. | ||
| 81 | def read(fileiter, pat, whilematch): | ||
| 82 | for line in fileiter: | ||
| 83 | if bool(pat.match(line)) == whilematch: | ||
| 84 | yield line | ||
| 85 | else: | ||
| 86 | break | ||
| 87 | |||
| 88 | def combine(fname): | ||
| 89 | f = file(fname) | ||
| 90 | fi = iter(f) | ||
| 91 | |||
| 92 | for line in read(fi, re.compile(r'^Remaining objects:$'), False): | ||
| 93 | pass | ||
| 94 | |||
| 95 | crack = re.compile(r'([a-zA-Z\d]+) \[(\d+)\] (.*)') | ||
| 96 | addr2rc = {} | ||
| 97 | addr2guts = {} | ||
| 98 | before = 0 | ||
| 99 | for line in read(fi, re.compile(r'^Remaining object addresses:$'), False): | ||
| 100 | m = crack.match(line) | ||
| 101 | if m: | ||
| 102 | addr, addr2rc[addr], addr2guts[addr] = m.groups() | ||
| 103 | before += 1 | ||
| 104 | else: | ||
| 105 | print '??? skipped:', line | ||
| 106 | |||
| 107 | after = 0 | ||
| 108 | for line in read(fi, crack, True): | ||
| 109 | after += 1 | ||
| 110 | m = crack.match(line) | ||
| 111 | assert m | ||
| 112 | addr, rc, guts = m.groups() # guts is type name here | ||
| 113 | if addr not in addr2rc: | ||
| 114 | print '??? new object created while tearing down:', line.rstrip() | ||
| 115 | continue | ||
| 116 | print addr, | ||
| 117 | if rc == addr2rc[addr]: | ||
| 118 | print '[%s]' % rc, | ||
| 119 | else: | ||
| 120 | print '[%s->%s]' % (addr2rc[addr], rc), | ||
| 121 | print guts, addr2guts[addr] | ||
| 122 | |||
| 123 | f.close() | ||
| 124 | print "%d objects before, %d after" % (before, after) | ||
| 125 | |||
| 126 | if __name__ == '__main__': | ||
| 127 | combine(sys.argv[1]) | ||
| @@ -0,0 +1,26 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Copy one file's atime and mtime to another | ||
| 4 | |||
| 5 | import sys | ||
| 6 | import os | ||
| 7 | from stat import ST_ATIME, ST_MTIME # Really constants 7 and 8 | ||
| 8 | |||
| 9 | def main(): | ||
| 10 | if len(sys.argv) <> 3: | ||
| 11 | sys.stderr.write('usage: copytime source destination\n') | ||
| 12 | sys.exit(2) | ||
| 13 | file1, file2 = sys.argv[1], sys.argv[2] | ||
| 14 | try: | ||
| 15 | stat1 = os.stat(file1) | ||
| 16 | except os.error: | ||
| 17 | sys.stderr.write(file1 + ': cannot stat\n') | ||
| 18 | sys.exit(1) | ||
| 19 | try: | ||
| 20 | os.utime(file2, (stat1[ST_ATIME], stat1[ST_MTIME])) | ||
| 21 | except os.error: | ||
| 22 | sys.stderr.write(file2 + ': cannot change time\n') | ||
| 23 | sys.exit(2) | ||
| 24 | |||
| 25 | if __name__ == '__main__': | ||
| 26 | main() | ||
| @@ -0,0 +1,23 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | "Replace CRLF with LF in argument files. Print names of changed files." | ||
| 3 | |||
| 4 | import sys, os | ||
| 5 | |||
| 6 | def main(): | ||
| 7 | for filename in sys.argv[1:]: | ||
| 8 | if os.path.isdir(filename): | ||
| 9 | print filename, "Directory!" | ||
| 10 | continue | ||
| 11 | data = open(filename, "rb").read() | ||
| 12 | if '\0' in data: | ||
| 13 | print filename, "Binary!" | ||
| 14 | continue | ||
| 15 | newdata = data.replace("\r\n", "\n") | ||
| 16 | if newdata != data: | ||
| 17 | print filename | ||
| 18 | f = open(filename, "wb") | ||
| 19 | f.write(newdata) | ||
| 20 | f.close() | ||
| 21 | |||
| 22 | if __name__ == '__main__': | ||
| 23 | main() | ||
| @@ -0,0 +1,72 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | """Print a list of files that are mentioned in CVS directories. | ||
| 4 | |||
| 5 | Usage: cvsfiles.py [-n file] [directory] ... | ||
| 6 | |||
| 7 | If the '-n file' option is given, only files under CVS that are newer | ||
| 8 | than the given file are printed; by default, all files under CVS are | ||
| 9 | printed. As a special case, if a file does not exist, it is always | ||
| 10 | printed. | ||
| 11 | """ | ||
| 12 | |||
| 13 | import os | ||
| 14 | import sys | ||
| 15 | import stat | ||
| 16 | import getopt | ||
| 17 | |||
| 18 | cutofftime = 0 | ||
| 19 | |||
| 20 | def main(): | ||
| 21 | try: | ||
| 22 | opts, args = getopt.getopt(sys.argv[1:], "n:") | ||
| 23 | except getopt.error, msg: | ||
| 24 | print msg | ||
| 25 | print __doc__, | ||
| 26 | return 1 | ||
| 27 | global cutofftime | ||
| 28 | newerfile = None | ||
| 29 | for o, a in opts: | ||
| 30 | if o == '-n': | ||
| 31 | cutofftime = getmtime(a) | ||
| 32 | if args: | ||
| 33 | for arg in args: | ||
| 34 | process(arg) | ||
| 35 | else: | ||
| 36 | process(".") | ||
| 37 | |||
| 38 | def process(dir): | ||
| 39 | cvsdir = 0 | ||
| 40 | subdirs = [] | ||
| 41 | names = os.listdir(dir) | ||
| 42 | for name in names: | ||
| 43 | fullname = os.path.join(dir, name) | ||
| 44 | if name == "CVS": | ||
| 45 | cvsdir = fullname | ||
| 46 | else: | ||
| 47 | if os.path.isdir(fullname): | ||
| 48 | if not os.path.islink(fullname): | ||
| 49 | subdirs.append(fullname) | ||
| 50 | if cvsdir: | ||
| 51 | entries = os.path.join(cvsdir, "Entries") | ||
| 52 | for e in open(entries).readlines(): | ||
| 53 | words = e.split('/') | ||
| 54 | if words[0] == '' and words[1:]: | ||
| 55 | name = words[1] | ||
| 56 | fullname = os.path.join(dir, name) | ||
| 57 | if cutofftime and getmtime(fullname) <= cutofftime: | ||
| 58 | pass | ||
| 59 | else: | ||
| 60 | print fullname | ||
| 61 | for sub in subdirs: | ||
| 62 | process(sub) | ||
| 63 | |||
| 64 | def getmtime(filename): | ||
| 65 | try: | ||
| 66 | st = os.stat(filename) | ||
| 67 | except os.error: | ||
| 68 | return 0 | ||
| 69 | return st[stat.ST_MTIME] | ||
| 70 | |||
| 71 | if __name__ == '__main__': | ||
| 72 | main() | ||
| @@ -0,0 +1,135 @@ | |||
| 1 | #!/usr/bin/env python | ||
| 2 | |||
| 3 | """ | ||
| 4 | Synopsis: %(prog)s [-h|-g|-b|-r|-a] dbfile [ picklefile ] | ||
| 5 | |||
| 6 | Convert the database file given on the command line to a pickle | ||
| 7 | representation. The optional flags indicate the type of the database: | ||
| 8 | |||
| 9 | -a - open using anydbm | ||
| 10 | -b - open as bsddb btree file | ||
| 11 | -d - open as dbm file | ||
| 12 | -g - open as gdbm file | ||
| 13 | -h - open as bsddb hash file | ||
| 14 | -r - open as bsddb recno file | ||
| 15 | |||
| 16 | The default is hash. If a pickle file is named it is opened for write | ||
| 17 | access (deleting any existing data). If no pickle file is named, the pickle | ||
| 18 | output is written to standard output. | ||
| 19 | |||
| 20 | """ | ||
| 21 | |||
| 22 | import getopt | ||
| 23 | try: | ||
| 24 | import bsddb | ||
| 25 | except ImportError: | ||
| 26 | bsddb = None | ||
| 27 | try: | ||
| 28 | import dbm | ||
| 29 | except ImportError: | ||
| 30 | dbm = None | ||
| 31 | try: | ||
| 32 | import gdbm | ||
| 33 | except ImportError: | ||
| 34 | gdbm = None | ||
| 35 | try: | ||
| 36 | import anydbm | ||
| 37 | except ImportError: | ||
| 38 | anydbm = None | ||
| 39 | import sys | ||
| 40 | try: | ||
| 41 | import cPickle as pickle | ||
| 42 | except ImportError: | ||
| 43 | import pickle | ||
| 44 | |||
| 45 | prog = sys.argv[0] | ||
| 46 | |||
| 47 | def usage(): | ||
| 48 | sys.stderr.write(__doc__ % globals()) | ||
| 49 | |||
| 50 | def main(args): | ||
| 51 | try: | ||
| 52 | opts, args = getopt.getopt(args, "hbrdag", | ||
| 53 | ["hash", "btree", "recno", "dbm", | ||
| 54 | "gdbm", "anydbm"]) | ||
| 55 | except getopt.error: | ||
| 56 | usage() | ||
| 57 | return 1 | ||
| 58 | |||
| 59 | if len(args) == 0 or len(args) > 2: | ||
| 60 | usage() | ||
| 61 | return 1 | ||
| 62 | elif len(args) == 1: | ||
| 63 | dbfile = args[0] | ||
| 64 | pfile = sys.stdout | ||
| 65 | else: | ||
| 66 | dbfile = args[0] | ||
| 67 | try: | ||
| 68 | pfile = open(args[1], 'wb') | ||
| 69 | except IOError: | ||
| 70 | sys.stderr.write("Unable to open %s\n" % args[1]) | ||
| 71 | return 1 | ||
| 72 | |||
| 73 | dbopen = None | ||
| 74 | for opt, arg in opts: | ||
| 75 | if opt in ("-h", "--hash"): | ||
| 76 | try: | ||
| 77 | dbopen = bsddb.hashopen | ||
| 78 | except AttributeError: | ||
| 79 | sys.stderr.write("bsddb module unavailable.\n") | ||
| 80 | return 1 | ||
| 81 | elif opt in ("-b", "--btree"): | ||
| 82 | try: | ||
| 83 | dbopen = bsddb.btopen | ||
| 84 | except AttributeError: | ||
| 85 | sys.stderr.write("bsddb module unavailable.\n") | ||
| 86 | return 1 | ||
| 87 | elif opt in ("-r", "--recno"): | ||
| 88 | try: | ||
| 89 | dbopen = bsddb.rnopen | ||
| 90 | except AttributeError: | ||
| 91 | sys.stderr.write("bsddb module unavailable.\n") | ||
| 92 | return 1 | ||
| 93 | elif opt in ("-a", "--anydbm"): | ||
| 94 | try: | ||
| 95 | dbopen = anydbm.open | ||
| 96 | except AttributeError: | ||
| 97 | sys.stderr.write("anydbm module unavailable.\n") | ||
| 98 | return 1 | ||
| 99 | elif opt in ("-g", "--gdbm"): | ||
| 100 | try: | ||
| 101 | dbopen = gdbm.open | ||
| 102 | except AttributeError: | ||
| 103 | sys.stderr.write("gdbm module unavailable.\n") | ||
| 104 | return 1 | ||
| 105 | elif opt in ("-d", "--dbm"): | ||
| 106 | try: | ||
| 107 | dbopen = dbm.open | ||
| 108 | except AttributeError: | ||
| 109 | sys.stderr.write("dbm module unavailable.\n") | ||
| 110 | return 1 | ||
| 111 | if dbopen is None: | ||
| 112 | if bsddb is None: | ||
| 113 | sys.stderr.write("bsddb module unavailable - ") | ||
| 114 | sys.stderr.write("must specify dbtype.\n") | ||
| 115 | return 1 | ||
| 116 | else: | ||
| 117 | dbopen = bsddb.hashopen | ||
| 118 | |||
| 119 | try: | ||
| 120 | db = dbopen(dbfile, 'r') | ||
| 121 | except bsddb.error: | ||
| 122 | sys.stderr.write("Unable to open %s. " % dbfile) | ||
| 123 | sys.stderr.write("Check for format or version mismatch.\n") | ||
| 124 | return 1 | ||
| 125 | |||
| 126 | for k in db.keys(): | ||
| 127 | pickle.dump((k, db[k]), pfile, 1==1) | ||
| 128 | |||
| 129 | db.close() | ||
| 130 | pfile.close() | ||
| 131 | |||
| 132 | return 0 | ||
| 133 | |||
| 134 | if __name__ == "__main__": | ||
| 135 | sys.exit(main(sys.argv[1:])) | ||
| @@ -0,0 +1,49 @@ | |||
| 1 | """ Command line interface to difflib.py providing diffs in four formats: | ||
| 2 | |||
| 3 | * ndiff: lists every line and highlights interline changes. | ||
| 4 | * context: highlights clusters of changes in a before/after format. | ||
| 5 | * unified: highlights clusters of changes in an inline format. | ||
| 6 | * html: generates side by side comparison with change highlights. | ||
| 7 | |||
| 8 | """ | ||
| 9 | |||
| 10 | import sys, os, time, difflib, optparse | ||
| 11 | |||
| 12 | def main(): | ||
| 13 | |||
| 14 | usage = "usage: %prog [options] fromfile tofile" | ||
| 15 | parser = optparse.OptionParser(usage) | ||
| 16 | parser.add_option("-c", action="store_true", default=False, help='Produce a context format diff (default)') | ||
| 17 | parser.add_option("-u", action="store_true", default=False, help='Produce a unified format diff') | ||
| 18 | parser.add_option("-m", action="store_true", default=False, help='Produce HTML side by side diff (can use -c and -l in conjunction)') | ||
| 19 | parser.add_option("-n", action="store_true", default=False, help='Produce a ndiff format diff') | ||
| 20 | parser.add_option("-l", "--lines", type="int", default=3, help='Set number of context lines (default 3)') | ||
| 21 | (options, args) = parser.parse_args() | ||
| 22 | |||
| 23 | if len(args) == 0: | ||
| 24 | parser.print_help() | ||
| 25 | sys.exit(1) | ||
| 26 | if len(args) != 2: | ||
| 27 | parser.error("need to specify both a fromfile and tofile") | ||
| 28 | |||
| 29 | n = options.lines | ||
| 30 | fromfile, tofile = args | ||
| 31 | |||
| 32 | fromdate = time.ctime(os.stat(fromfile).st_mtime) | ||
| 33 | todate = time.ctime(os.stat(tofile).st_mtime) | ||
| 34 | fromlines = open(fromfile, 'U').readlines() | ||
| 35 | tolines = open(tofile, 'U').readlines() | ||
| 36 | |||
| 37 | if options.u: | ||
| 38 | diff = difflib.unified_diff(fromlines, tolines, fromfile, tofile, fromdate, todate, n=n) | ||
| 39 | elif options.n: | ||
| 40 | diff = difflib.ndiff(fromlines, tolines) | ||
| 41 | elif options.m: | ||
| 42 | diff = difflib.HtmlDiff().make_file(fromlines,tolines,fromfile,tofile,context=options.c,numlines=n) | ||
| 43 | else: | ||
| 44 | diff = difflib.context_diff(fromlines, tolines, fromfile, tofile, fromdate, todate, n=n) | ||
| 45 | |||
| 46 | sys.stdout.writelines(diff) | ||
| 47 | |||
| 48 | if __name__ == '__main__': | ||
| 49 | main() | ||
| @@ -0,0 +1,54 @@ | |||
| 1 | Path: cwi.nl!sun4nl!mcsun!uunet!cs.utexas.edu!convex!usenet | ||
| 2 | From: tchrist@convex.COM (Tom Christiansen) | ||
| 3 | Newsgroups: comp.lang.perl | ||
| 4 | Subject: Re: The problems of Perl (Re: Question (silly?)) | ||
| 5 | Message-ID: <1992Jan17.053115.4220@convex.com> | ||
| 6 | Date: 17 Jan 92 05:31:15 GMT | ||
| 7 | References: <17458@ector.cs.purdue.edu> <1992Jan16.165347.25583@cherokee.uswest.com> <=#Hues+4@cs.psu.edu> | ||
| 8 | Sender: usenet@convex.com (news access account) | ||
| 9 | Reply-To: tchrist@convex.COM (Tom Christiansen) | ||
| 10 | Organization: CONVEX Realtime Development, Colorado Springs, CO | ||
| 11 | Lines: 83 | ||
| 12 | Nntp-Posting-Host: pixel.convex.com | ||
| 13 | |||
| 14 | From the keyboard of flee@cs.psu.edu (Felix Lee): | ||
| 15 | :And Perl is definitely awkward with data types. I haven't yet found a | ||
| 16 | :pleasant way of shoving non-trivial data types into Perl's grammar. | ||
| 17 | |||
| 18 | Yes, it's pretty aweful at that, alright. Sometimes I write perl programs | ||
| 19 | that need them, and sometimes it just takes a little creativity. But | ||
| 20 | sometimes it's not worth it. I actually wrote a C program the other day | ||
| 21 | (gasp) because I didn't want to deal with a game matrix with six links per node. | ||
| 22 | |||
| 23 | :Here's a very simple problem that's tricky to express in Perl: process | ||
| 24 | :the output of "du" to produce output that's indented to reflect the | ||
| 25 | :tree structure, and with each subtree sorted by size. Something like: | ||
| 26 | : 434 /etc | ||
| 27 | : | 344 . | ||
| 28 | : | 50 install | ||
| 29 | : | 35 uucp | ||
| 30 | : | 3 nserve | ||
| 31 | : | | 2 . | ||
| 32 | : | | 1 auth.info | ||
| 33 | : | 1 sm | ||
| 34 | : | 1 sm.bak | ||
| 35 | |||
| 36 | At first I thought I could just keep one local list around | ||
| 37 | at once, but this seems inherently recursive. Which means | ||
| 38 | I need an real recursive data structure. Maybe you could | ||
| 39 | do it with one of the %assoc arrays Larry uses in the begat | ||
| 40 | programs, but I broke down and got dirty. I think the hardest | ||
| 41 | part was matching Felix's desired output exactly. It's not | ||
| 42 | blazingly fast: I should probably inline the &childof routine, | ||
| 43 | but it *was* faster to write than I could have written the | ||
| 44 | equivalent C program. | ||
| 45 | |||
| 46 | |||
| 47 | --tom | ||
| 48 | |||
| 49 | -- | ||
| 50 | "GUIs normally make it simple to accomplish simple actions and impossible | ||
| 51 | to accomplish complex actions." --Doug Gwyn (22/Jun/91 in comp.unix.wizards) | ||
| 52 | |||
| 53 | Tom Christiansen tchrist@convex.com convex!tchrist | ||
| 54 | |||
| @@ -0,0 +1,60 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | # Format du output in a tree shape | ||
| 3 | |||
| 4 | import os, sys, errno | ||
| 5 | |||
| 6 | def main(): | ||
| 7 | p = os.popen('du ' + ' '.join(sys.argv[1:]), 'r') | ||
| 8 | total, d = None, {} | ||
| 9 | for line in p.readlines(): | ||
| 10 | i = 0 | ||
| 11 | while line[i] in '0123456789': i = i+1 | ||
| 12 | size = eval(line[:i]) | ||
| 13 | while line[i] in ' \t': i = i+1 | ||
| 14 | filename = line[i:-1] | ||
| 15 | comps = filename.split('/') | ||
| 16 | if comps[0] == '': comps[0] = '/' | ||
| 17 | if comps[len(comps)-1] == '': del comps[len(comps)-1] | ||
| 18 | total, d = store(size, comps, total, d) | ||
| 19 | try: | ||
| 20 | display(total, d) | ||
| 21 | except IOError, e: | ||
| 22 | if e.errno != errno.EPIPE: | ||
| 23 | raise | ||
| 24 | |||
| 25 | def store(size, comps, total, d): | ||
| 26 | if comps == []: | ||
| 27 | return size, d | ||
| 28 | if not d.has_key(comps[0]): | ||
| 29 | d[comps[0]] = None, {} | ||
| 30 | t1, d1 = d[comps[0]] | ||
| 31 | d[comps[0]] = store(size, comps[1:], t1, d1) | ||
| 32 | return total, d | ||
| 33 | |||
| 34 | def display(total, d): | ||
| 35 | show(total, d, '') | ||
| 36 | |||
| 37 | def show(total, d, prefix): | ||
| 38 | if not d: return | ||
| 39 | list = [] | ||
| 40 | sum = 0 | ||
| 41 | for key in d.keys(): | ||
| 42 | tsub, dsub = d[key] | ||
| 43 | list.append((tsub, key)) | ||
| 44 | if tsub is not None: sum = sum + tsub | ||
| 45 | ## if sum < total: | ||
| 46 | ## list.append((total - sum, os.curdir)) | ||
| 47 | list.sort() | ||
| 48 | list.reverse() | ||
| 49 | width = len(repr(list[0][0])) | ||
| 50 | for tsub, key in list: | ||
| 51 | if tsub is None: | ||
| 52 | psub = prefix | ||
| 53 | else: | ||
| 54 | print prefix + repr(tsub).rjust(width) + ' ' + key | ||
| 55 | psub = prefix + ' '*(width-1) + '|' + ' '*(len(key)+1) | ||
| 56 | if d.has_key(key): | ||
| 57 | show(tsub, d[key][1], psub) | ||
| 58 | |||
| 59 | if __name__ == '__main__': | ||
| 60 | main() | ||
| @@ -0,0 +1,56 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | """Create a TAGS file for Python programs, usable with GNU Emacs. | ||
| 3 | |||
| 4 | usage: eptags pyfiles... | ||
| 5 | |||
| 6 | The output TAGS file is usable with Emacs version 18, 19, 20. | ||
| 7 | Tagged are: | ||
| 8 | - functions (even inside other defs or classes) | ||
| 9 | - classes | ||
| 10 | |||
| 11 | eptags warns about files it cannot open. | ||
| 12 | eptags will not give warnings about duplicate tags. | ||
| 13 | |||
| 14 | BUGS: | ||
| 15 | Because of tag duplication (methods with the same name in different | ||
| 16 | classes), TAGS files are not very useful for most object-oriented | ||
| 17 | python projects. | ||
| 18 | """ | ||
| 19 | import sys,re | ||
| 20 | |||
| 21 | expr = r'^[ \t]*(def|class)[ \t]+([a-zA-Z_][a-zA-Z0-9_]*)[ \t]*[:\(]' | ||
| 22 | matcher = re.compile(expr) | ||
| 23 | |||
| 24 | def treat_file(filename, outfp): | ||
| 25 | """Append tags found in file named 'filename' to the open file 'outfp'""" | ||
| 26 | try: | ||
| 27 | fp = open(filename, 'r') | ||
| 28 | except: | ||
| 29 | sys.stderr.write('Cannot open %s\n'%filename) | ||
| 30 | return | ||
| 31 | charno = 0 | ||
| 32 | lineno = 0 | ||
| 33 | tags = [] | ||
| 34 | size = 0 | ||
| 35 | while 1: | ||
| 36 | line = fp.readline() | ||
| 37 | if not line: | ||
| 38 | break | ||
| 39 | lineno = lineno + 1 | ||
| 40 | m = matcher.search(line) | ||
| 41 | if m: | ||
| 42 | tag = m.group(0) + '\177%d,%d\n' % (lineno, charno) | ||
| 43 | tags.append(tag) | ||
| 44 | size = size + len(tag) | ||
| 45 | charno = charno + len(line) | ||
| 46 | outfp.write('\f\n%s,%d\n' % (filename,size)) | ||
| 47 | for tag in tags: | ||
| 48 | outfp.write(tag) | ||
| 49 | |||
| 50 | def main(): | ||
| 51 | outfp = open('TAGS', 'w') | ||
| 52 | for filename in sys.argv[1:]: | ||
| 53 | treat_file(filename, outfp) | ||
| 54 | |||
| 55 | if __name__=="__main__": | ||
| 56 | main() | ||
| @@ -0,0 +1,89 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | """finddiv - a grep-like tool that looks for division operators. | ||
| 4 | |||
| 5 | Usage: finddiv [-l] file_or_directory ... | ||
| 6 | |||
| 7 | For directory arguments, all files in the directory whose name ends in | ||
| 8 | .py are processed, and subdirectories are processed recursively. | ||
| 9 | |||
| 10 | This actually tokenizes the files to avoid false hits in comments or | ||
| 11 | strings literals. | ||
| 12 | |||
| 13 | By default, this prints all lines containing a / or /= operator, in | ||
| 14 | grep -n style. With the -l option specified, it prints the filename | ||
| 15 | of files that contain at least one / or /= operator. | ||
| 16 | """ | ||
| 17 | |||
| 18 | import os | ||
| 19 | import sys | ||
| 20 | import getopt | ||
| 21 | import tokenize | ||
| 22 | |||
| 23 | def main(): | ||
| 24 | try: | ||
| 25 | opts, args = getopt.getopt(sys.argv[1:], "lh") | ||
| 26 | except getopt.error, msg: | ||
| 27 | usage(msg) | ||
| 28 | return 2 | ||
| 29 | if not args: | ||
| 30 | usage("at least one file argument is required") | ||
| 31 | return 2 | ||
| 32 | listnames = 0 | ||
| 33 | for o, a in opts: | ||
| 34 | if o == "-h": | ||
| 35 | print __doc__ | ||
| 36 | return | ||
| 37 | if o == "-l": | ||
| 38 | listnames = 1 | ||
| 39 | exit = None | ||
| 40 | for filename in args: | ||
| 41 | x = process(filename, listnames) | ||
| 42 | exit = exit or x | ||
| 43 | return exit | ||
| 44 | |||
| 45 | def usage(msg): | ||
| 46 | sys.stderr.write("%s: %s\n" % (sys.argv[0], msg)) | ||
| 47 | sys.stderr.write("Usage: %s [-l] file ...\n" % sys.argv[0]) | ||
| 48 | sys.stderr.write("Try `%s -h' for more information.\n" % sys.argv[0]) | ||
| 49 | |||
| 50 | def process(filename, listnames): | ||
| 51 | if os.path.isdir(filename): | ||
| 52 | return processdir(filename, listnames) | ||
| 53 | try: | ||
| 54 | fp = open(filename) | ||
| 55 | except IOError, msg: | ||
| 56 | sys.stderr.write("Can't open: %s\n" % msg) | ||
| 57 | return 1 | ||
| 58 | g = tokenize.generate_tokens(fp.readline) | ||
| 59 | lastrow = None | ||
| 60 | for type, token, (row, col), end, line in g: | ||
| 61 | if token in ("/", "/="): | ||
| 62 | if listnames: | ||
| 63 | print filename | ||
| 64 | break | ||
| 65 | if row != lastrow: | ||
| 66 | lastrow = row | ||
| 67 | print "%s:%d:%s" % (filename, row, line), | ||
| 68 | fp.close() | ||
| 69 | |||
| 70 | def processdir(dir, listnames): | ||
| 71 | try: | ||
| 72 | names = os.listdir(dir) | ||
| 73 | except os.error, msg: | ||
| 74 | sys.stderr.write("Can't list directory: %s\n" % dir) | ||
| 75 | return 1 | ||
| 76 | files = [] | ||
| 77 | for name in names: | ||
| 78 | fn = os.path.join(dir, name) | ||
| 79 | if os.path.normcase(fn).endswith(".py") or os.path.isdir(fn): | ||
| 80 | files.append(fn) | ||
| 81 | files.sort(lambda a, b: cmp(os.path.normcase(a), os.path.normcase(b))) | ||
| 82 | exit = None | ||
| 83 | for fn in files: | ||
| 84 | x = process(fn, listnames) | ||
| 85 | exit = exit or x | ||
| 86 | return exit | ||
| 87 | |||
| 88 | if __name__ == "__main__": | ||
| 89 | sys.exit(main()) | ||
| @@ -0,0 +1,43 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # findlinksto | ||
| 4 | # | ||
| 5 | # find symbolic links to a path matching a regular expression | ||
| 6 | |||
| 7 | import os | ||
| 8 | import sys | ||
| 9 | import re | ||
| 10 | import getopt | ||
| 11 | |||
| 12 | def main(): | ||
| 13 | try: | ||
| 14 | opts, args = getopt.getopt(sys.argv[1:], '') | ||
| 15 | if len(args) < 2: | ||
| 16 | raise getopt.GetoptError('not enough arguments', None) | ||
| 17 | except getopt.GetoptError, msg: | ||
| 18 | sys.stdout = sys.stderr | ||
| 19 | print msg | ||
| 20 | print 'usage: findlinksto pattern directory ...' | ||
| 21 | sys.exit(2) | ||
| 22 | pat, dirs = args[0], args[1:] | ||
| 23 | prog = re.compile(pat) | ||
| 24 | for dirname in dirs: | ||
| 25 | os.path.walk(dirname, visit, prog) | ||
| 26 | |||
| 27 | def visit(prog, dirname, names): | ||
| 28 | if os.path.islink(dirname): | ||
| 29 | names[:] = [] | ||
| 30 | return | ||
| 31 | if os.path.ismount(dirname): | ||
| 32 | print 'descend into', dirname | ||
| 33 | for name in names: | ||
| 34 | name = os.path.join(dirname, name) | ||
| 35 | try: | ||
| 36 | linkto = os.readlink(name) | ||
| 37 | if prog.search(linkto) is not None: | ||
| 38 | print name, '->', linkto | ||
| 39 | except os.error: | ||
| 40 | pass | ||
| 41 | |||
| 42 | if __name__ == '__main__': | ||
| 43 | main() | ||
| @@ -0,0 +1,104 @@ | |||
| 1 | #!/usr/bin/env python | ||
| 2 | |||
| 3 | """List all those Python files that require a coding directive | ||
| 4 | |||
| 5 | Usage: nocoding.py dir1 [dir2...] | ||
| 6 | """ | ||
| 7 | |||
| 8 | __author__ = "Oleg Broytmann, Georg Brandl" | ||
| 9 | |||
| 10 | import sys, os, re, getopt | ||
| 11 | |||
| 12 | # our pysource module finds Python source files | ||
| 13 | try: | ||
| 14 | import pysource | ||
| 15 | except: | ||
| 16 | # emulate the module with a simple os.walk | ||
| 17 | class pysource: | ||
| 18 | has_python_ext = looks_like_python = can_be_compiled = None | ||
| 19 | def walk_python_files(self, paths, *args, **kwargs): | ||
| 20 | for path in paths: | ||
| 21 | if os.path.isfile(path): | ||
| 22 | yield path.endswith(".py") | ||
| 23 | elif os.path.isdir(path): | ||
| 24 | for root, dirs, files in os.walk(path): | ||
| 25 | for filename in files: | ||
| 26 | if filename.endswith(".py"): | ||
| 27 | yield os.path.join(root, filename) | ||
| 28 | pysource = pysource() | ||
| 29 | |||
| 30 | |||
| 31 | print >>sys.stderr, ("The pysource module is not available; " | ||
| 32 | "no sophisticated Python source file search will be done.") | ||
| 33 | |||
| 34 | |||
| 35 | decl_re = re.compile(r"coding[=:]\s*([-\w.]+)") | ||
| 36 | |||
| 37 | def get_declaration(line): | ||
| 38 | match = decl_re.search(line) | ||
| 39 | if match: | ||
| 40 | return match.group(1) | ||
| 41 | return '' | ||
| 42 | |||
| 43 | def has_correct_encoding(text, codec): | ||
| 44 | try: | ||
| 45 | unicode(text, codec) | ||
| 46 | except UnicodeDecodeError: | ||
| 47 | return False | ||
| 48 | else: | ||
| 49 | return True | ||
| 50 | |||
| 51 | def needs_declaration(fullpath): | ||
| 52 | try: | ||
| 53 | infile = open(fullpath, 'rU') | ||
| 54 | except IOError: # Oops, the file was removed - ignore it | ||
| 55 | return None | ||
| 56 | |||
| 57 | line1 = infile.readline() | ||
| 58 | line2 = infile.readline() | ||
| 59 | |||
| 60 | if get_declaration(line1) or get_declaration(line2): | ||
| 61 | # the file does have an encoding declaration, so trust it | ||
| 62 | infile.close() | ||
| 63 | return False | ||
| 64 | |||
| 65 | # check the whole file for non-ASCII characters | ||
| 66 | rest = infile.read() | ||
| 67 | infile.close() | ||
| 68 | |||
| 69 | if has_correct_encoding(line1+line2+rest, "ascii"): | ||
| 70 | return False | ||
| 71 | |||
| 72 | return True | ||
| 73 | |||
| 74 | |||
| 75 | usage = """Usage: %s [-cd] paths... | ||
| 76 | -c: recognize Python source files trying to compile them | ||
| 77 | -d: debug output""" % sys.argv[0] | ||
| 78 | |||
| 79 | try: | ||
| 80 | opts, args = getopt.getopt(sys.argv[1:], 'cd') | ||
| 81 | except getopt.error, msg: | ||
| 82 | print >>sys.stderr, msg | ||
| 83 | print >>sys.stderr, usage | ||
| 84 | sys.exit(1) | ||
| 85 | |||
| 86 | is_python = pysource.looks_like_python | ||
| 87 | debug = False | ||
| 88 | |||
| 89 | for o, a in opts: | ||
| 90 | if o == '-c': | ||
| 91 | is_python = pysource.can_be_compiled | ||
| 92 | elif o == '-d': | ||
| 93 | debug = True | ||
| 94 | |||
| 95 | if not args: | ||
| 96 | print >>sys.stderr, usage | ||
| 97 | sys.exit(1) | ||
| 98 | |||
| 99 | for fullpath in pysource.walk_python_files(args, is_python): | ||
| 100 | if debug: | ||
| 101 | print "Testing for coding: %s" % fullpath | ||
| 102 | result = needs_declaration(fullpath) | ||
| 103 | if result: | ||
| 104 | print fullpath | ||
| @@ -0,0 +1,314 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Perform massive identifier substitution on C source files. | ||
| 4 | # This actually tokenizes the files (to some extent) so it can | ||
| 5 | # avoid making substitutions inside strings or comments. | ||
| 6 | # Inside strings, substitutions are never made; inside comments, | ||
| 7 | # it is a user option (off by default). | ||
| 8 | # | ||
| 9 | # The substitutions are read from one or more files whose lines, | ||
| 10 | # when not empty, after stripping comments starting with #, | ||
| 11 | # must contain exactly two words separated by whitespace: the | ||
| 12 | # old identifier and its replacement. | ||
| 13 | # | ||
| 14 | # The option -r reverses the sense of the substitutions (this may be | ||
| 15 | # useful to undo a particular substitution). | ||
| 16 | # | ||
| 17 | # If the old identifier is prefixed with a '*' (with no intervening | ||
| 18 | # whitespace), then it will not be substituted inside comments. | ||
| 19 | # | ||
| 20 | # Command line arguments are files or directories to be processed. | ||
| 21 | # Directories are searched recursively for files whose name looks | ||
| 22 | # like a C file (ends in .h or .c). The special filename '-' means | ||
| 23 | # operate in filter mode: read stdin, write stdout. | ||
| 24 | # | ||
| 25 | # Symbolic links are always ignored (except as explicit directory | ||
| 26 | # arguments). | ||
| 27 | # | ||
| 28 | # The original files are kept as back-up with a "~" suffix. | ||
| 29 | # | ||
| 30 | # Changes made are reported to stdout in a diff-like format. | ||
| 31 | # | ||
| 32 | # NB: by changing only the function fixline() you can turn this | ||
| 33 | # into a program for different changes to C source files; by | ||
| 34 | # changing the function wanted() you can make a different selection of | ||
| 35 | # files. | ||
| 36 | |||
| 37 | import sys | ||
| 38 | import re | ||
| 39 | import os | ||
| 40 | from stat import * | ||
| 41 | import getopt | ||
| 42 | |||
| 43 | err = sys.stderr.write | ||
| 44 | dbg = err | ||
| 45 | rep = sys.stdout.write | ||
| 46 | |||
| 47 | def usage(): | ||
| 48 | progname = sys.argv[0] | ||
| 49 | err('Usage: ' + progname + | ||
| 50 | ' [-c] [-r] [-s file] ... file-or-directory ...\n') | ||
| 51 | err('\n') | ||
| 52 | err('-c : substitute inside comments\n') | ||
| 53 | err('-r : reverse direction for following -s options\n') | ||
| 54 | err('-s substfile : add a file of substitutions\n') | ||
| 55 | err('\n') | ||
| 56 | err('Each non-empty non-comment line in a substitution file must\n') | ||
| 57 | err('contain exactly two words: an identifier and its replacement.\n') | ||
| 58 | err('Comments start with a # character and end at end of line.\n') | ||
| 59 | err('If an identifier is preceded with a *, it is not substituted\n') | ||
| 60 | err('inside a comment even when -c is specified.\n') | ||
| 61 | |||
| 62 | def main(): | ||
| 63 | try: | ||
| 64 | opts, args = getopt.getopt(sys.argv[1:], 'crs:') | ||
| 65 | except getopt.error, msg: | ||
| 66 | err('Options error: ' + str(msg) + '\n') | ||
| 67 | usage() | ||
| 68 | sys.exit(2) | ||
| 69 | bad = 0 | ||
| 70 | if not args: # No arguments | ||
| 71 | usage() | ||
| 72 | sys.exit(2) | ||
| 73 | for opt, arg in opts: | ||
| 74 | if opt == '-c': | ||
| 75 | setdocomments() | ||
| 76 | if opt == '-r': | ||
| 77 | setreverse() | ||
| 78 | if opt == '-s': | ||
| 79 | addsubst(arg) | ||
| 80 | for arg in args: | ||
| 81 | if os.path.isdir(arg): | ||
| 82 | if recursedown(arg): bad = 1 | ||
| 83 | elif os.path.islink(arg): | ||
| 84 | err(arg + ': will not process symbolic links\n') | ||
| 85 | bad = 1 | ||
| 86 | else: | ||
| 87 | if fix(arg): bad = 1 | ||
| 88 | sys.exit(bad) | ||
| 89 | |||
| 90 | # Change this regular expression to select a different set of files | ||
| 91 | Wanted = '^[a-zA-Z0-9_]+\.[ch]$' | ||
| 92 | def wanted(name): | ||
| 93 | return re.match(Wanted, name) >= 0 | ||
| 94 | |||
| 95 | def recursedown(dirname): | ||
| 96 | dbg('recursedown(%r)\n' % (dirname,)) | ||
| 97 | bad = 0 | ||
| 98 | try: | ||
| 99 | names = os.listdir(dirname) | ||
| 100 | except os.error, msg: | ||
| 101 | err(dirname + ': cannot list directory: ' + str(msg) + '\n') | ||
| 102 | return 1 | ||
| 103 | names.sort() | ||
| 104 | subdirs = [] | ||
| 105 | for name in names: | ||
| 106 | if name in (os.curdir, os.pardir): continue | ||
| 107 | fullname = os.path.join(dirname, name) | ||
| 108 | if os.path.islink(fullname): pass | ||
| 109 | elif os.path.isdir(fullname): | ||
| 110 | subdirs.append(fullname) | ||
| 111 | elif wanted(name): | ||
| 112 | if fix(fullname): bad = 1 | ||
| 113 | for fullname in subdirs: | ||
| 114 | if recursedown(fullname): bad = 1 | ||
| 115 | return bad | ||
| 116 | |||
| 117 | def fix(filename): | ||
| 118 | ## dbg('fix(%r)\n' % (filename,)) | ||
| 119 | if filename == '-': | ||
| 120 | # Filter mode | ||
| 121 | f = sys.stdin | ||
| 122 | g = sys.stdout | ||
| 123 | else: | ||
| 124 | # File replacement mode | ||
| 125 | try: | ||
| 126 | f = open(filename, 'r') | ||
| 127 | except IOError, msg: | ||
| 128 | err(filename + ': cannot open: ' + str(msg) + '\n') | ||
| 129 | return 1 | ||
| 130 | head, tail = os.path.split(filename) | ||
| 131 | tempname = os.path.join(head, '@' + tail) | ||
| 132 | g = None | ||
| 133 | # If we find a match, we rewind the file and start over but | ||
| 134 | # now copy everything to a temp file. | ||
| 135 | lineno = 0 | ||
| 136 | initfixline() | ||
| 137 | while 1: | ||
| 138 | line = f.readline() | ||
| 139 | if not line: break | ||
| 140 | lineno = lineno + 1 | ||
| 141 | while line[-2:] == '\\\n': | ||
| 142 | nextline = f.readline() | ||
| 143 | if not nextline: break | ||
| 144 | line = line + nextline | ||
| 145 | lineno = lineno + 1 | ||
| 146 | newline = fixline(line) | ||
| 147 | if newline != line: | ||
| 148 | if g is None: | ||
| 149 | try: | ||
| 150 | g = open(tempname, 'w') | ||
| 151 | except IOError, msg: | ||
| 152 | f.close() | ||
| 153 | err(tempname+': cannot create: '+ | ||
| 154 | str(msg)+'\n') | ||
| 155 | return 1 | ||
| 156 | f.seek(0) | ||
| 157 | lineno = 0 | ||
| 158 | initfixline() | ||
| 159 | rep(filename + ':\n') | ||
| 160 | continue # restart from the beginning | ||
| 161 | rep(repr(lineno) + '\n') | ||
| 162 | rep('< ' + line) | ||
| 163 | rep('> ' + newline) | ||
| 164 | if g is not None: | ||
| 165 | g.write(newline) | ||
| 166 | |||
| 167 | # End of file | ||
| 168 | if filename == '-': return 0 # Done in filter mode | ||
| 169 | f.close() | ||
| 170 | if not g: return 0 # No changes | ||
| 171 | |||
| 172 | # Finishing touch -- move files | ||
| 173 | |||
| 174 | # First copy the file's mode to the temp file | ||
| 175 | try: | ||
| 176 | statbuf = os.stat(filename) | ||
| 177 | os.chmod(tempname, statbuf[ST_MODE] & 07777) | ||
| 178 | except os.error, msg: | ||
| 179 | err(tempname + ': warning: chmod failed (' + str(msg) + ')\n') | ||
| 180 | # Then make a backup of the original file as filename~ | ||
| 181 | try: | ||
| 182 | os.rename(filename, filename + '~') | ||
| 183 | except os.error, msg: | ||
| 184 | err(filename + ': warning: backup failed (' + str(msg) + ')\n') | ||
| 185 | # Now move the temp file to the original file | ||
| 186 | try: | ||
| 187 | os.rename(tempname, filename) | ||
| 188 | except os.error, msg: | ||
| 189 | err(filename + ': rename failed (' + str(msg) + ')\n') | ||
| 190 | return 1 | ||
| 191 | # Return succes | ||
| 192 | return 0 | ||
| 193 | |||
| 194 | # Tokenizing ANSI C (partly) | ||
| 195 | |||
| 196 | Identifier = '\(struct \)?[a-zA-Z_][a-zA-Z0-9_]+' | ||
| 197 | String = '"\([^\n\\"]\|\\\\.\)*"' | ||
| 198 | Char = '\'\([^\n\\\']\|\\\\.\)*\'' | ||
| 199 | CommentStart = '/\*' | ||
| 200 | CommentEnd = '\*/' | ||
| 201 | |||
| 202 | Hexnumber = '0[xX][0-9a-fA-F]*[uUlL]*' | ||
| 203 | Octnumber = '0[0-7]*[uUlL]*' | ||
| 204 | Decnumber = '[1-9][0-9]*[uUlL]*' | ||
| 205 | Intnumber = Hexnumber + '\|' + Octnumber + '\|' + Decnumber | ||
| 206 | Exponent = '[eE][-+]?[0-9]+' | ||
| 207 | Pointfloat = '\([0-9]+\.[0-9]*\|\.[0-9]+\)\(' + Exponent + '\)?' | ||
| 208 | Expfloat = '[0-9]+' + Exponent | ||
| 209 | Floatnumber = Pointfloat + '\|' + Expfloat | ||
| 210 | Number = Floatnumber + '\|' + Intnumber | ||
| 211 | |||
| 212 | # Anything else is an operator -- don't list this explicitly because of '/*' | ||
| 213 | |||
| 214 | OutsideComment = (Identifier, Number, String, Char, CommentStart) | ||
| 215 | OutsideCommentPattern = '(' + '|'.join(OutsideComment) + ')' | ||
| 216 | OutsideCommentProgram = re.compile(OutsideCommentPattern) | ||
| 217 | |||
| 218 | InsideComment = (Identifier, Number, CommentEnd) | ||
| 219 | InsideCommentPattern = '(' + '|'.join(InsideComment) + ')' | ||
| 220 | InsideCommentProgram = re.compile(InsideCommentPattern) | ||
| 221 | |||
| 222 | def initfixline(): | ||
| 223 | global Program | ||
| 224 | Program = OutsideCommentProgram | ||
| 225 | |||
| 226 | def fixline(line): | ||
| 227 | global Program | ||
| 228 | ## print '-->', repr(line) | ||
| 229 | i = 0 | ||
| 230 | while i < len(line): | ||
| 231 | i = Program.search(line, i) | ||
| 232 | if i < 0: break | ||
| 233 | found = Program.group(0) | ||
| 234 | ## if Program is InsideCommentProgram: print '...', | ||
| 235 | ## else: print ' ', | ||
| 236 | ## print found | ||
| 237 | if len(found) == 2: | ||
| 238 | if found == '/*': | ||
| 239 | Program = InsideCommentProgram | ||
| 240 | elif found == '*/': | ||
| 241 | Program = OutsideCommentProgram | ||
| 242 | n = len(found) | ||
| 243 | if Dict.has_key(found): | ||
| 244 | subst = Dict[found] | ||
| 245 | if Program is InsideCommentProgram: | ||
| 246 | if not Docomments: | ||
| 247 | print 'Found in comment:', found | ||
| 248 | i = i + n | ||
| 249 | continue | ||
| 250 | if NotInComment.has_key(found): | ||
| 251 | ## print 'Ignored in comment:', | ||
| 252 | ## print found, '-->', subst | ||
| 253 | ## print 'Line:', line, | ||
| 254 | subst = found | ||
| 255 | ## else: | ||
| 256 | ## print 'Substituting in comment:', | ||
| 257 | ## print found, '-->', subst | ||
| 258 | ## print 'Line:', line, | ||
| 259 | line = line[:i] + subst + line[i+n:] | ||
| 260 | n = len(subst) | ||
| 261 | i = i + n | ||
| 262 | return line | ||
| 263 | |||
| 264 | Docomments = 0 | ||
| 265 | def setdocomments(): | ||
| 266 | global Docomments | ||
| 267 | Docomments = 1 | ||
| 268 | |||
| 269 | Reverse = 0 | ||
| 270 | def setreverse(): | ||
| 271 | global Reverse | ||
| 272 | Reverse = (not Reverse) | ||
| 273 | |||
| 274 | Dict = {} | ||
| 275 | NotInComment = {} | ||
| 276 | def addsubst(substfile): | ||
| 277 | try: | ||
| 278 | fp = open(substfile, 'r') | ||
| 279 | except IOError, msg: | ||
| 280 | err(substfile + ': cannot read substfile: ' + str(msg) + '\n') | ||
| 281 | sys.exit(1) | ||
| 282 | lineno = 0 | ||
| 283 | while 1: | ||
| 284 | line = fp.readline() | ||
| 285 | if not line: break | ||
| 286 | lineno = lineno + 1 | ||
| 287 | try: | ||
| 288 | i = line.index('#') | ||
| 289 | except ValueError: | ||
| 290 | i = -1 # Happens to delete trailing \n | ||
| 291 | words = line[:i].split() | ||
| 292 | if not words: continue | ||
| 293 | if len(words) == 3 and words[0] == 'struct': | ||
| 294 | words[:2] = [words[0] + ' ' + words[1]] | ||
| 295 | elif len(words) <> 2: | ||
| 296 | err(substfile + '%s:%r: warning: bad line: %r' % (substfile, lineno, line)) | ||
| 297 | continue | ||
| 298 | if Reverse: | ||
| 299 | [value, key] = words | ||
| 300 | else: | ||
| 301 | [key, value] = words | ||
| 302 | if value[0] == '*': | ||
| 303 | value = value[1:] | ||
| 304 | if key[0] == '*': | ||
| 305 | key = key[1:] | ||
| 306 | NotInComment[key] = value | ||
| 307 | if Dict.has_key(key): | ||
| 308 | err('%s:%r: warning: overriding: %r %r\n' % (substfile, lineno, key, value)) | ||
| 309 | err('%s:%r: warning: previous: %r\n' % (substfile, lineno, Dict[key])) | ||
| 310 | Dict[key] = value | ||
| 311 | fp.close() | ||
| 312 | |||
| 313 | if __name__ == '__main__': | ||
| 314 | main() | ||
| @@ -0,0 +1,380 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | """fixdiv - tool to fix division operators. | ||
| 4 | |||
| 5 | To use this tool, first run `python -Qwarnall yourscript.py 2>warnings'. | ||
| 6 | This runs the script `yourscript.py' while writing warning messages | ||
| 7 | about all uses of the classic division operator to the file | ||
| 8 | `warnings'. The warnings look like this: | ||
| 9 | |||
| 10 | <file>:<line>: DeprecationWarning: classic <type> division | ||
| 11 | |||
| 12 | The warnings are written to stderr, so you must use `2>' for the I/O | ||
| 13 | redirect. I know of no way to redirect stderr on Windows in a DOS | ||
| 14 | box, so you will have to modify the script to set sys.stderr to some | ||
| 15 | kind of log file if you want to do this on Windows. | ||
| 16 | |||
| 17 | The warnings are not limited to the script; modules imported by the | ||
| 18 | script may also trigger warnings. In fact a useful technique is to | ||
| 19 | write a test script specifically intended to exercise all code in a | ||
| 20 | particular module or set of modules. | ||
| 21 | |||
| 22 | Then run `python fixdiv.py warnings'. This first reads the warnings, | ||
| 23 | looking for classic division warnings, and sorts them by file name and | ||
| 24 | line number. Then, for each file that received at least one warning, | ||
| 25 | it parses the file and tries to match the warnings up to the division | ||
| 26 | operators found in the source code. If it is successful, it writes | ||
| 27 | its findings to stdout, preceded by a line of dashes and a line of the | ||
| 28 | form: | ||
| 29 | |||
| 30 | Index: <file> | ||
| 31 | |||
| 32 | If the only findings found are suggestions to change a / operator into | ||
| 33 | a // operator, the output is acceptable input for the Unix 'patch' | ||
| 34 | program. | ||
| 35 | |||
| 36 | Here are the possible messages on stdout (N stands for a line number): | ||
| 37 | |||
| 38 | - A plain-diff-style change ('NcN', a line marked by '<', a line | ||
| 39 | containing '---', and a line marked by '>'): | ||
| 40 | |||
| 41 | A / operator was found that should be changed to //. This is the | ||
| 42 | recommendation when only int and/or long arguments were seen. | ||
| 43 | |||
| 44 | - 'True division / operator at line N' and a line marked by '=': | ||
| 45 | |||
| 46 | A / operator was found that can remain unchanged. This is the | ||
| 47 | recommendation when only float and/or complex arguments were seen. | ||
| 48 | |||
| 49 | - 'Ambiguous / operator (..., ...) at line N', line marked by '?': | ||
| 50 | |||
| 51 | A / operator was found for which int or long as well as float or | ||
| 52 | complex arguments were seen. This is highly unlikely; if it occurs, | ||
| 53 | you may have to restructure the code to keep the classic semantics, | ||
| 54 | or maybe you don't care about the classic semantics. | ||
| 55 | |||
| 56 | - 'No conclusive evidence on line N', line marked by '*': | ||
| 57 | |||
| 58 | A / operator was found for which no warnings were seen. This could | ||
| 59 | be code that was never executed, or code that was only executed | ||
| 60 | with user-defined objects as arguments. You will have to | ||
| 61 | investigate further. Note that // can be overloaded separately from | ||
| 62 | /, using __floordiv__. True division can also be separately | ||
| 63 | overloaded, using __truediv__. Classic division should be the same | ||
| 64 | as either of those. (XXX should I add a warning for division on | ||
| 65 | user-defined objects, to disambiguate this case from code that was | ||
| 66 | never executed?) | ||
| 67 | |||
| 68 | - 'Phantom ... warnings for line N', line marked by '*': | ||
| 69 | |||
| 70 | A warning was seen for a line not containing a / operator. The most | ||
| 71 | likely cause is a warning about code executed by 'exec' or eval() | ||
| 72 | (see note below), or an indirect invocation of the / operator, for | ||
| 73 | example via the div() function in the operator module. It could | ||
| 74 | also be caused by a change to the file between the time the test | ||
| 75 | script was run to collect warnings and the time fixdiv was run. | ||
| 76 | |||
| 77 | - 'More than one / operator in line N'; or | ||
| 78 | 'More than one / operator per statement in lines N-N': | ||
| 79 | |||
| 80 | The scanner found more than one / operator on a single line, or in a | ||
| 81 | statement split across multiple lines. Because the warnings | ||
| 82 | framework doesn't (and can't) show the offset within the line, and | ||
| 83 | the code generator doesn't always give the correct line number for | ||
| 84 | operations in a multi-line statement, we can't be sure whether all | ||
| 85 | operators in the statement were executed. To be on the safe side, | ||
| 86 | by default a warning is issued about this case. In practice, these | ||
| 87 | cases are usually safe, and the -m option suppresses these warning. | ||
| 88 | |||
| 89 | - 'Can't find the / operator in line N', line marked by '*': | ||
| 90 | |||
| 91 | This really shouldn't happen. It means that the tokenize module | ||
| 92 | reported a '/' operator but the line it returns didn't contain a '/' | ||
| 93 | character at the indicated position. | ||
| 94 | |||
| 95 | - 'Bad warning for line N: XYZ', line marked by '*': | ||
| 96 | |||
| 97 | This really shouldn't happen. It means that a 'classic XYZ | ||
| 98 | division' warning was read with XYZ being something other than | ||
| 99 | 'int', 'long', 'float', or 'complex'. | ||
| 100 | |||
| 101 | Notes: | ||
| 102 | |||
| 103 | - The augmented assignment operator /= is handled the same way as the | ||
| 104 | / operator. | ||
| 105 | |||
| 106 | - This tool never looks at the // operator; no warnings are ever | ||
| 107 | generated for use of this operator. | ||
| 108 | |||
| 109 | - This tool never looks at the / operator when a future division | ||
| 110 | statement is in effect; no warnings are generated in this case, and | ||
| 111 | because the tool only looks at files for which at least one classic | ||
| 112 | division warning was seen, it will never look at files containing a | ||
| 113 | future division statement. | ||
| 114 | |||
| 115 | - Warnings may be issued for code not read from a file, but executed | ||
| 116 | using an exec statement or the eval() function. These may have | ||
| 117 | <string> in the filename position, in which case the fixdiv script | ||
| 118 | will attempt and fail to open a file named '<string>' and issue a | ||
| 119 | warning about this failure; or these may be reported as 'Phantom' | ||
| 120 | warnings (see above). You're on your own to deal with these. You | ||
| 121 | could make all recommended changes and add a future division | ||
| 122 | statement to all affected files, and then re-run the test script; it | ||
| 123 | should not issue any warnings. If there are any, and you have a | ||
| 124 | hard time tracking down where they are generated, you can use the | ||
| 125 | -Werror option to force an error instead of a first warning, | ||
| 126 | generating a traceback. | ||
| 127 | |||
| 128 | - The tool should be run from the same directory as that from which | ||
| 129 | the original script was run, otherwise it won't be able to open | ||
| 130 | files given by relative pathnames. | ||
| 131 | """ | ||
| 132 | |||
| 133 | import sys | ||
| 134 | import getopt | ||
| 135 | import re | ||
| 136 | import tokenize | ||
| 137 | |||
| 138 | multi_ok = 0 | ||
| 139 | |||
| 140 | def main(): | ||
| 141 | try: | ||
| 142 | opts, args = getopt.getopt(sys.argv[1:], "hm") | ||
| 143 | except getopt.error, msg: | ||
| 144 | usage(msg) | ||
| 145 | return 2 | ||
| 146 | for o, a in opts: | ||
| 147 | if o == "-h": | ||
| 148 | print __doc__ | ||
| 149 | return | ||
| 150 | if o == "-m": | ||
| 151 | global multi_ok | ||
| 152 | multi_ok = 1 | ||
| 153 | if not args: | ||
| 154 | usage("at least one file argument is required") | ||
| 155 | return 2 | ||
| 156 | if args[1:]: | ||
| 157 | sys.stderr.write("%s: extra file arguments ignored\n", sys.argv[0]) | ||
| 158 | warnings = readwarnings(args[0]) | ||
| 159 | if warnings is None: | ||
| 160 | return 1 | ||
| 161 | files = warnings.keys() | ||
| 162 | if not files: | ||
| 163 | print "No classic division warnings read from", args[0] | ||
| 164 | return | ||
| 165 | files.sort() | ||
| 166 | exit = None | ||
| 167 | for filename in files: | ||
| 168 | x = process(filename, warnings[filename]) | ||
| 169 | exit = exit or x | ||
| 170 | return exit | ||
| 171 | |||
| 172 | def usage(msg): | ||
| 173 | sys.stderr.write("%s: %s\n" % (sys.argv[0], msg)) | ||
| 174 | sys.stderr.write("Usage: %s [-m] warnings\n" % sys.argv[0]) | ||
| 175 | sys.stderr.write("Try `%s -h' for more information.\n" % sys.argv[0]) | ||
| 176 | |||
| 177 | PATTERN = ("^(.+?):(\d+): DeprecationWarning: " | ||
| 178 | "classic (int|long|float|complex) division$") | ||
| 179 | |||
| 180 | def readwarnings(warningsfile): | ||
| 181 | prog = re.compile(PATTERN) | ||
| 182 | try: | ||
| 183 | f = open(warningsfile) | ||
| 184 | except IOError, msg: | ||
| 185 | sys.stderr.write("can't open: %s\n" % msg) | ||
| 186 | return | ||
| 187 | warnings = {} | ||
| 188 | while 1: | ||
| 189 | line = f.readline() | ||
| 190 | if not line: | ||
| 191 | break | ||
| 192 | m = prog.match(line) | ||
| 193 | if not m: | ||
| 194 | if line.find("division") >= 0: | ||
| 195 | sys.stderr.write("Warning: ignored input " + line) | ||
| 196 | continue | ||
| 197 | filename, lineno, what = m.groups() | ||
| 198 | list = warnings.get(filename) | ||
| 199 | if list is None: | ||
| 200 | warnings[filename] = list = [] | ||
| 201 | list.append((int(lineno), intern(what))) | ||
| 202 | f.close() | ||
| 203 | return warnings | ||
| 204 | |||
| 205 | def process(filename, list): | ||
| 206 | print "-"*70 | ||
| 207 | assert list # if this fails, readwarnings() is broken | ||
| 208 | try: | ||
| 209 | fp = open(filename) | ||
| 210 | except IOError, msg: | ||
| 211 | sys.stderr.write("can't open: %s\n" % msg) | ||
| 212 | return 1 | ||
| 213 | print "Index:", filename | ||
| 214 | f = FileContext(fp) | ||
| 215 | list.sort() | ||
| 216 | index = 0 # list[:index] has been processed, list[index:] is still to do | ||
| 217 | g = tokenize.generate_tokens(f.readline) | ||
| 218 | while 1: | ||
| 219 | startlineno, endlineno, slashes = lineinfo = scanline(g) | ||
| 220 | if startlineno is None: | ||
| 221 | break | ||
| 222 | assert startlineno <= endlineno is not None | ||
| 223 | orphans = [] | ||
| 224 | while index < len(list) and list[index][0] < startlineno: | ||
| 225 | orphans.append(list[index]) | ||
| 226 | index += 1 | ||
| 227 | if orphans: | ||
| 228 | reportphantomwarnings(orphans, f) | ||
| 229 | warnings = [] | ||
| 230 | while index < len(list) and list[index][0] <= endlineno: | ||
| 231 | warnings.append(list[index]) | ||
| 232 | index += 1 | ||
| 233 | if not slashes and not warnings: | ||
| 234 | pass | ||
| 235 | elif slashes and not warnings: | ||
| 236 | report(slashes, "No conclusive evidence") | ||
| 237 | elif warnings and not slashes: | ||
| 238 | reportphantomwarnings(warnings, f) | ||
| 239 | else: | ||
| 240 | if len(slashes) > 1: | ||
| 241 | if not multi_ok: | ||
| 242 | rows = [] | ||
| 243 | lastrow = None | ||
| 244 | for (row, col), line in slashes: | ||
| 245 | if row == lastrow: | ||
| 246 | continue | ||
| 247 | rows.append(row) | ||
| 248 | lastrow = row | ||
| 249 | assert rows | ||
| 250 | if len(rows) == 1: | ||
| 251 | print "*** More than one / operator in line", rows[0] | ||
| 252 | else: | ||
| 253 | print "*** More than one / operator per statement", | ||
| 254 | print "in lines %d-%d" % (rows[0], rows[-1]) | ||
| 255 | intlong = [] | ||
| 256 | floatcomplex = [] | ||
| 257 | bad = [] | ||
| 258 | for lineno, what in warnings: | ||
| 259 | if what in ("int", "long"): | ||
| 260 | intlong.append(what) | ||
| 261 | elif what in ("float", "complex"): | ||
| 262 | floatcomplex.append(what) | ||
| 263 | else: | ||
| 264 | bad.append(what) | ||
| 265 | lastrow = None | ||
| 266 | for (row, col), line in slashes: | ||
| 267 | if row == lastrow: | ||
| 268 | continue | ||
| 269 | lastrow = row | ||
| 270 | line = chop(line) | ||
| 271 | if line[col:col+1] != "/": | ||
| 272 | print "*** Can't find the / operator in line %d:" % row | ||
| 273 | print "*", line | ||
| 274 | continue | ||
| 275 | if bad: | ||
| 276 | print "*** Bad warning for line %d:" % row, bad | ||
| 277 | print "*", line | ||
| 278 | elif intlong and not floatcomplex: | ||
| 279 | print "%dc%d" % (row, row) | ||
| 280 | print "<", line | ||
| 281 | print "---" | ||
| 282 | print ">", line[:col] + "/" + line[col:] | ||
| 283 | elif floatcomplex and not intlong: | ||
| 284 | print "True division / operator at line %d:" % row | ||
| 285 | print "=", line | ||
| 286 | elif intlong and floatcomplex: | ||
| 287 | print "*** Ambiguous / operator (%s, %s) at line %d:" % ( | ||
| 288 | "|".join(intlong), "|".join(floatcomplex), row) | ||
| 289 | print "?", line | ||
| 290 | fp.close() | ||
| 291 | |||
| 292 | def reportphantomwarnings(warnings, f): | ||
| 293 | blocks = [] | ||
| 294 | lastrow = None | ||
| 295 | lastblock = None | ||
| 296 | for row, what in warnings: | ||
| 297 | if row != lastrow: | ||
| 298 | lastblock = [row] | ||
| 299 | blocks.append(lastblock) | ||
| 300 | lastblock.append(what) | ||
| 301 | for block in blocks: | ||
| 302 | row = block[0] | ||
| 303 | whats = "/".join(block[1:]) | ||
| 304 | print "*** Phantom %s warnings for line %d:" % (whats, row) | ||
| 305 | f.report(row, mark="*") | ||
| 306 | |||
| 307 | def report(slashes, message): | ||
| 308 | lastrow = None | ||
| 309 | for (row, col), line in slashes: | ||
| 310 | if row != lastrow: | ||
| 311 | print "*** %s on line %d:" % (message, row) | ||
| 312 | print "*", chop(line) | ||
| 313 | lastrow = row | ||
| 314 | |||
| 315 | class FileContext: | ||
| 316 | def __init__(self, fp, window=5, lineno=1): | ||
| 317 | self.fp = fp | ||
| 318 | self.window = 5 | ||
| 319 | self.lineno = 1 | ||
| 320 | self.eoflookahead = 0 | ||
| 321 | self.lookahead = [] | ||
| 322 | self.buffer = [] | ||
| 323 | def fill(self): | ||
| 324 | while len(self.lookahead) < self.window and not self.eoflookahead: | ||
| 325 | line = self.fp.readline() | ||
| 326 | if not line: | ||
| 327 | self.eoflookahead = 1 | ||
| 328 | break | ||
| 329 | self.lookahead.append(line) | ||
| 330 | def readline(self): | ||
| 331 | self.fill() | ||
| 332 | if not self.lookahead: | ||
| 333 | return "" | ||
| 334 | line = self.lookahead.pop(0) | ||
| 335 | self.buffer.append(line) | ||
| 336 | self.lineno += 1 | ||
| 337 | return line | ||
| 338 | def truncate(self): | ||
| 339 | del self.buffer[-window:] | ||
| 340 | def __getitem__(self, index): | ||
| 341 | self.fill() | ||
| 342 | bufstart = self.lineno - len(self.buffer) | ||
| 343 | lookend = self.lineno + len(self.lookahead) | ||
| 344 | if bufstart <= index < self.lineno: | ||
| 345 | return self.buffer[index - bufstart] | ||
| 346 | if self.lineno <= index < lookend: | ||
| 347 | return self.lookahead[index - self.lineno] | ||
| 348 | raise KeyError | ||
| 349 | def report(self, first, last=None, mark="*"): | ||
| 350 | if last is None: | ||
| 351 | last = first | ||
| 352 | for i in range(first, last+1): | ||
| 353 | try: | ||
| 354 | line = self[first] | ||
| 355 | except KeyError: | ||
| 356 | line = "<missing line>" | ||
| 357 | print mark, chop(line) | ||
| 358 | |||
| 359 | def scanline(g): | ||
| 360 | slashes = [] | ||
| 361 | startlineno = None | ||
| 362 | endlineno = None | ||
| 363 | for type, token, start, end, line in g: | ||
| 364 | endlineno = end[0] | ||
| 365 | if startlineno is None: | ||
| 366 | startlineno = endlineno | ||
| 367 | if token in ("/", "/="): | ||
| 368 | slashes.append((start, line)) | ||
| 369 | if type == tokenize.NEWLINE: | ||
| 370 | break | ||
| 371 | return startlineno, endlineno, slashes | ||
| 372 | |||
| 373 | def chop(line): | ||
| 374 | if line.endswith("\n"): | ||
| 375 | return line[:-1] | ||
| 376 | else: | ||
| 377 | return line | ||
| 378 | |||
| 379 | if __name__ == "__main__": | ||
| 380 | sys.exit(main()) | ||
| @@ -0,0 +1,49 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Add some standard cpp magic to a header file | ||
| 4 | |||
| 5 | import sys | ||
| 6 | |||
| 7 | def main(): | ||
| 8 | args = sys.argv[1:] | ||
| 9 | for filename in args: | ||
| 10 | process(filename) | ||
| 11 | |||
| 12 | def process(filename): | ||
| 13 | try: | ||
| 14 | f = open(filename, 'r') | ||
| 15 | except IOError, msg: | ||
| 16 | sys.stderr.write('%s: can\'t open: %s\n' % (filename, str(msg))) | ||
| 17 | return | ||
| 18 | data = f.read() | ||
| 19 | f.close() | ||
| 20 | if data[:2] <> '/*': | ||
| 21 | sys.stderr.write('%s does not begin with C comment\n' % filename) | ||
| 22 | return | ||
| 23 | try: | ||
| 24 | f = open(filename, 'w') | ||
| 25 | except IOError, msg: | ||
| 26 | sys.stderr.write('%s: can\'t write: %s\n' % (filename, str(msg))) | ||
| 27 | return | ||
| 28 | sys.stderr.write('Processing %s ...\n' % filename) | ||
| 29 | magic = 'Py_' | ||
| 30 | for c in filename: | ||
| 31 | if ord(c)<=0x80 and c.isalnum(): | ||
| 32 | magic = magic + c.upper() | ||
| 33 | else: magic = magic + '_' | ||
| 34 | sys.stdout = f | ||
| 35 | print '#ifndef', magic | ||
| 36 | print '#define', magic | ||
| 37 | print '#ifdef __cplusplus' | ||
| 38 | print 'extern "C" {' | ||
| 39 | print '#endif' | ||
| 40 | |||
| 41 | f.write(data) | ||
| 42 | |||
| 43 | print '#ifdef __cplusplus' | ||
| 44 | print '}' | ||
| 45 | print '#endif' | ||
| 46 | print '#endif /*', '!'+magic, '*/' | ||
| 47 | |||
| 48 | if __name__ == '__main__': | ||
| 49 | main() | ||
| @@ -0,0 +1,113 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | """(Ostensibly) fix copyright notices in files. | ||
| 4 | |||
| 5 | Actually, this sript will simply replace a block of text in a file from one | ||
| 6 | string to another. It will only do this once though, i.e. not globally | ||
| 7 | throughout the file. It writes a backup file and then does an os.rename() | ||
| 8 | dance for atomicity. | ||
| 9 | |||
| 10 | Usage: fixnotices.py [options] [filenames] | ||
| 11 | Options: | ||
| 12 | -h / --help | ||
| 13 | Print this message and exit | ||
| 14 | |||
| 15 | --oldnotice=file | ||
| 16 | Use the notice in the file as the old (to be replaced) string, instead | ||
| 17 | of the hard coded value in the script. | ||
| 18 | |||
| 19 | --newnotice=file | ||
| 20 | Use the notice in the file as the new (replacement) string, instead of | ||
| 21 | the hard coded value in the script. | ||
| 22 | |||
| 23 | --dry-run | ||
| 24 | Don't actually make the changes, but print out the list of files that | ||
| 25 | would change. When used with -v, a status will be printed for every | ||
| 26 | file. | ||
| 27 | |||
| 28 | -v / --verbose | ||
| 29 | Print a message for every file looked at, indicating whether the file | ||
| 30 | is changed or not. | ||
| 31 | """ | ||
| 32 | |||
| 33 | OLD_NOTICE = """/*********************************************************** | ||
| 34 | Copyright (c) 2000, BeOpen.com. | ||
| 35 | Copyright (c) 1995-2000, Corporation for National Research Initiatives. | ||
| 36 | Copyright (c) 1990-1995, Stichting Mathematisch Centrum. | ||
| 37 | All rights reserved. | ||
| 38 | |||
| 39 | See the file "Misc/COPYRIGHT" for information on usage and | ||
| 40 | redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. | ||
| 41 | ******************************************************************/ | ||
| 42 | """ | ||
| 43 | import os | ||
| 44 | import sys | ||
| 45 | import getopt | ||
| 46 | |||
| 47 | NEW_NOTICE = "" | ||
| 48 | DRYRUN = 0 | ||
| 49 | VERBOSE = 0 | ||
| 50 | |||
| 51 | |||
| 52 | def usage(code, msg=''): | ||
| 53 | print __doc__ % globals() | ||
| 54 | if msg: | ||
| 55 | print msg | ||
| 56 | sys.exit(code) | ||
| 57 | |||
| 58 | |||
| 59 | def main(): | ||
| 60 | global DRYRUN, OLD_NOTICE, NEW_NOTICE, VERBOSE | ||
| 61 | try: | ||
| 62 | opts, args = getopt.getopt(sys.argv[1:], 'hv', | ||
| 63 | ['help', 'oldnotice=', 'newnotice=', | ||
| 64 | 'dry-run', 'verbose']) | ||
| 65 | except getopt.error, msg: | ||
| 66 | usage(1, msg) | ||
| 67 | |||
| 68 | for opt, arg in opts: | ||
| 69 | if opt in ('-h', '--help'): | ||
| 70 | usage(0) | ||
| 71 | elif opt in ('-v', '--verbose'): | ||
| 72 | VERBOSE = 1 | ||
| 73 | elif opt == '--dry-run': | ||
| 74 | DRYRUN = 1 | ||
| 75 | elif opt == '--oldnotice': | ||
| 76 | fp = open(arg) | ||
| 77 | OLD_NOTICE = fp.read() | ||
| 78 | fp.close() | ||
| 79 | elif opt == '--newnotice': | ||
| 80 | fp = open(arg) | ||
| 81 | NEW_NOTICE = fp.read() | ||
| 82 | fp.close() | ||
| 83 | |||
| 84 | for arg in args: | ||
| 85 | process(arg) | ||
| 86 | |||
| 87 | |||
| 88 | def process(file): | ||
| 89 | f = open(file) | ||
| 90 | data = f.read() | ||
| 91 | f.close() | ||
| 92 | i = data.find(OLD_NOTICE) | ||
| 93 | if i < 0: | ||
| 94 | if VERBOSE: | ||
| 95 | print 'no change:', file | ||
| 96 | return | ||
| 97 | elif DRYRUN or VERBOSE: | ||
| 98 | print ' change:', file | ||
| 99 | if DRYRUN: | ||
| 100 | # Don't actually change the file | ||
| 101 | return | ||
| 102 | data = data[:i] + NEW_NOTICE + data[i+len(OLD_NOTICE):] | ||
| 103 | new = file + ".new" | ||
| 104 | backup = file + ".bak" | ||
| 105 | f = open(new, "w") | ||
| 106 | f.write(data) | ||
| 107 | f.close() | ||
| 108 | os.rename(file, backup) | ||
| 109 | os.rename(new, file) | ||
| 110 | |||
| 111 | |||
| 112 | if __name__ == '__main__': | ||
| 113 | main() | ||
| @@ -0,0 +1,33 @@ | |||
| 1 | #!/usr/bin/env python | ||
| 2 | |||
| 3 | # Fix Python script(s) to reference the interpreter via /usr/bin/env python. | ||
| 4 | # Warning: this overwrites the file without making a backup. | ||
| 5 | |||
| 6 | import sys | ||
| 7 | import re | ||
| 8 | |||
| 9 | |||
| 10 | def main(): | ||
| 11 | for filename in sys.argv[1:]: | ||
| 12 | try: | ||
| 13 | f = open(filename, 'r') | ||
| 14 | except IOError, msg: | ||
| 15 | print filename, ': can\'t open :', msg | ||
| 16 | continue | ||
| 17 | line = f.readline() | ||
| 18 | if not re.match('^#! */usr/local/bin/python', line): | ||
| 19 | print filename, ': not a /usr/local/bin/python script' | ||
| 20 | f.close() | ||
| 21 | continue | ||
| 22 | rest = f.read() | ||
| 23 | f.close() | ||
| 24 | line = re.sub('/usr/local/bin/python', | ||
| 25 | '/usr/bin/env python', line) | ||
| 26 | print filename, ':', repr(line) | ||
| 27 | f = open(filename, "w") | ||
| 28 | f.write(line) | ||
| 29 | f.write(rest) | ||
| 30 | f.close() | ||
| 31 | |||
| 32 | if __name__ == '__main__': | ||
| 33 | main() | ||
| @@ -0,0 +1,400 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | """Mirror a remote ftp subtree into a local directory tree. | ||
| 4 | |||
| 5 | usage: ftpmirror [-v] [-q] [-i] [-m] [-n] [-r] [-s pat] | ||
| 6 | [-l username [-p passwd [-a account]]] | ||
| 7 | hostname[:port] [remotedir [localdir]] | ||
| 8 | -v: verbose | ||
| 9 | -q: quiet | ||
| 10 | -i: interactive mode | ||
| 11 | -m: macintosh server (NCSA telnet 2.4) (implies -n -s '*.o') | ||
| 12 | -n: don't log in | ||
| 13 | -r: remove local files/directories no longer pertinent | ||
| 14 | -l username [-p passwd [-a account]]: login info (default .netrc or anonymous) | ||
| 15 | -s pat: skip files matching pattern | ||
| 16 | hostname: remote host w/ optional port separated by ':' | ||
| 17 | remotedir: remote directory (default initial) | ||
| 18 | localdir: local directory (default current) | ||
| 19 | """ | ||
| 20 | |||
| 21 | import os | ||
| 22 | import sys | ||
| 23 | import time | ||
| 24 | import getopt | ||
| 25 | import ftplib | ||
| 26 | import netrc | ||
| 27 | from fnmatch import fnmatch | ||
| 28 | |||
| 29 | # Print usage message and exit | ||
| 30 | def usage(*args): | ||
| 31 | sys.stdout = sys.stderr | ||
| 32 | for msg in args: print msg | ||
| 33 | print __doc__ | ||
| 34 | sys.exit(2) | ||
| 35 | |||
| 36 | verbose = 1 # 0 for -q, 2 for -v | ||
| 37 | interactive = 0 | ||
| 38 | mac = 0 | ||
| 39 | rmok = 0 | ||
| 40 | nologin = 0 | ||
| 41 | skippats = ['.', '..', '.mirrorinfo'] | ||
| 42 | |||
| 43 | # Main program: parse command line and start processing | ||
| 44 | def main(): | ||
| 45 | global verbose, interactive, mac, rmok, nologin | ||
| 46 | try: | ||
| 47 | opts, args = getopt.getopt(sys.argv[1:], 'a:bil:mnp:qrs:v') | ||
| 48 | except getopt.error, msg: | ||
| 49 | usage(msg) | ||
| 50 | login = '' | ||
| 51 | passwd = '' | ||
| 52 | account = '' | ||
| 53 | if not args: usage('hostname missing') | ||
| 54 | host = args[0] | ||
| 55 | port = 0 | ||
| 56 | if ':' in host: | ||
| 57 | host, port = host.split(':', 1) | ||
| 58 | port = int(port) | ||
| 59 | try: | ||
| 60 | auth = netrc.netrc().authenticators(host) | ||
| 61 | if auth is not None: | ||
| 62 | login, account, passwd = auth | ||
| 63 | except (netrc.NetrcParseError, IOError): | ||
| 64 | pass | ||
| 65 | for o, a in opts: | ||
| 66 | if o == '-l': login = a | ||
| 67 | if o == '-p': passwd = a | ||
| 68 | if o == '-a': account = a | ||
| 69 | if o == '-v': verbose = verbose + 1 | ||
| 70 | if o == '-q': verbose = 0 | ||
| 71 | if o == '-i': interactive = 1 | ||
| 72 | if o == '-m': mac = 1; nologin = 1; skippats.append('*.o') | ||
| 73 | if o == '-n': nologin = 1 | ||
| 74 | if o == '-r': rmok = 1 | ||
| 75 | if o == '-s': skippats.append(a) | ||
| 76 | remotedir = '' | ||
| 77 | localdir = '' | ||
| 78 | if args[1:]: | ||
| 79 | remotedir = args[1] | ||
| 80 | if args[2:]: | ||
| 81 | localdir = args[2] | ||
| 82 | if args[3:]: usage('too many arguments') | ||
| 83 | # | ||
| 84 | f = ftplib.FTP() | ||
| 85 | if verbose: print "Connecting to '%s%s'..." % (host, | ||
| 86 | (port and ":%d"%port or "")) | ||
| 87 | f.connect(host,port) | ||
| 88 | if not nologin: | ||
| 89 | if verbose: | ||
| 90 | print 'Logging in as %r...' % (login or 'anonymous') | ||
| 91 | f.login(login, passwd, account) | ||
| 92 | if verbose: print 'OK.' | ||
| 93 | pwd = f.pwd() | ||
| 94 | if verbose > 1: print 'PWD =', repr(pwd) | ||
| 95 | if remotedir: | ||
| 96 | if verbose > 1: print 'cwd(%s)' % repr(remotedir) | ||
| 97 | f.cwd(remotedir) | ||
| 98 | if verbose > 1: print 'OK.' | ||
| 99 | pwd = f.pwd() | ||
| 100 | if verbose > 1: print 'PWD =', repr(pwd) | ||
| 101 | # | ||
| 102 | mirrorsubdir(f, localdir) | ||
| 103 | |||
| 104 | # Core logic: mirror one subdirectory (recursively) | ||
| 105 | def mirrorsubdir(f, localdir): | ||
| 106 | pwd = f.pwd() | ||
| 107 | if localdir and not os.path.isdir(localdir): | ||
| 108 | if verbose: print 'Creating local directory', repr(localdir) | ||
| 109 | try: | ||
| 110 | makedir(localdir) | ||
| 111 | except os.error, msg: | ||
| 112 | print "Failed to establish local directory", repr(localdir) | ||
| 113 | return | ||
| 114 | infofilename = os.path.join(localdir, '.mirrorinfo') | ||
| 115 | try: | ||
| 116 | text = open(infofilename, 'r').read() | ||
| 117 | except IOError, msg: | ||
| 118 | text = '{}' | ||
| 119 | try: | ||
| 120 | info = eval(text) | ||
| 121 | except (SyntaxError, NameError): | ||
| 122 | print 'Bad mirror info in', repr(infofilename) | ||
| 123 | info = {} | ||
| 124 | subdirs = [] | ||
| 125 | listing = [] | ||
| 126 | if verbose: print 'Listing remote directory %r...' % (pwd,) | ||
| 127 | f.retrlines('LIST', listing.append) | ||
| 128 | filesfound = [] | ||
| 129 | for line in listing: | ||
| 130 | if verbose > 1: print '-->', repr(line) | ||
| 131 | if mac: | ||
| 132 | # Mac listing has just filenames; | ||
| 133 | # trailing / means subdirectory | ||
| 134 | filename = line.strip() | ||
| 135 | mode = '-' | ||
| 136 | if filename[-1:] == '/': | ||
| 137 | filename = filename[:-1] | ||
| 138 | mode = 'd' | ||
| 139 | infostuff = '' | ||
| 140 | else: | ||
| 141 | # Parse, assuming a UNIX listing | ||
| 142 | words = line.split(None, 8) | ||
| 143 | if len(words) < 6: | ||
| 144 | if verbose > 1: print 'Skipping short line' | ||
| 145 | continue | ||
| 146 | filename = words[-1].lstrip() | ||
| 147 | i = filename.find(" -> ") | ||
| 148 | if i >= 0: | ||
| 149 | # words[0] had better start with 'l'... | ||
| 150 | if verbose > 1: | ||
| 151 | print 'Found symbolic link %r' % (filename,) | ||
| 152 | linkto = filename[i+4:] | ||
| 153 | filename = filename[:i] | ||
| 154 | infostuff = words[-5:-1] | ||
| 155 | mode = words[0] | ||
| 156 | skip = 0 | ||
| 157 | for pat in skippats: | ||
| 158 | if fnmatch(filename, pat): | ||
| 159 | if verbose > 1: | ||
| 160 | print 'Skip pattern', repr(pat), | ||
| 161 | print 'matches', repr(filename) | ||
| 162 | skip = 1 | ||
| 163 | break | ||
| 164 | if skip: | ||
| 165 | continue | ||
| 166 | if mode[0] == 'd': | ||
| 167 | if verbose > 1: | ||
| 168 | print 'Remembering subdirectory', repr(filename) | ||
| 169 | subdirs.append(filename) | ||
| 170 | continue | ||
| 171 | filesfound.append(filename) | ||
| 172 | if info.has_key(filename) and info[filename] == infostuff: | ||
| 173 | if verbose > 1: | ||
| 174 | print 'Already have this version of',repr(filename) | ||
| 175 | continue | ||
| 176 | fullname = os.path.join(localdir, filename) | ||
| 177 | tempname = os.path.join(localdir, '@'+filename) | ||
| 178 | if interactive: | ||
| 179 | doit = askabout('file', filename, pwd) | ||
| 180 | if not doit: | ||
| 181 | if not info.has_key(filename): | ||
| 182 | info[filename] = 'Not retrieved' | ||
| 183 | continue | ||
| 184 | try: | ||
| 185 | os.unlink(tempname) | ||
| 186 | except os.error: | ||
| 187 | pass | ||
| 188 | if mode[0] == 'l': | ||
| 189 | if verbose: | ||
| 190 | print "Creating symlink %r -> %r" % (filename, linkto) | ||
| 191 | try: | ||
| 192 | os.symlink(linkto, tempname) | ||
| 193 | except IOError, msg: | ||
| 194 | print "Can't create %r: %s" % (tempname, msg) | ||
| 195 | continue | ||
| 196 | else: | ||
| 197 | try: | ||
| 198 | fp = open(tempname, 'wb') | ||
| 199 | except IOError, msg: | ||
| 200 | print "Can't create %r: %s" % (tempname, msg) | ||
| 201 | continue | ||
| 202 | if verbose: | ||
| 203 | print 'Retrieving %r from %r as %r...' % (filename, pwd, fullname) | ||
| 204 | if verbose: | ||
| 205 | fp1 = LoggingFile(fp, 1024, sys.stdout) | ||
| 206 | else: | ||
| 207 | fp1 = fp | ||
| 208 | t0 = time.time() | ||
| 209 | try: | ||
| 210 | f.retrbinary('RETR ' + filename, | ||
| 211 | fp1.write, 8*1024) | ||
| 212 | except ftplib.error_perm, msg: | ||
| 213 | print msg | ||
| 214 | t1 = time.time() | ||
| 215 | bytes = fp.tell() | ||
| 216 | fp.close() | ||
| 217 | if fp1 != fp: | ||
| 218 | fp1.close() | ||
| 219 | try: | ||
| 220 | os.unlink(fullname) | ||
| 221 | except os.error: | ||
| 222 | pass # Ignore the error | ||
| 223 | try: | ||
| 224 | os.rename(tempname, fullname) | ||
| 225 | except os.error, msg: | ||
| 226 | print "Can't rename %r to %r: %s" % (tempname, fullname, msg) | ||
| 227 | continue | ||
| 228 | info[filename] = infostuff | ||
| 229 | writedict(info, infofilename) | ||
| 230 | if verbose and mode[0] != 'l': | ||
| 231 | dt = t1 - t0 | ||
| 232 | kbytes = bytes / 1024.0 | ||
| 233 | print int(round(kbytes)), | ||
| 234 | print 'Kbytes in', | ||
| 235 | print int(round(dt)), | ||
| 236 | print 'seconds', | ||
| 237 | if t1 > t0: | ||
| 238 | print '(~%d Kbytes/sec)' % \ | ||
| 239 | int(round(kbytes/dt),) | ||
| 240 | |||
| 241 | # | ||
| 242 | # Remove files from info that are no longer remote | ||
| 243 | deletions = 0 | ||
| 244 | for filename in info.keys(): | ||
| 245 | if filename not in filesfound: | ||
| 246 | if verbose: | ||
| 247 | print "Removing obsolete info entry for", | ||
| 248 | print repr(filename), "in", repr(localdir or ".") | ||
| 249 | del info[filename] | ||
| 250 | deletions = deletions + 1 | ||
| 251 | if deletions: | ||
| 252 | writedict(info, infofilename) | ||
| 253 | # | ||
| 254 | # Remove local files that are no longer in the remote directory | ||
| 255 | try: | ||
| 256 | if not localdir: names = os.listdir(os.curdir) | ||
| 257 | else: names = os.listdir(localdir) | ||
| 258 | except os.error: | ||
| 259 | names = [] | ||
| 260 | for name in names: | ||
| 261 | if name[0] == '.' or info.has_key(name) or name in subdirs: | ||
| 262 | continue | ||
| 263 | skip = 0 | ||
| 264 | for pat in skippats: | ||
| 265 | if fnmatch(name, pat): | ||
| 266 | if verbose > 1: | ||
| 267 | print 'Skip pattern', repr(pat), | ||
| 268 | print 'matches', repr(name) | ||
| 269 | skip = 1 | ||
| 270 | break | ||
| 271 | if skip: | ||
| 272 | continue | ||
| 273 | fullname = os.path.join(localdir, name) | ||
| 274 | if not rmok: | ||
| 275 | if verbose: | ||
| 276 | print 'Local file', repr(fullname), | ||
| 277 | print 'is no longer pertinent' | ||
| 278 | continue | ||
| 279 | if verbose: print 'Removing local file/dir', repr(fullname) | ||
| 280 | remove(fullname) | ||
| 281 | # | ||
| 282 | # Recursively mirror subdirectories | ||
| 283 | for subdir in subdirs: | ||
| 284 | if interactive: | ||
| 285 | doit = askabout('subdirectory', subdir, pwd) | ||
| 286 | if not doit: continue | ||
| 287 | if verbose: print 'Processing subdirectory', repr(subdir) | ||
| 288 | localsubdir = os.path.join(localdir, subdir) | ||
| 289 | pwd = f.pwd() | ||
| 290 | if verbose > 1: | ||
| 291 | print 'Remote directory now:', repr(pwd) | ||
| 292 | print 'Remote cwd', repr(subdir) | ||
| 293 | try: | ||
| 294 | f.cwd(subdir) | ||
| 295 | except ftplib.error_perm, msg: | ||
| 296 | print "Can't chdir to", repr(subdir), ":", repr(msg) | ||
| 297 | else: | ||
| 298 | if verbose: print 'Mirroring as', repr(localsubdir) | ||
| 299 | mirrorsubdir(f, localsubdir) | ||
| 300 | if verbose > 1: print 'Remote cwd ..' | ||
| 301 | f.cwd('..') | ||
| 302 | newpwd = f.pwd() | ||
| 303 | if newpwd != pwd: | ||
| 304 | print 'Ended up in wrong directory after cd + cd ..' | ||
| 305 | print 'Giving up now.' | ||
| 306 | break | ||
| 307 | else: | ||
| 308 | if verbose > 1: print 'OK.' | ||
| 309 | |||
| 310 | # Helper to remove a file or directory tree | ||
| 311 | def remove(fullname): | ||
| 312 | if os.path.isdir(fullname) and not os.path.islink(fullname): | ||
| 313 | try: | ||
| 314 | names = os.listdir(fullname) | ||
| 315 | except os.error: | ||
| 316 | names = [] | ||
| 317 | ok = 1 | ||
| 318 | for name in names: | ||
| 319 | if not remove(os.path.join(fullname, name)): | ||
| 320 | ok = 0 | ||
| 321 | if not ok: | ||
| 322 | return 0 | ||
| 323 | try: | ||
| 324 | os.rmdir(fullname) | ||
| 325 | except os.error, msg: | ||
| 326 | print "Can't remove local directory %r: %s" % (fullname, msg) | ||
| 327 | return 0 | ||
| 328 | else: | ||
| 329 | try: | ||
| 330 | os.unlink(fullname) | ||
| 331 | except os.error, msg: | ||
| 332 | print "Can't remove local file %r: %s" % (fullname, msg) | ||
| 333 | return 0 | ||
| 334 | return 1 | ||
| 335 | |||
| 336 | # Wrapper around a file for writing to write a hash sign every block. | ||
| 337 | class LoggingFile: | ||
| 338 | def __init__(self, fp, blocksize, outfp): | ||
| 339 | self.fp = fp | ||
| 340 | self.bytes = 0 | ||
| 341 | self.hashes = 0 | ||
| 342 | self.blocksize = blocksize | ||
| 343 | self.outfp = outfp | ||
| 344 | def write(self, data): | ||
| 345 | self.bytes = self.bytes + len(data) | ||
| 346 | hashes = int(self.bytes) / self.blocksize | ||
| 347 | while hashes > self.hashes: | ||
| 348 | self.outfp.write('#') | ||
| 349 | self.outfp.flush() | ||
| 350 | self.hashes = self.hashes + 1 | ||
| 351 | self.fp.write(data) | ||
| 352 | def close(self): | ||
| 353 | self.outfp.write('\n') | ||
| 354 | |||
| 355 | # Ask permission to download a file. | ||
| 356 | def askabout(filetype, filename, pwd): | ||
| 357 | prompt = 'Retrieve %s %s from %s ? [ny] ' % (filetype, filename, pwd) | ||
| 358 | while 1: | ||
| 359 | reply = raw_input(prompt).strip().lower() | ||
| 360 | if reply in ['y', 'ye', 'yes']: | ||
| 361 | return 1 | ||
| 362 | if reply in ['', 'n', 'no', 'nop', 'nope']: | ||
| 363 | return 0 | ||
| 364 | print 'Please answer yes or no.' | ||
| 365 | |||
| 366 | # Create a directory if it doesn't exist. Recursively create the | ||
| 367 | # parent directory as well if needed. | ||
| 368 | def makedir(pathname): | ||
| 369 | if os.path.isdir(pathname): | ||
| 370 | return | ||
| 371 | dirname = os.path.dirname(pathname) | ||
| 372 | if dirname: makedir(dirname) | ||
| 373 | os.mkdir(pathname, 0777) | ||
| 374 | |||
| 375 | # Write a dictionary to a file in a way that can be read back using | ||
| 376 | # rval() but is still somewhat readable (i.e. not a single long line). | ||
| 377 | # Also creates a backup file. | ||
| 378 | def writedict(dict, filename): | ||
| 379 | dir, fname = os.path.split(filename) | ||
| 380 | tempname = os.path.join(dir, '@' + fname) | ||
| 381 | backup = os.path.join(dir, fname + '~') | ||
| 382 | try: | ||
| 383 | os.unlink(backup) | ||
| 384 | except os.error: | ||
| 385 | pass | ||
| 386 | fp = open(tempname, 'w') | ||
| 387 | fp.write('{\n') | ||
| 388 | for key, value in dict.items(): | ||
| 389 | fp.write('%r: %r,\n' % (key, value)) | ||
| 390 | fp.write('}\n') | ||
| 391 | fp.close() | ||
| 392 | try: | ||
| 393 | os.rename(filename, backup) | ||
| 394 | except os.error: | ||
| 395 | pass | ||
| 396 | os.rename(tempname, filename) | ||
| 397 | |||
| 398 | |||
| 399 | if __name__ == '__main__': | ||
| 400 | main() | ||
| @@ -0,0 +1,23 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | import sys, webbrowser | ||
| 4 | |||
| 5 | def main(): | ||
| 6 | args = sys.argv[1:] | ||
| 7 | if not args: | ||
| 8 | print "Usage: %s querystring" % sys.argv[0] | ||
| 9 | return | ||
| 10 | list = [] | ||
| 11 | for arg in args: | ||
| 12 | if '+' in arg: | ||
| 13 | arg = arg.replace('+', '%2B') | ||
| 14 | if ' ' in arg: | ||
| 15 | arg = '"%s"' % arg | ||
| 16 | arg = arg.replace(' ', '+') | ||
| 17 | list.append(arg) | ||
| 18 | s = '+'.join(list) | ||
| 19 | url = "http://www.google.com/search?q=%s" % s | ||
| 20 | webbrowser.open(url) | ||
| 21 | |||
| 22 | if __name__ == '__main__': | ||
| 23 | main() | ||
| @@ -0,0 +1,79 @@ | |||
| 1 | #! /usr/bin/env python2.3 | ||
| 2 | |||
| 3 | """Transform gprof(1) output into useful HTML.""" | ||
| 4 | |||
| 5 | import re, os, sys, cgi, webbrowser | ||
| 6 | |||
| 7 | header = """\ | ||
| 8 | <html> | ||
| 9 | <head> | ||
| 10 | <title>gprof output (%s)</title> | ||
| 11 | </head> | ||
| 12 | <body> | ||
| 13 | <pre> | ||
| 14 | """ | ||
| 15 | |||
| 16 | trailer = """\ | ||
| 17 | </pre> | ||
| 18 | </body> | ||
| 19 | </html> | ||
| 20 | """ | ||
| 21 | |||
| 22 | def add_escapes(input): | ||
| 23 | for line in input: | ||
| 24 | yield cgi.escape(line) | ||
| 25 | |||
| 26 | def main(): | ||
| 27 | filename = "gprof.out" | ||
| 28 | if sys.argv[1:]: | ||
| 29 | filename = sys.argv[1] | ||
| 30 | outputfilename = filename + ".html" | ||
| 31 | input = add_escapes(file(filename)) | ||
| 32 | output = file(outputfilename, "w") | ||
| 33 | output.write(header % filename) | ||
| 34 | for line in input: | ||
| 35 | output.write(line) | ||
| 36 | if line.startswith(" time"): | ||
| 37 | break | ||
| 38 | labels = {} | ||
| 39 | for line in input: | ||
| 40 | m = re.match(r"(.* )(\w+)\n", line) | ||
| 41 | if not m: | ||
| 42 | output.write(line) | ||
| 43 | break | ||
| 44 | stuff, fname = m.group(1, 2) | ||
| 45 | labels[fname] = fname | ||
| 46 | output.write('%s<a name="flat:%s" href="#call:%s">%s</a>\n' % | ||
| 47 | (stuff, fname, fname, fname)) | ||
| 48 | for line in input: | ||
| 49 | output.write(line) | ||
| 50 | if line.startswith("index % time"): | ||
| 51 | break | ||
| 52 | for line in input: | ||
| 53 | m = re.match(r"(.* )(\w+)(( <cycle.*>)? \[\d+\])\n", line) | ||
| 54 | if not m: | ||
| 55 | output.write(line) | ||
| 56 | if line.startswith("Index by function name"): | ||
| 57 | break | ||
| 58 | continue | ||
| 59 | prefix, fname, suffix = m.group(1, 2, 3) | ||
| 60 | if fname not in labels: | ||
| 61 | output.write(line) | ||
| 62 | continue | ||
| 63 | if line.startswith("["): | ||
| 64 | output.write('%s<a name="call:%s" href="#flat:%s">%s</a>%s\n' % | ||
| 65 | (prefix, fname, fname, fname, suffix)) | ||
| 66 | else: | ||
| 67 | output.write('%s<a href="#call:%s">%s</a>%s\n' % | ||
| 68 | (prefix, fname, fname, suffix)) | ||
| 69 | for line in input: | ||
| 70 | for part in re.findall(r"(\w+(?:\.c)?|\W+)", line): | ||
| 71 | if part in labels: | ||
| 72 | part = '<a href="#call:%s">%s</a>' % (part, part) | ||
| 73 | output.write(part) | ||
| 74 | output.write(trailer) | ||
| 75 | output.close() | ||
| 76 | webbrowser.open("file:" + os.path.abspath(outputfilename)) | ||
| 77 | |||
| 78 | if __name__ == '__main__': | ||
| 79 | main() | ||
| @@ -0,0 +1,175 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Read #define's and translate to Python code. | ||
| 4 | # Handle #include statements. | ||
| 5 | # Handle #define macros with one argument. | ||
| 6 | # Anything that isn't recognized or doesn't translate into valid | ||
| 7 | # Python is ignored. | ||
| 8 | |||
| 9 | # Without filename arguments, acts as a filter. | ||
| 10 | # If one or more filenames are given, output is written to corresponding | ||
| 11 | # filenames in the local directory, translated to all uppercase, with | ||
| 12 | # the extension replaced by ".py". | ||
| 13 | |||
| 14 | # By passing one or more options of the form "-i regular_expression" | ||
| 15 | # you can specify additional strings to be ignored. This is useful | ||
| 16 | # e.g. to ignore casts to u_long: simply specify "-i '(u_long)'". | ||
| 17 | |||
| 18 | # XXX To do: | ||
| 19 | # - turn trailing C comments into Python comments | ||
| 20 | # - turn C Boolean operators "&& || !" into Python "and or not" | ||
| 21 | # - what to do about #if(def)? | ||
| 22 | # - what to do about macros with multiple parameters? | ||
| 23 | |||
| 24 | import sys, re, getopt, os | ||
| 25 | |||
| 26 | p_define = re.compile('^[\t ]*#[\t ]*define[\t ]+([a-zA-Z0-9_]+)[\t ]+') | ||
| 27 | |||
| 28 | p_macro = re.compile( | ||
| 29 | '^[\t ]*#[\t ]*define[\t ]+' | ||
| 30 | '([a-zA-Z0-9_]+)\(([_a-zA-Z][_a-zA-Z0-9]*)\)[\t ]+') | ||
| 31 | |||
| 32 | p_include = re.compile('^[\t ]*#[\t ]*include[\t ]+<([a-zA-Z0-9_/\.]+)') | ||
| 33 | |||
| 34 | p_comment = re.compile(r'/\*([^*]+|\*+[^/])*(\*+/)?') | ||
| 35 | p_cpp_comment = re.compile('//.*') | ||
| 36 | |||
| 37 | ignores = [p_comment, p_cpp_comment] | ||
| 38 | |||
| 39 | p_char = re.compile(r"'(\\.[^\\]*|[^\\])'") | ||
| 40 | |||
| 41 | p_hex = re.compile(r"0x([0-9a-fA-F]+)L?") | ||
| 42 | |||
| 43 | filedict = {} | ||
| 44 | importable = {} | ||
| 45 | |||
| 46 | try: | ||
| 47 | searchdirs=os.environ['include'].split(';') | ||
| 48 | except KeyError: | ||
| 49 | try: | ||
| 50 | searchdirs=os.environ['INCLUDE'].split(';') | ||
| 51 | except KeyError: | ||
| 52 | try: | ||
| 53 | if sys.platform.find("beos") == 0: | ||
| 54 | searchdirs=os.environ['BEINCLUDES'].split(';') | ||
| 55 | elif sys.platform.startswith("atheos"): | ||
| 56 | searchdirs=os.environ['C_INCLUDE_PATH'].split(':') | ||
| 57 | else: | ||
| 58 | raise KeyError | ||
| 59 | except KeyError: | ||
| 60 | searchdirs=['/usr/include'] | ||
| 61 | |||
| 62 | def main(): | ||
| 63 | global filedict | ||
| 64 | opts, args = getopt.getopt(sys.argv[1:], 'i:') | ||
| 65 | for o, a in opts: | ||
| 66 | if o == '-i': | ||
| 67 | ignores.append(re.compile(a)) | ||
| 68 | if not args: | ||
| 69 | args = ['-'] | ||
| 70 | for filename in args: | ||
| 71 | if filename == '-': | ||
| 72 | sys.stdout.write('# Generated by h2py from stdin\n') | ||
| 73 | process(sys.stdin, sys.stdout) | ||
| 74 | else: | ||
| 75 | fp = open(filename, 'r') | ||
| 76 | outfile = os.path.basename(filename) | ||
| 77 | i = outfile.rfind('.') | ||
| 78 | if i > 0: outfile = outfile[:i] | ||
| 79 | modname = outfile.upper() | ||
| 80 | outfile = modname + '.py' | ||
| 81 | outfp = open(outfile, 'w') | ||
| 82 | outfp.write('# Generated by h2py from %s\n' % filename) | ||
| 83 | filedict = {} | ||
| 84 | for dir in searchdirs: | ||
| 85 | if filename[:len(dir)] == dir: | ||
| 86 | filedict[filename[len(dir)+1:]] = None # no '/' trailing | ||
| 87 | importable[filename[len(dir)+1:]] = modname | ||
| 88 | break | ||
| 89 | process(fp, outfp) | ||
| 90 | outfp.close() | ||
| 91 | fp.close() | ||
| 92 | |||
| 93 | def pytify(body): | ||
| 94 | # replace ignored patterns by spaces | ||
| 95 | for p in ignores: | ||
| 96 | body = p.sub(' ', body) | ||
| 97 | # replace char literals by ord(...) | ||
| 98 | body = p_char.sub('ord(\\0)', body) | ||
| 99 | # Compute negative hexadecimal constants | ||
| 100 | start = 0 | ||
| 101 | UMAX = 2*(sys.maxint+1) | ||
| 102 | while 1: | ||
| 103 | m = p_hex.search(body, start) | ||
| 104 | if not m: break | ||
| 105 | s,e = m.span() | ||
| 106 | val = long(body[slice(*m.span(1))], 16) | ||
| 107 | if val > sys.maxint: | ||
| 108 | val -= UMAX | ||
| 109 | body = body[:s] + "(" + str(val) + ")" + body[e:] | ||
| 110 | start = s + 1 | ||
| 111 | return body | ||
| 112 | |||
| 113 | def process(fp, outfp, env = {}): | ||
| 114 | lineno = 0 | ||
| 115 | while 1: | ||
| 116 | line = fp.readline() | ||
| 117 | if not line: break | ||
| 118 | lineno = lineno + 1 | ||
| 119 | match = p_define.match(line) | ||
| 120 | if match: | ||
| 121 | # gobble up continuation lines | ||
| 122 | while line[-2:] == '\\\n': | ||
| 123 | nextline = fp.readline() | ||
| 124 | if not nextline: break | ||
| 125 | lineno = lineno + 1 | ||
| 126 | line = line + nextline | ||
| 127 | name = match.group(1) | ||
| 128 | body = line[match.end():] | ||
| 129 | body = pytify(body) | ||
| 130 | ok = 0 | ||
| 131 | stmt = '%s = %s\n' % (name, body.strip()) | ||
| 132 | try: | ||
| 133 | exec stmt in env | ||
| 134 | except: | ||
| 135 | sys.stderr.write('Skipping: %s' % stmt) | ||
| 136 | else: | ||
| 137 | outfp.write(stmt) | ||
| 138 | match = p_macro.match(line) | ||
| 139 | if match: | ||
| 140 | macro, arg = match.group(1, 2) | ||
| 141 | body = line[match.end():] | ||
| 142 | body = pytify(body) | ||
| 143 | stmt = 'def %s(%s): return %s\n' % (macro, arg, body) | ||
| 144 | try: | ||
| 145 | exec stmt in env | ||
| 146 | except: | ||
| 147 | sys.stderr.write('Skipping: %s' % stmt) | ||
| 148 | else: | ||
| 149 | outfp.write(stmt) | ||
| 150 | match = p_include.match(line) | ||
| 151 | if match: | ||
| 152 | regs = match.regs | ||
| 153 | a, b = regs[1] | ||
| 154 | filename = line[a:b] | ||
| 155 | if importable.has_key(filename): | ||
| 156 | outfp.write('from %s import *\n' % importable[filename]) | ||
| 157 | elif not filedict.has_key(filename): | ||
| 158 | filedict[filename] = None | ||
| 159 | inclfp = None | ||
| 160 | for dir in searchdirs: | ||
| 161 | try: | ||
| 162 | inclfp = open(dir + '/' + filename) | ||
| 163 | break | ||
| 164 | except IOError: | ||
| 165 | pass | ||
| 166 | if inclfp: | ||
| 167 | outfp.write( | ||
| 168 | '\n# Included from %s\n' % filename) | ||
| 169 | process(inclfp, outfp, env) | ||
| 170 | else: | ||
| 171 | sys.stderr.write('Warning - could not find file %s\n' % | ||
| 172 | filename) | ||
| 173 | |||
| 174 | if __name__ == '__main__': | ||
| 175 | main() | ||
| @@ -0,0 +1,55 @@ | |||
| 1 | #!/usr/bin/env python | ||
| 2 | # -*- coding: iso-8859-1 -*- | ||
| 3 | |||
| 4 | """ | ||
| 5 | Run a Python script under hotshot's control. | ||
| 6 | |||
| 7 | Adapted from a posting on python-dev by Walter D�rwald | ||
| 8 | |||
| 9 | usage %prog [ %prog args ] filename [ filename args ] | ||
| 10 | |||
| 11 | Any arguments after the filename are used as sys.argv for the filename. | ||
| 12 | """ | ||
| 13 | |||
| 14 | import sys | ||
| 15 | import optparse | ||
| 16 | import os | ||
| 17 | import hotshot | ||
| 18 | import hotshot.stats | ||
| 19 | |||
| 20 | PROFILE = "hotshot.prof" | ||
| 21 | |||
| 22 | def run_hotshot(filename, profile, args): | ||
| 23 | prof = hotshot.Profile(profile) | ||
| 24 | sys.path.insert(0, os.path.dirname(filename)) | ||
| 25 | sys.argv = [filename] + args | ||
| 26 | prof.run("execfile(%r)" % filename) | ||
| 27 | prof.close() | ||
| 28 | stats = hotshot.stats.load(profile) | ||
| 29 | stats.sort_stats("time", "calls") | ||
| 30 | |||
| 31 | # print_stats uses unadorned print statements, so the only way | ||
| 32 | # to force output to stderr is to reassign sys.stdout temporarily | ||
| 33 | save_stdout = sys.stdout | ||
| 34 | sys.stdout = sys.stderr | ||
| 35 | stats.print_stats() | ||
| 36 | sys.stdout = save_stdout | ||
| 37 | |||
| 38 | return 0 | ||
| 39 | |||
| 40 | def main(args): | ||
| 41 | parser = optparse.OptionParser(__doc__) | ||
| 42 | parser.disable_interspersed_args() | ||
| 43 | parser.add_option("-p", "--profile", action="store", default=PROFILE, | ||
| 44 | dest="profile", help='Specify profile file to use') | ||
| 45 | (options, args) = parser.parse_args(args) | ||
| 46 | |||
| 47 | if len(args) == 0: | ||
| 48 | parser.print_help("missing script to execute") | ||
| 49 | return 1 | ||
| 50 | |||
| 51 | filename = args[0] | ||
| 52 | return run_hotshot(filename, options.profile, args[1:]) | ||
| 53 | |||
| 54 | if __name__ == "__main__": | ||
| 55 | sys.exit(main(sys.argv[1:])) | ||
| @@ -0,0 +1,5 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | from idlelib.PyShell import main | ||
| 4 | if __name__ == '__main__': | ||
| 5 | main() | ||
| @@ -0,0 +1,112 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Selectively preprocess #ifdef / #ifndef statements. | ||
| 4 | # Usage: | ||
| 5 | # ifdef [-Dname] ... [-Uname] ... [file] ... | ||
| 6 | # | ||
| 7 | # This scans the file(s), looking for #ifdef and #ifndef preprocessor | ||
| 8 | # commands that test for one of the names mentioned in the -D and -U | ||
| 9 | # options. On standard output it writes a copy of the input file(s) | ||
| 10 | # minus those code sections that are suppressed by the selected | ||
| 11 | # combination of defined/undefined symbols. The #if(n)def/#else/#else | ||
| 12 | # lines themselfs (if the #if(n)def tests for one of the mentioned | ||
| 13 | # names) are removed as well. | ||
| 14 | |||
| 15 | # Features: Arbitrary nesting of recognized and unrecognized | ||
| 16 | # preprocesor statements works correctly. Unrecognized #if* commands | ||
| 17 | # are left in place, so it will never remove too much, only too | ||
| 18 | # little. It does accept whitespace around the '#' character. | ||
| 19 | |||
| 20 | # Restrictions: There should be no comments or other symbols on the | ||
| 21 | # #if(n)def lines. The effect of #define/#undef commands in the input | ||
| 22 | # file or in included files is not taken into account. Tests using | ||
| 23 | # #if and the defined() pseudo function are not recognized. The #elif | ||
| 24 | # command is not recognized. Improperly nesting is not detected. | ||
| 25 | # Lines that look like preprocessor commands but which are actually | ||
| 26 | # part of comments or string literals will be mistaken for | ||
| 27 | # preprocessor commands. | ||
| 28 | |||
| 29 | import sys | ||
| 30 | import getopt | ||
| 31 | |||
| 32 | defs = [] | ||
| 33 | undefs = [] | ||
| 34 | |||
| 35 | def main(): | ||
| 36 | opts, args = getopt.getopt(sys.argv[1:], 'D:U:') | ||
| 37 | for o, a in opts: | ||
| 38 | if o == '-D': | ||
| 39 | defs.append(a) | ||
| 40 | if o == '-U': | ||
| 41 | undefs.append(a) | ||
| 42 | if not args: | ||
| 43 | args = ['-'] | ||
| 44 | for filename in args: | ||
| 45 | if filename == '-': | ||
| 46 | process(sys.stdin, sys.stdout) | ||
| 47 | else: | ||
| 48 | f = open(filename, 'r') | ||
| 49 | process(f, sys.stdout) | ||
| 50 | f.close() | ||
| 51 | |||
| 52 | def process(fpi, fpo): | ||
| 53 | keywords = ('if', 'ifdef', 'ifndef', 'else', 'endif') | ||
| 54 | ok = 1 | ||
| 55 | stack = [] | ||
| 56 | while 1: | ||
| 57 | line = fpi.readline() | ||
| 58 | if not line: break | ||
| 59 | while line[-2:] == '\\\n': | ||
| 60 | nextline = fpi.readline() | ||
| 61 | if not nextline: break | ||
| 62 | line = line + nextline | ||
| 63 | tmp = line.strip() | ||
| 64 | if tmp[:1] != '#': | ||
| 65 | if ok: fpo.write(line) | ||
| 66 | continue | ||
| 67 | tmp = tmp[1:].strip() | ||
| 68 | words = tmp.split() | ||
| 69 | keyword = words[0] | ||
| 70 | if keyword not in keywords: | ||
| 71 | if ok: fpo.write(line) | ||
| 72 | continue | ||
| 73 | if keyword in ('ifdef', 'ifndef') and len(words) == 2: | ||
| 74 | if keyword == 'ifdef': | ||
| 75 | ko = 1 | ||
| 76 | else: | ||
| 77 | ko = 0 | ||
| 78 | word = words[1] | ||
| 79 | if word in defs: | ||
| 80 | stack.append((ok, ko, word)) | ||
| 81 | if not ko: ok = 0 | ||
| 82 | elif word in undefs: | ||
| 83 | stack.append((ok, not ko, word)) | ||
| 84 | if ko: ok = 0 | ||
| 85 | else: | ||
| 86 | stack.append((ok, -1, word)) | ||
| 87 | if ok: fpo.write(line) | ||
| 88 | elif keyword == 'if': | ||
| 89 | stack.append((ok, -1, '')) | ||
| 90 | if ok: fpo.write(line) | ||
| 91 | elif keyword == 'else' and stack: | ||
| 92 | s_ok, s_ko, s_word = stack[-1] | ||
| 93 | if s_ko < 0: | ||
| 94 | if ok: fpo.write(line) | ||
| 95 | else: | ||
| 96 | s_ko = not s_ko | ||
| 97 | ok = s_ok | ||
| 98 | if not s_ko: ok = 0 | ||
| 99 | stack[-1] = s_ok, s_ko, s_word | ||
| 100 | elif keyword == 'endif' and stack: | ||
| 101 | s_ok, s_ko, s_word = stack[-1] | ||
| 102 | if s_ko < 0: | ||
| 103 | if ok: fpo.write(line) | ||
| 104 | del stack[-1] | ||
| 105 | ok = s_ok | ||
| 106 | else: | ||
| 107 | sys.stderr.write('Unknown keyword %s\n' % keyword) | ||
| 108 | if stack: | ||
| 109 | sys.stderr.write('stack: %s\n' % stack) | ||
| 110 | |||
| 111 | if __name__ == '__main__': | ||
| 112 | main() | ||
| @@ -0,0 +1,24 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | "Replace LF with CRLF in argument files. Print names of changed files." | ||
| 4 | |||
| 5 | import sys, re, os | ||
| 6 | |||
| 7 | def main(): | ||
| 8 | for filename in sys.argv[1:]: | ||
| 9 | if os.path.isdir(filename): | ||
| 10 | print filename, "Directory!" | ||
| 11 | continue | ||
| 12 | data = open(filename, "rb").read() | ||
| 13 | if '\0' in data: | ||
| 14 | print filename, "Binary!" | ||
| 15 | continue | ||
| 16 | newdata = re.sub("\r?\n", "\r\n", data) | ||
| 17 | if newdata != data: | ||
| 18 | print filename | ||
| 19 | f = open(filename, "wb") | ||
| 20 | f.write(newdata) | ||
| 21 | f.close() | ||
| 22 | |||
| 23 | if __name__ == '__main__': | ||
| 24 | main() | ||
| @@ -0,0 +1,80 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # linktree | ||
| 4 | # | ||
| 5 | # Make a copy of a directory tree with symbolic links to all files in the | ||
| 6 | # original tree. | ||
| 7 | # All symbolic links go to a special symbolic link at the top, so you | ||
| 8 | # can easily fix things if the original source tree moves. | ||
| 9 | # See also "mkreal". | ||
| 10 | # | ||
| 11 | # usage: mklinks oldtree newtree | ||
| 12 | |||
| 13 | import sys, os | ||
| 14 | |||
| 15 | LINK = '.LINK' # Name of special symlink at the top. | ||
| 16 | |||
| 17 | debug = 0 | ||
| 18 | |||
| 19 | def main(): | ||
| 20 | if not 3 <= len(sys.argv) <= 4: | ||
| 21 | print 'usage:', sys.argv[0], 'oldtree newtree [linkto]' | ||
| 22 | return 2 | ||
| 23 | oldtree, newtree = sys.argv[1], sys.argv[2] | ||
| 24 | if len(sys.argv) > 3: | ||
| 25 | link = sys.argv[3] | ||
| 26 | link_may_fail = 1 | ||
| 27 | else: | ||
| 28 | link = LINK | ||
| 29 | link_may_fail = 0 | ||
| 30 | if not os.path.isdir(oldtree): | ||
| 31 | print oldtree + ': not a directory' | ||
| 32 | return 1 | ||
| 33 | try: | ||
| 34 | os.mkdir(newtree, 0777) | ||
| 35 | except os.error, msg: | ||
| 36 | print newtree + ': cannot mkdir:', msg | ||
| 37 | return 1 | ||
| 38 | linkname = os.path.join(newtree, link) | ||
| 39 | try: | ||
| 40 | os.symlink(os.path.join(os.pardir, oldtree), linkname) | ||
| 41 | except os.error, msg: | ||
| 42 | if not link_may_fail: | ||
| 43 | print linkname + ': cannot symlink:', msg | ||
| 44 | return 1 | ||
| 45 | else: | ||
| 46 | print linkname + ': warning: cannot symlink:', msg | ||
| 47 | linknames(oldtree, newtree, link) | ||
| 48 | return 0 | ||
| 49 | |||
| 50 | def linknames(old, new, link): | ||
| 51 | if debug: print 'linknames', (old, new, link) | ||
| 52 | try: | ||
| 53 | names = os.listdir(old) | ||
| 54 | except os.error, msg: | ||
| 55 | print old + ': warning: cannot listdir:', msg | ||
| 56 | return | ||
| 57 | for name in names: | ||
| 58 | if name not in (os.curdir, os.pardir): | ||
| 59 | oldname = os.path.join(old, name) | ||
| 60 | linkname = os.path.join(link, name) | ||
| 61 | newname = os.path.join(new, name) | ||
| 62 | if debug > 1: print oldname, newname, linkname | ||
| 63 | if os.path.isdir(oldname) and \ | ||
| 64 | not os.path.islink(oldname): | ||
| 65 | try: | ||
| 66 | os.mkdir(newname, 0777) | ||
| 67 | ok = 1 | ||
| 68 | except: | ||
| 69 | print newname + \ | ||
| 70 | ': warning: cannot mkdir:', msg | ||
| 71 | ok = 0 | ||
| 72 | if ok: | ||
| 73 | linkname = os.path.join(os.pardir, | ||
| 74 | linkname) | ||
| 75 | linknames(oldname, newname, linkname) | ||
| 76 | else: | ||
| 77 | os.symlink(linkname, newname) | ||
| 78 | |||
| 79 | if __name__ == '__main__': | ||
| 80 | sys.exit(main()) | ||
| @@ -0,0 +1,28 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Find symbolic links and show where they point to. | ||
| 4 | # Arguments are directories to search; default is current directory. | ||
| 5 | # No recursion. | ||
| 6 | # (This is a totally different program from "findsymlinks.py"!) | ||
| 7 | |||
| 8 | import sys, os | ||
| 9 | |||
| 10 | def lll(dirname): | ||
| 11 | for name in os.listdir(dirname): | ||
| 12 | if name not in (os.curdir, os.pardir): | ||
| 13 | full = os.path.join(dirname, name) | ||
| 14 | if os.path.islink(full): | ||
| 15 | print name, '->', os.readlink(full) | ||
| 16 | def main(): | ||
| 17 | args = sys.argv[1:] | ||
| 18 | if not args: args = [os.curdir] | ||
| 19 | first = 1 | ||
| 20 | for arg in args: | ||
| 21 | if len(args) > 1: | ||
| 22 | if not first: print | ||
| 23 | first = 0 | ||
| 24 | print arg + ':' | ||
| 25 | lll(arg) | ||
| 26 | |||
| 27 | if __name__ == '__main__': | ||
| 28 | main() | ||
| @@ -0,0 +1,185 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | """Consolidate a bunch of CVS or RCS logs read from stdin. | ||
| 4 | |||
| 5 | Input should be the output of a CVS or RCS logging command, e.g. | ||
| 6 | |||
| 7 | cvs log -rrelease14: | ||
| 8 | |||
| 9 | which dumps all log messages from release1.4 upwards (assuming that | ||
| 10 | release 1.4 was tagged with tag 'release14'). Note the trailing | ||
| 11 | colon! | ||
| 12 | |||
| 13 | This collects all the revision records and outputs them sorted by date | ||
| 14 | rather than by file, collapsing duplicate revision record, i.e., | ||
| 15 | records with the same message for different files. | ||
| 16 | |||
| 17 | The -t option causes it to truncate (discard) the last revision log | ||
| 18 | entry; this is useful when using something like the above cvs log | ||
| 19 | command, which shows the revisions including the given tag, while you | ||
| 20 | probably want everything *since* that tag. | ||
| 21 | |||
| 22 | The -r option reverses the output (oldest first; the default is oldest | ||
| 23 | last). | ||
| 24 | |||
| 25 | The -b tag option restricts the output to *only* checkin messages | ||
| 26 | belonging to the given branch tag. The form -b HEAD restricts the | ||
| 27 | output to checkin messages belonging to the CVS head (trunk). (It | ||
| 28 | produces some output if tag is a non-branch tag, but this output is | ||
| 29 | not very useful.) | ||
| 30 | |||
| 31 | -h prints this message and exits. | ||
| 32 | |||
| 33 | XXX This code was created by reverse engineering CVS 1.9 and RCS 5.7 | ||
| 34 | from their output. | ||
| 35 | """ | ||
| 36 | |||
| 37 | import os, sys, errno, getopt, re | ||
| 38 | |||
| 39 | sep1 = '='*77 + '\n' # file separator | ||
| 40 | sep2 = '-'*28 + '\n' # revision separator | ||
| 41 | |||
| 42 | def main(): | ||
| 43 | """Main program""" | ||
| 44 | truncate_last = 0 | ||
| 45 | reverse = 0 | ||
| 46 | branch = None | ||
| 47 | opts, args = getopt.getopt(sys.argv[1:], "trb:h") | ||
| 48 | for o, a in opts: | ||
| 49 | if o == '-t': | ||
| 50 | truncate_last = 1 | ||
| 51 | elif o == '-r': | ||
| 52 | reverse = 1 | ||
| 53 | elif o == '-b': | ||
| 54 | branch = a | ||
| 55 | elif o == '-h': | ||
| 56 | print __doc__ | ||
| 57 | sys.exit(0) | ||
| 58 | database = [] | ||
| 59 | while 1: | ||
| 60 | chunk = read_chunk(sys.stdin) | ||
| 61 | if not chunk: | ||
| 62 | break | ||
| 63 | records = digest_chunk(chunk, branch) | ||
| 64 | if truncate_last: | ||
| 65 | del records[-1] | ||
| 66 | database[len(database):] = records | ||
| 67 | database.sort() | ||
| 68 | if not reverse: | ||
| 69 | database.reverse() | ||
| 70 | format_output(database) | ||
| 71 | |||
| 72 | def read_chunk(fp): | ||
| 73 | """Read a chunk -- data for one file, ending with sep1. | ||
| 74 | |||
| 75 | Split the chunk in parts separated by sep2. | ||
| 76 | |||
| 77 | """ | ||
| 78 | chunk = [] | ||
| 79 | lines = [] | ||
| 80 | while 1: | ||
| 81 | line = fp.readline() | ||
| 82 | if not line: | ||
| 83 | break | ||
| 84 | if line == sep1: | ||
| 85 | if lines: | ||
| 86 | chunk.append(lines) | ||
| 87 | break | ||
| 88 | if line == sep2: | ||
| 89 | if lines: | ||
| 90 | chunk.append(lines) | ||
| 91 | lines = [] | ||
| 92 | else: | ||
| 93 | lines.append(line) | ||
| 94 | return chunk | ||
| 95 | |||
| 96 | def digest_chunk(chunk, branch=None): | ||
| 97 | """Digest a chunk -- extract working file name and revisions""" | ||
| 98 | lines = chunk[0] | ||
| 99 | key = 'Working file:' | ||
| 100 | keylen = len(key) | ||
| 101 | for line in lines: | ||
| 102 | if line[:keylen] == key: | ||
| 103 | working_file = line[keylen:].strip() | ||
| 104 | break | ||
| 105 | else: | ||
| 106 | working_file = None | ||
| 107 | if branch is None: | ||
| 108 | pass | ||
| 109 | elif branch == "HEAD": | ||
| 110 | branch = re.compile(r"^\d+\.\d+$") | ||
| 111 | else: | ||
| 112 | revisions = {} | ||
| 113 | key = 'symbolic names:\n' | ||
| 114 | found = 0 | ||
| 115 | for line in lines: | ||
| 116 | if line == key: | ||
| 117 | found = 1 | ||
| 118 | elif found: | ||
| 119 | if line[0] in '\t ': | ||
| 120 | tag, rev = line.split() | ||
| 121 | if tag[-1] == ':': | ||
| 122 | tag = tag[:-1] | ||
| 123 | revisions[tag] = rev | ||
| 124 | else: | ||
| 125 | found = 0 | ||
| 126 | rev = revisions.get(branch) | ||
| 127 | branch = re.compile(r"^<>$") # <> to force a mismatch by default | ||
| 128 | if rev: | ||
| 129 | if rev.find('.0.') >= 0: | ||
| 130 | rev = rev.replace('.0.', '.') | ||
| 131 | branch = re.compile(r"^" + re.escape(rev) + r"\.\d+$") | ||
| 132 | records = [] | ||
| 133 | for lines in chunk[1:]: | ||
| 134 | revline = lines[0] | ||
| 135 | dateline = lines[1] | ||
| 136 | text = lines[2:] | ||
| 137 | words = dateline.split() | ||
| 138 | author = None | ||
| 139 | if len(words) >= 3 and words[0] == 'date:': | ||
| 140 | dateword = words[1] | ||
| 141 | timeword = words[2] | ||
| 142 | if timeword[-1:] == ';': | ||
| 143 | timeword = timeword[:-1] | ||
| 144 | date = dateword + ' ' + timeword | ||
| 145 | if len(words) >= 5 and words[3] == 'author:': | ||
| 146 | author = words[4] | ||
| 147 | if author[-1:] == ';': | ||
| 148 | author = author[:-1] | ||
| 149 | else: | ||
| 150 | date = None | ||
| 151 | text.insert(0, revline) | ||
| 152 | words = revline.split() | ||
| 153 | if len(words) >= 2 and words[0] == 'revision': | ||
| 154 | rev = words[1] | ||
| 155 | else: | ||
| 156 | # No 'revision' line -- weird... | ||
| 157 | rev = None | ||
| 158 | text.insert(0, revline) | ||
| 159 | if branch: | ||
| 160 | if rev is None or not branch.match(rev): | ||
| 161 | continue | ||
| 162 | records.append((date, working_file, rev, author, text)) | ||
| 163 | return records | ||
| 164 | |||
| 165 | def format_output(database): | ||
| 166 | prevtext = None | ||
| 167 | prev = [] | ||
| 168 | database.append((None, None, None, None, None)) # Sentinel | ||
| 169 | for (date, working_file, rev, author, text) in database: | ||
| 170 | if text != prevtext: | ||
| 171 | if prev: | ||
| 172 | print sep2, | ||
| 173 | for (p_date, p_working_file, p_rev, p_author) in prev: | ||
| 174 | print p_date, p_author, p_working_file, p_rev | ||
| 175 | sys.stdout.writelines(prevtext) | ||
| 176 | prev = [] | ||
| 177 | prev.append((date, working_file, rev, author)) | ||
| 178 | prevtext = text | ||
| 179 | |||
| 180 | if __name__ == '__main__': | ||
| 181 | try: | ||
| 182 | main() | ||
| 183 | except IOError, e: | ||
| 184 | if e.errno != errno.EPIPE: | ||
| 185 | raise | ||
| @@ -0,0 +1,237 @@ | |||
| 1 | """mailerdaemon - classes to parse mailer-daemon messages""" | ||
| 2 | |||
| 3 | import rfc822 | ||
| 4 | import calendar | ||
| 5 | import re | ||
| 6 | import os | ||
| 7 | import sys | ||
| 8 | |||
| 9 | Unparseable = 'mailerdaemon.Unparseable' | ||
| 10 | |||
| 11 | class ErrorMessage(rfc822.Message): | ||
| 12 | def __init__(self, fp): | ||
| 13 | rfc822.Message.__init__(self, fp) | ||
| 14 | self.sub = '' | ||
| 15 | |||
| 16 | def is_warning(self): | ||
| 17 | sub = self.getheader('Subject') | ||
| 18 | if not sub: | ||
| 19 | return 0 | ||
| 20 | sub = sub.lower() | ||
| 21 | if sub.startswith('waiting mail'): return 1 | ||
| 22 | if 'warning' in sub: return 1 | ||
| 23 | self.sub = sub | ||
| 24 | return 0 | ||
| 25 | |||
| 26 | def get_errors(self): | ||
| 27 | for p in EMPARSERS: | ||
| 28 | self.rewindbody() | ||
| 29 | try: | ||
| 30 | return p(self.fp, self.sub) | ||
| 31 | except Unparseable: | ||
| 32 | pass | ||
| 33 | raise Unparseable | ||
| 34 | |||
| 35 | # List of re's or tuples of re's. | ||
| 36 | # If a re, it should contain at least a group (?P<email>...) which | ||
| 37 | # should refer to the email address. The re can also contain a group | ||
| 38 | # (?P<reason>...) which should refer to the reason (error message). | ||
| 39 | # If no reason is present, the emparse_list_reason list is used to | ||
| 40 | # find a reason. | ||
| 41 | # If a tuple, the tuple should contain 2 re's. The first re finds a | ||
| 42 | # location, the second re is repeated one or more times to find | ||
| 43 | # multiple email addresses. The second re is matched (not searched) | ||
| 44 | # where the previous match ended. | ||
| 45 | # The re's are compiled using the re module. | ||
| 46 | emparse_list_list = [ | ||
| 47 | 'error: (?P<reason>unresolvable): (?P<email>.+)', | ||
| 48 | ('----- The following addresses had permanent fatal errors -----\n', | ||
| 49 | '(?P<email>[^ \n].*)\n( .*\n)?'), | ||
| 50 | 'remote execution.*\n.*rmail (?P<email>.+)', | ||
| 51 | ('The following recipients did not receive your message:\n\n', | ||
| 52 | ' +(?P<email>.*)\n(The following recipients did not receive your message:\n\n)?'), | ||
| 53 | '------- Failure Reasons --------\n\n(?P<reason>.*)\n(?P<email>.*)', | ||
| 54 | '^<(?P<email>.*)>:\n(?P<reason>.*)', | ||
| 55 | '^(?P<reason>User mailbox exceeds allowed size): (?P<email>.+)', | ||
| 56 | '^5\\d{2} <(?P<email>[^\n>]+)>\\.\\.\\. (?P<reason>.+)', | ||
| 57 | '^Original-Recipient: rfc822;(?P<email>.*)', | ||
| 58 | '^did not reach the following recipient\\(s\\):\n\n(?P<email>.*) on .*\n +(?P<reason>.*)', | ||
| 59 | '^ <(?P<email>[^\n>]+)> \\.\\.\\. (?P<reason>.*)', | ||
| 60 | '^Report on your message to: (?P<email>.*)\nReason: (?P<reason>.*)', | ||
| 61 | '^Your message was not delivered to +(?P<email>.*)\n +for the following reason:\n +(?P<reason>.*)', | ||
| 62 | '^ was not +(?P<email>[^ \n].*?) *\n.*\n.*\n.*\n because:.*\n +(?P<reason>[^ \n].*?) *\n', | ||
| 63 | ] | ||
| 64 | # compile the re's in the list and store them in-place. | ||
| 65 | for i in range(len(emparse_list_list)): | ||
| 66 | x = emparse_list_list[i] | ||
| 67 | if type(x) is type(''): | ||
| 68 | x = re.compile(x, re.MULTILINE) | ||
| 69 | else: | ||
| 70 | xl = [] | ||
| 71 | for x in x: | ||
| 72 | xl.append(re.compile(x, re.MULTILINE)) | ||
| 73 | x = tuple(xl) | ||
| 74 | del xl | ||
| 75 | emparse_list_list[i] = x | ||
| 76 | del x | ||
| 77 | del i | ||
| 78 | |||
| 79 | # list of re's used to find reasons (error messages). | ||
| 80 | # if a string, "<>" is replaced by a copy of the email address. | ||
| 81 | # The expressions are searched for in order. After the first match, | ||
| 82 | # no more expressions are searched for. So, order is important. | ||
| 83 | emparse_list_reason = [ | ||
| 84 | r'^5\d{2} <>\.\.\. (?P<reason>.*)', | ||
| 85 | '<>\.\.\. (?P<reason>.*)', | ||
| 86 | re.compile(r'^<<< 5\d{2} (?P<reason>.*)', re.MULTILINE), | ||
| 87 | re.compile('===== stderr was =====\nrmail: (?P<reason>.*)'), | ||
| 88 | re.compile('^Diagnostic-Code: (?P<reason>.*)', re.MULTILINE), | ||
| 89 | ] | ||
| 90 | emparse_list_from = re.compile('^From:', re.IGNORECASE|re.MULTILINE) | ||
| 91 | def emparse_list(fp, sub): | ||
| 92 | data = fp.read() | ||
| 93 | res = emparse_list_from.search(data) | ||
| 94 | if res is None: | ||
| 95 | from_index = len(data) | ||
| 96 | else: | ||
| 97 | from_index = res.start(0) | ||
| 98 | errors = [] | ||
| 99 | emails = [] | ||
| 100 | reason = None | ||
| 101 | for regexp in emparse_list_list: | ||
| 102 | if type(regexp) is type(()): | ||
| 103 | res = regexp[0].search(data, 0, from_index) | ||
| 104 | if res is not None: | ||
| 105 | try: | ||
| 106 | reason = res.group('reason') | ||
| 107 | except IndexError: | ||
| 108 | pass | ||
| 109 | while 1: | ||
| 110 | res = regexp[1].match(data, res.end(0), from_index) | ||
| 111 | if res is None: | ||
| 112 | break | ||
| 113 | emails.append(res.group('email')) | ||
| 114 | break | ||
| 115 | else: | ||
| 116 | res = regexp.search(data, 0, from_index) | ||
| 117 | if res is not None: | ||
| 118 | emails.append(res.group('email')) | ||
| 119 | try: | ||
| 120 | reason = res.group('reason') | ||
| 121 | except IndexError: | ||
| 122 | pass | ||
| 123 | break | ||
| 124 | if not emails: | ||
| 125 | raise Unparseable | ||
| 126 | if not reason: | ||
| 127 | reason = sub | ||
| 128 | if reason[:15] == 'returned mail: ': | ||
| 129 | reason = reason[15:] | ||
| 130 | for regexp in emparse_list_reason: | ||
| 131 | if type(regexp) is type(''): | ||
| 132 | for i in range(len(emails)-1,-1,-1): | ||
| 133 | email = emails[i] | ||
| 134 | exp = re.compile(re.escape(email).join(regexp.split('<>')), re.MULTILINE) | ||
| 135 | res = exp.search(data) | ||
| 136 | if res is not None: | ||
| 137 | errors.append(' '.join((email.strip()+': '+res.group('reason')).split())) | ||
| 138 | del emails[i] | ||
| 139 | continue | ||
| 140 | res = regexp.search(data) | ||
| 141 | if res is not None: | ||
| 142 | reason = res.group('reason') | ||
| 143 | break | ||
| 144 | for email in emails: | ||
| 145 | errors.append(' '.join((email.strip()+': '+reason).split())) | ||
| 146 | return errors | ||
| 147 | |||
| 148 | EMPARSERS = [emparse_list, ] | ||
| 149 | |||
| 150 | def sort_numeric(a, b): | ||
| 151 | a = int(a) | ||
| 152 | b = int(b) | ||
| 153 | if a < b: return -1 | ||
| 154 | elif a > b: return 1 | ||
| 155 | else: return 0 | ||
| 156 | |||
| 157 | def parsedir(dir, modify): | ||
| 158 | os.chdir(dir) | ||
| 159 | pat = re.compile('^[0-9]*$') | ||
| 160 | errordict = {} | ||
| 161 | errorfirst = {} | ||
| 162 | errorlast = {} | ||
| 163 | nok = nwarn = nbad = 0 | ||
| 164 | |||
| 165 | # find all numeric file names and sort them | ||
| 166 | files = filter(lambda fn, pat=pat: pat.match(fn) is not None, os.listdir('.')) | ||
| 167 | files.sort(sort_numeric) | ||
| 168 | |||
| 169 | for fn in files: | ||
| 170 | # Lets try to parse the file. | ||
| 171 | fp = open(fn) | ||
| 172 | m = ErrorMessage(fp) | ||
| 173 | sender = m.getaddr('From') | ||
| 174 | print '%s\t%-40s\t'%(fn, sender[1]), | ||
| 175 | |||
| 176 | if m.is_warning(): | ||
| 177 | fp.close() | ||
| 178 | print 'warning only' | ||
| 179 | nwarn = nwarn + 1 | ||
| 180 | if modify: | ||
| 181 | os.rename(fn, ','+fn) | ||
| 182 | ## os.unlink(fn) | ||
| 183 | continue | ||
| 184 | |||
| 185 | try: | ||
| 186 | errors = m.get_errors() | ||
| 187 | except Unparseable: | ||
| 188 | print '** Not parseable' | ||
| 189 | nbad = nbad + 1 | ||
| 190 | fp.close() | ||
| 191 | continue | ||
| 192 | print len(errors), 'errors' | ||
| 193 | |||
| 194 | # Remember them | ||
| 195 | for e in errors: | ||
| 196 | try: | ||
| 197 | mm, dd = m.getdate('date')[1:1+2] | ||
| 198 | date = '%s %02d' % (calendar.month_abbr[mm], dd) | ||
| 199 | except: | ||
| 200 | date = '??????' | ||
| 201 | if not errordict.has_key(e): | ||
| 202 | errordict[e] = 1 | ||
| 203 | errorfirst[e] = '%s (%s)' % (fn, date) | ||
| 204 | else: | ||
| 205 | errordict[e] = errordict[e] + 1 | ||
| 206 | errorlast[e] = '%s (%s)' % (fn, date) | ||
| 207 | |||
| 208 | fp.close() | ||
| 209 | nok = nok + 1 | ||
| 210 | if modify: | ||
| 211 | os.rename(fn, ','+fn) | ||
| 212 | ## os.unlink(fn) | ||
| 213 | |||
| 214 | print '--------------' | ||
| 215 | print nok, 'files parsed,',nwarn,'files warning-only,', | ||
| 216 | print nbad,'files unparseable' | ||
| 217 | print '--------------' | ||
| 218 | list = [] | ||
| 219 | for e in errordict.keys(): | ||
| 220 | list.append((errordict[e], errorfirst[e], errorlast[e], e)) | ||
| 221 | list.sort() | ||
| 222 | for num, first, last, e in list: | ||
| 223 | print '%d %s - %s\t%s' % (num, first, last, e) | ||
| 224 | |||
| 225 | def main(): | ||
| 226 | modify = 0 | ||
| 227 | if len(sys.argv) > 1 and sys.argv[1] == '-d': | ||
| 228 | modify = 1 | ||
| 229 | del sys.argv[1] | ||
| 230 | if len(sys.argv) > 1: | ||
| 231 | for folder in sys.argv[1:]: | ||
| 232 | parsedir(folder, modify) | ||
| 233 | else: | ||
| 234 | parsedir('/ufs/jack/Mail/errorsinbox', modify) | ||
| 235 | |||
| 236 | if __name__ == '__main__' or sys.argv[0] == __name__: | ||
| 237 | main() | ||
| @@ -0,0 +1,90 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | """Python utility to print MD5 checksums of argument files. | ||
| 4 | """ | ||
| 5 | |||
| 6 | |||
| 7 | bufsize = 8096 | ||
| 8 | fnfilter = None | ||
| 9 | rmode = 'rb' | ||
| 10 | |||
| 11 | usage = """ | ||
| 12 | usage: sum5 [-b] [-t] [-l] [-s bufsize] [file ...] | ||
| 13 | -b : read files in binary mode (default) | ||
| 14 | -t : read files in text mode (you almost certainly don't want this!) | ||
| 15 | -l : print last pathname component only | ||
| 16 | -s bufsize: read buffer size (default %d) | ||
| 17 | file ... : files to sum; '-' or no files means stdin | ||
| 18 | """ % bufsize | ||
| 19 | |||
| 20 | import sys | ||
| 21 | import os | ||
| 22 | import getopt | ||
| 23 | import md5 | ||
| 24 | |||
| 25 | def sum(*files): | ||
| 26 | sts = 0 | ||
| 27 | if files and isinstance(files[-1], file): | ||
| 28 | out, files = files[-1], files[:-1] | ||
| 29 | else: | ||
| 30 | out = sys.stdout | ||
| 31 | if len(files) == 1 and not isinstance(files[0], str): | ||
| 32 | files = files[0] | ||
| 33 | for f in files: | ||
| 34 | if isinstance(f, str): | ||
| 35 | if f == '-': | ||
| 36 | sts = printsumfp(sys.stdin, '<stdin>', out) or sts | ||
| 37 | else: | ||
| 38 | sts = printsum(f, out) or sts | ||
| 39 | else: | ||
| 40 | sts = sum(f, out) or sts | ||
| 41 | return sts | ||
| 42 | |||
| 43 | def printsum(filename, out=sys.stdout): | ||
| 44 | try: | ||
| 45 | fp = open(filename, rmode) | ||
| 46 | except IOError, msg: | ||
| 47 | sys.stderr.write('%s: Can\'t open: %s\n' % (filename, msg)) | ||
| 48 | return 1 | ||
| 49 | if fnfilter: | ||
| 50 | filename = fnfilter(filename) | ||
| 51 | sts = printsumfp(fp, filename, out) | ||
| 52 | fp.close() | ||
| 53 | return sts | ||
| 54 | |||
| 55 | def printsumfp(fp, filename, out=sys.stdout): | ||
| 56 | m = md5.new() | ||
| 57 | try: | ||
| 58 | while 1: | ||
| 59 | data = fp.read(bufsize) | ||
| 60 | if not data: | ||
| 61 | break | ||
| 62 | m.update(data) | ||
| 63 | except IOError, msg: | ||
| 64 | sys.stderr.write('%s: I/O error: %s\n' % (filename, msg)) | ||
| 65 | return 1 | ||
| 66 | out.write('%s %s\n' % (m.hexdigest(), filename)) | ||
| 67 | return 0 | ||
| 68 | |||
| 69 | def main(args = sys.argv[1:], out=sys.stdout): | ||
| 70 | global fnfilter, rmode, bufsize | ||
| 71 | try: | ||
| 72 | opts, args = getopt.getopt(args, 'blts:') | ||
| 73 | except getopt.error, msg: | ||
| 74 | sys.stderr.write('%s: %s\n%s' % (sys.argv[0], msg, usage)) | ||
| 75 | return 2 | ||
| 76 | for o, a in opts: | ||
| 77 | if o == '-l': | ||
| 78 | fnfilter = os.path.basename | ||
| 79 | elif o == '-b': | ||
| 80 | rmode = 'rb' | ||
| 81 | elif o == '-t': | ||
| 82 | rmode = 'r' | ||
| 83 | elif o == '-s': | ||
| 84 | bufsize = int(a) | ||
| 85 | if not args: | ||
| 86 | args = ['-'] | ||
| 87 | return sum(args, out) | ||
| 88 | |||
| 89 | if __name__ == '__main__' or __name__ == sys.argv[0]: | ||
| 90 | sys.exit(main(sys.argv[1:], sys.stdout)) | ||
| @@ -0,0 +1,171 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Fix Python source files to avoid using | ||
| 4 | # def method(self, (arg1, ..., argn)): | ||
| 5 | # instead of the more rational | ||
| 6 | # def method(self, arg1, ..., argn): | ||
| 7 | # | ||
| 8 | # Command line arguments are files or directories to be processed. | ||
| 9 | # Directories are searched recursively for files whose name looks | ||
| 10 | # like a python module. | ||
| 11 | # Symbolic links are always ignored (except as explicit directory | ||
| 12 | # arguments). Of course, the original file is kept as a back-up | ||
| 13 | # (with a "~" attached to its name). | ||
| 14 | # It complains about binaries (files containing null bytes) | ||
| 15 | # and about files that are ostensibly not Python files: if the first | ||
| 16 | # line starts with '#!' and does not contain the string 'python'. | ||
| 17 | # | ||
| 18 | # Changes made are reported to stdout in a diff-like format. | ||
| 19 | # | ||
| 20 | # Undoubtedly you can do this using find and sed or perl, but this is | ||
| 21 | # a nice example of Python code that recurses down a directory tree | ||
| 22 | # and uses regular expressions. Also note several subtleties like | ||
| 23 | # preserving the file's mode and avoiding to even write a temp file | ||
| 24 | # when no changes are needed for a file. | ||
| 25 | # | ||
| 26 | # NB: by changing only the function fixline() you can turn this | ||
| 27 | # into a program for a different change to Python programs... | ||
| 28 | |||
| 29 | import sys | ||
| 30 | import re | ||
| 31 | import os | ||
| 32 | from stat import * | ||
| 33 | |||
| 34 | err = sys.stderr.write | ||
| 35 | dbg = err | ||
| 36 | rep = sys.stdout.write | ||
| 37 | |||
| 38 | def main(): | ||
| 39 | bad = 0 | ||
| 40 | if not sys.argv[1:]: # No arguments | ||
| 41 | err('usage: ' + sys.argv[0] + ' file-or-directory ...\n') | ||
| 42 | sys.exit(2) | ||
| 43 | for arg in sys.argv[1:]: | ||
| 44 | if os.path.isdir(arg): | ||
| 45 | if recursedown(arg): bad = 1 | ||
| 46 | elif os.path.islink(arg): | ||
| 47 | err(arg + ': will not process symbolic links\n') | ||
| 48 | bad = 1 | ||
| 49 | else: | ||
| 50 | if fix(arg): bad = 1 | ||
| 51 | sys.exit(bad) | ||
| 52 | |||
| 53 | ispythonprog = re.compile('^[a-zA-Z0-9_]+\.py$') | ||
| 54 | def ispython(name): | ||
| 55 | return ispythonprog.match(name) >= 0 | ||
| 56 | |||
| 57 | def recursedown(dirname): | ||
| 58 | dbg('recursedown(%r)\n' % (dirname,)) | ||
| 59 | bad = 0 | ||
| 60 | try: | ||
| 61 | names = os.listdir(dirname) | ||
| 62 | except os.error, msg: | ||
| 63 | err('%s: cannot list directory: %r\n' % (dirname, msg)) | ||
| 64 | return 1 | ||
| 65 | names.sort() | ||
| 66 | subdirs = [] | ||
| 67 | for name in names: | ||
| 68 | if name in (os.curdir, os.pardir): continue | ||
| 69 | fullname = os.path.join(dirname, name) | ||
| 70 | if os.path.islink(fullname): pass | ||
| 71 | elif os.path.isdir(fullname): | ||
| 72 | subdirs.append(fullname) | ||
| 73 | elif ispython(name): | ||
| 74 | if fix(fullname): bad = 1 | ||
| 75 | for fullname in subdirs: | ||
| 76 | if recursedown(fullname): bad = 1 | ||
| 77 | return bad | ||
| 78 | |||
| 79 | def fix(filename): | ||
| 80 | ## dbg('fix(%r)\n' % (filename,)) | ||
| 81 | try: | ||
| 82 | f = open(filename, 'r') | ||
| 83 | except IOError, msg: | ||
| 84 | err('%s: cannot open: %r\n' % (filename, msg)) | ||
| 85 | return 1 | ||
| 86 | head, tail = os.path.split(filename) | ||
| 87 | tempname = os.path.join(head, '@' + tail) | ||
| 88 | g = None | ||
| 89 | # If we find a match, we rewind the file and start over but | ||
| 90 | # now copy everything to a temp file. | ||
| 91 | lineno = 0 | ||
| 92 | while 1: | ||
| 93 | line = f.readline() | ||
| 94 | if not line: break | ||
| 95 | lineno = lineno + 1 | ||
| 96 | if g is None and '\0' in line: | ||
| 97 | # Check for binary files | ||
| 98 | err(filename + ': contains null bytes; not fixed\n') | ||
| 99 | f.close() | ||
| 100 | return 1 | ||
| 101 | if lineno == 1 and g is None and line[:2] == '#!': | ||
| 102 | # Check for non-Python scripts | ||
| 103 | words = line[2:].split() | ||
| 104 | if words and re.search('[pP]ython', words[0]) < 0: | ||
| 105 | msg = filename + ': ' + words[0] | ||
| 106 | msg = msg + ' script; not fixed\n' | ||
| 107 | err(msg) | ||
| 108 | f.close() | ||
| 109 | return 1 | ||
| 110 | while line[-2:] == '\\\n': | ||
| 111 | nextline = f.readline() | ||
| 112 | if not nextline: break | ||
| 113 | line = line + nextline | ||
| 114 | lineno = lineno + 1 | ||
| 115 | newline = fixline(line) | ||
| 116 | if newline != line: | ||
| 117 | if g is None: | ||
| 118 | try: | ||
| 119 | g = open(tempname, 'w') | ||
| 120 | except IOError, msg: | ||
| 121 | f.close() | ||
| 122 | err('%s: cannot create: %r\n' % (tempname, msg)) | ||
| 123 | return 1 | ||
| 124 | f.seek(0) | ||
| 125 | lineno = 0 | ||
| 126 | rep(filename + ':\n') | ||
| 127 | continue # restart from the beginning | ||
| 128 | rep(repr(lineno) + '\n') | ||
| 129 | rep('< ' + line) | ||
| 130 | rep('> ' + newline) | ||
| 131 | if g is not None: | ||
| 132 | g.write(newline) | ||
| 133 | |||
| 134 | # End of file | ||
| 135 | f.close() | ||
| 136 | if not g: return 0 # No changes | ||
| 137 | |||
| 138 | # Finishing touch -- move files | ||
| 139 | |||
| 140 | # First copy the file's mode to the temp file | ||
| 141 | try: | ||
| 142 | statbuf = os.stat(filename) | ||
| 143 | os.chmod(tempname, statbuf[ST_MODE] & 07777) | ||
| 144 | except os.error, msg: | ||
| 145 | err('%s: warning: chmod failed (%r)\n' % (tempname, msg)) | ||
| 146 | # Then make a backup of the original file as filename~ | ||
| 147 | try: | ||
| 148 | os.rename(filename, filename + '~') | ||
| 149 | except os.error, msg: | ||
| 150 | err('%s: warning: backup failed (%r)\n' % (filename, msg)) | ||
| 151 | # Now move the temp file to the original file | ||
| 152 | try: | ||
| 153 | os.rename(tempname, filename) | ||
| 154 | except os.error, msg: | ||
| 155 | err('%s: rename failed (%r)\n' % (filename, msg)) | ||
| 156 | return 1 | ||
| 157 | # Return succes | ||
| 158 | return 0 | ||
| 159 | |||
| 160 | |||
| 161 | fixpat = '^[ \t]+def +[a-zA-Z0-9_]+ *( *self *, *(( *(.*) *)) *) *:' | ||
| 162 | fixprog = re.compile(fixpat) | ||
| 163 | |||
| 164 | def fixline(line): | ||
| 165 | if fixprog.match(line) >= 0: | ||
| 166 | (a, b), (c, d) = fixprog.regs[1:3] | ||
| 167 | line = line[:a] + line[c:d] + line[b:] | ||
| 168 | return line | ||
| 169 | |||
| 170 | if __name__ == '__main__': | ||
| 171 | main() | ||
| @@ -0,0 +1,66 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # mkreal | ||
| 4 | # | ||
| 5 | # turn a symlink to a directory into a real directory | ||
| 6 | |||
| 7 | import sys | ||
| 8 | import os | ||
| 9 | from stat import * | ||
| 10 | |||
| 11 | join = os.path.join | ||
| 12 | |||
| 13 | error = 'mkreal error' | ||
| 14 | |||
| 15 | BUFSIZE = 32*1024 | ||
| 16 | |||
| 17 | def mkrealfile(name): | ||
| 18 | st = os.stat(name) # Get the mode | ||
| 19 | mode = S_IMODE(st[ST_MODE]) | ||
| 20 | linkto = os.readlink(name) # Make sure again it's a symlink | ||
| 21 | f_in = open(name, 'r') # This ensures it's a file | ||
| 22 | os.unlink(name) | ||
| 23 | f_out = open(name, 'w') | ||
| 24 | while 1: | ||
| 25 | buf = f_in.read(BUFSIZE) | ||
| 26 | if not buf: break | ||
| 27 | f_out.write(buf) | ||
| 28 | del f_out # Flush data to disk before changing mode | ||
| 29 | os.chmod(name, mode) | ||
| 30 | |||
| 31 | def mkrealdir(name): | ||
| 32 | st = os.stat(name) # Get the mode | ||
| 33 | mode = S_IMODE(st[ST_MODE]) | ||
| 34 | linkto = os.readlink(name) | ||
| 35 | files = os.listdir(name) | ||
| 36 | os.unlink(name) | ||
| 37 | os.mkdir(name, mode) | ||
| 38 | os.chmod(name, mode) | ||
| 39 | linkto = join(os.pardir, linkto) | ||
| 40 | # | ||
| 41 | for filename in files: | ||
| 42 | if filename not in (os.curdir, os.pardir): | ||
| 43 | os.symlink(join(linkto, filename), join(name, filename)) | ||
| 44 | |||
| 45 | def main(): | ||
| 46 | sys.stdout = sys.stderr | ||
| 47 | progname = os.path.basename(sys.argv[0]) | ||
| 48 | if progname == '-c': progname = 'mkreal' | ||
| 49 | args = sys.argv[1:] | ||
| 50 | if not args: | ||
| 51 | print 'usage:', progname, 'path ...' | ||
| 52 | sys.exit(2) | ||
| 53 | status = 0 | ||
| 54 | for name in args: | ||
| 55 | if not os.path.islink(name): | ||
| 56 | print progname+':', name+':', 'not a symlink' | ||
| 57 | status = 1 | ||
| 58 | else: | ||
| 59 | if os.path.isdir(name): | ||
| 60 | mkrealdir(name) | ||
| 61 | else: | ||
| 62 | mkrealfile(name) | ||
| 63 | sys.exit(status) | ||
| 64 | |||
| 65 | if __name__ == '__main__': | ||
| 66 | main() | ||
| @@ -0,0 +1,133 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Module ndiff version 1.7.0 | ||
| 4 | # Released to the public domain 08-Dec-2000, | ||
| 5 | # by Tim Peters (tim.one@home.com). | ||
| 6 | |||
| 7 | # Provided as-is; use at your own risk; no warranty; no promises; enjoy! | ||
| 8 | |||
| 9 | # ndiff.py is now simply a front-end to the difflib.ndiff() function. | ||
| 10 | # Originally, it contained the difflib.SequenceMatcher class as well. | ||
| 11 | # This completes the raiding of reusable code from this formerly | ||
| 12 | # self-contained script. | ||
| 13 | |||
| 14 | """ndiff [-q] file1 file2 | ||
| 15 | or | ||
| 16 | ndiff (-r1 | -r2) < ndiff_output > file1_or_file2 | ||
| 17 | |||
| 18 | Print a human-friendly file difference report to stdout. Both inter- | ||
| 19 | and intra-line differences are noted. In the second form, recreate file1 | ||
| 20 | (-r1) or file2 (-r2) on stdout, from an ndiff report on stdin. | ||
| 21 | |||
| 22 | In the first form, if -q ("quiet") is not specified, the first two lines | ||
| 23 | of output are | ||
| 24 | |||
| 25 | -: file1 | ||
| 26 | +: file2 | ||
| 27 | |||
| 28 | Each remaining line begins with a two-letter code: | ||
| 29 | |||
| 30 | "- " line unique to file1 | ||
| 31 | "+ " line unique to file2 | ||
| 32 | " " line common to both files | ||
| 33 | "? " line not present in either input file | ||
| 34 | |||
| 35 | Lines beginning with "? " attempt to guide the eye to intraline | ||
| 36 | differences, and were not present in either input file. These lines can be | ||
| 37 | confusing if the source files contain tab characters. | ||
| 38 | |||
| 39 | The first file can be recovered by retaining only lines that begin with | ||
| 40 | " " or "- ", and deleting those 2-character prefixes; use ndiff with -r1. | ||
| 41 | |||
| 42 | The second file can be recovered similarly, but by retaining only " " and | ||
| 43 | "+ " lines; use ndiff with -r2; or, on Unix, the second file can be | ||
| 44 | recovered by piping the output through | ||
| 45 | |||
| 46 | sed -n '/^[+ ] /s/^..//p' | ||
| 47 | """ | ||
| 48 | |||
| 49 | __version__ = 1, 7, 0 | ||
| 50 | |||
| 51 | import difflib, sys | ||
| 52 | |||
| 53 | def fail(msg): | ||
| 54 | out = sys.stderr.write | ||
| 55 | out(msg + "\n\n") | ||
| 56 | out(__doc__) | ||
| 57 | return 0 | ||
| 58 | |||
| 59 | # open a file & return the file object; gripe and return 0 if it | ||
| 60 | # couldn't be opened | ||
| 61 | def fopen(fname): | ||
| 62 | try: | ||
| 63 | return open(fname, 'U') | ||
| 64 | except IOError, detail: | ||
| 65 | return fail("couldn't open " + fname + ": " + str(detail)) | ||
| 66 | |||
| 67 | # open two files & spray the diff to stdout; return false iff a problem | ||
| 68 | def fcompare(f1name, f2name): | ||
| 69 | f1 = fopen(f1name) | ||
| 70 | f2 = fopen(f2name) | ||
| 71 | if not f1 or not f2: | ||
| 72 | return 0 | ||
| 73 | |||
| 74 | a = f1.readlines(); f1.close() | ||
| 75 | b = f2.readlines(); f2.close() | ||
| 76 | for line in difflib.ndiff(a, b): | ||
| 77 | print line, | ||
| 78 | |||
| 79 | return 1 | ||
| 80 | |||
| 81 | # crack args (sys.argv[1:] is normal) & compare; | ||
| 82 | # return false iff a problem | ||
| 83 | |||
| 84 | def main(args): | ||
| 85 | import getopt | ||
| 86 | try: | ||
| 87 | opts, args = getopt.getopt(args, "qr:") | ||
| 88 | except getopt.error, detail: | ||
| 89 | return fail(str(detail)) | ||
| 90 | noisy = 1 | ||
| 91 | qseen = rseen = 0 | ||
| 92 | for opt, val in opts: | ||
| 93 | if opt == "-q": | ||
| 94 | qseen = 1 | ||
| 95 | noisy = 0 | ||
| 96 | elif opt == "-r": | ||
| 97 | rseen = 1 | ||
| 98 | whichfile = val | ||
| 99 | if qseen and rseen: | ||
| 100 | return fail("can't specify both -q and -r") | ||
| 101 | if rseen: | ||
| 102 | if args: | ||
| 103 | return fail("no args allowed with -r option") | ||
| 104 | if whichfile in ("1", "2"): | ||
| 105 | restore(whichfile) | ||
| 106 | return 1 | ||
| 107 | return fail("-r value must be 1 or 2") | ||
| 108 | if len(args) != 2: | ||
| 109 | return fail("need 2 filename args") | ||
| 110 | f1name, f2name = args | ||
| 111 | if noisy: | ||
| 112 | print '-:', f1name | ||
| 113 | print '+:', f2name | ||
| 114 | return fcompare(f1name, f2name) | ||
| 115 | |||
| 116 | # read ndiff output from stdin, and print file1 (which=='1') or | ||
| 117 | # file2 (which=='2') to stdout | ||
| 118 | |||
| 119 | def restore(which): | ||
| 120 | restored = difflib.restore(sys.stdin.readlines(), which) | ||
| 121 | sys.stdout.writelines(restored) | ||
| 122 | |||
| 123 | if __name__ == '__main__': | ||
| 124 | args = sys.argv[1:] | ||
| 125 | if "-profile" in args: | ||
| 126 | import profile, pstats | ||
| 127 | args.remove("-profile") | ||
| 128 | statf = "ndiff.pro" | ||
| 129 | profile.run("main(args)", statf) | ||
| 130 | stats = pstats.Stats(statf) | ||
| 131 | stats.strip_dirs().sort_stats('time').print_stats() | ||
| 132 | else: | ||
| 133 | main(args) | ||
| @@ -0,0 +1,103 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | """nm2def.py | ||
| 3 | |||
| 4 | Helpers to extract symbols from Unix libs and auto-generate | ||
| 5 | Windows definition files from them. Depends on nm(1). Tested | ||
| 6 | on Linux and Solaris only (-p option to nm is for Solaris only). | ||
| 7 | |||
| 8 | By Marc-Andre Lemburg, Aug 1998. | ||
| 9 | |||
| 10 | Additional notes: the output of nm is supposed to look like this: | ||
| 11 | |||
| 12 | acceler.o: | ||
| 13 | 000001fd T PyGrammar_AddAccelerators | ||
| 14 | U PyGrammar_FindDFA | ||
| 15 | 00000237 T PyGrammar_RemoveAccelerators | ||
| 16 | U _IO_stderr_ | ||
| 17 | U exit | ||
| 18 | U fprintf | ||
| 19 | U free | ||
| 20 | U malloc | ||
| 21 | U printf | ||
| 22 | |||
| 23 | grammar1.o: | ||
| 24 | 00000000 T PyGrammar_FindDFA | ||
| 25 | 00000034 T PyGrammar_LabelRepr | ||
| 26 | U _PyParser_TokenNames | ||
| 27 | U abort | ||
| 28 | U printf | ||
| 29 | U sprintf | ||
| 30 | |||
| 31 | ... | ||
| 32 | |||
| 33 | Even if this isn't the default output of your nm, there is generally an | ||
| 34 | option to produce this format (since it is the original v7 Unix format). | ||
| 35 | |||
| 36 | """ | ||
| 37 | import os,re,sys | ||
| 38 | |||
| 39 | PYTHONLIB = 'libpython'+sys.version[:3]+'.a' | ||
| 40 | PC_PYTHONLIB = 'Python'+sys.version[0]+sys.version[2]+'.dll' | ||
| 41 | NM = 'nm -p -g %s' # For Linux, use "nm -g %s" | ||
| 42 | |||
| 43 | def symbols(lib=PYTHONLIB,types=('T','C','D')): | ||
| 44 | |||
| 45 | lines = os.popen(NM % lib).readlines() | ||
| 46 | lines = [s.strip() for s in lines] | ||
| 47 | symbols = {} | ||
| 48 | for line in lines: | ||
| 49 | if len(line) == 0 or ':' in line: | ||
| 50 | continue | ||
| 51 | items = line.split() | ||
| 52 | if len(items) != 3: | ||
| 53 | continue | ||
| 54 | address, type, name = items | ||
| 55 | if type not in types: | ||
| 56 | continue | ||
| 57 | symbols[name] = address,type | ||
| 58 | return symbols | ||
| 59 | |||
| 60 | def export_list(symbols): | ||
| 61 | |||
| 62 | data = [] | ||
| 63 | code = [] | ||
| 64 | for name,(addr,type) in symbols.items(): | ||
| 65 | if type in ('C','D'): | ||
| 66 | data.append('\t'+name) | ||
| 67 | else: | ||
| 68 | code.append('\t'+name) | ||
| 69 | data.sort() | ||
| 70 | data.append('') | ||
| 71 | code.sort() | ||
| 72 | return ' DATA\n'.join(data)+'\n'+'\n'.join(code) | ||
| 73 | |||
| 74 | # Definition file template | ||
| 75 | DEF_TEMPLATE = """\ | ||
| 76 | EXPORTS | ||
| 77 | %s | ||
| 78 | """ | ||
| 79 | |||
| 80 | # Special symbols that have to be included even though they don't | ||
| 81 | # pass the filter | ||
| 82 | SPECIALS = ( | ||
| 83 | ) | ||
| 84 | |||
| 85 | def filter_Python(symbols,specials=SPECIALS): | ||
| 86 | |||
| 87 | for name in symbols.keys(): | ||
| 88 | if name[:2] == 'Py' or name[:3] == '_Py': | ||
| 89 | pass | ||
| 90 | elif name not in specials: | ||
| 91 | del symbols[name] | ||
| 92 | |||
| 93 | def main(): | ||
| 94 | |||
| 95 | s = symbols(PYTHONLIB) | ||
| 96 | filter_Python(s) | ||
| 97 | exports = export_list(s) | ||
| 98 | f = sys.stdout # open('PC/python_nt.def','w') | ||
| 99 | f.write(DEF_TEMPLATE % (exports)) | ||
| 100 | f.close() | ||
| 101 | |||
| 102 | if __name__ == '__main__': | ||
| 103 | main() | ||
| @@ -0,0 +1,215 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # objgraph | ||
| 4 | # | ||
| 5 | # Read "nm -o" input (on IRIX: "nm -Bo") of a set of libraries or modules | ||
| 6 | # and print various interesting listings, such as: | ||
| 7 | # | ||
| 8 | # - which names are used but not defined in the set (and used where), | ||
| 9 | # - which names are defined in the set (and where), | ||
| 10 | # - which modules use which other modules, | ||
| 11 | # - which modules are used by which other modules. | ||
| 12 | # | ||
| 13 | # Usage: objgraph [-cdu] [file] ... | ||
| 14 | # -c: print callers per objectfile | ||
| 15 | # -d: print callees per objectfile | ||
| 16 | # -u: print usage of undefined symbols | ||
| 17 | # If none of -cdu is specified, all are assumed. | ||
| 18 | # Use "nm -o" to generate the input (on IRIX: "nm -Bo"), | ||
| 19 | # e.g.: nm -o /lib/libc.a | objgraph | ||
| 20 | |||
| 21 | |||
| 22 | import sys | ||
| 23 | import os | ||
| 24 | import getopt | ||
| 25 | import re | ||
| 26 | |||
| 27 | # Types of symbols. | ||
| 28 | # | ||
| 29 | definitions = 'TRGDSBAEC' | ||
| 30 | externals = 'UV' | ||
| 31 | ignore = 'Nntrgdsbavuc' | ||
| 32 | |||
| 33 | # Regular expression to parse "nm -o" output. | ||
| 34 | # | ||
| 35 | matcher = re.compile('(.*):\t?........ (.) (.*)$') | ||
| 36 | |||
| 37 | # Store "item" in "dict" under "key". | ||
| 38 | # The dictionary maps keys to lists of items. | ||
| 39 | # If there is no list for the key yet, it is created. | ||
| 40 | # | ||
| 41 | def store(dict, key, item): | ||
| 42 | if dict.has_key(key): | ||
| 43 | dict[key].append(item) | ||
| 44 | else: | ||
| 45 | dict[key] = [item] | ||
| 46 | |||
| 47 | # Return a flattened version of a list of strings: the concatenation | ||
| 48 | # of its elements with intervening spaces. | ||
| 49 | # | ||
| 50 | def flat(list): | ||
| 51 | s = '' | ||
| 52 | for item in list: | ||
| 53 | s = s + ' ' + item | ||
| 54 | return s[1:] | ||
| 55 | |||
| 56 | # Global variables mapping defined/undefined names to files and back. | ||
| 57 | # | ||
| 58 | file2undef = {} | ||
| 59 | def2file = {} | ||
| 60 | file2def = {} | ||
| 61 | undef2file = {} | ||
| 62 | |||
| 63 | # Read one input file and merge the data into the tables. | ||
| 64 | # Argument is an open file. | ||
| 65 | # | ||
| 66 | def readinput(fp): | ||
| 67 | while 1: | ||
| 68 | s = fp.readline() | ||
| 69 | if not s: | ||
| 70 | break | ||
| 71 | # If you get any output from this line, | ||
| 72 | # it is probably caused by an unexpected input line: | ||
| 73 | if matcher.search(s) < 0: s; continue # Shouldn't happen | ||
| 74 | (ra, rb), (r1a, r1b), (r2a, r2b), (r3a, r3b) = matcher.regs[:4] | ||
| 75 | fn, name, type = s[r1a:r1b], s[r3a:r3b], s[r2a:r2b] | ||
| 76 | if type in definitions: | ||
| 77 | store(def2file, name, fn) | ||
| 78 | store(file2def, fn, name) | ||
| 79 | elif type in externals: | ||
| 80 | store(file2undef, fn, name) | ||
| 81 | store(undef2file, name, fn) | ||
| 82 | elif not type in ignore: | ||
| 83 | print fn + ':' + name + ': unknown type ' + type | ||
| 84 | |||
| 85 | # Print all names that were undefined in some module and where they are | ||
| 86 | # defined. | ||
| 87 | # | ||
| 88 | def printcallee(): | ||
| 89 | flist = file2undef.keys() | ||
| 90 | flist.sort() | ||
| 91 | for filename in flist: | ||
| 92 | print filename + ':' | ||
| 93 | elist = file2undef[filename] | ||
| 94 | elist.sort() | ||
| 95 | for ext in elist: | ||
| 96 | if len(ext) >= 8: | ||
| 97 | tabs = '\t' | ||
| 98 | else: | ||
| 99 | tabs = '\t\t' | ||
| 100 | if not def2file.has_key(ext): | ||
| 101 | print '\t' + ext + tabs + ' *undefined' | ||
| 102 | else: | ||
| 103 | print '\t' + ext + tabs + flat(def2file[ext]) | ||
| 104 | |||
| 105 | # Print for each module the names of the other modules that use it. | ||
| 106 | # | ||
| 107 | def printcaller(): | ||
| 108 | files = file2def.keys() | ||
| 109 | files.sort() | ||
| 110 | for filename in files: | ||
| 111 | callers = [] | ||
| 112 | for label in file2def[filename]: | ||
| 113 | if undef2file.has_key(label): | ||
| 114 | callers = callers + undef2file[label] | ||
| 115 | if callers: | ||
| 116 | callers.sort() | ||
| 117 | print filename + ':' | ||
| 118 | lastfn = '' | ||
| 119 | for fn in callers: | ||
| 120 | if fn <> lastfn: | ||
| 121 | print '\t' + fn | ||
| 122 | lastfn = fn | ||
| 123 | else: | ||
| 124 | print filename + ': unused' | ||
| 125 | |||
| 126 | # Print undefined names and where they are used. | ||
| 127 | # | ||
| 128 | def printundef(): | ||
| 129 | undefs = {} | ||
| 130 | for filename in file2undef.keys(): | ||
| 131 | for ext in file2undef[filename]: | ||
| 132 | if not def2file.has_key(ext): | ||
| 133 | store(undefs, ext, filename) | ||
| 134 | elist = undefs.keys() | ||
| 135 | elist.sort() | ||
| 136 | for ext in elist: | ||
| 137 | print ext + ':' | ||
| 138 | flist = undefs[ext] | ||
| 139 | flist.sort() | ||
| 140 | for filename in flist: | ||
| 141 | print '\t' + filename | ||
| 142 | |||
| 143 | # Print warning messages about names defined in more than one file. | ||
| 144 | # | ||
| 145 | def warndups(): | ||
| 146 | savestdout = sys.stdout | ||
| 147 | sys.stdout = sys.stderr | ||
| 148 | names = def2file.keys() | ||
| 149 | names.sort() | ||
| 150 | for name in names: | ||
| 151 | if len(def2file[name]) > 1: | ||
| 152 | print 'warning:', name, 'multiply defined:', | ||
| 153 | print flat(def2file[name]) | ||
| 154 | sys.stdout = savestdout | ||
| 155 | |||
| 156 | # Main program | ||
| 157 | # | ||
| 158 | def main(): | ||
| 159 | try: | ||
| 160 | optlist, args = getopt.getopt(sys.argv[1:], 'cdu') | ||
| 161 | except getopt.error: | ||
| 162 | sys.stdout = sys.stderr | ||
| 163 | print 'Usage:', os.path.basename(sys.argv[0]), | ||
| 164 | print '[-cdu] [file] ...' | ||
| 165 | print '-c: print callers per objectfile' | ||
| 166 | print '-d: print callees per objectfile' | ||
| 167 | print '-u: print usage of undefined symbols' | ||
| 168 | print 'If none of -cdu is specified, all are assumed.' | ||
| 169 | print 'Use "nm -o" to generate the input (on IRIX: "nm -Bo"),' | ||
| 170 | print 'e.g.: nm -o /lib/libc.a | objgraph' | ||
| 171 | return 1 | ||
| 172 | optu = optc = optd = 0 | ||
| 173 | for opt, void in optlist: | ||
| 174 | if opt == '-u': | ||
| 175 | optu = 1 | ||
| 176 | elif opt == '-c': | ||
| 177 | optc = 1 | ||
| 178 | elif opt == '-d': | ||
| 179 | optd = 1 | ||
| 180 | if optu == optc == optd == 0: | ||
| 181 | optu = optc = optd = 1 | ||
| 182 | if not args: | ||
| 183 | args = ['-'] | ||
| 184 | for filename in args: | ||
| 185 | if filename == '-': | ||
| 186 | readinput(sys.stdin) | ||
| 187 | else: | ||
| 188 | readinput(open(filename, 'r')) | ||
| 189 | # | ||
| 190 | warndups() | ||
| 191 | # | ||
| 192 | more = (optu + optc + optd > 1) | ||
| 193 | if optd: | ||
| 194 | if more: | ||
| 195 | print '---------------All callees------------------' | ||
| 196 | printcallee() | ||
| 197 | if optu: | ||
| 198 | if more: | ||
| 199 | print '---------------Undefined callees------------' | ||
| 200 | printundef() | ||
| 201 | if optc: | ||
| 202 | if more: | ||
| 203 | print '---------------All Callers------------------' | ||
| 204 | printcaller() | ||
| 205 | return 0 | ||
| 206 | |||
| 207 | # Call the main program. | ||
| 208 | # Use its return value as exit status. | ||
| 209 | # Catch interrupts to avoid stack trace. | ||
| 210 | # | ||
| 211 | if __name__ == '__main__': | ||
| 212 | try: | ||
| 213 | sys.exit(main()) | ||
| 214 | except KeyboardInterrupt: | ||
| 215 | sys.exit(1) | ||
| @@ -0,0 +1,64 @@ | |||
| 1 | #!/usr/local/bin/python | ||
| 2 | """ Utility for parsing HTML entity definitions available from: | ||
| 3 | |||
| 4 | http://www.w3.org/ as e.g. | ||
| 5 | http://www.w3.org/TR/REC-html40/HTMLlat1.ent | ||
| 6 | |||
| 7 | Input is read from stdin, output is written to stdout in form of a | ||
| 8 | Python snippet defining a dictionary "entitydefs" mapping literal | ||
| 9 | entity name to character or numeric entity. | ||
| 10 | |||
| 11 | Marc-Andre Lemburg, mal@lemburg.com, 1999. | ||
| 12 | Use as you like. NO WARRANTIES. | ||
| 13 | |||
| 14 | """ | ||
| 15 | import re,sys | ||
| 16 | import TextTools | ||
| 17 | |||
| 18 | entityRE = re.compile('<!ENTITY +(\w+) +CDATA +"([^"]+)" +-- +((?:.|\n)+?) *-->') | ||
| 19 | |||
| 20 | def parse(text,pos=0,endpos=None): | ||
| 21 | |||
| 22 | pos = 0 | ||
| 23 | if endpos is None: | ||
| 24 | endpos = len(text) | ||
| 25 | d = {} | ||
| 26 | while 1: | ||
| 27 | m = entityRE.search(text,pos,endpos) | ||
| 28 | if not m: | ||
| 29 | break | ||
| 30 | name,charcode,comment = m.groups() | ||
| 31 | d[name] = charcode,comment | ||
| 32 | pos = m.end() | ||
| 33 | return d | ||
| 34 | |||
| 35 | def writefile(f,defs): | ||
| 36 | |||
| 37 | f.write("entitydefs = {\n") | ||
| 38 | items = defs.items() | ||
| 39 | items.sort() | ||
| 40 | for name,(charcode,comment) in items: | ||
| 41 | if charcode[:2] == '&#': | ||
| 42 | code = int(charcode[2:-1]) | ||
| 43 | if code < 256: | ||
| 44 | charcode = "'\%o'" % code | ||
| 45 | else: | ||
| 46 | charcode = repr(charcode) | ||
| 47 | else: | ||
| 48 | charcode = repr(charcode) | ||
| 49 | comment = TextTools.collapse(comment) | ||
| 50 | f.write(" '%s':\t%s, \t# %s\n" % (name,charcode,comment)) | ||
| 51 | f.write('\n}\n') | ||
| 52 | |||
| 53 | if __name__ == '__main__': | ||
| 54 | if len(sys.argv) > 1: | ||
| 55 | infile = open(sys.argv[1]) | ||
| 56 | else: | ||
| 57 | infile = sys.stdin | ||
| 58 | if len(sys.argv) > 2: | ||
| 59 | outfile = open(sys.argv[2],'w') | ||
| 60 | else: | ||
| 61 | outfile = sys.stdout | ||
| 62 | text = infile.read() | ||
| 63 | defs = parse(text) | ||
| 64 | writefile(outfile,defs) | ||
| @@ -0,0 +1,149 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Change the #! line occurring in Python scripts. The new interpreter | ||
| 4 | # pathname must be given with a -i option. | ||
| 5 | # | ||
| 6 | # Command line arguments are files or directories to be processed. | ||
| 7 | # Directories are searched recursively for files whose name looks | ||
| 8 | # like a python module. | ||
| 9 | # Symbolic links are always ignored (except as explicit directory | ||
| 10 | # arguments). Of course, the original file is kept as a back-up | ||
| 11 | # (with a "~" attached to its name). | ||
| 12 | # | ||
| 13 | # Undoubtedly you can do this using find and sed or perl, but this is | ||
| 14 | # a nice example of Python code that recurses down a directory tree | ||
| 15 | # and uses regular expressions. Also note several subtleties like | ||
| 16 | # preserving the file's mode and avoiding to even write a temp file | ||
| 17 | # when no changes are needed for a file. | ||
| 18 | # | ||
| 19 | # NB: by changing only the function fixfile() you can turn this | ||
| 20 | # into a program for a different change to Python programs... | ||
| 21 | |||
| 22 | import sys | ||
| 23 | import re | ||
| 24 | import os | ||
| 25 | from stat import * | ||
| 26 | import getopt | ||
| 27 | |||
| 28 | err = sys.stderr.write | ||
| 29 | dbg = err | ||
| 30 | rep = sys.stdout.write | ||
| 31 | |||
| 32 | new_interpreter = None | ||
| 33 | |||
| 34 | def main(): | ||
| 35 | global new_interpreter | ||
| 36 | usage = ('usage: %s -i /interpreter file-or-directory ...\n' % | ||
| 37 | sys.argv[0]) | ||
| 38 | try: | ||
| 39 | opts, args = getopt.getopt(sys.argv[1:], 'i:') | ||
| 40 | except getopt.error, msg: | ||
| 41 | err(msg + '\n') | ||
| 42 | err(usage) | ||
| 43 | sys.exit(2) | ||
| 44 | for o, a in opts: | ||
| 45 | if o == '-i': | ||
| 46 | new_interpreter = a | ||
| 47 | if not new_interpreter or new_interpreter[0] != '/' or not args: | ||
| 48 | err('-i option or file-or-directory missing\n') | ||
| 49 | err(usage) | ||
| 50 | sys.exit(2) | ||
| 51 | bad = 0 | ||
| 52 | for arg in args: | ||
| 53 | if os.path.isdir(arg): | ||
| 54 | if recursedown(arg): bad = 1 | ||
| 55 | elif os.path.islink(arg): | ||
| 56 | err(arg + ': will not process symbolic links\n') | ||
| 57 | bad = 1 | ||
| 58 | else: | ||
| 59 | if fix(arg): bad = 1 | ||
| 60 | sys.exit(bad) | ||
| 61 | |||
| 62 | ispythonprog = re.compile('^[a-zA-Z0-9_]+\.py$') | ||
| 63 | def ispython(name): | ||
| 64 | return ispythonprog.match(name) >= 0 | ||
| 65 | |||
| 66 | def recursedown(dirname): | ||
| 67 | dbg('recursedown(%r)\n' % (dirname,)) | ||
| 68 | bad = 0 | ||
| 69 | try: | ||
| 70 | names = os.listdir(dirname) | ||
| 71 | except os.error, msg: | ||
| 72 | err('%s: cannot list directory: %r\n' % (dirname, msg)) | ||
| 73 | return 1 | ||
| 74 | names.sort() | ||
| 75 | subdirs = [] | ||
| 76 | for name in names: | ||
| 77 | if name in (os.curdir, os.pardir): continue | ||
| 78 | fullname = os.path.join(dirname, name) | ||
| 79 | if os.path.islink(fullname): pass | ||
| 80 | elif os.path.isdir(fullname): | ||
| 81 | subdirs.append(fullname) | ||
| 82 | elif ispython(name): | ||
| 83 | if fix(fullname): bad = 1 | ||
| 84 | for fullname in subdirs: | ||
| 85 | if recursedown(fullname): bad = 1 | ||
| 86 | return bad | ||
| 87 | |||
| 88 | def fix(filename): | ||
| 89 | ## dbg('fix(%r)\n' % (filename,)) | ||
| 90 | try: | ||
| 91 | f = open(filename, 'r') | ||
| 92 | except IOError, msg: | ||
| 93 | err('%s: cannot open: %r\n' % (filename, msg)) | ||
| 94 | return 1 | ||
| 95 | line = f.readline() | ||
| 96 | fixed = fixline(line) | ||
| 97 | if line == fixed: | ||
| 98 | rep(filename+': no change\n') | ||
| 99 | f.close() | ||
| 100 | return | ||
| 101 | head, tail = os.path.split(filename) | ||
| 102 | tempname = os.path.join(head, '@' + tail) | ||
| 103 | try: | ||
| 104 | g = open(tempname, 'w') | ||
| 105 | except IOError, msg: | ||
| 106 | f.close() | ||
| 107 | err('%s: cannot create: %r\n' % (tempname, msg)) | ||
| 108 | return 1 | ||
| 109 | rep(filename + ': updating\n') | ||
| 110 | g.write(fixed) | ||
| 111 | BUFSIZE = 8*1024 | ||
| 112 | while 1: | ||
| 113 | buf = f.read(BUFSIZE) | ||
| 114 | if not buf: break | ||
| 115 | g.write(buf) | ||
| 116 | g.close() | ||
| 117 | f.close() | ||
| 118 | |||
| 119 | # Finishing touch -- move files | ||
| 120 | |||
| 121 | # First copy the file's mode to the temp file | ||
| 122 | try: | ||
| 123 | statbuf = os.stat(filename) | ||
| 124 | os.chmod(tempname, statbuf[ST_MODE] & 07777) | ||
| 125 | except os.error, msg: | ||
| 126 | err('%s: warning: chmod failed (%r)\n' % (tempname, msg)) | ||
| 127 | # Then make a backup of the original file as filename~ | ||
| 128 | try: | ||
| 129 | os.rename(filename, filename + '~') | ||
| 130 | except os.error, msg: | ||
| 131 | err('%s: warning: backup failed (%r)\n' % (filename, msg)) | ||
| 132 | # Now move the temp file to the original file | ||
| 133 | try: | ||
| 134 | os.rename(tempname, filename) | ||
| 135 | except os.error, msg: | ||
| 136 | err('%s: rename failed (%r)\n' % (filename, msg)) | ||
| 137 | return 1 | ||
| 138 | # Return succes | ||
| 139 | return 0 | ||
| 140 | |||
| 141 | def fixline(line): | ||
| 142 | if not line.startswith('#!'): | ||
| 143 | return line | ||
| 144 | if "python" not in line: | ||
| 145 | return line | ||
| 146 | return '#! %s\n' % new_interpreter | ||
| 147 | |||
| 148 | if __name__ == '__main__': | ||
| 149 | main() | ||
| @@ -0,0 +1,167 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # pdeps | ||
| 4 | # | ||
| 5 | # Find dependencies between a bunch of Python modules. | ||
| 6 | # | ||
| 7 | # Usage: | ||
| 8 | # pdeps file1.py file2.py ... | ||
| 9 | # | ||
| 10 | # Output: | ||
| 11 | # Four tables separated by lines like '--- Closure ---': | ||
| 12 | # 1) Direct dependencies, listing which module imports which other modules | ||
| 13 | # 2) The inverse of (1) | ||
| 14 | # 3) Indirect dependencies, or the closure of the above | ||
| 15 | # 4) The inverse of (3) | ||
| 16 | # | ||
| 17 | # To do: | ||
| 18 | # - command line options to select output type | ||
| 19 | # - option to automatically scan the Python library for referenced modules | ||
| 20 | # - option to limit output to particular modules | ||
| 21 | |||
| 22 | |||
| 23 | import sys | ||
| 24 | import re | ||
| 25 | import os | ||
| 26 | |||
| 27 | |||
| 28 | # Main program | ||
| 29 | # | ||
| 30 | def main(): | ||
| 31 | args = sys.argv[1:] | ||
| 32 | if not args: | ||
| 33 | print 'usage: pdeps file.py file.py ...' | ||
| 34 | return 2 | ||
| 35 | # | ||
| 36 | table = {} | ||
| 37 | for arg in args: | ||
| 38 | process(arg, table) | ||
| 39 | # | ||
| 40 | print '--- Uses ---' | ||
| 41 | printresults(table) | ||
| 42 | # | ||
| 43 | print '--- Used By ---' | ||
| 44 | inv = inverse(table) | ||
| 45 | printresults(inv) | ||
| 46 | # | ||
| 47 | print '--- Closure of Uses ---' | ||
| 48 | reach = closure(table) | ||
| 49 | printresults(reach) | ||
| 50 | # | ||
| 51 | print '--- Closure of Used By ---' | ||
| 52 | invreach = inverse(reach) | ||
| 53 | printresults(invreach) | ||
| 54 | # | ||
| 55 | return 0 | ||
| 56 | |||
| 57 | |||
| 58 | # Compiled regular expressions to search for import statements | ||
| 59 | # | ||
| 60 | m_import = re.compile('^[ \t]*from[ \t]+([^ \t]+)[ \t]+') | ||
| 61 | m_from = re.compile('^[ \t]*import[ \t]+([^#]+)') | ||
| 62 | |||
| 63 | |||
| 64 | # Collect data from one file | ||
| 65 | # | ||
| 66 | def process(filename, table): | ||
| 67 | fp = open(filename, 'r') | ||
| 68 | mod = os.path.basename(filename) | ||
| 69 | if mod[-3:] == '.py': | ||
| 70 | mod = mod[:-3] | ||
| 71 | table[mod] = list = [] | ||
| 72 | while 1: | ||
| 73 | line = fp.readline() | ||
| 74 | if not line: break | ||
| 75 | while line[-1:] == '\\': | ||
| 76 | nextline = fp.readline() | ||
| 77 | if not nextline: break | ||
| 78 | line = line[:-1] + nextline | ||
| 79 | if m_import.match(line) >= 0: | ||
| 80 | (a, b), (a1, b1) = m_import.regs[:2] | ||
| 81 | elif m_from.match(line) >= 0: | ||
| 82 | (a, b), (a1, b1) = m_from.regs[:2] | ||
| 83 | else: continue | ||
| 84 | words = line[a1:b1].split(',') | ||
| 85 | # print '#', line, words | ||
| 86 | for word in words: | ||
| 87 | word = word.strip() | ||
| 88 | if word not in list: | ||
| 89 | list.append(word) | ||
| 90 | |||
| 91 | |||
| 92 | # Compute closure (this is in fact totally general) | ||
| 93 | # | ||
| 94 | def closure(table): | ||
| 95 | modules = table.keys() | ||
| 96 | # | ||
| 97 | # Initialize reach with a copy of table | ||
| 98 | # | ||
| 99 | reach = {} | ||
| 100 | for mod in modules: | ||
| 101 | reach[mod] = table[mod][:] | ||
| 102 | # | ||
| 103 | # Iterate until no more change | ||
| 104 | # | ||
| 105 | change = 1 | ||
| 106 | while change: | ||
| 107 | change = 0 | ||
| 108 | for mod in modules: | ||
| 109 | for mo in reach[mod]: | ||
| 110 | if mo in modules: | ||
| 111 | for m in reach[mo]: | ||
| 112 | if m not in reach[mod]: | ||
| 113 | reach[mod].append(m) | ||
| 114 | change = 1 | ||
| 115 | # | ||
| 116 | return reach | ||
| 117 | |||
| 118 | |||
| 119 | # Invert a table (this is again totally general). | ||
| 120 | # All keys of the original table are made keys of the inverse, | ||
| 121 | # so there may be empty lists in the inverse. | ||
| 122 | # | ||
| 123 | def inverse(table): | ||
| 124 | inv = {} | ||
| 125 | for key in table.keys(): | ||
| 126 | if not inv.has_key(key): | ||
| 127 | inv[key] = [] | ||
| 128 | for item in table[key]: | ||
| 129 | store(inv, item, key) | ||
| 130 | return inv | ||
| 131 | |||
| 132 | |||
| 133 | # Store "item" in "dict" under "key". | ||
| 134 | # The dictionary maps keys to lists of items. | ||
| 135 | # If there is no list for the key yet, it is created. | ||
| 136 | # | ||
| 137 | def store(dict, key, item): | ||
| 138 | if dict.has_key(key): | ||
| 139 | dict[key].append(item) | ||
| 140 | else: | ||
| 141 | dict[key] = [item] | ||
| 142 | |||
| 143 | |||
| 144 | # Tabulate results neatly | ||
| 145 | # | ||
| 146 | def printresults(table): | ||
| 147 | modules = table.keys() | ||
| 148 | maxlen = 0 | ||
| 149 | for mod in modules: maxlen = max(maxlen, len(mod)) | ||
| 150 | modules.sort() | ||
| 151 | for mod in modules: | ||
| 152 | list = table[mod] | ||
| 153 | list.sort() | ||
| 154 | print mod.ljust(maxlen), ':', | ||
| 155 | if mod in list: | ||
| 156 | print '(*)', | ||
| 157 | for ref in list: | ||
| 158 | print ref, | ||
| 159 | |||
| 160 | |||
| 161 | |||
| 162 | # Call main and honor exit status | ||
| 163 | if __name__ == '__main__': | ||
| 164 | try: | ||
| 165 | sys.exit(main()) | ||
| 166 | except KeyboardInterrupt: | ||
| 167 | sys.exit(1) | ||
| @@ -0,0 +1,147 @@ | |||
| 1 | #!/usr/bin/env python | ||
| 2 | |||
| 3 | """ | ||
| 4 | Synopsis: %(prog)s [-h|-b|-g|-r|-a|-d] [ picklefile ] dbfile | ||
| 5 | |||
| 6 | Read the given picklefile as a series of key/value pairs and write to a new | ||
| 7 | database. If the database already exists, any contents are deleted. The | ||
| 8 | optional flags indicate the type of the output database: | ||
| 9 | |||
| 10 | -a - open using anydbm | ||
| 11 | -b - open as bsddb btree file | ||
| 12 | -d - open as dbm file | ||
| 13 | -g - open as gdbm file | ||
| 14 | -h - open as bsddb hash file | ||
| 15 | -r - open as bsddb recno file | ||
| 16 | |||
| 17 | The default is hash. If a pickle file is named it is opened for read | ||
| 18 | access. If no pickle file is named, the pickle input is read from standard | ||
| 19 | input. | ||
| 20 | |||
| 21 | Note that recno databases can only contain integer keys, so you can't dump a | ||
| 22 | hash or btree database using db2pickle.py and reconstitute it to a recno | ||
| 23 | database with %(prog)s unless your keys are integers. | ||
| 24 | |||
| 25 | """ | ||
| 26 | |||
| 27 | import getopt | ||
| 28 | try: | ||
| 29 | import bsddb | ||
| 30 | except ImportError: | ||
| 31 | bsddb = None | ||
| 32 | try: | ||
| 33 | import dbm | ||
| 34 | except ImportError: | ||
| 35 | dbm = None | ||
| 36 | try: | ||
| 37 | import gdbm | ||
| 38 | except ImportError: | ||
| 39 | gdbm = None | ||
| 40 | try: | ||
| 41 | import anydbm | ||
| 42 | except ImportError: | ||
| 43 | anydbm = None | ||
| 44 | import sys | ||
| 45 | try: | ||
| 46 | import cPickle as pickle | ||
| 47 | except ImportError: | ||
| 48 | import pickle | ||
| 49 | |||
| 50 | prog = sys.argv[0] | ||
| 51 | |||
| 52 | def usage(): | ||
| 53 | sys.stderr.write(__doc__ % globals()) | ||
| 54 | |||
| 55 | def main(args): | ||
| 56 | try: | ||
| 57 | opts, args = getopt.getopt(args, "hbrdag", | ||
| 58 | ["hash", "btree", "recno", "dbm", "anydbm", | ||
| 59 | "gdbm"]) | ||
| 60 | except getopt.error: | ||
| 61 | usage() | ||
| 62 | return 1 | ||
| 63 | |||
| 64 | if len(args) == 0 or len(args) > 2: | ||
| 65 | usage() | ||
| 66 | return 1 | ||
| 67 | elif len(args) == 1: | ||
| 68 | pfile = sys.stdin | ||
| 69 | dbfile = args[0] | ||
| 70 | else: | ||
| 71 | try: | ||
| 72 | pfile = open(args[0], 'rb') | ||
| 73 | except IOError: | ||
| 74 | sys.stderr.write("Unable to open %s\n" % args[0]) | ||
| 75 | return 1 | ||
| 76 | dbfile = args[1] | ||
| 77 | |||
| 78 | dbopen = None | ||
| 79 | for opt, arg in opts: | ||
| 80 | if opt in ("-h", "--hash"): | ||
| 81 | try: | ||
| 82 | dbopen = bsddb.hashopen | ||
| 83 | except AttributeError: | ||
| 84 | sys.stderr.write("bsddb module unavailable.\n") | ||
| 85 | return 1 | ||
| 86 | elif opt in ("-b", "--btree"): | ||
| 87 | try: | ||
| 88 | dbopen = bsddb.btopen | ||
| 89 | except AttributeError: | ||
| 90 | sys.stderr.write("bsddb module unavailable.\n") | ||
| 91 | return 1 | ||
| 92 | elif opt in ("-r", "--recno"): | ||
| 93 | try: | ||
| 94 | dbopen = bsddb.rnopen | ||
| 95 | except AttributeError: | ||
| 96 | sys.stderr.write("bsddb module unavailable.\n") | ||
| 97 | return 1 | ||
| 98 | elif opt in ("-a", "--anydbm"): | ||
| 99 | try: | ||
| 100 | dbopen = anydbm.open | ||
| 101 | except AttributeError: | ||
| 102 | sys.stderr.write("anydbm module unavailable.\n") | ||
| 103 | return 1 | ||
| 104 | elif opt in ("-g", "--gdbm"): | ||
| 105 | try: | ||
| 106 | dbopen = gdbm.open | ||
| 107 | except AttributeError: | ||
| 108 | sys.stderr.write("gdbm module unavailable.\n") | ||
| 109 | return 1 | ||
| 110 | elif opt in ("-d", "--dbm"): | ||
| 111 | try: | ||
| 112 | dbopen = dbm.open | ||
| 113 | except AttributeError: | ||
| 114 | sys.stderr.write("dbm module unavailable.\n") | ||
| 115 | return 1 | ||
| 116 | if dbopen is None: | ||
| 117 | if bsddb is None: | ||
| 118 | sys.stderr.write("bsddb module unavailable - ") | ||
| 119 | sys.stderr.write("must specify dbtype.\n") | ||
| 120 | return 1 | ||
| 121 | else: | ||
| 122 | dbopen = bsddb.hashopen | ||
| 123 | |||
| 124 | try: | ||
| 125 | db = dbopen(dbfile, 'c') | ||
| 126 | except bsddb.error: | ||
| 127 | sys.stderr.write("Unable to open %s. " % dbfile) | ||
| 128 | sys.stderr.write("Check for format or version mismatch.\n") | ||
| 129 | return 1 | ||
| 130 | else: | ||
| 131 | for k in db.keys(): | ||
| 132 | del db[k] | ||
| 133 | |||
| 134 | while 1: | ||
| 135 | try: | ||
| 136 | (key, val) = pickle.load(pfile) | ||
| 137 | except EOFError: | ||
| 138 | break | ||
| 139 | db[key] = val | ||
| 140 | |||
| 141 | db.close() | ||
| 142 | pfile.close() | ||
| 143 | |||
| 144 | return 0 | ||
| 145 | |||
| 146 | if __name__ == "__main__": | ||
| 147 | sys.exit(main(sys.argv[1:])) | ||
| @@ -0,0 +1,543 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # This file contains a class and a main program that perform three | ||
| 4 | # related (though complimentary) formatting operations on Python | ||
| 5 | # programs. When called as "pindent -c", it takes a valid Python | ||
| 6 | # program as input and outputs a version augmented with block-closing | ||
| 7 | # comments. When called as "pindent -d", it assumes its input is a | ||
| 8 | # Python program with block-closing comments and outputs a commentless | ||
| 9 | # version. When called as "pindent -r" it assumes its input is a | ||
| 10 | # Python program with block-closing comments but with its indentation | ||
| 11 | # messed up, and outputs a properly indented version. | ||
| 12 | |||
| 13 | # A "block-closing comment" is a comment of the form '# end <keyword>' | ||
| 14 | # where <keyword> is the keyword that opened the block. If the | ||
| 15 | # opening keyword is 'def' or 'class', the function or class name may | ||
| 16 | # be repeated in the block-closing comment as well. Here is an | ||
| 17 | # example of a program fully augmented with block-closing comments: | ||
| 18 | |||
| 19 | # def foobar(a, b): | ||
| 20 | # if a == b: | ||
| 21 | # a = a+1 | ||
| 22 | # elif a < b: | ||
| 23 | # b = b-1 | ||
| 24 | # if b > a: a = a-1 | ||
| 25 | # # end if | ||
| 26 | # else: | ||
| 27 | # print 'oops!' | ||
| 28 | # # end if | ||
| 29 | # # end def foobar | ||
| 30 | |||
| 31 | # Note that only the last part of an if...elif...else... block needs a | ||
| 32 | # block-closing comment; the same is true for other compound | ||
| 33 | # statements (e.g. try...except). Also note that "short-form" blocks | ||
| 34 | # like the second 'if' in the example must be closed as well; | ||
| 35 | # otherwise the 'else' in the example would be ambiguous (remember | ||
| 36 | # that indentation is not significant when interpreting block-closing | ||
| 37 | # comments). | ||
| 38 | |||
| 39 | # The operations are idempotent (i.e. applied to their own output | ||
| 40 | # they yield an identical result). Running first "pindent -c" and | ||
| 41 | # then "pindent -r" on a valid Python program produces a program that | ||
| 42 | # is semantically identical to the input (though its indentation may | ||
| 43 | # be different). Running "pindent -e" on that output produces a | ||
| 44 | # program that only differs from the original in indentation. | ||
| 45 | |||
| 46 | # Other options: | ||
| 47 | # -s stepsize: set the indentation step size (default 8) | ||
| 48 | # -t tabsize : set the number of spaces a tab character is worth (default 8) | ||
| 49 | # -e : expand TABs into spaces | ||
| 50 | # file ... : input file(s) (default standard input) | ||
| 51 | # The results always go to standard output | ||
| 52 | |||
| 53 | # Caveats: | ||
| 54 | # - comments ending in a backslash will be mistaken for continued lines | ||
| 55 | # - continuations using backslash are always left unchanged | ||
| 56 | # - continuations inside parentheses are not extra indented by -r | ||
| 57 | # but must be indented for -c to work correctly (this breaks | ||
| 58 | # idempotency!) | ||
| 59 | # - continued lines inside triple-quoted strings are totally garbled | ||
| 60 | |||
| 61 | # Secret feature: | ||
| 62 | # - On input, a block may also be closed with an "end statement" -- | ||
| 63 | # this is a block-closing comment without the '#' sign. | ||
| 64 | |||
| 65 | # Possible improvements: | ||
| 66 | # - check syntax based on transitions in 'next' table | ||
| 67 | # - better error reporting | ||
| 68 | # - better error recovery | ||
| 69 | # - check identifier after class/def | ||
| 70 | |||
| 71 | # The following wishes need a more complete tokenization of the source: | ||
| 72 | # - Don't get fooled by comments ending in backslash | ||
| 73 | # - reindent continuation lines indicated by backslash | ||
| 74 | # - handle continuation lines inside parentheses/braces/brackets | ||
| 75 | # - handle triple quoted strings spanning lines | ||
| 76 | # - realign comments | ||
| 77 | # - optionally do much more thorough reformatting, a la C indent | ||
| 78 | |||
| 79 | # Defaults | ||
| 80 | STEPSIZE = 8 | ||
| 81 | TABSIZE = 8 | ||
| 82 | EXPANDTABS = 0 | ||
| 83 | |||
| 84 | import os | ||
| 85 | import re | ||
| 86 | import sys | ||
| 87 | |||
| 88 | next = {} | ||
| 89 | next['if'] = next['elif'] = 'elif', 'else', 'end' | ||
| 90 | next['while'] = next['for'] = 'else', 'end' | ||
| 91 | next['try'] = 'except', 'finally' | ||
| 92 | next['except'] = 'except', 'else', 'end' | ||
| 93 | next['else'] = next['finally'] = next['def'] = next['class'] = 'end' | ||
| 94 | next['end'] = () | ||
| 95 | start = 'if', 'while', 'for', 'try', 'def', 'class' | ||
| 96 | |||
| 97 | class PythonIndenter: | ||
| 98 | |||
| 99 | def __init__(self, fpi = sys.stdin, fpo = sys.stdout, | ||
| 100 | indentsize = STEPSIZE, tabsize = TABSIZE, expandtabs = EXPANDTABS): | ||
| 101 | self.fpi = fpi | ||
| 102 | self.fpo = fpo | ||
| 103 | self.indentsize = indentsize | ||
| 104 | self.tabsize = tabsize | ||
| 105 | self.lineno = 0 | ||
| 106 | self.expandtabs = expandtabs | ||
| 107 | self._write = fpo.write | ||
| 108 | self.kwprog = re.compile( | ||
| 109 | r'^\s*(?P<kw>[a-z]+)' | ||
| 110 | r'(\s+(?P<id>[a-zA-Z_]\w*))?' | ||
| 111 | r'[^\w]') | ||
| 112 | self.endprog = re.compile( | ||
| 113 | r'^\s*#?\s*end\s+(?P<kw>[a-z]+)' | ||
| 114 | r'(\s+(?P<id>[a-zA-Z_]\w*))?' | ||
| 115 | r'[^\w]') | ||
| 116 | self.wsprog = re.compile(r'^[ \t]*') | ||
| 117 | # end def __init__ | ||
| 118 | |||
| 119 | def write(self, line): | ||
| 120 | if self.expandtabs: | ||
| 121 | self._write(line.expandtabs(self.tabsize)) | ||
| 122 | else: | ||
| 123 | self._write(line) | ||
| 124 | # end if | ||
| 125 | # end def write | ||
| 126 | |||
| 127 | def readline(self): | ||
| 128 | line = self.fpi.readline() | ||
| 129 | if line: self.lineno = self.lineno + 1 | ||
| 130 | # end if | ||
| 131 | return line | ||
| 132 | # end def readline | ||
| 133 | |||
| 134 | def error(self, fmt, *args): | ||
| 135 | if args: fmt = fmt % args | ||
| 136 | # end if | ||
| 137 | sys.stderr.write('Error at line %d: %s\n' % (self.lineno, fmt)) | ||
| 138 | self.write('### %s ###\n' % fmt) | ||
| 139 | # end def error | ||
| 140 | |||
| 141 | def getline(self): | ||
| 142 | line = self.readline() | ||
| 143 | while line[-2:] == '\\\n': | ||
| 144 | line2 = self.readline() | ||
| 145 | if not line2: break | ||
| 146 | # end if | ||
| 147 | line = line + line2 | ||
| 148 | # end while | ||
| 149 | return line | ||
| 150 | # end def getline | ||
| 151 | |||
| 152 | def putline(self, line, indent = None): | ||
| 153 | if indent is None: | ||
| 154 | self.write(line) | ||
| 155 | return | ||
| 156 | # end if | ||
| 157 | tabs, spaces = divmod(indent*self.indentsize, self.tabsize) | ||
| 158 | i = 0 | ||
| 159 | m = self.wsprog.match(line) | ||
| 160 | if m: i = m.end() | ||
| 161 | # end if | ||
| 162 | self.write('\t'*tabs + ' '*spaces + line[i:]) | ||
| 163 | # end def putline | ||
| 164 | |||
| 165 | def reformat(self): | ||
| 166 | stack = [] | ||
| 167 | while 1: | ||
| 168 | line = self.getline() | ||
| 169 | if not line: break # EOF | ||
| 170 | # end if | ||
| 171 | m = self.endprog.match(line) | ||
| 172 | if m: | ||
| 173 | kw = 'end' | ||
| 174 | kw2 = m.group('kw') | ||
| 175 | if not stack: | ||
| 176 | self.error('unexpected end') | ||
| 177 | elif stack[-1][0] != kw2: | ||
| 178 | self.error('unmatched end') | ||
| 179 | # end if | ||
| 180 | del stack[-1:] | ||
| 181 | self.putline(line, len(stack)) | ||
| 182 | continue | ||
| 183 | # end if | ||
| 184 | m = self.kwprog.match(line) | ||
| 185 | if m: | ||
| 186 | kw = m.group('kw') | ||
| 187 | if kw in start: | ||
| 188 | self.putline(line, len(stack)) | ||
| 189 | stack.append((kw, kw)) | ||
| 190 | continue | ||
| 191 | # end if | ||
| 192 | if next.has_key(kw) and stack: | ||
| 193 | self.putline(line, len(stack)-1) | ||
| 194 | kwa, kwb = stack[-1] | ||
| 195 | stack[-1] = kwa, kw | ||
| 196 | continue | ||
| 197 | # end if | ||
| 198 | # end if | ||
| 199 | self.putline(line, len(stack)) | ||
| 200 | # end while | ||
| 201 | if stack: | ||
| 202 | self.error('unterminated keywords') | ||
| 203 | for kwa, kwb in stack: | ||
| 204 | self.write('\t%s\n' % kwa) | ||
| 205 | # end for | ||
| 206 | # end if | ||
| 207 | # end def reformat | ||
| 208 | |||
| 209 | def delete(self): | ||
| 210 | begin_counter = 0 | ||
| 211 | end_counter = 0 | ||
| 212 | while 1: | ||
| 213 | line = self.getline() | ||
| 214 | if not line: break # EOF | ||
| 215 | # end if | ||
| 216 | m = self.endprog.match(line) | ||
| 217 | if m: | ||
| 218 | end_counter = end_counter + 1 | ||
| 219 | continue | ||
| 220 | # end if | ||
| 221 | m = self.kwprog.match(line) | ||
| 222 | if m: | ||
| 223 | kw = m.group('kw') | ||
| 224 | if kw in start: | ||
| 225 | begin_counter = begin_counter + 1 | ||
| 226 | # end if | ||
| 227 | # end if | ||
| 228 | self.putline(line) | ||
| 229 | # end while | ||
| 230 | if begin_counter - end_counter < 0: | ||
| 231 | sys.stderr.write('Warning: input contained more end tags than expected\n') | ||
| 232 | elif begin_counter - end_counter > 0: | ||
| 233 | sys.stderr.write('Warning: input contained less end tags than expected\n') | ||
| 234 | # end if | ||
| 235 | # end def delete | ||
| 236 | |||
| 237 | def complete(self): | ||
| 238 | self.indentsize = 1 | ||
| 239 | stack = [] | ||
| 240 | todo = [] | ||
| 241 | thisid = '' | ||
| 242 | current, firstkw, lastkw, topid = 0, '', '', '' | ||
| 243 | while 1: | ||
| 244 | line = self.getline() | ||
| 245 | i = 0 | ||
| 246 | m = self.wsprog.match(line) | ||
| 247 | if m: i = m.end() | ||
| 248 | # end if | ||
| 249 | m = self.endprog.match(line) | ||
| 250 | if m: | ||
| 251 | thiskw = 'end' | ||
| 252 | endkw = m.group('kw') | ||
| 253 | thisid = m.group('id') | ||
| 254 | else: | ||
| 255 | m = self.kwprog.match(line) | ||
| 256 | if m: | ||
| 257 | thiskw = m.group('kw') | ||
| 258 | if not next.has_key(thiskw): | ||
| 259 | thiskw = '' | ||
| 260 | # end if | ||
| 261 | if thiskw in ('def', 'class'): | ||
| 262 | thisid = m.group('id') | ||
| 263 | else: | ||
| 264 | thisid = '' | ||
| 265 | # end if | ||
| 266 | elif line[i:i+1] in ('\n', '#'): | ||
| 267 | todo.append(line) | ||
| 268 | continue | ||
| 269 | else: | ||
| 270 | thiskw = '' | ||
| 271 | # end if | ||
| 272 | # end if | ||
| 273 | indent = len(line[:i].expandtabs(self.tabsize)) | ||
| 274 | while indent < current: | ||
| 275 | if firstkw: | ||
| 276 | if topid: | ||
| 277 | s = '# end %s %s\n' % ( | ||
| 278 | firstkw, topid) | ||
| 279 | else: | ||
| 280 | s = '# end %s\n' % firstkw | ||
| 281 | # end if | ||
| 282 | self.putline(s, current) | ||
| 283 | firstkw = lastkw = '' | ||
| 284 | # end if | ||
| 285 | current, firstkw, lastkw, topid = stack[-1] | ||
| 286 | del stack[-1] | ||
| 287 | # end while | ||
| 288 | if indent == current and firstkw: | ||
| 289 | if thiskw == 'end': | ||
| 290 | if endkw != firstkw: | ||
| 291 | self.error('mismatched end') | ||
| 292 | # end if | ||
| 293 | firstkw = lastkw = '' | ||
| 294 | elif not thiskw or thiskw in start: | ||
| 295 | if topid: | ||
| 296 | s = '# end %s %s\n' % ( | ||
| 297 | firstkw, topid) | ||
| 298 | else: | ||
| 299 | s = '# end %s\n' % firstkw | ||
| 300 | # end if | ||
| 301 | self.putline(s, current) | ||
| 302 | firstkw = lastkw = topid = '' | ||
| 303 | # end if | ||
| 304 | # end if | ||
| 305 | if indent > current: | ||
| 306 | stack.append((current, firstkw, lastkw, topid)) | ||
| 307 | if thiskw and thiskw not in start: | ||
| 308 | # error | ||
| 309 | thiskw = '' | ||
| 310 | # end if | ||
| 311 | current, firstkw, lastkw, topid = \ | ||
| 312 | indent, thiskw, thiskw, thisid | ||
| 313 | # end if | ||
| 314 | if thiskw: | ||
| 315 | if thiskw in start: | ||
| 316 | firstkw = lastkw = thiskw | ||
| 317 | topid = thisid | ||
| 318 | else: | ||
| 319 | lastkw = thiskw | ||
| 320 | # end if | ||
| 321 | # end if | ||
| 322 | for l in todo: self.write(l) | ||
| 323 | # end for | ||
| 324 | todo = [] | ||
| 325 | if not line: break | ||
| 326 | # end if | ||
| 327 | self.write(line) | ||
| 328 | # end while | ||
| 329 | # end def complete | ||
| 330 | |||
| 331 | # end class PythonIndenter | ||
| 332 | |||
| 333 | # Simplified user interface | ||
| 334 | # - xxx_filter(input, output): read and write file objects | ||
| 335 | # - xxx_string(s): take and return string object | ||
| 336 | # - xxx_file(filename): process file in place, return true iff changed | ||
| 337 | |||
| 338 | def complete_filter(input = sys.stdin, output = sys.stdout, | ||
| 339 | stepsize = STEPSIZE, tabsize = TABSIZE, expandtabs = EXPANDTABS): | ||
| 340 | pi = PythonIndenter(input, output, stepsize, tabsize, expandtabs) | ||
| 341 | pi.complete() | ||
| 342 | # end def complete_filter | ||
| 343 | |||
| 344 | def delete_filter(input= sys.stdin, output = sys.stdout, | ||
| 345 | stepsize = STEPSIZE, tabsize = TABSIZE, expandtabs = EXPANDTABS): | ||
| 346 | pi = PythonIndenter(input, output, stepsize, tabsize, expandtabs) | ||
| 347 | pi.delete() | ||
| 348 | # end def delete_filter | ||
| 349 | |||
| 350 | def reformat_filter(input = sys.stdin, output = sys.stdout, | ||
| 351 | stepsize = STEPSIZE, tabsize = TABSIZE, expandtabs = EXPANDTABS): | ||
| 352 | pi = PythonIndenter(input, output, stepsize, tabsize, expandtabs) | ||
| 353 | pi.reformat() | ||
| 354 | # end def reformat_filter | ||
| 355 | |||
| 356 | class StringReader: | ||
| 357 | def __init__(self, buf): | ||
| 358 | self.buf = buf | ||
| 359 | self.pos = 0 | ||
| 360 | self.len = len(self.buf) | ||
| 361 | # end def __init__ | ||
| 362 | def read(self, n = 0): | ||
| 363 | if n <= 0: | ||
| 364 | n = self.len - self.pos | ||
| 365 | else: | ||
| 366 | n = min(n, self.len - self.pos) | ||
| 367 | # end if | ||
| 368 | r = self.buf[self.pos : self.pos + n] | ||
| 369 | self.pos = self.pos + n | ||
| 370 | return r | ||
| 371 | # end def read | ||
| 372 | def readline(self): | ||
| 373 | i = self.buf.find('\n', self.pos) | ||
| 374 | return self.read(i + 1 - self.pos) | ||
| 375 | # end def readline | ||
| 376 | def readlines(self): | ||
| 377 | lines = [] | ||
| 378 | line = self.readline() | ||
| 379 | while line: | ||
| 380 | lines.append(line) | ||
| 381 | line = self.readline() | ||
| 382 | # end while | ||
| 383 | return lines | ||
| 384 | # end def readlines | ||
| 385 | # seek/tell etc. are left as an exercise for the reader | ||
| 386 | # end class StringReader | ||
| 387 | |||
| 388 | class StringWriter: | ||
| 389 | def __init__(self): | ||
| 390 | self.buf = '' | ||
| 391 | # end def __init__ | ||
| 392 | def write(self, s): | ||
| 393 | self.buf = self.buf + s | ||
| 394 | # end def write | ||
| 395 | def getvalue(self): | ||
| 396 | return self.buf | ||
| 397 | # end def getvalue | ||
| 398 | # end class StringWriter | ||
| 399 | |||
| 400 | def complete_string(source, stepsize = STEPSIZE, tabsize = TABSIZE, expandtabs = EXPANDTABS): | ||
| 401 | input = StringReader(source) | ||
| 402 | output = StringWriter() | ||
| 403 | pi = PythonIndenter(input, output, stepsize, tabsize, expandtabs) | ||
| 404 | pi.complete() | ||
| 405 | return output.getvalue() | ||
| 406 | # end def complete_string | ||
| 407 | |||
| 408 | def delete_string(source, stepsize = STEPSIZE, tabsize = TABSIZE, expandtabs = EXPANDTABS): | ||
| 409 | input = StringReader(source) | ||
| 410 | output = StringWriter() | ||
| 411 | pi = PythonIndenter(input, output, stepsize, tabsize, expandtabs) | ||
| 412 | pi.delete() | ||
| 413 | return output.getvalue() | ||
| 414 | # end def delete_string | ||
| 415 | |||
| 416 | def reformat_string(source, stepsize = STEPSIZE, tabsize = TABSIZE, expandtabs = EXPANDTABS): | ||
| 417 | input = StringReader(source) | ||
| 418 | output = StringWriter() | ||
| 419 | pi = PythonIndenter(input, output, stepsize, tabsize, expandtabs) | ||
| 420 | pi.reformat() | ||
| 421 | return output.getvalue() | ||
| 422 | # end def reformat_string | ||
| 423 | |||
| 424 | def complete_file(filename, stepsize = STEPSIZE, tabsize = TABSIZE, expandtabs = EXPANDTABS): | ||
| 425 | source = open(filename, 'r').read() | ||
| 426 | result = complete_string(source, stepsize, tabsize, expandtabs) | ||
| 427 | if source == result: return 0 | ||
| 428 | # end if | ||
| 429 | import os | ||
| 430 | try: os.rename(filename, filename + '~') | ||
| 431 | except os.error: pass | ||
| 432 | # end try | ||
| 433 | f = open(filename, 'w') | ||
| 434 | f.write(result) | ||
| 435 | f.close() | ||
| 436 | return 1 | ||
| 437 | # end def complete_file | ||
| 438 | |||
| 439 | def delete_file(filename, stepsize = STEPSIZE, tabsize = TABSIZE, expandtabs = EXPANDTABS): | ||
| 440 | source = open(filename, 'r').read() | ||
| 441 | result = delete_string(source, stepsize, tabsize, expandtabs) | ||
| 442 | if source == result: return 0 | ||
| 443 | # end if | ||
| 444 | import os | ||
| 445 | try: os.rename(filename, filename + '~') | ||
| 446 | except os.error: pass | ||
| 447 | # end try | ||
| 448 | f = open(filename, 'w') | ||
| 449 | f.write(result) | ||
| 450 | f.close() | ||
| 451 | return 1 | ||
| 452 | # end def delete_file | ||
| 453 | |||
| 454 | def reformat_file(filename, stepsize = STEPSIZE, tabsize = TABSIZE, expandtabs = EXPANDTABS): | ||
| 455 | source = open(filename, 'r').read() | ||
| 456 | result = reformat_string(source, stepsize, tabsize, expandtabs) | ||
| 457 | if source == result: return 0 | ||
| 458 | # end if | ||
| 459 | import os | ||
| 460 | try: os.rename(filename, filename + '~') | ||
| 461 | except os.error: pass | ||
| 462 | # end try | ||
| 463 | f = open(filename, 'w') | ||
| 464 | f.write(result) | ||
| 465 | f.close() | ||
| 466 | return 1 | ||
| 467 | # end def reformat_file | ||
| 468 | |||
| 469 | # Test program when called as a script | ||
| 470 | |||
| 471 | usage = """ | ||
| 472 | usage: pindent (-c|-d|-r) [-s stepsize] [-t tabsize] [-e] [file] ... | ||
| 473 | -c : complete a correctly indented program (add #end directives) | ||
| 474 | -d : delete #end directives | ||
| 475 | -r : reformat a completed program (use #end directives) | ||
| 476 | -s stepsize: indentation step (default %(STEPSIZE)d) | ||
| 477 | -t tabsize : the worth in spaces of a tab (default %(TABSIZE)d) | ||
| 478 | -e : expand TABs into spaces (defailt OFF) | ||
| 479 | [file] ... : files are changed in place, with backups in file~ | ||
| 480 | If no files are specified or a single - is given, | ||
| 481 | the program acts as a filter (reads stdin, writes stdout). | ||
| 482 | """ % vars() | ||
| 483 | |||
| 484 | def error_both(op1, op2): | ||
| 485 | sys.stderr.write('Error: You can not specify both '+op1+' and -'+op2[0]+' at the same time\n') | ||
| 486 | sys.stderr.write(usage) | ||
| 487 | sys.exit(2) | ||
| 488 | # end def error_both | ||
| 489 | |||
| 490 | def test(): | ||
| 491 | import getopt | ||
| 492 | try: | ||
| 493 | opts, args = getopt.getopt(sys.argv[1:], 'cdrs:t:e') | ||
| 494 | except getopt.error, msg: | ||
| 495 | sys.stderr.write('Error: %s\n' % msg) | ||
| 496 | sys.stderr.write(usage) | ||
| 497 | sys.exit(2) | ||
| 498 | # end try | ||
| 499 | action = None | ||
| 500 | stepsize = STEPSIZE | ||
| 501 | tabsize = TABSIZE | ||
| 502 | expandtabs = EXPANDTABS | ||
| 503 | for o, a in opts: | ||
| 504 | if o == '-c': | ||
| 505 | if action: error_both(o, action) | ||
| 506 | # end if | ||
| 507 | action = 'complete' | ||
| 508 | elif o == '-d': | ||
| 509 | if action: error_both(o, action) | ||
| 510 | # end if | ||
| 511 | action = 'delete' | ||
| 512 | elif o == '-r': | ||
| 513 | if action: error_both(o, action) | ||
| 514 | # end if | ||
| 515 | action = 'reformat' | ||
| 516 | elif o == '-s': | ||
| 517 | stepsize = int(a) | ||
| 518 | elif o == '-t': | ||
| 519 | tabsize = int(a) | ||
| 520 | elif o == '-e': | ||
| 521 | expandtabs = 1 | ||
| 522 | # end if | ||
| 523 | # end for | ||
| 524 | if not action: | ||
| 525 | sys.stderr.write( | ||
| 526 | 'You must specify -c(omplete), -d(elete) or -r(eformat)\n') | ||
| 527 | sys.stderr.write(usage) | ||
| 528 | sys.exit(2) | ||
| 529 | # end if | ||
| 530 | if not args or args == ['-']: | ||
| 531 | action = eval(action + '_filter') | ||
| 532 | action(sys.stdin, sys.stdout, stepsize, tabsize, expandtabs) | ||
| 533 | else: | ||
| 534 | action = eval(action + '_file') | ||
| 535 | for filename in args: | ||
| 536 | action(filename, stepsize, tabsize, expandtabs) | ||
| 537 | # end for | ||
| 538 | # end if | ||
| 539 | # end def test | ||
| 540 | |||
| 541 | if __name__ == '__main__': | ||
| 542 | test() | ||
| 543 | # end if | ||
| @@ -0,0 +1,53 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # ptags | ||
| 4 | # | ||
| 5 | # Create a tags file for Python programs, usable with vi. | ||
| 6 | # Tagged are: | ||
| 7 | # - functions (even inside other defs or classes) | ||
| 8 | # - classes | ||
| 9 | # - filenames | ||
| 10 | # Warns about files it cannot open. | ||
| 11 | # No warnings about duplicate tags. | ||
| 12 | |||
| 13 | import sys, re, os | ||
| 14 | |||
| 15 | tags = [] # Modified global variable! | ||
| 16 | |||
| 17 | def main(): | ||
| 18 | args = sys.argv[1:] | ||
| 19 | for filename in args: | ||
| 20 | treat_file(filename) | ||
| 21 | if tags: | ||
| 22 | fp = open('tags', 'w') | ||
| 23 | tags.sort() | ||
| 24 | for s in tags: fp.write(s) | ||
| 25 | |||
| 26 | |||
| 27 | expr = '^[ \t]*(def|class)[ \t]+([a-zA-Z0-9_]+)[ \t]*[:\(]' | ||
| 28 | matcher = re.compile(expr) | ||
| 29 | |||
| 30 | def treat_file(filename): | ||
| 31 | try: | ||
| 32 | fp = open(filename, 'r') | ||
| 33 | except: | ||
| 34 | sys.stderr.write('Cannot open %s\n' % filename) | ||
| 35 | return | ||
| 36 | base = os.path.basename(filename) | ||
| 37 | if base[-3:] == '.py': | ||
| 38 | base = base[:-3] | ||
| 39 | s = base + '\t' + filename + '\t' + '1\n' | ||
| 40 | tags.append(s) | ||
| 41 | while 1: | ||
| 42 | line = fp.readline() | ||
| 43 | if not line: | ||
| 44 | break | ||
| 45 | m = matcher.match(line) | ||
| 46 | if m: | ||
| 47 | content = m.group(0) | ||
| 48 | name = m.group(2) | ||
| 49 | s = name + '\t' + filename + '\t/^' + content + '/\n' | ||
| 50 | tags.append(s) | ||
| 51 | |||
| 52 | if __name__ == '__main__': | ||
| 53 | main() | ||
| @@ -0,0 +1,5 @@ | |||
| 1 | #!/usr/bin/env python | ||
| 2 | |||
| 3 | import pydoc | ||
| 4 | if __name__ == '__main__': | ||
| 5 | pydoc.cli() | ||
| @@ -0,0 +1,7 @@ | |||
| 1 | # Note: this file must not be named pydoc.pyw, lest it just end up | ||
| 2 | # importing itself (Python began allowing import of .pyw files | ||
| 3 | # between 2.2a1 and 2.2a2). | ||
| 4 | import pydoc | ||
| 5 | |||
| 6 | if __name__ == '__main__': | ||
| 7 | pydoc.gui() | ||
| @@ -0,0 +1,130 @@ | |||
| 1 | #!/usr/bin/env python | ||
| 2 | |||
| 3 | """\ | ||
| 4 | List python source files. | ||
| 5 | |||
| 6 | There are three functions to check whether a file is a Python source, listed | ||
| 7 | here with increasing complexity: | ||
| 8 | |||
| 9 | - has_python_ext() checks whether a file name ends in '.py[w]'. | ||
| 10 | - look_like_python() checks whether the file is not binary and either has | ||
| 11 | the '.py[w]' extension or the first line contains the word 'python'. | ||
| 12 | - can_be_compiled() checks whether the file can be compiled by compile(). | ||
| 13 | |||
| 14 | The file also must be of appropriate size - not bigger than a megabyte. | ||
| 15 | |||
| 16 | walk_python_files() recursively lists all Python files under the given directories. | ||
| 17 | """ | ||
| 18 | __author__ = "Oleg Broytmann, Georg Brandl" | ||
| 19 | |||
| 20 | __all__ = ["has_python_ext", "looks_like_python", "can_be_compiled", "walk_python_files"] | ||
| 21 | |||
| 22 | |||
| 23 | import sys, os, re | ||
| 24 | |||
| 25 | binary_re = re.compile('[\x00-\x08\x0E-\x1F\x7F]') | ||
| 26 | |||
| 27 | debug = False | ||
| 28 | |||
| 29 | def print_debug(msg): | ||
| 30 | if debug: print msg | ||
| 31 | |||
| 32 | |||
| 33 | def _open(fullpath): | ||
| 34 | try: | ||
| 35 | size = os.stat(fullpath).st_size | ||
| 36 | except OSError, err: # Permission denied - ignore the file | ||
| 37 | print_debug("%s: permission denied: %s" % (fullpath, err)) | ||
| 38 | return None | ||
| 39 | |||
| 40 | if size > 1024*1024: # too big | ||
| 41 | print_debug("%s: the file is too big: %d bytes" % (fullpath, size)) | ||
| 42 | return None | ||
| 43 | |||
| 44 | try: | ||
| 45 | return open(fullpath, 'rU') | ||
| 46 | except IOError, err: # Access denied, or a special file - ignore it | ||
| 47 | print_debug("%s: access denied: %s" % (fullpath, err)) | ||
| 48 | return None | ||
| 49 | |||
| 50 | def has_python_ext(fullpath): | ||
| 51 | return fullpath.endswith(".py") or fullpath.endswith(".pyw") | ||
| 52 | |||
| 53 | def looks_like_python(fullpath): | ||
| 54 | infile = _open(fullpath) | ||
| 55 | if infile is None: | ||
| 56 | return False | ||
| 57 | |||
| 58 | line = infile.readline() | ||
| 59 | infile.close() | ||
| 60 | |||
| 61 | if binary_re.search(line): | ||
| 62 | # file appears to be binary | ||
| 63 | print_debug("%s: appears to be binary" % fullpath) | ||
| 64 | return False | ||
| 65 | |||
| 66 | if fullpath.endswith(".py") or fullpath.endswith(".pyw"): | ||
| 67 | return True | ||
| 68 | elif "python" in line: | ||
| 69 | # disguised Python script (e.g. CGI) | ||
| 70 | return True | ||
| 71 | |||
| 72 | return False | ||
| 73 | |||
| 74 | def can_be_compiled(fullpath): | ||
| 75 | infile = _open(fullpath) | ||
| 76 | if infile is None: | ||
| 77 | return False | ||
| 78 | |||
| 79 | code = infile.read() | ||
| 80 | infile.close() | ||
| 81 | |||
| 82 | try: | ||
| 83 | compile(code, fullpath, "exec") | ||
| 84 | except Exception, err: | ||
| 85 | print_debug("%s: cannot compile: %s" % (fullpath, err)) | ||
| 86 | return False | ||
| 87 | |||
| 88 | return True | ||
| 89 | |||
| 90 | |||
| 91 | def walk_python_files(paths, is_python=looks_like_python, exclude_dirs=None): | ||
| 92 | """\ | ||
| 93 | Recursively yield all Python source files below the given paths. | ||
| 94 | |||
| 95 | paths: a list of files and/or directories to be checked. | ||
| 96 | is_python: a function that takes a file name and checks whether it is a | ||
| 97 | Python source file | ||
| 98 | exclude_dirs: a list of directory base names that should be excluded in | ||
| 99 | the search | ||
| 100 | """ | ||
| 101 | if exclude_dirs is None: | ||
| 102 | exclude_dirs=[] | ||
| 103 | |||
| 104 | for path in paths: | ||
| 105 | print_debug("testing: %s" % path) | ||
| 106 | if os.path.isfile(path): | ||
| 107 | if is_python(path): | ||
| 108 | yield path | ||
| 109 | elif os.path.isdir(path): | ||
| 110 | print_debug(" it is a directory") | ||
| 111 | for dirpath, dirnames, filenames in os.walk(path): | ||
| 112 | for exclude in exclude_dirs: | ||
| 113 | if exclude in dirnames: | ||
| 114 | dirnames.remove(exclude) | ||
| 115 | for filename in filenames: | ||
| 116 | fullpath = os.path.join(dirpath, filename) | ||
| 117 | print_debug("testing: %s" % fullpath) | ||
| 118 | if is_python(fullpath): | ||
| 119 | yield fullpath | ||
| 120 | else: | ||
| 121 | print_debug(" unknown type") | ||
| 122 | |||
| 123 | |||
| 124 | if __name__ == "__main__": | ||
| 125 | # Two simple examples/tests | ||
| 126 | for fullpath in walk_python_files(['.']): | ||
| 127 | print fullpath | ||
| 128 | print "----------" | ||
| 129 | for fullpath in walk_python_files(['.'], is_python=can_be_compiled): | ||
| 130 | print fullpath | ||
| @@ -0,0 +1,171 @@ | |||
| 1 | """Basic regular expression demostration facility (Perl style syntax).""" | ||
| 2 | |||
| 3 | from Tkinter import * | ||
| 4 | import re | ||
| 5 | |||
| 6 | class ReDemo: | ||
| 7 | |||
| 8 | def __init__(self, master): | ||
| 9 | self.master = master | ||
| 10 | |||
| 11 | self.promptdisplay = Label(self.master, anchor=W, | ||
| 12 | text="Enter a Perl-style regular expression:") | ||
| 13 | self.promptdisplay.pack(side=TOP, fill=X) | ||
| 14 | |||
| 15 | self.regexdisplay = Entry(self.master) | ||
| 16 | self.regexdisplay.pack(fill=X) | ||
| 17 | self.regexdisplay.focus_set() | ||
| 18 | |||
| 19 | self.addoptions() | ||
| 20 | |||
| 21 | self.statusdisplay = Label(self.master, text="", anchor=W) | ||
| 22 | self.statusdisplay.pack(side=TOP, fill=X) | ||
| 23 | |||
| 24 | self.labeldisplay = Label(self.master, anchor=W, | ||
| 25 | text="Enter a string to search:") | ||
| 26 | self.labeldisplay.pack(fill=X) | ||
| 27 | self.labeldisplay.pack(fill=X) | ||
| 28 | |||
| 29 | self.showframe = Frame(master) | ||
| 30 | self.showframe.pack(fill=X, anchor=W) | ||
| 31 | |||
| 32 | self.showvar = StringVar(master) | ||
| 33 | self.showvar.set("first") | ||
| 34 | |||
| 35 | self.showfirstradio = Radiobutton(self.showframe, | ||
| 36 | text="Highlight first match", | ||
| 37 | variable=self.showvar, | ||
| 38 | value="first", | ||
| 39 | command=self.recompile) | ||
| 40 | self.showfirstradio.pack(side=LEFT) | ||
| 41 | |||
| 42 | self.showallradio = Radiobutton(self.showframe, | ||
| 43 | text="Highlight all matches", | ||
| 44 | variable=self.showvar, | ||
| 45 | value="all", | ||
| 46 | command=self.recompile) | ||
| 47 | self.showallradio.pack(side=LEFT) | ||
| 48 | |||
| 49 | self.stringdisplay = Text(self.master, width=60, height=4) | ||
| 50 | self.stringdisplay.pack(fill=BOTH, expand=1) | ||
| 51 | self.stringdisplay.tag_configure("hit", background="yellow") | ||
| 52 | |||
| 53 | self.grouplabel = Label(self.master, text="Groups:", anchor=W) | ||
| 54 | self.grouplabel.pack(fill=X) | ||
| 55 | |||
| 56 | self.grouplist = Listbox(self.master) | ||
| 57 | self.grouplist.pack(expand=1, fill=BOTH) | ||
| 58 | |||
| 59 | self.regexdisplay.bind('<Key>', self.recompile) | ||
| 60 | self.stringdisplay.bind('<Key>', self.reevaluate) | ||
| 61 | |||
| 62 | self.compiled = None | ||
| 63 | self.recompile() | ||
| 64 | |||
| 65 | btags = self.regexdisplay.bindtags() | ||
| 66 | self.regexdisplay.bindtags(btags[1:] + btags[:1]) | ||
| 67 | |||
| 68 | btags = self.stringdisplay.bindtags() | ||
| 69 | self.stringdisplay.bindtags(btags[1:] + btags[:1]) | ||
| 70 | |||
| 71 | def addoptions(self): | ||
| 72 | self.frames = [] | ||
| 73 | self.boxes = [] | ||
| 74 | self.vars = [] | ||
| 75 | for name in ('IGNORECASE', | ||
| 76 | 'LOCALE', | ||
| 77 | 'MULTILINE', | ||
| 78 | 'DOTALL', | ||
| 79 | 'VERBOSE'): | ||
| 80 | if len(self.boxes) % 3 == 0: | ||
| 81 | frame = Frame(self.master) | ||
| 82 | frame.pack(fill=X) | ||
| 83 | self.frames.append(frame) | ||
| 84 | val = getattr(re, name) | ||
| 85 | var = IntVar() | ||
| 86 | box = Checkbutton(frame, | ||
| 87 | variable=var, text=name, | ||
| 88 | offvalue=0, onvalue=val, | ||
| 89 | command=self.recompile) | ||
| 90 | box.pack(side=LEFT) | ||
| 91 | self.boxes.append(box) | ||
| 92 | self.vars.append(var) | ||
| 93 | |||
| 94 | def getflags(self): | ||
| 95 | flags = 0 | ||
| 96 | for var in self.vars: | ||
| 97 | flags = flags | var.get() | ||
| 98 | flags = flags | ||
| 99 | return flags | ||
| 100 | |||
| 101 | def recompile(self, event=None): | ||
| 102 | try: | ||
| 103 | self.compiled = re.compile(self.regexdisplay.get(), | ||
| 104 | self.getflags()) | ||
| 105 | bg = self.promptdisplay['background'] | ||
| 106 | self.statusdisplay.config(text="", background=bg) | ||
| 107 | except re.error, msg: | ||
| 108 | self.compiled = None | ||
| 109 | self.statusdisplay.config( | ||
| 110 | text="re.error: %s" % str(msg), | ||
| 111 | background="red") | ||
| 112 | self.reevaluate() | ||
| 113 | |||
| 114 | def reevaluate(self, event=None): | ||
| 115 | try: | ||
| 116 | self.stringdisplay.tag_remove("hit", "1.0", END) | ||
| 117 | except TclError: | ||
| 118 | pass | ||
| 119 | try: | ||
| 120 | self.stringdisplay.tag_remove("hit0", "1.0", END) | ||
| 121 | except TclError: | ||
| 122 | pass | ||
| 123 | self.grouplist.delete(0, END) | ||
| 124 | if not self.compiled: | ||
| 125 | return | ||
| 126 | self.stringdisplay.tag_configure("hit", background="yellow") | ||
| 127 | self.stringdisplay.tag_configure("hit0", background="orange") | ||
| 128 | text = self.stringdisplay.get("1.0", END) | ||
| 129 | last = 0 | ||
| 130 | nmatches = 0 | ||
| 131 | while last <= len(text): | ||
| 132 | m = self.compiled.search(text, last) | ||
| 133 | if m is None: | ||
| 134 | break | ||
| 135 | first, last = m.span() | ||
| 136 | if last == first: | ||
| 137 | last = first+1 | ||
| 138 | tag = "hit0" | ||
| 139 | else: | ||
| 140 | tag = "hit" | ||
| 141 | pfirst = "1.0 + %d chars" % first | ||
| 142 | plast = "1.0 + %d chars" % last | ||
| 143 | self.stringdisplay.tag_add(tag, pfirst, plast) | ||
| 144 | if nmatches == 0: | ||
| 145 | self.stringdisplay.yview_pickplace(pfirst) | ||
| 146 | groups = list(m.groups()) | ||
| 147 | groups.insert(0, m.group()) | ||
| 148 | for i in range(len(groups)): | ||
| 149 | g = "%2d: %r" % (i, groups[i]) | ||
| 150 | self.grouplist.insert(END, g) | ||
| 151 | nmatches = nmatches + 1 | ||
| 152 | if self.showvar.get() == "first": | ||
| 153 | break | ||
| 154 | |||
| 155 | if nmatches == 0: | ||
| 156 | self.statusdisplay.config(text="(no match)", | ||
| 157 | background="yellow") | ||
| 158 | else: | ||
| 159 | self.statusdisplay.config(text="") | ||
| 160 | |||
| 161 | |||
| 162 | # Main function, run when invoked as a stand-alone Python program. | ||
| 163 | |||
| 164 | def main(): | ||
| 165 | root = Tk() | ||
| 166 | demo = ReDemo(root) | ||
| 167 | root.protocol('WM_DELETE_WINDOW', root.quit) | ||
| 168 | root.mainloop() | ||
| 169 | |||
| 170 | if __name__ == '__main__': | ||
| 171 | main() | ||
| @@ -0,0 +1,293 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Released to the public domain, by Tim Peters, 03 October 2000. | ||
| 4 | |||
| 5 | """reindent [-d][-r][-v] [ path ... ] | ||
| 6 | |||
| 7 | -d (--dryrun) Dry run. Analyze, but don't make any changes to, files. | ||
| 8 | -r (--recurse) Recurse. Search for all .py files in subdirectories too. | ||
| 9 | -v (--verbose) Verbose. Print informative msgs; else no output. | ||
| 10 | -h (--help) Help. Print this usage information and exit. | ||
| 11 | |||
| 12 | Change Python (.py) files to use 4-space indents and no hard tab characters. | ||
| 13 | Also trim excess spaces and tabs from ends of lines, and remove empty lines | ||
| 14 | at the end of files. Also ensure the last line ends with a newline. | ||
| 15 | |||
| 16 | If no paths are given on the command line, reindent operates as a filter, | ||
| 17 | reading a single source file from standard input and writing the transformed | ||
| 18 | source to standard output. In this case, the -d, -r and -v flags are | ||
| 19 | ignored. | ||
| 20 | |||
| 21 | You can pass one or more file and/or directory paths. When a directory | ||
| 22 | path, all .py files within the directory will be examined, and, if the -r | ||
| 23 | option is given, likewise recursively for subdirectories. | ||
| 24 | |||
| 25 | If output is not to standard output, reindent overwrites files in place, | ||
| 26 | renaming the originals with a .bak extension. If it finds nothing to | ||
| 27 | change, the file is left alone. If reindent does change a file, the changed | ||
| 28 | file is a fixed-point for future runs (i.e., running reindent on the | ||
| 29 | resulting .py file won't change it again). | ||
| 30 | |||
| 31 | The hard part of reindenting is figuring out what to do with comment | ||
| 32 | lines. So long as the input files get a clean bill of health from | ||
| 33 | tabnanny.py, reindent should do a good job. | ||
| 34 | """ | ||
| 35 | |||
| 36 | __version__ = "1" | ||
| 37 | |||
| 38 | import tokenize | ||
| 39 | import os | ||
| 40 | import sys | ||
| 41 | |||
| 42 | verbose = 0 | ||
| 43 | recurse = 0 | ||
| 44 | dryrun = 0 | ||
| 45 | |||
| 46 | def usage(msg=None): | ||
| 47 | if msg is not None: | ||
| 48 | print >> sys.stderr, msg | ||
| 49 | print >> sys.stderr, __doc__ | ||
| 50 | |||
| 51 | def errprint(*args): | ||
| 52 | sep = "" | ||
| 53 | for arg in args: | ||
| 54 | sys.stderr.write(sep + str(arg)) | ||
| 55 | sep = " " | ||
| 56 | sys.stderr.write("\n") | ||
| 57 | |||
| 58 | def main(): | ||
| 59 | import getopt | ||
| 60 | global verbose, recurse, dryrun | ||
| 61 | try: | ||
| 62 | opts, args = getopt.getopt(sys.argv[1:], "drvh", | ||
| 63 | ["dryrun", "recurse", "verbose", "help"]) | ||
| 64 | except getopt.error, msg: | ||
| 65 | usage(msg) | ||
| 66 | return | ||
| 67 | for o, a in opts: | ||
| 68 | if o in ('-d', '--dryrun'): | ||
| 69 | dryrun += 1 | ||
| 70 | elif o in ('-r', '--recurse'): | ||
| 71 | recurse += 1 | ||
| 72 | elif o in ('-v', '--verbose'): | ||
| 73 | verbose += 1 | ||
| 74 | elif o in ('-h', '--help'): | ||
| 75 | usage() | ||
| 76 | return | ||
| 77 | if not args: | ||
| 78 | r = Reindenter(sys.stdin) | ||
| 79 | r.run() | ||
| 80 | r.write(sys.stdout) | ||
| 81 | return | ||
| 82 | for arg in args: | ||
| 83 | check(arg) | ||
| 84 | |||
| 85 | def check(file): | ||
| 86 | if os.path.isdir(file) and not os.path.islink(file): | ||
| 87 | if verbose: | ||
| 88 | print "listing directory", file | ||
| 89 | names = os.listdir(file) | ||
| 90 | for name in names: | ||
| 91 | fullname = os.path.join(file, name) | ||
| 92 | if ((recurse and os.path.isdir(fullname) and | ||
| 93 | not os.path.islink(fullname)) | ||
| 94 | or name.lower().endswith(".py")): | ||
| 95 | check(fullname) | ||
| 96 | return | ||
| 97 | |||
| 98 | if verbose: | ||
| 99 | print "checking", file, "...", | ||
| 100 | try: | ||
| 101 | f = open(file) | ||
| 102 | except IOError, msg: | ||
| 103 | errprint("%s: I/O Error: %s" % (file, str(msg))) | ||
| 104 | return | ||
| 105 | |||
| 106 | r = Reindenter(f) | ||
| 107 | f.close() | ||
| 108 | if r.run(): | ||
| 109 | if verbose: | ||
| 110 | print "changed." | ||
| 111 | if dryrun: | ||
| 112 | print "But this is a dry run, so leaving it alone." | ||
| 113 | if not dryrun: | ||
| 114 | bak = file + ".bak" | ||
| 115 | if os.path.exists(bak): | ||
| 116 | os.remove(bak) | ||
| 117 | os.rename(file, bak) | ||
| 118 | if verbose: | ||
| 119 | print "renamed", file, "to", bak | ||
| 120 | f = open(file, "w") | ||
| 121 | r.write(f) | ||
| 122 | f.close() | ||
| 123 | if verbose: | ||
| 124 | print "wrote new", file | ||
| 125 | else: | ||
| 126 | if verbose: | ||
| 127 | print "unchanged." | ||
| 128 | |||
| 129 | def _rstrip(line, JUNK='\n \t'): | ||
| 130 | """Return line stripped of trailing spaces, tabs, newlines. | ||
| 131 | |||
| 132 | Note that line.rstrip() instead also strips sundry control characters, | ||
| 133 | but at least one known Emacs user expects to keep junk like that, not | ||
| 134 | mentioning Barry by name or anything <wink>. | ||
| 135 | """ | ||
| 136 | |||
| 137 | i = len(line) | ||
| 138 | while i > 0 and line[i-1] in JUNK: | ||
| 139 | i -= 1 | ||
| 140 | return line[:i] | ||
| 141 | |||
| 142 | class Reindenter: | ||
| 143 | |||
| 144 | def __init__(self, f): | ||
| 145 | self.find_stmt = 1 # next token begins a fresh stmt? | ||
| 146 | self.level = 0 # current indent level | ||
| 147 | |||
| 148 | # Raw file lines. | ||
| 149 | self.raw = f.readlines() | ||
| 150 | |||
| 151 | # File lines, rstripped & tab-expanded. Dummy at start is so | ||
| 152 | # that we can use tokenize's 1-based line numbering easily. | ||
| 153 | # Note that a line is all-blank iff it's "\n". | ||
| 154 | self.lines = [_rstrip(line).expandtabs() + "\n" | ||
| 155 | for line in self.raw] | ||
| 156 | self.lines.insert(0, None) | ||
| 157 | self.index = 1 # index into self.lines of next line | ||
| 158 | |||
| 159 | # List of (lineno, indentlevel) pairs, one for each stmt and | ||
| 160 | # comment line. indentlevel is -1 for comment lines, as a | ||
| 161 | # signal that tokenize doesn't know what to do about them; | ||
| 162 | # indeed, they're our headache! | ||
| 163 | self.stats = [] | ||
| 164 | |||
| 165 | def run(self): | ||
| 166 | tokenize.tokenize(self.getline, self.tokeneater) | ||
| 167 | # Remove trailing empty lines. | ||
| 168 | lines = self.lines | ||
| 169 | while lines and lines[-1] == "\n": | ||
| 170 | lines.pop() | ||
| 171 | # Sentinel. | ||
| 172 | stats = self.stats | ||
| 173 | stats.append((len(lines), 0)) | ||
| 174 | # Map count of leading spaces to # we want. | ||
| 175 | have2want = {} | ||
| 176 | # Program after transformation. | ||
| 177 | after = self.after = [] | ||
| 178 | # Copy over initial empty lines -- there's nothing to do until | ||
| 179 | # we see a line with *something* on it. | ||
| 180 | i = stats[0][0] | ||
| 181 | after.extend(lines[1:i]) | ||
| 182 | for i in range(len(stats)-1): | ||
| 183 | thisstmt, thislevel = stats[i] | ||
| 184 | nextstmt = stats[i+1][0] | ||
| 185 | have = getlspace(lines[thisstmt]) | ||
| 186 | want = thislevel * 4 | ||
| 187 | if want < 0: | ||
| 188 | # A comment line. | ||
| 189 | if have: | ||
| 190 | # An indented comment line. If we saw the same | ||
| 191 | # indentation before, reuse what it most recently | ||
| 192 | # mapped to. | ||
| 193 | want = have2want.get(have, -1) | ||
| 194 | if want < 0: | ||
| 195 | # Then it probably belongs to the next real stmt. | ||
| 196 | for j in xrange(i+1, len(stats)-1): | ||
| 197 | jline, jlevel = stats[j] | ||
| 198 | if jlevel >= 0: | ||
| 199 | if have == getlspace(lines[jline]): | ||
| 200 | want = jlevel * 4 | ||
| 201 | break | ||
| 202 | if want < 0: # Maybe it's a hanging | ||
| 203 | # comment like this one, | ||
| 204 | # in which case we should shift it like its base | ||
| 205 | # line got shifted. | ||
| 206 | for j in xrange(i-1, -1, -1): | ||
| 207 | jline, jlevel = stats[j] | ||
| 208 | if jlevel >= 0: | ||
| 209 | want = have + getlspace(after[jline-1]) - \ | ||
| 210 | getlspace(lines[jline]) | ||
| 211 | break | ||
| 212 | if want < 0: | ||
| 213 | # Still no luck -- leave it alone. | ||
| 214 | want = have | ||
| 215 | else: | ||
| 216 | want = 0 | ||
| 217 | assert want >= 0 | ||
| 218 | have2want[have] = want | ||
| 219 | diff = want - have | ||
| 220 | if diff == 0 or have == 0: | ||
| 221 | after.extend(lines[thisstmt:nextstmt]) | ||
| 222 | else: | ||
| 223 | for line in lines[thisstmt:nextstmt]: | ||
| 224 | if diff > 0: | ||
| 225 | if line == "\n": | ||
| 226 | after.append(line) | ||
| 227 | else: | ||
| 228 | after.append(" " * diff + line) | ||
| 229 | else: | ||
| 230 | remove = min(getlspace(line), -diff) | ||
| 231 | after.append(line[remove:]) | ||
| 232 | return self.raw != self.after | ||
| 233 | |||
| 234 | def write(self, f): | ||
| 235 | f.writelines(self.after) | ||
| 236 | |||
| 237 | # Line-getter for tokenize. | ||
| 238 | def getline(self): | ||
| 239 | if self.index >= len(self.lines): | ||
| 240 | line = "" | ||
| 241 | else: | ||
| 242 | line = self.lines[self.index] | ||
| 243 | self.index += 1 | ||
| 244 | return line | ||
| 245 | |||
| 246 | # Line-eater for tokenize. | ||
| 247 | def tokeneater(self, type, token, (sline, scol), end, line, | ||
| 248 | INDENT=tokenize.INDENT, | ||
| 249 | DEDENT=tokenize.DEDENT, | ||
| 250 | NEWLINE=tokenize.NEWLINE, | ||
| 251 | COMMENT=tokenize.COMMENT, | ||
| 252 | NL=tokenize.NL): | ||
| 253 | |||
| 254 | if type == NEWLINE: | ||
| 255 | # A program statement, or ENDMARKER, will eventually follow, | ||
| 256 | # after some (possibly empty) run of tokens of the form | ||
| 257 | # (NL | COMMENT)* (INDENT | DEDENT+)? | ||
| 258 | self.find_stmt = 1 | ||
| 259 | |||
| 260 | elif type == INDENT: | ||
| 261 | self.find_stmt = 1 | ||
| 262 | self.level += 1 | ||
| 263 | |||
| 264 | elif type == DEDENT: | ||
| 265 | self.find_stmt = 1 | ||
| 266 | self.level -= 1 | ||
| 267 | |||
| 268 | elif type == COMMENT: | ||
| 269 | if self.find_stmt: | ||
| 270 | self.stats.append((sline, -1)) | ||
| 271 | # but we're still looking for a new stmt, so leave | ||
| 272 | # find_stmt alone | ||
| 273 | |||
| 274 | elif type == NL: | ||
| 275 | pass | ||
| 276 | |||
| 277 | elif self.find_stmt: | ||
| 278 | # This is the first "real token" following a NEWLINE, so it | ||
| 279 | # must be the first token of the next program statement, or an | ||
| 280 | # ENDMARKER. | ||
| 281 | self.find_stmt = 0 | ||
| 282 | if line: # not endmarker | ||
| 283 | self.stats.append((sline, self.level)) | ||
| 284 | |||
| 285 | # Count number of leading blanks. | ||
| 286 | def getlspace(line): | ||
| 287 | i, n = 0, len(line) | ||
| 288 | while i < n and line[i] == " ": | ||
| 289 | i += 1 | ||
| 290 | return i | ||
| 291 | |||
| 292 | if __name__ == '__main__': | ||
| 293 | main() | ||
| @@ -0,0 +1,64 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | """Reverse grep. | ||
| 4 | |||
| 5 | Usage: rgrep [-i] pattern file | ||
| 6 | """ | ||
| 7 | |||
| 8 | import sys | ||
| 9 | import re | ||
| 10 | import getopt | ||
| 11 | |||
| 12 | def main(): | ||
| 13 | bufsize = 64*1024 | ||
| 14 | reflags = 0 | ||
| 15 | opts, args = getopt.getopt(sys.argv[1:], "i") | ||
| 16 | for o, a in opts: | ||
| 17 | if o == '-i': | ||
| 18 | reflags = reflags | re.IGNORECASE | ||
| 19 | if len(args) < 2: | ||
| 20 | usage("not enough arguments") | ||
| 21 | if len(args) > 2: | ||
| 22 | usage("exactly one file argument required") | ||
| 23 | pattern, filename = args | ||
| 24 | try: | ||
| 25 | prog = re.compile(pattern, reflags) | ||
| 26 | except re.error, msg: | ||
| 27 | usage("error in regular expression: %s" % str(msg)) | ||
| 28 | try: | ||
| 29 | f = open(filename) | ||
| 30 | except IOError, msg: | ||
| 31 | usage("can't open %s: %s" % (repr(filename), str(msg)), 1) | ||
| 32 | f.seek(0, 2) | ||
| 33 | pos = f.tell() | ||
| 34 | leftover = None | ||
| 35 | while pos > 0: | ||
| 36 | size = min(pos, bufsize) | ||
| 37 | pos = pos - size | ||
| 38 | f.seek(pos) | ||
| 39 | buffer = f.read(size) | ||
| 40 | lines = buffer.split("\n") | ||
| 41 | del buffer | ||
| 42 | if leftover is None: | ||
| 43 | if not lines[-1]: | ||
| 44 | del lines[-1] | ||
| 45 | else: | ||
| 46 | lines[-1] = lines[-1] + leftover | ||
| 47 | if pos > 0: | ||
| 48 | leftover = lines[0] | ||
| 49 | del lines[0] | ||
| 50 | else: | ||
| 51 | leftover = None | ||
| 52 | lines.reverse() | ||
| 53 | for line in lines: | ||
| 54 | if prog.search(line): | ||
| 55 | print line | ||
| 56 | |||
| 57 | def usage(msg, code=2): | ||
| 58 | sys.stdout = sys.stderr | ||
| 59 | print msg | ||
| 60 | print __doc__ | ||
| 61 | sys.exit(code) | ||
| 62 | |||
| 63 | if __name__ == '__main__': | ||
| 64 | main() | ||
| @@ -0,0 +1,19 @@ | |||
| 1 | from distutils.core import setup | ||
| 2 | |||
| 3 | if __name__ == '__main__': | ||
| 4 | setup( | ||
| 5 | scripts=[ | ||
| 6 | 'byteyears.py', | ||
| 7 | 'checkpyc.py', | ||
| 8 | 'copytime.py', | ||
| 9 | 'crlf.py', | ||
| 10 | 'dutree.py', | ||
| 11 | 'ftpmirror.py', | ||
| 12 | 'h2py.py', | ||
| 13 | 'lfcr.py', | ||
| 14 | 'logmerge.py', | ||
| 15 | '../../Lib/tabnanny.py', | ||
| 16 | '../../Lib/timeit.py', | ||
| 17 | 'untabify.py', | ||
| 18 | ], | ||
| 19 | ) | ||
| @@ -0,0 +1,30 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # suff | ||
| 4 | # | ||
| 5 | # show different suffixes amongst arguments | ||
| 6 | |||
| 7 | import sys | ||
| 8 | |||
| 9 | def main(): | ||
| 10 | files = sys.argv[1:] | ||
| 11 | suffixes = {} | ||
| 12 | for filename in files: | ||
| 13 | suff = getsuffix(filename) | ||
| 14 | if not suffixes.has_key(suff): | ||
| 15 | suffixes[suff] = [] | ||
| 16 | suffixes[suff].append(filename) | ||
| 17 | keys = suffixes.keys() | ||
| 18 | keys.sort() | ||
| 19 | for suff in keys: | ||
| 20 | print repr(suff), len(suffixes[suff]) | ||
| 21 | |||
| 22 | def getsuffix(filename): | ||
| 23 | suff = '' | ||
| 24 | for i in range(len(filename)): | ||
| 25 | if filename[i] == '.': | ||
| 26 | suff = filename[i:] | ||
| 27 | return suff | ||
| 28 | |||
| 29 | if __name__ == '__main__': | ||
| 30 | main() | ||
| @@ -0,0 +1,77 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | """ | ||
| 4 | SVN helper script. | ||
| 5 | |||
| 6 | Try to set the svn:eol-style property to "native" on every .py, .txt, .c and | ||
| 7 | .h file in the directory tree rooted at the current directory. | ||
| 8 | |||
| 9 | Files with the svn:eol-style property already set (to anything) are skipped. | ||
| 10 | |||
| 11 | svn will itself refuse to set this property on a file that's not under SVN | ||
| 12 | control, or that has a binary mime-type property set. This script inherits | ||
| 13 | that behavior, and passes on whatever warning message the failing "svn | ||
| 14 | propset" command produces. | ||
| 15 | |||
| 16 | In the Python project, it's safe to invoke this script from the root of | ||
| 17 | a checkout. | ||
| 18 | |||
| 19 | No output is produced for files that are ignored. For a file that gets | ||
| 20 | svn:eol-style set, output looks like: | ||
| 21 | |||
| 22 | property 'svn:eol-style' set on 'Lib\ctypes\__init__.py' | ||
| 23 | |||
| 24 | For a file not under version control: | ||
| 25 | |||
| 26 | svn: warning: 'patch-finalizer.txt' is not under version control | ||
| 27 | |||
| 28 | and for a file with a binary mime-type property: | ||
| 29 | |||
| 30 | svn: File 'Lib\test\test_pep263.py' has binary mime type property | ||
| 31 | """ | ||
| 32 | |||
| 33 | import re | ||
| 34 | import os | ||
| 35 | |||
| 36 | def proplist(root, fn): | ||
| 37 | "Return a list of property names for file fn in directory root" | ||
| 38 | path = os.path.join(root, ".svn", "props", fn+".svn-work") | ||
| 39 | try: | ||
| 40 | f = open(path) | ||
| 41 | except IOError: | ||
| 42 | # no properties file: not under version control | ||
| 43 | return [] | ||
| 44 | result = [] | ||
| 45 | while 1: | ||
| 46 | # key-value pairs, of the form | ||
| 47 | # K <length> | ||
| 48 | # <keyname>NL | ||
| 49 | # V length | ||
| 50 | # <value>NL | ||
| 51 | # END | ||
| 52 | line = f.readline() | ||
| 53 | if line.startswith("END"): | ||
| 54 | break | ||
| 55 | assert line.startswith("K ") | ||
| 56 | L = int(line.split()[1]) | ||
| 57 | key = f.read(L) | ||
| 58 | result.append(key) | ||
| 59 | f.readline() | ||
| 60 | line = f.readline() | ||
| 61 | assert line.startswith("V ") | ||
| 62 | L = int(line.split()[1]) | ||
| 63 | value = f.read(L) | ||
| 64 | f.readline() | ||
| 65 | f.close() | ||
| 66 | return result | ||
| 67 | |||
| 68 | possible_text_file = re.compile(r"\.([hc]|py|txt|sln|vcproj)$").search | ||
| 69 | |||
| 70 | for root, dirs, files in os.walk('.'): | ||
| 71 | if '.svn' in dirs: | ||
| 72 | dirs.remove('.svn') | ||
| 73 | for fn in files: | ||
| 74 | if possible_text_file(fn): | ||
| 75 | if 'svn:eol-style' not in proplist(root, fn): | ||
| 76 | path = os.path.join(root, fn) | ||
| 77 | os.system('svn propset svn:eol-style native "%s"' % path) | ||
| @@ -0,0 +1,233 @@ | |||
| 1 | """ TeXcheck.py -- rough syntax checking on Python style LaTeX documents. | ||
| 2 | |||
| 3 | Written by Raymond D. Hettinger <python at rcn.com> | ||
| 4 | Copyright (c) 2003 Python Software Foundation. All rights reserved. | ||
| 5 | |||
| 6 | Designed to catch common markup errors including: | ||
| 7 | * Unbalanced or mismatched parenthesis, brackets, and braces. | ||
| 8 | * Unbalanced or mismatched \\begin and \\end blocks. | ||
| 9 | * Misspelled or invalid LaTeX commands. | ||
| 10 | * Use of forward slashes instead of backslashes for commands. | ||
| 11 | * Table line size mismatches. | ||
| 12 | |||
| 13 | Sample command line usage: | ||
| 14 | python texcheck.py -k chapterheading -m lib/librandomtex *.tex | ||
| 15 | |||
| 16 | Options: | ||
| 17 | -m Munge parenthesis and brackets. [0,n) would normally mismatch. | ||
| 18 | -k keyword: Keyword is a valid LaTeX command. Do not include the backslash. | ||
| 19 | -d: Delimiter check only (useful for non-LaTeX files). | ||
| 20 | -h: Help | ||
| 21 | -s lineno: Start at lineno (useful for skipping complex sections). | ||
| 22 | -v: Verbose. Trace the matching of //begin and //end blocks. | ||
| 23 | """ | ||
| 24 | |||
| 25 | import re | ||
| 26 | import sys | ||
| 27 | import getopt | ||
| 28 | from itertools import izip, count, islice | ||
| 29 | import glob | ||
| 30 | |||
| 31 | cmdstr = r""" | ||
| 32 | \section \module \declaremodule \modulesynopsis \moduleauthor | ||
| 33 | \sectionauthor \versionadded \code \class \method \begin | ||
| 34 | \optional \var \ref \end \subsection \lineiii \hline \label | ||
| 35 | \indexii \textrm \ldots \keyword \stindex \index \item \note | ||
| 36 | \withsubitem \ttindex \footnote \citetitle \samp \opindex | ||
| 37 | \noindent \exception \strong \dfn \ctype \obindex \character | ||
| 38 | \indexiii \function \bifuncindex \refmodule \refbimodindex | ||
| 39 | \subsubsection \nodename \member \chapter \emph \ASCII \UNIX | ||
| 40 | \regexp \program \production \token \productioncont \term | ||
| 41 | \grammartoken \lineii \seemodule \file \EOF \documentclass | ||
| 42 | \usepackage \title \input \maketitle \ifhtml \fi \url \Cpp | ||
| 43 | \tableofcontents \kbd \programopt \envvar \refstmodindex | ||
| 44 | \cfunction \constant \NULL \moreargs \cfuncline \cdata | ||
| 45 | \textasciicircum \n \ABC \setindexsubitem \versionchanged | ||
| 46 | \deprecated \seetext \newcommand \POSIX \pep \warning \rfc | ||
| 47 | \verbatiminput \methodline \textgreater \seetitle \lineiv | ||
| 48 | \funclineni \ulink \manpage \funcline \dataline \unspecified | ||
| 49 | \textbackslash \mimetype \mailheader \seepep \textunderscore | ||
| 50 | \longprogramopt \infinity \plusminus \shortversion \version | ||
| 51 | \refmodindex \seerfc \makeindex \makemodindex \renewcommand | ||
| 52 | \indexname \appendix \protect \indexiv \mbox \textasciitilde | ||
| 53 | \platform \seeurl \leftmargin \labelwidth \localmoduletable | ||
| 54 | \LaTeX \copyright \memberline \backslash \pi \centerline | ||
| 55 | \caption \vspace \textwidth \menuselection \textless | ||
| 56 | \makevar \csimplemacro \menuselection \bfcode \sub \release | ||
| 57 | \email \kwindex \refexmodindex \filenq \e \menuselection | ||
| 58 | \exindex \linev \newsgroup \verbatim \setshortversion | ||
| 59 | \author \authoraddress \paragraph \subparagraph \cmemberline | ||
| 60 | \textbar \C \seelink | ||
| 61 | """ | ||
| 62 | |||
| 63 | def matchclose(c_lineno, c_symbol, openers, pairmap): | ||
| 64 | "Verify that closing delimiter matches most recent opening delimiter" | ||
| 65 | try: | ||
| 66 | o_lineno, o_symbol = openers.pop() | ||
| 67 | except IndexError: | ||
| 68 | print "\nDelimiter mismatch. On line %d, encountered closing '%s' without corresponding open" % (c_lineno, c_symbol) | ||
| 69 | return | ||
| 70 | if o_symbol in pairmap.get(c_symbol, [c_symbol]): return | ||
| 71 | print "\nOpener '%s' on line %d was not closed before encountering '%s' on line %d" % (o_symbol, o_lineno, c_symbol, c_lineno) | ||
| 72 | return | ||
| 73 | |||
| 74 | def checkit(source, opts, morecmds=[]): | ||
| 75 | """Check the LaTeX formatting in a sequence of lines. | ||
| 76 | |||
| 77 | Opts is a mapping of options to option values if any: | ||
| 78 | -m munge parenthesis and brackets | ||
| 79 | -d delimiters only checking | ||
| 80 | -v verbose trace of delimiter matching | ||
| 81 | -s lineno: linenumber to start scan (default is 1). | ||
| 82 | |||
| 83 | Morecmds is a sequence of LaTeX commands (without backslashes) that | ||
| 84 | are to be considered valid in the scan. | ||
| 85 | """ | ||
| 86 | |||
| 87 | texcmd = re.compile(r'\\[A-Za-z]+') | ||
| 88 | falsetexcmd = re.compile(r'\/([A-Za-z]+)') # Mismarked with forward slash | ||
| 89 | |||
| 90 | validcmds = set(cmdstr.split()) | ||
| 91 | for cmd in morecmds: | ||
| 92 | validcmds.add('\\' + cmd) | ||
| 93 | |||
| 94 | if '-m' in opts: | ||
| 95 | pairmap = {']':'[(', ')':'(['} # Munged openers | ||
| 96 | else: | ||
| 97 | pairmap = {']':'[', ')':'('} # Normal opener for a given closer | ||
| 98 | openpunct = set('([') # Set of valid openers | ||
| 99 | |||
| 100 | delimiters = re.compile(r'\\(begin|end){([_a-zA-Z]+)}|([()\[\]])') | ||
| 101 | braces = re.compile(r'({)|(})') | ||
| 102 | doubledwords = re.compile(r'(\b[A-za-z]+\b) \b\1\b') | ||
| 103 | spacingmarkup = re.compile(r'\\(ABC|ASCII|C|Cpp|EOF|infinity|NULL|plusminus|POSIX|UNIX)\s') | ||
| 104 | |||
| 105 | openers = [] # Stack of pending open delimiters | ||
| 106 | bracestack = [] # Stack of pending open braces | ||
| 107 | |||
| 108 | tablestart = re.compile(r'\\begin{(?:long)?table([iv]+)}') | ||
| 109 | tableline = re.compile(r'\\line([iv]+){') | ||
| 110 | tableend = re.compile(r'\\end{(?:long)?table([iv]+)}') | ||
| 111 | tablelevel = '' | ||
| 112 | tablestartline = 0 | ||
| 113 | |||
| 114 | startline = int(opts.get('-s', '1')) | ||
| 115 | lineno = 0 | ||
| 116 | |||
| 117 | for lineno, line in izip(count(startline), islice(source, startline-1, None)): | ||
| 118 | line = line.rstrip() | ||
| 119 | |||
| 120 | # Check balancing of open/close parenthesis, brackets, and begin/end blocks | ||
| 121 | for begend, name, punct in delimiters.findall(line): | ||
| 122 | if '-v' in opts: | ||
| 123 | print lineno, '|', begend, name, punct, | ||
| 124 | if begend == 'begin' and '-d' not in opts: | ||
| 125 | openers.append((lineno, name)) | ||
| 126 | elif punct in openpunct: | ||
| 127 | openers.append((lineno, punct)) | ||
| 128 | elif begend == 'end' and '-d' not in opts: | ||
| 129 | matchclose(lineno, name, openers, pairmap) | ||
| 130 | elif punct in pairmap: | ||
| 131 | matchclose(lineno, punct, openers, pairmap) | ||
| 132 | if '-v' in opts: | ||
| 133 | print ' --> ', openers | ||
| 134 | |||
| 135 | # Balance opening and closing braces | ||
| 136 | for open, close in braces.findall(line): | ||
| 137 | if open == '{': | ||
| 138 | bracestack.append(lineno) | ||
| 139 | if close == '}': | ||
| 140 | try: | ||
| 141 | bracestack.pop() | ||
| 142 | except IndexError: | ||
| 143 | print r'Warning, unmatched } on line %s.' % (lineno,) | ||
| 144 | |||
| 145 | # Optionally, skip LaTeX specific checks | ||
| 146 | if '-d' in opts: | ||
| 147 | continue | ||
| 148 | |||
| 149 | # Warn whenever forward slashes encountered with a LaTeX command | ||
| 150 | for cmd in falsetexcmd.findall(line): | ||
| 151 | if '822' in line or '.html' in line: | ||
| 152 | continue # Ignore false positives for urls and for /rfc822 | ||
| 153 | if '\\' + cmd in validcmds: | ||
| 154 | print 'Warning, forward slash used on line %d with cmd: /%s' % (lineno, cmd) | ||
| 155 | |||
| 156 | # Check for markup requiring {} for correct spacing | ||
| 157 | for cmd in spacingmarkup.findall(line): | ||
| 158 | print r'Warning, \%s should be written as \%s{} on line %d' % (cmd, cmd, lineno) | ||
| 159 | |||
| 160 | # Validate commands | ||
| 161 | nc = line.find(r'\newcommand') | ||
| 162 | if nc != -1: | ||
| 163 | start = line.find('{', nc) | ||
| 164 | end = line.find('}', start) | ||
| 165 | validcmds.add(line[start+1:end]) | ||
| 166 | for cmd in texcmd.findall(line): | ||
| 167 | if cmd not in validcmds: | ||
| 168 | print r'Warning, unknown tex cmd on line %d: \%s' % (lineno, cmd) | ||
| 169 | |||
| 170 | # Check table levels (make sure lineii only inside tableii) | ||
| 171 | m = tablestart.search(line) | ||
| 172 | if m: | ||
| 173 | tablelevel = m.group(1) | ||
| 174 | tablestartline = lineno | ||
| 175 | m = tableline.search(line) | ||
| 176 | if m and m.group(1) != tablelevel: | ||
| 177 | print r'Warning, \line%s on line %d does not match \table%s on line %d' % (m.group(1), lineno, tablelevel, tablestartline) | ||
| 178 | if tableend.search(line): | ||
| 179 | tablelevel = '' | ||
| 180 | |||
| 181 | # Style guide warnings | ||
| 182 | if 'e.g.' in line or 'i.e.' in line: | ||
| 183 | print r'Style warning, avoid use of i.e or e.g. on line %d' % (lineno,) | ||
| 184 | |||
| 185 | for dw in doubledwords.findall(line): | ||
| 186 | print r'Doubled word warning. "%s" on line %d' % (dw, lineno) | ||
| 187 | |||
| 188 | lastline = lineno | ||
| 189 | for lineno, symbol in openers: | ||
| 190 | print "Unmatched open delimiter '%s' on line %d" % (symbol, lineno) | ||
| 191 | for lineno in bracestack: | ||
| 192 | print "Unmatched { on line %d" % (lineno,) | ||
| 193 | print 'Done checking %d lines.' % (lastline,) | ||
| 194 | return 0 | ||
| 195 | |||
| 196 | def main(args=None): | ||
| 197 | if args is None: | ||
| 198 | args = sys.argv[1:] | ||
| 199 | optitems, arglist = getopt.getopt(args, "k:mdhs:v") | ||
| 200 | opts = dict(optitems) | ||
| 201 | if '-h' in opts or args==[]: | ||
| 202 | print __doc__ | ||
| 203 | return 0 | ||
| 204 | |||
| 205 | if len(arglist) < 1: | ||
| 206 | print 'Please specify a file to be checked' | ||
| 207 | return 1 | ||
| 208 | |||
| 209 | for i, filespec in enumerate(arglist): | ||
| 210 | if '*' in filespec or '?' in filespec: | ||
| 211 | arglist[i:i+1] = glob.glob(filespec) | ||
| 212 | |||
| 213 | morecmds = [v for k,v in optitems if k=='-k'] | ||
| 214 | err = [] | ||
| 215 | |||
| 216 | for filename in arglist: | ||
| 217 | print '=' * 30 | ||
| 218 | print "Checking", filename | ||
| 219 | try: | ||
| 220 | f = open(filename) | ||
| 221 | except IOError: | ||
| 222 | print 'Cannot open file %s.' % arglist[0] | ||
| 223 | return 2 | ||
| 224 | |||
| 225 | try: | ||
| 226 | err.append(checkit(f, opts, morecmds)) | ||
| 227 | finally: | ||
| 228 | f.close() | ||
| 229 | |||
| 230 | return max(err) | ||
| 231 | |||
| 232 | if __name__ == '__main__': | ||
| 233 | sys.exit(main()) | ||
| @@ -0,0 +1,2078 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Convert GNU texinfo files into HTML, one file per node. | ||
| 4 | # Based on Texinfo 2.14. | ||
| 5 | # Usage: texi2html [-d] [-d] [-c] inputfile outputdirectory | ||
| 6 | # The input file must be a complete texinfo file, e.g. emacs.texi. | ||
| 7 | # This creates many files (one per info node) in the output directory, | ||
| 8 | # overwriting existing files of the same name. All files created have | ||
| 9 | # ".html" as their extension. | ||
| 10 | |||
| 11 | |||
| 12 | # XXX To do: | ||
| 13 | # - handle @comment*** correctly | ||
| 14 | # - handle @xref {some words} correctly | ||
| 15 | # - handle @ftable correctly (items aren't indexed?) | ||
| 16 | # - handle @itemx properly | ||
| 17 | # - handle @exdent properly | ||
| 18 | # - add links directly to the proper line from indices | ||
| 19 | # - check against the definitive list of @-cmds; we still miss (among others): | ||
| 20 | # - @defindex (hard) | ||
| 21 | # - @c(omment) in the middle of a line (rarely used) | ||
| 22 | # - @this* (not really needed, only used in headers anyway) | ||
| 23 | # - @today{} (ever used outside title page?) | ||
| 24 | |||
| 25 | # More consistent handling of chapters/sections/etc. | ||
| 26 | # Lots of documentation | ||
| 27 | # Many more options: | ||
| 28 | # -top designate top node | ||
| 29 | # -links customize which types of links are included | ||
| 30 | # -split split at chapters or sections instead of nodes | ||
| 31 | # -name Allow different types of filename handling. Non unix systems | ||
| 32 | # will have problems with long node names | ||
| 33 | # ... | ||
| 34 | # Support the most recent texinfo version and take a good look at HTML 3.0 | ||
| 35 | # More debugging output (customizable) and more flexible error handling | ||
| 36 | # How about icons ? | ||
| 37 | |||
| 38 | # rpyron 2002-05-07 | ||
| 39 | # Robert Pyron <rpyron@alum.mit.edu> | ||
| 40 | # 1. BUGFIX: In function makefile(), strip blanks from the nodename. | ||
| 41 | # This is necesary to match the behavior of parser.makeref() and | ||
| 42 | # parser.do_node(). | ||
| 43 | # 2. BUGFIX fixed KeyError in end_ifset (well, I may have just made | ||
| 44 | # it go away, rather than fix it) | ||
| 45 | # 3. BUGFIX allow @menu and menu items inside @ifset or @ifclear | ||
| 46 | # 4. Support added for: | ||
| 47 | # @uref URL reference | ||
| 48 | # @image image file reference (see note below) | ||
| 49 | # @multitable output an HTML table | ||
| 50 | # @vtable | ||
| 51 | # 5. Partial support for accents, to match MAKEINFO output | ||
| 52 | # 6. I added a new command-line option, '-H basename', to specify | ||
| 53 | # HTML Help output. This will cause three files to be created | ||
| 54 | # in the current directory: | ||
| 55 | # `basename`.hhp HTML Help Workshop project file | ||
| 56 | # `basename`.hhc Contents file for the project | ||
| 57 | # `basename`.hhk Index file for the project | ||
| 58 | # When fed into HTML Help Workshop, the resulting file will be | ||
| 59 | # named `basename`.chm. | ||
| 60 | # 7. A new class, HTMLHelp, to accomplish item 6. | ||
| 61 | # 8. Various calls to HTMLHelp functions. | ||
| 62 | # A NOTE ON IMAGES: Just as 'outputdirectory' must exist before | ||
| 63 | # running this program, all referenced images must already exist | ||
| 64 | # in outputdirectory. | ||
| 65 | |||
| 66 | import os | ||
| 67 | import sys | ||
| 68 | import string | ||
| 69 | import re | ||
| 70 | |||
| 71 | MAGIC = '\\input texinfo' | ||
| 72 | |||
| 73 | cmprog = re.compile('^@([a-z]+)([ \t]|$)') # Command (line-oriented) | ||
| 74 | blprog = re.compile('^[ \t]*$') # Blank line | ||
| 75 | kwprog = re.compile('@[a-z]+') # Keyword (embedded, usually | ||
| 76 | # with {} args) | ||
| 77 | spprog = re.compile('[\n@{}&<>]') # Special characters in | ||
| 78 | # running text | ||
| 79 | # | ||
| 80 | # menu item (Yuck!) | ||
| 81 | miprog = re.compile('^\* ([^:]*):(:|[ \t]*([^\t,\n.]+)([^ \t\n]*))[ \t\n]*') | ||
| 82 | # 0 1 1 2 3 34 42 0 | ||
| 83 | # ----- ---------- --------- | ||
| 84 | # -|----------------------------- | ||
| 85 | # ----------------------------------------------------- | ||
| 86 | |||
| 87 | |||
| 88 | |||
| 89 | |||
| 90 | class HTMLNode: | ||
| 91 | """Some of the parser's functionality is separated into this class. | ||
| 92 | |||
| 93 | A Node accumulates its contents, takes care of links to other Nodes | ||
| 94 | and saves itself when it is finished and all links are resolved. | ||
| 95 | """ | ||
| 96 | |||
| 97 | DOCTYPE = '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">' | ||
| 98 | |||
| 99 | type = 0 | ||
| 100 | cont = '' | ||
| 101 | epilogue = '</BODY></HTML>\n' | ||
| 102 | |||
| 103 | def __init__(self, dir, name, topname, title, next, prev, up): | ||
| 104 | self.dirname = dir | ||
| 105 | self.name = name | ||
| 106 | if topname: | ||
| 107 | self.topname = topname | ||
| 108 | else: | ||
| 109 | self.topname = name | ||
| 110 | self.title = title | ||
| 111 | self.next = next | ||
| 112 | self.prev = prev | ||
| 113 | self.up = up | ||
| 114 | self.lines = [] | ||
| 115 | |||
| 116 | def write(self, *lines): | ||
| 117 | map(self.lines.append, lines) | ||
| 118 | |||
| 119 | def flush(self): | ||
| 120 | fp = open(self.dirname + '/' + makefile(self.name), 'w') | ||
| 121 | fp.write(self.prologue) | ||
| 122 | fp.write(self.text) | ||
| 123 | fp.write(self.epilogue) | ||
| 124 | fp.close() | ||
| 125 | |||
| 126 | def link(self, label, nodename, rel=None, rev=None): | ||
| 127 | if nodename: | ||
| 128 | if nodename.lower() == '(dir)': | ||
| 129 | addr = '../dir.html' | ||
| 130 | title = '' | ||
| 131 | else: | ||
| 132 | addr = makefile(nodename) | ||
| 133 | title = ' TITLE="%s"' % nodename | ||
| 134 | self.write(label, ': <A HREF="', addr, '"', \ | ||
| 135 | rel and (' REL=' + rel) or "", \ | ||
| 136 | rev and (' REV=' + rev) or "", \ | ||
| 137 | title, '>', nodename, '</A> \n') | ||
| 138 | |||
| 139 | def finalize(self): | ||
| 140 | length = len(self.lines) | ||
| 141 | self.text = ''.join(self.lines) | ||
| 142 | self.lines = [] | ||
| 143 | self.open_links() | ||
| 144 | self.output_links() | ||
| 145 | self.close_links() | ||
| 146 | links = ''.join(self.lines) | ||
| 147 | self.lines = [] | ||
| 148 | self.prologue = ( | ||
| 149 | self.DOCTYPE + | ||
| 150 | '\n<HTML><HEAD>\n' | ||
| 151 | ' <!-- Converted with texi2html and Python -->\n' | ||
| 152 | ' <TITLE>' + self.title + '</TITLE>\n' | ||
| 153 | ' <LINK REL=Next HREF="' | ||
| 154 | + makefile(self.next) + '" TITLE="' + self.next + '">\n' | ||
| 155 | ' <LINK REL=Previous HREF="' | ||
| 156 | + makefile(self.prev) + '" TITLE="' + self.prev + '">\n' | ||
| 157 | ' <LINK REL=Up HREF="' | ||
| 158 | + makefile(self.up) + '" TITLE="' + self.up + '">\n' | ||
| 159 | '</HEAD><BODY>\n' + | ||
| 160 | links) | ||
| 161 | if length > 20: | ||
| 162 | self.epilogue = '<P>\n%s</BODY></HTML>\n' % links | ||
| 163 | |||
| 164 | def open_links(self): | ||
| 165 | self.write('<HR>\n') | ||
| 166 | |||
| 167 | def close_links(self): | ||
| 168 | self.write('<HR>\n') | ||
| 169 | |||
| 170 | def output_links(self): | ||
| 171 | if self.cont != self.next: | ||
| 172 | self.link(' Cont', self.cont) | ||
| 173 | self.link(' Next', self.next, rel='Next') | ||
| 174 | self.link(' Prev', self.prev, rel='Previous') | ||
| 175 | self.link(' Up', self.up, rel='Up') | ||
| 176 | if self.name <> self.topname: | ||
| 177 | self.link(' Top', self.topname) | ||
| 178 | |||
| 179 | |||
| 180 | class HTML3Node(HTMLNode): | ||
| 181 | |||
| 182 | DOCTYPE = '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML Level 3//EN//3.0">' | ||
| 183 | |||
| 184 | def open_links(self): | ||
| 185 | self.write('<DIV CLASS=Navigation>\n <HR>\n') | ||
| 186 | |||
| 187 | def close_links(self): | ||
| 188 | self.write(' <HR>\n</DIV>\n') | ||
| 189 | |||
| 190 | |||
| 191 | class TexinfoParser: | ||
| 192 | |||
| 193 | COPYRIGHT_SYMBOL = "©" | ||
| 194 | FN_ID_PATTERN = "(%(id)s)" | ||
| 195 | FN_SOURCE_PATTERN = '<A NAME=footnoteref%(id)s' \ | ||
| 196 | ' HREF="#footnotetext%(id)s">' \ | ||
| 197 | + FN_ID_PATTERN + '</A>' | ||
| 198 | FN_TARGET_PATTERN = '<A NAME=footnotetext%(id)s' \ | ||
| 199 | ' HREF="#footnoteref%(id)s">' \ | ||
| 200 | + FN_ID_PATTERN + '</A>\n%(text)s<P>\n' | ||
| 201 | FN_HEADER = '\n<P>\n<HR NOSHADE SIZE=1 WIDTH=200>\n' \ | ||
| 202 | '<STRONG><EM>Footnotes</EM></STRONG>\n<P>' | ||
| 203 | |||
| 204 | |||
| 205 | Node = HTMLNode | ||
| 206 | |||
| 207 | # Initialize an instance | ||
| 208 | def __init__(self): | ||
| 209 | self.unknown = {} # statistics about unknown @-commands | ||
| 210 | self.filenames = {} # Check for identical filenames | ||
| 211 | self.debugging = 0 # larger values produce more output | ||
| 212 | self.print_headers = 0 # always print headers? | ||
| 213 | self.nodefp = None # open file we're writing to | ||
| 214 | self.nodelineno = 0 # Linenumber relative to node | ||
| 215 | self.links = None # Links from current node | ||
| 216 | self.savetext = None # If not None, save text head instead | ||
| 217 | self.savestack = [] # If not None, save text head instead | ||
| 218 | self.htmlhelp = None # html help data | ||
| 219 | self.dirname = 'tmp' # directory where files are created | ||
| 220 | self.includedir = '.' # directory to search @include files | ||
| 221 | self.nodename = '' # name of current node | ||
| 222 | self.topname = '' # name of top node (first node seen) | ||
| 223 | self.title = '' # title of this whole Texinfo tree | ||
| 224 | self.resetindex() # Reset all indices | ||
| 225 | self.contents = [] # Reset table of contents | ||
| 226 | self.numbering = [] # Reset section numbering counters | ||
| 227 | self.nofill = 0 # Normal operation: fill paragraphs | ||
| 228 | self.values={'html': 1} # Names that should be parsed in ifset | ||
| 229 | self.stackinfo={} # Keep track of state in the stack | ||
| 230 | # XXX The following should be reset per node?! | ||
| 231 | self.footnotes = [] # Reset list of footnotes | ||
| 232 | self.itemarg = None # Reset command used by @item | ||
| 233 | self.itemnumber = None # Reset number for @item in @enumerate | ||
| 234 | self.itemindex = None # Reset item index name | ||
| 235 | self.node = None | ||
| 236 | self.nodestack = [] | ||
| 237 | self.cont = 0 | ||
| 238 | self.includedepth = 0 | ||
| 239 | |||
| 240 | # Set htmlhelp helper class | ||
| 241 | def sethtmlhelp(self, htmlhelp): | ||
| 242 | self.htmlhelp = htmlhelp | ||
| 243 | |||
| 244 | # Set (output) directory name | ||
| 245 | def setdirname(self, dirname): | ||
| 246 | self.dirname = dirname | ||
| 247 | |||
| 248 | # Set include directory name | ||
| 249 | def setincludedir(self, includedir): | ||
| 250 | self.includedir = includedir | ||
| 251 | |||
| 252 | # Parse the contents of an entire file | ||
| 253 | def parse(self, fp): | ||
| 254 | line = fp.readline() | ||
| 255 | lineno = 1 | ||
| 256 | while line and (line[0] == '%' or blprog.match(line)): | ||
| 257 | line = fp.readline() | ||
| 258 | lineno = lineno + 1 | ||
| 259 | if line[:len(MAGIC)] <> MAGIC: | ||
| 260 | raise SyntaxError, 'file does not begin with %r' % (MAGIC,) | ||
| 261 | self.parserest(fp, lineno) | ||
| 262 | |||
| 263 | # Parse the contents of a file, not expecting a MAGIC header | ||
| 264 | def parserest(self, fp, initial_lineno): | ||
| 265 | lineno = initial_lineno | ||
| 266 | self.done = 0 | ||
| 267 | self.skip = 0 | ||
| 268 | self.stack = [] | ||
| 269 | accu = [] | ||
| 270 | while not self.done: | ||
| 271 | line = fp.readline() | ||
| 272 | self.nodelineno = self.nodelineno + 1 | ||
| 273 | if not line: | ||
| 274 | if accu: | ||
| 275 | if not self.skip: self.process(accu) | ||
| 276 | accu = [] | ||
| 277 | if initial_lineno > 0: | ||
| 278 | print '*** EOF before @bye' | ||
| 279 | break | ||
| 280 | lineno = lineno + 1 | ||
| 281 | mo = cmprog.match(line) | ||
| 282 | if mo: | ||
| 283 | a, b = mo.span(1) | ||
| 284 | cmd = line[a:b] | ||
| 285 | if cmd in ('noindent', 'refill'): | ||
| 286 | accu.append(line) | ||
| 287 | else: | ||
| 288 | if accu: | ||
| 289 | if not self.skip: | ||
| 290 | self.process(accu) | ||
| 291 | accu = [] | ||
| 292 | self.command(line, mo) | ||
| 293 | elif blprog.match(line) and \ | ||
| 294 | 'format' not in self.stack and \ | ||
| 295 | 'example' not in self.stack: | ||
| 296 | if accu: | ||
| 297 | if not self.skip: | ||
| 298 | self.process(accu) | ||
| 299 | if self.nofill: | ||
| 300 | self.write('\n') | ||
| 301 | else: | ||
| 302 | self.write('<P>\n') | ||
| 303 | accu = [] | ||
| 304 | else: | ||
| 305 | # Append the line including trailing \n! | ||
| 306 | accu.append(line) | ||
| 307 | # | ||
| 308 | if self.skip: | ||
| 309 | print '*** Still skipping at the end' | ||
| 310 | if self.stack: | ||
| 311 | print '*** Stack not empty at the end' | ||
| 312 | print '***', self.stack | ||
| 313 | if self.includedepth == 0: | ||
| 314 | while self.nodestack: | ||
| 315 | self.nodestack[-1].finalize() | ||
| 316 | self.nodestack[-1].flush() | ||
| 317 | del self.nodestack[-1] | ||
| 318 | |||
| 319 | # Start saving text in a buffer instead of writing it to a file | ||
| 320 | def startsaving(self): | ||
| 321 | if self.savetext <> None: | ||
| 322 | self.savestack.append(self.savetext) | ||
| 323 | # print '*** Recursively saving text, expect trouble' | ||
| 324 | self.savetext = '' | ||
| 325 | |||
| 326 | # Return the text saved so far and start writing to file again | ||
| 327 | def collectsavings(self): | ||
| 328 | savetext = self.savetext | ||
| 329 | if len(self.savestack) > 0: | ||
| 330 | self.savetext = self.savestack[-1] | ||
| 331 | del self.savestack[-1] | ||
| 332 | else: | ||
| 333 | self.savetext = None | ||
| 334 | return savetext or '' | ||
| 335 | |||
| 336 | # Write text to file, or save it in a buffer, or ignore it | ||
| 337 | def write(self, *args): | ||
| 338 | try: | ||
| 339 | text = ''.join(args) | ||
| 340 | except: | ||
| 341 | print args | ||
| 342 | raise TypeError | ||
| 343 | if self.savetext <> None: | ||
| 344 | self.savetext = self.savetext + text | ||
| 345 | elif self.nodefp: | ||
| 346 | self.nodefp.write(text) | ||
| 347 | elif self.node: | ||
| 348 | self.node.write(text) | ||
| 349 | |||
| 350 | # Complete the current node -- write footnotes and close file | ||
| 351 | def endnode(self): | ||
| 352 | if self.savetext <> None: | ||
| 353 | print '*** Still saving text at end of node' | ||
| 354 | dummy = self.collectsavings() | ||
| 355 | if self.footnotes: | ||
| 356 | self.writefootnotes() | ||
| 357 | if self.nodefp: | ||
| 358 | if self.nodelineno > 20: | ||
| 359 | self.write('<HR>\n') | ||
| 360 | [name, next, prev, up] = self.nodelinks[:4] | ||
| 361 | self.link('Next', next) | ||
| 362 | self.link('Prev', prev) | ||
| 363 | self.link('Up', up) | ||
| 364 | if self.nodename <> self.topname: | ||
| 365 | self.link('Top', self.topname) | ||
| 366 | self.write('<HR>\n') | ||
| 367 | self.write('</BODY>\n') | ||
| 368 | self.nodefp.close() | ||
| 369 | self.nodefp = None | ||
| 370 | elif self.node: | ||
| 371 | if not self.cont and \ | ||
| 372 | (not self.node.type or \ | ||
| 373 | (self.node.next and self.node.prev and self.node.up)): | ||
| 374 | self.node.finalize() | ||
| 375 | self.node.flush() | ||
| 376 | else: | ||
| 377 | self.nodestack.append(self.node) | ||
| 378 | self.node = None | ||
| 379 | self.nodename = '' | ||
| 380 | |||
| 381 | # Process a list of lines, expanding embedded @-commands | ||
| 382 | # This mostly distinguishes between menus and normal text | ||
| 383 | def process(self, accu): | ||
| 384 | if self.debugging > 1: | ||
| 385 | print '!'*self.debugging, 'process:', self.skip, self.stack, | ||
| 386 | if accu: print accu[0][:30], | ||
| 387 | if accu[0][30:] or accu[1:]: print '...', | ||
| 388 | |||
| 389 | if self.inmenu(): | ||
| 390 | # XXX should be done differently | ||
| 391 | for line in accu: | ||
| 392 | mo = miprog.match(line) | ||
| 393 | if not mo: | ||
| 394 | line = line.strip() + '\n' | ||
| 395 | self.expand(line) | ||
| 396 | continue | ||
| 397 | bgn, end = mo.span(0) | ||
| 398 | a, b = mo.span(1) | ||
| 399 | c, d = mo.span(2) | ||
| 400 | e, f = mo.span(3) | ||
| 401 | g, h = mo.span(4) | ||
| 402 | label = line[a:b] | ||
| 403 | nodename = line[c:d] | ||
| 404 | if nodename[0] == ':': nodename = label | ||
| 405 | else: nodename = line[e:f] | ||
| 406 | punct = line[g:h] | ||
| 407 | self.write(' <LI><A HREF="', | ||
| 408 | makefile(nodename), | ||
| 409 | '">', nodename, | ||
| 410 | '</A>', punct, '\n') | ||
| 411 | self.htmlhelp.menuitem(nodename) | ||
| 412 | self.expand(line[end:]) | ||
| 413 | else: | ||
| 414 | text = ''.join(accu) | ||
| 415 | self.expand(text) | ||
| 416 | |||
| 417 | # find 'menu' (we might be inside 'ifset' or 'ifclear') | ||
| 418 | def inmenu(self): | ||
| 419 | #if 'menu' in self.stack: | ||
| 420 | # print 'inmenu :', self.skip, self.stack, self.stackinfo | ||
| 421 | stack = self.stack | ||
| 422 | while stack and stack[-1] in ('ifset','ifclear'): | ||
| 423 | try: | ||
| 424 | if self.stackinfo[len(stack)]: | ||
| 425 | return 0 | ||
| 426 | except KeyError: | ||
| 427 | pass | ||
| 428 | stack = stack[:-1] | ||
| 429 | return (stack and stack[-1] == 'menu') | ||
| 430 | |||
| 431 | # Write a string, expanding embedded @-commands | ||
| 432 | def expand(self, text): | ||
| 433 | stack = [] | ||
| 434 | i = 0 | ||
| 435 | n = len(text) | ||
| 436 | while i < n: | ||
| 437 | start = i | ||
| 438 | mo = spprog.search(text, i) | ||
| 439 | if mo: | ||
| 440 | i = mo.start() | ||
| 441 | else: | ||
| 442 | self.write(text[start:]) | ||
| 443 | break | ||
| 444 | self.write(text[start:i]) | ||
| 445 | c = text[i] | ||
| 446 | i = i+1 | ||
| 447 | if c == '\n': | ||
| 448 | self.write('\n') | ||
| 449 | continue | ||
| 450 | if c == '<': | ||
| 451 | self.write('<') | ||
| 452 | continue | ||
| 453 | if c == '>': | ||
| 454 | self.write('>') | ||
| 455 | continue | ||
| 456 | if c == '&': | ||
| 457 | self.write('&') | ||
| 458 | continue | ||
| 459 | if c == '{': | ||
| 460 | stack.append('') | ||
| 461 | continue | ||
| 462 | if c == '}': | ||
| 463 | if not stack: | ||
| 464 | print '*** Unmatched }' | ||
| 465 | self.write('}') | ||
| 466 | continue | ||
| 467 | cmd = stack[-1] | ||
| 468 | del stack[-1] | ||
| 469 | try: | ||
| 470 | method = getattr(self, 'close_' + cmd) | ||
| 471 | except AttributeError: | ||
| 472 | self.unknown_close(cmd) | ||
| 473 | continue | ||
| 474 | method() | ||
| 475 | continue | ||
| 476 | if c <> '@': | ||
| 477 | # Cannot happen unless spprog is changed | ||
| 478 | raise RuntimeError, 'unexpected funny %r' % c | ||
| 479 | start = i | ||
| 480 | while i < n and text[i] in string.ascii_letters: i = i+1 | ||
| 481 | if i == start: | ||
| 482 | # @ plus non-letter: literal next character | ||
| 483 | i = i+1 | ||
| 484 | c = text[start:i] | ||
| 485 | if c == ':': | ||
| 486 | # `@:' means no extra space after | ||
| 487 | # preceding `.', `?', `!' or `:' | ||
| 488 | pass | ||
| 489 | else: | ||
| 490 | # `@.' means a sentence-ending period; | ||
| 491 | # `@@', `@{', `@}' quote `@', `{', `}' | ||
| 492 | self.write(c) | ||
| 493 | continue | ||
| 494 | cmd = text[start:i] | ||
| 495 | if i < n and text[i] == '{': | ||
| 496 | i = i+1 | ||
| 497 | stack.append(cmd) | ||
| 498 | try: | ||
| 499 | method = getattr(self, 'open_' + cmd) | ||
| 500 | except AttributeError: | ||
| 501 | self.unknown_open(cmd) | ||
| 502 | continue | ||
| 503 | method() | ||
| 504 | continue | ||
| 505 | try: | ||
| 506 | method = getattr(self, 'handle_' + cmd) | ||
| 507 | except AttributeError: | ||
| 508 | self.unknown_handle(cmd) | ||
| 509 | continue | ||
| 510 | method() | ||
| 511 | if stack: | ||
| 512 | print '*** Stack not empty at para:', stack | ||
| 513 | |||
| 514 | # --- Handle unknown embedded @-commands --- | ||
| 515 | |||
| 516 | def unknown_open(self, cmd): | ||
| 517 | print '*** No open func for @' + cmd + '{...}' | ||
| 518 | cmd = cmd + '{' | ||
| 519 | self.write('@', cmd) | ||
| 520 | if not self.unknown.has_key(cmd): | ||
| 521 | self.unknown[cmd] = 1 | ||
| 522 | else: | ||
| 523 | self.unknown[cmd] = self.unknown[cmd] + 1 | ||
| 524 | |||
| 525 | def unknown_close(self, cmd): | ||
| 526 | print '*** No close func for @' + cmd + '{...}' | ||
| 527 | cmd = '}' + cmd | ||
| 528 | self.write('}') | ||
| 529 | if not self.unknown.has_key(cmd): | ||
| 530 | self.unknown[cmd] = 1 | ||
| 531 | else: | ||
| 532 | self.unknown[cmd] = self.unknown[cmd] + 1 | ||
| 533 | |||
| 534 | def unknown_handle(self, cmd): | ||
| 535 | print '*** No handler for @' + cmd | ||
| 536 | self.write('@', cmd) | ||
| 537 | if not self.unknown.has_key(cmd): | ||
| 538 | self.unknown[cmd] = 1 | ||
| 539 | else: | ||
| 540 | self.unknown[cmd] = self.unknown[cmd] + 1 | ||
| 541 | |||
| 542 | # XXX The following sections should be ordered as the texinfo docs | ||
| 543 | |||
| 544 | # --- Embedded @-commands without {} argument list -- | ||
| 545 | |||
| 546 | def handle_noindent(self): pass | ||
| 547 | |||
| 548 | def handle_refill(self): pass | ||
| 549 | |||
| 550 | # --- Include file handling --- | ||
| 551 | |||
| 552 | def do_include(self, args): | ||
| 553 | file = args | ||
| 554 | file = os.path.join(self.includedir, file) | ||
| 555 | try: | ||
| 556 | fp = open(file, 'r') | ||
| 557 | except IOError, msg: | ||
| 558 | print '*** Can\'t open include file', repr(file) | ||
| 559 | return | ||
| 560 | print '!'*self.debugging, '--> file', repr(file) | ||
| 561 | save_done = self.done | ||
| 562 | save_skip = self.skip | ||
| 563 | save_stack = self.stack | ||
| 564 | self.includedepth = self.includedepth + 1 | ||
| 565 | self.parserest(fp, 0) | ||
| 566 | self.includedepth = self.includedepth - 1 | ||
| 567 | fp.close() | ||
| 568 | self.done = save_done | ||
| 569 | self.skip = save_skip | ||
| 570 | self.stack = save_stack | ||
| 571 | print '!'*self.debugging, '<-- file', repr(file) | ||
| 572 | |||
| 573 | # --- Special Insertions --- | ||
| 574 | |||
| 575 | def open_dmn(self): pass | ||
| 576 | def close_dmn(self): pass | ||
| 577 | |||
| 578 | def open_dots(self): self.write('...') | ||
| 579 | def close_dots(self): pass | ||
| 580 | |||
| 581 | def open_bullet(self): pass | ||
| 582 | def close_bullet(self): pass | ||
| 583 | |||
| 584 | def open_TeX(self): self.write('TeX') | ||
| 585 | def close_TeX(self): pass | ||
| 586 | |||
| 587 | def handle_copyright(self): self.write(self.COPYRIGHT_SYMBOL) | ||
| 588 | def open_copyright(self): self.write(self.COPYRIGHT_SYMBOL) | ||
| 589 | def close_copyright(self): pass | ||
| 590 | |||
| 591 | def open_minus(self): self.write('-') | ||
| 592 | def close_minus(self): pass | ||
| 593 | |||
| 594 | # --- Accents --- | ||
| 595 | |||
| 596 | # rpyron 2002-05-07 | ||
| 597 | # I would like to do at least as well as makeinfo when | ||
| 598 | # it is producing HTML output: | ||
| 599 | # | ||
| 600 | # input output | ||
| 601 | # @"o @"o umlaut accent | ||
| 602 | # @'o 'o acute accent | ||
| 603 | # @,{c} @,{c} cedilla accent | ||
| 604 | # @=o @=o macron/overbar accent | ||
| 605 | # @^o @^o circumflex accent | ||
| 606 | # @`o `o grave accent | ||
| 607 | # @~o @~o tilde accent | ||
| 608 | # @dotaccent{o} @dotaccent{o} overdot accent | ||
| 609 | # @H{o} @H{o} long Hungarian umlaut | ||
| 610 | # @ringaccent{o} @ringaccent{o} ring accent | ||
| 611 | # @tieaccent{oo} @tieaccent{oo} tie-after accent | ||
| 612 | # @u{o} @u{o} breve accent | ||
| 613 | # @ubaraccent{o} @ubaraccent{o} underbar accent | ||
| 614 | # @udotaccent{o} @udotaccent{o} underdot accent | ||
| 615 | # @v{o} @v{o} hacek or check accent | ||
| 616 | # @exclamdown{} ¡ upside-down ! | ||
| 617 | # @questiondown{} ¿ upside-down ? | ||
| 618 | # @aa{},@AA{} å,Å a,A with circle | ||
| 619 | # @ae{},@AE{} æ,Æ ae,AE ligatures | ||
| 620 | # @dotless{i} @dotless{i} dotless i | ||
| 621 | # @dotless{j} @dotless{j} dotless j | ||
| 622 | # @l{},@L{} l/,L/ suppressed-L,l | ||
| 623 | # @o{},@O{} ø,Ø O,o with slash | ||
| 624 | # @oe{},@OE{} oe,OE oe,OE ligatures | ||
| 625 | # @ss{} ß es-zet or sharp S | ||
| 626 | # | ||
| 627 | # The following character codes and approximations have been | ||
| 628 | # copied from makeinfo's HTML output. | ||
| 629 | |||
| 630 | def open_exclamdown(self): self.write('¡') # upside-down ! | ||
| 631 | def close_exclamdown(self): pass | ||
| 632 | def open_questiondown(self): self.write('¿') # upside-down ? | ||
| 633 | def close_questiondown(self): pass | ||
| 634 | def open_aa(self): self.write('å') # a with circle | ||
| 635 | def close_aa(self): pass | ||
| 636 | def open_AA(self): self.write('Å') # A with circle | ||
| 637 | def close_AA(self): pass | ||
| 638 | def open_ae(self): self.write('æ') # ae ligatures | ||
| 639 | def close_ae(self): pass | ||
| 640 | def open_AE(self): self.write('Æ') # AE ligatures | ||
| 641 | def close_AE(self): pass | ||
| 642 | def open_o(self): self.write('ø') # o with slash | ||
| 643 | def close_o(self): pass | ||
| 644 | def open_O(self): self.write('Ø') # O with slash | ||
| 645 | def close_O(self): pass | ||
| 646 | def open_ss(self): self.write('ß') # es-zet or sharp S | ||
| 647 | def close_ss(self): pass | ||
| 648 | def open_oe(self): self.write('oe') # oe ligatures | ||
| 649 | def close_oe(self): pass | ||
| 650 | def open_OE(self): self.write('OE') # OE ligatures | ||
| 651 | def close_OE(self): pass | ||
| 652 | def open_l(self): self.write('l/') # suppressed-l | ||
| 653 | def close_l(self): pass | ||
| 654 | def open_L(self): self.write('L/') # suppressed-L | ||
| 655 | def close_L(self): pass | ||
| 656 | |||
| 657 | # --- Special Glyphs for Examples --- | ||
| 658 | |||
| 659 | def open_result(self): self.write('=>') | ||
| 660 | def close_result(self): pass | ||
| 661 | |||
| 662 | def open_expansion(self): self.write('==>') | ||
| 663 | def close_expansion(self): pass | ||
| 664 | |||
| 665 | def open_print(self): self.write('-|') | ||
| 666 | def close_print(self): pass | ||
| 667 | |||
| 668 | def open_error(self): self.write('error-->') | ||
| 669 | def close_error(self): pass | ||
| 670 | |||
| 671 | def open_equiv(self): self.write('==') | ||
| 672 | def close_equiv(self): pass | ||
| 673 | |||
| 674 | def open_point(self): self.write('-!-') | ||
| 675 | def close_point(self): pass | ||
| 676 | |||
| 677 | # --- Cross References --- | ||
| 678 | |||
| 679 | def open_pxref(self): | ||
| 680 | self.write('see ') | ||
| 681 | self.startsaving() | ||
| 682 | def close_pxref(self): | ||
| 683 | self.makeref() | ||
| 684 | |||
| 685 | def open_xref(self): | ||
| 686 | self.write('See ') | ||
| 687 | self.startsaving() | ||
| 688 | def close_xref(self): | ||
| 689 | self.makeref() | ||
| 690 | |||
| 691 | def open_ref(self): | ||
| 692 | self.startsaving() | ||
| 693 | def close_ref(self): | ||
| 694 | self.makeref() | ||
| 695 | |||
| 696 | def open_inforef(self): | ||
| 697 | self.write('See info file ') | ||
| 698 | self.startsaving() | ||
| 699 | def close_inforef(self): | ||
| 700 | text = self.collectsavings() | ||
| 701 | args = [s.strip() for s in text.split(',')] | ||
| 702 | while len(args) < 3: args.append('') | ||
| 703 | node = args[0] | ||
| 704 | file = args[2] | ||
| 705 | self.write('`', file, '\', node `', node, '\'') | ||
| 706 | |||
| 707 | def makeref(self): | ||
| 708 | text = self.collectsavings() | ||
| 709 | args = [s.strip() for s in text.split(',')] | ||
| 710 | while len(args) < 5: args.append('') | ||
| 711 | nodename = label = args[0] | ||
| 712 | if args[2]: label = args[2] | ||
| 713 | file = args[3] | ||
| 714 | title = args[4] | ||
| 715 | href = makefile(nodename) | ||
| 716 | if file: | ||
| 717 | href = '../' + file + '/' + href | ||
| 718 | self.write('<A HREF="', href, '">', label, '</A>') | ||
| 719 | |||
| 720 | # rpyron 2002-05-07 uref support | ||
| 721 | def open_uref(self): | ||
| 722 | self.startsaving() | ||
| 723 | def close_uref(self): | ||
| 724 | text = self.collectsavings() | ||
| 725 | args = [s.strip() for s in text.split(',')] | ||
| 726 | while len(args) < 2: args.append('') | ||
| 727 | href = args[0] | ||
| 728 | label = args[1] | ||
| 729 | if not label: label = href | ||
| 730 | self.write('<A HREF="', href, '">', label, '</A>') | ||
| 731 | |||
| 732 | # rpyron 2002-05-07 image support | ||
| 733 | # GNU makeinfo producing HTML output tries `filename.png'; if | ||
| 734 | # that does not exist, it tries `filename.jpg'. If that does | ||
| 735 | # not exist either, it complains. GNU makeinfo does not handle | ||
| 736 | # GIF files; however, I include GIF support here because | ||
| 737 | # MySQL documentation uses GIF files. | ||
| 738 | |||
| 739 | def open_image(self): | ||
| 740 | self.startsaving() | ||
| 741 | def close_image(self): | ||
| 742 | self.makeimage() | ||
| 743 | def makeimage(self): | ||
| 744 | text = self.collectsavings() | ||
| 745 | args = [s.strip() for s in text.split(',')] | ||
| 746 | while len(args) < 5: args.append('') | ||
| 747 | filename = args[0] | ||
| 748 | width = args[1] | ||
| 749 | height = args[2] | ||
| 750 | alt = args[3] | ||
| 751 | ext = args[4] | ||
| 752 | |||
| 753 | # The HTML output will have a reference to the image | ||
| 754 | # that is relative to the HTML output directory, | ||
| 755 | # which is what 'filename' gives us. However, we need | ||
| 756 | # to find it relative to our own current directory, | ||
| 757 | # so we construct 'imagename'. | ||
| 758 | imagelocation = self.dirname + '/' + filename | ||
| 759 | |||
| 760 | if os.path.exists(imagelocation+'.png'): | ||
| 761 | filename += '.png' | ||
| 762 | elif os.path.exists(imagelocation+'.jpg'): | ||
| 763 | filename += '.jpg' | ||
| 764 | elif os.path.exists(imagelocation+'.gif'): # MySQL uses GIF files | ||
| 765 | filename += '.gif' | ||
| 766 | else: | ||
| 767 | print "*** Cannot find image " + imagelocation | ||
| 768 | #TODO: what is 'ext'? | ||
| 769 | self.write('<IMG SRC="', filename, '"', \ | ||
| 770 | width and (' WIDTH="' + width + '"') or "", \ | ||
| 771 | height and (' HEIGHT="' + height + '"') or "", \ | ||
| 772 | alt and (' ALT="' + alt + '"') or "", \ | ||
| 773 | '/>' ) | ||
| 774 | self.htmlhelp.addimage(imagelocation) | ||
| 775 | |||
| 776 | |||
| 777 | # --- Marking Words and Phrases --- | ||
| 778 | |||
| 779 | # --- Other @xxx{...} commands --- | ||
| 780 | |||
| 781 | def open_(self): pass # Used by {text enclosed in braces} | ||
| 782 | def close_(self): pass | ||
| 783 | |||
| 784 | open_asis = open_ | ||
| 785 | close_asis = close_ | ||
| 786 | |||
| 787 | def open_cite(self): self.write('<CITE>') | ||
| 788 | def close_cite(self): self.write('</CITE>') | ||
| 789 | |||
| 790 | def open_code(self): self.write('<CODE>') | ||
| 791 | def close_code(self): self.write('</CODE>') | ||
| 792 | |||
| 793 | def open_t(self): self.write('<TT>') | ||
| 794 | def close_t(self): self.write('</TT>') | ||
| 795 | |||
| 796 | def open_dfn(self): self.write('<DFN>') | ||
| 797 | def close_dfn(self): self.write('</DFN>') | ||
| 798 | |||
| 799 | def open_emph(self): self.write('<EM>') | ||
| 800 | def close_emph(self): self.write('</EM>') | ||
| 801 | |||
| 802 | def open_i(self): self.write('<I>') | ||
| 803 | def close_i(self): self.write('</I>') | ||
| 804 | |||
| 805 | def open_footnote(self): | ||
| 806 | # if self.savetext <> None: | ||
| 807 | # print '*** Recursive footnote -- expect weirdness' | ||
| 808 | id = len(self.footnotes) + 1 | ||
| 809 | self.write(self.FN_SOURCE_PATTERN % {'id': repr(id)}) | ||
| 810 | self.startsaving() | ||
| 811 | |||
| 812 | def close_footnote(self): | ||
| 813 | id = len(self.footnotes) + 1 | ||
| 814 | self.footnotes.append((id, self.collectsavings())) | ||
| 815 | |||
| 816 | def writefootnotes(self): | ||
| 817 | self.write(self.FN_HEADER) | ||
| 818 | for id, text in self.footnotes: | ||
| 819 | self.write(self.FN_TARGET_PATTERN | ||
| 820 | % {'id': repr(id), 'text': text}) | ||
| 821 | self.footnotes = [] | ||
| 822 | |||
| 823 | def open_file(self): self.write('<CODE>') | ||
| 824 | def close_file(self): self.write('</CODE>') | ||
| 825 | |||
| 826 | def open_kbd(self): self.write('<KBD>') | ||
| 827 | def close_kbd(self): self.write('</KBD>') | ||
| 828 | |||
| 829 | def open_key(self): self.write('<KEY>') | ||
| 830 | def close_key(self): self.write('</KEY>') | ||
| 831 | |||
| 832 | def open_r(self): self.write('<R>') | ||
| 833 | def close_r(self): self.write('</R>') | ||
| 834 | |||
| 835 | def open_samp(self): self.write('`<SAMP>') | ||
| 836 | def close_samp(self): self.write('</SAMP>\'') | ||
| 837 | |||
| 838 | def open_sc(self): self.write('<SMALLCAPS>') | ||
| 839 | def close_sc(self): self.write('</SMALLCAPS>') | ||
| 840 | |||
| 841 | def open_strong(self): self.write('<STRONG>') | ||
| 842 | def close_strong(self): self.write('</STRONG>') | ||
| 843 | |||
| 844 | def open_b(self): self.write('<B>') | ||
| 845 | def close_b(self): self.write('</B>') | ||
| 846 | |||
| 847 | def open_var(self): self.write('<VAR>') | ||
| 848 | def close_var(self): self.write('</VAR>') | ||
| 849 | |||
| 850 | def open_w(self): self.write('<NOBREAK>') | ||
| 851 | def close_w(self): self.write('</NOBREAK>') | ||
| 852 | |||
| 853 | def open_url(self): self.startsaving() | ||
| 854 | def close_url(self): | ||
| 855 | text = self.collectsavings() | ||
| 856 | self.write('<A HREF="', text, '">', text, '</A>') | ||
| 857 | |||
| 858 | def open_email(self): self.startsaving() | ||
| 859 | def close_email(self): | ||
| 860 | text = self.collectsavings() | ||
| 861 | self.write('<A HREF="mailto:', text, '">', text, '</A>') | ||
| 862 | |||
| 863 | open_titlefont = open_ | ||
| 864 | close_titlefont = close_ | ||
| 865 | |||
| 866 | def open_small(self): pass | ||
| 867 | def close_small(self): pass | ||
| 868 | |||
| 869 | def command(self, line, mo): | ||
| 870 | a, b = mo.span(1) | ||
| 871 | cmd = line[a:b] | ||
| 872 | args = line[b:].strip() | ||
| 873 | if self.debugging > 1: | ||
| 874 | print '!'*self.debugging, 'command:', self.skip, self.stack, \ | ||
| 875 | '@' + cmd, args | ||
| 876 | try: | ||
| 877 | func = getattr(self, 'do_' + cmd) | ||
| 878 | except AttributeError: | ||
| 879 | try: | ||
| 880 | func = getattr(self, 'bgn_' + cmd) | ||
| 881 | except AttributeError: | ||
| 882 | # don't complain if we are skipping anyway | ||
| 883 | if not self.skip: | ||
| 884 | self.unknown_cmd(cmd, args) | ||
| 885 | return | ||
| 886 | self.stack.append(cmd) | ||
| 887 | func(args) | ||
| 888 | return | ||
| 889 | if not self.skip or cmd == 'end': | ||
| 890 | func(args) | ||
| 891 | |||
| 892 | def unknown_cmd(self, cmd, args): | ||
| 893 | print '*** unknown', '@' + cmd, args | ||
| 894 | if not self.unknown.has_key(cmd): | ||
| 895 | self.unknown[cmd] = 1 | ||
| 896 | else: | ||
| 897 | self.unknown[cmd] = self.unknown[cmd] + 1 | ||
| 898 | |||
| 899 | def do_end(self, args): | ||
| 900 | words = args.split() | ||
| 901 | if not words: | ||
| 902 | print '*** @end w/o args' | ||
| 903 | else: | ||
| 904 | cmd = words[0] | ||
| 905 | if not self.stack or self.stack[-1] <> cmd: | ||
| 906 | print '*** @end', cmd, 'unexpected' | ||
| 907 | else: | ||
| 908 | del self.stack[-1] | ||
| 909 | try: | ||
| 910 | func = getattr(self, 'end_' + cmd) | ||
| 911 | except AttributeError: | ||
| 912 | self.unknown_end(cmd) | ||
| 913 | return | ||
| 914 | func() | ||
| 915 | |||
| 916 | def unknown_end(self, cmd): | ||
| 917 | cmd = 'end ' + cmd | ||
| 918 | print '*** unknown', '@' + cmd | ||
| 919 | if not self.unknown.has_key(cmd): | ||
| 920 | self.unknown[cmd] = 1 | ||
| 921 | else: | ||
| 922 | self.unknown[cmd] = self.unknown[cmd] + 1 | ||
| 923 | |||
| 924 | # --- Comments --- | ||
| 925 | |||
| 926 | def do_comment(self, args): pass | ||
| 927 | do_c = do_comment | ||
| 928 | |||
| 929 | # --- Conditional processing --- | ||
| 930 | |||
| 931 | def bgn_ifinfo(self, args): pass | ||
| 932 | def end_ifinfo(self): pass | ||
| 933 | |||
| 934 | def bgn_iftex(self, args): self.skip = self.skip + 1 | ||
| 935 | def end_iftex(self): self.skip = self.skip - 1 | ||
| 936 | |||
| 937 | def bgn_ignore(self, args): self.skip = self.skip + 1 | ||
| 938 | def end_ignore(self): self.skip = self.skip - 1 | ||
| 939 | |||
| 940 | def bgn_tex(self, args): self.skip = self.skip + 1 | ||
| 941 | def end_tex(self): self.skip = self.skip - 1 | ||
| 942 | |||
| 943 | def do_set(self, args): | ||
| 944 | fields = args.split(' ') | ||
| 945 | key = fields[0] | ||
| 946 | if len(fields) == 1: | ||
| 947 | value = 1 | ||
| 948 | else: | ||
| 949 | value = ' '.join(fields[1:]) | ||
| 950 | self.values[key] = value | ||
| 951 | |||
| 952 | def do_clear(self, args): | ||
| 953 | self.values[args] = None | ||
| 954 | |||
| 955 | def bgn_ifset(self, args): | ||
| 956 | if args not in self.values.keys() \ | ||
| 957 | or self.values[args] is None: | ||
| 958 | self.skip = self.skip + 1 | ||
| 959 | self.stackinfo[len(self.stack)] = 1 | ||
| 960 | else: | ||
| 961 | self.stackinfo[len(self.stack)] = 0 | ||
| 962 | def end_ifset(self): | ||
| 963 | try: | ||
| 964 | if self.stackinfo[len(self.stack) + 1]: | ||
| 965 | self.skip = self.skip - 1 | ||
| 966 | del self.stackinfo[len(self.stack) + 1] | ||
| 967 | except KeyError: | ||
| 968 | print '*** end_ifset: KeyError :', len(self.stack) + 1 | ||
| 969 | |||
| 970 | def bgn_ifclear(self, args): | ||
| 971 | if args in self.values.keys() \ | ||
| 972 | and self.values[args] is not None: | ||
| 973 | self.skip = self.skip + 1 | ||
| 974 | self.stackinfo[len(self.stack)] = 1 | ||
| 975 | else: | ||
| 976 | self.stackinfo[len(self.stack)] = 0 | ||
| 977 | def end_ifclear(self): | ||
| 978 | try: | ||
| 979 | if self.stackinfo[len(self.stack) + 1]: | ||
| 980 | self.skip = self.skip - 1 | ||
| 981 | del self.stackinfo[len(self.stack) + 1] | ||
| 982 | except KeyError: | ||
| 983 | print '*** end_ifclear: KeyError :', len(self.stack) + 1 | ||
| 984 | |||
| 985 | def open_value(self): | ||
| 986 | self.startsaving() | ||
| 987 | |||
| 988 | def close_value(self): | ||
| 989 | key = self.collectsavings() | ||
| 990 | if key in self.values.keys(): | ||
| 991 | self.write(self.values[key]) | ||
| 992 | else: | ||
| 993 | print '*** Undefined value: ', key | ||
| 994 | |||
| 995 | # --- Beginning a file --- | ||
| 996 | |||
| 997 | do_finalout = do_comment | ||
| 998 | do_setchapternewpage = do_comment | ||
| 999 | do_setfilename = do_comment | ||
| 1000 | |||
| 1001 | def do_settitle(self, args): | ||
| 1002 | self.startsaving() | ||
| 1003 | self.expand(args) | ||
| 1004 | self.title = self.collectsavings() | ||
| 1005 | def do_parskip(self, args): pass | ||
| 1006 | |||
| 1007 | # --- Ending a file --- | ||
| 1008 | |||
| 1009 | def do_bye(self, args): | ||
| 1010 | self.endnode() | ||
| 1011 | self.done = 1 | ||
| 1012 | |||
| 1013 | # --- Title page --- | ||
| 1014 | |||
| 1015 | def bgn_titlepage(self, args): self.skip = self.skip + 1 | ||
| 1016 | def end_titlepage(self): self.skip = self.skip - 1 | ||
| 1017 | def do_shorttitlepage(self, args): pass | ||
| 1018 | |||
| 1019 | def do_center(self, args): | ||
| 1020 | # Actually not used outside title page... | ||
| 1021 | self.write('<H1>') | ||
| 1022 | self.expand(args) | ||
| 1023 | self.write('</H1>\n') | ||
| 1024 | do_title = do_center | ||
| 1025 | do_subtitle = do_center | ||
| 1026 | do_author = do_center | ||
| 1027 | |||
| 1028 | do_vskip = do_comment | ||
| 1029 | do_vfill = do_comment | ||
| 1030 | do_smallbook = do_comment | ||
| 1031 | |||
| 1032 | do_paragraphindent = do_comment | ||
| 1033 | do_setchapternewpage = do_comment | ||
| 1034 | do_headings = do_comment | ||
| 1035 | do_footnotestyle = do_comment | ||
| 1036 | |||
| 1037 | do_evenheading = do_comment | ||
| 1038 | do_evenfooting = do_comment | ||
| 1039 | do_oddheading = do_comment | ||
| 1040 | do_oddfooting = do_comment | ||
| 1041 | do_everyheading = do_comment | ||
| 1042 | do_everyfooting = do_comment | ||
| 1043 | |||
| 1044 | # --- Nodes --- | ||
| 1045 | |||
| 1046 | def do_node(self, args): | ||
| 1047 | self.endnode() | ||
| 1048 | self.nodelineno = 0 | ||
| 1049 | parts = [s.strip() for s in args.split(',')] | ||
| 1050 | while len(parts) < 4: parts.append('') | ||
| 1051 | self.nodelinks = parts | ||
| 1052 | [name, next, prev, up] = parts[:4] | ||
| 1053 | file = self.dirname + '/' + makefile(name) | ||
| 1054 | if self.filenames.has_key(file): | ||
| 1055 | print '*** Filename already in use: ', file | ||
| 1056 | else: | ||
| 1057 | if self.debugging: print '!'*self.debugging, '--- writing', file | ||
| 1058 | self.filenames[file] = 1 | ||
| 1059 | # self.nodefp = open(file, 'w') | ||
| 1060 | self.nodename = name | ||
| 1061 | if self.cont and self.nodestack: | ||
| 1062 | self.nodestack[-1].cont = self.nodename | ||
| 1063 | if not self.topname: self.topname = name | ||
| 1064 | title = name | ||
| 1065 | if self.title: title = title + ' -- ' + self.title | ||
| 1066 | self.node = self.Node(self.dirname, self.nodename, self.topname, | ||
| 1067 | title, next, prev, up) | ||
| 1068 | self.htmlhelp.addnode(self.nodename,next,prev,up,file) | ||
| 1069 | |||
| 1070 | def link(self, label, nodename): | ||
| 1071 | if nodename: | ||
| 1072 | if nodename.lower() == '(dir)': | ||
| 1073 | addr = '../dir.html' | ||
| 1074 | else: | ||
| 1075 | addr = makefile(nodename) | ||
| 1076 | self.write(label, ': <A HREF="', addr, '" TYPE="', | ||
| 1077 | label, '">', nodename, '</A> \n') | ||
| 1078 | |||
| 1079 | # --- Sectioning commands --- | ||
| 1080 | |||
| 1081 | def popstack(self, type): | ||
| 1082 | if (self.node): | ||
| 1083 | self.node.type = type | ||
| 1084 | while self.nodestack: | ||
| 1085 | if self.nodestack[-1].type > type: | ||
| 1086 | self.nodestack[-1].finalize() | ||
| 1087 | self.nodestack[-1].flush() | ||
| 1088 | del self.nodestack[-1] | ||
| 1089 | elif self.nodestack[-1].type == type: | ||
| 1090 | if not self.nodestack[-1].next: | ||
| 1091 | self.nodestack[-1].next = self.node.name | ||
| 1092 | if not self.node.prev: | ||
| 1093 | self.node.prev = self.nodestack[-1].name | ||
| 1094 | self.nodestack[-1].finalize() | ||
| 1095 | self.nodestack[-1].flush() | ||
| 1096 | del self.nodestack[-1] | ||
| 1097 | else: | ||
| 1098 | if type > 1 and not self.node.up: | ||
| 1099 | self.node.up = self.nodestack[-1].name | ||
| 1100 | break | ||
| 1101 | |||
| 1102 | def do_chapter(self, args): | ||
| 1103 | self.heading('H1', args, 0) | ||
| 1104 | self.popstack(1) | ||
| 1105 | |||
| 1106 | def do_unnumbered(self, args): | ||
| 1107 | self.heading('H1', args, -1) | ||
| 1108 | self.popstack(1) | ||
| 1109 | def do_appendix(self, args): | ||
| 1110 | self.heading('H1', args, -1) | ||
| 1111 | self.popstack(1) | ||
| 1112 | def do_top(self, args): | ||
| 1113 | self.heading('H1', args, -1) | ||
| 1114 | def do_chapheading(self, args): | ||
| 1115 | self.heading('H1', args, -1) | ||
| 1116 | def do_majorheading(self, args): | ||
| 1117 | self.heading('H1', args, -1) | ||
| 1118 | |||
| 1119 | def do_section(self, args): | ||
| 1120 | self.heading('H1', args, 1) | ||
| 1121 | self.popstack(2) | ||
| 1122 | |||
| 1123 | def do_unnumberedsec(self, args): | ||
| 1124 | self.heading('H1', args, -1) | ||
| 1125 | self.popstack(2) | ||
| 1126 | def do_appendixsec(self, args): | ||
| 1127 | self.heading('H1', args, -1) | ||
| 1128 | self.popstack(2) | ||
| 1129 | do_appendixsection = do_appendixsec | ||
| 1130 | def do_heading(self, args): | ||
| 1131 | self.heading('H1', args, -1) | ||
| 1132 | |||
| 1133 | def do_subsection(self, args): | ||
| 1134 | self.heading('H2', args, 2) | ||
| 1135 | self.popstack(3) | ||
| 1136 | def do_unnumberedsubsec(self, args): | ||
| 1137 | self.heading('H2', args, -1) | ||
| 1138 | self.popstack(3) | ||
| 1139 | def do_appendixsubsec(self, args): | ||
| 1140 | self.heading('H2', args, -1) | ||
| 1141 | self.popstack(3) | ||
| 1142 | def do_subheading(self, args): | ||
| 1143 | self.heading('H2', args, -1) | ||
| 1144 | |||
| 1145 | def do_subsubsection(self, args): | ||
| 1146 | self.heading('H3', args, 3) | ||
| 1147 | self.popstack(4) | ||
| 1148 | def do_unnumberedsubsubsec(self, args): | ||
| 1149 | self.heading('H3', args, -1) | ||
| 1150 | self.popstack(4) | ||
| 1151 | def do_appendixsubsubsec(self, args): | ||
| 1152 | self.heading('H3', args, -1) | ||
| 1153 | self.popstack(4) | ||
| 1154 | def do_subsubheading(self, args): | ||
| 1155 | self.heading('H3', args, -1) | ||
| 1156 | |||
| 1157 | def heading(self, type, args, level): | ||
| 1158 | if level >= 0: | ||
| 1159 | while len(self.numbering) <= level: | ||
| 1160 | self.numbering.append(0) | ||
| 1161 | del self.numbering[level+1:] | ||
| 1162 | self.numbering[level] = self.numbering[level] + 1 | ||
| 1163 | x = '' | ||
| 1164 | for i in self.numbering: | ||
| 1165 | x = x + repr(i) + '.' | ||
| 1166 | args = x + ' ' + args | ||
| 1167 | self.contents.append((level, args, self.nodename)) | ||
| 1168 | self.write('<', type, '>') | ||
| 1169 | self.expand(args) | ||
| 1170 | self.write('</', type, '>\n') | ||
| 1171 | if self.debugging or self.print_headers: | ||
| 1172 | print '---', args | ||
| 1173 | |||
| 1174 | def do_contents(self, args): | ||
| 1175 | # pass | ||
| 1176 | self.listcontents('Table of Contents', 999) | ||
| 1177 | |||
| 1178 | def do_shortcontents(self, args): | ||
| 1179 | pass | ||
| 1180 | # self.listcontents('Short Contents', 0) | ||
| 1181 | do_summarycontents = do_shortcontents | ||
| 1182 | |||
| 1183 | def listcontents(self, title, maxlevel): | ||
| 1184 | self.write('<H1>', title, '</H1>\n<UL COMPACT PLAIN>\n') | ||
| 1185 | prevlevels = [0] | ||
| 1186 | for level, title, node in self.contents: | ||
| 1187 | if level > maxlevel: | ||
| 1188 | continue | ||
| 1189 | if level > prevlevels[-1]: | ||
| 1190 | # can only advance one level at a time | ||
| 1191 | self.write(' '*prevlevels[-1], '<UL PLAIN>\n') | ||
| 1192 | prevlevels.append(level) | ||
| 1193 | elif level < prevlevels[-1]: | ||
| 1194 | # might drop back multiple levels | ||
| 1195 | while level < prevlevels[-1]: | ||
| 1196 | del prevlevels[-1] | ||
| 1197 | self.write(' '*prevlevels[-1], | ||
| 1198 | '</UL>\n') | ||
| 1199 | self.write(' '*level, '<LI> <A HREF="', | ||
| 1200 | makefile(node), '">') | ||
| 1201 | self.expand(title) | ||
| 1202 | self.write('</A>\n') | ||
| 1203 | self.write('</UL>\n' * len(prevlevels)) | ||
| 1204 | |||
| 1205 | # --- Page lay-out --- | ||
| 1206 | |||
| 1207 | # These commands are only meaningful in printed text | ||
| 1208 | |||
| 1209 | def do_page(self, args): pass | ||
| 1210 | |||
| 1211 | def do_need(self, args): pass | ||
| 1212 | |||
| 1213 | def bgn_group(self, args): pass | ||
| 1214 | def end_group(self): pass | ||
| 1215 | |||
| 1216 | # --- Line lay-out --- | ||
| 1217 | |||
| 1218 | def do_sp(self, args): | ||
| 1219 | if self.nofill: | ||
| 1220 | self.write('\n') | ||
| 1221 | else: | ||
| 1222 | self.write('<P>\n') | ||
| 1223 | |||
| 1224 | def do_hline(self, args): | ||
| 1225 | self.write('<HR>') | ||
| 1226 | |||
| 1227 | # --- Function and variable definitions --- | ||
| 1228 | |||
| 1229 | def bgn_deffn(self, args): | ||
| 1230 | self.write('<DL>') | ||
| 1231 | self.do_deffnx(args) | ||
| 1232 | |||
| 1233 | def end_deffn(self): | ||
| 1234 | self.write('</DL>\n') | ||
| 1235 | |||
| 1236 | def do_deffnx(self, args): | ||
| 1237 | self.write('<DT>') | ||
| 1238 | words = splitwords(args, 2) | ||
| 1239 | [category, name], rest = words[:2], words[2:] | ||
| 1240 | self.expand('@b{%s}' % name) | ||
| 1241 | for word in rest: self.expand(' ' + makevar(word)) | ||
| 1242 | #self.expand(' -- ' + category) | ||
| 1243 | self.write('\n<DD>') | ||
| 1244 | self.index('fn', name) | ||
| 1245 | |||
| 1246 | def bgn_defun(self, args): self.bgn_deffn('Function ' + args) | ||
| 1247 | end_defun = end_deffn | ||
| 1248 | def do_defunx(self, args): self.do_deffnx('Function ' + args) | ||
| 1249 | |||
| 1250 | def bgn_defmac(self, args): self.bgn_deffn('Macro ' + args) | ||
| 1251 | end_defmac = end_deffn | ||
| 1252 | def do_defmacx(self, args): self.do_deffnx('Macro ' + args) | ||
| 1253 | |||
| 1254 | def bgn_defspec(self, args): self.bgn_deffn('{Special Form} ' + args) | ||
| 1255 | end_defspec = end_deffn | ||
| 1256 | def do_defspecx(self, args): self.do_deffnx('{Special Form} ' + args) | ||
| 1257 | |||
| 1258 | def bgn_defvr(self, args): | ||
| 1259 | self.write('<DL>') | ||
| 1260 | self.do_defvrx(args) | ||
| 1261 | |||
| 1262 | end_defvr = end_deffn | ||
| 1263 | |||
| 1264 | def do_defvrx(self, args): | ||
| 1265 | self.write('<DT>') | ||
| 1266 | words = splitwords(args, 2) | ||
| 1267 | [category, name], rest = words[:2], words[2:] | ||
| 1268 | self.expand('@code{%s}' % name) | ||
| 1269 | # If there are too many arguments, show them | ||
| 1270 | for word in rest: self.expand(' ' + word) | ||
| 1271 | #self.expand(' -- ' + category) | ||
| 1272 | self.write('\n<DD>') | ||
| 1273 | self.index('vr', name) | ||
| 1274 | |||
| 1275 | def bgn_defvar(self, args): self.bgn_defvr('Variable ' + args) | ||
| 1276 | end_defvar = end_defvr | ||
| 1277 | def do_defvarx(self, args): self.do_defvrx('Variable ' + args) | ||
| 1278 | |||
| 1279 | def bgn_defopt(self, args): self.bgn_defvr('{User Option} ' + args) | ||
| 1280 | end_defopt = end_defvr | ||
| 1281 | def do_defoptx(self, args): self.do_defvrx('{User Option} ' + args) | ||
| 1282 | |||
| 1283 | # --- Ditto for typed languages --- | ||
| 1284 | |||
| 1285 | def bgn_deftypefn(self, args): | ||
| 1286 | self.write('<DL>') | ||
| 1287 | self.do_deftypefnx(args) | ||
| 1288 | |||
| 1289 | end_deftypefn = end_deffn | ||
| 1290 | |||
| 1291 | def do_deftypefnx(self, args): | ||
| 1292 | self.write('<DT>') | ||
| 1293 | words = splitwords(args, 3) | ||
| 1294 | [category, datatype, name], rest = words[:3], words[3:] | ||
| 1295 | self.expand('@code{%s} @b{%s}' % (datatype, name)) | ||
| 1296 | for word in rest: self.expand(' ' + makevar(word)) | ||
| 1297 | #self.expand(' -- ' + category) | ||
| 1298 | self.write('\n<DD>') | ||
| 1299 | self.index('fn', name) | ||
| 1300 | |||
| 1301 | |||
| 1302 | def bgn_deftypefun(self, args): self.bgn_deftypefn('Function ' + args) | ||
| 1303 | end_deftypefun = end_deftypefn | ||
| 1304 | def do_deftypefunx(self, args): self.do_deftypefnx('Function ' + args) | ||
| 1305 | |||
| 1306 | def bgn_deftypevr(self, args): | ||
| 1307 | self.write('<DL>') | ||
| 1308 | self.do_deftypevrx(args) | ||
| 1309 | |||
| 1310 | end_deftypevr = end_deftypefn | ||
| 1311 | |||
| 1312 | def do_deftypevrx(self, args): | ||
| 1313 | self.write('<DT>') | ||
| 1314 | words = splitwords(args, 3) | ||
| 1315 | [category, datatype, name], rest = words[:3], words[3:] | ||
| 1316 | self.expand('@code{%s} @b{%s}' % (datatype, name)) | ||
| 1317 | # If there are too many arguments, show them | ||
| 1318 | for word in rest: self.expand(' ' + word) | ||
| 1319 | #self.expand(' -- ' + category) | ||
| 1320 | self.write('\n<DD>') | ||
| 1321 | self.index('fn', name) | ||
| 1322 | |||
| 1323 | def bgn_deftypevar(self, args): | ||
| 1324 | self.bgn_deftypevr('Variable ' + args) | ||
| 1325 | end_deftypevar = end_deftypevr | ||
| 1326 | def do_deftypevarx(self, args): | ||
| 1327 | self.do_deftypevrx('Variable ' + args) | ||
| 1328 | |||
| 1329 | # --- Ditto for object-oriented languages --- | ||
| 1330 | |||
| 1331 | def bgn_defcv(self, args): | ||
| 1332 | self.write('<DL>') | ||
| 1333 | self.do_defcvx(args) | ||
| 1334 | |||
| 1335 | end_defcv = end_deftypevr | ||
| 1336 | |||
| 1337 | def do_defcvx(self, args): | ||
| 1338 | self.write('<DT>') | ||
| 1339 | words = splitwords(args, 3) | ||
| 1340 | [category, classname, name], rest = words[:3], words[3:] | ||
| 1341 | self.expand('@b{%s}' % name) | ||
| 1342 | # If there are too many arguments, show them | ||
| 1343 | for word in rest: self.expand(' ' + word) | ||
| 1344 | #self.expand(' -- %s of @code{%s}' % (category, classname)) | ||
| 1345 | self.write('\n<DD>') | ||
| 1346 | self.index('vr', '%s @r{on %s}' % (name, classname)) | ||
| 1347 | |||
| 1348 | def bgn_defivar(self, args): | ||
| 1349 | self.bgn_defcv('{Instance Variable} ' + args) | ||
| 1350 | end_defivar = end_defcv | ||
| 1351 | def do_defivarx(self, args): | ||
| 1352 | self.do_defcvx('{Instance Variable} ' + args) | ||
| 1353 | |||
| 1354 | def bgn_defop(self, args): | ||
| 1355 | self.write('<DL>') | ||
| 1356 | self.do_defopx(args) | ||
| 1357 | |||
| 1358 | end_defop = end_defcv | ||
| 1359 | |||
| 1360 | def do_defopx(self, args): | ||
| 1361 | self.write('<DT>') | ||
| 1362 | words = splitwords(args, 3) | ||
| 1363 | [category, classname, name], rest = words[:3], words[3:] | ||
| 1364 | self.expand('@b{%s}' % name) | ||
| 1365 | for word in rest: self.expand(' ' + makevar(word)) | ||
| 1366 | #self.expand(' -- %s of @code{%s}' % (category, classname)) | ||
| 1367 | self.write('\n<DD>') | ||
| 1368 | self.index('fn', '%s @r{on %s}' % (name, classname)) | ||
| 1369 | |||
| 1370 | def bgn_defmethod(self, args): | ||
| 1371 | self.bgn_defop('Method ' + args) | ||
| 1372 | end_defmethod = end_defop | ||
| 1373 | def do_defmethodx(self, args): | ||
| 1374 | self.do_defopx('Method ' + args) | ||
| 1375 | |||
| 1376 | # --- Ditto for data types --- | ||
| 1377 | |||
| 1378 | def bgn_deftp(self, args): | ||
| 1379 | self.write('<DL>') | ||
| 1380 | self.do_deftpx(args) | ||
| 1381 | |||
| 1382 | end_deftp = end_defcv | ||
| 1383 | |||
| 1384 | def do_deftpx(self, args): | ||
| 1385 | self.write('<DT>') | ||
| 1386 | words = splitwords(args, 2) | ||
| 1387 | [category, name], rest = words[:2], words[2:] | ||
| 1388 | self.expand('@b{%s}' % name) | ||
| 1389 | for word in rest: self.expand(' ' + word) | ||
| 1390 | #self.expand(' -- ' + category) | ||
| 1391 | self.write('\n<DD>') | ||
| 1392 | self.index('tp', name) | ||
| 1393 | |||
| 1394 | # --- Making Lists and Tables | ||
| 1395 | |||
| 1396 | def bgn_enumerate(self, args): | ||
| 1397 | if not args: | ||
| 1398 | self.write('<OL>\n') | ||
| 1399 | self.stackinfo[len(self.stack)] = '</OL>\n' | ||
| 1400 | else: | ||
| 1401 | self.itemnumber = args | ||
| 1402 | self.write('<UL>\n') | ||
| 1403 | self.stackinfo[len(self.stack)] = '</UL>\n' | ||
| 1404 | def end_enumerate(self): | ||
| 1405 | self.itemnumber = None | ||
| 1406 | self.write(self.stackinfo[len(self.stack) + 1]) | ||
| 1407 | del self.stackinfo[len(self.stack) + 1] | ||
| 1408 | |||
| 1409 | def bgn_itemize(self, args): | ||
| 1410 | self.itemarg = args | ||
| 1411 | self.write('<UL>\n') | ||
| 1412 | def end_itemize(self): | ||
| 1413 | self.itemarg = None | ||
| 1414 | self.write('</UL>\n') | ||
| 1415 | |||
| 1416 | def bgn_table(self, args): | ||
| 1417 | self.itemarg = args | ||
| 1418 | self.write('<DL>\n') | ||
| 1419 | def end_table(self): | ||
| 1420 | self.itemarg = None | ||
| 1421 | self.write('</DL>\n') | ||
| 1422 | |||
| 1423 | def bgn_ftable(self, args): | ||
| 1424 | self.itemindex = 'fn' | ||
| 1425 | self.bgn_table(args) | ||
| 1426 | def end_ftable(self): | ||
| 1427 | self.itemindex = None | ||
| 1428 | self.end_table() | ||
| 1429 | |||
| 1430 | def bgn_vtable(self, args): | ||
| 1431 | self.itemindex = 'vr' | ||
| 1432 | self.bgn_table(args) | ||
| 1433 | def end_vtable(self): | ||
| 1434 | self.itemindex = None | ||
| 1435 | self.end_table() | ||
| 1436 | |||
| 1437 | def do_item(self, args): | ||
| 1438 | if self.itemindex: self.index(self.itemindex, args) | ||
| 1439 | if self.itemarg: | ||
| 1440 | if self.itemarg[0] == '@' and self.itemarg[1] and \ | ||
| 1441 | self.itemarg[1] in string.ascii_letters: | ||
| 1442 | args = self.itemarg + '{' + args + '}' | ||
| 1443 | else: | ||
| 1444 | # some other character, e.g. '-' | ||
| 1445 | args = self.itemarg + ' ' + args | ||
| 1446 | if self.itemnumber <> None: | ||
| 1447 | args = self.itemnumber + '. ' + args | ||
| 1448 | self.itemnumber = increment(self.itemnumber) | ||
| 1449 | if self.stack and self.stack[-1] == 'table': | ||
| 1450 | self.write('<DT>') | ||
| 1451 | self.expand(args) | ||
| 1452 | self.write('\n<DD>') | ||
| 1453 | elif self.stack and self.stack[-1] == 'multitable': | ||
| 1454 | self.write('<TR><TD>') | ||
| 1455 | self.expand(args) | ||
| 1456 | self.write('</TD>\n</TR>\n') | ||
| 1457 | else: | ||
| 1458 | self.write('<LI>') | ||
| 1459 | self.expand(args) | ||
| 1460 | self.write(' ') | ||
| 1461 | do_itemx = do_item # XXX Should suppress leading blank line | ||
| 1462 | |||
| 1463 | # rpyron 2002-05-07 multitable support | ||
| 1464 | def bgn_multitable(self, args): | ||
| 1465 | self.itemarg = None # should be handled by columnfractions | ||
| 1466 | self.write('<TABLE BORDER="">\n') | ||
| 1467 | def end_multitable(self): | ||
| 1468 | self.itemarg = None | ||
| 1469 | self.write('</TABLE>\n<BR>\n') | ||
| 1470 | def handle_columnfractions(self): | ||
| 1471 | # It would be better to handle this, but for now it's in the way... | ||
| 1472 | self.itemarg = None | ||
| 1473 | def handle_tab(self): | ||
| 1474 | self.write('</TD>\n <TD>') | ||
| 1475 | |||
| 1476 | # --- Enumerations, displays, quotations --- | ||
| 1477 | # XXX Most of these should increase the indentation somehow | ||
| 1478 | |||
| 1479 | def bgn_quotation(self, args): self.write('<BLOCKQUOTE>') | ||
| 1480 | def end_quotation(self): self.write('</BLOCKQUOTE>\n') | ||
| 1481 | |||
| 1482 | def bgn_example(self, args): | ||
| 1483 | self.nofill = self.nofill + 1 | ||
| 1484 | self.write('<PRE>') | ||
| 1485 | def end_example(self): | ||
| 1486 | self.write('</PRE>\n') | ||
| 1487 | self.nofill = self.nofill - 1 | ||
| 1488 | |||
| 1489 | bgn_lisp = bgn_example # Synonym when contents are executable lisp code | ||
| 1490 | end_lisp = end_example | ||
| 1491 | |||
| 1492 | bgn_smallexample = bgn_example # XXX Should use smaller font | ||
| 1493 | end_smallexample = end_example | ||
| 1494 | |||
| 1495 | bgn_smalllisp = bgn_lisp # Ditto | ||
| 1496 | end_smalllisp = end_lisp | ||
| 1497 | |||
| 1498 | bgn_display = bgn_example | ||
| 1499 | end_display = end_example | ||
| 1500 | |||
| 1501 | bgn_format = bgn_display | ||
| 1502 | end_format = end_display | ||
| 1503 | |||
| 1504 | def do_exdent(self, args): self.expand(args + '\n') | ||
| 1505 | # XXX Should really mess with indentation | ||
| 1506 | |||
| 1507 | def bgn_flushleft(self, args): | ||
| 1508 | self.nofill = self.nofill + 1 | ||
| 1509 | self.write('<PRE>\n') | ||
| 1510 | def end_flushleft(self): | ||
| 1511 | self.write('</PRE>\n') | ||
| 1512 | self.nofill = self.nofill - 1 | ||
| 1513 | |||
| 1514 | def bgn_flushright(self, args): | ||
| 1515 | self.nofill = self.nofill + 1 | ||
| 1516 | self.write('<ADDRESS COMPACT>\n') | ||
| 1517 | def end_flushright(self): | ||
| 1518 | self.write('</ADDRESS>\n') | ||
| 1519 | self.nofill = self.nofill - 1 | ||
| 1520 | |||
| 1521 | def bgn_menu(self, args): | ||
| 1522 | self.write('<DIR>\n') | ||
| 1523 | self.write(' <STRONG><EM>Menu</EM></STRONG><P>\n') | ||
| 1524 | self.htmlhelp.beginmenu() | ||
| 1525 | def end_menu(self): | ||
| 1526 | self.write('</DIR>\n') | ||
| 1527 | self.htmlhelp.endmenu() | ||
| 1528 | |||
| 1529 | def bgn_cartouche(self, args): pass | ||
| 1530 | def end_cartouche(self): pass | ||
| 1531 | |||
| 1532 | # --- Indices --- | ||
| 1533 | |||
| 1534 | def resetindex(self): | ||
| 1535 | self.noncodeindices = ['cp'] | ||
| 1536 | self.indextitle = {} | ||
| 1537 | self.indextitle['cp'] = 'Concept' | ||
| 1538 | self.indextitle['fn'] = 'Function' | ||
| 1539 | self.indextitle['ky'] = 'Keyword' | ||
| 1540 | self.indextitle['pg'] = 'Program' | ||
| 1541 | self.indextitle['tp'] = 'Type' | ||
| 1542 | self.indextitle['vr'] = 'Variable' | ||
| 1543 | # | ||
| 1544 | self.whichindex = {} | ||
| 1545 | for name in self.indextitle.keys(): | ||
| 1546 | self.whichindex[name] = [] | ||
| 1547 | |||
| 1548 | def user_index(self, name, args): | ||
| 1549 | if self.whichindex.has_key(name): | ||
| 1550 | self.index(name, args) | ||
| 1551 | else: | ||
| 1552 | print '*** No index named', repr(name) | ||
| 1553 | |||
| 1554 | def do_cindex(self, args): self.index('cp', args) | ||
| 1555 | def do_findex(self, args): self.index('fn', args) | ||
| 1556 | def do_kindex(self, args): self.index('ky', args) | ||
| 1557 | def do_pindex(self, args): self.index('pg', args) | ||
| 1558 | def do_tindex(self, args): self.index('tp', args) | ||
| 1559 | def do_vindex(self, args): self.index('vr', args) | ||
| 1560 | |||
| 1561 | def index(self, name, args): | ||
| 1562 | self.whichindex[name].append((args, self.nodename)) | ||
| 1563 | self.htmlhelp.index(args, self.nodename) | ||
| 1564 | |||
| 1565 | def do_synindex(self, args): | ||
| 1566 | words = args.split() | ||
| 1567 | if len(words) <> 2: | ||
| 1568 | print '*** bad @synindex', args | ||
| 1569 | return | ||
| 1570 | [old, new] = words | ||
| 1571 | if not self.whichindex.has_key(old) or \ | ||
| 1572 | not self.whichindex.has_key(new): | ||
| 1573 | print '*** bad key(s) in @synindex', args | ||
| 1574 | return | ||
| 1575 | if old <> new and \ | ||
| 1576 | self.whichindex[old] is not self.whichindex[new]: | ||
| 1577 | inew = self.whichindex[new] | ||
| 1578 | inew[len(inew):] = self.whichindex[old] | ||
| 1579 | self.whichindex[old] = inew | ||
| 1580 | do_syncodeindex = do_synindex # XXX Should use code font | ||
| 1581 | |||
| 1582 | def do_printindex(self, args): | ||
| 1583 | words = args.split() | ||
| 1584 | for name in words: | ||
| 1585 | if self.whichindex.has_key(name): | ||
| 1586 | self.prindex(name) | ||
| 1587 | else: | ||
| 1588 | print '*** No index named', repr(name) | ||
| 1589 | |||
| 1590 | def prindex(self, name): | ||
| 1591 | iscodeindex = (name not in self.noncodeindices) | ||
| 1592 | index = self.whichindex[name] | ||
| 1593 | if not index: return | ||
| 1594 | if self.debugging: | ||
| 1595 | print '!'*self.debugging, '--- Generating', \ | ||
| 1596 | self.indextitle[name], 'index' | ||
| 1597 | # The node already provides a title | ||
| 1598 | index1 = [] | ||
| 1599 | junkprog = re.compile('^(@[a-z]+)?{') | ||
| 1600 | for key, node in index: | ||
| 1601 | sortkey = key.lower() | ||
| 1602 | # Remove leading `@cmd{' from sort key | ||
| 1603 | # -- don't bother about the matching `}' | ||
| 1604 | oldsortkey = sortkey | ||
| 1605 | while 1: | ||
| 1606 | mo = junkprog.match(sortkey) | ||
| 1607 | if not mo: | ||
| 1608 | break | ||
| 1609 | i = mo.end() | ||
| 1610 | sortkey = sortkey[i:] | ||
| 1611 | index1.append((sortkey, key, node)) | ||
| 1612 | del index[:] | ||
| 1613 | index1.sort() | ||
| 1614 | self.write('<DL COMPACT>\n') | ||
| 1615 | prevkey = prevnode = None | ||
| 1616 | for sortkey, key, node in index1: | ||
| 1617 | if (key, node) == (prevkey, prevnode): | ||
| 1618 | continue | ||
| 1619 | if self.debugging > 1: print '!'*self.debugging, key, ':', node | ||
| 1620 | self.write('<DT>') | ||
| 1621 | if iscodeindex: key = '@code{' + key + '}' | ||
| 1622 | if key != prevkey: | ||
| 1623 | self.expand(key) | ||
| 1624 | self.write('\n<DD><A HREF="%s">%s</A>\n' % (makefile(node), node)) | ||
| 1625 | prevkey, prevnode = key, node | ||
| 1626 | self.write('</DL>\n') | ||
| 1627 | |||
| 1628 | # --- Final error reports --- | ||
| 1629 | |||
| 1630 | def report(self): | ||
| 1631 | if self.unknown: | ||
| 1632 | print '--- Unrecognized commands ---' | ||
| 1633 | cmds = self.unknown.keys() | ||
| 1634 | cmds.sort() | ||
| 1635 | for cmd in cmds: | ||
| 1636 | print cmd.ljust(20), self.unknown[cmd] | ||
| 1637 | |||
| 1638 | |||
| 1639 | class TexinfoParserHTML3(TexinfoParser): | ||
| 1640 | |||
| 1641 | COPYRIGHT_SYMBOL = "©" | ||
| 1642 | FN_ID_PATTERN = "[%(id)s]" | ||
| 1643 | FN_SOURCE_PATTERN = '<A ID=footnoteref%(id)s ' \ | ||
| 1644 | 'HREF="#footnotetext%(id)s">' + FN_ID_PATTERN + '</A>' | ||
| 1645 | FN_TARGET_PATTERN = '<FN ID=footnotetext%(id)s>\n' \ | ||
| 1646 | '<P><A HREF="#footnoteref%(id)s">' + FN_ID_PATTERN \ | ||
| 1647 | + '</A>\n%(text)s</P></FN>\n' | ||
| 1648 | FN_HEADER = '<DIV CLASS=footnotes>\n <HR NOSHADE WIDTH=200>\n' \ | ||
| 1649 | ' <STRONG><EM>Footnotes</EM></STRONG>\n <P>\n' | ||
| 1650 | |||
| 1651 | Node = HTML3Node | ||
| 1652 | |||
| 1653 | def bgn_quotation(self, args): self.write('<BQ>') | ||
| 1654 | def end_quotation(self): self.write('</BQ>\n') | ||
| 1655 | |||
| 1656 | def bgn_example(self, args): | ||
| 1657 | # this use of <CODE> would not be legal in HTML 2.0, | ||
| 1658 | # but is in more recent DTDs. | ||
| 1659 | self.nofill = self.nofill + 1 | ||
| 1660 | self.write('<PRE CLASS=example><CODE>') | ||
| 1661 | def end_example(self): | ||
| 1662 | self.write("</CODE></PRE>\n") | ||
| 1663 | self.nofill = self.nofill - 1 | ||
| 1664 | |||
| 1665 | def bgn_flushleft(self, args): | ||
| 1666 | self.nofill = self.nofill + 1 | ||
| 1667 | self.write('<PRE CLASS=flushleft>\n') | ||
| 1668 | |||
| 1669 | def bgn_flushright(self, args): | ||
| 1670 | self.nofill = self.nofill + 1 | ||
| 1671 | self.write('<DIV ALIGN=right CLASS=flushright><ADDRESS COMPACT>\n') | ||
| 1672 | def end_flushright(self): | ||
| 1673 | self.write('</ADDRESS></DIV>\n') | ||
| 1674 | self.nofill = self.nofill - 1 | ||
| 1675 | |||
| 1676 | def bgn_menu(self, args): | ||
| 1677 | self.write('<UL PLAIN CLASS=menu>\n') | ||
| 1678 | self.write(' <LH>Menu</LH>\n') | ||
| 1679 | def end_menu(self): | ||
| 1680 | self.write('</UL>\n') | ||
| 1681 | |||
| 1682 | |||
| 1683 | # rpyron 2002-05-07 | ||
| 1684 | class HTMLHelp: | ||
| 1685 | """ | ||
| 1686 | This class encapsulates support for HTML Help. Node names, | ||
| 1687 | file names, menu items, index items, and image file names are | ||
| 1688 | accumulated until a call to finalize(). At that time, three | ||
| 1689 | output files are created in the current directory: | ||
| 1690 | |||
| 1691 | `helpbase`.hhp is a HTML Help Workshop project file. | ||
| 1692 | It contains various information, some of | ||
| 1693 | which I do not understand; I just copied | ||
| 1694 | the default project info from a fresh | ||
| 1695 | installation. | ||
| 1696 | `helpbase`.hhc is the Contents file for the project. | ||
| 1697 | `helpbase`.hhk is the Index file for the project. | ||
| 1698 | |||
| 1699 | When these files are used as input to HTML Help Workshop, | ||
| 1700 | the resulting file will be named: | ||
| 1701 | |||
| 1702 | `helpbase`.chm | ||
| 1703 | |||
| 1704 | If none of the defaults in `helpbase`.hhp are changed, | ||
| 1705 | the .CHM file will have Contents, Index, Search, and | ||
| 1706 | Favorites tabs. | ||
| 1707 | """ | ||
| 1708 | |||
| 1709 | codeprog = re.compile('@code{(.*?)}') | ||
| 1710 | |||
| 1711 | def __init__(self,helpbase,dirname): | ||
| 1712 | self.helpbase = helpbase | ||
| 1713 | self.dirname = dirname | ||
| 1714 | self.projectfile = None | ||
| 1715 | self.contentfile = None | ||
| 1716 | self.indexfile = None | ||
| 1717 | self.nodelist = [] | ||
| 1718 | self.nodenames = {} # nodename : index | ||
| 1719 | self.nodeindex = {} | ||
| 1720 | self.filenames = {} # filename : filename | ||
| 1721 | self.indexlist = [] # (args,nodename) == (key,location) | ||
| 1722 | self.current = '' | ||
| 1723 | self.menudict = {} | ||
| 1724 | self.dumped = {} | ||
| 1725 | |||
| 1726 | |||
| 1727 | def addnode(self,name,next,prev,up,filename): | ||
| 1728 | node = (name,next,prev,up,filename) | ||
| 1729 | # add this file to dict | ||
| 1730 | # retrieve list with self.filenames.values() | ||
| 1731 | self.filenames[filename] = filename | ||
| 1732 | # add this node to nodelist | ||
| 1733 | self.nodeindex[name] = len(self.nodelist) | ||
| 1734 | self.nodelist.append(node) | ||
| 1735 | # set 'current' for menu items | ||
| 1736 | self.current = name | ||
| 1737 | self.menudict[self.current] = [] | ||
| 1738 | |||
| 1739 | def menuitem(self,nodename): | ||
| 1740 | menu = self.menudict[self.current] | ||
| 1741 | menu.append(nodename) | ||
| 1742 | |||
| 1743 | |||
| 1744 | def addimage(self,imagename): | ||
| 1745 | self.filenames[imagename] = imagename | ||
| 1746 | |||
| 1747 | def index(self, args, nodename): | ||
| 1748 | self.indexlist.append((args,nodename)) | ||
| 1749 | |||
| 1750 | def beginmenu(self): | ||
| 1751 | pass | ||
| 1752 | |||
| 1753 | def endmenu(self): | ||
| 1754 | pass | ||
| 1755 | |||
| 1756 | def finalize(self): | ||
| 1757 | if not self.helpbase: | ||
| 1758 | return | ||
| 1759 | |||
| 1760 | # generate interesting filenames | ||
| 1761 | resultfile = self.helpbase + '.chm' | ||
| 1762 | projectfile = self.helpbase + '.hhp' | ||
| 1763 | contentfile = self.helpbase + '.hhc' | ||
| 1764 | indexfile = self.helpbase + '.hhk' | ||
| 1765 | |||
| 1766 | # generate a reasonable title | ||
| 1767 | title = self.helpbase | ||
| 1768 | |||
| 1769 | # get the default topic file | ||
| 1770 | (topname,topnext,topprev,topup,topfile) = self.nodelist[0] | ||
| 1771 | defaulttopic = topfile | ||
| 1772 | |||
| 1773 | # PROJECT FILE | ||
| 1774 | try: | ||
| 1775 | fp = open(projectfile,'w') | ||
| 1776 | print>>fp, '[OPTIONS]' | ||
| 1777 | print>>fp, 'Auto Index=Yes' | ||
| 1778 | print>>fp, 'Binary TOC=No' | ||
| 1779 | print>>fp, 'Binary Index=Yes' | ||
| 1780 | print>>fp, 'Compatibility=1.1' | ||
| 1781 | print>>fp, 'Compiled file=' + resultfile + '' | ||
| 1782 | print>>fp, 'Contents file=' + contentfile + '' | ||
| 1783 | print>>fp, 'Default topic=' + defaulttopic + '' | ||
| 1784 | print>>fp, 'Error log file=ErrorLog.log' | ||
| 1785 | print>>fp, 'Index file=' + indexfile + '' | ||
| 1786 | print>>fp, 'Title=' + title + '' | ||
| 1787 | print>>fp, 'Display compile progress=Yes' | ||
| 1788 | print>>fp, 'Full-text search=Yes' | ||
| 1789 | print>>fp, 'Default window=main' | ||
| 1790 | print>>fp, '' | ||
| 1791 | print>>fp, '[WINDOWS]' | ||
| 1792 | print>>fp, ('main=,"' + contentfile + '","' + indexfile | ||
| 1793 | + '","","",,,,,0x23520,222,0x1046,[10,10,780,560],' | ||
| 1794 | '0xB0000,,,,,,0') | ||
| 1795 | print>>fp, '' | ||
| 1796 | print>>fp, '[FILES]' | ||
| 1797 | print>>fp, '' | ||
| 1798 | self.dumpfiles(fp) | ||
| 1799 | fp.close() | ||
| 1800 | except IOError, msg: | ||
| 1801 | print projectfile, ':', msg | ||
| 1802 | sys.exit(1) | ||
| 1803 | |||
| 1804 | # CONTENT FILE | ||
| 1805 | try: | ||
| 1806 | fp = open(contentfile,'w') | ||
| 1807 | print>>fp, '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">' | ||
| 1808 | print>>fp, '<!-- This file defines the table of contents -->' | ||
| 1809 | print>>fp, '<HTML>' | ||
| 1810 | print>>fp, '<HEAD>' | ||
| 1811 | print>>fp, ('<meta name="GENERATOR"' | ||
| 1812 | 'content="Microsoft® HTML Help Workshop 4.1">') | ||
| 1813 | print>>fp, '<!-- Sitemap 1.0 -->' | ||
| 1814 | print>>fp, '</HEAD>' | ||
| 1815 | print>>fp, '<BODY>' | ||
| 1816 | print>>fp, ' <OBJECT type="text/site properties">' | ||
| 1817 | print>>fp, ' <param name="Window Styles" value="0x800025">' | ||
| 1818 | print>>fp, ' <param name="comment" value="title:">' | ||
| 1819 | print>>fp, ' <param name="comment" value="base:">' | ||
| 1820 | print>>fp, ' </OBJECT>' | ||
| 1821 | self.dumpnodes(fp) | ||
| 1822 | print>>fp, '</BODY>' | ||
| 1823 | print>>fp, '</HTML>' | ||
| 1824 | fp.close() | ||
| 1825 | except IOError, msg: | ||
| 1826 | print contentfile, ':', msg | ||
| 1827 | sys.exit(1) | ||
| 1828 | |||
| 1829 | # INDEX FILE | ||
| 1830 | try: | ||
| 1831 | fp = open(indexfile ,'w') | ||
| 1832 | print>>fp, '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">' | ||
| 1833 | print>>fp, '<!-- This file defines the index -->' | ||
| 1834 | print>>fp, '<HTML>' | ||
| 1835 | print>>fp, '<HEAD>' | ||
| 1836 | print>>fp, ('<meta name="GENERATOR"' | ||
| 1837 | 'content="Microsoft® HTML Help Workshop 4.1">') | ||
| 1838 | print>>fp, '<!-- Sitemap 1.0 -->' | ||
| 1839 | print>>fp, '</HEAD>' | ||
| 1840 | print>>fp, '<BODY>' | ||
| 1841 | print>>fp, '<OBJECT type="text/site properties">' | ||
| 1842 | print>>fp, '</OBJECT>' | ||
| 1843 | self.dumpindex(fp) | ||
| 1844 | print>>fp, '</BODY>' | ||
| 1845 | print>>fp, '</HTML>' | ||
| 1846 | fp.close() | ||
| 1847 | except IOError, msg: | ||
| 1848 | print indexfile , ':', msg | ||
| 1849 | sys.exit(1) | ||
| 1850 | |||
| 1851 | def dumpfiles(self, outfile=sys.stdout): | ||
| 1852 | filelist = self.filenames.values() | ||
| 1853 | filelist.sort() | ||
| 1854 | for filename in filelist: | ||
| 1855 | print>>outfile, filename | ||
| 1856 | |||
| 1857 | def dumpnodes(self, outfile=sys.stdout): | ||
| 1858 | self.dumped = {} | ||
| 1859 | if self.nodelist: | ||
| 1860 | nodename, dummy, dummy, dummy, dummy = self.nodelist[0] | ||
| 1861 | self.topnode = nodename | ||
| 1862 | |||
| 1863 | print>>outfile, '<UL>' | ||
| 1864 | for node in self.nodelist: | ||
| 1865 | self.dumpnode(node,0,outfile) | ||
| 1866 | print>>outfile, '</UL>' | ||
| 1867 | |||
| 1868 | def dumpnode(self, node, indent=0, outfile=sys.stdout): | ||
| 1869 | if node: | ||
| 1870 | # Retrieve info for this node | ||
| 1871 | (nodename,next,prev,up,filename) = node | ||
| 1872 | self.current = nodename | ||
| 1873 | |||
| 1874 | # Have we been dumped already? | ||
| 1875 | if self.dumped.has_key(nodename): | ||
| 1876 | return | ||
| 1877 | self.dumped[nodename] = 1 | ||
| 1878 | |||
| 1879 | # Print info for this node | ||
| 1880 | print>>outfile, ' '*indent, | ||
| 1881 | print>>outfile, '<LI><OBJECT type="text/sitemap">', | ||
| 1882 | print>>outfile, '<param name="Name" value="' + nodename +'">', | ||
| 1883 | print>>outfile, '<param name="Local" value="'+ filename +'">', | ||
| 1884 | print>>outfile, '</OBJECT>' | ||
| 1885 | |||
| 1886 | # Does this node have menu items? | ||
| 1887 | try: | ||
| 1888 | menu = self.menudict[nodename] | ||
| 1889 | self.dumpmenu(menu,indent+2,outfile) | ||
| 1890 | except KeyError: | ||
| 1891 | pass | ||
| 1892 | |||
| 1893 | def dumpmenu(self, menu, indent=0, outfile=sys.stdout): | ||
| 1894 | if menu: | ||
| 1895 | currentnode = self.current | ||
| 1896 | if currentnode != self.topnode: # XXX this is a hack | ||
| 1897 | print>>outfile, ' '*indent + '<UL>' | ||
| 1898 | indent += 2 | ||
| 1899 | for item in menu: | ||
| 1900 | menunode = self.getnode(item) | ||
| 1901 | self.dumpnode(menunode,indent,outfile) | ||
| 1902 | if currentnode != self.topnode: # XXX this is a hack | ||
| 1903 | print>>outfile, ' '*indent + '</UL>' | ||
| 1904 | indent -= 2 | ||
| 1905 | |||
| 1906 | def getnode(self, nodename): | ||
| 1907 | try: | ||
| 1908 | index = self.nodeindex[nodename] | ||
| 1909 | return self.nodelist[index] | ||
| 1910 | except KeyError: | ||
| 1911 | return None | ||
| 1912 | except IndexError: | ||
| 1913 | return None | ||
| 1914 | |||
| 1915 | # (args,nodename) == (key,location) | ||
| 1916 | def dumpindex(self, outfile=sys.stdout): | ||
| 1917 | print>>outfile, '<UL>' | ||
| 1918 | for (key,location) in self.indexlist: | ||
| 1919 | key = self.codeexpand(key) | ||
| 1920 | location = makefile(location) | ||
| 1921 | location = self.dirname + '/' + location | ||
| 1922 | print>>outfile, '<LI><OBJECT type="text/sitemap">', | ||
| 1923 | print>>outfile, '<param name="Name" value="' + key + '">', | ||
| 1924 | print>>outfile, '<param name="Local" value="' + location + '">', | ||
| 1925 | print>>outfile, '</OBJECT>' | ||
| 1926 | print>>outfile, '</UL>' | ||
| 1927 | |||
| 1928 | def codeexpand(self, line): | ||
| 1929 | co = self.codeprog.match(line) | ||
| 1930 | if not co: | ||
| 1931 | return line | ||
| 1932 | bgn, end = co.span(0) | ||
| 1933 | a, b = co.span(1) | ||
| 1934 | line = line[:bgn] + line[a:b] + line[end:] | ||
| 1935 | return line | ||
| 1936 | |||
| 1937 | |||
| 1938 | # Put @var{} around alphabetic substrings | ||
| 1939 | def makevar(str): | ||
| 1940 | return '@var{'+str+'}' | ||
| 1941 | |||
| 1942 | |||
| 1943 | # Split a string in "words" according to findwordend | ||
| 1944 | def splitwords(str, minlength): | ||
| 1945 | words = [] | ||
| 1946 | i = 0 | ||
| 1947 | n = len(str) | ||
| 1948 | while i < n: | ||
| 1949 | while i < n and str[i] in ' \t\n': i = i+1 | ||
| 1950 | if i >= n: break | ||
| 1951 | start = i | ||
| 1952 | i = findwordend(str, i, n) | ||
| 1953 | words.append(str[start:i]) | ||
| 1954 | while len(words) < minlength: words.append('') | ||
| 1955 | return words | ||
| 1956 | |||
| 1957 | |||
| 1958 | # Find the end of a "word", matching braces and interpreting @@ @{ @} | ||
| 1959 | fwprog = re.compile('[@{} ]') | ||
| 1960 | def findwordend(str, i, n): | ||
| 1961 | level = 0 | ||
| 1962 | while i < n: | ||
| 1963 | mo = fwprog.search(str, i) | ||
| 1964 | if not mo: | ||
| 1965 | break | ||
| 1966 | i = mo.start() | ||
| 1967 | c = str[i]; i = i+1 | ||
| 1968 | if c == '@': i = i+1 # Next character is not special | ||
| 1969 | elif c == '{': level = level+1 | ||
| 1970 | elif c == '}': level = level-1 | ||
| 1971 | elif c == ' ' and level <= 0: return i-1 | ||
| 1972 | return n | ||
| 1973 | |||
| 1974 | |||
| 1975 | # Convert a node name into a file name | ||
| 1976 | def makefile(nodename): | ||
| 1977 | nodename = nodename.strip() | ||
| 1978 | return fixfunnychars(nodename) + '.html' | ||
| 1979 | |||
| 1980 | |||
| 1981 | # Characters that are perfectly safe in filenames and hyperlinks | ||
| 1982 | goodchars = string.ascii_letters + string.digits + '!@-=+.' | ||
| 1983 | |||
| 1984 | # Replace characters that aren't perfectly safe by dashes | ||
| 1985 | # Underscores are bad since Cern HTTPD treats them as delimiters for | ||
| 1986 | # encoding times, so you get mismatches if you compress your files: | ||
| 1987 | # a.html.gz will map to a_b.html.gz | ||
| 1988 | def fixfunnychars(addr): | ||
| 1989 | i = 0 | ||
| 1990 | while i < len(addr): | ||
| 1991 | c = addr[i] | ||
| 1992 | if c not in goodchars: | ||
| 1993 | c = '-' | ||
| 1994 | addr = addr[:i] + c + addr[i+1:] | ||
| 1995 | i = i + len(c) | ||
| 1996 | return addr | ||
| 1997 | |||
| 1998 | |||
| 1999 | # Increment a string used as an enumeration | ||
| 2000 | def increment(s): | ||
| 2001 | if not s: | ||
| 2002 | return '1' | ||
| 2003 | for sequence in string.digits, string.lowercase, string.uppercase: | ||
| 2004 | lastc = s[-1] | ||
| 2005 | if lastc in sequence: | ||
| 2006 | i = sequence.index(lastc) + 1 | ||
| 2007 | if i >= len(sequence): | ||
| 2008 | if len(s) == 1: | ||
| 2009 | s = sequence[0]*2 | ||
| 2010 | if s == '00': | ||
| 2011 | s = '10' | ||
| 2012 | else: | ||
| 2013 | s = increment(s[:-1]) + sequence[0] | ||
| 2014 | else: | ||
| 2015 | s = s[:-1] + sequence[i] | ||
| 2016 | return s | ||
| 2017 | return s # Don't increment | ||
| 2018 | |||
| 2019 | |||
| 2020 | def test(): | ||
| 2021 | import sys | ||
| 2022 | debugging = 0 | ||
| 2023 | print_headers = 0 | ||
| 2024 | cont = 0 | ||
| 2025 | html3 = 0 | ||
| 2026 | htmlhelp = '' | ||
| 2027 | |||
| 2028 | while sys.argv[1] == ['-d']: | ||
| 2029 | debugging = debugging + 1 | ||
| 2030 | del sys.argv[1] | ||
| 2031 | if sys.argv[1] == '-p': | ||
| 2032 | print_headers = 1 | ||
| 2033 | del sys.argv[1] | ||
| 2034 | if sys.argv[1] == '-c': | ||
| 2035 | cont = 1 | ||
| 2036 | del sys.argv[1] | ||
| 2037 | if sys.argv[1] == '-3': | ||
| 2038 | html3 = 1 | ||
| 2039 | del sys.argv[1] | ||
| 2040 | if sys.argv[1] == '-H': | ||
| 2041 | helpbase = sys.argv[2] | ||
| 2042 | del sys.argv[1:3] | ||
| 2043 | if len(sys.argv) <> 3: | ||
| 2044 | print 'usage: texi2hh [-d [-d]] [-p] [-c] [-3] [-H htmlhelp]', \ | ||
| 2045 | 'inputfile outputdirectory' | ||
| 2046 | sys.exit(2) | ||
| 2047 | |||
| 2048 | if html3: | ||
| 2049 | parser = TexinfoParserHTML3() | ||
| 2050 | else: | ||
| 2051 | parser = TexinfoParser() | ||
| 2052 | parser.cont = cont | ||
| 2053 | parser.debugging = debugging | ||
| 2054 | parser.print_headers = print_headers | ||
| 2055 | |||
| 2056 | file = sys.argv[1] | ||
| 2057 | dirname = sys.argv[2] | ||
| 2058 | parser.setdirname(dirname) | ||
| 2059 | parser.setincludedir(os.path.dirname(file)) | ||
| 2060 | |||
| 2061 | htmlhelp = HTMLHelp(helpbase, dirname) | ||
| 2062 | parser.sethtmlhelp(htmlhelp) | ||
| 2063 | |||
| 2064 | try: | ||
| 2065 | fp = open(file, 'r') | ||
| 2066 | except IOError, msg: | ||
| 2067 | print file, ':', msg | ||
| 2068 | sys.exit(1) | ||
| 2069 | |||
| 2070 | parser.parse(fp) | ||
| 2071 | fp.close() | ||
| 2072 | parser.report() | ||
| 2073 | |||
| 2074 | htmlhelp.finalize() | ||
| 2075 | |||
| 2076 | |||
| 2077 | if __name__ == "__main__": | ||
| 2078 | test() | ||
| @@ -0,0 +1,205 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | """Script to synchronize two source trees. | ||
| 4 | |||
| 5 | Invoke with two arguments: | ||
| 6 | |||
| 7 | python treesync.py slave master | ||
| 8 | |||
| 9 | The assumption is that "master" contains CVS administration while | ||
| 10 | slave doesn't. All files in the slave tree that have a CVS/Entries | ||
| 11 | entry in the master tree are synchronized. This means: | ||
| 12 | |||
| 13 | If the files differ: | ||
| 14 | if the slave file is newer: | ||
| 15 | normalize the slave file | ||
| 16 | if the files still differ: | ||
| 17 | copy the slave to the master | ||
| 18 | else (the master is newer): | ||
| 19 | copy the master to the slave | ||
| 20 | |||
| 21 | normalizing the slave means replacing CRLF with LF when the master | ||
| 22 | doesn't use CRLF | ||
| 23 | |||
| 24 | """ | ||
| 25 | |||
| 26 | import os, sys, stat, getopt | ||
| 27 | |||
| 28 | # Interactivity options | ||
| 29 | default_answer = "ask" | ||
| 30 | create_files = "yes" | ||
| 31 | create_directories = "no" | ||
| 32 | write_slave = "ask" | ||
| 33 | write_master = "ask" | ||
| 34 | |||
| 35 | def main(): | ||
| 36 | global always_no, always_yes | ||
| 37 | global create_directories, write_master, write_slave | ||
| 38 | opts, args = getopt.getopt(sys.argv[1:], "nym:s:d:f:a:") | ||
| 39 | for o, a in opts: | ||
| 40 | if o == '-y': | ||
| 41 | default_answer = "yes" | ||
| 42 | if o == '-n': | ||
| 43 | default_answer = "no" | ||
| 44 | if o == '-s': | ||
| 45 | write_slave = a | ||
| 46 | if o == '-m': | ||
| 47 | write_master = a | ||
| 48 | if o == '-d': | ||
| 49 | create_directories = a | ||
| 50 | if o == '-f': | ||
| 51 | create_files = a | ||
| 52 | if o == '-a': | ||
| 53 | create_files = create_directories = write_slave = write_master = a | ||
| 54 | try: | ||
| 55 | [slave, master] = args | ||
| 56 | except ValueError: | ||
| 57 | print "usage: python", sys.argv[0] or "treesync.py", | ||
| 58 | print "[-n] [-y] [-m y|n|a] [-s y|n|a] [-d y|n|a] [-f n|y|a]", | ||
| 59 | print "slavedir masterdir" | ||
| 60 | return | ||
| 61 | process(slave, master) | ||
| 62 | |||
| 63 | def process(slave, master): | ||
| 64 | cvsdir = os.path.join(master, "CVS") | ||
| 65 | if not os.path.isdir(cvsdir): | ||
| 66 | print "skipping master subdirectory", master | ||
| 67 | print "-- not under CVS" | ||
| 68 | return | ||
| 69 | print "-"*40 | ||
| 70 | print "slave ", slave | ||
| 71 | print "master", master | ||
| 72 | if not os.path.isdir(slave): | ||
| 73 | if not okay("create slave directory %s?" % slave, | ||
| 74 | answer=create_directories): | ||
| 75 | print "skipping master subdirectory", master | ||
| 76 | print "-- no corresponding slave", slave | ||
| 77 | return | ||
| 78 | print "creating slave directory", slave | ||
| 79 | try: | ||
| 80 | os.mkdir(slave) | ||
| 81 | except os.error, msg: | ||
| 82 | print "can't make slave directory", slave, ":", msg | ||
| 83 | return | ||
| 84 | else: | ||
| 85 | print "made slave directory", slave | ||
| 86 | cvsdir = None | ||
| 87 | subdirs = [] | ||
| 88 | names = os.listdir(master) | ||
| 89 | for name in names: | ||
| 90 | mastername = os.path.join(master, name) | ||
| 91 | slavename = os.path.join(slave, name) | ||
| 92 | if name == "CVS": | ||
| 93 | cvsdir = mastername | ||
| 94 | else: | ||
| 95 | if os.path.isdir(mastername) and not os.path.islink(mastername): | ||
| 96 | subdirs.append((slavename, mastername)) | ||
| 97 | if cvsdir: | ||
| 98 | entries = os.path.join(cvsdir, "Entries") | ||
| 99 | for e in open(entries).readlines(): | ||
| 100 | words = e.split('/') | ||
| 101 | if words[0] == '' and words[1:]: | ||
| 102 | name = words[1] | ||
| 103 | s = os.path.join(slave, name) | ||
| 104 | m = os.path.join(master, name) | ||
| 105 | compare(s, m) | ||
| 106 | for (s, m) in subdirs: | ||
| 107 | process(s, m) | ||
| 108 | |||
| 109 | def compare(slave, master): | ||
| 110 | try: | ||
| 111 | sf = open(slave, 'r') | ||
| 112 | except IOError: | ||
| 113 | sf = None | ||
| 114 | try: | ||
| 115 | mf = open(master, 'rb') | ||
| 116 | except IOError: | ||
| 117 | mf = None | ||
| 118 | if not sf: | ||
| 119 | if not mf: | ||
| 120 | print "Neither master nor slave exists", master | ||
| 121 | return | ||
| 122 | print "Creating missing slave", slave | ||
| 123 | copy(master, slave, answer=create_files) | ||
| 124 | return | ||
| 125 | if not mf: | ||
| 126 | print "Not updating missing master", master | ||
| 127 | return | ||
| 128 | if sf and mf: | ||
| 129 | if identical(sf, mf): | ||
| 130 | return | ||
| 131 | sft = mtime(sf) | ||
| 132 | mft = mtime(mf) | ||
| 133 | if mft > sft: | ||
| 134 | # Master is newer -- copy master to slave | ||
| 135 | sf.close() | ||
| 136 | mf.close() | ||
| 137 | print "Master ", master | ||
| 138 | print "is newer than slave", slave | ||
| 139 | copy(master, slave, answer=write_slave) | ||
| 140 | return | ||
| 141 | # Slave is newer -- copy slave to master | ||
| 142 | print "Slave is", sft-mft, "seconds newer than master" | ||
| 143 | # But first check what to do about CRLF | ||
| 144 | mf.seek(0) | ||
| 145 | fun = funnychars(mf) | ||
| 146 | mf.close() | ||
| 147 | sf.close() | ||
| 148 | if fun: | ||
| 149 | print "***UPDATING MASTER (BINARY COPY)***" | ||
| 150 | copy(slave, master, "rb", answer=write_master) | ||
| 151 | else: | ||
| 152 | print "***UPDATING MASTER***" | ||
| 153 | copy(slave, master, "r", answer=write_master) | ||
| 154 | |||
| 155 | BUFSIZE = 16*1024 | ||
| 156 | |||
| 157 | def identical(sf, mf): | ||
| 158 | while 1: | ||
| 159 | sd = sf.read(BUFSIZE) | ||
| 160 | md = mf.read(BUFSIZE) | ||
| 161 | if sd != md: return 0 | ||
| 162 | if not sd: break | ||
| 163 | return 1 | ||
| 164 | |||
| 165 | def mtime(f): | ||
| 166 | st = os.fstat(f.fileno()) | ||
| 167 | return st[stat.ST_MTIME] | ||
| 168 | |||
| 169 | def funnychars(f): | ||
| 170 | while 1: | ||
| 171 | buf = f.read(BUFSIZE) | ||
| 172 | if not buf: break | ||
| 173 | if '\r' in buf or '\0' in buf: return 1 | ||
| 174 | return 0 | ||
| 175 | |||
| 176 | def copy(src, dst, rmode="rb", wmode="wb", answer='ask'): | ||
| 177 | print "copying", src | ||
| 178 | print " to", dst | ||
| 179 | if not okay("okay to copy? ", answer): | ||
| 180 | return | ||
| 181 | f = open(src, rmode) | ||
| 182 | g = open(dst, wmode) | ||
| 183 | while 1: | ||
| 184 | buf = f.read(BUFSIZE) | ||
| 185 | if not buf: break | ||
| 186 | g.write(buf) | ||
| 187 | f.close() | ||
| 188 | g.close() | ||
| 189 | |||
| 190 | def okay(prompt, answer='ask'): | ||
| 191 | answer = answer.strip().lower() | ||
| 192 | if not answer or answer[0] not in 'ny': | ||
| 193 | answer = raw_input(prompt) | ||
| 194 | answer = answer.strip().lower() | ||
| 195 | if not answer: | ||
| 196 | answer = default_answer | ||
| 197 | if answer[:1] == 'y': | ||
| 198 | return 1 | ||
| 199 | if answer[:1] == 'n': | ||
| 200 | return 0 | ||
| 201 | print "Yes or No please -- try again:" | ||
| 202 | return okay(prompt) | ||
| 203 | |||
| 204 | if __name__ == '__main__': | ||
| 205 | main() | ||
| @@ -0,0 +1,52 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | "Replace tabs with spaces in argument files. Print names of changed files." | ||
| 4 | |||
| 5 | import os | ||
| 6 | import sys | ||
| 7 | import getopt | ||
| 8 | |||
| 9 | def main(): | ||
| 10 | tabsize = 8 | ||
| 11 | try: | ||
| 12 | opts, args = getopt.getopt(sys.argv[1:], "t:") | ||
| 13 | if not args: | ||
| 14 | raise getopt.error, "At least one file argument required" | ||
| 15 | except getopt.error, msg: | ||
| 16 | print msg | ||
| 17 | print "usage:", sys.argv[0], "[-t tabwidth] file ..." | ||
| 18 | return | ||
| 19 | for optname, optvalue in opts: | ||
| 20 | if optname == '-t': | ||
| 21 | tabsize = int(optvalue) | ||
| 22 | |||
| 23 | for filename in args: | ||
| 24 | process(filename, tabsize) | ||
| 25 | |||
| 26 | def process(filename, tabsize): | ||
| 27 | try: | ||
| 28 | f = open(filename) | ||
| 29 | text = f.read() | ||
| 30 | f.close() | ||
| 31 | except IOError, msg: | ||
| 32 | print "%r: I/O error: %s" % (filename, msg) | ||
| 33 | return | ||
| 34 | newtext = text.expandtabs(tabsize) | ||
| 35 | if newtext == text: | ||
| 36 | return | ||
| 37 | backup = filename + "~" | ||
| 38 | try: | ||
| 39 | os.unlink(backup) | ||
| 40 | except os.error: | ||
| 41 | pass | ||
| 42 | try: | ||
| 43 | os.rename(filename, backup) | ||
| 44 | except os.error: | ||
| 45 | pass | ||
| 46 | f = open(filename, "w") | ||
| 47 | f.write(newtext) | ||
| 48 | f.close() | ||
| 49 | print filename | ||
| 50 | |||
| 51 | if __name__ == '__main__': | ||
| 52 | main() | ||
| @@ -0,0 +1,60 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # Variant of "which". | ||
| 4 | # On stderr, near and total misses are reported. | ||
| 5 | # '-l<flags>' argument adds ls -l<flags> of each file found. | ||
| 6 | |||
| 7 | import sys | ||
| 8 | if sys.path[0] in (".", ""): del sys.path[0] | ||
| 9 | |||
| 10 | import sys, os | ||
| 11 | from stat import * | ||
| 12 | |||
| 13 | def msg(str): | ||
| 14 | sys.stderr.write(str + '\n') | ||
| 15 | |||
| 16 | def main(): | ||
| 17 | pathlist = os.environ['PATH'].split(os.pathsep) | ||
| 18 | |||
| 19 | sts = 0 | ||
| 20 | longlist = '' | ||
| 21 | |||
| 22 | if sys.argv[1:] and sys.argv[1][:2] == '-l': | ||
| 23 | longlist = sys.argv[1] | ||
| 24 | del sys.argv[1] | ||
| 25 | |||
| 26 | for prog in sys.argv[1:]: | ||
| 27 | ident = () | ||
| 28 | for dir in pathlist: | ||
| 29 | filename = os.path.join(dir, prog) | ||
| 30 | try: | ||
| 31 | st = os.stat(filename) | ||
| 32 | except os.error: | ||
| 33 | continue | ||
| 34 | if not S_ISREG(st[ST_MODE]): | ||
| 35 | msg(filename + ': not a disk file') | ||
| 36 | else: | ||
| 37 | mode = S_IMODE(st[ST_MODE]) | ||
| 38 | if mode & 0111: | ||
| 39 | if not ident: | ||
| 40 | print filename | ||
| 41 | ident = st[:3] | ||
| 42 | else: | ||
| 43 | if st[:3] == ident: | ||
| 44 | s = 'same as: ' | ||
| 45 | else: | ||
| 46 | s = 'also: ' | ||
| 47 | msg(s + filename) | ||
| 48 | else: | ||
| 49 | msg(filename + ': not executable') | ||
| 50 | if longlist: | ||
| 51 | sts = os.system('ls ' + longlist + ' ' + filename) | ||
| 52 | if sts: msg('"ls -l" exit status: ' + repr(sts)) | ||
| 53 | if not ident: | ||
| 54 | msg(prog + ': not found') | ||
| 55 | sts = 1 | ||
| 56 | |||
| 57 | sys.exit(sts) | ||
| 58 | |||
| 59 | if __name__ == '__main__': | ||
| 60 | main() | ||
| @@ -0,0 +1,117 @@ | |||
| 1 | #! /usr/bin/env python | ||
| 2 | |||
| 3 | # xxci | ||
| 4 | # | ||
| 5 | # check in files for which rcsdiff returns nonzero exit status | ||
| 6 | |||
| 7 | import sys | ||
| 8 | import os | ||
| 9 | from stat import * | ||
| 10 | import commands | ||
| 11 | import fnmatch | ||
| 12 | |||
| 13 | EXECMAGIC = '\001\140\000\010' | ||
| 14 | |||
| 15 | MAXSIZE = 200*1024 # Files this big must be binaries and are skipped. | ||
| 16 | |||
| 17 | def getargs(): | ||
| 18 | args = sys.argv[1:] | ||
| 19 | if args: | ||
| 20 | return args | ||
| 21 | print 'No arguments, checking almost *, in "ls -t" order' | ||
| 22 | list = [] | ||
| 23 | for file in os.listdir(os.curdir): | ||
| 24 | if not skipfile(file): | ||
| 25 | list.append((getmtime(file), file)) | ||
| 26 | list.sort() | ||
| 27 | if not list: | ||
| 28 | print 'Nothing to do -- exit 1' | ||
| 29 | sys.exit(1) | ||
| 30 | list.sort() | ||
| 31 | list.reverse() | ||
| 32 | for mtime, file in list: args.append(file) | ||
| 33 | return args | ||
| 34 | |||
| 35 | def getmtime(file): | ||
| 36 | try: | ||
| 37 | st = os.stat(file) | ||
| 38 | return st[ST_MTIME] | ||
| 39 | except os.error: | ||
| 40 | return -1 | ||
| 41 | |||
| 42 | badnames = ['tags', 'TAGS', 'xyzzy', 'nohup.out', 'core'] | ||
| 43 | badprefixes = ['.', ',', '@', '#', 'o.'] | ||
| 44 | badsuffixes = \ | ||
| 45 | ['~', '.a', '.o', '.old', '.bak', '.orig', '.new', '.prev', '.not', \ | ||
| 46 | '.pyc', '.fdc', '.rgb', '.elc', ',v'] | ||
| 47 | ignore = [] | ||
| 48 | |||
| 49 | def setup(): | ||
| 50 | ignore[:] = badnames | ||
| 51 | for p in badprefixes: | ||
| 52 | ignore.append(p + '*') | ||
| 53 | for p in badsuffixes: | ||
| 54 | ignore.append('*' + p) | ||
| 55 | try: | ||
| 56 | f = open('.xxcign', 'r') | ||
| 57 | except IOError: | ||
| 58 | return | ||
| 59 | ignore[:] = ignore + f.read().split() | ||
| 60 | |||
| 61 | def skipfile(file): | ||
| 62 | for p in ignore: | ||
| 63 | if fnmatch.fnmatch(file, p): return 1 | ||
| 64 | try: | ||
| 65 | st = os.lstat(file) | ||
| 66 | except os.error: | ||
| 67 | return 1 # Doesn't exist -- skip it | ||
| 68 | # Skip non-plain files. | ||
| 69 | if not S_ISREG(st[ST_MODE]): return 1 | ||
| 70 | # Skip huge files -- probably binaries. | ||
| 71 | if st[ST_SIZE] >= MAXSIZE: return 1 | ||
| 72 | # Skip executables | ||
| 73 | try: | ||
| 74 | data = open(file, 'r').read(len(EXECMAGIC)) | ||
| 75 | if data == EXECMAGIC: return 1 | ||
| 76 | except: | ||
| 77 | pass | ||
| 78 | return 0 | ||
| 79 | |||
| 80 | def badprefix(file): | ||
| 81 | for bad in badprefixes: | ||
| 82 | if file[:len(bad)] == bad: return 1 | ||
| 83 | return 0 | ||
| 84 | |||
| 85 | def badsuffix(file): | ||
| 86 | for bad in badsuffixes: | ||
| 87 | if file[-len(bad):] == bad: return 1 | ||
| 88 | return 0 | ||
| 89 | |||
| 90 | def go(args): | ||
| 91 | for file in args: | ||
| 92 | print file + ':' | ||
| 93 | if differing(file): | ||
| 94 | showdiffs(file) | ||
| 95 | if askyesno('Check in ' + file + ' ? '): | ||
| 96 | sts = os.system('rcs -l ' + file) # ignored | ||
| 97 | sts = os.system('ci -l ' + file) | ||
| 98 | |||
| 99 | def differing(file): | ||
| 100 | cmd = 'co -p ' + file + ' 2>/dev/null | cmp -s - ' + file | ||
| 101 | sts = os.system(cmd) | ||
| 102 | return sts != 0 | ||
| 103 | |||
| 104 | def showdiffs(file): | ||
| 105 | cmd = 'rcsdiff ' + file + ' 2>&1 | ${PAGER-more}' | ||
| 106 | sts = os.system(cmd) | ||
| 107 | |||
| 108 | def askyesno(prompt): | ||
| 109 | s = raw_input(prompt) | ||
| 110 | return s in ['y', 'yes'] | ||
| 111 | |||
| 112 | if __name__ == '__main__': | ||
| 113 | try: | ||
| 114 | setup() | ||
| 115 | go(getargs()) | ||
| 116 | except KeyboardInterrupt: | ||
| 117 | print '[Intr]' | ||