diff --git a/main.cpp b/main.cpp index 3f02773..8173e99 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 { diff --git a/simplecpp.cpp b/simplecpp.cpp index 9c3f3a0..66859d3 100755 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #if __cplusplus >= 201103L #ifdef SIMPLECPP_WINDOWS #include @@ -39,6 +40,12 @@ #include #include +#ifdef _WIN32 +using mode_t = unsigned short; +#else +#include +#endif + #ifdef SIMPLECPP_WINDOWS #include #undef ERROR @@ -3077,9 +3084,11 @@ static std::string openHeader(std::ifstream &f, const std::string &path) if (nonExistingFilesCache.contains(simplePath)) return ""; // file is known not to exist, skip expensive file open call #endif - f.open(simplePath.c_str()); - if (f.is_open()) - return simplePath; + if (simplecpp::isFile(simplePath)) { + f.open(simplePath.c_str()); + if (f.is_open()) + return simplePath; + } #ifdef SIMPLECPP_WINDOWS nonExistingFilesCache.add(simplePath); #endif @@ -3184,18 +3193,19 @@ std::map simplecpp::load(const simplecpp::To if (ret.find(filename) != ret.end()) continue; - std::ifstream fin(filename.c_str()); - if (!fin.is_open()) { - if (outputList) { - simplecpp::Output err(filenames); - err.type = simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND; - err.location = Location(filenames); - err.msg = "Can not open include file '" + filename + "' that is explicitly included."; - outputList->push_back(err); + { + std::ifstream fin(filename.c_str()); + if (!fin.is_open() || !isFile(filename)) { + if (outputList) { + simplecpp::Output err(filenames); + err.type = simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND; + err.location = Location(filenames); + err.msg = "Can not open include file '" + filename + "' that is explicitly included."; + outputList->push_back(err); + } + continue; } - continue; } - fin.close(); TokenList *tokenlist = new TokenList(filename, filenames, outputList); if (!tokenlist->front()) { @@ -3835,6 +3845,24 @@ std::string simplecpp::getCppStdString(const std::string &std) return ""; } +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; +} + #if (__cplusplus < 201103L) && !defined(__APPLE__) #undef nullptr #endif diff --git a/simplecpp.h b/simplecpp.h index 398024d..c2938be 100755 --- a/simplecpp.h +++ b/simplecpp.h @@ -373,6 +373,20 @@ namespace simplecpp { /** Returns the __cplusplus value for a given standard */ SIMPLECPP_LIB std::string getCppStdString(const std::string &std); + + /** + * @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); } #if defined(_MSC_VER) diff --git a/test.cpp b/test.cpp index d00658a..2c835be 100644 --- a/test.cpp +++ b/test.cpp @@ -1779,6 +1779,44 @@ static void missingHeader3() ASSERT_EQUALS("", toString(outputList)); } +#ifndef _WIN32 +static void missingHeader4() +{ + // 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 missingHeader5() +{ + // 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 missingHeader6() +{ + // 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 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)); +} +#endif + static void nestedInclude() { const char code[] = "#include \"test.h\"\n"; @@ -2806,6 +2844,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); @@ -2959,6 +3007,12 @@ int main(int argc, char **argv) TEST_CASE(missingHeader1); TEST_CASE(missingHeader2); TEST_CASE(missingHeader3); +#ifndef _WIN32 + TEST_CASE(missingHeader4); + TEST_CASE(missingHeader5); + TEST_CASE(missingHeader6); + TEST_CASE(missingHeader7); +#endif TEST_CASE(nestedInclude); TEST_CASE(systemInclude); @@ -3037,5 +3091,7 @@ int main(int argc, char **argv) TEST_CASE(fuzz_crash); + TEST_CASE(leak); + return numberOfFailedAssertions > 0 ? EXIT_FAILURE : EXIT_SUCCESS; }