LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/ods - ogrodsdriver.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 93 114 81.6 %
Date: 2024-11-21 22:18:42 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  ODS Translator
       4             :  * Purpose:  Implements OGRODSDriver.
       5             :  * Author:   Even Rouault, even dot rouault at spatialys.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2012, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_conv.h"
      14             : #include "ogr_ods.h"
      15             : #include "ogrsf_frmts.h"
      16             : 
      17             : using namespace OGRODS;
      18             : 
      19             : // g++ -DHAVE_EXPAT -g -Wall -fPIC ogr/ogrsf_frmts/ods/*.cpp -shared
      20             : // -o ogr_ODS.so -Iport -Igcore -Iogr -Iogr/ogrsf_frmts
      21             : // -Iogr/ogrsf_frmts/mem -Iogr/ogrsf_frmts/ods -L. -lgdal
      22             : 
      23             : /************************************************************************/
      24             : /*                              Identify()                              */
      25             : /************************************************************************/
      26             : 
      27       45288 : static int OGRODSDriverIdentify(GDALOpenInfo *poOpenInfo)
      28             : {
      29       45288 :     if (poOpenInfo->fpL == nullptr &&
      30       42681 :         STARTS_WITH_CI(poOpenInfo->pszFilename, "ODS:"))
      31             :     {
      32           4 :         return TRUE;
      33             :     }
      34             : 
      35       45284 :     if (EQUAL(CPLGetFilename(poOpenInfo->pszFilename), "content.xml"))
      36             :     {
      37           0 :         return poOpenInfo->nHeaderBytes != 0 &&
      38           0 :                strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
      39           0 :                       "<office:document-content") != nullptr;
      40             :     }
      41             : 
      42       45284 :     const char *pszExt = CPLGetExtension(poOpenInfo->pszFilename);
      43       45284 :     if (!EQUAL(pszExt, "ODS") && !EQUAL(pszExt, "ODS}"))
      44       45068 :         return FALSE;
      45             : 
      46         216 :     if (STARTS_WITH(poOpenInfo->pszFilename, "/vsizip/") ||
      47         216 :         STARTS_WITH(poOpenInfo->pszFilename, "/vsitar/"))
      48           0 :         return TRUE;
      49             : 
      50         356 :     return poOpenInfo->nHeaderBytes > 4 &&
      51         356 :            memcmp(poOpenInfo->pabyHeader, "PK\x03\x04", 4) == 0;
      52             : }
      53             : 
      54             : /************************************************************************/
      55             : /*                                Open()                                */
      56             : /************************************************************************/
      57             : 
      58          72 : static GDALDataset *OGRODSDriverOpen(GDALOpenInfo *poOpenInfo)
      59             : 
      60             : {
      61          72 :     if (!OGRODSDriverIdentify(poOpenInfo))
      62           0 :         return nullptr;
      63             : 
      64          72 :     const char *pszFilename = poOpenInfo->pszFilename;
      65          72 :     const bool bIsODSPrefixed =
      66          72 :         poOpenInfo->fpL == nullptr && STARTS_WITH_CI(pszFilename, "ODS:");
      67         144 :     const bool bIsVsiZipOrTarPrefixed = STARTS_WITH(pszFilename, "/vsizip/") ||
      68          72 :                                         STARTS_WITH(pszFilename, "/vsitar/");
      69          72 :     if (bIsVsiZipOrTarPrefixed)
      70             :     {
      71           0 :         if (poOpenInfo->eAccess != GA_ReadOnly)
      72           0 :             return nullptr;
      73             :     }
      74             : 
      75          72 :     bool bIsZIP = false;
      76          72 :     if (bIsODSPrefixed)
      77             :     {
      78           2 :         pszFilename += strlen("ODS:");
      79           2 :         if (!bIsVsiZipOrTarPrefixed)
      80             :         {
      81           2 :             VSILFILE *fp = VSIFOpenL(pszFilename, "rb");
      82           2 :             if (fp == nullptr)
      83           0 :                 return nullptr;
      84           2 :             GByte abyHeader[4] = {0};
      85           2 :             VSIFReadL(abyHeader, 1, 4, fp);
      86           2 :             VSIFCloseL(fp);
      87           2 :             bIsZIP = memcmp(abyHeader, "PK\x03\x04", 4) == 0;
      88             :         }
      89             :     }
      90             :     else
      91             :     {
      92          70 :         bIsZIP = true;
      93             :     }
      94             : 
      95         144 :     std::string osPrefixedFilename;
      96          72 :     if (bIsZIP)
      97             :     {
      98          71 :         if (!bIsVsiZipOrTarPrefixed)
      99             :         {
     100          71 :             osPrefixedFilename = "/vsizip/{";
     101          71 :             osPrefixedFilename += pszFilename;
     102          71 :             osPrefixedFilename += "}";
     103             :         }
     104             :         else
     105             :         {
     106           0 :             osPrefixedFilename = pszFilename;
     107             :         }
     108             :     }
     109             : 
     110         144 :     CPLString osContentFilename(pszFilename);
     111          72 :     if (bIsZIP)
     112             :     {
     113          71 :         osContentFilename.Printf("%s/content.xml", osPrefixedFilename.c_str());
     114             :     }
     115           1 :     else if (poOpenInfo->eAccess ==
     116             :              GA_Update) /* We cannot update the xml file, only the .ods */
     117             :     {
     118           0 :         return nullptr;
     119             :     }
     120             : 
     121          72 :     VSILFILE *fpContent = VSIFOpenL(osContentFilename, "rb");
     122          72 :     if (fpContent == nullptr)
     123           0 :         return nullptr;
     124             : 
     125             :     char szBuffer[1024];
     126          72 :     int nRead = (int)VSIFReadL(szBuffer, 1, sizeof(szBuffer) - 1, fpContent);
     127          72 :     szBuffer[nRead] = 0;
     128             : 
     129          72 :     if (strstr(szBuffer, "<office:document-content") == nullptr)
     130             :     {
     131           0 :         VSIFCloseL(fpContent);
     132           0 :         return nullptr;
     133             :     }
     134             : 
     135             :     /* We could also check that there's a <office:spreadsheet>, but it might be
     136             :      * further */
     137             :     /* in the XML due to styles, etc... */
     138             : 
     139          72 :     VSILFILE *fpSettings = nullptr;
     140          72 :     if (bIsZIP)
     141             :     {
     142             :         CPLString osTmpFilename(
     143          71 :             CPLSPrintf("%s/settings.xml", osPrefixedFilename.c_str()));
     144          71 :         fpSettings = VSIFOpenL(osTmpFilename, "rb");
     145             :     }
     146             : 
     147          72 :     OGRODSDataSource *poDS = new OGRODSDataSource(poOpenInfo->papszOpenOptions);
     148             : 
     149          72 :     if (!poDS->Open(pszFilename, fpContent, fpSettings,
     150          72 :                     poOpenInfo->eAccess == GA_Update))
     151             :     {
     152           0 :         delete poDS;
     153           0 :         poDS = nullptr;
     154             :     }
     155             :     else
     156             :     {
     157          72 :         poDS->SetDescription(poOpenInfo->pszFilename);
     158             :     }
     159             : 
     160          72 :     return poDS;
     161             : }
     162             : 
     163             : /************************************************************************/
     164             : /*                         OGRODSDriverCreate()                         */
     165             : /************************************************************************/
     166             : 
     167          38 : static GDALDataset *OGRODSDriverCreate(const char *pszName, int /* nXSize */,
     168             :                                        int /* nYSize */, int /* nBands */,
     169             :                                        GDALDataType /* eDT */,
     170             :                                        char **papszOptions)
     171             : 
     172             : {
     173          38 :     if (!EQUAL(CPLGetExtension(pszName), "ODS"))
     174             :     {
     175           0 :         CPLError(CE_Failure, CPLE_AppDefined, "File extension should be ODS");
     176           0 :         return nullptr;
     177             :     }
     178             : 
     179             :     /* -------------------------------------------------------------------- */
     180             :     /*      First, ensure there isn't any such file yet.                    */
     181             :     /* -------------------------------------------------------------------- */
     182             :     VSIStatBufL sStatBuf;
     183             : 
     184          38 :     if (VSIStatL(pszName, &sStatBuf) == 0)
     185             :     {
     186           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     187             :                  "It seems a file system object called '%s' already exists.",
     188             :                  pszName);
     189             : 
     190           0 :         return nullptr;
     191             :     }
     192             : 
     193             :     /* -------------------------------------------------------------------- */
     194             :     /*      Try to create datasource.                                       */
     195             :     /* -------------------------------------------------------------------- */
     196          38 :     OGRODSDataSource *poDS = new OGRODSDataSource(nullptr);
     197             : 
     198          38 :     if (!poDS->Create(pszName, papszOptions))
     199             :     {
     200           0 :         delete poDS;
     201           0 :         return nullptr;
     202             :     }
     203             : 
     204          38 :     return poDS;
     205             : }
     206             : 
     207             : /************************************************************************/
     208             : /*                           RegisterOGRODS()                           */
     209             : /************************************************************************/
     210             : 
     211        1595 : void RegisterOGRODS()
     212             : 
     213             : {
     214        1595 :     if (GDALGetDriverByName("ODS") != nullptr)
     215         302 :         return;
     216             : 
     217        1293 :     GDALDriver *poDriver = new GDALDriver();
     218             : 
     219        1293 :     poDriver->SetDescription("ODS");
     220        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
     221        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE_LAYER, "YES");
     222        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_DELETE_LAYER, "YES");
     223        1293 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Open Document/ LibreOffice / "
     224        1293 :                                                  "OpenOffice Spreadsheet");
     225        1293 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "ods");
     226        1293 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/vector/ods.html");
     227        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
     228        1293 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATATYPES,
     229             :                               "Integer Integer64 Real String Date DateTime "
     230        1293 :                               "Time Binary");
     231        1293 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATASUBTYPES, "Boolean");
     232        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_NONSPATIAL, "YES");
     233        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_MULTIPLE_VECTOR_LAYERS, "YES");
     234        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_MEASURED_GEOMETRIES, "YES");
     235        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_CURVE_GEOMETRIES, "YES");
     236        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES");
     237        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE_FIELD, "YES");
     238        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_DELETE_FIELD, "YES");
     239        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_REORDER_FIELDS, "YES");
     240        1293 :     poDriver->SetMetadataItem(GDAL_DMD_ALTER_FIELD_DEFN_FLAGS, "Name Type");
     241        1293 :     poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "OGRSQL SQLITE");
     242             : 
     243        1293 :     poDriver->SetMetadataItem(
     244             :         GDAL_DMD_OPENOPTIONLIST,
     245             :         "<OpenOptionList>"
     246             :         "  <Option name='FIELD_TYPES' type='string-select' "
     247             :         "description='If set to STRING, all fields will be of type String. "
     248             :         "Otherwise the driver autodetects the field type from field content.' "
     249             :         "default='AUTO'>"
     250             :         "    <Value>AUTO</Value>"
     251             :         "    <Value>STRING</Value>"
     252             :         "  </Option>"
     253             :         "  <Option name='HEADERS' type='string-select' "
     254             :         "description='Defines if the first line should be considered as "
     255             :         "containing the name of the fields.' "
     256             :         "default='AUTO'>"
     257             :         "    <Value>AUTO</Value>"
     258             :         "    <Value>FORCE</Value>"
     259             :         "    <Value>DISABLE</Value>"
     260             :         "  </Option>"
     261        1293 :         "</OpenOptionList>");
     262             : 
     263        1293 :     poDriver->pfnIdentify = OGRODSDriverIdentify;
     264        1293 :     poDriver->pfnOpen = OGRODSDriverOpen;
     265        1293 :     poDriver->pfnCreate = OGRODSDriverCreate;
     266             : 
     267        1293 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     268             : }

Generated by: LCOV version 1.14