LCOV - code coverage report
Current view: top level - ogr - ogrutils.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 964 1146 84.1 %
Date: 2024-11-21 22:18:42 Functions: 34 43 79.1 %

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

Generated by: LCOV version 1.14