LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/xlsx - ogrxlsxdatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1162 1338 86.8 %
Date: 2026-10-10 13:16:24 Functions: 77 82 93.9 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  XLSX Translator
       4             :  * Purpose:  Implements OGRXLSXDataSource class
       5             :  * Author:   Even Rouault, even dot rouault at spatialys.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2012-2014, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "ogr_xlsx.h"
      14             : #include "ogr_p.h"
      15             : #include "cpl_conv.h"
      16             : #include "cpl_time.h"
      17             : #include "cpl_vsi_error.h"
      18             : 
      19             : #include <algorithm>
      20             : 
      21             : namespace OGRXLSX
      22             : {
      23             : 
      24             : constexpr int PARSER_BUF_SIZE = 8192;
      25             : 
      26             : constexpr int NUMBER_OF_DAYS_BETWEEN_1900_AND_1970 = 25569;
      27             : constexpr int NUMBER_OF_SECONDS_PER_DAY = 86400;
      28             : 
      29             : /************************************************************************/
      30             : /*                            OGRXLSXLayer()                            */
      31             : /************************************************************************/
      32             : 
      33         167 : OGRXLSXLayer::OGRXLSXLayer(OGRXLSXDataSource *poDSIn, const char *pszFilename,
      34         167 :                            const char *pszName, int bUpdatedIn)
      35         167 :     : OGRMemLayer(pszName, nullptr, wkbNone), bInit(CPL_TO_BOOL(bUpdatedIn)),
      36         167 :       poDS(poDSIn), osFilename(pszFilename), bUpdated(CPL_TO_BOOL(bUpdatedIn)),
      37         334 :       bHasHeaderLine(false)
      38             : {
      39         167 :     SetAdvertizeUTF8(true);
      40         167 : }
      41             : 
      42             : /************************************************************************/
      43             : /*                                Init()                                */
      44             : /************************************************************************/
      45             : 
      46        8311 : void OGRXLSXLayer::Init()
      47             : {
      48        8311 :     if (!bInit)
      49             :     {
      50          84 :         bInit = true;
      51          84 :         CPLDebug("XLSX", "Init(%s)", GetName());
      52          84 :         poDS->BuildLayer(this);
      53             :     }
      54        8311 : }
      55             : 
      56             : /************************************************************************/
      57             : /*                              Updated()                               */
      58             : /************************************************************************/
      59             : 
      60        1574 : void OGRXLSXLayer::SetUpdated(bool bUpdatedIn)
      61             : {
      62        1574 :     if (bUpdatedIn && !bUpdated && poDS->GetUpdatable())
      63             :     {
      64          12 :         bUpdated = true;
      65          12 :         poDS->SetUpdated();
      66             :     }
      67        1562 :     else if (bUpdated && !bUpdatedIn)
      68             :     {
      69          30 :         bUpdated = false;
      70             :     }
      71        1574 : }
      72             : 
      73             : /************************************************************************/
      74             : /*                             SyncToDisk()                             */
      75             : /************************************************************************/
      76             : 
      77           0 : OGRErr OGRXLSXLayer::SyncToDisk()
      78             : {
      79           0 :     poDS->FlushCache(false);
      80           0 :     return OGRERR_NONE;
      81             : }
      82             : 
      83             : /************************************************************************/
      84             : /*                      TranslateFIDFromMemLayer()                      */
      85             : /************************************************************************/
      86             : 
      87             : // Translate a FID from MEM convention (0-based) to XLSX convention
      88        1679 : GIntBig OGRXLSXLayer::TranslateFIDFromMemLayer(GIntBig nFID) const
      89             : {
      90        1679 :     return nFID + (1 + (bHasHeaderLine ? 1 : 0));
      91             : }
      92             : 
      93             : /************************************************************************/
      94             : /*                       TranslateFIDToMemLayer()                       */
      95             : /************************************************************************/
      96             : 
      97             : // Translate a FID from XLSX convention to MEM convention (0-based)
      98          77 : GIntBig OGRXLSXLayer::TranslateFIDToMemLayer(GIntBig nFID) const
      99             : {
     100          77 :     if (nFID > 0)
     101          59 :         return nFID - (1 + (bHasHeaderLine ? 1 : 0));
     102          18 :     return OGRNullFID;
     103             : }
     104             : 
     105             : /************************************************************************/
     106             : /*                           GetNextFeature()                           */
     107             : /************************************************************************/
     108             : 
     109         866 : OGRFeature *OGRXLSXLayer::GetNextFeature()
     110             : {
     111         866 :     Init();
     112             : 
     113         866 :     OGRFeature *poFeature = OGRMemLayer::GetNextFeature();
     114         866 :     if (poFeature)
     115         701 :         poFeature->SetFID(TranslateFIDFromMemLayer(poFeature->GetFID()));
     116         866 :     return poFeature;
     117             : }
     118             : 
     119             : /************************************************************************/
     120             : /*                            CreateField()                             */
     121             : /************************************************************************/
     122             : 
     123         438 : OGRErr OGRXLSXLayer::CreateField(const OGRFieldDefn *poField, int bApproxOK)
     124             : {
     125         438 :     Init();
     126             : 
     127         438 :     if (GetLayerDefn()->GetFieldCount() >= 2000)
     128             :     {
     129           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     130             :                  "Maximum number of fields supported is 2000");
     131           0 :         return OGRERR_FAILURE;
     132             :     }
     133         438 :     SetUpdated();
     134         438 :     return OGRMemLayer::CreateField(poField, bApproxOK);
     135             : }
     136             : 
     137             : /************************************************************************/
     138             : /*                             GetFeature()                             */
     139             : /************************************************************************/
     140             : 
     141          50 : OGRFeature *OGRXLSXLayer::GetFeature(GIntBig nFeatureId)
     142             : {
     143          50 :     Init();
     144             : 
     145             :     OGRFeature *poFeature =
     146          50 :         OGRMemLayer::GetFeature(TranslateFIDToMemLayer(nFeatureId));
     147          50 :     if (poFeature)
     148          22 :         poFeature->SetFID(nFeatureId);
     149          50 :     return poFeature;
     150             : }
     151             : 
     152             : /************************************************************************/
     153             : /*                            ISetFeature()                             */
     154             : /************************************************************************/
     155             : 
     156          14 : OGRErr OGRXLSXLayer::ISetFeature(OGRFeature *poFeature)
     157             : {
     158          14 :     Init();
     159             : 
     160          14 :     const GIntBig nFIDOrigin = poFeature->GetFID();
     161          14 :     if (nFIDOrigin > 0)
     162             :     {
     163           5 :         const GIntBig nFIDMemLayer = TranslateFIDToMemLayer(nFIDOrigin);
     164           5 :         if (!GetFeatureRef(nFIDMemLayer))
     165           0 :             return OGRERR_NON_EXISTING_FEATURE;
     166           5 :         poFeature->SetFID(nFIDMemLayer);
     167             :     }
     168             :     else
     169             :     {
     170           9 :         return OGRERR_NON_EXISTING_FEATURE;
     171             :     }
     172           5 :     SetUpdated();
     173           5 :     OGRErr eErr = OGRMemLayer::ISetFeature(poFeature);
     174           5 :     poFeature->SetFID(nFIDOrigin);
     175           5 :     return eErr;
     176             : }
     177             : 
     178             : /************************************************************************/
     179             : /*                         ISetFeatureUniqPtr()                         */
     180             : /************************************************************************/
     181             : 
     182           0 : OGRErr OGRXLSXLayer::ISetFeatureUniqPtr(std::unique_ptr<OGRFeature> poFeature)
     183             : {
     184           0 :     const GIntBig nFIDOrigin = poFeature->GetFID();
     185           0 :     if (nFIDOrigin > 0)
     186             :     {
     187           0 :         const GIntBig nFIDMemLayer = TranslateFIDToMemLayer(nFIDOrigin);
     188           0 :         if (!GetFeatureRef(nFIDMemLayer))
     189           0 :             return OGRERR_NON_EXISTING_FEATURE;
     190           0 :         poFeature->SetFID(nFIDMemLayer);
     191             :     }
     192             :     else
     193             :     {
     194           0 :         return OGRERR_NON_EXISTING_FEATURE;
     195             :     }
     196           0 :     SetUpdated();
     197           0 :     return OGRMemLayer::ISetFeatureUniqPtr(std::move(poFeature));
     198             : }
     199             : 
     200             : /************************************************************************/
     201             : /*                           IUpdateFeature()                           */
     202             : /************************************************************************/
     203             : 
     204           2 : OGRErr OGRXLSXLayer::IUpdateFeature(OGRFeature *poFeature,
     205             :                                     int nUpdatedFieldsCount,
     206             :                                     const int *panUpdatedFieldsIdx,
     207             :                                     int nUpdatedGeomFieldsCount,
     208             :                                     const int *panUpdatedGeomFieldsIdx,
     209             :                                     bool bUpdateStyleString)
     210             : {
     211           2 :     Init();
     212             : 
     213           2 :     const GIntBig nFIDOrigin = poFeature->GetFID();
     214           2 :     if (nFIDOrigin != OGRNullFID)
     215           2 :         poFeature->SetFID(TranslateFIDToMemLayer(nFIDOrigin));
     216           2 :     SetUpdated();
     217           2 :     OGRErr eErr = OGRMemLayer::IUpdateFeature(
     218             :         poFeature, nUpdatedFieldsCount, panUpdatedFieldsIdx,
     219             :         nUpdatedGeomFieldsCount, panUpdatedGeomFieldsIdx, bUpdateStyleString);
     220           2 :     poFeature->SetFID(nFIDOrigin);
     221           2 :     return eErr;
     222             : }
     223             : 
     224             : /************************************************************************/
     225             : /*                           ICreateFeature()                           */
     226             : /************************************************************************/
     227             : 
     228         978 : OGRErr OGRXLSXLayer::ICreateFeature(OGRFeature *poFeature)
     229             : {
     230         978 :     Init();
     231             : 
     232         978 :     const GIntBig nFIDOrigin = poFeature->GetFID();
     233         978 :     if (nFIDOrigin > 0)
     234             :     {
     235           1 :         const GIntBig nFIDModified = TranslateFIDToMemLayer(nFIDOrigin);
     236           1 :         if (GetFeatureRef(nFIDModified))
     237             :         {
     238           0 :             SetUpdated();
     239           0 :             poFeature->SetFID(nFIDModified);
     240           0 :             OGRErr eErr = OGRMemLayer::ISetFeature(poFeature);
     241           0 :             poFeature->SetFID(nFIDOrigin);
     242           0 :             return eErr;
     243             :         }
     244             :     }
     245         978 :     SetUpdated();
     246         978 :     poFeature->SetFID(OGRNullFID);
     247         978 :     OGRErr eErr = OGRMemLayer::ICreateFeature(poFeature);
     248         978 :     poFeature->SetFID(TranslateFIDFromMemLayer(poFeature->GetFID()));
     249         978 :     return eErr;
     250             : }
     251             : 
     252             : /************************************************************************/
     253             : /*                       ICreateFeatureUniqPtr()                        */
     254             : /************************************************************************/
     255             : 
     256             : OGRErr
     257           0 : OGRXLSXLayer::ICreateFeatureUniqPtr(std::unique_ptr<OGRFeature> poFeature,
     258             :                                     GIntBig *pnFID)
     259             : {
     260           0 :     const GIntBig nFIDOrigin = poFeature->GetFID();
     261           0 :     if (nFIDOrigin > 0)
     262             :     {
     263           0 :         const GIntBig nFIDModified = TranslateFIDToMemLayer(nFIDOrigin);
     264           0 :         if (GetFeatureRef(nFIDModified))
     265             :         {
     266           0 :             SetUpdated();
     267           0 :             poFeature->SetFID(nFIDModified);
     268           0 :             OGRErr eErr = OGRMemLayer::ISetFeatureUniqPtr(std::move(poFeature));
     269           0 :             if (pnFID)
     270           0 :                 *pnFID = nFIDOrigin;
     271           0 :             return eErr;
     272             :         }
     273             :     }
     274           0 :     SetUpdated();
     275           0 :     poFeature->SetFID(OGRNullFID);
     276           0 :     GIntBig nNewFID = OGRNullFID;
     277             :     const OGRErr eErr =
     278           0 :         OGRMemLayer::ICreateFeatureUniqPtr(std::move(poFeature), &nNewFID);
     279           0 :     if (pnFID)
     280           0 :         *pnFID = TranslateFIDFromMemLayer(nNewFID);
     281           0 :     return eErr;
     282             : }
     283             : 
     284             : /************************************************************************/
     285             : /*                           DeleteFeature()                            */
     286             : /************************************************************************/
     287             : 
     288          19 : OGRErr OGRXLSXLayer::DeleteFeature(GIntBig nFID)
     289             : {
     290          19 :     Init();
     291          19 :     SetUpdated();
     292          19 :     return OGRMemLayer::DeleteFeature(TranslateFIDToMemLayer(nFID));
     293             : }
     294             : 
     295             : /************************************************************************/
     296             : /*                             GetDataset()                             */
     297             : /************************************************************************/
     298             : 
     299           3 : GDALDataset *OGRXLSXLayer::GetDataset()
     300             : {
     301           3 :     return poDS;
     302             : }
     303             : 
     304             : /************************************************************************/
     305             : /*                           TestCapability()                           */
     306             : /************************************************************************/
     307             : 
     308         476 : bool OGRXLSXLayer::TestCapability(const char *pszCap) const
     309             : 
     310             : {
     311         476 :     if (EQUAL(pszCap, OLCUpsertFeature))
     312           9 :         return false;
     313         467 :     return OGRMemLayer::TestCapability(pszCap);
     314             : }
     315             : 
     316             : /************************************************************************/
     317             : /*                         OGRXLSXDataSource()                          */
     318             : /************************************************************************/
     319             : 
     320          42 : OGRXLSXDataSource::OGRXLSXDataSource(CSLConstList papszOpenOptionsIn)
     321             :     : pszName(nullptr), bUpdatable(false), bUpdated(false), nLayers(0),
     322             :       papoLayers(nullptr), bFirstLineIsHeaders(false),
     323          42 :       bAutodetectTypes(!EQUAL(
     324             :           CSLFetchNameValueDef(papszOpenOptionsIn, "FIELD_TYPES",
     325             :                                CPLGetConfigOption("OGR_XLSX_FIELD_TYPES", "")),
     326             :           "STRING")),
     327             :       oParser(nullptr), bStopParsing(false), nWithoutEventCounter(0),
     328             :       nDataHandlerCounter(0), nCurLine(0), nCurCol(0), poCurLayer(nullptr),
     329          84 :       nStackDepth(0), nDepth(0), bInCellXFS(false)
     330             : {
     331          42 :     stateStack[0].eVal = STATE_DEFAULT;
     332          42 :     stateStack[0].nBeginDepth = 0;
     333          42 : }
     334             : 
     335             : /************************************************************************/
     336             : /*                         ~OGRXLSXDataSource()                         */
     337             : /************************************************************************/
     338             : 
     339          84 : OGRXLSXDataSource::~OGRXLSXDataSource()
     340             : 
     341             : {
     342          42 :     OGRXLSXDataSource::Close();
     343          84 : }
     344             : 
     345             : /************************************************************************/
     346             : /*                               Close()                                */
     347             : /************************************************************************/
     348             : 
     349          84 : CPLErr OGRXLSXDataSource::Close(GDALProgressFunc, void *)
     350             : {
     351          84 :     CPLErr eErr = CE_None;
     352          84 :     if (nOpenFlags != OPEN_FLAGS_CLOSED)
     353             :     {
     354          42 :         if (OGRXLSXDataSource::FlushCache(true) != CE_None)
     355           0 :             eErr = CE_Failure;
     356             : 
     357          42 :         CPLFree(pszName);
     358             : 
     359         197 :         for (int i = 0; i < nLayers; i++)
     360         155 :             delete papoLayers[i];
     361          42 :         CPLFree(papoLayers);
     362             : 
     363          42 :         if (GDALDataset::Close() != CE_None)
     364           0 :             eErr = CE_Failure;
     365             :     }
     366          84 :     return eErr;
     367             : }
     368             : 
     369             : /************************************************************************/
     370             : /*                           TestCapability()                           */
     371             : /************************************************************************/
     372             : 
     373          90 : bool OGRXLSXDataSource::TestCapability(const char *pszCap) const
     374             : 
     375             : {
     376          90 :     if (EQUAL(pszCap, ODsCCreateLayer))
     377          14 :         return bUpdatable;
     378          76 :     else if (EQUAL(pszCap, ODsCDeleteLayer))
     379           2 :         return bUpdatable;
     380          74 :     else if (EQUAL(pszCap, ODsCRandomLayerWrite))
     381           0 :         return bUpdatable;
     382          74 :     else if (EQUAL(pszCap, ODsCZGeometries))
     383          18 :         return true;
     384          56 :     else if (EQUAL(pszCap, ODsCMeasuredGeometries))
     385          18 :         return true;
     386          38 :     else if (EQUAL(pszCap, ODsCCurveGeometries))
     387          18 :         return true;
     388             :     else
     389          20 :         return false;
     390             : }
     391             : 
     392             : /************************************************************************/
     393             : /*                              GetLayer()                              */
     394             : /************************************************************************/
     395             : 
     396         937 : const OGRLayer *OGRXLSXDataSource::GetLayer(int iLayer) const
     397             : 
     398             : {
     399         937 :     if (iLayer < 0 || iLayer >= nLayers)
     400           4 :         return nullptr;
     401             : 
     402         933 :     return papoLayers[iLayer];
     403             : }
     404             : 
     405             : /************************************************************************/
     406             : /*                           GetLayerCount()                            */
     407             : /************************************************************************/
     408             : 
     409         497 : int OGRXLSXDataSource::GetLayerCount() const
     410             : {
     411         497 :     return nLayers;
     412             : }
     413             : 
     414             : /************************************************************************/
     415             : /*                                Open()                                */
     416             : /************************************************************************/
     417             : 
     418          34 : int OGRXLSXDataSource::Open(const char *pszFilename,
     419             :                             const char *pszPrefixedFilename,
     420             :                             VSILFILE *fpWorkbook, VSILFILE *fpWorkbookRels,
     421             :                             VSILFILE *fpSharedStrings, VSILFILE *fpStyles,
     422             :                             int bUpdateIn)
     423             : 
     424             : {
     425          34 :     SetDescription(pszFilename);
     426             : 
     427          34 :     bUpdatable = CPL_TO_BOOL(bUpdateIn);
     428             : 
     429          34 :     pszName = CPLStrdup(pszFilename);
     430          34 :     osPrefixedFilename = pszPrefixedFilename;
     431             : 
     432          34 :     AnalyseWorkbookRels(fpWorkbookRels);
     433          34 :     AnalyseWorkbook(fpWorkbook);
     434          34 :     AnalyseSharedStrings(fpSharedStrings);
     435          34 :     AnalyseStyles(fpStyles);
     436             : 
     437             :     /* Remove empty layers at the end, which tend to be there */
     438          46 :     while (nLayers > 1)
     439             :     {
     440          30 :         papoLayers[nLayers - 1]->Init();
     441          59 :         if ((papoLayers[nLayers - 1]->m_osCols.empty() ||
     442          29 :              papoLayers[nLayers - 1]->m_osCols.find("max=\"1025\" min=\"1\"") !=
     443          60 :                  std::string::npos) &&
     444          25 :             papoLayers[nLayers - 1]->GetFeatureCount(false) == 0)
     445             :         {
     446          12 :             delete papoLayers[nLayers - 1];
     447          12 :             nLayers--;
     448             :         }
     449             :         else
     450          18 :             break;
     451             :     }
     452             : 
     453          34 :     return TRUE;
     454             : }
     455             : 
     456             : /************************************************************************/
     457             : /*                               Create()                               */
     458             : /************************************************************************/
     459             : 
     460           8 : int OGRXLSXDataSource::Create(const char *pszFilename,
     461             :                               CPL_UNUSED CSLConstList papszOptions)
     462             : {
     463           8 :     bUpdated = true;
     464           8 :     bUpdatable = true;
     465             : 
     466           8 :     pszName = CPLStrdup(pszFilename);
     467             : 
     468           8 :     return TRUE;
     469             : }
     470             : 
     471             : /************************************************************************/
     472             : /*                           GetUnprefixed()                            */
     473             : /************************************************************************/
     474             : 
     475       13944 : static const char *GetUnprefixed(const char *pszStr)
     476             : {
     477       13944 :     const char *pszColumn = strchr(pszStr, ':');
     478       13944 :     if (pszColumn)
     479          71 :         return pszColumn + 1;
     480       13873 :     return pszStr;
     481             : }
     482             : 
     483             : /************************************************************************/
     484             : /*                          startElementCbk()                           */
     485             : /************************************************************************/
     486             : 
     487        4173 : static void XMLCALL startElementCbk(void *pUserData, const char *pszNameIn,
     488             :                                     const char **ppszAttr)
     489             : {
     490        4173 :     ((OGRXLSXDataSource *)pUserData)->startElementCbk(pszNameIn, ppszAttr);
     491        4173 : }
     492             : 
     493        4173 : void OGRXLSXDataSource::startElementCbk(const char *pszNameIn,
     494             :                                         const char **ppszAttr)
     495             : {
     496        4173 :     if (bStopParsing)
     497           0 :         return;
     498             : 
     499        4173 :     pszNameIn = GetUnprefixed(pszNameIn);
     500             : 
     501        4173 :     nWithoutEventCounter = 0;
     502        4173 :     switch (stateStack[nStackDepth].eVal)
     503             :     {
     504        1051 :         case STATE_DEFAULT:
     505        1051 :             startElementDefault(pszNameIn, ppszAttr);
     506        1051 :             break;
     507         248 :         case STATE_COLS:
     508         248 :             startElementCols(pszNameIn, ppszAttr);
     509         248 :             break;
     510         842 :         case STATE_SHEETDATA:
     511         842 :             startElementTable(pszNameIn, ppszAttr);
     512         842 :             break;
     513        1012 :         case STATE_ROW:
     514        1012 :             startElementRow(pszNameIn, ppszAttr);
     515        1012 :             break;
     516        1020 :         case STATE_CELL:
     517        1020 :             startElementCell(pszNameIn, ppszAttr);
     518        1020 :             break;
     519           0 :         case STATE_TEXTV:
     520           0 :             break;
     521           0 :         default:
     522           0 :             break;
     523             :     }
     524        4173 :     nDepth++;
     525             : }
     526             : 
     527             : /************************************************************************/
     528             : /*                           endElementCbk()                            */
     529             : /************************************************************************/
     530             : 
     531        4173 : static void XMLCALL endElementCbk(void *pUserData, const char *pszNameIn)
     532             : {
     533        4173 :     ((OGRXLSXDataSource *)pUserData)->endElementCbk(pszNameIn);
     534        4173 : }
     535             : 
     536        4173 : void OGRXLSXDataSource::endElementCbk(const char *pszNameIn)
     537             : {
     538        4173 :     if (bStopParsing)
     539           0 :         return;
     540             : 
     541        4173 :     pszNameIn = GetUnprefixed(pszNameIn);
     542             : 
     543        4173 :     nWithoutEventCounter = 0;
     544             : 
     545        4173 :     nDepth--;
     546        4173 :     switch (stateStack[nStackDepth].eVal)
     547             :     {
     548         888 :         case STATE_DEFAULT:
     549         888 :             break;
     550          84 :         case STATE_SHEETDATA:
     551          84 :             endElementTable(pszNameIn);
     552          84 :             break;
     553         327 :         case STATE_COLS:
     554         327 :             endElementCols(pszNameIn);
     555         327 :             break;
     556         842 :         case STATE_ROW:
     557         842 :             endElementRow(pszNameIn);
     558         842 :             break;
     559        1068 :         case STATE_CELL:
     560        1068 :             endElementCell(pszNameIn);
     561        1068 :             break;
     562         964 :         case STATE_TEXTV:
     563         964 :             break;
     564           0 :         default:
     565           0 :             break;
     566             :     }
     567             : 
     568        4173 :     if (stateStack[nStackDepth].nBeginDepth == nDepth)
     569        3065 :         nStackDepth--;
     570             : }
     571             : 
     572             : /************************************************************************/
     573             : /*                           dataHandlerCbk()                           */
     574             : /************************************************************************/
     575             : 
     576        3070 : static void XMLCALL dataHandlerCbk(void *pUserData, const char *data, int nLen)
     577             : {
     578        3070 :     ((OGRXLSXDataSource *)pUserData)->dataHandlerCbk(data, nLen);
     579        3070 : }
     580             : 
     581        3070 : void OGRXLSXDataSource::dataHandlerCbk(const char *data, int nLen)
     582             : {
     583        3070 :     if (bStopParsing)
     584           0 :         return;
     585             : 
     586        3070 :     nDataHandlerCounter++;
     587        3070 :     if (nDataHandlerCounter >= PARSER_BUF_SIZE)
     588             :     {
     589           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     590             :                  "File probably corrupted (million laugh pattern)");
     591           0 :         XML_StopParser(oParser, XML_FALSE);
     592           0 :         bStopParsing = true;
     593           0 :         return;
     594             :     }
     595             : 
     596        3070 :     nWithoutEventCounter = 0;
     597             : 
     598        3070 :     switch (stateStack[nStackDepth].eVal)
     599             :     {
     600         605 :         case STATE_DEFAULT:
     601         605 :             break;
     602         146 :         case STATE_SHEETDATA:
     603         146 :             break;
     604         445 :         case STATE_ROW:
     605         445 :             break;
     606         800 :         case STATE_CELL:
     607         800 :             break;
     608         964 :         case STATE_TEXTV:
     609         964 :             dataHandlerTextV(data, nLen);
     610         964 :             break;
     611         110 :         default:
     612         110 :             break;
     613             :     }
     614             : }
     615             : 
     616             : /************************************************************************/
     617             : /*                             PushState()                              */
     618             : /************************************************************************/
     619             : 
     620        4174 : void OGRXLSXDataSource::PushState(HandlerStateEnum eVal)
     621             : {
     622        4174 :     if (nStackDepth + 1 == STACK_SIZE)
     623             :     {
     624           0 :         bStopParsing = true;
     625           0 :         return;
     626             :     }
     627        4174 :     nStackDepth++;
     628        4174 :     stateStack[nStackDepth].eVal = eVal;
     629        4174 :     stateStack[nStackDepth].nBeginDepth = nDepth;
     630             : }
     631             : 
     632             : /************************************************************************/
     633             : /*                         GetAttributeValue()                          */
     634             : /************************************************************************/
     635             : 
     636       11819 : static const char *GetAttributeValue(const char **ppszAttr, const char *pszKey,
     637             :                                      const char *pszDefaultVal)
     638             : {
     639       11819 :     while (*ppszAttr)
     640             :     {
     641       11398 :         if (strcmp(ppszAttr[0], pszKey) == 0)
     642        4977 :             return ppszAttr[1];
     643        6421 :         ppszAttr += 2;
     644             :     }
     645         421 :     return pszDefaultVal;
     646             : }
     647             : 
     648             : /************************************************************************/
     649             : /*                          GetOGRFieldType()                           */
     650             : /************************************************************************/
     651             : 
     652        1018 : OGRFieldType OGRXLSXDataSource::GetOGRFieldType(const char *pszValue,
     653             :                                                 const char *pszValueType,
     654             :                                                 OGRFieldSubType &eSubType)
     655             : {
     656        1018 :     eSubType = OFSTNone;
     657        1018 :     if (!bAutodetectTypes || pszValueType == nullptr)
     658          32 :         return OFTString;
     659         986 :     else if (strcmp(pszValueType, "string") == 0)
     660         420 :         return OFTString;
     661         566 :     else if (strcmp(pszValueType, "float") == 0)
     662             :     {
     663         405 :         CPLValueType eValueType = CPLGetValueType(pszValue);
     664         405 :         if (eValueType == CPL_VALUE_STRING)
     665          14 :             return OFTString;
     666         391 :         else if (eValueType == CPL_VALUE_INTEGER)
     667             :         {
     668         260 :             GIntBig nVal = CPLAtoGIntBig(pszValue);
     669         260 :             if (!CPL_INT64_FITS_ON_INT32(nVal))
     670           1 :                 return OFTInteger64;
     671             :             else
     672         259 :                 return OFTInteger;
     673             :         }
     674             :         else
     675         131 :             return OFTReal;
     676             :     }
     677         161 :     else if (strcmp(pszValueType, "datetime") == 0 ||
     678         106 :              strcmp(pszValueType, "datetime_ms") == 0)
     679             :     {
     680          56 :         return OFTDateTime;
     681             :     }
     682         105 :     else if (strcmp(pszValueType, "date") == 0)
     683             :     {
     684          56 :         return OFTDate;
     685             :     }
     686          49 :     else if (strcmp(pszValueType, "time") == 0)
     687             :     {
     688          14 :         return OFTTime;
     689             :     }
     690          35 :     else if (strcmp(pszValueType, "bool") == 0)
     691             :     {
     692           1 :         eSubType = OFSTBoolean;
     693           1 :         return OFTInteger;
     694             :     }
     695             :     else
     696          34 :         return OFTString;
     697             : }
     698             : 
     699             : /************************************************************************/
     700             : /*                              SetField()                              */
     701             : /************************************************************************/
     702             : 
     703         841 : static void SetField(OGRFeature *poFeature, int i, const char *pszValue,
     704             :                      const char *pszCellType)
     705             : {
     706         841 :     if (pszValue[0] == '\0')
     707          43 :         return;
     708             : 
     709         798 :     OGRFieldType eType = poFeature->GetFieldDefnRef(i)->GetType();
     710             : 
     711         798 :     if (strcmp(pszCellType, "time") == 0 || strcmp(pszCellType, "date") == 0 ||
     712         748 :         strcmp(pszCellType, "datetime") == 0 ||
     713         706 :         strcmp(pszCellType, "datetime_ms") == 0)
     714             :     {
     715             :         struct tm sTm;
     716          93 :         const double dfNumberOfDaysSince1900 = CPLAtof(pszValue);
     717          93 :         if (!(std::fabs(dfNumberOfDaysSince1900) < 365.0 * 10000))
     718           0 :             return;
     719          93 :         double dfNumberOfSecsSince1900 =
     720             :             dfNumberOfDaysSince1900 * NUMBER_OF_SECONDS_PER_DAY;
     721          93 :         if (std::fabs(dfNumberOfSecsSince1900 -
     722          93 :                       std::round(dfNumberOfSecsSince1900)) < 1e-3)
     723          92 :             dfNumberOfSecsSince1900 = std::round(dfNumberOfSecsSince1900);
     724          93 :         const GIntBig nUnixTime =
     725          93 :             static_cast<GIntBig>(dfNumberOfSecsSince1900) -
     726             :             static_cast<GIntBig>(NUMBER_OF_DAYS_BETWEEN_1900_AND_1970) *
     727             :                 NUMBER_OF_SECONDS_PER_DAY;
     728          93 :         CPLUnixTimeToYMDHMS(nUnixTime, &sTm);
     729             : 
     730          93 :         if (eType == OFTTime || eType == OFTDate || eType == OFTDateTime)
     731             :         {
     732          73 :             double fFracSec = fmod(dfNumberOfSecsSince1900, 1);
     733          73 :             poFeature->SetField(i, sTm.tm_year + 1900, sTm.tm_mon + 1,
     734             :                                 sTm.tm_mday, sTm.tm_hour, sTm.tm_min,
     735          73 :                                 static_cast<float>(sTm.tm_sec + fFracSec), 0);
     736             :         }
     737          20 :         else if (strcmp(pszCellType, "time") == 0)
     738             :         {
     739           4 :             poFeature->SetField(i, CPLSPrintf("%02d:%02d:%02d", sTm.tm_hour,
     740             :                                               sTm.tm_min, sTm.tm_sec));
     741             :         }
     742          16 :         else if (strcmp(pszCellType, "date") == 0)
     743             :         {
     744           8 :             poFeature->SetField(i,
     745           8 :                                 CPLSPrintf("%04d/%02d/%02d", sTm.tm_year + 1900,
     746           8 :                                            sTm.tm_mon + 1, sTm.tm_mday));
     747             :         }
     748             :         else /* if (strcmp(pszCellType, "datetime") == 0) */
     749             :         {
     750           8 :             double fFracSec = fmod(dfNumberOfSecsSince1900, 1);
     751           8 :             poFeature->SetField(i, sTm.tm_year + 1900, sTm.tm_mon + 1,
     752             :                                 sTm.tm_mday, sTm.tm_hour, sTm.tm_min,
     753           8 :                                 static_cast<float>(sTm.tm_sec + fFracSec), 0);
     754          93 :         }
     755             :     }
     756             :     else
     757         705 :         poFeature->SetField(i, pszValue);
     758             : }
     759             : 
     760             : /************************************************************************/
     761             : /*                          DetectHeaderLine()                          */
     762             : /************************************************************************/
     763             : 
     764          60 : void OGRXLSXDataSource::DetectHeaderLine()
     765             : 
     766             : {
     767          60 :     bool bHeaderLineCandidate = true;
     768             : 
     769         281 :     for (size_t i = 0; i < apoFirstLineTypes.size(); i++)
     770             :     {
     771         244 :         if (apoFirstLineTypes[i] != "string")
     772             :         {
     773             :             /* If the values in the first line are not text, then it is */
     774             :             /* not a header line */
     775          23 :             bHeaderLineCandidate = false;
     776          23 :             break;
     777             :         }
     778             :     }
     779             : 
     780          60 :     size_t nCountTextOnCurLine = 0;
     781          60 :     size_t nCountNonEmptyOnCurLine = 0;
     782         267 :     for (size_t i = 0; bHeaderLineCandidate && i < apoCurLineTypes.size(); i++)
     783             :     {
     784         207 :         if (apoCurLineTypes[i] == "string")
     785             :         {
     786             :             /* If there are only text values on the second line, then we cannot
     787             :              */
     788             :             /* know if it is a header line or just a regular line */
     789          63 :             nCountTextOnCurLine++;
     790             :         }
     791         144 :         else if (apoCurLineTypes[i] != "")
     792             :         {
     793         130 :             nCountNonEmptyOnCurLine++;
     794             :         }
     795             :     }
     796             : 
     797             :     const char *pszXLSXHeaders =
     798          60 :         CSLFetchNameValueDef(papszOpenOptions, "HEADERS",
     799             :                              CPLGetConfigOption("OGR_XLSX_HEADERS", ""));
     800          60 :     bFirstLineIsHeaders = false;
     801          60 :     if (EQUAL(pszXLSXHeaders, "FORCE"))
     802           1 :         bFirstLineIsHeaders = true;
     803          59 :     else if (EQUAL(pszXLSXHeaders, "DISABLE"))
     804           3 :         bFirstLineIsHeaders = false;
     805          62 :     else if (bHeaderLineCandidate && !apoFirstLineTypes.empty() &&
     806          50 :              apoFirstLineTypes.size() >= apoCurLineTypes.size() &&
     807         112 :              nCountTextOnCurLine != apoFirstLineTypes.size() &&
     808             :              nCountNonEmptyOnCurLine != 0)
     809             :     {
     810          19 :         bFirstLineIsHeaders = true;
     811             :     }
     812          60 :     CPLDebug("XLSX", "%s %s", poCurLayer ? poCurLayer->GetName() : "NULL layer",
     813          60 :              bFirstLineIsHeaders ? "has header line" : "has no header line");
     814          60 : }
     815             : 
     816             : /************************************************************************/
     817             : /*                        startElementDefault()                         */
     818             : /************************************************************************/
     819             : 
     820        1051 : void OGRXLSXDataSource::startElementDefault(const char *pszNameIn,
     821             :                                             CPL_UNUSED const char **ppszAttr)
     822             : {
     823        1051 :     if (strcmp(pszNameIn, "cols") == 0)
     824             :     {
     825          79 :         PushState(STATE_COLS);
     826          79 :         m_osCols = "<cols>";
     827             :     }
     828         972 :     else if (strcmp(pszNameIn, "sheetData") == 0)
     829             :     {
     830          84 :         apoFirstLineValues.resize(0);
     831          84 :         apoFirstLineTypes.resize(0);
     832          84 :         nCurLine = 0;
     833          84 :         PushState(STATE_SHEETDATA);
     834             :     }
     835        1051 : }
     836             : 
     837             : /************************************************************************/
     838             : /*                          startElementCols()                          */
     839             : /************************************************************************/
     840             : 
     841         248 : void OGRXLSXDataSource::startElementCols(const char *pszNameIn,
     842             :                                          const char **ppszAttr)
     843             : {
     844         248 :     m_osCols.append("<");
     845         248 :     m_osCols.append(pszNameIn);
     846        1542 :     for (const char **iter = ppszAttr; iter && iter[0] && iter[1]; iter += 2)
     847             :     {
     848        1294 :         m_osCols.append(" ");
     849        1294 :         m_osCols.append(iter[0]);
     850        1294 :         m_osCols.append("=\"");
     851        1294 :         char *pszXML = OGRGetXML_UTF8_EscapedString(iter[1]);
     852        1294 :         m_osCols.append(pszXML);
     853        1294 :         CPLFree(pszXML);
     854        1294 :         m_osCols.append("\"");
     855             :     }
     856         248 :     m_osCols.append(">");
     857         248 : }
     858             : 
     859             : /************************************************************************/
     860             : /*                           endElementCell()                           */
     861             : /************************************************************************/
     862             : 
     863         327 : void OGRXLSXDataSource::endElementCols(const char *pszNameIn)
     864             : {
     865         327 :     m_osCols.append("</");
     866         327 :     m_osCols.append(pszNameIn);
     867         327 :     m_osCols.append(">");
     868         327 : }
     869             : 
     870             : /************************************************************************/
     871             : /*                         startElementTable()                          */
     872             : /************************************************************************/
     873             : 
     874         842 : void OGRXLSXDataSource::startElementTable(const char *pszNameIn,
     875             :                                           const char **ppszAttr)
     876             : {
     877         842 :     if (strcmp(pszNameIn, "row") == 0)
     878             :     {
     879         842 :         PushState(STATE_ROW);
     880             : 
     881         842 :         nCurCol = 0;
     882         842 :         apoCurLineValues.clear();
     883         842 :         apoCurLineTypes.clear();
     884             : 
     885             :         int nNewCurLine;
     886         842 :         if (const char *pszR = GetAttributeValue(ppszAttr, "r", nullptr))
     887             :         {
     888         839 :             nNewCurLine = atoi(pszR);
     889         839 :             if (nNewCurLine <= 0)
     890             :             {
     891           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid row: %d",
     892             :                          nNewCurLine);
     893           0 :                 return;
     894             :             }
     895         839 :             nNewCurLine--;
     896             :         }
     897             :         else
     898             :         {
     899           3 :             nNewCurLine = nCurLine;
     900             :         }
     901             :         const int nFields = std::max(
     902        2526 :             static_cast<int>(apoFirstLineValues.size()),
     903         842 :             poCurLayer != nullptr ? poCurLayer->GetLayerDefn()->GetFieldCount()
     904         842 :                                   : 0);
     905         842 :         if (nNewCurLine > nCurLine &&
     906          16 :             (nNewCurLine - nCurLine > 10000 ||
     907          10 :              (nFields > 0 && nNewCurLine - nCurLine > 100000 / nFields)))
     908             :         {
     909           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     910             :                      "Invalid row: %d. Too big gap with previous valid row",
     911             :                      nNewCurLine);
     912           0 :             return;
     913             :         }
     914         917 :         for (; nCurLine < nNewCurLine;)
     915             :         {
     916          75 :             const int nCurLineBefore = nCurLine;
     917          75 :             endElementRow("row");
     918             : 
     919          75 :             nCurCol = 0;
     920          75 :             apoCurLineValues.clear();
     921          75 :             apoCurLineTypes.clear();
     922          75 :             if (nCurLineBefore == nCurLine)
     923           0 :                 break;
     924             :         }
     925             :     }
     926             : }
     927             : 
     928             : /************************************************************************/
     929             : /*                          endElementTable()                           */
     930             : /************************************************************************/
     931             : 
     932          84 : void OGRXLSXDataSource::endElementTable(CPL_UNUSED const char *pszNameIn)
     933             : {
     934          84 :     if (stateStack[nStackDepth].nBeginDepth == nDepth && poCurLayer != nullptr)
     935             :     {
     936          84 :         CPLAssert(strcmp(pszNameIn, "sheetData") == 0);
     937             : 
     938          84 :         if (nCurLine == 0 || (nCurLine == 1 && apoFirstLineValues.empty()))
     939             :         {
     940             :             // no rows
     941             :         }
     942          68 :         else if (nCurLine == 1)
     943             :         {
     944             :             /* If we have only one single line in the sheet */
     945          50 :             for (size_t i = 0; i < apoFirstLineValues.size(); i++)
     946             :             {
     947          42 :                 const char *pszFieldName = CPLSPrintf("Field%d", (int)i + 1);
     948          42 :                 OGRFieldSubType eSubType = OFSTNone;
     949             :                 OGRFieldType eType =
     950          42 :                     GetOGRFieldType(apoFirstLineValues[i].c_str(),
     951          42 :                                     apoFirstLineTypes[i].c_str(), eSubType);
     952          42 :                 OGRFieldDefn oFieldDefn(pszFieldName, eType);
     953          42 :                 oFieldDefn.SetSubType(eSubType);
     954          42 :                 if (poCurLayer->CreateField(&oFieldDefn) != OGRERR_NONE)
     955             :                 {
     956           0 :                     return;
     957             :                 }
     958             :             }
     959             : 
     960           8 :             OGRFeature *poFeature = new OGRFeature(poCurLayer->GetLayerDefn());
     961          50 :             for (size_t i = 0; i < apoFirstLineValues.size(); i++)
     962             :             {
     963          84 :                 SetField(poFeature, static_cast<int>(i),
     964          42 :                          apoFirstLineValues[i].c_str(),
     965          42 :                          apoFirstLineTypes[i].c_str());
     966             :             }
     967           8 :             CPL_IGNORE_RET_VAL(poCurLayer->CreateFeature(poFeature));
     968           8 :             delete poFeature;
     969             :         }
     970             : 
     971          84 :         if (poCurLayer)
     972             :         {
     973          84 :             poCurLayer->SetUpdatable(bUpdatable);
     974          84 :             poCurLayer->SetUpdated(false);
     975             :         }
     976             : 
     977          84 :         poCurLayer = nullptr;
     978             :     }
     979             : }
     980             : 
     981             : /************************************************************************/
     982             : /*                          startElementRow()                           */
     983             : /************************************************************************/
     984             : 
     985        1012 : void OGRXLSXDataSource::startElementRow(const char *pszNameIn,
     986             :                                         const char **ppszAttr)
     987             : {
     988        1012 :     if (strcmp(pszNameIn, "c") == 0)
     989             :     {
     990        1012 :         PushState(STATE_CELL);
     991             : 
     992        1012 :         const char *pszR = GetAttributeValue(ppszAttr, "r", nullptr);
     993        1012 :         if (pszR && pszR[0] >= 'A' && pszR[0] <= 'Z')
     994             :         {
     995             :             /* Convert col number from base 26 */
     996             :             /*
     997             :             A Z   AA AZ   BA BZ   ZA   ZZ   AAA    ZZZ      AAAA
     998             :             0 25  26 51   52 77   676  701  702    18277    18278
     999             :             */
    1000        1007 :             int nNewCurCol = (pszR[0] - 'A');
    1001        1007 :             int i = 1;
    1002        1010 :             while (pszR[i] >= 'A' && pszR[i] <= 'Z' && nNewCurCol <= 2000)
    1003             :             {
    1004             :                 // We wouldn't need the +1 if this was a proper base 26
    1005           3 :                 nNewCurCol = (nNewCurCol + 1) * 26 + (pszR[i] - 'A');
    1006           3 :                 i++;
    1007             :             }
    1008        1007 :             if (nNewCurCol > 2000)
    1009             :             {
    1010           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1011             :                          "Limiting number of columns to 2000");
    1012           0 :                 nNewCurCol = 2000;
    1013             :             }
    1014        1195 :             for (; nCurCol < nNewCurCol; nCurCol++)
    1015             :             {
    1016         188 :                 apoCurLineValues.push_back("");
    1017         188 :                 apoCurLineTypes.push_back("");
    1018             :             }
    1019             :         }
    1020             : 
    1021        1012 :         osValueType = "float";
    1022             : 
    1023        1012 :         const char *pszS = GetAttributeValue(ppszAttr, "s", "-1");
    1024        1012 :         int nS = atoi(pszS);
    1025        1012 :         if (nS >= 0 && nS < (int)apoStyles.size())
    1026             :         {
    1027         771 :             XLSXFieldTypeExtended eType = apoStyles[nS];
    1028         771 :             if (eType.eType == OFTDateTime)
    1029             :             {
    1030          43 :                 if (eType.bHasMS)
    1031           1 :                     osValueType = "datetime_ms";
    1032             :                 else
    1033          42 :                     osValueType = "datetime";
    1034             :             }
    1035         728 :             else if (eType.eType == OFTDate)
    1036          42 :                 osValueType = "date";
    1037         686 :             else if (eType.eType == OFTTime)
    1038          10 :                 osValueType = "time";
    1039             :         }
    1040         241 :         else if (nS != -1)
    1041           0 :             CPLDebug("XLSX", "Cannot find style %d", nS);
    1042             : 
    1043        1012 :         const char *pszT = GetAttributeValue(ppszAttr, "t", "");
    1044        1012 :         if (EQUAL(pszT, "s"))
    1045         501 :             osValueType = "stringLookup";
    1046         511 :         else if (EQUAL(pszT, "inlineStr"))
    1047          28 :             osValueType = "string";
    1048         483 :         else if (EQUAL(pszT, "b"))
    1049           1 :             osValueType = "bool";
    1050             : 
    1051        1012 :         osValue = "";
    1052             :     }
    1053        1012 : }
    1054             : 
    1055             : /************************************************************************/
    1056             : /*                           endElementRow()                            */
    1057             : /************************************************************************/
    1058             : 
    1059         917 : void OGRXLSXDataSource::endElementRow(CPL_UNUSED const char *pszNameIn)
    1060             : {
    1061         917 :     if (stateStack[nStackDepth].nBeginDepth == nDepth && poCurLayer != nullptr)
    1062             :     {
    1063         917 :         CPLAssert(strcmp(pszNameIn, "row") == 0);
    1064             : 
    1065             :         /* Backup first line values and types in special arrays */
    1066         917 :         if (nCurLine == 0)
    1067             :         {
    1068          68 :             apoFirstLineTypes = apoCurLineTypes;
    1069          68 :             apoFirstLineValues = apoCurLineValues;
    1070             : 
    1071             : #if skip_leading_empty_rows
    1072             :             if (apoFirstLineTypes.empty())
    1073             :             {
    1074             :                 /* Skip leading empty rows */
    1075             :                 apoFirstLineTypes.resize(0);
    1076             :                 apoFirstLineValues.resize(0);
    1077             :                 return;
    1078             :             }
    1079             : #endif
    1080             :         }
    1081             : 
    1082         917 :         if (nCurLine == 1)
    1083             :         {
    1084          60 :             DetectHeaderLine();
    1085             : 
    1086          60 :             poCurLayer->SetHasHeaderLine(bFirstLineIsHeaders);
    1087             : 
    1088          60 :             if (bFirstLineIsHeaders)
    1089             :             {
    1090         186 :                 for (size_t i = 0; i < apoFirstLineValues.size(); i++)
    1091             :                 {
    1092         166 :                     const char *pszFieldName = apoFirstLineValues[i].c_str();
    1093         166 :                     if (pszFieldName[0] == '\0')
    1094           0 :                         pszFieldName = CPLSPrintf("Field%d", (int)i + 1);
    1095         166 :                     bool bUnknownType = true;
    1096         166 :                     OGRFieldType eType = OFTString;
    1097         166 :                     OGRFieldSubType eSubType = OFSTNone;
    1098         331 :                     if (i < apoCurLineValues.size() &&
    1099         165 :                         !apoCurLineValues[i].empty())
    1100             :                     {
    1101         160 :                         eType = GetOGRFieldType(apoCurLineValues[i].c_str(),
    1102         160 :                                                 apoCurLineTypes[i].c_str(),
    1103             :                                                 eSubType);
    1104         160 :                         bUnknownType = false;
    1105             :                     }
    1106         166 :                     OGRFieldDefn oFieldDefn(pszFieldName, eType);
    1107         166 :                     oFieldDefn.SetSubType(eSubType);
    1108         166 :                     if (bUnknownType)
    1109             :                     {
    1110           6 :                         poCurLayer->oSetFieldsOfUnknownType.insert(
    1111           6 :                             poCurLayer->GetLayerDefn()->GetFieldCount());
    1112             :                     }
    1113         166 :                     if (poCurLayer->CreateField(&oFieldDefn) != OGRERR_NONE)
    1114             :                     {
    1115           0 :                         return;
    1116             :                     }
    1117             :                 }
    1118             :             }
    1119             :             else
    1120             :             {
    1121         132 :                 for (size_t i = 0; i < apoFirstLineValues.size(); i++)
    1122             :                 {
    1123             :                     const char *pszFieldName =
    1124          92 :                         CPLSPrintf("Field%d", (int)i + 1);
    1125          92 :                     OGRFieldSubType eSubType = OFSTNone;
    1126             :                     OGRFieldType eType =
    1127          92 :                         GetOGRFieldType(apoFirstLineValues[i].c_str(),
    1128          92 :                                         apoFirstLineTypes[i].c_str(), eSubType);
    1129          92 :                     OGRFieldDefn oFieldDefn(pszFieldName, eType);
    1130          92 :                     oFieldDefn.SetSubType(eSubType);
    1131          92 :                     if (poCurLayer->CreateField(&oFieldDefn) != OGRERR_NONE)
    1132             :                     {
    1133           0 :                         return;
    1134             :                     }
    1135             :                 }
    1136             : 
    1137             :                 OGRFeature *poFeature =
    1138          40 :                     new OGRFeature(poCurLayer->GetLayerDefn());
    1139         132 :                 for (size_t i = 0; i < apoFirstLineValues.size(); i++)
    1140             :                 {
    1141         184 :                     SetField(poFeature, static_cast<int>(i),
    1142          92 :                              apoFirstLineValues[i].c_str(),
    1143          92 :                              apoFirstLineTypes[i].c_str());
    1144             :                 }
    1145          40 :                 CPL_IGNORE_RET_VAL(poCurLayer->CreateFeature(poFeature));
    1146          40 :                 delete poFeature;
    1147             :             }
    1148             :         }
    1149             : 
    1150         917 :         if (nCurLine >= 1)
    1151             :         {
    1152             :             /* Add new fields found on following lines. */
    1153         849 :             if (apoCurLineValues.size() >
    1154         849 :                 (size_t)poCurLayer->GetLayerDefn()->GetFieldCount())
    1155             :             {
    1156          34 :                 GIntBig nFeatureCount = poCurLayer->GetFeatureCount(false);
    1157          68 :                 if (nFeatureCount > 0 &&
    1158             :                     static_cast<size_t>(
    1159          34 :                         apoCurLineValues.size() -
    1160          34 :                         poCurLayer->GetLayerDefn()->GetFieldCount()) >
    1161          34 :                         static_cast<size_t>(100000 / nFeatureCount))
    1162             :                 {
    1163           0 :                     CPLError(CE_Failure, CPLE_NotSupported,
    1164             :                              "Adding too many columns to too many "
    1165             :                              "existing features");
    1166           0 :                     return;
    1167             :                 }
    1168          61 :                 for (size_t i =
    1169          34 :                          (size_t)poCurLayer->GetLayerDefn()->GetFieldCount();
    1170          95 :                      i < apoCurLineValues.size(); i++)
    1171             :                 {
    1172             :                     const char *pszFieldName =
    1173          61 :                         CPLSPrintf("Field%d", (int)i + 1);
    1174          61 :                     OGRFieldSubType eSubType = OFSTNone;
    1175             :                     OGRFieldType eType =
    1176          61 :                         GetOGRFieldType(apoCurLineValues[i].c_str(),
    1177          61 :                                         apoCurLineTypes[i].c_str(), eSubType);
    1178          61 :                     OGRFieldDefn oFieldDefn(pszFieldName, eType);
    1179          61 :                     oFieldDefn.SetSubType(eSubType);
    1180          61 :                     if (poCurLayer->CreateField(&oFieldDefn) != OGRERR_NONE)
    1181             :                     {
    1182           0 :                         return;
    1183             :                     }
    1184             :                 }
    1185             :             }
    1186             : 
    1187             :             /* Update field type if necessary */
    1188         849 :             if (bAutodetectTypes)
    1189             :             {
    1190        1671 :                 for (size_t i = 0; i < apoCurLineValues.size(); i++)
    1191             :                 {
    1192         832 :                     if (!apoCurLineValues[i].empty())
    1193             :                     {
    1194         663 :                         OGRFieldSubType eValSubType = OFSTNone;
    1195        1326 :                         OGRFieldType eValType = GetOGRFieldType(
    1196         663 :                             apoCurLineValues[i].c_str(),
    1197         663 :                             apoCurLineTypes[i].c_str(), eValSubType);
    1198         663 :                         OGRLayer *poCurLayerAsLayer = poCurLayer;
    1199             :                         OGRFieldDefn *poFieldDefn =
    1200         663 :                             poCurLayerAsLayer->GetLayerDefn()->GetFieldDefn(
    1201         663 :                                 static_cast<int>(i));
    1202         663 :                         const OGRFieldType eFieldType = poFieldDefn->GetType();
    1203         663 :                         auto oIter = poCurLayer->oSetFieldsOfUnknownType.find(
    1204         663 :                             static_cast<int>(i));
    1205         663 :                         if (oIter != poCurLayer->oSetFieldsOfUnknownType.end())
    1206             :                         {
    1207           4 :                             poCurLayer->oSetFieldsOfUnknownType.erase(oIter);
    1208             : 
    1209             :                             auto oTemporaryUnsealer(
    1210           8 :                                 poFieldDefn->GetTemporaryUnsealer());
    1211           4 :                             poFieldDefn->SetType(eValType);
    1212           4 :                             poFieldDefn->SetSubType(eValSubType);
    1213             :                         }
    1214         659 :                         else if (eFieldType == OFTDateTime &&
    1215          28 :                                  (eValType == OFTDate || eValType == OFTTime))
    1216             :                         {
    1217             :                             /* ok */
    1218             :                         }
    1219         659 :                         else if (eFieldType == OFTReal &&
    1220          72 :                                  (eValType == OFTInteger ||
    1221             :                                   eValType == OFTInteger64))
    1222             :                         {
    1223             :                             /* ok */;
    1224             :                         }
    1225         653 :                         else if (eFieldType == OFTInteger64 &&
    1226             :                                  eValType == OFTInteger)
    1227             :                         {
    1228             :                             /* ok */;
    1229             :                         }
    1230         652 :                         else if (eFieldType != OFTString &&
    1231             :                                  eValType != eFieldType)
    1232             :                         {
    1233          36 :                             OGRFieldDefn oNewFieldDefn(poFieldDefn);
    1234          18 :                             oNewFieldDefn.SetSubType(OFSTNone);
    1235          18 :                             if ((eFieldType == OFTDate ||
    1236          11 :                                  eFieldType == OFTTime) &&
    1237             :                                 eValType == OFTDateTime)
    1238           4 :                                 oNewFieldDefn.SetType(OFTDateTime);
    1239          14 :                             else if ((eFieldType == OFTInteger ||
    1240           7 :                                       eFieldType == OFTInteger64) &&
    1241             :                                      eValType == OFTReal)
    1242           6 :                                 oNewFieldDefn.SetType(OFTReal);
    1243           8 :                             else if (eFieldType == OFTInteger &&
    1244             :                                      eValType == OFTInteger64)
    1245           1 :                                 oNewFieldDefn.SetType(OFTInteger64);
    1246             :                             else
    1247           7 :                                 oNewFieldDefn.SetType(OFTString);
    1248          18 :                             poCurLayer->AlterFieldDefn(static_cast<int>(i),
    1249             :                                                        &oNewFieldDefn,
    1250          18 :                                                        ALTER_TYPE_FLAG);
    1251             :                         }
    1252          98 :                         else if (eFieldType == OFTInteger &&
    1253          98 :                                  poFieldDefn->GetSubType() == OFSTBoolean &&
    1254         732 :                                  eValType == OFTInteger &&
    1255           0 :                                  eValSubType != OFSTBoolean)
    1256             :                         {
    1257           0 :                             poFieldDefn->SetSubType(OFSTNone);
    1258             :                         }
    1259             :                     }
    1260             :                 }
    1261             :             }
    1262             : 
    1263             :             /* Add feature for current line */
    1264         849 :             OGRFeature *poFeature = new OGRFeature(poCurLayer->GetLayerDefn());
    1265        1749 :             for (size_t i = 0; i < apoCurLineValues.size(); i++)
    1266             :             {
    1267         900 :                 if (!apoCurLineValues[i].empty())
    1268             :                 {
    1269        1414 :                     SetField(poFeature, static_cast<int>(i),
    1270         707 :                              apoCurLineValues[i].c_str(),
    1271         707 :                              apoCurLineTypes[i].c_str());
    1272             :                 }
    1273             :             }
    1274         849 :             CPL_IGNORE_RET_VAL(poCurLayer->CreateFeature(poFeature));
    1275         849 :             delete poFeature;
    1276             :         }
    1277             : 
    1278         917 :         nCurLine++;
    1279             :     }
    1280             : }
    1281             : 
    1282             : /************************************************************************/
    1283             : /*                          startElementCell()                          */
    1284             : /************************************************************************/
    1285             : 
    1286        1020 : void OGRXLSXDataSource::startElementCell(const char *pszNameIn,
    1287             :                                          CPL_UNUSED const char **ppszAttr)
    1288             : {
    1289        1020 :     if (osValue.empty() && strcmp(pszNameIn, "v") == 0)
    1290             :     {
    1291         936 :         PushState(STATE_TEXTV);
    1292             :     }
    1293          84 :     else if (osValue.empty() && strcmp(pszNameIn, "t") == 0)
    1294             :     {
    1295          28 :         PushState(STATE_TEXTV);
    1296             :     }
    1297        1020 : }
    1298             : 
    1299             : /************************************************************************/
    1300             : /*                           endElementCell()                           */
    1301             : /************************************************************************/
    1302             : 
    1303        1068 : void OGRXLSXDataSource::endElementCell(CPL_UNUSED const char *pszNameIn)
    1304             : {
    1305        1068 :     if (stateStack[nStackDepth].nBeginDepth == nDepth)
    1306             :     {
    1307        1012 :         CPLAssert(strcmp(pszNameIn, "c") == 0);
    1308             : 
    1309        1012 :         if (osValueType == "stringLookup")
    1310             :         {
    1311         501 :             int nIndex = atoi(osValue);
    1312         501 :             if (nIndex >= 0 && nIndex < (int)(apoSharedStrings.size()))
    1313         501 :                 osValue = apoSharedStrings[nIndex];
    1314             :             else
    1315           0 :                 CPLDebug("XLSX", "Cannot find string %d", nIndex);
    1316         501 :             osValueType = "string";
    1317             :         }
    1318             : 
    1319        1012 :         apoCurLineValues.push_back(osValue);
    1320        1012 :         apoCurLineTypes.push_back(osValueType);
    1321             : 
    1322        1012 :         nCurCol += 1;
    1323             :     }
    1324        1068 : }
    1325             : 
    1326             : /************************************************************************/
    1327             : /*                          dataHandlerTextV()                          */
    1328             : /************************************************************************/
    1329             : 
    1330         964 : void OGRXLSXDataSource::dataHandlerTextV(const char *data, int nLen)
    1331             : {
    1332         964 :     osValue.append(data, nLen);
    1333         964 : }
    1334             : 
    1335             : /************************************************************************/
    1336             : /*                             BuildLayer()                             */
    1337             : /************************************************************************/
    1338             : 
    1339          84 : void OGRXLSXDataSource::BuildLayer(OGRXLSXLayer *poLayer)
    1340             : {
    1341          84 :     poCurLayer = poLayer;
    1342             : 
    1343          84 :     const char *pszSheetFilename = poLayer->GetFilename().c_str();
    1344          84 :     VSILFILE *fp = VSIFOpenL(pszSheetFilename, "rb");
    1345          84 :     if (fp == nullptr)
    1346             :     {
    1347           0 :         CPLDebug("XLSX", "Cannot open file %s for sheet %s", pszSheetFilename,
    1348             :                  poLayer->GetName());
    1349           0 :         return;
    1350             :     }
    1351             : 
    1352          84 :     const bool bUpdatedBackup = bUpdated;
    1353             : 
    1354          84 :     oParser = OGRCreateExpatXMLParser();
    1355          84 :     m_osCols.clear();
    1356          84 :     XML_SetElementHandler(oParser, OGRXLSX::startElementCbk,
    1357             :                           OGRXLSX::endElementCbk);
    1358          84 :     XML_SetCharacterDataHandler(oParser, OGRXLSX::dataHandlerCbk);
    1359          84 :     XML_SetUserData(oParser, this);
    1360             : 
    1361          84 :     VSIFSeekL(fp, 0, SEEK_SET);
    1362             : 
    1363          84 :     bStopParsing = false;
    1364          84 :     nWithoutEventCounter = 0;
    1365          84 :     nDataHandlerCounter = 0;
    1366          84 :     nStackDepth = 0;
    1367          84 :     nDepth = 0;
    1368          84 :     stateStack[0].eVal = STATE_DEFAULT;
    1369          84 :     stateStack[0].nBeginDepth = 0;
    1370             : 
    1371         168 :     std::vector<char> aBuf(PARSER_BUF_SIZE);
    1372          84 :     int nDone = 0;
    1373           8 :     do
    1374             :     {
    1375          92 :         nDataHandlerCounter = 0;
    1376             :         unsigned int nLen =
    1377          92 :             (unsigned int)VSIFReadL(aBuf.data(), 1, aBuf.size(), fp);
    1378          92 :         nDone = (nLen < aBuf.size());
    1379          92 :         if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR)
    1380             :         {
    1381           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1382             :                      "XML parsing of %s file failed: %s", pszSheetFilename,
    1383           0 :                      OGRExpatGetParseError(oParser));
    1384           0 :             bStopParsing = true;
    1385             :         }
    1386          92 :         nWithoutEventCounter++;
    1387          92 :     } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);
    1388             : 
    1389          84 :     XML_ParserFree(oParser);
    1390          84 :     oParser = nullptr;
    1391             : 
    1392          84 :     if (nWithoutEventCounter == 10)
    1393             :     {
    1394           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1395             :                  "Too much data inside one element. File probably corrupted");
    1396           0 :         bStopParsing = true;
    1397             :     }
    1398             : 
    1399          84 :     VSIFCloseL(fp);
    1400             : 
    1401          84 :     bUpdated = bUpdatedBackup;
    1402          84 :     poLayer->m_osCols = m_osCols;
    1403             : }
    1404             : 
    1405             : /************************************************************************/
    1406             : /*                         startElementSSCbk()                          */
    1407             : /************************************************************************/
    1408             : 
    1409        1249 : static void XMLCALL startElementSSCbk(void *pUserData, const char *pszNameIn,
    1410             :                                       const char **ppszAttr)
    1411             : {
    1412        1249 :     ((OGRXLSXDataSource *)pUserData)->startElementSSCbk(pszNameIn, ppszAttr);
    1413        1249 : }
    1414             : 
    1415        1249 : void OGRXLSXDataSource::startElementSSCbk(const char *pszNameIn,
    1416             :                                           CPL_UNUSED const char **ppszAttr)
    1417             : {
    1418        1249 :     if (bStopParsing)
    1419           0 :         return;
    1420             : 
    1421        1249 :     pszNameIn = GetUnprefixed(pszNameIn);
    1422             : 
    1423        1249 :     nWithoutEventCounter = 0;
    1424        1249 :     switch (stateStack[nStackDepth].eVal)
    1425             :     {
    1426         627 :         case STATE_DEFAULT:
    1427             :         {
    1428         627 :             if (strcmp(pszNameIn, "si") == 0)
    1429             :             {
    1430         595 :                 PushState(STATE_SI);
    1431         595 :                 osCurrentString = "";
    1432             :             }
    1433         627 :             break;
    1434             :         }
    1435         622 :         case STATE_SI:
    1436             :         {
    1437         622 :             if (strcmp(pszNameIn, "t") == 0)
    1438             :             {
    1439         598 :                 PushState(STATE_T);
    1440             :             }
    1441         622 :             break;
    1442             :         }
    1443           0 :         default:
    1444           0 :             break;
    1445             :     }
    1446        1249 :     nDepth++;
    1447             : }
    1448             : 
    1449             : /************************************************************************/
    1450             : /*                          endElementSSCbk()                           */
    1451             : /************************************************************************/
    1452             : 
    1453        1249 : static void XMLCALL endElementSSCbk(void *pUserData, const char *pszNameIn)
    1454             : {
    1455        1249 :     ((OGRXLSXDataSource *)pUserData)->endElementSSCbk(pszNameIn);
    1456        1249 : }
    1457             : 
    1458        1249 : void OGRXLSXDataSource::endElementSSCbk(const char * /*pszNameIn*/)
    1459             : {
    1460        1249 :     if (bStopParsing)
    1461           0 :         return;
    1462             : 
    1463             :     // If we were to use pszNameIn, then we need:
    1464             :     // pszNameIn = GetUnprefixed(pszNameIn);
    1465             : 
    1466        1249 :     nWithoutEventCounter = 0;
    1467             : 
    1468        1249 :     nDepth--;
    1469        1249 :     switch (stateStack[nStackDepth].eVal)
    1470             :     {
    1471          32 :         case STATE_DEFAULT:
    1472          32 :             break;
    1473         598 :         case STATE_T:
    1474         598 :             break;
    1475         619 :         case STATE_SI:
    1476             :         {
    1477         619 :             if (stateStack[nStackDepth].nBeginDepth == nDepth)
    1478             :             {
    1479         595 :                 apoSharedStrings.push_back(osCurrentString);
    1480             :             }
    1481         619 :             break;
    1482             :         }
    1483           0 :         default:
    1484           0 :             break;
    1485             :     }
    1486             : 
    1487        1249 :     if (stateStack[nStackDepth].nBeginDepth == nDepth)
    1488        1225 :         nStackDepth--;
    1489             : }
    1490             : 
    1491             : /************************************************************************/
    1492             : /*                          dataHandlerSSCbk()                          */
    1493             : /************************************************************************/
    1494             : 
    1495        1104 : static void XMLCALL dataHandlerSSCbk(void *pUserData, const char *data,
    1496             :                                      int nLen)
    1497             : {
    1498        1104 :     ((OGRXLSXDataSource *)pUserData)->dataHandlerSSCbk(data, nLen);
    1499        1104 : }
    1500             : 
    1501        1104 : void OGRXLSXDataSource::dataHandlerSSCbk(const char *data, int nLen)
    1502             : {
    1503        1104 :     if (bStopParsing)
    1504           0 :         return;
    1505             : 
    1506        1104 :     nDataHandlerCounter++;
    1507        1104 :     if (nDataHandlerCounter >= PARSER_BUF_SIZE)
    1508             :     {
    1509           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1510             :                  "File probably corrupted (million laugh pattern)");
    1511           0 :         XML_StopParser(oParser, XML_FALSE);
    1512           0 :         bStopParsing = true;
    1513           0 :         return;
    1514             :     }
    1515             : 
    1516        1104 :     nWithoutEventCounter = 0;
    1517             : 
    1518        1104 :     switch (stateStack[nStackDepth].eVal)
    1519             :     {
    1520         162 :         case STATE_DEFAULT:
    1521         162 :             break;
    1522         298 :         case STATE_SI:
    1523         298 :             break;
    1524         644 :         case STATE_T:
    1525         644 :             osCurrentString.append(data, nLen);
    1526         644 :             break;
    1527           0 :         default:
    1528           0 :             break;
    1529             :     }
    1530             : }
    1531             : 
    1532             : /************************************************************************/
    1533             : /*                        AnalyseSharedStrings()                        */
    1534             : /************************************************************************/
    1535             : 
    1536          34 : void OGRXLSXDataSource::AnalyseSharedStrings(VSILFILE *fpSharedStrings)
    1537             : {
    1538          34 :     if (fpSharedStrings == nullptr)
    1539           2 :         return;
    1540             : 
    1541          32 :     oParser = OGRCreateExpatXMLParser();
    1542          32 :     XML_SetElementHandler(oParser, OGRXLSX::startElementSSCbk,
    1543             :                           OGRXLSX::endElementSSCbk);
    1544          32 :     XML_SetCharacterDataHandler(oParser, OGRXLSX::dataHandlerSSCbk);
    1545          32 :     XML_SetUserData(oParser, this);
    1546             : 
    1547          32 :     VSIFSeekL(fpSharedStrings, 0, SEEK_SET);
    1548             : 
    1549          32 :     bStopParsing = false;
    1550          32 :     nWithoutEventCounter = 0;
    1551          32 :     nDataHandlerCounter = 0;
    1552          32 :     nStackDepth = 0;
    1553          32 :     nDepth = 0;
    1554          32 :     stateStack[0].eVal = STATE_DEFAULT;
    1555          32 :     stateStack[0].nBeginDepth = 0;
    1556             : 
    1557          64 :     std::vector<char> aBuf(PARSER_BUF_SIZE);
    1558          32 :     int nDone = 0;
    1559           0 :     do
    1560             :     {
    1561          32 :         nDataHandlerCounter = 0;
    1562          32 :         unsigned int nLen = (unsigned int)VSIFReadL(aBuf.data(), 1, aBuf.size(),
    1563          32 :                                                     fpSharedStrings);
    1564          32 :         nDone = (nLen < aBuf.size());
    1565          32 :         if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR)
    1566             :         {
    1567           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1568             :                      "XML parsing of sharedStrings.xml file failed: %s",
    1569           0 :                      OGRExpatGetParseError(oParser));
    1570           0 :             bStopParsing = true;
    1571             :         }
    1572          32 :         nWithoutEventCounter++;
    1573          32 :     } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);
    1574             : 
    1575          32 :     XML_ParserFree(oParser);
    1576          32 :     oParser = nullptr;
    1577             : 
    1578          32 :     if (nWithoutEventCounter == 10)
    1579             :     {
    1580           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1581             :                  "Too much data inside one element. File probably corrupted");
    1582           0 :         bStopParsing = true;
    1583             :     }
    1584             : 
    1585          32 :     VSIFCloseL(fpSharedStrings);
    1586             : }
    1587             : 
    1588             : /************************************************************************/
    1589             : /*                       startElementWBRelsCbk()                        */
    1590             : /************************************************************************/
    1591             : 
    1592         251 : static void XMLCALL startElementWBRelsCbk(void *pUserData,
    1593             :                                           const char *pszNameIn,
    1594             :                                           const char **ppszAttr)
    1595             : {
    1596             :     ((OGRXLSXDataSource *)pUserData)
    1597         251 :         ->startElementWBRelsCbk(pszNameIn, ppszAttr);
    1598         251 : }
    1599             : 
    1600         251 : void OGRXLSXDataSource::startElementWBRelsCbk(const char *pszNameIn,
    1601             :                                               const char **ppszAttr)
    1602             : {
    1603         251 :     if (bStopParsing)
    1604           0 :         return;
    1605             : 
    1606         251 :     pszNameIn = GetUnprefixed(pszNameIn);
    1607             : 
    1608         251 :     nWithoutEventCounter = 0;
    1609         251 :     if (strcmp(pszNameIn, "Relationship") == 0)
    1610             :     {
    1611         217 :         const char *pszId = GetAttributeValue(ppszAttr, "Id", nullptr);
    1612         217 :         const char *pszType = GetAttributeValue(ppszAttr, "Type", nullptr);
    1613         217 :         const char *pszTarget = GetAttributeValue(ppszAttr, "Target", nullptr);
    1614         217 :         if (pszId && pszType && pszTarget &&
    1615         217 :             strstr(pszType, "/worksheet") != nullptr)
    1616             :         {
    1617         149 :             oMapRelsIdToTarget[pszId] = pszTarget;
    1618             :         }
    1619             :     }
    1620             : }
    1621             : 
    1622             : /************************************************************************/
    1623             : /*                        AnalyseWorkbookRels()                         */
    1624             : /************************************************************************/
    1625             : 
    1626          34 : void OGRXLSXDataSource::AnalyseWorkbookRels(VSILFILE *fpWorkbookRels)
    1627             : {
    1628          34 :     oParser = OGRCreateExpatXMLParser();
    1629          34 :     XML_SetElementHandler(oParser, OGRXLSX::startElementWBRelsCbk, nullptr);
    1630          34 :     XML_SetUserData(oParser, this);
    1631             : 
    1632          34 :     VSIFSeekL(fpWorkbookRels, 0, SEEK_SET);
    1633             : 
    1634          34 :     bStopParsing = false;
    1635          34 :     nWithoutEventCounter = 0;
    1636          34 :     nDataHandlerCounter = 0;
    1637             : 
    1638          68 :     std::vector<char> aBuf(PARSER_BUF_SIZE);
    1639          34 :     int nDone = 0;
    1640           0 :     do
    1641             :     {
    1642          34 :         nDataHandlerCounter = 0;
    1643          34 :         unsigned int nLen = (unsigned int)VSIFReadL(aBuf.data(), 1, aBuf.size(),
    1644          34 :                                                     fpWorkbookRels);
    1645          34 :         nDone = (nLen < aBuf.size());
    1646          34 :         if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR)
    1647             :         {
    1648           0 :             CPLError(
    1649             :                 CE_Failure, CPLE_AppDefined,
    1650             :                 "XML parsing of xl/_rels/workbook.xml.rels file failed: %s",
    1651           0 :                 OGRExpatGetParseError(oParser));
    1652           0 :             bStopParsing = true;
    1653             :         }
    1654          34 :         nWithoutEventCounter++;
    1655          34 :     } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);
    1656             : 
    1657          34 :     XML_ParserFree(oParser);
    1658          34 :     oParser = nullptr;
    1659             : 
    1660          34 :     if (nWithoutEventCounter == 10)
    1661             :     {
    1662           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1663             :                  "Too much data inside one element. File probably corrupted");
    1664           0 :         bStopParsing = true;
    1665             :     }
    1666             : 
    1667          34 :     VSIFCloseL(fpWorkbookRels);
    1668          34 : }
    1669             : 
    1670             : /************************************************************************/
    1671             : /*                         startElementWBCbk()                          */
    1672             : /************************************************************************/
    1673             : 
    1674         404 : static void XMLCALL startElementWBCbk(void *pUserData, const char *pszNameIn,
    1675             :                                       const char **ppszAttr)
    1676             : {
    1677         404 :     ((OGRXLSXDataSource *)pUserData)->startElementWBCbk(pszNameIn, ppszAttr);
    1678         404 : }
    1679             : 
    1680         404 : void OGRXLSXDataSource::startElementWBCbk(const char *pszNameIn,
    1681             :                                           const char **ppszAttr)
    1682             : {
    1683         404 :     if (bStopParsing)
    1684           0 :         return;
    1685             : 
    1686         404 :     pszNameIn = GetUnprefixed(pszNameIn);
    1687             : 
    1688         404 :     nWithoutEventCounter = 0;
    1689         404 :     if (strcmp(pszNameIn, "sheet") == 0)
    1690             :     {
    1691         149 :         const char *pszSheetName = GetAttributeValue(ppszAttr, "name", nullptr);
    1692         149 :         const char *pszId = GetAttributeValue(ppszAttr, "r:id", nullptr);
    1693         298 :         if (pszSheetName && pszId &&
    1694         596 :             oMapRelsIdToTarget.find(pszId) != oMapRelsIdToTarget.end() &&
    1695         298 :             m_oSetSheetId.find(pszId) == m_oSetSheetId.end())
    1696             :         {
    1697         149 :             const auto &osTarget(oMapRelsIdToTarget[pszId]);
    1698         149 :             m_oSetSheetId.insert(pszId);
    1699         149 :             CPLString osFilename;
    1700         149 :             if (osTarget.empty())
    1701           0 :                 return;
    1702         149 :             if (osTarget[0] == '/')
    1703             :             {
    1704           2 :                 int nIdx = 1;
    1705           2 :                 while (osTarget[nIdx] == '/')
    1706           0 :                     nIdx++;
    1707           2 :                 if (osTarget[nIdx] == '\0')
    1708           0 :                     return;
    1709             :                 // Is it an "absolute" path ?
    1710           2 :                 osFilename = osPrefixedFilename + osTarget;
    1711             :             }
    1712             :             else
    1713             :             {
    1714             :                 // or relative to the /xl subdirectory
    1715         147 :                 osFilename = osPrefixedFilename + CPLString("/xl/") + osTarget;
    1716             :             }
    1717         298 :             papoLayers = (OGRXLSXLayer **)CPLRealloc(
    1718         149 :                 papoLayers, (nLayers + 1) * sizeof(OGRXLSXLayer *));
    1719         149 :             papoLayers[nLayers++] =
    1720         149 :                 new OGRXLSXLayer(this, osFilename, pszSheetName);
    1721             :         }
    1722             :     }
    1723             : }
    1724             : 
    1725             : /************************************************************************/
    1726             : /*                          AnalyseWorkbook()                           */
    1727             : /************************************************************************/
    1728             : 
    1729          34 : void OGRXLSXDataSource::AnalyseWorkbook(VSILFILE *fpWorkbook)
    1730             : {
    1731          34 :     oParser = OGRCreateExpatXMLParser();
    1732          34 :     XML_SetElementHandler(oParser, OGRXLSX::startElementWBCbk, nullptr);
    1733          34 :     XML_SetUserData(oParser, this);
    1734             : 
    1735          34 :     VSIFSeekL(fpWorkbook, 0, SEEK_SET);
    1736             : 
    1737          34 :     bStopParsing = false;
    1738          34 :     nWithoutEventCounter = 0;
    1739          34 :     nDataHandlerCounter = 0;
    1740             : 
    1741          68 :     std::vector<char> aBuf(PARSER_BUF_SIZE);
    1742          34 :     int nDone = 0;
    1743           0 :     do
    1744             :     {
    1745          34 :         nDataHandlerCounter = 0;
    1746             :         unsigned int nLen =
    1747          34 :             (unsigned int)VSIFReadL(aBuf.data(), 1, aBuf.size(), fpWorkbook);
    1748          34 :         nDone = (nLen < aBuf.size());
    1749          34 :         if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR)
    1750             :         {
    1751           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1752             :                      "XML parsing of workbook.xml file failed: %s",
    1753           0 :                      OGRExpatGetParseError(oParser));
    1754           0 :             bStopParsing = true;
    1755             :         }
    1756          34 :         nWithoutEventCounter++;
    1757          34 :     } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);
    1758             : 
    1759          34 :     XML_ParserFree(oParser);
    1760          34 :     oParser = nullptr;
    1761             : 
    1762          34 :     if (nWithoutEventCounter == 10)
    1763             :     {
    1764           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1765             :                  "Too much data inside one element. File probably corrupted");
    1766           0 :         bStopParsing = true;
    1767             :     }
    1768             : 
    1769          34 :     VSIFCloseL(fpWorkbook);
    1770          34 : }
    1771             : 
    1772             : /************************************************************************/
    1773             : /*                       startElementStylesCbk()                        */
    1774             : /************************************************************************/
    1775             : 
    1776        1847 : static void XMLCALL startElementStylesCbk(void *pUserData,
    1777             :                                           const char *pszNameIn,
    1778             :                                           const char **ppszAttr)
    1779             : {
    1780             :     ((OGRXLSXDataSource *)pUserData)
    1781        1847 :         ->startElementStylesCbk(pszNameIn, ppszAttr);
    1782        1847 : }
    1783             : 
    1784        1847 : void OGRXLSXDataSource::startElementStylesCbk(const char *pszNameIn,
    1785             :                                               const char **ppszAttr)
    1786             : {
    1787        1847 :     if (bStopParsing)
    1788           0 :         return;
    1789             : 
    1790        1847 :     pszNameIn = GetUnprefixed(pszNameIn);
    1791             : 
    1792        1847 :     nWithoutEventCounter = 0;
    1793        1847 :     if (strcmp(pszNameIn, "numFmt") == 0)
    1794             :     {
    1795             :         const char *pszFormatCode =
    1796         176 :             GetAttributeValue(ppszAttr, "formatCode", nullptr);
    1797         176 :         const char *pszNumFmtId = GetAttributeValue(ppszAttr, "numFmtId", "-1");
    1798         176 :         int nNumFmtId = atoi(pszNumFmtId);
    1799         176 :         if (pszFormatCode && nNumFmtId >= 164)
    1800             :         {
    1801         465 :             int bHasDate = strstr(pszFormatCode, "DD") != nullptr ||
    1802         113 :                            strstr(pszFormatCode, "dd") != nullptr ||
    1803         402 :                            strstr(pszFormatCode, "YY") != nullptr ||
    1804         113 :                            strstr(pszFormatCode, "yy") != nullptr;
    1805         291 :             int bHasTime = strstr(pszFormatCode, "HH") != nullptr ||
    1806         115 :                            strstr(pszFormatCode, "hh") != nullptr;
    1807         176 :             if (bHasDate && bHasTime)
    1808          37 :                 apoMapStyleFormats[nNumFmtId] = XLSXFieldTypeExtended(
    1809             :                     OFTDateTime,
    1810          62 :                     strstr(pszFormatCode, "SS.000") != nullptr ||
    1811          25 :                         strstr(pszFormatCode, "ss.000") != nullptr);
    1812         139 :             else if (bHasDate)
    1813          26 :                 apoMapStyleFormats[nNumFmtId] = XLSXFieldTypeExtended(OFTDate);
    1814         113 :             else if (bHasTime)
    1815          24 :                 apoMapStyleFormats[nNumFmtId] = XLSXFieldTypeExtended(OFTTime);
    1816             :             else
    1817          89 :                 apoMapStyleFormats[nNumFmtId] = XLSXFieldTypeExtended(OFTReal);
    1818             :         }
    1819             :     }
    1820        1671 :     else if (strcmp(pszNameIn, "cellXfs") == 0)
    1821             :     {
    1822          32 :         bInCellXFS = true;
    1823             :     }
    1824        1639 :     else if (bInCellXFS && strcmp(pszNameIn, "xf") == 0)
    1825             :     {
    1826         219 :         const char *pszNumFmtId = GetAttributeValue(ppszAttr, "numFmtId", "-1");
    1827         219 :         int nNumFmtId = atoi(pszNumFmtId);
    1828         219 :         XLSXFieldTypeExtended eType(OFTReal);
    1829         219 :         if (nNumFmtId >= 0)
    1830             :         {
    1831         219 :             if (nNumFmtId < 164)
    1832             :             {
    1833             :                 // From
    1834             :                 // http://social.msdn.microsoft.com/Forums/en-US/oxmlsdk/thread/e27aaf16-b900-4654-8210-83c5774a179c/
    1835           7 :                 if (nNumFmtId >= 14 && nNumFmtId <= 17)
    1836           0 :                     eType = XLSXFieldTypeExtended(OFTDate);
    1837           7 :                 else if (nNumFmtId >= 18 && nNumFmtId <= 21)
    1838           0 :                     eType = XLSXFieldTypeExtended(OFTTime);
    1839           7 :                 else if (nNumFmtId == 22)
    1840           0 :                     eType = XLSXFieldTypeExtended(OFTDateTime);
    1841             :             }
    1842             :             else
    1843             :             {
    1844             :                 std::map<int, XLSXFieldTypeExtended>::iterator oIter =
    1845         212 :                     apoMapStyleFormats.find(nNumFmtId);
    1846         212 :                 if (oIter != apoMapStyleFormats.end())
    1847         212 :                     eType = oIter->second;
    1848             :                 else
    1849           0 :                     CPLDebug("XLSX",
    1850             :                              "Cannot find entry in <numFmts> with numFmtId=%d",
    1851             :                              nNumFmtId);
    1852             :             }
    1853             :         }
    1854             : #if DEBUG_VERBOSE
    1855             :         printf("style[%lu] = %d\n", /*ok*/
    1856             :                apoStyles.size(), static_cast<int>(eType.eType));
    1857             : #endif
    1858             : 
    1859         219 :         apoStyles.push_back(eType);
    1860             :     }
    1861             : }
    1862             : 
    1863             : /************************************************************************/
    1864             : /*                        endElementStylesCbk()                         */
    1865             : /************************************************************************/
    1866             : 
    1867        1847 : static void XMLCALL endElementStylesCbk(void *pUserData, const char *pszNameIn)
    1868             : {
    1869        1847 :     ((OGRXLSXDataSource *)pUserData)->endElementStylesCbk(pszNameIn);
    1870        1847 : }
    1871             : 
    1872        1847 : void OGRXLSXDataSource::endElementStylesCbk(const char *pszNameIn)
    1873             : {
    1874        1847 :     if (bStopParsing)
    1875           0 :         return;
    1876             : 
    1877        1847 :     pszNameIn = GetUnprefixed(pszNameIn);
    1878             : 
    1879        1847 :     nWithoutEventCounter = 0;
    1880        1847 :     if (strcmp(pszNameIn, "cellXfs") == 0)
    1881             :     {
    1882          32 :         bInCellXFS = false;
    1883             :     }
    1884             : }
    1885             : 
    1886             : /************************************************************************/
    1887             : /*                           AnalyseStyles()                            */
    1888             : /************************************************************************/
    1889             : 
    1890          34 : void OGRXLSXDataSource::AnalyseStyles(VSILFILE *fpStyles)
    1891             : {
    1892          34 :     if (fpStyles == nullptr)
    1893           2 :         return;
    1894             : 
    1895          32 :     oParser = OGRCreateExpatXMLParser();
    1896          32 :     XML_SetElementHandler(oParser, OGRXLSX::startElementStylesCbk,
    1897             :                           OGRXLSX::endElementStylesCbk);
    1898          32 :     XML_SetUserData(oParser, this);
    1899             : 
    1900          32 :     VSIFSeekL(fpStyles, 0, SEEK_SET);
    1901             : 
    1902          32 :     bStopParsing = false;
    1903          32 :     nWithoutEventCounter = 0;
    1904          32 :     nDataHandlerCounter = 0;
    1905          32 :     bInCellXFS = false;
    1906             : 
    1907          64 :     std::vector<char> aBuf(PARSER_BUF_SIZE);
    1908          32 :     int nDone = 0;
    1909           0 :     do
    1910             :     {
    1911          32 :         nDataHandlerCounter = 0;
    1912             :         unsigned int nLen =
    1913          32 :             (unsigned int)VSIFReadL(aBuf.data(), 1, aBuf.size(), fpStyles);
    1914          32 :         nDone = (nLen < aBuf.size());
    1915          32 :         if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR)
    1916             :         {
    1917           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1918             :                      "XML parsing of styles.xml file failed: %s",
    1919           0 :                      OGRExpatGetParseError(oParser));
    1920           0 :             bStopParsing = true;
    1921             :         }
    1922          32 :         nWithoutEventCounter++;
    1923          32 :     } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);
    1924             : 
    1925          32 :     XML_ParserFree(oParser);
    1926          32 :     oParser = nullptr;
    1927             : 
    1928          32 :     if (nWithoutEventCounter == 10)
    1929             :     {
    1930           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1931             :                  "Too much data inside one element. File probably corrupted");
    1932           0 :         bStopParsing = true;
    1933             :     }
    1934             : 
    1935          32 :     VSIFCloseL(fpStyles);
    1936             : }
    1937             : 
    1938             : /************************************************************************/
    1939             : /*                            ICreateLayer()                            */
    1940             : /************************************************************************/
    1941             : 
    1942             : OGRLayer *
    1943          18 : OGRXLSXDataSource::ICreateLayer(const char *pszLayerName,
    1944             :                                 const OGRGeomFieldDefn * /*poGeomFieldDefn*/,
    1945             :                                 CSLConstList papszOptions)
    1946             : 
    1947             : {
    1948             :     /* -------------------------------------------------------------------- */
    1949             :     /*      Verify we are in update mode.                                   */
    1950             :     /* -------------------------------------------------------------------- */
    1951          18 :     if (!bUpdatable)
    1952             :     {
    1953           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
    1954             :                  "Data source %s opened read-only.\n"
    1955             :                  "New layer %s cannot be created.\n",
    1956             :                  pszName, pszLayerName);
    1957             : 
    1958           0 :         return nullptr;
    1959             :     }
    1960             : 
    1961             :     /* -------------------------------------------------------------------- */
    1962             :     /*      Do we already have this layer?  If so, should we blow it        */
    1963             :     /*      away?                                                           */
    1964             :     /* -------------------------------------------------------------------- */
    1965          50 :     for (int iLayer = 0; iLayer < nLayers; iLayer++)
    1966             :     {
    1967          32 :         if (EQUAL(pszLayerName, papoLayers[iLayer]->GetName()))
    1968             :         {
    1969           0 :             if (CSLFetchNameValue(papszOptions, "OVERWRITE") != nullptr &&
    1970           0 :                 !EQUAL(CSLFetchNameValue(papszOptions, "OVERWRITE"), "NO"))
    1971             :             {
    1972           0 :                 DeleteLayer(pszLayerName);
    1973             :             }
    1974             :             else
    1975             :             {
    1976           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1977             :                          "Layer %s already exists, CreateLayer failed.\n"
    1978             :                          "Use the layer creation option OVERWRITE=YES to "
    1979             :                          "replace it.",
    1980             :                          pszLayerName);
    1981           0 :                 return nullptr;
    1982             :             }
    1983             :         }
    1984             :     }
    1985             : 
    1986             :     /* -------------------------------------------------------------------- */
    1987             :     /*      Create the layer object.                                        */
    1988             :     /* -------------------------------------------------------------------- */
    1989             :     OGRXLSXLayer *poLayer =
    1990             :         new OGRXLSXLayer(this,
    1991          36 :                          CPLSPrintf("/vsizip/%s/xl/worksheets/sheet%d.xml",
    1992          18 :                                     pszName, nLayers + 1),
    1993          18 :                          pszLayerName, TRUE);
    1994             : 
    1995          36 :     papoLayers = (OGRXLSXLayer **)CPLRealloc(
    1996          18 :         papoLayers, (nLayers + 1) * sizeof(OGRXLSXLayer *));
    1997          18 :     papoLayers[nLayers] = poLayer;
    1998          18 :     nLayers++;
    1999             : 
    2000          18 :     bUpdated = true;
    2001             : 
    2002          18 :     return poLayer;
    2003             : }
    2004             : 
    2005             : /************************************************************************/
    2006             : /*                            DeleteLayer()                             */
    2007             : /************************************************************************/
    2008             : 
    2009           0 : void OGRXLSXDataSource::DeleteLayer(const char *pszLayerName)
    2010             : 
    2011             : {
    2012             :     /* -------------------------------------------------------------------- */
    2013             :     /*      Verify we are in update mode.                                   */
    2014             :     /* -------------------------------------------------------------------- */
    2015           0 :     if (!bUpdatable)
    2016             :     {
    2017           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
    2018             :                  "Data source %s opened read-only.\n"
    2019             :                  "Layer %s cannot be deleted.\n",
    2020             :                  pszName, pszLayerName);
    2021             : 
    2022           0 :         return;
    2023             :     }
    2024             : 
    2025             :     /* -------------------------------------------------------------------- */
    2026             :     /*      Try to find layer.                                              */
    2027             :     /* -------------------------------------------------------------------- */
    2028           0 :     int iLayer = 0;
    2029           0 :     for (; iLayer < nLayers; iLayer++)
    2030             :     {
    2031           0 :         if (EQUAL(pszLayerName, papoLayers[iLayer]->GetName()))
    2032           0 :             break;
    2033             :     }
    2034             : 
    2035           0 :     if (iLayer == nLayers)
    2036             :     {
    2037           0 :         CPLError(
    2038             :             CE_Failure, CPLE_AppDefined,
    2039             :             "Attempt to delete layer '%s', but this layer is not known to OGR.",
    2040             :             pszLayerName);
    2041           0 :         return;
    2042             :     }
    2043             : 
    2044           0 :     DeleteLayer(iLayer);
    2045             : }
    2046             : 
    2047             : /************************************************************************/
    2048             : /*                            DeleteLayer()                             */
    2049             : /************************************************************************/
    2050             : 
    2051           0 : OGRErr OGRXLSXDataSource::DeleteLayer(int iLayer)
    2052             : {
    2053           0 :     if (iLayer < 0 || iLayer >= nLayers)
    2054             :     {
    2055           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2056             :                  "Layer %d not in legal range of 0 to %d.", iLayer,
    2057           0 :                  nLayers - 1);
    2058           0 :         return OGRERR_FAILURE;
    2059             :     }
    2060             : 
    2061             :     /* -------------------------------------------------------------------- */
    2062             :     /*      Blow away our OGR structures related to the layer.  This is     */
    2063             :     /*      pretty dangerous if anything has a reference to this layer!     */
    2064             :     /* -------------------------------------------------------------------- */
    2065             : 
    2066           0 :     delete papoLayers[iLayer];
    2067           0 :     memmove(papoLayers + iLayer, papoLayers + iLayer + 1,
    2068           0 :             sizeof(void *) * (nLayers - iLayer - 1));
    2069           0 :     nLayers--;
    2070             : 
    2071           0 :     bUpdated = true;
    2072             : 
    2073           0 :     return OGRERR_NONE;
    2074             : }
    2075             : 
    2076             : /************************************************************************/
    2077             : /*                           WriteOverride()                            */
    2078             : /************************************************************************/
    2079             : 
    2080         114 : static void WriteOverride(VSILFILE *fp, const char *pszPartName,
    2081             :                           const char *pszContentType)
    2082             : {
    2083         114 :     VSIFPrintfL(fp, "<Override PartName=\"%s\" ContentType=\"%s\"/>\n",
    2084             :                 pszPartName, pszContentType);
    2085         114 : }
    2086             : 
    2087             : static const char XML_HEADER[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
    2088             : static const char MAIN_NS[] =
    2089             :     "xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"";
    2090             : static const char SCHEMA_OD[] =
    2091             :     "http://schemas.openxmlformats.org/officeDocument/2006";
    2092             : static const char SCHEMA_OD_RS[] =
    2093             :     "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
    2094             : static const char SCHEMA_PACKAGE[] =
    2095             :     "http://schemas.openxmlformats.org/package/2006";
    2096             : static const char SCHEMA_PACKAGE_RS[] =
    2097             :     "http://schemas.openxmlformats.org/package/2006/relationships";
    2098             : 
    2099             : /************************************************************************/
    2100             : /*                         WriteContentTypes()                          */
    2101             : /************************************************************************/
    2102             : 
    2103          12 : static bool WriteContentTypes(const char *pszName, int nLayers)
    2104             : {
    2105             :     CPLString osTmpFilename(
    2106          24 :         CPLSPrintf("/vsizip/%s/[Content_Types].xml", pszName));
    2107          12 :     VSILFILE *fp = VSIFOpenL(osTmpFilename, "wb");
    2108          12 :     if (!fp)
    2109           0 :         return false;
    2110             :     // TODO(schwehr): Convert all strlen(XML_HEADER) to constexpr with
    2111             :     // switch to C++11 or newer.
    2112          12 :     VSIFWriteL(XML_HEADER, strlen(XML_HEADER), 1, fp);
    2113          12 :     VSIFPrintfL(fp, "<Types xmlns=\"%s/content-types\">\n", SCHEMA_PACKAGE);
    2114          12 :     WriteOverride(fp, "/_rels/.rels",
    2115             :                   "application/vnd.openxmlformats-package.relationships+xml");
    2116          12 :     WriteOverride(fp, "/docProps/core.xml",
    2117             :                   "application/vnd.openxmlformats-package.core-properties+xml");
    2118          12 :     WriteOverride(fp, "/docProps/app.xml",
    2119             :                   "application/"
    2120             :                   "vnd.openxmlformats-officedocument.extended-properties+xml");
    2121          12 :     WriteOverride(fp, "/xl/_rels/workbook.xml.rels",
    2122             :                   "application/vnd.openxmlformats-package.relationships+xml");
    2123          42 :     for (int i = 0; i < nLayers; i++)
    2124             :     {
    2125          30 :         WriteOverride(
    2126             :             fp, CPLSPrintf("/xl/worksheets/sheet%d.xml", i + 1),
    2127             :             "application/"
    2128             :             "vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml");
    2129             :     }
    2130          12 :     WriteOverride(fp, "/xl/styles.xml",
    2131             :                   "application/"
    2132             :                   "vnd.openxmlformats-officedocument.spreadsheetml.styles+xml");
    2133          12 :     WriteOverride(
    2134             :         fp, "/xl/workbook.xml",
    2135             :         "application/"
    2136             :         "vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml");
    2137          12 :     WriteOverride(
    2138             :         fp, "/xl/sharedStrings.xml",
    2139             :         "application/"
    2140             :         "vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml");
    2141          12 :     VSIFPrintfL(fp, "</Types>\n");
    2142          12 :     VSIFCloseL(fp);
    2143          12 :     return true;
    2144             : }
    2145             : 
    2146             : /************************************************************************/
    2147             : /*                              WriteApp()                              */
    2148             : /************************************************************************/
    2149             : 
    2150          12 : static bool WriteApp(const char *pszName)
    2151             : {
    2152          24 :     CPLString osTmpFilename(CPLSPrintf("/vsizip/%s/docProps/app.xml", pszName));
    2153          12 :     VSILFILE *fp = VSIFOpenL(osTmpFilename, "wb");
    2154          12 :     if (!fp)
    2155           0 :         return false;
    2156          12 :     VSIFWriteL(XML_HEADER, strlen(XML_HEADER), 1, fp);
    2157          12 :     VSIFPrintfL(fp,
    2158             :                 "<Properties xmlns=\"%s/extended-properties\" "
    2159             :                 "xmlns:vt=\"%s/docPropsVTypes\">\n",
    2160             :                 SCHEMA_OD, SCHEMA_OD);
    2161          12 :     VSIFPrintfL(fp, "<TotalTime>0</TotalTime>\n");
    2162          12 :     VSIFPrintfL(fp, "</Properties>\n");
    2163          12 :     VSIFCloseL(fp);
    2164          12 :     return true;
    2165             : }
    2166             : 
    2167             : /************************************************************************/
    2168             : /*                             WriteCore()                              */
    2169             : /************************************************************************/
    2170             : 
    2171          12 : static bool WriteCore(const char *pszName)
    2172             : {
    2173             :     CPLString osTmpFilename(
    2174          24 :         CPLSPrintf("/vsizip/%s/docProps/core.xml", pszName));
    2175          12 :     VSILFILE *fp = VSIFOpenL(osTmpFilename, "wb");
    2176          12 :     if (!fp)
    2177           0 :         return false;
    2178          12 :     VSIFWriteL(XML_HEADER, strlen(XML_HEADER), 1, fp);
    2179          12 :     VSIFPrintfL(fp,
    2180             :                 "<cp:coreProperties xmlns:cp=\"%s/metadata/core-properties\" "
    2181             :                 "xmlns:dc=\"http://purl.org/dc/elements/1.1/\" "
    2182             :                 "xmlns:dcmitype=\"http://purl.org/dc/dcmitype/\" "
    2183             :                 "xmlns:dcterms=\"http://purl.org/dc/terms/\" "
    2184             :                 "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n",
    2185             :                 SCHEMA_PACKAGE);
    2186          12 :     VSIFPrintfL(fp, "<cp:revision>0</cp:revision>\n");
    2187          12 :     VSIFPrintfL(fp, "</cp:coreProperties>\n");
    2188          12 :     VSIFCloseL(fp);
    2189          12 :     return true;
    2190             : }
    2191             : 
    2192             : /************************************************************************/
    2193             : /*                           WriteWorkbook()                            */
    2194             : /************************************************************************/
    2195             : 
    2196          12 : static bool WriteWorkbook(const char *pszName, GDALDataset *poDS)
    2197             : {
    2198          24 :     CPLString osTmpFilename(CPLSPrintf("/vsizip/%s/xl/workbook.xml", pszName));
    2199          12 :     VSILFILE *fp = VSIFOpenL(osTmpFilename, "wb");
    2200          12 :     if (!fp)
    2201           0 :         return false;
    2202          12 :     VSIFWriteL(XML_HEADER, strlen(XML_HEADER), 1, fp);
    2203          12 :     VSIFPrintfL(fp, "<workbook %s xmlns:r=\"%s\">\n", MAIN_NS, SCHEMA_OD_RS);
    2204          12 :     VSIFPrintfL(fp, "<fileVersion appName=\"Calc\"/>\n");
    2205             :     /*
    2206             :     VSIFPrintfL(fp, "<workbookPr backupFile=\"false\" showObjects=\"all\"
    2207             :     date1904=\"false\"/>\n"); VSIFPrintfL(fp, "<workbookProtection/>\n");
    2208             :     VSIFPrintfL(fp, "<bookViews>\n");
    2209             :     VSIFPrintfL(fp, "<workbookView activeTab=\"0\" firstSheet=\"0\"
    2210             :     showHorizontalScroll=\"true\" " "showSheetTabs=\"true\"
    2211             :     showVerticalScroll=\"true\" tabRatio=\"600\" windowHeight=\"8192\" "
    2212             :                     "windowWidth=\"16384\" xWindow=\"0\" yWindow=\"0\"/>\n");
    2213             :     VSIFPrintfL(fp, "</bookViews>\n");
    2214             :     */
    2215          12 :     VSIFPrintfL(fp, "<sheets>\n");
    2216          42 :     for (int i = 0; i < poDS->GetLayerCount(); i++)
    2217             :     {
    2218          30 :         auto poLayer = poDS->GetLayer(i);
    2219          30 :         const char *pszLayerName = poLayer->GetName();
    2220          30 :         char *pszXML = OGRGetXML_UTF8_EscapedString(pszLayerName);
    2221          30 :         VSIFPrintfL(fp,
    2222             :                     "<sheet name=\"%s\" sheetId=\"%d\" state=\"visible\" "
    2223             :                     "r:id=\"rId%d\"/>\n",
    2224             :                     pszXML, i + 1, i + 2);
    2225          30 :         CPLFree(pszXML);
    2226             :     }
    2227          12 :     VSIFPrintfL(fp, "</sheets>\n");
    2228          12 :     VSIFPrintfL(fp, "<calcPr iterateCount=\"100\" refMode=\"A1\" "
    2229             :                     "iterate=\"false\" iterateDelta=\"0.001\"/>\n");
    2230          12 :     VSIFPrintfL(fp, "</workbook>\n");
    2231          12 :     VSIFCloseL(fp);
    2232          12 :     return true;
    2233             : }
    2234             : 
    2235             : /************************************************************************/
    2236             : /*                           BuildColString()                           */
    2237             : /************************************************************************/
    2238             : 
    2239         338 : static CPLString BuildColString(int nCol)
    2240             : {
    2241             :     /*
    2242             :     A Z   AA AZ   BA BZ   ZA   ZZ   AAA    ZZZ      AAAA
    2243             :     0 25  26 51   52 77   676  701  702    18277    18278
    2244             :     */
    2245         338 :     CPLString osRet;
    2246         338 :     osRet += (nCol % 26) + 'A';
    2247         342 :     while (nCol >= 26)
    2248             :     {
    2249           4 :         nCol /= 26;
    2250             :         // We would not need a decrement if this was a proper base 26
    2251             :         // numeration scheme.
    2252           4 :         nCol--;
    2253           4 :         osRet += (nCol % 26) + 'A';
    2254             :     }
    2255         338 :     const size_t nSize = osRet.size();
    2256         342 :     for (size_t l = 0; l < nSize / 2; l++)
    2257             :     {
    2258           4 :         char chTmp = osRet[nSize - 1 - l];
    2259           4 :         osRet[nSize - 1 - l] = osRet[l];
    2260           4 :         osRet[l] = chTmp;
    2261             :     }
    2262         338 :     return osRet;
    2263             : }
    2264             : 
    2265             : /************************************************************************/
    2266             : /*                             WriteLayer()                             */
    2267             : /************************************************************************/
    2268             : 
    2269          30 : static bool WriteLayer(const char *pszName, OGRXLSXLayer *poLayer, int iLayer,
    2270             :                        std::map<std::string, int> &oStringMap,
    2271             :                        std::vector<std::string> &oStringList)
    2272             : {
    2273             :     CPLString osTmpFilename(CPLSPrintf("/vsizip/%s/xl/worksheets/sheet%d.xml",
    2274          60 :                                        pszName, iLayer + 1));
    2275          30 :     VSILFILE *fp = VSIFOpenL(osTmpFilename, "wb");
    2276          30 :     if (!fp)
    2277           0 :         return false;
    2278          30 :     VSIFWriteL(XML_HEADER, strlen(XML_HEADER), 1, fp);
    2279          30 :     VSIFPrintfL(fp, "<worksheet %s xmlns:r=\"%s\">\n", MAIN_NS, SCHEMA_OD_RS);
    2280             :     /*
    2281             :     VSIFPrintfL(fp, "<sheetViews>\n");
    2282             :     VSIFPrintfL(fp, "<sheetView colorId=\"64\" defaultGridColor=\"true\"
    2283             :     rightToLeft=\"false\" showFormulas=\"false\" showGridLines=\"true\"
    2284             :     showOutlineSymbols=\"true\" showRowColHeaders=\"true\" showZeros=\"true\"
    2285             :     tabSelected=\"%s\" topLeftCell=\"A1\" view=\"normal\"
    2286             :     windowProtection=\"false\" workbookViewId=\"0\" zoomScale=\"100\"
    2287             :     zoomScaleNormal=\"100\" zoomScalePageLayoutView=\"60\">\n", (i == 0) ?
    2288             :     "true" : "false"); VSIFPrintfL(fp, "<selection activeCell=\"A1\"
    2289             :     activeCellId=\"0\" pane=\"topLeft\" sqref=\"A1\"/>\n"); VSIFPrintfL(fp,
    2290             :     "</sheetView>\n"); VSIFPrintfL(fp, "</sheetViews>\n");*/
    2291             : 
    2292          30 :     poLayer->ResetReading();
    2293             : 
    2294          30 :     OGRFeature *poFeature = poLayer->GetNextFeature();
    2295             : 
    2296          30 :     const OGRFeatureDefn *poFDefn = poLayer->GetLayerDefn();
    2297          30 :     bool bHasHeaders = false;
    2298          30 :     int iRow = 1;
    2299             : 
    2300          30 :     const int nFields = poFDefn->GetFieldCount();
    2301          30 :     if (nFields > 0)
    2302             :     {
    2303          28 :         VSIFPrintfL(fp, "<cols>\n");
    2304         141 :         for (int j = 0; j < nFields; j++)
    2305             :         {
    2306         113 :             int nWidth = 15;
    2307         113 :             if (poFDefn->GetFieldDefn(j)->GetType() == OFTDateTime)
    2308           9 :                 nWidth = 29;
    2309         113 :             VSIFPrintfL(fp, "<col min=\"%d\" max=\"%d\" width=\"%d\"/>\n",
    2310             :                         j + 1, 1024, nWidth);
    2311             : 
    2312         113 :             if (strcmp(poFDefn->GetFieldDefn(j)->GetNameRef(),
    2313         113 :                        CPLSPrintf("Field%d", j + 1)) != 0)
    2314          42 :                 bHasHeaders = true;
    2315             :         }
    2316          28 :         VSIFPrintfL(fp, "</cols>\n");
    2317             :     }
    2318           2 :     else if (!poLayer->GetCols().empty())
    2319           2 :         VSIFPrintfL(fp, "%s\n", poLayer->GetCols().c_str());
    2320             : 
    2321          30 :     VSIFPrintfL(fp, "<sheetData>\n");
    2322             : 
    2323          30 :     if (bHasHeaders && poFeature != nullptr)
    2324             :     {
    2325           8 :         VSIFPrintfL(fp, "<row r=\"%d\">\n", iRow);
    2326          48 :         for (int j = 0; j < nFields; j++)
    2327             :         {
    2328          40 :             const char *pszVal = poFDefn->GetFieldDefn(j)->GetNameRef();
    2329             :             std::map<std::string, int>::iterator oIter =
    2330          40 :                 oStringMap.find(pszVal);
    2331          40 :             int nStringIndex = 0;
    2332          40 :             if (oIter != oStringMap.end())
    2333           3 :                 nStringIndex = oIter->second;
    2334             :             else
    2335             :             {
    2336          37 :                 nStringIndex = (int)oStringList.size();
    2337          37 :                 oStringMap[pszVal] = nStringIndex;
    2338          37 :                 oStringList.push_back(pszVal);
    2339             :             }
    2340             : 
    2341          80 :             CPLString osCol = BuildColString(j);
    2342             : 
    2343          40 :             VSIFPrintfL(fp, "<c r=\"%s%d\" t=\"s\">\n", osCol.c_str(), iRow);
    2344          40 :             VSIFPrintfL(fp, "<v>%d</v>\n", nStringIndex);
    2345          40 :             VSIFPrintfL(fp, "</c>\n");
    2346             :         }
    2347           8 :         VSIFPrintfL(fp, "</row>\n");
    2348             : 
    2349           8 :         iRow++;
    2350             :     }
    2351             : 
    2352         173 :     while (poFeature != nullptr)
    2353             :     {
    2354         143 :         VSIFPrintfL(fp, "<row r=\"%d\">\n", iRow);
    2355         755 :         for (int j = 0; j < nFields; j++)
    2356             :         {
    2357         612 :             if (poFeature->IsFieldSetAndNotNull(j))
    2358             :             {
    2359         596 :                 CPLString osCol = BuildColString(j);
    2360             : 
    2361         298 :                 const OGRFieldDefn *poFieldDefn = poFDefn->GetFieldDefn(j);
    2362         298 :                 OGRFieldType eType = poFieldDefn->GetType();
    2363             : 
    2364         298 :                 if (eType == OFTReal)
    2365             :                 {
    2366          66 :                     VSIFPrintfL(fp, "<c r=\"%s%d\">\n", osCol.c_str(), iRow);
    2367          66 :                     VSIFPrintfL(fp, "<v>%.16g</v>\n",
    2368             :                                 poFeature->GetFieldAsDouble(j));
    2369          66 :                     VSIFPrintfL(fp, "</c>\n");
    2370             :                 }
    2371         232 :                 else if (eType == OFTInteger)
    2372             :                 {
    2373          34 :                     OGRFieldSubType eSubType = poFieldDefn->GetSubType();
    2374          34 :                     if (eSubType == OFSTBoolean)
    2375           1 :                         VSIFPrintfL(fp, "<c r=\"%s%d\" t=\"b\" s=\"5\">\n",
    2376             :                                     osCol.c_str(), iRow);
    2377             :                     else
    2378          33 :                         VSIFPrintfL(fp, "<c r=\"%s%d\">\n", osCol.c_str(),
    2379             :                                     iRow);
    2380          34 :                     VSIFPrintfL(fp, "<v>%d</v>\n",
    2381             :                                 poFeature->GetFieldAsInteger(j));
    2382          34 :                     VSIFPrintfL(fp, "</c>\n");
    2383             :                 }
    2384         198 :                 else if (eType == OFTInteger64)
    2385             :                 {
    2386          33 :                     VSIFPrintfL(fp, "<c r=\"%s%d\">\n", osCol.c_str(), iRow);
    2387          33 :                     VSIFPrintfL(fp, "<v>" CPL_FRMT_GIB "</v>\n",
    2388             :                                 poFeature->GetFieldAsInteger64(j));
    2389          33 :                     VSIFPrintfL(fp, "</c>\n");
    2390             :                 }
    2391         165 :                 else if (eType == OFTDate || eType == OFTDateTime ||
    2392             :                          eType == OFTTime)
    2393             :                 {
    2394          15 :                     int nYear = 0;
    2395          15 :                     int nMonth = 0;
    2396          15 :                     int nDay = 0;
    2397          15 :                     int nHour = 0;
    2398          15 :                     int nMinute = 0;
    2399          15 :                     int nTZFlag = 0;
    2400          15 :                     float fSecond = 0.0f;
    2401          15 :                     poFeature->GetFieldAsDateTime(j, &nYear, &nMonth, &nDay,
    2402             :                                                   &nHour, &nMinute, &fSecond,
    2403             :                                                   &nTZFlag);
    2404             :                     struct tm brokendowntime;
    2405          15 :                     memset(&brokendowntime, 0, sizeof(brokendowntime));
    2406          15 :                     brokendowntime.tm_year =
    2407          15 :                         (eType == OFTTime) ? 70 : nYear - 1900;
    2408          15 :                     brokendowntime.tm_mon = (eType == OFTTime) ? 0 : nMonth - 1;
    2409          15 :                     brokendowntime.tm_mday = (eType == OFTTime) ? 1 : nDay;
    2410          15 :                     brokendowntime.tm_hour = nHour;
    2411          15 :                     brokendowntime.tm_min = nMinute;
    2412          15 :                     brokendowntime.tm_sec = (int)fSecond;
    2413          15 :                     GIntBig nUnixTime = CPLYMDHMSToUnixTime(&brokendowntime);
    2414          15 :                     double dfNumberOfDaysSince1900 =
    2415          15 :                         (1.0 * nUnixTime / NUMBER_OF_SECONDS_PER_DAY);
    2416          15 :                     dfNumberOfDaysSince1900 +=
    2417          15 :                         fmod(fSecond, 1) / NUMBER_OF_SECONDS_PER_DAY;
    2418          28 :                     int s = (eType == OFTDate)       ? 1
    2419          13 :                             : (eType == OFTDateTime) ? 2
    2420             :                                                      : 3;
    2421          15 :                     if (eType == OFTDateTime && OGR_GET_MS(fSecond))
    2422           1 :                         s = 4;
    2423          15 :                     VSIFPrintfL(fp, "<c r=\"%s%d\" s=\"%d\">\n", osCol.c_str(),
    2424             :                                 iRow, s);
    2425          15 :                     if (eType != OFTTime)
    2426          13 :                         dfNumberOfDaysSince1900 +=
    2427             :                             NUMBER_OF_DAYS_BETWEEN_1900_AND_1970;
    2428          15 :                     if (eType == OFTDate)
    2429           2 :                         VSIFPrintfL(fp, "<v>%d</v>\n",
    2430           2 :                                     (int)(dfNumberOfDaysSince1900 + 0.1));
    2431             :                     else
    2432          13 :                         VSIFPrintfL(fp, "<v>%.16g</v>\n",
    2433             :                                     dfNumberOfDaysSince1900);
    2434          15 :                     VSIFPrintfL(fp, "</c>\n");
    2435             :                 }
    2436             :                 else
    2437             :                 {
    2438         150 :                     const char *pszVal = poFeature->GetFieldAsString(j);
    2439             :                     std::map<std::string, int>::iterator oIter =
    2440         150 :                         oStringMap.find(pszVal);
    2441         150 :                     int nStringIndex = 0;
    2442         150 :                     if (oIter != oStringMap.end())
    2443          38 :                         nStringIndex = oIter->second;
    2444             :                     else
    2445             :                     {
    2446         112 :                         nStringIndex = (int)oStringList.size();
    2447         112 :                         oStringMap[pszVal] = nStringIndex;
    2448         112 :                         oStringList.push_back(pszVal);
    2449             :                     }
    2450         150 :                     VSIFPrintfL(fp, "<c r=\"%s%d\" t=\"s\">\n", osCol.c_str(),
    2451             :                                 iRow);
    2452         150 :                     VSIFPrintfL(fp, "<v>%d</v>\n", nStringIndex);
    2453         150 :                     VSIFPrintfL(fp, "</c>\n");
    2454             :                 }
    2455             :             }
    2456             :         }
    2457         143 :         VSIFPrintfL(fp, "</row>\n");
    2458             : 
    2459         143 :         iRow++;
    2460         143 :         delete poFeature;
    2461         143 :         poFeature = poLayer->GetNextFeature();
    2462             :     }
    2463          30 :     VSIFPrintfL(fp, "</sheetData>\n");
    2464          30 :     VSIFPrintfL(fp, "</worksheet>\n");
    2465          30 :     VSIFCloseL(fp);
    2466          30 :     return true;
    2467             : }
    2468             : 
    2469             : /************************************************************************/
    2470             : /*                         WriteSharedStrings()                         */
    2471             : /************************************************************************/
    2472             : 
    2473          12 : static bool WriteSharedStrings(const char *pszName,
    2474             :                                std::vector<std::string> &oStringList)
    2475             : {
    2476             :     CPLString osTmpFilename(
    2477          24 :         CPLSPrintf("/vsizip/%s/xl/sharedStrings.xml", pszName));
    2478          12 :     VSILFILE *fp = VSIFOpenL(osTmpFilename, "wb");
    2479          12 :     if (!fp)
    2480           0 :         return false;
    2481          12 :     VSIFWriteL(XML_HEADER, strlen(XML_HEADER), 1, fp);
    2482          12 :     VSIFPrintfL(fp, "<sst %s uniqueCount=\"%d\">\n", MAIN_NS,
    2483          12 :                 (int)oStringList.size());
    2484         161 :     for (int i = 0; i < (int)oStringList.size(); i++)
    2485             :     {
    2486         149 :         VSIFPrintfL(fp, "<si>\n");
    2487         149 :         char *pszXML = OGRGetXML_UTF8_EscapedString(oStringList[i].c_str());
    2488         149 :         VSIFPrintfL(fp, "<t>%s</t>\n", pszXML);
    2489         149 :         CPLFree(pszXML);
    2490         149 :         VSIFPrintfL(fp, "</si>\n");
    2491             :     }
    2492          12 :     VSIFPrintfL(fp, "</sst>\n");
    2493          12 :     VSIFCloseL(fp);
    2494          12 :     return true;
    2495             : }
    2496             : 
    2497             : /************************************************************************/
    2498             : /*                            WriteStyles()                             */
    2499             : /************************************************************************/
    2500             : 
    2501          12 : static bool WriteStyles(const char *pszName)
    2502             : {
    2503          24 :     CPLString osTmpFilename(CPLSPrintf("/vsizip/%s/xl/styles.xml", pszName));
    2504          12 :     VSILFILE *fp = VSIFOpenL(osTmpFilename, "wb");
    2505          12 :     if (!fp)
    2506           0 :         return false;
    2507          12 :     VSIFWriteL(XML_HEADER, strlen(XML_HEADER), 1, fp);
    2508          12 :     VSIFPrintfL(fp, "<styleSheet %s>\n", MAIN_NS);
    2509          12 :     VSIFPrintfL(fp, "<numFmts count=\"4\">\n");
    2510          12 :     VSIFPrintfL(fp, "<numFmt formatCode=\"GENERAL\" numFmtId=\"164\"/>\n");
    2511          12 :     VSIFPrintfL(fp, "<numFmt formatCode=\"DD/MM/YY\" numFmtId=\"165\"/>\n");
    2512          12 :     VSIFPrintfL(
    2513             :         fp,
    2514             :         "<numFmt formatCode=\"DD/MM/YYYY\\ HH:MM:SS\" numFmtId=\"166\"/>\n");
    2515          12 :     VSIFPrintfL(fp, "<numFmt formatCode=\"HH:MM:SS\" numFmtId=\"167\"/>\n");
    2516          12 :     VSIFPrintfL(fp, "<numFmt formatCode=\"DD/MM/YYYY\\ HH:MM:SS.000\" "
    2517             :                     "numFmtId=\"168\"/>\n");
    2518          12 :     VSIFPrintfL(fp, "<numFmt "
    2519             :                     "formatCode=\"&quot;TRUE&quot;;&quot;TRUE&quot;;&quot;"
    2520             :                     "FALSE&quot;\" numFmtId=\"169\"/>\n");
    2521          12 :     VSIFPrintfL(fp, "</numFmts>\n");
    2522          12 :     VSIFPrintfL(fp, "<fonts count=\"1\">\n");
    2523          12 :     VSIFPrintfL(fp, "<font>\n");
    2524          12 :     VSIFPrintfL(fp, "<name val=\"Arial\"/>\n");
    2525          12 :     VSIFPrintfL(fp, "<family val=\"2\"/>\n");
    2526          12 :     VSIFPrintfL(fp, "<sz val=\"10\"/>\n");
    2527          12 :     VSIFPrintfL(fp, "</font>\n");
    2528          12 :     VSIFPrintfL(fp, "</fonts>\n");
    2529          12 :     VSIFPrintfL(fp, "<fills count=\"1\">\n");
    2530          12 :     VSIFPrintfL(fp, "<fill>\n");
    2531          12 :     VSIFPrintfL(fp, "<patternFill patternType=\"none\"/>\n");
    2532          12 :     VSIFPrintfL(fp, "</fill>\n");
    2533          12 :     VSIFPrintfL(fp, "</fills>\n");
    2534          12 :     VSIFPrintfL(fp, "<borders count=\"1\">\n");
    2535          12 :     VSIFPrintfL(fp, "<border diagonalDown=\"false\" diagonalUp=\"false\">\n");
    2536          12 :     VSIFPrintfL(fp, "<left/>\n");
    2537          12 :     VSIFPrintfL(fp, "<right/>\n");
    2538          12 :     VSIFPrintfL(fp, "<top/>\n");
    2539          12 :     VSIFPrintfL(fp, "<bottom/>\n");
    2540          12 :     VSIFPrintfL(fp, "<diagonal/>\n");
    2541          12 :     VSIFPrintfL(fp, "</border>\n");
    2542          12 :     VSIFPrintfL(fp, "</borders>\n");
    2543          12 :     VSIFPrintfL(fp, "<cellStyleXfs count=\"1\">\n");
    2544          12 :     VSIFPrintfL(fp, "<xf numFmtId=\"164\">\n");
    2545          12 :     VSIFPrintfL(fp, "</xf>\n");
    2546          12 :     VSIFPrintfL(fp, "</cellStyleXfs>\n");
    2547          12 :     VSIFPrintfL(fp, "<cellXfs count=\"6\">\n");
    2548          12 :     VSIFPrintfL(fp, "<xf numFmtId=\"164\" xfId=\"0\"/>\n");
    2549          12 :     VSIFPrintfL(fp, "<xf numFmtId=\"165\" xfId=\"0\"/>\n");
    2550          12 :     VSIFPrintfL(fp, "<xf numFmtId=\"166\" xfId=\"0\"/>\n");
    2551          12 :     VSIFPrintfL(fp, "<xf numFmtId=\"167\" xfId=\"0\"/>\n");
    2552          12 :     VSIFPrintfL(fp, "<xf numFmtId=\"168\" xfId=\"0\"/>\n");
    2553          12 :     VSIFPrintfL(fp, "<xf numFmtId=\"169\" xfId=\"0\"/>\n");
    2554          12 :     VSIFPrintfL(fp, "</cellXfs>\n");
    2555          12 :     VSIFPrintfL(fp, "<cellStyles count=\"1\">\n");
    2556          12 :     VSIFPrintfL(fp, "<cellStyle builtinId=\"0\" customBuiltin=\"false\" "
    2557             :                     "name=\"Normal\" xfId=\"0\"/>\n");
    2558          12 :     VSIFPrintfL(fp, "</cellStyles>\n");
    2559          12 :     VSIFPrintfL(fp, "</styleSheet>\n");
    2560          12 :     VSIFCloseL(fp);
    2561          12 :     return true;
    2562             : }
    2563             : 
    2564             : /************************************************************************/
    2565             : /*                         WriteWorkbookRels()                          */
    2566             : /************************************************************************/
    2567             : 
    2568          12 : static bool WriteWorkbookRels(const char *pszName, int nLayers)
    2569             : {
    2570             :     CPLString osTmpFilename(
    2571          24 :         CPLSPrintf("/vsizip/%s/xl/_rels/workbook.xml.rels", pszName));
    2572          12 :     VSILFILE *fp = VSIFOpenL(osTmpFilename, "wb");
    2573          12 :     if (!fp)
    2574           0 :         return false;
    2575          12 :     VSIFWriteL(XML_HEADER, strlen(XML_HEADER), 1, fp);
    2576          12 :     VSIFPrintfL(fp, "<Relationships xmlns=\"%s\">\n", SCHEMA_PACKAGE_RS);
    2577          12 :     VSIFPrintfL(fp,
    2578             :                 "<Relationship Id=\"rId1\" Type=\"%s/styles\" "
    2579             :                 "Target=\"styles.xml\"/>\n",
    2580             :                 SCHEMA_OD_RS);
    2581          42 :     for (int i = 0; i < nLayers; i++)
    2582             :     {
    2583          30 :         VSIFPrintfL(fp,
    2584             :                     "<Relationship Id=\"rId%d\" Type=\"%s/worksheet\" "
    2585             :                     "Target=\"worksheets/sheet%d.xml\"/>\n",
    2586             :                     2 + i, SCHEMA_OD_RS, 1 + i);
    2587             :     }
    2588          12 :     VSIFPrintfL(fp,
    2589             :                 "<Relationship Id=\"rId%d\" Type=\"%s/sharedStrings\" "
    2590             :                 "Target=\"sharedStrings.xml\"/>\n",
    2591             :                 2 + nLayers, SCHEMA_OD_RS);
    2592          12 :     VSIFPrintfL(fp, "</Relationships>\n");
    2593          12 :     VSIFCloseL(fp);
    2594          12 :     return true;
    2595             : }
    2596             : 
    2597             : /************************************************************************/
    2598             : /*                            WriteDotRels()                            */
    2599             : /************************************************************************/
    2600             : 
    2601          12 : static bool WriteDotRels(const char *pszName)
    2602             : {
    2603          24 :     CPLString osTmpFilename(CPLSPrintf("/vsizip/%s/_rels/.rels", pszName));
    2604          12 :     VSILFILE *fp = VSIFOpenL(osTmpFilename, "wb");
    2605          12 :     if (!fp)
    2606           0 :         return false;
    2607          12 :     VSIFWriteL(XML_HEADER, strlen(XML_HEADER), 1, fp);
    2608          12 :     VSIFPrintfL(fp, "<Relationships xmlns=\"%s\">\n", SCHEMA_PACKAGE_RS);
    2609          12 :     VSIFPrintfL(fp,
    2610             :                 "<Relationship Id=\"rId1\" Type=\"%s/officeDocument\" "
    2611             :                 "Target=\"xl/workbook.xml\"/>\n",
    2612             :                 SCHEMA_OD_RS);
    2613          12 :     VSIFPrintfL(
    2614             :         fp,
    2615             :         "<Relationship Id=\"rId2\" Type=\"%s/metadata/core-properties\" "
    2616             :         "Target=\"docProps/core.xml\"/>\n",
    2617             :         SCHEMA_PACKAGE_RS);
    2618          12 :     VSIFPrintfL(fp,
    2619             :                 "<Relationship Id=\"rId3\" Type=\"%s/extended-properties\" "
    2620             :                 "Target=\"docProps/app.xml\"/>\n",
    2621             :                 SCHEMA_OD_RS);
    2622          12 :     VSIFPrintfL(fp, "</Relationships>\n");
    2623          12 :     VSIFCloseL(fp);
    2624          12 :     return true;
    2625             : }
    2626             : 
    2627             : /************************************************************************/
    2628             : /*                             FlushCache()                             */
    2629             : /************************************************************************/
    2630             : 
    2631          47 : CPLErr OGRXLSXDataSource::FlushCache(bool /* bAtClosing */)
    2632             : {
    2633          47 :     if (!bUpdated)
    2634          35 :         return CE_None;
    2635             : 
    2636             :     /* Cause all layers to be loaded */
    2637          42 :     for (int i = 0; i < nLayers; i++)
    2638             :     {
    2639          30 :         ((OGRXLSXLayer *)papoLayers[i])->GetLayerDefn();
    2640             :     }
    2641             : 
    2642             :     VSIStatBufL sStat;
    2643          12 :     if (VSIStatL(pszName, &sStat) == 0)
    2644             :     {
    2645           4 :         if (VSIUnlink(pszName) != 0)
    2646             :         {
    2647           0 :             CPLError(CE_Failure, CPLE_FileIO, "Cannot delete %s", pszName);
    2648           0 :             return CE_Failure;
    2649             :         }
    2650             :     }
    2651             : 
    2652          24 :     CPLConfigOptionSetter oZip64Disable("CPL_CREATE_ZIP64", "NO", false);
    2653             : 
    2654             :     /* Maintain new ZIP files opened */
    2655          24 :     CPLString osTmpFilename(CPLSPrintf("/vsizip/%s", pszName));
    2656          12 :     VSILFILE *fpZIP = VSIFOpenExL(osTmpFilename, "wb", true);
    2657          12 :     if (fpZIP == nullptr)
    2658             :     {
    2659           0 :         CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s: %s", pszName,
    2660             :                  VSIGetLastErrorMsg());
    2661           0 :         return CE_Failure;
    2662             :     }
    2663             : 
    2664          12 :     bool bOK = WriteContentTypes(pszName, nLayers);
    2665             : 
    2666             :     // VSIMkdir(CPLSPrintf("/vsizip/%s/docProps", pszName),0755);
    2667          12 :     bOK &= WriteApp(pszName);
    2668          12 :     bOK &= WriteCore(pszName);
    2669             : 
    2670             :     // VSIMkdir(CPLSPrintf("/vsizip/%s/xl", pszName),0755);
    2671          12 :     bOK &= WriteWorkbook(pszName, this);
    2672             : 
    2673          24 :     std::map<std::string, int> oStringMap;
    2674          12 :     std::vector<std::string> oStringList;
    2675             : 
    2676             :     // VSIMkdir(CPLSPrintf("/vsizip/%s/xl/worksheets", pszName),0755);
    2677          42 :     for (int i = 0; i < nLayers; i++)
    2678             :     {
    2679          30 :         bOK &= WriteLayer(pszName, papoLayers[i], i, oStringMap, oStringList);
    2680             :     }
    2681             : 
    2682          12 :     bOK &= WriteSharedStrings(pszName, oStringList);
    2683          12 :     bOK &= WriteStyles(pszName);
    2684             : 
    2685             :     // VSIMkdir(CPLSPrintf("/vsizip/%s/xl/_rels", pszName),0755);
    2686          12 :     bOK &= WriteWorkbookRels(pszName, nLayers);
    2687             : 
    2688             :     // VSIMkdir(CPLSPrintf("/vsizip/%s/_rels", pszName),0755);
    2689          12 :     bOK &= WriteDotRels(pszName);
    2690             : 
    2691             :     /* Now close ZIP file */
    2692          12 :     if (VSIFCloseL(fpZIP) != 0)
    2693           0 :         bOK = false;
    2694             : 
    2695             :     /* Reset updated flag at datasource and layer level */
    2696          12 :     bUpdated = false;
    2697          42 :     for (int i = 0; i < nLayers; i++)
    2698             :     {
    2699          30 :         ((OGRXLSXLayer *)papoLayers[i])->SetUpdated(false);
    2700             :     }
    2701             : 
    2702          12 :     if (!bOK)
    2703             :     {
    2704           0 :         CPLError(CE_Failure, CPLE_FileIO, "Failure when saving %s", pszName);
    2705             :     }
    2706             : 
    2707          12 :     return bOK ? CE_None : CE_Failure;
    2708             : }
    2709             : 
    2710             : }  // namespace OGRXLSX

Generated by: LCOV version 1.14