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

Generated by: LCOV version 1.14