"Fossies" - the Fresh Open Source Software Archive

Member "cppcheck-1.89/lib/errorlogger.cpp" (1 Sep 2019, 29815 Bytes) of package /windows/misc/cppcheck-1.89.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "errorlogger.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.88_vs_1.89.

    1 /*
    2  * Cppcheck - A tool for static C/C++ code analysis
    3  * Copyright (C) 2007-2019 Cppcheck team.
    4  *
    5  * This program is free software: you can redistribute it and/or modify
    6  * it under the terms of the GNU General Public License as published by
    7  * the Free Software Foundation, either version 3 of the License, or
    8  * (at your option) any later version.
    9  *
   10  * This program is distributed in the hope that it will be useful,
   11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13  * GNU General Public License for more details.
   14  *
   15  * You should have received a copy of the GNU General Public License
   16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   17  */
   18 
   19 #include "errorlogger.h"
   20 
   21 #include "cppcheck.h"
   22 #include "mathlib.h"
   23 #include "path.h"
   24 #include "token.h"
   25 #include "tokenlist.h"
   26 #include "utils.h"
   27 
   28 #include <tinyxml2.h>
   29 #include <array>
   30 #include <cassert>
   31 #include <cctype>
   32 #include <cstdlib>
   33 #include <cstring>
   34 #include <iomanip>
   35 
   36 InternalError::InternalError(const Token *tok, const std::string &errorMsg, Type type) :
   37     token(tok), errorMessage(errorMsg), type(type)
   38 {
   39     switch (type) {
   40     case AST:
   41         id = "internalAstError";
   42         break;
   43     case SYNTAX:
   44         id = "syntaxError";
   45         break;
   46     case UNKNOWN_MACRO:
   47         id = "unknownMacro";
   48         break;
   49     case INTERNAL:
   50         id = "cppcheckError";
   51         break;
   52     case LIMIT:
   53         id = "cppcheckLimit";
   54         break;
   55     case INSTANTIATION:
   56         id = "instantiationError";
   57         break;
   58     }
   59 }
   60 
   61 ErrorLogger::ErrorMessage::ErrorMessage()
   62     : severity(Severity::none), cwe(0U), inconclusive(false)
   63 {
   64 }
   65 
   66 ErrorLogger::ErrorMessage::ErrorMessage(const std::list<FileLocation> &callStack, const std::string& file1, Severity::SeverityType severity, const std::string &msg, const std::string &id, bool inconclusive) :
   67     callStack(callStack), // locations for this error message
   68     id(id),               // set the message id
   69     file0(file1),
   70     severity(severity),   // severity for this error message
   71     cwe(0U),
   72     inconclusive(inconclusive)
   73 {
   74     // set the summary and verbose messages
   75     setmsg(msg);
   76 }
   77 
   78 
   79 
   80 ErrorLogger::ErrorMessage::ErrorMessage(const std::list<FileLocation> &callStack, const std::string& file1, Severity::SeverityType severity, const std::string &msg, const std::string &id, const CWE &cwe, bool inconclusive) :
   81     callStack(callStack), // locations for this error message
   82     id(id),               // set the message id
   83     file0(file1),
   84     severity(severity),   // severity for this error message
   85     cwe(cwe.id),
   86     inconclusive(inconclusive)
   87 {
   88     // set the summary and verbose messages
   89     setmsg(msg);
   90 }
   91 
   92 ErrorLogger::ErrorMessage::ErrorMessage(const std::list<const Token*>& callstack, const TokenList* list, Severity::SeverityType severity, const std::string& id, const std::string& msg, bool inconclusive)
   93     : id(id), severity(severity), cwe(0U), inconclusive(inconclusive)
   94 {
   95     // Format callstack
   96     for (std::list<const Token *>::const_iterator it = callstack.begin(); it != callstack.end(); ++it) {
   97         // --errorlist can provide null values here
   98         if (!(*it))
   99             continue;
  100 
  101         callStack.emplace_back(*it, list);
  102     }
  103 
  104     if (list && !list->getFiles().empty())
  105         file0 = list->getFiles()[0];
  106 
  107     setmsg(msg);
  108 }
  109 
  110 
  111 ErrorLogger::ErrorMessage::ErrorMessage(const std::list<const Token*>& callstack, const TokenList* list, Severity::SeverityType severity, const std::string& id, const std::string& msg, const CWE &cwe, bool inconclusive)
  112     : id(id), severity(severity), cwe(cwe.id), inconclusive(inconclusive)
  113 {
  114     // Format callstack
  115     for (std::list<const Token *>::const_iterator it = callstack.begin(); it != callstack.end(); ++it) {
  116         // --errorlist can provide null values here
  117         if (!(*it))
  118             continue;
  119 
  120         callStack.emplace_back(*it, list);
  121     }
  122 
  123     if (list && !list->getFiles().empty())
  124         file0 = list->getFiles()[0];
  125 
  126     setmsg(msg);
  127 }
  128 
  129 ErrorLogger::ErrorMessage::ErrorMessage(const ErrorPath &errorPath, const TokenList *tokenList, Severity::SeverityType severity, const char id[], const std::string &msg, const CWE &cwe, bool inconclusive)
  130     : id(id), severity(severity), cwe(cwe.id), inconclusive(inconclusive)
  131 {
  132     // Format callstack
  133     for (ErrorPath::const_iterator it = errorPath.begin(); it != errorPath.end(); ++it) {
  134         const Token *tok = it->first;
  135         const std::string &info = it->second;
  136 
  137         // --errorlist can provide null values here
  138         if (tok)
  139             callStack.emplace_back(tok, info, tokenList);
  140     }
  141 
  142     if (tokenList && !tokenList->getFiles().empty())
  143         file0 = tokenList->getFiles()[0];
  144 
  145     setmsg(msg);
  146 }
  147 
  148 ErrorLogger::ErrorMessage::ErrorMessage(const tinyxml2::XMLElement * const errmsg)
  149     : severity(Severity::none),
  150       cwe(0U),
  151       inconclusive(false)
  152 {
  153     const char * const unknown = "<UNKNOWN>";
  154 
  155     const char *attr = errmsg->Attribute("id");
  156     id = attr ? attr : unknown;
  157 
  158     attr = errmsg->Attribute("severity");
  159     severity = attr ? Severity::fromString(attr) : Severity::none;
  160 
  161     attr = errmsg->Attribute("cwe");
  162     std::istringstream(attr ? attr : "0") >> cwe.id;
  163 
  164     attr = errmsg->Attribute("inconclusive");
  165     inconclusive = attr && (std::strcmp(attr, "true") == 0);
  166 
  167     attr = errmsg->Attribute("msg");
  168     mShortMessage = attr ? attr : "";
  169 
  170     attr = errmsg->Attribute("verbose");
  171     mVerboseMessage = attr ? attr : "";
  172 
  173     for (const tinyxml2::XMLElement *e = errmsg->FirstChildElement(); e; e = e->NextSiblingElement()) {
  174         if (std::strcmp(e->Name(),"location")==0) {
  175             const char *strfile = e->Attribute("file");
  176             const char *strinfo = e->Attribute("info");
  177             const char *strline = e->Attribute("line");
  178             const char *strcolumn = e->Attribute("column");
  179 
  180             const char *file = strfile ? strfile : unknown;
  181             const char *info = strinfo ? strinfo : "";
  182             const int line = strline ? std::atoi(strline) : 0;
  183             const int column = strcolumn ? std::atoi(strcolumn) : 0;
  184             callStack.emplace_back(file, info, line, column);
  185         }
  186     }
  187 }
  188 
  189 void ErrorLogger::ErrorMessage::setmsg(const std::string &msg)
  190 {
  191     // If a message ends to a '\n' and contains only a one '\n'
  192     // it will cause the mVerboseMessage to be empty which will show
  193     // as an empty message to the user if --verbose is used.
  194     // Even this doesn't cause problems with messages that have multiple
  195     // lines, none of the error messages should end into it.
  196     assert(!endsWith(msg,'\n'));
  197 
  198     // The summary and verbose message are separated by a newline
  199     // If there is no newline then both the summary and verbose messages
  200     // are the given message
  201     const std::string::size_type pos = msg.find('\n');
  202     const std::string symbolName = mSymbolNames.empty() ? std::string() : mSymbolNames.substr(0, mSymbolNames.find('\n'));
  203     if (pos == std::string::npos) {
  204         mShortMessage = replaceStr(msg, "$symbol", symbolName);
  205         mVerboseMessage = replaceStr(msg, "$symbol", symbolName);
  206     } else if (msg.compare(0,8,"$symbol:") == 0) {
  207         mSymbolNames += msg.substr(8, pos-7);
  208         setmsg(msg.substr(pos + 1));
  209     } else {
  210         mShortMessage = replaceStr(msg.substr(0, pos), "$symbol", symbolName);
  211         mVerboseMessage = replaceStr(msg.substr(pos + 1), "$symbol", symbolName);
  212     }
  213 }
  214 
  215 Suppressions::ErrorMessage ErrorLogger::ErrorMessage::toSuppressionsErrorMessage() const
  216 {
  217     Suppressions::ErrorMessage ret;
  218     ret.errorId = id;
  219     if (!callStack.empty()) {
  220         ret.setFileName(callStack.back().getfile(false));
  221         ret.lineNumber = callStack.back().line;
  222     }
  223     ret.inconclusive = inconclusive;
  224     ret.symbolNames = mSymbolNames;
  225     return ret;
  226 }
  227 
  228 
  229 std::string ErrorLogger::ErrorMessage::serialize() const
  230 {
  231     // Serialize this message into a simple string
  232     std::ostringstream oss;
  233     oss << id.length() << " " << id;
  234     oss << Severity::toString(severity).length() << " " << Severity::toString(severity);
  235     oss << MathLib::toString(cwe.id).length() << " " << MathLib::toString(cwe.id);
  236     if (inconclusive) {
  237         const std::string inconclusive("inconclusive");
  238         oss << inconclusive.length() << " " << inconclusive;
  239     }
  240 
  241     const std::string saneShortMessage = fixInvalidChars(mShortMessage);
  242     const std::string saneVerboseMessage = fixInvalidChars(mVerboseMessage);
  243 
  244     oss << saneShortMessage.length() << " " << saneShortMessage;
  245     oss << saneVerboseMessage.length() << " " << saneVerboseMessage;
  246     oss << callStack.size() << " ";
  247 
  248     for (std::list<ErrorLogger::ErrorMessage::FileLocation>::const_iterator loc = callStack.begin(); loc != callStack.end(); ++loc) {
  249         std::ostringstream smallStream;
  250         smallStream << (*loc).line << '\t' << (*loc).column << '\t' << (*loc).getfile(false) << '\t' << loc->getOrigFile(false) << '\t' << loc->getinfo();
  251         oss << smallStream.str().length() << " " << smallStream.str();
  252     }
  253 
  254     return oss.str();
  255 }
  256 
  257 bool ErrorLogger::ErrorMessage::deserialize(const std::string &data)
  258 {
  259     inconclusive = false;
  260     callStack.clear();
  261     std::istringstream iss(data);
  262     std::array<std::string, 5> results;
  263     std::size_t elem = 0;
  264     while (iss.good()) {
  265         unsigned int len = 0;
  266         if (!(iss >> len))
  267             return false;
  268 
  269         iss.get();
  270         std::string temp;
  271         for (unsigned int i = 0; i < len && iss.good(); ++i) {
  272             const char c = static_cast<char>(iss.get());
  273             temp.append(1, c);
  274         }
  275 
  276         if (temp == "inconclusive") {
  277             inconclusive = true;
  278             continue;
  279         }
  280 
  281         results[elem++] = temp;
  282         if (elem == 5)
  283             break;
  284     }
  285 
  286     if (elem != 5)
  287         throw InternalError(nullptr, "Internal Error: Deserialization of error message failed");
  288 
  289     id = results[0];
  290     severity = Severity::fromString(results[1]);
  291     std::istringstream scwe(results[2]);
  292     scwe >> cwe.id;
  293     mShortMessage = results[3];
  294     mVerboseMessage = results[4];
  295 
  296     unsigned int stackSize = 0;
  297     if (!(iss >> stackSize))
  298         return false;
  299 
  300     while (iss.good()) {
  301         unsigned int len = 0;
  302         if (!(iss >> len))
  303             return false;
  304 
  305         iss.get();
  306         std::string temp;
  307         for (unsigned int i = 0; i < len && iss.good(); ++i) {
  308             const char c = static_cast<char>(iss.get());
  309             temp.append(1, c);
  310         }
  311 
  312         std::vector<std::string> substrings;
  313         for (std::string::size_type pos = 0; pos < temp.size() && substrings.size() < 5; ++pos) {
  314             if (substrings.size() == 4) {
  315                 substrings.push_back(temp.substr(pos));
  316                 break;
  317             }
  318             const std::string::size_type start = pos;
  319             pos = temp.find("\t", pos);
  320             if (pos == std::string::npos) {
  321                 substrings.push_back(temp.substr(start));
  322                 break;
  323             }
  324             substrings.push_back(temp.substr(start, pos - start));
  325         }
  326         if (substrings.size() < 4)
  327             throw InternalError(nullptr, "Internal Error: serializing/deserializing of error message failed!");
  328 
  329         // (*loc).line << '\t' << (*loc).column << '\t' << (*loc).getfile(false) << '\t' << loc->getOrigFile(false) << '\t' << loc->getinfo();
  330 
  331         ErrorLogger::ErrorMessage::FileLocation loc(substrings[3], MathLib::toLongNumber(substrings[0]), MathLib::toLongNumber(substrings[1]));
  332         loc.setfile(substrings[2]);
  333         if (substrings.size() == 5)
  334             loc.setinfo(substrings[4]);
  335 
  336         callStack.push_back(loc);
  337 
  338         if (callStack.size() >= stackSize)
  339             break;
  340     }
  341 
  342     return true;
  343 }
  344 
  345 std::string ErrorLogger::ErrorMessage::getXMLHeader()
  346 {
  347     // xml_version 1 is the default xml format
  348 
  349     tinyxml2::XMLPrinter printer;
  350 
  351     // standard xml header
  352     printer.PushDeclaration("xml version=\"1.0\" encoding=\"UTF-8\"");
  353 
  354     // header
  355     printer.OpenElement("results", false);
  356 
  357     printer.PushAttribute("version", 2);
  358     printer.OpenElement("cppcheck", false);
  359     printer.PushAttribute("version", CppCheck::version());
  360     printer.CloseElement(false);
  361     printer.OpenElement("errors", false);
  362 
  363     return std::string(printer.CStr()) + '>';
  364 }
  365 
  366 std::string ErrorLogger::ErrorMessage::getXMLFooter()
  367 {
  368     return "    </errors>\n</results>";
  369 }
  370 
  371 // There is no utf-8 support around but the strings should at least be safe for to tinyxml2.
  372 // See #5300 "Invalid encoding in XML output" and  #6431 "Invalid XML created - Invalid encoding of string literal "
  373 std::string ErrorLogger::ErrorMessage::fixInvalidChars(const std::string& raw)
  374 {
  375     std::string result;
  376     result.reserve(raw.length());
  377     std::string::const_iterator from=raw.begin();
  378     while (from!=raw.end()) {
  379         if (std::isprint(static_cast<unsigned char>(*from))) {
  380             result.push_back(*from);
  381         } else {
  382             std::ostringstream es;
  383             // straight cast to (unsigned) doesn't work out.
  384             const unsigned uFrom = (unsigned char)*from;
  385             es << '\\' << std::setbase(8) << std::setw(3) << std::setfill('0') << uFrom;
  386             result += es.str();
  387         }
  388         ++from;
  389     }
  390     return result;
  391 }
  392 
  393 std::string ErrorLogger::ErrorMessage::toXML() const
  394 {
  395     tinyxml2::XMLPrinter printer(nullptr, false, 2);
  396     printer.OpenElement("error", false);
  397     printer.PushAttribute("id", id.c_str());
  398     printer.PushAttribute("severity", Severity::toString(severity).c_str());
  399     printer.PushAttribute("msg", fixInvalidChars(mShortMessage).c_str());
  400     printer.PushAttribute("verbose", fixInvalidChars(mVerboseMessage).c_str());
  401     if (cwe.id)
  402         printer.PushAttribute("cwe", cwe.id);
  403     if (inconclusive)
  404         printer.PushAttribute("inconclusive", "true");
  405 
  406     for (std::list<FileLocation>::const_reverse_iterator it = callStack.rbegin(); it != callStack.rend(); ++it) {
  407         printer.OpenElement("location", false);
  408         if (!file0.empty() && (*it).getfile() != file0)
  409             printer.PushAttribute("file0", Path::toNativeSeparators(file0).c_str());
  410         printer.PushAttribute("file", (*it).getfile().c_str());
  411         printer.PushAttribute("line", std::max((*it).line,0));
  412         printer.PushAttribute("column", (*it).column);
  413         if (!it->getinfo().empty())
  414             printer.PushAttribute("info", fixInvalidChars(it->getinfo()).c_str());
  415         printer.CloseElement(false);
  416     }
  417     for (std::string::size_type pos = 0; pos < mSymbolNames.size();) {
  418         const std::string::size_type pos2 = mSymbolNames.find('\n', pos);
  419         std::string symbolName;
  420         if (pos2 == std::string::npos) {
  421             symbolName = mSymbolNames.substr(pos);
  422             pos = pos2;
  423         } else {
  424             symbolName = mSymbolNames.substr(pos, pos2-pos);
  425             pos = pos2 + 1;
  426         }
  427         printer.OpenElement("symbol", false);
  428         printer.PushText(symbolName.c_str());
  429         printer.CloseElement(false);
  430     }
  431     printer.CloseElement(false);
  432     return printer.CStr();
  433 }
  434 
  435 void ErrorLogger::ErrorMessage::findAndReplace(std::string &source, const std::string &searchFor, const std::string &replaceWith)
  436 {
  437     std::string::size_type index = 0;
  438     while ((index = source.find(searchFor, index)) != std::string::npos) {
  439         source.replace(index, searchFor.length(), replaceWith);
  440         index += replaceWith.length();
  441     }
  442 }
  443 
  444 // TODO: read info from some shared resource instead?
  445 static std::string readCode(const std::string &file, int linenr, int column, const char endl[])
  446 {
  447     std::ifstream fin(file);
  448     std::string line;
  449     while (linenr > 0 && std::getline(fin,line)) {
  450         linenr--;
  451     }
  452     const std::string::size_type endPos = line.find_last_not_of("\r\n\t ");
  453     if (endPos + 1 < line.size())
  454         line.erase(endPos + 1);
  455     std::string::size_type pos = 0;
  456     while ((pos = line.find('\t', pos)) != std::string::npos)
  457         line[pos] = ' ';
  458     return line + endl + std::string((column>0 ? column-1 : column), ' ') + '^';
  459 }
  460 
  461 std::string ErrorLogger::ErrorMessage::toString(bool verbose, const std::string &templateFormat, const std::string &templateLocation) const
  462 {
  463     // Save this ErrorMessage in plain text.
  464 
  465     // No template is given
  466     if (templateFormat.empty()) {
  467         std::ostringstream text;
  468         if (!callStack.empty())
  469             text << callStackToString(callStack) << ": ";
  470         if (severity != Severity::none) {
  471             text << '(' << Severity::toString(severity);
  472             if (inconclusive)
  473                 text << ", inconclusive";
  474             text << ") ";
  475         }
  476         text << (verbose ? mVerboseMessage : mShortMessage);
  477         return text.str();
  478     }
  479 
  480     // template is given. Reformat the output according to it
  481     std::string result = templateFormat;
  482     // Support a few special characters to allow to specific formatting, see http://sourceforge.net/apps/phpbb/cppcheck/viewtopic.php?f=4&t=494&sid=21715d362c0dbafd3791da4d9522f814
  483     // Substitution should be done first so messages from cppcheck never get translated.
  484     findAndReplace(result, "\\b", "\b");
  485     findAndReplace(result, "\\n", "\n");
  486     findAndReplace(result, "\\r", "\r");
  487     findAndReplace(result, "\\t", "\t");
  488 
  489     findAndReplace(result, "{id}", id);
  490     if (result.find("{inconclusive:") != std::string::npos) {
  491         const std::string::size_type pos1 = result.find("{inconclusive:");
  492         const std::string::size_type pos2 = result.find('}', pos1+1);
  493         const std::string replaceFrom = result.substr(pos1,pos2-pos1+1);
  494         const std::string replaceWith = inconclusive ? result.substr(pos1+14, pos2-pos1-14) : std::string();
  495         findAndReplace(result, replaceFrom, replaceWith);
  496     }
  497     findAndReplace(result, "{severity}", Severity::toString(severity));
  498     findAndReplace(result, "{cwe}", MathLib::toString(cwe.id));
  499     findAndReplace(result, "{message}", verbose ? mVerboseMessage : mShortMessage);
  500     findAndReplace(result, "{callstack}", callStack.empty() ? emptyString : callStackToString(callStack));
  501     if (!callStack.empty()) {
  502         findAndReplace(result, "{file}", callStack.back().getfile());
  503         findAndReplace(result, "{line}", MathLib::toString(callStack.back().line));
  504         findAndReplace(result, "{column}", MathLib::toString(callStack.back().column));
  505         if (result.find("{code}") != std::string::npos) {
  506             const std::string::size_type pos = result.find('\r');
  507             const char *endl;
  508             if (pos == std::string::npos)
  509                 endl = "\n";
  510             else if (pos+1 < result.size() && result[pos+1] == '\n')
  511                 endl = "\r\n";
  512             else
  513                 endl = "\r";
  514             findAndReplace(result, "{code}", readCode(callStack.back().getOrigFile(), callStack.back().line, callStack.back().column, endl));
  515         }
  516     } else {
  517         findAndReplace(result, "{file}", "nofile");
  518         findAndReplace(result, "{line}", "0");
  519         findAndReplace(result, "{column}", "0");
  520         findAndReplace(result, "{code}", emptyString);
  521     }
  522 
  523     if (!templateLocation.empty() && callStack.size() >= 2U) {
  524         for (const FileLocation &fileLocation : callStack) {
  525             std::string text = templateLocation;
  526 
  527             findAndReplace(text, "\\b", "\b");
  528             findAndReplace(text, "\\n", "\n");
  529             findAndReplace(text, "\\r", "\r");
  530             findAndReplace(text, "\\t", "\t");
  531 
  532             findAndReplace(text, "{file}", fileLocation.getfile());
  533             findAndReplace(text, "{line}", MathLib::toString(fileLocation.line));
  534             findAndReplace(text, "{column}", MathLib::toString(fileLocation.column));
  535             findAndReplace(text, "{info}", fileLocation.getinfo().empty() ? mShortMessage : fileLocation.getinfo());
  536             if (text.find("{code}") != std::string::npos) {
  537                 const std::string::size_type pos = text.find('\r');
  538                 const char *endl;
  539                 if (pos == std::string::npos)
  540                     endl = "\n";
  541                 else if (pos+1 < text.size() && text[pos+1] == '\n')
  542                     endl = "\r\n";
  543                 else
  544                     endl = "\r";
  545                 findAndReplace(text, "{code}", readCode(fileLocation.getOrigFile(), fileLocation.line, fileLocation.column, endl));
  546             }
  547             result += '\n' + text;
  548         }
  549     }
  550 
  551     return result;
  552 }
  553 
  554 bool ErrorLogger::reportUnmatchedSuppressions(const std::list<Suppressions::Suppression> &unmatched)
  555 {
  556     bool err = false;
  557     // Report unmatched suppressions
  558     for (const Suppressions::Suppression &s : unmatched) {
  559         // don't report "unmatchedSuppression" as unmatched
  560         if (s.errorId == "unmatchedSuppression")
  561             continue;
  562 
  563         // check if this unmatched suppression is suppressed
  564         bool suppressed = false;
  565         for (const Suppressions::Suppression &s2 : unmatched) {
  566             if (s2.errorId == "unmatchedSuppression") {
  567                 if ((s2.fileName == "*" || s2.fileName == s.fileName) &&
  568                     (s2.lineNumber == Suppressions::Suppression::NO_LINE || s2.lineNumber == s.lineNumber)) {
  569                     suppressed = true;
  570                     break;
  571                 }
  572             }
  573         }
  574 
  575         if (suppressed)
  576             continue;
  577 
  578         std::list<ErrorLogger::ErrorMessage::FileLocation> callStack;
  579         if (!s.fileName.empty())
  580             callStack.emplace_back(s.fileName, s.lineNumber, 0);
  581         reportErr(ErrorLogger::ErrorMessage(callStack, emptyString, Severity::information, "Unmatched suppression: " + s.errorId, "unmatchedSuppression", false));
  582         err = true;
  583     }
  584     return err;
  585 }
  586 
  587 std::string ErrorLogger::callStackToString(const std::list<ErrorLogger::ErrorMessage::FileLocation> &callStack)
  588 {
  589     std::ostringstream ostr;
  590     for (std::list<ErrorLogger::ErrorMessage::FileLocation>::const_iterator tok = callStack.begin(); tok != callStack.end(); ++tok) {
  591         ostr << (tok == callStack.begin() ? "" : " -> ") << tok->stringify();
  592     }
  593     return ostr.str();
  594 }
  595 
  596 
  597 ErrorLogger::ErrorMessage::FileLocation::FileLocation(const Token* tok, const TokenList* tokenList)
  598     : fileIndex(tok->fileIndex()), line(tok->linenr()), column(tok->column()), mOrigFileName(tokenList->getOrigFile(tok)), mFileName(tokenList->file(tok))
  599 {
  600 }
  601 
  602 ErrorLogger::ErrorMessage::FileLocation::FileLocation(const Token* tok, const std::string &info, const TokenList* tokenList)
  603     : fileIndex(tok->fileIndex()), line(tok->linenr()), column(tok->column()), mOrigFileName(tokenList->getOrigFile(tok)), mFileName(tokenList->file(tok)), mInfo(info)
  604 {
  605 }
  606 
  607 std::string ErrorLogger::ErrorMessage::FileLocation::getfile(bool convert) const
  608 {
  609     if (convert)
  610         return Path::toNativeSeparators(mFileName);
  611     return mFileName;
  612 }
  613 
  614 std::string ErrorLogger::ErrorMessage::FileLocation::getOrigFile(bool convert) const
  615 {
  616     if (convert)
  617         return Path::toNativeSeparators(mOrigFileName);
  618     return mOrigFileName;
  619 }
  620 
  621 void ErrorLogger::ErrorMessage::FileLocation::setfile(const std::string &file)
  622 {
  623     mFileName = file;
  624     mFileName = Path::fromNativeSeparators(mFileName);
  625     mFileName = Path::simplifyPath(mFileName);
  626 }
  627 
  628 std::string ErrorLogger::ErrorMessage::FileLocation::stringify() const
  629 {
  630     std::ostringstream oss;
  631     oss << '[' << Path::toNativeSeparators(mFileName);
  632     if (line != Suppressions::Suppression::NO_LINE)
  633         oss << ':' << line;
  634     oss << ']';
  635     return oss.str();
  636 }
  637 
  638 std::string ErrorLogger::toxml(const std::string &str)
  639 {
  640     std::ostringstream xml;
  641     for (std::size_t i = 0U; i < str.length(); i++) {
  642         const unsigned char c = str[i];
  643         switch (c) {
  644         case '<':
  645             xml << "&lt;";
  646             break;
  647         case '>':
  648             xml << "&gt;";
  649             break;
  650         case '&':
  651             xml << "&amp;";
  652             break;
  653         case '\"':
  654             xml << "&quot;";
  655             break;
  656         case '\0':
  657             xml << "\\0";
  658             break;
  659         default:
  660             if (c >= ' ' && c <= 0x7f)
  661                 xml << c;
  662             else
  663                 xml << 'x';
  664             break;
  665         }
  666     }
  667     return xml.str();
  668 }
  669 
  670 std::string ErrorLogger::plistHeader(const std::string &version, const std::vector<std::string> &files)
  671 {
  672     std::ostringstream ostr;
  673     ostr << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
  674          << "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\r\n"
  675          << "<plist version=\"1.0\">\r\n"
  676          << "<dict>\r\n"
  677          << " <key>clang_version</key>\r\n"
  678          << "<string>cppcheck version " << version << "</string>\r\n"
  679          << " <key>files</key>\r\n"
  680          << " <array>\r\n";
  681     for (unsigned int i = 0; i < files.size(); ++i)
  682         ostr << "  <string>" << ErrorLogger::toxml(files[i]) << "</string>\r\n";
  683     ostr       << " </array>\r\n"
  684                << " <key>diagnostics</key>\r\n"
  685                << " <array>\r\n";
  686     return ostr.str();
  687 }
  688 
  689 static std::string plistLoc(const char indent[], const ErrorLogger::ErrorMessage::FileLocation &loc)
  690 {
  691     std::ostringstream ostr;
  692     ostr << indent << "<dict>\r\n"
  693          << indent << ' ' << "<key>line</key><integer>" << loc.line << "</integer>\r\n"
  694          << indent << ' ' << "<key>col</key><integer>" << loc.column << "</integer>\r\n"
  695          << indent << ' ' << "<key>file</key><integer>" << loc.fileIndex << "</integer>\r\n"
  696          << indent << "</dict>\r\n";
  697     return ostr.str();
  698 }
  699 
  700 std::string ErrorLogger::plistData(const ErrorLogger::ErrorMessage &msg)
  701 {
  702     std::ostringstream plist;
  703     plist << "  <dict>\r\n"
  704           << "   <key>path</key>\r\n"
  705           << "   <array>\r\n";
  706 
  707     std::list<ErrorLogger::ErrorMessage::FileLocation>::const_iterator prev = msg.callStack.begin();
  708 
  709     for (std::list<ErrorLogger::ErrorMessage::FileLocation>::const_iterator it = msg.callStack.begin(); it != msg.callStack.end(); ++it) {
  710         if (prev != it) {
  711             plist << "    <dict>\r\n"
  712                   << "     <key>kind</key><string>control</string>\r\n"
  713                   << "     <key>edges</key>\r\n"
  714                   << "      <array>\r\n"
  715                   << "       <dict>\r\n"
  716                   << "        <key>start</key>\r\n"
  717                   << "         <array>\r\n"
  718                   << plistLoc("          ", *prev)
  719                   << plistLoc("          ", *prev)
  720                   << "         </array>\r\n"
  721                   << "        <key>end</key>\r\n"
  722                   << "         <array>\r\n"
  723                   << plistLoc("          ", *it)
  724                   << plistLoc("          ", *it)
  725                   << "         </array>\r\n"
  726                   << "       </dict>\r\n"
  727                   << "      </array>\r\n"
  728                   << "    </dict>\r\n";
  729             prev = it;
  730         }
  731 
  732         std::list<ErrorLogger::ErrorMessage::FileLocation>::const_iterator next = it;
  733         ++next;
  734         const std::string message = (it->getinfo().empty() && next == msg.callStack.end() ? msg.shortMessage() : it->getinfo());
  735 
  736         plist << "    <dict>\r\n"
  737               << "     <key>kind</key><string>event</string>\r\n"
  738               << "     <key>location</key>\r\n"
  739               << plistLoc("     ", *it)
  740               << "     <key>ranges</key>\r\n"
  741               << "     <array>\r\n"
  742               << "       <array>\r\n"
  743               << plistLoc("        ", *it)
  744               << plistLoc("        ", *it)
  745               << "       </array>\r\n"
  746               << "     </array>\r\n"
  747               << "     <key>depth</key><integer>0</integer>\r\n"
  748               << "     <key>extended_message</key>\r\n"
  749               << "     <string>" << ErrorLogger::toxml(message) << "</string>\r\n"
  750               << "     <key>message</key>\r\n"
  751               << "     <string>" << ErrorLogger::toxml(message) << "</string>\r\n"
  752               << "    </dict>\r\n";
  753     }
  754 
  755     plist << "   </array>\r\n"
  756           << "   <key>description</key><string>" << ErrorLogger::toxml(msg.shortMessage()) << "</string>\r\n"
  757           << "   <key>category</key><string>" << Severity::toString(msg.severity) << "</string>\r\n"
  758           << "   <key>type</key><string>" << ErrorLogger::toxml(msg.shortMessage()) << "</string>\r\n"
  759           << "   <key>check_name</key><string>" << msg.id << "</string>\r\n"
  760           << "   <!-- This hash is experimental and going to change! -->\r\n"
  761           << "   <key>issue_hash_content_of_line_in_context</key><string>" << 0 << "</string>\r\n"
  762           << "  <key>issue_context_kind</key><string></string>\r\n"
  763           << "  <key>issue_context</key><string></string>\r\n"
  764           << "  <key>issue_hash_function_offset</key><string></string>\r\n"
  765           << "  <key>location</key>\r\n"
  766           << plistLoc("  ", msg.callStack.back())
  767           << "  </dict>\r\n";
  768     return plist.str();
  769 }
  770 
  771 
  772 std::string replaceStr(std::string s, const std::string &from, const std::string &to)
  773 {
  774     std::string::size_type pos1 = 0;
  775     while (pos1 < s.size()) {
  776         pos1 = s.find(from, pos1);
  777         if (pos1 == std::string::npos)
  778             return s;
  779         if (pos1 > 0 && (s[pos1-1] == '_' || std::isalnum(s[pos1-1]))) {
  780             pos1++;
  781             continue;
  782         }
  783         const std::string::size_type pos2 = pos1 + from.size();
  784         if (pos2 >= s.size())
  785             return s.substr(0,pos1) + to;
  786         if (s[pos2] == '_' || std::isalnum(s[pos2])) {
  787             pos1++;
  788             continue;
  789         }
  790         s = s.substr(0,pos1) + to + s.substr(pos2);
  791         pos1 += to.size();
  792     }
  793     return s;
  794 }