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 1372 88.3 %
Date: 2025-09-10 17:48:50 Functions: 32 32 100.0 %

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

Generated by: LCOV version 1.14