/*****************************************************************************\ * * * 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 . * * 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 #include #include #include #include /* 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 /* 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; }