diff --git a/man/cppcheck.1.xml b/man/cppcheck.1.xml
index e766ed252..9e009599c 100644
--- a/man/cppcheck.1.xml
+++ b/man/cppcheck.1.xml
@@ -111,6 +111,7 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/
+
@@ -200,6 +201,12 @@ files, this is not needed.
Check coding style.
+
+
+
+ Format the error messages. E.g. '{file}:{line},{severity},{id},{message}' or '{file}({line}):({severity}) {message}'
+
+
diff --git a/src/cppcheck.cpp b/src/cppcheck.cpp
index 76da83c09..f488c8164 100644
--- a/src/cppcheck.cpp
+++ b/src/cppcheck.cpp
@@ -172,7 +172,22 @@ std::string CppCheck::parseFromArgs(int argc, const char* const argv[])
_settings._includePaths.push_back(path);
}
-// Include paths
+
+ // Output formatter
+ else if (strcmp(argv[i], "--template") == 0)
+ {
+ // "--template path/"
+ if (strcmp(argv[i], "--template") == 0)
+ {
+ ++i;
+ if (i >= argc)
+ return "cppcheck: argument to '--template' is missing\n";
+
+ _settings._outputFormat = argv[i];
+ }
+ }
+
+ // Include paths
else if (strcmp(argv[i], "-j") == 0 ||
strncmp(argv[i], "-j", 2) == 0)
{
@@ -308,6 +323,9 @@ std::string CppCheck::parseFromArgs(int argc, const char* const argv[])
" -j [jobs] Start [jobs] threads to do the checking simultaneously.\n"
" -q, --quiet Only print error messages\n"
" -s, --style Check coding style\n"
+ " --template '[text]' Format the error messages. E.g.\n"
+ " '{file}:{line},{severity},{id},{message}' or\n"
+ " '{file}({line}):({severity}) {message}'\n"
" --unused-functions Check if there are unused functions\n"
" -v, --verbose More detailed error reports\n"
" --version Print out version number\n"
diff --git a/src/cppcheckexecutor.cpp b/src/cppcheckexecutor.cpp
index 3707b807d..509a08a50 100644
--- a/src/cppcheckexecutor.cpp
+++ b/src/cppcheckexecutor.cpp
@@ -112,6 +112,6 @@ void CppCheckExecutor::reportErr(const ErrorLogger::ErrorMessage &msg)
}
else
{
- reportErr(msg.toText());
+ reportErr(msg.toText(_settings._outputFormat));
}
}
diff --git a/src/errorlogger.cpp b/src/errorlogger.cpp
index 2a22fe3d2..2164b3fb7 100644
--- a/src/errorlogger.cpp
+++ b/src/errorlogger.cpp
@@ -158,15 +158,50 @@ std::string ErrorLogger::ErrorMessage::toXML() const
return xml.str();
}
-std::string ErrorLogger::ErrorMessage::toText() const
+void ErrorLogger::ErrorMessage::findAndReplace(std::string &source, const std::string &searchFor, const std::string &replaceWith)
{
- std::ostringstream text;
- if (!_callStack.empty())
- text << callStackToString(_callStack) << ": ";
- if (!_severity.empty())
- text << "(" << _severity << ") ";
- text << _msg;
- return text.str();
+ std::string::size_type index = 0;
+ while ((index = source.find(searchFor, index)) != std::string::npos)
+ {
+ source.replace(index, searchFor.length(), replaceWith);
+ index += replaceWith.length() - searchFor.length() + 1;
+ }
+}
+
+std::string ErrorLogger::ErrorMessage::toText(const std::string &outputFormat) const
+{
+ if (outputFormat.length() == 0)
+ {
+ std::ostringstream text;
+ if (!_callStack.empty())
+ text << callStackToString(_callStack) << ": ";
+ if (!_severity.empty())
+ text << "(" << _severity << ") ";
+ text << _msg;
+ return text.str();
+ }
+ else
+ {
+ std::string result = outputFormat;
+ findAndReplace(result, "{id}", _id);
+ findAndReplace(result, "{severity}", _severity);
+ findAndReplace(result, "{message}", _msg);
+
+ if (!_callStack.empty())
+ {
+ std::ostringstream oss;
+ oss << _callStack.back().line;
+ findAndReplace(result, "{line}", oss.str());
+ findAndReplace(result, "{file}", _callStack.back().getfile());
+ }
+ else
+ {
+ findAndReplace(result, "{file}", "");
+ findAndReplace(result, "{line}", "");
+ }
+
+ return result;
+ }
}
void ErrorLogger::_writemsg(const Tokenizer *tokenizer, const Token *tok, const char severity[], const std::string &msg, const std::string &id)
diff --git a/src/errorlogger.h b/src/errorlogger.h
index adbc8646d..35b042794 100644
--- a/src/errorlogger.h
+++ b/src/errorlogger.h
@@ -66,7 +66,21 @@ public:
static std::string getXMLHeader();
static std::string getXMLFooter();
- std::string toText() const;
+ /**
+ * Format the error message into a string.
+ * @param outputFormat Empty string to use default output format
+ * or template to be used. E.g. "{file}:{line},{severity},{id},{message}"
+ */
+ std::string toText(const std::string &outputFormat = "") const;
+
+ /**
+ * Replace all occurances of searchFor with replaceWith in the
+ * given source.
+ * @param source The string to modify
+ * @param searchFor What should be searched for
+ * @param replaceWith What will replace the found item
+ */
+ static void findAndReplace(std::string &source, const std::string &searchFor, const std::string &replaceWith);
std::string serialize() const;
bool deserialize(const std::string &data);
std::list _callStack;
diff --git a/src/settings.h b/src/settings.h
index 4ff8bad01..57acb255f 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -65,6 +65,10 @@ public:
Default value is 0. */
int _exitCode;
+ /** The output format in which the errors are printed in text mode,
+ e.g. "{severity} {file}:{line} {message} {id}" */
+ std::string _outputFormat;
+
#ifdef __GNUC__
/** show timing information */
bool _showtime;
diff --git a/test/testcppcheck.cpp b/test/testcppcheck.cpp
index f2911b9a6..46100a39d 100644
--- a/test/testcppcheck.cpp
+++ b/test/testcppcheck.cpp
@@ -53,6 +53,7 @@ private:
TEST_CASE(xml);
TEST_CASE(include);
+ TEST_CASE(templateFormat);
}
void linenumbers()
@@ -104,6 +105,20 @@ private:
ASSERT_EQUALS("[ab/ef.h:0]: ", errmsg.toText());
}
+ void templateFormat()
+ {
+ ErrorLogger::ErrorMessage errmsg;
+ ErrorLogger::ErrorMessage::FileLocation loc;
+ loc.file = "some/{file}file.cpp";
+ loc.line = 10;
+ errmsg._callStack.push_back(loc);
+ errmsg._id = "testId";
+ errmsg._severity = "testSeverity";
+ errmsg._msg = "long testMessage";
+ ASSERT_EQUALS("", errmsg.toXML());
+ ASSERT_EQUALS("[some/{file}file.cpp:10]: (testSeverity) long testMessage", errmsg.toText());
+ ASSERT_EQUALS("testId-some/{file}file.cpp,testSeverity.10?{long testMessage}", errmsg.toText("{id}-{file},{severity}.{line}?{{message}}"));
+ }
};
REGISTER_TEST(TestCppcheck)