1#! /usr/bin/env python3
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 as 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 as 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), end=' ')
68    fp.close()
69
70def processdir(dir, listnames):
71    try:
72        names = os.listdir(dir)
73    except OSError as 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(key=os.path.normcase)
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())
90