void FindFiles()

in source/code/scxcorelib/pal/scxdirectoryinfo.cpp [335:574]


        void FindFiles(const SCXCoreLib::SCXFilePath& path, const SCXCoreLib::SCXDirectorySearchOptions& options)
        {
#if defined(WIN32)
            struct _wfinddata_t c_file;     // Directory entry. From <wchar.h>
            intptr_t hFile;                 // Handle to directory. From <crtdefs>.h
            errno_t eno;                    // Temporary errno storage

            // Extract wide char representation of directory name from SCXFilePath and append a * wildcard
            std::wstring wdirname(path.GetDirectory());
            wdirname += L'*';

            // Open the directory and get the handle
            hFile = _wfindfirst(wdirname.c_str(), &c_file);
            if( hFile == -1L ) {
                // It failed. Read error code and make an exception of it.
                eno = errno;
                errno = 0;

                // Not finding the directory is an exception
                if (eno == ENOENT) { 
                    throw SCXCoreLib::SCXFilePathNotFoundException(path.GetDirectory(), SCXSRCLOCATION);
                }

                wchar_t errtxt[100];
                __wcserror_s(errtxt, sizeof errtxt / sizeof(wchar_t), wdirname.c_str());
                if (eno == EINVAL) {
                    // Unlike Unix, we get EINVAL even if the dir is read protected
                    throw SCXCoreLib::SCXInvalidArgumentException(wdirname, errtxt, SCXSRCLOCATION);
                } else {
                    throw SCXCoreLib::SCXResourceExhaustedException(errtxt, wdirname, SCXSRCLOCATION);
                }
            }
            do {
                // Undocumented feature: Junction Files/Reparse Points are silently skipped
                if ((c_file.attrib & 0x400)) { continue; }

                // Check file attributes and decide if this file should be included
                bool isSystem = ((c_file.attrib & _A_SYSTEM) != 0);

                // Case 1: This is a system file and those should not be listed at all
                if (isSystem && 0 == (options & SCXCoreLib::eDirSearchOptionSys)) { continue; }

                // Case 2: This is a subdir
                if (c_file.attrib & _A_SUBDIR) {

                    if (0 == (options & SCXCoreLib::eDirSearchOptionDir)) { continue;} // ...and those should not be listed
                    // Do away with . and ..
                    if (c_file.name[0] == L'.'
                        && (c_file.name[1] == L'\0'
                            || (c_file.name[1] == L'.' && c_file.name[2] == L'\0'))) { continue; }

                    SCXCoreLib::SCXFilePath f(path);         // Use current path and filename
                    f.SetFilename(L"");                     // Only interested in path component
                    f.AppendDirectory(std::wstring(c_file.name));
                    m_deps->Result(f, false);
                }
                else {
                    // Case 3: This an ordinay file
                    if (0 == (options & SCXCoreLib::eDirSearchOptionFile) && !isSystem) {continue;}// ... and those should not be listed unless being system files
                    SCXCoreLib::SCXFilePath f(path);         // Use current path and filename
                    f.SetFilename(std::wstring(c_file.name));
                    m_deps->Result(f, false);
                }

            } while( _wfindnext( hFile, &c_file ) == 0 );

            // Check errno but first release resources. ENOENT is ok (and expected).
            _get_errno(&eno);
            _findclose(hFile);
            if (eno != ENOENT && eno != 0) { 
                // If we get here something went wrong and an exception should be thrown
                wchar_t errtxt[100];
                __wcserror_s(errtxt, sizeof errtxt / sizeof(wchar_t), wdirname.c_str());
                if (eno == EINVAL) {
                    throw SCXCoreLib::SCXInvalidArgumentException(wdirname, errtxt, SCXSRCLOCATION);
                } else {
                    throw SCXCoreLib::SCXResourceExhaustedException(errtxt, wdirname, SCXSRCLOCATION);
                }
            }
#else /* WIN32 */
    /*
      Note: This implementation is supposed to be mostly POSIX 1003.1 compliant.
      Using the d_type field of the dirent structure is however not. Posix
      don't require that this field is present. Instead you do a stat() call
      on each file to figure out its file type. This means an extra
      file access for each file in the directory. Using d_type is ok on
      all BSD derived unixes such as Linux. On Solaris it isn't, and HP/UX is still unknown.

      Strictly speaking, readdir() is not thread safe. But that only applies
      if multiple threads access the same dirstream and that is not the case
      in this code.

      Right now the function is hardwired to use UTF-8 as external character set.
      I would like see a method like SCXFilePath::GetLocalEncoding() that returns
      a char* with the fully qualified pathname encoded in the local character set.
    */

            DIR *dirp = 0;                  // Directory stream object. From <dirent.h>
            struct dirent *dentp = 0;       // Pointer to a directory entry. From <dirent.h>
            int eno = 0;                    // Temporary storage for errno.
            char filenamebuf[PATH_MAX];     // Used to construct all filenames
            char *nameendptr = 0;           // Position after directory name
            size_t nameendsz = 0;           // Remaing space in filenamebuf
            size_t copysz = 0;              // How many characters to strncopy to filenamebuf

            errno = 0;

            // Extract "char *" representation of directory name from SCXFilePath.
            std::wstring wdirname(path.GetDirectory());
            std::string dirname(SCXCoreLib::StrToUTF8(wdirname)); // May throw, but that's ok?
            strncpy(filenamebuf, dirname.c_str(), sizeof(filenamebuf));
            // In the future I expect to be able to just do the following and be done with it
            // path.GetLocalEncoding(filenamebuf, sizeof(filenamebuf));

            // Prepare file name buffer for reuse (every name is temporarily stored here)
            nameendptr = filenamebuf + strlen(filenamebuf);
            // if (nameendptr[-1] != '/') { *nameendptr = '/'; ++nameendptr; *nameendptr = '\0'; }
            nameendsz = sizeof(filenamebuf) - (nameendptr - filenamebuf) - 2;

            // Open the directory and get the handle
            dirp = opendir(filenamebuf);
            if (dirp == 0) {
                // It failed. Read error code and make an exception of it.
                eno = errno;
                errno = 0;

                if (eno == ENOENT) { 
                    throw SCXCoreLib::SCXFilePathNotFoundException(path.GetDirectory(), SCXSRCLOCATION);
                }

                std::string errtxt(SCXCoreLib::strerror(eno));

                if (eno == EACCES || eno == ENOTDIR) {
                    throw SCXCoreLib::SCXInvalidArgumentException(wdirname, SCXCoreLib::StrFromUTF8(errtxt), SCXSRCLOCATION);
                } else {
                    // All other errno's are resource related. Memory, descriptors, etc.
                    // Note that the parameters supplied to the exception are not a very good
                    // match to what it expects.
                    throw SCXCoreLib::SCXResourceExhaustedException(SCXCoreLib::StrFromUTF8(errtxt), wdirname, SCXSRCLOCATION);
                }
            }

            // Read a directory entry
            struct dirent *currentDir = NULL;
            int len_entry = offsetof(struct dirent, d_name) + PATH_MAX + 1;
            currentDir = (struct dirent *)malloc(len_entry);
#if defined(sun)
            while (NULL != (dentp = readdir_r(dirp, currentDir))) {
#else
            while ((0 == readdir_r(dirp, currentDir, &dentp)) && NULL != dentp) {
#endif
                bool isdir = false;                         // Is this a directory entry
                SCXCoreLib::SCXFileSystem::SCXStatStruct* pStat = 0;
                // Only BSD-based C-libraries have the d_type entry in the DIR structure
                // In all others we must always do an explict stat call to find out the type
#if defined(linux)
                // Check file type and decide if this file should be included
                switch (dentp->d_type) {

                case DT_LNK:     /* Always dereference symlinks */
                case DT_UNKNOWN: /* File type can't be found out from dirent. Do a stat() call. */
#endif
                    /* Decide how many characters to copy. (We do this because the buffer is big)*/
                    copysz = MIN(nameendsz, static_cast<size_t>(_D_ALLOC_NAMLEN(dentp)));
                    strncpy(nameendptr, dentp->d_name, copysz);
                    
                    pStat = m_deps->DoStat(filenamebuf);
                    if (0 == pStat) {
                        /* We must account for links that point to non-existing files */
                        if (errno == ENOENT) { errno = 0; continue; }
                        else { goto after; }
                    }
                    if (S_ISREG(pStat->st_mode)) {
                        if (0 == (options & SCXCoreLib::eDirSearchOptionFile)) { continue; }
                        isdir = false;
                    } else if (S_ISDIR(pStat->st_mode)) {
                        if (0 == (options & SCXCoreLib::eDirSearchOptionDir)) { continue; }
                        isdir = true;
                    }
                    else {
                        if (0 == (options & SCXCoreLib::eDirSearchOptionSys)) { continue; }
                    }
#if defined(linux)
                    break;

                case DT_REG:
                    // This is a regular file
                    if (0 == (options & SCXCoreLib::eDirSearchOptionFile)) { continue; }
                    isdir = false;
                    break;

                case DT_DIR:
                    // This is a directory
                    if (0 == (options & SCXCoreLib::eDirSearchOptionDir)) { continue; }
                    isdir = true;
                    break;

                default:
                    // This is a "system file" (Device, socket, etc.)
                    if (0 == (options & SCXCoreLib::eDirSearchOptionSys)) { continue; }
                }
#endif
                if (isdir) {
                    // Do away with . and ..
                    if (dentp->d_name[0] == '.'
                        && (dentp->d_name[1] == '\0'
                            || (dentp->d_name[1] == '.' && dentp->d_name[2] == '\0'))) { continue;}
                }

                // make new SCXFilePath structure with current path and filename, add to result
                std::string dname(dentp->d_name);
                std::wstring wdname(SCXCoreLib::StrFromUTF8(dname));

                SCXCoreLib::SCXFilePath f(path);             // Use current path and filename
                if (isdir) {
                    f.SetFilename(L"");                     // Only interested in path component
                    f.AppendDirectory(wdname);
                } else { // Ordinary file or system
                    f.SetFilename(wdname);
                }
                m_deps->Result(f, pStat != 0);
            }

        after:
            if (currentDir)
            {
                free(currentDir);
            }
            eno = errno;
            errno = 0;
            closedir(dirp);
            if (eno != 0) { 
                throw SCXCoreLib::SCXErrnoException(L"stat", eno, SCXSRCLOCATION);
            }
#endif
        }

    private:
        SCXCoreLib::SCXHandle<SCXDirectoryEnumeratorBehaviour> m_deps; //!< Dependency object.
    };