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

Generated by: LCOV version 1.14