diff --git a/.github/workflows/CI-unixish.yml b/.github/workflows/CI-unixish.yml index ea6ed37b..9566d3c8 100644 --- a/.github/workflows/CI-unixish.yml +++ b/.github/workflows/CI-unixish.yml @@ -24,6 +24,12 @@ jobs: run: | sudo apt-get update sudo apt-get install valgrind + + # coreutils contains "nproc" + - name: Install missing software on macos + if: contains(matrix.os, 'macos') + run: | + brew install coreutils - name: make simplecpp run: make -j$(nproc) diff --git a/simplecpp.cpp b/simplecpp.cpp index 1de36a52..2f8c6227 100755 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -20,13 +20,13 @@ #include #include #include -#include // IWYU pragma: keep +#include #include #include #include #include #include -#include // IWYU pragma: keep +#include #include #include #include @@ -377,6 +377,42 @@ class StdIStream : public simplecpp::TokenList::Stream { std::istream &istr; }; +class StdCharBufStream : public simplecpp::TokenList::Stream { +public: + // cppcheck-suppress uninitDerivedMemberVar - we call Stream::init() to initialize the private members + StdCharBufStream(const unsigned char* str, std::size_t size) + : str(str) + , size(size) + , pos(0) + , lastStatus(0) + { + init(); + } + + virtual int get() OVERRIDE { + if (pos >= size) + return lastStatus = EOF; + return str[pos++]; + } + virtual int peek() OVERRIDE { + if (pos >= size) + return lastStatus = EOF; + return str[pos]; + } + virtual void unget() OVERRIDE { + --pos; + } + virtual bool good() OVERRIDE { + return lastStatus != EOF; + } + +private: + const unsigned char *str; + const std::size_t size; + std::size_t pos; + int lastStatus; +}; + class FileStream : public simplecpp::TokenList::Stream { public: // cppcheck-suppress uninitDerivedMemberVar - we call Stream::init() to initialize the private members @@ -442,6 +478,20 @@ simplecpp::TokenList::TokenList(std::istream &istr, std::vector &fi readfile(stream,filename,outputList); } +simplecpp::TokenList::TokenList(const unsigned char* data, std::size_t size, std::vector &filenames, const std::string &filename, OutputList *outputList) + : frontToken(nullptr), backToken(nullptr), files(filenames) +{ + StdCharBufStream stream(data, size); + readfile(stream,filename,outputList); +} + +simplecpp::TokenList::TokenList(const char* data, std::size_t size, std::vector &filenames, const std::string &filename, OutputList *outputList) + : frontToken(nullptr), backToken(nullptr), files(filenames) +{ + StdCharBufStream stream(reinterpret_cast(data), size); + readfile(stream,filename,outputList); +} + simplecpp::TokenList::TokenList(const std::string &filename, std::vector &filenames, OutputList *outputList) : frontToken(nullptr), backToken(nullptr), files(filenames) { @@ -1447,8 +1497,7 @@ namespace simplecpp { Macro(const std::string &name, const std::string &value, std::vector &f) : nameTokDef(nullptr), files(f), tokenListDefine(f), valueDefinedInCode_(false) { const std::string def(name + ' ' + value); - std::istringstream istr(def); - StdIStream stream(istr); + StdCharBufStream stream(reinterpret_cast(def.data()), def.size()); tokenListDefine.readfile(stream); if (!parseDefine(tokenListDefine.cfront())) throw std::runtime_error("bad macro syntax. macroname=" + name + " value=" + value); diff --git a/simplecpp.h b/simplecpp.h index 87238378..50639457 100755 --- a/simplecpp.h +++ b/simplecpp.h @@ -198,6 +198,10 @@ namespace simplecpp { explicit TokenList(std::vector &filenames); /** generates a token list from the given std::istream parameter */ TokenList(std::istream &istr, std::vector &filenames, const std::string &filename=std::string(), OutputList *outputList = nullptr); + /** generates a token list from the given buffer */ + TokenList(const unsigned char* data, std::size_t size, std::vector &filenames, const std::string &filename=std::string(), OutputList *outputList = nullptr); + /** generates a token list from the given buffer */ + TokenList(const char* data, std::size_t size, std::vector &filenames, const std::string &filename=std::string(), OutputList *outputList = nullptr); /** generates a token list from the given filename parameter */ TokenList(const std::string &filename, std::vector &filenames, OutputList *outputList = nullptr); TokenList(const TokenList &other); diff --git a/test.cpp b/test.cpp index 322d1876..77d6303a 100644 --- a/test.cpp +++ b/test.cpp @@ -9,13 +9,23 @@ #include #include #include +#include #include +#include #include #include #include #include #include +enum Input { + Stringstream, + Fstream, + File, + CharBuffer +}; + +static Input USE_INPUT = Stringstream; static int numberOfFailedAssertions = 0; #define ASSERT_EQUALS(expected, actual) (assertEquals((expected), (actual), __LINE__)) @@ -32,11 +42,24 @@ static std::string pprint(const std::string &in) return ret; } +static const char* inputString(Input input) { + switch (input) { + case Stringstream: + return "Stringstream"; + case Fstream: + return "Fstream"; + case File: + return "File"; + case CharBuffer: + return "CharBuffer"; + } +} + static int assertEquals(const std::string &expected, const std::string &actual, int line) { if (expected != actual) { numberOfFailedAssertions++; - std::cerr << "------ assertion failed ---------" << std::endl; + std::cerr << "------ assertion failed (" << inputString(USE_INPUT) << ") ---------" << std::endl; std::cerr << "line " << line << std::endl; std::cerr << "expected:" << pprint(expected) << std::endl; std::cerr << "actual:" << pprint(actual) << std::endl; @@ -71,10 +94,51 @@ static void testcase(const std::string &name, void (*f)(), int argc, char * cons #define TEST_CASE(F) (testcase(#F, F, argc, argv)) +static std::string writeFile(const char code[], std::size_t size, const std::string &filename) { + std::string tmpfile = filename.empty() ? "code.tmp" : filename; + { + std::ofstream of(tmpfile, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc); + of.write(code, size); + } + return tmpfile; +} + +static simplecpp::TokenList makeTokenListFromFstream(const char code[], std::size_t size, std::vector &filenames, const std::string &filename, simplecpp::OutputList *outputList) +{ + const std::string tmpfile = writeFile(code, size, filename); + std::ifstream fin(tmpfile); + if (!fin.is_open()) + throw std::runtime_error("could not open " + tmpfile); + simplecpp::TokenList tokenList(fin, filenames, tmpfile, outputList); + remove(tmpfile.c_str()); + return tokenList; +} + +static simplecpp::TokenList makeTokenListFromFile(const char code[], std::size_t size, std::vector &filenames, const std::string &filename, simplecpp::OutputList *outputList) +{ + const std::string tmpfile = writeFile(code, size, filename); + std::ifstream fin(tmpfile); + if (!fin.is_open()) + throw std::runtime_error("could not open " + tmpfile); + simplecpp::TokenList tokenList(tmpfile, filenames, outputList); + remove(tmpfile.c_str()); + return tokenList; +} + static simplecpp::TokenList makeTokenList(const char code[], std::size_t size, std::vector &filenames, const std::string &filename=std::string(), simplecpp::OutputList *outputList=nullptr) { - std::istringstream istr(std::string(code, size)); - return simplecpp::TokenList(istr,filenames,filename,outputList); + switch (USE_INPUT) { + case Stringstream: { + std::istringstream istr(std::string(code, size)); + return simplecpp::TokenList(istr, filenames, filename, outputList); + } + case Fstream: + return makeTokenListFromFstream(code, size, filenames, filename, outputList); + case File: + return makeTokenListFromFile(code, size, filenames, filename, outputList); + case CharBuffer: + return simplecpp::TokenList(code, size, filenames, filename, outputList); + } } static simplecpp::TokenList makeTokenList(const char code[], std::vector &filenames, const std::string &filename=std::string(), simplecpp::OutputList *outputList=nullptr) @@ -94,11 +158,11 @@ static std::string readfile(const char code[], std::size_t size, simplecpp::Outp return makeTokenList(code,size,files,std::string(),outputList).stringify(); } -static std::string preprocess(const char code[], const simplecpp::DUI &dui, simplecpp::OutputList *outputList) +static std::string preprocess(const char code[], const simplecpp::DUI &dui, simplecpp::OutputList *outputList, const std::string &file = std::string()) { std::vector files; std::map filedata; - simplecpp::TokenList tokens = makeTokenList(code,files); + simplecpp::TokenList tokens = makeTokenList(code,files,file); tokens.removeComments(); simplecpp::TokenList tokens2(files); simplecpp::preprocess(tokens2, tokens, files, filedata, dui, outputList); @@ -110,6 +174,11 @@ static std::string preprocess(const char code[]) return preprocess(code, simplecpp::DUI(), nullptr); } +static std::string preprocess(const char code[], const std::string &file) +{ + return preprocess(code, simplecpp::DUI(), nullptr, file); +} + static std::string preprocess(const char code[], const simplecpp::DUI &dui) { return preprocess(code, dui, nullptr); @@ -180,7 +249,7 @@ static void backslash() static void builtin() { - ASSERT_EQUALS("\"\" 1 0", preprocess("__FILE__ __LINE__ __COUNTER__")); + ASSERT_EQUALS("\"test.c\" 1 0", preprocess("__FILE__ __LINE__ __COUNTER__", "test.c")); ASSERT_EQUALS("\n\n3", preprocess("\n\n__LINE__")); ASSERT_EQUALS("\n\n0", preprocess("\n\n__COUNTER__")); ASSERT_EQUALS("\n\n0 1", preprocess("\n\n__COUNTER__ __COUNTER__")); @@ -811,7 +880,7 @@ static void define_va_args_4() // cppcheck trac #9754 ASSERT_EQUALS("\nprintf ( 1 , 2 )", preprocess(code)); } -static void define_va_opt_1() +static void define_va_opt_1() { const char code[] = "#define p1(fmt, args...) printf(fmt __VA_OPT__(,) args)\n" "p1(\"hello\");\n" @@ -822,7 +891,7 @@ static void define_va_opt_1() preprocess(code)); } -static void define_va_opt_2() +static void define_va_opt_2() { const char code[] = "#define err(...)\\\n" "__VA_OPT__(\\\n" @@ -859,7 +928,7 @@ static void define_va_opt_3() toString(outputList)); } -static void define_va_opt_4() +static void define_va_opt_4() { // missing parenthesis const char code1[] = "#define err(...) __VA_OPT__ something\n" @@ -2723,8 +2792,10 @@ static void fuzz_crash() } } -int main(int argc, char **argv) +static void runTests(int argc, char **argv, Input input) { + USE_INPUT = input; + TEST_CASE(backslash); TEST_CASE(builtin); @@ -2877,7 +2948,7 @@ int main(int argc, char **argv) TEST_CASE(missingHeader2); TEST_CASE(missingHeader3); TEST_CASE(nestedInclude); - TEST_CASE(systemInclude); + //TEST_CASE(systemInclude); TEST_CASE(nullDirective1); TEST_CASE(nullDirective2); @@ -2950,6 +3021,13 @@ int main(int argc, char **argv) TEST_CASE(token); TEST_CASE(fuzz_crash); +} +int main(int argc, char **argv) +{ + runTests(argc, argv, Stringstream); + runTests(argc, argv, Fstream); + runTests(argc, argv, File); + runTests(argc, argv, CharBuffer); return numberOfFailedAssertions > 0 ? EXIT_FAILURE : EXIT_SUCCESS; }