osutil: major listdir cleanup
authorMatt Mackall <mpm@selenic.com>
Fri, 12 Sep 2008 15:11:02 -0500
changeset 7031 19e8d034932e
parent 7030 20a5dd5d6dd9
child 7032 7dfac37cfabf
osutil: major listdir cleanup
mercurial/osutil.c
--- a/mercurial/osutil.c	Fri Sep 12 16:15:01 2008 +0200
+++ b/mercurial/osutil.c	Fri Sep 12 15:11:02 2008 -0500
@@ -96,208 +96,119 @@
 	listdir_stat_new,          /* tp_new */
 };
 
-static PyObject *listfiles(PyObject *list, DIR *dir,
-			   int keep_stat, int *need_stat)
-{
-	struct dirent *ent;
-	PyObject *name, *py_kind, *val;
-
-#ifdef DT_REG
-	*need_stat = 0;
+#ifdef AT_SYMLINK_NOFOLLOW
+#define USEFDOPEN 1
 #else
-	*need_stat = 1;
-#endif
-
-	for (ent = readdir(dir); ent; ent = readdir(dir)) {
-		int kind = -1;
-
-		if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
-			continue;
-
-#ifdef DT_REG
-		if (!keep_stat)
-			switch (ent->d_type) {
-			case DT_REG: kind = S_IFREG; break;
-			case DT_DIR: kind = S_IFDIR; break;
-			case DT_LNK: kind = S_IFLNK; break;
-			case DT_BLK: kind = S_IFBLK; break;
-			case DT_CHR: kind = S_IFCHR; break;
-			case DT_FIFO: kind = S_IFIFO; break;
-			case DT_SOCK: kind = S_IFSOCK; break;
-			default:
-				*need_stat = 0;
-				break;
-			}
+#define USEFDOPEN 0
 #endif
 
-		if (kind != -1)
-			py_kind = PyInt_FromLong(kind);
-		else {
-			py_kind = Py_None;
-			Py_INCREF(Py_None);
-		}
-
-		val = PyTuple_New(keep_stat ? 3 : 2);
-		name = PyString_FromString(ent->d_name);
-
-		if (!name || !py_kind || !val) {
-			Py_XDECREF(name);
-			Py_XDECREF(py_kind);
-			Py_XDECREF(val);
-			return PyErr_NoMemory();
-		}
-
-		PyTuple_SET_ITEM(val, 0, name);
-		PyTuple_SET_ITEM(val, 1, py_kind);
-		if (keep_stat) {
-			PyTuple_SET_ITEM(val, 2, Py_None);
-			Py_INCREF(Py_None);
-		}
-
-		PyList_Append(list, val);
-		Py_DECREF(val);
-	}
-
-	return 0;
-}
-
-static PyObject *statfiles(PyObject *list, PyObject *ctor_args, int keep,
-			   char *path, int len, int dfd)
+int entkind(struct dirent *ent)
 {
-	struct stat buf;
-	struct stat *stp = &buf;
-	int kind;
-	int ret;
-	ssize_t i;
-	ssize_t size = PyList_Size(list);
-
-	for (i = 0; i < size; i++) {
-		PyObject *elt = PyList_GetItem(list, i);
-		char *name = PyString_AsString(PyTuple_GET_ITEM(elt, 0));
-		PyObject *py_st = NULL;
-		PyObject *py_kind = PyTuple_GET_ITEM(elt, 1);
-
-		kind = py_kind == Py_None ? -1 : PyInt_AsLong(py_kind);
-		if (kind != -1 && !keep)
-			continue;
-
-		strncpy(path + len + 1, name, PATH_MAX - len);
-		path[PATH_MAX] = 0;
-
-		if (keep) {
-			py_st = PyObject_CallObject(
-				(PyObject *)&listdir_stat_type, ctor_args);
-			if (!py_st)
-				return PyErr_NoMemory();
-			stp = &((struct listdir_stat *)py_st)->st;
-			PyTuple_SET_ITEM(elt, 2, py_st);
-		}
-
-#ifdef AT_SYMLINK_NOFOLLOW
-		ret = fstatat(dfd, name, stp, AT_SYMLINK_NOFOLLOW);
-#else
-		ret = lstat(path, stp);
+#ifdef DT_REG
+	switch (ent->d_type) {
+	case DT_REG: return S_IFREG;
+	case DT_DIR: return S_IFDIR;
+	case DT_LNK: return S_IFLNK;
+	case DT_BLK: return S_IFBLK;
+	case DT_CHR: return S_IFCHR;
+	case DT_FIFO: return S_IFIFO;
+	case DT_SOCK: return S_IFSOCK;
+	}
 #endif
-		if (ret == -1)
-			return PyErr_SetFromErrnoWithFilename(PyExc_OSError,
-							      path);
-
-		if (kind == -1) {
-			if (S_ISREG(stp->st_mode))
-				kind = S_IFREG;
-			else if (S_ISDIR(stp->st_mode))
-				kind = S_IFDIR;
-			else if (S_ISLNK(stp->st_mode))
-				kind = S_IFLNK;
-			else if (S_ISBLK(stp->st_mode))
-				kind = S_IFBLK;
-			else if (S_ISCHR(stp->st_mode))
-				kind = S_IFCHR;
-			else if (S_ISFIFO(stp->st_mode))
-				kind = S_IFIFO;
-			else if (S_ISSOCK(stp->st_mode))
-				kind = S_IFSOCK;
-			else
-				kind = stp->st_mode;
-		}
-
-		if (py_kind == Py_None && kind != -1) {
-			py_kind = PyInt_FromLong(kind);
-			if (!py_kind)
-				return PyErr_NoMemory();
-			Py_XDECREF(Py_None);
-			PyTuple_SET_ITEM(elt, 1, py_kind);
-		}
-	}
-
-	return 0;
 }
 
 static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs)
 {
 	static char *kwlist[] = { "path", "stat", NULL };
-	DIR *dir = NULL;
-	PyObject *statobj = NULL;
-	PyObject *list = NULL;
-	PyObject *err = NULL;
-	PyObject *ctor_args = NULL;
-	char *path;
-	char full_path[PATH_MAX + 10];
-	int path_len;
-	int need_stat, keep_stat;
-	int dfd;
+	PyObject *statflag = NULL, *list, *elem, *stat, *ret = NULL;
+	char fullpath[PATH_MAX + 10], *path;
+	int pathlen, keepstat, kind, dfd, err;
+	struct stat st;
+	struct dirent *ent;
+	DIR *dir;
 
 	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|O:listdir", kwlist,
-					 &path, &path_len, &statobj))
-		goto bail;
+					 &path, &pathlen, &statflag))
+		goto error_parse;
+
+	strncpy(fullpath, path, PATH_MAX);
+	fullpath[pathlen] = '/';
+	keepstat = statflag && PyObject_IsTrue(statflag);
 
-	keep_stat = statobj && PyObject_IsTrue(statobj);
-
-#ifdef AT_SYMLINK_NOFOLLOW
-	dfd = open(path, O_RDONLY);
-	if (dfd != -1)
+	if (USEFDOPEN) {
+		dfd = open(path, O_RDONLY);
+		if (dfd == -1) {
+			PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
+			goto error_parse;
+		}
 		dir = fdopendir(dfd);
-#else
-	dir = opendir(path);
-	dfd = -1;
-#endif
+	} else
+		dir = opendir(path);
 	if (!dir) {
-		err = PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
-		goto bail;
-	}
+		PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
+		goto error_dir;
+ 	}
 
 	list = PyList_New(0);
-	ctor_args = PyTuple_New(0);
-	if (!list || !ctor_args)
-		goto bail;
+	if (!list)
+		goto error_list;
+
+	while ((ent = readdir(dir))) {
+		if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
+			continue;
 
-	strncpy(full_path, path, PATH_MAX);
-	full_path[path_len] = '/';
+		kind = entkind(ent);
+		if (kind == -1 || keepstat) {
+			if (USEFDOPEN)
+				err = fstatat(dfd, ent->d_name, &st,
+					      AT_SYMLINK_NOFOLLOW);
+			else {
+				strncpy(fullpath + pathlen + 1, ent->d_name,
+					PATH_MAX - pathlen);
+				fullpath[PATH_MAX] = 0;
+				err = lstat(fullpath, &st);
+			}
+			if (err == -1) {
+				strncpy(fullpath + pathlen + 1, ent->d_name,
+					PATH_MAX - pathlen);
+				fullpath[PATH_MAX] = 0;
+				PyErr_SetFromErrnoWithFilename(PyExc_OSError,
+							       fullpath);
+				goto error;
+			}
+			kind = st.st_mode & S_IFMT;
+		}
 
-	err = listfiles(list, dir, keep_stat, &need_stat);
-	if (err)
-		goto bail;
+		if (keepstat) {
+			stat = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
+			if (!stat)
+				goto error;
+			memcpy(&((struct listdir_stat *)stat)->st, &st, sizeof(st));
+			elem = Py_BuildValue("siN", ent->d_name, kind, stat);
+		} else
+			elem = Py_BuildValue("si", ent->d_name, kind);
+		if (!elem)
+			goto error;
+
+		PyList_Append(list, elem);
+		Py_DECREF(elem);
+	}
 
 	PyList_Sort(list);
-
-	if (!keep_stat && !need_stat)
-		goto done;
-
-	err = statfiles(list, ctor_args, keep_stat, full_path, path_len, dfd);
-	if (!err)
-		goto done;
+	ret = list;
+	Py_INCREF(ret);
 
- bail:
-	Py_XDECREF(list);
-
- done:
-	Py_XDECREF(ctor_args);
-	if (dir)
-		closedir(dir);
-	return err ? err : list;
+error:
+	Py_DECREF(list);
+error_list:
+	closedir(dir);
+error_dir:
+	if (USEFDOPEN)
+ 		close(dfd);
+error_parse:
+	return ret;
 }
 
-
 static char osutil_doc[] = "Native operating system services.";
 
 static PyMethodDef methods[] = {