Script to help identify undocumented modules.  Use -h or --help for
usage information.
diff --git a/Doc/tools/listmodules b/Doc/tools/listmodules
new file mode 100755
index 0000000..b000199
--- /dev/null
+++ b/Doc/tools/listmodules
@@ -0,0 +1,159 @@
+#! /usr/bin/env python
+#  -*- Python -*-
+#
+#  This script can be used to identify undocumented modules in the Python
+#  standard library.  Use it like this:
+#
+#  .../Doc/tools/listmodules --ignore-from .../Doc/paper-<paper>/modlib.idx
+
+"""%(program)s - list modules in the Python standard library
+
+-a, --annotate	  Annotate the module names with the subdirectory they live in
+-c, --categorize  Group the modules by subdirectory
+-i <file>,
+--ignore-from <file>  Ignore the modules listed in <file>.  <file> may contain
+		  a list of module names or a module index file as produced
+                  when formatting the Python documentation (.idx flavor).
+
+If neither -a nor -c are given, the modules are listed in alphabetical order.
+
+Note that -a and -c are mutually exclusive.
+
+Limitation:  Modules loadable as shared objects are not listed.
+"""
+
+__version__ = '$Revision$'
+
+import getopt
+import os
+import re
+import string
+import sys
+
+
+REMOVE_DIRS = ["dos-8x3", "lib-old", "lib-stdwin", "test"]
+
+
+def main():
+    args = sys.argv[1:]
+    annotate = 0
+    builtin = 0
+    categorize = 0
+    ignore_dict = {}
+    ignore = ignore_dict.has_key
+    try:
+        opts, args = getopt.getopt(
+            args, "abchi:",
+            ["annotate", "built-in", "categorize", "help", "ignore-from="])
+    except getopt.error, msg:
+        sys.stdout = sys.stderr
+        print msg
+        print
+        usage()
+        sys.exit(2)
+    for opt, arg in opts:
+        if opt in ("-a", "--annotate"):
+            annotate = 1
+        elif opt in ("-b", "--built-in"):
+            builtin = 1
+        elif opt in ("-c", "--categorize"):
+            categorize = 1
+        elif opt in ("-h", "--help"):
+            usage()
+            sys.exit()
+        elif opt in ("-i", "--ignore-from"):
+            data = open(arg).read()
+            if data[:1] == "\\":
+                ignore_from_idx(data, ignore_dict)
+            else:
+                ignore_from_filelist(data, ignore_dict)
+    if args or (annotate and categorize):
+        usage()
+        sys.exit(2)
+    #
+    # Populate the database:
+    #
+    srcdir = os.path.normpath(os.path.join(
+        os.path.dirname(sys.argv[0]), os.pardir, os.pardir))
+    os.chdir(srcdir)
+    fp = os.popen("find Lib -name \*.py -print", "r")
+    modules_by_name = {}
+    modules_by_dir = {}
+    if builtin:
+        l = []
+        modules_by_dir["<builtin>"] = l
+        for name in sys.builtin_module_names:
+            if not ignore(name):
+                modules_by_name[name] = "<built-in>"
+                l.append(name)
+    rx = re.compile("Lib/plat-[a-z0-9]*/", re.IGNORECASE)
+    while 1:
+        line = fp.readline()
+        if not line:
+            break
+        m = rx.match(line)
+        if m:
+            line = "Lib/plat-*/" + line[m.end():]
+        line = line[4:-4]                # strip off 'Lib/' and '.py\n'
+        dir, name = os.path.split(line)
+        dir = dir or "<standard>"
+        if ignore(name):
+            continue
+        if dir not in REMOVE_DIRS:
+            modules_by_name[name] = dir
+            l = modules_by_dir.get(dir, [])
+            modules_by_dir[dir] = l
+            if name not in l:
+                l.append(name)
+    #
+    # Dump the results:
+    #
+    if annotate:
+        modules = modules_by_name.items()
+        modules.sort()
+        width = max(map(len, modules_by_name.keys()))
+        format = "%%-%ds  %%s" % width
+        for name, dir in modules:
+            if dir and dir[0] != "<":
+                print format % (name, dir)
+            else:
+                print name
+    elif categorize:
+        modules = modules_by_dir.items()
+        modules.sort()
+        width = max(map(len, modules_by_dir.keys()))
+        format = "%%-%ds  %%s" % width
+        for dir, names in modules:
+            names.sort()
+            print format % (dir, names[0])
+            for name in names[1:]:
+                print format % ('', name)
+            print
+    else:
+        modules = modules_by_name.keys()
+        modules.sort()
+        print string.join(modules, "\n")
+
+
+def ignore_from_filelist(data, ignore_dict):
+    for name in string.split(data):
+        ignore_dict[name] = name
+
+def ignore_from_idx(data, ignore_dict):
+    data = string.replace(data, r"\hackscore  {}", "_")
+    rx = re.compile(r"\indexentry {([^@]*)@")
+    for line in string.split(data, "\n"):
+        m = rx.match(line)
+        if m:
+            name = m.group(1)
+            ignore_dict[name] = name
+
+
+def usage():
+    vars = {}
+    vars["program"] = os.path.basename(sys.argv[0])
+    print __doc__ % vars
+
+
+if __name__ == "__main__":
+    main()