Coverage Report

Created: 2025-12-31 08:30

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