diff --git a/main.cpp b/main.cpp index a6d14386..2cb71827 100644 --- a/main.cpp +++ b/main.cpp @@ -121,6 +121,10 @@ int main(int argc, char **argv) std::cout << "error: could not open file '" << filename << "'" << std::endl; std::exit(1); } + if (!simplecpp::isFile(filename)) { + std::cout << "error: could not open file '" << filename << "' - not a regular file" << std::endl; + std::exit(1); + } rawtokens = new simplecpp::TokenList(f, files,filename,&outputList); } else { rawtokens = new simplecpp::TokenList(filename,files,&outputList); diff --git a/simplecpp.cpp b/simplecpp.cpp index fd327549..8d6c9e37 100644 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -42,8 +42,10 @@ #ifdef _WIN32 # include +using mode_t = unsigned short; #else # include +# include #endif static bool isHex(const std::string &s) @@ -2979,9 +2981,11 @@ static std::string openHeaderDirect(std::ifstream &f, const std::string &path) if (nonExistingFilesCache.contains(path)) return ""; // file is known not to exist, skip expensive file open call #endif - f.open(path.c_str()); - if (f.is_open()) - return path; + if (simplecpp::isFile(path)) { + f.open(path.c_str()); + if (f.is_open()) + return path; + } #ifdef SIMPLECPP_WINDOWS nonExistingFilesCache.add(path); #endif @@ -3102,6 +3106,9 @@ bool simplecpp::FileDataCache::getFileId(const std::string &path, FileID &id) if (stat(path.c_str(), &statbuf) != 0) return false; + if ((statbuf.st_mode & S_IFMT) != S_IFREG) + return false; + id.dev = statbuf.st_dev; id.ino = statbuf.st_ino; @@ -3838,3 +3845,21 @@ std::string simplecpp::getCppStdString(const std::string &std) { return getCppStdString(getCppStd(std)); } + +static mode_t file_type(const std::string &path) +{ + struct stat file_stat; + if (stat(path.c_str(), &file_stat) == -1) + return 0; + return file_stat.st_mode & S_IFMT; +} + +bool simplecpp::isFile(const std::string &path) +{ + return file_type(path) == S_IFREG; +} + +bool simplecpp::isDirectory(const std::string &path) +{ + return file_type(path) == S_IFDIR; +} diff --git a/simplecpp.h b/simplecpp.h index 8268fa8d..1efdc96e 100644 --- a/simplecpp.h +++ b/simplecpp.h @@ -353,6 +353,20 @@ namespace simplecpp { bool removeComments; /** remove comment tokens from included files */ }; + /** + * @brief Checks if given path is a file + * @param path Path to be checked + * @return true if given path is a file + */ + SIMPLECPP_LIB bool isFile(const std::string &path); + + /** + * @brief Checks if a given path is a directory + * @param path Path to be checked + * @return true if given path is a directory + */ + SIMPLECPP_LIB bool isDirectory(const std::string &path); + struct SIMPLECPP_LIB FileData { /** The canonical filename associated with this data */ std::string filename; diff --git a/test.cpp b/test.cpp index ccb653ca..bc321fc9 100644 --- a/test.cpp +++ b/test.cpp @@ -2053,6 +2053,44 @@ static void missingHeader4() ASSERT_EQUALS("file0,1,syntax_error,No header in #include\n", toString(outputList)); } +#ifndef _WIN32 +static void missingHeader5() +{ + // this is a directory + const char code[] = "#include \"/\"\n"; + simplecpp::OutputList outputList; + ASSERT_EQUALS("", preprocess(code, &outputList)); + ASSERT_EQUALS("file0,1,missing_header,Header not found: \"/\"\n", toString(outputList)); +} + +static void missingHeader6() +{ + // this is a directory + const char code[] = "#include \"/usr\"\n"; + simplecpp::OutputList outputList; + ASSERT_EQUALS("", preprocess(code, &outputList)); + ASSERT_EQUALS("file0,1,missing_header,Header not found: \"/usr\"\n", toString(outputList)); +} + +static void missingHeader7() +{ + // this is a directory + const char code[] = "#include \n"; + simplecpp::OutputList outputList; + ASSERT_EQUALS("", preprocess(code, &outputList)); + ASSERT_EQUALS("file0,1,missing_header,Header not found: \n", toString(outputList)); +} + +static void missingHeader8() +{ + // this is a directory + const char code[] = "#include \n"; + simplecpp::OutputList outputList; + ASSERT_EQUALS("", preprocess(code, &outputList)); + ASSERT_EQUALS("file0,1,missing_header,Header not found: \n", toString(outputList)); +} +#endif + static void nestedInclude() { const char code[] = "#include \"test.h\"\n"; @@ -3183,6 +3221,16 @@ static void fuzz_crash() } } +static void leak() +{ + const char code[] = "#include\n" + "#include\n"; + simplecpp::OutputList outputList; + ASSERT_EQUALS("", preprocess(code, &outputList)); + ASSERT_EQUALS("file0,1,missing_header,Header not found: \n" + "file0,2,missing_header,Header not found: \n", toString(outputList)); +} + int main(int argc, char **argv) { TEST_CASE(backslash); @@ -3356,6 +3404,12 @@ int main(int argc, char **argv) TEST_CASE(missingHeader2); TEST_CASE(missingHeader3); TEST_CASE(missingHeader4); +#ifndef _WIN32 + TEST_CASE(missingHeader5); + TEST_CASE(missingHeader6); + TEST_CASE(missingHeader7); + TEST_CASE(missingHeader8); +#endif TEST_CASE(nestedInclude); TEST_CASE(systemInclude); TEST_CASE(circularInclude); @@ -3437,5 +3491,7 @@ int main(int argc, char **argv) TEST_CASE(fuzz_crash); + TEST_CASE(leak); + return numberOfFailedAssertions > 0 ? EXIT_FAILURE : EXIT_SUCCESS; }