History

add hg and python

cinap_lenrek committed 15 years ago

Commit 4bb9e221 parent 987265c3706 files · +1228772 −-233Browse files

Showing the changes to sys/src/cmd/python/Tools/scripts (63 of 3706 files). Show all

Unified · Split

63 files
  1. sys/src/cmd/python/Tools/scripts/README +66 −0
  2. sys/src/cmd/python/Tools/scripts/byext.py +131 −0
  3. sys/src/cmd/python/Tools/scripts/byteyears.py +61 −0
  4. sys/src/cmd/python/Tools/scripts/checkappend.py +167 −0
  5. sys/src/cmd/python/Tools/scripts/checkpyc.py +66 −0
  6. sys/src/cmd/python/Tools/scripts/classfix.py +190 −0
  7. sys/src/cmd/python/Tools/scripts/cleanfuture.py +276 −0
  8. sys/src/cmd/python/Tools/scripts/combinerefs.py +127 −0
  9. sys/src/cmd/python/Tools/scripts/copytime.py +26 −0
  10. sys/src/cmd/python/Tools/scripts/crlf.py +23 −0
  11. sys/src/cmd/python/Tools/scripts/cvsfiles.py +72 −0
  12. sys/src/cmd/python/Tools/scripts/db2pickle.py +135 −0
  13. sys/src/cmd/python/Tools/scripts/diff.py +49 −0
  14. sys/src/cmd/python/Tools/scripts/dutree.doc +54 −0
  15. sys/src/cmd/python/Tools/scripts/dutree.py +60 −0
  16. sys/src/cmd/python/Tools/scripts/eptags.py +56 −0
  17. sys/src/cmd/python/Tools/scripts/finddiv.py +89 −0
  18. sys/src/cmd/python/Tools/scripts/findlinksto.py +43 −0
  19. sys/src/cmd/python/Tools/scripts/findnocoding.py +104 −0
  20. sys/src/cmd/python/Tools/scripts/fixcid.py +314 −0
  21. sys/src/cmd/python/Tools/scripts/fixdiv.py +380 −0
  22. sys/src/cmd/python/Tools/scripts/fixheader.py +49 −0
  23. sys/src/cmd/python/Tools/scripts/fixnotice.py +113 −0
  24. sys/src/cmd/python/Tools/scripts/fixps.py +33 −0
  25. sys/src/cmd/python/Tools/scripts/ftpmirror.py +400 −0
  26. sys/src/cmd/python/Tools/scripts/google.py +23 −0
  27. sys/src/cmd/python/Tools/scripts/gprof2html.py +79 −0
  28. sys/src/cmd/python/Tools/scripts/h2py.py +175 −0
  29. sys/src/cmd/python/Tools/scripts/hotshotmain.py +55 −0
  30. sys/src/cmd/python/Tools/scripts/idle +5 −0
  31. sys/src/cmd/python/Tools/scripts/ifdef.py +112 −0
  32. sys/src/cmd/python/Tools/scripts/lfcr.py +24 −0
  33. sys/src/cmd/python/Tools/scripts/linktree.py +80 −0
  34. sys/src/cmd/python/Tools/scripts/lll.py +28 −0
  35. sys/src/cmd/python/Tools/scripts/logmerge.py +185 −0
  36. sys/src/cmd/python/Tools/scripts/mailerdaemon.py +237 −0
  37. sys/src/cmd/python/Tools/scripts/md5sum.py +90 −0
  38. sys/src/cmd/python/Tools/scripts/methfix.py +171 −0
  39. sys/src/cmd/python/Tools/scripts/mkreal.py +66 −0
  40. sys/src/cmd/python/Tools/scripts/ndiff.py +133 −0
  41. sys/src/cmd/python/Tools/scripts/nm2def.py +103 −0
  42. sys/src/cmd/python/Tools/scripts/objgraph.py +215 −0
  43. sys/src/cmd/python/Tools/scripts/parseentities.py +64 −0
  44. sys/src/cmd/python/Tools/scripts/pathfix.py +149 −0
  45. sys/src/cmd/python/Tools/scripts/pdeps.py +167 −0
  46. sys/src/cmd/python/Tools/scripts/pickle2db.py +147 −0
  47. sys/src/cmd/python/Tools/scripts/pindent.py +543 −0
  48. sys/src/cmd/python/Tools/scripts/ptags.py +53 −0
  49. sys/src/cmd/python/Tools/scripts/pydoc +5 −0
  50. sys/src/cmd/python/Tools/scripts/pydocgui.pyw +7 −0
  51. sys/src/cmd/python/Tools/scripts/pysource.py +130 −0
  52. sys/src/cmd/python/Tools/scripts/redemo.py +171 −0
  53. sys/src/cmd/python/Tools/scripts/reindent.py +293 −0
  54. sys/src/cmd/python/Tools/scripts/rgrep.py +64 −0
  55. sys/src/cmd/python/Tools/scripts/setup.py +19 −0
  56. sys/src/cmd/python/Tools/scripts/suff.py +30 −0
  57. sys/src/cmd/python/Tools/scripts/svneol.py +77 −0
  58. sys/src/cmd/python/Tools/scripts/texcheck.py +233 −0
  59. sys/src/cmd/python/Tools/scripts/texi2html.py +2078 −0
  60. sys/src/cmd/python/Tools/scripts/treesync.py +205 −0
  61. sys/src/cmd/python/Tools/scripts/untabify.py +52 −0
  62. sys/src/cmd/python/Tools/scripts/which.py +60 −0
  63. sys/src/cmd/python/Tools/scripts/xxci.py +117 −0
sys/src/cmd/python/Tools/scripts/README+66−0
@@ -0,0 +1,66 @@
1This directory contains a collection of executable Python scripts that
2are useful while building, extending or managing Python. Some (e.g.,
3dutree or lll) are also generally useful UNIX tools.
4
5See also the Demo/scripts directory!
6
7byext.py Print lines/words/chars stats of files by extension
8byteyears.py Print product of a file's size and age
9checkappend.py Search for multi-argument .append() calls
10checkpyc.py Check presence and validity of ".pyc" files
11classfix.py Convert old class syntax to new
12cleanfuture.py Fix reduntant Python __future__ statements
13combinerefs.py A helper for analyzing PYTHONDUMPREFS output.
14copytime.py Copy one file's atime and mtime to another
15crlf.py Change CRLF line endings to LF (Windows to Unix)
16cvsfiles.py Print a list of files that are under CVS
17db2pickle.py Dump a database file to a pickle
18diff.py Print file diffs in context, unified, or ndiff formats
19dutree.py Format du(1) output as a tree sorted by size
20eptags.py Create Emacs TAGS file for Python modules
21finddiv.py A grep-like tool that looks for division operators.
22findlinksto.py Recursively find symbolic links to a given path prefix
23findnocoding.py Find source files which need an encoding declaration
24fixcid.py Massive identifier substitution on C source files
25fixdiv.py Tool to fix division operators.
26fixheader.py Add some cpp magic to a C include file
27fixnotice.py Fix the copyright notice in source files
28fixps.py Fix Python scripts' first line (if #!)
29ftpmirror.py FTP mirror script
30google.py Open a webbrowser with Google.
31gprof2html.py Transform gprof(1) output into useful HTML.
32h2py.py Translate #define's into Python assignments
33hotshotmain.py Main program to run script under control of hotshot
34idle Main program to start IDLE
35ifdef.py Remove #if(n)def groups from C sources
36lfcr.py Change LF line endings to CRLF (Unix to Windows)
37linktree.py Make a copy of a tree with links to original files
38lll.py Find and list symbolic links in current directory
39logmerge.py Consolidate CVS/RCS logs read from stdin
40mailerdaemon.py parse error messages from mailer daemons (Sjoerd&Jack)
41md5sum.py Print MD5 checksums of argument files.
42methfix.py Fix old method syntax def f(self, (a1, ..., aN)):
43mkreal.py Turn a symbolic link into a real file or directory
44ndiff.py Intelligent diff between text files (Tim Peters)
45nm2def.py Create a template for PC/python_nt.def (Marc Lemburg)
46objgraph.py Print object graph from nm output on a library
47parseentities.py Utility for parsing HTML entity definitions
48pathfix.py Change #!/usr/local/bin/python into something else
49pdeps.py Print dependencies between Python modules
50pickle2db.py Load a pickle generated by db2pickle.py to a database
51pindent.py Indent Python code, giving block-closing comments
52ptags.py Create vi tags file for Python modules
53pydoc Python documentation browser.
54pysource.py Find Python source files
55redemo.py Basic regular expression demonstration facility
56reindent.py Change .py files to use 4-space indents.
57rgrep.py Reverse grep through a file (useful for big logfiles)
58setup.py Install all scripts listed here.
59suff.py Sort a list of files by suffix
60svneol.py Sets svn:eol-style on all files in directory.
61texcheck.py Validate Python LaTeX formatting (Raymond Hettinger)
62texi2html.py Convert GNU texinfo files into HTML
63treesync.py Synchronize source trees (very ideosyncratic)
64untabify.py Replace tabs with spaces in argument files
65which.py Find a program in $PATH
66xxci.py Wrapper for rcsdiff and ci
sys/src/cmd/python/Tools/scripts/byext.py+131−0
@@ -0,0 +1,131 @@
1#! /usr/bin/env python
2
3"""Show file statistics by extension."""
4
5import os
6import sys
7
8class 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 print
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 print
120 printheader() # Another header at the bottom
121
122def 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
130if __name__ == "__main__":
131 main()
sys/src/cmd/python/Tools/scripts/byteyears.py+61−0
@@ -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
9import sys, os, time
10from stat import *
11
12def 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
60if __name__ == '__main__':
61 main()
sys/src/cmd/python/Tools/scripts/checkappend.py+167−0
@@ -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
7Usage: specify one or more file or directory paths:
8 checkappend [-v] file_or_dir [file_or_dir] ...
9
10Each file_or_dir is checked for multi-argument .append() calls. When
11a directory, all .py files in the directory, and recursively in its
12subdirectories, are checked.
13
14Use -v for status msgs. Use -vv for more status msgs.
15
16In the absence of -v, the only output is pairs of the form
17
18 filename(linenumber):
19 line containing the suspicious append
20
21Note that this finds multi-argument append calls regardless of whether
22they're attached to list objects. If a module defines a class with an
23append method that takes more than one argument, calls to that method
24will be listed.
25
26Note that this will not find multi-argument list.append calls made via a
27bound 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
36import os
37import sys
38import getopt
39import tokenize
40
41verbose = 0
42
43def errprint(*args):
44 msg = ' '.join(args)
45 sys.stderr.write(msg)
46 sys.stderr.write("\n")
47
48def 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
65def 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
97class 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
166if __name__ == '__main__':
167 main()
sys/src/cmd/python/Tools/scripts/checkpyc.py+66−0
@@ -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
5import sys
6import os
7from stat import ST_MTIME
8import imp
9
10def 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
60def 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
65if __name__ == '__main__':
66 main()
sys/src/cmd/python/Tools/scripts/classfix.py+190−0
@@ -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
32import sys
33import re
34import os
35from stat import *
36
37err = sys.stderr.write
38dbg = err
39rep = sys.stdout.write
40
41def 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
56ispythonprog = re.compile('^[a-zA-Z0-9_]+\.py$')
57def ispython(name):
58 return ispythonprog.match(name) >= 0
59
60def 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
82def 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.
151classexpr = '^([ \t]*class +[a-zA-Z0-9_]+) *( *) *((=.*)?):'
152classprog = re.compile(classexpr)
153
154# Expressions for finding base class expressions.
155baseexpr = '^ *(.*) *( *) *$'
156baseprog = re.compile(baseexpr)
157
158def 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
189if __name__ == '__main__':
190 main()
sys/src/cmd/python/Tools/scripts/cleanfuture.py+276−0
@@ -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
9Search Python (.py) files for future statements, and remove the features
10from such statements that are already mandatory in the version of Python
11you're using.
12
13Pass 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
15given, likewise recursively for subdirectories.
16
17Overwrites files in place, renaming the originals with a .bak extension. If
18cleanfuture finds nothing to change, the file is left alone. If cleanfuture
19does change a file, the changed file is a fixed-point (i.e., running
20cleanfuture on the resulting .py file won't change it again, at least not
21until you try it again with a later Python release).
22
23Limitations: 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
30Example: Assuming you're using Python 2.2, if a file containing
31
32from __future__ import nested_scopes, generators
33
34is analyzed by cleanfuture, the line is rewritten to
35
36from __future__ import generators
37
38because nested_scopes is no longer optional in 2.2 but generators is.
39"""
40
41import __future__
42import tokenize
43import os
44import sys
45
46dryrun = 0
47recurse = 0
48verbose = 0
49
50def 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
57def 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
78def 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
134class 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
275if __name__ == '__main__':
276 main()
sys/src/cmd/python/Tools/scripts/combinerefs.py+127−0
@@ -0,0 +1,127 @@
1#! /usr/bin/env python
2
3"""
4combinerefs path
5
6A helper for analyzing PYTHONDUMPREFS output.
7
8When the PYTHONDUMPREFS envar is set in a debug build, at Python shutdown
9time Py_Finalize() prints the list of all live objects twice: first it
10prints the repr() of each object while the interpreter is still fully intact.
11After cleaning up everything it can, it prints all remaining live objects
12again, but the second time just prints their addresses, refcounts, and type
13names (because the interpreter has been torn down, calling repr methods at
14this point can get into infinite loops or blow up).
15
16Save all this output into a file, then run this script passing the path to
17that file. The script finds both output chunks, combines them, then prints
18a line of output for each object still alive at the end:
19
20 address refcnt typename repr
21
22address is the address of the object, in whatever format the platform C
23produces for a %p format code.
24
25refcnt is of the form
26
27 "[" ref "]"
28
29when the object's refcount is the same in both PYTHONDUMPREFS output blocks,
30or
31
32 "[" ref_before "->" ref_after "]"
33
34if the refcount changed.
35
36typename is object->ob_type->tp_name, extracted from the second PYTHONDUMPREFS
37output block.
38
39repr is repr(object), extracted from the first PYTHONDUMPREFS output block.
40CAUTION: If object is a container type, it may not actually contain all the
41objects shown in the repr: the repr was captured from the first output block,
42and some of the containees may have been released since then. For example,
43it's common for the line showing the dict of interned strings to display
44strings 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
47The objects are listed in allocation order, with most-recently allocated
48printed first, and the first object allocated printed last.
49
50
51Simple examples:
52
53 00857060 [14] str '__len__'
54
55The str object '__len__' is alive at shutdown time, and both PYTHONDUMPREFS
56output blocks said there were 14 references to it. This is probably due to
57C modules that intern the string "__len__" and keep a reference to it in a
58file static.
59
60 00857038 [46->5] tuple ()
61
6246-5 = 41 references to the empty tuple were removed by the cleanup actions
63between the times PYTHONDUMPREFS produced output.
64
65 00858028 [1025->1456] str '<dummy key>'
66
67The string '<dummy key>', which is used in dictobject.c to overwrite a real
68key that gets deleted, grew several hundred references during cleanup. It
69suggests that stuff did get removed from dicts by cleanup, but that the dicts
70themselves are staying alive for some reason. """
71
72import re
73import 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.
81def read(fileiter, pat, whilematch):
82 for line in fileiter:
83 if bool(pat.match(line)) == whilematch:
84 yield line
85 else:
86 break
87
88def 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
126if __name__ == '__main__':
127 combine(sys.argv[1])
sys/src/cmd/python/Tools/scripts/copytime.py+26−0
@@ -0,0 +1,26 @@
1#! /usr/bin/env python
2
3# Copy one file's atime and mtime to another
4
5import sys
6import os
7from stat import ST_ATIME, ST_MTIME # Really constants 7 and 8
8
9def 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
25if __name__ == '__main__':
26 main()
sys/src/cmd/python/Tools/scripts/crlf.py+23−0
@@ -0,0 +1,23 @@
1#! /usr/bin/env python
2"Replace CRLF with LF in argument files. Print names of changed files."
3
4import sys, os
5
6def 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
22if __name__ == '__main__':
23 main()
sys/src/cmd/python/Tools/scripts/cvsfiles.py+72−0
@@ -0,0 +1,72 @@
1#! /usr/bin/env python
2
3"""Print a list of files that are mentioned in CVS directories.
4
5Usage: cvsfiles.py [-n file] [directory] ...
6
7If the '-n file' option is given, only files under CVS that are newer
8than the given file are printed; by default, all files under CVS are
9printed. As a special case, if a file does not exist, it is always
10printed.
11"""
12
13import os
14import sys
15import stat
16import getopt
17
18cutofftime = 0
19
20def 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
38def 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
64def getmtime(filename):
65 try:
66 st = os.stat(filename)
67 except os.error:
68 return 0
69 return st[stat.ST_MTIME]
70
71if __name__ == '__main__':
72 main()
sys/src/cmd/python/Tools/scripts/db2pickle.py+135−0
@@ -0,0 +1,135 @@
1#!/usr/bin/env python
2
3"""
4Synopsis: %(prog)s [-h|-g|-b|-r|-a] dbfile [ picklefile ]
5
6Convert the database file given on the command line to a pickle
7representation. 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
16The default is hash. If a pickle file is named it is opened for write
17access (deleting any existing data). If no pickle file is named, the pickle
18output is written to standard output.
19
20"""
21
22import getopt
23try:
24 import bsddb
25except ImportError:
26 bsddb = None
27try:
28 import dbm
29except ImportError:
30 dbm = None
31try:
32 import gdbm
33except ImportError:
34 gdbm = None
35try:
36 import anydbm
37except ImportError:
38 anydbm = None
39import sys
40try:
41 import cPickle as pickle
42except ImportError:
43 import pickle
44
45prog = sys.argv[0]
46
47def usage():
48 sys.stderr.write(__doc__ % globals())
49
50def 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
134if __name__ == "__main__":
135 sys.exit(main(sys.argv[1:]))
sys/src/cmd/python/Tools/scripts/diff.py+49−0
@@ -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
10import sys, os, time, difflib, optparse
11
12def 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
48if __name__ == '__main__':
49 main()
sys/src/cmd/python/Tools/scripts/dutree.doc+54−0
@@ -0,0 +1,54 @@
1Path: cwi.nl!sun4nl!mcsun!uunet!cs.utexas.edu!convex!usenet
2From: tchrist@convex.COM (Tom Christiansen)
3Newsgroups: comp.lang.perl
4Subject: Re: The problems of Perl (Re: Question (silly?))
5Message-ID: <1992Jan17.053115.4220@convex.com>
6Date: 17 Jan 92 05:31:15 GMT
7References: <17458@ector.cs.purdue.edu> <1992Jan16.165347.25583@cherokee.uswest.com> <=#Hues+4@cs.psu.edu>
8Sender: usenet@convex.com (news access account)
9Reply-To: tchrist@convex.COM (Tom Christiansen)
10Organization: CONVEX Realtime Development, Colorado Springs, CO
11Lines: 83
12Nntp-Posting-Host: pixel.convex.com
13
14From 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
18Yes, it's pretty aweful at that, alright. Sometimes I write perl programs
19that need them, and sometimes it just takes a little creativity. But
20sometimes 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
36At first I thought I could just keep one local list around
37at once, but this seems inherently recursive. Which means
38I need an real recursive data structure. Maybe you could
39do it with one of the %assoc arrays Larry uses in the begat
40programs, but I broke down and got dirty. I think the hardest
41part was matching Felix's desired output exactly. It's not
42blazingly fast: I should probably inline the &childof routine,
43but it *was* faster to write than I could have written the
44equivalent C program.
45
46
47--tom
48
49--
50"GUIs normally make it simple to accomplish simple actions and impossible
51to accomplish complex actions." --Doug Gwyn (22/Jun/91 in comp.unix.wizards)
52
53 Tom Christiansen tchrist@convex.com convex!tchrist
54
sys/src/cmd/python/Tools/scripts/dutree.py+60−0
@@ -0,0 +1,60 @@
1#! /usr/bin/env python
2# Format du output in a tree shape
3
4import os, sys, errno
5
6def 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
25def 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
34def display(total, d):
35 show(total, d, '')
36
37def 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
59if __name__ == '__main__':
60 main()
sys/src/cmd/python/Tools/scripts/eptags.py+56−0
@@ -0,0 +1,56 @@
1#! /usr/bin/env python
2"""Create a TAGS file for Python programs, usable with GNU Emacs.
3
4usage: eptags pyfiles...
5
6The output TAGS file is usable with Emacs version 18, 19, 20.
7Tagged are:
8 - functions (even inside other defs or classes)
9 - classes
10
11eptags warns about files it cannot open.
12eptags will not give warnings about duplicate tags.
13
14BUGS:
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"""
19import sys,re
20
21expr = r'^[ \t]*(def|class)[ \t]+([a-zA-Z_][a-zA-Z0-9_]*)[ \t]*[:\(]'
22matcher = re.compile(expr)
23
24def 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
50def main():
51 outfp = open('TAGS', 'w')
52 for filename in sys.argv[1:]:
53 treat_file(filename, outfp)
54
55if __name__=="__main__":
56 main()
sys/src/cmd/python/Tools/scripts/finddiv.py+89−0
@@ -0,0 +1,89 @@
1#! /usr/bin/env python
2
3"""finddiv - a grep-like tool that looks for division operators.
4
5Usage: finddiv [-l] file_or_directory ...
6
7For directory arguments, all files in the directory whose name ends in
8.py are processed, and subdirectories are processed recursively.
9
10This actually tokenizes the files to avoid false hits in comments or
11strings literals.
12
13By default, this prints all lines containing a / or /= operator, in
14grep -n style. With the -l option specified, it prints the filename
15of files that contain at least one / or /= operator.
16"""
17
18import os
19import sys
20import getopt
21import tokenize
22
23def 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
45def 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
50def 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
70def 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
88if __name__ == "__main__":
89 sys.exit(main())
sys/src/cmd/python/Tools/scripts/findlinksto.py+43−0
@@ -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
7import os
8import sys
9import re
10import getopt
11
12def 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
27def 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
42if __name__ == '__main__':
43 main()
sys/src/cmd/python/Tools/scripts/findnocoding.py+104−0
@@ -0,0 +1,104 @@
1#!/usr/bin/env python
2
3"""List all those Python files that require a coding directive
4
5Usage: nocoding.py dir1 [dir2...]
6"""
7
8__author__ = "Oleg Broytmann, Georg Brandl"
9
10import sys, os, re, getopt
11
12# our pysource module finds Python source files
13try:
14 import pysource
15except:
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
35decl_re = re.compile(r"coding[=:]\s*([-\w.]+)")
36
37def get_declaration(line):
38 match = decl_re.search(line)
39 if match:
40 return match.group(1)
41 return ''
42
43def has_correct_encoding(text, codec):
44 try:
45 unicode(text, codec)
46 except UnicodeDecodeError:
47 return False
48 else:
49 return True
50
51def 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
75usage = """Usage: %s [-cd] paths...
76 -c: recognize Python source files trying to compile them
77 -d: debug output""" % sys.argv[0]
78
79try:
80 opts, args = getopt.getopt(sys.argv[1:], 'cd')
81except getopt.error, msg:
82 print >>sys.stderr, msg
83 print >>sys.stderr, usage
84 sys.exit(1)
85
86is_python = pysource.looks_like_python
87debug = False
88
89for o, a in opts:
90 if o == '-c':
91 is_python = pysource.can_be_compiled
92 elif o == '-d':
93 debug = True
94
95if not args:
96 print >>sys.stderr, usage
97 sys.exit(1)
98
99for 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
sys/src/cmd/python/Tools/scripts/fixcid.py+314−0
@@ -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
37import sys
38import re
39import os
40from stat import *
41import getopt
42
43err = sys.stderr.write
44dbg = err
45rep = sys.stdout.write
46
47def 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
62def 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
91Wanted = '^[a-zA-Z0-9_]+\.[ch]$'
92def wanted(name):
93 return re.match(Wanted, name) >= 0
94
95def 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
117def 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
196Identifier = '\(struct \)?[a-zA-Z_][a-zA-Z0-9_]+'
197String = '"\([^\n\\"]\|\\\\.\)*"'
198Char = '\'\([^\n\\\']\|\\\\.\)*\''
199CommentStart = '/\*'
200CommentEnd = '\*/'
201
202Hexnumber = '0[xX][0-9a-fA-F]*[uUlL]*'
203Octnumber = '0[0-7]*[uUlL]*'
204Decnumber = '[1-9][0-9]*[uUlL]*'
205Intnumber = Hexnumber + '\|' + Octnumber + '\|' + Decnumber
206Exponent = '[eE][-+]?[0-9]+'
207Pointfloat = '\([0-9]+\.[0-9]*\|\.[0-9]+\)\(' + Exponent + '\)?'
208Expfloat = '[0-9]+' + Exponent
209Floatnumber = Pointfloat + '\|' + Expfloat
210Number = Floatnumber + '\|' + Intnumber
211
212# Anything else is an operator -- don't list this explicitly because of '/*'
213
214OutsideComment = (Identifier, Number, String, Char, CommentStart)
215OutsideCommentPattern = '(' + '|'.join(OutsideComment) + ')'
216OutsideCommentProgram = re.compile(OutsideCommentPattern)
217
218InsideComment = (Identifier, Number, CommentEnd)
219InsideCommentPattern = '(' + '|'.join(InsideComment) + ')'
220InsideCommentProgram = re.compile(InsideCommentPattern)
221
222def initfixline():
223 global Program
224 Program = OutsideCommentProgram
225
226def 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
264Docomments = 0
265def setdocomments():
266 global Docomments
267 Docomments = 1
268
269Reverse = 0
270def setreverse():
271 global Reverse
272 Reverse = (not Reverse)
273
274Dict = {}
275NotInComment = {}
276def 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
313if __name__ == '__main__':
314 main()
sys/src/cmd/python/Tools/scripts/fixdiv.py+380−0
@@ -0,0 +1,380 @@
1#! /usr/bin/env python
2
3"""fixdiv - tool to fix division operators.
4
5To use this tool, first run `python -Qwarnall yourscript.py 2>warnings'.
6This runs the script `yourscript.py' while writing warning messages
7about 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
12The warnings are written to stderr, so you must use `2>' for the I/O
13redirect. I know of no way to redirect stderr on Windows in a DOS
14box, so you will have to modify the script to set sys.stderr to some
15kind of log file if you want to do this on Windows.
16
17The warnings are not limited to the script; modules imported by the
18script may also trigger warnings. In fact a useful technique is to
19write a test script specifically intended to exercise all code in a
20particular module or set of modules.
21
22Then run `python fixdiv.py warnings'. This first reads the warnings,
23looking for classic division warnings, and sorts them by file name and
24line number. Then, for each file that received at least one warning,
25it parses the file and tries to match the warnings up to the division
26operators found in the source code. If it is successful, it writes
27its findings to stdout, preceded by a line of dashes and a line of the
28form:
29
30 Index: <file>
31
32If the only findings found are suggestions to change a / operator into
33a // operator, the output is acceptable input for the Unix 'patch'
34program.
35
36Here 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
101Notes:
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
133import sys
134import getopt
135import re
136import tokenize
137
138multi_ok = 0
139
140def 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
172def 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
177PATTERN = ("^(.+?):(\d+): DeprecationWarning: "
178 "classic (int|long|float|complex) division$")
179
180def 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
205def 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
292def 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
307def 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
315class 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
359def 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
373def chop(line):
374 if line.endswith("\n"):
375 return line[:-1]
376 else:
377 return line
378
379if __name__ == "__main__":
380 sys.exit(main())
sys/src/cmd/python/Tools/scripts/fixheader.py+49−0
@@ -0,0 +1,49 @@
1#! /usr/bin/env python
2
3# Add some standard cpp magic to a header file
4
5import sys
6
7def main():
8 args = sys.argv[1:]
9 for filename in args:
10 process(filename)
11
12def 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 print
41 f.write(data)
42 print
43 print '#ifdef __cplusplus'
44 print '}'
45 print '#endif'
46 print '#endif /*', '!'+magic, '*/'
47
48if __name__ == '__main__':
49 main()
sys/src/cmd/python/Tools/scripts/fixnotice.py+113−0
@@ -0,0 +1,113 @@
1#! /usr/bin/env python
2
3"""(Ostensibly) fix copyright notices in files.
4
5Actually, this sript will simply replace a block of text in a file from one
6string to another. It will only do this once though, i.e. not globally
7throughout the file. It writes a backup file and then does an os.rename()
8dance for atomicity.
9
10Usage: fixnotices.py [options] [filenames]
11Options:
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
33OLD_NOTICE = """/***********************************************************
34Copyright (c) 2000, BeOpen.com.
35Copyright (c) 1995-2000, Corporation for National Research Initiatives.
36Copyright (c) 1990-1995, Stichting Mathematisch Centrum.
37All rights reserved.
38
39See the file "Misc/COPYRIGHT" for information on usage and
40redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
41******************************************************************/
42"""
43import os
44import sys
45import getopt
46
47NEW_NOTICE = ""
48DRYRUN = 0
49VERBOSE = 0
50
51
52def usage(code, msg=''):
53 print __doc__ % globals()
54 if msg:
55 print msg
56 sys.exit(code)
57
58
59def 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
88def 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
112if __name__ == '__main__':
113 main()
sys/src/cmd/python/Tools/scripts/fixps.py+33−0
@@ -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
6import sys
7import re
8
9
10def 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
32if __name__ == '__main__':
33 main()
sys/src/cmd/python/Tools/scripts/ftpmirror.py+400−0
@@ -0,0 +1,400 @@
1#! /usr/bin/env python
2
3"""Mirror a remote ftp subtree into a local directory tree.
4
5usage: 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
16hostname: remote host w/ optional port separated by ':'
17remotedir: remote directory (default initial)
18localdir: local directory (default current)
19"""
20
21import os
22import sys
23import time
24import getopt
25import ftplib
26import netrc
27from fnmatch import fnmatch
28
29# Print usage message and exit
30def usage(*args):
31 sys.stdout = sys.stderr
32 for msg in args: print msg
33 print __doc__
34 sys.exit(2)
35
36verbose = 1 # 0 for -q, 2 for -v
37interactive = 0
38mac = 0
39rmok = 0
40nologin = 0
41skippats = ['.', '..', '.mirrorinfo']
42
43# Main program: parse command line and start processing
44def 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)
105def 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 print
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
311def 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.
337class 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.
356def 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.
368def 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.
378def 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
399if __name__ == '__main__':
400 main()
sys/src/cmd/python/Tools/scripts/google.py+23−0
@@ -0,0 +1,23 @@
1#! /usr/bin/env python
2
3import sys, webbrowser
4
5def 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
22if __name__ == '__main__':
23 main()
sys/src/cmd/python/Tools/scripts/gprof2html.py+79−0
@@ -0,0 +1,79 @@
1#! /usr/bin/env python2.3
2
3"""Transform gprof(1) output into useful HTML."""
4
5import re, os, sys, cgi, webbrowser
6
7header = """\
8<html>
9<head>
10 <title>gprof output (%s)</title>
11</head>
12<body>
13<pre>
14"""
15
16trailer = """\
17</pre>
18</body>
19</html>
20"""
21
22def add_escapes(input):
23 for line in input:
24 yield cgi.escape(line)
25
26def 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+)(( &lt;cycle.*&gt;)? \[\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
78if __name__ == '__main__':
79 main()
sys/src/cmd/python/Tools/scripts/h2py.py+175−0
@@ -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
24import sys, re, getopt, os
25
26p_define = re.compile('^[\t ]*#[\t ]*define[\t ]+([a-zA-Z0-9_]+)[\t ]+')
27
28p_macro = re.compile(
29 '^[\t ]*#[\t ]*define[\t ]+'
30 '([a-zA-Z0-9_]+)\(([_a-zA-Z][_a-zA-Z0-9]*)\)[\t ]+')
31
32p_include = re.compile('^[\t ]*#[\t ]*include[\t ]+<([a-zA-Z0-9_/\.]+)')
33
34p_comment = re.compile(r'/\*([^*]+|\*+[^/])*(\*+/)?')
35p_cpp_comment = re.compile('//.*')
36
37ignores = [p_comment, p_cpp_comment]
38
39p_char = re.compile(r"'(\\.[^\\]*|[^\\])'")
40
41p_hex = re.compile(r"0x([0-9a-fA-F]+)L?")
42
43filedict = {}
44importable = {}
45
46try:
47 searchdirs=os.environ['include'].split(';')
48except 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
62def 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
93def 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
113def 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
174if __name__ == '__main__':
175 main()
sys/src/cmd/python/Tools/scripts/hotshotmain.py+55−0
@@ -0,0 +1,55 @@
1#!/usr/bin/env python
2# -*- coding: iso-8859-1 -*-
3
4"""
5Run a Python script under hotshot's control.
6
7Adapted from a posting on python-dev by Walter D�rwald
8
9usage %prog [ %prog args ] filename [ filename args ]
10
11Any arguments after the filename are used as sys.argv for the filename.
12"""
13
14import sys
15import optparse
16import os
17import hotshot
18import hotshot.stats
19
20PROFILE = "hotshot.prof"
21
22def 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
40def 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
54if __name__ == "__main__":
55 sys.exit(main(sys.argv[1:]))
sys/src/cmd/python/Tools/scripts/idle+5−0
@@ -0,0 +1,5 @@
1#! /usr/bin/env python
2
3from idlelib.PyShell import main
4if __name__ == '__main__':
5 main()
sys/src/cmd/python/Tools/scripts/ifdef.py+112−0
@@ -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
29import sys
30import getopt
31
32defs = []
33undefs = []
34
35def 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
52def 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
111if __name__ == '__main__':
112 main()
sys/src/cmd/python/Tools/scripts/lfcr.py+24−0
@@ -0,0 +1,24 @@
1#! /usr/bin/env python
2
3"Replace LF with CRLF in argument files. Print names of changed files."
4
5import sys, re, os
6
7def 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
23if __name__ == '__main__':
24 main()
sys/src/cmd/python/Tools/scripts/linktree.py+80−0
@@ -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
13import sys, os
14
15LINK = '.LINK' # Name of special symlink at the top.
16
17debug = 0
18
19def 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
50def 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
79if __name__ == '__main__':
80 sys.exit(main())
sys/src/cmd/python/Tools/scripts/lll.py+28−0
@@ -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
8import sys, os
9
10def 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)
16def 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
27if __name__ == '__main__':
28 main()
sys/src/cmd/python/Tools/scripts/logmerge.py+185−0
@@ -0,0 +1,185 @@
1#! /usr/bin/env python
2
3"""Consolidate a bunch of CVS or RCS logs read from stdin.
4
5Input should be the output of a CVS or RCS logging command, e.g.
6
7 cvs log -rrelease14:
8
9which dumps all log messages from release1.4 upwards (assuming that
10release 1.4 was tagged with tag 'release14'). Note the trailing
11colon!
12
13This collects all the revision records and outputs them sorted by date
14rather than by file, collapsing duplicate revision record, i.e.,
15records with the same message for different files.
16
17The -t option causes it to truncate (discard) the last revision log
18entry; this is useful when using something like the above cvs log
19command, which shows the revisions including the given tag, while you
20probably want everything *since* that tag.
21
22The -r option reverses the output (oldest first; the default is oldest
23last).
24
25The -b tag option restricts the output to *only* checkin messages
26belonging to the given branch tag. The form -b HEAD restricts the
27output to checkin messages belonging to the CVS head (trunk). (It
28produces some output if tag is a non-branch tag, but this output is
29not very useful.)
30
31-h prints this message and exits.
32
33XXX This code was created by reverse engineering CVS 1.9 and RCS 5.7
34from their output.
35"""
36
37import os, sys, errno, getopt, re
38
39sep1 = '='*77 + '\n' # file separator
40sep2 = '-'*28 + '\n' # revision separator
41
42def 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
72def 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
96def 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
165def 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
180if __name__ == '__main__':
181 try:
182 main()
183 except IOError, e:
184 if e.errno != errno.EPIPE:
185 raise
sys/src/cmd/python/Tools/scripts/mailerdaemon.py+237−0
@@ -0,0 +1,237 @@
1"""mailerdaemon - classes to parse mailer-daemon messages"""
2
3import rfc822
4import calendar
5import re
6import os
7import sys
8
9Unparseable = 'mailerdaemon.Unparseable'
10
11class 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.
46emparse_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.
65for 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
77del 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.
83emparse_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 ]
90emparse_list_from = re.compile('^From:', re.IGNORECASE|re.MULTILINE)
91def 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
148EMPARSERS = [emparse_list, ]
149
150def 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
157def 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
225def 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
236if __name__ == '__main__' or sys.argv[0] == __name__:
237 main()
sys/src/cmd/python/Tools/scripts/md5sum.py+90−0
@@ -0,0 +1,90 @@
1#! /usr/bin/env python
2
3"""Python utility to print MD5 checksums of argument files.
4"""
5
6
7bufsize = 8096
8fnfilter = None
9rmode = 'rb'
10
11usage = """
12usage: 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)
17file ... : files to sum; '-' or no files means stdin
18""" % bufsize
19
20import sys
21import os
22import getopt
23import md5
24
25def 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
43def 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
55def 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
69def 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
89if __name__ == '__main__' or __name__ == sys.argv[0]:
90 sys.exit(main(sys.argv[1:], sys.stdout))
sys/src/cmd/python/Tools/scripts/methfix.py+171−0
@@ -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
29import sys
30import re
31import os
32from stat import *
33
34err = sys.stderr.write
35dbg = err
36rep = sys.stdout.write
37
38def 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
53ispythonprog = re.compile('^[a-zA-Z0-9_]+\.py$')
54def ispython(name):
55 return ispythonprog.match(name) >= 0
56
57def 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
79def 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
161fixpat = '^[ \t]+def +[a-zA-Z0-9_]+ *( *self *, *(( *(.*) *)) *) *:'
162fixprog = re.compile(fixpat)
163
164def 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
170if __name__ == '__main__':
171 main()
sys/src/cmd/python/Tools/scripts/mkreal.py+66−0
@@ -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
7import sys
8import os
9from stat import *
10
11join = os.path.join
12
13error = 'mkreal error'
14
15BUFSIZE = 32*1024
16
17def 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
31def 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
45def 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
65if __name__ == '__main__':
66 main()
sys/src/cmd/python/Tools/scripts/ndiff.py+133−0
@@ -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
16ndiff (-r1 | -r2) < ndiff_output > file1_or_file2
17
18Print a human-friendly file difference report to stdout. Both inter-
19and 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
22In the first form, if -q ("quiet") is not specified, the first two lines
23of output are
24
25-: file1
26+: file2
27
28Each 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
35Lines beginning with "? " attempt to guide the eye to intraline
36differences, and were not present in either input file. These lines can be
37confusing if the source files contain tab characters.
38
39The 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
42The 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
44recovered by piping the output through
45
46 sed -n '/^[+ ] /s/^..//p'
47"""
48
49__version__ = 1, 7, 0
50
51import difflib, sys
52
53def 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
61def 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
68def 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
84def 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
119def restore(which):
120 restored = difflib.restore(sys.stdin.readlines(), which)
121 sys.stdout.writelines(restored)
122
123if __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)
sys/src/cmd/python/Tools/scripts/nm2def.py+103−0
@@ -0,0 +1,103 @@
1#! /usr/bin/env python
2"""nm2def.py
3
4Helpers to extract symbols from Unix libs and auto-generate
5Windows definition files from them. Depends on nm(1). Tested
6on Linux and Solaris only (-p option to nm is for Solaris only).
7
8By Marc-Andre Lemburg, Aug 1998.
9
10Additional notes: the output of nm is supposed to look like this:
11
12acceler.o:
13000001fd T PyGrammar_AddAccelerators
14 U PyGrammar_FindDFA
1500000237 T PyGrammar_RemoveAccelerators
16 U _IO_stderr_
17 U exit
18 U fprintf
19 U free
20 U malloc
21 U printf
22
23grammar1.o:
2400000000 T PyGrammar_FindDFA
2500000034 T PyGrammar_LabelRepr
26 U _PyParser_TokenNames
27 U abort
28 U printf
29 U sprintf
30
31...
32
33Even if this isn't the default output of your nm, there is generally an
34option to produce this format (since it is the original v7 Unix format).
35
36"""
37import os,re,sys
38
39PYTHONLIB = 'libpython'+sys.version[:3]+'.a'
40PC_PYTHONLIB = 'Python'+sys.version[0]+sys.version[2]+'.dll'
41NM = 'nm -p -g %s' # For Linux, use "nm -g %s"
42
43def 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
60def 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
75DEF_TEMPLATE = """\
76EXPORTS
77%s
78"""
79
80# Special symbols that have to be included even though they don't
81# pass the filter
82SPECIALS = (
83 )
84
85def 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
93def 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
102if __name__ == '__main__':
103 main()
sys/src/cmd/python/Tools/scripts/objgraph.py+215−0
@@ -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
22import sys
23import os
24import getopt
25import re
26
27# Types of symbols.
28#
29definitions = 'TRGDSBAEC'
30externals = 'UV'
31ignore = 'Nntrgdsbavuc'
32
33# Regular expression to parse "nm -o" output.
34#
35matcher = 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#
41def 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#
50def 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#
58file2undef = {}
59def2file = {}
60file2def = {}
61undef2file = {}
62
63# Read one input file and merge the data into the tables.
64# Argument is an open file.
65#
66def 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#
88def 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#
107def 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#
128def 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#
145def 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#
158def 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#
211if __name__ == '__main__':
212 try:
213 sys.exit(main())
214 except KeyboardInterrupt:
215 sys.exit(1)
sys/src/cmd/python/Tools/scripts/parseentities.py+64−0
@@ -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"""
15import re,sys
16import TextTools
17
18entityRE = re.compile('<!ENTITY +(\w+) +CDATA +"([^"]+)" +-- +((?:.|\n)+?) *-->')
19
20def 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
35def 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
53if __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)
sys/src/cmd/python/Tools/scripts/pathfix.py+149−0
@@ -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
22import sys
23import re
24import os
25from stat import *
26import getopt
27
28err = sys.stderr.write
29dbg = err
30rep = sys.stdout.write
31
32new_interpreter = None
33
34def 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
62ispythonprog = re.compile('^[a-zA-Z0-9_]+\.py$')
63def ispython(name):
64 return ispythonprog.match(name) >= 0
65
66def 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
88def 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
141def 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
148if __name__ == '__main__':
149 main()
sys/src/cmd/python/Tools/scripts/pdeps.py+167−0
@@ -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
23import sys
24import re
25import os
26
27
28# Main program
29#
30def 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#
60m_import = re.compile('^[ \t]*from[ \t]+([^ \t]+)[ \t]+')
61m_from = re.compile('^[ \t]*import[ \t]+([^#]+)')
62
63
64# Collect data from one file
65#
66def 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#
94def 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#
123def 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#
137def 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#
146def 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 print
160
161
162# Call main and honor exit status
163if __name__ == '__main__':
164 try:
165 sys.exit(main())
166 except KeyboardInterrupt:
167 sys.exit(1)
sys/src/cmd/python/Tools/scripts/pickle2db.py+147−0
@@ -0,0 +1,147 @@
1#!/usr/bin/env python
2
3"""
4Synopsis: %(prog)s [-h|-b|-g|-r|-a|-d] [ picklefile ] dbfile
5
6Read the given picklefile as a series of key/value pairs and write to a new
7database. If the database already exists, any contents are deleted. The
8optional 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
17The default is hash. If a pickle file is named it is opened for read
18access. If no pickle file is named, the pickle input is read from standard
19input.
20
21Note that recno databases can only contain integer keys, so you can't dump a
22hash or btree database using db2pickle.py and reconstitute it to a recno
23database with %(prog)s unless your keys are integers.
24
25"""
26
27import getopt
28try:
29 import bsddb
30except ImportError:
31 bsddb = None
32try:
33 import dbm
34except ImportError:
35 dbm = None
36try:
37 import gdbm
38except ImportError:
39 gdbm = None
40try:
41 import anydbm
42except ImportError:
43 anydbm = None
44import sys
45try:
46 import cPickle as pickle
47except ImportError:
48 import pickle
49
50prog = sys.argv[0]
51
52def usage():
53 sys.stderr.write(__doc__ % globals())
54
55def 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
146if __name__ == "__main__":
147 sys.exit(main(sys.argv[1:]))
sys/src/cmd/python/Tools/scripts/pindent.py+543−0
@@ -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
80STEPSIZE = 8
81TABSIZE = 8
82EXPANDTABS = 0
83
84import os
85import re
86import sys
87
88next = {}
89next['if'] = next['elif'] = 'elif', 'else', 'end'
90next['while'] = next['for'] = 'else', 'end'
91next['try'] = 'except', 'finally'
92next['except'] = 'except', 'else', 'end'
93next['else'] = next['finally'] = next['def'] = next['class'] = 'end'
94next['end'] = ()
95start = 'if', 'while', 'for', 'try', 'def', 'class'
96
97class 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
338def 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
344def 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
350def 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
356class 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
388class 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
400def 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
408def 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
416def 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
424def 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
439def 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
454def 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
471usage = """
472usage: 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~
480If no files are specified or a single - is given,
481the program acts as a filter (reads stdin, writes stdout).
482""" % vars()
483
484def 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
490def 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
541if __name__ == '__main__':
542 test()
543# end if
sys/src/cmd/python/Tools/scripts/ptags.py+53−0
@@ -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
13import sys, re, os
14
15tags = [] # Modified global variable!
16
17def 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
27expr = '^[ \t]*(def|class)[ \t]+([a-zA-Z0-9_]+)[ \t]*[:\(]'
28matcher = re.compile(expr)
29
30def 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
52if __name__ == '__main__':
53 main()
sys/src/cmd/python/Tools/scripts/pydoc+5−0
@@ -0,0 +1,5 @@
1#!/usr/bin/env python
2
3import pydoc
4if __name__ == '__main__':
5 pydoc.cli()
sys/src/cmd/python/Tools/scripts/pydocgui.pyw+7−0
@@ -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).
4import pydoc
5
6if __name__ == '__main__':
7 pydoc.gui()
sys/src/cmd/python/Tools/scripts/pysource.py+130−0
@@ -0,0 +1,130 @@
1#!/usr/bin/env python
2
3"""\
4List python source files.
5
6There are three functions to check whether a file is a Python source, listed
7here 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
14The file also must be of appropriate size - not bigger than a megabyte.
15
16walk_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
23import sys, os, re
24
25binary_re = re.compile('[\x00-\x08\x0E-\x1F\x7F]')
26
27debug = False
28
29def print_debug(msg):
30 if debug: print msg
31
32
33def _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
50def has_python_ext(fullpath):
51 return fullpath.endswith(".py") or fullpath.endswith(".pyw")
52
53def 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
74def 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
91def 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
124if __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
sys/src/cmd/python/Tools/scripts/redemo.py+171−0
@@ -0,0 +1,171 @@
1"""Basic regular expression demostration facility (Perl style syntax)."""
2
3from Tkinter import *
4import re
5
6class 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
164def main():
165 root = Tk()
166 demo = ReDemo(root)
167 root.protocol('WM_DELETE_WINDOW', root.quit)
168 root.mainloop()
169
170if __name__ == '__main__':
171 main()
sys/src/cmd/python/Tools/scripts/reindent.py+293−0
@@ -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
12Change Python (.py) files to use 4-space indents and no hard tab characters.
13Also trim excess spaces and tabs from ends of lines, and remove empty lines
14at the end of files. Also ensure the last line ends with a newline.
15
16If no paths are given on the command line, reindent operates as a filter,
17reading a single source file from standard input and writing the transformed
18source to standard output. In this case, the -d, -r and -v flags are
19ignored.
20
21You can pass one or more file and/or directory paths. When a directory
22path, all .py files within the directory will be examined, and, if the -r
23option is given, likewise recursively for subdirectories.
24
25If output is not to standard output, reindent overwrites files in place,
26renaming the originals with a .bak extension. If it finds nothing to
27change, the file is left alone. If reindent does change a file, the changed
28file is a fixed-point for future runs (i.e., running reindent on the
29resulting .py file won't change it again).
30
31The hard part of reindenting is figuring out what to do with comment
32lines. So long as the input files get a clean bill of health from
33tabnanny.py, reindent should do a good job.
34"""
35
36__version__ = "1"
37
38import tokenize
39import os
40import sys
41
42verbose = 0
43recurse = 0
44dryrun = 0
45
46def usage(msg=None):
47 if msg is not None:
48 print >> sys.stderr, msg
49 print >> sys.stderr, __doc__
50
51def errprint(*args):
52 sep = ""
53 for arg in args:
54 sys.stderr.write(sep + str(arg))
55 sep = " "
56 sys.stderr.write("\n")
57
58def 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
85def 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
129def _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
142class 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.
286def getlspace(line):
287 i, n = 0, len(line)
288 while i < n and line[i] == " ":
289 i += 1
290 return i
291
292if __name__ == '__main__':
293 main()
sys/src/cmd/python/Tools/scripts/rgrep.py+64−0
@@ -0,0 +1,64 @@
1#! /usr/bin/env python
2
3"""Reverse grep.
4
5Usage: rgrep [-i] pattern file
6"""
7
8import sys
9import re
10import getopt
11
12def 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
57def usage(msg, code=2):
58 sys.stdout = sys.stderr
59 print msg
60 print __doc__
61 sys.exit(code)
62
63if __name__ == '__main__':
64 main()
sys/src/cmd/python/Tools/scripts/setup.py+19−0
@@ -0,0 +1,19 @@
1from distutils.core import setup
2
3if __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 )
sys/src/cmd/python/Tools/scripts/suff.py+30−0
@@ -0,0 +1,30 @@
1#! /usr/bin/env python
2
3# suff
4#
5# show different suffixes amongst arguments
6
7import sys
8
9def 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
22def getsuffix(filename):
23 suff = ''
24 for i in range(len(filename)):
25 if filename[i] == '.':
26 suff = filename[i:]
27 return suff
28
29if __name__ == '__main__':
30 main()
sys/src/cmd/python/Tools/scripts/svneol.py+77−0
@@ -0,0 +1,77 @@
1#! /usr/bin/env python
2
3"""
4SVN helper script.
5
6Try 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
9Files with the svn:eol-style property already set (to anything) are skipped.
10
11svn will itself refuse to set this property on a file that's not under SVN
12control, or that has a binary mime-type property set. This script inherits
13that behavior, and passes on whatever warning message the failing "svn
14propset" command produces.
15
16In the Python project, it's safe to invoke this script from the root of
17a checkout.
18
19No output is produced for files that are ignored. For a file that gets
20svn:eol-style set, output looks like:
21
22 property 'svn:eol-style' set on 'Lib\ctypes\__init__.py'
23
24For a file not under version control:
25
26 svn: warning: 'patch-finalizer.txt' is not under version control
27
28and 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
33import re
34import os
35
36def 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
68possible_text_file = re.compile(r"\.([hc]|py|txt|sln|vcproj)$").search
69
70for 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)
sys/src/cmd/python/Tools/scripts/texcheck.py+233−0
@@ -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
6Designed 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
13Sample command line usage:
14 python texcheck.py -k chapterheading -m lib/librandomtex *.tex
15
16Options:
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
25import re
26import sys
27import getopt
28from itertools import izip, count, islice
29import glob
30
31cmdstr = 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
63def 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
74def 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
196def 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
232if __name__ == '__main__':
233 sys.exit(main())
sys/src/cmd/python/Tools/scripts/texi2html.py+2078−0
@@ -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
66import os
67import sys
68import string
69import re
70
71MAGIC = '\\input texinfo'
72
73cmprog = re.compile('^@([a-z]+)([ \t]|$)') # Command (line-oriented)
74blprog = re.compile('^[ \t]*$') # Blank line
75kwprog = re.compile('@[a-z]+') # Keyword (embedded, usually
76 # with {} args)
77spprog = re.compile('[\n@{}&<>]') # Special characters in
78 # running text
79 #
80 # menu item (Yuck!)
81miprog = 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
90class 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
180class 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
191class TexinfoParser:
192
193 COPYRIGHT_SYMBOL = "&copy;"
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 print
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('&lt;')
452 continue
453 if c == '>':
454 self.write('&gt;')
455 continue
456 if c == '&':
457 self.write('&amp;')
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{} &#161; upside-down !
617 # @questiondown{} &#191; upside-down ?
618 # @aa{},@AA{} &#229;,&#197; a,A with circle
619 # @ae{},@AE{} &#230;,&#198; 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{} &#248;,&#216; O,o with slash
624 # @oe{},@OE{} oe,OE oe,OE ligatures
625 # @ss{} &#223; 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('&#161;') # upside-down !
631 def close_exclamdown(self): pass
632 def open_questiondown(self): self.write('&#191;') # upside-down ?
633 def close_questiondown(self): pass
634 def open_aa(self): self.write('&#229;') # a with circle
635 def close_aa(self): pass
636 def open_AA(self): self.write('&#197;') # A with circle
637 def close_AA(self): pass
638 def open_ae(self): self.write('&#230;') # ae ligatures
639 def close_ae(self): pass
640 def open_AE(self): self.write('&#198;') # AE ligatures
641 def close_AE(self): pass
642 def open_o(self): self.write('&#248;') # o with slash
643 def close_o(self): pass
644 def open_O(self): self.write('&#216;') # O with slash
645 def close_O(self): pass
646 def open_ss(self): self.write('&#223;') # 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('=&gt;')
660 def close_result(self): pass
661
662 def open_expansion(self): self.write('==&gt;')
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--&gt;')
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
1639class TexinfoParserHTML3(TexinfoParser):
1640
1641 COPYRIGHT_SYMBOL = "&copy;"
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
1684class 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&reg; 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&reg; 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
1939def makevar(str):
1940 return '@var{'+str+'}'
1941
1942
1943# Split a string in "words" according to findwordend
1944def 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 @@ @{ @}
1959fwprog = re.compile('[@{} ]')
1960def 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
1976def makefile(nodename):
1977 nodename = nodename.strip()
1978 return fixfunnychars(nodename) + '.html'
1979
1980
1981# Characters that are perfectly safe in filenames and hyperlinks
1982goodchars = 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
1988def 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
2000def 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
2020def 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
2077if __name__ == "__main__":
2078 test()
sys/src/cmd/python/Tools/scripts/treesync.py+205−0
@@ -0,0 +1,205 @@
1#! /usr/bin/env python
2
3"""Script to synchronize two source trees.
4
5Invoke with two arguments:
6
7python treesync.py slave master
8
9The assumption is that "master" contains CVS administration while
10slave doesn't. All files in the slave tree that have a CVS/Entries
11entry 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
26import os, sys, stat, getopt
27
28# Interactivity options
29default_answer = "ask"
30create_files = "yes"
31create_directories = "no"
32write_slave = "ask"
33write_master = "ask"
34
35def 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
63def 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
109def 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
155BUFSIZE = 16*1024
156
157def 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
165def mtime(f):
166 st = os.fstat(f.fileno())
167 return st[stat.ST_MTIME]
168
169def 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
176def 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
190def 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
204if __name__ == '__main__':
205 main()
sys/src/cmd/python/Tools/scripts/untabify.py+52−0
@@ -0,0 +1,52 @@
1#! /usr/bin/env python
2
3"Replace tabs with spaces in argument files. Print names of changed files."
4
5import os
6import sys
7import getopt
8
9def 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
26def 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
51if __name__ == '__main__':
52 main()
sys/src/cmd/python/Tools/scripts/which.py+60−0
@@ -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
7import sys
8if sys.path[0] in (".", ""): del sys.path[0]
9
10import sys, os
11from stat import *
12
13def msg(str):
14 sys.stderr.write(str + '\n')
15
16def 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
59if __name__ == '__main__':
60 main()
sys/src/cmd/python/Tools/scripts/xxci.py+117−0
@@ -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
7import sys
8import os
9from stat import *
10import commands
11import fnmatch
12
13EXECMAGIC = '\001\140\000\010'
14
15MAXSIZE = 200*1024 # Files this big must be binaries and are skipped.
16
17def 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
35def getmtime(file):
36 try:
37 st = os.stat(file)
38 return st[ST_MTIME]
39 except os.error:
40 return -1
41
42badnames = ['tags', 'TAGS', 'xyzzy', 'nohup.out', 'core']
43badprefixes = ['.', ',', '@', '#', 'o.']
44badsuffixes = \
45 ['~', '.a', '.o', '.old', '.bak', '.orig', '.new', '.prev', '.not', \
46 '.pyc', '.fdc', '.rgb', '.elc', ',v']
47ignore = []
48
49def 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
61def 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
80def badprefix(file):
81 for bad in badprefixes:
82 if file[:len(bad)] == bad: return 1
83 return 0
84
85def badsuffix(file):
86 for bad in badsuffixes:
87 if file[-len(bad):] == bad: return 1
88 return 0
89
90def 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
99def differing(file):
100 cmd = 'co -p ' + file + ' 2>/dev/null | cmp -s - ' + file
101 sts = os.system(cmd)
102 return sts != 0
103
104def showdiffs(file):
105 cmd = 'rcsdiff ' + file + ' 2>&1 | ${PAGER-more}'
106 sts = os.system(cmd)
107
108def askyesno(prompt):
109 s = raw_input(prompt)
110 return s in ['y', 'yes']
111
112if __name__ == '__main__':
113 try:
114 setup()
115 go(getargs())
116 except KeyboardInterrupt:
117 print '[Intr]'