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