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

Generated by: LCOV version 1.14