LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/xlsx - ogrxlsxdatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1134 1280 88.6 %
Date: 2024-05-02 22:57:13 Functions: 73 76 96.1 %

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

Generated by: LCOV version 1.14