Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(32807)

Side by Side Diff: Modules/posixmodule.c

Issue 22524: PEP 471 implementation: os.scandir() directory scanning function
Patch Set: Created 4 years, 8 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« Lib/test/test_scandir.py ('K') | « Lib/test/test_scandir.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 1
2 /* POSIX module implementation */ 2 /* POSIX module implementation */
3 3
4 /* This file is also used for Windows NT/MS-Win. In that case the 4 /* This file is also used for Windows NT/MS-Win. In that case the
5 module actually calls itself 'nt', not 'posix', and a few 5 module actually calls itself 'nt', not 'posix', and a few
6 functions are either unimplemented or implemented differently. The source 6 functions are either unimplemented or implemented differently. The source
7 assumes that for Windows NT, the macro 'MS_WINDOWS' is defined independent 7 assumes that for Windows NT, the macro 'MS_WINDOWS' is defined independent
8 of the compiler used. Different compilers define their own feature 8 of the compiler used. Different compilers define their own feature
9 test macro, e.g. '__BORLANDC__' or '_MSC_VER'. */ 9 test macro, e.g. '__BORLANDC__' or '_MSC_VER'. */
10 10
11 11
12 12
13 #ifdef __APPLE__ 13 #ifdef __APPLE__
14 /* 14 /*
15 * Step 1 of support for weak-linking a number of symbols existing on 15 * Step 1 of support for weak-linking a number of symbols existing on
16 * OSX 10.4 and later, see the comment in the #ifdef __APPLE__ block 16 * OSX 10.4 and later, see the comment in the #ifdef __APPLE__ block
17 * at the end of this file for more information. 17 * at the end of this file for more information.
18 */ 18 */
19 # pragma weak lchown 19 # pragma weak lchown
20 # pragma weak statvfs 20 # pragma weak statvfs
21 # pragma weak fstatvfs 21 # pragma weak fstatvfs
22 22
23 #endif /* __APPLE__ */ 23 #endif /* __APPLE__ */
24 24
25 #define PY_SSIZE_T_CLEAN 25 #define PY_SSIZE_T_CLEAN
26 26
27 #include "Python.h" 27 #include "Python.h"
28 #include "structmember.h"
28 #ifndef MS_WINDOWS 29 #ifndef MS_WINDOWS
29 #include "posixmodule.h" 30 #include "posixmodule.h"
30 #else 31 #else
31 #include "winreparse.h" 32 #include "winreparse.h"
32 #endif 33 #endif
33 34
34 #ifdef __cplusplus 35 #ifdef __cplusplus
35 extern "C" { 36 extern "C" {
36 #endif 37 #endif
37 38
(...skipping 16309 matching lines...) Expand 10 before | Expand all | Expand 10 after
16347 return NULL; 16348 return NULL;
16348 16349
16349 if (!_PyVerify_fd(fd)) 16350 if (!_PyVerify_fd(fd))
16350 return posix_error(); 16351 return posix_error();
16351 16352
16352 if (_Py_set_blocking(fd, blocking) < 0) 16353 if (_Py_set_blocking(fd, blocking) < 0)
16353 return NULL; 16354 return NULL;
16354 Py_RETURN_NONE; 16355 Py_RETURN_NONE;
16355 } 16356 }
16356 #endif /* !MS_WINDOWS */ 16357 #endif /* !MS_WINDOWS */
16358
16359
16360 /* Begin implementation of scandir and DirEntry */
16361
16362 PyDoc_STRVAR(posix_scandir__doc__,
16363 "scandir(path='.') -> iterator of DirEntry objects for given path");
16364
16365 static char *_follow_symlinks_keywords[] = {"follow_symlinks", NULL};
haypo 2014/10/07 23:28:49 no need to use a "_" prefix, the variable is local
16366
16367 typedef struct {
16368 PyObject_HEAD
16369 PyObject *name;
16370 PyObject *path;
haypo 2014/10/07 23:28:49 I would prefer to store the "scandir_path" (direct
benhoyt 2014/10/18 22:54:31 Could do. How do you do read-only properties in C?
16371 PyObject *stat;
16372 PyObject *lstat;
16373 #if defined(MS_WINDOWS) && !defined(HAVE_OPENDIR)
16374 struct win32_stat win32_lstat;
16375 #else
16376 unsigned char d_type;
16377 #endif
16378 } DirEntry;
16379
16380 static void
16381 DirEntry_dealloc(DirEntry *entry)
16382 {
16383 Py_XDECREF(entry->name);
16384 Py_XDECREF(entry->path);
16385 Py_XDECREF(entry->stat);
16386 Py_XDECREF(entry->lstat);
16387 Py_TYPE(entry)->tp_free((PyObject *)entry);
16388 }
16389
16390 #if defined(MS_WINDOWS) && !defined(HAVE_OPENDIR)
haypo 2014/10/07 23:28:49 Why not using FindFirstFile if opendir() is availa
benhoyt 2014/10/18 22:54:31 True. I copied this from the existing posix_listdi
16391
16392 typedef unsigned short mode_t;
haypo 2014/10/07 23:28:49 It's not a good idea to reuse names (mode_t) which
benhoyt 2014/10/18 22:54:31 Fixed.
16393
16394 static PyObject *
16395 DirEntry_is_symlink(DirEntry *self)
16396 {
16397 return PyBool_FromLong((self->win32_lstat.st_mode & S_IFMT) == S_IFLNK);
16398 }
16399
16400 static PyObject *
16401 DirEntry_do_stat(DirEntry *self, int follow_symlinks)
haypo 2014/10/07 23:28:49 I suggest the name: DirEntry_get_stat(). IMO two
benhoyt 2014/10/18 22:54:31 Fair point, fixed.
16402 {
16403 if (follow_symlinks) {
16404 if (!self->stat) {
16405 if ((self->win32_lstat.st_mode & S_IFMT) == S_IFLNK) {
16406 path_t path = PATH_T_INITIALIZE("DirEntry.stat", NULL, 0, 0);
16407
16408 if (!path_converter(self->path, &path)) {
16409 return NULL;
16410 }
16411 self->stat = posix_do_stat("DirEntry.stat", &path,
16412 DEFAULT_DIR_FD, follow_symlinks);
haypo 2014/10/07 23:28:49 You can pass 1 instead of follow_symlinks here, to
16413 path_cleanup(&path);
haypo 2014/10/07 23:28:49 I would prefer an explicit error check here (self-
benhoyt 2014/10/18 22:54:31 I don't understand why this is needed. You need to
16414 }
16415 else {
16416 if (!self->lstat) {
16417 self->lstat = _pystat_fromstructstat(&self->win32_lstat);
haypo 2014/10/07 23:28:49 I would prefer an explicit error check here.
benhoyt 2014/10/18 22:54:31 Some as above -- the error/NULL will simply bubble
16418 }
16419 Py_XINCREF(self->lstat);
haypo 2014/10/07 23:28:49 So Py_XINCREF can be replaced with Py_INCREF.
benhoyt 2014/10/18 22:54:31 Hmmm, but is this an advantage? It's just makes th
16420 self->stat = self->lstat;
16421 }
16422 }
16423 Py_XINCREF(self->stat);
16424 return self->stat;
16425 }
16426 else {
16427 if (!self->lstat) {
16428 self->lstat = _pystat_fromstructstat(&self->win32_lstat);
16429 }
16430 Py_XINCREF(self->lstat);
16431 return self->lstat;
16432 }
16433 }
16434
16435 #else /* POSIX || HAVE_OPENDIR */
16436
16437 /* Forward reference */
16438 static PyObject *
16439 DirEntry_is_dir_file(DirEntry *self, int follow_symlinks, mode_t mode_bits);
16440
16441 static PyObject *
16442 DirEntry_is_symlink(DirEntry *self)
16443 {
16444 if (self->d_type != DT_UNKNOWN) {
16445 return PyBool_FromLong(self->d_type == DT_LNK);
16446 }
16447 else {
16448 return DirEntry_is_dir_file(self, 0, S_IFLNK);
16449 }
16450 }
16451
16452 static PyObject *
16453 DirEntry_fetch_stat(DirEntry *self, int follow_symlinks)
16454 {
16455 PyObject *result;
16456 path_t path = PATH_T_INITIALIZE("DirEntry.stat", NULL, 0, 0);
16457
16458 if (!path_converter(self->path, &path)) {
16459 return NULL;
16460 }
16461 result = posix_do_stat("DirEntry.stat", &path, DEFAULT_DIR_FD, follow_symlin ks);
16462 path_cleanup(&path);
16463 return result;
16464 }
16465
16466 static PyObject *
16467 DirEntry_do_stat(DirEntry *self, int follow_symlinks)
16468 {
16469 if (follow_symlinks) {
16470 if (!self->stat) {
16471 int is_symlink;
16472 PyObject *po_is_symlink = DirEntry_is_symlink(self);
16473 if (!po_is_symlink) {
16474 return NULL;
16475 }
16476 is_symlink = PyObject_IsTrue(po_is_symlink);
16477 Py_DECREF(po_is_symlink);
16478
16479 if (is_symlink) {
16480 self->stat = DirEntry_fetch_stat(self, 1);
16481 }
16482 else {
16483 if (!self->lstat) {
16484 self->lstat = DirEntry_fetch_stat(self, 0);
16485 }
16486 Py_XINCREF(self->lstat);
16487 self->stat = self->lstat;
16488 }
16489 }
16490 Py_XINCREF(self->stat);
16491 return self->stat;
16492 }
16493 else {
16494 if (!self->lstat) {
16495 self->lstat = DirEntry_fetch_stat(self, 0);
16496 }
16497 Py_XINCREF(self->lstat);
16498 return self->lstat;
16499 }
16500 }
16501
16502 #endif
16503
16504 static PyObject *
16505 DirEntry_stat(DirEntry *self, PyObject *args, PyObject *kwargs)
16506 {
16507 int follow_symlinks = 1;
16508
16509 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|$p:DirEntry.stat",
16510 _follow_symlinks_keywords,
16511 &follow_symlinks)) {
16512 return NULL;
16513 }
16514
16515 return DirEntry_do_stat(self, follow_symlinks);
16516 }
16517
16518 static PyObject *
16519 DirEntry_is_dir_file(DirEntry *self, int follow_symlinks, mode_t mode_bits)
haypo 2014/10/07 23:28:49 I suggest the name: DirEntry_test_file_type() or D
benhoyt 2014/10/18 22:54:31 Done.
16520 {
16521 PyObject *stat = NULL;
16522 PyObject *st_mode = NULL;
16523 int mode;
16524 int result = 0;
16525 int is_symlink;
16526
16527 #if defined(MS_WINDOWS) && !defined(HAVE_OPENDIR)
16528 is_symlink = (self->win32_lstat.st_mode & S_IFMT) == S_IFLNK;
16529 if (follow_symlinks && is_symlink) {
16530 #else
16531 is_symlink = self->d_type == DT_LNK;
16532 if (self->d_type == DT_UNKNOWN || (follow_symlinks && is_symlink)) {
haypo 2014/10/07 23:28:49 I don't like #ifdef/#else which both start a "{" b
benhoyt 2014/10/18 22:54:31 Good call, done.
16533 #endif
16534 stat = DirEntry_do_stat(self, follow_symlinks);
16535 if (!stat) {
16536 if (PyErr_ExceptionMatches(PyExc_OSError) && errno == ENOENT) {
16537 /* If file doesn't exist (anymore), then return False
16538 (say it's not a directory) */
16539 PyErr_Clear();
16540 Py_RETURN_FALSE;
haypo 2014/10/07 23:28:49 Oh, this behaviour was not defined explicitly in t
benhoyt 2014/10/18 22:54:31 It may not be in the PEP, but we did discuss it on
16541 }
16542 goto error;
16543 }
16544 st_mode = PyObject_GetAttrString(stat, "st_mode");
haypo 2014/10/07 23:28:49 You can call Py_CLEAR(stat) here.
benhoyt 2014/10/18 22:54:31 I don't really understand why you'd use Py_CLEAR(s
16545 if (!st_mode) {
haypo 2014/10/07 23:28:49 style: i prefer without {...} for such simple stat
benhoyt 2014/10/18 22:54:31 I've used consistent always-include-the-braces sty
16546 goto error;
16547 }
16548
16549 mode = PyLong_AsLong(st_mode);
haypo 2014/10/07 23:28:49 You have to check for errors: if (mode == -1 && Py
benhoyt 2014/10/18 22:54:31 Done. But where is it documented that PyLong_AsLon
16550 Py_DECREF(st_mode);
16551 Py_DECREF(stat);
16552 result = (mode & S_IFMT) == mode_bits;
16553 }
16554 else if (is_symlink) {
16555 result = 0;
haypo 2014/10/07 23:28:49 I don't understand why you don't check: result = (
benhoyt 2014/10/18 22:54:31 Yeah, that's right. Added an assert.
16556 }
16557 else {
haypo 2014/10/07 23:28:49 I guess that mode_bits can only be S_IFDIR or S_IF
16558 #if defined(MS_WINDOWS) && !defined(HAVE_OPENDIR)
16559 unsigned long dir_bits = self->win32_lstat.st_file_attributes &
16560 FILE_ATTRIBUTE_DIRECTORY;
16561 result = (mode_bits == S_IFDIR) ? dir_bits != 0 :
16562 dir_bits == 0;
16563 #else
16564 result = (mode_bits == S_IFDIR) ? self->d_type == DT_DIR :
haypo 2014/10/07 23:28:49 style: I prefer an explicit if() instead of the te
benhoyt 2014/10/18 22:54:31 Done.
16565 self->d_type == DT_REG;
16566 #endif
16567 }
16568
16569 return PyBool_FromLong(result);
16570
16571 error:
16572 Py_XDECREF(st_mode);
16573 Py_XDECREF(stat);
16574 return NULL;
16575 }
16576
16577 static PyObject *
16578 DirEntry_is_dir(DirEntry *self, PyObject *args, PyObject *kwargs)
16579 {
16580 int follow_symlinks = 1;
16581
16582 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|$p:DirEntry.is_dir",
16583 _follow_symlinks_keywords,
16584 &follow_symlinks)) {
16585 return NULL;
16586 }
16587
16588 return DirEntry_is_dir_file(self, follow_symlinks, (mode_t)S_IFDIR);
16589 }
16590
16591 static PyObject *
16592 DirEntry_is_file(DirEntry *self, PyObject *args, PyObject *kwargs)
16593 {
16594 int follow_symlinks = 1;
16595
16596 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|$p:DirEntry.is_file",
16597 _follow_symlinks_keywords,
16598 &follow_symlinks)) {
16599 return NULL;
16600 }
16601
16602 return DirEntry_is_dir_file(self, follow_symlinks, (mode_t)S_IFREG);
16603 }
16604
16605 static PyMemberDef DirEntry_members[] = {
16606 {"name", T_OBJECT_EX, offsetof(DirEntry, name), READONLY,
16607 "this entry's filename, relative to scandir()'s \"path\" argument"},
haypo 2014/10/07 23:28:49 In the PEP, you wrote "the entry's filename". It's
16608 {"path", T_OBJECT_EX, offsetof(DirEntry, path), READONLY,
16609 "this entry's full path name, equivalent of os.path.join(scandir_path, entr y.name)"},
16610 {NULL}
16611 };
16612
16613 static PyMethodDef DirEntry_methods[] = {
16614 {"is_dir", (PyCFunction)DirEntry_is_dir, METH_VARARGS | METH_KEYWORDS,
16615 "return True if this entry is a directory; cached per entry"
16616 },
16617 {"is_file", (PyCFunction)DirEntry_is_file, METH_VARARGS | METH_KEYWORDS,
16618 "return True if this entry is a file; cached per entry"
16619 },
16620 {"is_symlink", (PyCFunction)DirEntry_is_symlink, METH_NOARGS,
16621 "return True if this entry is a symbolic link; cached per entry"
16622 },
16623 {"stat", (PyCFunction)DirEntry_stat, METH_VARARGS | METH_KEYWORDS,
16624 "return stat_result object for this entry; cached per entry"
16625 },
16626 {NULL}
16627 };
16628
16629 PyTypeObject DirEntryType = {
16630 PyVarObject_HEAD_INIT(NULL, 0)
16631 "DirEntry", /* tp_name */
16632 sizeof(DirEntry), /* tp_basicsize */
16633 0, /* tp_itemsize */
16634 /* methods */
16635 (destructor)DirEntry_dealloc, /* tp_dealloc */
16636 0, /* tp_print */
16637 0, /* tp_getattr */
16638 0, /* tp_setattr */
16639 0, /* tp_compare */
16640 0, /* tp_repr */
16641 0, /* tp_as_number */
16642 0, /* tp_as_sequence */
16643 0, /* tp_as_mapping */
16644 0, /* tp_hash */
16645 0, /* tp_call */
16646 0, /* tp_str */
16647 0, /* tp_getattro */
16648 0, /* tp_setattro */
16649 0, /* tp_as_buffer */
16650 Py_TPFLAGS_DEFAULT, /* tp_flags */
16651 0, /* tp_doc */
16652 0, /* tp_traverse */
16653 0, /* tp_clear */
16654 0, /* tp_richcompare */
16655 0, /* tp_weaklistoffset */
16656 0, /* tp_iter */
16657 0, /* tp_iternext */
16658 DirEntry_methods, /* tp_methods */
16659 DirEntry_members, /* tp_members */
16660 };
16661
16662 static char *
16663 _join_path_filenameA(char *path_narrow, char* filename, Py_ssize_t filename_len)
haypo 2014/10/07 23:28:49 no need for "_" prefix. the second parameter may
16664 {
16665 Py_ssize_t path_len;
16666 char *result;
16667
16668 if (!path_narrow) { /* Default arg: "." */
16669 path_narrow = ".";
haypo 2014/10/07 23:28:49 Does it mean that you build a path like "./name"?
benhoyt 2014/10/18 22:54:31 No, this happens when you call scandir() with no a
16670 path_len = 1;
16671 }
16672 else {
16673 path_len = strlen(path_narrow);
16674 }
16675 path_len = strlen(path_narrow);
haypo 2014/10/07 23:28:49 hum? this second call looks like a mistake.
benhoyt 2014/10/18 22:54:31 You're dead right. Fixed.
16676
16677 if (filename_len == -1) {
haypo 2014/10/07 23:28:49 style: no need for {...}
16678 filename_len = strlen(filename);
16679 }
16680
16681 /* The +2 is for the path separator and the NUL */
16682 result = PyMem_Malloc(path_len + filename_len + 2);
haypo 2014/10/07 23:28:49 you can write it: path_len + 1 + filename_len + 1,
16683 if (!result) {
16684 PyErr_NoMemory();
16685 return NULL;
16686 }
16687 strcpy(result, path_narrow);
16688 if (path_len > 0) {
haypo 2014/10/07 23:28:49 I don't understand how path_len can be negative. I
benhoyt 2014/10/18 22:54:31 I don't see how path_len can be negative either. I
16689 char ch = result[path_len - 1];
16690 #if defined(MS_WINDOWS) && !defined(HAVE_OPENDIR)
haypo 2014/10/07 23:28:49 ntpath.join() is very complex. This code should be
benhoyt 2014/10/18 22:54:31 I think this is okay. This isn't an actual os.path
16691 if (ch != '\\' && ch != '/' && ch != ':')
16692 result[path_len++] = '\\';
haypo 2014/10/07 23:28:49 nitpick: you may check before calling PyMem_Malloc
benhoyt 2014/10/18 22:54:31 It might save 1 byte in some cases, so I'd rather
16693 #else
16694 if (ch != '/')
16695 result[path_len++] = '/';
16696 #endif
16697 strcpy(result + path_len, filename);
haypo 2014/10/07 23:28:49 filename_len is not used there. I guess that it's
benhoyt 2014/10/18 22:54:31 Yeah, just an optimization -- if we know it, we pa
16698 }
16699 return result;
16700 }
16701
16702 #if defined(MS_WINDOWS) && !defined(HAVE_OPENDIR)
16703
16704 static void
16705 find_data_to_stat(WIN32_FIND_DATAW *data, struct win32_stat *result)
16706 {
16707 /* Note: data argument can point to a WIN32_FIND_DATAW or a
16708 WIN32_FIND_DATAA struct, as the first members are in the same
16709 position, and cFileName is not used here
16710 */
16711 memset(result, 0, sizeof(*result));
16712
16713 result->st_mode = attributes_to_mode(data->dwFileAttributes);
16714 if ((data->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0 &&
16715 (data->dwReserved0 == IO_REPARSE_TAG_SYMLINK)) {
16716 /* first clear the S_IFMT bits */
16717 result->st_mode ^= (result->st_mode & S_IFMT);
16718 /* now set the bits that make this a symlink */
16719 result->st_mode |= S_IFLNK;
16720 }
16721
16722 result->st_size = (((__int64)data->nFileSizeHigh)<<32) + data->nFileSizeLow;
16723
16724 FILE_TIME_to_time_t_nsec(&data->ftCreationTime, &result->st_ctime, &result-> st_ctime_nsec);
16725 FILE_TIME_to_time_t_nsec(&data->ftLastWriteTime, &result->st_mtime, &result- >st_mtime_nsec);
16726 FILE_TIME_to_time_t_nsec(&data->ftLastAccessTime, &result->st_atime, &result ->st_atime_nsec);
16727
16728 result->st_file_attributes = data->dwFileAttributes;
16729 }
16730
16731 static wchar_t *
16732 _join_path_filenameW(wchar_t *path_wide, wchar_t* filename)
haypo 2014/10/07 23:28:49 same remaks than _join_path_filenameA ;)
16733 {
16734 Py_ssize_t path_len;
16735 wchar_t *result;
16736
16737 if (!path_wide) { /* Default arg: "." */
16738 path_wide = L".";
16739 path_len = 1;
16740 }
16741 else {
16742 path_len = wcslen(path_wide);
16743 }
16744 /* The +2 is for the path separator and the NUL */
16745 result = PyMem_Malloc((path_len + wcslen(filename) + 2) * sizeof(wchar_t));
16746 if (!result) {
16747 PyErr_NoMemory();
16748 return NULL;
16749 }
16750 wcscpy(result, path_wide);
16751 if (path_len > 0) {
16752 wchar_t ch = result[path_len - 1];
16753 if (ch != SEP && ch != ALTSEP && ch != L':')
16754 result[path_len++] = SEP;
16755 wcscpy(result + path_len, filename);
16756 }
16757 return result;
16758 }
16759
16760 static PyObject *
16761 make_DirEntry(path_t *path, void *data)
haypo 2014/10/07 23:28:49 the classic naming theme is: DirEntry_new.
benhoyt 2014/10/18 22:54:31 Fixed.
16762 {
16763 DirEntry *entry;
16764
16765 entry = PyObject_New(DirEntry, &DirEntryType);
16766 if (!entry) {
16767 return NULL;
16768 }
16769 entry->name = NULL;
16770 entry->path = NULL;
16771 entry->stat = NULL;
16772 entry->lstat = NULL;
16773
16774 if (!path->narrow) {
16775 WIN32_FIND_DATAW *dataW = (WIN32_FIND_DATAW *)data;
16776 wchar_t *path_strW;
16777
16778 entry->name = PyUnicode_FromWideChar(dataW->cFileName, wcslen(dataW->cFi leName));
16779 if (!entry->name) {
16780 goto error;
16781 }
16782
16783 path_strW = _join_path_filenameW(path->wide, dataW->cFileName);
16784 if (!path_strW) {
16785 goto error;
16786 }
16787 entry->path = PyUnicode_FromWideChar(path_strW, wcslen(path_strW));
16788 PyMem_Free(path_strW);
16789 if (!entry->path) {
16790 goto error;
16791 }
16792 }
16793 else {
16794 WIN32_FIND_DATAA *dataA = (WIN32_FIND_DATAA *)data;
16795 char *path_strA;
16796
16797 entry->name = PyBytes_FromString(dataA->cFileName);
16798 if (!entry->name) {
16799 goto error;
16800 }
16801
16802 path_strA = _join_path_filenameA(path->narrow, dataA->cFileName, -1);
16803 if (!path_strA) {
16804 goto error;
16805 }
16806 entry->path = PyBytes_FromString(path_strA);
16807 PyMem_Free(path_strA);
16808 if (!entry->path) {
16809 goto error;
16810 }
16811 }
16812 find_data_to_stat((WIN32_FIND_DATAW *)data, &entry->win32_lstat);
16813
16814 return (PyObject *)entry;
16815
16816 error:
16817 Py_XDECREF(entry);
16818 return NULL;
16819 }
16820
16821 #else /* POSIX || HAVE_OPENDIR */
16822
16823 static PyObject *
16824 make_DirEntry(path_t *path, char *name, Py_ssize_t name_len, unsigned char d_typ e)
16825 {
16826 DirEntry *entry;
16827 char *joined_path;
16828
16829 entry = PyObject_New(DirEntry, &DirEntryType);
16830 if (!entry) {
16831 return NULL;
16832 }
16833 entry->name = NULL;
16834 entry->path = NULL;
16835 entry->stat = NULL;
16836 entry->lstat = NULL;
16837
16838 joined_path = _join_path_filenameA(path->narrow, name, name_len);
16839 if (!joined_path) {
16840 goto error;
16841 }
16842
16843 if (!path->narrow || !PyBytes_Check(path->object)) {
16844 entry->name = PyUnicode_DecodeFSDefaultAndSize(name, name_len);
16845 entry->path = PyUnicode_DecodeFSDefault(joined_path);
16846 }
16847 else {
16848 entry->name = PyBytes_FromStringAndSize(name, name_len);
16849 entry->path = PyBytes_FromString(joined_path);
16850 }
16851 PyMem_Free(joined_path);
16852 if (!entry->name || !entry->path) {
16853 goto error;
16854 }
16855
16856 entry->d_type = d_type;
16857
16858 return (PyObject *)entry;
16859
16860 error:
16861 Py_XDECREF(entry);
16862 return NULL;
16863 }
16864 #endif
16865
16866 typedef struct {
16867 PyObject_HEAD
16868 path_t path;
16869 int yield_name; /* for when listdir() is implemented using scandir() */
16870 #if defined(MS_WINDOWS) && !defined(HAVE_OPENDIR)
16871 HANDLE handle;
16872 #else
16873 DIR *dirp;
16874 #endif
16875 } ScandirIterator;
16876
16877 static void
16878 ScandirIterator_dealloc(ScandirIterator *iterator)
16879 {
16880 Py_XDECREF(iterator->path.object);
16881 path_cleanup(&iterator->path);
16882 Py_TYPE(iterator)->tp_free((PyObject *)iterator);
16883 }
16884
16885 #if defined(MS_WINDOWS) && !defined(HAVE_OPENDIR)
16886
16887 static PyObject *
16888 ScandirIterator_iternext(ScandirIterator *iterator)
16889 {
16890 union { /* We only use one at a time, so save space */
16891 WIN32_FIND_DATAW W;
16892 WIN32_FIND_DATAA A;
16893 } FileData;
16894
16895 int is_unicode = !iterator->path.narrow;
16896
16897 while (1) {
16898 if (iterator->handle == INVALID_HANDLE_VALUE) {
16899 /* First time around, prepare path and call FindFirstFile */
16900 if (is_unicode) {
16901 wchar_t *path_strW;
16902
16903 path_strW = _join_path_filenameW(iterator->path.wide, L"*.*");
16904 if (!path_strW) {
16905 return NULL;
16906 }
16907
16908 Py_BEGIN_ALLOW_THREADS
16909 iterator->handle = FindFirstFileW(path_strW, &FileData.W);
16910 Py_END_ALLOW_THREADS
16911
16912 PyMem_Free(path_strW); /* We're done with path_strW now */
16913 }
16914 else {
16915 char *path_strA;
16916
16917 path_strA = _join_path_filenameA(iterator->path.narrow, "*.*", - 1);
16918 if (!path_strA) {
16919 return NULL;
16920 }
16921
16922 Py_BEGIN_ALLOW_THREADS
16923 iterator->handle = FindFirstFileA(path_strA, &FileData.A);
16924 Py_END_ALLOW_THREADS
16925
16926 PyMem_Free(path_strA); /* We're done with path_strA now */
16927 }
16928
16929 if (iterator->handle == INVALID_HANDLE_VALUE) {
16930 if (GetLastError() != ERROR_FILE_NOT_FOUND) {
16931 return path_error(&iterator->path);
16932 }
16933 /* No files found, stop iterating */
16934 PyErr_SetNone(PyExc_StopIteration);
16935 return NULL;
16936 }
16937 }
16938 else {
16939 BOOL success;
16940
16941 Py_BEGIN_ALLOW_THREADS
16942 success = is_unicode ? FindNextFileW(iterator->handle, &FileData.W) :
16943 FindNextFileA(iterator->handle, &FileData.A);
16944 Py_END_ALLOW_THREADS
16945
16946 if (!success) {
16947 if (GetLastError() != ERROR_NO_MORE_FILES) {
16948 return path_error(&iterator->path);
16949 }
16950 /* No more files found in directory, stop iterating */
16951 Py_BEGIN_ALLOW_THREADS
16952 success = FindClose(iterator->handle);
16953 Py_END_ALLOW_THREADS
16954 if (!success) {
16955 return path_error(&iterator->path);
16956 }
16957 iterator->handle = INVALID_HANDLE_VALUE;
16958
16959 PyErr_SetNone(PyExc_StopIteration);
16960 return NULL;
16961 }
16962 }
16963
16964 /* Skip over . and .. */
16965 if (is_unicode) {
16966 if (wcscmp(FileData.W.cFileName, L".") != 0 &&
16967 wcscmp(FileData.W.cFileName, L"..") != 0) {
16968 if (iterator->yield_name) {
16969 return PyUnicode_FromWideChar(FileData.W.cFileName, wcslen(F ileData.W.cFileName));
16970 }
16971 else {
16972 return make_DirEntry(&iterator->path, &FileData.W);
16973 }
16974 }
16975 }
16976 else {
16977 if (strcmp(FileData.A.cFileName, ".") != 0 &&
16978 strcmp(FileData.A.cFileName, "..") != 0) {
16979 if (iterator->yield_name) {
16980 return PyBytes_FromString(FileData.A.cFileName);
16981 }
16982 else {
16983 return make_DirEntry(&iterator->path, &FileData.A);
16984 }
16985 }
16986 }
16987
16988 /* Loop till we get a non-dot directory or finish iterating */
16989 }
16990 }
16991
16992 #else /* POSIX || HAVE_OPENDIR */
16993
16994 static PyObject *
16995 ScandirIterator_iternext(ScandirIterator *iterator)
16996 {
16997 struct dirent *direntp;
16998 Py_ssize_t name_len;
16999 int is_dot;
17000
17001 if (!iterator->dirp) {
17002 /* First time iterating, prepare path and call opendir */
17003 errno = 0;
17004 Py_BEGIN_ALLOW_THREADS
17005 iterator->dirp = opendir(iterator->path.narrow ? iterator->path.narrow : ".");
17006 Py_END_ALLOW_THREADS
17007
17008 if (!iterator->dirp) {
17009 return path_error(&iterator->path);
17010 }
17011 }
17012
17013 while (1) {
17014 errno = 0;
17015 Py_BEGIN_ALLOW_THREADS
17016 direntp = readdir(iterator->dirp);
17017 Py_END_ALLOW_THREADS
17018
17019 if (!direntp) {
17020 int result;
17021
17022 if (errno != 0) {
17023 return path_error(&iterator->path);
17024 }
17025
17026 /* No more files found in directory, stop iterating */
17027 Py_BEGIN_ALLOW_THREADS
17028 result = closedir(iterator->dirp);
17029 Py_END_ALLOW_THREADS
17030 if (result != 0) {
17031 return path_error(&iterator->path);
17032 }
17033 iterator->dirp = NULL;
17034
17035 PyErr_SetNone(PyExc_StopIteration);
17036 return NULL;
17037 }
17038
17039 /* Skip over . and .. */
17040 name_len = NAMLEN(direntp);
17041 is_dot = direntp->d_name[0] == '.' &&
17042 (name_len == 1 || (direntp->d_name[1] == '.' && name_len == 2)) ;
17043 if (!is_dot) {
17044 if (!iterator->yield_name) {
17045 return make_DirEntry(&iterator->path, direntp->d_name, name_len,
17046 direntp->d_type);
17047 }
17048 if (!iterator->path.narrow || !PyBytes_Check(iterator->path.object)) {
17049 return PyUnicode_DecodeFSDefaultAndSize(direntp->d_name, name_le n);
17050 }
17051 else {
17052 return PyBytes_FromStringAndSize(direntp->d_name, name_len);
17053 }
17054 }
17055
17056 /* Loop till we get a non-dot directory or finish iterating */
17057 }
17058 }
17059
17060 #endif
17061
17062 PyTypeObject ScandirIteratorType = {
17063 PyVarObject_HEAD_INIT(NULL, 0)
17064 "ScandirIterator", /* tp_name */
17065 sizeof(ScandirIterator), /* tp_basicsize */
17066 0, /* tp_itemsize */
17067 /* methods */
17068 (destructor)ScandirIterator_dealloc, /* tp_dealloc */
17069 0, /* tp_print */
17070 0, /* tp_getattr */
17071 0, /* tp_setattr */
17072 0, /* tp_compare */
17073 0, /* tp_repr */
17074 0, /* tp_as_number */
17075 0, /* tp_as_sequence */
17076 0, /* tp_as_mapping */
17077 0, /* tp_hash */
17078 0, /* tp_call */
17079 0, /* tp_str */
17080 0, /* tp_getattro */
17081 0, /* tp_setattro */
17082 0, /* tp_as_buffer */
17083 Py_TPFLAGS_DEFAULT, /* tp_flags */
17084 0, /* tp_doc */
17085 0, /* tp_traverse */
17086 0, /* tp_clear */
17087 0, /* tp_richcompare */
17088 0, /* tp_weaklistoffset */
17089 PyObject_SelfIter, /* tp_iter */
17090 (iternextfunc)ScandirIterator_iternext, /* tp_iternext */
17091 };
17092
17093 static PyObject *
17094 posix_scandir(PyObject *self, PyObject *args, PyObject *kwargs)
17095 {
17096 ScandirIterator *iterator;
17097 static char *keywords[] = {"path", NULL};
17098
17099 iterator = PyObject_New(ScandirIterator, &ScandirIteratorType);
17100 if (!iterator) {
17101 return NULL;
17102 }
17103 iterator->yield_name = 0;
17104 memset(&iterator->path, 0, sizeof(path_t));
17105 iterator->path.function_name = "scandir";
17106 iterator->path.nullable = 1;
17107
17108 #if defined(MS_WINDOWS) && !defined(HAVE_OPENDIR)
17109 iterator->handle = INVALID_HANDLE_VALUE;
17110 #else
17111 iterator->dirp = NULL;
17112 #endif
17113
17114 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O&:scandir", keywords,
17115 path_converter, &iterator->path)) {
17116 Py_DECREF(iterator);
17117 return NULL;
17118 }
17119
17120 /* path_converter doesn't keep path.object around, so do it
17121 manually for the lifetime of the iterator here (the refcount
17122 is decremented in ScandirIterator_dealloc)
17123 */
17124 Py_XINCREF(iterator->path.object);
17125
17126 return (PyObject *)iterator;
17127 }
17128
17129 /* End implementation of scandir and DirEntry */
16357 17130
16358 17131
16359 /*[clinic input] 17132 /*[clinic input]
16360 dump buffer 17133 dump buffer
16361 [clinic start generated code]*/ 17134 [clinic start generated code]*/
16362 17135
16363 #ifndef OS_TTYNAME_METHODDEF 17136 #ifndef OS_TTYNAME_METHODDEF
16364 #define OS_TTYNAME_METHODDEF 17137 #define OS_TTYNAME_METHODDEF
16365 #endif /* !defined(OS_TTYNAME_METHODDEF) */ 17138 #endif /* !defined(OS_TTYNAME_METHODDEF) */
16366 17139
(...skipping 654 matching lines...) Expand 10 before | Expand all | Expand 10 after
17021 #endif 17794 #endif
17022 OS_CPU_COUNT_METHODDEF 17795 OS_CPU_COUNT_METHODDEF
17023 OS_GET_INHERITABLE_METHODDEF 17796 OS_GET_INHERITABLE_METHODDEF
17024 OS_SET_INHERITABLE_METHODDEF 17797 OS_SET_INHERITABLE_METHODDEF
17025 OS_GET_HANDLE_INHERITABLE_METHODDEF 17798 OS_GET_HANDLE_INHERITABLE_METHODDEF
17026 OS_SET_HANDLE_INHERITABLE_METHODDEF 17799 OS_SET_HANDLE_INHERITABLE_METHODDEF
17027 #ifndef MS_WINDOWS 17800 #ifndef MS_WINDOWS
17028 {"get_blocking", posix_get_blocking, METH_VARARGS, get_blocking__doc__}, 17801 {"get_blocking", posix_get_blocking, METH_VARARGS, get_blocking__doc__},
17029 {"set_blocking", posix_set_blocking, METH_VARARGS, set_blocking__doc__}, 17802 {"set_blocking", posix_set_blocking, METH_VARARGS, set_blocking__doc__},
17030 #endif 17803 #endif
17804 {"scandir", (PyCFunction)posix_scandir, METH_VARARGS | METH_KEYWORDS,
17805 posix_scandir__doc__},
17031 {NULL, NULL} /* Sentinel */ 17806 {NULL, NULL} /* Sentinel */
17032 }; 17807 };
17033 17808
17034 17809
17035 #if defined(HAVE_SYMLINK) && defined(MS_WINDOWS) 17810 #if defined(HAVE_SYMLINK) && defined(MS_WINDOWS)
17036 static int 17811 static int
17037 enable_symlink() 17812 enable_symlink()
17038 { 17813 {
17039 HANDLE tok; 17814 HANDLE tok;
17040 TOKEN_PRIVILEGES tok_priv; 17815 TOKEN_PRIVILEGES tok_priv;
(...skipping 650 matching lines...) Expand 10 before | Expand all | Expand 10 after
17691 #if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) 18466 #if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER)
17692 sched_param_desc.name = MODNAME ".sched_param"; 18467 sched_param_desc.name = MODNAME ".sched_param";
17693 if (PyStructSequence_InitType2(&SchedParamType, &sched_param_desc) < 0) 18468 if (PyStructSequence_InitType2(&SchedParamType, &sched_param_desc) < 0)
17694 return NULL; 18469 return NULL;
17695 SchedParamType.tp_new = os_sched_param; 18470 SchedParamType.tp_new = os_sched_param;
17696 #endif 18471 #endif
17697 18472
17698 /* initialize TerminalSize_info */ 18473 /* initialize TerminalSize_info */
17699 if (PyStructSequence_InitType2(&TerminalSizeType, 18474 if (PyStructSequence_InitType2(&TerminalSizeType,
17700 &TerminalSize_desc) < 0) 18475 &TerminalSize_desc) < 0)
18476 return NULL;
18477
18478 /* initialize scandir types */
18479 if (PyType_Ready(&ScandirIteratorType) < 0)
18480 return NULL;
18481 if (PyType_Ready(&DirEntryType) < 0)
17701 return NULL; 18482 return NULL;
17702 } 18483 }
17703 #if defined(HAVE_WAITID) && !defined(__APPLE__) 18484 #if defined(HAVE_WAITID) && !defined(__APPLE__)
17704 Py_INCREF((PyObject*) &WaitidResultType); 18485 Py_INCREF((PyObject*) &WaitidResultType);
17705 PyModule_AddObject(m, "waitid_result", (PyObject*) &WaitidResultType); 18486 PyModule_AddObject(m, "waitid_result", (PyObject*) &WaitidResultType);
17706 #endif 18487 #endif
17707 Py_INCREF((PyObject*) &StatResultType); 18488 Py_INCREF((PyObject*) &StatResultType);
17708 PyModule_AddObject(m, "stat_result", (PyObject*) &StatResultType); 18489 PyModule_AddObject(m, "stat_result", (PyObject*) &StatResultType);
17709 Py_INCREF((PyObject*) &StatVFSResultType); 18490 Py_INCREF((PyObject*) &StatVFSResultType);
17710 PyModule_AddObject(m, "statvfs_result", 18491 PyModule_AddObject(m, "statvfs_result",
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after
17798 PyModule_AddObject(m, "_have_functions", list); 18579 PyModule_AddObject(m, "_have_functions", list);
17799 18580
17800 initialized = 1; 18581 initialized = 1;
17801 18582
17802 return m; 18583 return m;
17803 } 18584 }
17804 18585
17805 #ifdef __cplusplus 18586 #ifdef __cplusplus
17806 } 18587 }
17807 #endif 18588 #endif
OLDNEW
« Lib/test/test_scandir.py ('K') | « Lib/test/test_scandir.py ('k') | no next file » | no next file with comments »

RSS Feeds Recent Issues | This issue
This is Rietveld 894c83f36cb7+