LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/csv - ogrcsvlayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1207 1366 88.4 %
Date: 2025-05-31 00:00:17 Functions: 31 31 100.0 %

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

Generated by: LCOV version 1.14