##// END OF EJS Templates
Merge with crew-stable
Brendan Cully -
r8723:b4a1b901 merge default
parent child Browse files
Show More
@@ -1,529 +1,534 b''
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 // some platforms lack the PATH_MAX definition (eg. GNU/Hurd)
27 #ifndef PATH_MAX
28 #define PATH_MAX 4096
29 #endif
30
26 31 #ifdef _WIN32
27 32 /*
28 33 stat struct compatible with hg expectations
29 34 Mercurial only uses st_mode, st_size and st_mtime
30 35 the rest is kept to minimize changes between implementations
31 36 */
32 37 struct hg_stat {
33 38 int st_dev;
34 39 int st_mode;
35 40 int st_nlink;
36 41 __int64 st_size;
37 42 int st_mtime;
38 43 int st_ctime;
39 44 };
40 45 struct listdir_stat {
41 46 PyObject_HEAD
42 47 struct hg_stat st;
43 48 };
44 49 #else
45 50 struct listdir_stat {
46 51 PyObject_HEAD
47 52 struct stat st;
48 53 };
49 54 #endif
50 55
51 56 #define listdir_slot(name) \
52 57 static PyObject *listdir_stat_##name(PyObject *self, void *x) \
53 58 { \
54 59 return PyInt_FromLong(((struct listdir_stat *)self)->st.name); \
55 60 }
56 61
57 62 listdir_slot(st_dev)
58 63 listdir_slot(st_mode)
59 64 listdir_slot(st_nlink)
60 65 #ifdef _WIN32
61 66 static PyObject *listdir_stat_st_size(PyObject *self, void *x)
62 67 {
63 68 return PyLong_FromLongLong(
64 69 (PY_LONG_LONG)((struct listdir_stat *)self)->st.st_size);
65 70 }
66 71 #else
67 72 listdir_slot(st_size)
68 73 #endif
69 74 listdir_slot(st_mtime)
70 75 listdir_slot(st_ctime)
71 76
72 77 static struct PyGetSetDef listdir_stat_getsets[] = {
73 78 {"st_dev", listdir_stat_st_dev, 0, 0, 0},
74 79 {"st_mode", listdir_stat_st_mode, 0, 0, 0},
75 80 {"st_nlink", listdir_stat_st_nlink, 0, 0, 0},
76 81 {"st_size", listdir_stat_st_size, 0, 0, 0},
77 82 {"st_mtime", listdir_stat_st_mtime, 0, 0, 0},
78 83 {"st_ctime", listdir_stat_st_ctime, 0, 0, 0},
79 84 {0, 0, 0, 0, 0}
80 85 };
81 86
82 87 static PyObject *listdir_stat_new(PyTypeObject *t, PyObject *a, PyObject *k)
83 88 {
84 89 return t->tp_alloc(t, 0);
85 90 }
86 91
87 92 static void listdir_stat_dealloc(PyObject *o)
88 93 {
89 94 o->ob_type->tp_free(o);
90 95 }
91 96
92 97 static PyTypeObject listdir_stat_type = {
93 98 PyObject_HEAD_INIT(NULL)
94 99 0, /*ob_size*/
95 100 "osutil.stat", /*tp_name*/
96 101 sizeof(struct listdir_stat), /*tp_basicsize*/
97 102 0, /*tp_itemsize*/
98 103 (destructor)listdir_stat_dealloc, /*tp_dealloc*/
99 104 0, /*tp_print*/
100 105 0, /*tp_getattr*/
101 106 0, /*tp_setattr*/
102 107 0, /*tp_compare*/
103 108 0, /*tp_repr*/
104 109 0, /*tp_as_number*/
105 110 0, /*tp_as_sequence*/
106 111 0, /*tp_as_mapping*/
107 112 0, /*tp_hash */
108 113 0, /*tp_call*/
109 114 0, /*tp_str*/
110 115 0, /*tp_getattro*/
111 116 0, /*tp_setattro*/
112 117 0, /*tp_as_buffer*/
113 118 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
114 119 "stat objects", /* tp_doc */
115 120 0, /* tp_traverse */
116 121 0, /* tp_clear */
117 122 0, /* tp_richcompare */
118 123 0, /* tp_weaklistoffset */
119 124 0, /* tp_iter */
120 125 0, /* tp_iternext */
121 126 0, /* tp_methods */
122 127 0, /* tp_members */
123 128 listdir_stat_getsets, /* tp_getset */
124 129 0, /* tp_base */
125 130 0, /* tp_dict */
126 131 0, /* tp_descr_get */
127 132 0, /* tp_descr_set */
128 133 0, /* tp_dictoffset */
129 134 0, /* tp_init */
130 135 0, /* tp_alloc */
131 136 listdir_stat_new, /* tp_new */
132 137 };
133 138
134 139 #ifdef _WIN32
135 140
136 141 static int to_python_time(const FILETIME *tm)
137 142 {
138 143 /* number of seconds between epoch and January 1 1601 */
139 144 const __int64 a0 = (__int64)134774L * (__int64)24L * (__int64)3600L;
140 145 /* conversion factor from 100ns to 1s */
141 146 const __int64 a1 = 10000000;
142 147 /* explicit (int) cast to suspend compiler warnings */
143 148 return (int)((((__int64)tm->dwHighDateTime << 32)
144 149 + tm->dwLowDateTime) / a1 - a0);
145 150 }
146 151
147 152 static PyObject *make_item(const WIN32_FIND_DATAA *fd, int wantstat)
148 153 {
149 154 PyObject *py_st;
150 155 struct hg_stat *stp;
151 156
152 157 int kind = (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
153 158 ? _S_IFDIR : _S_IFREG;
154 159
155 160 if (!wantstat)
156 161 return Py_BuildValue("si", fd->cFileName, kind);
157 162
158 163 py_st = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
159 164 if (!py_st)
160 165 return NULL;
161 166
162 167 stp = &((struct listdir_stat *)py_st)->st;
163 168 /*
164 169 use kind as st_mode
165 170 rwx bits on Win32 are meaningless
166 171 and Hg does not use them anyway
167 172 */
168 173 stp->st_mode = kind;
169 174 stp->st_mtime = to_python_time(&fd->ftLastWriteTime);
170 175 stp->st_ctime = to_python_time(&fd->ftCreationTime);
171 176 if (kind == _S_IFREG)
172 177 stp->st_size = ((__int64)fd->nFileSizeHigh << 32)
173 178 + fd->nFileSizeLow;
174 179 return Py_BuildValue("siN", fd->cFileName,
175 180 kind, py_st);
176 181 }
177 182
178 183 static PyObject *_listdir(char *path, int plen, int wantstat, char *skip)
179 184 {
180 185 PyObject *rval = NULL; /* initialize - return value */
181 186 PyObject *list;
182 187 HANDLE fh;
183 188 WIN32_FIND_DATAA fd;
184 189 char *pattern;
185 190
186 191 /* build the path + \* pattern string */
187 192 pattern = malloc(plen+3); /* path + \* + \0 */
188 193 if (!pattern) {
189 194 PyErr_NoMemory();
190 195 goto error_nomem;
191 196 }
192 197 strcpy(pattern, path);
193 198
194 199 if (plen > 0) {
195 200 char c = path[plen-1];
196 201 if (c != ':' && c != '/' && c != '\\')
197 202 pattern[plen++] = '\\';
198 203 }
199 204 strcpy(pattern + plen, "*");
200 205
201 206 fh = FindFirstFileA(pattern, &fd);
202 207 if (fh == INVALID_HANDLE_VALUE) {
203 208 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
204 209 goto error_file;
205 210 }
206 211
207 212 list = PyList_New(0);
208 213 if (!list)
209 214 goto error_list;
210 215
211 216 do {
212 217 PyObject *item;
213 218
214 219 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
215 220 if (!strcmp(fd.cFileName, ".")
216 221 || !strcmp(fd.cFileName, ".."))
217 222 continue;
218 223
219 224 if (skip && !strcmp(fd.cFileName, skip)) {
220 225 rval = PyList_New(0);
221 226 goto error;
222 227 }
223 228 }
224 229
225 230 item = make_item(&fd, wantstat);
226 231 if (!item)
227 232 goto error;
228 233
229 234 if (PyList_Append(list, item)) {
230 235 Py_XDECREF(item);
231 236 goto error;
232 237 }
233 238
234 239 Py_XDECREF(item);
235 240 } while (FindNextFileA(fh, &fd));
236 241
237 242 if (GetLastError() != ERROR_NO_MORE_FILES) {
238 243 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
239 244 goto error;
240 245 }
241 246
242 247 rval = list;
243 248 Py_XINCREF(rval);
244 249 error:
245 250 Py_XDECREF(list);
246 251 error_list:
247 252 FindClose(fh);
248 253 error_file:
249 254 free(pattern);
250 255 error_nomem:
251 256 return rval;
252 257 }
253 258
254 259 #else
255 260
256 261 int entkind(struct dirent *ent)
257 262 {
258 263 #ifdef DT_REG
259 264 switch (ent->d_type) {
260 265 case DT_REG: return S_IFREG;
261 266 case DT_DIR: return S_IFDIR;
262 267 case DT_LNK: return S_IFLNK;
263 268 case DT_BLK: return S_IFBLK;
264 269 case DT_CHR: return S_IFCHR;
265 270 case DT_FIFO: return S_IFIFO;
266 271 case DT_SOCK: return S_IFSOCK;
267 272 }
268 273 #endif
269 274 return -1;
270 275 }
271 276
272 277 static PyObject *_listdir(char *path, int pathlen, int keepstat, char *skip)
273 278 {
274 279 PyObject *list, *elem, *stat, *ret = NULL;
275 280 char fullpath[PATH_MAX + 10];
276 281 int kind, err;
277 282 struct stat st;
278 283 struct dirent *ent;
279 284 DIR *dir;
280 285 #ifdef AT_SYMLINK_NOFOLLOW
281 286 int dfd = -1;
282 287 #endif
283 288
284 289 if (pathlen >= PATH_MAX) {
285 290 PyErr_SetString(PyExc_ValueError, "path too long");
286 291 goto error_value;
287 292 }
288 293 strncpy(fullpath, path, PATH_MAX);
289 294 fullpath[pathlen] = '/';
290 295
291 296 #ifdef AT_SYMLINK_NOFOLLOW
292 297 dfd = open(path, O_RDONLY);
293 298 if (dfd == -1) {
294 299 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
295 300 goto error_value;
296 301 }
297 302 dir = fdopendir(dfd);
298 303 #else
299 304 dir = opendir(path);
300 305 #endif
301 306 if (!dir) {
302 307 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
303 308 goto error_dir;
304 309 }
305 310
306 311 list = PyList_New(0);
307 312 if (!list)
308 313 goto error_list;
309 314
310 315 while ((ent = readdir(dir))) {
311 316 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
312 317 continue;
313 318
314 319 kind = entkind(ent);
315 320 if (kind == -1 || keepstat) {
316 321 #ifdef AT_SYMLINK_NOFOLLOW
317 322 err = fstatat(dfd, ent->d_name, &st,
318 323 AT_SYMLINK_NOFOLLOW);
319 324 #else
320 325 strncpy(fullpath + pathlen + 1, ent->d_name,
321 326 PATH_MAX - pathlen);
322 327 fullpath[PATH_MAX] = 0;
323 328 err = lstat(fullpath, &st);
324 329 #endif
325 330 if (err == -1) {
326 331 strncpy(fullpath + pathlen + 1, ent->d_name,
327 332 PATH_MAX - pathlen);
328 333 fullpath[PATH_MAX] = 0;
329 334 PyErr_SetFromErrnoWithFilename(PyExc_OSError,
330 335 fullpath);
331 336 goto error;
332 337 }
333 338 kind = st.st_mode & S_IFMT;
334 339 }
335 340
336 341 /* quit early? */
337 342 if (skip && kind == S_IFDIR && !strcmp(ent->d_name, skip)) {
338 343 ret = PyList_New(0);
339 344 goto error;
340 345 }
341 346
342 347 if (keepstat) {
343 348 stat = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
344 349 if (!stat)
345 350 goto error;
346 351 memcpy(&((struct listdir_stat *)stat)->st, &st, sizeof(st));
347 352 elem = Py_BuildValue("siN", ent->d_name, kind, stat);
348 353 } else
349 354 elem = Py_BuildValue("si", ent->d_name, kind);
350 355 if (!elem)
351 356 goto error;
352 357
353 358 PyList_Append(list, elem);
354 359 Py_DECREF(elem);
355 360 }
356 361
357 362 ret = list;
358 363 Py_INCREF(ret);
359 364
360 365 error:
361 366 Py_DECREF(list);
362 367 error_list:
363 368 closedir(dir);
364 369 error_dir:
365 370 #ifdef AT_SYMLINK_NOFOLLOW
366 371 close(dfd);
367 372 #endif
368 373 error_value:
369 374 return ret;
370 375 }
371 376
372 377 #endif /* ndef _WIN32 */
373 378
374 379 static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs)
375 380 {
376 381 PyObject *statobj = NULL; /* initialize - optional arg */
377 382 PyObject *skipobj = NULL; /* initialize - optional arg */
378 383 char *path, *skip = NULL;
379 384 int wantstat, plen;
380 385
381 386 static char *kwlist[] = {"path", "stat", "skip", NULL};
382 387
383 388 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|OO:listdir",
384 389 kwlist, &path, &plen, &statobj, &skipobj))
385 390 return NULL;
386 391
387 392 wantstat = statobj && PyObject_IsTrue(statobj);
388 393
389 394 if (skipobj && skipobj != Py_None) {
390 395 skip = PyString_AsString(skipobj);
391 396 if (!skip)
392 397 return NULL;
393 398 }
394 399
395 400 return _listdir(path, plen, wantstat, skip);
396 401 }
397 402
398 403 #ifdef _WIN32
399 404 static PyObject *posixfile(PyObject *self, PyObject *args, PyObject *kwds)
400 405 {
401 406 static char *kwlist[] = {"name", "mode", "buffering", NULL};
402 407 PyObject *file_obj = NULL;
403 408 char *name = NULL;
404 409 char *mode = "rb";
405 410 DWORD access = 0;
406 411 DWORD creation;
407 412 HANDLE handle;
408 413 int fd, flags = 0;
409 414 int bufsize = -1;
410 415 char m0, m1, m2;
411 416 char fpmode[4];
412 417 int fppos = 0;
413 418 int plus;
414 419 FILE *fp;
415 420
416 421 if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|si:posixfile", kwlist,
417 422 Py_FileSystemDefaultEncoding,
418 423 &name, &mode, &bufsize))
419 424 return NULL;
420 425
421 426 m0 = mode[0];
422 427 m1 = m0 ? mode[1] : '\0';
423 428 m2 = m1 ? mode[2] : '\0';
424 429 plus = m1 == '+' || m2 == '+';
425 430
426 431 fpmode[fppos++] = m0;
427 432 if (m1 == 'b' || m2 == 'b') {
428 433 flags = _O_BINARY;
429 434 fpmode[fppos++] = 'b';
430 435 }
431 436 else
432 437 flags = _O_TEXT;
433 438 if (plus) {
434 439 flags |= _O_RDWR;
435 440 access = GENERIC_READ | GENERIC_WRITE;
436 441 fpmode[fppos++] = '+';
437 442 }
438 443 fpmode[fppos++] = '\0';
439 444
440 445 switch (m0) {
441 446 case 'r':
442 447 creation = OPEN_EXISTING;
443 448 if (!plus) {
444 449 flags |= _O_RDONLY;
445 450 access = GENERIC_READ;
446 451 }
447 452 break;
448 453 case 'w':
449 454 creation = CREATE_ALWAYS;
450 455 if (!plus) {
451 456 access = GENERIC_WRITE;
452 457 flags |= _O_WRONLY;
453 458 }
454 459 break;
455 460 case 'a':
456 461 creation = OPEN_ALWAYS;
457 462 flags |= _O_APPEND;
458 463 if (!plus) {
459 464 flags |= _O_WRONLY;
460 465 access = GENERIC_WRITE;
461 466 }
462 467 break;
463 468 default:
464 469 PyErr_Format(PyExc_ValueError,
465 470 "mode string must begin with one of 'r', 'w', "
466 471 "or 'a', not '%c'", m0);
467 472 goto bail;
468 473 }
469 474
470 475 handle = CreateFile(name, access,
471 476 FILE_SHARE_READ | FILE_SHARE_WRITE |
472 477 FILE_SHARE_DELETE,
473 478 NULL,
474 479 creation,
475 480 FILE_ATTRIBUTE_NORMAL,
476 481 0);
477 482
478 483 if (handle == INVALID_HANDLE_VALUE) {
479 484 PyErr_SetFromWindowsErrWithFilename(GetLastError(), name);
480 485 goto bail;
481 486 }
482 487
483 488 fd = _open_osfhandle((intptr_t) handle, flags);
484 489 if (fd == -1) {
485 490 CloseHandle(handle);
486 491 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
487 492 goto bail;
488 493 }
489 494
490 495 fp = _fdopen(fd, fpmode);
491 496 if (fp == NULL) {
492 497 _close(fd);
493 498 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
494 499 goto bail;
495 500 }
496 501
497 502 file_obj = PyFile_FromFile(fp, name, mode, fclose);
498 503 if (file_obj == NULL) {
499 504 fclose(fp);
500 505 goto bail;
501 506 }
502 507
503 508 PyFile_SetBufSize(file_obj, bufsize);
504 509 bail:
505 510 PyMem_Free(name);
506 511 return file_obj;
507 512 }
508 513 #endif
509 514
510 515 static char osutil_doc[] = "Native operating system services.";
511 516
512 517 static PyMethodDef methods[] = {
513 518 {"listdir", (PyCFunction)listdir, METH_VARARGS | METH_KEYWORDS,
514 519 "list a directory\n"},
515 520 #ifdef _WIN32
516 521 {"posixfile", (PyCFunction)posixfile, METH_VARARGS | METH_KEYWORDS,
517 522 "Open a file with POSIX-like semantics.\n"
518 523 "On error, this function may raise either a WindowsError or an IOError."},
519 524 #endif
520 525 {NULL, NULL}
521 526 };
522 527
523 528 PyMODINIT_FUNC initosutil(void)
524 529 {
525 530 if (PyType_Ready(&listdir_stat_type) == -1)
526 531 return;
527 532
528 533 Py_InitModule3("osutil", methods, osutil_doc);
529 534 }
General Comments 0
You need to be logged in to leave comments. Login now