From 906fc557b70b2b2995785c9b37e212d2f86b469e Mon Sep 17 00:00:00 2001
From: Elijah Newren <newren@gmail.com>
Date: Thu, 27 May 2021 04:53:56 +0000
Subject: [PATCH] dir: introduce readdir_skip_dot_and_dotdot() helper

Many places in the code were doing
    while ((d = readdir(dir)) != NULL) {
        if (is_dot_or_dotdot(d->d_name))
            continue;
        ...process d...
    }
Introduce a readdir_skip_dot_and_dotdot() helper to make that a one-liner:
    while ((d = readdir_skip_dot_and_dotdot(dir)) != NULL) {
        ...process d...
    }

This helper particularly simplifies checks for empty directories.

Also use this helper in read_cached_dir() so that our statistics are
consistent across platforms.  (In other words, read_cached_dir() should
have been using is_dot_or_dotdot() and skipping such entries, but did
not and left it to treat_path() to detect and mark such entries as
path_none.)

Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/clean.c    |  4 +---
 builtin/worktree.c |  4 +---
 diff-no-index.c    |  5 ++---
 dir.c              | 25 ++++++++++++++++---------
 dir.h              |  2 ++
 entry.c            |  5 +----
 notes-merge.c      |  5 +----
 object-file.c      |  4 +---
 packfile.c         |  5 +----
 rerere.c           |  4 +---
 worktree.c         | 12 +++---------
 11 files changed, 30 insertions(+), 45 deletions(-)

Index: git-2.26.2/builtin/clean.c
===================================================================
--- git-2.26.2.orig/builtin/clean.c
+++ git-2.26.2/builtin/clean.c
@@ -188,10 +188,8 @@ static int remove_dirs(struct strbuf *pa
 	strbuf_complete(path, '/');
 
 	len = path->len;
-	while ((e = readdir(dir)) != NULL) {
+	while ((e = readdir_skip_dot_and_dotdot(dir)) != NULL) {
 		struct stat st;
-		if (is_dot_or_dotdot(e->d_name))
-			continue;
 
 		strbuf_setlen(path, len);
 		strbuf_addstr(path, e->d_name);
Index: git-2.26.2/builtin/worktree.c
===================================================================
--- git-2.26.2.orig/builtin/worktree.c
+++ git-2.26.2/builtin/worktree.c
@@ -140,9 +140,7 @@ static void prune_worktrees(void)
 	struct dirent *d;
 	if (!dir)
 		return;
-	while ((d = readdir(dir)) != NULL) {
-		if (is_dot_or_dotdot(d->d_name))
-			continue;
+	while ((d = readdir_skip_dot_and_dotdot(dir)) != NULL) {
 		strbuf_reset(&reason);
 		if (!prune_worktree(d->d_name, &reason))
 			continue;
Index: git-2.26.2/diff-no-index.c
===================================================================
--- git-2.26.2.orig/diff-no-index.c
+++ git-2.26.2/diff-no-index.c
@@ -26,9 +26,8 @@ static int read_directory_contents(const
 	if (!(dir = opendir(path)))
 		return error("Could not open directory %s", path);
 
-	while ((e = readdir(dir)))
-		if (!is_dot_or_dotdot(e->d_name))
-			string_list_insert(list, e->d_name);
+	while ((e = readdir_skip_dot_and_dotdot(dir)))
+		string_list_insert(list, e->d_name);
 
 	closedir(dir);
 	return 0;
Index: git-2.26.2/dir.c
===================================================================
--- git-2.26.2.orig/dir.c
+++ git-2.26.2/dir.c
@@ -54,6 +54,17 @@ static enum path_treatment read_director
 static int resolve_dtype(int dtype, struct index_state *istate,
 			 const char *path, int len);
 
+struct dirent *readdir_skip_dot_and_dotdot(DIR *dirp)
+{
+	struct dirent *e;
+
+	while ((e = readdir(dirp)) != NULL) {
+		if (!is_dot_or_dotdot(e->d_name))
+			break;
+	}
+	return e;
+}
+
 int count_slashes(const char *s)
 {
 	int cnt = 0;
@@ -2156,7 +2167,7 @@ static int read_cached_dir(struct cached
 	struct dirent *de;
 
 	if (cdir->fdir) {
-		de = readdir(cdir->fdir);
+		de = readdir_skip_dot_and_dotdot(cdir->fdir);
 		if (!de) {
 			cdir->d_name = NULL;
 			cdir->d_type = DT_UNKNOWN;
@@ -2782,11 +2793,9 @@ int is_empty_dir(const char *path)
 	if (!dir)
 		return 0;
 
-	while ((e = readdir(dir)) != NULL)
-		if (!is_dot_or_dotdot(e->d_name)) {
-			ret = 0;
-			break;
-		}
+	e = readdir_skip_dot_and_dotdot(dir);
+	if (e)
+		ret = 0;
 
 	closedir(dir);
 	return ret;
@@ -2826,10 +2835,8 @@ static int remove_dir_recurse(struct str
 	strbuf_complete(path, '/');
 
 	len = path->len;
-	while ((e = readdir(dir)) != NULL) {
+	while ((e = readdir_skip_dot_and_dotdot(dir)) != NULL) {
 		struct stat st;
-		if (is_dot_or_dotdot(e->d_name))
-			continue;
 
 		strbuf_setlen(path, len);
 		strbuf_addstr(path, e->d_name);
Index: git-2.26.2/dir.h
===================================================================
--- git-2.26.2.orig/dir.h
+++ git-2.26.2/dir.h
@@ -339,6 +339,8 @@ struct dir_struct {
 	unsigned unmanaged_exclude_files;
 };
 
+struct dirent *readdir_skip_dot_and_dotdot(DIR *dirp);
+
 /*Count the number of slashes for string s*/
 int count_slashes(const char *s);
 
Index: git-2.26.2/entry.c
===================================================================
--- git-2.26.2.orig/entry.c
+++ git-2.26.2/entry.c
@@ -56,12 +56,9 @@ static void remove_subtree(struct strbuf
 
 	if (!dir)
 		die_errno("cannot opendir '%s'", path->buf);
-	while ((de = readdir(dir)) != NULL) {
+	while ((de = readdir_skip_dot_and_dotdot(dir)) != NULL) {
 		struct stat st;
 
-		if (is_dot_or_dotdot(de->d_name))
-			continue;
-
 		strbuf_addch(path, '/');
 		strbuf_addstr(path, de->d_name);
 		if (lstat(path->buf, &st))
Index: git-2.26.2/notes-merge.c
===================================================================
--- git-2.26.2.orig/notes-merge.c
+++ git-2.26.2/notes-merge.c
@@ -695,13 +695,10 @@ int notes_merge_commit(struct notes_merg
 
 	strbuf_addch(&path, '/');
 	baselen = path.len;
-	while ((e = readdir(dir)) != NULL) {
+	while ((e = readdir_skip_dot_and_dotdot(dir)) != NULL) {
 		struct stat st;
 		struct object_id obj_oid, blob_oid;
 
-		if (is_dot_or_dotdot(e->d_name))
-			continue;
-
 		if (get_oid_hex(e->d_name, &obj_oid)) {
 			if (o->verbosity >= 3)
 				printf("Skipping non-SHA1 entry '%s%s'\n",
Index: git-2.26.2/packfile.c
===================================================================
--- git-2.26.2.orig/packfile.c
+++ git-2.26.2/packfile.c
@@ -817,10 +817,7 @@ void for_each_file_in_pack_dir(const cha
 	}
 	strbuf_addch(&path, '/');
 	dirnamelen = path.len;
-	while ((de = readdir(dir)) != NULL) {
-		if (is_dot_or_dotdot(de->d_name))
-			continue;
-
+	while ((de = readdir_skip_dot_and_dotdot(dir)) != NULL) {
 		strbuf_setlen(&path, dirnamelen);
 		strbuf_addstr(&path, de->d_name);
 
Index: git-2.26.2/rerere.c
===================================================================
--- git-2.26.2.orig/rerere.c
+++ git-2.26.2/rerere.c
@@ -1198,13 +1198,11 @@ void rerere_gc(struct repository *r, str
 	if (!dir)
 		die_errno(_("unable to open rr-cache directory"));
 	/* Collect stale conflict IDs ... */
-	while ((e = readdir(dir))) {
+	while ((e = readdir_skip_dot_and_dotdot(dir))) {
 		struct rerere_dir *rr_dir;
 		struct rerere_id id;
 		int now_empty;
 
-		if (is_dot_or_dotdot(e->d_name))
-			continue;
 		rr_dir = find_rerere_dir(e->d_name);
 		if (!rr_dir)
 			continue; /* or should we remove e->d_name? */
Index: git-2.26.2/worktree.c
===================================================================
--- git-2.26.2.orig/worktree.c
+++ git-2.26.2/worktree.c
@@ -146,10 +146,8 @@ struct worktree **get_worktrees(unsigned
 	dir = opendir(path.buf);
 	strbuf_release(&path);
 	if (dir) {
-		while ((d = readdir(dir)) != NULL) {
+		while ((d = readdir_skip_dot_and_dotdot(dir)) != NULL) {
 			struct worktree *linked = NULL;
-			if (is_dot_or_dotdot(d->d_name))
-				continue;
 
 			if ((linked = get_linked_worktree(d->d_name))) {
 				ALLOC_GROW(list, counter + 1, alloc);
@@ -488,13 +486,9 @@ int submodule_uses_worktrees(const char
 	if (!dir)
 		return 0;
 
-	while ((d = readdir(dir)) != NULL) {
-		if (is_dot_or_dotdot(d->d_name))
-			continue;
-
+	d = readdir_skip_dot_and_dotdot(dir);
+	if (d != NULL)
 		ret = 1;
-		break;
-	}
 	closedir(dir);
 	return ret;
 }
