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 1315 88.4 %
Date: 2025-09-10 17:48:50 Functions: 77 80 96.2 %

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

Generated by: LCOV version 1.14