LCOV - code coverage report
Current view: top level - gcore - gdaljp2metadatagenerator.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 198 213 93.0 %
Date: 2024-11-21 22:18:42 Functions: 15 15 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  GDALJP2Metadata: metadata generator
       5             :  * Author:   Even Rouault <even dot rouault at spatialys dot com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2015, European Union Satellite Centre
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_port.h"
      14             : #include "gdaljp2metadatagenerator.h"
      15             : 
      16             : #include <cstddef>
      17             : 
      18             : #ifdef HAVE_LIBXML2
      19             : 
      20             : #if defined(__GNUC__)
      21             : #pragma GCC diagnostic push
      22             : #pragma GCC diagnostic ignored "-Wold-style-cast"
      23             : #endif
      24             : 
      25             : #ifdef __clang__
      26             : #pragma clang diagnostic push
      27             : #pragma clang diagnostic ignored "-Wunknown-pragmas"
      28             : #pragma clang diagnostic ignored "-Wdocumentation"
      29             : #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
      30             : #endif
      31             : 
      32             : #include <libxml/parser.h>
      33             : #include <libxml/tree.h>
      34             : #include <libxml/xpath.h>
      35             : #include <libxml/xpathInternals.h>
      36             : 
      37             : #ifdef __clang__
      38             : #pragma clang diagnostic pop
      39             : #endif
      40             : 
      41             : #if defined(__GNUC__)
      42             : #pragma GCC diagnostic pop
      43             : #endif
      44             : 
      45             : // For CHECK_ARITY and clang 5
      46             : // CHECK_ARITY: check the number of args passed to an XPath function matches.
      47             : #undef NULL
      48             : #define NULL nullptr
      49             : 
      50             : // Simplified version of the macro proposed by libxml2
      51             : // The reason is when running against filegdbAPI which includes it own copy
      52             : // of libxml2, and the check 'ctxt->valueNr < ctxt->valueFrame + (x)'
      53             : // done by libxml2 CHECK_ARITY() thus points to garbage
      54             : #undef CHECK_ARITY
      55             : #define CHECK_ARITY(x)                                                         \
      56             :     if (ctxt == NULL)                                                          \
      57             :         return;                                                                \
      58             :     if (nargs != (x))                                                          \
      59             :         XP_ERROR(XPATH_INVALID_ARITY);
      60             : 
      61             : /************************************************************************/
      62             : /*                            GDALGMLJP2Expr                            */
      63             : /************************************************************************/
      64             : 
      65             : enum class GDALGMLJP2ExprType
      66             : {
      67             :     GDALGMLJP2Expr_Unknown,
      68             :     GDALGMLJP2Expr_XPATH,
      69             :     GDALGMLJP2Expr_STRING_LITERAL,
      70             : };
      71             : 
      72             : class GDALGMLJP2Expr
      73             : {
      74             :     static void SkipSpaces(const char *&pszStr);
      75             : 
      76             :   public:
      77             :     GDALGMLJP2ExprType eType = GDALGMLJP2ExprType::GDALGMLJP2Expr_Unknown;
      78             :     CPLString osValue{};
      79             : 
      80           8 :     GDALGMLJP2Expr() = default;
      81             : 
      82           2 :     explicit GDALGMLJP2Expr(const char *pszVal)
      83           2 :         : eType(GDALGMLJP2ExprType::GDALGMLJP2Expr_STRING_LITERAL),
      84           2 :           osValue(pszVal)
      85             :     {
      86           2 :     }
      87             : 
      88           5 :     explicit GDALGMLJP2Expr(const CPLString &osVal)
      89           5 :         : eType(GDALGMLJP2ExprType::GDALGMLJP2Expr_STRING_LITERAL),
      90           5 :           osValue(osVal)
      91             :     {
      92           5 :     }
      93             : 
      94          15 :     ~GDALGMLJP2Expr() = default;
      95             : 
      96             :     GDALGMLJP2Expr Evaluate(xmlXPathContextPtr pXPathCtx, xmlDocPtr pDoc);
      97             : 
      98             :     static GDALGMLJP2Expr *Build(const char *pszOriStr, const char *&pszStr);
      99             :     static void
     100             :     ReportError(const char *pszOriStr, const char *pszStr,
     101             :                 const char *pszIntroMessage = "Parsing error at:\n");
     102             : };
     103             : 
     104             : /************************************************************************/
     105             : /*                         ReportError()                                */
     106             : /************************************************************************/
     107             : 
     108           5 : void GDALGMLJP2Expr::ReportError(const char *pszOriStr, const char *pszStr,
     109             :                                  const char *pszIntroMessage)
     110             : {
     111           5 :     size_t nDist = static_cast<size_t>(pszStr - pszOriStr);
     112           5 :     if (nDist > 40)
     113           0 :         nDist = 40;
     114          10 :     CPLString osErrMsg(pszIntroMessage);
     115          15 :     CPLString osInvalidExpr = CPLString(pszStr - nDist).substr(0, nDist + 20);
     116          60 :     for (int i = static_cast<int>(nDist) - 1; i >= 0; --i)
     117             :     {
     118          55 :         if (osInvalidExpr[i] == '\n')
     119             :         {
     120           0 :             osInvalidExpr = osInvalidExpr.substr(i + 1);
     121           0 :             nDist -= i + 1;
     122           0 :             break;
     123             :         }
     124             :     }
     125          79 :     for (size_t i = nDist; i < osInvalidExpr.size(); ++i)
     126             :     {
     127          74 :         if (osInvalidExpr[i] == '\n')
     128             :         {
     129           0 :             osInvalidExpr.resize(i);
     130           0 :             break;
     131             :         }
     132             :     }
     133           5 :     osErrMsg += osInvalidExpr;
     134           5 :     osErrMsg += "\n";
     135          60 :     for (size_t i = 0; i < nDist; ++i)
     136          55 :         osErrMsg += " ";
     137           5 :     osErrMsg += "^";
     138           5 :     CPLError(CE_Failure, CPLE_AppDefined, "%s", osErrMsg.c_str());
     139           5 : }
     140             : 
     141             : /************************************************************************/
     142             : /*                        SkipSpaces()                                  */
     143             : /************************************************************************/
     144             : 
     145          47 : void GDALGMLJP2Expr::SkipSpaces(const char *&pszStr)
     146             : {
     147          47 :     while (*pszStr == ' ' || *pszStr == '\t' || *pszStr == '\r' ||
     148          39 :            *pszStr == '\n')
     149           8 :         ++pszStr;
     150          39 : }
     151             : 
     152             : /************************************************************************/
     153             : /*                             Build()                                  */
     154             : /************************************************************************/
     155             : 
     156          24 : GDALGMLJP2Expr *GDALGMLJP2Expr::Build(const char *pszOriStr,
     157             :                                       const char *&pszStr)
     158             : {
     159          24 :     if (STARTS_WITH_CI(pszStr, "{{{"))
     160             :     {
     161          12 :         pszStr += strlen("{{{");
     162          12 :         SkipSpaces(pszStr);
     163          12 :         GDALGMLJP2Expr *poExpr = Build(pszOriStr, pszStr);
     164          12 :         if (poExpr == nullptr)
     165           4 :             return nullptr;
     166           8 :         SkipSpaces(pszStr);
     167           8 :         if (!STARTS_WITH_CI(pszStr, "}}}"))
     168             :         {
     169           1 :             ReportError(pszOriStr, pszStr);
     170           1 :             delete poExpr;
     171           1 :             return nullptr;
     172             :         }
     173           7 :         pszStr += strlen("}}}");
     174           7 :         return poExpr;
     175             :     }
     176          12 :     else if (STARTS_WITH_CI(pszStr, "XPATH"))
     177             :     {
     178          10 :         pszStr += strlen("XPATH");
     179          10 :         SkipSpaces(pszStr);
     180          10 :         if (*pszStr != '(')
     181             :         {
     182           1 :             ReportError(pszOriStr, pszStr);
     183           1 :             return nullptr;
     184             :         }
     185           9 :         ++pszStr;
     186           9 :         SkipSpaces(pszStr);
     187          18 :         CPLString l_osValue;
     188           9 :         int nParenthesisIndent = 0;
     189           9 :         char chLiteralQuote = '\0';
     190         138 :         while (*pszStr)
     191             :         {
     192         137 :             if (chLiteralQuote != '\0')
     193             :             {
     194          38 :                 if (*pszStr == chLiteralQuote)
     195           5 :                     chLiteralQuote = '\0';
     196          38 :                 l_osValue += *pszStr;
     197          38 :                 ++pszStr;
     198             :             }
     199          99 :             else if (*pszStr == '\'' || *pszStr == '"')
     200             :             {
     201           5 :                 chLiteralQuote = *pszStr;
     202           5 :                 l_osValue += *pszStr;
     203           5 :                 ++pszStr;
     204             :             }
     205          94 :             else if (*pszStr == '(')
     206             :             {
     207           7 :                 ++nParenthesisIndent;
     208           7 :                 l_osValue += *pszStr;
     209           7 :                 ++pszStr;
     210             :             }
     211          87 :             else if (*pszStr == ')')
     212             :             {
     213          15 :                 nParenthesisIndent--;
     214          15 :                 if (nParenthesisIndent < 0)
     215             :                 {
     216           8 :                     pszStr++;
     217           8 :                     GDALGMLJP2Expr *poExpr = new GDALGMLJP2Expr();
     218           8 :                     poExpr->eType = GDALGMLJP2ExprType::GDALGMLJP2Expr_XPATH;
     219           8 :                     poExpr->osValue = l_osValue;
     220             : #if DEBUG_VERBOSE
     221             :                     CPLDebug("GMLJP2", "XPath expression '%s'",
     222             :                              l_osValue.c_str());
     223             : #endif
     224           8 :                     return poExpr;
     225             :                 }
     226           7 :                 l_osValue += *pszStr;
     227           7 :                 ++pszStr;
     228             :             }
     229             :             else
     230             :             {
     231          72 :                 l_osValue += *pszStr;
     232          72 :                 pszStr++;
     233             :             }
     234             :         }
     235           1 :         ReportError(pszOriStr, pszStr);
     236           1 :         return nullptr;
     237             :     }
     238             :     else
     239             :     {
     240           2 :         ReportError(pszOriStr, pszStr);
     241           2 :         return nullptr;
     242             :     }
     243             : }
     244             : 
     245             : /************************************************************************/
     246             : /*                       GDALGMLJP2HexFormatter()                       */
     247             : /************************************************************************/
     248             : 
     249          16 : static const char *GDALGMLJP2HexFormatter(GByte nVal)
     250             : {
     251          16 :     return CPLSPrintf("%02X", nVal);
     252             : }
     253             : 
     254             : /************************************************************************/
     255             : /*                            Evaluate()                                */
     256             : /************************************************************************/
     257             : 
     258             : static CPLString GDALGMLJP2EvalExpr(const CPLString &osTemplate,
     259             :                                     xmlXPathContextPtr pXPathCtx,
     260             :                                     xmlDocPtr pDoc);
     261             : 
     262           7 : GDALGMLJP2Expr GDALGMLJP2Expr::Evaluate(xmlXPathContextPtr pXPathCtx,
     263             :                                         xmlDocPtr pDoc)
     264             : {
     265           7 :     switch (eType)
     266             :     {
     267           7 :         case GDALGMLJP2ExprType::GDALGMLJP2Expr_XPATH:
     268             :         {
     269           7 :             xmlXPathObjectPtr pXPathObj = xmlXPathEvalExpression(
     270           7 :                 reinterpret_cast<const xmlChar *>(osValue.c_str()), pXPathCtx);
     271           7 :             if (pXPathObj == nullptr)
     272           2 :                 return GDALGMLJP2Expr("");
     273             : 
     274             :             // Add result of the evaluation.
     275          10 :             CPLString osXMLRes;
     276           5 :             if (pXPathObj->type == XPATH_STRING)
     277           2 :                 osXMLRes = reinterpret_cast<const char *>(pXPathObj->stringval);
     278           3 :             else if (pXPathObj->type == XPATH_BOOLEAN)
     279           1 :                 osXMLRes = pXPathObj->boolval ? "true" : "false";
     280           2 :             else if (pXPathObj->type == XPATH_NUMBER)
     281           1 :                 osXMLRes = CPLSPrintf("%.16g", pXPathObj->floatval);
     282           1 :             else if (pXPathObj->type == XPATH_NODESET)
     283             :             {
     284           1 :                 xmlNodeSetPtr pNodes = pXPathObj->nodesetval;
     285           1 :                 int nNodes = (pNodes) ? pNodes->nodeNr : 0;
     286           2 :                 for (int i = 0; i < nNodes; i++)
     287             :                 {
     288           1 :                     xmlNodePtr pCur = pNodes->nodeTab[i];
     289             : 
     290           1 :                     xmlBufferPtr pBuf = xmlBufferCreate();
     291           1 :                     xmlNodeDump(pBuf, pDoc, pCur, 2, 1);
     292             :                     osXMLRes +=
     293           1 :                         reinterpret_cast<const char *>(xmlBufferContent(pBuf));
     294           1 :                     xmlBufferFree(pBuf);
     295             :                 }
     296             :             }
     297             : 
     298           5 :             xmlXPathFreeObject(pXPathObj);
     299           5 :             return GDALGMLJP2Expr(osXMLRes);
     300             :         }
     301           0 :         default:
     302           0 :             CPLAssert(false);
     303             :             return GDALGMLJP2Expr("");
     304             :     }
     305             : }
     306             : 
     307             : /************************************************************************/
     308             : /*                        GDALGMLJP2EvalExpr()                          */
     309             : /************************************************************************/
     310             : 
     311           8 : static CPLString GDALGMLJP2EvalExpr(const CPLString &osTemplate,
     312             :                                     xmlXPathContextPtr pXPathCtx,
     313             :                                     xmlDocPtr pDoc)
     314             : {
     315           8 :     CPLString osXMLRes;
     316           8 :     size_t nPos = 0;
     317             :     while (true)
     318             :     {
     319             :         // Get next expression.
     320          15 :         size_t nStartPos = osTemplate.find("{{{", nPos);
     321          15 :         if (nStartPos == std::string::npos)
     322             :         {
     323             :             // Add terminating portion of the template.
     324           3 :             osXMLRes += osTemplate.substr(nPos);
     325           3 :             break;
     326             :         }
     327             : 
     328             :         // Add portion of template before the expression.
     329          12 :         osXMLRes += osTemplate.substr(nPos, nStartPos - nPos);
     330             : 
     331          12 :         const char *pszExpr = osTemplate.c_str() + nStartPos;
     332          12 :         GDALGMLJP2Expr *poExpr = GDALGMLJP2Expr::Build(pszExpr, pszExpr);
     333          12 :         if (poExpr == nullptr)
     334           5 :             break;
     335           7 :         nPos = static_cast<size_t>(pszExpr - osTemplate.c_str());
     336           7 :         osXMLRes += poExpr->Evaluate(pXPathCtx, pDoc).osValue;
     337           7 :         delete poExpr;
     338           7 :     }
     339           8 :     return osXMLRes;
     340             : }
     341             : 
     342             : /************************************************************************/
     343             : /*                      GDALGMLJP2XPathErrorHandler()                   */
     344             : /************************************************************************/
     345             : 
     346           2 : static void GDALGMLJP2XPathErrorHandler(void * /* userData */,
     347             : #if LIBXML_VERSION >= 21200
     348             :                                         const xmlError *error
     349             : #else
     350             :                                         xmlErrorPtr error
     351             : #endif
     352             : )
     353             : {
     354           2 :     if (error->domain == XML_FROM_XPATH && error->str1 != nullptr &&
     355           2 :         error->int1 < static_cast<int>(strlen(error->str1)))
     356             :     {
     357           0 :         GDALGMLJP2Expr::ReportError(error->str1, error->str1 + error->int1,
     358             :                                     "XPath error:\n");
     359             :     }
     360             :     else
     361             :     {
     362           2 :         CPLError(CE_Failure, CPLE_AppDefined, "An error occurred in libxml2");
     363             :     }
     364           2 : }
     365             : 
     366             : /************************************************************************/
     367             : /*                    GDALGMLJP2RegisterNamespaces()                    */
     368             : /************************************************************************/
     369             : 
     370          28 : static void GDALGMLJP2RegisterNamespaces(xmlXPathContextPtr pXPathCtx,
     371             :                                          xmlNode *pNode)
     372             : {
     373          28 :     for (; pNode; pNode = pNode->next)
     374             :     {
     375          10 :         if (pNode->type == XML_ELEMENT_NODE)
     376             :         {
     377           9 :             if (pNode->ns != nullptr && pNode->ns->prefix != nullptr)
     378             :             {
     379             :                 // printf("%s %s\n",pNode->ns->prefix, pNode->ns->href);
     380           0 :                 if (xmlXPathRegisterNs(pXPathCtx, pNode->ns->prefix,
     381           0 :                                        pNode->ns->href) != 0)
     382             :                 {
     383           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
     384             :                              "Registration of namespace %s failed",
     385           0 :                              reinterpret_cast<const char *>(pNode->ns->prefix));
     386             :                 }
     387             :             }
     388             :         }
     389             : 
     390          10 :         GDALGMLJP2RegisterNamespaces(pXPathCtx, pNode->children);
     391             :     }
     392          18 : }
     393             : 
     394             : /************************************************************************/
     395             : /*                         GDALGMLJP2XPathIf()                          */
     396             : /************************************************************************/
     397             : 
     398           2 : static void GDALGMLJP2XPathIf(xmlXPathParserContextPtr ctxt, int nargs)
     399             : {
     400             :     xmlXPathObjectPtr cond_val, then_val, else_val;
     401             : 
     402           2 :     CHECK_ARITY(3);
     403           2 :     else_val = valuePop(ctxt);
     404           2 :     then_val = valuePop(ctxt);
     405           2 :     CAST_TO_BOOLEAN
     406           2 :     cond_val = valuePop(ctxt);
     407             : 
     408           2 :     if (cond_val->boolval)
     409             :     {
     410           1 :         xmlXPathFreeObject(else_val);
     411           1 :         valuePush(ctxt, then_val);
     412             :     }
     413             :     else
     414             :     {
     415           1 :         xmlXPathFreeObject(then_val);
     416           1 :         valuePush(ctxt, else_val);
     417             :     }
     418           2 :     xmlXPathFreeObject(cond_val);
     419             : }
     420             : 
     421             : /************************************************************************/
     422             : /*                        GDALGMLJP2XPathUUID()                         */
     423             : /************************************************************************/
     424             : 
     425           1 : static void GDALGMLJP2XPathUUID(xmlXPathParserContextPtr ctxt, int nargs)
     426             : {
     427           1 :     CHECK_ARITY(0);
     428             : 
     429           2 :     CPLString osRet;
     430             :     static int nCounter = 0;
     431             :     // coverity[store_truncates_time_t]
     432           1 :     srand(static_cast<unsigned int>(time(nullptr)) + nCounter);
     433           1 :     ++nCounter;
     434           5 :     for (int i = 0; i < 4; i++)
     435           4 :         osRet += GDALGMLJP2HexFormatter(rand() & 0xFF);
     436           1 :     osRet += "-";
     437           1 :     osRet += GDALGMLJP2HexFormatter(rand() & 0xFF);
     438           1 :     osRet += GDALGMLJP2HexFormatter(rand() & 0xFF);
     439           1 :     osRet += "-";
     440             :     // Set the version number bits (4 == random).
     441           1 :     osRet += GDALGMLJP2HexFormatter((rand() & 0x0F) | 0x40);
     442           1 :     osRet += GDALGMLJP2HexFormatter(rand() & 0xFF);
     443           1 :     osRet += "-";
     444             :     // Set the variant bits.
     445           1 :     osRet += GDALGMLJP2HexFormatter((rand() & 0x3F) | 0x80);
     446           1 :     osRet += GDALGMLJP2HexFormatter(rand() & 0xFF);
     447           1 :     osRet += "-";
     448           7 :     for (int i = 0; i < 6; ++i)
     449             :     {
     450             :         // coverity[dont_call]
     451           6 :         osRet += GDALGMLJP2HexFormatter(rand() & 0xFF);
     452             :     }
     453             : 
     454           1 :     valuePush(ctxt, xmlXPathNewString(
     455           1 :                         reinterpret_cast<const xmlChar *>(osRet.c_str())));
     456             : }
     457             : 
     458             : #endif  // LIBXML2
     459             : 
     460             : /************************************************************************/
     461             : /*                      GDALGMLJP2GenerateMetadata()                    */
     462             : /************************************************************************/
     463             : 
     464             : #ifdef HAVE_LIBXML2
     465          11 : CPLXMLNode *GDALGMLJP2GenerateMetadata(const CPLString &osTemplateFile,
     466             :                                        const CPLString &osSourceFile)
     467             : {
     468          11 :     GByte *pabyStr = nullptr;
     469          11 :     if (!VSIIngestFile(nullptr, osTemplateFile, &pabyStr, nullptr, -1))
     470           1 :         return nullptr;
     471          20 :     CPLString osTemplate(reinterpret_cast<char *>(pabyStr));
     472          10 :     CPLFree(pabyStr);
     473             : 
     474          10 :     if (!VSIIngestFile(nullptr, osSourceFile, &pabyStr, nullptr, -1))
     475           1 :         return nullptr;
     476          18 :     CPLString osSource(reinterpret_cast<char *>(pabyStr));
     477           9 :     CPLFree(pabyStr);
     478             : 
     479             :     xmlDocPtr pDoc =
     480           9 :         xmlParseDoc(reinterpret_cast<const xmlChar *>(osSource.c_str()));
     481           9 :     if (pDoc == nullptr)
     482             :     {
     483           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot parse %s",
     484             :                  osSourceFile.c_str());
     485           1 :         return nullptr;
     486             :     }
     487             : 
     488           8 :     xmlXPathContextPtr pXPathCtx = xmlXPathNewContext(pDoc);
     489           8 :     if (pXPathCtx == nullptr)
     490             :     {
     491           0 :         xmlFreeDoc(pDoc);
     492           0 :         return nullptr;
     493             :     }
     494             : 
     495           8 :     xmlXPathRegisterFunc(pXPathCtx, reinterpret_cast<const xmlChar *>("if"),
     496             :                          GDALGMLJP2XPathIf);
     497           8 :     xmlXPathRegisterFunc(pXPathCtx, reinterpret_cast<const xmlChar *>("uuid"),
     498             :                          GDALGMLJP2XPathUUID);
     499             : 
     500           8 :     pXPathCtx->error = GDALGMLJP2XPathErrorHandler;
     501             : 
     502           8 :     GDALGMLJP2RegisterNamespaces(pXPathCtx, xmlDocGetRootElement(pDoc));
     503             : 
     504          16 :     CPLString osXMLRes = GDALGMLJP2EvalExpr(osTemplate, pXPathCtx, pDoc);
     505             : 
     506           8 :     xmlXPathFreeContext(pXPathCtx);
     507           8 :     xmlFreeDoc(pDoc);
     508             : 
     509           8 :     return CPLParseXMLString(osXMLRes);
     510             : }
     511             : #else   // !HAVE_LIBXML2
     512             : CPLXMLNode *GDALGMLJP2GenerateMetadata(const CPLString & /* osTemplateFile */,
     513             :                                        const CPLString & /* osSourceFile */
     514             : )
     515             : {
     516             :     return nullptr;
     517             : }
     518             : #endif  // HAVE_LIBXML2

Generated by: LCOV version 1.14