##// END OF EJS Templates
osutil: make statfiles check for interrupts periodically...
Bryan O'Sullivan -
r26983:f9f2f29c default
parent child Browse files
Show More
@@ -1,848 +1,853
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 #include <errno.h>
16 16
17 17 #ifdef _WIN32
18 18 #include <windows.h>
19 19 #include <io.h>
20 20 #else
21 21 #include <dirent.h>
22 22 #include <sys/stat.h>
23 23 #include <sys/types.h>
24 24 #include <unistd.h>
25 25 #endif
26 26
27 27 #ifdef __APPLE__
28 28 #include <sys/attr.h>
29 29 #include <sys/vnode.h>
30 30 #endif
31 31
32 32 #include "util.h"
33 33
34 34 /* some platforms lack the PATH_MAX definition (eg. GNU/Hurd) */
35 35 #ifndef PATH_MAX
36 36 #define PATH_MAX 4096
37 37 #endif
38 38
39 39 #ifdef _WIN32
40 40 /*
41 41 stat struct compatible with hg expectations
42 42 Mercurial only uses st_mode, st_size and st_mtime
43 43 the rest is kept to minimize changes between implementations
44 44 */
45 45 struct hg_stat {
46 46 int st_dev;
47 47 int st_mode;
48 48 int st_nlink;
49 49 __int64 st_size;
50 50 int st_mtime;
51 51 int st_ctime;
52 52 };
53 53 struct listdir_stat {
54 54 PyObject_HEAD
55 55 struct hg_stat st;
56 56 };
57 57 #else
58 58 struct listdir_stat {
59 59 PyObject_HEAD
60 60 struct stat st;
61 61 };
62 62 #endif
63 63
64 64 #define listdir_slot(name) \
65 65 static PyObject *listdir_stat_##name(PyObject *self, void *x) \
66 66 { \
67 67 return PyInt_FromLong(((struct listdir_stat *)self)->st.name); \
68 68 }
69 69
70 70 listdir_slot(st_dev)
71 71 listdir_slot(st_mode)
72 72 listdir_slot(st_nlink)
73 73 #ifdef _WIN32
74 74 static PyObject *listdir_stat_st_size(PyObject *self, void *x)
75 75 {
76 76 return PyLong_FromLongLong(
77 77 (PY_LONG_LONG)((struct listdir_stat *)self)->st.st_size);
78 78 }
79 79 #else
80 80 listdir_slot(st_size)
81 81 #endif
82 82 listdir_slot(st_mtime)
83 83 listdir_slot(st_ctime)
84 84
85 85 static struct PyGetSetDef listdir_stat_getsets[] = {
86 86 {"st_dev", listdir_stat_st_dev, 0, 0, 0},
87 87 {"st_mode", listdir_stat_st_mode, 0, 0, 0},
88 88 {"st_nlink", listdir_stat_st_nlink, 0, 0, 0},
89 89 {"st_size", listdir_stat_st_size, 0, 0, 0},
90 90 {"st_mtime", listdir_stat_st_mtime, 0, 0, 0},
91 91 {"st_ctime", listdir_stat_st_ctime, 0, 0, 0},
92 92 {0, 0, 0, 0, 0}
93 93 };
94 94
95 95 static PyObject *listdir_stat_new(PyTypeObject *t, PyObject *a, PyObject *k)
96 96 {
97 97 return t->tp_alloc(t, 0);
98 98 }
99 99
100 100 static void listdir_stat_dealloc(PyObject *o)
101 101 {
102 102 o->ob_type->tp_free(o);
103 103 }
104 104
105 105 static PyTypeObject listdir_stat_type = {
106 106 PyVarObject_HEAD_INIT(NULL, 0)
107 107 "osutil.stat", /*tp_name*/
108 108 sizeof(struct listdir_stat), /*tp_basicsize*/
109 109 0, /*tp_itemsize*/
110 110 (destructor)listdir_stat_dealloc, /*tp_dealloc*/
111 111 0, /*tp_print*/
112 112 0, /*tp_getattr*/
113 113 0, /*tp_setattr*/
114 114 0, /*tp_compare*/
115 115 0, /*tp_repr*/
116 116 0, /*tp_as_number*/
117 117 0, /*tp_as_sequence*/
118 118 0, /*tp_as_mapping*/
119 119 0, /*tp_hash */
120 120 0, /*tp_call*/
121 121 0, /*tp_str*/
122 122 0, /*tp_getattro*/
123 123 0, /*tp_setattro*/
124 124 0, /*tp_as_buffer*/
125 125 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
126 126 "stat objects", /* tp_doc */
127 127 0, /* tp_traverse */
128 128 0, /* tp_clear */
129 129 0, /* tp_richcompare */
130 130 0, /* tp_weaklistoffset */
131 131 0, /* tp_iter */
132 132 0, /* tp_iternext */
133 133 0, /* tp_methods */
134 134 0, /* tp_members */
135 135 listdir_stat_getsets, /* tp_getset */
136 136 0, /* tp_base */
137 137 0, /* tp_dict */
138 138 0, /* tp_descr_get */
139 139 0, /* tp_descr_set */
140 140 0, /* tp_dictoffset */
141 141 0, /* tp_init */
142 142 0, /* tp_alloc */
143 143 listdir_stat_new, /* tp_new */
144 144 };
145 145
146 146 #ifdef _WIN32
147 147
148 148 static int to_python_time(const FILETIME *tm)
149 149 {
150 150 /* number of seconds between epoch and January 1 1601 */
151 151 const __int64 a0 = (__int64)134774L * (__int64)24L * (__int64)3600L;
152 152 /* conversion factor from 100ns to 1s */
153 153 const __int64 a1 = 10000000;
154 154 /* explicit (int) cast to suspend compiler warnings */
155 155 return (int)((((__int64)tm->dwHighDateTime << 32)
156 156 + tm->dwLowDateTime) / a1 - a0);
157 157 }
158 158
159 159 static PyObject *make_item(const WIN32_FIND_DATAA *fd, int wantstat)
160 160 {
161 161 PyObject *py_st;
162 162 struct hg_stat *stp;
163 163
164 164 int kind = (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
165 165 ? _S_IFDIR : _S_IFREG;
166 166
167 167 if (!wantstat)
168 168 return Py_BuildValue("si", fd->cFileName, kind);
169 169
170 170 py_st = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
171 171 if (!py_st)
172 172 return NULL;
173 173
174 174 stp = &((struct listdir_stat *)py_st)->st;
175 175 /*
176 176 use kind as st_mode
177 177 rwx bits on Win32 are meaningless
178 178 and Hg does not use them anyway
179 179 */
180 180 stp->st_mode = kind;
181 181 stp->st_mtime = to_python_time(&fd->ftLastWriteTime);
182 182 stp->st_ctime = to_python_time(&fd->ftCreationTime);
183 183 if (kind == _S_IFREG)
184 184 stp->st_size = ((__int64)fd->nFileSizeHigh << 32)
185 185 + fd->nFileSizeLow;
186 186 return Py_BuildValue("siN", fd->cFileName,
187 187 kind, py_st);
188 188 }
189 189
190 190 static PyObject *_listdir(char *path, int plen, int wantstat, char *skip)
191 191 {
192 192 PyObject *rval = NULL; /* initialize - return value */
193 193 PyObject *list;
194 194 HANDLE fh;
195 195 WIN32_FIND_DATAA fd;
196 196 char *pattern;
197 197
198 198 /* build the path + \* pattern string */
199 199 pattern = malloc(plen + 3); /* path + \* + \0 */
200 200 if (!pattern) {
201 201 PyErr_NoMemory();
202 202 goto error_nomem;
203 203 }
204 204 strcpy(pattern, path);
205 205
206 206 if (plen > 0) {
207 207 char c = path[plen-1];
208 208 if (c != ':' && c != '/' && c != '\\')
209 209 pattern[plen++] = '\\';
210 210 }
211 211 strcpy(pattern + plen, "*");
212 212
213 213 fh = FindFirstFileA(pattern, &fd);
214 214 if (fh == INVALID_HANDLE_VALUE) {
215 215 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
216 216 goto error_file;
217 217 }
218 218
219 219 list = PyList_New(0);
220 220 if (!list)
221 221 goto error_list;
222 222
223 223 do {
224 224 PyObject *item;
225 225
226 226 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
227 227 if (!strcmp(fd.cFileName, ".")
228 228 || !strcmp(fd.cFileName, ".."))
229 229 continue;
230 230
231 231 if (skip && !strcmp(fd.cFileName, skip)) {
232 232 rval = PyList_New(0);
233 233 goto error;
234 234 }
235 235 }
236 236
237 237 item = make_item(&fd, wantstat);
238 238 if (!item)
239 239 goto error;
240 240
241 241 if (PyList_Append(list, item)) {
242 242 Py_XDECREF(item);
243 243 goto error;
244 244 }
245 245
246 246 Py_XDECREF(item);
247 247 } while (FindNextFileA(fh, &fd));
248 248
249 249 if (GetLastError() != ERROR_NO_MORE_FILES) {
250 250 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
251 251 goto error;
252 252 }
253 253
254 254 rval = list;
255 255 Py_XINCREF(rval);
256 256 error:
257 257 Py_XDECREF(list);
258 258 error_list:
259 259 FindClose(fh);
260 260 error_file:
261 261 free(pattern);
262 262 error_nomem:
263 263 return rval;
264 264 }
265 265
266 266 #else
267 267
268 268 int entkind(struct dirent *ent)
269 269 {
270 270 #ifdef DT_REG
271 271 switch (ent->d_type) {
272 272 case DT_REG: return S_IFREG;
273 273 case DT_DIR: return S_IFDIR;
274 274 case DT_LNK: return S_IFLNK;
275 275 case DT_BLK: return S_IFBLK;
276 276 case DT_CHR: return S_IFCHR;
277 277 case DT_FIFO: return S_IFIFO;
278 278 case DT_SOCK: return S_IFSOCK;
279 279 }
280 280 #endif
281 281 return -1;
282 282 }
283 283
284 284 static PyObject *makestat(const struct stat *st)
285 285 {
286 286 PyObject *stat;
287 287
288 288 stat = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
289 289 if (stat)
290 290 memcpy(&((struct listdir_stat *)stat)->st, st, sizeof(*st));
291 291 return stat;
292 292 }
293 293
294 294 static PyObject *_listdir_stat(char *path, int pathlen, int keepstat,
295 295 char *skip)
296 296 {
297 297 PyObject *list, *elem, *stat = NULL, *ret = NULL;
298 298 char fullpath[PATH_MAX + 10];
299 299 int kind, err;
300 300 struct stat st;
301 301 struct dirent *ent;
302 302 DIR *dir;
303 303 #ifdef AT_SYMLINK_NOFOLLOW
304 304 int dfd = -1;
305 305 #endif
306 306
307 307 if (pathlen >= PATH_MAX) {
308 308 errno = ENAMETOOLONG;
309 309 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
310 310 goto error_value;
311 311 }
312 312 strncpy(fullpath, path, PATH_MAX);
313 313 fullpath[pathlen] = '/';
314 314
315 315 #ifdef AT_SYMLINK_NOFOLLOW
316 316 dfd = open(path, O_RDONLY);
317 317 if (dfd == -1) {
318 318 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
319 319 goto error_value;
320 320 }
321 321 dir = fdopendir(dfd);
322 322 #else
323 323 dir = opendir(path);
324 324 #endif
325 325 if (!dir) {
326 326 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
327 327 goto error_dir;
328 328 }
329 329
330 330 list = PyList_New(0);
331 331 if (!list)
332 332 goto error_list;
333 333
334 334 while ((ent = readdir(dir))) {
335 335 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
336 336 continue;
337 337
338 338 kind = entkind(ent);
339 339 if (kind == -1 || keepstat) {
340 340 #ifdef AT_SYMLINK_NOFOLLOW
341 341 err = fstatat(dfd, ent->d_name, &st,
342 342 AT_SYMLINK_NOFOLLOW);
343 343 #else
344 344 strncpy(fullpath + pathlen + 1, ent->d_name,
345 345 PATH_MAX - pathlen);
346 346 fullpath[PATH_MAX] = '\0';
347 347 err = lstat(fullpath, &st);
348 348 #endif
349 349 if (err == -1) {
350 350 /* race with file deletion? */
351 351 if (errno == ENOENT)
352 352 continue;
353 353 strncpy(fullpath + pathlen + 1, ent->d_name,
354 354 PATH_MAX - pathlen);
355 355 fullpath[PATH_MAX] = 0;
356 356 PyErr_SetFromErrnoWithFilename(PyExc_OSError,
357 357 fullpath);
358 358 goto error;
359 359 }
360 360 kind = st.st_mode & S_IFMT;
361 361 }
362 362
363 363 /* quit early? */
364 364 if (skip && kind == S_IFDIR && !strcmp(ent->d_name, skip)) {
365 365 ret = PyList_New(0);
366 366 goto error;
367 367 }
368 368
369 369 if (keepstat) {
370 370 stat = makestat(&st);
371 371 if (!stat)
372 372 goto error;
373 373 elem = Py_BuildValue("siN", ent->d_name, kind, stat);
374 374 } else
375 375 elem = Py_BuildValue("si", ent->d_name, kind);
376 376 if (!elem)
377 377 goto error;
378 378 stat = NULL;
379 379
380 380 PyList_Append(list, elem);
381 381 Py_DECREF(elem);
382 382 }
383 383
384 384 ret = list;
385 385 Py_INCREF(ret);
386 386
387 387 error:
388 388 Py_DECREF(list);
389 389 Py_XDECREF(stat);
390 390 error_list:
391 391 closedir(dir);
392 392 error_dir:
393 393 #ifdef AT_SYMLINK_NOFOLLOW
394 394 close(dfd);
395 395 #endif
396 396 error_value:
397 397 return ret;
398 398 }
399 399
400 400 #ifdef __APPLE__
401 401
402 402 typedef struct {
403 403 u_int32_t length;
404 404 attrreference_t name;
405 405 fsobj_type_t obj_type;
406 406 struct timespec mtime;
407 407 #if __LITTLE_ENDIAN__
408 408 mode_t access_mask;
409 409 uint16_t padding;
410 410 #else
411 411 uint16_t padding;
412 412 mode_t access_mask;
413 413 #endif
414 414 off_t size;
415 415 } __attribute__((packed)) attrbuf_entry;
416 416
417 417 int attrkind(attrbuf_entry *entry)
418 418 {
419 419 switch (entry->obj_type) {
420 420 case VREG: return S_IFREG;
421 421 case VDIR: return S_IFDIR;
422 422 case VLNK: return S_IFLNK;
423 423 case VBLK: return S_IFBLK;
424 424 case VCHR: return S_IFCHR;
425 425 case VFIFO: return S_IFIFO;
426 426 case VSOCK: return S_IFSOCK;
427 427 }
428 428 return -1;
429 429 }
430 430
431 431 /* get these many entries at a time */
432 432 #define LISTDIR_BATCH_SIZE 50
433 433
434 434 static PyObject *_listdir_batch(char *path, int pathlen, int keepstat,
435 435 char *skip, bool *fallback)
436 436 {
437 437 PyObject *list, *elem, *stat = NULL, *ret = NULL;
438 438 int kind, err;
439 439 unsigned long index;
440 440 unsigned int count, old_state, new_state;
441 441 bool state_seen = false;
442 442 attrbuf_entry *entry;
443 443 /* from the getattrlist(2) man page: a path can be no longer than
444 444 (NAME_MAX * 3 + 1) bytes. Also, "The getattrlist() function will
445 445 silently truncate attribute data if attrBufSize is too small." So
446 446 pass in a buffer big enough for the worst case. */
447 447 char attrbuf[LISTDIR_BATCH_SIZE * (sizeof(attrbuf_entry) + NAME_MAX * 3 + 1)];
448 448 unsigned int basep_unused;
449 449
450 450 struct stat st;
451 451 int dfd = -1;
452 452
453 453 /* these must match the attrbuf_entry struct, otherwise you'll end up
454 454 with garbage */
455 455 struct attrlist requested_attr = {0};
456 456 requested_attr.bitmapcount = ATTR_BIT_MAP_COUNT;
457 457 requested_attr.commonattr = (ATTR_CMN_NAME | ATTR_CMN_OBJTYPE |
458 458 ATTR_CMN_MODTIME | ATTR_CMN_ACCESSMASK);
459 459 requested_attr.fileattr = ATTR_FILE_TOTALSIZE;
460 460
461 461 *fallback = false;
462 462
463 463 if (pathlen >= PATH_MAX) {
464 464 errno = ENAMETOOLONG;
465 465 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
466 466 goto error_value;
467 467 }
468 468
469 469 dfd = open(path, O_RDONLY);
470 470 if (dfd == -1) {
471 471 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
472 472 goto error_value;
473 473 }
474 474
475 475 list = PyList_New(0);
476 476 if (!list)
477 477 goto error_dir;
478 478
479 479 do {
480 480 count = LISTDIR_BATCH_SIZE;
481 481 err = getdirentriesattr(dfd, &requested_attr, &attrbuf,
482 482 sizeof(attrbuf), &count, &basep_unused,
483 483 &new_state, 0);
484 484 if (err < 0) {
485 485 if (errno == ENOTSUP) {
486 486 /* We're on a filesystem that doesn't support
487 487 getdirentriesattr. Fall back to the
488 488 stat-based implementation. */
489 489 *fallback = true;
490 490 } else
491 491 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
492 492 goto error;
493 493 }
494 494
495 495 if (!state_seen) {
496 496 old_state = new_state;
497 497 state_seen = true;
498 498 } else if (old_state != new_state) {
499 499 /* There's an edge case with getdirentriesattr. Consider
500 500 the following initial list of files:
501 501
502 502 a
503 503 b
504 504 <--
505 505 c
506 506 d
507 507
508 508 If the iteration is paused at the arrow, and b is
509 509 deleted before it is resumed, getdirentriesattr will
510 510 not return d at all! Ordinarily we're expected to
511 511 restart the iteration from the beginning. To avoid
512 512 getting stuck in a retry loop here, fall back to
513 513 stat. */
514 514 *fallback = true;
515 515 goto error;
516 516 }
517 517
518 518 entry = (attrbuf_entry *)attrbuf;
519 519
520 520 for (index = 0; index < count; index++) {
521 521 char *filename = ((char *)&entry->name) +
522 522 entry->name.attr_dataoffset;
523 523
524 524 if (!strcmp(filename, ".") || !strcmp(filename, ".."))
525 525 continue;
526 526
527 527 kind = attrkind(entry);
528 528 if (kind == -1) {
529 529 PyErr_Format(PyExc_OSError,
530 530 "unknown object type %u for file "
531 531 "%s%s!",
532 532 entry->obj_type, path, filename);
533 533 goto error;
534 534 }
535 535
536 536 /* quit early? */
537 537 if (skip && kind == S_IFDIR && !strcmp(filename, skip)) {
538 538 ret = PyList_New(0);
539 539 goto error;
540 540 }
541 541
542 542 if (keepstat) {
543 543 /* from the getattrlist(2) man page: "Only the
544 544 permission bits ... are valid". */
545 545 st.st_mode = (entry->access_mask & ~S_IFMT) | kind;
546 546 st.st_mtime = entry->mtime.tv_sec;
547 547 st.st_size = entry->size;
548 548 stat = makestat(&st);
549 549 if (!stat)
550 550 goto error;
551 551 elem = Py_BuildValue("siN", filename, kind, stat);
552 552 } else
553 553 elem = Py_BuildValue("si", filename, kind);
554 554 if (!elem)
555 555 goto error;
556 556 stat = NULL;
557 557
558 558 PyList_Append(list, elem);
559 559 Py_DECREF(elem);
560 560
561 561 entry = (attrbuf_entry *)((char *)entry + entry->length);
562 562 }
563 563 } while (err == 0);
564 564
565 565 ret = list;
566 566 Py_INCREF(ret);
567 567
568 568 error:
569 569 Py_DECREF(list);
570 570 Py_XDECREF(stat);
571 571 error_dir:
572 572 close(dfd);
573 573 error_value:
574 574 return ret;
575 575 }
576 576
577 577 #endif /* __APPLE__ */
578 578
579 579 static PyObject *_listdir(char *path, int pathlen, int keepstat, char *skip)
580 580 {
581 581 #ifdef __APPLE__
582 582 PyObject *ret;
583 583 bool fallback = false;
584 584
585 585 ret = _listdir_batch(path, pathlen, keepstat, skip, &fallback);
586 586 if (ret != NULL || !fallback)
587 587 return ret;
588 588 #endif
589 589 return _listdir_stat(path, pathlen, keepstat, skip);
590 590 }
591 591
592 592 static PyObject *statfiles(PyObject *self, PyObject *args)
593 593 {
594 594 PyObject *names, *stats;
595 595 Py_ssize_t i, count;
596 596
597 597 if (!PyArg_ParseTuple(args, "O:statfiles", &names))
598 598 return NULL;
599 599
600 600 count = PySequence_Length(names);
601 601 if (count == -1) {
602 602 PyErr_SetString(PyExc_TypeError, "not a sequence");
603 603 return NULL;
604 604 }
605 605
606 606 stats = PyList_New(count);
607 607 if (stats == NULL)
608 608 return NULL;
609 609
610 610 for (i = 0; i < count; i++) {
611 611 PyObject *stat, *pypath;
612 612 struct stat st;
613 613 int ret, kind;
614 614 char *path;
615 615
616 /* With a large file count or on a slow filesystem,
617 don't block signals for long (issue4878). */
618 if ((i % 1000) == 999 && PyErr_CheckSignals() == -1)
619 goto bail;
620
616 621 pypath = PySequence_GetItem(names, i);
617 622 if (!pypath)
618 623 goto bail;
619 624 path = PyString_AsString(pypath);
620 625 if (path == NULL) {
621 626 Py_DECREF(pypath);
622 627 PyErr_SetString(PyExc_TypeError, "not a string");
623 628 goto bail;
624 629 }
625 630 ret = lstat(path, &st);
626 631 Py_DECREF(pypath);
627 632 kind = st.st_mode & S_IFMT;
628 633 if (ret != -1 && (kind == S_IFREG || kind == S_IFLNK)) {
629 634 stat = makestat(&st);
630 635 if (stat == NULL)
631 636 goto bail;
632 637 PyList_SET_ITEM(stats, i, stat);
633 638 } else {
634 639 Py_INCREF(Py_None);
635 640 PyList_SET_ITEM(stats, i, Py_None);
636 641 }
637 642 }
638 643
639 644 return stats;
640 645
641 646 bail:
642 647 Py_DECREF(stats);
643 648 return NULL;
644 649 }
645 650
646 651 #endif /* ndef _WIN32 */
647 652
648 653 static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs)
649 654 {
650 655 PyObject *statobj = NULL; /* initialize - optional arg */
651 656 PyObject *skipobj = NULL; /* initialize - optional arg */
652 657 char *path, *skip = NULL;
653 658 int wantstat, plen;
654 659
655 660 static char *kwlist[] = {"path", "stat", "skip", NULL};
656 661
657 662 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|OO:listdir",
658 663 kwlist, &path, &plen, &statobj, &skipobj))
659 664 return NULL;
660 665
661 666 wantstat = statobj && PyObject_IsTrue(statobj);
662 667
663 668 if (skipobj && skipobj != Py_None) {
664 669 skip = PyBytes_AsString(skipobj);
665 670 if (!skip)
666 671 return NULL;
667 672 }
668 673
669 674 return _listdir(path, plen, wantstat, skip);
670 675 }
671 676
672 677 #ifdef _WIN32
673 678 static PyObject *posixfile(PyObject *self, PyObject *args, PyObject *kwds)
674 679 {
675 680 static char *kwlist[] = {"name", "mode", "buffering", NULL};
676 681 PyObject *file_obj = NULL;
677 682 char *name = NULL;
678 683 char *mode = "rb";
679 684 DWORD access = 0;
680 685 DWORD creation;
681 686 HANDLE handle;
682 687 int fd, flags = 0;
683 688 int bufsize = -1;
684 689 char m0, m1, m2;
685 690 char fpmode[4];
686 691 int fppos = 0;
687 692 int plus;
688 693 FILE *fp;
689 694
690 695 if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|si:posixfile", kwlist,
691 696 Py_FileSystemDefaultEncoding,
692 697 &name, &mode, &bufsize))
693 698 return NULL;
694 699
695 700 m0 = mode[0];
696 701 m1 = m0 ? mode[1] : '\0';
697 702 m2 = m1 ? mode[2] : '\0';
698 703 plus = m1 == '+' || m2 == '+';
699 704
700 705 fpmode[fppos++] = m0;
701 706 if (m1 == 'b' || m2 == 'b') {
702 707 flags = _O_BINARY;
703 708 fpmode[fppos++] = 'b';
704 709 }
705 710 else
706 711 flags = _O_TEXT;
707 712 if (m0 == 'r' && !plus) {
708 713 flags |= _O_RDONLY;
709 714 access = GENERIC_READ;
710 715 } else {
711 716 /*
712 717 work around http://support.microsoft.com/kb/899149 and
713 718 set _O_RDWR for 'w' and 'a', even if mode has no '+'
714 719 */
715 720 flags |= _O_RDWR;
716 721 access = GENERIC_READ | GENERIC_WRITE;
717 722 fpmode[fppos++] = '+';
718 723 }
719 724 fpmode[fppos++] = '\0';
720 725
721 726 switch (m0) {
722 727 case 'r':
723 728 creation = OPEN_EXISTING;
724 729 break;
725 730 case 'w':
726 731 creation = CREATE_ALWAYS;
727 732 break;
728 733 case 'a':
729 734 creation = OPEN_ALWAYS;
730 735 flags |= _O_APPEND;
731 736 break;
732 737 default:
733 738 PyErr_Format(PyExc_ValueError,
734 739 "mode string must begin with one of 'r', 'w', "
735 740 "or 'a', not '%c'", m0);
736 741 goto bail;
737 742 }
738 743
739 744 handle = CreateFile(name, access,
740 745 FILE_SHARE_READ | FILE_SHARE_WRITE |
741 746 FILE_SHARE_DELETE,
742 747 NULL,
743 748 creation,
744 749 FILE_ATTRIBUTE_NORMAL,
745 750 0);
746 751
747 752 if (handle == INVALID_HANDLE_VALUE) {
748 753 PyErr_SetFromWindowsErrWithFilename(GetLastError(), name);
749 754 goto bail;
750 755 }
751 756
752 757 fd = _open_osfhandle((intptr_t)handle, flags);
753 758
754 759 if (fd == -1) {
755 760 CloseHandle(handle);
756 761 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
757 762 goto bail;
758 763 }
759 764 #ifndef IS_PY3K
760 765 fp = _fdopen(fd, fpmode);
761 766 if (fp == NULL) {
762 767 _close(fd);
763 768 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
764 769 goto bail;
765 770 }
766 771
767 772 file_obj = PyFile_FromFile(fp, name, mode, fclose);
768 773 if (file_obj == NULL) {
769 774 fclose(fp);
770 775 goto bail;
771 776 }
772 777
773 778 PyFile_SetBufSize(file_obj, bufsize);
774 779 #else
775 780 file_obj = PyFile_FromFd(fd, name, mode, bufsize, NULL, NULL, NULL, 1);
776 781 if (file_obj == NULL)
777 782 goto bail;
778 783 #endif
779 784 bail:
780 785 PyMem_Free(name);
781 786 return file_obj;
782 787 }
783 788 #endif
784 789
785 790 #ifdef __APPLE__
786 791 #include <ApplicationServices/ApplicationServices.h>
787 792
788 793 static PyObject *isgui(PyObject *self)
789 794 {
790 795 CFDictionaryRef dict = CGSessionCopyCurrentDictionary();
791 796
792 797 if (dict != NULL) {
793 798 CFRelease(dict);
794 799 Py_RETURN_TRUE;
795 800 } else {
796 801 Py_RETURN_FALSE;
797 802 }
798 803 }
799 804 #endif
800 805
801 806 static char osutil_doc[] = "Native operating system services.";
802 807
803 808 static PyMethodDef methods[] = {
804 809 {"listdir", (PyCFunction)listdir, METH_VARARGS | METH_KEYWORDS,
805 810 "list a directory\n"},
806 811 #ifdef _WIN32
807 812 {"posixfile", (PyCFunction)posixfile, METH_VARARGS | METH_KEYWORDS,
808 813 "Open a file with POSIX-like semantics.\n"
809 814 "On error, this function may raise either a WindowsError or an IOError."},
810 815 #else
811 816 {"statfiles", (PyCFunction)statfiles, METH_VARARGS | METH_KEYWORDS,
812 817 "stat a series of files or symlinks\n"
813 818 "Returns None for non-existent entries and entries of other types.\n"},
814 819 #endif
815 820 #ifdef __APPLE__
816 821 {
817 822 "isgui", (PyCFunction)isgui, METH_NOARGS,
818 823 "Is a CoreGraphics session available?"
819 824 },
820 825 #endif
821 826 {NULL, NULL}
822 827 };
823 828
824 829 #ifdef IS_PY3K
825 830 static struct PyModuleDef osutil_module = {
826 831 PyModuleDef_HEAD_INIT,
827 832 "osutil",
828 833 osutil_doc,
829 834 -1,
830 835 methods
831 836 };
832 837
833 838 PyMODINIT_FUNC PyInit_osutil(void)
834 839 {
835 840 if (PyType_Ready(&listdir_stat_type) < 0)
836 841 return NULL;
837 842
838 843 return PyModule_Create(&osutil_module);
839 844 }
840 845 #else
841 846 PyMODINIT_FUNC initosutil(void)
842 847 {
843 848 if (PyType_Ready(&listdir_stat_type) == -1)
844 849 return;
845 850
846 851 Py_InitModule3("osutil", methods, osutil_doc);
847 852 }
848 853 #endif
General Comments 0
You need to be logged in to leave comments. Login now