homework-jianmu/deps/MsvcLibX/src/dirent.c

710 lines
25 KiB
C

/*****************************************************************************\
* *
* Filename: dirent.c *
* *
* Description: DOS/WIN32 port of standard C library's dirent.h functions *
* *
* Notes: TO DO: Make Wide & MultiByte versions of scandir 4 Windows*
* *
* There are also remains of an OS/2 implementation here. *
* It's not maintained anymore. Left in as a historic *
* reference, in the unlikely case somebody needs it. *
* *
* History: *
* 2012-01-09 JFL Created this file, based on dirc and other programs. *
* 2012-01-19 JFL Added standard errno management. *
* 2012-05-22 JFL Fixed a bug in the DOS version, which failed if the *
* directory name was longer than 12 bytes. *
* 2012-05-23 JFL Fixed opendir to return errors as per the C lib spec. *
* 2013-02-12 JFL Added code to filter reparse points, and keep only *
* real junctions and symlinks. *
* 2013-02-13 JFL Moved dirent2stat() to lstat.c, as there must actually *
* be 4 WIN32 versions, for the four versions of struct stat.*
* 2013-02-26 JFL Moved the proprietary file time management routines to *
* the new filetime.c module. *
* 2014-02-27 JFL Changed the WIN32 output name encoding to UTF-8. *
* 2014-03-20 JFL Restructured Windows opendir and readdir functions into *
* Wide and MultiByte versions, and changed the Unicode and *
* Ansi versions to macros. *
* 2014-03-24 JFL Renamed "statx.h" as the standard <sys/stat.h>. *
* 2015-12-14 JFL Bug fix: WIN32 readdirW always read the root on "D:". *
* Bug fix: DOS opendir failed on root dirs, like "D:\". *
* *
* Copyright 2016 Hewlett Packard Enterprise Development LP *
* Licensed under the Apache 2.0 license - www.apache.org/licenses/LICENSE-2.0 *
\*****************************************************************************/
#define _CRT_SECURE_NO_WARNINGS 1 /* Avoid Visual C++ security warnings */
#define _UTF8_SOURCE /* Generate the UTF-8 version of WIN32 printf & scandir */
#include "msvcDirent.h" /* Include our associated .h, in the same dir as this .c. Do not use <>. */
#ifndef _DIRENT_FOR_DOS_WINDOWS
#error "This requires MsvcLibX own version of dirent.h for DOS/Windows"
#endif
/* Microsoft C libraries include files */
#include <io.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include <errno.h>
/* MsvcLibX library extensions */
#include "msvcUnistd.h" /* For readlink() */
#include "sys/msvcStat.h" /* For Filetime2String() */
#include "msvcDebugm.h" /* Use our house debugging framework */
#include "msvcLimits.h" /* Use our house debugging framework */
/*****************************************************************************\
* *
* MS_DOS Version *
* *
\*****************************************************************************/
#ifdef _MSDOS
#define OFFSET_OF(pointer) ((uint16_t)(uint32_t)(void far *)pointer)
#define SEGMENT_OF(pointer) ((uint16_t)(((uint32_t)(void far *)pointer) >> 16))
void put_dta(char *p_dta) { /* Set the MS-DOS Disk Transfer Address */
union REGS inreg;
union REGS outreg;
struct SREGS sregs;
inreg.h.ah = 0x1a;
inreg.x.dx = OFFSET_OF(p_dta);
sregs.ds = SEGMENT_OF(p_dta);
intdosx(&inreg, &outreg, &sregs);
}
#define CF 0x0001 /* Carry flag bit mask */
int get_file_attributes(const char *name, unsigned *pAttr) { /* Get File Attributes */
union REGS inreg;
union REGS outreg;
struct SREGS sregs;
inreg.x.ax = 0x4300;
inreg.x.dx = OFFSET_OF(name);
sregs.ds = SEGMENT_OF(name);
intdosx(&inreg, &outreg, &sregs);
if (CF & outreg.x.cflag) {
errno = outreg.x.ax;
return errno;
}
*pAttr = outreg.x.cx;
return 0;
}
/* Workaround for a VMWare player shared folders bug:
DOS function 4e (search next) sometimes returns several times the same entry. */
static fileinfo previousFI;
#ifdef _DEBUG
static int report_workaround(char *s) {
DEBUG_PRINTF((s));
return 1;
}
#define REPORT_WORKAROUND(args) report_workaround args
#else
#define REPORT_WORKAROUND(args) 1
#endif
int srch1st(char *pszFile, uint16_t wAttr, fileinfo *pInfo) { /* Search first matching file */
union REGS inreg;
union REGS outreg;
struct SREGS sregs;
DEBUG_ENTER(("srch1st(\"%s\", 0x%04X, 0x%p);\n", pszFile, wAttr, pInfo));
/* Make sure the DTA is assigned before calling DOS functions 4E and 4F */
put_dta((char *)pInfo);
inreg.h.ah = 0x4e;
inreg.x.cx = wAttr;
inreg.x.dx = OFFSET_OF(pszFile);
sregs.ds = SEGMENT_OF(pszFile);
intdosx(&inreg, &outreg, &sregs);
if (CF & outreg.x.cflag) {
DEBUG_LEAVE(("return %d; // DOS error code\n", outreg.x.ax));
return (int)(outreg.x.ax);
}
previousFI = *pInfo; /* Save it for the workaround for the VMWare player bug */
DEBUG_LEAVE(("return 0; // Success\n"));
return 0;
}
int srchnext(fileinfo *pInfo) { /* Search next matching file */
union REGS inreg;
union REGS outreg;
DEBUG_ENTER(("srchnext(0x%p);\n", pInfo));
/* Make sure the DTA is assigned before calling DOS functions 4E and 4F */
put_dta((char *)pInfo);
inreg.h.ah = 0x4f;
do {
intdos(&inreg, &outreg);
if (CF & outreg.x.cflag) {
DEBUG_LEAVE(("return %d; // DOS error code\n", outreg.x.ax));
return(outreg.x.ax);
}
} while ((!strncmp(previousFI.fiFileName, pInfo->fiFileName, sizeof(previousFI)))
&& REPORT_WORKAROUND(("// Skipped one duplicate entry\n")));
previousFI = *pInfo; /* Save it for the workaround for the VMWare player bug */
DEBUG_LEAVE(("return 0; // Success\n"));
return 0;
}
DIR *opendir(const char *name) { /* Open a directory */
DIR *pDir = NULL;
size_t lName;
unsigned attr;
char *pszWildCards = "\\*.*";
char *pszCopy;
DEBUG_ENTER(("opendir(\"%s\");\n", name));
lName = strlen(name);
if (lName == 0) {
opendir_noent:
errno = ENOENT;
opendir_failed:
if (!_sys_errlist[ENOTDIR][0]) _sys_errlist[ENOTDIR] = "Not a directory"; /* Workaround for the missing entry in MSVC list */
if (pDir) free(pDir);
DEBUG_LEAVE(("return NULL; // errno=%d - %s\n", errno, strerror(errno)));
return NULL;
}
pDir = (DIR *)malloc(sizeof(DIR) + lName + 5); /* + 5 for wildcards suffix */
if (!pDir) goto opendir_failed;
/* Work on a copy of the directory name */
pszCopy = (char *)(pDir + 1);
strcpy(pszCopy, name);
/* First change: Except for the root, Remove the trailing \s, which confuses get_file_attributes() */
while ((lName > 1) && (name[lName-1] == '\\') && (name[lName-2] != ':')) pszCopy[--lName] = '\0';
if (get_file_attributes(pszCopy, &attr)) goto opendir_noent;
if (!(attr & _A_SUBDIR)) {
errno = ENOTDIR;
goto opendir_failed;
}
if (name[lName-1] == '\\') pszWildCards += 1; /* Avoid duplicating the \ */
strcpy(pszCopy+lName, pszWildCards);
pDir->first = 1;
DEBUG_LEAVE(("return 0x%p;\n", pDir));
return pDir;
}
int closedir(DIR *pDir) { /* Close the directory. Return 0 if successful, -1 if not. */
DEBUG_PRINTF(("closedir(0x%p);\n", pDir));
if (pDir) free(pDir);
return 0;
}
_dirent *readdir(DIR *pDir) { /* Read a directory entry. Return pDir, or NULL for EOF or error. */
int iErr;
_dirent *pDirent = &pDir->sDirent;
fileinfo *pFI = (fileinfo *)(pDirent->d_reserved); /* Address of the fileinfo structure embedded there */
#ifdef _DEBUG
char szTime[40];
#endif
DEBUG_ENTER(("readdir(0x%p);\n", pDir));
if (pDir->first) { /* First search */
iErr = srch1st((char *)(pDir+1), 0x3F, pFI);
pDir->first = 0;
} else {
iErr = srchnext(pFI);
}
if (!iErr) {
pDirent->d_type = DT_REG; /* A normal file by default */
if (pDirent->d_attribs & _A_SUBDIR) pDirent->d_type = DT_DIR; /* Subdirectory */
if (pDirent->d_attribs & _A_VOLID) pDirent->d_type = DT_VOLID; /* Volume ID file */
DEBUG_LEAVE(("return 0x%p; // %s %02X %10ld %s\n",
pDirent,
Filetime2String(pDirent->d_date, pDirent->d_time, szTime, sizeof(szTime)),
pDirent->d_attribs,
pDirent->d_filesize,
pDirent->d_name));
return &pDir->sDirent;
}
switch (iErr) { /* Correct a few errors that do not map well to C library errors */
case ESRCH: iErr = ENOTDIR; break;
case EXDEV: iErr = 0; break; /* End of files is NOT an error */
}
if (iErr) {
errno = iErr; /* MS-DOS' errno.h maps C-library errnos to DOS' errors */
DEBUG_LEAVE(("return NULL; // errno=%d - %s\n", errno, strerror(errno)));
} else {
DEBUG_LEAVE(("return NULL; // End of directory\n"));
}
return NULL;
}
#endif /* defined(_MSDOS) */
/*****************************************************************************\
* *
* WIN32 Version *
* *
\*****************************************************************************/
#ifdef _WIN32
#include <windows.h>
/* Requires including windows.h and especially the kernel section */
DIR *opendirW(const WCHAR *wszName) { /* Open a directory - Wide char version */
DIR *pDir;
int err;
DWORD dw;
DEBUG_CODE(
char szUtf8[UTF8_PATH_MAX];
)
DEBUG_WSTR2UTF8(wszName, szUtf8, sizeof(szUtf8));
DEBUG_ENTER(("opendir(\"%s\");\n", szUtf8));
dw = GetFileAttributesW(wszName);
err = 0;
if (dw == INVALID_FILE_ATTRIBUTES) {
err = errno = Win32ErrorToErrno();
} else {
if (!(dw & _A_SUBDIR)) {
err = errno = ENOTDIR;
}
}
if (err) {
RETURN_CONST_COMMENT(NULL, ("errno=%d - %s\n", errno, strerror(errno)));
}
if (lstrlenW(wszName) >= sizeof(pDir->wszDirName)) {
errno = ENAMETOOLONG;
RETURN_CONST_COMMENT(NULL, ("errno=%d - %s\n", errno, strerror(errno)));
}
pDir = malloc(sizeof(DIR));
if (!pDir) {
errno = ENOMEM;
RETURN_CONST_COMMENT(NULL, ("errno=%d - %s\n", errno, strerror(errno)));
}
pDir->hFindFile = INVALID_HANDLE_VALUE;
lstrcpyW(pDir->wszDirName, wszName);
DEBUG_LEAVE(("return 0x%p; // Success\n", pDir));
return pDir;
}
DIR *opendirM(const char *name, UINT cp) { /* Open a directory - MultiByte char version */
WCHAR wszName[PATH_MAX];
int n;
/* Convert the pathname to a unicode string, with the proper extension prefixes if it's longer than 260 bytes */
n = MultiByteToWidePath(cp, /* CodePage, (CP_ACP, CP_OEMCP, CP_UTF8, ...) */
name, /* lpMultiByteStr, */
wszName, /* lpWideCharStr, */
COUNTOF(wszName) /* cchWideChar, */
);
if (!n) {
errno = Win32ErrorToErrno();
DEBUG_PRINTF(("opendirM(\"%s\"); // Can't convert name to Unicode: errno=%d - %s\n", name, errno, strerror(errno)));
return NULL;
}
return opendirW(wszName);
}
int closedir(DIR *pDir) { /* Close the directory. Return 0 if successful, -1 if not. */
DEBUG_PRINTF(("closedir(0x%p);\n", pDir));
if (pDir) {
if (pDir->hFindFile != INVALID_HANDLE_VALUE) FindClose(pDir->hFindFile);
pDir->hFindFile = INVALID_HANDLE_VALUE;
free(pDir);
}
return 0;
}
/* Read a directory entry. Return pDir, or NULL for EOF or error. Wide char version. */
_dirent *readdirW(DIR *pDir) {
int iErr = 0;
_dirent *pDirent = &pDir->sDirent;
int bIsJunction = FALSE;
int bIsMountPoint = FALSE;
DWORD dwTag = 0; /* Reparse points tag */
DWORD dwAttr;
int n;
DEBUG_CODE(
char szTime[40];
char szUtf8[UTF8_PATH_MAX];
)
DEBUG_ENTER(("readdir(0x%p);\n", pDir));
if (pDir->hFindFile == INVALID_HANDLE_VALUE) {
WCHAR wszPattern[MAX_PATH+5];
lstrcpyW(wszPattern, pDir->wszDirName);
n = lstrlenW(wszPattern);
if (n && (wszPattern[n-1] != L'\\') && (wszPattern[n-1] != L':')) wszPattern[n++] = L'\\';
lstrcpyW(wszPattern+n, L"*.*");
pDir->hFindFile = FindFirstFileW(wszPattern, &pDir->wfd);
if (pDir->hFindFile == INVALID_HANDLE_VALUE) {
iErr = Win32ErrorToErrno();
if (!iErr) iErr = ENOENT;
}
} else {
iErr = !FindNextFileW(pDir->hFindFile, &pDir->wfd);
if (iErr) {
iErr = Win32ErrorToErrno();
if (!iErr) iErr = ENOENT;
}
}
if (iErr) {
switch (iErr) { /* Correct a few errors that do not map well to C library errors */
case ESRCH: iErr = ENOTDIR; break;
case EXDEV: iErr = 0; break; /* End of files is NOT an error */
}
if (iErr) {
errno = iErr; /* Windows' errno.h maps C-library errnos to Windows' errors */
DEBUG_LEAVE(("return NULL; // errno=%d - %s\n", errno, strerror(errno)));
} else {
DEBUG_LEAVE(("return NULL; // End of directory\n"));
}
return NULL;
}
/* Set the standard fields */
lstrcpyW((WCHAR *)(pDirent->d_name), pDir->wfd.cFileName);
dwAttr = pDir->wfd.dwFileAttributes;
check_attr_again:
if (dwAttr & FILE_ATTRIBUTE_REPARSE_POINT) {
/* JUNCTIONs and SYMLINKDs both have the FILE_ATTRIBUTE_DIRECTORY flag also set.
// Test the FILE_ATTRIBUTE_REPARSE_POINT flag first, to make sure they're seen as symbolic links.
//
// All symlinks are reparse points, but not all reparse points are symlinks. */
dwTag = pDir->wfd.dwReserved0; /* No need to call GetReparseTag(), we got it already. */
switch (dwTag) {
case IO_REPARSE_TAG_MOUNT_POINT: /* NTFS junction or mount point */
{ /* We must read the link to distinguish junctions from mount points. */
WCHAR wszPath[PATH_MAX];
WCHAR wszBuf[PATH_MAX];
ssize_t n = lstrlenW(pDir->wszDirName);
if ((n + 1 + lstrlenW(pDir->wfd.cFileName)) >= PATH_MAX) {
errno = ENAMETOOLONG; /* DIRNAME\LINKNAME won't fit in wszPath[] */
DEBUG_LEAVE(("return NULL; // errno=%d - %s\n", errno, strerror(errno)));
}
bIsMountPoint = TRUE;
lstrcpyW(wszPath, pDir->wszDirName);
if (n && (wszPath[n-1] != L'\\')) wszPath[n++] = L'\\';
lstrcpyW(wszPath+n, pDir->wfd.cFileName);
n = readlinkW(wszPath, wszBuf, COUNTOF(wszBuf));
/* Junction targets are absolute pathnames, starting with a drive letter. Ex: C: */
/* readlink() fails if the reparse point does not target a valid pathname */
if (n < 0) goto this_is_not_a_symlink; /* This is not a junction. */
bIsJunction = TRUE; /* Else this is a junction. Fall through to the symlink case. */
}
case IO_REPARSE_TAG_SYMLINK: /* NTFS symbolic link */
pDirent->d_type = DT_LNK; /* Symbolic link */
break;
default: /* Anything else is definitely not like a Unix symlink */
this_is_not_a_symlink:
dwAttr &= ~FILE_ATTRIBUTE_REPARSE_POINT;
goto check_attr_again;
}
} else if (dwAttr & FILE_ATTRIBUTE_DIRECTORY)
pDirent->d_type = DT_DIR; /* Subdirectory */
else if (dwAttr & FILE_ATTRIBUTE_DEVICE)
pDirent->d_type = DT_CHR; /* Device (we don't know if character or block) */
else
pDirent->d_type = DT_REG; /* A normal file by default */
/* Set the OS-specific extensions */
lstrcpyW((WCHAR *)(pDirent->d_shortname), pDir->wfd.cAlternateFileName);
pDirent->d_attribs = dwAttr;
pDirent->d_ReparseTag = dwTag;
pDirent->d_CreationTime = pDir->wfd.ftCreationTime;
pDirent->d_LastAccessTime = pDir->wfd.ftLastAccessTime;
pDirent->d_LastWriteTime = pDir->wfd.ftLastWriteTime;
(*(ULARGE_INTEGER *)&(pDirent->d_filesize)).LowPart = pDir->wfd.nFileSizeLow;
(*(ULARGE_INTEGER *)&(pDirent->d_filesize)).HighPart = pDir->wfd.nFileSizeHigh;
DEBUG_WSTR2UTF8((WCHAR *)(pDirent->d_name), szUtf8, sizeof(szUtf8));
DEBUG_LEAVE(("return 0x%p; // %s 0x%05X %10lld %s\n",
pDirent,
Filetime2String(&pDirent->d_LastWriteTime, szTime, sizeof(szTime)),
(int)(pDirent->d_attribs),
pDirent->d_filesize,
szUtf8));
return &(pDir->sDirent);
}
/* Read a directory entry. Return pDir, or NULL for EOF or error. MultiByte char version. */
_dirent *readdirM(DIR *pDir, UINT cp) {
_dirent *pDirent;
int n;
char *pszDefaultChar;
pDirent = readdirW(pDir);
if (!pDirent) return NULL;
/* Convert the name field back to MultiByte encoding */
pszDefaultChar = (cp == CP_UTF8) ? NULL : "?";
n = WideCharToMultiByte(cp, /* CodePage, (CP_ACP, CP_OEMCP, CP_UTF8, ...) */
0, /* dwFlags, */
pDir->wfd.cFileName, /* lpWideCharStr, */
lstrlenW(pDir->wfd.cFileName)+1, /* cchWideChar, */
pDirent->d_name, /* lpMultiByteStr, */
sizeof(pDirent->d_name), /* cbMultiByte, */
pszDefaultChar, /* lpDefaultChar, */
NULL /* lpUsedDefaultChar */
);
if (!n) {
errno = Win32ErrorToErrno();
DEBUG_PRINTF(("readdirM(0x%p); // Error converting name back from Unicode. errno=%d - %s\n", pDir, errno, strerror(errno)));
return NULL;
}
/* Convert the short name field back to MultiByte encoding */
n = WideCharToMultiByte(cp, /* CodePage, (CP_ACP, CP_OEMCP, CP_UTF8, ...) */
0, /* dwFlags, */
pDir->wfd.cAlternateFileName, /* lpWideCharStr, */
lstrlenW(pDir->wfd.cAlternateFileName)+1, /* cchWideChar, */
pDirent->d_shortname, /* lpMultiByteStr, */
sizeof(pDirent->d_shortname), /* cbMultiByte, */
pszDefaultChar, /* lpDefaultChar, */
NULL /* lpUsedDefaultChar */
);
if (!n) {
errno = Win32ErrorToErrno();
DEBUG_PRINTF(("readdirM(0x%p); // Error converting short name back from Unicode. errno=%d - %s\n", pDir, errno, strerror(errno)));
return NULL;
}
return pDirent;
}
#endif
/*****************************************************************************\
* *
* OS/2 1.x Version *
* *
\*****************************************************************************/
#ifdef _OS2
/* Requires including os2.h at the beginning of this file, and before that
defining the INCL_DOSFILEMGR constant to enable the necessary section */
int SetDirent(DIR *pDir) { /* Convert the FILEFINDBUF to a dirent structure */
char *pc;
_dirent *pDirent = &pDir->sDirent;
FILEFINDBUF *pbuf = &pDir->buf;
pbuf->achName[pbuf->cchName] = '\0';
pc = strrchr(pbuf->achName, '\\'); /* Directory separator character */
if (!pc)
pc = &pbuf->achName[0];
else
pc += 1; /* Skip the \ */
pDirent->d_name = malloc(strlen(pc)+1);
if (!pDirent->d_name) return ENOMEM;
strcpy(pDirent->d_name, pc);
pDirent->attribs = pbuf->attrFile;
pDirent->d_type = Atype2Dtype(pDirent->attribs);
pDirent->time = *(uint16_t *)&pbuf->ftimeLastWrite;
pDirent->date = *(uint16_t *)&pbuf->fdateLastWrite;
pDirent->filesize = pbuf->cbFile;
return 0;
}
int srch1st(char *pszFile, uint8_t bAttrReq, uint8_t bAttrCmp, DIR *pDir) { /* Search first matching file */
int n;
int err;
pDir->hDir = -1;
n = 1; /* Get one file */
err = DosFindFirst2(pszFile, &pDir->hDir, bAttrReq & 0x7F, &pDir->buf,
sizeof(FILEFINDBUF), &n, FIL_STANDARD, 0L);
if (err || !n) return 1;
return SetDirent(pDir);
}
int srchnext(DIR *pDir) { /* Search next matching file */
int n;
int err;
n = 1; /* Get one file */
err = DosFindNext(pDir->hDir, &pDir->buf, sizeof(FILEFINDBUF), &n);
if (err || !n) return 1;
return SetDirent(&buf);
}
int srchdone(DIR *pDir) {
int err;
err = DosFindClose(pDir->hDir);
pDir->hDir = -1;
return err;
}
DIR *opendir(const char *name) { /* Open a directory */
DIR *pDir;
DEBUG_ENTER(("opendir(\"%s\");\n", name));
err = _access(name, 0);
/* To do: Get the file attributes, and check that it's a directory */
if (err) {
DEBUG_LEAVE(("return NULL; // errno=%d - %s\n", errno, strerror(errno)));
return NULL;
}
pDir = malloc(sizeof(DIR));
if (pDir) {
pDir->hDir = -1;
pDir->bAttrReq = _A_HIDDEN | _A_SYSTEM | _A_SUBDIR;
pDir->bAttrCmp = 0;
strcpy(pDir->sDirent.d_name, name);
}
DEBUG_LEAVE(("return 0x%p;\n", pDir));
return pDir;
}
int closedir(DIR *pDir) { /* Close the directory. Return 0 if successful, -1 if not. */
DEBUG_PRINTF(("closedir(0x%p);\n", pDir));
if (pDir) {
srchdone(pDir);
free(pDir);
}
return 0;
}
_dirent *readdir(DIR *pDir) { /* Read a directory entry. Return pDir, or NULL for EOF or error. */
int iErr;
DEBUG_ENTER(("readdir(0x%p);\n", pDir));
if (pDir->hDir == -1) {
iErr = srch1st(pDir->sDirent.d_name, pDir->bAttrReq, pDir->bAttrCmp, pDir);
} else {
iErr = srchnext(pDir);
}
if (iErr) {
DEBUG_LEAVE(("return NULL; // OS/2 found nothing\n",
return NULL;
}
DEBUG_LEAVE(("return 0x%p; // OS/2 found: %04X %04X %02X %10lld %s\n",
&pDir->sDirent
(int)(pDirent->time),
(int)(pDirent->date),
(int)(pDirent->attribs),
pDirent->filesize,
pDirent->d_name));
return &pDir->sDirent;
}
#endif
/*****************************************************************************\
* *
* End of OS-specific opendir/readdir/closedir versions *
* *
\*****************************************************************************/
/*****************************************************************************\
* *
* Function: scandir *
* *
* Description: Select entries in a directory *
* *
* Arguments: const char *name Directory name *
* _dirent ***namelist where to store the result array *
* int (*cbSelect)() Selection callback function *
* int (*cbCompare)() Comparison function for sorting it *
* *
* Return value: # of entries in the array, or -1 if error. *
* *
* Notes: *
* *
* History: *
* 2012-01-11 JFL Initial implementation *
* *
\*****************************************************************************/
#pragma warning(disable:4706) /* Ignore the "assignment within conditional expression" warning */
int scandir(const char *pszName,
_dirent ***resultList,
int (*cbSelect) (const _dirent *),
int (__cdecl *cbCompare) (const _dirent **,
const _dirent **)) {
int n = 0;
DIR *pDir;
_dirent *pDirent;
_dirent *pDirent2;
_dirent **pList = NULL;
_dirent **pList2;
DEBUG_ENTER(("scandir(\"%s\", 0x%p, 0x%p, 0x%p);\n", pszName, resultList, cbSelect, cbCompare));
pDir = opendir(pszName);
if (!pDir) {
DEBUG_LEAVE(("return -1; // errno=%d\n", errno));
return -1;
}
while (pDirent = readdir(pDir)) {
if (cbSelect && !cbSelect(pDirent)) continue; /* We don't want this one. Continue search. */
/* OK, we've selected this one. So append a copy of this _dirent to the list. */
n += 1;
pList2 = (_dirent **)realloc(pList, n * sizeof(_dirent *));
pDirent2 = malloc(sizeof(_dirent));
if (!pList2 || !pDirent2) {
if (pDirent2) free(pDirent2);
for (n-=1; n>0; ) free(pList[--n]);
/* errno = ENOMEM; /* Out of memory. Should already be set by malloc failure */
DEBUG_LEAVE(("return -1; // errno=%d\n", errno));
return -1;
}
*pDirent2 = *pDirent;
pList = pList2;
pList[n-1] = pDirent2;
}
closedir(pDir);
/* 2016-09-23 JFL I don't understand why this warning still fires, so leaving it enabled for now */
#ifdef M_I86TM /* This warning appears only when compiling for the DOS tiny memory model ?!? */
/* #pragma warning(disable:4220) /* Ignore the "varargs matches remaining parameters" warning */
#endif
if (cbCompare) qsort(pList, n, sizeof(_dirent *), cbCompare);
#ifdef M_I86TM
#pragma warning(default:4220) /* Ignore the "varargs matches remaining parameters" warning */
#endif
*resultList = pList;
DEBUG_LEAVE(("return %d;\n", n));
return n;
}
#pragma warning(default:4706)
int __cdecl alphasort(const _dirent **ppDE1, const _dirent **ppDE2) {
int ret;
/* Sort names a-la Windows, that is case insensitive */
ret = _strnicmp((*ppDE1)->d_name, (*ppDE2)->d_name, NAME_MAX);
if (ret) return ret;
/* For the remote chance that we're accessing a Unix share */
ret = _strnicmp((*ppDE1)->d_name, (*ppDE2)->d_name, NAME_MAX);
if (ret) return ret;
return 0;
}