LCOV - code coverage report
Current view: top level - ogr - ogrutils.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 957 1134 84.4 %
Date: 2024-05-04 12:52:34 Functions: 33 41 80.5 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Utility functions for OGR classes, including some related to
       5             :  *           parsing well known text format vectors.
       6             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 1999, Frank Warmerdam
      10             :  * Copyright (c) 2008-2014, Even Rouault <even dot rouault at spatialys.com>
      11             :  *
      12             :  * Permission is hereby granted, free of charge, to any person obtaining a
      13             :  * copy of this software and associated documentation files (the "Software"),
      14             :  * to deal in the Software without restriction, including without limitation
      15             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      16             :  * and/or sell copies of the Software, and to permit persons to whom the
      17             :  * Software is furnished to do so, subject to the following conditions:
      18             :  *
      19             :  * The above copyright notice and this permission notice shall be included
      20             :  * in all copies or substantial portions of the Software.
      21             :  *
      22             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      23             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      24             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      25             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      26             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      27             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      28             :  * DEALINGS IN THE SOFTWARE.
      29             :  ****************************************************************************/
      30             : 
      31             : #include "cpl_port.h"
      32             : #include "ogr_geometry.h"
      33             : #include "ogr_p.h"
      34             : 
      35             : #include <cassert>
      36             : #include <cmath>
      37             : #include <cstddef>
      38             : #include <cstdio>
      39             : #include <cstdlib>
      40             : #include <cstring>
      41             : #include <cctype>
      42             : #include <limits>
      43             : #include <sstream>
      44             : #include <iomanip>
      45             : 
      46             : #include "cpl_conv.h"
      47             : #include "cpl_error.h"
      48             : #include "cpl_string.h"
      49             : #include "cpl_time.h"
      50             : #include "cpl_vsi.h"
      51             : #include "gdal.h"
      52             : #include "ogr_core.h"
      53             : #include "ogr_geometry.h"
      54             : #include "ogrsf_frmts.h"
      55             : 
      56             : // Returns whether a double fits within an int.
      57             : // Unable to put this in cpl_port.h as include limit breaks grib.
      58       65835 : inline bool CPLIsDoubleAnInt(double d)
      59             : {
      60             :     // Write it this way to detect NaN
      61      131652 :     if (!(d >= std::numeric_limits<int>::min() &&
      62       65817 :           d <= std::numeric_limits<int>::max()))
      63             :     {
      64          19 :         return false;
      65             :     }
      66       65816 :     return d == static_cast<double>(static_cast<int>(d));
      67             : }
      68             : 
      69             : namespace
      70             : {
      71             : 
      72             : // Remove trailing zeros except the last one.
      73       70503 : std::string removeTrailingZeros(std::string s)
      74             : {
      75       70503 :     auto pos = s.find('.');
      76       70503 :     if (pos == std::string::npos)
      77        7650 :         return s;
      78             : 
      79             :     // Remove zeros at the end.  We know this won't be npos because we
      80             :     // have a decimal point.
      81       62853 :     auto nzpos = s.find_last_not_of('0');
      82       62853 :     s = s.substr(0, nzpos + 1);
      83             : 
      84             :     // Make sure there is one 0 after the decimal point.
      85       62853 :     if (s.back() == '.')
      86       16890 :         s += '0';
      87       62853 :     return s;
      88             : }
      89             : 
      90             : // Round a string representing a number by 1 in the least significant digit.
      91          33 : std::string roundup(std::string s)
      92             : {
      93             :     // Remove a negative sign if it exists to make processing
      94             :     // more straigtforward.
      95          33 :     bool negative(false);
      96          33 :     if (s[0] == '-')
      97             :     {
      98           9 :         negative = true;
      99           9 :         s = s.substr(1);
     100             :     }
     101             : 
     102             :     // Go from the back to the front.  If we increment a digit other than
     103             :     // a '9', we're done.  If we increment a '9', set it to a '0' and move
     104             :     // to the next (more significant) digit.  If we get to the front of the
     105             :     // string, add a '1' to the front of the string.
     106         210 :     for (int pos = static_cast<int>(s.size() - 1); pos >= 0; pos--)
     107             :     {
     108         210 :         if (s[pos] == '.')
     109           3 :             continue;
     110         207 :         s[pos]++;
     111             : 
     112             :         // Incrementing past 9 gets you a colon in ASCII.
     113         207 :         if (s[pos] != ':')
     114          33 :             break;
     115             :         else
     116         174 :             s[pos] = '0';
     117         174 :         if (pos == 0)
     118           0 :             s = '1' + s;
     119             :     }
     120          33 :     if (negative)
     121           9 :         s = '-' + s;
     122          33 :     return s;
     123             : }
     124             : 
     125             : // This attempts to eliminate what is likely binary -> decimal representation
     126             : // error or the result of low-order rounding with calculations.  The result
     127             : // may be more visually pleasing and takes up fewer places.
     128       24966 : std::string intelliround(std::string &s)
     129             : {
     130             :     // If there is no decimal point, just return.
     131       24966 :     auto dotPos = s.find(".");
     132       24966 :     if (dotPos == std::string::npos)
     133           0 :         return s;
     134             : 
     135             :     // Don't mess with exponential formatting.
     136       24966 :     if (s.find_first_of("eE") != std::string::npos)
     137           0 :         return s;
     138       24966 :     size_t iDotPos = static_cast<size_t>(dotPos);
     139       24966 :     size_t nCountBeforeDot = iDotPos - 1;
     140       24966 :     if (s[0] == '-')
     141        1384 :         nCountBeforeDot--;
     142       24966 :     size_t i = s.size();
     143             : 
     144             :     // If we don't have ten characters, don't do anything.
     145       24966 :     if (i <= 10)
     146        2363 :         return s;
     147             : 
     148             :     /* -------------------------------------------------------------------- */
     149             :     /*      Trim trailing 00000x's as they are likely roundoff error.       */
     150             :     /* -------------------------------------------------------------------- */
     151       42440 :     if (s[i - 2] == '0' && s[i - 3] == '0' && s[i - 4] == '0' &&
     152       42440 :         s[i - 5] == '0' && s[i - 6] == '0')
     153             :     {
     154       19281 :         s.resize(s.size() - 1);
     155             :     }
     156             :     // I don't understand this case exactly.  It's like saying if the
     157             :     // value is large enough and there are sufficient sig digits before
     158             :     // a bunch of zeros, remove the zeros and any digits at the end that
     159             :     // may be nonzero.  Perhaps if we can't exactly explain in words what
     160             :     // we're doing here, we shouldn't do it?  Perhaps it should
     161             :     // be generalized?
     162             :     // The value "12345.000000011" invokes this case, if anyone
     163             :     // is interested.
     164        2529 :     else if (iDotPos < i - 8 && (nCountBeforeDot >= 4 || s[i - 3] == '0') &&
     165         408 :              (nCountBeforeDot >= 5 || s[i - 4] == '0') &&
     166         247 :              (nCountBeforeDot >= 6 || s[i - 5] == '0') &&
     167         208 :              (nCountBeforeDot >= 7 || s[i - 6] == '0') &&
     168        6045 :              (nCountBeforeDot >= 8 || s[i - 7] == '0') && s[i - 8] == '0' &&
     169         194 :              s[i - 9] == '0')
     170             :     {
     171         194 :         s.resize(s.size() - 8);
     172             :     }
     173             :     /* -------------------------------------------------------------------- */
     174             :     /*      Trim trailing 99999x's as they are likely roundoff error.       */
     175             :     /* -------------------------------------------------------------------- */
     176        3466 :     else if (s[i - 2] == '9' && s[i - 3] == '9' && s[i - 4] == '9' &&
     177        3466 :              s[i - 5] == '9' && s[i - 6] == '9')
     178             :     {
     179          23 :         s.resize(i - 6);
     180          23 :         s = roundup(s);
     181             :     }
     182        2308 :     else if (iDotPos < i - 9 && (nCountBeforeDot >= 4 || s[i - 3] == '9') &&
     183         267 :              (nCountBeforeDot >= 5 || s[i - 4] == '9') &&
     184          64 :              (nCountBeforeDot >= 6 || s[i - 5] == '9') &&
     185          17 :              (nCountBeforeDot >= 7 || s[i - 6] == '9') &&
     186        5423 :              (nCountBeforeDot >= 8 || s[i - 7] == '9') && s[i - 8] == '9' &&
     187          10 :              s[i - 9] == '9')
     188             :     {
     189          10 :         s.resize(i - 9);
     190          10 :         s = roundup(s);
     191             :     }
     192       22603 :     return s;
     193             : }
     194             : 
     195             : }  // unnamed namespace
     196             : 
     197             : /************************************************************************/
     198             : /*                        OGRFormatDouble()                             */
     199             : /************************************************************************/
     200             : 
     201       22170 : void OGRFormatDouble(char *pszBuffer, int nBufferLen, double dfVal,
     202             :                      char chDecimalSep, int nPrecision,
     203             :                      char chConversionSpecifier)
     204             : {
     205       22170 :     OGRWktOptions opts;
     206             : 
     207       22170 :     opts.xyPrecision = nPrecision;
     208       22170 :     opts.zPrecision = nPrecision;
     209       22170 :     opts.mPrecision = nPrecision;
     210       22170 :     opts.format = (chConversionSpecifier == 'g' || chConversionSpecifier == 'G')
     211       44340 :                       ? OGRWktFormat::G
     212             :                       : OGRWktFormat::F;
     213             : 
     214       22170 :     std::string s = OGRFormatDouble(dfVal, opts, 1);
     215       22170 :     if (chDecimalSep != '\0' && chDecimalSep != '.')
     216             :     {
     217           0 :         auto pos = s.find('.');
     218           0 :         if (pos != std::string::npos)
     219           0 :             s.replace(pos, 1, std::string(1, chDecimalSep));
     220             :     }
     221       22170 :     if (s.size() + 1 > static_cast<size_t>(nBufferLen))
     222             :     {
     223           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     224             :                  "Truncated double value %s to "
     225             :                  "%s.",
     226           0 :                  s.data(), s.substr(0, nBufferLen - 1).data());
     227           0 :         s.resize(nBufferLen - 1);
     228             :     }
     229       22170 :     strcpy(pszBuffer, s.data());
     230       22170 : }
     231             : 
     232             : /// Simplified OGRFormatDouble that can be made to adhere to provided
     233             : /// options.
     234       70528 : std::string OGRFormatDouble(double val, const OGRWktOptions &opts, int nDimIdx)
     235             : {
     236             :     // So to have identical cross platform representation.
     237       70528 :     if (std::isinf(val))
     238           2 :         return (val > 0) ? "inf" : "-inf";
     239       70526 :     if (std::isnan(val))
     240          23 :         return "nan";
     241             : 
     242      141006 :     std::ostringstream oss;
     243       70503 :     oss.imbue(std::locale::classic());  // Make sure we output decimal points.
     244       70503 :     bool l_round(opts.round);
     245       70503 :     if (opts.format == OGRWktFormat::F ||
     246       48274 :         (opts.format == OGRWktFormat::Default && fabs(val) < 1))
     247       24966 :         oss << std::fixed;
     248             :     else
     249             :     {
     250             :         // Uppercase because OGC spec says capital 'E'.
     251       45537 :         oss << std::uppercase;
     252       45537 :         l_round = false;
     253             :     }
     254          65 :     oss << std::setprecision(nDimIdx < 3    ? opts.xyPrecision
     255             :                              : nDimIdx == 3 ? opts.zPrecision
     256       70568 :                                             : opts.mPrecision);
     257       70503 :     oss << val;
     258             : 
     259       70503 :     std::string sval = oss.str();
     260             : 
     261       70503 :     if (l_round)
     262       24966 :         sval = intelliround(sval);
     263       70503 :     return removeTrailingZeros(std::move(sval));
     264             : }
     265             : 
     266             : /************************************************************************/
     267             : /*                        OGRMakeWktCoordinate()                        */
     268             : /*                                                                      */
     269             : /*      Format a well known text coordinate, trying to keep the         */
     270             : /*      ASCII representation compact, but accurate.  These rules        */
     271             : /*      will have to tighten up in the future.                          */
     272             : /*                                                                      */
     273             : /*      Currently a new point should require no more than 64            */
     274             : /*      characters barring the X or Y value being extremely large.      */
     275             : /************************************************************************/
     276             : 
     277         961 : void OGRMakeWktCoordinate(char *pszTarget, double x, double y, double z,
     278             :                           int nDimension)
     279             : 
     280             : {
     281             :     std::string wkt =
     282         961 :         OGRMakeWktCoordinate(x, y, z, nDimension, OGRWktOptions());
     283         961 :     memcpy(pszTarget, wkt.data(), wkt.size() + 1);
     284         961 : }
     285             : 
     286       48282 : static bool isInteger(const std::string &s)
     287             : {
     288       48282 :     return s.find_first_not_of("0123456789") == std::string::npos;
     289             : }
     290             : 
     291        3904 : std::string OGRMakeWktCoordinate(double x, double y, double z, int nDimension,
     292             :                                  const OGRWktOptions &opts)
     293             : {
     294        3904 :     std::string wkt;
     295             : 
     296             :     // Why do we do this?  Seems especially strange since we're ADDING
     297             :     // ".0" onto values in the case below.  The "&&" here also seems strange.
     298        5614 :     if (opts.format == OGRWktFormat::Default && CPLIsDoubleAnInt(x) &&
     299        1710 :         CPLIsDoubleAnInt(y))
     300             :     {
     301        1604 :         wkt = std::to_string(static_cast<int>(x));
     302        1604 :         wkt += ' ';
     303        1604 :         wkt += std::to_string(static_cast<int>(y));
     304             :     }
     305             :     else
     306             :     {
     307        2300 :         wkt = OGRFormatDouble(x, opts, 1);
     308             :         // ABELL - Why do we do special formatting?
     309        2300 :         if (isInteger(wkt))
     310         101 :             wkt += ".0";
     311        2300 :         wkt += ' ';
     312             : 
     313        4600 :         std::string yval = OGRFormatDouble(y, opts, 2);
     314        2300 :         if (isInteger(yval))
     315         963 :             yval += ".0";
     316        2300 :         wkt += yval;
     317             :     }
     318             : 
     319        3904 :     if (nDimension == 3)
     320             :     {
     321         916 :         wkt += ' ';
     322         916 :         if (opts.format == OGRWktFormat::Default && CPLIsDoubleAnInt(z))
     323         905 :             wkt += std::to_string(static_cast<int>(z));
     324             :         else
     325             :         {
     326          11 :             wkt += OGRFormatDouble(z, opts, 3);
     327             :         }
     328             :     }
     329        3904 :     return wkt;
     330             : }
     331             : 
     332             : /************************************************************************/
     333             : /*                        OGRMakeWktCoordinateM()                       */
     334             : /*                                                                      */
     335             : /*      Format a well known text coordinate, trying to keep the         */
     336             : /*      ASCII representation compact, but accurate.  These rules        */
     337             : /*      will have to tighten up in the future.                          */
     338             : /*                                                                      */
     339             : /*      Currently a new point should require no more than 64            */
     340             : /*      characters barring the X or Y value being extremely large.      */
     341             : /************************************************************************/
     342             : 
     343           0 : void OGRMakeWktCoordinateM(char *pszTarget, double x, double y, double z,
     344             :                            double m, OGRBoolean hasZ, OGRBoolean hasM)
     345             : 
     346             : {
     347             :     std::string wkt =
     348           0 :         OGRMakeWktCoordinateM(x, y, z, m, hasZ, hasM, OGRWktOptions());
     349           0 :     memcpy(pszTarget, wkt.data(), wkt.size() + 1);
     350           0 : }
     351             : 
     352       37648 : std::string OGRMakeWktCoordinateM(double x, double y, double z, double m,
     353             :                                   OGRBoolean hasZ, OGRBoolean hasM,
     354             :                                   const OGRWktOptions &opts)
     355             : {
     356       37648 :     std::string wkt;
     357       54192 :     if (opts.format == OGRWktFormat::Default && CPLIsDoubleAnInt(x) &&
     358       16544 :         CPLIsDoubleAnInt(y))
     359             :     {
     360       15807 :         wkt = std::to_string(static_cast<int>(x));
     361       15807 :         wkt += ' ';
     362       15807 :         wkt += std::to_string(static_cast<int>(y));
     363             :     }
     364             :     else
     365             :     {
     366       21841 :         wkt = OGRFormatDouble(x, opts, 1);
     367       21841 :         if (isInteger(wkt))
     368         724 :             wkt += ".0";
     369       21841 :         wkt += ' ';
     370             : 
     371       43682 :         std::string yval = OGRFormatDouble(y, opts, 2);
     372       21841 :         if (isInteger(yval))
     373        5835 :             yval += ".0";
     374       21841 :         wkt += yval;
     375             :     }
     376             : 
     377       37648 :     if (hasZ)
     378             :     {
     379        3475 :         wkt += ' ';
     380        3475 :         if (opts.format == OGRWktFormat::Default && CPLIsDoubleAnInt(z))
     381        3416 :             wkt += std::to_string(static_cast<int>(z));
     382             :         else
     383          59 :             wkt += OGRFormatDouble(z, opts, 3);
     384             :     }
     385             : 
     386       37648 :     if (hasM)
     387             :     {
     388        1674 :         wkt += ' ';
     389        1674 :         if (opts.format == OGRWktFormat::Default && CPLIsDoubleAnInt(m))
     390        1668 :             wkt += std::to_string(static_cast<int>(m));
     391             :         else
     392           6 :             wkt += OGRFormatDouble(m, opts, 4);
     393             :     }
     394       37648 :     return wkt;
     395             : }
     396             : 
     397             : /************************************************************************/
     398             : /*                          OGRWktReadToken()                           */
     399             : /*                                                                      */
     400             : /*      Read one token or delimiter and put into token buffer.  Pre     */
     401             : /*      and post white space is swallowed.                              */
     402             : /************************************************************************/
     403             : 
     404     1294550 : const char *OGRWktReadToken(const char *pszInput, char *pszToken)
     405             : 
     406             : {
     407     1294550 :     if (pszInput == nullptr)
     408           0 :         return nullptr;
     409             : 
     410             :     /* -------------------------------------------------------------------- */
     411             :     /*      Swallow pre-white space.                                        */
     412             :     /* -------------------------------------------------------------------- */
     413     1294550 :     while (*pszInput == ' ' || *pszInput == '\t' || *pszInput == '\n' ||
     414     1294550 :            *pszInput == '\r')
     415           3 :         ++pszInput;
     416             : 
     417             :     /* -------------------------------------------------------------------- */
     418             :     /*      If this is a delimiter, read just one character.                */
     419             :     /* -------------------------------------------------------------------- */
     420     1294550 :     if (*pszInput == '(' || *pszInput == ')' || *pszInput == ',')
     421             :     {
     422      429975 :         pszToken[0] = *pszInput;
     423      429975 :         pszToken[1] = '\0';
     424             : 
     425      429975 :         ++pszInput;
     426             :     }
     427             : 
     428             :     /* -------------------------------------------------------------------- */
     429             :     /*      Or if it alpha numeric read till we reach non-alpha numeric     */
     430             :     /*      text.                                                           */
     431             :     /* -------------------------------------------------------------------- */
     432             :     else
     433             :     {
     434      864575 :         int iChar = 0;
     435             : 
     436     6359940 :         while (iChar < OGR_WKT_TOKEN_MAX - 1 &&
     437     6359940 :                ((*pszInput >= 'a' && *pszInput <= 'z') ||
     438     6359180 :                 (*pszInput >= 'A' && *pszInput <= 'Z') ||
     439     4896230 :                 (*pszInput >= '0' && *pszInput <= '9') || *pszInput == '.' ||
     440     1012250 :                 *pszInput == '+' || *pszInput == '-'))
     441             :         {
     442     5495360 :             pszToken[iChar++] = *(pszInput++);
     443             :         }
     444             : 
     445      864575 :         pszToken[iChar++] = '\0';
     446             :     }
     447             : 
     448             :     /* -------------------------------------------------------------------- */
     449             :     /*      Eat any trailing white space.                                   */
     450             :     /* -------------------------------------------------------------------- */
     451     1726170 :     while (*pszInput == ' ' || *pszInput == '\t' || *pszInput == '\n' ||
     452     1294550 :            *pszInput == '\r')
     453      431616 :         ++pszInput;
     454             : 
     455     1294550 :     return pszInput;
     456             : }
     457             : 
     458             : /************************************************************************/
     459             : /*                          OGRWktReadPoints()                          */
     460             : /*                                                                      */
     461             : /*      Read a point string.  The point list must be contained in       */
     462             : /*      brackets and each point pair separated by a comma.              */
     463             : /************************************************************************/
     464             : 
     465           0 : const char *OGRWktReadPoints(const char *pszInput, OGRRawPoint **ppaoPoints,
     466             :                              double **ppadfZ, int *pnMaxPoints,
     467             :                              int *pnPointsRead)
     468             : 
     469             : {
     470           0 :     const char *pszOrigInput = pszInput;
     471           0 :     *pnPointsRead = 0;
     472             : 
     473           0 :     if (pszInput == nullptr)
     474           0 :         return nullptr;
     475             : 
     476             :     /* -------------------------------------------------------------------- */
     477             :     /*      Eat any leading white space.                                    */
     478             :     /* -------------------------------------------------------------------- */
     479           0 :     while (*pszInput == ' ' || *pszInput == '\t')
     480           0 :         ++pszInput;
     481             : 
     482             :     /* -------------------------------------------------------------------- */
     483             :     /*      If this isn't an opening bracket then we have a problem.        */
     484             :     /* -------------------------------------------------------------------- */
     485           0 :     if (*pszInput != '(')
     486             :     {
     487           0 :         CPLDebug("OGR", "Expected '(', but got %s in OGRWktReadPoints().",
     488             :                  pszInput);
     489             : 
     490           0 :         return pszInput;
     491             :     }
     492             : 
     493           0 :     ++pszInput;
     494             : 
     495             :     /* ==================================================================== */
     496             :     /*      This loop reads a single point.  It will continue till we       */
     497             :     /*      run out of well formed points, or a closing bracket is          */
     498             :     /*      encountered.                                                    */
     499             :     /* ==================================================================== */
     500           0 :     char szDelim[OGR_WKT_TOKEN_MAX] = {};
     501             : 
     502           0 :     do
     503             :     {
     504             :         /* --------------------------------------------------------------------
     505             :          */
     506             :         /*      Read the X and Y values, verify they are numeric. */
     507             :         /* --------------------------------------------------------------------
     508             :          */
     509           0 :         char szTokenX[OGR_WKT_TOKEN_MAX] = {};
     510           0 :         char szTokenY[OGR_WKT_TOKEN_MAX] = {};
     511             : 
     512           0 :         pszInput = OGRWktReadToken(pszInput, szTokenX);
     513           0 :         pszInput = OGRWktReadToken(pszInput, szTokenY);
     514             : 
     515           0 :         if ((!isdigit(static_cast<unsigned char>(szTokenX[0])) &&
     516           0 :              szTokenX[0] != '-' && szTokenX[0] != '.') ||
     517           0 :             (!isdigit(static_cast<unsigned char>(szTokenY[0])) &&
     518           0 :              szTokenY[0] != '-' && szTokenY[0] != '.'))
     519           0 :             return nullptr;
     520             : 
     521             :         /* --------------------------------------------------------------------
     522             :          */
     523             :         /*      Do we need to grow the point list to hold this point? */
     524             :         /* --------------------------------------------------------------------
     525             :          */
     526           0 :         if (*pnPointsRead == *pnMaxPoints)
     527             :         {
     528           0 :             *pnMaxPoints = *pnMaxPoints * 2 + 10;
     529           0 :             *ppaoPoints = static_cast<OGRRawPoint *>(
     530           0 :                 CPLRealloc(*ppaoPoints, sizeof(OGRRawPoint) * *pnMaxPoints));
     531             : 
     532           0 :             if (*ppadfZ != nullptr)
     533             :             {
     534           0 :                 *ppadfZ = static_cast<double *>(
     535           0 :                     CPLRealloc(*ppadfZ, sizeof(double) * *pnMaxPoints));
     536             :             }
     537             :         }
     538             : 
     539             :         /* --------------------------------------------------------------------
     540             :          */
     541             :         /*      Add point to list. */
     542             :         /* --------------------------------------------------------------------
     543             :          */
     544           0 :         (*ppaoPoints)[*pnPointsRead].x = CPLAtof(szTokenX);
     545           0 :         (*ppaoPoints)[*pnPointsRead].y = CPLAtof(szTokenY);
     546             : 
     547             :         /* --------------------------------------------------------------------
     548             :          */
     549             :         /*      Do we have a Z coordinate? */
     550             :         /* --------------------------------------------------------------------
     551             :          */
     552           0 :         pszInput = OGRWktReadToken(pszInput, szDelim);
     553             : 
     554           0 :         if (isdigit(static_cast<unsigned char>(szDelim[0])) ||
     555           0 :             szDelim[0] == '-' || szDelim[0] == '.')
     556             :         {
     557           0 :             if (*ppadfZ == nullptr)
     558             :             {
     559           0 :                 *ppadfZ = static_cast<double *>(
     560           0 :                     CPLCalloc(sizeof(double), *pnMaxPoints));
     561             :             }
     562             : 
     563           0 :             (*ppadfZ)[*pnPointsRead] = CPLAtof(szDelim);
     564             : 
     565           0 :             pszInput = OGRWktReadToken(pszInput, szDelim);
     566             :         }
     567           0 :         else if (*ppadfZ != nullptr)
     568             :         {
     569           0 :             (*ppadfZ)[*pnPointsRead] = 0.0;
     570             :         }
     571             : 
     572           0 :         ++(*pnPointsRead);
     573             : 
     574             :         /* --------------------------------------------------------------------
     575             :          */
     576             :         /*      Do we have a M coordinate? */
     577             :         /*      If we do, just skip it. */
     578             :         /* --------------------------------------------------------------------
     579             :          */
     580           0 :         if (isdigit(static_cast<unsigned char>(szDelim[0])) ||
     581           0 :             szDelim[0] == '-' || szDelim[0] == '.')
     582             :         {
     583           0 :             pszInput = OGRWktReadToken(pszInput, szDelim);
     584             :         }
     585             : 
     586             :         /* --------------------------------------------------------------------
     587             :          */
     588             :         /*      Read next delimiter ... it should be a comma if there are */
     589             :         /*      more points. */
     590             :         /* --------------------------------------------------------------------
     591             :          */
     592           0 :         if (szDelim[0] != ')' && szDelim[0] != ',')
     593             :         {
     594           0 :             CPLDebug("OGR",
     595             :                      "Corrupt input in OGRWktReadPoints().  "
     596             :                      "Got `%s' when expecting `,' or `)', near `%s' in %s.",
     597             :                      szDelim, pszInput, pszOrigInput);
     598           0 :             return nullptr;
     599             :         }
     600           0 :     } while (szDelim[0] == ',');
     601             : 
     602           0 :     return pszInput;
     603             : }
     604             : 
     605             : /************************************************************************/
     606             : /*                          OGRWktReadPointsM()                         */
     607             : /*                                                                      */
     608             : /*      Read a point string.  The point list must be contained in       */
     609             : /*      brackets and each point pair separated by a comma.              */
     610             : /************************************************************************/
     611             : 
     612      121224 : const char *OGRWktReadPointsM(const char *pszInput, OGRRawPoint **ppaoPoints,
     613             :                               double **ppadfZ, double **ppadfM, int *flags,
     614             :                               int *pnMaxPoints, int *pnPointsRead)
     615             : 
     616             : {
     617      121224 :     const char *pszOrigInput = pszInput;
     618      240965 :     const bool bNoFlags = !(*flags & OGRGeometry::OGR_G_3D) &&
     619      119741 :                           !(*flags & OGRGeometry::OGR_G_MEASURED);
     620      121224 :     *pnPointsRead = 0;
     621             : 
     622      121224 :     if (pszInput == nullptr)
     623           0 :         return nullptr;
     624             : 
     625             :     /* -------------------------------------------------------------------- */
     626             :     /*      Eat any leading white space.                                    */
     627             :     /* -------------------------------------------------------------------- */
     628      121224 :     while (*pszInput == ' ' || *pszInput == '\t')
     629           0 :         ++pszInput;
     630             : 
     631             :     /* -------------------------------------------------------------------- */
     632             :     /*      If this isn't an opening bracket then we have a problem.        */
     633             :     /* -------------------------------------------------------------------- */
     634      121224 :     if (*pszInput != '(')
     635             :     {
     636          26 :         CPLDebug("OGR", "Expected '(', but got %s in OGRWktReadPointsM().",
     637             :                  pszInput);
     638             : 
     639          26 :         return pszInput;
     640             :     }
     641             : 
     642      121198 :     ++pszInput;
     643             : 
     644             :     /* ==================================================================== */
     645             :     /*      This loop reads a single point.  It will continue till we       */
     646             :     /*      run out of well formed points, or a closing bracket is          */
     647             :     /*      encountered.                                                    */
     648             :     /* ==================================================================== */
     649      121198 :     char szDelim[OGR_WKT_TOKEN_MAX] = {};
     650             : 
     651      105521 :     do
     652             :     {
     653             :         /* --------------------------------------------------------------------
     654             :          */
     655             :         /*      Read the X and Y values, verify they are numeric. */
     656             :         /* --------------------------------------------------------------------
     657             :          */
     658      226719 :         char szTokenX[OGR_WKT_TOKEN_MAX] = {};
     659      226719 :         char szTokenY[OGR_WKT_TOKEN_MAX] = {};
     660             : 
     661      226719 :         pszInput = OGRWktReadToken(pszInput, szTokenX);
     662      226719 :         pszInput = OGRWktReadToken(pszInput, szTokenY);
     663             : 
     664      226719 :         if ((!isdigit(static_cast<unsigned char>(szTokenX[0])) &&
     665       66517 :              szTokenX[0] != '-' && szTokenX[0] != '.' &&
     666          48 :              !EQUAL(szTokenX, "nan")) ||
     667      226677 :             (!isdigit(static_cast<unsigned char>(szTokenY[0])) &&
     668       21511 :              szTokenY[0] != '-' && szTokenY[0] != '.' &&
     669           8 :              !EQUAL(szTokenY, "nan")))
     670          57 :             return nullptr;
     671             : 
     672             :         /* --------------------------------------------------------------------
     673             :          */
     674             :         /*      Do we need to grow the point list to hold this point? */
     675             :         /* --------------------------------------------------------------------
     676             :          */
     677      226675 :         if (*pnPointsRead == *pnMaxPoints)
     678             :         {
     679      120781 :             *pnMaxPoints = *pnMaxPoints * 2 + 10;
     680      120781 :             *ppaoPoints = static_cast<OGRRawPoint *>(
     681      120781 :                 CPLRealloc(*ppaoPoints, sizeof(OGRRawPoint) * *pnMaxPoints));
     682             : 
     683      120781 :             if (*ppadfZ != nullptr)
     684             :             {
     685         163 :                 *ppadfZ = static_cast<double *>(
     686         163 :                     CPLRealloc(*ppadfZ, sizeof(double) * *pnMaxPoints));
     687             :             }
     688             : 
     689      120781 :             if (*ppadfM != nullptr)
     690             :             {
     691          54 :                 *ppadfM = static_cast<double *>(
     692          54 :                     CPLRealloc(*ppadfM, sizeof(double) * *pnMaxPoints));
     693             :             }
     694             :         }
     695             : 
     696             :         /* --------------------------------------------------------------------
     697             :          */
     698             :         /*      Add point to list. */
     699             :         /* --------------------------------------------------------------------
     700             :          */
     701      226675 :         (*ppaoPoints)[*pnPointsRead].x = CPLAtof(szTokenX);
     702      226675 :         (*ppaoPoints)[*pnPointsRead].y = CPLAtof(szTokenY);
     703             : 
     704             :         /* --------------------------------------------------------------------
     705             :          */
     706             :         /*      Read the next token. */
     707             :         /* --------------------------------------------------------------------
     708             :          */
     709      226675 :         pszInput = OGRWktReadToken(pszInput, szDelim);
     710             : 
     711             :         /* --------------------------------------------------------------------
     712             :          */
     713             :         /*      If there are unexpectedly more coordinates, they are Z. */
     714             :         /* --------------------------------------------------------------------
     715             :          */
     716             : 
     717      226675 :         if (!(*flags & OGRGeometry::OGR_G_3D) &&
     718      216345 :             !(*flags & OGRGeometry::OGR_G_MEASURED) &&
     719      215584 :             (isdigit(static_cast<unsigned char>(szDelim[0])) ||
     720      170422 :              szDelim[0] == '-' || szDelim[0] == '.' || EQUAL(szDelim, "nan")))
     721             :         {
     722       45343 :             *flags |= OGRGeometry::OGR_G_3D;
     723             :         }
     724             : 
     725             :         /* --------------------------------------------------------------------
     726             :          */
     727             :         /*      Get Z if flag says so. */
     728             :         /*      Zero out possible remains from earlier strings. */
     729             :         /* --------------------------------------------------------------------
     730             :          */
     731             : 
     732      226675 :         if (*flags & OGRGeometry::OGR_G_3D)
     733             :         {
     734       55673 :             if (*ppadfZ == nullptr)
     735             :             {
     736       46382 :                 *ppadfZ = static_cast<double *>(
     737       46382 :                     CPLCalloc(sizeof(double), *pnMaxPoints));
     738             :             }
     739       55673 :             if (isdigit(static_cast<unsigned char>(szDelim[0])) ||
     740        2605 :                 szDelim[0] == '-' || szDelim[0] == '.' || EQUAL(szDelim, "nan"))
     741             :             {
     742       55594 :                 (*ppadfZ)[*pnPointsRead] = CPLAtof(szDelim);
     743       55594 :                 pszInput = OGRWktReadToken(pszInput, szDelim);
     744             :             }
     745             :             else
     746             :             {
     747          79 :                 (*ppadfZ)[*pnPointsRead] = 0.0;
     748             :             }
     749             :         }
     750      171002 :         else if (*ppadfZ != nullptr)
     751             :         {
     752          43 :             (*ppadfZ)[*pnPointsRead] = 0.0;
     753             :         }
     754             : 
     755             :         /* --------------------------------------------------------------------
     756             :          */
     757             :         /*      If there are unexpectedly even more coordinates, */
     758             :         /*      they are discarded unless there were no flags originally. */
     759             :         /*      This is for backwards compatibility. Should this be an error? */
     760             :         /* --------------------------------------------------------------------
     761             :          */
     762             : 
     763      226675 :         if (!(*flags & OGRGeometry::OGR_G_MEASURED) &&
     764      224591 :             (isdigit(static_cast<unsigned char>(szDelim[0])) ||
     765      224590 :              szDelim[0] == '-' || szDelim[0] == '.' || EQUAL(szDelim, "nan")))
     766             :         {
     767           5 :             if (bNoFlags)
     768             :             {
     769           5 :                 *flags |= OGRGeometry::OGR_G_MEASURED;
     770             :             }
     771             :             else
     772             :             {
     773           0 :                 pszInput = OGRWktReadToken(pszInput, szDelim);
     774             :             }
     775             :         }
     776             : 
     777             :         /* --------------------------------------------------------------------
     778             :          */
     779             :         /*      Get M if flag says so. */
     780             :         /*      Zero out possible remains from earlier strings. */
     781             :         /* --------------------------------------------------------------------
     782             :          */
     783             : 
     784      226675 :         if (*flags & OGRGeometry::OGR_G_MEASURED)
     785             :         {
     786        2089 :             if (*ppadfM == nullptr)
     787             :             {
     788         663 :                 *ppadfM = static_cast<double *>(
     789         663 :                     CPLCalloc(sizeof(double), *pnMaxPoints));
     790             :             }
     791        2089 :             if (isdigit(static_cast<unsigned char>(szDelim[0])) ||
     792         145 :                 szDelim[0] == '-' || szDelim[0] == '.' || EQUAL(szDelim, "nan"))
     793             :             {
     794        2083 :                 (*ppadfM)[*pnPointsRead] = CPLAtof(szDelim);
     795        2083 :                 pszInput = OGRWktReadToken(pszInput, szDelim);
     796             :             }
     797             :             else
     798             :             {
     799           6 :                 (*ppadfM)[*pnPointsRead] = 0.0;
     800             :             }
     801             :         }
     802      224586 :         else if (*ppadfM != nullptr)
     803             :         {
     804           0 :             (*ppadfM)[*pnPointsRead] = 0.0;
     805             :         }
     806             : 
     807             :         /* --------------------------------------------------------------------
     808             :          */
     809             :         /*      If there are still more coordinates and we do not have Z */
     810             :         /*      then we have a case of flags == M and four coordinates. */
     811             :         /*      This is allowed in BNF. */
     812             :         /* --------------------------------------------------------------------
     813             :          */
     814             : 
     815      226675 :         if (!(*flags & OGRGeometry::OGR_G_3D) &&
     816      171002 :             (isdigit(static_cast<unsigned char>(szDelim[0])) ||
     817      171002 :              szDelim[0] == '-' || szDelim[0] == '.' || EQUAL(szDelim, "nan")))
     818             :         {
     819           0 :             *flags |= OGRGeometry::OGR_G_3D;
     820           0 :             if (*ppadfZ == nullptr)
     821             :             {
     822           0 :                 *ppadfZ = static_cast<double *>(
     823           0 :                     CPLCalloc(sizeof(double), *pnMaxPoints));
     824             :             }
     825           0 :             (*ppadfZ)[*pnPointsRead] = (*ppadfM)[*pnPointsRead];
     826           0 :             (*ppadfM)[*pnPointsRead] = CPLAtof(szDelim);
     827           0 :             pszInput = OGRWktReadToken(pszInput, szDelim);
     828             :         }
     829             : 
     830             :         /* --------------------------------------------------------------------
     831             :          */
     832             :         /*      Increase points index. */
     833             :         /* --------------------------------------------------------------------
     834             :          */
     835      226675 :         ++(*pnPointsRead);
     836             : 
     837             :         /* --------------------------------------------------------------------
     838             :          */
     839             :         /*      The next delimiter should be a comma or an ending bracket. */
     840             :         /* --------------------------------------------------------------------
     841             :          */
     842      226675 :         if (szDelim[0] != ')' && szDelim[0] != ',')
     843             :         {
     844          13 :             CPLDebug("OGR",
     845             :                      "Corrupt input in OGRWktReadPointsM()  "
     846             :                      "Got `%s' when expecting `,' or `)', near `%s' in %s.",
     847             :                      szDelim, pszInput, pszOrigInput);
     848          13 :             return nullptr;
     849             :         }
     850      226662 :     } while (szDelim[0] == ',');
     851             : 
     852      121141 :     return pszInput;
     853             : }
     854             : 
     855             : /************************************************************************/
     856             : /*                             OGRMalloc()                              */
     857             : /*                                                                      */
     858             : /*      Cover for CPLMalloc()                                           */
     859             : /************************************************************************/
     860             : 
     861           0 : void *OGRMalloc(size_t size)
     862             : 
     863             : {
     864           0 :     return CPLMalloc(size);
     865             : }
     866             : 
     867             : /************************************************************************/
     868             : /*                             OGRCalloc()                              */
     869             : /*                                                                      */
     870             : /*      Cover for CPLCalloc()                                           */
     871             : /************************************************************************/
     872             : 
     873           0 : void *OGRCalloc(size_t count, size_t size)
     874             : 
     875             : {
     876           0 :     return CPLCalloc(count, size);
     877             : }
     878             : 
     879             : /************************************************************************/
     880             : /*                             OGRRealloc()                             */
     881             : /*                                                                      */
     882             : /*      Cover for CPLRealloc()                                          */
     883             : /************************************************************************/
     884             : 
     885           0 : void *OGRRealloc(void *pOld, size_t size)
     886             : 
     887             : {
     888           0 :     return CPLRealloc(pOld, size);
     889             : }
     890             : 
     891             : /************************************************************************/
     892             : /*                              OGRFree()                               */
     893             : /*                                                                      */
     894             : /*      Cover for CPLFree().                                            */
     895             : /************************************************************************/
     896             : 
     897           0 : void OGRFree(void *pMemory)
     898             : 
     899             : {
     900           0 :     CPLFree(pMemory);
     901           0 : }
     902             : 
     903             : /**
     904             :  * \fn OGRGeneralCmdLineProcessor(int, char***, int)
     905             :  * General utility option processing.
     906             :  *
     907             :  * This function is intended to provide a variety of generic commandline
     908             :  * options for all OGR commandline utilities.  It takes care of the following
     909             :  * commandline options:
     910             :  *
     911             :  *  --version: report version of GDAL in use.
     912             :  *  --license: report GDAL license info.
     913             :  *  --format [format]: report details of one format driver.
     914             :  *  --formats: report all format drivers configured.
     915             :  *  --optfile filename: expand an option file into the argument list.
     916             :  *  --config key value: set system configuration option.
     917             :  *  --debug [on/off/value]: set debug level.
     918             :  *  --pause: Pause for user input (allows time to attach debugger)
     919             :  *  --locale [locale]: Install a locale using setlocale() (debugging)
     920             :  *  --help-general: report detailed help on general options.
     921             :  *
     922             :  * The argument array is replaced "in place" and should be freed with
     923             :  * CSLDestroy() when no longer needed.  The typical usage looks something
     924             :  * like the following.  Note that the formats should be registered so that
     925             :  * the --formats option will work properly.
     926             :  *
     927             :  *  int main( int argc, char ** argv )
     928             :  *  {
     929             :  *    OGRRegisterAll();
     930             :  *
     931             :  *    argc = OGRGeneralCmdLineProcessor( argc, &argv, 0 );
     932             :  *    if( argc < 1 )
     933             :  *        exit( -argc );
     934             :  *
     935             :  * @param nArgc number of values in the argument list.
     936             :  * @param ppapszArgv pointer to the argument list array (will be updated in
     937             :  * place).
     938             :  * @param nOptions unused.
     939             :  *
     940             :  * @return updated nArgc argument count.  Return of 0 requests terminate
     941             :  * without error, return of -1 requests exit with error code.
     942             :  */
     943             : 
     944         306 : int OGRGeneralCmdLineProcessor(int nArgc, char ***ppapszArgv,
     945             :                                CPL_UNUSED int nOptions)
     946             : 
     947             : {
     948         306 :     return GDALGeneralCmdLineProcessor(nArgc, ppapszArgv, GDAL_OF_VECTOR);
     949             : }
     950             : 
     951             : /************************************************************************/
     952             : /*                       OGRTimezoneToTZFlag()                          */
     953             : /************************************************************************/
     954             : 
     955             : /** \brief Converts a text timezone into OGR TZFlag integer representation.
     956             :  *
     957             :  * @param pszTZ "UTC", "Etc/UTC", or "(+/-)[0-9][0-9](:?)[0-9][0-9]"
     958             :  * @param bEmitErrorIfUnhandledFormat Whether to emit an error if pszTZ is
     959             :  *                                    a non-empty string with unrecognized
     960             :  *                                    format.
     961             :  */
     962        1251 : int OGRTimezoneToTZFlag(const char *pszTZ, bool bEmitErrorIfUnhandledFormat)
     963             : {
     964        1251 :     int nTZFlag = OGR_TZFLAG_UNKNOWN;
     965        1251 :     const size_t nTZLen = strlen(pszTZ);
     966        1251 :     if (strcmp(pszTZ, "UTC") == 0 || strcmp(pszTZ, "Etc/UTC") == 0)
     967             :     {
     968         193 :         nTZFlag = OGR_TZFLAG_UTC;
     969             :     }
     970        1058 :     else if ((pszTZ[0] == '+' || pszTZ[0] == '-') &&
     971         410 :              ((nTZLen == 6 && pszTZ[3] == ':') ||
     972           0 :               (nTZLen == 5 && pszTZ[3] >= '0' && pszTZ[3] <= '9')))
     973             :     {
     974         410 :         int nTZHour = atoi(pszTZ + 1);
     975         410 :         int nTZMin = atoi(pszTZ + (nTZLen == 6 ? 4 : 3));
     976         410 :         if (nTZHour >= 0 && nTZHour <= 14 && nTZMin >= 0 && nTZMin < 60 &&
     977         410 :             (nTZMin % 15) == 0)
     978             :         {
     979         410 :             nTZFlag = (nTZHour * 4) + (nTZMin / 15);
     980         410 :             if (pszTZ[0] == '+')
     981             :             {
     982         209 :                 nTZFlag = OGR_TZFLAG_UTC + nTZFlag;
     983             :             }
     984             :             else
     985             :             {
     986         201 :                 nTZFlag = OGR_TZFLAG_UTC - nTZFlag;
     987             :             }
     988         410 :         }
     989             :     }
     990         648 :     else if (pszTZ[0] != 0 && bEmitErrorIfUnhandledFormat)
     991             :     {
     992           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Unrecognized timezone: '%s'",
     993             :                  pszTZ);
     994             :     }
     995        1251 :     return nTZFlag;
     996             : }
     997             : 
     998             : /************************************************************************/
     999             : /*                       OGRTZFlagToTimezone()                          */
    1000             : /************************************************************************/
    1001             : 
    1002             : /** \brief Converts a OGR TZFlag integer representation into a string
    1003             :  *
    1004             :  * @param nTZFlag OGR TZFlag integer (only ones with
    1005             :  *        value > OGR_TZFLAG_MIXED_TZ will yield a non-empty output)
    1006             :  * @param pszUTCRepresentation String to return if nTZFlag == OGR_TZFLAG_UTC.
    1007             :  *                             Typically "UTC", "Z", or "+00:00"
    1008             :  */
    1009          14 : std::string OGRTZFlagToTimezone(int nTZFlag, const char *pszUTCRepresentation)
    1010             : {
    1011          14 :     if (nTZFlag == OGR_TZFLAG_UTC)
    1012             :     {
    1013           0 :         return pszUTCRepresentation;
    1014             :     }
    1015          14 :     else if (nTZFlag > OGR_TZFLAG_MIXED_TZ)
    1016             :     {
    1017             :         char chSign;
    1018          14 :         const int nOffset = (nTZFlag - OGR_TZFLAG_UTC) * 15;
    1019          14 :         int nHours = static_cast<int>(nOffset / 60);  // Round towards zero.
    1020          14 :         const int nMinutes = std::abs(nOffset - nHours * 60);
    1021             : 
    1022          14 :         if (nOffset < 0)
    1023             :         {
    1024           7 :             chSign = '-';
    1025           7 :             nHours = std::abs(nHours);
    1026             :         }
    1027             :         else
    1028             :         {
    1029           7 :             chSign = '+';
    1030             :         }
    1031          14 :         return CPLSPrintf("%c%02d:%02d", chSign, nHours, nMinutes);
    1032             :     }
    1033             :     else
    1034             :     {
    1035           0 :         return std::string();
    1036             :     }
    1037             : }
    1038             : 
    1039             : /************************************************************************/
    1040             : /*                            OGRParseDate()                            */
    1041             : /*                                                                      */
    1042             : /*      Parse a variety of text date formats into an OGRField.          */
    1043             : /************************************************************************/
    1044             : 
    1045             : /**
    1046             :  * Parse date string.
    1047             :  *
    1048             :  * This function attempts to parse a date string in a variety of formats
    1049             :  * into the OGRField.Date format suitable for use with OGR.  Generally
    1050             :  * speaking this function is expecting values like:
    1051             :  *
    1052             :  *   YYYY-MM-DD HH:MM:SS(.sss)?+nn
    1053             :  *   or YYYY-MM-DDTHH:MM:SS(.sss)?Z (ISO 8601 format)
    1054             :  *   or YYYY-MM-DDZ
    1055             :  *   or THH:MM(:SS(.sss)?)?Z? (ISO 8601 extended format)
    1056             :  *   or THHMM(SS(.sss)?)?Z? (ISO 8601 basic format)
    1057             :  *
    1058             :  * The seconds may also have a decimal portion (parsed as milliseconds).  And
    1059             :  * just dates (YYYY-MM-DD) or just times (HH:MM:SS[.sss]) are also supported.
    1060             :  * The date may also be in YYYY/MM/DD format.  If the year is less than 100
    1061             :  * and greater than 30 a "1900" century value will be set.  If it is less than
    1062             :  * 30 and greater than -1 then a "2000" century value will be set.  In
    1063             :  * the future this function may be generalized, and additional control
    1064             :  * provided through nOptions, but an nOptions value of "0" should always do
    1065             :  * a reasonable default form of processing.
    1066             :  *
    1067             :  * The value of psField will be indeterminate if the function fails (returns
    1068             :  * FALSE).
    1069             :  *
    1070             :  * @param pszInput the input date string.
    1071             :  * @param psField the OGRField that will be updated with the parsed result.
    1072             :  * @param nOptions parsing options. 0 or OGRPARSEDATE_OPTION_LAX
    1073             :  *
    1074             :  * @return TRUE if apparently successful or FALSE on failure.
    1075             :  */
    1076             : 
    1077       12489 : int OGRParseDate(const char *pszInput, OGRField *psField, int nOptions)
    1078             : {
    1079       12489 :     psField->Date.Year = 0;
    1080       12489 :     psField->Date.Month = 0;
    1081       12489 :     psField->Date.Day = 0;
    1082       12489 :     psField->Date.Hour = 0;
    1083       12489 :     psField->Date.Minute = 0;
    1084       12489 :     psField->Date.Second = 0;
    1085       12489 :     psField->Date.TZFlag = 0;
    1086       12489 :     psField->Date.Reserved = 0;
    1087             : 
    1088             :     /* -------------------------------------------------------------------- */
    1089             :     /*      Do we have a date?                                              */
    1090             :     /* -------------------------------------------------------------------- */
    1091       12490 :     while (*pszInput == ' ')
    1092           1 :         ++pszInput;
    1093             : 
    1094       12489 :     bool bGotSomething = false;
    1095       12489 :     bool bTFound = false;
    1096       12489 :     if (strchr(pszInput, '-') || strchr(pszInput, '/'))
    1097             :     {
    1098        7980 :         if (!(*pszInput == '-' || *pszInput == '+' ||
    1099        7979 :               (*pszInput >= '0' && *pszInput <= '9')))
    1100           9 :             return FALSE;
    1101        7971 :         int nYear = atoi(pszInput);
    1102       15941 :         if (nYear > std::numeric_limits<GInt16>::max() ||
    1103        7970 :             nYear < std::numeric_limits<GInt16>::min())
    1104             :         {
    1105           1 :             CPLError(CE_Failure, CPLE_NotSupported,
    1106             :                      "Years < %d or > %d are not supported",
    1107           1 :                      std::numeric_limits<GInt16>::min(),
    1108           1 :                      std::numeric_limits<GInt16>::max());
    1109           1 :             return FALSE;
    1110             :         }
    1111        7970 :         psField->Date.Year = static_cast<GInt16>(nYear);
    1112        7970 :         if ((pszInput[1] == '-' || pszInput[1] == '/') ||
    1113        7968 :             (pszInput[1] != '\0' && (pszInput[2] == '-' || pszInput[2] == '/')))
    1114             :         {
    1115           6 :             if (psField->Date.Year < 100 && psField->Date.Year >= 30)
    1116           1 :                 psField->Date.Year += 1900;
    1117           5 :             else if (psField->Date.Year < 30 && psField->Date.Year >= 0)
    1118           5 :                 psField->Date.Year += 2000;
    1119             :         }
    1120             : 
    1121        7970 :         if (*pszInput == '-')
    1122           1 :             ++pszInput;
    1123       39835 :         while (*pszInput >= '0' && *pszInput <= '9')
    1124       31865 :             ++pszInput;
    1125        7970 :         if (*pszInput != '-' && *pszInput != '/')
    1126           1 :             return FALSE;
    1127             :         else
    1128        7969 :             ++pszInput;
    1129             : 
    1130        7969 :         if (!(*pszInput >= '0' && *pszInput <= '9'))
    1131           2 :             return FALSE;
    1132        7967 :         if (!(pszInput[1] >= '0' && pszInput[1] <= '9'))
    1133             :         {
    1134          16 :             if ((nOptions & OGRPARSEDATE_OPTION_LAX) == 0)
    1135           1 :                 return FALSE;
    1136          15 :             const int nMonth = (pszInput[0] - '0');
    1137          15 :             if (nMonth == 0)
    1138           1 :                 return FALSE;
    1139          14 :             psField->Date.Month = static_cast<GByte>(nMonth);
    1140          14 :             ++pszInput;
    1141             :         }
    1142             :         else
    1143             :         {
    1144        7951 :             const int nMonth = (pszInput[0] - '0') * 10 + (pszInput[1] - '0');
    1145        7951 :             if (nMonth == 0 || nMonth > 12)
    1146           8 :                 return FALSE;
    1147        7943 :             psField->Date.Month = static_cast<GByte>(nMonth);
    1148             : 
    1149        7943 :             pszInput += 2;
    1150             :         }
    1151        7957 :         if (*pszInput != '-' && *pszInput != '/')
    1152           1 :             return FALSE;
    1153             :         else
    1154        7956 :             ++pszInput;
    1155             : 
    1156        7956 :         if (!(*pszInput >= '0' && *pszInput <= '9'))
    1157           3 :             return FALSE;
    1158        7953 :         if (!(pszInput[1] >= '0' && pszInput[1] <= '9'))
    1159             :         {
    1160           8 :             if ((nOptions & OGRPARSEDATE_OPTION_LAX) == 0)
    1161           1 :                 return FALSE;
    1162           7 :             const int nDay = (pszInput[0] - '0');
    1163           7 :             if (nDay == 0)
    1164           1 :                 return FALSE;
    1165           6 :             psField->Date.Day = static_cast<GByte>(nDay);
    1166           6 :             ++pszInput;
    1167             :         }
    1168             :         else
    1169             :         {
    1170        7945 :             const int nDay = (pszInput[0] - '0') * 10 + (pszInput[1] - '0');
    1171        7945 :             if (nDay == 0 || nDay > 31)
    1172           5 :                 return FALSE;
    1173        7940 :             psField->Date.Day = static_cast<GByte>(nDay);
    1174             : 
    1175        7940 :             pszInput += 2;
    1176             :         }
    1177        7946 :         if (*pszInput == '\0')
    1178        3382 :             return TRUE;
    1179             : 
    1180        4564 :         bGotSomething = true;
    1181             : 
    1182             :         // If ISO 8601 format.
    1183        4564 :         if (*pszInput == 'T')
    1184             :         {
    1185        2061 :             bTFound = true;
    1186        2061 :             ++pszInput;
    1187             :         }
    1188        2503 :         else if (*pszInput == 'Z')
    1189           1 :             return TRUE;
    1190        2502 :         else if (*pszInput != ' ')
    1191           1 :             return FALSE;
    1192             :     }
    1193             : 
    1194             :     /* -------------------------------------------------------------------- */
    1195             :     /*      Do we have a time?                                              */
    1196             :     /* -------------------------------------------------------------------- */
    1197       11636 :     while (*pszInput == ' ')
    1198        2565 :         ++pszInput;
    1199        9071 :     if (*pszInput == 'T')
    1200             :     {
    1201          15 :         bTFound = true;
    1202          15 :         ++pszInput;
    1203             :     }
    1204             : 
    1205        9071 :     if (bTFound || strchr(pszInput, ':'))
    1206             :     {
    1207        8384 :         if (!(*pszInput >= '0' && *pszInput <= '9'))
    1208          27 :             return FALSE;
    1209        8357 :         if (!(pszInput[1] >= '0' && pszInput[1] <= '9'))
    1210             :         {
    1211          10 :             if ((nOptions & OGRPARSEDATE_OPTION_LAX) == 0)
    1212           4 :                 return FALSE;
    1213             : 
    1214           6 :             if (!((bTFound || pszInput[1] == ':')))
    1215           1 :                 return FALSE;
    1216           5 :             const int nHour = (pszInput[0] - '0');
    1217           5 :             psField->Date.Hour = static_cast<GByte>(nHour);
    1218             : 
    1219           5 :             pszInput++;
    1220             :         }
    1221             :         else
    1222             :         {
    1223        8347 :             if (!((bTFound || pszInput[2] == ':')))
    1224           0 :                 return FALSE;
    1225        8347 :             const int nHour = (pszInput[0] - '0') * 10 + (pszInput[1] - '0');
    1226        8347 :             if (nHour > 23)
    1227           1 :                 return FALSE;
    1228        8346 :             psField->Date.Hour = static_cast<GByte>(nHour);
    1229             : 
    1230        8346 :             pszInput += 2;
    1231             :         }
    1232        8351 :         if (*pszInput == ':')
    1233        8348 :             ++pszInput;
    1234             : 
    1235        8351 :         if (!(*pszInput >= '0' && *pszInput <= '9'))
    1236           6 :             return FALSE;
    1237        8345 :         if (!(pszInput[1] >= '0' && pszInput[1] <= '9'))
    1238             :         {
    1239           4 :             if ((nOptions & OGRPARSEDATE_OPTION_LAX) == 0)
    1240           1 :                 return FALSE;
    1241             : 
    1242           3 :             const int nMinute = (pszInput[0] - '0');
    1243           3 :             psField->Date.Minute = static_cast<GByte>(nMinute);
    1244             : 
    1245           3 :             pszInput++;
    1246             :         }
    1247             :         else
    1248             :         {
    1249        8341 :             const int nMinute = (pszInput[0] - '0') * 10 + (pszInput[1] - '0');
    1250        8341 :             if (nMinute > 59)
    1251           1 :                 return FALSE;
    1252        8340 :             psField->Date.Minute = static_cast<GByte>(nMinute);
    1253             : 
    1254        8340 :             pszInput += 2;
    1255             :         }
    1256             : 
    1257        8343 :         if ((bTFound && *pszInput >= '0' && *pszInput <= '9') ||
    1258        8341 :             *pszInput == ':')
    1259             :         {
    1260        8327 :             if (*pszInput == ':')
    1261        8325 :                 ++pszInput;
    1262             : 
    1263        8327 :             if (!(*pszInput >= '0' && *pszInput <= '9' &&
    1264        8325 :                   (((nOptions & OGRPARSEDATE_OPTION_LAX) != 0) ||
    1265        8321 :                    (pszInput[1] >= '0' && pszInput[1] <= '9'))))
    1266           3 :                 return FALSE;
    1267        8324 :             const double dfSeconds = CPLAtof(pszInput);
    1268             :             // We accept second=60 for leap seconds
    1269        8324 :             if (dfSeconds > 60.0)
    1270           1 :                 return FALSE;
    1271        8323 :             psField->Date.Second = static_cast<float>(dfSeconds);
    1272             : 
    1273        8323 :             pszInput += 2;
    1274        8323 :             if (*pszInput == '.')
    1275             :             {
    1276        1899 :                 ++pszInput;
    1277        7602 :                 while (*pszInput >= '0' && *pszInput <= '9')
    1278             :                 {
    1279        5703 :                     ++pszInput;
    1280             :                 }
    1281             :             }
    1282             : 
    1283             :             // If ISO 8601 format.
    1284        8323 :             if (*pszInput == 'Z')
    1285             :             {
    1286         372 :                 psField->Date.TZFlag = 100;
    1287             :             }
    1288             :         }
    1289             : 
    1290        8339 :         bGotSomething = true;
    1291             :     }
    1292         687 :     else if (bGotSomething && *pszInput != '\0')
    1293           1 :         return FALSE;
    1294             : 
    1295             :     // No date or time!
    1296        9025 :     if (!bGotSomething)
    1297         625 :         return FALSE;
    1298             : 
    1299             :     /* -------------------------------------------------------------------- */
    1300             :     /*      Do we have a timezone?                                          */
    1301             :     /* -------------------------------------------------------------------- */
    1302        8404 :     while (*pszInput == ' ')
    1303           4 :         ++pszInput;
    1304             : 
    1305        8400 :     if (*pszInput == '-' || *pszInput == '+')
    1306             :     {
    1307             :         // +HH integral offset
    1308         262 :         if (strlen(pszInput) <= 3)
    1309             :         {
    1310         120 :             psField->Date.TZFlag = static_cast<GByte>(100 + atoi(pszInput) * 4);
    1311             :         }
    1312         142 :         else if (pszInput[3] == ':'  // +HH:MM offset
    1313          72 :                  && atoi(pszInput + 4) % 15 == 0)
    1314             :         {
    1315          72 :             psField->Date.TZFlag = static_cast<GByte>(
    1316          72 :                 100 + atoi(pszInput + 1) * 4 + (atoi(pszInput + 4) / 15));
    1317             : 
    1318          72 :             if (pszInput[0] == '-')
    1319          19 :                 psField->Date.TZFlag = -1 * (psField->Date.TZFlag - 100) + 100;
    1320             :         }
    1321          70 :         else if (isdigit(static_cast<unsigned char>(pszInput[3])) &&
    1322          70 :                  isdigit(
    1323          70 :                      static_cast<unsigned char>(pszInput[4]))  // +HHMM offset
    1324          69 :                  && atoi(pszInput + 3) % 15 == 0)
    1325             :         {
    1326          69 :             psField->Date.TZFlag = static_cast<GByte>(
    1327          69 :                 100 + static_cast<GByte>(CPLScanLong(pszInput + 1, 2)) * 4 +
    1328          69 :                 (atoi(pszInput + 3) / 15));
    1329             : 
    1330          69 :             if (pszInput[0] == '-')
    1331           0 :                 psField->Date.TZFlag = -1 * (psField->Date.TZFlag - 100) + 100;
    1332             :         }
    1333           1 :         else if (isdigit(static_cast<unsigned char>(pszInput[3])) &&
    1334           1 :                  pszInput[4] == '\0'  // +HMM offset
    1335           1 :                  && atoi(pszInput + 2) % 15 == 0)
    1336             :         {
    1337           1 :             psField->Date.TZFlag = static_cast<GByte>(
    1338           1 :                 100 + static_cast<GByte>(CPLScanLong(pszInput + 1, 1)) * 4 +
    1339           1 :                 (atoi(pszInput + 2) / 15));
    1340             : 
    1341           1 :             if (pszInput[0] == '-')
    1342           0 :                 psField->Date.TZFlag = -1 * (psField->Date.TZFlag - 100) + 100;
    1343             :         }
    1344             :         // otherwise ignore any timezone info.
    1345             :     }
    1346             : 
    1347        8400 :     return TRUE;
    1348             : }
    1349             : 
    1350             : /************************************************************************/
    1351             : /*               OGRParseDateTimeYYYYMMDDTHHMMZ()                       */
    1352             : /************************************************************************/
    1353             : 
    1354           9 : bool OGRParseDateTimeYYYYMMDDTHHMMZ(const char *pszInput, size_t nLen,
    1355             :                                     OGRField *psField)
    1356             : {
    1357             :     // Detect "YYYY-MM-DDTHH:MM[Z]" (16 or 17 characters)
    1358           9 :     if ((nLen == 16 || (nLen == 17 && pszInput[16] == 'Z')) &&
    1359           4 :         pszInput[4] == '-' && pszInput[7] == '-' && pszInput[10] == 'T' &&
    1360           4 :         pszInput[13] == ':' && static_cast<unsigned>(pszInput[0] - '0') <= 9 &&
    1361           4 :         static_cast<unsigned>(pszInput[1] - '0') <= 9 &&
    1362           4 :         static_cast<unsigned>(pszInput[2] - '0') <= 9 &&
    1363           4 :         static_cast<unsigned>(pszInput[3] - '0') <= 9 &&
    1364           4 :         static_cast<unsigned>(pszInput[5] - '0') <= 9 &&
    1365           4 :         static_cast<unsigned>(pszInput[6] - '0') <= 9 &&
    1366           4 :         static_cast<unsigned>(pszInput[8] - '0') <= 9 &&
    1367           4 :         static_cast<unsigned>(pszInput[9] - '0') <= 9 &&
    1368           4 :         static_cast<unsigned>(pszInput[11] - '0') <= 9 &&
    1369           4 :         static_cast<unsigned>(pszInput[12] - '0') <= 9 &&
    1370           4 :         static_cast<unsigned>(pszInput[14] - '0') <= 9 &&
    1371           4 :         static_cast<unsigned>(pszInput[15] - '0') <= 9)
    1372             :     {
    1373           4 :         psField->Date.Year = static_cast<GInt16>(
    1374           4 :             ((((pszInput[0] - '0') * 10 + (pszInput[1] - '0')) * 10) +
    1375           4 :              (pszInput[2] - '0')) *
    1376           4 :                 10 +
    1377           4 :             (pszInput[3] - '0'));
    1378           4 :         psField->Date.Month =
    1379           4 :             static_cast<GByte>((pszInput[5] - '0') * 10 + (pszInput[6] - '0'));
    1380           4 :         psField->Date.Day =
    1381           4 :             static_cast<GByte>((pszInput[8] - '0') * 10 + (pszInput[9] - '0'));
    1382           4 :         psField->Date.Hour = static_cast<GByte>((pszInput[11] - '0') * 10 +
    1383           4 :                                                 (pszInput[12] - '0'));
    1384           4 :         psField->Date.Minute = static_cast<GByte>((pszInput[14] - '0') * 10 +
    1385           4 :                                                   (pszInput[15] - '0'));
    1386           4 :         psField->Date.Second = 0.0f;
    1387           4 :         psField->Date.TZFlag = nLen == 16 ? 0 : 100;
    1388           4 :         psField->Date.Reserved = 0;
    1389           4 :         if (psField->Date.Month == 0 || psField->Date.Month > 12 ||
    1390           4 :             psField->Date.Day == 0 || psField->Date.Day > 31 ||
    1391           4 :             psField->Date.Hour > 23 || psField->Date.Minute > 59)
    1392             :         {
    1393           1 :             return false;
    1394             :         }
    1395           3 :         return true;
    1396             :     }
    1397             : 
    1398           5 :     return false;
    1399             : }
    1400             : 
    1401             : /************************************************************************/
    1402             : /*               OGRParseDateTimeYYYYMMDDTHHMMSSZ()                     */
    1403             : /************************************************************************/
    1404             : 
    1405          36 : bool OGRParseDateTimeYYYYMMDDTHHMMSSZ(const char *pszInput, size_t nLen,
    1406             :                                       OGRField *psField)
    1407             : {
    1408             :     // Detect "YYYY-MM-DDTHH:MM:SS[Z]" (19 or 20 characters)
    1409          36 :     if ((nLen == 19 || (nLen == 20 && pszInput[19] == 'Z')) &&
    1410           7 :         pszInput[4] == '-' && pszInput[7] == '-' && pszInput[10] == 'T' &&
    1411           7 :         pszInput[13] == ':' && pszInput[16] == ':' &&
    1412           7 :         static_cast<unsigned>(pszInput[0] - '0') <= 9 &&
    1413           7 :         static_cast<unsigned>(pszInput[1] - '0') <= 9 &&
    1414           7 :         static_cast<unsigned>(pszInput[2] - '0') <= 9 &&
    1415           7 :         static_cast<unsigned>(pszInput[3] - '0') <= 9 &&
    1416           7 :         static_cast<unsigned>(pszInput[5] - '0') <= 9 &&
    1417           7 :         static_cast<unsigned>(pszInput[6] - '0') <= 9 &&
    1418           7 :         static_cast<unsigned>(pszInput[8] - '0') <= 9 &&
    1419           7 :         static_cast<unsigned>(pszInput[9] - '0') <= 9 &&
    1420           7 :         static_cast<unsigned>(pszInput[11] - '0') <= 9 &&
    1421           7 :         static_cast<unsigned>(pszInput[12] - '0') <= 9 &&
    1422           7 :         static_cast<unsigned>(pszInput[14] - '0') <= 9 &&
    1423           7 :         static_cast<unsigned>(pszInput[15] - '0') <= 9 &&
    1424           7 :         static_cast<unsigned>(pszInput[17] - '0') <= 9 &&
    1425           7 :         static_cast<unsigned>(pszInput[18] - '0') <= 9)
    1426             :     {
    1427           7 :         psField->Date.Year = static_cast<GInt16>(
    1428           7 :             ((((pszInput[0] - '0') * 10 + (pszInput[1] - '0')) * 10) +
    1429           7 :              (pszInput[2] - '0')) *
    1430           7 :                 10 +
    1431           7 :             (pszInput[3] - '0'));
    1432           7 :         psField->Date.Month =
    1433           7 :             static_cast<GByte>((pszInput[5] - '0') * 10 + (pszInput[6] - '0'));
    1434           7 :         psField->Date.Day =
    1435           7 :             static_cast<GByte>((pszInput[8] - '0') * 10 + (pszInput[9] - '0'));
    1436           7 :         psField->Date.Hour = static_cast<GByte>((pszInput[11] - '0') * 10 +
    1437           7 :                                                 (pszInput[12] - '0'));
    1438           7 :         psField->Date.Minute = static_cast<GByte>((pszInput[14] - '0') * 10 +
    1439           7 :                                                   (pszInput[15] - '0'));
    1440           7 :         psField->Date.Second = static_cast<float>(
    1441           7 :             ((pszInput[17] - '0') * 10 + (pszInput[18] - '0')));
    1442           7 :         psField->Date.TZFlag = nLen == 19 ? 0 : 100;
    1443           7 :         psField->Date.Reserved = 0;
    1444           7 :         if (psField->Date.Month == 0 || psField->Date.Month > 12 ||
    1445           7 :             psField->Date.Day == 0 || psField->Date.Day > 31 ||
    1446           7 :             psField->Date.Hour > 23 || psField->Date.Minute > 59 ||
    1447           7 :             psField->Date.Second >= 61.0f)
    1448             :         {
    1449           1 :             return false;
    1450             :         }
    1451           6 :         return true;
    1452             :     }
    1453             : 
    1454          29 :     return false;
    1455             : }
    1456             : 
    1457             : /************************************************************************/
    1458             : /*              OGRParseDateTimeYYYYMMDDTHHMMSSsssZ()                   */
    1459             : /************************************************************************/
    1460             : 
    1461         501 : bool OGRParseDateTimeYYYYMMDDTHHMMSSsssZ(const char *pszInput, size_t nLen,
    1462             :                                          OGRField *psField)
    1463             : {
    1464             :     // Detect "YYYY-MM-DDTHH:MM:SS.SSS[Z]" (23 or 24 characters)
    1465         501 :     if ((nLen == 23 || (nLen == 24 && pszInput[23] == 'Z')) &&
    1466         493 :         pszInput[4] == '-' && pszInput[7] == '-' && pszInput[10] == 'T' &&
    1467         493 :         pszInput[13] == ':' && pszInput[16] == ':' && pszInput[19] == '.' &&
    1468         493 :         static_cast<unsigned>(pszInput[0] - '0') <= 9 &&
    1469         493 :         static_cast<unsigned>(pszInput[1] - '0') <= 9 &&
    1470         493 :         static_cast<unsigned>(pszInput[2] - '0') <= 9 &&
    1471         493 :         static_cast<unsigned>(pszInput[3] - '0') <= 9 &&
    1472         493 :         static_cast<unsigned>(pszInput[5] - '0') <= 9 &&
    1473         493 :         static_cast<unsigned>(pszInput[6] - '0') <= 9 &&
    1474         493 :         static_cast<unsigned>(pszInput[8] - '0') <= 9 &&
    1475         493 :         static_cast<unsigned>(pszInput[9] - '0') <= 9 &&
    1476         493 :         static_cast<unsigned>(pszInput[11] - '0') <= 9 &&
    1477         493 :         static_cast<unsigned>(pszInput[12] - '0') <= 9 &&
    1478         493 :         static_cast<unsigned>(pszInput[14] - '0') <= 9 &&
    1479         493 :         static_cast<unsigned>(pszInput[15] - '0') <= 9 &&
    1480         493 :         static_cast<unsigned>(pszInput[17] - '0') <= 9 &&
    1481         493 :         static_cast<unsigned>(pszInput[18] - '0') <= 9 &&
    1482         493 :         static_cast<unsigned>(pszInput[20] - '0') <= 9 &&
    1483         493 :         static_cast<unsigned>(pszInput[21] - '0') <= 9 &&
    1484         493 :         static_cast<unsigned>(pszInput[22] - '0') <= 9)
    1485             :     {
    1486         493 :         psField->Date.Year = static_cast<GInt16>(
    1487         493 :             ((((pszInput[0] - '0') * 10 + (pszInput[1] - '0')) * 10) +
    1488         493 :              (pszInput[2] - '0')) *
    1489         493 :                 10 +
    1490         493 :             (pszInput[3] - '0'));
    1491         493 :         psField->Date.Month =
    1492         493 :             static_cast<GByte>((pszInput[5] - '0') * 10 + (pszInput[6] - '0'));
    1493         493 :         psField->Date.Day =
    1494         493 :             static_cast<GByte>((pszInput[8] - '0') * 10 + (pszInput[9] - '0'));
    1495         493 :         psField->Date.Hour = static_cast<GByte>((pszInput[11] - '0') * 10 +
    1496         493 :                                                 (pszInput[12] - '0'));
    1497         493 :         psField->Date.Minute = static_cast<GByte>((pszInput[14] - '0') * 10 +
    1498         493 :                                                   (pszInput[15] - '0'));
    1499         493 :         psField->Date.Second = static_cast<float>(
    1500         493 :             ((pszInput[17] - '0') * 10 + (pszInput[18] - '0')) +
    1501         493 :             ((pszInput[20] - '0') * 100 + (pszInput[21] - '0') * 10 +
    1502         493 :              (pszInput[22] - '0')) /
    1503             :                 1000.0);
    1504         493 :         psField->Date.TZFlag = nLen == 23 ? 0 : 100;
    1505         493 :         psField->Date.Reserved = 0;
    1506         493 :         if (psField->Date.Month == 0 || psField->Date.Month > 12 ||
    1507         493 :             psField->Date.Day == 0 || psField->Date.Day > 31 ||
    1508         493 :             psField->Date.Hour > 23 || psField->Date.Minute > 59 ||
    1509         493 :             psField->Date.Second >= 61.0f)
    1510             :         {
    1511           1 :             return false;
    1512             :         }
    1513         492 :         return true;
    1514             :     }
    1515             : 
    1516           8 :     return false;
    1517             : }
    1518             : 
    1519             : /************************************************************************/
    1520             : /*                           OGRParseXMLDateTime()                      */
    1521             : /************************************************************************/
    1522             : 
    1523       18277 : int OGRParseXMLDateTime(const char *pszXMLDateTime, OGRField *psField)
    1524             : {
    1525       18277 :     int year = 0;
    1526       18277 :     int month = 0;
    1527       18277 :     int day = 0;
    1528       18277 :     int hour = 0;
    1529       18277 :     int minute = 0;
    1530       18277 :     int TZHour = 0;
    1531       18277 :     int TZMinute = 0;
    1532       18277 :     float second = 0;
    1533       18277 :     char c = '\0';
    1534       18277 :     int TZ = 0;
    1535       18277 :     bool bRet = false;
    1536             : 
    1537             :     // Date is expressed as a UTC date.
    1538       36554 :     if (sscanf(pszXMLDateTime, "%04d-%02d-%02dT%02d:%02d:%f%c", &year, &month,
    1539       35083 :                &day, &hour, &minute, &second, &c) == 7 &&
    1540       16806 :         c == 'Z')
    1541             :     {
    1542       16803 :         TZ = 100;
    1543       16803 :         bRet = true;
    1544             :     }
    1545             :     // Date is expressed as a UTC date, with a timezone.
    1546        2948 :     else if (sscanf(pszXMLDateTime, "%04d-%02d-%02dT%02d:%02d:%f%c%02d:%02d",
    1547             :                     &year, &month, &day, &hour, &minute, &second, &c, &TZHour,
    1548        1477 :                     &TZMinute) == 9 &&
    1549           3 :              (c == '+' || c == '-'))
    1550             :     {
    1551           3 :         TZ = 100 + ((c == '+') ? 1 : -1) * ((TZHour * 60 + TZMinute) / 15);
    1552           3 :         bRet = true;
    1553             :     }
    1554             :     // Date is expressed into an unknown timezone.
    1555        1471 :     else if (sscanf(pszXMLDateTime, "%04d-%02d-%02dT%02d:%02d:%f", &year,
    1556        1471 :                     &month, &day, &hour, &minute, &second) == 6)
    1557             :     {
    1558         387 :         TZ = 0;
    1559         387 :         bRet = true;
    1560             :     }
    1561             :     // Date is expressed as a UTC date with only year:month:day.
    1562        1084 :     else if (sscanf(pszXMLDateTime, "%04d-%02d-%02d", &year, &month, &day) == 3)
    1563             :     {
    1564        1084 :         TZ = 0;
    1565        1084 :         bRet = true;
    1566             :     }
    1567             :     // Date is expressed as a UTC date with only year:month.
    1568           0 :     else if (sscanf(pszXMLDateTime, "%04d-%02d", &year, &month) == 2)
    1569             :     {
    1570           0 :         TZ = 0;
    1571           0 :         bRet = true;
    1572           0 :         day = 1;
    1573             :     }
    1574             : 
    1575       18277 :     if (!bRet)
    1576           0 :         return FALSE;
    1577             : 
    1578       18277 :     psField->Date.Year = static_cast<GInt16>(year);
    1579       18277 :     psField->Date.Month = static_cast<GByte>(month);
    1580       18277 :     psField->Date.Day = static_cast<GByte>(day);
    1581       18277 :     psField->Date.Hour = static_cast<GByte>(hour);
    1582       18277 :     psField->Date.Minute = static_cast<GByte>(minute);
    1583       18277 :     psField->Date.Second = second;
    1584       18277 :     psField->Date.TZFlag = static_cast<GByte>(TZ);
    1585       18277 :     psField->Date.Reserved = 0;
    1586             : 
    1587       18277 :     return TRUE;
    1588             : }
    1589             : 
    1590             : /************************************************************************/
    1591             : /*                      OGRParseRFC822DateTime()                        */
    1592             : /************************************************************************/
    1593             : 
    1594             : static const char *const aszMonthStr[] = {"Jan", "Feb", "Mar", "Apr",
    1595             :                                           "May", "Jun", "Jul", "Aug",
    1596             :                                           "Sep", "Oct", "Nov", "Dec"};
    1597             : 
    1598          27 : int OGRParseRFC822DateTime(const char *pszRFC822DateTime, OGRField *psField)
    1599             : {
    1600             :     int nYear, nMonth, nDay, nHour, nMinute, nSecond, nTZFlag;
    1601          27 :     if (!CPLParseRFC822DateTime(pszRFC822DateTime, &nYear, &nMonth, &nDay,
    1602             :                                 &nHour, &nMinute, &nSecond, &nTZFlag, nullptr))
    1603             :     {
    1604           6 :         return false;
    1605             :     }
    1606             : 
    1607          21 :     psField->Date.Year = static_cast<GInt16>(nYear);
    1608          21 :     psField->Date.Month = static_cast<GByte>(nMonth);
    1609          21 :     psField->Date.Day = static_cast<GByte>(nDay);
    1610          21 :     psField->Date.Hour = static_cast<GByte>(nHour);
    1611          21 :     psField->Date.Minute = static_cast<GByte>(nMinute);
    1612          21 :     psField->Date.Second = (nSecond < 0) ? 0.0f : static_cast<float>(nSecond);
    1613          21 :     psField->Date.TZFlag = static_cast<GByte>(nTZFlag);
    1614          21 :     psField->Date.Reserved = 0;
    1615             : 
    1616          21 :     return true;
    1617             : }
    1618             : 
    1619             : /**
    1620             :   * Returns the day of the week in Gregorian calendar
    1621             :   *
    1622             :   * @param day : day of the month, between 1 and 31
    1623             :   * @param month : month of the year, between 1 (Jan) and 12 (Dec)
    1624             :   * @param year : year
    1625             : 
    1626             :   * @return day of the week : 0 for Monday, ... 6 for Sunday
    1627             :   */
    1628             : 
    1629          12 : int OGRGetDayOfWeek(int day, int month, int year)
    1630             : {
    1631             :     // Reference: Zeller's congruence.
    1632          12 :     const int q = day;
    1633          12 :     int m = month;
    1634          12 :     if (month >= 3)
    1635             :     {
    1636             :         // m = month;
    1637             :     }
    1638             :     else
    1639             :     {
    1640           0 :         m = month + 12;
    1641           0 :         year--;
    1642             :     }
    1643          12 :     const int K = year % 100;
    1644          12 :     const int J = year / 100;
    1645          12 :     const int h = (q + (((m + 1) * 26) / 10) + K + K / 4 + J / 4 + 5 * J) % 7;
    1646          12 :     return (h + 5) % 7;
    1647             : }
    1648             : 
    1649             : /************************************************************************/
    1650             : /*                         OGRGetRFC822DateTime()                       */
    1651             : /************************************************************************/
    1652             : 
    1653          12 : char *OGRGetRFC822DateTime(const OGRField *psField)
    1654             : {
    1655          12 :     char *pszTZ = nullptr;
    1656          12 :     const char *const aszDayOfWeek[] = {"Mon", "Tue", "Wed", "Thu",
    1657             :                                         "Fri", "Sat", "Sun"};
    1658             : 
    1659          24 :     int dayofweek = OGRGetDayOfWeek(psField->Date.Day, psField->Date.Month,
    1660          12 :                                     psField->Date.Year);
    1661             : 
    1662          12 :     int month = psField->Date.Month;
    1663          12 :     if (month < 1 || month > 12)
    1664           0 :         month = 1;
    1665             : 
    1666          12 :     int TZFlag = psField->Date.TZFlag;
    1667          12 :     if (TZFlag == 0 || TZFlag == 100)
    1668             :     {
    1669           0 :         pszTZ = CPLStrdup("GMT");
    1670             :     }
    1671             :     else
    1672             :     {
    1673          12 :         int TZOffset = std::abs(TZFlag - 100) * 15;
    1674          12 :         int TZHour = TZOffset / 60;
    1675          12 :         int TZMinute = TZOffset - TZHour * 60;
    1676          12 :         pszTZ = CPLStrdup(CPLSPrintf("%c%02d%02d", TZFlag > 100 ? '+' : '-',
    1677             :                                      TZHour, TZMinute));
    1678             :     }
    1679          12 :     char *pszRet = CPLStrdup(CPLSPrintf(
    1680          12 :         "%s, %02d %s %04d %02d:%02d:%02d %s", aszDayOfWeek[dayofweek],
    1681          12 :         psField->Date.Day, aszMonthStr[month - 1], psField->Date.Year,
    1682          12 :         psField->Date.Hour, psField->Date.Minute,
    1683          12 :         static_cast<int>(psField->Date.Second), pszTZ));
    1684          12 :     CPLFree(pszTZ);
    1685          12 :     return pszRet;
    1686             : }
    1687             : 
    1688             : /************************************************************************/
    1689             : /*                            OGRGetXMLDateTime()                       */
    1690             : /************************************************************************/
    1691             : 
    1692         842 : char *OGRGetXMLDateTime(const OGRField *psField)
    1693             : {
    1694             :     char *pszRet =
    1695         842 :         static_cast<char *>(CPLMalloc(OGR_SIZEOF_ISO8601_DATETIME_BUFFER));
    1696         842 :     OGRGetISO8601DateTime(psField, false, pszRet);
    1697         842 :     return pszRet;
    1698             : }
    1699             : 
    1700           0 : char *OGRGetXMLDateTime(const OGRField *psField, bool bAlwaysMillisecond)
    1701             : {
    1702             :     char *pszRet =
    1703           0 :         static_cast<char *>(CPLMalloc(OGR_SIZEOF_ISO8601_DATETIME_BUFFER));
    1704           0 :     OGRGetISO8601DateTime(psField, bAlwaysMillisecond, pszRet);
    1705           0 :     return pszRet;
    1706             : }
    1707             : 
    1708        1000 : int OGRGetISO8601DateTime(const OGRField *psField, bool bAlwaysMillisecond,
    1709             :                           char szBuffer[OGR_SIZEOF_ISO8601_DATETIME_BUFFER])
    1710             : {
    1711             :     OGRISO8601Format sFormat;
    1712        1000 :     sFormat.ePrecision = bAlwaysMillisecond ? OGRISO8601Precision::MILLISECOND
    1713             :                                             : OGRISO8601Precision::AUTO;
    1714        2000 :     return OGRGetISO8601DateTime(psField, sFormat, szBuffer);
    1715             : }
    1716             : 
    1717        1163 : int OGRGetISO8601DateTime(const OGRField *psField,
    1718             :                           const OGRISO8601Format &sFormat,
    1719             :                           char szBuffer[OGR_SIZEOF_ISO8601_DATETIME_BUFFER])
    1720             : {
    1721        1163 :     const GInt16 year = psField->Date.Year;
    1722        1163 :     const GByte month = psField->Date.Month;
    1723        1163 :     const GByte day = psField->Date.Day;
    1724        1163 :     const GByte hour = psField->Date.Hour;
    1725        1163 :     const GByte minute = psField->Date.Minute;
    1726        1163 :     const float second = psField->Date.Second;
    1727        1163 :     const GByte TZFlag = psField->Date.TZFlag;
    1728             : 
    1729        1163 :     if (year < 0 || year >= 10000)
    1730             :     {
    1731           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1732             :                  "OGRGetISO8601DateTime(): year %d unsupported ", year);
    1733           0 :         szBuffer[0] = 0;
    1734           0 :         return 0;
    1735             :     }
    1736             : 
    1737        1163 :     int nYear = year;
    1738        1163 :     szBuffer[3] = (nYear % 10) + '0';
    1739        1163 :     nYear /= 10;
    1740        1163 :     szBuffer[2] = (nYear % 10) + '0';
    1741        1163 :     nYear /= 10;
    1742        1163 :     szBuffer[1] = (nYear % 10) + '0';
    1743        1163 :     nYear /= 10;
    1744        1163 :     szBuffer[0] = static_cast<char>(nYear /*% 10*/ + '0');
    1745        1163 :     szBuffer[4] = '-';
    1746        1163 :     szBuffer[5] = ((month / 10) % 10) + '0';
    1747        1163 :     szBuffer[6] = (month % 10) + '0';
    1748        1163 :     szBuffer[7] = '-';
    1749        1163 :     szBuffer[8] = ((day / 10) % 10) + '0';
    1750        1163 :     szBuffer[9] = (day % 10) + '0';
    1751        1163 :     szBuffer[10] = 'T';
    1752        1163 :     szBuffer[11] = ((hour / 10) % 10) + '0';
    1753        1163 :     szBuffer[12] = (hour % 10) + '0';
    1754        1163 :     szBuffer[13] = ':';
    1755        1163 :     szBuffer[14] = ((minute / 10) % 10) + '0';
    1756        1163 :     szBuffer[15] = (minute % 10) + '0';
    1757             :     int nPos;
    1758        1163 :     if (sFormat.ePrecision == OGRISO8601Precision::MINUTE)
    1759             :     {
    1760           2 :         nPos = 16;
    1761             :     }
    1762             :     else
    1763             :     {
    1764        1161 :         szBuffer[16] = ':';
    1765             : 
    1766        2174 :         if (sFormat.ePrecision == OGRISO8601Precision::MILLISECOND ||
    1767        2024 :             (sFormat.ePrecision == OGRISO8601Precision::AUTO &&
    1768        1011 :              OGR_GET_MS(second)))
    1769             :         {
    1770             :             /* Below is equivalent of the below snprintf(), but hand-made for
    1771             :              * faster execution. */
    1772             :             /* snprintf(szBuffer, nMaxSize,
    1773             :                                    "%04d-%02u-%02uT%02u:%02u:%06.3f%s",
    1774             :                                    year, month, day, hour, minute, second,
    1775             :                                    szTimeZone);
    1776             :             */
    1777         186 :             int nMilliSecond = static_cast<int>(second * 1000.0f + 0.5f);
    1778         186 :             szBuffer[22] = (nMilliSecond % 10) + '0';
    1779         186 :             nMilliSecond /= 10;
    1780         186 :             szBuffer[21] = (nMilliSecond % 10) + '0';
    1781         186 :             nMilliSecond /= 10;
    1782         186 :             szBuffer[20] = (nMilliSecond % 10) + '0';
    1783         186 :             nMilliSecond /= 10;
    1784         186 :             szBuffer[19] = '.';
    1785         186 :             szBuffer[18] = (nMilliSecond % 10) + '0';
    1786         186 :             nMilliSecond /= 10;
    1787         186 :             szBuffer[17] = (nMilliSecond % 10) + '0';
    1788         186 :             nPos = 23;
    1789             :         }
    1790             :         else
    1791             :         {
    1792             :             /* Below is equivalent of the below snprintf(), but hand-made for
    1793             :              * faster execution. */
    1794             :             /* snprintf(szBuffer, nMaxSize,
    1795             :                                    "%04d-%02u-%02uT%02u:%02u:%02u%s",
    1796             :                                    year, month, day, hour, minute,
    1797             :                                    static_cast<GByte>(second), szTimeZone);
    1798             :             */
    1799         975 :             int nSecond = static_cast<int>(second + 0.5f);
    1800         975 :             szBuffer[17] = ((nSecond / 10) % 10) + '0';
    1801         975 :             szBuffer[18] = (nSecond % 10) + '0';
    1802         975 :             nPos = 19;
    1803             :         }
    1804             :     }
    1805             : 
    1806        1163 :     switch (TZFlag)
    1807             :     {
    1808         908 :         case 0:  // Unknown time zone
    1809             :         case 1:  // Local time zone (not specified)
    1810         908 :             break;
    1811             : 
    1812         238 :         case 100:  // GMT
    1813         238 :             szBuffer[nPos++] = 'Z';
    1814         238 :             break;
    1815             : 
    1816          17 :         default:  // Offset (in quarter-hour units) from GMT
    1817          17 :             const int TZOffset = std::abs(TZFlag - 100) * 15;
    1818          17 :             const int TZHour = TZOffset / 60;
    1819          17 :             const int TZMinute = TZOffset % 60;
    1820             : 
    1821          17 :             szBuffer[nPos++] = (TZFlag > 100) ? '+' : '-';
    1822          17 :             szBuffer[nPos++] = ((TZHour / 10) % 10) + '0';
    1823          17 :             szBuffer[nPos++] = (TZHour % 10) + '0';
    1824          17 :             szBuffer[nPos++] = ':';
    1825          17 :             szBuffer[nPos++] = ((TZMinute / 10) % 10) + '0';
    1826          17 :             szBuffer[nPos++] = (TZMinute % 10) + '0';
    1827             :     }
    1828             : 
    1829        1163 :     szBuffer[nPos] = 0;
    1830             : 
    1831        1163 :     return nPos;
    1832             : }
    1833             : 
    1834             : /************************************************************************/
    1835             : /*                 OGRGetXML_UTF8_EscapedString()                       */
    1836             : /************************************************************************/
    1837             : 
    1838        2841 : char *OGRGetXML_UTF8_EscapedString(const char *pszString)
    1839             : {
    1840        2841 :     char *pszEscaped = nullptr;
    1841        2842 :     if (!CPLIsUTF8(pszString, -1) &&
    1842           1 :         CPLTestBool(CPLGetConfigOption("OGR_FORCE_ASCII", "YES")))
    1843             :     {
    1844             :         static bool bFirstTime = true;
    1845           1 :         if (bFirstTime)
    1846             :         {
    1847           1 :             bFirstTime = false;
    1848           1 :             CPLError(CE_Warning, CPLE_AppDefined,
    1849             :                      "%s is not a valid UTF-8 string. Forcing it to ASCII.  "
    1850             :                      "If you still want the original string and change the XML "
    1851             :                      "file encoding afterwards, you can define "
    1852             :                      "OGR_FORCE_ASCII=NO as configuration option.  "
    1853             :                      "This warning won't be issued anymore",
    1854             :                      pszString);
    1855             :         }
    1856             :         else
    1857             :         {
    1858           0 :             CPLDebug("OGR",
    1859             :                      "%s is not a valid UTF-8 string. Forcing it to ASCII",
    1860             :                      pszString);
    1861             :         }
    1862           1 :         char *pszTemp = CPLForceToASCII(pszString, -1, '?');
    1863           1 :         pszEscaped = CPLEscapeString(pszTemp, -1, CPLES_XML);
    1864           1 :         CPLFree(pszTemp);
    1865             :     }
    1866             :     else
    1867        2840 :         pszEscaped = CPLEscapeString(pszString, -1, CPLES_XML);
    1868        2841 :     return pszEscaped;
    1869             : }
    1870             : 
    1871             : /************************************************************************/
    1872             : /*                        OGRCompareDate()                              */
    1873             : /************************************************************************/
    1874             : 
    1875         153 : int OGRCompareDate(const OGRField *psFirstTuple, const OGRField *psSecondTuple)
    1876             : {
    1877             :     // TODO: We ignore TZFlag.
    1878             : 
    1879         153 :     if (psFirstTuple->Date.Year < psSecondTuple->Date.Year)
    1880          12 :         return -1;
    1881         141 :     else if (psFirstTuple->Date.Year > psSecondTuple->Date.Year)
    1882           3 :         return 1;
    1883             : 
    1884         138 :     if (psFirstTuple->Date.Month < psSecondTuple->Date.Month)
    1885           1 :         return -1;
    1886         137 :     else if (psFirstTuple->Date.Month > psSecondTuple->Date.Month)
    1887           6 :         return 1;
    1888             : 
    1889         131 :     if (psFirstTuple->Date.Day < psSecondTuple->Date.Day)
    1890           8 :         return -1;
    1891         123 :     else if (psFirstTuple->Date.Day > psSecondTuple->Date.Day)
    1892           8 :         return 1;
    1893             : 
    1894         115 :     if (psFirstTuple->Date.Hour < psSecondTuple->Date.Hour)
    1895           1 :         return -1;
    1896         114 :     else if (psFirstTuple->Date.Hour > psSecondTuple->Date.Hour)
    1897           8 :         return 1;
    1898             : 
    1899         106 :     if (psFirstTuple->Date.Minute < psSecondTuple->Date.Minute)
    1900           0 :         return -1;
    1901         106 :     else if (psFirstTuple->Date.Minute > psSecondTuple->Date.Minute)
    1902           0 :         return 1;
    1903             : 
    1904         106 :     if (psFirstTuple->Date.Second < psSecondTuple->Date.Second)
    1905           9 :         return -1;
    1906          97 :     else if (psFirstTuple->Date.Second > psSecondTuple->Date.Second)
    1907          11 :         return 1;
    1908             : 
    1909          86 :     return 0;
    1910             : }
    1911             : 
    1912             : /************************************************************************/
    1913             : /*                        OGRFastAtof()                                 */
    1914             : /************************************************************************/
    1915             : 
    1916             : // On Windows, CPLAtof() is very slow if the number is followed by other long
    1917             : // content.  Just extract the number into a short string before calling
    1918             : // CPLAtof() on it.
    1919           0 : static double OGRCallAtofOnShortString(const char *pszStr)
    1920             : {
    1921           0 :     const char *p = pszStr;
    1922           0 :     while (*p == ' ' || *p == '\t')
    1923           0 :         ++p;
    1924             : 
    1925           0 :     char szTemp[128] = {};
    1926           0 :     int nCounter = 0;
    1927           0 :     while (*p == '+' || *p == '-' || (*p >= '0' && *p <= '9') || *p == '.' ||
    1928           0 :            (*p == 'e' || *p == 'E' || *p == 'd' || *p == 'D'))
    1929             :     {
    1930           0 :         szTemp[nCounter++] = *(p++);
    1931           0 :         if (nCounter == 127)
    1932           0 :             return CPLAtof(pszStr);
    1933             :     }
    1934           0 :     szTemp[nCounter] = '\0';
    1935           0 :     return CPLAtof(szTemp);
    1936             : }
    1937             : 
    1938             : /** Same contract as CPLAtof, except than it doesn't always call the
    1939             :  *  system CPLAtof() that may be slow on some platforms. For simple but
    1940             :  *  common strings, it'll use a faster implementation (up to 20x faster
    1941             :  *  than CPLAtof() on MS runtime libraries) that has no guaranty to return
    1942             :  *  exactly the same floating point number.
    1943             :  */
    1944             : 
    1945      391355 : double OGRFastAtof(const char *pszStr)
    1946             : {
    1947      391355 :     double dfVal = 0;
    1948      391355 :     double dfSign = 1.0;
    1949      391355 :     const char *p = pszStr;
    1950             : 
    1951      391355 :     constexpr double adfTenPower[] = {
    1952             :         1e0,  1e1,  1e2,  1e3,  1e4,  1e5,  1e6,  1e7,  1e8,  1e9,  1e10,
    1953             :         1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21,
    1954             :         1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29, 1e30, 1e31};
    1955             : 
    1956      391357 :     while (*p == ' ' || *p == '\t')
    1957           2 :         ++p;
    1958             : 
    1959      391355 :     if (*p == '+')
    1960           9 :         ++p;
    1961      391346 :     else if (*p == '-')
    1962             :     {
    1963        1500 :         dfSign = -1.0;
    1964        1500 :         ++p;
    1965             :     }
    1966             : 
    1967             :     while (true)
    1968             :     {
    1969     2580830 :         if (*p >= '0' && *p <= '9')
    1970             :         {
    1971     2189480 :             dfVal = dfVal * 10.0 + (*p - '0');
    1972     2189480 :             ++p;
    1973             :         }
    1974      391355 :         else if (*p == '.')
    1975             :         {
    1976      384032 :             ++p;
    1977      384032 :             break;
    1978             :         }
    1979        7323 :         else if (*p == 'e' || *p == 'E' || *p == 'd' || *p == 'D')
    1980           0 :             return OGRCallAtofOnShortString(pszStr);
    1981             :         else
    1982        7323 :             return dfSign * dfVal;
    1983             :     }
    1984             : 
    1985      384032 :     unsigned int countFractionnal = 0;
    1986             :     while (true)
    1987             :     {
    1988     1451160 :         if (*p >= '0' && *p <= '9')
    1989             :         {
    1990     1067130 :             dfVal = dfVal * 10.0 + (*p - '0');
    1991     1067130 :             ++countFractionnal;
    1992     1067130 :             ++p;
    1993             :         }
    1994      384032 :         else if (*p == 'e' || *p == 'E' || *p == 'd' || *p == 'D')
    1995           0 :             return OGRCallAtofOnShortString(pszStr);
    1996             :         else
    1997             :         {
    1998      384032 :             if (countFractionnal < CPL_ARRAYSIZE(adfTenPower))
    1999      384032 :                 return dfSign * (dfVal / adfTenPower[countFractionnal]);
    2000             :             else
    2001           0 :                 return OGRCallAtofOnShortString(pszStr);
    2002             :         }
    2003             :     }
    2004             : }
    2005             : 
    2006             : /**
    2007             :  * Check that panPermutation is a permutation of [0, nSize-1].
    2008             :  * @param panPermutation an array of nSize elements.
    2009             :  * @param nSize size of the array.
    2010             :  * @return OGRERR_NONE if panPermutation is a permutation of [0, nSize - 1].
    2011             :  * @since OGR 1.9.0
    2012             :  */
    2013         139 : OGRErr OGRCheckPermutation(const int *panPermutation, int nSize)
    2014             : {
    2015         139 :     OGRErr eErr = OGRERR_NONE;
    2016         139 :     int *panCheck = static_cast<int *>(CPLCalloc(nSize, sizeof(int)));
    2017         811 :     for (int i = 0; i < nSize; ++i)
    2018             :     {
    2019         679 :         if (panPermutation[i] < 0 || panPermutation[i] >= nSize)
    2020             :         {
    2021           3 :             CPLError(CE_Failure, CPLE_IllegalArg, "Bad value for element %d",
    2022             :                      i);
    2023           3 :             eErr = OGRERR_FAILURE;
    2024           3 :             break;
    2025             :         }
    2026         676 :         if (panCheck[panPermutation[i]] != 0)
    2027             :         {
    2028           4 :             CPLError(CE_Failure, CPLE_IllegalArg,
    2029             :                      "Array is not a permutation of [0,%d]", nSize - 1);
    2030           4 :             eErr = OGRERR_FAILURE;
    2031           4 :             break;
    2032             :         }
    2033         672 :         panCheck[panPermutation[i]] = 1;
    2034             :     }
    2035         139 :     CPLFree(panCheck);
    2036         139 :     return eErr;
    2037             : }
    2038             : 
    2039     1275590 : OGRErr OGRReadWKBGeometryType(const unsigned char *pabyData,
    2040             :                               OGRwkbVariant eWkbVariant,
    2041             :                               OGRwkbGeometryType *peGeometryType)
    2042             : {
    2043     1275590 :     if (!peGeometryType)
    2044           0 :         return OGRERR_FAILURE;
    2045             : 
    2046             :     /* -------------------------------------------------------------------- */
    2047             :     /*      Get the byte order byte.                                        */
    2048             :     /* -------------------------------------------------------------------- */
    2049     1275590 :     int nByteOrder = DB2_V72_FIX_BYTE_ORDER(*pabyData);
    2050     1275590 :     if (!(nByteOrder == wkbXDR || nByteOrder == wkbNDR))
    2051         123 :         return OGRERR_CORRUPT_DATA;
    2052     1275460 :     OGRwkbByteOrder eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
    2053             : 
    2054             :     /* -------------------------------------------------------------------- */
    2055             :     /*      Get the geometry type.                                          */
    2056             :     /* -------------------------------------------------------------------- */
    2057     1275460 :     bool bIs3D = false;
    2058     1275460 :     bool bIsMeasured = false;
    2059     1275460 :     int iRawType = 0;
    2060             : 
    2061     1275460 :     memcpy(&iRawType, pabyData + 1, 4);
    2062     1275460 :     if (OGR_SWAP(eByteOrder))
    2063             :     {
    2064         855 :         CPL_SWAP32PTR(&iRawType);
    2065             :     }
    2066             : 
    2067             :     // Test for M bit in PostGIS WKB, see ogrgeometry.cpp:4956.
    2068     1275460 :     if (0x40000000 & iRawType)
    2069             :     {
    2070        2068 :         iRawType &= ~0x40000000;
    2071        2068 :         bIsMeasured = true;
    2072             :     }
    2073             :     // Old-style OGC z-bit is flipped? Tests also Z bit in PostGIS WKB.
    2074     1275460 :     if (wkb25DBitInternalUse & iRawType)
    2075             :     {
    2076             :         // Clean off top 3 bytes.
    2077        6035 :         iRawType &= 0x000000FF;
    2078        6035 :         bIs3D = true;
    2079             :     }
    2080             : 
    2081             :     // ISO SQL/MM Part3 draft -> Deprecated.
    2082             :     // See http://jtc1sc32.org/doc/N1101-1150/32N1107-WD13249-3--spatial.pdf
    2083     1275460 :     if (iRawType == 1000001)
    2084           0 :         iRawType = wkbCircularString;
    2085     1275460 :     else if (iRawType == 1000002)
    2086           0 :         iRawType = wkbCompoundCurve;
    2087     1275460 :     else if (iRawType == 1000003)
    2088           0 :         iRawType = wkbCurvePolygon;
    2089     1275460 :     else if (iRawType == 1000004)
    2090           0 :         iRawType = wkbMultiCurve;
    2091     1275460 :     else if (iRawType == 1000005)
    2092           0 :         iRawType = wkbMultiSurface;
    2093     1275460 :     else if (iRawType == 2000001)
    2094           0 :         iRawType = wkbPointZM;
    2095     1275460 :     else if (iRawType == 2000002)
    2096           0 :         iRawType = wkbLineStringZM;
    2097     1275460 :     else if (iRawType == 2000003)
    2098           0 :         iRawType = wkbCircularStringZM;
    2099     1275460 :     else if (iRawType == 2000004)
    2100           0 :         iRawType = wkbCompoundCurveZM;
    2101     1275460 :     else if (iRawType == 2000005)
    2102           0 :         iRawType = wkbPolygonZM;
    2103     1275460 :     else if (iRawType == 2000006)
    2104           0 :         iRawType = wkbCurvePolygonZM;
    2105     1275460 :     else if (iRawType == 2000007)
    2106           0 :         iRawType = wkbMultiPointZM;
    2107     1275460 :     else if (iRawType == 2000008)
    2108           0 :         iRawType = wkbMultiCurveZM;
    2109     1275460 :     else if (iRawType == 2000009)
    2110           0 :         iRawType = wkbMultiLineStringZM;
    2111     1275460 :     else if (iRawType == 2000010)
    2112           0 :         iRawType = wkbMultiSurfaceZM;
    2113     1275460 :     else if (iRawType == 2000011)
    2114           0 :         iRawType = wkbMultiPolygonZM;
    2115     1275460 :     else if (iRawType == 2000012)
    2116           0 :         iRawType = wkbGeometryCollectionZM;
    2117     1275460 :     else if (iRawType == 3000001)
    2118           0 :         iRawType = wkbPoint25D;
    2119     1275460 :     else if (iRawType == 3000002)
    2120           0 :         iRawType = wkbLineString25D;
    2121     1275460 :     else if (iRawType == 3000003)
    2122           0 :         iRawType = wkbCircularStringZ;
    2123     1275460 :     else if (iRawType == 3000004)
    2124           0 :         iRawType = wkbCompoundCurveZ;
    2125     1275460 :     else if (iRawType == 3000005)
    2126           0 :         iRawType = wkbPolygon25D;
    2127     1275460 :     else if (iRawType == 3000006)
    2128           0 :         iRawType = wkbCurvePolygonZ;
    2129     1275460 :     else if (iRawType == 3000007)
    2130           0 :         iRawType = wkbMultiPoint25D;
    2131     1275460 :     else if (iRawType == 3000008)
    2132           0 :         iRawType = wkbMultiCurveZ;
    2133     1275460 :     else if (iRawType == 3000009)
    2134           0 :         iRawType = wkbMultiLineString25D;
    2135     1275460 :     else if (iRawType == 3000010)
    2136           0 :         iRawType = wkbMultiSurfaceZ;
    2137     1275460 :     else if (iRawType == 3000011)
    2138           0 :         iRawType = wkbMultiPolygon25D;
    2139     1275460 :     else if (iRawType == 3000012)
    2140           0 :         iRawType = wkbGeometryCollection25D;
    2141     1275460 :     else if (iRawType == 4000001)
    2142           0 :         iRawType = wkbPointM;
    2143     1275460 :     else if (iRawType == 4000002)
    2144           0 :         iRawType = wkbLineStringM;
    2145     1275460 :     else if (iRawType == 4000003)
    2146           0 :         iRawType = wkbCircularStringM;
    2147     1275460 :     else if (iRawType == 4000004)
    2148           0 :         iRawType = wkbCompoundCurveM;
    2149     1275460 :     else if (iRawType == 4000005)
    2150           0 :         iRawType = wkbPolygonM;
    2151     1275460 :     else if (iRawType == 4000006)
    2152           0 :         iRawType = wkbCurvePolygonM;
    2153     1275460 :     else if (iRawType == 4000007)
    2154           0 :         iRawType = wkbMultiPointM;
    2155     1275460 :     else if (iRawType == 4000008)
    2156           0 :         iRawType = wkbMultiCurveM;
    2157     1275460 :     else if (iRawType == 4000009)
    2158           0 :         iRawType = wkbMultiLineStringM;
    2159     1275460 :     else if (iRawType == 4000010)
    2160           0 :         iRawType = wkbMultiSurfaceM;
    2161     1275460 :     else if (iRawType == 4000011)
    2162           0 :         iRawType = wkbMultiPolygonM;
    2163     1275460 :     else if (iRawType == 4000012)
    2164           0 :         iRawType = wkbGeometryCollectionM;
    2165             : 
    2166             :     // Sometimes the Z flag is in the 2nd byte?
    2167     1275460 :     if (iRawType & (wkb25DBitInternalUse >> 16))
    2168             :     {
    2169             :         // Clean off top 3 bytes.
    2170         163 :         iRawType &= 0x000000FF;
    2171         163 :         bIs3D = true;
    2172             :     }
    2173             : 
    2174     1275460 :     if (eWkbVariant == wkbVariantPostGIS1)
    2175             :     {
    2176           0 :         if (iRawType == POSTGIS15_CURVEPOLYGON)
    2177           0 :             iRawType = wkbCurvePolygon;
    2178           0 :         else if (iRawType == POSTGIS15_MULTICURVE)
    2179           0 :             iRawType = wkbMultiCurve;
    2180           0 :         else if (iRawType == POSTGIS15_MULTISURFACE)
    2181           0 :             iRawType = wkbMultiSurface;
    2182             :     }
    2183             : 
    2184     1275460 :     if (bIs3D)
    2185             :     {
    2186        6198 :         iRawType += 1000;
    2187             :     }
    2188     1275460 :     if (bIsMeasured)
    2189             :     {
    2190        2068 :         iRawType += 2000;
    2191             :     }
    2192             : 
    2193             :     // ISO SQL/MM style types are between 1-17, 1001-1017, 2001-2017, and
    2194             :     // 3001-3017.
    2195     1275460 :     if (!((iRawType > 0 && iRawType <= 17) ||
    2196      159825 :           (iRawType > 1000 && iRawType <= 1017) ||
    2197      154338 :           (iRawType > 2000 && iRawType <= 2017) ||
    2198      153023 :           (iRawType > 3000 && iRawType <= 3017)))
    2199             :     {
    2200        1130 :         CPLError(CE_Failure, CPLE_NotSupported, "Unsupported WKB type %d",
    2201             :                  iRawType);
    2202        1130 :         return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
    2203             :     }
    2204             : 
    2205             :     // Convert to OGRwkbGeometryType value.
    2206     1274330 :     if (iRawType >= 1001 && iRawType <= 1007)
    2207             :     {
    2208        5324 :         iRawType -= 1000;
    2209        5324 :         iRawType |= wkb25DBitInternalUse;
    2210             :     }
    2211             : 
    2212     1274330 :     *peGeometryType = static_cast<OGRwkbGeometryType>(iRawType);
    2213             : 
    2214     1274330 :     return OGRERR_NONE;
    2215             : }
    2216             : 
    2217             : /************************************************************************/
    2218             : /*                      OGRReadWKTGeometryType()                        */
    2219             : /************************************************************************/
    2220             : 
    2221         400 : OGRErr OGRReadWKTGeometryType(const char *pszWKT,
    2222             :                               OGRwkbGeometryType *peGeometryType)
    2223             : {
    2224         400 :     if (!peGeometryType)
    2225           0 :         return OGRERR_FAILURE;
    2226             : 
    2227         400 :     OGRwkbGeometryType eGeomType = wkbUnknown;
    2228         400 :     if (STARTS_WITH_CI(pszWKT, "POINT"))
    2229          92 :         eGeomType = wkbPoint;
    2230         308 :     else if (STARTS_WITH_CI(pszWKT, "LINESTRING"))
    2231          43 :         eGeomType = wkbLineString;
    2232         265 :     else if (STARTS_WITH_CI(pszWKT, "POLYGON"))
    2233          65 :         eGeomType = wkbPolygon;
    2234         200 :     else if (STARTS_WITH_CI(pszWKT, "MULTIPOINT"))
    2235          31 :         eGeomType = wkbMultiPoint;
    2236         169 :     else if (STARTS_WITH_CI(pszWKT, "MULTILINESTRING"))
    2237          35 :         eGeomType = wkbMultiLineString;
    2238         134 :     else if (STARTS_WITH_CI(pszWKT, "MULTIPOLYGON"))
    2239          66 :         eGeomType = wkbMultiPolygon;
    2240          68 :     else if (STARTS_WITH_CI(pszWKT, "GEOMETRYCOLLECTION"))
    2241          43 :         eGeomType = wkbGeometryCollection;
    2242          25 :     else if (STARTS_WITH_CI(pszWKT, "CIRCULARSTRING"))
    2243           1 :         eGeomType = wkbCircularString;
    2244          24 :     else if (STARTS_WITH_CI(pszWKT, "COMPOUNDCURVE"))
    2245           1 :         eGeomType = wkbCompoundCurve;
    2246          23 :     else if (STARTS_WITH_CI(pszWKT, "CURVEPOLYGON"))
    2247           1 :         eGeomType = wkbCurvePolygon;
    2248          22 :     else if (STARTS_WITH_CI(pszWKT, "MULTICURVE"))
    2249           1 :         eGeomType = wkbMultiCurve;
    2250          21 :     else if (STARTS_WITH_CI(pszWKT, "MULTISURFACE"))
    2251           1 :         eGeomType = wkbMultiSurface;
    2252          20 :     else if (STARTS_WITH_CI(pszWKT, "POLYHEDRALSURFACE"))
    2253           1 :         eGeomType = wkbPolyhedralSurface;
    2254          19 :     else if (STARTS_WITH_CI(pszWKT, "TIN"))
    2255           1 :         eGeomType = wkbTIN;
    2256             :     else
    2257          18 :         return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
    2258             : 
    2259         382 :     if (strstr(pszWKT, " ZM"))
    2260          34 :         eGeomType = OGR_GT_SetModifier(eGeomType, true, true);
    2261         348 :     else if (strstr(pszWKT, " Z"))
    2262          94 :         eGeomType = OGR_GT_SetModifier(eGeomType, true, false);
    2263         254 :     else if (strstr(pszWKT, " M"))
    2264          34 :         eGeomType = OGR_GT_SetModifier(eGeomType, false, true);
    2265             : 
    2266         382 :     *peGeometryType = eGeomType;
    2267             : 
    2268         382 :     return OGRERR_NONE;
    2269             : }
    2270             : 
    2271             : /************************************************************************/
    2272             : /*                        OGRFormatFloat()                              */
    2273             : /************************************************************************/
    2274             : 
    2275          66 : int OGRFormatFloat(char *pszBuffer, int nBufferLen, float fVal, int nPrecision,
    2276             :                    char chConversionSpecifier)
    2277             : {
    2278             :     // So to have identical cross platform representation.
    2279          66 :     if (std::isinf(fVal))
    2280           2 :         return CPLsnprintf(pszBuffer, nBufferLen, (fVal > 0) ? "inf" : "-inf");
    2281          64 :     if (std::isnan(fVal))
    2282           1 :         return CPLsnprintf(pszBuffer, nBufferLen, "nan");
    2283             : 
    2284          63 :     int nSize = 0;
    2285          63 :     char szFormatting[32] = {};
    2286          63 :     constexpr int MAX_SIGNIFICANT_DIGITS_FLOAT32 = 8;
    2287          63 :     const int nInitialSignificantFigures =
    2288          63 :         nPrecision >= 0 ? nPrecision : MAX_SIGNIFICANT_DIGITS_FLOAT32;
    2289             : 
    2290          63 :     CPLsnprintf(szFormatting, sizeof(szFormatting), "%%.%d%c",
    2291             :                 nInitialSignificantFigures, chConversionSpecifier);
    2292          63 :     nSize = CPLsnprintf(pszBuffer, nBufferLen, szFormatting, fVal);
    2293          63 :     const char *pszDot = strchr(pszBuffer, '.');
    2294             : 
    2295             :     // Try to avoid 0.34999999 or 0.15000001 rounding issues by
    2296             :     // decreasing a bit precision.
    2297          63 :     if (nInitialSignificantFigures >= 8 && pszDot != nullptr &&
    2298          52 :         (strstr(pszDot, "99999") != nullptr ||
    2299          42 :          strstr(pszDot, "00000") != nullptr))
    2300             :     {
    2301          24 :         const CPLString osOriBuffer(pszBuffer, nSize);
    2302             : 
    2303          12 :         bool bOK = false;
    2304          12 :         for (int i = 1; i <= 3; i++)
    2305             :         {
    2306          12 :             CPLsnprintf(szFormatting, sizeof(szFormatting), "%%.%d%c",
    2307             :                         nInitialSignificantFigures - i, chConversionSpecifier);
    2308          12 :             nSize = CPLsnprintf(pszBuffer, nBufferLen, szFormatting, fVal);
    2309          12 :             pszDot = strchr(pszBuffer, '.');
    2310          12 :             if (pszDot != nullptr && strstr(pszDot, "99999") == nullptr &&
    2311          36 :                 strstr(pszDot, "00000") == nullptr &&
    2312          12 :                 static_cast<float>(CPLAtof(pszBuffer)) == fVal)
    2313             :             {
    2314          12 :                 bOK = true;
    2315          12 :                 break;
    2316             :             }
    2317             :         }
    2318          12 :         if (!bOK)
    2319             :         {
    2320           0 :             memcpy(pszBuffer, osOriBuffer.c_str(), osOriBuffer.size() + 1);
    2321           0 :             nSize = static_cast<int>(osOriBuffer.size());
    2322             :         }
    2323             :     }
    2324             : 
    2325          63 :     if (nSize + 2 < static_cast<int>(nBufferLen) &&
    2326          63 :         strchr(pszBuffer, '.') == nullptr && strchr(pszBuffer, 'e') == nullptr)
    2327             :     {
    2328          10 :         nSize += CPLsnprintf(pszBuffer + nSize, nBufferLen - nSize, ".0");
    2329             :     }
    2330             : 
    2331          63 :     return nSize;
    2332             : }

Generated by: LCOV version 1.14