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 1343 86.5 %
Date: 2026-01-23 20:24:11 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        8302 : void OGRXLSXLayer::Init()
      47             : {
      48        8302 :     if (!bInit)
      49             :     {
      50          84 :         bInit = true;
      51          84 :         CPLDebug("XLSX", "Init(%s)", GetName());
      52          84 :         poDS->BuildLayer(this);
      53             :     }
      54        8302 : }
      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         454 : int OGRXLSXLayer::TestCapability(const char *pszCap) const
     309             : 
     310             : {
     311         454 :     if (EQUAL(pszCap, OLCUpsertFeature))
     312           9 :         return false;
     313         445 :     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 : int 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         955 : int OGRXLSXDataSource::GetLayerCount() const
     410             : {
     411         955 :     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 char **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(CPL_TO_BOOL(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 at line %d, column %d",
    1383             :                      pszSheetFilename,
    1384             :                      XML_ErrorString(XML_GetErrorCode(oParser)),
    1385           0 :                      (int)XML_GetCurrentLineNumber(oParser),
    1386           0 :                      (int)XML_GetCurrentColumnNumber(oParser));
    1387           0 :             bStopParsing = true;
    1388             :         }
    1389          92 :         nWithoutEventCounter++;
    1390          92 :     } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);
    1391             : 
    1392          84 :     XML_ParserFree(oParser);
    1393          84 :     oParser = nullptr;
    1394             : 
    1395          84 :     if (nWithoutEventCounter == 10)
    1396             :     {
    1397           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1398             :                  "Too much data inside one element. File probably corrupted");
    1399           0 :         bStopParsing = true;
    1400             :     }
    1401             : 
    1402          84 :     VSIFCloseL(fp);
    1403             : 
    1404          84 :     bUpdated = bUpdatedBackup;
    1405          84 :     poLayer->m_osCols = m_osCols;
    1406             : }
    1407             : 
    1408             : /************************************************************************/
    1409             : /*                          startElementSSCbk()                         */
    1410             : /************************************************************************/
    1411             : 
    1412        1249 : static void XMLCALL startElementSSCbk(void *pUserData, const char *pszNameIn,
    1413             :                                       const char **ppszAttr)
    1414             : {
    1415        1249 :     ((OGRXLSXDataSource *)pUserData)->startElementSSCbk(pszNameIn, ppszAttr);
    1416        1249 : }
    1417             : 
    1418        1249 : void OGRXLSXDataSource::startElementSSCbk(const char *pszNameIn,
    1419             :                                           CPL_UNUSED const char **ppszAttr)
    1420             : {
    1421        1249 :     if (bStopParsing)
    1422           0 :         return;
    1423             : 
    1424        1249 :     pszNameIn = GetUnprefixed(pszNameIn);
    1425             : 
    1426        1249 :     nWithoutEventCounter = 0;
    1427        1249 :     switch (stateStack[nStackDepth].eVal)
    1428             :     {
    1429         627 :         case STATE_DEFAULT:
    1430             :         {
    1431         627 :             if (strcmp(pszNameIn, "si") == 0)
    1432             :             {
    1433         595 :                 PushState(STATE_SI);
    1434         595 :                 osCurrentString = "";
    1435             :             }
    1436         627 :             break;
    1437             :         }
    1438         622 :         case STATE_SI:
    1439             :         {
    1440         622 :             if (strcmp(pszNameIn, "t") == 0)
    1441             :             {
    1442         598 :                 PushState(STATE_T);
    1443             :             }
    1444         622 :             break;
    1445             :         }
    1446           0 :         default:
    1447           0 :             break;
    1448             :     }
    1449        1249 :     nDepth++;
    1450             : }
    1451             : 
    1452             : /************************************************************************/
    1453             : /*                           endElementSSCbk()                          */
    1454             : /************************************************************************/
    1455             : 
    1456        1249 : static void XMLCALL endElementSSCbk(void *pUserData, const char *pszNameIn)
    1457             : {
    1458        1249 :     ((OGRXLSXDataSource *)pUserData)->endElementSSCbk(pszNameIn);
    1459        1249 : }
    1460             : 
    1461        1249 : void OGRXLSXDataSource::endElementSSCbk(const char * /*pszNameIn*/)
    1462             : {
    1463        1249 :     if (bStopParsing)
    1464           0 :         return;
    1465             : 
    1466             :     // If we were to use pszNameIn, then we need:
    1467             :     // pszNameIn = GetUnprefixed(pszNameIn);
    1468             : 
    1469        1249 :     nWithoutEventCounter = 0;
    1470             : 
    1471        1249 :     nDepth--;
    1472        1249 :     switch (stateStack[nStackDepth].eVal)
    1473             :     {
    1474          32 :         case STATE_DEFAULT:
    1475          32 :             break;
    1476         598 :         case STATE_T:
    1477         598 :             break;
    1478         619 :         case STATE_SI:
    1479             :         {
    1480         619 :             if (stateStack[nStackDepth].nBeginDepth == nDepth)
    1481             :             {
    1482         595 :                 apoSharedStrings.push_back(osCurrentString);
    1483             :             }
    1484         619 :             break;
    1485             :         }
    1486           0 :         default:
    1487           0 :             break;
    1488             :     }
    1489             : 
    1490        1249 :     if (stateStack[nStackDepth].nBeginDepth == nDepth)
    1491        1225 :         nStackDepth--;
    1492             : }
    1493             : 
    1494             : /************************************************************************/
    1495             : /*                           dataHandlerSSCbk()                         */
    1496             : /************************************************************************/
    1497             : 
    1498        1104 : static void XMLCALL dataHandlerSSCbk(void *pUserData, const char *data,
    1499             :                                      int nLen)
    1500             : {
    1501        1104 :     ((OGRXLSXDataSource *)pUserData)->dataHandlerSSCbk(data, nLen);
    1502        1104 : }
    1503             : 
    1504        1104 : void OGRXLSXDataSource::dataHandlerSSCbk(const char *data, int nLen)
    1505             : {
    1506        1104 :     if (bStopParsing)
    1507           0 :         return;
    1508             : 
    1509        1104 :     nDataHandlerCounter++;
    1510        1104 :     if (nDataHandlerCounter >= PARSER_BUF_SIZE)
    1511             :     {
    1512           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1513             :                  "File probably corrupted (million laugh pattern)");
    1514           0 :         XML_StopParser(oParser, XML_FALSE);
    1515           0 :         bStopParsing = true;
    1516           0 :         return;
    1517             :     }
    1518             : 
    1519        1104 :     nWithoutEventCounter = 0;
    1520             : 
    1521        1104 :     switch (stateStack[nStackDepth].eVal)
    1522             :     {
    1523         162 :         case STATE_DEFAULT:
    1524         162 :             break;
    1525         298 :         case STATE_SI:
    1526         298 :             break;
    1527         644 :         case STATE_T:
    1528         644 :             osCurrentString.append(data, nLen);
    1529         644 :             break;
    1530           0 :         default:
    1531           0 :             break;
    1532             :     }
    1533             : }
    1534             : 
    1535             : /************************************************************************/
    1536             : /*                          AnalyseSharedStrings()                      */
    1537             : /************************************************************************/
    1538             : 
    1539          34 : void OGRXLSXDataSource::AnalyseSharedStrings(VSILFILE *fpSharedStrings)
    1540             : {
    1541          34 :     if (fpSharedStrings == nullptr)
    1542           2 :         return;
    1543             : 
    1544          32 :     oParser = OGRCreateExpatXMLParser();
    1545          32 :     XML_SetElementHandler(oParser, OGRXLSX::startElementSSCbk,
    1546             :                           OGRXLSX::endElementSSCbk);
    1547          32 :     XML_SetCharacterDataHandler(oParser, OGRXLSX::dataHandlerSSCbk);
    1548          32 :     XML_SetUserData(oParser, this);
    1549             : 
    1550          32 :     VSIFSeekL(fpSharedStrings, 0, SEEK_SET);
    1551             : 
    1552          32 :     bStopParsing = false;
    1553          32 :     nWithoutEventCounter = 0;
    1554          32 :     nDataHandlerCounter = 0;
    1555          32 :     nStackDepth = 0;
    1556          32 :     nDepth = 0;
    1557          32 :     stateStack[0].eVal = STATE_DEFAULT;
    1558          32 :     stateStack[0].nBeginDepth = 0;
    1559             : 
    1560          64 :     std::vector<char> aBuf(PARSER_BUF_SIZE);
    1561          32 :     int nDone = 0;
    1562           0 :     do
    1563             :     {
    1564          32 :         nDataHandlerCounter = 0;
    1565          32 :         unsigned int nLen = (unsigned int)VSIFReadL(aBuf.data(), 1, aBuf.size(),
    1566          32 :                                                     fpSharedStrings);
    1567          32 :         nDone = (nLen < aBuf.size());
    1568          32 :         if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR)
    1569             :         {
    1570           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1571             :                      "XML parsing of %s file failed : %s at line %d, column %d",
    1572             :                      "sharedStrings.xml",
    1573             :                      XML_ErrorString(XML_GetErrorCode(oParser)),
    1574           0 :                      (int)XML_GetCurrentLineNumber(oParser),
    1575           0 :                      (int)XML_GetCurrentColumnNumber(oParser));
    1576           0 :             bStopParsing = true;
    1577             :         }
    1578          32 :         nWithoutEventCounter++;
    1579          32 :     } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);
    1580             : 
    1581          32 :     XML_ParserFree(oParser);
    1582          32 :     oParser = nullptr;
    1583             : 
    1584          32 :     if (nWithoutEventCounter == 10)
    1585             :     {
    1586           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1587             :                  "Too much data inside one element. File probably corrupted");
    1588           0 :         bStopParsing = true;
    1589             :     }
    1590             : 
    1591          32 :     VSIFCloseL(fpSharedStrings);
    1592             : }
    1593             : 
    1594             : /************************************************************************/
    1595             : /*                        startElementWBRelsCbk()                       */
    1596             : /************************************************************************/
    1597             : 
    1598         251 : static void XMLCALL startElementWBRelsCbk(void *pUserData,
    1599             :                                           const char *pszNameIn,
    1600             :                                           const char **ppszAttr)
    1601             : {
    1602             :     ((OGRXLSXDataSource *)pUserData)
    1603         251 :         ->startElementWBRelsCbk(pszNameIn, ppszAttr);
    1604         251 : }
    1605             : 
    1606         251 : void OGRXLSXDataSource::startElementWBRelsCbk(const char *pszNameIn,
    1607             :                                               const char **ppszAttr)
    1608             : {
    1609         251 :     if (bStopParsing)
    1610           0 :         return;
    1611             : 
    1612         251 :     pszNameIn = GetUnprefixed(pszNameIn);
    1613             : 
    1614         251 :     nWithoutEventCounter = 0;
    1615         251 :     if (strcmp(pszNameIn, "Relationship") == 0)
    1616             :     {
    1617         217 :         const char *pszId = GetAttributeValue(ppszAttr, "Id", nullptr);
    1618         217 :         const char *pszType = GetAttributeValue(ppszAttr, "Type", nullptr);
    1619         217 :         const char *pszTarget = GetAttributeValue(ppszAttr, "Target", nullptr);
    1620         217 :         if (pszId && pszType && pszTarget &&
    1621         217 :             strstr(pszType, "/worksheet") != nullptr)
    1622             :         {
    1623         149 :             oMapRelsIdToTarget[pszId] = pszTarget;
    1624             :         }
    1625             :     }
    1626             : }
    1627             : 
    1628             : /************************************************************************/
    1629             : /*                          AnalyseWorkbookRels()                       */
    1630             : /************************************************************************/
    1631             : 
    1632          34 : void OGRXLSXDataSource::AnalyseWorkbookRels(VSILFILE *fpWorkbookRels)
    1633             : {
    1634          34 :     oParser = OGRCreateExpatXMLParser();
    1635          34 :     XML_SetElementHandler(oParser, OGRXLSX::startElementWBRelsCbk, nullptr);
    1636          34 :     XML_SetUserData(oParser, this);
    1637             : 
    1638          34 :     VSIFSeekL(fpWorkbookRels, 0, SEEK_SET);
    1639             : 
    1640          34 :     bStopParsing = false;
    1641          34 :     nWithoutEventCounter = 0;
    1642          34 :     nDataHandlerCounter = 0;
    1643             : 
    1644          68 :     std::vector<char> aBuf(PARSER_BUF_SIZE);
    1645          34 :     int nDone = 0;
    1646           0 :     do
    1647             :     {
    1648          34 :         nDataHandlerCounter = 0;
    1649          34 :         unsigned int nLen = (unsigned int)VSIFReadL(aBuf.data(), 1, aBuf.size(),
    1650          34 :                                                     fpWorkbookRels);
    1651          34 :         nDone = (nLen < aBuf.size());
    1652          34 :         if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR)
    1653             :         {
    1654           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1655             :                      "XML parsing of %s file failed : %s at line %d, column %d",
    1656             :                      "xl/_rels/workbook.xml.rels",
    1657             :                      XML_ErrorString(XML_GetErrorCode(oParser)),
    1658           0 :                      (int)XML_GetCurrentLineNumber(oParser),
    1659           0 :                      (int)XML_GetCurrentColumnNumber(oParser));
    1660           0 :             bStopParsing = true;
    1661             :         }
    1662          34 :         nWithoutEventCounter++;
    1663          34 :     } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);
    1664             : 
    1665          34 :     XML_ParserFree(oParser);
    1666          34 :     oParser = nullptr;
    1667             : 
    1668          34 :     if (nWithoutEventCounter == 10)
    1669             :     {
    1670           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1671             :                  "Too much data inside one element. File probably corrupted");
    1672           0 :         bStopParsing = true;
    1673             :     }
    1674             : 
    1675          34 :     VSIFCloseL(fpWorkbookRels);
    1676          34 : }
    1677             : 
    1678             : /************************************************************************/
    1679             : /*                          startElementWBCbk()                         */
    1680             : /************************************************************************/
    1681             : 
    1682         404 : static void XMLCALL startElementWBCbk(void *pUserData, const char *pszNameIn,
    1683             :                                       const char **ppszAttr)
    1684             : {
    1685         404 :     ((OGRXLSXDataSource *)pUserData)->startElementWBCbk(pszNameIn, ppszAttr);
    1686         404 : }
    1687             : 
    1688         404 : void OGRXLSXDataSource::startElementWBCbk(const char *pszNameIn,
    1689             :                                           const char **ppszAttr)
    1690             : {
    1691         404 :     if (bStopParsing)
    1692           0 :         return;
    1693             : 
    1694         404 :     pszNameIn = GetUnprefixed(pszNameIn);
    1695             : 
    1696         404 :     nWithoutEventCounter = 0;
    1697         404 :     if (strcmp(pszNameIn, "sheet") == 0)
    1698             :     {
    1699         149 :         const char *pszSheetName = GetAttributeValue(ppszAttr, "name", nullptr);
    1700         149 :         const char *pszId = GetAttributeValue(ppszAttr, "r:id", nullptr);
    1701         298 :         if (pszSheetName && pszId &&
    1702         596 :             oMapRelsIdToTarget.find(pszId) != oMapRelsIdToTarget.end() &&
    1703         298 :             m_oSetSheetId.find(pszId) == m_oSetSheetId.end())
    1704             :         {
    1705         149 :             const auto &osTarget(oMapRelsIdToTarget[pszId]);
    1706         149 :             m_oSetSheetId.insert(pszId);
    1707         149 :             CPLString osFilename;
    1708         149 :             if (osTarget.empty())
    1709           0 :                 return;
    1710         149 :             if (osTarget[0] == '/')
    1711             :             {
    1712           2 :                 int nIdx = 1;
    1713           2 :                 while (osTarget[nIdx] == '/')
    1714           0 :                     nIdx++;
    1715           2 :                 if (osTarget[nIdx] == '\0')
    1716           0 :                     return;
    1717             :                 // Is it an "absolute" path ?
    1718           2 :                 osFilename = osPrefixedFilename + osTarget;
    1719             :             }
    1720             :             else
    1721             :             {
    1722             :                 // or relative to the /xl subdirectory
    1723         147 :                 osFilename = osPrefixedFilename + CPLString("/xl/") + osTarget;
    1724             :             }
    1725         298 :             papoLayers = (OGRXLSXLayer **)CPLRealloc(
    1726         149 :                 papoLayers, (nLayers + 1) * sizeof(OGRXLSXLayer *));
    1727         149 :             papoLayers[nLayers++] =
    1728         149 :                 new OGRXLSXLayer(this, osFilename, pszSheetName);
    1729             :         }
    1730             :     }
    1731             : }
    1732             : 
    1733             : /************************************************************************/
    1734             : /*                             AnalyseWorkbook()                        */
    1735             : /************************************************************************/
    1736             : 
    1737          34 : void OGRXLSXDataSource::AnalyseWorkbook(VSILFILE *fpWorkbook)
    1738             : {
    1739          34 :     oParser = OGRCreateExpatXMLParser();
    1740          34 :     XML_SetElementHandler(oParser, OGRXLSX::startElementWBCbk, nullptr);
    1741          34 :     XML_SetUserData(oParser, this);
    1742             : 
    1743          34 :     VSIFSeekL(fpWorkbook, 0, SEEK_SET);
    1744             : 
    1745          34 :     bStopParsing = false;
    1746          34 :     nWithoutEventCounter = 0;
    1747          34 :     nDataHandlerCounter = 0;
    1748             : 
    1749          68 :     std::vector<char> aBuf(PARSER_BUF_SIZE);
    1750          34 :     int nDone = 0;
    1751           0 :     do
    1752             :     {
    1753          34 :         nDataHandlerCounter = 0;
    1754             :         unsigned int nLen =
    1755          34 :             (unsigned int)VSIFReadL(aBuf.data(), 1, aBuf.size(), fpWorkbook);
    1756          34 :         nDone = (nLen < aBuf.size());
    1757          34 :         if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR)
    1758             :         {
    1759           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1760             :                      "XML parsing of %s file failed : %s at line %d, column %d",
    1761             :                      "workbook.xml", XML_ErrorString(XML_GetErrorCode(oParser)),
    1762           0 :                      (int)XML_GetCurrentLineNumber(oParser),
    1763           0 :                      (int)XML_GetCurrentColumnNumber(oParser));
    1764           0 :             bStopParsing = true;
    1765             :         }
    1766          34 :         nWithoutEventCounter++;
    1767          34 :     } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);
    1768             : 
    1769          34 :     XML_ParserFree(oParser);
    1770          34 :     oParser = nullptr;
    1771             : 
    1772          34 :     if (nWithoutEventCounter == 10)
    1773             :     {
    1774           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1775             :                  "Too much data inside one element. File probably corrupted");
    1776           0 :         bStopParsing = true;
    1777             :     }
    1778             : 
    1779          34 :     VSIFCloseL(fpWorkbook);
    1780          34 : }
    1781             : 
    1782             : /************************************************************************/
    1783             : /*                       startElementStylesCbk()                        */
    1784             : /************************************************************************/
    1785             : 
    1786        1847 : static void XMLCALL startElementStylesCbk(void *pUserData,
    1787             :                                           const char *pszNameIn,
    1788             :                                           const char **ppszAttr)
    1789             : {
    1790             :     ((OGRXLSXDataSource *)pUserData)
    1791        1847 :         ->startElementStylesCbk(pszNameIn, ppszAttr);
    1792        1847 : }
    1793             : 
    1794        1847 : void OGRXLSXDataSource::startElementStylesCbk(const char *pszNameIn,
    1795             :                                               const char **ppszAttr)
    1796             : {
    1797        1847 :     if (bStopParsing)
    1798           0 :         return;
    1799             : 
    1800        1847 :     pszNameIn = GetUnprefixed(pszNameIn);
    1801             : 
    1802        1847 :     nWithoutEventCounter = 0;
    1803        1847 :     if (strcmp(pszNameIn, "numFmt") == 0)
    1804             :     {
    1805             :         const char *pszFormatCode =
    1806         176 :             GetAttributeValue(ppszAttr, "formatCode", nullptr);
    1807         176 :         const char *pszNumFmtId = GetAttributeValue(ppszAttr, "numFmtId", "-1");
    1808         176 :         int nNumFmtId = atoi(pszNumFmtId);
    1809         176 :         if (pszFormatCode && nNumFmtId >= 164)
    1810             :         {
    1811         465 :             int bHasDate = strstr(pszFormatCode, "DD") != nullptr ||
    1812         113 :                            strstr(pszFormatCode, "dd") != nullptr ||
    1813         402 :                            strstr(pszFormatCode, "YY") != nullptr ||
    1814         113 :                            strstr(pszFormatCode, "yy") != nullptr;
    1815         291 :             int bHasTime = strstr(pszFormatCode, "HH") != nullptr ||
    1816         115 :                            strstr(pszFormatCode, "hh") != nullptr;
    1817         176 :             if (bHasDate && bHasTime)
    1818          37 :                 apoMapStyleFormats[nNumFmtId] = XLSXFieldTypeExtended(
    1819             :                     OFTDateTime,
    1820          62 :                     strstr(pszFormatCode, "SS.000") != nullptr ||
    1821          25 :                         strstr(pszFormatCode, "ss.000") != nullptr);
    1822         139 :             else if (bHasDate)
    1823          26 :                 apoMapStyleFormats[nNumFmtId] = XLSXFieldTypeExtended(OFTDate);
    1824         113 :             else if (bHasTime)
    1825          24 :                 apoMapStyleFormats[nNumFmtId] = XLSXFieldTypeExtended(OFTTime);
    1826             :             else
    1827          89 :                 apoMapStyleFormats[nNumFmtId] = XLSXFieldTypeExtended(OFTReal);
    1828             :         }
    1829             :     }
    1830        1671 :     else if (strcmp(pszNameIn, "cellXfs") == 0)
    1831             :     {
    1832          32 :         bInCellXFS = true;
    1833             :     }
    1834        1639 :     else if (bInCellXFS && strcmp(pszNameIn, "xf") == 0)
    1835             :     {
    1836         219 :         const char *pszNumFmtId = GetAttributeValue(ppszAttr, "numFmtId", "-1");
    1837         219 :         int nNumFmtId = atoi(pszNumFmtId);
    1838         219 :         XLSXFieldTypeExtended eType(OFTReal);
    1839         219 :         if (nNumFmtId >= 0)
    1840             :         {
    1841         219 :             if (nNumFmtId < 164)
    1842             :             {
    1843             :                 // From
    1844             :                 // http://social.msdn.microsoft.com/Forums/en-US/oxmlsdk/thread/e27aaf16-b900-4654-8210-83c5774a179c/
    1845           7 :                 if (nNumFmtId >= 14 && nNumFmtId <= 17)
    1846           0 :                     eType = XLSXFieldTypeExtended(OFTDate);
    1847           7 :                 else if (nNumFmtId >= 18 && nNumFmtId <= 21)
    1848           0 :                     eType = XLSXFieldTypeExtended(OFTTime);
    1849           7 :                 else if (nNumFmtId == 22)
    1850           0 :                     eType = XLSXFieldTypeExtended(OFTDateTime);
    1851             :             }
    1852             :             else
    1853             :             {
    1854             :                 std::map<int, XLSXFieldTypeExtended>::iterator oIter =
    1855         212 :                     apoMapStyleFormats.find(nNumFmtId);
    1856         212 :                 if (oIter != apoMapStyleFormats.end())
    1857         212 :                     eType = oIter->second;
    1858             :                 else
    1859           0 :                     CPLDebug("XLSX",
    1860             :                              "Cannot find entry in <numFmts> with numFmtId=%d",
    1861             :                              nNumFmtId);
    1862             :             }
    1863             :         }
    1864             : #if DEBUG_VERBOSE
    1865             :         printf("style[%lu] = %d\n", /*ok*/
    1866             :                apoStyles.size(), static_cast<int>(eType.eType));
    1867             : #endif
    1868             : 
    1869         219 :         apoStyles.push_back(eType);
    1870             :     }
    1871             : }
    1872             : 
    1873             : /************************************************************************/
    1874             : /*                       endElementStylesCbk()                          */
    1875             : /************************************************************************/
    1876             : 
    1877        1847 : static void XMLCALL endElementStylesCbk(void *pUserData, const char *pszNameIn)
    1878             : {
    1879        1847 :     ((OGRXLSXDataSource *)pUserData)->endElementStylesCbk(pszNameIn);
    1880        1847 : }
    1881             : 
    1882        1847 : void OGRXLSXDataSource::endElementStylesCbk(const char *pszNameIn)
    1883             : {
    1884        1847 :     if (bStopParsing)
    1885           0 :         return;
    1886             : 
    1887        1847 :     pszNameIn = GetUnprefixed(pszNameIn);
    1888             : 
    1889        1847 :     nWithoutEventCounter = 0;
    1890        1847 :     if (strcmp(pszNameIn, "cellXfs") == 0)
    1891             :     {
    1892          32 :         bInCellXFS = false;
    1893             :     }
    1894             : }
    1895             : 
    1896             : /************************************************************************/
    1897             : /*                             AnalyseStyles()                          */
    1898             : /************************************************************************/
    1899             : 
    1900          34 : void OGRXLSXDataSource::AnalyseStyles(VSILFILE *fpStyles)
    1901             : {
    1902          34 :     if (fpStyles == nullptr)
    1903           2 :         return;
    1904             : 
    1905          32 :     oParser = OGRCreateExpatXMLParser();
    1906          32 :     XML_SetElementHandler(oParser, OGRXLSX::startElementStylesCbk,
    1907             :                           OGRXLSX::endElementStylesCbk);
    1908          32 :     XML_SetUserData(oParser, this);
    1909             : 
    1910          32 :     VSIFSeekL(fpStyles, 0, SEEK_SET);
    1911             : 
    1912          32 :     bStopParsing = false;
    1913          32 :     nWithoutEventCounter = 0;
    1914          32 :     nDataHandlerCounter = 0;
    1915          32 :     bInCellXFS = false;
    1916             : 
    1917          64 :     std::vector<char> aBuf(PARSER_BUF_SIZE);
    1918          32 :     int nDone = 0;
    1919           0 :     do
    1920             :     {
    1921          32 :         nDataHandlerCounter = 0;
    1922             :         unsigned int nLen =
    1923          32 :             (unsigned int)VSIFReadL(aBuf.data(), 1, aBuf.size(), fpStyles);
    1924          32 :         nDone = (nLen < aBuf.size());
    1925          32 :         if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR)
    1926             :         {
    1927           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1928             :                      "XML parsing of %s file failed : %s at line %d, column %d",
    1929             :                      "styles.xml", XML_ErrorString(XML_GetErrorCode(oParser)),
    1930           0 :                      (int)XML_GetCurrentLineNumber(oParser),
    1931           0 :                      (int)XML_GetCurrentColumnNumber(oParser));
    1932           0 :             bStopParsing = true;
    1933             :         }
    1934          32 :         nWithoutEventCounter++;
    1935          32 :     } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);
    1936             : 
    1937          32 :     XML_ParserFree(oParser);
    1938          32 :     oParser = nullptr;
    1939             : 
    1940          32 :     if (nWithoutEventCounter == 10)
    1941             :     {
    1942           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1943             :                  "Too much data inside one element. File probably corrupted");
    1944           0 :         bStopParsing = true;
    1945             :     }
    1946             : 
    1947          32 :     VSIFCloseL(fpStyles);
    1948             : }
    1949             : 
    1950             : /************************************************************************/
    1951             : /*                           ICreateLayer()                             */
    1952             : /************************************************************************/
    1953             : 
    1954             : OGRLayer *
    1955          18 : OGRXLSXDataSource::ICreateLayer(const char *pszLayerName,
    1956             :                                 const OGRGeomFieldDefn * /*poGeomFieldDefn*/,
    1957             :                                 CSLConstList papszOptions)
    1958             : 
    1959             : {
    1960             :     /* -------------------------------------------------------------------- */
    1961             :     /*      Verify we are in update mode.                                   */
    1962             :     /* -------------------------------------------------------------------- */
    1963          18 :     if (!bUpdatable)
    1964             :     {
    1965           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
    1966             :                  "Data source %s opened read-only.\n"
    1967             :                  "New layer %s cannot be created.\n",
    1968             :                  pszName, pszLayerName);
    1969             : 
    1970           0 :         return nullptr;
    1971             :     }
    1972             : 
    1973             :     /* -------------------------------------------------------------------- */
    1974             :     /*      Do we already have this layer?  If so, should we blow it        */
    1975             :     /*      away?                                                           */
    1976             :     /* -------------------------------------------------------------------- */
    1977          50 :     for (int iLayer = 0; iLayer < nLayers; iLayer++)
    1978             :     {
    1979          32 :         if (EQUAL(pszLayerName, papoLayers[iLayer]->GetName()))
    1980             :         {
    1981           0 :             if (CSLFetchNameValue(papszOptions, "OVERWRITE") != nullptr &&
    1982           0 :                 !EQUAL(CSLFetchNameValue(papszOptions, "OVERWRITE"), "NO"))
    1983             :             {
    1984           0 :                 DeleteLayer(pszLayerName);
    1985             :             }
    1986             :             else
    1987             :             {
    1988           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1989             :                          "Layer %s already exists, CreateLayer failed.\n"
    1990             :                          "Use the layer creation option OVERWRITE=YES to "
    1991             :                          "replace it.",
    1992             :                          pszLayerName);
    1993           0 :                 return nullptr;
    1994             :             }
    1995             :         }
    1996             :     }
    1997             : 
    1998             :     /* -------------------------------------------------------------------- */
    1999             :     /*      Create the layer object.                                        */
    2000             :     /* -------------------------------------------------------------------- */
    2001             :     OGRXLSXLayer *poLayer =
    2002             :         new OGRXLSXLayer(this,
    2003          36 :                          CPLSPrintf("/vsizip/%s/xl/worksheets/sheet%d.xml",
    2004          18 :                                     pszName, nLayers + 1),
    2005          18 :                          pszLayerName, TRUE);
    2006             : 
    2007          36 :     papoLayers = (OGRXLSXLayer **)CPLRealloc(
    2008          18 :         papoLayers, (nLayers + 1) * sizeof(OGRXLSXLayer *));
    2009          18 :     papoLayers[nLayers] = poLayer;
    2010          18 :     nLayers++;
    2011             : 
    2012          18 :     bUpdated = true;
    2013             : 
    2014          18 :     return poLayer;
    2015             : }
    2016             : 
    2017             : /************************************************************************/
    2018             : /*                            DeleteLayer()                             */
    2019             : /************************************************************************/
    2020             : 
    2021           0 : void OGRXLSXDataSource::DeleteLayer(const char *pszLayerName)
    2022             : 
    2023             : {
    2024             :     /* -------------------------------------------------------------------- */
    2025             :     /*      Verify we are in update mode.                                   */
    2026             :     /* -------------------------------------------------------------------- */
    2027           0 :     if (!bUpdatable)
    2028             :     {
    2029           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
    2030             :                  "Data source %s opened read-only.\n"
    2031             :                  "Layer %s cannot be deleted.\n",
    2032             :                  pszName, pszLayerName);
    2033             : 
    2034           0 :         return;
    2035             :     }
    2036             : 
    2037             :     /* -------------------------------------------------------------------- */
    2038             :     /*      Try to find layer.                                              */
    2039             :     /* -------------------------------------------------------------------- */
    2040           0 :     int iLayer = 0;
    2041           0 :     for (; iLayer < nLayers; iLayer++)
    2042             :     {
    2043           0 :         if (EQUAL(pszLayerName, papoLayers[iLayer]->GetName()))
    2044           0 :             break;
    2045             :     }
    2046             : 
    2047           0 :     if (iLayer == nLayers)
    2048             :     {
    2049           0 :         CPLError(
    2050             :             CE_Failure, CPLE_AppDefined,
    2051             :             "Attempt to delete layer '%s', but this layer is not known to OGR.",
    2052             :             pszLayerName);
    2053           0 :         return;
    2054             :     }
    2055             : 
    2056           0 :     DeleteLayer(iLayer);
    2057             : }
    2058             : 
    2059             : /************************************************************************/
    2060             : /*                            DeleteLayer()                             */
    2061             : /************************************************************************/
    2062             : 
    2063           0 : OGRErr OGRXLSXDataSource::DeleteLayer(int iLayer)
    2064             : {
    2065           0 :     if (iLayer < 0 || iLayer >= nLayers)
    2066             :     {
    2067           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2068             :                  "Layer %d not in legal range of 0 to %d.", iLayer,
    2069           0 :                  nLayers - 1);
    2070           0 :         return OGRERR_FAILURE;
    2071             :     }
    2072             : 
    2073             :     /* -------------------------------------------------------------------- */
    2074             :     /*      Blow away our OGR structures related to the layer.  This is     */
    2075             :     /*      pretty dangerous if anything has a reference to this layer!     */
    2076             :     /* -------------------------------------------------------------------- */
    2077             : 
    2078           0 :     delete papoLayers[iLayer];
    2079           0 :     memmove(papoLayers + iLayer, papoLayers + iLayer + 1,
    2080           0 :             sizeof(void *) * (nLayers - iLayer - 1));
    2081           0 :     nLayers--;
    2082             : 
    2083           0 :     bUpdated = true;
    2084             : 
    2085           0 :     return OGRERR_NONE;
    2086             : }
    2087             : 
    2088             : /************************************************************************/
    2089             : /*                            WriteOverride()                           */
    2090             : /************************************************************************/
    2091             : 
    2092         114 : static void WriteOverride(VSILFILE *fp, const char *pszPartName,
    2093             :                           const char *pszContentType)
    2094             : {
    2095         114 :     VSIFPrintfL(fp, "<Override PartName=\"%s\" ContentType=\"%s\"/>\n",
    2096             :                 pszPartName, pszContentType);
    2097         114 : }
    2098             : 
    2099             : static const char XML_HEADER[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
    2100             : static const char MAIN_NS[] =
    2101             :     "xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"";
    2102             : static const char SCHEMA_OD[] =
    2103             :     "http://schemas.openxmlformats.org/officeDocument/2006";
    2104             : static const char SCHEMA_OD_RS[] =
    2105             :     "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
    2106             : static const char SCHEMA_PACKAGE[] =
    2107             :     "http://schemas.openxmlformats.org/package/2006";
    2108             : static const char SCHEMA_PACKAGE_RS[] =
    2109             :     "http://schemas.openxmlformats.org/package/2006/relationships";
    2110             : 
    2111             : /************************************************************************/
    2112             : /*                           WriteContentTypes()                        */
    2113             : /************************************************************************/
    2114             : 
    2115          12 : static bool WriteContentTypes(const char *pszName, int nLayers)
    2116             : {
    2117             :     CPLString osTmpFilename(
    2118          24 :         CPLSPrintf("/vsizip/%s/[Content_Types].xml", pszName));
    2119          12 :     VSILFILE *fp = VSIFOpenL(osTmpFilename, "wb");
    2120          12 :     if (!fp)
    2121           0 :         return false;
    2122             :     // TODO(schwehr): Convert all strlen(XML_HEADER) to constexpr with
    2123             :     // switch to C++11 or newer.
    2124          12 :     VSIFWriteL(XML_HEADER, strlen(XML_HEADER), 1, fp);
    2125          12 :     VSIFPrintfL(fp, "<Types xmlns=\"%s/content-types\">\n", SCHEMA_PACKAGE);
    2126          12 :     WriteOverride(fp, "/_rels/.rels",
    2127             :                   "application/vnd.openxmlformats-package.relationships+xml");
    2128          12 :     WriteOverride(fp, "/docProps/core.xml",
    2129             :                   "application/vnd.openxmlformats-package.core-properties+xml");
    2130          12 :     WriteOverride(fp, "/docProps/app.xml",
    2131             :                   "application/"
    2132             :                   "vnd.openxmlformats-officedocument.extended-properties+xml");
    2133          12 :     WriteOverride(fp, "/xl/_rels/workbook.xml.rels",
    2134             :                   "application/vnd.openxmlformats-package.relationships+xml");
    2135          42 :     for (int i = 0; i < nLayers; i++)
    2136             :     {
    2137          30 :         WriteOverride(
    2138             :             fp, CPLSPrintf("/xl/worksheets/sheet%d.xml", i + 1),
    2139             :             "application/"
    2140             :             "vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml");
    2141             :     }
    2142          12 :     WriteOverride(fp, "/xl/styles.xml",
    2143             :                   "application/"
    2144             :                   "vnd.openxmlformats-officedocument.spreadsheetml.styles+xml");
    2145          12 :     WriteOverride(
    2146             :         fp, "/xl/workbook.xml",
    2147             :         "application/"
    2148             :         "vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml");
    2149          12 :     WriteOverride(
    2150             :         fp, "/xl/sharedStrings.xml",
    2151             :         "application/"
    2152             :         "vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml");
    2153          12 :     VSIFPrintfL(fp, "</Types>\n");
    2154          12 :     VSIFCloseL(fp);
    2155          12 :     return true;
    2156             : }
    2157             : 
    2158             : /************************************************************************/
    2159             : /*                             WriteApp()                               */
    2160             : /************************************************************************/
    2161             : 
    2162          12 : static bool WriteApp(const char *pszName)
    2163             : {
    2164          24 :     CPLString osTmpFilename(CPLSPrintf("/vsizip/%s/docProps/app.xml", pszName));
    2165          12 :     VSILFILE *fp = VSIFOpenL(osTmpFilename, "wb");
    2166          12 :     if (!fp)
    2167           0 :         return false;
    2168          12 :     VSIFWriteL(XML_HEADER, strlen(XML_HEADER), 1, fp);
    2169          12 :     VSIFPrintfL(fp,
    2170             :                 "<Properties xmlns=\"%s/extended-properties\" "
    2171             :                 "xmlns:vt=\"%s/docPropsVTypes\">\n",
    2172             :                 SCHEMA_OD, SCHEMA_OD);
    2173          12 :     VSIFPrintfL(fp, "<TotalTime>0</TotalTime>\n");
    2174          12 :     VSIFPrintfL(fp, "</Properties>\n");
    2175          12 :     VSIFCloseL(fp);
    2176          12 :     return true;
    2177             : }
    2178             : 
    2179             : /************************************************************************/
    2180             : /*                             WriteCore()                              */
    2181             : /************************************************************************/
    2182             : 
    2183          12 : static bool WriteCore(const char *pszName)
    2184             : {
    2185             :     CPLString osTmpFilename(
    2186          24 :         CPLSPrintf("/vsizip/%s/docProps/core.xml", pszName));
    2187          12 :     VSILFILE *fp = VSIFOpenL(osTmpFilename, "wb");
    2188          12 :     if (!fp)
    2189           0 :         return false;
    2190          12 :     VSIFWriteL(XML_HEADER, strlen(XML_HEADER), 1, fp);
    2191          12 :     VSIFPrintfL(fp,
    2192             :                 "<cp:coreProperties xmlns:cp=\"%s/metadata/core-properties\" "
    2193             :                 "xmlns:dc=\"http://purl.org/dc/elements/1.1/\" "
    2194             :                 "xmlns:dcmitype=\"http://purl.org/dc/dcmitype/\" "
    2195             :                 "xmlns:dcterms=\"http://purl.org/dc/terms/\" "
    2196             :                 "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n",
    2197             :                 SCHEMA_PACKAGE);
    2198          12 :     VSIFPrintfL(fp, "<cp:revision>0</cp:revision>\n");
    2199          12 :     VSIFPrintfL(fp, "</cp:coreProperties>\n");
    2200          12 :     VSIFCloseL(fp);
    2201          12 :     return true;
    2202             : }
    2203             : 
    2204             : /************************************************************************/
    2205             : /*                            WriteWorkbook()                           */
    2206             : /************************************************************************/
    2207             : 
    2208          12 : static bool WriteWorkbook(const char *pszName, GDALDataset *poDS)
    2209             : {
    2210          24 :     CPLString osTmpFilename(CPLSPrintf("/vsizip/%s/xl/workbook.xml", pszName));
    2211          12 :     VSILFILE *fp = VSIFOpenL(osTmpFilename, "wb");
    2212          12 :     if (!fp)
    2213           0 :         return false;
    2214          12 :     VSIFWriteL(XML_HEADER, strlen(XML_HEADER), 1, fp);
    2215          12 :     VSIFPrintfL(fp, "<workbook %s xmlns:r=\"%s\">\n", MAIN_NS, SCHEMA_OD_RS);
    2216          12 :     VSIFPrintfL(fp, "<fileVersion appName=\"Calc\"/>\n");
    2217             :     /*
    2218             :     VSIFPrintfL(fp, "<workbookPr backupFile=\"false\" showObjects=\"all\"
    2219             :     date1904=\"false\"/>\n"); VSIFPrintfL(fp, "<workbookProtection/>\n");
    2220             :     VSIFPrintfL(fp, "<bookViews>\n");
    2221             :     VSIFPrintfL(fp, "<workbookView activeTab=\"0\" firstSheet=\"0\"
    2222             :     showHorizontalScroll=\"true\" " "showSheetTabs=\"true\"
    2223             :     showVerticalScroll=\"true\" tabRatio=\"600\" windowHeight=\"8192\" "
    2224             :                     "windowWidth=\"16384\" xWindow=\"0\" yWindow=\"0\"/>\n");
    2225             :     VSIFPrintfL(fp, "</bookViews>\n");
    2226             :     */
    2227          12 :     VSIFPrintfL(fp, "<sheets>\n");
    2228          42 :     for (int i = 0; i < poDS->GetLayerCount(); i++)
    2229             :     {
    2230          30 :         auto poLayer = poDS->GetLayer(i);
    2231          30 :         const char *pszLayerName = poLayer->GetName();
    2232          30 :         char *pszXML = OGRGetXML_UTF8_EscapedString(pszLayerName);
    2233          30 :         VSIFPrintfL(fp,
    2234             :                     "<sheet name=\"%s\" sheetId=\"%d\" state=\"visible\" "
    2235             :                     "r:id=\"rId%d\"/>\n",
    2236             :                     pszXML, i + 1, i + 2);
    2237          30 :         CPLFree(pszXML);
    2238             :     }
    2239          12 :     VSIFPrintfL(fp, "</sheets>\n");
    2240          12 :     VSIFPrintfL(fp, "<calcPr iterateCount=\"100\" refMode=\"A1\" "
    2241             :                     "iterate=\"false\" iterateDelta=\"0.001\"/>\n");
    2242          12 :     VSIFPrintfL(fp, "</workbook>\n");
    2243          12 :     VSIFCloseL(fp);
    2244          12 :     return true;
    2245             : }
    2246             : 
    2247             : /************************************************************************/
    2248             : /*                            BuildColString()                          */
    2249             : /************************************************************************/
    2250             : 
    2251         338 : static CPLString BuildColString(int nCol)
    2252             : {
    2253             :     /*
    2254             :     A Z   AA AZ   BA BZ   ZA   ZZ   AAA    ZZZ      AAAA
    2255             :     0 25  26 51   52 77   676  701  702    18277    18278
    2256             :     */
    2257         338 :     CPLString osRet;
    2258         338 :     osRet += (nCol % 26) + 'A';
    2259         342 :     while (nCol >= 26)
    2260             :     {
    2261           4 :         nCol /= 26;
    2262             :         // We would not need a decrement if this was a proper base 26
    2263             :         // numeration scheme.
    2264           4 :         nCol--;
    2265           4 :         osRet += (nCol % 26) + 'A';
    2266             :     }
    2267         338 :     const size_t nSize = osRet.size();
    2268         342 :     for (size_t l = 0; l < nSize / 2; l++)
    2269             :     {
    2270           4 :         char chTmp = osRet[nSize - 1 - l];
    2271           4 :         osRet[nSize - 1 - l] = osRet[l];
    2272           4 :         osRet[l] = chTmp;
    2273             :     }
    2274         338 :     return osRet;
    2275             : }
    2276             : 
    2277             : /************************************************************************/
    2278             : /*                             WriteLayer()                             */
    2279             : /************************************************************************/
    2280             : 
    2281          30 : static bool WriteLayer(const char *pszName, OGRXLSXLayer *poLayer, int iLayer,
    2282             :                        std::map<std::string, int> &oStringMap,
    2283             :                        std::vector<std::string> &oStringList)
    2284             : {
    2285             :     CPLString osTmpFilename(CPLSPrintf("/vsizip/%s/xl/worksheets/sheet%d.xml",
    2286          60 :                                        pszName, iLayer + 1));
    2287          30 :     VSILFILE *fp = VSIFOpenL(osTmpFilename, "wb");
    2288          30 :     if (!fp)
    2289           0 :         return false;
    2290          30 :     VSIFWriteL(XML_HEADER, strlen(XML_HEADER), 1, fp);
    2291          30 :     VSIFPrintfL(fp, "<worksheet %s xmlns:r=\"%s\">\n", MAIN_NS, SCHEMA_OD_RS);
    2292             :     /*
    2293             :     VSIFPrintfL(fp, "<sheetViews>\n");
    2294             :     VSIFPrintfL(fp, "<sheetView colorId=\"64\" defaultGridColor=\"true\"
    2295             :     rightToLeft=\"false\" showFormulas=\"false\" showGridLines=\"true\"
    2296             :     showOutlineSymbols=\"true\" showRowColHeaders=\"true\" showZeros=\"true\"
    2297             :     tabSelected=\"%s\" topLeftCell=\"A1\" view=\"normal\"
    2298             :     windowProtection=\"false\" workbookViewId=\"0\" zoomScale=\"100\"
    2299             :     zoomScaleNormal=\"100\" zoomScalePageLayoutView=\"60\">\n", (i == 0) ?
    2300             :     "true" : "false"); VSIFPrintfL(fp, "<selection activeCell=\"A1\"
    2301             :     activeCellId=\"0\" pane=\"topLeft\" sqref=\"A1\"/>\n"); VSIFPrintfL(fp,
    2302             :     "</sheetView>\n"); VSIFPrintfL(fp, "</sheetViews>\n");*/
    2303             : 
    2304          30 :     poLayer->ResetReading();
    2305             : 
    2306          30 :     OGRFeature *poFeature = poLayer->GetNextFeature();
    2307             : 
    2308          30 :     const OGRFeatureDefn *poFDefn = poLayer->GetLayerDefn();
    2309          30 :     bool bHasHeaders = false;
    2310          30 :     int iRow = 1;
    2311             : 
    2312          30 :     const int nFields = poFDefn->GetFieldCount();
    2313          30 :     if (nFields > 0)
    2314             :     {
    2315          28 :         VSIFPrintfL(fp, "<cols>\n");
    2316         141 :         for (int j = 0; j < nFields; j++)
    2317             :         {
    2318         113 :             int nWidth = 15;
    2319         113 :             if (poFDefn->GetFieldDefn(j)->GetType() == OFTDateTime)
    2320           9 :                 nWidth = 29;
    2321         113 :             VSIFPrintfL(fp, "<col min=\"%d\" max=\"%d\" width=\"%d\"/>\n",
    2322             :                         j + 1, 1024, nWidth);
    2323             : 
    2324         113 :             if (strcmp(poFDefn->GetFieldDefn(j)->GetNameRef(),
    2325         113 :                        CPLSPrintf("Field%d", j + 1)) != 0)
    2326          42 :                 bHasHeaders = true;
    2327             :         }
    2328          28 :         VSIFPrintfL(fp, "</cols>\n");
    2329             :     }
    2330           2 :     else if (!poLayer->GetCols().empty())
    2331           2 :         VSIFPrintfL(fp, "%s\n", poLayer->GetCols().c_str());
    2332             : 
    2333          30 :     VSIFPrintfL(fp, "<sheetData>\n");
    2334             : 
    2335          30 :     if (bHasHeaders && poFeature != nullptr)
    2336             :     {
    2337           8 :         VSIFPrintfL(fp, "<row r=\"%d\">\n", iRow);
    2338          48 :         for (int j = 0; j < nFields; j++)
    2339             :         {
    2340          40 :             const char *pszVal = poFDefn->GetFieldDefn(j)->GetNameRef();
    2341             :             std::map<std::string, int>::iterator oIter =
    2342          40 :                 oStringMap.find(pszVal);
    2343          40 :             int nStringIndex = 0;
    2344          40 :             if (oIter != oStringMap.end())
    2345           3 :                 nStringIndex = oIter->second;
    2346             :             else
    2347             :             {
    2348          37 :                 nStringIndex = (int)oStringList.size();
    2349          37 :                 oStringMap[pszVal] = nStringIndex;
    2350          37 :                 oStringList.push_back(pszVal);
    2351             :             }
    2352             : 
    2353          80 :             CPLString osCol = BuildColString(j);
    2354             : 
    2355          40 :             VSIFPrintfL(fp, "<c r=\"%s%d\" t=\"s\">\n", osCol.c_str(), iRow);
    2356          40 :             VSIFPrintfL(fp, "<v>%d</v>\n", nStringIndex);
    2357          40 :             VSIFPrintfL(fp, "</c>\n");
    2358             :         }
    2359           8 :         VSIFPrintfL(fp, "</row>\n");
    2360             : 
    2361           8 :         iRow++;
    2362             :     }
    2363             : 
    2364         173 :     while (poFeature != nullptr)
    2365             :     {
    2366         143 :         VSIFPrintfL(fp, "<row r=\"%d\">\n", iRow);
    2367         755 :         for (int j = 0; j < nFields; j++)
    2368             :         {
    2369         612 :             if (poFeature->IsFieldSetAndNotNull(j))
    2370             :             {
    2371         596 :                 CPLString osCol = BuildColString(j);
    2372             : 
    2373         298 :                 const OGRFieldDefn *poFieldDefn = poFDefn->GetFieldDefn(j);
    2374         298 :                 OGRFieldType eType = poFieldDefn->GetType();
    2375             : 
    2376         298 :                 if (eType == OFTReal)
    2377             :                 {
    2378          66 :                     VSIFPrintfL(fp, "<c r=\"%s%d\">\n", osCol.c_str(), iRow);
    2379          66 :                     VSIFPrintfL(fp, "<v>%.16g</v>\n",
    2380             :                                 poFeature->GetFieldAsDouble(j));
    2381          66 :                     VSIFPrintfL(fp, "</c>\n");
    2382             :                 }
    2383         232 :                 else if (eType == OFTInteger)
    2384             :                 {
    2385          34 :                     OGRFieldSubType eSubType = poFieldDefn->GetSubType();
    2386          34 :                     if (eSubType == OFSTBoolean)
    2387           1 :                         VSIFPrintfL(fp, "<c r=\"%s%d\" t=\"b\" s=\"5\">\n",
    2388             :                                     osCol.c_str(), iRow);
    2389             :                     else
    2390          33 :                         VSIFPrintfL(fp, "<c r=\"%s%d\">\n", osCol.c_str(),
    2391             :                                     iRow);
    2392          34 :                     VSIFPrintfL(fp, "<v>%d</v>\n",
    2393             :                                 poFeature->GetFieldAsInteger(j));
    2394          34 :                     VSIFPrintfL(fp, "</c>\n");
    2395             :                 }
    2396         198 :                 else if (eType == OFTInteger64)
    2397             :                 {
    2398          33 :                     VSIFPrintfL(fp, "<c r=\"%s%d\">\n", osCol.c_str(), iRow);
    2399          33 :                     VSIFPrintfL(fp, "<v>" CPL_FRMT_GIB "</v>\n",
    2400             :                                 poFeature->GetFieldAsInteger64(j));
    2401          33 :                     VSIFPrintfL(fp, "</c>\n");
    2402             :                 }
    2403         165 :                 else if (eType == OFTDate || eType == OFTDateTime ||
    2404             :                          eType == OFTTime)
    2405             :                 {
    2406          15 :                     int nYear = 0;
    2407          15 :                     int nMonth = 0;
    2408          15 :                     int nDay = 0;
    2409          15 :                     int nHour = 0;
    2410          15 :                     int nMinute = 0;
    2411          15 :                     int nTZFlag = 0;
    2412          15 :                     float fSecond = 0.0f;
    2413          15 :                     poFeature->GetFieldAsDateTime(j, &nYear, &nMonth, &nDay,
    2414             :                                                   &nHour, &nMinute, &fSecond,
    2415             :                                                   &nTZFlag);
    2416             :                     struct tm brokendowntime;
    2417          15 :                     memset(&brokendowntime, 0, sizeof(brokendowntime));
    2418          15 :                     brokendowntime.tm_year =
    2419          15 :                         (eType == OFTTime) ? 70 : nYear - 1900;
    2420          15 :                     brokendowntime.tm_mon = (eType == OFTTime) ? 0 : nMonth - 1;
    2421          15 :                     brokendowntime.tm_mday = (eType == OFTTime) ? 1 : nDay;
    2422          15 :                     brokendowntime.tm_hour = nHour;
    2423          15 :                     brokendowntime.tm_min = nMinute;
    2424          15 :                     brokendowntime.tm_sec = (int)fSecond;
    2425          15 :                     GIntBig nUnixTime = CPLYMDHMSToUnixTime(&brokendowntime);
    2426          15 :                     double dfNumberOfDaysSince1900 =
    2427          15 :                         (1.0 * nUnixTime / NUMBER_OF_SECONDS_PER_DAY);
    2428          15 :                     dfNumberOfDaysSince1900 +=
    2429          15 :                         fmod(fSecond, 1) / NUMBER_OF_SECONDS_PER_DAY;
    2430          28 :                     int s = (eType == OFTDate)       ? 1
    2431          13 :                             : (eType == OFTDateTime) ? 2
    2432             :                                                      : 3;
    2433          15 :                     if (eType == OFTDateTime && OGR_GET_MS(fSecond))
    2434           1 :                         s = 4;
    2435          15 :                     VSIFPrintfL(fp, "<c r=\"%s%d\" s=\"%d\">\n", osCol.c_str(),
    2436             :                                 iRow, s);
    2437          15 :                     if (eType != OFTTime)
    2438          13 :                         dfNumberOfDaysSince1900 +=
    2439             :                             NUMBER_OF_DAYS_BETWEEN_1900_AND_1970;
    2440          15 :                     if (eType == OFTDate)
    2441           2 :                         VSIFPrintfL(fp, "<v>%d</v>\n",
    2442           2 :                                     (int)(dfNumberOfDaysSince1900 + 0.1));
    2443             :                     else
    2444          13 :                         VSIFPrintfL(fp, "<v>%.16g</v>\n",
    2445             :                                     dfNumberOfDaysSince1900);
    2446          15 :                     VSIFPrintfL(fp, "</c>\n");
    2447             :                 }
    2448             :                 else
    2449             :                 {
    2450         150 :                     const char *pszVal = poFeature->GetFieldAsString(j);
    2451             :                     std::map<std::string, int>::iterator oIter =
    2452         150 :                         oStringMap.find(pszVal);
    2453         150 :                     int nStringIndex = 0;
    2454         150 :                     if (oIter != oStringMap.end())
    2455          38 :                         nStringIndex = oIter->second;
    2456             :                     else
    2457             :                     {
    2458         112 :                         nStringIndex = (int)oStringList.size();
    2459         112 :                         oStringMap[pszVal] = nStringIndex;
    2460         112 :                         oStringList.push_back(pszVal);
    2461             :                     }
    2462         150 :                     VSIFPrintfL(fp, "<c r=\"%s%d\" t=\"s\">\n", osCol.c_str(),
    2463             :                                 iRow);
    2464         150 :                     VSIFPrintfL(fp, "<v>%d</v>\n", nStringIndex);
    2465         150 :                     VSIFPrintfL(fp, "</c>\n");
    2466             :                 }
    2467             :             }
    2468             :         }
    2469         143 :         VSIFPrintfL(fp, "</row>\n");
    2470             : 
    2471         143 :         iRow++;
    2472         143 :         delete poFeature;
    2473         143 :         poFeature = poLayer->GetNextFeature();
    2474             :     }
    2475          30 :     VSIFPrintfL(fp, "</sheetData>\n");
    2476          30 :     VSIFPrintfL(fp, "</worksheet>\n");
    2477          30 :     VSIFCloseL(fp);
    2478          30 :     return true;
    2479             : }
    2480             : 
    2481             : /************************************************************************/
    2482             : /*                        WriteSharedStrings()                          */
    2483             : /************************************************************************/
    2484             : 
    2485          12 : static bool WriteSharedStrings(const char *pszName,
    2486             :                                std::vector<std::string> &oStringList)
    2487             : {
    2488             :     CPLString osTmpFilename(
    2489          24 :         CPLSPrintf("/vsizip/%s/xl/sharedStrings.xml", pszName));
    2490          12 :     VSILFILE *fp = VSIFOpenL(osTmpFilename, "wb");
    2491          12 :     if (!fp)
    2492           0 :         return false;
    2493          12 :     VSIFWriteL(XML_HEADER, strlen(XML_HEADER), 1, fp);
    2494          12 :     VSIFPrintfL(fp, "<sst %s uniqueCount=\"%d\">\n", MAIN_NS,
    2495          12 :                 (int)oStringList.size());
    2496         161 :     for (int i = 0; i < (int)oStringList.size(); i++)
    2497             :     {
    2498         149 :         VSIFPrintfL(fp, "<si>\n");
    2499         149 :         char *pszXML = OGRGetXML_UTF8_EscapedString(oStringList[i].c_str());
    2500         149 :         VSIFPrintfL(fp, "<t>%s</t>\n", pszXML);
    2501         149 :         CPLFree(pszXML);
    2502         149 :         VSIFPrintfL(fp, "</si>\n");
    2503             :     }
    2504          12 :     VSIFPrintfL(fp, "</sst>\n");
    2505          12 :     VSIFCloseL(fp);
    2506          12 :     return true;
    2507             : }
    2508             : 
    2509             : /************************************************************************/
    2510             : /*                           WriteStyles()                              */
    2511             : /************************************************************************/
    2512             : 
    2513          12 : static bool WriteStyles(const char *pszName)
    2514             : {
    2515          24 :     CPLString osTmpFilename(CPLSPrintf("/vsizip/%s/xl/styles.xml", pszName));
    2516          12 :     VSILFILE *fp = VSIFOpenL(osTmpFilename, "wb");
    2517          12 :     if (!fp)
    2518           0 :         return false;
    2519          12 :     VSIFWriteL(XML_HEADER, strlen(XML_HEADER), 1, fp);
    2520          12 :     VSIFPrintfL(fp, "<styleSheet %s>\n", MAIN_NS);
    2521          12 :     VSIFPrintfL(fp, "<numFmts count=\"4\">\n");
    2522          12 :     VSIFPrintfL(fp, "<numFmt formatCode=\"GENERAL\" numFmtId=\"164\"/>\n");
    2523          12 :     VSIFPrintfL(fp, "<numFmt formatCode=\"DD/MM/YY\" numFmtId=\"165\"/>\n");
    2524          12 :     VSIFPrintfL(
    2525             :         fp,
    2526             :         "<numFmt formatCode=\"DD/MM/YYYY\\ HH:MM:SS\" numFmtId=\"166\"/>\n");
    2527          12 :     VSIFPrintfL(fp, "<numFmt formatCode=\"HH:MM:SS\" numFmtId=\"167\"/>\n");
    2528          12 :     VSIFPrintfL(fp, "<numFmt formatCode=\"DD/MM/YYYY\\ HH:MM:SS.000\" "
    2529             :                     "numFmtId=\"168\"/>\n");
    2530          12 :     VSIFPrintfL(fp, "<numFmt "
    2531             :                     "formatCode=\"&quot;TRUE&quot;;&quot;TRUE&quot;;&quot;"
    2532             :                     "FALSE&quot;\" numFmtId=\"169\"/>\n");
    2533          12 :     VSIFPrintfL(fp, "</numFmts>\n");
    2534          12 :     VSIFPrintfL(fp, "<fonts count=\"1\">\n");
    2535          12 :     VSIFPrintfL(fp, "<font>\n");
    2536          12 :     VSIFPrintfL(fp, "<name val=\"Arial\"/>\n");
    2537          12 :     VSIFPrintfL(fp, "<family val=\"2\"/>\n");
    2538          12 :     VSIFPrintfL(fp, "<sz val=\"10\"/>\n");
    2539          12 :     VSIFPrintfL(fp, "</font>\n");
    2540          12 :     VSIFPrintfL(fp, "</fonts>\n");
    2541          12 :     VSIFPrintfL(fp, "<fills count=\"1\">\n");
    2542          12 :     VSIFPrintfL(fp, "<fill>\n");
    2543          12 :     VSIFPrintfL(fp, "<patternFill patternType=\"none\"/>\n");
    2544          12 :     VSIFPrintfL(fp, "</fill>\n");
    2545          12 :     VSIFPrintfL(fp, "</fills>\n");
    2546          12 :     VSIFPrintfL(fp, "<borders count=\"1\">\n");
    2547          12 :     VSIFPrintfL(fp, "<border diagonalDown=\"false\" diagonalUp=\"false\">\n");
    2548          12 :     VSIFPrintfL(fp, "<left/>\n");
    2549          12 :     VSIFPrintfL(fp, "<right/>\n");
    2550          12 :     VSIFPrintfL(fp, "<top/>\n");
    2551          12 :     VSIFPrintfL(fp, "<bottom/>\n");
    2552          12 :     VSIFPrintfL(fp, "<diagonal/>\n");
    2553          12 :     VSIFPrintfL(fp, "</border>\n");
    2554          12 :     VSIFPrintfL(fp, "</borders>\n");
    2555          12 :     VSIFPrintfL(fp, "<cellStyleXfs count=\"1\">\n");
    2556          12 :     VSIFPrintfL(fp, "<xf numFmtId=\"164\">\n");
    2557          12 :     VSIFPrintfL(fp, "</xf>\n");
    2558          12 :     VSIFPrintfL(fp, "</cellStyleXfs>\n");
    2559          12 :     VSIFPrintfL(fp, "<cellXfs count=\"6\">\n");
    2560          12 :     VSIFPrintfL(fp, "<xf numFmtId=\"164\" xfId=\"0\"/>\n");
    2561          12 :     VSIFPrintfL(fp, "<xf numFmtId=\"165\" xfId=\"0\"/>\n");
    2562          12 :     VSIFPrintfL(fp, "<xf numFmtId=\"166\" xfId=\"0\"/>\n");
    2563          12 :     VSIFPrintfL(fp, "<xf numFmtId=\"167\" xfId=\"0\"/>\n");
    2564          12 :     VSIFPrintfL(fp, "<xf numFmtId=\"168\" xfId=\"0\"/>\n");
    2565          12 :     VSIFPrintfL(fp, "<xf numFmtId=\"169\" xfId=\"0\"/>\n");
    2566          12 :     VSIFPrintfL(fp, "</cellXfs>\n");
    2567          12 :     VSIFPrintfL(fp, "<cellStyles count=\"1\">\n");
    2568          12 :     VSIFPrintfL(fp, "<cellStyle builtinId=\"0\" customBuiltin=\"false\" "
    2569             :                     "name=\"Normal\" xfId=\"0\"/>\n");
    2570          12 :     VSIFPrintfL(fp, "</cellStyles>\n");
    2571          12 :     VSIFPrintfL(fp, "</styleSheet>\n");
    2572          12 :     VSIFCloseL(fp);
    2573          12 :     return true;
    2574             : }
    2575             : 
    2576             : /************************************************************************/
    2577             : /*                           WriteWorkbookRels()                        */
    2578             : /************************************************************************/
    2579             : 
    2580          12 : static bool WriteWorkbookRels(const char *pszName, int nLayers)
    2581             : {
    2582             :     CPLString osTmpFilename(
    2583          24 :         CPLSPrintf("/vsizip/%s/xl/_rels/workbook.xml.rels", pszName));
    2584          12 :     VSILFILE *fp = VSIFOpenL(osTmpFilename, "wb");
    2585          12 :     if (!fp)
    2586           0 :         return false;
    2587          12 :     VSIFWriteL(XML_HEADER, strlen(XML_HEADER), 1, fp);
    2588          12 :     VSIFPrintfL(fp, "<Relationships xmlns=\"%s\">\n", SCHEMA_PACKAGE_RS);
    2589          12 :     VSIFPrintfL(fp,
    2590             :                 "<Relationship Id=\"rId1\" Type=\"%s/styles\" "
    2591             :                 "Target=\"styles.xml\"/>\n",
    2592             :                 SCHEMA_OD_RS);
    2593          42 :     for (int i = 0; i < nLayers; i++)
    2594             :     {
    2595          30 :         VSIFPrintfL(fp,
    2596             :                     "<Relationship Id=\"rId%d\" Type=\"%s/worksheet\" "
    2597             :                     "Target=\"worksheets/sheet%d.xml\"/>\n",
    2598             :                     2 + i, SCHEMA_OD_RS, 1 + i);
    2599             :     }
    2600          12 :     VSIFPrintfL(fp,
    2601             :                 "<Relationship Id=\"rId%d\" Type=\"%s/sharedStrings\" "
    2602             :                 "Target=\"sharedStrings.xml\"/>\n",
    2603             :                 2 + nLayers, SCHEMA_OD_RS);
    2604          12 :     VSIFPrintfL(fp, "</Relationships>\n");
    2605          12 :     VSIFCloseL(fp);
    2606          12 :     return true;
    2607             : }
    2608             : 
    2609             : /************************************************************************/
    2610             : /*                             WriteDotRels()                           */
    2611             : /************************************************************************/
    2612             : 
    2613          12 : static bool WriteDotRels(const char *pszName)
    2614             : {
    2615          24 :     CPLString osTmpFilename(CPLSPrintf("/vsizip/%s/_rels/.rels", pszName));
    2616          12 :     VSILFILE *fp = VSIFOpenL(osTmpFilename, "wb");
    2617          12 :     if (!fp)
    2618           0 :         return false;
    2619          12 :     VSIFWriteL(XML_HEADER, strlen(XML_HEADER), 1, fp);
    2620          12 :     VSIFPrintfL(fp, "<Relationships xmlns=\"%s\">\n", SCHEMA_PACKAGE_RS);
    2621          12 :     VSIFPrintfL(fp,
    2622             :                 "<Relationship Id=\"rId1\" Type=\"%s/officeDocument\" "
    2623             :                 "Target=\"xl/workbook.xml\"/>\n",
    2624             :                 SCHEMA_OD_RS);
    2625          12 :     VSIFPrintfL(
    2626             :         fp,
    2627             :         "<Relationship Id=\"rId2\" Type=\"%s/metadata/core-properties\" "
    2628             :         "Target=\"docProps/core.xml\"/>\n",
    2629             :         SCHEMA_PACKAGE_RS);
    2630          12 :     VSIFPrintfL(fp,
    2631             :                 "<Relationship Id=\"rId3\" Type=\"%s/extended-properties\" "
    2632             :                 "Target=\"docProps/app.xml\"/>\n",
    2633             :                 SCHEMA_OD_RS);
    2634          12 :     VSIFPrintfL(fp, "</Relationships>\n");
    2635          12 :     VSIFCloseL(fp);
    2636          12 :     return true;
    2637             : }
    2638             : 
    2639             : /************************************************************************/
    2640             : /*                            FlushCache()                              */
    2641             : /************************************************************************/
    2642             : 
    2643          47 : CPLErr OGRXLSXDataSource::FlushCache(bool /* bAtClosing */)
    2644             : {
    2645          47 :     if (!bUpdated)
    2646          35 :         return CE_None;
    2647             : 
    2648             :     /* Cause all layers to be loaded */
    2649          42 :     for (int i = 0; i < nLayers; i++)
    2650             :     {
    2651          30 :         ((OGRXLSXLayer *)papoLayers[i])->GetLayerDefn();
    2652             :     }
    2653             : 
    2654             :     VSIStatBufL sStat;
    2655          12 :     if (VSIStatL(pszName, &sStat) == 0)
    2656             :     {
    2657           4 :         if (VSIUnlink(pszName) != 0)
    2658             :         {
    2659           0 :             CPLError(CE_Failure, CPLE_FileIO, "Cannot delete %s", pszName);
    2660           0 :             return CE_Failure;
    2661             :         }
    2662             :     }
    2663             : 
    2664          24 :     CPLConfigOptionSetter oZip64Disable("CPL_CREATE_ZIP64", "NO", false);
    2665             : 
    2666             :     /* Maintain new ZIP files opened */
    2667          24 :     CPLString osTmpFilename(CPLSPrintf("/vsizip/%s", pszName));
    2668          12 :     VSILFILE *fpZIP = VSIFOpenExL(osTmpFilename, "wb", true);
    2669          12 :     if (fpZIP == nullptr)
    2670             :     {
    2671           0 :         CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s: %s", pszName,
    2672             :                  VSIGetLastErrorMsg());
    2673           0 :         return CE_Failure;
    2674             :     }
    2675             : 
    2676          12 :     bool bOK = WriteContentTypes(pszName, nLayers);
    2677             : 
    2678             :     // VSIMkdir(CPLSPrintf("/vsizip/%s/docProps", pszName),0755);
    2679          12 :     bOK &= WriteApp(pszName);
    2680          12 :     bOK &= WriteCore(pszName);
    2681             : 
    2682             :     // VSIMkdir(CPLSPrintf("/vsizip/%s/xl", pszName),0755);
    2683          12 :     bOK &= WriteWorkbook(pszName, this);
    2684             : 
    2685          24 :     std::map<std::string, int> oStringMap;
    2686          12 :     std::vector<std::string> oStringList;
    2687             : 
    2688             :     // VSIMkdir(CPLSPrintf("/vsizip/%s/xl/worksheets", pszName),0755);
    2689          42 :     for (int i = 0; i < nLayers; i++)
    2690             :     {
    2691          30 :         bOK &= WriteLayer(pszName, papoLayers[i], i, oStringMap, oStringList);
    2692             :     }
    2693             : 
    2694          12 :     bOK &= WriteSharedStrings(pszName, oStringList);
    2695          12 :     bOK &= WriteStyles(pszName);
    2696             : 
    2697             :     // VSIMkdir(CPLSPrintf("/vsizip/%s/xl/_rels", pszName),0755);
    2698          12 :     bOK &= WriteWorkbookRels(pszName, nLayers);
    2699             : 
    2700             :     // VSIMkdir(CPLSPrintf("/vsizip/%s/_rels", pszName),0755);
    2701          12 :     bOK &= WriteDotRels(pszName);
    2702             : 
    2703             :     /* Now close ZIP file */
    2704          12 :     if (VSIFCloseL(fpZIP) != 0)
    2705           0 :         bOK = false;
    2706             : 
    2707             :     /* Reset updated flag at datasource and layer level */
    2708          12 :     bUpdated = false;
    2709          42 :     for (int i = 0; i < nLayers; i++)
    2710             :     {
    2711          30 :         ((OGRXLSXLayer *)papoLayers[i])->SetUpdated(false);
    2712             :     }
    2713             : 
    2714          12 :     if (!bOK)
    2715             :     {
    2716           0 :         CPLError(CE_Failure, CPLE_FileIO, "Failure when saving %s", pszName);
    2717             :     }
    2718             : 
    2719          12 :     return bOK ? CE_None : CE_Failure;
    2720             : }
    2721             : 
    2722             : }  // namespace OGRXLSX

Generated by: LCOV version 1.14