From 3de63bc1739354df946c0b6dddd96058d8952bb7 Mon Sep 17 00:00:00 2001 From: Ron Yorston <rmy@pobox.com> Date: Mon, 17 Mar 2025 08:59:56 +0000 Subject: win32: fake directories in readdir(2) The emulation of readdir(2) didn't include the '.' and '..' directories in the root of a logical drive. This resulted in the 'ls' applet (and others) not matching the expected Unix behaviour. Alter the emulation of readdir(2) to include fake '.' and '..' directories if necessary. Adds 88-112 bytes. (GitHub issue #487) --- win32/dirent.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/win32/dirent.c b/win32/dirent.c index 795fc779c..f0e8deae2 100644 --- a/win32/dirent.c +++ b/win32/dirent.c @@ -3,7 +3,9 @@ struct DIR { struct dirent dd_dir; HANDLE dd_handle; /* FindFirstFile handle */ - int dd_stat; /* 0-based index */ + int not_first; + int got_dot; + int got_dotdot; }; static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAA *fdata) @@ -59,9 +61,11 @@ DIR *opendir(const char *name) } /* initialize DIR structure and copy first dir entry */ - dir = xmalloc(sizeof(DIR)); + dir = xzalloc(sizeof(DIR)); dir->dd_handle = h; - dir->dd_stat = 0; + /* dir->not_first = 0; */ + /* dir->got_dot = 0; */ + /* dir->got_dotdot = 0; */ finddata2dirent(&dir->dd_dir, &fdata); return dir; } @@ -74,11 +78,17 @@ struct dirent *readdir(DIR *dir) } /* if first entry, dirent has already been set up by opendir */ - if (dir->dd_stat) { + if (dir->not_first) { /* get next entry and convert from WIN32_FIND_DATA to dirent */ WIN32_FIND_DATAA fdata; if (FindNextFileA(dir->dd_handle, &fdata)) { finddata2dirent(&dir->dd_dir, &fdata); + } else if (!dir->got_dot) { + strcpy(dir->dd_dir.d_name, "."); + dir->dd_dir.d_type = DT_DIR; + } else if (!dir->got_dotdot) { + strcpy(dir->dd_dir.d_name, ".."); + dir->dd_dir.d_type = DT_DIR; } else { DWORD lasterr = GetLastError(); /* POSIX says you shouldn't set errno when readdir can't @@ -89,7 +99,15 @@ struct dirent *readdir(DIR *dir) } } - ++dir->dd_stat; + /* Have we seen '.' or '..'? */ + if (dir->dd_dir.d_name[0] == '.') { + if (dir->dd_dir.d_name[1] == '\0') + dir->got_dot = TRUE; + else if (dir->dd_dir.d_name[1] == '.' && dir->dd_dir.d_name[2] == '\0') + dir->got_dotdot = TRUE; + } + + dir->not_first = TRUE; return &dir->dd_dir; } -- cgit v1.2.3-55-g6feb