Tuesday, April 28, 2020

C++ stack implementation to iterate over all files and sub-directories recursively with wildcard search pattern

Here's an actual working C++ implementation of iterating over every file/directory using a file wildcard characters [*?] in a file search pattern or file filter using a stack implementation and Unicode compliant to handle multiple languages.

No change required in default stack size in Visual Studio 2010 under Linker->System->Stack Reserve Size and Stack Commit Size, but if this fails to compile, then change these limits.


#include <windows.h>
#include <string>
#include <vector>
#include <stack>
#include <iostream>
#include <atlconv.h>
#include <io.h>
using namespace std;

//---------------------------------------------------------------------------------
//Mon 27-Apr-20 1:57pm  - working stack implementation of sub-dir transversal
//https://metadataconsulting.blogspot.com/2020/04/C-stack-implementation-to-iterate-over-all-files-and-sub-directories-recursively-with-wildcard-search-pattern.html
//---------------------------------------------------------------------------------
bool ListFiles(std::wstring path, std::wstring mask, std::vector<std::wstring>& files) {
    
    //HANDLE hFind = INVALID_HANDLE_VALUE;
    long h; 
    errno_t err;
    _wfinddata_t ffd;
    std::wstring spec;
    std::wstring pathtemp; 
    std::stack<std::wstring> directories;
    std::stack<std::wstring> transversal;

    if (path.length() > 0)
    {
        std::wstring::iterator it = path.end() - 1;
        if (*it == '\\')
        {
             path.erase(it);
        }
    }

    directories.push(path);
    transversal.push(path); 
    files.clear();
    
    //Enumerate all directories first, then all files //Tue 28-Apr-20 4:46pm  - 
    while (!transversal.empty()) {
        
        path = transversal.top();
        spec = path + L"\\*"; 
        transversal.pop();
        
        h = _wfindfirst(spec.c_str(), &ffd);
    
        if (h > 0) {
    
            do {
                if (wcscmp(ffd.name, L".") != 0 && 
                    wcscmp(ffd.name, L"..") != 0) {
                    pathtemp = path + L"\\" + ffd.name; 
                    if (ffd.attrib & FILE_ATTRIBUTE_DIRECTORY) {
                        std::wcout << "searched dir: " << pathtemp + L"\\" << " \n"; 
                        directories.push(pathtemp);
                        transversal.push(pathtemp);
                    }
                }
            } while (_wfindnext(h, &ffd) == 0);

            if (GetLastError() != ERROR_NO_MORE_FILES) {
                _findclose(h);
                return false;
            }

            _findclose(h);
            h = -1; 
        }
    }

    while (!directories.empty()) {
        
        path = directories.top();
        spec = path + L"\\" + mask;
        directories.pop();
    
        h = _wfindfirst(spec.c_str(), &ffd);
        //_get_errno( &err );

        //shortcuts recursive sub-directories transversal, do not use
        /*if (err==EINVAL) {
            std::wcout << "ERROR: Invalid path encontered." << spec << std::endl; 
            return false;
        }
        else if (err==ENOENT) {
            std::wcout << "No results for file pattern " << mask << std::endl;  
            return false;
        }*/
            
        if (h > 0) {
            //std::wcout << "ERROR: " << spec << " invalid path encontered.\n"; 
            //no files found
            //return false;
        

            do {
                if (wcscmp(ffd.name, L".") != 0 && 
                    wcscmp(ffd.name, L"..") != 0) {
                    pathtemp = path + L"\\" + ffd.name; 
                    if (!(ffd.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
                        files.push_back(pathtemp);
                    }
                }
            } while (_wfindnext(h, &ffd) == 0);

            if (GetLastError() != ERROR_NO_MORE_FILES) {
                _findclose(h);
                return false;
            }
        
        }
        _findclose(h);
        //h = INVALID_HANDLE_VALUE;
        h = -1;
        
    }

    return true;
}

int main(int argc, char* argv[])
{
    vector<wstring> files;

    int cnt = 0; 

    if (ListFiles(L"C:\\Windows\\Temp", L"*.log", files)) {
        for (vector<wstring>::iterator it = files.begin(); 
             it != files.end(); 
             ++it) {
            wcout  << ++cnt << ". " << it->c_str() << endl;
        }
    }
    return 0;
}

No comments:

Post a Comment