LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/csv - ogrcsvlayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1212 1373 88.3 %
Date: 2026-04-15 22:10:00 Functions: 32 32 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  CSV Translator
       4             :  * Purpose:  Implements OGRCSVLayer class.
       5             :  * Author:   Frank Warmerdam <warmerdam@pobox.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2004, Frank Warmerdam <warmerdam@pobox.com>
       9             :  * Copyright (c) 2008-2014, 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 <climits>
      19             : #include <cstddef>
      20             : #include <cstdio>
      21             : #include <cstdlib>
      22             : #include <cstring>
      23             : #include <algorithm>
      24             : #include <cinttypes>
      25             : #include <limits>
      26             : #include <string>
      27             : #include <vector>
      28             : 
      29             : #include "cpl_conv.h"
      30             : #include "cpl_csv.h"
      31             : #include "cpl_error.h"
      32             : #include "cpl_string.h"
      33             : #include "cpl_vsi.h"
      34             : #include "cpl_vsi_virtual.h"
      35             : #include "ogr_api.h"
      36             : #include "ogr_core.h"
      37             : #include "ogr_feature.h"
      38             : #include "ogr_geometry.h"
      39             : #include "ogr_p.h"
      40             : #include "ogr_spatialref.h"
      41             : #include "ogrsf_frmts.h"
      42             : 
      43             : #define DIGIT_ZERO '0'
      44             : 
      45             : IOGRCSVLayer::~IOGRCSVLayer() = default;
      46             : 
      47             : /************************************************************************/
      48             : /*                            OGRCSVLayer()                             */
      49             : /*                                                                      */
      50             : /*      Note that the OGRCSVLayer assumes ownership of the passed       */
      51             : /*      file pointer.                                                   */
      52             : /************************************************************************/
      53             : 
      54         813 : OGRCSVLayer::OGRCSVLayer(GDALDataset *poDS, const char *pszLayerNameIn,
      55             :                          VSILFILE *fp, int nMaxLineSize,
      56             :                          const char *pszFilenameIn, int bNewIn,
      57         813 :                          int bInWriteModeIn, char chDelimiterIn)
      58             :     : m_poDS(poDS), m_poFeatureDefn(OGRFeatureDefnRefCountedPtr::makeInstance(
      59             :                         pszLayerNameIn)),
      60         813 :       fpCSV(fp), m_nMaxLineSize(nMaxLineSize), bNew(CPL_TO_BOOL(bNewIn)),
      61         813 :       bInWriteMode(CPL_TO_BOOL(bInWriteModeIn)),
      62         813 :       pszFilename(CPLStrdup(pszFilenameIn)), nTotalFeatures(bNewIn ? 0 : -1)
      63             : {
      64         813 :     szDelimiter[0] = chDelimiterIn;
      65         813 :     szDelimiter[1] = 0;
      66         813 :     SetDescription(m_poFeatureDefn->GetName());
      67         813 :     m_poFeatureDefn->SetGeomType(wkbNone);
      68         813 : }
      69             : 
      70             : /************************************************************************/
      71             : /*                              Matches()                               */
      72             : /************************************************************************/
      73             : 
      74       11088 : bool OGRCSVLayer::Matches(const char *pszFieldName, char **papszPossibleNames)
      75             : {
      76       11088 :     if (papszPossibleNames == nullptr)
      77           0 :         return false;
      78       11200 :     for (char **papszIter = papszPossibleNames; *papszIter; papszIter++)
      79             :     {
      80         154 :         const char *pszPattern = *papszIter;
      81         154 :         const char *pszStar = strstr(pszPattern, "*");
      82         154 :         if (pszStar == nullptr)
      83             :         {
      84         145 :             if (EQUAL(pszFieldName, pszPattern))
      85          39 :                 return true;
      86             :         }
      87             :         else
      88             :         {
      89           9 :             if (pszStar == pszPattern)
      90             :             {
      91           6 :                 if (strlen(pszPattern) >= 3 &&
      92           6 :                     pszPattern[strlen(pszPattern) - 1] == '*')
      93             :                 {
      94             :                     // *pattern*
      95           3 :                     CPLString oPattern(pszPattern + 1);
      96           3 :                     oPattern.pop_back();
      97           3 :                     if (CPLString(pszFieldName).ifind(oPattern) !=
      98             :                         std::string::npos)
      99           3 :                         return true;
     100             :                 }
     101             :                 else
     102             :                 {
     103             :                     // *pattern
     104           3 :                     if (strlen(pszFieldName) >= strlen(pszPattern) - 1 &&
     105           1 :                         EQUAL(pszFieldName + strlen(pszFieldName) -
     106             :                                   (strlen(pszPattern) - 1),
     107             :                               pszPattern + 1))
     108             :                     {
     109           1 :                         return true;
     110             :                     }
     111             :                 }
     112             :             }
     113           3 :             else if (pszPattern[strlen(pszPattern) - 1] == '*')
     114             :             {
     115             :                 // pattern*
     116           3 :                 if (EQUALN(pszFieldName, pszPattern, strlen(pszPattern) - 1))
     117           1 :                     return true;
     118             :             }
     119             :         }
     120             :     }
     121       11046 :     return false;
     122             : }
     123             : 
     124             : /************************************************************************/
     125             : /*                          BuildFeatureDefn()                          */
     126             : /************************************************************************/
     127             : 
     128         813 : void OGRCSVLayer::BuildFeatureDefn(const char *pszNfdcGeomField,
     129             :                                    const char *pszGeonamesGeomFieldPrefix,
     130             :                                    CSLConstList papszOpenOptions)
     131             : {
     132         813 :     bMergeDelimiter = CPLFetchBool(papszOpenOptions, "MERGE_SEPARATOR", false);
     133         813 :     bEmptyStringNull =
     134         813 :         CPLFetchBool(papszOpenOptions, "EMPTY_STRING_AS_NULL", false);
     135             : 
     136             :     // If this is not a new file, read ahead to establish if it is
     137             :     // already in CRLF (DOS) mode, or just a normal unix CR mode.
     138         813 :     if (!bNew && bInWriteMode)
     139             :     {
     140          97 :         int nBytesRead = 0;
     141          97 :         char chNewByte = '\0';
     142             : 
     143       55787 :         while (nBytesRead < 10000 && VSIFReadL(&chNewByte, 1, 1, fpCSV) == 1)
     144             :         {
     145       55691 :             if (chNewByte == 13)
     146             :             {
     147           1 :                 bUseCRLF = true;
     148           1 :                 break;
     149             :             }
     150       55690 :             nBytesRead++;
     151             :         }
     152          97 :         VSIRewindL(fpCSV);
     153             :     }
     154             : 
     155             :     // Check if the first record seems to be field definitions or
     156             :     // not.  We assume it is field definitions if the HEADERS option
     157             :     // not supplied and none of the values are strictly numeric.
     158         813 :     char **papszTokens = nullptr;
     159         813 :     int nFieldCount = 0;
     160             : 
     161         813 :     if (!bNew)
     162             :     {
     163         662 :         const char *pszLine = CPLReadLineL(fpCSV);
     164         662 :         if (pszLine != nullptr)
     165             :         {
     166             :             // Detect and remove UTF-8 BOM marker if found (#4623).
     167         662 :             if (reinterpret_cast<const unsigned char *>(pszLine)[0] == 0xEF &&
     168           2 :                 reinterpret_cast<const unsigned char *>(pszLine)[1] == 0xBB &&
     169           2 :                 reinterpret_cast<const unsigned char *>(pszLine)[2] == 0xBF)
     170             :             {
     171           2 :                 pszLine += 3;
     172             :             }
     173             : 
     174             :             // Tokenize the strings and preserve quotes, so we can separate
     175             :             // string from numeric this is only used in the test for
     176             :             // bHasFieldNames (bug #4361).
     177             :             papszTokens =
     178         662 :                 CSLTokenizeString2(pszLine, szDelimiter,
     179             :                                    (CSLT_HONOURSTRINGS | CSLT_ALLOWEMPTYTOKENS |
     180             :                                     CSLT_PRESERVEQUOTES));
     181         662 :             nFieldCount = CSLCount(papszTokens);
     182             : 
     183         662 :             if (nFieldCount > 0 && papszTokens[0][0] == '"')
     184          29 :                 m_eStringQuoting = StringQuoting::ALWAYS;
     185             : 
     186             :             const char *pszCSVHeaders =
     187         662 :                 CSLFetchNameValueDef(papszOpenOptions, "HEADERS", "AUTO");
     188             : 
     189         662 :             if (EQUAL(pszCSVHeaders, "YES"))
     190             :             {
     191           0 :                 bHasFieldNames = true;
     192             :             }
     193         662 :             else if (EQUAL(pszCSVHeaders, "NO"))
     194             :             {
     195           0 :                 bHasFieldNames = false;
     196             :             }
     197             :             else
     198             :             {
     199             :                 // Detect via checking for the presence of numeric values.
     200         662 :                 bHasFieldNames = true;
     201        3546 :                 for (int iField = 0; iField < nFieldCount && bHasFieldNames;
     202             :                      iField++)
     203             :                 {
     204             :                     const CPLValueType eType =
     205        2884 :                         CPLGetValueType(papszTokens[iField]);
     206        2884 :                     if (eType == CPL_VALUE_INTEGER || eType == CPL_VALUE_REAL)
     207             :                     {
     208             :                         // We have a numeric field, therefore do not consider
     209             :                         // the first line as field names.
     210          67 :                         bHasFieldNames = false;
     211             :                     }
     212             :                 }
     213             : 
     214             :                 const CPLString osExt =
     215        1324 :                     OGRCSVDataSource::GetRealExtension(pszFilename);
     216             : 
     217             :                 // Eurostat .tsv files.
     218         663 :                 if (EQUAL(osExt, "tsv") && nFieldCount > 1 &&
     219         664 :                     strchr(papszTokens[0], ',') != nullptr &&
     220           1 :                     strchr(papszTokens[0], '\\') != nullptr)
     221             :                 {
     222           1 :                     bHasFieldNames = true;
     223           1 :                     bIsEurostatTSV = true;
     224             :                 }
     225             :             }
     226             : 
     227             :             // Tokenize without quotes to get the actual values.
     228         662 :             VSIRewindL(fpCSV);
     229         662 :             CSLDestroy(papszTokens);
     230             :             papszTokens =
     231        1324 :                 CSVReadParseLine3L(fpCSV, m_nMaxLineSize, szDelimiter,
     232             :                                    true,   // bHonourStrings
     233             :                                    false,  // bKeepLeadingAndClosingQuotes
     234         662 :                                    bMergeDelimiter,
     235             :                                    true  // bSkipBOM
     236             :                 );
     237         662 :             nFieldCount = CSLCount(papszTokens);
     238             :         }
     239             :     }
     240             :     else
     241             :     {
     242         151 :         bHasFieldNames = false;
     243             :     }
     244             : 
     245         813 :     if (!bNew)
     246         662 :         ResetReading();
     247             : 
     248             :     int nMaxFieldCount =
     249         813 :         atoi(CPLGetConfigOption("OGR_CSV_MAX_FIELD_COUNT", "2000"));
     250         813 :     if (nFieldCount > nMaxFieldCount)
     251             :     {
     252           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     253             :                  "%d columns detected. Limiting to %d. "
     254             :                  "Set OGR_CSV_MAX_FIELD_COUNT configuration option "
     255             :                  "to allow more fields.",
     256             :                  nFieldCount, nMaxFieldCount);
     257           0 :         nFieldCount = nMaxFieldCount;
     258             :     }
     259         813 :     if (nFieldCount > 100000)
     260           0 :         nFieldCount = 100000;  // to please coverity
     261             : 
     262         813 :     nCSVFieldCount = nFieldCount;
     263             : 
     264             :     // coverity[tainted_data]
     265         813 :     panGeomFieldIndex = static_cast<int *>(CPLCalloc(nFieldCount, sizeof(int)));
     266        4023 :     for (int iField = 0; iField < nFieldCount; iField++)
     267             :     {
     268        3210 :         panGeomFieldIndex[iField] = -1;
     269             :     }
     270             : 
     271             :     // Check for geonames.org tables.
     272         813 :     if (!bHasFieldNames && nFieldCount == 19)
     273             :     {
     274           1 :         if (CPLGetValueType(papszTokens[0]) == CPL_VALUE_INTEGER &&
     275           1 :             CPLGetValueType(papszTokens[4]) == CPL_VALUE_REAL &&
     276           1 :             CPLGetValueType(papszTokens[5]) == CPL_VALUE_REAL &&
     277           1 :             CPLAtof(papszTokens[4]) >= -90 && CPLAtof(papszTokens[4]) <= 90 &&
     278           2 :             CPLAtof(papszTokens[5]) >= -180 && CPLAtof(papszTokens[4]) <= 180)
     279             :         {
     280           1 :             CSLDestroy(papszTokens);
     281           1 :             papszTokens = nullptr;
     282             : 
     283             :             static const struct
     284             :             {
     285             :                 const char *pszName;
     286             :                 OGRFieldType eType;
     287             :             } asGeonamesFieldDesc[] = {
     288             :                 {"GEONAMEID", OFTString}, {"NAME", OFTString},
     289             :                 {"ASCIINAME", OFTString}, {"ALTNAMES", OFTString},
     290             :                 {"LATITUDE", OFTReal},    {"LONGITUDE", OFTReal},
     291             :                 {"FEATCLASS", OFTString}, {"FEATCODE", OFTString},
     292             :                 {"COUNTRY", OFTString},   {"CC2", OFTString},
     293             :                 {"ADMIN1", OFTString},    {"ADMIN2", OFTString},
     294             :                 {"ADMIN3", OFTString},    {"ADMIN4", OFTString},
     295             :                 {"POPULATION", OFTReal},  {"ELEVATION", OFTInteger},
     296             :                 {"GTOPO30", OFTInteger},  {"TIMEZONE", OFTString},
     297             :                 {"MODDATE", OFTString}};
     298             : 
     299          20 :             for (int iField = 0; iField < nFieldCount; iField++)
     300             :             {
     301          19 :                 OGRFieldDefn oFieldDefn(asGeonamesFieldDesc[iField].pszName,
     302          38 :                                         asGeonamesFieldDesc[iField].eType);
     303          19 :                 m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
     304             :             }
     305             : 
     306           1 :             iLatitudeField = 4;
     307           1 :             iLongitudeField = 5;
     308             : 
     309           1 :             nFieldCount = 0;
     310             : 
     311           1 :             bHonourStrings = false;
     312             :         }
     313             :     }
     314             : 
     315             :     // Search a csvt file for types.
     316         813 :     char **papszFieldTypes = nullptr;
     317         813 :     if (!bNew)
     318             :     {
     319             :         // Only try to read .csvt from files that have an extension
     320         662 :         if (!CPLGetExtensionSafe(pszFilename).empty())
     321             :         {
     322             :             std::string osCSVTFilename =
     323        1324 :                 CPLResetExtensionSafe(pszFilename, "csvt");
     324         662 :             VSILFILE *fpCSVT = VSIFOpenL(osCSVTFilename.c_str(), "r");
     325         662 :             if (fpCSVT != nullptr)
     326             :             {
     327         133 :                 m_osCSVTFilename = std::move(osCSVTFilename);
     328         133 :                 VSIRewindL(fpCSVT);
     329             :                 papszFieldTypes =
     330         133 :                     CSVReadParseLine3L(fpCSVT, m_nMaxLineSize, ",",
     331             :                                        true,   // bHonourStrings
     332             :                                        false,  // bKeepLeadingAndClosingQuotes
     333             :                                        false,  // bMergeDelimiter,
     334             :                                        true    // bSkipBOM
     335             :                     );
     336         133 :                 VSIFCloseL(fpCSVT);
     337             :             }
     338             :         }
     339             :     }
     340             : 
     341             :     // Optionally auto-detect types.
     342        1342 :     if (!bNew && papszFieldTypes == nullptr &&
     343         529 :         CPLTestBool(
     344             :             CSLFetchNameValueDef(papszOpenOptions, "AUTODETECT_TYPE", "NO")))
     345             :     {
     346          25 :         papszFieldTypes = AutodetectFieldTypes(papszOpenOptions, nFieldCount);
     347          25 :         if (papszFieldTypes != nullptr)
     348             :         {
     349          25 :             bKeepSourceColumns = CPLTestBool(CSLFetchNameValueDef(
     350             :                 papszOpenOptions, "KEEP_SOURCE_COLUMNS", "NO"));
     351             :         }
     352             :     }
     353             : 
     354         813 :     char **papszGeomPossibleNames = CSLTokenizeString2(
     355             :         CSLFetchNameValue(papszOpenOptions, "GEOM_POSSIBLE_NAMES"), ",", 0);
     356         813 :     char **papszXPossibleNames = CSLTokenizeString2(
     357             :         CSLFetchNameValue(papszOpenOptions, "X_POSSIBLE_NAMES"), ",", 0);
     358         813 :     char **papszYPossibleNames = CSLTokenizeString2(
     359             :         CSLFetchNameValue(papszOpenOptions, "Y_POSSIBLE_NAMES"), ",", 0);
     360         813 :     char **papszZPossibleNames = CSLTokenizeString2(
     361             :         CSLFetchNameValue(papszOpenOptions, "Z_POSSIBLE_NAMES"), ",", 0);
     362         813 :     bKeepGeomColumns = CPLTestBool(
     363             :         CSLFetchNameValueDef(papszOpenOptions, "KEEP_GEOM_COLUMNS", "YES"));
     364             : 
     365             :     // Build field definitions.
     366         813 :     m_poFeatureDefn->ReserveSpaceForFields(nFieldCount);
     367             : 
     368         813 :     constexpr int knMAX_GEOM_COLUMNS = 100;
     369         813 :     bool bWarnedMaxGeomFields = false;
     370             : 
     371         813 :     const int nFieldTypesCount = CSLCount(papszFieldTypes);
     372             : 
     373        4000 :     for (int iField = 0; !bIsEurostatTSV && iField < nFieldCount; iField++)
     374             :     {
     375        3187 :         char *pszFieldName = nullptr;
     376             :         char szFieldNameBuffer[100];
     377             : 
     378        3187 :         if (bHasFieldNames)
     379             :         {
     380        2754 :             pszFieldName = papszTokens[iField];
     381             : 
     382             :             // Trim white space.
     383        2762 :             while (*pszFieldName == ' ')
     384           8 :                 pszFieldName++;
     385             : 
     386        2762 :             while (pszFieldName[0] != '\0' &&
     387        2762 :                    pszFieldName[strlen(pszFieldName) - 1] == ' ')
     388           8 :                 pszFieldName[strlen(pszFieldName) - 1] = '\0';
     389             : 
     390        2754 :             if (*pszFieldName == '\0')
     391           0 :                 pszFieldName = nullptr;
     392             :         }
     393             : 
     394        3187 :         if (pszFieldName == nullptr)
     395             :         {
     396             :             // Re-read single column CSV files that have a trailing comma
     397             :             // in the header line.
     398         433 :             if (iField == 1 && nFieldCount == 2 && papszTokens[1][0] == '\0')
     399             :             {
     400           0 :                 nCSVFieldCount = 1;
     401           0 :                 nFieldCount = 1;
     402           0 :                 break;
     403             :             }
     404         433 :             pszFieldName = szFieldNameBuffer;
     405         433 :             snprintf(szFieldNameBuffer, sizeof(szFieldNameBuffer), "field_%d",
     406             :                      iField + 1);
     407             :         }
     408             : 
     409        3187 :         OGRFieldDefn oField(pszFieldName, OFTString);
     410        3187 :         if (papszFieldTypes != nullptr && iField < nFieldTypesCount)
     411             :         {
     412         952 :             if (EQUAL(papszFieldTypes[iField], "WKT"))
     413             :             {
     414          14 :                 if (bKeepGeomColumns)
     415          14 :                     m_poFeatureDefn->AddFieldDefn(&oField);
     416             : 
     417          14 :                 if (m_poFeatureDefn->GetGeomFieldCount() == knMAX_GEOM_COLUMNS)
     418             :                 {
     419           0 :                     if (!bWarnedMaxGeomFields)
     420             :                     {
     421           0 :                         CPLError(CE_Warning, CPLE_NotSupported,
     422             :                                  "A maximum number of %d geometry fields is "
     423             :                                  "supported. "
     424             :                                  "Only the first ones are taken into account.",
     425             :                                  knMAX_GEOM_COLUMNS);
     426           0 :                         bWarnedMaxGeomFields = true;
     427             :                     }
     428           0 :                     continue;
     429             :                 }
     430             : 
     431          14 :                 eGeometryFormat = OGR_CSV_GEOM_AS_WKT;
     432          28 :                 panGeomFieldIndex[iField] =
     433          14 :                     m_poFeatureDefn->GetGeomFieldCount();
     434          28 :                 std::string osGeomColName;
     435          14 :                 if (bKeepGeomColumns)
     436          14 :                     osGeomColName += "geom_";
     437          14 :                 osGeomColName += oField.GetNameRef();
     438             :                 OGRGeomFieldDefn oGeomFieldDefn(osGeomColName.c_str(),
     439          28 :                                                 wkbUnknown);
     440          14 :                 m_poFeatureDefn->AddGeomFieldDefn(&oGeomFieldDefn);
     441          14 :                 continue;
     442             :             }
     443         938 :             else if (EQUAL(papszFieldTypes[iField], "CoordX") ||
     444         934 :                      EQUAL(papszFieldTypes[iField], "Point(X)"))
     445             :             {
     446           4 :                 oField.SetType(OFTReal);
     447           4 :                 iLongitudeField = iField;
     448           4 :                 osXField = oField.GetNameRef();
     449           4 :                 if (bKeepGeomColumns)
     450           4 :                     m_poFeatureDefn->AddFieldDefn(&oField);
     451           4 :                 continue;
     452             :             }
     453         934 :             else if (EQUAL(papszFieldTypes[iField], "CoordY") ||
     454         930 :                      EQUAL(papszFieldTypes[iField], "Point(Y)"))
     455             :             {
     456           4 :                 oField.SetType(OFTReal);
     457           4 :                 iLatitudeField = iField;
     458           4 :                 osYField = oField.GetNameRef();
     459           4 :                 if (bKeepGeomColumns)
     460           4 :                     m_poFeatureDefn->AddFieldDefn(&oField);
     461           4 :                 continue;
     462             :             }
     463         930 :             else if (EQUAL(papszFieldTypes[iField], "CoordZ") ||
     464         930 :                      EQUAL(papszFieldTypes[iField], "Point(Z)"))
     465             :             {
     466           0 :                 oField.SetType(OFTReal);
     467           0 :                 iZField = iField;
     468           0 :                 osZField = oField.GetNameRef();
     469           0 :                 if (bKeepGeomColumns)
     470           0 :                     m_poFeatureDefn->AddFieldDefn(&oField);
     471           0 :                 continue;
     472             :             }
     473         930 :             else if (EQUAL(papszFieldTypes[iField], "Integer(Boolean)"))
     474             :             {
     475          13 :                 oField.SetType(OFTInteger);
     476          13 :                 oField.SetSubType(OFSTBoolean);
     477          13 :                 oField.SetWidth(1);
     478             :             }
     479         917 :             else if (EQUAL(papszFieldTypes[iField], "Integer(Int16)"))
     480             :             {
     481           1 :                 oField.SetType(OFTInteger);
     482           1 :                 oField.SetSubType(OFSTInt16);
     483             :             }
     484         916 :             else if (EQUAL(papszFieldTypes[iField], "Real(Float32)"))
     485             :             {
     486           1 :                 oField.SetType(OFTReal);
     487           1 :                 oField.SetSubType(OFSTFloat32);
     488             :             }
     489             :             else
     490             :             {
     491         915 :                 char *pszLeftParenthesis = strchr(papszFieldTypes[iField], '(');
     492         915 :                 if (pszLeftParenthesis &&
     493         155 :                     pszLeftParenthesis != papszFieldTypes[iField] &&
     494         155 :                     pszLeftParenthesis[1] >= '0' &&
     495         155 :                     pszLeftParenthesis[1] <= '9')
     496             :                 {
     497         155 :                     char *pszDot = strchr(pszLeftParenthesis, '.');
     498         155 :                     if (pszDot)
     499          45 :                         *pszDot = 0;
     500         155 :                     *pszLeftParenthesis = 0;
     501             : 
     502         155 :                     if (pszLeftParenthesis[-1] == ' ')
     503         119 :                         pszLeftParenthesis[-1] = 0;
     504             : 
     505         155 :                     const int nWidth = atoi(pszLeftParenthesis + 1);
     506         155 :                     const int nPrecision = pszDot ? atoi(pszDot + 1) : 0;
     507             : 
     508         155 :                     oField.SetWidth(nWidth);
     509         155 :                     oField.SetPrecision(nPrecision);
     510             :                 }
     511             : 
     512         915 :                 if (EQUAL(papszFieldTypes[iField], "Integer"))
     513          89 :                     oField.SetType(OFTInteger);
     514         826 :                 else if (EQUAL(papszFieldTypes[iField], "Integer64"))
     515          13 :                     oField.SetType(OFTInteger64);
     516         813 :                 else if (EQUAL(papszFieldTypes[iField], "Real"))
     517         226 :                     oField.SetType(OFTReal);
     518         587 :                 else if (EQUAL(papszFieldTypes[iField], "String"))
     519         472 :                     oField.SetType(OFTString);
     520         115 :                 else if (EQUAL(papszFieldTypes[iField], "Date"))
     521          24 :                     oField.SetType(OFTDate);
     522          91 :                 else if (EQUAL(papszFieldTypes[iField], "Time"))
     523          21 :                     oField.SetType(OFTTime);
     524          70 :                 else if (EQUAL(papszFieldTypes[iField], "DateTime"))
     525          61 :                     oField.SetType(OFTDateTime);
     526           9 :                 else if (EQUAL(papszFieldTypes[iField], "JSonStringList"))
     527           3 :                     oField.SetType(OFTStringList);
     528           6 :                 else if (EQUAL(papszFieldTypes[iField], "JSonIntegerList"))
     529           2 :                     oField.SetType(OFTIntegerList);
     530           4 :                 else if (EQUAL(papszFieldTypes[iField], "JSonInteger64List"))
     531           2 :                     oField.SetType(OFTInteger64List);
     532           2 :                 else if (EQUAL(papszFieldTypes[iField], "JSonRealList"))
     533           2 :                     oField.SetType(OFTRealList);
     534             :                 else
     535           0 :                     CPLError(CE_Warning, CPLE_NotSupported, "Unknown type : %s",
     536           0 :                              papszFieldTypes[iField]);
     537             :             }
     538             :         }
     539             : 
     540        3165 :         if (Matches(oField.GetNameRef(), papszZPossibleNames))
     541             :         {
     542           4 :             oField.SetType(OFTReal);
     543           4 :             iZField = iField;
     544           4 :             osZField = oField.GetNameRef();
     545           4 :             if (!bKeepGeomColumns)
     546           2 :                 continue;
     547             :         }
     548        3161 :         else if ((iNfdcLatitudeS != -1 && iNfdcLongitudeS != -1) ||
     549        3161 :                  (iLatitudeField != -1 && iLongitudeField != -1))
     550             :         {
     551             :             // Do nothing.
     552             :         }
     553        3148 :         else if ((EQUAL(oField.GetNameRef(), "WKT") ||
     554        3643 :                   STARTS_WITH_CI(oField.GetNameRef(), "_WKT")) &&
     555         495 :                  oField.GetType() == OFTString)
     556             :         {
     557         495 :             if (m_poFeatureDefn->GetGeomFieldCount() == knMAX_GEOM_COLUMNS)
     558             :             {
     559           1 :                 if (!bWarnedMaxGeomFields)
     560             :                 {
     561           1 :                     CPLError(
     562             :                         CE_Warning, CPLE_NotSupported,
     563             :                         "A maximum number of %d geometry fields is supported. "
     564             :                         "Only the first ones are taken into account.",
     565             :                         knMAX_GEOM_COLUMNS);
     566           1 :                     bWarnedMaxGeomFields = true;
     567             :                 }
     568             :             }
     569             :             else
     570             :             {
     571         494 :                 eGeometryFormat = OGR_CSV_GEOM_AS_WKT;
     572             : 
     573         988 :                 panGeomFieldIndex[iField] =
     574         494 :                     m_poFeatureDefn->GetGeomFieldCount();
     575             :                 OGRGeomFieldDefn oGeomFieldDefn(
     576         494 :                     EQUAL(pszFieldName, "WKT")
     577             :                         ? ""
     578         247 :                         : CPLSPrintf("geom_%s", pszFieldName),
     579         741 :                     wkbUnknown);
     580             : 
     581             :                 // Useful hack for RFC 41 testing.
     582         494 :                 const char *pszEPSG = strstr(pszFieldName, "_EPSG_");
     583         494 :                 if (pszEPSG != nullptr)
     584             :                 {
     585         242 :                     const int nEPSGCode = atoi(pszEPSG + strlen("_EPSG_"));
     586             :                     auto poSRS =
     587         484 :                         OGRSpatialReferenceRefCountedPtr::makeInstance();
     588         242 :                     poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     589         242 :                     poSRS->importFromEPSG(nEPSGCode);
     590         242 :                     oGeomFieldDefn.SetSpatialRef(poSRS.get());
     591             :                 }
     592             : 
     593         494 :                 if (strstr(pszFieldName, "_POINT"))
     594         102 :                     oGeomFieldDefn.SetType(wkbPoint);
     595         392 :                 else if (strstr(pszFieldName, "_LINESTRING"))
     596          34 :                     oGeomFieldDefn.SetType(wkbLineString);
     597         358 :                 else if (strstr(pszFieldName, "_POLYGON"))
     598          68 :                     oGeomFieldDefn.SetType(wkbPolygon);
     599         290 :                 else if (strstr(pszFieldName, "_MULTIPOINT"))
     600           0 :                     oGeomFieldDefn.SetType(wkbMultiPoint);
     601         290 :                 else if (strstr(pszFieldName, "_MULTILINESTRING"))
     602           0 :                     oGeomFieldDefn.SetType(wkbMultiLineString);
     603         290 :                 else if (strstr(pszFieldName, "_MULTIPOLYGON"))
     604           0 :                     oGeomFieldDefn.SetType(wkbMultiPolygon);
     605         290 :                 else if (strstr(pszFieldName, "_CIRCULARSTRING"))
     606           0 :                     oGeomFieldDefn.SetType(wkbCircularString);
     607         290 :                 else if (strstr(pszFieldName, "_COMPOUNDCURVE"))
     608           0 :                     oGeomFieldDefn.SetType(wkbCompoundCurve);
     609         290 :                 else if (strstr(pszFieldName, "_CURVEPOLYGON"))
     610           4 :                     oGeomFieldDefn.SetType(wkbCurvePolygon);
     611         286 :                 else if (strstr(pszFieldName, "_CURVE"))
     612           0 :                     oGeomFieldDefn.SetType(wkbCurve);
     613         286 :                 else if (strstr(pszFieldName, "_SURFACE"))
     614           0 :                     oGeomFieldDefn.SetType(wkbSurface);
     615         286 :                 else if (strstr(pszFieldName, "_MULTICURVE"))
     616           0 :                     oGeomFieldDefn.SetType(wkbMultiCurve);
     617         286 :                 else if (strstr(pszFieldName, "_MULTISURFACE"))
     618           0 :                     oGeomFieldDefn.SetType(wkbMultiSurface);
     619         286 :                 else if (strstr(pszFieldName, "_POLYHEDRALSURFACE"))
     620           0 :                     oGeomFieldDefn.SetType(wkbPolyhedralSurface);
     621         286 :                 else if (strstr(pszFieldName, "_TIN"))
     622           0 :                     oGeomFieldDefn.SetType(wkbTIN);
     623         286 :                 else if (strstr(pszFieldName, "_TRIANGLE"))
     624           0 :                     oGeomFieldDefn.SetType(wkbTriangle);
     625             : 
     626         494 :                 m_poFeatureDefn->AddGeomFieldDefn(&oGeomFieldDefn);
     627         494 :                 if (!bKeepGeomColumns)
     628           0 :                     continue;
     629             :             }
     630             :         }
     631        2653 :         else if (Matches(oField.GetNameRef(), papszGeomPossibleNames))
     632             :         {
     633          12 :             eGeometryFormat = OGR_CSV_GEOM_AS_SOME_GEOM_FORMAT;
     634          12 :             panGeomFieldIndex[iField] = m_poFeatureDefn->GetGeomFieldCount();
     635          12 :             OGRGeomFieldDefn oGeomFieldDefn(oField.GetNameRef(), wkbUnknown);
     636          12 :             m_poFeatureDefn->AddGeomFieldDefn(&oGeomFieldDefn);
     637          12 :             if (!bKeepGeomColumns)
     638           7 :                 continue;
     639             :         }
     640        2654 :         else if (Matches(oField.GetNameRef(), papszXPossibleNames) &&
     641          13 :                  m_poFeatureDefn->GetGeomFieldCount() == 0)
     642             :         {
     643          12 :             oField.SetType(OFTReal);
     644          12 :             iLongitudeField = iField;
     645          12 :             osXField = oField.GetNameRef();
     646          12 :             if (!bKeepGeomColumns)
     647           6 :                 continue;
     648             :         }
     649        2642 :         else if (Matches(oField.GetNameRef(), papszYPossibleNames) &&
     650          13 :                  m_poFeatureDefn->GetGeomFieldCount() == 0)
     651             :         {
     652          12 :             oField.SetType(OFTReal);
     653          12 :             iLatitudeField = iField;
     654          12 :             osYField = oField.GetNameRef();
     655          12 :             if (!bKeepGeomColumns)
     656           6 :                 continue;
     657             :         }
     658             : 
     659             :         // TODO(schwehr): URL broken.
     660             :         // http://www.faa.gov/airports/airport_safety/airportdata_5010/menu/index.cfm
     661             :         // specific
     662        2617 :         else if (pszNfdcGeomField != nullptr &&
     663           0 :                  EQUALN(oField.GetNameRef(), pszNfdcGeomField,
     664           0 :                         strlen(pszNfdcGeomField)) &&
     665           0 :                  EQUAL(oField.GetNameRef() + strlen(pszNfdcGeomField),
     666        2617 :                        "LatitudeS") &&
     667           0 :                  m_poFeatureDefn->GetGeomFieldCount() == 0)
     668             :         {
     669           0 :             iNfdcLatitudeS = iField;
     670           0 :             if (!bKeepGeomColumns)
     671           0 :                 continue;
     672             :         }
     673        2617 :         else if (pszNfdcGeomField != nullptr &&
     674           0 :                  EQUALN(oField.GetNameRef(), pszNfdcGeomField,
     675           0 :                         strlen(pszNfdcGeomField)) &&
     676           0 :                  EQUAL(oField.GetNameRef() + strlen(pszNfdcGeomField),
     677        2617 :                        "LongitudeS") &&
     678           0 :                  m_poFeatureDefn->GetGeomFieldCount() == 0)
     679             :         {
     680           0 :             iNfdcLongitudeS = iField;
     681           0 :             if (!bKeepGeomColumns)
     682           0 :                 continue;
     683             :         }
     684             :         // GNIS specific.
     685        2617 :         else if (pszGeonamesGeomFieldPrefix != nullptr &&
     686           0 :                  EQUALN(oField.GetNameRef(), pszGeonamesGeomFieldPrefix,
     687           0 :                         strlen(pszGeonamesGeomFieldPrefix)) &&
     688           0 :                  (EQUAL(oField.GetNameRef() +
     689             :                             strlen(pszGeonamesGeomFieldPrefix),
     690           0 :                         "_LAT_DEC") ||
     691           0 :                   EQUAL(oField.GetNameRef() +
     692             :                             strlen(pszGeonamesGeomFieldPrefix),
     693           0 :                         "_LATITUDE_DEC") ||
     694           0 :                   EQUAL(oField.GetNameRef() +
     695             :                             strlen(pszGeonamesGeomFieldPrefix),
     696        2617 :                         "_LATITUDE")) &&
     697           0 :                  m_poFeatureDefn->GetGeomFieldCount() == 0)
     698             :         {
     699           0 :             m_bIsGNIS = true;
     700           0 :             oField.SetType(OFTReal);
     701           0 :             iLatitudeField = iField;
     702           0 :             osYField = oField.GetNameRef();
     703           0 :             if (!bKeepGeomColumns)
     704           0 :                 continue;
     705             :         }
     706        2617 :         else if (pszGeonamesGeomFieldPrefix != nullptr &&
     707           0 :                  EQUALN(oField.GetNameRef(), pszGeonamesGeomFieldPrefix,
     708           0 :                         strlen(pszGeonamesGeomFieldPrefix)) &&
     709           0 :                  (EQUAL(oField.GetNameRef() +
     710             :                             strlen(pszGeonamesGeomFieldPrefix),
     711           0 :                         "_LONG_DEC") ||
     712           0 :                   EQUAL(oField.GetNameRef() +
     713             :                             strlen(pszGeonamesGeomFieldPrefix),
     714           0 :                         "_LONGITUDE_DEC") ||
     715           0 :                   EQUAL(oField.GetNameRef() +
     716             :                             strlen(pszGeonamesGeomFieldPrefix),
     717        2617 :                         "_LONGITUDE")) &&
     718           0 :                  m_poFeatureDefn->GetGeomFieldCount() == 0)
     719             :         {
     720           0 :             m_bIsGNIS = true;
     721           0 :             oField.SetType(OFTReal);
     722           0 :             iLongitudeField = iField;
     723           0 :             osXField = oField.GetNameRef();
     724           0 :             if (!bKeepGeomColumns)
     725           0 :                 continue;
     726             :         }
     727             : 
     728        3144 :         m_poFeatureDefn->AddFieldDefn(&oField);
     729             : 
     730        3144 :         if (bKeepSourceColumns && oField.GetType() != OFTString)
     731             :         {
     732             :             OGRFieldDefn oFieldOriginal(
     733          22 :                 CPLSPrintf("%s_original", oField.GetNameRef()), OFTString);
     734          11 :             m_poFeatureDefn->AddFieldDefn(&oFieldOriginal);
     735             :         }
     736             :     }
     737             : 
     738         813 :     if (iNfdcLatitudeS != -1 && iNfdcLongitudeS != -1)
     739             :     {
     740           0 :         bHonourStrings = false;
     741           0 :         if (m_poFeatureDefn->GetGeomFieldCount() == 0)
     742             :         {
     743           0 :             m_poFeatureDefn->SetGeomType(wkbPoint);
     744             :         }
     745             :         else
     746             :         {
     747           0 :             iNfdcLatitudeS = -1;
     748           0 :             iNfdcLongitudeS = -1;
     749           0 :             iLatitudeField = -1;
     750           0 :             iLongitudeField = -1;
     751             :         }
     752             :     }
     753         813 :     else if (iLatitudeField != -1 && iLongitudeField != -1)
     754             :     {
     755          17 :         if (m_poFeatureDefn->GetGeomFieldCount() == 0)
     756             :         {
     757          17 :             m_poFeatureDefn->SetGeomType(iZField >= 0 ? wkbPoint25D : wkbPoint);
     758             :         }
     759             :         else
     760             :         {
     761           0 :             iNfdcLatitudeS = -1;
     762           0 :             iNfdcLongitudeS = -1;
     763           0 :             iLatitudeField = -1;
     764           0 :             iLongitudeField = -1;
     765             :         }
     766             :     }
     767             : 
     768        1091 :     if (m_poFeatureDefn->GetGeomFieldCount() > 0 &&
     769         278 :         m_poFeatureDefn->GetGeomFieldDefn(0)->GetSpatialRef() == nullptr)
     770             :     {
     771             :         VSILFILE *fpPRJ =
     772         189 :             VSIFOpenL(CPLResetExtensionSafe(pszFilename, "prj").c_str(), "rb");
     773         189 :         if (fpPRJ != nullptr)
     774             :         {
     775           2 :             GByte *pabyRet = nullptr;
     776           2 :             if (VSIIngestFile(fpPRJ, nullptr, &pabyRet, nullptr, 1000000))
     777             :             {
     778           4 :                 auto poSRS = OGRSpatialReferenceRefCountedPtr::makeInstance();
     779           2 :                 poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     780           2 :                 if (poSRS->SetFromUserInput(
     781             :                         reinterpret_cast<const char *>(pabyRet),
     782             :                         OGRSpatialReference::
     783           2 :                             SET_FROM_USER_INPUT_LIMITATIONS_get()) ==
     784             :                     OGRERR_NONE)
     785             :                 {
     786           4 :                     m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(
     787           2 :                         poSRS.get());
     788             :                 }
     789             :             }
     790           2 :             CPLFree(pabyRet);
     791           2 :             VSIFCloseL(fpPRJ);
     792             :         }
     793             :     }
     794             : 
     795         813 :     CSLDestroy(papszGeomPossibleNames);
     796         813 :     CSLDestroy(papszXPossibleNames);
     797         813 :     CSLDestroy(papszYPossibleNames);
     798         813 :     CSLDestroy(papszZPossibleNames);
     799             : 
     800             :     // Build field definitions for Eurostat TSV files.
     801             : 
     802        1626 :     CPLString osSeqDim;
     803         817 :     for (int iField = 0; bIsEurostatTSV && iField < nFieldCount; iField++)
     804             :     {
     805           4 :         if (iField == 0)
     806             :         {
     807           1 :             char **papszDims = CSLTokenizeString2(papszTokens[0], ",\\", 0);
     808           1 :             nEurostatDims = CSLCount(papszDims) - 1;
     809           3 :             for (int iSubField = 0; iSubField < nEurostatDims; iSubField++)
     810             :             {
     811           4 :                 OGRFieldDefn oField(papszDims[iSubField], OFTString);
     812           2 :                 m_poFeatureDefn->AddFieldDefn(&oField);
     813             :             }
     814             : 
     815           1 :             if (nEurostatDims >= 0)
     816           1 :                 osSeqDim = papszDims[nEurostatDims];
     817             :             else
     818           0 :                 CPLError(CE_Warning, CPLE_AppDefined, "Invalid nEurostatDims");
     819             : 
     820           1 :             CSLDestroy(papszDims);
     821             :         }
     822             :         else
     823             :         {
     824           3 :             if (papszTokens[iField][0] != '\0' &&
     825           3 :                 papszTokens[iField][strlen(papszTokens[iField]) - 1] == ' ')
     826           3 :                 papszTokens[iField][strlen(papszTokens[iField]) - 1] = '\0';
     827             : 
     828             :             OGRFieldDefn oField(
     829           3 :                 CPLSPrintf("%s_%s", osSeqDim.c_str(), papszTokens[iField]),
     830           6 :                 OFTReal);
     831           3 :             m_poFeatureDefn->AddFieldDefn(&oField);
     832             : 
     833             :             OGRFieldDefn oField2(
     834           3 :                 CPLSPrintf("%s_%s_flag", osSeqDim.c_str(), papszTokens[iField]),
     835           6 :                 OFTString);
     836           3 :             m_poFeatureDefn->AddFieldDefn(&oField2);
     837             :         }
     838             :     }
     839             : 
     840         813 :     CSLDestroy(papszTokens);
     841         813 :     CSLDestroy(papszFieldTypes);
     842         813 : }
     843             : 
     844             : /************************************************************************/
     845             : /*                            GetFileList()                             */
     846             : /************************************************************************/
     847             : 
     848          29 : std::vector<std::string> OGRCSVLayer::GetFileList()
     849             : {
     850          29 :     std::vector<std::string> ret;
     851          29 :     ret.emplace_back(pszFilename);
     852          29 :     if (!m_osCSVTFilename.empty())
     853          11 :         ret.emplace_back(m_osCSVTFilename);
     854          29 :     return ret;
     855             : }
     856             : 
     857             : /************************************************************************/
     858             : /*                            OGRCSVIsTrue()                            */
     859             : /************************************************************************/
     860             : 
     861         193 : static bool OGRCSVIsTrue(const char *pszStr)
     862             : {
     863         190 :     return EQUAL(pszStr, "t") || EQUAL(pszStr, "true") || EQUAL(pszStr, "y") ||
     864         383 :            EQUAL(pszStr, "yes") || EQUAL(pszStr, "on");
     865             : }
     866             : 
     867             : /************************************************************************/
     868             : /*                           OGRCSVIsFalse()                            */
     869             : /************************************************************************/
     870             : 
     871         172 : static bool OGRCSVIsFalse(const char *pszStr)
     872             : {
     873         169 :     return EQUAL(pszStr, "f") || EQUAL(pszStr, "false") || EQUAL(pszStr, "n") ||
     874         341 :            EQUAL(pszStr, "no") || EQUAL(pszStr, "off");
     875             : }
     876             : 
     877             : /************************************************************************/
     878             : /*                        AutodetectFieldTypes()                        */
     879             : /************************************************************************/
     880             : 
     881          25 : char **OGRCSVLayer::AutodetectFieldTypes(CSLConstList papszOpenOptions,
     882             :                                          int nFieldCount)
     883             : {
     884             :     const bool bStreaming =
     885          50 :         STARTS_WITH(pszFilename, "/vsistdin") ||
     886             :         // config option for testing purposes only
     887          25 :         CPLTestBool(CPLGetConfigOption("OGR_CSV_SIMULATE_VSISTDIN", "NO"));
     888          25 :     constexpr int STREAMING_LIMIT = 1000 * 1000;
     889             :     // Use 1 000 000 as default maximum distance to be compatible with
     890             :     // /vsistdin/ caching.
     891          25 :     vsi_l_offset nBytes = static_cast<vsi_l_offset>(CPLAtoGIntBig(
     892             :         CSLFetchNameValueDef(papszOpenOptions, "AUTODETECT_SIZE_LIMIT",
     893          25 :                              CPLSPrintf("%d", STREAMING_LIMIT))));
     894          25 :     if (nBytes == 0)
     895           2 :         nBytes = static_cast<vsi_l_offset>(-1);  // unlimited size
     896          25 :     if (bStreaming && (nBytes == 0 || nBytes > STREAMING_LIMIT))
     897             :     {
     898           1 :         CPLError(CE_Warning, CPLE_AppDefined,
     899             :                  "Limiting AUTODETECT_SIZE_LIMIT to %d for /vsistdin/",
     900             :                  STREAMING_LIMIT);
     901           1 :         nBytes = STREAMING_LIMIT;
     902             :     }
     903             : 
     904          25 :     ResetReading();
     905             : 
     906             :     const char *pszAutodetectWidth =
     907          25 :         CSLFetchNameValueDef(papszOpenOptions, "AUTODETECT_WIDTH", "NO");
     908             : 
     909          25 :     const bool bAutodetectWidthForIntOrReal = EQUAL(pszAutodetectWidth, "YES");
     910          41 :     const bool bAutodetectWidth = bAutodetectWidthForIntOrReal ||
     911          16 :                                   EQUAL(pszAutodetectWidth, "STRING_ONLY");
     912             : 
     913          25 :     const bool bQuotedFieldAsString = CPLTestBool(CSLFetchNameValueDef(
     914             :         papszOpenOptions, "QUOTED_FIELDS_AS_STRING", "NO"));
     915             : 
     916             :     // This will be returned as the result.
     917          25 :     char **papszFieldTypes = nullptr;
     918             : 
     919          25 :     char *pszData = nullptr;
     920          25 :     VSILFILE *fp = fpCSV;
     921          50 :     std::string osTmpMemFile;
     922          25 :     size_t nRead = 0;
     923          25 :     int nRequested = 0;
     924          25 :     if (bStreaming)
     925             :     {
     926             :         // The above ResetReading() will skip the header line,
     927             :         // so VSIFTellL(fpCSV) != 0
     928           2 :         nRequested =
     929           2 :             static_cast<int>(nBytes) - static_cast<int>(VSIFTellL(fpCSV));
     930           2 :         if (nRequested <= 0)
     931           0 :             return nullptr;
     932           2 :         pszData = static_cast<char *>(VSI_MALLOC_VERBOSE(nRequested + 1));
     933           2 :         if (pszData == nullptr)
     934           0 :             return nullptr;
     935           2 :         nRead = VSIFReadL(pszData, 1, nRequested, fpCSV);
     936           2 :         pszData[nRead] = 0;
     937             : 
     938           2 :         osTmpMemFile = VSIMemGenerateHiddenFilename("temp.csv");
     939           2 :         fp = VSIFileFromMemBuffer(osTmpMemFile.c_str(),
     940             :                                   reinterpret_cast<GByte *>(pszData), nRead,
     941             :                                   FALSE);
     942             :     }
     943             : 
     944          50 :     std::vector<OGRFieldType> aeFieldType(nFieldCount);
     945          50 :     std::vector<int> abFieldBoolean(nFieldCount);
     946          50 :     std::vector<int> abFieldSet(nFieldCount);
     947          50 :     std::vector<int> abFinalTypeStringSet(nFieldCount);
     948          50 :     std::vector<int> anFieldWidth(nFieldCount);
     949          25 :     std::vector<int> anFieldPrecision(nFieldCount);
     950          25 :     int nStringFieldCount = 0;
     951             : 
     952          95 :     while (!fp->Eof() && !fp->Error())
     953             :     {
     954             :         char **papszTokens =
     955         170 :             CSVReadParseLine3L(fp, m_nMaxLineSize, szDelimiter,
     956             :                                true,  // bHonourStrings
     957          85 :                                bQuotedFieldAsString, bMergeDelimiter,
     958             :                                true  // bSkipBOM
     959             :             );
     960             :         // Can happen if we just reach EOF while trying to read new bytes.
     961          85 :         if (papszTokens == nullptr)
     962           0 :             break;
     963             : 
     964          85 :         if (bStreaming)
     965             :         {
     966             :             // Ignore last line if it is truncated.
     967          13 :             if (fp->Eof() && nRead == static_cast<size_t>(nRequested) &&
     968          13 :                 pszData[nRead - 1] != 13 && pszData[nRead - 1] != 10)
     969             :             {
     970           1 :                 CSLDestroy(papszTokens);
     971           1 :                 break;
     972             :             }
     973             :         }
     974          74 :         else if (VSIFTellL(fp) > nBytes)
     975             :         {
     976          13 :             CSLDestroy(papszTokens);
     977          13 :             break;
     978             :         }
     979             : 
     980        1192 :         for (int iField = 0;
     981        1192 :              iField < nFieldCount && papszTokens[iField] != nullptr; iField++)
     982             :         {
     983        1121 :             if (papszTokens[iField][0] == 0)
     984         482 :                 continue;
     985         695 :             if (abFinalTypeStringSet[iField] && !bAutodetectWidth)
     986          56 :                 continue;
     987         639 :             if (szDelimiter[0] == ';')
     988             :             {
     989           0 :                 char *chComma = strchr(papszTokens[iField], ',');
     990           0 :                 if (chComma)
     991           0 :                     *chComma = '.';
     992             :             }
     993         639 :             const CPLValueType eType = CPLGetValueType(papszTokens[iField]);
     994             : 
     995         639 :             if (bAutodetectWidth)
     996             :             {
     997         324 :                 int nFieldWidth = static_cast<int>(strlen(papszTokens[iField]));
     998         324 :                 if (papszTokens[iField][0] == '"' &&
     999           6 :                     papszTokens[iField][nFieldWidth - 1] == '"')
    1000             :                 {
    1001           6 :                     nFieldWidth -= 2;
    1002             :                 }
    1003         324 :                 int nFieldPrecision = 0;
    1004         324 :                 if (eType == CPL_VALUE_REAL && bAutodetectWidthForIntOrReal)
    1005             :                 {
    1006          40 :                     const char *pszDot = strchr(papszTokens[iField], '.');
    1007          40 :                     if (pszDot != nullptr)
    1008          40 :                         nFieldPrecision = static_cast<int>(strlen(pszDot + 1));
    1009             :                 }
    1010             : 
    1011         324 :                 if (nFieldWidth > anFieldWidth[iField])
    1012         205 :                     anFieldWidth[iField] = nFieldWidth;
    1013         324 :                 if (nFieldPrecision > anFieldPrecision[iField])
    1014          32 :                     anFieldPrecision[iField] = nFieldPrecision;
    1015             :             }
    1016             : 
    1017             :             OGRFieldType eOGRFieldType;
    1018         639 :             bool bIsBoolean = false;
    1019         639 :             if (eType == CPL_VALUE_INTEGER)
    1020             :             {
    1021         131 :                 GIntBig nVal = CPLAtoGIntBig(papszTokens[iField]);
    1022         131 :                 if (!CPL_INT64_FITS_ON_INT32(nVal))
    1023           6 :                     eOGRFieldType = OFTInteger64;
    1024             :                 else
    1025         125 :                     eOGRFieldType = OFTInteger;
    1026             :             }
    1027         508 :             else if (eType == CPL_VALUE_REAL ||
    1028         416 :                      EQUAL(papszTokens[iField], "inf") ||
    1029         415 :                      EQUAL(papszTokens[iField], "-inf") ||
    1030         414 :                      EQUAL(papszTokens[iField], "nan"))
    1031             :             {
    1032          95 :                 eOGRFieldType = OFTReal;
    1033             :             }
    1034         413 :             else if (abFieldSet[iField] && aeFieldType[iField] == OFTString)
    1035             :             {
    1036          38 :                 eOGRFieldType = OFTString;
    1037          38 :                 if (abFieldBoolean[iField])
    1038             :                 {
    1039          13 :                     abFieldBoolean[iField] =
    1040          21 :                         OGRCSVIsTrue(papszTokens[iField]) ||
    1041           8 :                         OGRCSVIsFalse(papszTokens[iField]);
    1042             :                 }
    1043             :             }
    1044             :             else
    1045             :             {
    1046             :                 OGRField sWrkField;
    1047         375 :                 CPLPushErrorHandler(CPLQuietErrorHandler);
    1048         375 :                 const bool bSuccess = CPL_TO_BOOL(
    1049         375 :                     OGRParseDate(papszTokens[iField], &sWrkField, 0));
    1050         375 :                 CPLPopErrorHandler();
    1051         375 :                 CPLErrorReset();
    1052         375 :                 if (bSuccess)
    1053             :                 {
    1054         209 :                     const bool bHasDate =
    1055         418 :                         strchr(papszTokens[iField], '/') != nullptr ||
    1056         209 :                         strchr(papszTokens[iField], '-') != nullptr;
    1057         209 :                     const bool bHasTime =
    1058         209 :                         strchr(papszTokens[iField], ':') != nullptr;
    1059         209 :                     if (bHasDate && bHasTime)
    1060          82 :                         eOGRFieldType = OFTDateTime;
    1061         127 :                     else if (bHasDate)
    1062          79 :                         eOGRFieldType = OFTDate;
    1063             :                     else
    1064          48 :                         eOGRFieldType = OFTTime;
    1065             :                 }
    1066             :                 else
    1067             :                 {
    1068         166 :                     eOGRFieldType = OFTString;
    1069         324 :                     bIsBoolean = OGRCSVIsTrue(papszTokens[iField]) ||
    1070         158 :                                  OGRCSVIsFalse(papszTokens[iField]);
    1071             :                 }
    1072             :             }
    1073             : 
    1074         166 :             const auto SetFinalStringType = [&abFinalTypeStringSet,
    1075             :                                              &aeFieldType, &nStringFieldCount,
    1076         664 :                                              iField]()
    1077             :             {
    1078         166 :                 if (!abFinalTypeStringSet[iField])
    1079             :                 {
    1080         166 :                     aeFieldType[iField] = OFTString;
    1081         166 :                     abFinalTypeStringSet[iField] = true;
    1082         166 :                     nStringFieldCount++;
    1083             :                 }
    1084         805 :             };
    1085             : 
    1086         639 :             if (!abFieldSet[iField])
    1087             :             {
    1088         356 :                 aeFieldType[iField] = eOGRFieldType;
    1089         356 :                 abFieldSet[iField] = TRUE;
    1090         356 :                 abFieldBoolean[iField] = bIsBoolean;
    1091         356 :                 if (eOGRFieldType == OFTString && !bIsBoolean)
    1092             :                 {
    1093          50 :                     SetFinalStringType();
    1094             :                 }
    1095             :             }
    1096         283 :             else if (aeFieldType[iField] != eOGRFieldType)
    1097             :             {
    1098             :                 // Promotion rules.
    1099         164 :                 if (aeFieldType[iField] == OFTInteger)
    1100             :                 {
    1101          39 :                     if (eOGRFieldType == OFTInteger64 ||
    1102             :                         eOGRFieldType == OFTReal)
    1103          16 :                         aeFieldType[iField] = eOGRFieldType;
    1104             :                     else
    1105             :                     {
    1106          23 :                         SetFinalStringType();
    1107             :                     }
    1108             :                 }
    1109         125 :                 else if (aeFieldType[iField] == OFTInteger64)
    1110             :                 {
    1111           2 :                     if (eOGRFieldType == OFTReal)
    1112           1 :                         aeFieldType[iField] = eOGRFieldType;
    1113           1 :                     else if (eOGRFieldType != OFTInteger)
    1114             :                     {
    1115           0 :                         SetFinalStringType();
    1116             :                     }
    1117             :                 }
    1118         123 :                 else if (aeFieldType[iField] == OFTReal)
    1119             :                 {
    1120          35 :                     if (eOGRFieldType != OFTInteger &&
    1121             :                         eOGRFieldType != OFTInteger64)
    1122             :                     {
    1123          20 :                         SetFinalStringType();
    1124             :                     }
    1125             :                 }
    1126          88 :                 else if (aeFieldType[iField] == OFTDate)
    1127             :                 {
    1128          31 :                     if (eOGRFieldType == OFTDateTime)
    1129          14 :                         aeFieldType[iField] = OFTDateTime;
    1130             :                     else
    1131             :                     {
    1132          17 :                         SetFinalStringType();
    1133             :                     }
    1134             :                 }
    1135          57 :                 else if (aeFieldType[iField] == OFTDateTime)
    1136             :                 {
    1137          40 :                     if (eOGRFieldType != OFTDate)
    1138             :                     {
    1139          26 :                         SetFinalStringType();
    1140             :                     }
    1141             :                 }
    1142          17 :                 else if (aeFieldType[iField] == OFTTime)
    1143             :                 {
    1144          17 :                     SetFinalStringType();
    1145             :                 }
    1146             :             }
    1147         213 :             else if (!abFinalTypeStringSet[iField] &&
    1148         213 :                      eOGRFieldType == OFTString && !bIsBoolean)
    1149             :             {
    1150          13 :                 SetFinalStringType();
    1151             :             }
    1152             :         }
    1153             : 
    1154          71 :         CSLDestroy(papszTokens);
    1155             : 
    1156             :         // If all fields are String and we don't need to compute width,
    1157             :         // just stop auto-detection now.
    1158          71 :         if (nStringFieldCount == nFieldCount && !bAutodetectWidth)
    1159             :         {
    1160           1 :             CPLDebugOnly("CSV",
    1161             :                          "AutodetectFieldTypes() stopped after "
    1162             :                          "reading " CPL_FRMT_GUIB " bytes",
    1163             :                          static_cast<GUIntBig>(VSIFTellL(fp)));
    1164           1 :             break;
    1165             :         }
    1166             :     }
    1167             : 
    1168             :     papszFieldTypes =
    1169          25 :         static_cast<char **>(CPLCalloc(nFieldCount + 1, sizeof(char *)));
    1170         402 :     for (int iField = 0; iField < nFieldCount; iField++)
    1171             :     {
    1172         377 :         CPLString osFieldType;
    1173         377 :         if (!abFieldSet[iField])
    1174          21 :             osFieldType = "String";
    1175         356 :         else if (aeFieldType[iField] == OFTInteger)
    1176          42 :             osFieldType = "Integer";
    1177         314 :         else if (aeFieldType[iField] == OFTInteger64)
    1178           3 :             osFieldType = "Integer64";
    1179         311 :         else if (aeFieldType[iField] == OFTReal)
    1180          52 :             osFieldType = "Real";
    1181         259 :         else if (aeFieldType[iField] == OFTDateTime)
    1182          56 :             osFieldType = "DateTime";
    1183         203 :         else if (aeFieldType[iField] == OFTDate)
    1184          20 :             osFieldType = "Date";
    1185         183 :         else if (aeFieldType[iField] == OFTTime)
    1186          17 :             osFieldType = "Time";
    1187         166 :         else if (aeFieldType[iField] == OFTStringList)
    1188           0 :             osFieldType = "JSonStringList";
    1189         166 :         else if (aeFieldType[iField] == OFTIntegerList)
    1190           0 :             osFieldType = "JSonIntegerList";
    1191         166 :         else if (aeFieldType[iField] == OFTInteger64List)
    1192           0 :             osFieldType = "JSonInteger64List";
    1193         166 :         else if (aeFieldType[iField] == OFTRealList)
    1194           0 :             osFieldType = "JSonRealList";
    1195         166 :         else if (abFieldBoolean[iField])
    1196          10 :             osFieldType = "Integer(Boolean)";
    1197             :         else
    1198         156 :             osFieldType = "String";
    1199             : 
    1200         377 :         if (!abFieldBoolean[iField])
    1201             :         {
    1202         541 :             if (anFieldWidth[iField] > 0 &&
    1203         174 :                 (aeFieldType[iField] == OFTString ||
    1204          95 :                  (bAutodetectWidthForIntOrReal &&
    1205          95 :                   (aeFieldType[iField] == OFTInteger ||
    1206          72 :                    aeFieldType[iField] == OFTInteger64))))
    1207             :             {
    1208          91 :                 osFieldType += CPLSPrintf(" (%d)", anFieldWidth[iField]);
    1209             :             }
    1210         348 :             else if (anFieldWidth[iField] > 0 && bAutodetectWidthForIntOrReal &&
    1211          72 :                      aeFieldType[iField] == OFTReal)
    1212             :             {
    1213          24 :                 osFieldType += CPLSPrintf(" (%d.%d)", anFieldWidth[iField],
    1214          24 :                                           anFieldPrecision[iField]);
    1215             :             }
    1216             :         }
    1217             : 
    1218         377 :         papszFieldTypes[iField] = CPLStrdup(osFieldType);
    1219             :     }
    1220             : 
    1221          25 :     if (bStreaming)
    1222             :     {
    1223           2 :         VSIFCloseL(fp);
    1224           2 :         VSIUnlink(osTmpMemFile.c_str());
    1225           2 :         VSIFree(pszData);
    1226             :     }
    1227             : 
    1228          25 :     ResetReading();
    1229             : 
    1230          25 :     return papszFieldTypes;
    1231             : }
    1232             : 
    1233             : /************************************************************************/
    1234             : /*                            ~OGRCSVLayer()                            */
    1235             : /************************************************************************/
    1236             : 
    1237        1624 : OGRCSVLayer::~OGRCSVLayer()
    1238             : 
    1239             : {
    1240         812 :     if (m_nFeaturesRead > 0)
    1241             :     {
    1242         974 :         CPLDebug("CSV", "%d features read on layer '%s'.",
    1243         487 :                  static_cast<int>(m_nFeaturesRead), m_poFeatureDefn->GetName());
    1244             :     }
    1245             : 
    1246             :     // Make sure the header file is written even if no features are written.
    1247         812 :     if (bNew && bInWriteMode)
    1248          27 :         WriteHeader();
    1249             : 
    1250         812 :     CPLFree(panGeomFieldIndex);
    1251             : 
    1252         812 :     CPLFree(pszFilename);
    1253             : 
    1254         812 :     if (fpCSV)
    1255         812 :         VSIFCloseL(fpCSV);
    1256        1624 : }
    1257             : 
    1258             : /************************************************************************/
    1259             : /*                            ResetReading()                            */
    1260             : /************************************************************************/
    1261             : 
    1262        3875 : void OGRCSVLayer::ResetReading()
    1263             : 
    1264             : {
    1265        3875 :     if (fpCSV)
    1266        3875 :         VSIRewindL(fpCSV);
    1267             : 
    1268        3875 :     if (bHasFieldNames)
    1269        3744 :         CSLDestroy(CSVReadParseLine3L(fpCSV, m_nMaxLineSize, szDelimiter,
    1270        3744 :                                       bHonourStrings,
    1271             :                                       false,  // bKeepLeadingAndClosingQuotes
    1272             :                                       false,  // bMergeDelimiter,
    1273             :                                       true    // bSkipBOM
    1274             :                                       ));
    1275             : 
    1276        3875 :     bNeedRewindBeforeRead = false;
    1277             : 
    1278        3875 :     m_nNextFID = FID_INITIAL_VALUE;
    1279        3875 : }
    1280             : 
    1281             : /************************************************************************/
    1282             : /*                         GetNextLineTokens()                          */
    1283             : /************************************************************************/
    1284             : 
    1285       49766 : char **OGRCSVLayer::GetNextLineTokens()
    1286             : {
    1287             :     while (true)
    1288             :     {
    1289             :         // Read the CSV record.
    1290       99532 :         char **papszTokens = CSVReadParseLine3L(
    1291       49766 :             fpCSV, m_nMaxLineSize, szDelimiter, bHonourStrings,
    1292             :             false,  // bKeepLeadingAndClosingQuotes
    1293       49766 :             bMergeDelimiter,
    1294             :             true  // bSkipBOM
    1295             :         );
    1296       49766 :         if (papszTokens == nullptr)
    1297        1463 :             return nullptr;
    1298             : 
    1299       48303 :         if (papszTokens[0] != nullptr)
    1300       48303 :             return papszTokens;
    1301             : 
    1302           0 :         CSLDestroy(papszTokens);
    1303           0 :     }
    1304             : }
    1305             : 
    1306             : /************************************************************************/
    1307             : /*                             GetFeature()                             */
    1308             : /************************************************************************/
    1309             : 
    1310          59 : OGRFeature *OGRCSVLayer::GetFeature(GIntBig nFID)
    1311             : {
    1312          59 :     if (nFID < FID_INITIAL_VALUE || fpCSV == nullptr)
    1313           7 :         return nullptr;
    1314          52 :     if (nFID < m_nNextFID || bNeedRewindBeforeRead)
    1315          20 :         ResetReading();
    1316         109 :     while (m_nNextFID < nFID)
    1317             :     {
    1318          71 :         char **papszTokens = GetNextLineTokens();
    1319          71 :         if (papszTokens == nullptr)
    1320          14 :             return nullptr;
    1321          57 :         CSLDestroy(papszTokens);
    1322          57 :         m_nNextFID++;
    1323             :     }
    1324          38 :     return GetNextUnfilteredFeature();
    1325             : }
    1326             : 
    1327             : /************************************************************************/
    1328             : /*                      GetNextUnfilteredFeature()                      */
    1329             : /************************************************************************/
    1330             : 
    1331       48849 : OGRFeature *OGRCSVLayer::GetNextUnfilteredFeature()
    1332             : 
    1333             : {
    1334       48849 :     if (fpCSV == nullptr)
    1335           0 :         return nullptr;
    1336             : 
    1337             :     // Read the CSV record.
    1338       48849 :     char **papszTokens = GetNextLineTokens();
    1339       48849 :     if (papszTokens == nullptr)
    1340        1419 :         return nullptr;
    1341             : 
    1342             :     // Create the OGR feature.
    1343       47430 :     OGRFeature *poFeature = new OGRFeature(m_poFeatureDefn.get());
    1344             : 
    1345             :     // Set attributes for any indicated attribute records.
    1346       47430 :     int iOGRField = 0;
    1347             :     const int nAttrCount = std::min(
    1348       47430 :         CSLCount(papszTokens), nCSVFieldCount + (bHiddenWKTColumn ? 1 : 0));
    1349             : 
    1350      262783 :     for (int iAttr = 0; !bIsEurostatTSV && iAttr < nAttrCount; iAttr++)
    1351             :     {
    1352             : 
    1353             :         // Skip deleted fields if OGR_SCHEMA with schemaType=Full was specified and fields were removed
    1354      215353 :         if (OGRCSVDataSource *poCsvDs = static_cast<OGRCSVDataSource *>(m_poDS))
    1355             :         {
    1356      215353 :             if (!poCsvDs->DeletedFieldIndexes().empty())
    1357             :             {
    1358             :                 const auto &deletedFieldIndexes =
    1359          14 :                     poCsvDs->DeletedFieldIndexes();
    1360          24 :                 if (std::find(deletedFieldIndexes.cbegin(),
    1361             :                               deletedFieldIndexes.cend(),
    1362          14 :                               iAttr) != deletedFieldIndexes.cend())
    1363             :                 {
    1364          40 :                     continue;
    1365             :                 }
    1366             :             }
    1367             :         }
    1368             : 
    1369      215343 :         if ((iAttr == iLongitudeField || iAttr == iLatitudeField ||
    1370      214465 :              iAttr == iZField) &&
    1371        1282 :             !bKeepGeomColumns)
    1372             :         {
    1373          14 :             continue;
    1374             :         }
    1375      215329 :         int iGeom = 0;
    1376      215329 :         if (bHiddenWKTColumn)
    1377             :         {
    1378          18 :             if (iAttr != 0)
    1379           9 :                 iGeom = panGeomFieldIndex[iAttr - 1];
    1380             :         }
    1381             :         else
    1382             :         {
    1383      215311 :             iGeom = panGeomFieldIndex[iAttr];
    1384             :         }
    1385      215329 :         if (iGeom >= 0)
    1386             :         {
    1387             :             const OGRGeomFieldDefn *poGeomFieldDefn =
    1388        3622 :                 m_poFeatureDefn->GetGeomFieldDefn(iGeom);
    1389        6454 :             if (papszTokens[iAttr][0] != '\0' &&
    1390        2832 :                 !(poGeomFieldDefn->IsIgnored()))
    1391             :             {
    1392        2242 :                 const char *pszStr = papszTokens[iAttr];
    1393        2242 :                 while (*pszStr == ' ')
    1394           0 :                     pszStr++;
    1395        4484 :                 std::unique_ptr<OGRGeometry> poGeom = nullptr;
    1396             :                 OGRErr eErr;
    1397             : 
    1398        2242 :                 if (EQUAL(poGeomFieldDefn->GetNameRef(), ""))
    1399             :                 {
    1400         356 :                     std::tie(poGeom, eErr) =
    1401         712 :                         OGRGeometryFactory::createFromWkt(pszStr);
    1402         356 :                     if (eErr != OGRERR_NONE)
    1403             :                     {
    1404           1 :                         CPLError(CE_Warning, CPLE_AppDefined,
    1405             :                                  "Ignoring invalid WKT: %s", pszStr);
    1406             :                     }
    1407             :                 }
    1408             :                 else
    1409             :                 {
    1410        3772 :                     CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    1411             : 
    1412        1886 :                     std::tie(poGeom, eErr) =
    1413        3772 :                         OGRGeometryFactory::createFromWkt(pszStr);
    1414             : 
    1415        1886 :                     if (!poGeom && *pszStr == '{')
    1416             :                     {
    1417           1 :                         poGeom.reset(OGRGeometry::FromHandle(
    1418             :                             OGR_G_CreateGeometryFromJson(pszStr)));
    1419             :                     }
    1420        1886 :                     else if (!poGeom && ((*pszStr >= '0' && *pszStr <= '9') ||
    1421           1 :                                          (*pszStr >= 'a' && *pszStr <= 'z') ||
    1422           0 :                                          (*pszStr >= 'A' && *pszStr <= 'Z')))
    1423             :                     {
    1424           5 :                         poGeom.reset(
    1425             :                             OGRGeometryFromHexEWKB(pszStr, nullptr, FALSE));
    1426             :                     }
    1427             :                 }
    1428             : 
    1429        2242 :                 if (poGeom)
    1430             :                 {
    1431        4480 :                     poGeom->assignSpatialReference(
    1432        2240 :                         poGeomFieldDefn->GetSpatialRef());
    1433        2240 :                     poFeature->SetGeomField(iGeom, std::move(poGeom));
    1434             :                 }
    1435             :             }
    1436             : 
    1437        3622 :             const bool bHasAttributeField =
    1438        3622 :                 bKeepGeomColumns && !(iAttr == 0 && bHiddenWKTColumn);
    1439        3622 :             if (!bHasAttributeField)
    1440          16 :                 continue;
    1441             :         }
    1442             : 
    1443             :         const OGRFieldDefn *poFieldDefn =
    1444      215313 :             m_poFeatureDefn->GetFieldDefn(iOGRField);
    1445      215313 :         const OGRFieldType eFieldType = poFieldDefn->GetType();
    1446      215313 :         const OGRFieldSubType eFieldSubType = poFieldDefn->GetSubType();
    1447             : 
    1448          41 :         const auto WarnOnceBadValue = [this, poFieldDefn]()
    1449             :         {
    1450          23 :             if (!bWarningBadTypeOrWidth)
    1451             :             {
    1452           9 :                 bWarningBadTypeOrWidth = true;
    1453           9 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1454             :                          "Invalid value type found in record %" PRId64
    1455             :                          " for field %s. "
    1456             :                          "This warning will no longer be emitted",
    1457             :                          m_nNextFID, poFieldDefn->GetNameRef());
    1458             :             };
    1459      215336 :         };
    1460             : 
    1461          36 :         const auto WarnTooLargeWidth = [this, poFieldDefn]()
    1462             :         {
    1463          12 :             if (!bWarningBadTypeOrWidth)
    1464             :             {
    1465          12 :                 bWarningBadTypeOrWidth = true;
    1466          12 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1467             :                          "Value with a width greater than field width "
    1468             :                          "found in record %" PRId64 " for field %s. "
    1469             :                          "This warning will no longer be emitted",
    1470             :                          m_nNextFID, poFieldDefn->GetNameRef());
    1471             :             };
    1472      215325 :         };
    1473             : 
    1474      215313 :         if (eFieldType == OFTInteger && eFieldSubType == OFSTBoolean)
    1475             :         {
    1476          14 :             if (papszTokens[iAttr][0] != '\0' && !poFieldDefn->IsIgnored())
    1477             :             {
    1478          21 :                 if (OGRCSVIsTrue(papszTokens[iAttr]) ||
    1479           7 :                     strcmp(papszTokens[iAttr], "1") == 0)
    1480             :                 {
    1481           8 :                     poFeature->SetField(iOGRField, 1);
    1482             :                 }
    1483           7 :                 else if (OGRCSVIsFalse(papszTokens[iAttr]) ||
    1484           1 :                          strcmp(papszTokens[iAttr], "0") == 0)
    1485             :                 {
    1486           5 :                     poFeature->SetField(iOGRField, 0);
    1487             :                 }
    1488             :                 else
    1489             :                 {
    1490             :                     // Set to TRUE because it's different than 0 but emit a warning
    1491           1 :                     poFeature->SetField(iOGRField, 1);
    1492           1 :                     WarnOnceBadValue();
    1493             :                 }
    1494             :             }
    1495             :         }
    1496      215299 :         else if (eFieldType == OFTInteger || eFieldType == OFTInteger64)
    1497             :         {
    1498         228 :             if (papszTokens[iAttr][0] != '\0' && !poFieldDefn->IsIgnored())
    1499             :             {
    1500         161 :                 char *endptr = nullptr;
    1501             :                 const GIntBig nVal = static_cast<GIntBig>(
    1502         161 :                     std::strtoll(papszTokens[iAttr], &endptr, 10));
    1503         161 :                 if (endptr == papszTokens[iAttr] + strlen(papszTokens[iAttr]))
    1504             :                 {
    1505         148 :                     poFeature->SetField(iOGRField, nVal);
    1506         141 :                     if (!bWarningBadTypeOrWidth &&
    1507         289 :                         poFieldDefn->GetWidth() > 0 &&
    1508          17 :                         static_cast<int>(strlen(papszTokens[iAttr])) >
    1509          17 :                             poFieldDefn->GetWidth())
    1510             :                     {
    1511           1 :                         WarnTooLargeWidth();
    1512             :                     }
    1513             :                 }
    1514             :                 else
    1515             :                 {
    1516          13 :                     WarnOnceBadValue();
    1517             :                 }
    1518         228 :             }
    1519             :         }
    1520      215071 :         else if (eFieldType == OFTReal)
    1521             :         {
    1522        3424 :             if (papszTokens[iAttr][0] != '\0' && !poFieldDefn->IsIgnored())
    1523             :             {
    1524        2945 :                 char *chComma = strchr(papszTokens[iAttr], ',');
    1525        2945 :                 if (chComma)
    1526           5 :                     *chComma = '.';
    1527        2945 :                 char *endptr = nullptr;
    1528             :                 const double dfVal =
    1529        2945 :                     CPLStrtodDelim(papszTokens[iAttr], &endptr, '.');
    1530        2945 :                 if (endptr == papszTokens[iAttr] + strlen(papszTokens[iAttr]))
    1531             :                 {
    1532        2938 :                     poFeature->SetField(iOGRField, dfVal);
    1533        2933 :                     if (!bWarningBadTypeOrWidth &&
    1534        5871 :                         poFieldDefn->GetWidth() > 0 &&
    1535          53 :                         static_cast<int>(strlen(papszTokens[iAttr])) >
    1536          53 :                             poFieldDefn->GetWidth())
    1537             :                     {
    1538          10 :                         WarnTooLargeWidth();
    1539             :                     }
    1540        5851 :                     else if (!bWarningBadTypeOrWidth &&
    1541        2923 :                              poFieldDefn->GetWidth() > 0)
    1542             :                     {
    1543          43 :                         const char *pszDot = strchr(papszTokens[iAttr], '.');
    1544          43 :                         const int nPrecision =
    1545             :                             pszDot != nullptr
    1546          43 :                                 ? static_cast<int>(strlen(pszDot + 1))
    1547             :                                 : 0;
    1548          43 :                         if (nPrecision > poFieldDefn->GetPrecision())
    1549             :                         {
    1550           1 :                             bWarningBadTypeOrWidth = true;
    1551           1 :                             CPLError(CE_Warning, CPLE_AppDefined,
    1552             :                                      "Value with a precision greater than "
    1553             :                                      "field precision found in record %" PRId64
    1554             :                                      " for field %s. "
    1555             :                                      "This warning will no longer be emitted",
    1556             :                                      m_nNextFID, poFieldDefn->GetNameRef());
    1557             :                         }
    1558             :                     }
    1559             :                 }
    1560             :                 else
    1561             :                 {
    1562           7 :                     WarnOnceBadValue();
    1563             :                 }
    1564             :             }
    1565             :         }
    1566      211647 :         else if (eFieldType != OFTString)
    1567             :         {
    1568         224 :             if (papszTokens[iAttr][0] != '\0' && !poFieldDefn->IsIgnored())
    1569             :             {
    1570         115 :                 poFeature->SetField(iOGRField, papszTokens[iAttr]);
    1571         220 :                 if (!bWarningBadTypeOrWidth &&
    1572         105 :                     !poFeature->IsFieldSetAndNotNull(iOGRField))
    1573             :                 {
    1574           2 :                     WarnOnceBadValue();
    1575             :                 }
    1576             :             }
    1577             :         }
    1578      211423 :         else if (!poFieldDefn->IsIgnored())
    1579             :         {
    1580      209749 :             if (bEmptyStringNull && papszTokens[iAttr][0] == '\0')
    1581             :             {
    1582           1 :                 poFeature->SetFieldNull(iOGRField);
    1583             :             }
    1584             :             else
    1585             :             {
    1586      209748 :                 poFeature->SetField(iOGRField, papszTokens[iAttr]);
    1587      209793 :                 if (!bWarningBadTypeOrWidth && poFieldDefn->GetWidth() > 0 &&
    1588          45 :                     static_cast<int>(strlen(papszTokens[iAttr])) >
    1589          45 :                         poFieldDefn->GetWidth())
    1590             :                 {
    1591           1 :                     WarnTooLargeWidth();
    1592             :                 }
    1593             :             }
    1594             :         }
    1595             : 
    1596      215313 :         if (bKeepSourceColumns && eFieldType != OFTString)
    1597             :         {
    1598          11 :             iOGRField++;
    1599          21 :             if (papszTokens[iAttr][0] != '\0' &&
    1600          10 :                 !m_poFeatureDefn->GetFieldDefn(iOGRField)->IsIgnored())
    1601             :             {
    1602          10 :                 poFeature->SetField(iOGRField, papszTokens[iAttr]);
    1603             :             }
    1604             :         }
    1605             : 
    1606      215313 :         iOGRField++;
    1607             :     }
    1608             : 
    1609             :     // Eurostat TSV files.
    1610             : 
    1611       47434 :     for (int iAttr = 0; bIsEurostatTSV && iAttr < nAttrCount; iAttr++)
    1612             :     {
    1613           4 :         if (iAttr == 0)
    1614             :         {
    1615           1 :             char **papszDims = CSLTokenizeString2(papszTokens[0], ",", 0);
    1616           1 :             if (CSLCount(papszDims) != nEurostatDims)
    1617             :             {
    1618           0 :                 CSLDestroy(papszDims);
    1619           0 :                 break;
    1620             :             }
    1621           3 :             for (int iSubAttr = 0; iSubAttr < nEurostatDims; iSubAttr++)
    1622             :             {
    1623           2 :                 if (!m_poFeatureDefn->GetFieldDefn(iSubAttr)->IsIgnored())
    1624           2 :                     poFeature->SetField(iSubAttr, papszDims[iSubAttr]);
    1625             :             }
    1626           1 :             CSLDestroy(papszDims);
    1627             :         }
    1628             :         else
    1629             :         {
    1630           3 :             char **papszVals = CSLTokenizeString2(papszTokens[iAttr], " ", 0);
    1631           3 :             CPLValueType eType = CPLGetValueType(papszVals[0]);
    1632           3 :             if ((papszVals[0] && papszVals[0][0] != '\0') &&
    1633           2 :                 (eType == CPL_VALUE_INTEGER || eType == CPL_VALUE_REAL))
    1634             :             {
    1635           2 :                 if (!m_poFeatureDefn
    1636           2 :                          ->GetFieldDefn(nEurostatDims + 2 * (iAttr - 1))
    1637           2 :                          ->IsIgnored())
    1638           2 :                     poFeature->SetField(nEurostatDims + 2 * (iAttr - 1),
    1639             :                                         papszVals[0]);
    1640             :             }
    1641           3 :             if (CSLCount(papszVals) == 2)
    1642             :             {
    1643           1 :                 if (!m_poFeatureDefn
    1644           1 :                          ->GetFieldDefn(nEurostatDims + 2 * (iAttr - 1) + 1)
    1645           1 :                          ->IsIgnored())
    1646           1 :                     poFeature->SetField(nEurostatDims + 2 * (iAttr - 1) + 1,
    1647           1 :                                         papszVals[1]);
    1648             :             }
    1649           3 :             CSLDestroy(papszVals);
    1650             :         }
    1651             :     }
    1652             : 
    1653             :     // Is it a numeric value parsable by local-aware CPLAtofM()
    1654        1271 :     const auto IsCPLAtofMParsable = [](char *pszVal)
    1655             :     {
    1656        1271 :         auto l_eType = CPLGetValueType(pszVal);
    1657        1271 :         if (l_eType == CPL_VALUE_INTEGER || l_eType == CPL_VALUE_REAL)
    1658        1271 :             return true;
    1659           0 :         char *pszComma = strchr(pszVal, ',');
    1660           0 :         if (pszComma)
    1661             :         {
    1662           0 :             *pszComma = '.';
    1663           0 :             l_eType = CPLGetValueType(pszVal);
    1664           0 :             *pszComma = ',';
    1665             :         }
    1666           0 :         return l_eType == CPL_VALUE_REAL;
    1667             :     };
    1668             : 
    1669             :     // http://www.faa.gov/airports/airport_safety/airportdata_5010/menu/index.cfm
    1670             :     // specific
    1671             : 
    1672       47430 :     if (iNfdcLatitudeS != -1 && iNfdcLongitudeS != -1 &&
    1673           0 :         nAttrCount > iNfdcLatitudeS && nAttrCount > iNfdcLongitudeS &&
    1674           0 :         papszTokens[iNfdcLongitudeS][0] != 0 &&
    1675           0 :         papszTokens[iNfdcLatitudeS][0] != 0)
    1676             :     {
    1677             :         const double dfLon =
    1678           0 :             CPLAtof(papszTokens[iNfdcLongitudeS]) / 3600.0 *
    1679           0 :             (strchr(papszTokens[iNfdcLongitudeS], 'W') ? -1.0 : 1.0);
    1680             :         const double dfLat =
    1681           0 :             CPLAtof(papszTokens[iNfdcLatitudeS]) / 3600.0 *
    1682           0 :             (strchr(papszTokens[iNfdcLatitudeS], 'S') ? -1.0 : 1.0);
    1683           0 :         if (!m_poFeatureDefn->GetGeomFieldDefn(0)->IsIgnored())
    1684           0 :             poFeature->SetGeometryDirectly(new OGRPoint(dfLon, dfLat));
    1685             :     }
    1686             : 
    1687         439 :     else if (iLatitudeField != -1 && iLongitudeField != -1 &&
    1688         439 :              nAttrCount > iLatitudeField && nAttrCount > iLongitudeField &&
    1689         439 :              papszTokens[iLongitudeField][0] != 0 &&
    1690         434 :              papszTokens[iLatitudeField][0] != 0 &&
    1691       48303 :              IsCPLAtofMParsable(papszTokens[iLongitudeField]) &&
    1692         434 :              IsCPLAtofMParsable(papszTokens[iLatitudeField]))
    1693             :     {
    1694         434 :         if (!m_bIsGNIS ||
    1695             :             // GNIS specific: some records have dummy 0,0 value.
    1696           0 :             (papszTokens[iLongitudeField][0] != DIGIT_ZERO ||
    1697           0 :              papszTokens[iLongitudeField][1] != '\0' ||
    1698           0 :              papszTokens[iLatitudeField][0] != DIGIT_ZERO ||
    1699           0 :              papszTokens[iLatitudeField][1] != '\0'))
    1700             :         {
    1701         434 :             const double dfLon = CPLAtofM(papszTokens[iLongitudeField]);
    1702         434 :             const double dfLat = CPLAtofM(papszTokens[iLatitudeField]);
    1703         434 :             if (!m_poFeatureDefn->GetGeomFieldDefn(0)->IsIgnored())
    1704             :             {
    1705         404 :                 if (iZField != -1 && nAttrCount > iZField &&
    1706        1241 :                     papszTokens[iZField][0] != 0 &&
    1707         403 :                     IsCPLAtofMParsable(papszTokens[iZField]))
    1708         403 :                     poFeature->SetGeometryDirectly(new OGRPoint(
    1709         403 :                         dfLon, dfLat, CPLAtofM(papszTokens[iZField])));
    1710             :                 else
    1711          31 :                     poFeature->SetGeometryDirectly(new OGRPoint(dfLon, dfLat));
    1712             :             }
    1713             :         }
    1714             :     }
    1715             : 
    1716       47430 :     CSLDestroy(papszTokens);
    1717             : 
    1718       47430 :     if ((m_nNextFID % 100000) == 0)
    1719             :     {
    1720           0 :         CPLDebug("CSV", "FID = %" PRId64 ", file offset = %" PRIu64, m_nNextFID,
    1721           0 :                  static_cast<uint64_t>(fpCSV->Tell()));
    1722             :     }
    1723             : 
    1724             :     // Translate the record id.
    1725       47430 :     poFeature->SetFID(m_nNextFID++);
    1726             : 
    1727       47430 :     m_nFeaturesRead++;
    1728             : 
    1729       47430 :     return poFeature;
    1730             : }
    1731             : 
    1732             : /************************************************************************/
    1733             : /*                           GetNextFeature()                           */
    1734             : /************************************************************************/
    1735             : 
    1736       48128 : OGRFeature *OGRCSVLayer::GetNextFeature()
    1737             : 
    1738             : {
    1739       48128 :     if (bNeedRewindBeforeRead)
    1740           5 :         ResetReading();
    1741             : 
    1742             :     // Read features till we find one that satisfies our current
    1743             :     // spatial criteria.
    1744             :     while (true)
    1745             :     {
    1746       48811 :         OGRFeature *poFeature = GetNextUnfilteredFeature();
    1747       48811 :         if (poFeature == nullptr)
    1748        1417 :             return nullptr;
    1749             : 
    1750       95131 :         if ((m_poFilterGeom == nullptr ||
    1751       94682 :              FilterGeometry(poFeature->GetGeomFieldRef(m_iGeomFieldFilter))) &&
    1752       47288 :             (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
    1753       46711 :             return poFeature;
    1754             : 
    1755         683 :         delete poFeature;
    1756         683 :     }
    1757             : }
    1758             : 
    1759             : /************************************************************************/
    1760             : /*                           TestCapability()                           */
    1761             : /************************************************************************/
    1762             : 
    1763        3759 : int OGRCSVLayer::TestCapability(const char *pszCap) const
    1764             : 
    1765             : {
    1766        3759 :     if (EQUAL(pszCap, OLCSequentialWrite))
    1767         354 :         return bInWriteMode && !bKeepSourceColumns && bKeepGeomColumns;
    1768        3405 :     else if (EQUAL(pszCap, OLCCreateField))
    1769         983 :         return bNew && !bHasFieldNames;
    1770        2422 :     else if (EQUAL(pszCap, OLCCreateGeomField))
    1771          72 :         return bNew && !bHasFieldNames &&
    1772          72 :                eGeometryFormat == OGR_CSV_GEOM_AS_WKT;
    1773        2386 :     else if (EQUAL(pszCap, OLCIgnoreFields))
    1774        1053 :         return TRUE;
    1775        1333 :     else if (EQUAL(pszCap, OLCCurveGeometries))
    1776         165 :         return TRUE;
    1777        1168 :     else if (EQUAL(pszCap, OLCMeasuredGeometries))
    1778         353 :         return TRUE;
    1779         815 :     else if (EQUAL(pszCap, OLCZGeometries))
    1780          56 :         return TRUE;
    1781             :     else
    1782         759 :         return FALSE;
    1783             : }
    1784             : 
    1785             : /************************************************************************/
    1786             : /*                           PreCreateField()                           */
    1787             : /************************************************************************/
    1788             : 
    1789             : OGRCSVCreateFieldAction
    1790         984 : OGRCSVLayer::PreCreateField(OGRFeatureDefn *poFeatureDefn,
    1791             :                             const std::set<CPLString> &oSetFields,
    1792             :                             const OGRFieldDefn *poNewField, int bApproxOK)
    1793             : {
    1794             :     // Does this duplicate an existing field?
    1795         984 :     if (oSetFields.find(CPLString(poNewField->GetNameRef()).toupper()) !=
    1796        1968 :         oSetFields.end())
    1797             :     {
    1798           6 :         if (poFeatureDefn->GetGeomFieldIndex(poNewField->GetNameRef()) >= 0 ||
    1799           3 :             poFeatureDefn->GetGeomFieldIndex(
    1800           3 :                 CPLSPrintf("geom_%s", poNewField->GetNameRef())) >= 0)
    1801             :         {
    1802           2 :             return CREATE_FIELD_DO_NOTHING;
    1803             :         }
    1804           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1805             :                  "Attempt to create field %s, "
    1806             :                  "but a field with this name already exists.",
    1807             :                  poNewField->GetNameRef());
    1808             : 
    1809           1 :         return CREATE_FIELD_ERROR;
    1810             :     }
    1811             : 
    1812             :     // Is this a legal field type for CSV?
    1813         981 :     switch (poNewField->GetType())
    1814             :     {
    1815         981 :         case OFTInteger:
    1816             :         case OFTInteger64:
    1817             :         case OFTReal:
    1818             :         case OFTString:
    1819             :         case OFTIntegerList:
    1820             :         case OFTInteger64List:
    1821             :         case OFTRealList:
    1822             :         case OFTStringList:
    1823             :         case OFTTime:
    1824             :         case OFTDate:
    1825             :         case OFTDateTime:
    1826             :             // These types are OK.
    1827         981 :             break;
    1828             : 
    1829           0 :         default:
    1830           0 :             if (bApproxOK)
    1831             :             {
    1832           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1833             :                          "Attempt to create field of type %s, but this is not "
    1834             :                          "supported "
    1835             :                          "for .csv files.  Just treating as a plain string.",
    1836             :                          poNewField->GetFieldTypeName(poNewField->GetType()));
    1837             :             }
    1838             :             else
    1839             :             {
    1840           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1841             :                          "Attempt to create field of type %s, but this is not "
    1842             :                          "supported "
    1843             :                          "for .csv files.",
    1844             :                          poNewField->GetFieldTypeName(poNewField->GetType()));
    1845           0 :                 return CREATE_FIELD_ERROR;
    1846             :             }
    1847             :     }
    1848         981 :     return CREATE_FIELD_PROCEED;
    1849             : }
    1850             : 
    1851             : /************************************************************************/
    1852             : /*                            CreateField()                             */
    1853             : /************************************************************************/
    1854             : 
    1855         503 : OGRErr OGRCSVLayer::CreateField(const OGRFieldDefn *poNewField, int bApproxOK)
    1856             : 
    1857             : {
    1858             :     // If we have already written our field names, then we are not
    1859             :     // allowed to add new fields.
    1860         503 :     if (!TestCapability(OLCCreateField))
    1861             :     {
    1862           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1863             :                  "Unable to create new fields after first feature written.");
    1864           1 :         return OGRERR_FAILURE;
    1865             :     }
    1866             : 
    1867         502 :     if (nCSVFieldCount >= 10000)
    1868             :     {
    1869           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Limiting to 10000 fields");
    1870           0 :         return OGRERR_FAILURE;
    1871             :     }
    1872             : 
    1873         502 :     if (m_oSetFields.empty())
    1874             :     {
    1875         138 :         for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
    1876             :         {
    1877             :             m_oSetFields.insert(
    1878          28 :                 CPLString(m_poFeatureDefn->GetFieldDefn(i)->GetNameRef())
    1879          28 :                     .toupper());
    1880             :         }
    1881             :     }
    1882             : 
    1883         502 :     const OGRCSVCreateFieldAction eAction = PreCreateField(
    1884         502 :         m_poFeatureDefn.get(), m_oSetFields, poNewField, bApproxOK);
    1885         502 :     if (eAction == CREATE_FIELD_DO_NOTHING)
    1886           2 :         return OGRERR_NONE;
    1887         500 :     if (eAction == CREATE_FIELD_ERROR)
    1888           0 :         return OGRERR_FAILURE;
    1889             : 
    1890             :     // Seems ok, add to field list.
    1891         500 :     m_poFeatureDefn->AddFieldDefn(poNewField);
    1892         500 :     nCSVFieldCount++;
    1893         500 :     m_oSetFields.insert(CPLString(poNewField->GetNameRef()).toupper());
    1894             : 
    1895        1000 :     panGeomFieldIndex = static_cast<int *>(CPLRealloc(
    1896         500 :         panGeomFieldIndex, sizeof(int) * m_poFeatureDefn->GetFieldCount()));
    1897         500 :     panGeomFieldIndex[m_poFeatureDefn->GetFieldCount() - 1] = -1;
    1898             : 
    1899         500 :     return OGRERR_NONE;
    1900             : }
    1901             : 
    1902             : /************************************************************************/
    1903             : /*                          CreateGeomField()                           */
    1904             : /************************************************************************/
    1905             : 
    1906          21 : OGRErr OGRCSVLayer::CreateGeomField(const OGRGeomFieldDefn *poGeomField,
    1907             :                                     int /* bApproxOK */)
    1908             : {
    1909          21 :     if (!TestCapability(OLCCreateGeomField))
    1910             :     {
    1911           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1912             :                  "Unable to create new fields after first feature written.");
    1913           0 :         return OGRERR_FAILURE;
    1914             :     }
    1915             : 
    1916             :     // Does this duplicate an existing field?
    1917          21 :     if (m_poFeatureDefn->GetGeomFieldIndex(poGeomField->GetNameRef()) >= 0)
    1918             :     {
    1919           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1920             :                  "Attempt to create geom field %s, "
    1921             :                  "but a field with this name already exists.",
    1922             :                  poGeomField->GetNameRef());
    1923             : 
    1924           1 :         return OGRERR_FAILURE;
    1925             :     }
    1926          40 :     OGRGeomFieldDefn oGeomField(poGeomField);
    1927          20 :     const auto poSRSOri = poGeomField->GetSpatialRef();
    1928          20 :     if (poSRSOri)
    1929             :     {
    1930          12 :         auto poSRSClone = OGRSpatialReferenceRefCountedPtr::makeClone(poSRSOri);
    1931           6 :         poSRSClone->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1932           6 :         oGeomField.SetSpatialRef(poSRSClone.get());
    1933             :     }
    1934          20 :     m_poFeatureDefn->AddGeomFieldDefn(&oGeomField);
    1935             : 
    1936          20 :     const char *pszName = poGeomField->GetNameRef();
    1937          20 :     if (EQUAL(pszName, ""))
    1938             :     {
    1939           3 :         const int nIdx = m_poFeatureDefn->GetFieldIndex("WKT");
    1940           3 :         if (nIdx >= 0)
    1941             :         {
    1942           0 :             panGeomFieldIndex[nIdx] = m_poFeatureDefn->GetGeomFieldCount() - 1;
    1943           0 :             return OGRERR_NONE;
    1944             :         }
    1945           3 :         pszName = "WKT";
    1946             :     }
    1947          20 :     if (STARTS_WITH_CI(pszName, "geom_") && strlen(pszName) >= strlen("geom_"))
    1948          15 :         pszName += strlen("geom_");
    1949          20 :     if (!EQUAL(pszName, "WKT") && !STARTS_WITH_CI(pszName, "_WKT"))
    1950           2 :         pszName = CPLSPrintf("_WKT%s", pszName);
    1951             : 
    1952          20 :     OGRFieldDefn oRegularFieldDefn(pszName, OFTString);
    1953          20 :     m_poFeatureDefn->AddFieldDefn(&oRegularFieldDefn);
    1954          20 :     nCSVFieldCount++;
    1955             : 
    1956          40 :     panGeomFieldIndex = static_cast<int *>(CPLRealloc(
    1957          20 :         panGeomFieldIndex, sizeof(int) * m_poFeatureDefn->GetFieldCount()));
    1958          20 :     panGeomFieldIndex[m_poFeatureDefn->GetFieldCount() - 1] =
    1959          20 :         m_poFeatureDefn->GetGeomFieldCount() - 1;
    1960             : 
    1961          20 :     return OGRERR_NONE;
    1962             : }
    1963             : 
    1964             : /************************************************************************/
    1965             : /*                            WriteHeader()                             */
    1966             : /*                                                                      */
    1967             : /*      Write the header, and possibly the .csvt file if they           */
    1968             : /*      haven't already been written.                                   */
    1969             : /************************************************************************/
    1970             : 
    1971         151 : OGRErr OGRCSVLayer::WriteHeader()
    1972             : {
    1973         151 :     CPLAssert(bNew);
    1974             : 
    1975             :     // Write field names if we haven't written them yet.
    1976             :     // Write .csvt file if needed.
    1977         151 :     bNew = false;
    1978         151 :     bHasFieldNames = true;
    1979             : 
    1980         603 :     const auto CreateCSV = [this]()
    1981             :     {
    1982         151 :         if (STARTS_WITH(pszFilename, "/vsistdout/") ||
    1983         150 :             STARTS_WITH(pszFilename, "/vsizip/"))
    1984           1 :             fpCSV = VSIFOpenL(pszFilename, "wb");
    1985             :         else
    1986         150 :             fpCSV = VSIFOpenL(pszFilename, "w+b");
    1987             : 
    1988         151 :         if (fpCSV == nullptr)
    1989             :         {
    1990           0 :             CPLError(CE_Failure, CPLE_OpenFailed, "Failed to create %s:\n%s",
    1991           0 :                      pszFilename, VSIStrerror(errno));
    1992           0 :             return OGRERR_FAILURE;
    1993             :         }
    1994         151 :         return OGRERR_NONE;
    1995         151 :     };
    1996             : 
    1997         151 :     if (!m_bWriteHeader)
    1998           1 :         return CreateCSV();
    1999             : 
    2000         150 :     bool bOK = true;
    2001             : 
    2002         333 :     for (int iFile = 0; iFile < (bCreateCSVT ? 2 : 1); iFile++)
    2003             :     {
    2004         183 :         VSILFILE *fpCSVT = nullptr;
    2005         183 :         if (bCreateCSVT && iFile == 0)
    2006             :         {
    2007             :             char *pszDirName =
    2008          33 :                 CPLStrdup(CPLGetDirnameSafe(pszFilename).c_str());
    2009             :             char *pszBaseName =
    2010          33 :                 CPLStrdup(CPLGetBasenameSafe(pszFilename).c_str());
    2011          33 :             fpCSVT = VSIFOpenL(
    2012          66 :                 CPLFormFilenameSafe(pszDirName, pszBaseName, ".csvt").c_str(),
    2013             :                 "wb");
    2014          33 :             CPLFree(pszDirName);
    2015          33 :             CPLFree(pszBaseName);
    2016             :         }
    2017             :         else
    2018             :         {
    2019         150 :             if (CreateCSV() != OGRERR_NONE)
    2020           0 :                 return OGRERR_FAILURE;
    2021             :         }
    2022             : 
    2023         183 :         if (bWriteBOM && fpCSV)
    2024             :         {
    2025           2 :             bOK &= VSIFWriteL("\xEF\xBB\xBF", 1, 3, fpCSV) > 0;
    2026             :         }
    2027             : 
    2028         183 :         bool bNeedDelimiter = false;
    2029         183 :         if (eGeometryFormat == OGR_CSV_GEOM_AS_XYZ)
    2030             :         {
    2031           3 :             if (fpCSV)
    2032           2 :                 bOK &=
    2033           2 :                     VSIFPrintfL(fpCSV, "X%sY%sZ", szDelimiter, szDelimiter) > 0;
    2034           3 :             if (fpCSVT)
    2035           1 :                 bOK &= VSIFPrintfL(fpCSVT, "%s", "CoordX,CoordY,Real") > 0;
    2036           3 :             bNeedDelimiter = true;
    2037             :         }
    2038         180 :         else if (eGeometryFormat == OGR_CSV_GEOM_AS_XY)
    2039             :         {
    2040           4 :             if (fpCSV)
    2041           2 :                 bOK &= VSIFPrintfL(fpCSV, "X%sY", szDelimiter) > 0;
    2042           4 :             if (fpCSVT)
    2043           2 :                 bOK &= VSIFPrintfL(fpCSVT, "%s", "CoordX,CoordY") > 0;
    2044           4 :             bNeedDelimiter = true;
    2045             :         }
    2046         176 :         else if (eGeometryFormat == OGR_CSV_GEOM_AS_YX)
    2047             :         {
    2048           2 :             if (fpCSV)
    2049           1 :                 bOK &= VSIFPrintfL(fpCSV, "Y%sX", szDelimiter) > 0;
    2050           2 :             if (fpCSVT)
    2051           1 :                 bOK &= VSIFPrintfL(fpCSVT, "%s", "CoordY,CoordX") > 0;
    2052           2 :             bNeedDelimiter = true;
    2053             :         }
    2054         174 :         else if (bHiddenWKTColumn)
    2055             :         {
    2056          49 :             if (fpCSV)
    2057             :             {
    2058             :                 const char *pszColName =
    2059          35 :                     m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
    2060          35 :                 bOK &= VSIFPrintfL(fpCSV, "%s", pszColName) >= 0;
    2061             :             }
    2062          49 :             if (fpCSVT)
    2063          14 :                 bOK &= VSIFPrintfL(fpCSVT, "%s", "WKT") > 0;
    2064          49 :             bNeedDelimiter = true;
    2065             :         }
    2066             : 
    2067         880 :         for (int iField = 0; iField < m_poFeatureDefn->GetFieldCount();
    2068             :              iField++)
    2069             :         {
    2070         697 :             if (bNeedDelimiter)
    2071             :             {
    2072         590 :                 if (fpCSV)
    2073         425 :                     bOK &= VSIFPrintfL(fpCSV, "%s", szDelimiter) > 0;
    2074         590 :                 if (fpCSVT)
    2075         165 :                     bOK &= VSIFPrintfL(fpCSVT, "%s", ",") > 0;
    2076             :             }
    2077         697 :             bNeedDelimiter = true;
    2078             : 
    2079        1394 :             char *pszEscaped = CPLEscapeString(
    2080         697 :                 m_poFeatureDefn->GetFieldDefn(iField)->GetNameRef(), -1,
    2081         697 :                 m_eStringQuoting == StringQuoting::ALWAYS
    2082             :                     ? CPLES_CSV_FORCE_QUOTING
    2083             :                     : CPLES_CSV);
    2084         697 :             if (pszEscaped == nullptr)
    2085             :             {
    2086           0 :                 if (fpCSVT)
    2087           0 :                     VSIFCloseL(fpCSVT);
    2088           0 :                 return OGRERR_FAILURE;
    2089             :             }
    2090             : 
    2091         697 :             if (fpCSV)
    2092             :             {
    2093         517 :                 bool bAddDoubleQuote = false;
    2094         517 :                 if (szDelimiter[0] == ' ' && pszEscaped[0] != '"' &&
    2095           2 :                     strchr(pszEscaped, ' ') != nullptr)
    2096           1 :                     bAddDoubleQuote = true;
    2097         517 :                 if (bAddDoubleQuote)
    2098           1 :                     bOK &= VSIFWriteL("\"", 1, 1, fpCSV) > 0;
    2099         517 :                 bOK &= VSIFPrintfL(fpCSV, "%s", pszEscaped) >= 0;
    2100         517 :                 if (bAddDoubleQuote)
    2101           1 :                     bOK &= VSIFWriteL("\"", 1, 1, fpCSV) > 0;
    2102             :             }
    2103         697 :             CPLFree(pszEscaped);
    2104             : 
    2105         697 :             if (fpCSVT)
    2106             :             {
    2107         180 :                 int nWidth = m_poFeatureDefn->GetFieldDefn(iField)->GetWidth();
    2108             :                 const int nPrecision =
    2109         180 :                     m_poFeatureDefn->GetFieldDefn(iField)->GetPrecision();
    2110             : 
    2111         180 :                 switch (m_poFeatureDefn->GetFieldDefn(iField)->GetType())
    2112             :                 {
    2113          54 :                     case OFTInteger:
    2114             :                     {
    2115          54 :                         if (m_poFeatureDefn->GetFieldDefn(iField)
    2116          54 :                                 ->GetSubType() == OFSTBoolean)
    2117             :                         {
    2118           9 :                             nWidth = 0;
    2119           9 :                             bOK &= VSIFPrintfL(fpCSVT, "%s",
    2120           9 :                                                "Integer(Boolean)") > 0;
    2121             :                         }
    2122          45 :                         else if (m_poFeatureDefn->GetFieldDefn(iField)
    2123          45 :                                      ->GetSubType() == OFSTInt16)
    2124             :                         {
    2125          11 :                             nWidth = 0;
    2126          11 :                             bOK &=
    2127          11 :                                 VSIFPrintfL(fpCSVT, "%s", "Integer(Int16)") > 0;
    2128             :                         }
    2129             :                         else
    2130             :                         {
    2131          34 :                             bOK &= VSIFPrintfL(fpCSVT, "%s", "Integer") > 0;
    2132             :                         }
    2133          54 :                         break;
    2134             :                     }
    2135          15 :                     case OFTInteger64:
    2136          15 :                         bOK &= VSIFPrintfL(fpCSVT, "%s", "Integer64") > 0;
    2137          15 :                         break;
    2138          47 :                     case OFTReal:
    2139             :                     {
    2140          47 :                         if (m_poFeatureDefn->GetFieldDefn(iField)
    2141          47 :                                 ->GetSubType() == OFSTFloat32)
    2142             :                         {
    2143          11 :                             nWidth = 0;
    2144          11 :                             bOK &=
    2145          11 :                                 VSIFPrintfL(fpCSVT, "%s", "Real(Float32)") > 0;
    2146             :                         }
    2147             :                         else
    2148             :                         {
    2149          36 :                             bOK &= VSIFPrintfL(fpCSVT, "%s", "Real") > 0;
    2150             :                         }
    2151          47 :                         break;
    2152             :                     }
    2153           6 :                     case OFTDate:
    2154           6 :                         bOK &= VSIFPrintfL(fpCSVT, "%s", "Date") > 0;
    2155           6 :                         break;
    2156           1 :                     case OFTTime:
    2157           1 :                         bOK &= VSIFPrintfL(fpCSVT, "%s", "Time") > 0;
    2158           1 :                         break;
    2159          11 :                     case OFTDateTime:
    2160          11 :                         bOK &= VSIFPrintfL(fpCSVT, "%s", "DateTime") > 0;
    2161          11 :                         break;
    2162           3 :                     case OFTStringList:
    2163           3 :                         bOK &= VSIFPrintfL(fpCSVT, "%s", "JSonStringList") > 0;
    2164           3 :                         break;
    2165           2 :                     case OFTIntegerList:
    2166           2 :                         bOK &= VSIFPrintfL(fpCSVT, "%s", "JSonIntegerList") > 0;
    2167           2 :                         break;
    2168           2 :                     case OFTInteger64List:
    2169           2 :                         bOK &=
    2170           2 :                             VSIFPrintfL(fpCSVT, "%s", "JSonInteger64List") > 0;
    2171           2 :                         break;
    2172           2 :                     case OFTRealList:
    2173           2 :                         bOK &= VSIFPrintfL(fpCSVT, "%s", "JSonRealList") > 0;
    2174           2 :                         break;
    2175          37 :                     default:
    2176          37 :                         bOK &= VSIFPrintfL(fpCSVT, "%s", "String") > 0;
    2177          37 :                         break;
    2178             :                 }
    2179             : 
    2180         180 :                 if (nWidth != 0)
    2181             :                 {
    2182          25 :                     if (nPrecision != 0)
    2183          10 :                         bOK &= VSIFPrintfL(fpCSVT, "(%d.%d)", nWidth,
    2184          10 :                                            nPrecision) > 0;
    2185             :                     else
    2186          15 :                         bOK &= VSIFPrintfL(fpCSVT, "(%d)", nWidth) > 0;
    2187             :                 }
    2188             :             }
    2189             :         }
    2190             : 
    2191         183 :         if (bUseCRLF)
    2192             :         {
    2193           1 :             if (fpCSV)
    2194           1 :                 bOK &= VSIFPutcL(13, fpCSV) > 0;
    2195           1 :             if (fpCSVT)
    2196           0 :                 bOK &= VSIFPutcL(13, fpCSVT) > 0;
    2197             :         }
    2198         183 :         if (fpCSV)
    2199         150 :             bOK &= VSIFPutcL('\n', fpCSV) > 0;
    2200         183 :         if (fpCSVT)
    2201          33 :             bOK &= VSIFPutcL('\n', fpCSVT) > 0;
    2202         183 :         if (fpCSVT)
    2203          33 :             VSIFCloseL(fpCSVT);
    2204             :     }
    2205             : 
    2206         150 :     return (!bOK || fpCSV == nullptr) ? OGRERR_FAILURE : OGRERR_NONE;
    2207             : }
    2208             : 
    2209             : /************************************************************************/
    2210             : /*                           ICreateFeature()                           */
    2211             : /************************************************************************/
    2212             : 
    2213         375 : OGRErr OGRCSVLayer::ICreateFeature(OGRFeature *poNewFeature)
    2214             : 
    2215             : {
    2216         375 :     if (!bInWriteMode)
    2217             :     {
    2218           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2219             :                  "The CreateFeature() operation is not permitted on a "
    2220             :                  "read-only CSV.");
    2221           1 :         return OGRERR_FAILURE;
    2222             :     }
    2223             : 
    2224             :     // If we need rewind, it means that we have just written a feature before
    2225             :     // so there's no point seeking to the end of the file, as we're already
    2226             :     // at the end.
    2227         374 :     bool bNeedSeekEnd = !bNeedRewindBeforeRead;
    2228             : 
    2229         374 :     bNeedRewindBeforeRead = true;
    2230             : 
    2231             :     // Write field names if we haven't written them yet.
    2232             :     // Write .csvt file if needed.
    2233         374 :     if (bNew)
    2234             :     {
    2235         124 :         const OGRErr eErr = WriteHeader();
    2236         124 :         if (eErr != OGRERR_NONE)
    2237           0 :             return eErr;
    2238         124 :         bNeedSeekEnd = false;
    2239             :     }
    2240             : 
    2241         374 :     if (fpCSV == nullptr)
    2242           0 :         return OGRERR_FAILURE;
    2243             : 
    2244         374 :     bool bRet = true;
    2245             : 
    2246             :     // Make sure we are at the end of the file.
    2247         374 :     if (bNeedSeekEnd)
    2248             :     {
    2249          21 :         if (bFirstFeatureAppendedDuringSession)
    2250             :         {
    2251             :             // Add a newline character to the end of the file if necessary.
    2252          21 :             bFirstFeatureAppendedDuringSession = false;
    2253          21 :             bRet &= VSIFSeekL(fpCSV, 0, SEEK_END) >= 0;
    2254          21 :             bRet &= VSIFSeekL(fpCSV, VSIFTellL(fpCSV) - 1, SEEK_SET) >= 0;
    2255          21 :             char chLast = '\0';
    2256          21 :             bRet &= VSIFReadL(&chLast, 1, 1, fpCSV) > 0;
    2257          21 :             bRet &= VSIFSeekL(fpCSV, 0, SEEK_END) >= 0;
    2258          21 :             if (chLast != '\n')
    2259             :             {
    2260           0 :                 if (bUseCRLF)
    2261           0 :                     bRet &= VSIFPutcL(13, fpCSV) != EOF;
    2262           0 :                 bRet &= VSIFPutcL('\n', fpCSV) != EOF;
    2263             :             }
    2264             :         }
    2265             :         else
    2266             :         {
    2267           0 :             bRet &= VSIFSeekL(fpCSV, 0, SEEK_END) >= 0;
    2268             :         }
    2269             :     }
    2270             : 
    2271         374 :     bool bNeedDelimiter = false;
    2272         374 :     bool bEmptyLine = true;
    2273             : 
    2274          92 :     const auto GetWktOptions = [](const OGRGeomFieldDefn *poGeomFieldDefn)
    2275             :     {
    2276          92 :         const auto &sCoordPrec = poGeomFieldDefn->GetCoordinatePrecision();
    2277             : 
    2278          92 :         OGRWktOptions wktOptions;
    2279          92 :         wktOptions.variant = wkbVariantIso;
    2280          92 :         if (sCoordPrec.dfXYResolution != OGRGeomCoordinatePrecision::UNKNOWN)
    2281             :         {
    2282           3 :             wktOptions.format = OGRWktFormat::F;
    2283           3 :             wktOptions.xyPrecision =
    2284           3 :                 OGRGeomCoordinatePrecision::ResolutionToPrecision(
    2285           3 :                     sCoordPrec.dfXYResolution);
    2286             :         }
    2287          92 :         if (sCoordPrec.dfZResolution != OGRGeomCoordinatePrecision::UNKNOWN)
    2288             :         {
    2289           2 :             wktOptions.format = OGRWktFormat::F;
    2290           2 :             wktOptions.zPrecision =
    2291           2 :                 OGRGeomCoordinatePrecision::ResolutionToPrecision(
    2292           2 :                     sCoordPrec.dfZResolution);
    2293             :         }
    2294          92 :         if (sCoordPrec.dfMResolution != OGRGeomCoordinatePrecision::UNKNOWN)
    2295             :         {
    2296           2 :             wktOptions.format = OGRWktFormat::F;
    2297           2 :             wktOptions.mPrecision =
    2298           2 :                 OGRGeomCoordinatePrecision::ResolutionToPrecision(
    2299           2 :                     sCoordPrec.dfMResolution);
    2300             :         }
    2301             : 
    2302          92 :         return wktOptions;
    2303             :     };
    2304             : 
    2305             :     // Write out the geometry.
    2306         374 :     if (eGeometryFormat == OGR_CSV_GEOM_AS_XYZ ||
    2307         372 :         eGeometryFormat == OGR_CSV_GEOM_AS_XY ||
    2308         369 :         eGeometryFormat == OGR_CSV_GEOM_AS_YX)
    2309             :     {
    2310           6 :         const OGRGeometry *poGeom = poNewFeature->GetGeometryRef();
    2311           6 :         if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    2312             :         {
    2313           5 :             const auto poGeomFieldDefn = m_poFeatureDefn->GetGeomFieldDefn(0);
    2314           5 :             const OGRPoint *poPoint = poGeom->toPoint();
    2315           5 :             std::string osCoord;
    2316           5 :             if (eGeometryFormat == OGR_CSV_GEOM_AS_XYZ)
    2317           4 :                 osCoord = OGRMakeWktCoordinate(poPoint->getX(), poPoint->getY(),
    2318             :                                                poPoint->getZ(), 3,
    2319           6 :                                                GetWktOptions(poGeomFieldDefn));
    2320           3 :             else if (eGeometryFormat == OGR_CSV_GEOM_AS_XY)
    2321             :                 osCoord =
    2322           4 :                     OGRMakeWktCoordinate(poPoint->getX(), poPoint->getY(), 0, 2,
    2323           6 :                                          GetWktOptions(poGeomFieldDefn));
    2324             :             else
    2325             :                 osCoord =
    2326           2 :                     OGRMakeWktCoordinate(poPoint->getY(), poPoint->getX(), 0, 2,
    2327           3 :                                          GetWktOptions(poGeomFieldDefn));
    2328             : 
    2329          41 :             for (char &ch : osCoord)
    2330             :             {
    2331          36 :                 if (ch == ' ')
    2332           7 :                     ch = szDelimiter[0];
    2333             :             }
    2334           5 :             bRet &= VSIFPrintfL(fpCSV, "%s", osCoord.c_str()) > 0;
    2335             :         }
    2336             :         else
    2337             :         {
    2338           1 :             bRet &= VSIFPrintfL(fpCSV, "%s", szDelimiter) > 0;
    2339           1 :             if (eGeometryFormat == OGR_CSV_GEOM_AS_XYZ)
    2340           0 :                 bRet &= VSIFPrintfL(fpCSV, "%s", szDelimiter) > 0;
    2341             :         }
    2342           6 :         bEmptyLine = false;
    2343           6 :         bNeedDelimiter = true;
    2344             :     }
    2345         368 :     else if (bHiddenWKTColumn)
    2346             :     {
    2347          74 :         const OGRGeometry *poGeom = poNewFeature->GetGeomFieldRef(0);
    2348          74 :         if (poGeom)
    2349             :         {
    2350          67 :             const auto poGeomFieldDefn = m_poFeatureDefn->GetGeomFieldDefn(0);
    2351             :             const std::string wkt =
    2352         134 :                 poGeom->exportToWkt(GetWktOptions(poGeomFieldDefn));
    2353          67 :             if (!wkt.empty())
    2354             :             {
    2355          67 :                 bRet &= VSIFWriteL("\"", 1, 1, fpCSV) > 0;
    2356          67 :                 bRet &= VSIFWriteL(wkt.c_str(), wkt.size(), 1, fpCSV) > 0;
    2357          67 :                 bRet &= VSIFWriteL("\"", 1, 1, fpCSV) > 0;
    2358          67 :                 bEmptyLine = false;
    2359             :             }
    2360             :         }
    2361          74 :         bNeedDelimiter = true;
    2362             :     }
    2363             : 
    2364             :     // Write out all the field values.
    2365        2061 :     for (int iField = 0; iField < m_poFeatureDefn->GetFieldCount(); iField++)
    2366             :     {
    2367        1687 :         char *pszEscaped = nullptr;
    2368             : 
    2369        1687 :         if (bNeedDelimiter)
    2370             :         {
    2371        1393 :             bRet &= VSIFPrintfL(fpCSV, "%s", szDelimiter) > 0;
    2372        1393 :             bEmptyLine = false;
    2373             :         }
    2374        1687 :         bNeedDelimiter = true;
    2375             : 
    2376        1687 :         if (eGeometryFormat == OGR_CSV_GEOM_AS_WKT &&
    2377         530 :             panGeomFieldIndex[iField] >= 0)
    2378             :         {
    2379          24 :             const int iGeom = panGeomFieldIndex[iField];
    2380          24 :             const OGRGeometry *poGeom = poNewFeature->GetGeomFieldRef(iGeom);
    2381          24 :             if (poGeom)
    2382             :             {
    2383             :                 const auto poGeomFieldDefn =
    2384          20 :                     m_poFeatureDefn->GetGeomFieldDefn(iGeom);
    2385             :                 const std::string wkt =
    2386          40 :                     poGeom->exportToWkt(GetWktOptions(poGeomFieldDefn));
    2387          20 :                 if (!wkt.empty())
    2388             :                 {
    2389             :                     char *pszNew =
    2390          20 :                         static_cast<char *>(CPLMalloc(1 + wkt.size() + 1 + 1));
    2391          20 :                     pszNew[0] = '"';
    2392          20 :                     memcpy(pszNew + 1, wkt.c_str(), wkt.size());
    2393          20 :                     pszNew[1 + wkt.size()] = '"';
    2394          20 :                     pszNew[1 + wkt.size() + 1] = '\0';
    2395          20 :                     CPLFree(pszEscaped);
    2396          20 :                     pszEscaped = pszNew;
    2397             :                 }
    2398             :                 else
    2399             :                 {
    2400           0 :                     CPLFree(pszEscaped);
    2401           0 :                     pszEscaped = CPLStrdup("");
    2402             :                 }
    2403             :             }
    2404             :             else
    2405             :             {
    2406           4 :                 CPLFree(pszEscaped);
    2407           4 :                 pszEscaped = CPLStrdup("");
    2408          24 :             }
    2409             :         }
    2410             :         else
    2411             :         {
    2412             :             const OGRFieldType eType(
    2413        1663 :                 m_poFeatureDefn->GetFieldDefn(iField)->GetType());
    2414        1663 :             if (eType == OFTReal || eType == OFTInteger ||
    2415             :                 eType == OFTInteger64)
    2416             :             {
    2417         757 :                 if (m_poFeatureDefn->GetFieldDefn(iField)->GetSubType() ==
    2418         788 :                         OFSTFloat32 &&
    2419          31 :                     poNewFeature->IsFieldSetAndNotNull(iField))
    2420             :                 {
    2421          11 :                     pszEscaped = CPLStrdup(CPLSPrintf(
    2422             :                         "%.8g", poNewFeature->GetFieldAsDouble(iField)));
    2423             :                 }
    2424             :                 else
    2425             :                 {
    2426             :                     pszEscaped =
    2427         746 :                         CPLStrdup(poNewFeature->GetFieldAsString(iField));
    2428             :                 }
    2429             :             }
    2430         906 :             else if (eType == OFTStringList || eType == OFTIntegerList ||
    2431         899 :                      eType == OFTInteger64List || eType == OFTRealList)
    2432             :             {
    2433           9 :                 char *pszJSon = poNewFeature->GetFieldAsSerializedJSon(iField);
    2434           9 :                 if (pszJSon)
    2435             :                 {
    2436           9 :                     pszEscaped = CPLEscapeString(pszJSon, -1,
    2437           9 :                                                  m_eStringQuoting ==
    2438             :                                                          StringQuoting::ALWAYS
    2439             :                                                      ? CPLES_CSV_FORCE_QUOTING
    2440             :                                                      : CPLES_CSV);
    2441             :                 }
    2442             :                 else
    2443             :                 {
    2444           0 :                     pszEscaped = CPLStrdup("");
    2445             :                 }
    2446           9 :                 CPLFree(pszJSon);
    2447             :             }
    2448             :             else
    2449             :             {
    2450         897 :                 const char *pszContent = poNewFeature->GetFieldAsString(iField);
    2451         897 :                 pszEscaped = CPLEscapeString(
    2452             :                     pszContent, -1,
    2453         897 :                     (m_eStringQuoting == StringQuoting::ALWAYS ||
    2454         887 :                      (m_eStringQuoting == StringQuoting::IF_AMBIGUOUS &&
    2455         744 :                       (CPLGetValueType(pszContent) != CPL_VALUE_STRING ||
    2456         577 :                        (pszContent[0] == DIGIT_ZERO && pszContent[1] != '\0' &&
    2457           2 :                         pszContent[1] != '.'))))
    2458             :                         ? CPLES_CSV_FORCE_QUOTING
    2459             :                         : CPLES_CSV);
    2460             :             }
    2461             :         }
    2462        1687 :         if (pszEscaped == nullptr)
    2463             :         {
    2464           0 :             return OGRERR_FAILURE;
    2465             :         }
    2466        1687 :         const size_t nLen = strlen(pszEscaped);
    2467        1687 :         bool bAddDoubleQuote = false;
    2468        1687 :         if (szDelimiter[0] == ' ' && pszEscaped[0] != '"' &&
    2469           2 :             strchr(pszEscaped, ' ') != nullptr)
    2470           1 :             bAddDoubleQuote = true;
    2471        1687 :         if (bAddDoubleQuote)
    2472           1 :             bRet &= VSIFWriteL("\"", 1, 1, fpCSV) > 0;
    2473        1687 :         if (nLen)
    2474             :         {
    2475        1125 :             bRet &= VSIFWriteL(pszEscaped, nLen, 1, fpCSV) > 0;
    2476        1125 :             bEmptyLine = false;
    2477             :         }
    2478        1687 :         if (bAddDoubleQuote)
    2479           1 :             bRet &= VSIFWriteL("\"", 1, 1, fpCSV) > 0;
    2480        1687 :         CPLFree(pszEscaped);
    2481             :     }
    2482             : 
    2483         374 :     if (bEmptyLine)
    2484           1 :         bRet &= VSIFPrintfL(fpCSV, "\"\"") > 0;
    2485             : 
    2486         374 :     if (bUseCRLF)
    2487           4 :         bRet &= VSIFPutcL(13, fpCSV) != EOF;
    2488         374 :     bRet &= VSIFPutcL('\n', fpCSV) != EOF;
    2489             : 
    2490         374 :     if (nTotalFeatures >= 0)
    2491         354 :         nTotalFeatures++;
    2492             : 
    2493         374 :     return bRet ? OGRERR_NONE : OGRERR_FAILURE;
    2494             : }
    2495             : 
    2496             : /************************************************************************/
    2497             : /*                              SetCRLF()                               */
    2498             : /************************************************************************/
    2499             : 
    2500         151 : void OGRCSVLayer::SetCRLF(bool bNewValue)
    2501             : {
    2502         151 :     bUseCRLF = bNewValue;
    2503         151 : }
    2504             : 
    2505             : /************************************************************************/
    2506             : /*                          SetWriteGeometry()                          */
    2507             : /************************************************************************/
    2508             : 
    2509          50 : void OGRCSVLayer::SetWriteGeometry(OGRwkbGeometryType eGType,
    2510             :                                    OGRCSVGeometryFormat eGeometryFormatIn,
    2511             :                                    const char *pszGeomCol)
    2512             : {
    2513          50 :     eGeometryFormat = eGeometryFormatIn;
    2514          50 :     if (eGeometryFormat == OGR_CSV_GEOM_AS_WKT && eGType != wkbNone)
    2515             :     {
    2516          70 :         OGRGeomFieldDefn oGFld(pszGeomCol, eGType);
    2517          35 :         bHiddenWKTColumn = true;
    2518             :         // We don't use CreateGeomField() since we don't want to generate
    2519             :         // a geometry field in first position, as it confuses applications
    2520             :         // (such as MapServer <= 6.4) that assume that the first regular field
    2521             :         // they add will be at index 0.
    2522          70 :         m_poFeatureDefn->AddGeomFieldDefn(&oGFld);
    2523             :     }
    2524             :     else
    2525             :     {
    2526          15 :         m_poFeatureDefn->SetGeomType(eGType);
    2527             :     }
    2528          50 : }
    2529             : 
    2530             : /************************************************************************/
    2531             : /*                           SetCreateCSVT()                            */
    2532             : /************************************************************************/
    2533             : 
    2534          38 : void OGRCSVLayer::SetCreateCSVT(bool bCreateCSVTIn)
    2535             : {
    2536          38 :     bCreateCSVT = bCreateCSVTIn;
    2537          38 : }
    2538             : 
    2539             : /************************************************************************/
    2540             : /*                            SetWriteBOM()                             */
    2541             : /************************************************************************/
    2542             : 
    2543          11 : void OGRCSVLayer::SetWriteBOM(bool bWriteBOMIn)
    2544             : {
    2545          11 :     bWriteBOM = bWriteBOMIn;
    2546          11 : }
    2547             : 
    2548             : /************************************************************************/
    2549             : /*                          GetFeatureCount()                           */
    2550             : /************************************************************************/
    2551             : 
    2552         110 : GIntBig OGRCSVLayer::GetFeatureCount(int bForce)
    2553             : {
    2554         110 :     if (m_poFilterGeom != nullptr || m_poAttrQuery != nullptr)
    2555             :     {
    2556          12 :         return OGRLayer::GetFeatureCount(bForce);
    2557             :     }
    2558             : 
    2559          98 :     if (nTotalFeatures >= 0)
    2560          67 :         return nTotalFeatures;
    2561             : 
    2562          31 :     if (fpCSV == nullptr)
    2563           0 :         return 0;
    2564             : 
    2565          31 :     ResetReading();
    2566             : 
    2567          31 :     if (szDelimiter[0] == '\t' && !bHonourStrings)
    2568             :     {
    2569           1 :         const int nBufSize = 4096;
    2570           1 :         char szBuffer[nBufSize + 1] = {};
    2571             : 
    2572           1 :         nTotalFeatures = 0;
    2573           1 :         bool bLastWasNewLine = false;
    2574             :         while (true)
    2575             :         {
    2576             :             const int nRead =
    2577           1 :                 static_cast<int>(VSIFReadL(szBuffer, 1, nBufSize, fpCSV));
    2578           1 :             szBuffer[nRead] = 0;
    2579           1 :             if (nTotalFeatures == 0 && szBuffer[0] != 13 && szBuffer[0] != 10)
    2580           1 :                 nTotalFeatures = 1;
    2581        1061 :             for (int i = 0; i < nRead; i++)
    2582             :             {
    2583        1060 :                 if (szBuffer[i] == 13 || szBuffer[i] == 10)
    2584             :                 {
    2585          10 :                     bLastWasNewLine = true;
    2586             :                 }
    2587        1050 :                 else if (bLastWasNewLine)
    2588             :                 {
    2589           9 :                     nTotalFeatures++;
    2590           9 :                     bLastWasNewLine = false;
    2591             :                 }
    2592             :             }
    2593             : 
    2594           1 :             if (nRead < nBufSize)
    2595           1 :                 break;
    2596           1 :         }
    2597             :     }
    2598             :     else
    2599             :     {
    2600          30 :         nTotalFeatures = 0;
    2601             :         while (true)
    2602             :         {
    2603         846 :             char **papszTokens = GetNextLineTokens();
    2604         846 :             if (papszTokens == nullptr)
    2605          30 :                 break;
    2606             : 
    2607         816 :             nTotalFeatures++;
    2608             : 
    2609         816 :             CSLDestroy(papszTokens);
    2610         816 :         }
    2611             :     }
    2612             : 
    2613          31 :     ResetReading();
    2614             : 
    2615          31 :     return nTotalFeatures;
    2616             : }
    2617             : 
    2618             : /************************************************************************/
    2619             : /*                             SyncToDisk()                             */
    2620             : /************************************************************************/
    2621             : 
    2622         268 : OGRErr OGRCSVLayer::SyncToDisk()
    2623             : {
    2624         268 :     if (bInWriteMode && fpCSV != nullptr)
    2625             :     {
    2626         244 :         if (VSIFFlushL(fpCSV) != 0)
    2627           0 :             return OGRERR_FAILURE;
    2628             :     }
    2629         268 :     return OGRERR_NONE;
    2630             : }

Generated by: LCOV version 1.14