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