LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/georss - ogrgeorsslayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1039 1190 87.3 %
Date: 2024-05-04 12:52:34 Functions: 31 32 96.9 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GeoRSS Translator
       4             :  * Purpose:  Implements OGRGeoRSSLayer class.
       5             :  * Author:   Even Rouault, even dot rouault at spatialys.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2008-2014, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * Permission is hereby granted, free of charge, to any person obtaining a
      11             :  * copy of this software and associated documentation files (the "Software"),
      12             :  * to deal in the Software without restriction, including without limitation
      13             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      14             :  * and/or sell copies of the Software, and to permit persons to whom the
      15             :  * Software is furnished to do so, subject to the following conditions:
      16             :  *
      17             :  * The above copyright notice and this permission notice shall be included
      18             :  * in all copies or substantial portions of the Software.
      19             :  *
      20             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      21             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      22             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      23             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      24             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      25             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      26             :  * DEALINGS IN THE SOFTWARE.
      27             :  ****************************************************************************/
      28             : 
      29             : #include "cpl_port.h"
      30             : #include "ogr_georss.h"
      31             : 
      32             : #include <cstddef>
      33             : #include <cstdio>
      34             : #include <cstdlib>
      35             : #include <cstring>
      36             : 
      37             : #include "cpl_conv.h"
      38             : #include "cpl_error.h"
      39             : #include "cpl_hash_set.h"
      40             : #include "cpl_minixml.h"
      41             : #include "cpl_string.h"
      42             : #include "cpl_vsi.h"
      43             : #ifdef HAVE_EXPAT
      44             : #include "expat.h"
      45             : #endif
      46             : #include "ogr_api.h"
      47             : #include "ogr_core.h"
      48             : #include "ogr_expat.h"
      49             : #include "ogr_feature.h"
      50             : #include "ogr_geometry.h"
      51             : #include "ogr_p.h"
      52             : #include "ogr_spatialref.h"
      53             : #include "ogrsf_frmts.h"
      54             : 
      55             : static const char *const apszAllowedATOMFieldNamesWithSubElements[] = {
      56             :     "author", "contributor", nullptr};
      57             : 
      58             : static const char *const apszAllowedRSSFieldNames[] = {
      59             :     "title",          "link",          "description",
      60             :     "author",         "category",      "category_domain",
      61             :     "comments",       "enclosure_url", "enclosure_length",
      62             :     "enclosure_type", "guid",          "guid_isPermaLink",
      63             :     "pubDate",        "source",        "source_url",
      64             :     nullptr};
      65             : 
      66             : static const char *const apszAllowedATOMFieldNames[] = {"category_term",
      67             :                                                         "category_scheme",
      68             :                                                         "category_label",
      69             :                                                         "content",
      70             :                                                         "content_type",
      71             :                                                         "content_xml_lang",
      72             :                                                         "content_xml_base",
      73             :                                                         "summary",
      74             :                                                         "summary_type",
      75             :                                                         "summary_xml_lang",
      76             :                                                         "summary_xml_base",
      77             :                                                         "author_name",
      78             :                                                         "author_uri",
      79             :                                                         "author_email",
      80             :                                                         "contributor_name",
      81             :                                                         "contributor_uri",
      82             :                                                         "contributor_email",
      83             :                                                         "link_href",
      84             :                                                         "link_rel",
      85             :                                                         "link_type",
      86             :                                                         "link_length",
      87             :                                                         "id",
      88             :                                                         "published",
      89             :                                                         "rights",
      90             :                                                         "source",
      91             :                                                         "title",
      92             :                                                         "updated",
      93             :                                                         nullptr};
      94             : 
      95             : #define IS_LAT_ELEMENT(pszName)                                                \
      96             :     (STARTS_WITH(pszName, "geo:lat") || STARTS_WITH(pszName, "icbm:lat") ||    \
      97             :      STARTS_WITH(pszName, "geourl:lat"))
      98             : 
      99             : #define IS_LON_ELEMENT(pszName)                                                \
     100             :     (STARTS_WITH(pszName, "geo:lon") || STARTS_WITH(pszName, "icbm:lon") ||    \
     101             :      STARTS_WITH(pszName, "geourl:lon"))
     102             : 
     103             : #define IS_GEO_ELEMENT(pszName)                                                \
     104             :     (strcmp(pszName, "georss:point") == 0 ||                                   \
     105             :      strcmp(pszName, "georss:line") == 0 ||                                    \
     106             :      strcmp(pszName, "georss:box") == 0 ||                                     \
     107             :      strcmp(pszName, "georss:polygon") == 0 ||                                 \
     108             :      strcmp(pszName, "georss:where") == 0 || STARTS_WITH(pszName, "gml:") ||   \
     109             :      STARTS_WITH(pszName, "geo:") || STARTS_WITH(pszName, "icbm:") ||          \
     110             :      STARTS_WITH(pszName, "geourl:"))
     111             : 
     112             : /************************************************************************/
     113             : /*                            OGRGeoRSSLayer()                          */
     114             : /************************************************************************/
     115             : 
     116          68 : OGRGeoRSSLayer::OGRGeoRSSLayer(const char *pszFilename,
     117             :                                const char *pszLayerName,
     118             :                                OGRGeoRSSDataSource *poDS_,
     119          68 :                                OGRSpatialReference *poSRSIn, bool bWriteMode_)
     120          68 :     : poFeatureDefn(new OGRFeatureDefn(pszLayerName)), poSRS(poSRSIn),
     121          68 :       poDS(poDS_), eFormat(poDS_->GetFormat()), bWriteMode(bWriteMode_),
     122             :       nTotalFeatureCount(0), eof(false), nNextFID(0), fpGeoRSS(nullptr),
     123             :       bHasReadSchema(false),
     124             : #ifdef HAVE_EXPAT
     125             :       oParser(nullptr), oSchemaParser(nullptr),
     126             : #endif
     127             :       poGlobalGeom(nullptr), bStopParsing(false), bInFeature(false),
     128             :       hasFoundLat(false), hasFoundLon(false),
     129             : #ifdef HAVE_EXPAT
     130             :       latVal(0.0), lonVal(0.0),
     131             : #endif
     132             :       pszSubElementName(nullptr), pszSubElementValue(nullptr),
     133             :       nSubElementValueLen(0),
     134             : #ifdef HAVE_EXPAT
     135             :       iCurrentField(0),
     136             : #endif
     137             :       bInSimpleGeometry(false), bInGMLGeometry(false), bInGeoLat(false),
     138             :       bInGeoLong(false),
     139             : #ifdef HAVE_EXPAT
     140             :       bFoundGeom(false), bSameSRS(false),
     141             : #endif
     142             :       eGeomType(wkbUnknown), pszGMLSRSName(nullptr), bInTagWithSubTag(false),
     143             :       pszTagWithSubTag(nullptr), currentDepth(0), featureDepth(0),
     144             :       geometryDepth(0),
     145             : #ifdef HAVE_EXPAT
     146             :       currentFieldDefn(nullptr), nWithoutEventCounter(0),
     147             :       nDataHandlerCounter(0),
     148             : #endif
     149             :       setOfFoundFields(nullptr), poFeature(nullptr), ppoFeatureTab(nullptr),
     150         136 :       nFeatureTabLength(0), nFeatureTabIndex(0)
     151             : {
     152          68 :     SetDescription(poFeatureDefn->GetName());
     153          68 :     poFeatureDefn->Reference();
     154             : 
     155          68 :     if (poSRS)
     156             :     {
     157           1 :         poSRS->Reference();
     158           1 :         poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
     159             :     }
     160             : 
     161          68 :     if (!bWriteMode)
     162             :     {
     163          14 :         fpGeoRSS = VSIFOpenL(pszFilename, "r");
     164          14 :         if (fpGeoRSS == nullptr)
     165             :         {
     166           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s",
     167             :                      pszFilename);
     168           0 :             return;
     169             :         }
     170             :     }
     171             : 
     172          68 :     OGRGeoRSSLayer::ResetReading();
     173             : }
     174             : 
     175             : /************************************************************************/
     176             : /*                            ~OGRGeoRSSLayer()                            */
     177             : /************************************************************************/
     178             : 
     179         136 : OGRGeoRSSLayer::~OGRGeoRSSLayer()
     180             : 
     181             : {
     182             : #ifdef HAVE_EXPAT
     183          68 :     if (oParser)
     184          14 :         XML_ParserFree(oParser);
     185             : #endif
     186          68 :     poFeatureDefn->Release();
     187             : 
     188          68 :     if (poSRS)
     189          10 :         poSRS->Release();
     190             : 
     191          68 :     CPLFree(pszSubElementName);
     192          68 :     CPLFree(pszSubElementValue);
     193          68 :     CPLFree(pszGMLSRSName);
     194          68 :     CPLFree(pszTagWithSubTag);
     195          68 :     if (setOfFoundFields)
     196          13 :         CPLHashSetDestroy(setOfFoundFields);
     197          68 :     if (poGlobalGeom)
     198           0 :         delete poGlobalGeom;
     199             : 
     200          68 :     for (int i = nFeatureTabIndex; i < nFeatureTabLength; i++)
     201           0 :         delete ppoFeatureTab[i];
     202          68 :     CPLFree(ppoFeatureTab);
     203             : 
     204          68 :     if (poFeature)
     205           0 :         delete poFeature;
     206             : 
     207          68 :     if (fpGeoRSS)
     208          14 :         VSIFCloseL(fpGeoRSS);
     209         136 : }
     210             : 
     211             : /************************************************************************/
     212             : /*                            GetLayerDefn()                            */
     213             : /************************************************************************/
     214             : 
     215         369 : OGRFeatureDefn *OGRGeoRSSLayer::GetLayerDefn()
     216             : {
     217         369 :     if (!bHasReadSchema)
     218          63 :         LoadSchema();
     219             : 
     220         369 :     return poFeatureDefn;
     221             : }
     222             : 
     223             : #ifdef HAVE_EXPAT
     224             : 
     225         304 : static void XMLCALL startElementCbk(void *pUserData, const char *pszName,
     226             :                                     const char **ppszAttr)
     227             : {
     228         304 :     ((OGRGeoRSSLayer *)pUserData)->startElementCbk(pszName, ppszAttr);
     229         304 : }
     230             : 
     231         304 : static void XMLCALL endElementCbk(void *pUserData, const char *pszName)
     232             : {
     233         304 :     ((OGRGeoRSSLayer *)pUserData)->endElementCbk(pszName);
     234         304 : }
     235             : 
     236         889 : static void XMLCALL dataHandlerCbk(void *pUserData, const char *data, int nLen)
     237             : {
     238         889 :     ((OGRGeoRSSLayer *)pUserData)->dataHandlerCbk(data, nLen);
     239         889 : }
     240             : 
     241             : #endif
     242             : 
     243             : /************************************************************************/
     244             : /*                            ResetReading()                            */
     245             : /************************************************************************/
     246             : 
     247          84 : void OGRGeoRSSLayer::ResetReading()
     248             : 
     249             : {
     250          84 :     if (bWriteMode)
     251          70 :         return;
     252             : 
     253          14 :     eof = false;
     254          14 :     nNextFID = 0;
     255          14 :     if (fpGeoRSS)
     256             :     {
     257          14 :         VSIFSeekL(fpGeoRSS, 0, SEEK_SET);
     258             : #ifdef HAVE_EXPAT
     259          14 :         if (oParser)
     260           0 :             XML_ParserFree(oParser);
     261             : 
     262          14 :         oParser = OGRCreateExpatXMLParser();
     263          14 :         XML_SetElementHandler(oParser, ::startElementCbk, ::endElementCbk);
     264          14 :         XML_SetCharacterDataHandler(oParser, ::dataHandlerCbk);
     265          14 :         XML_SetUserData(oParser, this);
     266             : #endif
     267             :     }
     268          14 :     bInFeature = false;
     269          14 :     hasFoundLat = false;
     270          14 :     hasFoundLon = false;
     271          14 :     bInSimpleGeometry = false;
     272          14 :     bInGMLGeometry = false;
     273          14 :     bInGeoLat = false;
     274          14 :     bInGeoLong = false;
     275          14 :     eGeomType = wkbUnknown;
     276          14 :     CPLFree(pszSubElementName);
     277          14 :     pszSubElementName = nullptr;
     278          14 :     CPLFree(pszSubElementValue);
     279          14 :     pszSubElementValue = nullptr;
     280          14 :     nSubElementValueLen = 0;
     281          14 :     CPLFree(pszGMLSRSName);
     282          14 :     pszGMLSRSName = nullptr;
     283             : 
     284          14 :     if (setOfFoundFields)
     285           0 :         CPLHashSetDestroy(setOfFoundFields);
     286          14 :     setOfFoundFields = nullptr;
     287             : 
     288          14 :     for (int i = nFeatureTabIndex; i < nFeatureTabLength; i++)
     289           0 :         delete ppoFeatureTab[i];
     290          14 :     CPLFree(ppoFeatureTab);
     291          14 :     nFeatureTabIndex = 0;
     292          14 :     nFeatureTabLength = 0;
     293          14 :     ppoFeatureTab = nullptr;
     294          14 :     if (poFeature)
     295           0 :         delete poFeature;
     296          14 :     poFeature = nullptr;
     297             : 
     298          14 :     currentDepth = 0;
     299          14 :     featureDepth = 0;
     300          14 :     geometryDepth = 0;
     301          14 :     bInTagWithSubTag = false;
     302          14 :     CPLFree(pszTagWithSubTag);
     303          14 :     pszTagWithSubTag = nullptr;
     304             : }
     305             : 
     306             : #ifdef HAVE_EXPAT
     307             : 
     308             : /************************************************************************/
     309             : /*                      AddStrToSubElementValue()                       */
     310             : /************************************************************************/
     311             : 
     312         211 : void OGRGeoRSSLayer::AddStrToSubElementValue(const char *pszStr)
     313             : {
     314         211 :     int len = static_cast<int>(strlen(pszStr));
     315             :     char *pszNewSubElementValue = static_cast<char *>(
     316         211 :         VSI_REALLOC_VERBOSE(pszSubElementValue, nSubElementValueLen + len + 1));
     317         211 :     if (pszNewSubElementValue == nullptr)
     318             :     {
     319           0 :         XML_StopParser(oParser, XML_FALSE);
     320           0 :         bStopParsing = true;
     321           0 :         return;
     322             :     }
     323         211 :     pszSubElementValue = pszNewSubElementValue;
     324             : 
     325         211 :     memcpy(pszSubElementValue + nSubElementValueLen, pszStr, len);
     326         211 :     nSubElementValueLen += len;
     327             : }
     328             : 
     329             : /************************************************************************/
     330             : /*              OGRGeoRSS_GetOGRCompatibleTagName()                     */
     331             : /************************************************************************/
     332             : 
     333             : /** Replace ':' from XML NS element name by '_' more OGR friendly */
     334         346 : static char *OGRGeoRSS_GetOGRCompatibleTagName(const char *pszName)
     335             : {
     336         346 :     char *pszModName = CPLStrdup(pszName);
     337        3046 :     for (int i = 0; pszModName[i] != 0; i++)
     338             :     {
     339        2700 :         if (pszModName[i] == ':')
     340          28 :             pszModName[i] = '_';
     341             :     }
     342         346 :     return pszModName;
     343             : }
     344             : 
     345             : /************************************************************************/
     346             : /*               OGRGeoRSSLayerATOMTagHasSubElement()                   */
     347             : /************************************************************************/
     348             : 
     349         120 : static bool OGRGeoRSSLayerATOMTagHasSubElement(const char *pszName)
     350             : {
     351         312 :     for (unsigned int i = 0;
     352         312 :          apszAllowedATOMFieldNamesWithSubElements[i] != nullptr; i++)
     353             :     {
     354         228 :         if (strcmp(pszName, apszAllowedATOMFieldNamesWithSubElements[i]) == 0)
     355          36 :             return true;
     356             :     }
     357          84 :     return false;
     358             : }
     359             : 
     360             : /************************************************************************/
     361             : /*                        startElementCbk()                            */
     362             : /************************************************************************/
     363             : 
     364         304 : void OGRGeoRSSLayer::startElementCbk(const char *pszName, const char **ppszAttr)
     365             : {
     366         304 :     bool bSerializeTag = false;
     367         304 :     const char *pszNoNSName = pszName;
     368         304 :     const char *pszColon = strchr(pszNoNSName, ':');
     369         304 :     if (pszColon)
     370          77 :         pszNoNSName = pszColon + 1;
     371             : 
     372         304 :     if (bStopParsing)
     373           0 :         return;
     374             : 
     375         304 :     if ((eFormat == GEORSS_ATOM && currentDepth == 1 &&
     376          23 :          strcmp(pszNoNSName, "entry") == 0) ||
     377         301 :         ((eFormat == GEORSS_RSS || eFormat == GEORSS_RSS_RDF) && !bInFeature &&
     378          68 :          (currentDepth == 1 || currentDepth == 2) &&
     379          58 :          strcmp(pszNoNSName, "item") == 0))
     380             :     {
     381          28 :         featureDepth = currentDepth;
     382             : 
     383          28 :         if (poFeature)
     384           0 :             delete poFeature;
     385             : 
     386          28 :         poFeature = new OGRFeature(poFeatureDefn);
     387          28 :         poFeature->SetFID(nNextFID++);
     388             : 
     389          28 :         bInFeature = true;
     390          28 :         hasFoundLat = false;
     391          28 :         hasFoundLon = false;
     392          28 :         bInSimpleGeometry = false;
     393          28 :         bInGMLGeometry = false;
     394          28 :         bInGeoLat = false;
     395          28 :         bInGeoLong = false;
     396          28 :         eGeomType = wkbUnknown;
     397          28 :         geometryDepth = 0;
     398          28 :         bInTagWithSubTag = false;
     399             : 
     400          28 :         if (setOfFoundFields)
     401          15 :             CPLHashSetDestroy(setOfFoundFields);
     402          28 :         setOfFoundFields =
     403          28 :             CPLHashSetNew(CPLHashSetHashStr, CPLHashSetEqualStr, CPLFree);
     404             :     }
     405         276 :     else if (bInFeature && bInTagWithSubTag && currentDepth == 3)
     406             :     {
     407             :         char *pszFieldName =
     408          15 :             CPLStrdup(CPLSPrintf("%s_%s", pszTagWithSubTag, pszNoNSName));
     409             : 
     410          15 :         CPLFree(pszSubElementName);
     411          15 :         pszSubElementName = nullptr;
     412          15 :         CPLFree(pszSubElementValue);
     413          15 :         pszSubElementValue = nullptr;
     414          15 :         nSubElementValueLen = 0;
     415             : 
     416          15 :         iCurrentField = poFeatureDefn->GetFieldIndex(pszFieldName);
     417          15 :         if (iCurrentField >= 0)
     418          15 :             pszSubElementName = CPLStrdup(pszFieldName);
     419             : 
     420          15 :         CPLFree(pszFieldName);
     421             :     }
     422         291 :     else if (bInFeature && eFormat == GEORSS_ATOM && currentDepth == 2 &&
     423          30 :              OGRGeoRSSLayerATOMTagHasSubElement(pszNoNSName))
     424             :     {
     425           9 :         CPLFree(pszTagWithSubTag);
     426           9 :         pszTagWithSubTag = CPLStrdup(pszNoNSName);
     427             : 
     428           9 :         int count = 1;
     429          12 :         while (CPLHashSetLookup(setOfFoundFields, pszTagWithSubTag) != nullptr)
     430             :         {
     431           3 :             count++;
     432           3 :             CPLFree(pszTagWithSubTag);
     433           3 :             pszTagWithSubTag =
     434           3 :                 CPLStrdup(CPLSPrintf("%s%d", pszNoNSName, count));
     435             :         }
     436           9 :         CPLHashSetInsert(setOfFoundFields, CPLStrdup(pszTagWithSubTag));
     437             : 
     438           9 :         bInTagWithSubTag = true;
     439             :     }
     440         252 :     else if (bInGMLGeometry)
     441             :     {
     442          17 :         bSerializeTag = true;
     443             :     }
     444         235 :     else if (bInSimpleGeometry || bInGeoLat || bInGeoLong)
     445             :     {
     446             :         /* Should not happen for a valid document. */
     447             :     }
     448         235 :     else if (IS_LAT_ELEMENT(pszName))
     449             :     {
     450           1 :         CPLFree(pszSubElementValue);
     451           1 :         pszSubElementValue = nullptr;
     452           1 :         nSubElementValueLen = 0;
     453           1 :         bInGeoLat = true;
     454             :     }
     455         234 :     else if (IS_LON_ELEMENT(pszName))
     456             :     {
     457           1 :         CPLFree(pszSubElementValue);
     458           1 :         pszSubElementValue = nullptr;
     459           1 :         nSubElementValueLen = 0;
     460           1 :         bInGeoLong = true;
     461             :     }
     462         233 :     else if (strcmp(pszName, "georss:point") == 0 ||
     463         230 :              strcmp(pszName, "georss:line") == 0 ||
     464         228 :              strcmp(pszName, "geo:line") == 0 ||
     465         228 :              strcmp(pszName, "georss:polygon") == 0 ||
     466         225 :              strcmp(pszName, "georss:box") == 0)
     467             :     {
     468          10 :         CPLFree(pszSubElementValue);
     469          10 :         pszSubElementValue = nullptr;
     470          10 :         nSubElementValueLen = 0;
     471          17 :         eGeomType = strcmp(pszName, "georss:point") == 0 ? wkbPoint
     472          12 :                     : (strcmp(pszName, "georss:line") == 0 ||
     473           5 :                        strcmp(pszName, "geo:line") == 0)
     474          12 :                         ? wkbLineString
     475           7 :                     : (strcmp(pszName, "georss:polygon") == 0 ||
     476           2 :                        strcmp(pszName, "georss:box") == 0)
     477           7 :                         ? wkbPolygon
     478             :                         : wkbUnknown;
     479          10 :         bInSimpleGeometry = true;
     480          10 :         geometryDepth = currentDepth;
     481             :     }
     482         223 :     else if (strcmp(pszName, "gml:Point") == 0 ||
     483         220 :              strcmp(pszName, "gml:LineString") == 0 ||
     484         217 :              strcmp(pszName, "gml:Polygon") == 0 ||
     485         214 :              strcmp(pszName, "gml:MultiPoint") == 0 ||
     486         214 :              strcmp(pszName, "gml:MultiLineString") == 0 ||
     487         214 :              strcmp(pszName, "gml:MultiPolygon") == 0 ||
     488         214 :              strcmp(pszName, "gml:Envelope") == 0)
     489             :     {
     490          10 :         CPLFree(pszSubElementValue);
     491          10 :         pszSubElementValue = nullptr;
     492          10 :         nSubElementValueLen = 0;
     493          10 :         AddStrToSubElementValue(CPLSPrintf("<%s>", pszName));
     494          10 :         bInGMLGeometry = true;
     495          10 :         geometryDepth = currentDepth;
     496          10 :         CPLFree(pszGMLSRSName);
     497          10 :         pszGMLSRSName = nullptr;
     498          11 :         for (int i = 0; ppszAttr[i]; i += 2)
     499             :         {
     500           1 :             if (strcmp(ppszAttr[i], "srsName") == 0)
     501             :             {
     502           1 :                 if (pszGMLSRSName == nullptr)
     503           1 :                     pszGMLSRSName = CPLStrdup(ppszAttr[i + 1]);
     504             :             }
     505          10 :         }
     506             :     }
     507         213 :     else if (bInFeature && currentDepth == featureDepth + 1)
     508             :     {
     509         137 :         CPLFree(pszSubElementName);
     510         137 :         pszSubElementName = nullptr;
     511         137 :         CPLFree(pszSubElementValue);
     512         137 :         pszSubElementValue = nullptr;
     513         137 :         nSubElementValueLen = 0;
     514         137 :         iCurrentField = -1;
     515             : 
     516         137 :         if (pszName != pszNoNSName && STARTS_WITH(pszName, "atom:"))
     517           7 :             pszName = pszNoNSName;
     518             : 
     519         137 :         pszSubElementName = CPLStrdup(pszName);
     520         137 :         int count = 1;
     521         145 :         while (CPLHashSetLookup(setOfFoundFields, pszSubElementName) != nullptr)
     522             :         {
     523           8 :             count++;
     524           8 :             if (count == 100)
     525             :             {
     526           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     527             :                          "Too many repeated fields");
     528           0 :                 CPLFree(pszSubElementName);
     529           0 :                 pszSubElementName = nullptr;
     530           0 :                 break;
     531             :             }
     532           8 :             CPLFree(pszSubElementName);
     533           8 :             pszSubElementName = CPLStrdup(CPLSPrintf("%s%d", pszName, count));
     534             :         }
     535         137 :         if (pszSubElementName)
     536             :         {
     537         137 :             CPLHashSetInsert(setOfFoundFields, CPLStrdup(pszSubElementName));
     538             : 
     539             :             char *pszCompatibleName =
     540         137 :                 OGRGeoRSS_GetOGRCompatibleTagName(pszSubElementName);
     541         137 :             iCurrentField = poFeatureDefn->GetFieldIndex(pszCompatibleName);
     542         137 :             CPLFree(pszSubElementName);
     543             : 
     544         137 :             for (int i = 0;
     545         178 :                  ppszAttr[i] != nullptr && ppszAttr[i + 1] != nullptr; i += 2)
     546             :             {
     547          41 :                 char *pszAttrCompatibleName = OGRGeoRSS_GetOGRCompatibleTagName(
     548          41 :                     CPLSPrintf("%s_%s", pszCompatibleName, ppszAttr[i]));
     549             :                 const int iAttrField =
     550          41 :                     poFeatureDefn->GetFieldIndex(pszAttrCompatibleName);
     551          41 :                 if (iAttrField >= 0)
     552             :                 {
     553          41 :                     if (poFeatureDefn->GetFieldDefn(iAttrField)->GetType() ==
     554             :                         OFTReal)
     555           0 :                         poFeature->SetField(iAttrField,
     556           0 :                                             CPLAtof(ppszAttr[i + 1]));
     557             :                     else
     558          41 :                         poFeature->SetField(iAttrField, ppszAttr[i + 1]);
     559             :                 }
     560          41 :                 CPLFree(pszAttrCompatibleName);
     561             :             }
     562             : 
     563         137 :             if (iCurrentField < 0)
     564             :             {
     565          16 :                 pszSubElementName = nullptr;
     566             :             }
     567             :             else
     568             :             {
     569         121 :                 pszSubElementName = CPLStrdup(pszCompatibleName);
     570             :             }
     571         137 :             CPLFree(pszCompatibleName);
     572         137 :         }
     573             :     }
     574          76 :     else if (bInFeature && currentDepth > featureDepth + 1 &&
     575           9 :              pszSubElementName != nullptr)
     576             :     {
     577           9 :         bSerializeTag = true;
     578             :     }
     579             : 
     580         304 :     if (bSerializeTag)
     581             :     {
     582          26 :         AddStrToSubElementValue("<");
     583          26 :         AddStrToSubElementValue(pszName);
     584          29 :         for (int i = 0; ppszAttr[i] != nullptr && ppszAttr[i + 1] != nullptr;
     585           3 :              i += 2)
     586             :         {
     587           3 :             AddStrToSubElementValue(" ");
     588           3 :             AddStrToSubElementValue(ppszAttr[i]);
     589           3 :             AddStrToSubElementValue("=\"");
     590           3 :             AddStrToSubElementValue(ppszAttr[i + 1]);
     591           3 :             AddStrToSubElementValue("\"");
     592             :         }
     593          26 :         AddStrToSubElementValue(">");
     594             :     }
     595             : 
     596         304 :     currentDepth++;
     597             : }
     598             : 
     599             : /************************************************************************/
     600             : /*            OGRGeoRSSLayerTrimLeadingAndTrailingSpaces()              */
     601             : /************************************************************************/
     602             : 
     603          10 : static void OGRGeoRSSLayerTrimLeadingAndTrailingSpaces(char *pszStr)
     604             : {
     605             :     // Trim leading spaces, tabs and newlines.
     606          10 :     int i = 0;
     607          13 :     while (pszStr[i] != '\0' &&
     608          13 :            (pszStr[i] == ' ' || pszStr[i] == '\t' || pszStr[i] == '\n'))
     609           3 :         i++;
     610          10 :     memmove(pszStr, pszStr + i, strlen(pszStr + i) + 1);
     611             : 
     612             :     // Trim trailing spaces, tabs and newlines.
     613          10 :     i = static_cast<int>(strlen(pszStr)) - 1;
     614          13 :     while (i >= 0 &&
     615          13 :            (pszStr[i] == ' ' || pszStr[i] == '\t' || pszStr[i] == '\n'))
     616             :     {
     617           3 :         pszStr[i] = '\0';
     618           3 :         i--;
     619             :     }
     620          10 : }
     621             : 
     622             : /************************************************************************/
     623             : /*                           endElementCbk()                            */
     624             : /************************************************************************/
     625             : 
     626         304 : void OGRGeoRSSLayer::endElementCbk(const char *pszName)
     627             : {
     628         304 :     OGRGeometry *poGeom = nullptr;
     629             : 
     630         304 :     if (bStopParsing)
     631           0 :         return;
     632             : 
     633         304 :     currentDepth--;
     634         304 :     const char *pszNoNSName = pszName;
     635         304 :     const char *pszColon = strchr(pszNoNSName, ':');
     636         304 :     if (pszColon)
     637          77 :         pszNoNSName = pszColon + 1;
     638             : 
     639         304 :     if (bInFeature && currentDepth == featureDepth)
     640             :     {
     641          28 :         bInFeature = false;
     642          28 :         bInTagWithSubTag = false;
     643             : 
     644          28 :         if (hasFoundLat && hasFoundLon)
     645           1 :             poFeature->SetGeometryDirectly(new OGRPoint(lonVal, latVal));
     646          36 :         else if (poFeature->GetGeometryRef() == nullptr &&
     647           9 :                  poGlobalGeom != nullptr)
     648           0 :             poFeature->SetGeometry(poGlobalGeom);
     649             : 
     650          28 :         hasFoundLat = false;
     651          28 :         hasFoundLon = false;
     652             : 
     653          28 :         if (poSRS != nullptr && poFeature->GetGeometryRef() != nullptr)
     654          19 :             poFeature->GetGeometryRef()->assignSpatialReference(poSRS);
     655             : 
     656          56 :         if ((m_poFilterGeom == nullptr ||
     657          56 :              FilterGeometry(poFeature->GetGeometryRef())) &&
     658          28 :             (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
     659             :         {
     660          56 :             ppoFeatureTab = static_cast<OGRFeature **>(CPLRealloc(
     661          28 :                 ppoFeatureTab, sizeof(OGRFeature *) * (nFeatureTabLength + 1)));
     662          28 :             ppoFeatureTab[nFeatureTabLength] = poFeature;
     663          28 :             nFeatureTabLength++;
     664             :         }
     665             :         else
     666             :         {
     667           0 :             delete poFeature;
     668             :         }
     669          28 :         poFeature = nullptr;
     670          28 :         return;
     671             :     }
     672             : 
     673         276 :     if (bInTagWithSubTag && currentDepth == 3)
     674             :     {
     675             :         char *pszFieldName =
     676          15 :             CPLStrdup(CPLSPrintf("%s_%s", pszTagWithSubTag, pszNoNSName));
     677             : 
     678          15 :         if (iCurrentField != -1 && pszSubElementName &&
     679          15 :             strcmp(pszFieldName, pszSubElementName) == 0 && poFeature &&
     680          15 :             pszSubElementValue && nSubElementValueLen)
     681             :         {
     682          15 :             pszSubElementValue[nSubElementValueLen] = 0;
     683          15 :             if (poFeatureDefn->GetFieldDefn(iCurrentField)->GetType() ==
     684             :                 OFTReal)
     685           0 :                 poFeature->SetField(iCurrentField, CPLAtof(pszSubElementValue));
     686             :             else
     687          15 :                 poFeature->SetField(iCurrentField, pszSubElementValue);
     688             :         }
     689             : 
     690          15 :         CPLFree(pszSubElementName);
     691          15 :         pszSubElementName = nullptr;
     692          15 :         CPLFree(pszSubElementValue);
     693          15 :         pszSubElementValue = nullptr;
     694          15 :         nSubElementValueLen = 0;
     695             : 
     696          15 :         CPLFree(pszFieldName);
     697             :     }
     698         291 :     else if (bInFeature && eFormat == GEORSS_ATOM && currentDepth == 2 &&
     699          30 :              OGRGeoRSSLayerATOMTagHasSubElement(pszNoNSName))
     700             :     {
     701           9 :         bInTagWithSubTag = false;
     702             :     }
     703         252 :     else if (bInGMLGeometry)
     704             :     {
     705          27 :         AddStrToSubElementValue("</");
     706          27 :         AddStrToSubElementValue(pszName);
     707          27 :         AddStrToSubElementValue(">");
     708          27 :         if (currentDepth > geometryDepth)
     709             :         {
     710             :         }
     711             :         else
     712             :         {
     713          10 :             pszSubElementValue[nSubElementValueLen] = 0;
     714          10 :             CPLAssert(STARTS_WITH(pszName, "gml:"));
     715          10 :             poGeom = (OGRGeometry *)OGR_G_CreateFromGML(pszSubElementValue);
     716             : 
     717          10 :             if (poGeom != nullptr && !poGeom->IsEmpty())
     718             :             {
     719           9 :                 bool bSwapCoordinates = false;
     720           9 :                 if (pszGMLSRSName)
     721             :                 {
     722             :                     OGRSpatialReference *poSRSFeature =
     723           1 :                         new OGRSpatialReference();
     724           1 :                     poSRSFeature->importFromURN(pszGMLSRSName);
     725           1 :                     poGeom->assignSpatialReference(poSRSFeature);
     726           1 :                     poSRSFeature->Release();
     727             :                 }
     728             :                 else
     729             :                 {
     730           8 :                     bSwapCoordinates = true; /* lat, lon WGS 84 */
     731             :                 }
     732             : 
     733           9 :                 if (bSwapCoordinates)
     734             :                 {
     735           8 :                     poGeom->swapXY();
     736             :                 }
     737             :             }
     738          10 :             bInGMLGeometry = false;
     739             :         }
     740             :     }
     741         225 :     else if (bInSimpleGeometry)
     742             :     {
     743          10 :         if (currentDepth > geometryDepth)
     744             :         {
     745             :             // Should not happen for a valid document.
     746             :         }
     747             :         else
     748             :         {
     749          10 :             if (pszSubElementValue)
     750             :             {
     751          10 :                 pszSubElementValue[nSubElementValueLen] = 0;
     752             : 
     753             :                 // Trim any leading and trailing spaces, tabs, newlines, etc.
     754          10 :                 OGRGeoRSSLayerTrimLeadingAndTrailingSpaces(pszSubElementValue);
     755             : 
     756             :                 // Caution: Order is latitude, longitude.
     757          20 :                 char **papszTokens = CSLTokenizeStringComplex(
     758          10 :                     pszSubElementValue, " ,", TRUE, FALSE);
     759             : 
     760          10 :                 const int nTokens = CSLCount(papszTokens);
     761          10 :                 if ((nTokens % 2) != 0 ||
     762           9 :                     (eGeomType == wkbPoint && nTokens != 2) ||
     763           9 :                     (eGeomType == wkbLineString && nTokens < 4) ||
     764           9 :                     (strcmp(pszName, "georss:polygon") == 0 && nTokens < 6) ||
     765           9 :                     (strcmp(pszName, "georss:box") == 0 && nTokens != 4))
     766             :                 {
     767           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
     768             :                              "Wrong number of coordinates in %s",
     769             :                              pszSubElementValue);
     770             :                 }
     771           9 :                 else if (eGeomType == wkbPoint)
     772             :                 {
     773           6 :                     poGeom = new OGRPoint(CPLAtof(papszTokens[1]),
     774           3 :                                           CPLAtof(papszTokens[0]));
     775             :                 }
     776           6 :                 else if (eGeomType == wkbLineString)
     777             :                 {
     778           2 :                     OGRLineString *poLineString = new OGRLineString();
     779           2 :                     poGeom = poLineString;
     780           8 :                     for (int i = 0; i < nTokens; i += 2)
     781             :                     {
     782           6 :                         poLineString->addPoint(CPLAtof(papszTokens[i + 1]),
     783           6 :                                                CPLAtof(papszTokens[i]));
     784             :                     }
     785             :                 }
     786           4 :                 else if (eGeomType == wkbPolygon)
     787             :                 {
     788           4 :                     OGRPolygon *poPolygon = new OGRPolygon();
     789           4 :                     OGRLinearRing *poLinearRing = new OGRLinearRing();
     790           4 :                     poGeom = poPolygon;
     791           4 :                     poPolygon->addRingDirectly(poLinearRing);
     792           4 :                     if (strcmp(pszName, "georss:polygon") == 0)
     793             :                     {
     794          18 :                         for (int i = 0; i < nTokens; i += 2)
     795             :                         {
     796          15 :                             poLinearRing->addPoint(CPLAtof(papszTokens[i + 1]),
     797          15 :                                                    CPLAtof(papszTokens[i]));
     798             :                         }
     799             :                     }
     800             :                     else
     801             :                     {
     802           1 :                         const double lat1 = CPLAtof(papszTokens[0]);
     803           1 :                         const double lon1 = CPLAtof(papszTokens[1]);
     804           1 :                         const double lat2 = CPLAtof(papszTokens[2]);
     805           1 :                         const double lon2 = CPLAtof(papszTokens[3]);
     806           1 :                         poLinearRing->addPoint(lon1, lat1);
     807           1 :                         poLinearRing->addPoint(lon1, lat2);
     808           1 :                         poLinearRing->addPoint(lon2, lat2);
     809           1 :                         poLinearRing->addPoint(lon2, lat1);
     810           1 :                         poLinearRing->addPoint(lon1, lat1);
     811             :                     }
     812             :                 }
     813             : 
     814          10 :                 CSLDestroy(papszTokens);
     815             :             }
     816          10 :             bInSimpleGeometry = false;
     817             :         }
     818             :     }
     819         215 :     else if (IS_LAT_ELEMENT(pszName))
     820             :     {
     821           1 :         if (pszSubElementValue)
     822             :         {
     823           1 :             hasFoundLat = true;
     824           1 :             pszSubElementValue[nSubElementValueLen] = 0;
     825           1 :             latVal = CPLAtof(pszSubElementValue);
     826             :         }
     827           1 :         bInGeoLat = false;
     828             :     }
     829         214 :     else if (IS_LON_ELEMENT(pszName))
     830             :     {
     831           1 :         if (pszSubElementValue)
     832             :         {
     833           1 :             hasFoundLon = true;
     834           1 :             pszSubElementValue[nSubElementValueLen] = 0;
     835           1 :             lonVal = CPLAtof(pszSubElementValue);
     836             :         }
     837           1 :         bInGeoLong = false;
     838             :     }
     839         213 :     else if (bInFeature && currentDepth == featureDepth + 1)
     840             :     {
     841         137 :         if (iCurrentField != -1 && pszSubElementName && poFeature &&
     842         121 :             pszSubElementValue && nSubElementValueLen)
     843             :         {
     844         121 :             pszSubElementValue[nSubElementValueLen] = 0;
     845         121 :             if (poFeatureDefn->GetFieldDefn(iCurrentField)->GetType() ==
     846             :                 OFTDateTime)
     847             :             {
     848             :                 OGRField sField;
     849          27 :                 if (OGRParseRFC822DateTime(pszSubElementValue, &sField))
     850             :                 {
     851          21 :                     poFeature->SetField(iCurrentField, &sField);
     852             :                 }
     853           6 :                 else if (OGRParseXMLDateTime(pszSubElementValue, &sField))
     854             :                 {
     855           6 :                     poFeature->SetField(iCurrentField, &sField);
     856             :                 }
     857             :                 else
     858             :                 {
     859           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
     860             :                              "Could not parse %s as a valid dateTime",
     861             :                              pszSubElementValue);
     862             :                 }
     863             :             }
     864             :             else
     865             :             {
     866          94 :                 if (poFeatureDefn->GetFieldDefn(iCurrentField)->GetType() ==
     867             :                     OFTReal)
     868           0 :                     poFeature->SetField(iCurrentField,
     869           0 :                                         CPLAtof(pszSubElementValue));
     870             :                 else
     871          94 :                     poFeature->SetField(iCurrentField, pszSubElementValue);
     872             :             }
     873             :         }
     874             : 
     875         137 :         CPLFree(pszSubElementName);
     876         137 :         pszSubElementName = nullptr;
     877         137 :         CPLFree(pszSubElementValue);
     878         137 :         pszSubElementValue = nullptr;
     879         137 :         nSubElementValueLen = 0;
     880             :     }
     881          76 :     else if (bInFeature && currentDepth > featureDepth + 1 &&
     882           9 :              pszSubElementName != nullptr)
     883             :     {
     884           9 :         AddStrToSubElementValue("</");
     885           9 :         AddStrToSubElementValue(pszName);
     886           9 :         AddStrToSubElementValue(">");
     887             :     }
     888             : 
     889         276 :     if (poGeom != nullptr)
     890             :     {
     891          18 :         if (poFeature != nullptr)
     892             :         {
     893          18 :             poFeature->SetGeometryDirectly(poGeom);
     894             :         }
     895           0 :         else if (!bInFeature)
     896             :         {
     897           0 :             if (poGlobalGeom != nullptr)
     898           0 :                 delete poGlobalGeom;
     899           0 :             poGlobalGeom = poGeom;
     900             :         }
     901             :         else
     902             :         {
     903           0 :             delete poGeom;
     904             :         }
     905             :     }
     906         258 :     else if (!bInFeature && hasFoundLat && hasFoundLon)
     907             :     {
     908           0 :         if (poGlobalGeom != nullptr)
     909           0 :             delete poGlobalGeom;
     910           0 :         poGlobalGeom = new OGRPoint(lonVal, latVal);
     911           0 :         hasFoundLat = false;
     912           0 :         hasFoundLon = false;
     913             :     }
     914             : }
     915             : 
     916             : /************************************************************************/
     917             : /*                          dataHandlerCbk()                            */
     918             : /************************************************************************/
     919             : 
     920         889 : void OGRGeoRSSLayer::dataHandlerCbk(const char *data, int nLen)
     921             : {
     922         889 :     if (bStopParsing)
     923           0 :         return;
     924             : 
     925         889 :     if (bInGMLGeometry || bInSimpleGeometry || bInGeoLat || bInGeoLong ||
     926         834 :         pszSubElementName != nullptr)
     927             :     {
     928         208 :         char *pszNewSubElementValue = static_cast<char *>(VSI_REALLOC_VERBOSE(
     929             :             pszSubElementValue, nSubElementValueLen + nLen + 1));
     930         208 :         if (pszNewSubElementValue == nullptr)
     931             :         {
     932           0 :             XML_StopParser(oSchemaParser, XML_FALSE);
     933           0 :             bStopParsing = true;
     934           0 :             return;
     935             :         }
     936         208 :         pszSubElementValue = pszNewSubElementValue;
     937         208 :         memcpy(pszSubElementValue + nSubElementValueLen, data, nLen);
     938         208 :         nSubElementValueLen += nLen;
     939             :     }
     940             : }
     941             : #endif
     942             : 
     943             : /************************************************************************/
     944             : /*                           GetNextFeature()                           */
     945             : /************************************************************************/
     946             : 
     947          44 : OGRFeature *OGRGeoRSSLayer::GetNextFeature()
     948             : {
     949          44 :     if (bWriteMode)
     950             :     {
     951          16 :         CPLError(CE_Failure, CPLE_NotSupported,
     952             :                  "Cannot read features when writing a GeoRSS file");
     953          16 :         return nullptr;
     954             :     }
     955             : 
     956          28 :     if (fpGeoRSS == nullptr)
     957           0 :         return nullptr;
     958             : 
     959          28 :     if (!bHasReadSchema)
     960           4 :         LoadSchema();
     961             : 
     962          28 :     if (bStopParsing)
     963           0 :         return nullptr;
     964             : 
     965             : #ifdef HAVE_EXPAT
     966          28 :     if (nFeatureTabIndex < nFeatureTabLength)
     967             :     {
     968          15 :         return ppoFeatureTab[nFeatureTabIndex++];
     969             :     }
     970             : 
     971          13 :     if (VSIFEofL(fpGeoRSS))
     972           0 :         return nullptr;
     973             : 
     974          13 :     CPLFree(ppoFeatureTab);
     975          13 :     ppoFeatureTab = nullptr;
     976          13 :     nFeatureTabLength = 0;
     977          13 :     nFeatureTabIndex = 0;
     978             : 
     979          13 :     int nDone = 0;
     980          26 :     std::vector<char> aBuf(PARSER_BUF_SIZE);
     981           0 :     do
     982             :     {
     983             :         unsigned int nLen = static_cast<unsigned int>(
     984          13 :             VSIFReadL(aBuf.data(), 1, aBuf.size(), fpGeoRSS));
     985          13 :         nDone = VSIFEofL(fpGeoRSS);
     986          13 :         if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR)
     987             :         {
     988           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     989             :                      "XML parsing of GeoRSS file failed : %s "
     990             :                      "at line %d, column %d",
     991             :                      XML_ErrorString(XML_GetErrorCode(oParser)),
     992           0 :                      static_cast<int>(XML_GetCurrentLineNumber(oParser)),
     993           0 :                      static_cast<int>(XML_GetCurrentColumnNumber(oParser)));
     994           0 :             bStopParsing = true;
     995             :         }
     996          13 :     } while (!nDone && !bStopParsing && nFeatureTabLength == 0);
     997             : 
     998          13 :     return nFeatureTabLength ? ppoFeatureTab[nFeatureTabIndex++] : nullptr;
     999             : #else
    1000             :     return nullptr;
    1001             : #endif
    1002             : }
    1003             : 
    1004             : /************************************************************************/
    1005             : /*              OGRGeoRSSLayerIsStandardFieldInternal()                 */
    1006             : /************************************************************************/
    1007             : 
    1008         187 : static bool OGRGeoRSSLayerIsStandardFieldInternal(const char *pszName,
    1009             :                                                   const char *const *papszNames)
    1010             : {
    1011        2223 :     for (unsigned int i = 0; papszNames[i] != nullptr; i++)
    1012             :     {
    1013        2121 :         if (strcmp(pszName, papszNames[i]) == 0)
    1014             :         {
    1015          74 :             return true;
    1016             :         }
    1017             : 
    1018        2047 :         const char *pszUnderscore = strchr(papszNames[i], '_');
    1019        2047 :         if (pszUnderscore == nullptr)
    1020             :         {
    1021        1115 :             size_t nLen = strlen(papszNames[i]);
    1022        1115 :             if (strncmp(pszName, papszNames[i], nLen) == 0)
    1023             :             {
    1024          12 :                 size_t k = nLen;
    1025          18 :                 while (pszName[k] >= '0' && pszName[k] <= '9')
    1026           6 :                     k++;
    1027          12 :                 if (pszName[k] == '\0')
    1028           3 :                     return true;
    1029             :             }
    1030             :         }
    1031             :         else
    1032             :         {
    1033         932 :             const size_t nLen =
    1034         932 :                 static_cast<size_t>(pszUnderscore - papszNames[i]);
    1035         932 :             if (strncmp(pszName, papszNames[i], nLen) == 0)
    1036             :             {
    1037          23 :                 size_t k = nLen;
    1038          37 :                 while (pszName[k] >= '0' && pszName[k] <= '9')
    1039          14 :                     k++;
    1040          23 :                 if (pszName[k] == '_' &&
    1041          23 :                     strcmp(pszName + k, pszUnderscore) == 0)
    1042           8 :                     return true;
    1043             :             }
    1044             :         }
    1045             :     }
    1046         102 :     return false;
    1047             : }
    1048             : 
    1049             : /************************************************************************/
    1050             : /*               OGRGeoRSSLayer::IsStandardField()                      */
    1051             : /************************************************************************/
    1052             : 
    1053         187 : bool OGRGeoRSSLayer::IsStandardField(const char *pszName)
    1054             : {
    1055         187 :     if (eFormat == GEORSS_RSS)
    1056             :     {
    1057         165 :         return OGRGeoRSSLayerIsStandardFieldInternal(pszName,
    1058         165 :                                                      apszAllowedRSSFieldNames);
    1059             :     }
    1060             :     else
    1061             :     {
    1062          22 :         return OGRGeoRSSLayerIsStandardFieldInternal(pszName,
    1063          22 :                                                      apszAllowedATOMFieldNames);
    1064             :     }
    1065             : }
    1066             : 
    1067             : /************************************************************************/
    1068             : /*                 OGRGeoRSSLayerSplitComposedField()                   */
    1069             : /************************************************************************/
    1070             : 
    1071         103 : static void OGRGeoRSSLayerSplitComposedField(const char *pszName,
    1072             :                                              std::string &osElementName,
    1073             :                                              std::string &osNumber,
    1074             :                                              std::string &osAttributeName)
    1075             : {
    1076         103 :     osElementName = pszName;
    1077             : 
    1078         103 :     int i = 0;
    1079         751 :     while (pszName[i] != '\0' && pszName[i] != '_' &&
    1080         663 :            !(pszName[i] >= '0' && pszName[i] <= '9'))
    1081             :     {
    1082         648 :         i++;
    1083             :     }
    1084             : 
    1085         103 :     osElementName.resize(i);
    1086             : 
    1087         103 :     if (pszName[i] >= '0' && pszName[i] <= '9')
    1088             :     {
    1089          15 :         osNumber = pszName + i;
    1090          15 :         const auto nPos = osNumber.find('_');
    1091          15 :         if (nPos != std::string::npos)
    1092             :         {
    1093          11 :             osAttributeName = osNumber.substr(nPos + 1);
    1094          11 :             osNumber.resize(nPos);
    1095             :         }
    1096             :         else
    1097             :         {
    1098           4 :             osAttributeName.clear();
    1099          15 :         }
    1100             :     }
    1101             :     else
    1102             :     {
    1103          88 :         osNumber.clear();
    1104          88 :         if (pszName[i] == '_')
    1105             :         {
    1106          29 :             osAttributeName = pszName + i + 1;
    1107             :         }
    1108             :         else
    1109             :         {
    1110          59 :             osAttributeName.clear();
    1111             :         }
    1112             :     }
    1113         103 : }
    1114             : 
    1115             : /************************************************************************/
    1116             : /*                 OGRGeoRSSLayerWriteSimpleElement()                   */
    1117             : /************************************************************************/
    1118             : 
    1119           8 : static void OGRGeoRSSLayerWriteSimpleElement(VSILFILE *fp,
    1120             :                                              const char *pszElementName,
    1121             :                                              const char *pszNumber,
    1122             :                                              const char *const *papszNames,
    1123             :                                              OGRFeatureDefn *poFeatureDefn,
    1124             :                                              OGRFeature *poFeature)
    1125             : {
    1126           8 :     VSIFPrintfL(fp, "      <%s", pszElementName);
    1127             : 
    1128         152 :     for (unsigned int k = 0; papszNames[k] != nullptr; k++)
    1129             :     {
    1130         144 :         if (strncmp(papszNames[k], pszElementName, strlen(pszElementName)) ==
    1131          20 :                 0 &&
    1132          20 :             papszNames[k][strlen(pszElementName)] == '_')
    1133             :         {
    1134          14 :             const char *pszAttributeName =
    1135          14 :                 papszNames[k] + strlen(pszElementName) + 1;
    1136          14 :             char *pszFieldName = CPLStrdup(CPLSPrintf(
    1137             :                 "%s%s_%s", pszElementName, pszNumber, pszAttributeName));
    1138          14 :             int iIndex = poFeatureDefn->GetFieldIndex(pszFieldName);
    1139          14 :             if (iIndex != -1 && poFeature->IsFieldSetAndNotNull(iIndex))
    1140             :             {
    1141          13 :                 char *pszValue = OGRGetXML_UTF8_EscapedString(
    1142             :                     poFeature->GetFieldAsString(iIndex));
    1143          13 :                 VSIFPrintfL(fp, " %s=\"%s\"", pszAttributeName, pszValue);
    1144          13 :                 CPLFree(pszValue);
    1145             :             }
    1146          14 :             CPLFree(pszFieldName);
    1147             :         }
    1148             :     }
    1149             : 
    1150             :     char *pszFieldName =
    1151           8 :         CPLStrdup(CPLSPrintf("%s%s", pszElementName, pszNumber));
    1152           8 :     const int iIndex = poFeatureDefn->GetFieldIndex(pszFieldName);
    1153           8 :     if (iIndex != -1 && poFeature->IsFieldSetAndNotNull(iIndex))
    1154             :     {
    1155           6 :         VSIFPrintfL(fp, ">");
    1156             : 
    1157             :         char *pszValue =
    1158           6 :             OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString(iIndex));
    1159           6 :         VSIFPrintfL(fp, "%s", pszValue);
    1160           6 :         CPLFree(pszValue);
    1161             : 
    1162           6 :         VSIFPrintfL(fp, "</%s>\n", pszElementName);
    1163             :     }
    1164             :     else
    1165             :     {
    1166           2 :         VSIFPrintfL(fp, "/>\n");
    1167             :     }
    1168           8 :     CPLFree(pszFieldName);
    1169           8 : }
    1170             : 
    1171             : /************************************************************************/
    1172             : /*                           ICreateFeature()                            */
    1173             : /************************************************************************/
    1174             : 
    1175          95 : OGRErr OGRGeoRSSLayer::ICreateFeature(OGRFeature *poFeatureIn)
    1176             : 
    1177             : {
    1178          95 :     VSILFILE *fp = poDS->GetOutputFP();
    1179          95 :     if (fp == nullptr)
    1180           0 :         return OGRERR_FAILURE;
    1181             : 
    1182          95 :     nNextFID++;
    1183             : 
    1184             :     // Verify that compulsory feeds are set.
    1185             :     // Otherwise put some default value in them.
    1186          95 :     if (eFormat == GEORSS_RSS)
    1187             :     {
    1188          94 :         const int iFieldTitle = poFeatureDefn->GetFieldIndex("title");
    1189             :         const int iFieldDescription =
    1190          94 :             poFeatureDefn->GetFieldIndex("description");
    1191             : 
    1192          94 :         VSIFPrintfL(fp, "    <item>\n");
    1193             : 
    1194         106 :         if ((iFieldTitle == -1 ||
    1195          94 :              !poFeatureIn->IsFieldSetAndNotNull(iFieldTitle)) &&
    1196           0 :             (iFieldDescription == -1 ||
    1197           0 :              !poFeatureIn->IsFieldSetAndNotNull(iFieldDescription)))
    1198             :         {
    1199          82 :             VSIFPrintfL(fp, "      <title>Feature %d</title>\n", nNextFID);
    1200             :         }
    1201             :     }
    1202             :     else
    1203             :     {
    1204           1 :         VSIFPrintfL(fp, "    <entry>\n");
    1205             : 
    1206           1 :         const int iFieldId = poFeatureDefn->GetFieldIndex("id");
    1207           1 :         const int iFieldTitle = poFeatureDefn->GetFieldIndex("title");
    1208           1 :         const int iFieldUpdated = poFeatureDefn->GetFieldIndex("updated");
    1209             : 
    1210           1 :         if (iFieldId == -1 || !poFeatureIn->IsFieldSetAndNotNull(iFieldId))
    1211             :         {
    1212           0 :             VSIFPrintfL(fp, "      <id>Feature %d</id>\n", nNextFID);
    1213             :         }
    1214             : 
    1215           2 :         if (iFieldTitle == -1 ||
    1216           1 :             !poFeatureIn->IsFieldSetAndNotNull(iFieldTitle))
    1217             :         {
    1218           0 :             VSIFPrintfL(fp, "      <title>Title for feature %d</title>\n",
    1219             :                         nNextFID);
    1220             :         }
    1221             : 
    1222           2 :         if (iFieldUpdated == -1 ||
    1223           1 :             !poFeatureIn->IsFieldSetAndNotNull(iFieldUpdated))
    1224             :         {
    1225           0 :             VSIFPrintfL(fp, "      <updated>2009-01-01T00:00:00Z</updated>\n");
    1226             :         }
    1227             :     }
    1228             : 
    1229          95 :     const int nFieldCount = poFeatureDefn->GetFieldCount();
    1230          95 :     int *pbUsed = static_cast<int *>(CPLCalloc(sizeof(int), nFieldCount));
    1231             : 
    1232         226 :     for (int i = 0; i < nFieldCount; i++)
    1233             :     {
    1234         131 :         OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(i);
    1235         131 :         const char *pszName = poFieldDefn->GetNameRef();
    1236             : 
    1237         131 :         if (!poFeatureIn->IsFieldSetAndNotNull(i))
    1238          48 :             continue;
    1239             : 
    1240         166 :         std::string osElementName;
    1241         166 :         std::string osNumber;
    1242         166 :         std::string osAttributeName;
    1243          83 :         OGRGeoRSSLayerSplitComposedField(pszName, osElementName, osNumber,
    1244             :                                          osAttributeName);
    1245             : 
    1246          83 :         bool bWillSkip = false;
    1247             :         // Handle Atom entries with elements with sub-elements like
    1248             :         // <author><name>...</name><uri>...</uri></author>
    1249          83 :         if (eFormat == GEORSS_ATOM)
    1250             :         {
    1251          52 :             for (unsigned int k = 0;
    1252          52 :                  apszAllowedATOMFieldNamesWithSubElements[k] != nullptr; k++)
    1253             :             {
    1254          74 :                 if (osElementName ==
    1255          42 :                         apszAllowedATOMFieldNamesWithSubElements[k] &&
    1256           5 :                     !osAttributeName.empty())
    1257             :                 {
    1258           5 :                     bWillSkip = true;
    1259           5 :                     if (pbUsed[i])
    1260           2 :                         break;
    1261             : 
    1262           3 :                     VSIFPrintfL(fp, "      <%s>\n", osElementName.c_str());
    1263             : 
    1264          23 :                     for (int j = i; j < nFieldCount; j++)
    1265             :                     {
    1266          20 :                         poFieldDefn = poFeatureDefn->GetFieldDefn(j);
    1267          20 :                         if (!poFeatureIn->IsFieldSetAndNotNull(j))
    1268           0 :                             continue;
    1269             : 
    1270          40 :                         std::string osElementName2;
    1271          40 :                         std::string osNumber2;
    1272          40 :                         std::string osAttributeName2;
    1273          20 :                         OGRGeoRSSLayerSplitComposedField(
    1274             :                             poFieldDefn->GetNameRef(), osElementName2,
    1275             :                             osNumber2, osAttributeName2);
    1276             : 
    1277          26 :                         if (osElementName2 == osElementName &&
    1278          26 :                             osNumber2 == osNumber && !osAttributeName2.empty())
    1279             :                         {
    1280           5 :                             pbUsed[j] = TRUE;
    1281             : 
    1282           5 :                             char *pszValue = OGRGetXML_UTF8_EscapedString(
    1283             :                                 poFeatureIn->GetFieldAsString(j));
    1284           5 :                             VSIFPrintfL(fp, "        <%s>%s</%s>\n",
    1285             :                                         osAttributeName2.c_str(), pszValue,
    1286             :                                         osAttributeName2.c_str());
    1287           5 :                             CPLFree(pszValue);
    1288             :                         }
    1289             :                     }
    1290             : 
    1291           3 :                     VSIFPrintfL(fp, "      </%s>\n", osElementName.c_str());
    1292             : 
    1293           3 :                     break;
    1294             :                 }
    1295             :             }
    1296             :         }
    1297             : 
    1298          83 :         if (bWillSkip)
    1299             :         {
    1300             :             // Do nothing
    1301             :         }
    1302          78 :         else if (eFormat == GEORSS_RSS && strcmp(pszName, "pubDate") == 0)
    1303             :         {
    1304          12 :             const OGRField *psField = poFeatureIn->GetRawFieldRef(i);
    1305          12 :             char *pszDate = OGRGetRFC822DateTime(psField);
    1306          12 :             VSIFPrintfL(fp, "      <%s>%s</%s>\n", pszName, pszDate, pszName);
    1307          12 :             CPLFree(pszDate);
    1308             :         }
    1309          66 :         else if (eFormat == GEORSS_ATOM && (strcmp(pszName, "updated") == 0 ||
    1310          14 :                                             strcmp(pszName, "published") == 0))
    1311             :         {
    1312           2 :             const OGRField *psField = poFeatureIn->GetRawFieldRef(i);
    1313           2 :             char *pszDate = OGRGetXMLDateTime(psField);
    1314           2 :             VSIFPrintfL(fp, "      <%s>%s</%s>\n", pszName, pszDate, pszName);
    1315           2 :             CPLFree(pszDate);
    1316             :         }
    1317          64 :         else if (strcmp(pszName, "dc_date") == 0)
    1318             :         {
    1319           0 :             const OGRField *psField = poFeatureIn->GetRawFieldRef(i);
    1320           0 :             char *pszDate = OGRGetXMLDateTime(psField);
    1321           0 :             VSIFPrintfL(fp, "      <%s>%s</%s>\n", "dc:date", pszDate,
    1322             :                         "dc:date");
    1323           0 :             CPLFree(pszDate);
    1324             :         }
    1325             :         // RSS fields with content and attributes.
    1326         154 :         else if (eFormat == GEORSS_RSS &&
    1327         129 :                  (osElementName == "category" || osElementName == "guid" ||
    1328          39 :                   osElementName == "source"))
    1329             :         {
    1330          12 :             if (osAttributeName.empty())
    1331             :             {
    1332           6 :                 OGRGeoRSSLayerWriteSimpleElement(
    1333             :                     fp, osElementName.c_str(), osNumber.c_str(),
    1334             :                     apszAllowedRSSFieldNames, poFeatureDefn, poFeatureIn);
    1335             :             }
    1336             :         }
    1337             :         // RSS field with attribute only.
    1338          52 :         else if (eFormat == GEORSS_RSS && osElementName == "enclosure")
    1339             :         {
    1340           0 :             if (osAttributeName == "url")
    1341             :             {
    1342           0 :                 OGRGeoRSSLayerWriteSimpleElement(
    1343             :                     fp, osElementName.c_str(), osNumber.c_str(),
    1344             :                     apszAllowedRSSFieldNames, poFeatureDefn, poFeatureIn);
    1345             :             }
    1346             :         }
    1347             :         /* ATOM fields with attribute only */
    1348          78 :         else if (eFormat == GEORSS_ATOM &&
    1349          26 :                  (osElementName == "category" || osElementName == "link"))
    1350             :         {
    1351          21 :             if ((osElementName == "category" && osAttributeName == "term") ||
    1352          14 :                 (osElementName == "link" && osAttributeName == "href"))
    1353             :             {
    1354           2 :                 OGRGeoRSSLayerWriteSimpleElement(
    1355             :                     fp, osElementName.c_str(), osNumber.c_str(),
    1356             :                     apszAllowedATOMFieldNames, poFeatureDefn, poFeatureIn);
    1357             :             }
    1358             :         }
    1359          45 :         else if (eFormat == GEORSS_ATOM && (STARTS_WITH(pszName, "content") ||
    1360           2 :                                             STARTS_WITH(pszName, "summary")))
    1361             :         {
    1362           4 :             if (strchr(pszName, '_') == nullptr)
    1363             :             {
    1364           1 :                 VSIFPrintfL(fp, "      <%s", pszName);
    1365             : 
    1366           1 :                 bool bIsXHTML = false;
    1367             :                 char *pszFieldName =
    1368           1 :                     CPLStrdup(CPLSPrintf("%s_%s", pszName, "type"));
    1369           1 :                 int iIndex = poFeatureDefn->GetFieldIndex(pszFieldName);
    1370           1 :                 if (iIndex != -1 && poFeatureIn->IsFieldSetAndNotNull(iIndex))
    1371             :                 {
    1372           1 :                     bIsXHTML = strcmp(poFeatureIn->GetFieldAsString(iIndex),
    1373             :                                       "xhtml") == 0;
    1374           1 :                     char *pszValue = OGRGetXML_UTF8_EscapedString(
    1375             :                         poFeatureIn->GetFieldAsString(iIndex));
    1376           1 :                     VSIFPrintfL(fp, " %s=\"%s\"", "type", pszValue);
    1377           1 :                     CPLFree(pszValue);
    1378             :                 }
    1379           1 :                 CPLFree(pszFieldName);
    1380             : 
    1381             :                 pszFieldName =
    1382           1 :                     CPLStrdup(CPLSPrintf("%s_%s", pszName, "xml_lang"));
    1383           1 :                 iIndex = poFeatureDefn->GetFieldIndex(pszFieldName);
    1384           1 :                 if (iIndex != -1 && poFeatureIn->IsFieldSetAndNotNull(iIndex))
    1385             :                 {
    1386           1 :                     char *pszValue = OGRGetXML_UTF8_EscapedString(
    1387             :                         poFeatureIn->GetFieldAsString(iIndex));
    1388           1 :                     VSIFPrintfL(fp, " %s=\"%s\"", "xml:lang", pszValue);
    1389           1 :                     CPLFree(pszValue);
    1390             :                 }
    1391           1 :                 CPLFree(pszFieldName);
    1392             : 
    1393             :                 pszFieldName =
    1394           1 :                     CPLStrdup(CPLSPrintf("%s_%s", pszName, "xml_base"));
    1395           1 :                 iIndex = poFeatureDefn->GetFieldIndex(pszFieldName);
    1396           1 :                 if (iIndex != -1 && poFeatureIn->IsFieldSetAndNotNull(iIndex))
    1397             :                 {
    1398           1 :                     char *pszValue = OGRGetXML_UTF8_EscapedString(
    1399             :                         poFeatureIn->GetFieldAsString(iIndex));
    1400           1 :                     VSIFPrintfL(fp, " %s=\"%s\"", "xml:base", pszValue);
    1401           1 :                     CPLFree(pszValue);
    1402             :                 }
    1403           1 :                 CPLFree(pszFieldName);
    1404             : 
    1405           1 :                 VSIFPrintfL(fp, ">");
    1406           1 :                 if (bIsXHTML)
    1407             :                 {
    1408           1 :                     VSIFPrintfL(fp, "%s", poFeatureIn->GetFieldAsString(i));
    1409             :                 }
    1410             :                 else
    1411             :                 {
    1412           0 :                     char *pszValue = OGRGetXML_UTF8_EscapedString(
    1413             :                         poFeatureIn->GetFieldAsString(i));
    1414           0 :                     VSIFPrintfL(fp, "%s", pszValue);
    1415           0 :                     CPLFree(pszValue);
    1416             :                 }
    1417           1 :                 VSIFPrintfL(fp, "      </%s>\n", pszName);
    1418           4 :             }
    1419             :         }
    1420          41 :         else if (STARTS_WITH(pszName, "dc_subject"))
    1421             :         {
    1422           0 :             if (strchr(pszName + strlen("dc_subject"), '_') == nullptr)
    1423             :             {
    1424           0 :                 VSIFPrintfL(fp, "      <%s", "dc:subject");
    1425             : 
    1426             :                 char *pszFieldName =
    1427           0 :                     CPLStrdup(CPLSPrintf("%s_%s", pszName, "xml_lang"));
    1428           0 :                 int iIndex = poFeatureDefn->GetFieldIndex(pszFieldName);
    1429           0 :                 if (iIndex != -1 && poFeatureIn->IsFieldSetAndNotNull(iIndex))
    1430             :                 {
    1431           0 :                     char *pszValue = OGRGetXML_UTF8_EscapedString(
    1432             :                         poFeatureIn->GetFieldAsString(iIndex));
    1433           0 :                     VSIFPrintfL(fp, " %s=\"%s\"", "xml:lang", pszValue);
    1434           0 :                     CPLFree(pszValue);
    1435             :                 }
    1436           0 :                 CPLFree(pszFieldName);
    1437             : 
    1438           0 :                 char *pszValue = OGRGetXML_UTF8_EscapedString(
    1439             :                     poFeatureIn->GetFieldAsString(i));
    1440           0 :                 VSIFPrintfL(fp, ">%s</%s>\n", pszValue, "dc:subject");
    1441           0 :                 CPLFree(pszValue);
    1442             :             }
    1443             :         }
    1444             :         else
    1445             :         {
    1446          41 :             char *pszTagName = CPLStrdup(pszName);
    1447          41 :             if (IsStandardField(pszName) == FALSE)
    1448             :             {
    1449           3 :                 int nCountUnderscore = 0;
    1450          29 :                 for (int j = 0; pszTagName[j] != 0; j++)
    1451             :                 {
    1452          26 :                     if (pszTagName[j] == '_')
    1453             :                     {
    1454           2 :                         if (nCountUnderscore == 0)
    1455           2 :                             pszTagName[j] = ':';
    1456           2 :                         nCountUnderscore++;
    1457             :                     }
    1458          24 :                     else if (pszTagName[j] == ' ')
    1459           0 :                         pszTagName[j] = '_';
    1460             :                 }
    1461           3 :                 if (nCountUnderscore == 0)
    1462             :                 {
    1463           1 :                     char *pszTemp = CPLStrdup(CPLSPrintf("ogr:%s", pszTagName));
    1464           1 :                     CPLFree(pszTagName);
    1465           1 :                     pszTagName = pszTemp;
    1466             :                 }
    1467             :             }
    1468             :             char *pszValue =
    1469          41 :                 OGRGetXML_UTF8_EscapedString(poFeatureIn->GetFieldAsString(i));
    1470          41 :             VSIFPrintfL(fp, "      <%s>%s</%s>\n", pszTagName, pszValue,
    1471             :                         pszTagName);
    1472          41 :             CPLFree(pszValue);
    1473          41 :             CPLFree(pszTagName);
    1474             :         }
    1475             :     }
    1476             : 
    1477          95 :     CPLFree(pbUsed);
    1478             : 
    1479          95 :     OGRGeoRSSGeomDialect eGeomDialect = poDS->GetGeomDialect();
    1480          95 :     OGRGeometry *poGeom = poFeatureIn->GetGeometryRef();
    1481          95 :     if (poGeom != nullptr && !poGeom->IsEmpty())
    1482             :     {
    1483          59 :         char *pszURN = nullptr;
    1484          59 :         bool bSwapCoordinates = false;
    1485          59 :         if (eGeomDialect == GEORSS_GML)
    1486             :         {
    1487           5 :             if (poSRS != nullptr)
    1488             :             {
    1489           1 :                 const char *pszAuthorityName = poSRS->GetAuthorityName(nullptr);
    1490           1 :                 const char *pszAuthorityCode = poSRS->GetAuthorityCode(nullptr);
    1491           1 :                 if (pszAuthorityName != nullptr &&
    1492           1 :                     EQUAL(pszAuthorityName, "EPSG") &&
    1493             :                     pszAuthorityCode != nullptr)
    1494             :                 {
    1495           1 :                     if (!EQUAL(pszAuthorityCode, "4326"))
    1496           1 :                         pszURN = CPLStrdup(CPLSPrintf(
    1497             :                             "urn:ogc:def:crs:EPSG::%s", pszAuthorityCode));
    1498             : 
    1499             :                     /* In case the SRS is a geographic SRS and that we have */
    1500             :                     /* no axis definition, we assume that the order is */
    1501             :                     /* lon/lat. */
    1502             :                     const char *pszAxisName =
    1503           1 :                         poSRS->GetAxis(nullptr, 0, nullptr);
    1504           1 :                     if (poSRS->IsGeographic() &&
    1505           0 :                         (pszAxisName == nullptr ||
    1506           0 :                          STARTS_WITH_CI(pszAxisName, "Lon")))
    1507             :                     {
    1508           0 :                         bSwapCoordinates = true;
    1509           1 :                     }
    1510             :                 }
    1511             :                 else
    1512             :                 {
    1513             :                     static bool bOnce = false;
    1514           0 :                     if (!bOnce)
    1515             :                     {
    1516           0 :                         bOnce = true;
    1517           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
    1518             :                                  "Could not translate SRS into GML urn");
    1519             :                     }
    1520             :                 }
    1521             :             }
    1522             :             else
    1523             :             {
    1524           4 :                 bSwapCoordinates = true;
    1525             :             }
    1526             :         }
    1527             : 
    1528          59 :         char szCoord[75] = {};
    1529          59 :         switch (wkbFlatten(poGeom->getGeometryType()))
    1530             :         {
    1531          12 :             case wkbPoint:
    1532             :             {
    1533          12 :                 OGRPoint *poPoint = poGeom->toPoint();
    1534          12 :                 const double x = poPoint->getX();
    1535          12 :                 const double y = poPoint->getY();
    1536          12 :                 if (eGeomDialect == GEORSS_GML)
    1537             :                 {
    1538           2 :                     VSIFPrintfL(fp, "      <georss:where><gml:Point");
    1539           2 :                     if (pszURN != nullptr)
    1540           1 :                         VSIFPrintfL(fp, " srsName=\"%s\"", pszURN);
    1541           2 :                     if (poGeom->getCoordinateDimension() == 3)
    1542             :                     {
    1543           0 :                         OGRMakeWktCoordinate(szCoord, bSwapCoordinates ? y : x,
    1544             :                                              bSwapCoordinates ? x : y,
    1545             :                                              poPoint->getZ(), 3);
    1546           0 :                         VSIFPrintfL(fp, " srsDimension=\"3\"><gml:pos>%s",
    1547             :                                     szCoord);
    1548             :                     }
    1549             :                     else
    1550             :                     {
    1551           2 :                         OGRMakeWktCoordinate(szCoord, bSwapCoordinates ? y : x,
    1552             :                                              bSwapCoordinates ? x : y, 0, 2);
    1553           2 :                         VSIFPrintfL(fp, "><gml:pos>%s", szCoord);
    1554             :                     }
    1555           2 :                     VSIFPrintfL(fp, "</gml:pos></gml:Point></georss:where>\n");
    1556             :                 }
    1557          10 :                 else if (eGeomDialect == GEORSS_SIMPLE)
    1558             :                 {
    1559           9 :                     OGRMakeWktCoordinate(szCoord, y, x, 0, 2);
    1560           9 :                     VSIFPrintfL(fp, "      <georss:point>%s</georss:point>\n",
    1561             :                                 szCoord);
    1562             :                 }
    1563           1 :                 else if (eGeomDialect == GEORSS_W3C_GEO)
    1564             :                 {
    1565           1 :                     OGRFormatDouble(szCoord, sizeof(szCoord), y, '.');
    1566           1 :                     VSIFPrintfL(fp, "      <geo:lat>%s</geo:lat>\n", szCoord);
    1567           1 :                     OGRFormatDouble(szCoord, sizeof(szCoord), x, '.');
    1568           1 :                     VSIFPrintfL(fp, "      <geo:long>%s</geo:long>\n", szCoord);
    1569             :                 }
    1570          12 :                 break;
    1571             :             }
    1572             : 
    1573          11 :             case wkbLineString:
    1574             :             {
    1575          11 :                 OGRLineString *poLineString = poGeom->toLineString();
    1576          11 :                 if (eGeomDialect == GEORSS_GML)
    1577             :                 {
    1578           1 :                     VSIFPrintfL(fp, "      <georss:where><gml:LineString");
    1579           1 :                     if (pszURN != nullptr)
    1580           0 :                         VSIFPrintfL(fp, " srsName=\"%s\"", pszURN);
    1581           1 :                     VSIFPrintfL(fp, "><gml:posList>\n");
    1582           1 :                     const int n = poLineString->getNumPoints();
    1583           4 :                     for (int i = 0; i < n; i++)
    1584             :                     {
    1585           3 :                         const double x = poLineString->getX(i);
    1586           3 :                         const double y = poLineString->getY(i);
    1587           3 :                         OGRMakeWktCoordinate(szCoord, bSwapCoordinates ? y : x,
    1588             :                                              bSwapCoordinates ? x : y, 0, 2);
    1589           3 :                         VSIFPrintfL(fp, "%s ", szCoord);
    1590             :                     }
    1591           1 :                     VSIFPrintfL(
    1592             :                         fp, "</gml:posList></gml:LineString></georss:where>\n");
    1593             :                 }
    1594          10 :                 else if (eGeomDialect == GEORSS_SIMPLE)
    1595             :                 {
    1596           9 :                     VSIFPrintfL(fp, "      <georss:line>\n");
    1597           9 :                     int n = poLineString->getNumPoints();
    1598          28 :                     for (int i = 0; i < n; i++)
    1599             :                     {
    1600          19 :                         double x = poLineString->getX(i);
    1601          19 :                         double y = poLineString->getY(i);
    1602          19 :                         OGRMakeWktCoordinate(szCoord, y, x, 0, 2);
    1603          19 :                         VSIFPrintfL(fp, "%s ", szCoord);
    1604             :                     }
    1605           9 :                     VSIFPrintfL(fp, "</georss:line>\n");
    1606             :                 }
    1607             :                 else
    1608             :                 {
    1609             :                     // Not supported.
    1610             :                 }
    1611          11 :                 break;
    1612             :             }
    1613             : 
    1614          12 :             case wkbPolygon:
    1615             :             {
    1616          12 :                 OGRPolygon *poPolygon = poGeom->toPolygon();
    1617          12 :                 OGRLineString *poLineString = poPolygon->getExteriorRing();
    1618          12 :                 if (poLineString == nullptr)
    1619           0 :                     break;
    1620             : 
    1621          12 :                 if (eGeomDialect == GEORSS_GML)
    1622             :                 {
    1623           2 :                     VSIFPrintfL(fp, "      <georss:where><gml:Polygon");
    1624           2 :                     if (pszURN != nullptr)
    1625           0 :                         VSIFPrintfL(fp, " srsName=\"%s\"", pszURN);
    1626           2 :                     VSIFPrintfL(
    1627             :                         fp, "><gml:exterior><gml:LinearRing><gml:posList>\n");
    1628           2 :                     const int n = poLineString->getNumPoints();
    1629          12 :                     for (int i = 0; i < n; i++)
    1630             :                     {
    1631          10 :                         const double x = poLineString->getX(i);
    1632          10 :                         const double y = poLineString->getY(i);
    1633          10 :                         OGRMakeWktCoordinate(szCoord, bSwapCoordinates ? y : x,
    1634             :                                              bSwapCoordinates ? x : y, 0, 2);
    1635          10 :                         VSIFPrintfL(fp, "%s ", szCoord);
    1636             :                     }
    1637           2 :                     VSIFPrintfL(fp,
    1638             :                                 "</gml:posList></gml:LinearRing></gml:exterior>"
    1639             :                                 "</gml:Polygon></georss:where>\n");
    1640             :                 }
    1641          10 :                 else if (eGeomDialect == GEORSS_SIMPLE)
    1642             :                 {
    1643           8 :                     VSIFPrintfL(fp, "      <georss:polygon>\n");
    1644           8 :                     const int n = poLineString->getNumPoints();
    1645          48 :                     for (int i = 0; i < n; i++)
    1646             :                     {
    1647          40 :                         const double x = poLineString->getX(i);
    1648          40 :                         const double y = poLineString->getY(i);
    1649          40 :                         OGRMakeWktCoordinate(szCoord, y, x, 0, 2);
    1650          40 :                         VSIFPrintfL(fp, "%s ", szCoord);
    1651             :                     }
    1652           8 :                     VSIFPrintfL(fp, "</georss:polygon>\n");
    1653             :                 }
    1654             :                 else
    1655             :                 {
    1656             :                     /* Not supported */
    1657             :                 }
    1658          12 :                 break;
    1659             :             }
    1660             : 
    1661          24 :             default:
    1662             :                 /* Not supported */
    1663          24 :                 break;
    1664             :         }
    1665          59 :         CPLFree(pszURN);
    1666             :     }
    1667             : 
    1668          95 :     if (eFormat == GEORSS_RSS)
    1669          94 :         VSIFPrintfL(fp, "    </item>\n");
    1670             :     else
    1671           1 :         VSIFPrintfL(fp, "    </entry>\n");
    1672             : 
    1673          95 :     return OGRERR_NONE;
    1674             : }
    1675             : 
    1676             : /************************************************************************/
    1677             : /*                            CreateField()                             */
    1678             : /************************************************************************/
    1679             : 
    1680         146 : OGRErr OGRGeoRSSLayer::CreateField(const OGRFieldDefn *poFieldDefn,
    1681             :                                    CPL_UNUSED int bApproxOK)
    1682             : {
    1683         146 :     const char *pszName = poFieldDefn->GetNameRef();
    1684         126 :     if (((eFormat == GEORSS_RSS && strcmp(pszName, "pubDate") == 0) ||
    1685         143 :          (eFormat == GEORSS_ATOM && (strcmp(pszName, "updated") == 0 ||
    1686          19 :                                      strcmp(pszName, "published") == 0)) ||
    1687         292 :          strcmp(pszName, "dc:date") == 0) &&
    1688           5 :         poFieldDefn->GetType() != OFTDateTime)
    1689             :     {
    1690           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Wrong field type for %s",
    1691             :                  pszName);
    1692           0 :         return OGRERR_FAILURE;
    1693             :     }
    1694             : 
    1695         447 :     for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++)
    1696             :     {
    1697         301 :         if (strcmp(poFeatureDefn->GetFieldDefn(iField)->GetNameRef(),
    1698         301 :                    pszName) == 0)
    1699             :         {
    1700           0 :             return OGRERR_FAILURE;
    1701             :         }
    1702             :     }
    1703             : 
    1704         146 :     if (IsStandardField(pszName))
    1705             :     {
    1706          47 :         poFeatureDefn->AddFieldDefn(poFieldDefn);
    1707          47 :         return OGRERR_NONE;
    1708             :     }
    1709             : 
    1710          99 :     if (poDS->GetUseExtensions() == FALSE)
    1711             :     {
    1712          96 :         CPLError(
    1713             :             CE_Failure, CPLE_NotSupported,
    1714             :             "Field of name '%s' is not supported in %s schema. "
    1715             :             "Use USE_EXTENSIONS creation option to allow use of extensions.",
    1716          96 :             pszName, (eFormat == GEORSS_RSS) ? "RSS" : "ATOM");
    1717          96 :         return OGRERR_FAILURE;
    1718             :     }
    1719             :     else
    1720             :     {
    1721           3 :         poFeatureDefn->AddFieldDefn(poFieldDefn);
    1722           3 :         return OGRERR_NONE;
    1723             :     }
    1724             : }
    1725             : 
    1726             : #ifdef HAVE_EXPAT
    1727             : 
    1728         304 : static void XMLCALL startElementLoadSchemaCbk(void *pUserData,
    1729             :                                               const char *pszName,
    1730             :                                               const char **ppszAttr)
    1731             : {
    1732         304 :     ((OGRGeoRSSLayer *)pUserData)->startElementLoadSchemaCbk(pszName, ppszAttr);
    1733         304 : }
    1734             : 
    1735         304 : static void XMLCALL endElementLoadSchemaCbk(void *pUserData,
    1736             :                                             const char *pszName)
    1737             : {
    1738         304 :     ((OGRGeoRSSLayer *)pUserData)->endElementLoadSchemaCbk(pszName);
    1739         304 : }
    1740             : 
    1741         889 : static void XMLCALL dataHandlerLoadSchemaCbk(void *pUserData, const char *data,
    1742             :                                              int nLen)
    1743             : {
    1744         889 :     ((OGRGeoRSSLayer *)pUserData)->dataHandlerLoadSchemaCbk(data, nLen);
    1745         889 : }
    1746             : 
    1747             : /************************************************************************/
    1748             : /*                       LoadSchema()                         */
    1749             : /************************************************************************/
    1750             : 
    1751             : /** This function parses the whole file to detect the fields */
    1752          67 : void OGRGeoRSSLayer::LoadSchema()
    1753             : {
    1754          67 :     if (bHasReadSchema)
    1755          54 :         return;
    1756             : 
    1757          67 :     bHasReadSchema = true;
    1758             : 
    1759          67 :     if (fpGeoRSS == nullptr)
    1760          54 :         return;
    1761             : 
    1762          13 :     oSchemaParser = OGRCreateExpatXMLParser();
    1763          13 :     XML_SetElementHandler(oSchemaParser, ::startElementLoadSchemaCbk,
    1764             :                           ::endElementLoadSchemaCbk);
    1765          13 :     XML_SetCharacterDataHandler(oSchemaParser, ::dataHandlerLoadSchemaCbk);
    1766          13 :     XML_SetUserData(oSchemaParser, this);
    1767             : 
    1768          13 :     VSIFSeekL(fpGeoRSS, 0, SEEK_SET);
    1769             : 
    1770          13 :     bInFeature = false;
    1771          13 :     currentDepth = 0;
    1772          13 :     currentFieldDefn = nullptr;
    1773          13 :     pszSubElementName = nullptr;
    1774          13 :     pszSubElementValue = nullptr;
    1775          13 :     nSubElementValueLen = 0;
    1776          13 :     bSameSRS = true;
    1777          13 :     CPLFree(pszGMLSRSName);
    1778          13 :     pszGMLSRSName = nullptr;
    1779          13 :     eGeomType = wkbUnknown;
    1780          13 :     bFoundGeom = false;
    1781          13 :     bInTagWithSubTag = false;
    1782          13 :     pszTagWithSubTag = nullptr;
    1783          13 :     bStopParsing = false;
    1784          13 :     nWithoutEventCounter = 0;
    1785          13 :     nTotalFeatureCount = 0;
    1786          13 :     setOfFoundFields = nullptr;
    1787             : 
    1788          26 :     std::vector<char> aBuf(PARSER_BUF_SIZE);
    1789          13 :     int nDone = 0;
    1790           0 :     do
    1791             :     {
    1792          13 :         nDataHandlerCounter = 0;
    1793             :         unsigned int nLen =
    1794          13 :             (unsigned int)VSIFReadL(aBuf.data(), 1, aBuf.size(), fpGeoRSS);
    1795          13 :         nDone = VSIFEofL(fpGeoRSS);
    1796          13 :         if (XML_Parse(oSchemaParser, aBuf.data(), nLen, nDone) ==
    1797             :             XML_STATUS_ERROR)
    1798             :         {
    1799           0 :             CPLError(
    1800             :                 CE_Failure, CPLE_AppDefined,
    1801             :                 "XML parsing of GeoRSS file failed : %s at line %d, column %d",
    1802             :                 XML_ErrorString(XML_GetErrorCode(oSchemaParser)),
    1803           0 :                 static_cast<int>(XML_GetCurrentLineNumber(oSchemaParser)),
    1804           0 :                 static_cast<int>(XML_GetCurrentColumnNumber(oSchemaParser)));
    1805           0 :             bStopParsing = true;
    1806             :         }
    1807          13 :         nWithoutEventCounter++;
    1808          13 :     } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);
    1809             : 
    1810          13 :     XML_ParserFree(oSchemaParser);
    1811             : 
    1812          13 :     if (nWithoutEventCounter == 10)
    1813             :     {
    1814           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1815             :                  "Too much data inside one element. File probably corrupted");
    1816           0 :         bStopParsing = true;
    1817             :     }
    1818             : 
    1819          13 :     CPLAssert(poSRS == nullptr);
    1820          13 :     if (bSameSRS && bFoundGeom)
    1821             :     {
    1822           9 :         if (pszGMLSRSName == nullptr)
    1823             :         {
    1824           8 :             poSRS = new OGRSpatialReference();
    1825           8 :             poSRS->SetWellKnownGeogCS("WGS84");
    1826           8 :             poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1827             :         }
    1828             :         else
    1829             :         {
    1830           1 :             poSRS = new OGRSpatialReference();
    1831           1 :             poSRS->importFromURN(pszGMLSRSName);
    1832             :         }
    1833             :     }
    1834             : 
    1835          13 :     if (eGeomType != wkbUnknown)
    1836           5 :         poFeatureDefn->SetGeomType(eGeomType);
    1837          13 :     if (poFeatureDefn->GetGeomFieldCount() != 0)
    1838          13 :         poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
    1839             : 
    1840          13 :     if (setOfFoundFields)
    1841          13 :         CPLHashSetDestroy(setOfFoundFields);
    1842          13 :     setOfFoundFields = nullptr;
    1843          13 :     CPLFree(pszGMLSRSName);
    1844          13 :     pszGMLSRSName = nullptr;
    1845          13 :     CPLFree(pszTagWithSubTag);
    1846          13 :     pszTagWithSubTag = nullptr;
    1847             : 
    1848          13 :     VSIFSeekL(fpGeoRSS, 0, SEEK_SET);
    1849             : }
    1850             : 
    1851             : /************************************************************************/
    1852             : /*                  startElementLoadSchemaCbk()                         */
    1853             : /************************************************************************/
    1854             : 
    1855         304 : void OGRGeoRSSLayer::startElementLoadSchemaCbk(const char *pszName,
    1856             :                                                const char **ppszAttr)
    1857             : {
    1858         304 :     if (bStopParsing)
    1859           0 :         return;
    1860             : 
    1861         304 :     nWithoutEventCounter = 0;
    1862         304 :     const char *pszNoNSName = pszName;
    1863         304 :     const char *pszColon = strchr(pszNoNSName, ':');
    1864         304 :     if (pszColon)
    1865          77 :         pszNoNSName = pszColon + 1;
    1866             : 
    1867         304 :     if ((eFormat == GEORSS_ATOM && currentDepth == 1 &&
    1868          23 :          strcmp(pszNoNSName, "entry") == 0) ||
    1869         301 :         ((eFormat == GEORSS_RSS || eFormat == GEORSS_RSS_RDF) && !bInFeature &&
    1870          68 :          (currentDepth == 1 || currentDepth == 2) &&
    1871          58 :          strcmp(pszNoNSName, "item") == 0))
    1872             :     {
    1873          28 :         bInFeature = true;
    1874          28 :         featureDepth = currentDepth;
    1875             : 
    1876          28 :         nTotalFeatureCount++;
    1877             : 
    1878          28 :         if (setOfFoundFields)
    1879          15 :             CPLHashSetDestroy(setOfFoundFields);
    1880          28 :         setOfFoundFields =
    1881          28 :             CPLHashSetNew(CPLHashSetHashStr, CPLHashSetEqualStr, CPLFree);
    1882             :     }
    1883         276 :     else if (bInTagWithSubTag && currentDepth == 3)
    1884             :     {
    1885             :         char *pszFieldName =
    1886          15 :             CPLStrdup(CPLSPrintf("%s_%s", pszTagWithSubTag, pszNoNSName));
    1887          15 :         if (poFeatureDefn->GetFieldIndex(pszFieldName) == -1)
    1888             :         {
    1889          30 :             OGRFieldDefn newFieldDefn(pszFieldName, OFTString);
    1890          15 :             poFeatureDefn->AddFieldDefn(&newFieldDefn);
    1891             : 
    1892          15 :             if (poFeatureDefn->GetFieldCount() == 100)
    1893             :             {
    1894           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1895             :                          "Too many fields. File probably corrupted");
    1896           0 :                 XML_StopParser(oSchemaParser, XML_FALSE);
    1897           0 :                 bStopParsing = true;
    1898             :             }
    1899             :         }
    1900          15 :         CPLFree(pszFieldName);
    1901             :     }
    1902         291 :     else if (bInFeature && eFormat == GEORSS_ATOM && currentDepth == 2 &&
    1903          30 :              OGRGeoRSSLayerATOMTagHasSubElement(pszNoNSName))
    1904             :     {
    1905           9 :         CPLFree(pszTagWithSubTag);
    1906           9 :         pszTagWithSubTag = CPLStrdup(pszNoNSName);
    1907             : 
    1908           9 :         int count = 1;
    1909          12 :         while (CPLHashSetLookup(setOfFoundFields, pszTagWithSubTag) != nullptr)
    1910             :         {
    1911           3 :             count++;
    1912           3 :             CPLFree(pszTagWithSubTag);
    1913           3 :             pszTagWithSubTag =
    1914           3 :                 CPLStrdup(CPLSPrintf("%s%d", pszNoNSName, count));
    1915           3 :             if (pszTagWithSubTag[0] == 0)
    1916             :             {
    1917           0 :                 XML_StopParser(oSchemaParser, XML_FALSE);
    1918           0 :                 bStopParsing = true;
    1919           0 :                 break;
    1920             :             }
    1921             :         }
    1922           9 :         CPLHashSetInsert(setOfFoundFields, CPLStrdup(pszTagWithSubTag));
    1923             : 
    1924           9 :         bInTagWithSubTag = true;
    1925             :     }
    1926         252 :     else if (bInFeature && currentDepth == featureDepth + 1 &&
    1927         149 :              !IS_GEO_ELEMENT(pszName))
    1928             :     {
    1929         127 :         if (pszName != pszNoNSName && STARTS_WITH(pszName, "atom:"))
    1930           7 :             pszName = pszNoNSName;
    1931             : 
    1932         127 :         CPLFree(pszSubElementName);
    1933         127 :         pszSubElementName = CPLStrdup(pszName);
    1934             : 
    1935         127 :         int count = 1;
    1936         135 :         while (CPLHashSetLookup(setOfFoundFields, pszSubElementName) != nullptr)
    1937             :         {
    1938           8 :             count++;
    1939           8 :             CPLFree(pszSubElementName);
    1940           8 :             pszSubElementName = CPLStrdup(CPLSPrintf("%s%d", pszName, count));
    1941             :         }
    1942         127 :         CPLHashSetInsert(setOfFoundFields, CPLStrdup(pszSubElementName));
    1943             : 
    1944             :         // Create field definition for element.
    1945             :         char *pszCompatibleName =
    1946         127 :             OGRGeoRSS_GetOGRCompatibleTagName(pszSubElementName);
    1947         127 :         int iField = poFeatureDefn->GetFieldIndex(pszCompatibleName);
    1948         127 :         if (iField >= 0)
    1949             :         {
    1950          66 :             currentFieldDefn = poFeatureDefn->GetFieldDefn(iField);
    1951             :         }
    1952          61 :         else if (!((eFormat == GEORSS_RSS || eFormat == GEORSS_RSS_RDF) &&
    1953          40 :                    strcmp(pszNoNSName, "enclosure") == 0) &&
    1954          61 :                  !(eFormat == GEORSS_ATOM &&
    1955          21 :                    strcmp(pszNoNSName, "link") == 0) &&
    1956          55 :                  !(eFormat == GEORSS_ATOM &&
    1957          15 :                    strcmp(pszNoNSName, "category") == 0))
    1958             :         {
    1959             :             OGRFieldType eFieldType;
    1960          55 :             if (((eFormat == GEORSS_RSS || eFormat == GEORSS_RSS_RDF) &&
    1961          40 :                  strcmp(pszNoNSName, "pubDate") == 0) ||
    1962          49 :                 (eFormat == GEORSS_ATOM &&
    1963          15 :                  strcmp(pszNoNSName, "updated") == 0) ||
    1964          46 :                 (eFormat == GEORSS_ATOM &&
    1965          12 :                  strcmp(pszNoNSName, "published") == 0) ||
    1966          43 :                 strcmp(pszName, "dc:date") == 0)
    1967          12 :                 eFieldType = OFTDateTime;
    1968             :             else
    1969          43 :                 eFieldType = OFTInteger;
    1970             : 
    1971         110 :             OGRFieldDefn newFieldDefn(pszCompatibleName, eFieldType);
    1972          55 :             poFeatureDefn->AddFieldDefn(&newFieldDefn);
    1973          55 :             currentFieldDefn =
    1974          55 :                 poFeatureDefn->GetFieldDefn(poFeatureDefn->GetFieldCount() - 1);
    1975             : 
    1976          55 :             if (poFeatureDefn->GetFieldCount() == 100)
    1977             :             {
    1978           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1979             :                          "Too many fields. File probably corrupted");
    1980           0 :                 XML_StopParser(oSchemaParser, XML_FALSE);
    1981           0 :                 bStopParsing = true;
    1982             :             }
    1983             :         }
    1984             : 
    1985             :         // Create field definitions for attributes.
    1986         168 :         for (int i = 0; ppszAttr[i] != nullptr && ppszAttr[i + 1] != nullptr &&
    1987          41 :                         !bStopParsing;
    1988          41 :              i += 2)
    1989             :         {
    1990          41 :             char *pszAttrCompatibleName = OGRGeoRSS_GetOGRCompatibleTagName(
    1991          41 :                 CPLSPrintf("%s_%s", pszSubElementName, ppszAttr[i]));
    1992          41 :             iField = poFeatureDefn->GetFieldIndex(pszAttrCompatibleName);
    1993          41 :             OGRFieldDefn *currentAttrFieldDefn = nullptr;
    1994          41 :             if (iField >= 0)
    1995             :             {
    1996           0 :                 currentAttrFieldDefn = poFeatureDefn->GetFieldDefn(iField);
    1997             :             }
    1998             :             else
    1999             :             {
    2000          82 :                 OGRFieldDefn newFieldDefn(pszAttrCompatibleName, OFTInteger);
    2001          41 :                 poFeatureDefn->AddFieldDefn(&newFieldDefn);
    2002         123 :                 currentAttrFieldDefn = poFeatureDefn->GetFieldDefn(
    2003          41 :                     poFeatureDefn->GetFieldCount() - 1);
    2004             : 
    2005          41 :                 if (poFeatureDefn->GetFieldCount() == 100)
    2006             :                 {
    2007           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    2008             :                              "Too many fields. File probably corrupted");
    2009           0 :                     XML_StopParser(oSchemaParser, XML_FALSE);
    2010           0 :                     bStopParsing = true;
    2011             :                 }
    2012             :             }
    2013          41 :             if (currentAttrFieldDefn->GetType() == OFTInteger ||
    2014           0 :                 currentAttrFieldDefn->GetType() == OFTReal)
    2015             :             {
    2016          41 :                 const CPLValueType eType = CPLGetValueType(ppszAttr[i + 1]);
    2017          41 :                 if (eType == CPL_VALUE_REAL)
    2018             :                 {
    2019           0 :                     currentAttrFieldDefn->SetType(OFTReal);
    2020             :                 }
    2021          41 :                 else if (eType == CPL_VALUE_STRING)
    2022             :                 {
    2023          38 :                     currentAttrFieldDefn->SetType(OFTString);
    2024             :                 }
    2025             :             }
    2026          41 :             CPLFree(pszAttrCompatibleName);
    2027             :         }
    2028             : 
    2029         127 :         CPLFree(pszCompatibleName);
    2030             :     }
    2031         125 :     else if (strcmp(pszName, "georss:point") == 0 ||
    2032         122 :              strcmp(pszName, "georss:line") == 0 ||
    2033         120 :              strcmp(pszName, "geo:line") == 0 || IS_LAT_ELEMENT(pszName) ||
    2034         119 :              strcmp(pszName, "georss:polygon") == 0 ||
    2035         116 :              strcmp(pszName, "georss:box") == 0)
    2036             :     {
    2037          11 :         if (bSameSRS)
    2038             :         {
    2039          11 :             if (pszGMLSRSName != nullptr)
    2040           0 :                 bSameSRS = false;
    2041             :         }
    2042             :     }
    2043         114 :     else if (strcmp(pszName, "gml:Point") == 0 ||
    2044         111 :              strcmp(pszName, "gml:LineString") == 0 ||
    2045         108 :              strcmp(pszName, "gml:Polygon") == 0 ||
    2046         105 :              strcmp(pszName, "gml:MultiPoint") == 0 ||
    2047         105 :              strcmp(pszName, "gml:MultiLineString") == 0 ||
    2048         105 :              strcmp(pszName, "gml:MultiPolygon") == 0 ||
    2049         105 :              strcmp(pszName, "gml:Envelope") == 0)
    2050             :     {
    2051          10 :         if (bSameSRS)
    2052             :         {
    2053          10 :             bool bFoundSRS = false;
    2054          10 :             for (int i = 0; ppszAttr[i] != nullptr; i += 2)
    2055             :             {
    2056           1 :                 if (strcmp(ppszAttr[i], "srsName") == 0)
    2057             :                 {
    2058           1 :                     bFoundSRS = true;
    2059           1 :                     if (pszGMLSRSName != nullptr)
    2060             :                     {
    2061           0 :                         if (strcmp(pszGMLSRSName, ppszAttr[i + 1]) != 0)
    2062           0 :                             bSameSRS = false;
    2063             :                     }
    2064             :                     else
    2065           1 :                         pszGMLSRSName = CPLStrdup(ppszAttr[i + 1]);
    2066           1 :                     break;
    2067             :                 }
    2068             :             }
    2069          10 :             if (!bFoundSRS && pszGMLSRSName != nullptr)
    2070           0 :                 bSameSRS = false;
    2071             :         }
    2072             :     }
    2073             : 
    2074         304 :     if (!bInFeature || currentDepth >= featureDepth + 1)
    2075             :     {
    2076         276 :         int nDimension = 2;
    2077         367 :         for (int i = 0; ppszAttr[i] != nullptr && ppszAttr[i + 1] != nullptr;
    2078          91 :              i += 2)
    2079             :         {
    2080          91 :             if (strcmp(ppszAttr[i], "srsDimension") == 0)
    2081             :             {
    2082           0 :                 nDimension = atoi(ppszAttr[i + 1]);
    2083           0 :                 break;
    2084             :             }
    2085             :         }
    2086             : 
    2087         276 :         OGRwkbGeometryType eFoundGeomType = wkbUnknown;
    2088         276 :         if (strcmp(pszName, "georss:point") == 0 || IS_LAT_ELEMENT(pszName) ||
    2089         272 :             strcmp(pszName, "gml:Point") == 0)
    2090             :         {
    2091           7 :             eFoundGeomType = wkbPoint;
    2092             :         }
    2093         269 :         else if (strcmp(pszName, "gml:MultiPoint") == 0)
    2094             :         {
    2095           0 :             eFoundGeomType = wkbMultiPoint;
    2096             :         }
    2097         269 :         else if (strcmp(pszName, "georss:line") == 0 ||
    2098         267 :                  strcmp(pszName, "geo:line") == 0 ||
    2099         267 :                  strcmp(pszName, "gml:LineString") == 0)
    2100             :         {
    2101           5 :             eFoundGeomType = wkbLineString;
    2102             :         }
    2103         264 :         else if (strcmp(pszName, "gml:MultiLineString") == 0)
    2104             :         {
    2105           0 :             eFoundGeomType = wkbMultiLineString;
    2106             :         }
    2107         264 :         else if (strcmp(pszName, "georss:polygon") == 0 ||
    2108         261 :                  strcmp(pszName, "gml:Polygon") == 0 ||
    2109         258 :                  strcmp(pszName, "gml:Envelope") == 0 ||
    2110         257 :                  strcmp(pszName, "georss:box") == 0)
    2111             :         {
    2112           9 :             eFoundGeomType = wkbPolygon;
    2113             :         }
    2114         255 :         else if (strcmp(pszName, "gml:MultiPolygon") == 0)
    2115             :         {
    2116           0 :             eFoundGeomType = wkbMultiPolygon;
    2117             :         }
    2118             : 
    2119         276 :         if (eFoundGeomType != wkbUnknown)
    2120             :         {
    2121          21 :             if (!bFoundGeom)
    2122             :             {
    2123           9 :                 eGeomType = eFoundGeomType;
    2124           9 :                 bFoundGeom = true;
    2125             :             }
    2126          12 :             else if (wkbFlatten(eGeomType) != eFoundGeomType)
    2127             :             {
    2128          12 :                 eGeomType = wkbUnknown;
    2129             :             }
    2130             : 
    2131          21 :             if (nDimension == 3)
    2132           0 :                 eGeomType = wkbSetZ(eGeomType);
    2133             :         }
    2134             :     }
    2135             : 
    2136         304 :     currentDepth++;
    2137             : }
    2138             : 
    2139             : /************************************************************************/
    2140             : /*                   endElementLoadSchemaCbk()                          */
    2141             : /************************************************************************/
    2142             : 
    2143         304 : void OGRGeoRSSLayer::endElementLoadSchemaCbk(const char *pszName)
    2144             : {
    2145         304 :     if (bStopParsing)
    2146           0 :         return;
    2147             : 
    2148         304 :     nWithoutEventCounter = 0;
    2149             : 
    2150         304 :     currentDepth--;
    2151             : 
    2152         304 :     if (!bInFeature)
    2153          67 :         return;
    2154             : 
    2155         237 :     const char *pszNoNSName = pszName;
    2156         237 :     const char *pszColon = strchr(pszNoNSName, ':');
    2157         237 :     if (pszColon)
    2158          68 :         pszNoNSName = pszColon + 1;
    2159             : 
    2160         237 :     if ((eFormat == GEORSS_ATOM && currentDepth == 1 &&
    2161           3 :          strcmp(pszNoNSName, "entry") == 0) ||
    2162         234 :         ((eFormat == GEORSS_RSS || eFormat == GEORSS_RSS_RDF) &&
    2163         180 :          (currentDepth == 1 || currentDepth == 2) &&
    2164          25 :          strcmp(pszNoNSName, "item") == 0))
    2165             :     {
    2166          28 :         bInFeature = false;
    2167             :     }
    2168         239 :     else if (eFormat == GEORSS_ATOM && currentDepth == 2 &&
    2169          30 :              OGRGeoRSSLayerATOMTagHasSubElement(pszNoNSName))
    2170             :     {
    2171           9 :         bInTagWithSubTag = false;
    2172             :     }
    2173         200 :     else if (currentDepth == featureDepth + 1 && pszSubElementName)
    2174             :     {
    2175             :         // Patch field type.
    2176         127 :         if (pszSubElementValue && nSubElementValueLen && currentFieldDefn)
    2177             :         {
    2178         121 :             pszSubElementValue[nSubElementValueLen] = 0;
    2179         199 :             if (currentFieldDefn->GetType() == OFTInteger ||
    2180          78 :                 currentFieldDefn->GetType() == OFTReal)
    2181             :             {
    2182          43 :                 const CPLValueType eType = CPLGetValueType(pszSubElementValue);
    2183          43 :                 if (eType == CPL_VALUE_REAL)
    2184             :                 {
    2185           0 :                     currentFieldDefn->SetType(OFTReal);
    2186             :                 }
    2187          43 :                 else if (eType == CPL_VALUE_STRING)
    2188             :                 {
    2189          42 :                     currentFieldDefn->SetType(OFTString);
    2190             :                 }
    2191             :             }
    2192             :         }
    2193             : 
    2194         127 :         CPLFree(pszSubElementName);
    2195         127 :         pszSubElementName = nullptr;
    2196         127 :         CPLFree(pszSubElementValue);
    2197         127 :         pszSubElementValue = nullptr;
    2198         127 :         nSubElementValueLen = 0;
    2199         127 :         currentFieldDefn = nullptr;
    2200             :     }
    2201             : }
    2202             : 
    2203             : /************************************************************************/
    2204             : /*                   dataHandlerLoadSchemaCbk()                         */
    2205             : /************************************************************************/
    2206             : 
    2207         889 : void OGRGeoRSSLayer::dataHandlerLoadSchemaCbk(const char *data, int nLen)
    2208             : {
    2209         889 :     if (bStopParsing)
    2210           0 :         return;
    2211             : 
    2212         889 :     nDataHandlerCounter++;
    2213         889 :     if (nDataHandlerCounter >= PARSER_BUF_SIZE)
    2214             :     {
    2215           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2216             :                  "File probably corrupted (million laugh pattern)");
    2217           0 :         XML_StopParser(oSchemaParser, XML_FALSE);
    2218           0 :         bStopParsing = true;
    2219           0 :         return;
    2220             :     }
    2221             : 
    2222         889 :     nWithoutEventCounter = 0;
    2223             : 
    2224         889 :     if (pszSubElementName)
    2225             :     {
    2226         138 :         char *pszNewSubElementValue = static_cast<char *>(VSI_REALLOC_VERBOSE(
    2227             :             pszSubElementValue, nSubElementValueLen + nLen + 1));
    2228         138 :         if (pszNewSubElementValue == nullptr)
    2229             :         {
    2230           0 :             XML_StopParser(oSchemaParser, XML_FALSE);
    2231           0 :             bStopParsing = true;
    2232           0 :             return;
    2233             :         }
    2234         138 :         pszSubElementValue = pszNewSubElementValue;
    2235         138 :         memcpy(pszSubElementValue + nSubElementValueLen, data, nLen);
    2236         138 :         nSubElementValueLen += nLen;
    2237         138 :         if (nSubElementValueLen > 100000)
    2238             :         {
    2239           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2240             :                      "Too much data inside one element. "
    2241             :                      "File probably corrupted");
    2242           0 :             XML_StopParser(oSchemaParser, XML_FALSE);
    2243           0 :             bStopParsing = true;
    2244             :         }
    2245             :     }
    2246             : }
    2247             : #else
    2248             : void OGRGeoRSSLayer::LoadSchema()
    2249             : {
    2250             : }
    2251             : #endif
    2252             : 
    2253             : /************************************************************************/
    2254             : /*                           TestCapability()                           */
    2255             : /************************************************************************/
    2256             : 
    2257         108 : int OGRGeoRSSLayer::TestCapability(const char *pszCap)
    2258             : 
    2259             : {
    2260         108 :     if (EQUAL(pszCap, OLCFastFeatureCount))
    2261           0 :         return !bWriteMode && bHasReadSchema && m_poFilterGeom == nullptr &&
    2262           0 :                m_poAttrQuery == nullptr;
    2263             : 
    2264         108 :     else if (EQUAL(pszCap, OLCStringsAsUTF8))
    2265           0 :         return TRUE;
    2266             : 
    2267         108 :     else if (EQUAL(pszCap, OLCSequentialWrite))
    2268          16 :         return bWriteMode;
    2269          92 :     else if (EQUAL(pszCap, OLCCreateField))
    2270          16 :         return bWriteMode;
    2271          76 :     else if (EQUAL(pszCap, OLCZGeometries))
    2272           0 :         return TRUE;
    2273             :     else
    2274          76 :         return FALSE;
    2275             : }
    2276             : 
    2277             : /************************************************************************/
    2278             : /*                          GetFeatureCount()                           */
    2279             : /************************************************************************/
    2280             : 
    2281           0 : GIntBig OGRGeoRSSLayer::GetFeatureCount(int bForce)
    2282             : 
    2283             : {
    2284           0 :     if (bWriteMode)
    2285             :     {
    2286           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    2287             :                  "Cannot read features when writing a GeoRSS file");
    2288           0 :         return 0;
    2289             :     }
    2290             : 
    2291           0 :     if (!bHasReadSchema)
    2292           0 :         LoadSchema();
    2293             : 
    2294           0 :     if (m_poFilterGeom != nullptr || m_poAttrQuery != nullptr)
    2295           0 :         return OGRLayer::GetFeatureCount(bForce);
    2296             : 
    2297           0 :     return nTotalFeatureCount;
    2298             : }
    2299             : 
    2300             : /************************************************************************/
    2301             : /*                             GetDataset()                             */
    2302             : /************************************************************************/
    2303             : 
    2304          19 : GDALDataset *OGRGeoRSSLayer::GetDataset()
    2305             : {
    2306          19 :     return poDS;
    2307             : }

Generated by: LCOV version 1.14