##// END OF EJS Templates
osutil: fix up check-code issues
Matt Mackall -
r13736:f3c4421e default
parent child Browse files
Show More
@@ -1,308 +1,308
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 #
2 #
3 # check-code - a style and portability checker for Mercurial
3 # check-code - a style and portability checker for Mercurial
4 #
4 #
5 # Copyright 2010 Matt Mackall <mpm@selenic.com>
5 # Copyright 2010 Matt Mackall <mpm@selenic.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 import re, glob, os, sys
10 import re, glob, os, sys
11 import keyword
11 import keyword
12 import optparse
12 import optparse
13
13
14 def repquote(m):
14 def repquote(m):
15 t = re.sub(r"\w", "x", m.group('text'))
15 t = re.sub(r"\w", "x", m.group('text'))
16 t = re.sub(r"[^\sx]", "o", t)
16 t = re.sub(r"[^\sx]", "o", t)
17 return m.group('quote') + t + m.group('quote')
17 return m.group('quote') + t + m.group('quote')
18
18
19 def reppython(m):
19 def reppython(m):
20 comment = m.group('comment')
20 comment = m.group('comment')
21 if comment:
21 if comment:
22 return "#" * len(comment)
22 return "#" * len(comment)
23 return repquote(m)
23 return repquote(m)
24
24
25 def repcomment(m):
25 def repcomment(m):
26 return m.group(1) + "#" * len(m.group(2))
26 return m.group(1) + "#" * len(m.group(2))
27
27
28 def repccomment(m):
28 def repccomment(m):
29 t = re.sub(r"((?<=\n) )|\S", "x", m.group(2))
29 t = re.sub(r"((?<=\n) )|\S", "x", m.group(2))
30 return m.group(1) + t + "*/"
30 return m.group(1) + t + "*/"
31
31
32 def repcallspaces(m):
32 def repcallspaces(m):
33 t = re.sub(r"\n\s+", "\n", m.group(2))
33 t = re.sub(r"\n\s+", "\n", m.group(2))
34 return m.group(1) + t
34 return m.group(1) + t
35
35
36 def repinclude(m):
36 def repinclude(m):
37 return m.group(1) + "<foo>"
37 return m.group(1) + "<foo>"
38
38
39 def rephere(m):
39 def rephere(m):
40 t = re.sub(r"\S", "x", m.group(2))
40 t = re.sub(r"\S", "x", m.group(2))
41 return m.group(1) + t
41 return m.group(1) + t
42
42
43
43
44 testpats = [
44 testpats = [
45 (r'(pushd|popd)', "don't use 'pushd' or 'popd', use 'cd'"),
45 (r'(pushd|popd)', "don't use 'pushd' or 'popd', use 'cd'"),
46 (r'\W\$?\(\([^\)]*\)\)', "don't use (()) or $(()), use 'expr'"),
46 (r'\W\$?\(\([^\)]*\)\)', "don't use (()) or $(()), use 'expr'"),
47 (r'^function', "don't use 'function', use old style"),
47 (r'^function', "don't use 'function', use old style"),
48 (r'grep.*-q', "don't use 'grep -q', redirect to /dev/null"),
48 (r'grep.*-q', "don't use 'grep -q', redirect to /dev/null"),
49 (r'echo.*\\n', "don't use 'echo \\n', use printf"),
49 (r'echo.*\\n', "don't use 'echo \\n', use printf"),
50 (r'echo -n', "don't use 'echo -n', use printf"),
50 (r'echo -n', "don't use 'echo -n', use printf"),
51 (r'^diff.*-\w*N', "don't use 'diff -N'"),
51 (r'^diff.*-\w*N', "don't use 'diff -N'"),
52 (r'(^| )wc[^|]*$', "filter wc output"),
52 (r'(^| )wc[^|]*$', "filter wc output"),
53 (r'head -c', "don't use 'head -c', use 'dd'"),
53 (r'head -c', "don't use 'head -c', use 'dd'"),
54 (r'ls.*-\w*R', "don't use 'ls -R', use 'find'"),
54 (r'ls.*-\w*R', "don't use 'ls -R', use 'find'"),
55 (r'printf.*\\\d\d\d', "don't use 'printf \NNN', use Python"),
55 (r'printf.*\\\d\d\d', "don't use 'printf \NNN', use Python"),
56 (r'printf.*\\x', "don't use printf \\x, use Python"),
56 (r'printf.*\\x', "don't use printf \\x, use Python"),
57 (r'\$\(.*\)', "don't use $(expr), use `expr`"),
57 (r'\$\(.*\)', "don't use $(expr), use `expr`"),
58 (r'rm -rf \*', "don't use naked rm -rf, target a directory"),
58 (r'rm -rf \*', "don't use naked rm -rf, target a directory"),
59 (r'(^|\|\s*)grep (-\w\s+)*[^|]*[(|]\w',
59 (r'(^|\|\s*)grep (-\w\s+)*[^|]*[(|]\w',
60 "use egrep for extended grep syntax"),
60 "use egrep for extended grep syntax"),
61 (r'/bin/', "don't use explicit paths for tools"),
61 (r'/bin/', "don't use explicit paths for tools"),
62 (r'\$PWD', "don't use $PWD, use `pwd`"),
62 (r'\$PWD', "don't use $PWD, use `pwd`"),
63 (r'[^\n]\Z', "no trailing newline"),
63 (r'[^\n]\Z', "no trailing newline"),
64 (r'export.*=', "don't export and assign at once"),
64 (r'export.*=', "don't export and assign at once"),
65 ('^([^"\']|("[^"]*")|(\'[^\']*\'))*\\^', "^ must be quoted"),
65 ('^([^"\']|("[^"]*")|(\'[^\']*\'))*\\^', "^ must be quoted"),
66 (r'^source\b', "don't use 'source', use '.'"),
66 (r'^source\b', "don't use 'source', use '.'"),
67 (r'touch -d', "don't use 'touch -d', use 'touch -t' instead"),
67 (r'touch -d', "don't use 'touch -d', use 'touch -t' instead"),
68 (r'ls\s+[^|-]+\s+-', "options to 'ls' must come before filenames"),
68 (r'ls\s+[^|-]+\s+-', "options to 'ls' must come before filenames"),
69 (r'[^>]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"),
69 (r'[^>]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"),
70 ]
70 ]
71
71
72 testfilters = [
72 testfilters = [
73 (r"( *)(#([^\n]*\S)?)", repcomment),
73 (r"( *)(#([^\n]*\S)?)", repcomment),
74 (r"<<(\S+)((.|\n)*?\n\1)", rephere),
74 (r"<<(\S+)((.|\n)*?\n\1)", rephere),
75 ]
75 ]
76
76
77 uprefix = r"^ \$ "
77 uprefix = r"^ \$ "
78 uprefixc = r"^ > "
78 uprefixc = r"^ > "
79 utestpats = [
79 utestpats = [
80 (r'^(\S| $ ).*(\S\s+|^\s+)\n', "trailing whitespace on non-output"),
80 (r'^(\S| $ ).*(\S\s+|^\s+)\n', "trailing whitespace on non-output"),
81 (uprefix + r'.*\|\s*sed', "use regex test output patterns instead of sed"),
81 (uprefix + r'.*\|\s*sed', "use regex test output patterns instead of sed"),
82 (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"),
82 (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"),
83 (uprefix + r'.*\$\?', "explicit exit code checks unnecessary"),
83 (uprefix + r'.*\$\?', "explicit exit code checks unnecessary"),
84 (uprefix + r'.*\|\| echo.*(fail|error)',
84 (uprefix + r'.*\|\| echo.*(fail|error)',
85 "explicit exit code checks unnecessary"),
85 "explicit exit code checks unnecessary"),
86 (uprefix + r'set -e', "don't use set -e"),
86 (uprefix + r'set -e', "don't use set -e"),
87 (uprefixc + r'( *)\t', "don't use tabs to indent"),
87 (uprefixc + r'( *)\t', "don't use tabs to indent"),
88 ]
88 ]
89
89
90 for p, m in testpats:
90 for p, m in testpats:
91 if p.startswith('^'):
91 if p.startswith('^'):
92 p = uprefix + p[1:]
92 p = uprefix + p[1:]
93 else:
93 else:
94 p = uprefix + p
94 p = uprefix + p
95 utestpats.append((p, m))
95 utestpats.append((p, m))
96
96
97 utestfilters = [
97 utestfilters = [
98 (r"( *)(#([^\n]*\S)?)", repcomment),
98 (r"( *)(#([^\n]*\S)?)", repcomment),
99 ]
99 ]
100
100
101 pypats = [
101 pypats = [
102 (r'^\s*def\s*\w+\s*\(.*,\s*\(',
102 (r'^\s*def\s*\w+\s*\(.*,\s*\(',
103 "tuple parameter unpacking not available in Python 3+"),
103 "tuple parameter unpacking not available in Python 3+"),
104 (r'lambda\s*\(.*,.*\)',
104 (r'lambda\s*\(.*,.*\)',
105 "tuple parameter unpacking not available in Python 3+"),
105 "tuple parameter unpacking not available in Python 3+"),
106 (r'(?<!def)\s+(cmp)\(', "cmp is not available in Python 3+"),
106 (r'(?<!def)\s+(cmp)\(', "cmp is not available in Python 3+"),
107 (r'\breduce\s*\(.*', "reduce is not available in Python 3+"),
107 (r'\breduce\s*\(.*', "reduce is not available in Python 3+"),
108 (r'\.has_key\b', "dict.has_key is not available in Python 3+"),
108 (r'\.has_key\b', "dict.has_key is not available in Python 3+"),
109 (r'^\s*\t', "don't use tabs"),
109 (r'^\s*\t', "don't use tabs"),
110 (r'\S;\s*\n', "semicolon"),
110 (r'\S;\s*\n', "semicolon"),
111 (r'\w,\w', "missing whitespace after ,"),
111 (r'\w,\w', "missing whitespace after ,"),
112 (r'\w[+/*\-<>]\w', "missing whitespace in expression"),
112 (r'\w[+/*\-<>]\w', "missing whitespace in expression"),
113 (r'^\s+\w+=\w+[^,)]$', "missing whitespace in assignment"),
113 (r'^\s+\w+=\w+[^,)]$', "missing whitespace in assignment"),
114 (r'.{85}', "line too long"),
114 (r'.{85}', "line too long"),
115 (r'.{81}', "warning: line over 80 characters"),
115 (r'.{81}', "warning: line over 80 characters"),
116 (r'[^\n]\Z', "no trailing newline"),
116 (r'[^\n]\Z', "no trailing newline"),
117 (r'(\S\s+|^\s+)\n', "trailing whitespace"),
117 (r'(\S\s+|^\s+)\n', "trailing whitespace"),
118 # (r'^\s+[^_ ][^_. ]+_[^_]+\s*=', "don't use underbars in identifiers"),
118 # (r'^\s+[^_ ][^_. ]+_[^_]+\s*=', "don't use underbars in identifiers"),
119 # (r'\w*[a-z][A-Z]\w*\s*=', "don't use camelcase in identifiers"),
119 # (r'\w*[a-z][A-Z]\w*\s*=', "don't use camelcase in identifiers"),
120 (r'^\s*(if|while|def|class|except|try)\s[^[]*:\s*[^\]#\s]+',
120 (r'^\s*(if|while|def|class|except|try)\s[^[]*:\s*[^\]#\s]+',
121 "linebreak after :"),
121 "linebreak after :"),
122 (r'class\s[^(]:', "old-style class, use class foo(object)"),
122 (r'class\s[^(]:', "old-style class, use class foo(object)"),
123 (r'\b(%s)\(' % '|'.join(keyword.kwlist),
123 (r'\b(%s)\(' % '|'.join(keyword.kwlist),
124 "Python keyword is not a function"),
124 "Python keyword is not a function"),
125 (r',]', "unneeded trailing ',' in list"),
125 (r',]', "unneeded trailing ',' in list"),
126 # (r'class\s[A-Z][^\(]*\((?!Exception)',
126 # (r'class\s[A-Z][^\(]*\((?!Exception)',
127 # "don't capitalize non-exception classes"),
127 # "don't capitalize non-exception classes"),
128 # (r'in range\(', "use xrange"),
128 # (r'in range\(', "use xrange"),
129 # (r'^\s*print\s+', "avoid using print in core and extensions"),
129 # (r'^\s*print\s+', "avoid using print in core and extensions"),
130 (r'[\x80-\xff]', "non-ASCII character literal"),
130 (r'[\x80-\xff]', "non-ASCII character literal"),
131 (r'("\')\.format\(', "str.format() not available in Python 2.4"),
131 (r'("\')\.format\(', "str.format() not available in Python 2.4"),
132 (r'^\s*with\s+', "with not available in Python 2.4"),
132 (r'^\s*with\s+', "with not available in Python 2.4"),
133 (r'^\s*except.* as .*:', "except as not available in Python 2.4"),
133 (r'^\s*except.* as .*:', "except as not available in Python 2.4"),
134 (r'^\s*os\.path\.relpath', "relpath not available in Python 2.4"),
134 (r'^\s*os\.path\.relpath', "relpath not available in Python 2.4"),
135 (r'(?<!def)\s+(any|all|format)\(',
135 (r'(?<!def)\s+(any|all|format)\(',
136 "any/all/format not available in Python 2.4"),
136 "any/all/format not available in Python 2.4"),
137 (r'(?<!def)\s+(callable)\(',
137 (r'(?<!def)\s+(callable)\(',
138 "callable not available in Python 3, use hasattr(f, '__call__')"),
138 "callable not available in Python 3, use hasattr(f, '__call__')"),
139 (r'if\s.*\selse', "if ... else form not available in Python 2.4"),
139 (r'if\s.*\selse', "if ... else form not available in Python 2.4"),
140 (r'^\s*(%s)\s\s' % '|'.join(keyword.kwlist),
140 (r'^\s*(%s)\s\s' % '|'.join(keyword.kwlist),
141 "gratuitous whitespace after Python keyword"),
141 "gratuitous whitespace after Python keyword"),
142 (r'([\(\[]\s\S)|(\S\s[\)\]])', "gratuitous whitespace in () or []"),
142 (r'([\(\[]\s\S)|(\S\s[\)\]])', "gratuitous whitespace in () or []"),
143 # (r'\s\s=', "gratuitous whitespace before ="),
143 # (r'\s\s=', "gratuitous whitespace before ="),
144 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\S',
144 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\S',
145 "missing whitespace around operator"),
145 "missing whitespace around operator"),
146 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\s',
146 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\s',
147 "missing whitespace around operator"),
147 "missing whitespace around operator"),
148 (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=)\S',
148 (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=)\S',
149 "missing whitespace around operator"),
149 "missing whitespace around operator"),
150 (r'[^+=*!<>&| -](\s=|=\s)[^= ]',
150 (r'[^+=*!<>&| -](\s=|=\s)[^= ]',
151 "wrong whitespace around ="),
151 "wrong whitespace around ="),
152 (r'raise Exception', "don't raise generic exceptions"),
152 (r'raise Exception', "don't raise generic exceptions"),
153 (r'ui\.(status|progress|write|note|warn)\([\'\"]x',
153 (r'ui\.(status|progress|write|note|warn)\([\'\"]x',
154 "warning: unwrapped ui message"),
154 "warning: unwrapped ui message"),
155 (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"),
155 (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"),
156 (r' [=!]=\s+(True|False|None)',
156 (r' [=!]=\s+(True|False|None)',
157 "comparison with singleton, use 'is' or 'is not' instead"),
157 "comparison with singleton, use 'is' or 'is not' instead"),
158 ]
158 ]
159
159
160 pyfilters = [
160 pyfilters = [
161 (r"""(?msx)(?P<comment>\#.*?$)|
161 (r"""(?msx)(?P<comment>\#.*?$)|
162 ((?P<quote>('''|\"\"\"|(?<!')'(?!')|(?<!")"(?!")))
162 ((?P<quote>('''|\"\"\"|(?<!')'(?!')|(?<!")"(?!")))
163 (?P<text>(([^\\]|\\.)*?))
163 (?P<text>(([^\\]|\\.)*?))
164 (?P=quote))""", reppython),
164 (?P=quote))""", reppython),
165 ]
165 ]
166
166
167 cpats = [
167 cpats = [
168 (r'//', "don't use //-style comments"),
168 (r'//', "don't use //-style comments"),
169 (r'^ ', "don't use spaces to indent"),
169 (r'^ ', "don't use spaces to indent"),
170 (r'\S\t', "don't use tabs except for indent"),
170 (r'\S\t', "don't use tabs except for indent"),
171 (r'(\S\s+|^\s+)\n', "trailing whitespace"),
171 (r'(\S\s+|^\s+)\n', "trailing whitespace"),
172 (r'.{85}', "line too long"),
172 (r'.{85}', "line too long"),
173 (r'(while|if|do|for)\(', "use space after while/if/do/for"),
173 (r'(while|if|do|for)\(', "use space after while/if/do/for"),
174 (r'return\(', "return is not a function"),
174 (r'return\(', "return is not a function"),
175 (r' ;', "no space before ;"),
175 (r' ;', "no space before ;"),
176 (r'\w+\* \w+', "use int *foo, not int* foo"),
176 (r'\w+\* \w+', "use int *foo, not int* foo"),
177 (r'\([^\)]+\) \w+', "use (int)foo, not (int) foo"),
177 (r'\([^\)]+\) \w+', "use (int)foo, not (int) foo"),
178 (r'\S+ (\+\+|--)', "use foo++, not foo ++"),
178 (r'\S+ (\+\+|--)', "use foo++, not foo ++"),
179 (r'\w,\w', "missing whitespace after ,"),
179 (r'\w,\w', "missing whitespace after ,"),
180 (r'\w[+/*]\w', "missing whitespace in expression"),
180 (r'^[^#]\w[+/*]\w', "missing whitespace in expression"),
181 (r'^#\s+\w', "use #foo, not # foo"),
181 (r'^#\s+\w', "use #foo, not # foo"),
182 (r'[^\n]\Z', "no trailing newline"),
182 (r'[^\n]\Z', "no trailing newline"),
183 ]
183 ]
184
184
185 cfilters = [
185 cfilters = [
186 (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment),
186 (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment),
187 (r'''(?P<quote>(?<!")")(?P<text>([^"]|\\")+)"(?!")''', repquote),
187 (r'''(?P<quote>(?<!")")(?P<text>([^"]|\\")+)"(?!")''', repquote),
188 (r'''(#\s*include\s+<)([^>]+)>''', repinclude),
188 (r'''(#\s*include\s+<)([^>]+)>''', repinclude),
189 (r'(\()([^)]+\))', repcallspaces),
189 (r'(\()([^)]+\))', repcallspaces),
190 ]
190 ]
191
191
192 checks = [
192 checks = [
193 ('python', r'.*\.(py|cgi)$', pyfilters, pypats),
193 ('python', r'.*\.(py|cgi)$', pyfilters, pypats),
194 ('test script', r'(.*/)?test-[^.~]*$', testfilters, testpats),
194 ('test script', r'(.*/)?test-[^.~]*$', testfilters, testpats),
195 ('c', r'.*\.c$', cfilters, cpats),
195 ('c', r'.*\.c$', cfilters, cpats),
196 ('unified test', r'.*\.t$', utestfilters, utestpats),
196 ('unified test', r'.*\.t$', utestfilters, utestpats),
197 ]
197 ]
198
198
199 class norepeatlogger(object):
199 class norepeatlogger(object):
200 def __init__(self):
200 def __init__(self):
201 self._lastseen = None
201 self._lastseen = None
202
202
203 def log(self, fname, lineno, line, msg, blame):
203 def log(self, fname, lineno, line, msg, blame):
204 """print error related a to given line of a given file.
204 """print error related a to given line of a given file.
205
205
206 The faulty line will also be printed but only once in the case
206 The faulty line will also be printed but only once in the case
207 of multiple errors.
207 of multiple errors.
208
208
209 :fname: filename
209 :fname: filename
210 :lineno: line number
210 :lineno: line number
211 :line: actual content of the line
211 :line: actual content of the line
212 :msg: error message
212 :msg: error message
213 """
213 """
214 msgid = fname, lineno, line
214 msgid = fname, lineno, line
215 if msgid != self._lastseen:
215 if msgid != self._lastseen:
216 if blame:
216 if blame:
217 print "%s:%d (%s):" % (fname, lineno, blame)
217 print "%s:%d (%s):" % (fname, lineno, blame)
218 else:
218 else:
219 print "%s:%d:" % (fname, lineno)
219 print "%s:%d:" % (fname, lineno)
220 print " > %s" % line
220 print " > %s" % line
221 self._lastseen = msgid
221 self._lastseen = msgid
222 print " " + msg
222 print " " + msg
223
223
224 _defaultlogger = norepeatlogger()
224 _defaultlogger = norepeatlogger()
225
225
226 def getblame(f):
226 def getblame(f):
227 lines = []
227 lines = []
228 for l in os.popen('hg annotate -un %s' % f):
228 for l in os.popen('hg annotate -un %s' % f):
229 start, line = l.split(':', 1)
229 start, line = l.split(':', 1)
230 user, rev = start.split()
230 user, rev = start.split()
231 lines.append((line[1:-1], user, rev))
231 lines.append((line[1:-1], user, rev))
232 return lines
232 return lines
233
233
234 def checkfile(f, logfunc=_defaultlogger.log, maxerr=None, warnings=False,
234 def checkfile(f, logfunc=_defaultlogger.log, maxerr=None, warnings=False,
235 blame=False):
235 blame=False):
236 """checks style and portability of a given file
236 """checks style and portability of a given file
237
237
238 :f: filepath
238 :f: filepath
239 :logfunc: function used to report error
239 :logfunc: function used to report error
240 logfunc(filename, linenumber, linecontent, errormessage)
240 logfunc(filename, linenumber, linecontent, errormessage)
241 :maxerr: number of error to display before arborting.
241 :maxerr: number of error to display before arborting.
242 Set to None (default) to report all errors
242 Set to None (default) to report all errors
243
243
244 return True if no error is found, False otherwise.
244 return True if no error is found, False otherwise.
245 """
245 """
246 blamecache = None
246 blamecache = None
247 result = True
247 result = True
248 for name, match, filters, pats in checks:
248 for name, match, filters, pats in checks:
249 fc = 0
249 fc = 0
250 if not re.match(match, f):
250 if not re.match(match, f):
251 continue
251 continue
252 fp = open(f)
252 fp = open(f)
253 pre = post = fp.read()
253 pre = post = fp.read()
254 fp.close()
254 fp.close()
255 if "no-" + "check-code" in pre:
255 if "no-" + "check-code" in pre:
256 break
256 break
257 for p, r in filters:
257 for p, r in filters:
258 post = re.sub(p, r, post)
258 post = re.sub(p, r, post)
259 # print post # uncomment to show filtered version
259 # print post # uncomment to show filtered version
260 z = enumerate(zip(pre.splitlines(), post.splitlines(True)))
260 z = enumerate(zip(pre.splitlines(), post.splitlines(True)))
261 for n, l in z:
261 for n, l in z:
262 if "check-code" + "-ignore" in l[0]:
262 if "check-code" + "-ignore" in l[0]:
263 continue
263 continue
264 for p, msg in pats:
264 for p, msg in pats:
265 if not warnings and msg.startswith("warning"):
265 if not warnings and msg.startswith("warning"):
266 continue
266 continue
267 if re.search(p, l[1]):
267 if re.search(p, l[1]):
268 bd = ""
268 bd = ""
269 if blame:
269 if blame:
270 bd = 'working directory'
270 bd = 'working directory'
271 if not blamecache:
271 if not blamecache:
272 blamecache = getblame(f)
272 blamecache = getblame(f)
273 if n < len(blamecache):
273 if n < len(blamecache):
274 bl, bu, br = blamecache[n]
274 bl, bu, br = blamecache[n]
275 if bl == l[0]:
275 if bl == l[0]:
276 bd = '%s@%s' % (bu, br)
276 bd = '%s@%s' % (bu, br)
277 logfunc(f, n + 1, l[0], msg, bd)
277 logfunc(f, n + 1, l[0], msg, bd)
278 fc += 1
278 fc += 1
279 result = False
279 result = False
280 if maxerr is not None and fc >= maxerr:
280 if maxerr is not None and fc >= maxerr:
281 print " (too many errors, giving up)"
281 print " (too many errors, giving up)"
282 break
282 break
283 break
283 break
284 return result
284 return result
285
285
286 if __name__ == "__main__":
286 if __name__ == "__main__":
287 parser = optparse.OptionParser("%prog [options] [files]")
287 parser = optparse.OptionParser("%prog [options] [files]")
288 parser.add_option("-w", "--warnings", action="store_true",
288 parser.add_option("-w", "--warnings", action="store_true",
289 help="include warning-level checks")
289 help="include warning-level checks")
290 parser.add_option("-p", "--per-file", type="int",
290 parser.add_option("-p", "--per-file", type="int",
291 help="max warnings per file")
291 help="max warnings per file")
292 parser.add_option("-b", "--blame", action="store_true",
292 parser.add_option("-b", "--blame", action="store_true",
293 help="use annotate to generate blame info")
293 help="use annotate to generate blame info")
294
294
295 parser.set_defaults(per_file=15, warnings=False, blame=False)
295 parser.set_defaults(per_file=15, warnings=False, blame=False)
296 (options, args) = parser.parse_args()
296 (options, args) = parser.parse_args()
297
297
298 if len(args) == 0:
298 if len(args) == 0:
299 check = glob.glob("*")
299 check = glob.glob("*")
300 else:
300 else:
301 check = args
301 check = args
302
302
303 for f in check:
303 for f in check:
304 ret = 0
304 ret = 0
305 if not checkfile(f, maxerr=options.per_file, warnings=options.warnings,
305 if not checkfile(f, maxerr=options.per_file, warnings=options.warnings,
306 blame=options.blame):
306 blame=options.blame):
307 ret = 1
307 ret = 1
308 sys.exit(ret)
308 sys.exit(ret)
@@ -1,576 +1,576
1 /*
1 /*
2 osutil.c - native operating system services
2 osutil.c - native operating system services
3
3
4 Copyright 2007 Matt Mackall and others
4 Copyright 2007 Matt Mackall and others
5
5
6 This software may be used and distributed according to the terms of
6 This software may be used and distributed according to the terms of
7 the GNU General Public License, incorporated herein by reference.
7 the GNU General Public License, incorporated herein by reference.
8 */
8 */
9
9
10 #define _ATFILE_SOURCE
10 #define _ATFILE_SOURCE
11 #include <Python.h>
11 #include <Python.h>
12 #include <fcntl.h>
12 #include <fcntl.h>
13 #include <stdio.h>
13 #include <stdio.h>
14 #include <string.h>
14 #include <string.h>
15
15
16 #ifdef _WIN32
16 #ifdef _WIN32
17 #include <windows.h>
17 #include <windows.h>
18 #include <io.h>
18 #include <io.h>
19 #else
19 #else
20 #include <dirent.h>
20 #include <dirent.h>
21 #include <sys/stat.h>
21 #include <sys/stat.h>
22 #include <sys/types.h>
22 #include <sys/types.h>
23 #include <unistd.h>
23 #include <unistd.h>
24 #endif
24 #endif
25
25
26 #include "util.h"
26 #include "util.h"
27
27
28 /* some platforms lack the PATH_MAX definition (eg. GNU/Hurd) */
28 /* some platforms lack the PATH_MAX definition (eg. GNU/Hurd) */
29 #ifndef PATH_MAX
29 #ifndef PATH_MAX
30 #define PATH_MAX 4096
30 #define PATH_MAX 4096
31 #endif
31 #endif
32
32
33 #ifdef _WIN32
33 #ifdef _WIN32
34 /*
34 /*
35 stat struct compatible with hg expectations
35 stat struct compatible with hg expectations
36 Mercurial only uses st_mode, st_size and st_mtime
36 Mercurial only uses st_mode, st_size and st_mtime
37 the rest is kept to minimize changes between implementations
37 the rest is kept to minimize changes between implementations
38 */
38 */
39 struct hg_stat {
39 struct hg_stat {
40 int st_dev;
40 int st_dev;
41 int st_mode;
41 int st_mode;
42 int st_nlink;
42 int st_nlink;
43 __int64 st_size;
43 __int64 st_size;
44 int st_mtime;
44 int st_mtime;
45 int st_ctime;
45 int st_ctime;
46 };
46 };
47 struct listdir_stat {
47 struct listdir_stat {
48 PyObject_HEAD
48 PyObject_HEAD
49 struct hg_stat st;
49 struct hg_stat st;
50 };
50 };
51 #else
51 #else
52 struct listdir_stat {
52 struct listdir_stat {
53 PyObject_HEAD
53 PyObject_HEAD
54 struct stat st;
54 struct stat st;
55 };
55 };
56 #endif
56 #endif
57
57
58 #define listdir_slot(name) \
58 #define listdir_slot(name) \
59 static PyObject *listdir_stat_##name(PyObject *self, void *x) \
59 static PyObject *listdir_stat_##name(PyObject *self, void *x) \
60 { \
60 { \
61 return PyInt_FromLong(((struct listdir_stat *)self)->st.name); \
61 return PyInt_FromLong(((struct listdir_stat *)self)->st.name); \
62 }
62 }
63
63
64 listdir_slot(st_dev)
64 listdir_slot(st_dev)
65 listdir_slot(st_mode)
65 listdir_slot(st_mode)
66 listdir_slot(st_nlink)
66 listdir_slot(st_nlink)
67 #ifdef _WIN32
67 #ifdef _WIN32
68 static PyObject *listdir_stat_st_size(PyObject *self, void *x)
68 static PyObject *listdir_stat_st_size(PyObject *self, void *x)
69 {
69 {
70 return PyLong_FromLongLong(
70 return PyLong_FromLongLong(
71 (PY_LONG_LONG)((struct listdir_stat *)self)->st.st_size);
71 (PY_LONG_LONG)((struct listdir_stat *)self)->st.st_size);
72 }
72 }
73 #else
73 #else
74 listdir_slot(st_size)
74 listdir_slot(st_size)
75 #endif
75 #endif
76 listdir_slot(st_mtime)
76 listdir_slot(st_mtime)
77 listdir_slot(st_ctime)
77 listdir_slot(st_ctime)
78
78
79 static struct PyGetSetDef listdir_stat_getsets[] = {
79 static struct PyGetSetDef listdir_stat_getsets[] = {
80 {"st_dev", listdir_stat_st_dev, 0, 0, 0},
80 {"st_dev", listdir_stat_st_dev, 0, 0, 0},
81 {"st_mode", listdir_stat_st_mode, 0, 0, 0},
81 {"st_mode", listdir_stat_st_mode, 0, 0, 0},
82 {"st_nlink", listdir_stat_st_nlink, 0, 0, 0},
82 {"st_nlink", listdir_stat_st_nlink, 0, 0, 0},
83 {"st_size", listdir_stat_st_size, 0, 0, 0},
83 {"st_size", listdir_stat_st_size, 0, 0, 0},
84 {"st_mtime", listdir_stat_st_mtime, 0, 0, 0},
84 {"st_mtime", listdir_stat_st_mtime, 0, 0, 0},
85 {"st_ctime", listdir_stat_st_ctime, 0, 0, 0},
85 {"st_ctime", listdir_stat_st_ctime, 0, 0, 0},
86 {0, 0, 0, 0, 0}
86 {0, 0, 0, 0, 0}
87 };
87 };
88
88
89 static PyObject *listdir_stat_new(PyTypeObject *t, PyObject *a, PyObject *k)
89 static PyObject *listdir_stat_new(PyTypeObject *t, PyObject *a, PyObject *k)
90 {
90 {
91 return t->tp_alloc(t, 0);
91 return t->tp_alloc(t, 0);
92 }
92 }
93
93
94 static void listdir_stat_dealloc(PyObject *o)
94 static void listdir_stat_dealloc(PyObject *o)
95 {
95 {
96 o->ob_type->tp_free(o);
96 o->ob_type->tp_free(o);
97 }
97 }
98
98
99 static PyTypeObject listdir_stat_type = {
99 static PyTypeObject listdir_stat_type = {
100 PyVarObject_HEAD_INIT(NULL, 0)
100 PyVarObject_HEAD_INIT(NULL, 0)
101 "osutil.stat", /*tp_name*/
101 "osutil.stat", /*tp_name*/
102 sizeof(struct listdir_stat), /*tp_basicsize*/
102 sizeof(struct listdir_stat), /*tp_basicsize*/
103 0, /*tp_itemsize*/
103 0, /*tp_itemsize*/
104 (destructor)listdir_stat_dealloc, /*tp_dealloc*/
104 (destructor)listdir_stat_dealloc, /*tp_dealloc*/
105 0, /*tp_print*/
105 0, /*tp_print*/
106 0, /*tp_getattr*/
106 0, /*tp_getattr*/
107 0, /*tp_setattr*/
107 0, /*tp_setattr*/
108 0, /*tp_compare*/
108 0, /*tp_compare*/
109 0, /*tp_repr*/
109 0, /*tp_repr*/
110 0, /*tp_as_number*/
110 0, /*tp_as_number*/
111 0, /*tp_as_sequence*/
111 0, /*tp_as_sequence*/
112 0, /*tp_as_mapping*/
112 0, /*tp_as_mapping*/
113 0, /*tp_hash */
113 0, /*tp_hash */
114 0, /*tp_call*/
114 0, /*tp_call*/
115 0, /*tp_str*/
115 0, /*tp_str*/
116 0, /*tp_getattro*/
116 0, /*tp_getattro*/
117 0, /*tp_setattro*/
117 0, /*tp_setattro*/
118 0, /*tp_as_buffer*/
118 0, /*tp_as_buffer*/
119 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
119 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
120 "stat objects", /* tp_doc */
120 "stat objects", /* tp_doc */
121 0, /* tp_traverse */
121 0, /* tp_traverse */
122 0, /* tp_clear */
122 0, /* tp_clear */
123 0, /* tp_richcompare */
123 0, /* tp_richcompare */
124 0, /* tp_weaklistoffset */
124 0, /* tp_weaklistoffset */
125 0, /* tp_iter */
125 0, /* tp_iter */
126 0, /* tp_iternext */
126 0, /* tp_iternext */
127 0, /* tp_methods */
127 0, /* tp_methods */
128 0, /* tp_members */
128 0, /* tp_members */
129 listdir_stat_getsets, /* tp_getset */
129 listdir_stat_getsets, /* tp_getset */
130 0, /* tp_base */
130 0, /* tp_base */
131 0, /* tp_dict */
131 0, /* tp_dict */
132 0, /* tp_descr_get */
132 0, /* tp_descr_get */
133 0, /* tp_descr_set */
133 0, /* tp_descr_set */
134 0, /* tp_dictoffset */
134 0, /* tp_dictoffset */
135 0, /* tp_init */
135 0, /* tp_init */
136 0, /* tp_alloc */
136 0, /* tp_alloc */
137 listdir_stat_new, /* tp_new */
137 listdir_stat_new, /* tp_new */
138 };
138 };
139
139
140 #ifdef _WIN32
140 #ifdef _WIN32
141
141
142 static int to_python_time(const FILETIME *tm)
142 static int to_python_time(const FILETIME *tm)
143 {
143 {
144 /* number of seconds between epoch and January 1 1601 */
144 /* number of seconds between epoch and January 1 1601 */
145 const __int64 a0 = (__int64)134774L * (__int64)24L * (__int64)3600L;
145 const __int64 a0 = (__int64)134774L * (__int64)24L * (__int64)3600L;
146 /* conversion factor from 100ns to 1s */
146 /* conversion factor from 100ns to 1s */
147 const __int64 a1 = 10000000;
147 const __int64 a1 = 10000000;
148 /* explicit (int) cast to suspend compiler warnings */
148 /* explicit (int) cast to suspend compiler warnings */
149 return (int)((((__int64)tm->dwHighDateTime << 32)
149 return (int)((((__int64)tm->dwHighDateTime << 32)
150 + tm->dwLowDateTime) / a1 - a0);
150 + tm->dwLowDateTime) / a1 - a0);
151 }
151 }
152
152
153 static PyObject *make_item(const WIN32_FIND_DATAA *fd, int wantstat)
153 static PyObject *make_item(const WIN32_FIND_DATAA *fd, int wantstat)
154 {
154 {
155 PyObject *py_st;
155 PyObject *py_st;
156 struct hg_stat *stp;
156 struct hg_stat *stp;
157
157
158 int kind = (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
158 int kind = (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
159 ? _S_IFDIR : _S_IFREG;
159 ? _S_IFDIR : _S_IFREG;
160
160
161 if (!wantstat)
161 if (!wantstat)
162 return Py_BuildValue("si", fd->cFileName, kind);
162 return Py_BuildValue("si", fd->cFileName, kind);
163
163
164 py_st = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
164 py_st = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
165 if (!py_st)
165 if (!py_st)
166 return NULL;
166 return NULL;
167
167
168 stp = &((struct listdir_stat *)py_st)->st;
168 stp = &((struct listdir_stat *)py_st)->st;
169 /*
169 /*
170 use kind as st_mode
170 use kind as st_mode
171 rwx bits on Win32 are meaningless
171 rwx bits on Win32 are meaningless
172 and Hg does not use them anyway
172 and Hg does not use them anyway
173 */
173 */
174 stp->st_mode = kind;
174 stp->st_mode = kind;
175 stp->st_mtime = to_python_time(&fd->ftLastWriteTime);
175 stp->st_mtime = to_python_time(&fd->ftLastWriteTime);
176 stp->st_ctime = to_python_time(&fd->ftCreationTime);
176 stp->st_ctime = to_python_time(&fd->ftCreationTime);
177 if (kind == _S_IFREG)
177 if (kind == _S_IFREG)
178 stp->st_size = ((__int64)fd->nFileSizeHigh << 32)
178 stp->st_size = ((__int64)fd->nFileSizeHigh << 32)
179 + fd->nFileSizeLow;
179 + fd->nFileSizeLow;
180 return Py_BuildValue("siN", fd->cFileName,
180 return Py_BuildValue("siN", fd->cFileName,
181 kind, py_st);
181 kind, py_st);
182 }
182 }
183
183
184 static PyObject *_listdir(char *path, int plen, int wantstat, char *skip)
184 static PyObject *_listdir(char *path, int plen, int wantstat, char *skip)
185 {
185 {
186 PyObject *rval = NULL; /* initialize - return value */
186 PyObject *rval = NULL; /* initialize - return value */
187 PyObject *list;
187 PyObject *list;
188 HANDLE fh;
188 HANDLE fh;
189 WIN32_FIND_DATAA fd;
189 WIN32_FIND_DATAA fd;
190 char *pattern;
190 char *pattern;
191
191
192 /* build the path + \* pattern string */
192 /* build the path + \* pattern string */
193 pattern = malloc(plen + 3); /* path + \* + \0 */
193 pattern = malloc(plen + 3); /* path + \* + \0 */
194 if (!pattern) {
194 if (!pattern) {
195 PyErr_NoMemory();
195 PyErr_NoMemory();
196 goto error_nomem;
196 goto error_nomem;
197 }
197 }
198 strcpy(pattern, path);
198 strcpy(pattern, path);
199
199
200 if (plen > 0) {
200 if (plen > 0) {
201 char c = path[plen-1];
201 char c = path[plen-1];
202 if (c != ':' && c != '/' && c != '\\')
202 if (c != ':' && c != '/' && c != '\\')
203 pattern[plen++] = '\\';
203 pattern[plen++] = '\\';
204 }
204 }
205 strcpy(pattern + plen, "*");
205 strcpy(pattern + plen, "*");
206
206
207 fh = FindFirstFileA(pattern, &fd);
207 fh = FindFirstFileA(pattern, &fd);
208 if (fh == INVALID_HANDLE_VALUE) {
208 if (fh == INVALID_HANDLE_VALUE) {
209 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
209 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
210 goto error_file;
210 goto error_file;
211 }
211 }
212
212
213 list = PyList_New(0);
213 list = PyList_New(0);
214 if (!list)
214 if (!list)
215 goto error_list;
215 goto error_list;
216
216
217 do {
217 do {
218 PyObject *item;
218 PyObject *item;
219
219
220 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
220 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
221 if (!strcmp(fd.cFileName, ".")
221 if (!strcmp(fd.cFileName, ".")
222 || !strcmp(fd.cFileName, ".."))
222 || !strcmp(fd.cFileName, ".."))
223 continue;
223 continue;
224
224
225 if (skip && !strcmp(fd.cFileName, skip)) {
225 if (skip && !strcmp(fd.cFileName, skip)) {
226 rval = PyList_New(0);
226 rval = PyList_New(0);
227 goto error;
227 goto error;
228 }
228 }
229 }
229 }
230
230
231 item = make_item(&fd, wantstat);
231 item = make_item(&fd, wantstat);
232 if (!item)
232 if (!item)
233 goto error;
233 goto error;
234
234
235 if (PyList_Append(list, item)) {
235 if (PyList_Append(list, item)) {
236 Py_XDECREF(item);
236 Py_XDECREF(item);
237 goto error;
237 goto error;
238 }
238 }
239
239
240 Py_XDECREF(item);
240 Py_XDECREF(item);
241 } while (FindNextFileA(fh, &fd));
241 } while (FindNextFileA(fh, &fd));
242
242
243 if (GetLastError() != ERROR_NO_MORE_FILES) {
243 if (GetLastError() != ERROR_NO_MORE_FILES) {
244 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
244 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
245 goto error;
245 goto error;
246 }
246 }
247
247
248 rval = list;
248 rval = list;
249 Py_XINCREF(rval);
249 Py_XINCREF(rval);
250 error:
250 error:
251 Py_XDECREF(list);
251 Py_XDECREF(list);
252 error_list:
252 error_list:
253 FindClose(fh);
253 FindClose(fh);
254 error_file:
254 error_file:
255 free(pattern);
255 free(pattern);
256 error_nomem:
256 error_nomem:
257 return rval;
257 return rval;
258 }
258 }
259
259
260 #else
260 #else
261
261
262 int entkind(struct dirent *ent)
262 int entkind(struct dirent *ent)
263 {
263 {
264 #ifdef DT_REG
264 #ifdef DT_REG
265 switch (ent->d_type) {
265 switch (ent->d_type) {
266 case DT_REG: return S_IFREG;
266 case DT_REG: return S_IFREG;
267 case DT_DIR: return S_IFDIR;
267 case DT_DIR: return S_IFDIR;
268 case DT_LNK: return S_IFLNK;
268 case DT_LNK: return S_IFLNK;
269 case DT_BLK: return S_IFBLK;
269 case DT_BLK: return S_IFBLK;
270 case DT_CHR: return S_IFCHR;
270 case DT_CHR: return S_IFCHR;
271 case DT_FIFO: return S_IFIFO;
271 case DT_FIFO: return S_IFIFO;
272 case DT_SOCK: return S_IFSOCK;
272 case DT_SOCK: return S_IFSOCK;
273 }
273 }
274 #endif
274 #endif
275 return -1;
275 return -1;
276 }
276 }
277
277
278 static PyObject *_listdir(char *path, int pathlen, int keepstat, char *skip)
278 static PyObject *_listdir(char *path, int pathlen, int keepstat, char *skip)
279 {
279 {
280 PyObject *list, *elem, *stat, *ret = NULL;
280 PyObject *list, *elem, *stat, *ret = NULL;
281 char fullpath[PATH_MAX + 10];
281 char fullpath[PATH_MAX + 10];
282 int kind, err;
282 int kind, err;
283 struct stat st;
283 struct stat st;
284 struct dirent *ent;
284 struct dirent *ent;
285 DIR *dir;
285 DIR *dir;
286 #ifdef AT_SYMLINK_NOFOLLOW
286 #ifdef AT_SYMLINK_NOFOLLOW
287 int dfd = -1;
287 int dfd = -1;
288 #endif
288 #endif
289
289
290 if (pathlen >= PATH_MAX) {
290 if (pathlen >= PATH_MAX) {
291 PyErr_SetString(PyExc_ValueError, "path too long");
291 PyErr_SetString(PyExc_ValueError, "path too long");
292 goto error_value;
292 goto error_value;
293 }
293 }
294 strncpy(fullpath, path, PATH_MAX);
294 strncpy(fullpath, path, PATH_MAX);
295 fullpath[pathlen] = '/';
295 fullpath[pathlen] = '/';
296
296
297 #ifdef AT_SYMLINK_NOFOLLOW
297 #ifdef AT_SYMLINK_NOFOLLOW
298 dfd = open(path, O_RDONLY);
298 dfd = open(path, O_RDONLY);
299 if (dfd == -1) {
299 if (dfd == -1) {
300 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
300 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
301 goto error_value;
301 goto error_value;
302 }
302 }
303 dir = fdopendir(dfd);
303 dir = fdopendir(dfd);
304 #else
304 #else
305 dir = opendir(path);
305 dir = opendir(path);
306 #endif
306 #endif
307 if (!dir) {
307 if (!dir) {
308 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
308 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
309 goto error_dir;
309 goto error_dir;
310 }
310 }
311
311
312 list = PyList_New(0);
312 list = PyList_New(0);
313 if (!list)
313 if (!list)
314 goto error_list;
314 goto error_list;
315
315
316 while ((ent = readdir(dir))) {
316 while ((ent = readdir(dir))) {
317 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
317 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
318 continue;
318 continue;
319
319
320 kind = entkind(ent);
320 kind = entkind(ent);
321 if (kind == -1 || keepstat) {
321 if (kind == -1 || keepstat) {
322 #ifdef AT_SYMLINK_NOFOLLOW
322 #ifdef AT_SYMLINK_NOFOLLOW
323 err = fstatat(dfd, ent->d_name, &st,
323 err = fstatat(dfd, ent->d_name, &st,
324 AT_SYMLINK_NOFOLLOW);
324 AT_SYMLINK_NOFOLLOW);
325 #else
325 #else
326 strncpy(fullpath + pathlen + 1, ent->d_name,
326 strncpy(fullpath + pathlen + 1, ent->d_name,
327 PATH_MAX - pathlen);
327 PATH_MAX - pathlen);
328 fullpath[PATH_MAX] = 0;
328 fullpath[PATH_MAX] = 0;
329 err = lstat(fullpath, &st);
329 err = lstat(fullpath, &st);
330 #endif
330 #endif
331 if (err == -1) {
331 if (err == -1) {
332 strncpy(fullpath + pathlen + 1, ent->d_name,
332 strncpy(fullpath + pathlen + 1, ent->d_name,
333 PATH_MAX - pathlen);
333 PATH_MAX - pathlen);
334 fullpath[PATH_MAX] = 0;
334 fullpath[PATH_MAX] = 0;
335 PyErr_SetFromErrnoWithFilename(PyExc_OSError,
335 PyErr_SetFromErrnoWithFilename(PyExc_OSError,
336 fullpath);
336 fullpath);
337 goto error;
337 goto error;
338 }
338 }
339 kind = st.st_mode & S_IFMT;
339 kind = st.st_mode & S_IFMT;
340 }
340 }
341
341
342 /* quit early? */
342 /* quit early? */
343 if (skip && kind == S_IFDIR && !strcmp(ent->d_name, skip)) {
343 if (skip && kind == S_IFDIR && !strcmp(ent->d_name, skip)) {
344 ret = PyList_New(0);
344 ret = PyList_New(0);
345 goto error;
345 goto error;
346 }
346 }
347
347
348 if (keepstat) {
348 if (keepstat) {
349 stat = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
349 stat = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
350 if (!stat)
350 if (!stat)
351 goto error;
351 goto error;
352 memcpy(&((struct listdir_stat *)stat)->st, &st, sizeof(st));
352 memcpy(&((struct listdir_stat *)stat)->st, &st, sizeof(st));
353 elem = Py_BuildValue("siN", ent->d_name, kind, stat);
353 elem = Py_BuildValue("siN", ent->d_name, kind, stat);
354 } else
354 } else
355 elem = Py_BuildValue("si", ent->d_name, kind);
355 elem = Py_BuildValue("si", ent->d_name, kind);
356 if (!elem)
356 if (!elem)
357 goto error;
357 goto error;
358
358
359 PyList_Append(list, elem);
359 PyList_Append(list, elem);
360 Py_DECREF(elem);
360 Py_DECREF(elem);
361 }
361 }
362
362
363 ret = list;
363 ret = list;
364 Py_INCREF(ret);
364 Py_INCREF(ret);
365
365
366 error:
366 error:
367 Py_DECREF(list);
367 Py_DECREF(list);
368 error_list:
368 error_list:
369 closedir(dir);
369 closedir(dir);
370 error_dir:
370 error_dir:
371 #ifdef AT_SYMLINK_NOFOLLOW
371 #ifdef AT_SYMLINK_NOFOLLOW
372 close(dfd);
372 close(dfd);
373 #endif
373 #endif
374 error_value:
374 error_value:
375 return ret;
375 return ret;
376 }
376 }
377
377
378 #endif /* ndef _WIN32 */
378 #endif /* ndef _WIN32 */
379
379
380 static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs)
380 static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs)
381 {
381 {
382 PyObject *statobj = NULL; /* initialize - optional arg */
382 PyObject *statobj = NULL; /* initialize - optional arg */
383 PyObject *skipobj = NULL; /* initialize - optional arg */
383 PyObject *skipobj = NULL; /* initialize - optional arg */
384 char *path, *skip = NULL;
384 char *path, *skip = NULL;
385 int wantstat, plen;
385 int wantstat, plen;
386
386
387 static char *kwlist[] = {"path", "stat", "skip", NULL};
387 static char *kwlist[] = {"path", "stat", "skip", NULL};
388
388
389 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|OO:listdir",
389 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|OO:listdir",
390 kwlist, &path, &plen, &statobj, &skipobj))
390 kwlist, &path, &plen, &statobj, &skipobj))
391 return NULL;
391 return NULL;
392
392
393 wantstat = statobj && PyObject_IsTrue(statobj);
393 wantstat = statobj && PyObject_IsTrue(statobj);
394
394
395 if (skipobj && skipobj != Py_None) {
395 if (skipobj && skipobj != Py_None) {
396 skip = PyBytes_AsString(skipobj);
396 skip = PyBytes_AsString(skipobj);
397 if (!skip)
397 if (!skip)
398 return NULL;
398 return NULL;
399 }
399 }
400
400
401 return _listdir(path, plen, wantstat, skip);
401 return _listdir(path, plen, wantstat, skip);
402 }
402 }
403
403
404 #ifdef _WIN32
404 #ifdef _WIN32
405 static PyObject *posixfile(PyObject *self, PyObject *args, PyObject *kwds)
405 static PyObject *posixfile(PyObject *self, PyObject *args, PyObject *kwds)
406 {
406 {
407 static char *kwlist[] = {"name", "mode", "buffering", NULL};
407 static char *kwlist[] = {"name", "mode", "buffering", NULL};
408 PyObject *file_obj = NULL;
408 PyObject *file_obj = NULL;
409 char *name = NULL;
409 char *name = NULL;
410 char *mode = "rb";
410 char *mode = "rb";
411 DWORD access = 0;
411 DWORD access = 0;
412 DWORD creation;
412 DWORD creation;
413 HANDLE handle;
413 HANDLE handle;
414 int fd, flags = 0;
414 int fd, flags = 0;
415 int bufsize = -1;
415 int bufsize = -1;
416 char m0, m1, m2;
416 char m0, m1, m2;
417 char fpmode[4];
417 char fpmode[4];
418 int fppos = 0;
418 int fppos = 0;
419 int plus;
419 int plus;
420 FILE *fp;
420 FILE *fp;
421
421
422 if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|si:posixfile", kwlist,
422 if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|si:posixfile", kwlist,
423 Py_FileSystemDefaultEncoding,
423 Py_FileSystemDefaultEncoding,
424 &name, &mode, &bufsize))
424 &name, &mode, &bufsize))
425 return NULL;
425 return NULL;
426
426
427 m0 = mode[0];
427 m0 = mode[0];
428 m1 = m0 ? mode[1] : '\0';
428 m1 = m0 ? mode[1] : '\0';
429 m2 = m1 ? mode[2] : '\0';
429 m2 = m1 ? mode[2] : '\0';
430 plus = m1 == '+' || m2 == '+';
430 plus = m1 == '+' || m2 == '+';
431
431
432 fpmode[fppos++] = m0;
432 fpmode[fppos++] = m0;
433 if (m1 == 'b' || m2 == 'b') {
433 if (m1 == 'b' || m2 == 'b') {
434 flags = _O_BINARY;
434 flags = _O_BINARY;
435 fpmode[fppos++] = 'b';
435 fpmode[fppos++] = 'b';
436 }
436 }
437 else
437 else
438 flags = _O_TEXT;
438 flags = _O_TEXT;
439 if (m0 == 'r' && !plus) {
439 if (m0 == 'r' && !plus) {
440 flags |= _O_RDONLY;
440 flags |= _O_RDONLY;
441 access = GENERIC_READ;
441 access = GENERIC_READ;
442 } else {
442 } else {
443 /*
443 /*
444 work around http://support.microsoft.com/kb/899149 and
444 work around http://support.microsoft.com/kb/899149 and
445 set _O_RDWR for 'w' and 'a', even if mode has no '+'
445 set _O_RDWR for 'w' and 'a', even if mode has no '+'
446 */
446 */
447 flags |= _O_RDWR;
447 flags |= _O_RDWR;
448 access = GENERIC_READ | GENERIC_WRITE;
448 access = GENERIC_READ | GENERIC_WRITE;
449 fpmode[fppos++] = '+';
449 fpmode[fppos++] = '+';
450 }
450 }
451 fpmode[fppos++] = '\0';
451 fpmode[fppos++] = '\0';
452
452
453 switch (m0) {
453 switch (m0) {
454 case 'r':
454 case 'r':
455 creation = OPEN_EXISTING;
455 creation = OPEN_EXISTING;
456 break;
456 break;
457 case 'w':
457 case 'w':
458 creation = CREATE_ALWAYS;
458 creation = CREATE_ALWAYS;
459 break;
459 break;
460 case 'a':
460 case 'a':
461 creation = OPEN_ALWAYS;
461 creation = OPEN_ALWAYS;
462 flags |= _O_APPEND;
462 flags |= _O_APPEND;
463 break;
463 break;
464 default:
464 default:
465 PyErr_Format(PyExc_ValueError,
465 PyErr_Format(PyExc_ValueError,
466 "mode string must begin with one of 'r', 'w', "
466 "mode string must begin with one of 'r', 'w', "
467 "or 'a', not '%c'", m0);
467 "or 'a', not '%c'", m0);
468 goto bail;
468 goto bail;
469 }
469 }
470
470
471 handle = CreateFile(name, access,
471 handle = CreateFile(name, access,
472 FILE_SHARE_READ | FILE_SHARE_WRITE |
472 FILE_SHARE_READ | FILE_SHARE_WRITE |
473 FILE_SHARE_DELETE,
473 FILE_SHARE_DELETE,
474 NULL,
474 NULL,
475 creation,
475 creation,
476 FILE_ATTRIBUTE_NORMAL,
476 FILE_ATTRIBUTE_NORMAL,
477 0);
477 0);
478
478
479 if (handle == INVALID_HANDLE_VALUE) {
479 if (handle == INVALID_HANDLE_VALUE) {
480 PyErr_SetFromWindowsErrWithFilename(GetLastError(), name);
480 PyErr_SetFromWindowsErrWithFilename(GetLastError(), name);
481 goto bail;
481 goto bail;
482 }
482 }
483
483
484 fd = _open_osfhandle((intptr_t)handle, flags);
484 fd = _open_osfhandle((intptr_t)handle, flags);
485
485
486 if (fd == -1) {
486 if (fd == -1) {
487 CloseHandle(handle);
487 CloseHandle(handle);
488 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
488 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
489 goto bail;
489 goto bail;
490 }
490 }
491 #ifndef IS_PY3K
491 #ifndef IS_PY3K
492 fp = _fdopen(fd, fpmode);
492 fp = _fdopen(fd, fpmode);
493 if (fp == NULL) {
493 if (fp == NULL) {
494 _close(fd);
494 _close(fd);
495 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
495 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
496 goto bail;
496 goto bail;
497 }
497 }
498
498
499 file_obj = PyFile_FromFile(fp, name, mode, fclose);
499 file_obj = PyFile_FromFile(fp, name, mode, fclose);
500 if (file_obj == NULL) {
500 if (file_obj == NULL) {
501 fclose(fp);
501 fclose(fp);
502 goto bail;
502 goto bail;
503 }
503 }
504
504
505 PyFile_SetBufSize(file_obj, bufsize);
505 PyFile_SetBufSize(file_obj, bufsize);
506 #else
506 #else
507 file_obj = PyFile_FromFd(fd, name, mode, bufsize, NULL, NULL, NULL, 1);
507 file_obj = PyFile_FromFd(fd, name, mode, bufsize, NULL, NULL, NULL, 1);
508 if (file_obj == NULL)
508 if (file_obj == NULL)
509 goto bail;
509 goto bail;
510 #endif
510 #endif
511 bail:
511 bail:
512 PyMem_Free(name);
512 PyMem_Free(name);
513 return file_obj;
513 return file_obj;
514 }
514 }
515 #endif
515 #endif
516
516
517 #ifdef __APPLE__
517 #ifdef __APPLE__
518 #import <ApplicationServices/ApplicationServices.h>
518 #import <ApplicationServices/ApplicationServices.h>
519
519
520 static PyObject *isgui(PyObject *self)
520 static PyObject *isgui(PyObject *self)
521 {
521 {
522 CFDictionaryRef dict = CGSessionCopyCurrentDictionary();
522 CFDictionaryRef dict = CGSessionCopyCurrentDictionary();
523
523
524 if (dict != NULL) {
524 if (dict != NULL) {
525 CFRelease(dict);
525 CFRelease(dict);
526 return Py_True;
526 return Py_True;
527 } else {
527 } else {
528 return Py_False;
528 return Py_False;
529 }
529 }
530 }
530 }
531 #endif
531 #endif
532
532
533 static char osutil_doc[] = "Native operating system services.";
533 static char osutil_doc[] = "Native operating system services.";
534
534
535 static PyMethodDef methods[] = {
535 static PyMethodDef methods[] = {
536 {"listdir", (PyCFunction)listdir, METH_VARARGS | METH_KEYWORDS,
536 {"listdir", (PyCFunction)listdir, METH_VARARGS | METH_KEYWORDS,
537 "list a directory\n"},
537 "list a directory\n"},
538 #ifdef _WIN32
538 #ifdef _WIN32
539 {"posixfile", (PyCFunction)posixfile, METH_VARARGS | METH_KEYWORDS,
539 {"posixfile", (PyCFunction)posixfile, METH_VARARGS | METH_KEYWORDS,
540 "Open a file with POSIX-like semantics.\n"
540 "Open a file with POSIX-like semantics.\n"
541 "On error, this function may raise either a WindowsError or an IOError."},
541 "On error, this function may raise either a WindowsError or an IOError."},
542 #endif
542 #endif
543 #ifdef __APPLE__
543 #ifdef __APPLE__
544 {
544 {
545 "isgui", (PyCFunction)isgui, METH_NOARGS,
545 "isgui", (PyCFunction)isgui, METH_NOARGS,
546 "Is a CoreGraphics session available?"
546 "Is a CoreGraphics session available?"
547 },
547 },
548 #endif
548 #endif
549 {NULL, NULL}
549 {NULL, NULL}
550 };
550 };
551
551
552 #ifdef IS_PY3K
552 #ifdef IS_PY3K
553 static struct PyModuleDef osutil_module = {
553 static struct PyModuleDef osutil_module = {
554 PyModuleDef_HEAD_INIT,
554 PyModuleDef_HEAD_INIT,
555 "osutil",
555 "osutil",
556 osutil_doc,
556 osutil_doc,
557 -1,
557 -1,
558 methods
558 methods
559 };
559 };
560
560
561 PyMODINIT_FUNC PyInit_osutil(void)
561 PyMODINIT_FUNC PyInit_osutil(void)
562 {
562 {
563 if (PyType_Ready(&listdir_stat_type) < 0)
563 if (PyType_Ready(&listdir_stat_type) < 0)
564 return NULL;
564 return NULL;
565
565
566 return PyModule_Create(&osutil_module);
566 return PyModule_Create(&osutil_module);
567 }
567 }
568 #else
568 #else
569 PyMODINIT_FUNC initosutil(void)
569 PyMODINIT_FUNC initosutil(void)
570 {
570 {
571 if (PyType_Ready(&listdir_stat_type) == -1)
571 if (PyType_Ready(&listdir_stat_type) == -1)
572 return;
572 return;
573
573
574 Py_InitModule3("osutil", methods, osutil_doc);
574 Py_InitModule3("osutil", methods, osutil_doc);
575 }
575 }
576 #endif
576 #endif
General Comments 0
You need to be logged in to leave comments. Login now