LCOV - code coverage report
Current view: directory - ogr/ogrsf_frmts/georss - ogrgeorssdatasource.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 213 176 82.6 %
Date: 2010-01-09 Functions: 12 11 91.7 %

       1                 : /******************************************************************************
       2                 :  * $Id: ogrgeorssdatasource.cpp 17848 2009-10-17 12:59:16Z rouault $
       3                 :  *
       4                 :  * Project:  GeoRSS Translator
       5                 :  * Purpose:  Implements OGRGeoRSSDataSource class
       6                 :  * Author:   Even Rouault, even dot rouault at mines dash paris dot org
       7                 :  *
       8                 :  ******************************************************************************
       9                 :  * Copyright (c) 2008, Even Rouault
      10                 :  *
      11                 :  * Permission is hereby granted, free of charge, to any person obtaining a
      12                 :  * copy of this software and associated documentation files (the "Software"),
      13                 :  * to deal in the Software without restriction, including without limitation
      14                 :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      15                 :  * and/or sell copies of the Software, and to permit persons to whom the
      16                 :  * Software is furnished to do so, subject to the following conditions:
      17                 :  *
      18                 :  * The above copyright notice and this permission notice shall be included
      19                 :  * in all copies or substantial portions of the Software.
      20                 :  *
      21                 :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      22                 :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      23                 :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      24                 :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      25                 :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      26                 :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      27                 :  * DEALINGS IN THE SOFTWARE.
      28                 :  ****************************************************************************/
      29                 : 
      30                 : #include "ogr_georss.h"
      31                 : #include "cpl_conv.h"
      32                 : #include "cpl_string.h"
      33                 : #include "cpl_csv.h"
      34                 : 
      35                 : CPL_CVSID("$Id: ogrgeorssdatasource.cpp 17848 2009-10-17 12:59:16Z rouault $");
      36                 : 
      37                 : /************************************************************************/
      38                 : /*                          OGRGeoRSSDataSource()                          */
      39                 : /************************************************************************/
      40                 : 
      41              27 : OGRGeoRSSDataSource::OGRGeoRSSDataSource()
      42                 : 
      43                 : {
      44              27 :     papoLayers = NULL;
      45              27 :     nLayers = 0;
      46                 : 
      47              27 :     fpOutput = NULL;
      48                 : 
      49              27 :     pszName = NULL;
      50                 : 
      51              27 :     eFormat = GEORSS_RSS;
      52              27 :     eGeomDialect = GEORSS_SIMPLE;
      53              27 :     bUseExtensions = FALSE;
      54              27 :     bWriteHeaderAndFooter = TRUE;
      55              27 : }
      56                 : 
      57                 : /************************************************************************/
      58                 : /*                         ~OGRGeoRSSDataSource()                          */
      59                 : /************************************************************************/
      60                 : 
      61              54 : OGRGeoRSSDataSource::~OGRGeoRSSDataSource()
      62                 : 
      63                 : {
      64              27 :     if ( fpOutput != NULL )
      65                 :     {
      66               7 :         if (bWriteHeaderAndFooter)
      67                 :         {
      68               7 :             if (eFormat == GEORSS_RSS)
      69                 :             {
      70               6 :                 VSIFPrintf(fpOutput, "  </channel>\n");
      71               6 :                 VSIFPrintf(fpOutput, "</rss>\n");
      72                 :             }
      73                 :             else
      74                 :             {
      75               1 :                 VSIFPrintf(fpOutput, "</feed>\n");
      76                 :             }
      77                 :         }
      78               7 :         if ( fpOutput != stdout )
      79               7 :             VSIFClose( fpOutput);
      80                 :     }
      81                 : 
      82              46 :     for( int i = 0; i < nLayers; i++ )
      83              19 :         delete papoLayers[i];
      84              27 :     CPLFree( papoLayers );
      85              27 :     CPLFree( pszName );
      86              54 : }
      87                 : 
      88                 : /************************************************************************/
      89                 : /*                           TestCapability()                           */
      90                 : /************************************************************************/
      91                 : 
      92               0 : int OGRGeoRSSDataSource::TestCapability( const char * pszCap )
      93                 : 
      94                 : {
      95               0 :     if( EQUAL(pszCap,ODsCCreateLayer) )
      96               0 :         return TRUE;
      97               0 :     else if( EQUAL(pszCap,ODsCDeleteLayer) )
      98               0 :         return FALSE;
      99                 :     else
     100               0 :         return FALSE;
     101                 : }
     102                 : 
     103                 : /************************************************************************/
     104                 : /*                              GetLayer()                              */
     105                 : /************************************************************************/
     106                 : 
     107              12 : OGRLayer *OGRGeoRSSDataSource::GetLayer( int iLayer )
     108                 : 
     109                 : {
     110              12 :     if( iLayer < 0 || iLayer >= nLayers )
     111               0 :         return NULL;
     112                 :     else
     113              12 :         return papoLayers[iLayer];
     114                 : }
     115                 : 
     116                 : /************************************************************************/
     117                 : /*                            CreateLayer()                             */
     118                 : /************************************************************************/
     119                 : 
     120               7 : OGRLayer * OGRGeoRSSDataSource::CreateLayer( const char * pszLayerName,
     121                 :                                              OGRSpatialReference *poSRS,
     122                 :                                              OGRwkbGeometryType eType,
     123                 :                                              char ** papszOptions )
     124                 : 
     125                 : {
     126               7 :     if (fpOutput == NULL)
     127               0 :         return NULL;
     128                 : 
     129               7 :     if (poSRS != NULL && eGeomDialect != GEORSS_GML)
     130                 :     {
     131               1 :         OGRSpatialReference oSRS;
     132               1 :         oSRS.SetWellKnownGeogCS("WGS84");
     133               1 :         if (poSRS->IsSame(&oSRS) == FALSE)
     134                 :         {
     135                 :             CPLError(CE_Failure, CPLE_NotSupported,
     136               1 :                      "For a non GML dialect, only WGS84 SRS is supported");
     137               1 :             return NULL;
     138               0 :         }
     139                 :     }
     140                 : 
     141               6 :     nLayers++;
     142               6 :     papoLayers = (OGRGeoRSSLayer **) CPLRealloc(papoLayers, nLayers * sizeof(OGRGeoRSSLayer*));
     143               6 :     papoLayers[nLayers-1] = new OGRGeoRSSLayer( pszName, pszLayerName, this, poSRS, TRUE );
     144                 :     
     145               6 :     return papoLayers[nLayers-1];
     146                 : }
     147                 : 
     148                 : #ifdef HAVE_EXPAT
     149                 : /************************************************************************/
     150                 : /*                startElementValidateCbk()                             */
     151                 : /************************************************************************/
     152                 : 
     153             307 : void OGRGeoRSSDataSource::startElementValidateCbk(const char *pszName, const char **ppszAttr)
     154                 : {
     155             307 :     if (validity == GEORSS_VALIDITY_UNKNOWN)
     156                 :     {
     157              14 :         if (strcmp(pszName, "rss") == 0)
     158                 :         {
     159              11 :             validity = GEORSS_VALIDITY_VALID;
     160              11 :             eFormat = GEORSS_RSS;
     161                 :         }
     162               3 :         else if (strcmp(pszName, "feed") == 0)
     163                 :         {
     164               3 :             validity = GEORSS_VALIDITY_VALID;
     165               3 :             eFormat = GEORSS_ATOM;
     166                 :         }
     167                 :         else
     168                 :         {
     169               0 :             validity = GEORSS_VALIDITY_INVALID;
     170                 :         }
     171                 :     }
     172             307 : }
     173                 : 
     174                 : 
     175                 : /************************************************************************/
     176                 : /*                      dataHandlerValidateCbk()                        */
     177                 : /************************************************************************/
     178                 : 
     179             889 : void OGRGeoRSSDataSource::dataHandlerValidateCbk(const char *data, int nLen)
     180                 : {
     181             889 :     nDataHandlerCounter ++;
     182             889 :     if (nDataHandlerCounter >= BUFSIZ)
     183                 :     {
     184               0 :         CPLError(CE_Failure, CPLE_AppDefined, "File probably corrupted (million laugh pattern)");
     185               0 :         XML_StopParser(oCurrentParser, XML_FALSE);
     186                 :     }
     187             889 : }
     188                 : 
     189                 : 
     190             307 : static void XMLCALL startElementValidateCbk(void *pUserData, const char *pszName, const char **ppszAttr)
     191                 : {
     192             307 :     OGRGeoRSSDataSource* poDS = (OGRGeoRSSDataSource*) pUserData;
     193             307 :     poDS->startElementValidateCbk(pszName, ppszAttr);
     194             307 : }
     195                 : 
     196             889 : static void XMLCALL dataHandlerValidateCbk(void *pUserData, const char *data, int nLen)
     197                 : {
     198             889 :     OGRGeoRSSDataSource* poDS = (OGRGeoRSSDataSource*) pUserData;
     199             889 :     poDS->dataHandlerValidateCbk(data, nLen);
     200             889 : }
     201                 : #endif
     202                 : 
     203                 : /************************************************************************/
     204                 : /*                                Open()                                */
     205                 : /************************************************************************/
     206                 : 
     207              20 : int OGRGeoRSSDataSource::Open( const char * pszFilename, int bUpdateIn)
     208                 : 
     209                 : {
     210              20 :     if (bUpdateIn)
     211                 :     {
     212                 :         CPLError(CE_Failure, CPLE_NotSupported,
     213               0 :                     "OGR/GeoRSS driver does not support opening a file in update mode");
     214               0 :         return FALSE;
     215                 :     }
     216                 : #ifdef HAVE_EXPAT
     217              20 :     pszName = CPLStrdup( pszFilename );
     218                 : 
     219                 : /* -------------------------------------------------------------------- */
     220                 : /*      Determine what sort of object this is.                          */
     221                 : /* -------------------------------------------------------------------- */
     222                 :     VSIStatBufL sStatBuf;
     223                 : 
     224              20 :     if( VSIStatL( pszFilename, &sStatBuf ) != 0 )
     225               2 :         return FALSE;
     226                 :     
     227              18 :     if( VSI_ISDIR(sStatBuf.st_mode) )
     228               0 :         return FALSE;
     229                 : 
     230              18 :     FILE* fp = VSIFOpenL(pszFilename, "r");
     231              18 :     if (fp == NULL)
     232               0 :         return FALSE;
     233                 :     
     234              18 :     validity = GEORSS_VALIDITY_UNKNOWN;
     235                 :     
     236              18 :     XML_Parser oParser = OGRCreateExpatXMLParser();
     237              18 :     XML_SetUserData(oParser, this);
     238              18 :     XML_SetElementHandler(oParser, ::startElementValidateCbk, NULL);
     239              18 :     XML_SetCharacterDataHandler(oParser, ::dataHandlerValidateCbk);
     240              18 :     oCurrentParser = oParser;
     241                 :     
     242                 :     char aBuf[BUFSIZ];
     243                 :     int nDone;
     244                 :     unsigned int nLen;
     245              18 :     int nCount = 0;
     246                 :     
     247                 :     /* Begin to parse the file and look for the <rss> or <feed> element */
     248                 :     /* It *MUST* be the first element of an XML file */
     249                 :     /* So once we have read the first element, we know if we can */
     250                 :     /* handle the file or not with that driver */
     251               0 :     do
     252                 :     {
     253              18 :         nDataHandlerCounter = 0;
     254              18 :         nLen = (unsigned int) VSIFReadL( aBuf, 1, sizeof(aBuf), fp );
     255              18 :         nDone = VSIFEofL(fp);
     256              18 :         if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR)
     257                 :         {
     258               5 :             if (nLen <= BUFSIZ-1)
     259               3 :                 aBuf[nLen] = 0;
     260                 :             else
     261               2 :                 aBuf[BUFSIZ-1] = 0;
     262               5 :             if (strstr(aBuf, "<?xml") && (strstr(aBuf, "<rss") || strstr(aBuf, "<feed")))
     263                 :             {
     264                 :                 CPLError(CE_Failure, CPLE_AppDefined,
     265                 :                         "XML parsing of GeoRSS file failed : %s at line %d, column %d",
     266                 :                         XML_ErrorString(XML_GetErrorCode(oParser)),
     267                 :                         (int)XML_GetCurrentLineNumber(oParser),
     268               1 :                         (int)XML_GetCurrentColumnNumber(oParser));
     269                 :             }
     270               5 :             validity = GEORSS_VALIDITY_INVALID;
     271               5 :             break;
     272                 :         }
     273              13 :         if (validity == GEORSS_VALIDITY_INVALID)
     274                 :         {
     275               0 :             break;
     276                 :         }
     277              13 :         else if (validity == GEORSS_VALIDITY_VALID)
     278                 :         {
     279              13 :             break;
     280                 :         }
     281                 :         else
     282                 :         {
     283                 :             /* After reading 50 * BUFSIZ bytes, and not finding whether the file */
     284                 :             /* is GeoRSS or not, we give up and fail silently */
     285               0 :             nCount ++;
     286               0 :             if (nCount == 50)
     287               0 :                 break;
     288                 :         }
     289                 :     } while (!nDone && nLen > 0 );
     290                 :     
     291              18 :     XML_ParserFree(oParser);
     292                 :     
     293              18 :     VSIFCloseL(fp);
     294                 :     
     295              18 :     if (validity == GEORSS_VALIDITY_VALID)
     296                 :     {
     297              13 :         CPLDebug("GeoRSS", "%s seems to be a GeoRSS file.", pszFilename);
     298                 : 
     299              13 :         nLayers = 1;
     300              13 :         papoLayers = (OGRGeoRSSLayer **) CPLRealloc(papoLayers, nLayers * sizeof(OGRGeoRSSLayer*));
     301              13 :         papoLayers[0] = new OGRGeoRSSLayer( pszName, "georss", this, NULL, FALSE );
     302                 :     }
     303                 : 
     304              18 :     return (validity == GEORSS_VALIDITY_VALID);
     305                 : #else
     306                 :     char aBuf[256];
     307                 :     FILE* fp = VSIFOpenL(pszFilename, "r");
     308                 :     if (fp)
     309                 :     {
     310                 :         unsigned int nLen = (unsigned int)VSIFReadL( aBuf, 1, 255, fp );
     311                 :         aBuf[nLen] = 0;
     312                 :         if (strstr(aBuf, "<?xml") && (strstr(aBuf, "<rss") || strstr(aBuf, "<feed")))
     313                 :         {
     314                 :             CPLError(CE_Failure, CPLE_NotSupported,
     315                 :                     "OGR/GeoRSS driver has not been built with read support. Expat library required");
     316                 :         }
     317                 :         VSIFCloseL(fp);
     318                 :     }
     319                 :     return FALSE;
     320                 : #endif
     321                 : }
     322                 : 
     323                 : 
     324                 : /************************************************************************/
     325                 : /*                               Create()                               */
     326                 : /************************************************************************/
     327                 : 
     328               7 : int OGRGeoRSSDataSource::Create( const char *pszFilename, 
     329                 :                                  char **papszOptions )
     330                 : {
     331               7 :     if( fpOutput != NULL)
     332                 :     {
     333                 :         CPLAssert( FALSE );
     334               0 :         return FALSE;
     335                 :     }
     336                 : 
     337                 : /* -------------------------------------------------------------------- */
     338                 : /*     Do not override exiting file.                                    */
     339                 : /* -------------------------------------------------------------------- */
     340                 :     VSIStatBufL sStatBuf;
     341                 : 
     342               7 :     if( VSIStatL( pszFilename, &sStatBuf ) == 0 )
     343                 :     {
     344                 :         CPLError(CE_Failure, CPLE_NotSupported,
     345                 :                  "You have to delete %s before being able to create it with the GeoRSS driver",
     346               0 :                  pszFilename);
     347               0 :         return FALSE;
     348                 :     }
     349                 :     
     350                 : /* -------------------------------------------------------------------- */
     351                 : /*      Create the output file.                                         */
     352                 : /* -------------------------------------------------------------------- */
     353               7 :     pszName = CPLStrdup( pszFilename );
     354                 : 
     355               7 :     if( EQUAL(pszFilename,"stdout") )
     356               0 :         fpOutput = stdout;
     357                 :     else
     358               7 :         fpOutput = VSIFOpen( pszFilename, "w" );
     359               7 :     if( fpOutput == NULL )
     360                 :     {
     361                 :         CPLError( CE_Failure, CPLE_OpenFailed, 
     362                 :                   "Failed to create GeoRSS file %s.", 
     363               0 :                   pszFilename );
     364               0 :         return FALSE;
     365                 :     }
     366                 : 
     367               7 :     const char* pszFormat = CSLFetchNameValue(papszOptions, "FORMAT");
     368               7 :     if (pszFormat)
     369                 :     {
     370               1 :         if (EQUAL(pszFormat, "RSS"))
     371               0 :             eFormat = GEORSS_RSS;
     372               1 :         else if (EQUAL(pszFormat, "ATOM"))
     373               1 :             eFormat = GEORSS_ATOM;
     374                 :         else
     375                 :         {
     376                 :             CPLError(CE_Warning, CPLE_NotSupported,
     377               0 :                      "Unsupported value for %s : %s", "FORMAT", pszFormat);
     378                 :         }
     379                 :     }
     380                 : 
     381               7 :     const char* pszGeomDialect = CSLFetchNameValue(papszOptions, "GEOM_DIALECT");
     382               7 :     if (pszGeomDialect)
     383                 :     {
     384               3 :         if (EQUAL(pszGeomDialect, "GML"))
     385               2 :             eGeomDialect = GEORSS_GML;
     386               1 :         else if (EQUAL(pszGeomDialect, "SIMPLE"))
     387               0 :             eGeomDialect = GEORSS_SIMPLE;
     388               1 :         else if (EQUAL(pszGeomDialect, "W3C_GEO"))
     389               1 :             eGeomDialect = GEORSS_W3C_GEO;
     390                 :         else
     391                 :         {
     392                 :             CPLError(CE_Warning, CPLE_NotSupported,
     393               0 :                      "Unsupported value for %s : %s", "GEOM_DIALECT", pszGeomDialect);
     394                 :         }
     395                 :     }
     396                 : 
     397               7 :     const char* pszWriteHeaderAndFooter = CSLFetchNameValue(papszOptions, "WRITE_HEADER_AND_FOOTER");
     398               7 :     if (pszWriteHeaderAndFooter && CSLTestBoolean(pszWriteHeaderAndFooter) == FALSE)
     399                 :     {
     400               0 :         bWriteHeaderAndFooter = FALSE;
     401               0 :         return TRUE;
     402                 :     }
     403                 : 
     404               7 :     const char* pszHeader = NULL;
     405               7 :     const char* pszTitle = NULL;
     406               7 :     const char* pszDescription = NULL;
     407               7 :     const char* pszLink = NULL;
     408               7 :     const char* pszUpdated = NULL;
     409               7 :     const char* pszAuthorName = NULL;
     410               7 :     const char* pszId = NULL;
     411                 : 
     412               7 :     pszHeader = CSLFetchNameValue(papszOptions, "HEADER");
     413                 : 
     414              13 :     if (eFormat == GEORSS_RSS && pszHeader == NULL)
     415                 :     {
     416               6 :         pszTitle = CSLFetchNameValue(papszOptions, "TITLE");
     417               6 :         if (pszTitle == NULL)
     418               6 :             pszTitle = "title";
     419                 : 
     420               6 :         pszDescription = CSLFetchNameValue(papszOptions, "DESCRIPTION");
     421               6 :         if (pszDescription == NULL)
     422               6 :             pszDescription = "channel_description";
     423                 : 
     424               6 :         pszLink = CSLFetchNameValue(papszOptions, "LINK");
     425               6 :         if (pszLink == NULL)
     426               6 :             pszLink = "channel_link";
     427                 : 
     428                 :     }
     429               1 :     else if (eFormat == GEORSS_ATOM && pszHeader == NULL)
     430                 :     {
     431               1 :         pszTitle = CSLFetchNameValue(papszOptions, "TITLE");
     432               1 :         if (pszTitle == NULL)
     433               1 :             pszTitle = "title";
     434                 : 
     435               1 :         pszUpdated = CSLFetchNameValue(papszOptions, "UPDATED");
     436               1 :         if (pszUpdated == NULL)
     437               1 :             pszUpdated = "2009-01-01T00:00:00Z";
     438                 : 
     439               1 :         pszAuthorName = CSLFetchNameValue(papszOptions, "AUTHOR_NAME");
     440               1 :         if (pszAuthorName == NULL)
     441               1 :             pszAuthorName = "author";
     442                 : 
     443               1 :         pszId = CSLFetchNameValue(papszOptions, "ID");
     444               1 :         if (pszId == NULL)
     445               1 :             pszId = "id";
     446                 :     }
     447                 : 
     448               7 :     const char* pszUseExtensions = CSLFetchNameValue( papszOptions, "USE_EXTENSIONS");
     449               7 :     bUseExtensions =  (pszUseExtensions && CSLTestBoolean(pszUseExtensions));
     450                 : 
     451                 : /* -------------------------------------------------------------------- */
     452                 : /*     Output header of GeoRSS file.                                       */
     453                 : /* -------------------------------------------------------------------- */
     454               7 :     VSIFPrintf(fpOutput, "<?xml version=\"1.0\"?>\n");
     455               7 :     if (eFormat == GEORSS_RSS)
     456                 :     {
     457               6 :         VSIFPrintf(fpOutput, "<rss version=\"2.0\" ");
     458               6 :         if (eGeomDialect == GEORSS_GML)
     459               2 :             VSIFPrintf(fpOutput, "xmlns:georss=\"http://www.georss.org/georss\" xmlns:gml=\"http://www.opengis.net/gml\"");
     460               4 :         else if (eGeomDialect == GEORSS_SIMPLE)
     461               3 :             VSIFPrintf(fpOutput, "xmlns:georss=\"http://www.georss.org/georss\"");
     462                 :         else
     463               1 :             VSIFPrintf(fpOutput, "xmlns:geo=\"http://www.w3.org/2003/01/geo/wgs84_pos#\"");
     464               6 :         VSIFPrintf(fpOutput, ">\n");
     465               6 :         VSIFPrintf(fpOutput, "  <channel>\n");
     466               6 :         if (pszHeader)
     467                 :         {
     468               0 :             VSIFPrintf(fpOutput, "%s", pszHeader);
     469                 :         }
     470                 :         else
     471                 :         {
     472               6 :             VSIFPrintf(fpOutput, "    <title>%s</title>\n", pszTitle);
     473               6 :             VSIFPrintf(fpOutput, "    <description>%s</description>\n", pszDescription);
     474               6 :             VSIFPrintf(fpOutput, "    <link>%s</link>\n", pszLink);
     475                 :         }
     476                 :     }
     477                 :     else
     478                 :     {
     479               1 :         VSIFPrintf(fpOutput, "<feed xmlns=\"http://www.w3.org/2005/Atom\" ");
     480               1 :         if (eGeomDialect == GEORSS_GML)
     481               0 :             VSIFPrintf(fpOutput, "xmlns:gml=\"http://www.opengis.net/gml\"");
     482               1 :         else if (eGeomDialect == GEORSS_SIMPLE)
     483               1 :             VSIFPrintf(fpOutput, "xmlns:georss=\"http://www.georss.org/georss\"");
     484                 :         else
     485               0 :             VSIFPrintf(fpOutput, "xmlns:geo=\"http://www.w3.org/2003/01/geo/wgs84_pos#\"");
     486               1 :         VSIFPrintf(fpOutput, ">\n");
     487               1 :         if (pszHeader)
     488                 :         {
     489               0 :             VSIFPrintf(fpOutput, "%s", pszHeader);
     490                 :         }
     491                 :         else
     492                 :         {
     493               1 :             VSIFPrintf(fpOutput, "  <title>%s</title>\n", pszTitle);
     494               1 :             VSIFPrintf(fpOutput, "  <updated>%s</updated>\n", pszUpdated);
     495               1 :             VSIFPrintf(fpOutput, "  <author><name>%s</name></author>\n", pszAuthorName);
     496               1 :             VSIFPrintf(fpOutput, "  <id>%s</id>\n", pszId);
     497                 :         }
     498                 :     }
     499                 : 
     500               7 :     return TRUE;
     501                 : }

Generated by: LCOV version 1.7