LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/csv - ogrcsvdatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 433 556 77.9 %
Date: 2024-11-21 22:18:42 Functions: 23 23 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  CSV Translator
       4             :  * Purpose:  Implements OGRCSVDataSource class
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2004, Frank Warmerdam <warmerdam@pobox.com>
       9             :  * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "ogr_csv.h"
      16             : 
      17             : #include <cerrno>
      18             : #include <cstring>
      19             : #include <string>
      20             : 
      21             : #include "cpl_conv.h"
      22             : #include "cpl_csv.h"
      23             : #include "cpl_error.h"
      24             : #include "cpl_string.h"
      25             : #include "cpl_vsi.h"
      26             : #include "cpl_vsi_virtual.h"
      27             : #include "ogr_core.h"
      28             : #include "ogr_feature.h"
      29             : #include "ogr_geometry.h"
      30             : #include "ogr_spatialref.h"
      31             : #include "ogreditablelayer.h"
      32             : #include "ogrsf_frmts.h"
      33             : 
      34             : /************************************************************************/
      35             : /*                     OGRCSVEditableLayerSynchronizer                  */
      36             : /************************************************************************/
      37             : 
      38             : class OGRCSVEditableLayerSynchronizer final
      39             :     : public IOGREditableLayerSynchronizer
      40             : {
      41             :     OGRCSVLayer *m_poCSVLayer;
      42             :     char **m_papszOpenOptions;
      43             : 
      44             :   public:
      45         208 :     OGRCSVEditableLayerSynchronizer(OGRCSVLayer *poCSVLayer,
      46             :                                     CSLConstList papszOpenOptions)
      47         208 :         : m_poCSVLayer(poCSVLayer),
      48         208 :           m_papszOpenOptions(CSLDuplicate(papszOpenOptions))
      49             :     {
      50         208 :     }
      51             : 
      52             :     virtual ~OGRCSVEditableLayerSynchronizer() override;
      53             : 
      54             :     virtual OGRErr EditableSyncToDisk(OGRLayer *poEditableLayer,
      55             :                                       OGRLayer **ppoDecoratedLayer) override;
      56             : 
      57          18 :     std::vector<std::string> GetFileList()
      58             :     {
      59          18 :         return m_poCSVLayer->GetFileList();
      60             :     }
      61             : };
      62             : 
      63             : /************************************************************************/
      64             : /*                     ~OGRCSVEditableLayerSynchronizer()               */
      65             : /************************************************************************/
      66             : 
      67         416 : OGRCSVEditableLayerSynchronizer::~OGRCSVEditableLayerSynchronizer()
      68             : {
      69         208 :     CSLDestroy(m_papszOpenOptions);
      70         416 : }
      71             : 
      72             : /************************************************************************/
      73             : /*                       EditableSyncToDisk()                           */
      74             : /************************************************************************/
      75             : 
      76           9 : OGRErr OGRCSVEditableLayerSynchronizer::EditableSyncToDisk(
      77             :     OGRLayer *poEditableLayer, OGRLayer **ppoDecoratedLayer)
      78             : {
      79           9 :     CPLAssert(m_poCSVLayer == *ppoDecoratedLayer);
      80             : 
      81           9 :     GDALDataset *poDS = m_poCSVLayer->GetDataset();
      82          18 :     const CPLString osLayerName(m_poCSVLayer->GetName());
      83          18 :     const CPLString osFilename(m_poCSVLayer->GetFilename());
      84           9 :     const bool bCreateCSVT = m_poCSVLayer->GetCreateCSVT();
      85          18 :     const CPLString osCSVTFilename(CPLResetExtension(osFilename, "csvt"));
      86             :     VSIStatBufL sStatBuf;
      87           9 :     const bool bHasCSVT = VSIStatL(osCSVTFilename, &sStatBuf) == 0;
      88          18 :     CPLString osTmpFilename(osFilename);
      89          18 :     CPLString osTmpCSVTFilename(osFilename);
      90           9 :     if (VSIStatL(osFilename, &sStatBuf) == 0)
      91             :     {
      92           9 :         osTmpFilename += "_ogr_tmp.csv";
      93           9 :         osTmpCSVTFilename += "_ogr_tmp.csvt";
      94             :     }
      95           9 :     const char chDelimiter = m_poCSVLayer->GetDelimiter();
      96             :     OGRCSVLayer *poCSVTmpLayer = new OGRCSVLayer(
      97           9 :         poDS, osLayerName, nullptr, -1, osTmpFilename, true, true, chDelimiter);
      98           9 :     poCSVTmpLayer->BuildFeatureDefn(nullptr, nullptr, m_papszOpenOptions);
      99           9 :     poCSVTmpLayer->SetCRLF(m_poCSVLayer->GetCRLF());
     100           9 :     poCSVTmpLayer->SetCreateCSVT(bCreateCSVT || bHasCSVT);
     101           9 :     poCSVTmpLayer->SetWriteBOM(m_poCSVLayer->GetWriteBOM());
     102           9 :     poCSVTmpLayer->SetStringQuoting(m_poCSVLayer->GetStringQuoting());
     103             : 
     104           9 :     if (m_poCSVLayer->GetGeometryFormat() == OGR_CSV_GEOM_AS_WKT)
     105           5 :         poCSVTmpLayer->SetWriteGeometry(wkbNone, OGR_CSV_GEOM_AS_WKT, nullptr);
     106             : 
     107             :     const bool bKeepGeomColmuns =
     108           9 :         CPLFetchBool(m_papszOpenOptions, "KEEP_GEOM_COLUMNS", true);
     109             : 
     110           9 :     OGRErr eErr = OGRERR_NONE;
     111           9 :     OGRFeatureDefn *poEditableFDefn = poEditableLayer->GetLayerDefn();
     112          31 :     for (int i = 0; eErr == OGRERR_NONE && i < poEditableFDefn->GetFieldCount();
     113             :          i++)
     114             :     {
     115          44 :         OGRFieldDefn oFieldDefn(poEditableFDefn->GetFieldDefn(i));
     116          22 :         int iGeomFieldIdx = 0;
     117          70 :         if ((EQUAL(oFieldDefn.GetNameRef(), "WKT") &&
     118          38 :              (iGeomFieldIdx = poEditableFDefn->GetGeomFieldIndex("")) >= 0) ||
     119          32 :             (bKeepGeomColmuns &&
     120          16 :              (iGeomFieldIdx = poEditableFDefn->GetGeomFieldIndex(
     121          38 :                   (std::string("geom_") + oFieldDefn.GetNameRef()).c_str())) >=
     122             :                  0))
     123             :         {
     124             :             OGRGeomFieldDefn oGeomFieldDefn(
     125           5 :                 poEditableFDefn->GetGeomFieldDefn(iGeomFieldIdx));
     126           5 :             eErr = poCSVTmpLayer->CreateGeomField(&oGeomFieldDefn);
     127             :         }
     128             :         else
     129             :         {
     130          17 :             eErr = poCSVTmpLayer->CreateField(&oFieldDefn);
     131             :         }
     132             :     }
     133             : 
     134          12 :     const bool bHasXY = !m_poCSVLayer->GetXField().empty() &&
     135           3 :                         !m_poCSVLayer->GetYField().empty();
     136           9 :     const bool bHasZ = !m_poCSVLayer->GetZField().empty();
     137           9 :     if (bHasXY && !bKeepGeomColmuns)
     138             :     {
     139           4 :         if (poCSVTmpLayer->GetLayerDefn()->GetFieldIndex(
     140           4 :                 m_poCSVLayer->GetXField()) < 0)
     141             :         {
     142           4 :             OGRFieldDefn oFieldDefn(m_poCSVLayer->GetXField(), OFTReal);
     143           2 :             if (eErr == OGRERR_NONE)
     144           2 :                 eErr = poCSVTmpLayer->CreateField(&oFieldDefn);
     145             :         }
     146           4 :         if (poCSVTmpLayer->GetLayerDefn()->GetFieldIndex(
     147           4 :                 m_poCSVLayer->GetYField()) < 0)
     148             :         {
     149           4 :             OGRFieldDefn oFieldDefn(m_poCSVLayer->GetYField(), OFTReal);
     150           2 :             if (eErr == OGRERR_NONE)
     151           2 :                 eErr = poCSVTmpLayer->CreateField(&oFieldDefn);
     152             :         }
     153           3 :         if (bHasZ && poCSVTmpLayer->GetLayerDefn()->GetFieldIndex(
     154           1 :                          m_poCSVLayer->GetZField()) < 0)
     155             :         {
     156           2 :             OGRFieldDefn oFieldDefn(m_poCSVLayer->GetZField(), OFTReal);
     157           1 :             if (eErr == OGRERR_NONE)
     158           1 :                 eErr = poCSVTmpLayer->CreateField(&oFieldDefn);
     159             :         }
     160             :     }
     161             : 
     162           9 :     int nFirstGeomColIdx = 0;
     163           9 :     if (m_poCSVLayer->HasHiddenWKTColumn())
     164             :     {
     165           2 :         poCSVTmpLayer->SetWriteGeometry(
     166           1 :             poEditableFDefn->GetGeomFieldDefn(0)->GetType(),
     167             :             OGR_CSV_GEOM_AS_WKT,
     168           1 :             poEditableFDefn->GetGeomFieldDefn(0)->GetNameRef());
     169           1 :         nFirstGeomColIdx = 1;
     170             :     }
     171             : 
     172           9 :     if (!(poEditableFDefn->GetGeomFieldCount() == 1 && bHasXY))
     173             :     {
     174          12 :         for (int i = nFirstGeomColIdx;
     175          12 :              eErr == OGRERR_NONE && i < poEditableFDefn->GetGeomFieldCount();
     176             :              i++)
     177             :         {
     178             :             OGRGeomFieldDefn oGeomFieldDefn(
     179           6 :                 poEditableFDefn->GetGeomFieldDefn(i));
     180          12 :             if (poCSVTmpLayer->GetLayerDefn()->GetGeomFieldIndex(
     181          12 :                     oGeomFieldDefn.GetNameRef()) >= 0)
     182           5 :                 continue;
     183           1 :             eErr = poCSVTmpLayer->CreateGeomField(&oGeomFieldDefn);
     184             :         }
     185             :     }
     186             : 
     187           9 :     poEditableLayer->ResetReading();
     188             : 
     189             :     // Disable all filters.
     190           9 :     const char *pszQueryStringConst = poEditableLayer->GetAttrQueryString();
     191             :     char *pszQueryStringBak =
     192           9 :         pszQueryStringConst ? CPLStrdup(pszQueryStringConst) : nullptr;
     193           9 :     poEditableLayer->SetAttributeFilter(nullptr);
     194             : 
     195           9 :     const int iFilterGeomIndexBak = poEditableLayer->GetGeomFieldFilter();
     196           9 :     OGRGeometry *poFilterGeomBak = poEditableLayer->GetSpatialFilter();
     197           9 :     if (poFilterGeomBak)
     198           1 :         poFilterGeomBak = poFilterGeomBak->clone();
     199           9 :     poEditableLayer->SetSpatialFilter(nullptr);
     200             : 
     201             :     auto aoMapSrcToTargetIdx =
     202             :         poCSVTmpLayer->GetLayerDefn()->ComputeMapForSetFrom(
     203          18 :             poEditableLayer->GetLayerDefn(), true);
     204           9 :     aoMapSrcToTargetIdx.push_back(
     205           9 :         -1);  // add dummy entry to be sure that .data() is valid
     206             : 
     207          21 :     for (auto &&poFeature : poEditableLayer)
     208             :     {
     209          12 :         if (eErr != OGRERR_NONE)
     210           0 :             break;
     211             :         OGRFeature *poNewFeature =
     212          12 :             new OGRFeature(poCSVTmpLayer->GetLayerDefn());
     213          12 :         poNewFeature->SetFrom(poFeature.get(), aoMapSrcToTargetIdx.data(),
     214             :                               true);
     215          12 :         if (bHasXY)
     216             :         {
     217           3 :             OGRGeometry *poGeom = poFeature->GetGeometryRef();
     218           6 :             if (poGeom != nullptr &&
     219           3 :                 wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
     220             :             {
     221           3 :                 auto poPoint = poGeom->toPoint();
     222           3 :                 poNewFeature->SetField(m_poCSVLayer->GetXField(),
     223             :                                        poPoint->getX());
     224           3 :                 poNewFeature->SetField(m_poCSVLayer->GetYField(),
     225             :                                        poPoint->getY());
     226           3 :                 if (bHasZ)
     227             :                 {
     228           1 :                     poNewFeature->SetField(m_poCSVLayer->GetZField(),
     229             :                                            poPoint->getZ());
     230             :                 }
     231             :             }
     232             :         }
     233          12 :         eErr = poCSVTmpLayer->CreateFeature(poNewFeature);
     234          12 :         delete poNewFeature;
     235             :     }
     236           9 :     delete poCSVTmpLayer;
     237             : 
     238             :     // Restore filters.
     239           9 :     poEditableLayer->SetAttributeFilter(pszQueryStringBak);
     240           9 :     CPLFree(pszQueryStringBak);
     241           9 :     poEditableLayer->SetSpatialFilter(iFilterGeomIndexBak, poFilterGeomBak);
     242           9 :     delete poFilterGeomBak;
     243             : 
     244           9 :     if (eErr != OGRERR_NONE)
     245             :     {
     246           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Error while creating %s",
     247             :                  osTmpFilename.c_str());
     248           0 :         VSIUnlink(osTmpFilename);
     249           0 :         VSIUnlink(CPLResetExtension(osTmpFilename, "csvt"));
     250           0 :         return eErr;
     251             :     }
     252             : 
     253           9 :     delete m_poCSVLayer;
     254             : 
     255           9 :     if (osFilename != osTmpFilename)
     256             :     {
     257           9 :         const CPLString osTmpOriFilename(osFilename + ".ogr_bak");
     258           9 :         const CPLString osTmpOriCSVTFilename(osCSVTFilename + ".ogr_bak");
     259          18 :         if (VSIRename(osFilename, osTmpOriFilename) != 0 ||
     260           4 :             (bHasCSVT &&
     261           4 :              VSIRename(osCSVTFilename, osTmpOriCSVTFilename) != 0) ||
     262          22 :             VSIRename(osTmpFilename, osFilename) != 0 ||
     263           4 :             (bHasCSVT && VSIRename(osTmpCSVTFilename, osCSVTFilename) != 0))
     264             :         {
     265           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot rename files");
     266           0 :             *ppoDecoratedLayer = nullptr;
     267           0 :             m_poCSVLayer = nullptr;
     268           0 :             return OGRERR_FAILURE;
     269             :         }
     270           9 :         VSIUnlink(osTmpOriFilename);
     271           9 :         if (bHasCSVT)
     272           4 :             VSIUnlink(osTmpOriCSVTFilename);
     273             :     }
     274             : 
     275           9 :     VSILFILE *fp = VSIFOpenL(osFilename, "rb+");
     276           9 :     if (fp == nullptr)
     277             :     {
     278           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot reopen updated %s",
     279             :                  osFilename.c_str());
     280           0 :         *ppoDecoratedLayer = nullptr;
     281           0 :         m_poCSVLayer = nullptr;
     282           0 :         return OGRERR_FAILURE;
     283             :     }
     284             : 
     285           9 :     m_poCSVLayer =
     286             :         new OGRCSVLayer(poDS, osLayerName, fp, -1, osFilename, false, /* new */
     287             :                         true, /* update */
     288           9 :                         chDelimiter);
     289           9 :     m_poCSVLayer->BuildFeatureDefn(nullptr, nullptr, m_papszOpenOptions);
     290           9 :     *ppoDecoratedLayer = m_poCSVLayer;
     291             : 
     292           9 :     return OGRERR_NONE;
     293             : }
     294             : 
     295             : /************************************************************************/
     296             : /*                        OGRCSVEditableLayer                           */
     297             : /************************************************************************/
     298             : 
     299             : class OGRCSVEditableLayer final : public IOGRCSVLayer, public OGREditableLayer
     300             : {
     301             :     std::set<CPLString> m_oSetFields;
     302             : 
     303             :   public:
     304             :     OGRCSVEditableLayer(OGRCSVLayer *poCSVLayer, CSLConstList papszOpenOptions);
     305             : 
     306         326 :     OGRLayer *GetLayer() override
     307             :     {
     308         326 :         return this;
     309             :     }
     310             : 
     311          18 :     std::vector<std::string> GetFileList() override
     312             :     {
     313             :         return cpl::down_cast<OGRCSVEditableLayerSynchronizer *>(
     314             :                    m_poSynchronizer)
     315          18 :             ->GetFileList();
     316             :     }
     317             : 
     318             :     virtual OGRErr CreateField(const OGRFieldDefn *poField,
     319             :                                int bApproxOK = TRUE) override;
     320             :     virtual OGRErr DeleteField(int iField) override;
     321             :     virtual OGRErr AlterFieldDefn(int iField, OGRFieldDefn *poNewFieldDefn,
     322             :                                   int nFlagsIn) override;
     323             :     virtual GIntBig GetFeatureCount(int bForce = TRUE) override;
     324             : };
     325             : 
     326             : /************************************************************************/
     327             : /*                       GRCSVEditableLayer()                           */
     328             : /************************************************************************/
     329             : 
     330         208 : OGRCSVEditableLayer::OGRCSVEditableLayer(OGRCSVLayer *poCSVLayer,
     331         208 :                                          CSLConstList papszOpenOptions)
     332             :     : OGREditableLayer(
     333             :           poCSVLayer, true,
     334         208 :           new OGRCSVEditableLayerSynchronizer(poCSVLayer, papszOpenOptions),
     335         416 :           true)
     336             : {
     337         208 :     SetSupportsCreateGeomField(true);
     338         208 :     SetSupportsCurveGeometries(true);
     339         208 : }
     340             : 
     341             : /************************************************************************/
     342             : /*                            CreateField()                             */
     343             : /************************************************************************/
     344             : 
     345         399 : OGRErr OGRCSVEditableLayer::CreateField(const OGRFieldDefn *poNewField,
     346             :                                         int bApproxOK)
     347             : 
     348             : {
     349         399 :     if (m_poEditableFeatureDefn->GetFieldCount() >= 10000)
     350             :     {
     351           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Limiting to 10000 fields");
     352           0 :         return OGRERR_FAILURE;
     353             :     }
     354             : 
     355         399 :     if (m_oSetFields.empty())
     356             :     {
     357         101 :         for (int i = 0; i < m_poEditableFeatureDefn->GetFieldCount(); i++)
     358             :         {
     359             :             m_oSetFields.insert(
     360           8 :                 CPLString(
     361           4 :                     m_poEditableFeatureDefn->GetFieldDefn(i)->GetNameRef())
     362           8 :                     .toupper());
     363             :         }
     364             :     }
     365             : 
     366         798 :     const OGRCSVCreateFieldAction eAction = OGRCSVLayer::PreCreateField(
     367         399 :         m_poEditableFeatureDefn, m_oSetFields, poNewField, bApproxOK);
     368         399 :     if (eAction == CREATE_FIELD_DO_NOTHING)
     369           0 :         return OGRERR_NONE;
     370         399 :     if (eAction == CREATE_FIELD_ERROR)
     371           1 :         return OGRERR_FAILURE;
     372         398 :     OGRErr eErr = OGREditableLayer::CreateField(poNewField, bApproxOK);
     373         398 :     if (eErr == OGRERR_NONE)
     374             :     {
     375         398 :         m_oSetFields.insert(CPLString(poNewField->GetNameRef()).toupper());
     376             :     }
     377         398 :     return eErr;
     378             : }
     379             : 
     380           3 : OGRErr OGRCSVEditableLayer::DeleteField(int iField)
     381             : {
     382           3 :     m_oSetFields.clear();
     383           3 :     return OGREditableLayer::DeleteField(iField);
     384             : }
     385             : 
     386           2 : OGRErr OGRCSVEditableLayer::AlterFieldDefn(int iField,
     387             :                                            OGRFieldDefn *poNewFieldDefn,
     388             :                                            int nFlagsIn)
     389             : {
     390           2 :     m_oSetFields.clear();
     391           2 :     return OGREditableLayer::AlterFieldDefn(iField, poNewFieldDefn, nFlagsIn);
     392             : }
     393             : 
     394             : /************************************************************************/
     395             : /*                        GetFeatureCount()                             */
     396             : /************************************************************************/
     397             : 
     398          28 : GIntBig OGRCSVEditableLayer::GetFeatureCount(int bForce)
     399             : {
     400          28 :     const GIntBig nRet = OGREditableLayer::GetFeatureCount(bForce);
     401          28 :     if (m_poDecoratedLayer != nullptr && m_nNextFID <= 0)
     402             :     {
     403             :         const GIntBig nTotalFeatureCount =
     404           8 :             static_cast<OGRCSVLayer *>(m_poDecoratedLayer)
     405           4 :                 ->GetTotalFeatureCount();
     406           4 :         if (nTotalFeatureCount >= 0)
     407           3 :             SetNextFID(nTotalFeatureCount + 1);
     408             :     }
     409          28 :     return nRet;
     410             : }
     411             : 
     412             : /************************************************************************/
     413             : /*                          OGRCSVDataSource()                          */
     414             : /************************************************************************/
     415             : 
     416             : OGRCSVDataSource::OGRCSVDataSource() = default;
     417             : 
     418             : /************************************************************************/
     419             : /*                         ~OGRCSVDataSource()                          */
     420             : /************************************************************************/
     421             : 
     422        2392 : OGRCSVDataSource::~OGRCSVDataSource()
     423             : 
     424             : {
     425        1196 :     m_apoLayers.clear();
     426             : 
     427        1196 :     if (bUpdate)
     428         249 :         OGRCSVDriverRemoveFromMap(pszName, this);
     429             : 
     430        1196 :     CPLFree(pszName);
     431        2392 : }
     432             : 
     433             : /************************************************************************/
     434             : /*                           TestCapability()                           */
     435             : /************************************************************************/
     436             : 
     437         200 : int OGRCSVDataSource::TestCapability(const char *pszCap)
     438             : 
     439             : {
     440         200 :     if (EQUAL(pszCap, ODsCCreateLayer))
     441          65 :         return bUpdate;
     442         135 :     else if (EQUAL(pszCap, ODsCDeleteLayer))
     443          19 :         return bUpdate;
     444         116 :     else if (EQUAL(pszCap, ODsCCreateGeomFieldAfterCreateLayer))
     445          30 :         return bUpdate && bEnableGeometryFields;
     446          86 :     else if (EQUAL(pszCap, ODsCCurveGeometries))
     447           3 :         return TRUE;
     448          83 :     else if (EQUAL(pszCap, ODsCMeasuredGeometries))
     449           3 :         return TRUE;
     450          80 :     else if (EQUAL(pszCap, ODsCZGeometries))
     451           2 :         return TRUE;
     452          78 :     else if (EQUAL(pszCap, ODsCRandomLayerWrite))
     453           0 :         return bUpdate;
     454             :     else
     455          78 :         return FALSE;
     456             : }
     457             : 
     458             : /************************************************************************/
     459             : /*                              GetLayer()                              */
     460             : /************************************************************************/
     461             : 
     462         873 : OGRLayer *OGRCSVDataSource::GetLayer(int iLayer)
     463             : 
     464             : {
     465         873 :     if (iLayer < 0 || iLayer >= static_cast<int>(m_apoLayers.size()))
     466           2 :         return nullptr;
     467             : 
     468         871 :     return m_apoLayers[iLayer]->GetLayer();
     469             : }
     470             : 
     471             : /************************************************************************/
     472             : /*                          GetRealExtension()                          */
     473             : /************************************************************************/
     474             : 
     475        7091 : CPLString OGRCSVDataSource::GetRealExtension(CPLString osFilename)
     476             : {
     477       14182 :     const CPLString osExt = CPLGetExtension(osFilename);
     478        7091 :     if (STARTS_WITH(osFilename, "/vsigzip/") && EQUAL(osExt, "gz"))
     479             :     {
     480          14 :         if (osFilename.size() > 7 &&
     481           7 :             EQUAL(osFilename + osFilename.size() - 7, ".csv.gz"))
     482           0 :             return "csv";
     483          14 :         else if (osFilename.size() > 7 &&
     484           7 :                  EQUAL(osFilename + osFilename.size() - 7, ".tsv.gz"))
     485           0 :             return "tsv";
     486          14 :         else if (osFilename.size() > 7 &&
     487           7 :                  EQUAL(osFilename + osFilename.size() - 7, ".psv.gz"))
     488           0 :             return "psv";
     489             :     }
     490        7091 :     return osExt;
     491             : }
     492             : 
     493             : /************************************************************************/
     494             : /*                                Open()                                */
     495             : /************************************************************************/
     496             : 
     497        1137 : bool OGRCSVDataSource::Open(const char *pszFilename, bool bUpdateIn,
     498             :                             bool bForceOpen, CSLConstList papszOpenOptionsIn,
     499             :                             bool bSingleDriver)
     500             : 
     501             : {
     502        1137 :     pszName = CPLStrdup(pszFilename);
     503        1137 :     bUpdate = CPL_TO_BOOL(bUpdateIn);
     504             : 
     505        1137 :     if (bUpdate && bForceOpen && EQUAL(pszFilename, "/vsistdout/"))
     506           1 :         return TRUE;
     507             : 
     508             :     // For writable /vsizip/, do nothing more.
     509        1136 :     if (bUpdate && bForceOpen && STARTS_WITH(pszFilename, "/vsizip/"))
     510           0 :         return TRUE;
     511             : 
     512        2272 :     CPLString osFilename(pszFilename);
     513        2272 :     const CPLString osBaseFilename = CPLGetFilename(pszFilename);
     514        2272 :     const CPLString osExt = GetRealExtension(osFilename);
     515             : 
     516        1136 :     bool bIgnoreExtension = bSingleDriver;
     517        1136 :     bool bUSGeonamesFile = false;
     518        1136 :     if (STARTS_WITH_CI(osFilename, "CSV:"))
     519             :     {
     520           0 :         bIgnoreExtension = true;
     521           0 :         osFilename = osFilename.substr(strlen("CSV:"));
     522             :     }
     523             : 
     524             :     // Those are *not* real .XLS files, but text file with tab as column
     525             :     // separator.
     526        1136 :     if (EQUAL(osBaseFilename, "NfdcFacilities.xls") ||
     527        1136 :         EQUAL(osBaseFilename, "NfdcRunways.xls") ||
     528        3408 :         EQUAL(osBaseFilename, "NfdcRemarks.xls") ||
     529        1136 :         EQUAL(osBaseFilename, "NfdcSchedules.xls"))
     530             :     {
     531           0 :         if (bUpdate)
     532           0 :             return FALSE;
     533           0 :         bIgnoreExtension = true;
     534             :     }
     535        1136 :     else if ((STARTS_WITH_CI(osBaseFilename, "NationalFile_") ||
     536        1136 :               STARTS_WITH_CI(osBaseFilename, "POP_PLACES_") ||
     537        1136 :               STARTS_WITH_CI(osBaseFilename, "HIST_FEATURES_") ||
     538        1136 :               STARTS_WITH_CI(osBaseFilename, "US_CONCISE_") ||
     539        1136 :               STARTS_WITH_CI(osBaseFilename, "AllNames_") ||
     540        1136 :               STARTS_WITH_CI(osBaseFilename, "Feature_Description_History_") ||
     541        1136 :               STARTS_WITH_CI(osBaseFilename, "ANTARCTICA_") ||
     542        1136 :               STARTS_WITH_CI(osBaseFilename, "GOVT_UNITS_") ||
     543        1136 :               STARTS_WITH_CI(osBaseFilename, "NationalFedCodes_") ||
     544        1136 :               STARTS_WITH_CI(osBaseFilename, "AllStates_") ||
     545        2272 :               STARTS_WITH_CI(osBaseFilename, "AllStatesFedCodes_") ||
     546        1136 :               (osBaseFilename.size() > 2 &&
     547        2256 :                STARTS_WITH_CI(osBaseFilename + 2, "_Features_")) ||
     548        1136 :               (osBaseFilename.size() > 2 &&
     549        2272 :                STARTS_WITH_CI(osBaseFilename + 2, "_FedCodes_"))) &&
     550           0 :              (EQUAL(osExt, "txt") || EQUAL(osExt, "zip")))
     551             :     {
     552           0 :         if (bUpdate)
     553           0 :             return FALSE;
     554           0 :         bIgnoreExtension = true;
     555           0 :         bUSGeonamesFile = true;
     556             : 
     557           0 :         if (EQUAL(osExt, "zip") && strstr(osFilename, "/vsizip/") == nullptr)
     558             :         {
     559           0 :             osFilename = "/vsizip/" + osFilename;
     560             :         }
     561             :     }
     562        2271 :     else if (EQUAL(osBaseFilename, "allCountries.txt") ||
     563        1135 :              EQUAL(osBaseFilename, "allCountries.zip"))
     564             :     {
     565           1 :         if (bUpdate)
     566           0 :             return FALSE;
     567           1 :         bIgnoreExtension = true;
     568             : 
     569           1 :         if (EQUAL(osExt, "zip") && strstr(osFilename, "/vsizip/") == nullptr)
     570             :         {
     571           0 :             osFilename = "/vsizip/" + osFilename;
     572             :         }
     573             :     }
     574             : 
     575             :     // Determine what sort of object this is.
     576             :     VSIStatBufL sStatBuf;
     577             : 
     578        1136 :     if (VSIStatExL(osFilename, &sStatBuf, VSI_STAT_NATURE_FLAG) != 0)
     579           0 :         return FALSE;
     580             : 
     581             :     // Is this a single CSV file?
     582        1501 :     if (VSI_ISREG(sStatBuf.st_mode) &&
     583         365 :         (bIgnoreExtension || EQUAL(osExt, "csv") || EQUAL(osExt, "tsv") ||
     584           2 :          EQUAL(osExt, "psv")))
     585             :     {
     586         450 :         if (EQUAL(CPLGetFilename(osFilename), "NfdcFacilities.xls"))
     587             :         {
     588           0 :             return OpenTable(osFilename, papszOpenOptionsIn, "ARP");
     589             :         }
     590         450 :         else if (EQUAL(CPLGetFilename(osFilename), "NfdcRunways.xls"))
     591             :         {
     592           0 :             OpenTable(osFilename, papszOpenOptionsIn, "BaseEndPhysical");
     593           0 :             OpenTable(osFilename, papszOpenOptionsIn, "BaseEndDisplaced");
     594           0 :             OpenTable(osFilename, papszOpenOptionsIn, "ReciprocalEndPhysical");
     595           0 :             OpenTable(osFilename, papszOpenOptionsIn, "ReciprocalEndDisplaced");
     596           0 :             return !m_apoLayers.empty();
     597             :         }
     598         450 :         else if (bUSGeonamesFile)
     599             :         {
     600             :             // GNIS specific.
     601           0 :             if (STARTS_WITH_CI(osBaseFilename, "NationalFedCodes_") ||
     602           0 :                 STARTS_WITH_CI(osBaseFilename, "AllStatesFedCodes_") ||
     603           0 :                 STARTS_WITH_CI(osBaseFilename, "ANTARCTICA_") ||
     604           0 :                 (osBaseFilename.size() > 2 &&
     605           0 :                  STARTS_WITH_CI(osBaseFilename + 2, "_FedCodes_")))
     606             :             {
     607           0 :                 OpenTable(osFilename, papszOpenOptionsIn, nullptr, "PRIMARY");
     608             :             }
     609           0 :             else if (STARTS_WITH_CI(osBaseFilename, "GOVT_UNITS_") ||
     610           0 :                      STARTS_WITH_CI(osBaseFilename,
     611             :                                     "Feature_Description_History_"))
     612             :             {
     613           0 :                 OpenTable(osFilename, papszOpenOptionsIn, nullptr, "");
     614             :             }
     615             :             else
     616             :             {
     617           0 :                 OpenTable(osFilename, papszOpenOptionsIn, nullptr, "PRIM");
     618           0 :                 OpenTable(osFilename, papszOpenOptionsIn, nullptr, "SOURCE");
     619             :             }
     620           0 :             return !m_apoLayers.empty();
     621             :         }
     622             : 
     623         450 :         return OpenTable(osFilename, papszOpenOptionsIn);
     624             :     }
     625             : 
     626             :     // Is this a single a ZIP file with only a CSV file inside?
     627         690 :     if (STARTS_WITH(osFilename, "/vsizip/") && EQUAL(osExt, "zip") &&
     628           4 :         VSI_ISREG(sStatBuf.st_mode))
     629             :     {
     630           1 :         char **papszFiles = VSIReadDir(osFilename);
     631           2 :         if (CSLCount(papszFiles) != 1 ||
     632           1 :             !EQUAL(CPLGetExtension(papszFiles[0]), "CSV"))
     633             :         {
     634           1 :             CSLDestroy(papszFiles);
     635           1 :             return FALSE;
     636             :         }
     637           0 :         osFilename = CPLFormFilename(osFilename, papszFiles[0], nullptr);
     638           0 :         CSLDestroy(papszFiles);
     639           0 :         return OpenTable(osFilename, papszOpenOptionsIn);
     640             :     }
     641             : 
     642             :     // Otherwise it has to be a directory.
     643         685 :     if (!VSI_ISDIR(sStatBuf.st_mode))
     644           0 :         return FALSE;
     645             : 
     646             :     // Scan through for entries ending in .csv.
     647         685 :     int nNotCSVCount = 0;
     648         685 :     char **papszNames = VSIReadDir(osFilename);
     649             : 
     650       27824 :     for (int i = 0; papszNames != nullptr && papszNames[i] != nullptr; i++)
     651             :     {
     652             :         const CPLString oSubFilename =
     653       27139 :             CPLFormFilename(osFilename, papszNames[i], nullptr);
     654             : 
     655       27139 :         if (EQUAL(papszNames[i], ".") || EQUAL(papszNames[i], ".."))
     656         658 :             continue;
     657             : 
     658       26481 :         if (EQUAL(CPLGetExtension(oSubFilename), "csvt"))
     659          79 :             continue;
     660             : 
     661       52804 :         if (VSIStatL(oSubFilename, &sStatBuf) != 0 ||
     662       26402 :             !VSI_ISREG(sStatBuf.st_mode))
     663             :         {
     664         110 :             nNotCSVCount++;
     665         110 :             continue;
     666             :         }
     667             : 
     668       26292 :         if (EQUAL(CPLGetExtension(oSubFilename), "csv"))
     669             :         {
     670         139 :             if (!OpenTable(oSubFilename, papszOpenOptionsIn))
     671             :             {
     672           0 :                 CPLDebug("CSV", "Cannot open %s", oSubFilename.c_str());
     673           0 :                 nNotCSVCount++;
     674           0 :                 continue;
     675             :             }
     676             :         }
     677             :         // GNIS specific.
     678       78459 :         else if (strlen(papszNames[i]) > 2 &&
     679       26153 :                  STARTS_WITH_CI(papszNames[i] + 2, "_Features_") &&
     680           0 :                  EQUAL(CPLGetExtension(papszNames[i]), "txt"))
     681             :         {
     682             :             bool bRet =
     683           0 :                 OpenTable(oSubFilename, papszOpenOptionsIn, nullptr, "PRIM");
     684           0 :             bRet |=
     685           0 :                 OpenTable(oSubFilename, papszOpenOptionsIn, nullptr, "SOURCE");
     686           0 :             if (!bRet)
     687             :             {
     688           0 :                 CPLDebug("CSV", "Cannot open %s", oSubFilename.c_str());
     689           0 :                 nNotCSVCount++;
     690           0 :                 continue;
     691             :             }
     692             :         }
     693             :         // GNIS specific.
     694      104612 :         else if (strlen(papszNames[i]) > 2 &&
     695       26153 :                  STARTS_WITH_CI(papszNames[i] + 2, "_FedCodes_") &&
     696           0 :                  EQUAL(CPLGetExtension(papszNames[i]), "txt"))
     697             :         {
     698           0 :             if (!OpenTable(oSubFilename, papszOpenOptionsIn, nullptr,
     699             :                            "PRIMARY"))
     700             :             {
     701           0 :                 CPLDebug("CSV", "Cannot open %s", oSubFilename.c_str());
     702           0 :                 nNotCSVCount++;
     703           0 :                 continue;
     704             :             }
     705             :         }
     706             :         else
     707             :         {
     708       26153 :             nNotCSVCount++;
     709       26153 :             continue;
     710             :         }
     711             :     }
     712             : 
     713         685 :     CSLDestroy(papszNames);
     714             : 
     715             :     // We presume that this is indeed intended to be a CSV
     716             :     // datasource if over half the files were .csv files.
     717         685 :     return bForceOpen || nNotCSVCount < GetLayerCount();
     718             : }
     719             : 
     720             : /************************************************************************/
     721             : /*                              OpenTable()                             */
     722             : /************************************************************************/
     723             : 
     724         589 : bool OGRCSVDataSource::OpenTable(const char *pszFilename,
     725             :                                  CSLConstList papszOpenOptionsIn,
     726             :                                  const char *pszNfdcRunwaysGeomField,
     727             :                                  const char *pszGeonamesGeomFieldPrefix)
     728             : 
     729             : {
     730             :     // Open the file.
     731         589 :     VSILFILE *fp = nullptr;
     732             : 
     733         589 :     if (bUpdate)
     734          88 :         fp = VSIFOpenExL(pszFilename, "rb+", true);
     735             :     else
     736         501 :         fp = VSIFOpenExL(pszFilename, "rb", true);
     737         589 :     if (fp == nullptr)
     738             :     {
     739           0 :         CPLError(CE_Warning, CPLE_OpenFailed, "Failed to open %s.",
     740             :                  VSIGetLastErrorMsg());
     741           0 :         return false;
     742             :     }
     743             : 
     744         589 :     if (!bUpdate && strstr(pszFilename, "/vsigzip/") == nullptr &&
     745         501 :         strstr(pszFilename, "/vsizip/") == nullptr)
     746         417 :         fp = VSICreateBufferedReaderHandle(fp);
     747             : 
     748        1178 :     CPLString osLayerName = CPLGetBasename(pszFilename);
     749        1178 :     CPLString osExt = CPLGetExtension(pszFilename);
     750         589 :     if (STARTS_WITH(pszFilename, "/vsigzip/") && EQUAL(osExt, "gz"))
     751             :     {
     752           0 :         if (strlen(pszFilename) > 7 &&
     753           0 :             EQUAL(pszFilename + strlen(pszFilename) - 7, ".csv.gz"))
     754             :         {
     755           0 :             osLayerName = osLayerName.substr(0, osLayerName.size() - 4);
     756           0 :             osExt = "csv";
     757             :         }
     758           0 :         else if (strlen(pszFilename) > 7 &&
     759           0 :                  EQUAL(pszFilename + strlen(pszFilename) - 7, ".tsv.gz"))
     760             :         {
     761           0 :             osLayerName = osLayerName.substr(0, osLayerName.size() - 4);
     762           0 :             osExt = "tsv";
     763             :         }
     764           0 :         else if (strlen(pszFilename) > 7 &&
     765           0 :                  EQUAL(pszFilename + strlen(pszFilename) - 7, ".psv.gz"))
     766             :         {
     767           0 :             osLayerName = osLayerName.substr(0, osLayerName.size() - 4);
     768           0 :             osExt = "psv";
     769             :         }
     770             :     }
     771             : 
     772         589 :     int nMaxLineSize = atoi(CPLGetConfigOption(
     773             :         "OGR_CSV_MAX_LINE_SIZE",
     774             :         CSLFetchNameValueDef(papszOpenOptionsIn, "MAX_LINE_SIZE",
     775         589 :                              CPLSPrintf("%d", OGR_CSV_DEFAULT_MAX_LINE_SIZE))));
     776         589 :     size_t nMaxLineSizeAsSize_t = static_cast<size_t>(nMaxLineSize);
     777         589 :     if (nMaxLineSize == 0)
     778             :     {
     779           0 :         nMaxLineSize = -1;
     780           0 :         nMaxLineSizeAsSize_t = static_cast<size_t>(-1);
     781             :     }
     782             : 
     783             :     // Read and parse a line to detect separator.
     784             : 
     785        1178 :     std::string osLine;
     786             :     {
     787         589 :         const char *pszLine = CPLReadLine2L(fp, nMaxLineSize, nullptr);
     788         589 :         if (pszLine == nullptr)
     789             :         {
     790           0 :             VSIFCloseL(fp);
     791           0 :             return false;
     792             :         }
     793         589 :         osLine = pszLine;
     794             :     }
     795             : 
     796         589 :     char chDelimiter = ',';
     797             :     const char *pszDelimiter =
     798         589 :         CSLFetchNameValueDef(papszOpenOptionsIn, "SEPARATOR", "AUTO");
     799         589 :     if (EQUAL(pszDelimiter, "AUTO"))
     800             :     {
     801         581 :         chDelimiter = CSVDetectSeperator(osLine.c_str());
     802         581 :         if (chDelimiter != '\t' && osLine.find('\t') != std::string::npos)
     803             :         {
     804             :             // Force the delimiter to be TAB for a .tsv file that has a tabulation
     805             :             // in its first line */
     806           0 :             if (EQUAL(osExt, "tsv"))
     807             :             {
     808           0 :                 chDelimiter = '\t';
     809             :             }
     810             :             else
     811             :             {
     812           0 :                 for (int nDontHonourStrings = 0; nDontHonourStrings <= 1;
     813             :                      nDontHonourStrings++)
     814             :                 {
     815             :                     const bool bHonourStrings =
     816           0 :                         !CPL_TO_BOOL(nDontHonourStrings);
     817             :                     // Read the first 2 lines to see if they have the same number
     818             :                     // of fields, if using tabulation.
     819           0 :                     VSIRewindL(fp);
     820           0 :                     char **papszTokens = CSVReadParseLine3L(
     821             :                         fp, nMaxLineSizeAsSize_t, "\t", bHonourStrings,
     822             :                         false,  // bKeepLeadingAndClosingQuotes
     823             :                         false,  // bMergeDelimiter
     824             :                         true    // bSkipBOM
     825             :                     );
     826           0 :                     const int nTokens1 = CSLCount(papszTokens);
     827           0 :                     CSLDestroy(papszTokens);
     828           0 :                     papszTokens = CSVReadParseLine3L(
     829             :                         fp, nMaxLineSizeAsSize_t, "\t", bHonourStrings,
     830             :                         false,  // bKeepLeadingAndClosingQuotes
     831             :                         false,  // bMergeDelimiter
     832             :                         true    // bSkipBOM
     833             :                     );
     834           0 :                     const int nTokens2 = CSLCount(papszTokens);
     835           0 :                     CSLDestroy(papszTokens);
     836           0 :                     if (nTokens1 >= 2 && nTokens1 == nTokens2)
     837             :                     {
     838           0 :                         chDelimiter = '\t';
     839           0 :                         break;
     840             :                     }
     841             :                 }
     842             :             }
     843             :         }
     844             : 
     845             :         // GNIS specific.
     846         581 :         if (pszGeonamesGeomFieldPrefix != nullptr &&
     847           0 :             osLine.find('|') != std::string::npos)
     848           0 :             chDelimiter = '|';
     849             :     }
     850           8 :     else if (EQUAL(pszDelimiter, "COMMA"))
     851           4 :         chDelimiter = ',';
     852           4 :     else if (EQUAL(pszDelimiter, "SEMICOLON"))
     853           1 :         chDelimiter = ';';
     854           3 :     else if (EQUAL(pszDelimiter, "TAB"))
     855           1 :         chDelimiter = '\t';
     856           2 :     else if (EQUAL(pszDelimiter, "SPACE"))
     857           1 :         chDelimiter = ' ';
     858           1 :     else if (EQUAL(pszDelimiter, "PIPE"))
     859           1 :         chDelimiter = '|';
     860             :     else
     861             :     {
     862           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     863             :                  "SEPARATOR=%s not understood, use one of COMMA, "
     864             :                  "SEMICOLON, TAB, SPACE or PIPE",
     865             :                  pszDelimiter);
     866             :     }
     867             : 
     868         589 :     VSIRewindL(fp);
     869             : 
     870             :     // Create a layer.
     871         589 :     if (pszNfdcRunwaysGeomField != nullptr)
     872             :     {
     873           0 :         osLayerName += "_";
     874           0 :         osLayerName += pszNfdcRunwaysGeomField;
     875             :     }
     876         589 :     else if (pszGeonamesGeomFieldPrefix != nullptr &&
     877           0 :              !EQUAL(pszGeonamesGeomFieldPrefix, ""))
     878             :     {
     879           0 :         osLayerName += "_";
     880           0 :         osLayerName += pszGeonamesGeomFieldPrefix;
     881             :     }
     882         589 :     if (EQUAL(pszFilename, "/vsistdin/"))
     883           0 :         osLayerName = "layer";
     884             : 
     885             :     auto poCSVLayer =
     886             :         std::make_unique<OGRCSVLayer>(this, osLayerName, fp, nMaxLineSize,
     887         589 :                                       pszFilename, FALSE, bUpdate, chDelimiter);
     888         589 :     poCSVLayer->BuildFeatureDefn(pszNfdcRunwaysGeomField,
     889             :                                  pszGeonamesGeomFieldPrefix,
     890             :                                  papszOpenOptionsIn);
     891         589 :     if (bUpdate)
     892             :     {
     893          88 :         m_apoLayers.emplace_back(std::make_unique<OGRCSVEditableLayer>(
     894         176 :             poCSVLayer.release(), papszOpenOptionsIn));
     895             :     }
     896             :     else
     897             :     {
     898         501 :         m_apoLayers.emplace_back(std::move(poCSVLayer));
     899             :     }
     900             : 
     901         589 :     return true;
     902             : }
     903             : 
     904             : /************************************************************************/
     905             : /*                           ICreateLayer()                             */
     906             : /************************************************************************/
     907             : 
     908             : OGRLayer *
     909         125 : OGRCSVDataSource::ICreateLayer(const char *pszLayerName,
     910             :                                const OGRGeomFieldDefn *poSrcGeomFieldDefn,
     911             :                                CSLConstList papszOptions)
     912             : {
     913             :     // Verify we are in update mode.
     914         125 :     if (!bUpdate)
     915             :     {
     916           1 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
     917             :                  "Data source %s opened read-only.\n"
     918             :                  "New layer %s cannot be created.",
     919             :                  pszName, pszLayerName);
     920             : 
     921           1 :         return nullptr;
     922             :     }
     923             : 
     924             :     const auto eGType =
     925         124 :         poSrcGeomFieldDefn ? poSrcGeomFieldDefn->GetType() : wkbNone;
     926             :     const auto poSpatialRef =
     927         124 :         poSrcGeomFieldDefn ? poSrcGeomFieldDefn->GetSpatialRef() : nullptr;
     928             : 
     929             :     // Verify that the datasource is a directory.
     930             :     VSIStatBufL sStatBuf;
     931             : 
     932         124 :     if (STARTS_WITH(pszName, "/vsizip/"))
     933             :     {
     934             :         // Do nothing.
     935             :     }
     936         247 :     else if (!EQUAL(pszName, "/vsistdout/") &&
     937         123 :              (VSIStatL(pszName, &sStatBuf) != 0 ||
     938         123 :               !VSI_ISDIR(sStatBuf.st_mode)))
     939             :     {
     940           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     941             :                  "Attempt to create csv layer (file) against a "
     942             :                  "non-directory datasource.");
     943           0 :         return nullptr;
     944             :     }
     945             : 
     946             :     const bool bCreateCSVT =
     947         124 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "CREATE_CSVT", "NO"));
     948             : 
     949             :     // What filename would we use?
     950         248 :     CPLString osFilename;
     951             : 
     952         124 :     if (strcmp(pszName, "/vsistdout/") == 0)
     953             :     {
     954           1 :         osFilename = pszName;
     955           1 :         if (bCreateCSVT)
     956             :         {
     957           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     958             :                      "CREATE_CSVT is not compatible with /vsistdout/ output");
     959           0 :             return nullptr;
     960             :         }
     961             :     }
     962         123 :     else if (osDefaultCSVName != "")
     963             :     {
     964          59 :         osFilename = CPLFormFilename(pszName, osDefaultCSVName, nullptr);
     965          59 :         osDefaultCSVName = "";
     966             :     }
     967             :     else
     968             :     {
     969          64 :         osFilename = CPLFormFilename(pszName, pszLayerName, "csv");
     970             :     }
     971             : 
     972             :     // Does this directory/file already exist?
     973         124 :     if (VSIStatL(osFilename, &sStatBuf) == 0)
     974             :     {
     975           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     976             :                  "Attempt to create layer %s, but %s already exists.",
     977             :                  pszLayerName, osFilename.c_str());
     978           1 :         return nullptr;
     979             :     }
     980             : 
     981             :     // Create the empty file.
     982             : 
     983         123 :     const char *pszDelimiter = CSLFetchNameValue(papszOptions, "SEPARATOR");
     984         123 :     char chDelimiter = ',';
     985         123 :     if (pszDelimiter != nullptr)
     986             :     {
     987           2 :         if (EQUAL(pszDelimiter, "COMMA"))
     988           0 :             chDelimiter = ',';
     989           2 :         else if (EQUAL(pszDelimiter, "SEMICOLON"))
     990           1 :             chDelimiter = ';';
     991           1 :         else if (EQUAL(pszDelimiter, "TAB"))
     992           0 :             chDelimiter = '\t';
     993           1 :         else if (EQUAL(pszDelimiter, "SPACE"))
     994           1 :             chDelimiter = ' ';
     995             :         else
     996             :         {
     997           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     998             :                      "SEPARATOR=%s not understood, use one of "
     999             :                      "COMMA, SEMICOLON, SPACE or TAB.",
    1000             :                      pszDelimiter);
    1001             :         }
    1002             :     }
    1003             : 
    1004             :     // Create a layer.
    1005             : 
    1006             :     auto poCSVLayer = std::make_unique<OGRCSVLayer>(
    1007         246 :         this, pszLayerName, nullptr, -1, osFilename, true, true, chDelimiter);
    1008             : 
    1009         123 :     poCSVLayer->BuildFeatureDefn();
    1010             : 
    1011             :     // Was a particular CRLF order requested?
    1012         123 :     const char *pszCRLFFormat = CSLFetchNameValue(papszOptions, "LINEFORMAT");
    1013         123 :     bool bUseCRLF = false;
    1014             : 
    1015         123 :     if (pszCRLFFormat == nullptr)
    1016             :     {
    1017             : #ifdef _WIN32
    1018             :         bUseCRLF = true;
    1019             : #endif
    1020             :     }
    1021          18 :     else if (EQUAL(pszCRLFFormat, "CRLF"))
    1022             :     {
    1023           1 :         bUseCRLF = true;
    1024             :     }
    1025          17 :     else if (!EQUAL(pszCRLFFormat, "LF"))
    1026             :     {
    1027           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1028             :                  "LINEFORMAT=%s not understood, use one of CRLF or LF.",
    1029             :                  pszCRLFFormat);
    1030             : #ifdef _WIN32
    1031             :         bUseCRLF = true;
    1032             : #endif
    1033             :     }
    1034             : 
    1035         123 :     poCSVLayer->SetCRLF(bUseCRLF);
    1036             : 
    1037             :     const char *pszStringQuoting =
    1038         123 :         CSLFetchNameValueDef(papszOptions, "STRING_QUOTING", "IF_AMBIGUOUS");
    1039         246 :     poCSVLayer->SetStringQuoting(
    1040         123 :         EQUAL(pszStringQuoting, "IF_NEEDED")
    1041             :             ? OGRCSVLayer::StringQuoting::IF_NEEDED
    1042         110 :         : EQUAL(pszStringQuoting, "ALWAYS")
    1043         110 :             ? OGRCSVLayer::StringQuoting::ALWAYS
    1044             :             : OGRCSVLayer::StringQuoting::IF_AMBIGUOUS);
    1045             : 
    1046             :     // Should we write the geometry?
    1047         123 :     const char *pszGeometry = CSLFetchNameValue(papszOptions, "GEOMETRY");
    1048         123 :     if (bEnableGeometryFields)
    1049             :     {
    1050          11 :         poCSVLayer->SetWriteGeometry(
    1051             :             eGType, OGR_CSV_GEOM_AS_WKT,
    1052             :             CSLFetchNameValueDef(papszOptions, "GEOMETRY_NAME", "WKT"));
    1053             :     }
    1054         112 :     else if (pszGeometry != nullptr)
    1055             :     {
    1056          35 :         if (EQUAL(pszGeometry, "AS_WKT"))
    1057             :         {
    1058          28 :             poCSVLayer->SetWriteGeometry(
    1059             :                 eGType, OGR_CSV_GEOM_AS_WKT,
    1060             :                 CSLFetchNameValueDef(papszOptions, "GEOMETRY_NAME", "WKT"));
    1061             :         }
    1062           7 :         else if (EQUAL(pszGeometry, "AS_XYZ") || EQUAL(pszGeometry, "AS_XY") ||
    1063           2 :                  EQUAL(pszGeometry, "AS_YX"))
    1064             :         {
    1065           6 :             if (eGType == wkbUnknown || wkbFlatten(eGType) == wkbPoint)
    1066             :             {
    1067          10 :                 poCSVLayer->SetWriteGeometry(
    1068           5 :                     eGType, EQUAL(pszGeometry, "AS_XYZ")  ? OGR_CSV_GEOM_AS_XYZ
    1069           3 :                             : EQUAL(pszGeometry, "AS_XY") ? OGR_CSV_GEOM_AS_XY
    1070             :                                                           : OGR_CSV_GEOM_AS_YX);
    1071             :             }
    1072             :             else
    1073             :             {
    1074           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1075             :                          "Geometry type %s is not compatible with "
    1076             :                          "GEOMETRY=%s.",
    1077             :                          OGRGeometryTypeToName(eGType), pszGeometry);
    1078           1 :                 return nullptr;
    1079             :             }
    1080             :         }
    1081             :         else
    1082             :         {
    1083           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    1084             :                      "Unsupported value %s for creation option GEOMETRY",
    1085             :                      pszGeometry);
    1086           1 :             return nullptr;
    1087             :         }
    1088             :     }
    1089             : 
    1090             :     // Should we create a CSVT file?
    1091         121 :     if (bCreateCSVT)
    1092             :     {
    1093          28 :         poCSVLayer->SetCreateCSVT(true);
    1094             : 
    1095             :         // Create .prj file.
    1096          28 :         if (poSpatialRef != nullptr)
    1097             :         {
    1098           7 :             char *pszWKT = nullptr;
    1099           7 :             poSpatialRef->exportToWkt(&pszWKT);
    1100           7 :             if (pszWKT)
    1101             :             {
    1102             :                 VSILFILE *fpPRJ =
    1103           7 :                     VSIFOpenL(CPLResetExtension(osFilename, "prj"), "wb");
    1104           7 :                 if (fpPRJ)
    1105             :                 {
    1106           7 :                     CPL_IGNORE_RET_VAL(VSIFPrintfL(fpPRJ, "%s\n", pszWKT));
    1107           7 :                     VSIFCloseL(fpPRJ);
    1108             :                 }
    1109           7 :                 CPLFree(pszWKT);
    1110             :             }
    1111             :         }
    1112             :     }
    1113             : 
    1114             :     // Should we write a UTF8 BOM?
    1115         121 :     const char *pszWriteBOM = CSLFetchNameValue(papszOptions, "WRITE_BOM");
    1116         121 :     if (pszWriteBOM)
    1117           2 :         poCSVLayer->SetWriteBOM(CPLTestBool(pszWriteBOM));
    1118             : 
    1119         121 :     if (poCSVLayer->GetLayerDefn()->GetGeomFieldCount() > 0 &&
    1120             :         poSrcGeomFieldDefn)
    1121             :     {
    1122          39 :         auto poGeomFieldDefn = poCSVLayer->GetLayerDefn()->GetGeomFieldDefn(0);
    1123          39 :         poGeomFieldDefn->SetCoordinatePrecision(
    1124             :             poSrcGeomFieldDefn->GetCoordinatePrecision());
    1125             :     }
    1126             : 
    1127         121 :     if (osFilename != "/vsistdout/")
    1128         120 :         m_apoLayers.emplace_back(std::make_unique<OGRCSVEditableLayer>(
    1129         240 :             poCSVLayer.release(), nullptr));
    1130             :     else
    1131           1 :         m_apoLayers.emplace_back(std::move(poCSVLayer));
    1132             : 
    1133         121 :     return m_apoLayers.back()->GetLayer();
    1134             : }
    1135             : 
    1136             : /************************************************************************/
    1137             : /*                            DeleteLayer()                             */
    1138             : /************************************************************************/
    1139             : 
    1140          20 : OGRErr OGRCSVDataSource::DeleteLayer(int iLayer)
    1141             : 
    1142             : {
    1143             :     // Verify we are in update mode.
    1144          20 :     if (!bUpdate)
    1145             :     {
    1146           1 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
    1147             :                  "Data source %s opened read-only.\n"
    1148             :                  "Layer %d cannot be deleted.",
    1149             :                  pszName, iLayer);
    1150             : 
    1151           1 :         return OGRERR_FAILURE;
    1152             :     }
    1153             : 
    1154          19 :     if (iLayer < 0 || iLayer >= GetLayerCount())
    1155             :     {
    1156           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    1157             :                  "Layer %d not in legal range of 0 to %d.", iLayer,
    1158           2 :                  GetLayerCount() - 1);
    1159           2 :         return OGRERR_FAILURE;
    1160             :     }
    1161             : 
    1162          34 :     for (const auto &osFilename : m_apoLayers[iLayer]->GetFileList())
    1163             :     {
    1164          17 :         VSIUnlink(osFilename.c_str());
    1165             :     }
    1166             : 
    1167          17 :     m_apoLayers.erase(m_apoLayers.begin() + iLayer);
    1168             : 
    1169          17 :     return OGRERR_NONE;
    1170             : }
    1171             : 
    1172             : /************************************************************************/
    1173             : /*                       CreateForSingleFile()                          */
    1174             : /************************************************************************/
    1175             : 
    1176          59 : void OGRCSVDataSource::CreateForSingleFile(const char *pszDirname,
    1177             :                                            const char *pszFilename)
    1178             : {
    1179          59 :     pszName = CPLStrdup(pszDirname);
    1180          59 :     bUpdate = true;
    1181          59 :     osDefaultCSVName = CPLGetFilename(pszFilename);
    1182          59 : }
    1183             : 
    1184             : /************************************************************************/
    1185             : /*                            GetFileList()                             */
    1186             : /************************************************************************/
    1187             : 
    1188           7 : char **OGRCSVDataSource::GetFileList()
    1189             : {
    1190          14 :     CPLStringList oFileList;
    1191          19 :     for (auto &poLayer : m_apoLayers)
    1192             :     {
    1193          35 :         for (const auto &osFilename : poLayer->GetFileList())
    1194             :         {
    1195          23 :             oFileList.AddString(osFilename.c_str());
    1196             :         }
    1197             :     }
    1198          14 :     return oFileList.StealList();
    1199             : }

Generated by: LCOV version 1.14