Blame cli/filelister.cpp

Packit 2035a7
/*
Packit 2035a7
 * Cppcheck - A tool for static C/C++ code analysis
Packit 2035a7
 * Copyright (C) 2007-2018 Cppcheck team.
Packit 2035a7
 *
Packit 2035a7
 * This program is free software: you can redistribute it and/or modify
Packit 2035a7
 * it under the terms of the GNU General Public License as published by
Packit 2035a7
 * the Free Software Foundation, either version 3 of the License, or
Packit 2035a7
 * (at your option) any later version.
Packit 2035a7
 *
Packit 2035a7
 * This program is distributed in the hope that it will be useful,
Packit 2035a7
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 2035a7
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 2035a7
 * GNU General Public License for more details.
Packit 2035a7
 *
Packit 2035a7
 * You should have received a copy of the GNU General Public License
Packit 2035a7
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit 2035a7
 */
Packit 2035a7
Packit 2035a7
#include "filelister.h"
Packit 2035a7
Packit 2035a7
#include "path.h"
Packit 2035a7
#include "pathmatch.h"
Packit 2035a7
#include "utils.h"
Packit 2035a7
Packit 2035a7
#include <cstddef>
Packit 2035a7
#include <cstring>
Packit 2035a7
Packit 2035a7
#ifdef _WIN32
Packit 2035a7
Packit 2035a7
///////////////////////////////////////////////////////////////////////////////
Packit 2035a7
////// This code is WIN32 systems /////////////////////////////////////////////
Packit 2035a7
///////////////////////////////////////////////////////////////////////////////
Packit 2035a7
Packit 2035a7
#include <windows.h>
Packit 2035a7
#ifndef __BORLANDC__
Packit 2035a7
#include <shlwapi.h>
Packit 2035a7
#endif
Packit 2035a7
Packit 2035a7
// Here is the catch: cppcheck core is Ansi code (using char type).
Packit 2035a7
// When compiling Unicode targets WinAPI automatically uses *W Unicode versions
Packit 2035a7
// of called functions. Thus, we explicitly call *A versions of the functions.
Packit 2035a7
Packit 2035a7
static BOOL MyIsDirectory(const std::string& path)
Packit 2035a7
{
Packit 2035a7
#ifdef __BORLANDC__
Packit 2035a7
    return (GetFileAttributes(path.c_str()) & FILE_ATTRIBUTE_DIRECTORY);
Packit 2035a7
#else
Packit 2035a7
// See http://msdn.microsoft.com/en-us/library/bb773621(VS.85).aspx
Packit 2035a7
    return PathIsDirectoryA(path.c_str());
Packit 2035a7
#endif
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
static HANDLE MyFindFirstFile(const std::string& path, LPWIN32_FIND_DATAA findData)
Packit 2035a7
{
Packit 2035a7
    HANDLE hFind = FindFirstFileA(path.c_str(), findData);
Packit 2035a7
    return hFind;
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
static BOOL MyFileExists(const std::string& path)
Packit 2035a7
{
Packit 2035a7
#ifdef __BORLANDC__
Packit 2035a7
    DWORD fa = GetFileAttributes(path.c_str());
Packit 2035a7
    BOOL result = FALSE;
Packit 2035a7
    if (fa != INVALID_FILE_ATTRIBUTES && !(fa & FILE_ATTRIBUTE_DIRECTORY))
Packit 2035a7
        result = TRUE;
Packit 2035a7
#else
Packit 2035a7
    BOOL result = PathFileExistsA(path.c_str());
Packit 2035a7
#endif
Packit 2035a7
    return result;
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
void FileLister::recursiveAddFiles(std::map<std::string, std::size_t> &files, const std::string &path, const std::set<std::string> &extra, const PathMatch& ignored)
Packit 2035a7
{
Packit 2035a7
    addFiles(files, path, extra, true, ignored);
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
void FileLister::addFiles(std::map<std::string, std::size_t> &files, const std::string &path, const std::set<std::string> &extra, bool recursive, const PathMatch& ignored)
Packit 2035a7
{
Packit 2035a7
    const std::string cleanedPath = Path::toNativeSeparators(path);
Packit 2035a7
Packit 2035a7
    // basedir is the base directory which is used to form pathnames.
Packit 2035a7
    // It always has a trailing backslash available for concatenation.
Packit 2035a7
    std::string basedir;
Packit 2035a7
Packit 2035a7
    // searchPattern is the search string passed into FindFirst and FindNext.
Packit 2035a7
    std::string searchPattern = cleanedPath;
Packit 2035a7
Packit 2035a7
    // The user wants to check all files in a dir
Packit 2035a7
    const bool checkAllFilesInDir = (MyIsDirectory(cleanedPath) != FALSE);
Packit 2035a7
Packit 2035a7
    if (checkAllFilesInDir) {
Packit 2035a7
        char c = cleanedPath.back();
Packit 2035a7
        switch (c) {
Packit 2035a7
        case '\\':
Packit 2035a7
            searchPattern += '*';
Packit 2035a7
            basedir = cleanedPath;
Packit 2035a7
            break;
Packit 2035a7
        case '*':
Packit 2035a7
            basedir = cleanedPath.substr(0, cleanedPath.length() - 1);
Packit 2035a7
            break;
Packit 2035a7
        default:
Packit 2035a7
            searchPattern += "\\*";
Packit 2035a7
            if (cleanedPath != ".")
Packit 2035a7
                basedir = cleanedPath + '\\';
Packit 2035a7
        }
Packit 2035a7
    } else {
Packit 2035a7
        std::string::size_type pos = cleanedPath.find_last_of('\\');
Packit 2035a7
        if (std::string::npos != pos) {
Packit 2035a7
            basedir = cleanedPath.substr(0, pos + 1);
Packit 2035a7
        }
Packit 2035a7
    }
Packit 2035a7
Packit 2035a7
    WIN32_FIND_DATAA ffd;
Packit 2035a7
    HANDLE hFind = MyFindFirstFile(searchPattern, &ffd;;
Packit 2035a7
    if (INVALID_HANDLE_VALUE == hFind)
Packit 2035a7
        return;
Packit 2035a7
Packit 2035a7
    do {
Packit 2035a7
        if (ffd.cFileName[0] == '.' || ffd.cFileName[0] == '\0')
Packit 2035a7
            continue;
Packit 2035a7
Packit 2035a7
        const char* ansiFfd = ffd.cFileName;
Packit 2035a7
        if (std::strchr(ansiFfd,'?')) {
Packit 2035a7
            ansiFfd = ffd.cAlternateFileName;
Packit 2035a7
        }
Packit 2035a7
Packit 2035a7
        const std::string fname(basedir + ansiFfd);
Packit 2035a7
Packit 2035a7
        if ((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
Packit 2035a7
            // File
Packit 2035a7
            if ((!checkAllFilesInDir || Path::acceptFile(fname, extra)) && !ignored.match(fname)) {
Packit 2035a7
                const std::string nativename = Path::fromNativeSeparators(fname);
Packit 2035a7
Packit 2035a7
                // Limitation: file sizes are assumed to fit in a 'size_t'
Packit 2035a7
#ifdef _WIN64
Packit 2035a7
                files[nativename] = (static_cast<std::size_t>(ffd.nFileSizeHigh) << 32) | ffd.nFileSizeLow;
Packit 2035a7
#else
Packit 2035a7
                files[nativename] = ffd.nFileSizeLow;
Packit 2035a7
#endif
Packit 2035a7
            }
Packit 2035a7
        } else {
Packit 2035a7
            // Directory
Packit 2035a7
            if (recursive) {
Packit 2035a7
                if (!ignored.match(fname))
Packit 2035a7
                    FileLister::recursiveAddFiles(files, fname, extra, ignored);
Packit 2035a7
            }
Packit 2035a7
        }
Packit 2035a7
    } while (FindNextFileA(hFind, &ffd) != FALSE);
Packit 2035a7
Packit 2035a7
    FindClose(hFind);
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
bool FileLister::isDirectory(const std::string &path)
Packit 2035a7
{
Packit 2035a7
    return (MyIsDirectory(path) != FALSE);
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
bool FileLister::fileExists(const std::string &path)
Packit 2035a7
{
Packit 2035a7
    return (MyFileExists(path) != FALSE);
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
Packit 2035a7
#else
Packit 2035a7
Packit 2035a7
///////////////////////////////////////////////////////////////////////////////
Packit 2035a7
////// This code is POSIX-style systems ///////////////////////////////////////
Packit 2035a7
///////////////////////////////////////////////////////////////////////////////
Packit 2035a7
Packit 2035a7
#if defined(__CYGWIN__)
Packit 2035a7
#undef __STRICT_ANSI__
Packit 2035a7
#endif
Packit 2035a7
Packit 2035a7
#include <dirent.h>
Packit 2035a7
#include <sys/stat.h>
Packit 2035a7
Packit 2035a7
Packit 2035a7
static void addFiles2(std::map<std::string, std::size_t> &files,
Packit 2035a7
                      const std::string &path,
Packit 2035a7
                      const std::set<std::string> &extra,
Packit 2035a7
                      bool recursive,
Packit 2035a7
                      const PathMatch& ignored
Packit 2035a7
                     )
Packit 2035a7
{
Packit 2035a7
    struct stat file_stat;
Packit 2035a7
    if (stat(path.c_str(), &file_stat) != -1) {
Packit 2035a7
        if ((file_stat.st_mode & S_IFMT) == S_IFDIR) {
Packit 2035a7
            DIR * dir = opendir(path.c_str());
Packit 2035a7
Packit 2035a7
            if (!dir)
Packit 2035a7
                return;
Packit 2035a7
Packit 2035a7
            dirent entry;
Packit 2035a7
            dirent * dir_result;
Packit 2035a7
Packit 2035a7
            std::string new_path;
Packit 2035a7
            new_path.reserve(path.length() + 100);// prealloc some memory to avoid constant new/deletes in loop
Packit 2035a7
Packit 2035a7
            while ((readdir_r(dir, &entry, &dir_result) == 0) && (dir_result != nullptr)) {
Packit 2035a7
Packit 2035a7
                if ((std::strcmp(dir_result->d_name, ".") == 0) ||
Packit 2035a7
                    (std::strcmp(dir_result->d_name, "..") == 0))
Packit 2035a7
                    continue;
Packit 2035a7
Packit 2035a7
                new_path = path + '/' + dir_result->d_name;
Packit 2035a7
Packit 2035a7
                if (dir_result->d_type == DT_DIR || (dir_result->d_type == DT_UNKNOWN && FileLister::isDirectory(new_path))) {
Packit 2035a7
                    if (recursive && !ignored.match(new_path)) {
Packit 2035a7
                        addFiles2(files, new_path, extra, recursive, ignored);
Packit 2035a7
                    }
Packit 2035a7
                } else {
Packit 2035a7
                    if (Path::acceptFile(new_path, extra) && !ignored.match(new_path)) {
Packit 2035a7
                        files[new_path] = file_stat.st_size;
Packit 2035a7
                    }
Packit 2035a7
                }
Packit 2035a7
            }
Packit 2035a7
            closedir(dir);
Packit 2035a7
        } else
Packit 2035a7
            files[path] = file_stat.st_size;
Packit 2035a7
    }
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
void FileLister::recursiveAddFiles(std::map<std::string, std::size_t> &files, const std::string &path, const std::set<std::string> &extra, const PathMatch& ignored)
Packit 2035a7
{
Packit 2035a7
    addFiles(files, path, extra, true, ignored);
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
void FileLister::addFiles(std::map<std::string, std::size_t> &files, const std::string &path, const std::set<std::string> &extra, bool recursive, const PathMatch& ignored)
Packit 2035a7
{
Packit 2035a7
    if (!path.empty()) {
Packit 2035a7
        std::string corrected_path = path;
Packit 2035a7
        if (endsWith(corrected_path, '/'))
Packit 2035a7
            corrected_path.erase(corrected_path.end() - 1);
Packit 2035a7
Packit 2035a7
        addFiles2(files, corrected_path, extra, recursive, ignored);
Packit 2035a7
    }
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
bool FileLister::isDirectory(const std::string &path)
Packit 2035a7
{
Packit 2035a7
    struct stat file_stat;
Packit 2035a7
    return (stat(path.c_str(), &file_stat) != -1 && (file_stat.st_mode & S_IFMT) == S_IFDIR);
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
bool FileLister::fileExists(const std::string &path)
Packit 2035a7
{
Packit 2035a7
    struct stat file_stat;
Packit 2035a7
    return (stat(path.c_str(), &file_stat) != -1 && (file_stat.st_mode & S_IFMT) == S_IFREG);
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
#endif