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

Generated by: LCOV version 1.14