Cppcheck
filelister.cpp
Go to the documentation of this file.
00001 /*
00002  * Cppcheck - A tool for static C/C++ code analysis
00003  * Copyright (C) 2007-2013 Daniel Marjamäki and Cppcheck team.
00004  *
00005  * This program is free software: you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation, either version 3 of the License, or
00008  * (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00017  */
00018 
00019 #include <cstring>
00020 #include <string>
00021 #include <sstream>
00022 #include "filelister.h"
00023 #include "path.h"
00024 
00025 
00026 #ifdef _WIN32
00027 
00028 ///////////////////////////////////////////////////////////////////////////////
00029 ////// This code is WIN32 systems /////////////////////////////////////////////
00030 ///////////////////////////////////////////////////////////////////////////////
00031 
00032 #include <windows.h>
00033 #ifndef __BORLANDC__
00034 #include <shlwapi.h>
00035 #endif
00036 
00037 // Here is the catch: cppcheck core is Ansi code (using char type).
00038 // When compiling Unicode targets WinAPI automatically uses *W Unicode versions
00039 // of called functions. Thus, we explicitly call *A versions of the functions.
00040 
00041 static BOOL MyIsDirectory(const std::string& path)
00042 {
00043 #ifdef __BORLANDC__
00044     return (GetFileAttributes(path.c_str()) & FILE_ATTRIBUTE_DIRECTORY);
00045 #else
00046 // See http://msdn.microsoft.com/en-us/library/bb773621(VS.85).aspx
00047     return PathIsDirectoryA(path.c_str());
00048 #endif
00049 }
00050 
00051 static HANDLE MyFindFirstFile(const std::string& path, LPWIN32_FIND_DATAA findData)
00052 {
00053     HANDLE hFind = FindFirstFileA(path.c_str(), findData);
00054     return hFind;
00055 }
00056 
00057 static BOOL MyFileExists(const std::string& path)
00058 {
00059 #ifdef __BORLANDC__
00060     DWORD fa = GetFileAttributes(path.c_str());
00061     BOOL result = FALSE;
00062     if (fa != INVALID_FILE_ATTRIBUTES && !(fa & FILE_ATTRIBUTE_DIRECTORY))
00063         result = TRUE;
00064 #else
00065     BOOL result = PathFileExistsA(path.c_str());
00066 #endif
00067     return result;
00068 }
00069 
00070 void FileLister::recursiveAddFiles(std::map<std::string, std::size_t> &files, const std::string &path)
00071 {
00072     const std::string cleanedPath = Path::toNativeSeparators(path);
00073 
00074     // basedir is the base directory which is used to form pathnames.
00075     // It always has a trailing backslash available for concatenation.
00076     std::string basedir;
00077 
00078     // searchPattern is the search string passed into FindFirst and FindNext.
00079     std::string searchPattern = cleanedPath;
00080 
00081     // The user wants to check all files in a dir
00082     const bool checkAllFilesInDir = (MyIsDirectory(cleanedPath) != FALSE);
00083 
00084     if (checkAllFilesInDir) {
00085         char c = cleanedPath[ cleanedPath.size()-1 ];
00086         switch (c) {
00087         case '\\':
00088             searchPattern += '*';
00089             basedir = cleanedPath;
00090             break;
00091         case '*':
00092             basedir = cleanedPath.substr(0, cleanedPath.length() - 1);
00093             break;
00094         default:
00095             searchPattern += "\\*";
00096             if (cleanedPath != ".")
00097                 basedir = cleanedPath + '\\';
00098         }
00099     } else {
00100         std::string::size_type pos = cleanedPath.find_last_of('\\');
00101         if (std::string::npos != pos) {
00102             basedir = cleanedPath.substr(0, pos + 1);
00103         }
00104     }
00105 
00106     WIN32_FIND_DATAA ffd;
00107     HANDLE hFind = MyFindFirstFile(searchPattern, &ffd);
00108     if (INVALID_HANDLE_VALUE == hFind)
00109         return;
00110 
00111     do {
00112         if (ffd.cFileName[0] == '.' || ffd.cFileName[0] == '\0')
00113             continue;
00114 
00115         const char* ansiFfd = ffd.cFileName;
00116         if (strchr(ansiFfd,'?')) {
00117             ansiFfd = ffd.cAlternateFileName;
00118         }
00119 
00120         const std::string fname(basedir + ansiFfd);
00121 
00122         if ((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
00123             // File
00124             const std::string nativename = Path::fromNativeSeparators(fname);
00125 
00126             if (!checkAllFilesInDir || Path::acceptFile(fname)) {
00127                 // Limitation: file sizes are assumed to fit in a 'size_t'
00128 #ifdef _WIN64
00129                 files[nativename] = (static_cast<std::size_t>(ffd.nFileSizeHigh) << 32) | ffd.nFileSizeLow;
00130 #else
00131                 files[nativename] = ffd.nFileSizeLow;
00132 #endif
00133             }
00134         } else {
00135             // Directory
00136             FileLister::recursiveAddFiles(files, fname);
00137         }
00138     } while (FindNextFileA(hFind, &ffd) != FALSE);
00139 
00140     if (INVALID_HANDLE_VALUE != hFind) {
00141         FindClose(hFind);
00142     }
00143 }
00144 
00145 bool FileLister::isDirectory(const std::string &path)
00146 {
00147     return (MyIsDirectory(path) != FALSE);
00148 }
00149 
00150 bool FileLister::fileExists(const std::string &path)
00151 {
00152     return (MyFileExists(path) == TRUE);
00153 }
00154 
00155 
00156 #else
00157 
00158 ///////////////////////////////////////////////////////////////////////////////
00159 ////// This code is POSIX-style systems ///////////////////////////////////////
00160 ///////////////////////////////////////////////////////////////////////////////
00161 
00162 #include <glob.h>
00163 #include <unistd.h>
00164 #include <stdlib.h>
00165 #include <limits.h>
00166 #include <sys/stat.h>
00167 
00168 // Get absolute path. Returns empty string if path does not exist or other error.
00169 std::string FileLister::getAbsolutePath(const std::string& path)
00170 {
00171     std::string absolute_path;
00172 
00173 #ifdef PATH_MAX
00174     char buf[PATH_MAX];
00175     if (realpath(path.c_str(), buf) != NULL)
00176         absolute_path = buf;
00177 #else
00178     char *dynamic_buf;
00179     if ((dynamic_buf = realpath(path.c_str(), NULL)) != NULL) {
00180         absolute_path = dynamic_buf;
00181         free(dynamic_buf);
00182     }
00183 #endif
00184 
00185     return absolute_path;
00186 }
00187 
00188 void FileLister::recursiveAddFiles2(std::set<std::string> &seen_paths,
00189                                     std::map<std::string, std::size_t> &files,
00190                                     const std::string &path)
00191 {
00192     std::ostringstream oss;
00193     oss << path;
00194     if (path.length() > 0 && path[path.length()-1] == '/')
00195         oss << "*";
00196 
00197     glob_t glob_results;
00198     glob(oss.str().c_str(), GLOB_MARK, 0, &glob_results);
00199     for (unsigned int i = 0; i < glob_results.gl_pathc; i++) {
00200         const std::string filename = glob_results.gl_pathv[i];
00201         if (filename == "." || filename == ".." || filename.length() == 0)
00202             continue;
00203 
00204         // Determine absolute path. Empty filename if path does not exist
00205         const std::string absolute_path = getAbsolutePath(filename);
00206         if (absolute_path.empty())
00207             continue;
00208 
00209         // Did we already process this entry?
00210         if (seen_paths.find(absolute_path) != seen_paths.end())
00211             continue;
00212 
00213         if (filename[filename.length()-1] != '/') {
00214             // File
00215 
00216             if (Path::sameFileName(path,filename) || Path::acceptFile(filename)) {
00217                 seen_paths.insert(absolute_path);
00218 
00219                 struct stat sb;
00220                 if (stat(absolute_path.c_str(), &sb) == 0) {
00221                     // Limitation: file sizes are assumed to fit in a 'size_t'
00222                     files[filename] = static_cast<std::size_t>(sb.st_size);
00223                 } else
00224                     files[filename] = 0;
00225             }
00226         } else {
00227             // Directory
00228 
00229             seen_paths.insert(absolute_path);
00230             recursiveAddFiles2(seen_paths, files, filename);
00231         }
00232     }
00233     globfree(&glob_results);
00234 }
00235 
00236 
00237 void FileLister::recursiveAddFiles(std::map<std::string, std::size_t> &files, const std::string &path)
00238 {
00239     std::set<std::string> seen_paths;
00240     recursiveAddFiles2(seen_paths, files, path);
00241 }
00242 
00243 bool FileLister::isDirectory(const std::string &path)
00244 {
00245     bool ret = false;
00246 
00247     glob_t glob_results;
00248     glob(path.c_str(), GLOB_MARK, 0, &glob_results);
00249     if (glob_results.gl_pathc == 1) {
00250         const std::string glob_path = glob_results.gl_pathv[0];
00251         if (!glob_path.empty() && glob_path[glob_path.size() - 1] == '/') {
00252             ret = true;
00253         }
00254     }
00255     globfree(&glob_results);
00256 
00257     return ret;
00258 }
00259 
00260 bool FileLister::fileExists(const std::string &path)
00261 {
00262     struct stat statinfo;
00263     int result = stat(path.c_str(), &statinfo);
00264 
00265     if (result < 0) { // Todo: should check errno == ENOENT?
00266         // File not found
00267         return false;
00268     }
00269 
00270     // Check if file is regular file
00271     if ((statinfo.st_mode & S_IFMT) == S_IFREG)
00272         return true;
00273 
00274     return false;
00275 }
00276 
00277 #endif