Coverage Report

Created: 2025-07-23 09:13

/src/gdal/ogr/ogrsf_frmts/shape/shpopen.c
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  Shapelib
4
 * Purpose:  Implementation of core Shapefile read/write functions.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 1999, 2001, Frank Warmerdam
9
 * Copyright (c) 2011-2024, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
12
 ******************************************************************************/
13
14
#include "shapefil_private.h"
15
16
#include <assert.h>
17
#include <errno.h>
18
#include <limits.h>
19
#include <math.h>
20
#include <stdbool.h>
21
#include <stdint.h>
22
#include <stdio.h>
23
#include <stdlib.h>
24
#include <string.h>
25
26
#ifndef FALSE
27
#define FALSE 0
28
#define TRUE 1
29
#endif
30
31
2.93M
#define ByteCopy(a, b, c) memcpy(b, a, c)
32
#ifndef MAX
33
#define MIN(a, b) ((a < b) ? a : b)
34
#define MAX(a, b) ((a > b) ? a : b)
35
#endif
36
37
#ifndef USE_CPL
38
#if defined(_MSC_VER)
39
#if _MSC_VER < 1900
40
#define snprintf _snprintf
41
#endif
42
#elif defined(_WIN32)
43
#ifndef snprintf
44
#define snprintf _snprintf
45
#endif
46
#endif
47
#endif
48
49
/* Allows customization of the message in vendored builds (such as GDAL) */
50
#ifndef SHP_RESTORE_SHX_HINT_MESSAGE
51
#define SHP_RESTORE_SHX_HINT_MESSAGE                                           \
52
    " Use SHPRestoreSHX() to restore or create it."
53
#endif
54
55
/************************************************************************/
56
/*                          SHPWriteHeader()                            */
57
/*                                                                      */
58
/*      Write out a header for the .shp and .shx files as well as the   */
59
/*      contents of the index (.shx) file.                              */
60
/************************************************************************/
61
62
void SHPAPI_CALL SHPWriteHeader(SHPHandle psSHP)
63
1.14k
{
64
1.14k
    if (psSHP->fpSHX == SHPLIB_NULLPTR)
65
0
    {
66
0
        psSHP->sHooks.Error("SHPWriteHeader failed : SHX file is closed");
67
0
        return;
68
0
    }
69
70
    /* -------------------------------------------------------------------- */
71
    /*      Prepare header block for .shp file.                             */
72
    /* -------------------------------------------------------------------- */
73
74
1.14k
    unsigned char abyHeader[100] = {0};
75
1.14k
    abyHeader[2] = 0x27; /* magic cookie */
76
1.14k
    abyHeader[3] = 0x0a;
77
78
1.14k
    uint32_t i32 = psSHP->nFileSize / 2; /* file size */
79
1.14k
    ByteCopy(&i32, abyHeader + 24, 4);
80
1.14k
#if !defined(SHP_BIG_ENDIAN)
81
1.14k
    SHP_SWAP32(abyHeader + 24);
82
1.14k
#endif
83
84
1.14k
    i32 = 1000; /* version */
85
1.14k
    ByteCopy(&i32, abyHeader + 28, 4);
86
#if defined(SHP_BIG_ENDIAN)
87
    SHP_SWAP32(abyHeader + 28);
88
#endif
89
90
1.14k
    i32 = psSHP->nShapeType; /* shape type */
91
1.14k
    ByteCopy(&i32, abyHeader + 32, 4);
92
#if defined(SHP_BIG_ENDIAN)
93
    SHP_SWAP32(abyHeader + 32);
94
#endif
95
96
1.14k
    double dValue = psSHP->adBoundsMin[0]; /* set bounds */
97
1.14k
    ByteCopy(&dValue, abyHeader + 36, 8);
98
#if defined(SHP_BIG_ENDIAN)
99
    SHP_SWAP64(abyHeader + 36);
100
#endif
101
1.14k
    dValue = psSHP->adBoundsMin[1];
102
1.14k
    ByteCopy(&dValue, abyHeader + 44, 8);
103
#if defined(SHP_BIG_ENDIAN)
104
    SHP_SWAP64(abyHeader + 44);
105
#endif
106
1.14k
    dValue = psSHP->adBoundsMax[0];
107
1.14k
    ByteCopy(&dValue, abyHeader + 52, 8);
108
#if defined(SHP_BIG_ENDIAN)
109
    SHP_SWAP64(abyHeader + 52);
110
#endif
111
112
1.14k
    dValue = psSHP->adBoundsMax[1];
113
1.14k
    ByteCopy(&dValue, abyHeader + 60, 8);
114
#if defined(SHP_BIG_ENDIAN)
115
    SHP_SWAP64(abyHeader + 60);
116
#endif
117
118
1.14k
    dValue = psSHP->adBoundsMin[2]; /* z */
119
1.14k
    ByteCopy(&dValue, abyHeader + 68, 8);
120
#if defined(SHP_BIG_ENDIAN)
121
    SHP_SWAP64(abyHeader + 68);
122
#endif
123
124
1.14k
    dValue = psSHP->adBoundsMax[2];
125
1.14k
    ByteCopy(&dValue, abyHeader + 76, 8);
126
#if defined(SHP_BIG_ENDIAN)
127
    SHP_SWAP64(abyHeader + 76);
128
#endif
129
130
1.14k
    dValue = psSHP->adBoundsMin[3]; /* m */
131
1.14k
    ByteCopy(&dValue, abyHeader + 84, 8);
132
#if defined(SHP_BIG_ENDIAN)
133
    SHP_SWAP64(abyHeader + 84);
134
#endif
135
136
1.14k
    dValue = psSHP->adBoundsMax[3];
137
1.14k
    ByteCopy(&dValue, abyHeader + 92, 8);
138
#if defined(SHP_BIG_ENDIAN)
139
    SHP_SWAP64(abyHeader + 92);
140
#endif
141
142
    /* -------------------------------------------------------------------- */
143
    /*      Write .shp file header.                                         */
144
    /* -------------------------------------------------------------------- */
145
1.14k
    if (psSHP->sHooks.FSeek(psSHP->fpSHP, 0, 0) != 0 ||
146
1.14k
        psSHP->sHooks.FWrite(abyHeader, 100, 1, psSHP->fpSHP) != 1)
147
0
    {
148
0
        char szErrorMsg[200];
149
150
0
        snprintf(szErrorMsg, sizeof(szErrorMsg),
151
0
                 "Failure writing .shp header: %s", strerror(errno));
152
0
        szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
153
0
        psSHP->sHooks.Error(szErrorMsg);
154
0
        return;
155
0
    }
156
157
    /* -------------------------------------------------------------------- */
158
    /*      Prepare, and write .shx file header.                            */
159
    /* -------------------------------------------------------------------- */
160
1.14k
    i32 = (psSHP->nRecords * 2 * sizeof(uint32_t) + 100) / 2; /* file size */
161
1.14k
    ByteCopy(&i32, abyHeader + 24, 4);
162
1.14k
#if !defined(SHP_BIG_ENDIAN)
163
1.14k
    SHP_SWAP32(abyHeader + 24);
164
1.14k
#endif
165
166
1.14k
    if (psSHP->sHooks.FSeek(psSHP->fpSHX, 0, 0) != 0 ||
167
1.14k
        psSHP->sHooks.FWrite(abyHeader, 100, 1, psSHP->fpSHX) != 1)
168
0
    {
169
0
        char szErrorMsg[200];
170
171
0
        snprintf(szErrorMsg, sizeof(szErrorMsg),
172
0
                 "Failure writing .shx header: %s", strerror(errno));
173
0
        szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
174
0
        psSHP->sHooks.Error(szErrorMsg);
175
176
0
        return;
177
0
    }
178
179
    /* -------------------------------------------------------------------- */
180
    /*      Write out the .shx contents.                                    */
181
    /* -------------------------------------------------------------------- */
182
1.14k
    uint32_t *panSHX =
183
1.14k
        STATIC_CAST(uint32_t *, malloc(sizeof(uint32_t) * 2 * psSHP->nRecords));
184
1.14k
    if (panSHX == SHPLIB_NULLPTR)
185
0
    {
186
0
        psSHP->sHooks.Error("Failure allocatin panSHX");
187
0
        return;
188
0
    }
189
190
1.94M
    for (int i = 0; i < psSHP->nRecords; i++)
191
1.94M
    {
192
1.94M
        panSHX[i * 2] = psSHP->panRecOffset[i] / 2;
193
1.94M
        panSHX[i * 2 + 1] = psSHP->panRecSize[i] / 2;
194
1.94M
#if !defined(SHP_BIG_ENDIAN)
195
1.94M
        SHP_SWAP32(panSHX + i * 2);
196
1.94M
        SHP_SWAP32(panSHX + i * 2 + 1);
197
1.94M
#endif
198
1.94M
    }
199
200
1.14k
    if (STATIC_CAST(int, psSHP->sHooks.FWrite(panSHX, sizeof(uint32_t) * 2,
201
1.14k
                                              psSHP->nRecords, psSHP->fpSHX)) !=
202
1.14k
        psSHP->nRecords)
203
0
    {
204
0
        char szErrorMsg[200];
205
206
0
        snprintf(szErrorMsg, sizeof(szErrorMsg),
207
0
                 "Failure writing .shx contents: %s", strerror(errno));
208
0
        szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
209
0
        psSHP->sHooks.Error(szErrorMsg);
210
0
    }
211
212
1.14k
    free(panSHX);
213
214
    /* -------------------------------------------------------------------- */
215
    /*      Flush to disk.                                                  */
216
    /* -------------------------------------------------------------------- */
217
1.14k
    psSHP->sHooks.FFlush(psSHP->fpSHP);
218
1.14k
    psSHP->sHooks.FFlush(psSHP->fpSHX);
219
1.14k
}
220
221
/************************************************************************/
222
/*                              SHPOpen()                               */
223
/************************************************************************/
224
225
SHPHandle SHPAPI_CALL SHPOpen(const char *pszLayer, const char *pszAccess)
226
0
{
227
0
    SAHooks sHooks;
228
229
0
    SASetupDefaultHooks(&sHooks);
230
231
0
    return SHPOpenLL(pszLayer, pszAccess, &sHooks);
232
0
}
233
234
/************************************************************************/
235
/*                      SHPGetLenWithoutExtension()                     */
236
/************************************************************************/
237
238
static int SHPGetLenWithoutExtension(const char *pszBasename)
239
16.6k
{
240
16.6k
    const int nLen = STATIC_CAST(int, strlen(pszBasename));
241
16.6k
    for (int i = nLen - 1;
242
66.6k
         i > 0 && pszBasename[i] != '/' && pszBasename[i] != '\\'; i--)
243
66.6k
    {
244
66.6k
        if (pszBasename[i] == '.')
245
16.6k
        {
246
16.6k
            return i;
247
16.6k
        }
248
66.6k
    }
249
0
    return nLen;
250
16.6k
}
251
252
/************************************************************************/
253
/*                              SHPOpen()                               */
254
/*                                                                      */
255
/*      Open the .shp and .shx files based on the basename of the       */
256
/*      files or either file name.                                      */
257
/************************************************************************/
258
259
SHPHandle SHPAPI_CALL SHPOpenLL(const char *pszLayer, const char *pszAccess,
260
                                const SAHooks *psHooks)
261
16.0k
{
262
    /* -------------------------------------------------------------------- */
263
    /*      Ensure the access string is one of the legal ones.  We          */
264
    /*      ensure the result string indicates binary to avoid common       */
265
    /*      problems on Windows.                                            */
266
    /* -------------------------------------------------------------------- */
267
16.0k
    bool bLazySHXLoading = false;
268
16.0k
    if (strcmp(pszAccess, "rb+") == 0 || strcmp(pszAccess, "r+b") == 0 ||
269
16.0k
        strcmp(pszAccess, "r+") == 0)
270
0
    {
271
0
        pszAccess = "r+b";
272
0
    }
273
16.0k
    else
274
16.0k
    {
275
16.0k
        bLazySHXLoading = strchr(pszAccess, 'l') != SHPLIB_NULLPTR;
276
16.0k
        pszAccess = "rb";
277
16.0k
    }
278
279
    /* -------------------------------------------------------------------- */
280
    /*  Initialize the info structure.                  */
281
    /* -------------------------------------------------------------------- */
282
16.0k
    SHPHandle psSHP = STATIC_CAST(SHPHandle, calloc(1, sizeof(SHPInfo)));
283
16.0k
    if (!psSHP)
284
0
        return SHPLIB_NULLPTR;
285
286
16.0k
    psSHP->bUpdated = FALSE;
287
16.0k
    memcpy(&(psSHP->sHooks), psHooks, sizeof(SAHooks));
288
289
    /* -------------------------------------------------------------------- */
290
    /*  Open the .shp and .shx files.  Note that files pulled from  */
291
    /*  a PC to Unix with upper case filenames won't work!      */
292
    /* -------------------------------------------------------------------- */
293
16.0k
    const int nLenWithoutExtension = SHPGetLenWithoutExtension(pszLayer);
294
16.0k
    char *pszFullname = STATIC_CAST(char *, malloc(nLenWithoutExtension + 5));
295
16.0k
    if (!pszFullname)
296
0
    {
297
0
        free(psSHP);
298
0
        return SHPLIB_NULLPTR;
299
0
    }
300
16.0k
    memcpy(pszFullname, pszLayer, nLenWithoutExtension);
301
16.0k
    memcpy(pszFullname + nLenWithoutExtension, ".shp", 5);
302
16.0k
    psSHP->fpSHP =
303
16.0k
        psSHP->sHooks.FOpen(pszFullname, pszAccess, psSHP->sHooks.pvUserData);
304
16.0k
    if (psSHP->fpSHP == SHPLIB_NULLPTR)
305
14.1k
    {
306
14.1k
        memcpy(pszFullname + nLenWithoutExtension, ".SHP", 5);
307
14.1k
        psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess,
308
14.1k
                                           psSHP->sHooks.pvUserData);
309
14.1k
    }
310
311
16.0k
    if (psSHP->fpSHP == SHPLIB_NULLPTR)
312
14.1k
    {
313
14.1k
        const size_t nMessageLen = strlen(pszFullname) * 2 + 256;
314
14.1k
        char *pszMessage = STATIC_CAST(char *, malloc(nMessageLen));
315
14.1k
        if (pszMessage)
316
14.1k
        {
317
14.1k
            pszFullname[nLenWithoutExtension] = 0;
318
14.1k
            snprintf(pszMessage, nMessageLen,
319
14.1k
                     "Unable to open %s.shp or %s.SHP in %s mode.", pszFullname,
320
14.1k
                     pszFullname, pszAccess);
321
14.1k
            psHooks->Error(pszMessage);
322
14.1k
            free(pszMessage);
323
14.1k
        }
324
325
14.1k
        free(psSHP);
326
14.1k
        free(pszFullname);
327
328
14.1k
        return SHPLIB_NULLPTR;
329
14.1k
    }
330
331
1.88k
    memcpy(pszFullname + nLenWithoutExtension, ".shx", 5);
332
1.88k
    psSHP->fpSHX =
333
1.88k
        psSHP->sHooks.FOpen(pszFullname, pszAccess, psSHP->sHooks.pvUserData);
334
1.88k
    if (psSHP->fpSHX == SHPLIB_NULLPTR)
335
762
    {
336
762
        memcpy(pszFullname + nLenWithoutExtension, ".SHX", 5);
337
762
        psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess,
338
762
                                           psSHP->sHooks.pvUserData);
339
762
    }
340
341
1.88k
    if (psSHP->fpSHX == SHPLIB_NULLPTR)
342
762
    {
343
762
        const size_t nMessageLen =
344
762
            64 + strlen(pszFullname) * 2 + strlen(SHP_RESTORE_SHX_HINT_MESSAGE);
345
762
        char *pszMessage = STATIC_CAST(char *, malloc(nMessageLen));
346
762
        if (pszMessage)
347
762
        {
348
762
            pszFullname[nLenWithoutExtension] = 0;
349
762
            snprintf(
350
762
                pszMessage, nMessageLen,
351
762
                "Unable to open %s.shx or %s.SHX." SHP_RESTORE_SHX_HINT_MESSAGE,
352
762
                pszFullname, pszFullname);
353
762
            psHooks->Error(pszMessage);
354
762
            free(pszMessage);
355
762
        }
356
357
762
        psSHP->sHooks.FClose(psSHP->fpSHP);
358
762
        free(psSHP);
359
762
        free(pszFullname);
360
762
        return SHPLIB_NULLPTR;
361
762
    }
362
363
1.11k
    free(pszFullname);
364
365
    /* -------------------------------------------------------------------- */
366
    /*  Read the file size from the SHP file.               */
367
    /* -------------------------------------------------------------------- */
368
1.11k
    unsigned char *pabyBuf = STATIC_CAST(unsigned char *, malloc(100));
369
1.11k
    if (!pabyBuf || psSHP->sHooks.FRead(pabyBuf, 100, 1, psSHP->fpSHP) != 1)
370
2
    {
371
2
        psSHP->sHooks.Error(".shp file is unreadable, or corrupt.");
372
2
        psSHP->sHooks.FClose(psSHP->fpSHP);
373
2
        psSHP->sHooks.FClose(psSHP->fpSHX);
374
2
        free(pabyBuf);
375
2
        free(psSHP);
376
377
2
        return SHPLIB_NULLPTR;
378
2
    }
379
380
1.11k
    psSHP->nFileSize = (STATIC_CAST(unsigned int, pabyBuf[24]) << 24) |
381
1.11k
                       (pabyBuf[25] << 16) | (pabyBuf[26] << 8) | pabyBuf[27];
382
1.11k
    if (psSHP->nFileSize < UINT_MAX / 2)
383
1.03k
        psSHP->nFileSize *= 2;
384
87
    else
385
87
        psSHP->nFileSize = (UINT_MAX / 2) * 2;
386
387
    /* -------------------------------------------------------------------- */
388
    /*  Read SHX file Header info                                           */
389
    /* -------------------------------------------------------------------- */
390
1.11k
    if (psSHP->sHooks.FRead(pabyBuf, 100, 1, psSHP->fpSHX) != 1 ||
391
1.11k
        pabyBuf[0] != 0 || pabyBuf[1] != 0 || pabyBuf[2] != 0x27 ||
392
1.11k
        (pabyBuf[3] != 0x0a && pabyBuf[3] != 0x0d))
393
7
    {
394
7
        psSHP->sHooks.Error(".shx file is unreadable, or corrupt.");
395
7
        psSHP->sHooks.FClose(psSHP->fpSHP);
396
7
        psSHP->sHooks.FClose(psSHP->fpSHX);
397
7
        free(pabyBuf);
398
7
        free(psSHP);
399
400
7
        return SHPLIB_NULLPTR;
401
7
    }
402
403
1.11k
    psSHP->nRecords = pabyBuf[27] | (pabyBuf[26] << 8) | (pabyBuf[25] << 16) |
404
1.11k
                      ((pabyBuf[24] & 0x7F) << 24);
405
1.11k
    psSHP->nRecords = (psSHP->nRecords - 50) / 4;
406
407
1.11k
    psSHP->nShapeType = pabyBuf[32];
408
409
1.11k
    if (psSHP->nRecords < 0 || psSHP->nRecords > 256000000)
410
3
    {
411
3
        char szErrorMsg[200];
412
413
3
        snprintf(szErrorMsg, sizeof(szErrorMsg),
414
3
                 "Record count in .shx header is %d, which seems\n"
415
3
                 "unreasonable.  Assuming header is corrupt.",
416
3
                 psSHP->nRecords);
417
3
        szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
418
3
        psSHP->sHooks.Error(szErrorMsg);
419
3
        psSHP->sHooks.FClose(psSHP->fpSHP);
420
3
        psSHP->sHooks.FClose(psSHP->fpSHX);
421
3
        free(psSHP);
422
3
        free(pabyBuf);
423
424
3
        return SHPLIB_NULLPTR;
425
3
    }
426
427
    /* If a lot of records are advertized, check that the file is big enough */
428
    /* to hold them */
429
1.10k
    if (psSHP->nRecords >= 1024 * 1024)
430
692
    {
431
692
        psSHP->sHooks.FSeek(psSHP->fpSHX, 0, 2);
432
692
        const SAOffset nFileSize = psSHP->sHooks.FTell(psSHP->fpSHX);
433
692
        if (nFileSize > 100 &&
434
692
            nFileSize / 2 < STATIC_CAST(SAOffset, psSHP->nRecords * 4 + 50))
435
692
        {
436
692
            psSHP->nRecords = STATIC_CAST(int, (nFileSize - 100) / 8);
437
692
        }
438
692
        psSHP->sHooks.FSeek(psSHP->fpSHX, 100, 0);
439
692
    }
440
441
    /* -------------------------------------------------------------------- */
442
    /*      Read the bounds.                                                */
443
    /* -------------------------------------------------------------------- */
444
1.10k
    double dValue;
445
446
#if defined(SHP_BIG_ENDIAN)
447
    SHP_SWAP64(pabyBuf + 36);
448
#endif
449
1.10k
    memcpy(&dValue, pabyBuf + 36, 8);
450
1.10k
    psSHP->adBoundsMin[0] = dValue;
451
452
#if defined(SHP_BIG_ENDIAN)
453
    SHP_SWAP64(pabyBuf + 44);
454
#endif
455
1.10k
    memcpy(&dValue, pabyBuf + 44, 8);
456
1.10k
    psSHP->adBoundsMin[1] = dValue;
457
458
#if defined(SHP_BIG_ENDIAN)
459
    SHP_SWAP64(pabyBuf + 52);
460
#endif
461
1.10k
    memcpy(&dValue, pabyBuf + 52, 8);
462
1.10k
    psSHP->adBoundsMax[0] = dValue;
463
464
#if defined(SHP_BIG_ENDIAN)
465
    SHP_SWAP64(pabyBuf + 60);
466
#endif
467
1.10k
    memcpy(&dValue, pabyBuf + 60, 8);
468
1.10k
    psSHP->adBoundsMax[1] = dValue;
469
470
#if defined(SHP_BIG_ENDIAN)
471
    SHP_SWAP64(pabyBuf + 68); /* z */
472
#endif
473
1.10k
    memcpy(&dValue, pabyBuf + 68, 8);
474
1.10k
    psSHP->adBoundsMin[2] = dValue;
475
476
#if defined(SHP_BIG_ENDIAN)
477
    SHP_SWAP64(pabyBuf + 76);
478
#endif
479
1.10k
    memcpy(&dValue, pabyBuf + 76, 8);
480
1.10k
    psSHP->adBoundsMax[2] = dValue;
481
482
#if defined(SHP_BIG_ENDIAN)
483
    SHP_SWAP64(pabyBuf + 84); /* z */
484
#endif
485
1.10k
    memcpy(&dValue, pabyBuf + 84, 8);
486
1.10k
    psSHP->adBoundsMin[3] = dValue;
487
488
#if defined(SHP_BIG_ENDIAN)
489
    SHP_SWAP64(pabyBuf + 92);
490
#endif
491
1.10k
    memcpy(&dValue, pabyBuf + 92, 8);
492
1.10k
    psSHP->adBoundsMax[3] = dValue;
493
494
1.10k
    free(pabyBuf);
495
496
    /* -------------------------------------------------------------------- */
497
    /*  Read the .shx file to get the offsets to each record in     */
498
    /*  the .shp file.                          */
499
    /* -------------------------------------------------------------------- */
500
1.10k
    psSHP->nMaxRecords = psSHP->nRecords;
501
502
1.10k
    psSHP->panRecOffset =
503
1.10k
        STATIC_CAST(unsigned int *,
504
1.10k
                    malloc(sizeof(unsigned int) * MAX(1, psSHP->nMaxRecords)));
505
1.10k
    psSHP->panRecSize =
506
1.10k
        STATIC_CAST(unsigned int *,
507
1.10k
                    malloc(sizeof(unsigned int) * MAX(1, psSHP->nMaxRecords)));
508
1.10k
    if (bLazySHXLoading)
509
0
        pabyBuf = SHPLIB_NULLPTR;
510
1.10k
    else
511
1.10k
        pabyBuf =
512
1.10k
            STATIC_CAST(unsigned char *, malloc(8 * MAX(1, psSHP->nRecords)));
513
514
1.10k
    if (psSHP->panRecOffset == SHPLIB_NULLPTR ||
515
1.10k
        psSHP->panRecSize == SHPLIB_NULLPTR ||
516
1.10k
        (!bLazySHXLoading && pabyBuf == SHPLIB_NULLPTR))
517
0
    {
518
0
        char szErrorMsg[200];
519
520
0
        snprintf(
521
0
            szErrorMsg, sizeof(szErrorMsg),
522
0
            "Not enough memory to allocate requested memory (nRecords=%d).\n"
523
0
            "Probably broken SHP file",
524
0
            psSHP->nRecords);
525
0
        szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
526
0
        psSHP->sHooks.Error(szErrorMsg);
527
0
        psSHP->sHooks.FClose(psSHP->fpSHP);
528
0
        psSHP->sHooks.FClose(psSHP->fpSHX);
529
0
        if (psSHP->panRecOffset)
530
0
            free(psSHP->panRecOffset);
531
0
        if (psSHP->panRecSize)
532
0
            free(psSHP->panRecSize);
533
0
        if (pabyBuf)
534
0
            free(pabyBuf);
535
0
        free(psSHP);
536
0
        return SHPLIB_NULLPTR;
537
0
    }
538
539
1.10k
    if (bLazySHXLoading)
540
0
    {
541
0
        memset(psSHP->panRecOffset, 0,
542
0
               sizeof(unsigned int) * MAX(1, psSHP->nMaxRecords));
543
0
        memset(psSHP->panRecSize, 0,
544
0
               sizeof(unsigned int) * MAX(1, psSHP->nMaxRecords));
545
0
        free(pabyBuf);  // sometimes make cppcheck happy, but
546
0
        return (psSHP);
547
0
    }
548
549
1.10k
    if (STATIC_CAST(int, psSHP->sHooks.FRead(pabyBuf, 8, psSHP->nRecords,
550
1.10k
                                             psSHP->fpSHX)) != psSHP->nRecords)
551
2
    {
552
2
        char szErrorMsg[200];
553
554
2
        snprintf(szErrorMsg, sizeof(szErrorMsg),
555
2
                 "Failed to read all values for %d records in .shx file: %s.",
556
2
                 psSHP->nRecords, strerror(errno));
557
2
        szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
558
2
        psSHP->sHooks.Error(szErrorMsg);
559
560
        /* SHX is short or unreadable for some reason. */
561
2
        psSHP->sHooks.FClose(psSHP->fpSHP);
562
2
        psSHP->sHooks.FClose(psSHP->fpSHX);
563
2
        free(psSHP->panRecOffset);
564
2
        free(psSHP->panRecSize);
565
2
        free(pabyBuf);
566
2
        free(psSHP);
567
568
2
        return SHPLIB_NULLPTR;
569
2
    }
570
571
    /* In read-only mode, we can close the SHX now */
572
1.10k
    if (strcmp(pszAccess, "rb") == 0)
573
1.10k
    {
574
1.10k
        psSHP->sHooks.FClose(psSHP->fpSHX);
575
1.10k
        psSHP->fpSHX = SHPLIB_NULLPTR;
576
1.10k
    }
577
578
42.9k
    for (int i = 0; i < psSHP->nRecords; i++)
579
41.8k
    {
580
41.8k
        unsigned int nOffset;
581
41.8k
        memcpy(&nOffset, pabyBuf + i * 8, 4);
582
41.8k
#if !defined(SHP_BIG_ENDIAN)
583
41.8k
        SHP_SWAP32(&nOffset);
584
41.8k
#endif
585
586
41.8k
        unsigned int nLength;
587
41.8k
        memcpy(&nLength, pabyBuf + i * 8 + 4, 4);
588
41.8k
#if !defined(SHP_BIG_ENDIAN)
589
41.8k
        SHP_SWAP32(&nLength);
590
41.8k
#endif
591
592
41.8k
        if (nOffset > STATIC_CAST(unsigned int, INT_MAX))
593
10
        {
594
10
            char str[128];
595
10
            snprintf(str, sizeof(str), "Invalid offset for entity %d", i);
596
10
            str[sizeof(str) - 1] = '\0';
597
598
10
            psSHP->sHooks.Error(str);
599
10
            SHPClose(psSHP);
600
10
            free(pabyBuf);
601
10
            return SHPLIB_NULLPTR;
602
10
        }
603
41.8k
        if (nLength > STATIC_CAST(unsigned int, INT_MAX / 2 - 4))
604
11
        {
605
11
            char str[128];
606
11
            snprintf(str, sizeof(str), "Invalid length for entity %d", i);
607
11
            str[sizeof(str) - 1] = '\0';
608
609
11
            psSHP->sHooks.Error(str);
610
11
            SHPClose(psSHP);
611
11
            free(pabyBuf);
612
11
            return SHPLIB_NULLPTR;
613
11
        }
614
41.8k
        psSHP->panRecOffset[i] = nOffset * 2;
615
41.8k
        psSHP->panRecSize[i] = nLength * 2;
616
41.8k
    }
617
1.08k
    free(pabyBuf);
618
619
1.08k
    return (psSHP);
620
1.10k
}
621
622
/************************************************************************/
623
/*                              SHPOpenLLEx()                           */
624
/*                                                                      */
625
/*      Open the .shp and .shx files based on the basename of the       */
626
/*      files or either file name. It generally invokes SHPRestoreSHX() */
627
/*      in case when bRestoreSHX equals true.                           */
628
/************************************************************************/
629
630
SHPHandle SHPAPI_CALL SHPOpenLLEx(const char *pszLayer, const char *pszAccess,
631
                                  const SAHooks *psHooks, int bRestoreSHX)
632
16.0k
{
633
16.0k
    if (!bRestoreSHX)
634
16.0k
        return SHPOpenLL(pszLayer, pszAccess, psHooks);
635
0
    else
636
0
    {
637
0
        if (SHPRestoreSHX(pszLayer, pszAccess, psHooks))
638
0
        {
639
0
            return SHPOpenLL(pszLayer, pszAccess, psHooks);
640
0
        }
641
0
    }
642
643
0
    return SHPLIB_NULLPTR;
644
16.0k
}
645
646
/************************************************************************/
647
/*                              SHPRestoreSHX()                         */
648
/*                                                                      */
649
/*      Restore .SHX file using associated .SHP file.                   */
650
/*                                                                      */
651
/************************************************************************/
652
653
int SHPAPI_CALL SHPRestoreSHX(const char *pszLayer, const char *pszAccess,
654
                              const SAHooks *psHooks)
655
0
{
656
    /* -------------------------------------------------------------------- */
657
    /*      Ensure the access string is one of the legal ones.  We          */
658
    /*      ensure the result string indicates binary to avoid common       */
659
    /*      problems on Windows.                                            */
660
    /* -------------------------------------------------------------------- */
661
0
    if (strcmp(pszAccess, "rb+") == 0 || strcmp(pszAccess, "r+b") == 0 ||
662
0
        strcmp(pszAccess, "r+") == 0)
663
0
    {
664
0
        pszAccess = "r+b";
665
0
    }
666
0
    else
667
0
    {
668
0
        pszAccess = "rb";
669
0
    }
670
671
    /* -------------------------------------------------------------------- */
672
    /*  Open the .shp file.  Note that files pulled from                    */
673
    /*  a PC to Unix with upper case filenames won't work!                  */
674
    /* -------------------------------------------------------------------- */
675
0
    const int nLenWithoutExtension = SHPGetLenWithoutExtension(pszLayer);
676
0
    char *pszFullname = STATIC_CAST(char *, malloc(nLenWithoutExtension + 5));
677
0
    if (!pszFullname)
678
0
        return 0;
679
0
    memcpy(pszFullname, pszLayer, nLenWithoutExtension);
680
0
    memcpy(pszFullname + nLenWithoutExtension, ".shp", 5);
681
0
    SAFile fpSHP = psHooks->FOpen(pszFullname, pszAccess, psHooks->pvUserData);
682
0
    if (fpSHP == SHPLIB_NULLPTR)
683
0
    {
684
0
        memcpy(pszFullname + nLenWithoutExtension, ".SHP", 5);
685
0
        fpSHP = psHooks->FOpen(pszFullname, pszAccess, psHooks->pvUserData);
686
0
    }
687
688
0
    if (fpSHP == SHPLIB_NULLPTR)
689
0
    {
690
0
        const size_t nMessageLen = strlen(pszFullname) * 2 + 256;
691
0
        char *pszMessage = STATIC_CAST(char *, malloc(nMessageLen));
692
0
        if (pszMessage)
693
0
        {
694
0
            pszFullname[nLenWithoutExtension] = 0;
695
0
            snprintf(pszMessage, nMessageLen,
696
0
                     "Unable to open %s.shp or %s.SHP.", pszFullname,
697
0
                     pszFullname);
698
0
            psHooks->Error(pszMessage);
699
0
            free(pszMessage);
700
0
        }
701
702
0
        free(pszFullname);
703
704
0
        return (0);
705
0
    }
706
707
    /* -------------------------------------------------------------------- */
708
    /*  Read the file size from the SHP file.                               */
709
    /* -------------------------------------------------------------------- */
710
0
    unsigned char *pabyBuf = STATIC_CAST(unsigned char *, malloc(100));
711
0
    if (psHooks->FRead(pabyBuf, 100, 1, fpSHP) != 1)
712
0
    {
713
0
        psHooks->Error(".shp file is unreadable, or corrupt.");
714
0
        psHooks->FClose(fpSHP);
715
716
0
        free(pabyBuf);
717
0
        free(pszFullname);
718
719
0
        return (0);
720
0
    }
721
722
0
    unsigned int nSHPFilesize = (STATIC_CAST(unsigned int, pabyBuf[24]) << 24) |
723
0
                                (pabyBuf[25] << 16) | (pabyBuf[26] << 8) |
724
0
                                pabyBuf[27];
725
0
    if (nSHPFilesize < UINT_MAX / 2)
726
0
        nSHPFilesize *= 2;
727
0
    else
728
0
        nSHPFilesize = (UINT_MAX / 2) * 2;
729
730
0
    memcpy(pszFullname + nLenWithoutExtension, ".shx", 5);
731
0
    const char pszSHXAccess[] = "w+b";
732
0
    SAFile fpSHX =
733
0
        psHooks->FOpen(pszFullname, pszSHXAccess, psHooks->pvUserData);
734
0
    if (fpSHX == SHPLIB_NULLPTR)
735
0
    {
736
0
        size_t nMessageLen = strlen(pszFullname) * 2 + 256;
737
0
        char *pszMessage = STATIC_CAST(char *, malloc(nMessageLen));
738
0
        if (pszMessage)
739
0
        {
740
0
            pszFullname[nLenWithoutExtension] = 0;
741
0
            snprintf(pszMessage, nMessageLen,
742
0
                     "Error opening file %s.shx for writing", pszFullname);
743
0
            psHooks->Error(pszMessage);
744
0
            free(pszMessage);
745
0
        }
746
747
0
        psHooks->FClose(fpSHP);
748
749
0
        free(pabyBuf);
750
0
        free(pszFullname);
751
752
0
        return (0);
753
0
    }
754
755
    /* -------------------------------------------------------------------- */
756
    /*  Open SHX and create it using SHP file content.                      */
757
    /* -------------------------------------------------------------------- */
758
0
    psHooks->FSeek(fpSHP, 100, 0);
759
0
    char *pabySHXHeader = STATIC_CAST(char *, malloc(100));
760
0
    if (!pabySHXHeader)
761
0
    {
762
0
        psHooks->FClose(fpSHP);
763
764
0
        free(pabyBuf);
765
0
        free(pszFullname);
766
767
0
        return (0);
768
0
    }
769
0
    memcpy(pabySHXHeader, pabyBuf, 100);
770
0
    psHooks->FWrite(pabySHXHeader, 100, 1, fpSHX);
771
0
    free(pabyBuf);
772
773
    // unsigned int nCurrentRecordOffset = 0;
774
0
    unsigned int nCurrentSHPOffset = 100;
775
0
    unsigned int nRealSHXContentSize = 100;
776
0
    int nRetCode = TRUE;
777
0
    unsigned int nRecordOffset = 50;
778
779
0
    while (nCurrentSHPOffset < nSHPFilesize)
780
0
    {
781
0
        unsigned int niRecord = 0;
782
0
        unsigned int nRecordLength = 0;
783
0
        int nSHPType;
784
785
0
        if (psHooks->FRead(&niRecord, 4, 1, fpSHP) == 1 &&
786
0
            psHooks->FRead(&nRecordLength, 4, 1, fpSHP) == 1 &&
787
0
            psHooks->FRead(&nSHPType, 4, 1, fpSHP) == 1)
788
0
        {
789
0
            char abyReadRecord[8];
790
0
            unsigned int nRecordOffsetBE = nRecordOffset;
791
792
0
#if !defined(SHP_BIG_ENDIAN)
793
0
            SHP_SWAP32(&nRecordOffsetBE);
794
0
#endif
795
0
            memcpy(abyReadRecord, &nRecordOffsetBE, 4);
796
0
            memcpy(abyReadRecord + 4, &nRecordLength, 4);
797
798
0
#if !defined(SHP_BIG_ENDIAN)
799
0
            SHP_SWAP32(&nRecordLength);
800
0
#endif
801
#if defined(SHP_BIG_ENDIAN)
802
            SHP_SWAP32(&nSHPType);
803
#endif
804
805
            // Sanity check on record length
806
0
            if (nRecordLength < 1 ||
807
0
                nRecordLength > (nSHPFilesize - (nCurrentSHPOffset + 8)) / 2)
808
0
            {
809
0
                char szErrorMsg[200];
810
0
                snprintf(szErrorMsg, sizeof(szErrorMsg),
811
0
                         "Error parsing .shp to restore .shx. "
812
0
                         "Invalid record length = %u at record starting at "
813
0
                         "offset %u",
814
0
                         nRecordLength, nCurrentSHPOffset);
815
0
                psHooks->Error(szErrorMsg);
816
817
0
                nRetCode = FALSE;
818
0
                break;
819
0
            }
820
821
            // Sanity check on record type
822
0
            if (nSHPType != SHPT_NULL && nSHPType != SHPT_POINT &&
823
0
                nSHPType != SHPT_ARC && nSHPType != SHPT_POLYGON &&
824
0
                nSHPType != SHPT_MULTIPOINT && nSHPType != SHPT_POINTZ &&
825
0
                nSHPType != SHPT_ARCZ && nSHPType != SHPT_POLYGONZ &&
826
0
                nSHPType != SHPT_MULTIPOINTZ && nSHPType != SHPT_POINTM &&
827
0
                nSHPType != SHPT_ARCM && nSHPType != SHPT_POLYGONM &&
828
0
                nSHPType != SHPT_MULTIPOINTM && nSHPType != SHPT_MULTIPATCH)
829
0
            {
830
0
                char szErrorMsg[200];
831
0
                snprintf(szErrorMsg, sizeof(szErrorMsg),
832
0
                         "Error parsing .shp to restore .shx. "
833
0
                         "Invalid shape type = %d at record starting at "
834
0
                         "offset %u",
835
0
                         nSHPType, nCurrentSHPOffset);
836
0
                psHooks->Error(szErrorMsg);
837
838
0
                nRetCode = FALSE;
839
0
                break;
840
0
            }
841
842
0
            psHooks->FWrite(abyReadRecord, 8, 1, fpSHX);
843
844
0
            nRecordOffset += nRecordLength + 4;
845
            // nCurrentRecordOffset += 8;
846
0
            nCurrentSHPOffset += 8 + nRecordLength * 2;
847
848
0
            psHooks->FSeek(fpSHP, nCurrentSHPOffset, 0);
849
0
            nRealSHXContentSize += 8;
850
0
        }
851
0
        else
852
0
        {
853
0
            char szErrorMsg[200];
854
0
            snprintf(szErrorMsg, sizeof(szErrorMsg),
855
0
                     "Error parsing .shp to restore .shx. "
856
0
                     "Cannot read first bytes of record starting at "
857
0
                     "offset %u",
858
0
                     nCurrentSHPOffset);
859
0
            psHooks->Error(szErrorMsg);
860
861
0
            nRetCode = FALSE;
862
0
            break;
863
0
        }
864
0
    }
865
0
    if (nRetCode && nCurrentSHPOffset != nSHPFilesize)
866
0
    {
867
0
        psHooks->Error("Error parsing .shp to restore .shx. "
868
0
                       "Not expected number of bytes");
869
870
0
        nRetCode = FALSE;
871
0
    }
872
873
0
    nRealSHXContentSize /= 2;  // Bytes counted -> WORDs
874
0
#if !defined(SHP_BIG_ENDIAN)
875
0
    SHP_SWAP32(&nRealSHXContentSize);
876
0
#endif
877
878
0
    psHooks->FSeek(fpSHX, 24, 0);
879
0
    psHooks->FWrite(&nRealSHXContentSize, 4, 1, fpSHX);
880
881
0
    psHooks->FClose(fpSHP);
882
0
    psHooks->FClose(fpSHX);
883
884
0
    free(pszFullname);
885
0
    free(pabySHXHeader);
886
887
0
    return nRetCode;
888
0
}
889
890
/************************************************************************/
891
/*                              SHPClose()                              */
892
/*                                                                      */
893
/*      Close the .shp and .shx files.                                  */
894
/************************************************************************/
895
896
void SHPAPI_CALL SHPClose(SHPHandle psSHP)
897
1.74k
{
898
1.74k
    if (psSHP == SHPLIB_NULLPTR)
899
0
        return;
900
901
    /* -------------------------------------------------------------------- */
902
    /*      Update the header if we have modified anything.                 */
903
    /* -------------------------------------------------------------------- */
904
1.74k
    if (psSHP->bUpdated)
905
571
        SHPWriteHeader(psSHP);
906
907
    /* -------------------------------------------------------------------- */
908
    /*      Free all resources, and close files.                            */
909
    /* -------------------------------------------------------------------- */
910
1.74k
    free(psSHP->panRecOffset);
911
1.74k
    free(psSHP->panRecSize);
912
913
1.74k
    if (psSHP->fpSHX != SHPLIB_NULLPTR)
914
641
        psSHP->sHooks.FClose(psSHP->fpSHX);
915
1.74k
    psSHP->sHooks.FClose(psSHP->fpSHP);
916
917
1.74k
    if (psSHP->pabyRec != SHPLIB_NULLPTR)
918
1.03k
    {
919
1.03k
        free(psSHP->pabyRec);
920
1.03k
    }
921
922
1.74k
    if (psSHP->pabyObjectBuf != SHPLIB_NULLPTR)
923
214
    {
924
214
        free(psSHP->pabyObjectBuf);
925
214
    }
926
1.74k
    if (psSHP->psCachedObject != SHPLIB_NULLPTR)
927
1.72k
    {
928
1.72k
        free(psSHP->psCachedObject);
929
1.72k
    }
930
931
1.74k
    free(psSHP);
932
1.74k
}
933
934
/************************************************************************/
935
/*                    SHPSetFastModeReadObject()                        */
936
/************************************************************************/
937
938
/* If setting bFastMode = TRUE, the content of SHPReadObject() is owned by the SHPHandle. */
939
/* So you cannot have 2 valid instances of SHPReadObject() simultaneously. */
940
/* The SHPObject padfZ and padfM members may be NULL depending on the geometry */
941
/* type. It is illegal to free at hand any of the pointer members of the SHPObject structure */
942
void SHPAPI_CALL SHPSetFastModeReadObject(SHPHandle hSHP, int bFastMode)
943
1.72k
{
944
1.72k
    if (bFastMode)
945
1.72k
    {
946
1.72k
        if (hSHP->psCachedObject == SHPLIB_NULLPTR)
947
1.72k
        {
948
1.72k
            hSHP->psCachedObject =
949
1.72k
                STATIC_CAST(SHPObject *, calloc(1, sizeof(SHPObject)));
950
1.72k
            assert(hSHP->psCachedObject != SHPLIB_NULLPTR);
951
1.72k
        }
952
1.72k
    }
953
954
1.72k
    hSHP->bFastModeReadObject = bFastMode;
955
1.72k
}
956
957
/************************************************************************/
958
/*                             SHPGetInfo()                             */
959
/*                                                                      */
960
/*      Fetch general information about the shape file.                 */
961
/************************************************************************/
962
963
void SHPAPI_CALL SHPGetInfo(const SHPHandle psSHP, int *pnEntities,
964
                            int *pnShapeType, double *padfMinBound,
965
                            double *padfMaxBound)
966
0
{
967
0
    if (psSHP == SHPLIB_NULLPTR)
968
0
        return;
969
970
0
    if (pnEntities != SHPLIB_NULLPTR)
971
0
        *pnEntities = psSHP->nRecords;
972
973
0
    if (pnShapeType != SHPLIB_NULLPTR)
974
0
        *pnShapeType = psSHP->nShapeType;
975
976
0
    for (int i = 0; i < 4; i++)
977
0
    {
978
0
        if (padfMinBound != SHPLIB_NULLPTR)
979
0
            padfMinBound[i] = psSHP->adBoundsMin[i];
980
0
        if (padfMaxBound != SHPLIB_NULLPTR)
981
0
            padfMaxBound[i] = psSHP->adBoundsMax[i];
982
0
    }
983
0
}
984
985
/************************************************************************/
986
/*                             SHPCreate()                              */
987
/*                                                                      */
988
/*      Create a new shape file and return a handle to the open         */
989
/*      shape file with read/write access.                              */
990
/************************************************************************/
991
992
SHPHandle SHPAPI_CALL SHPCreate(const char *pszLayer, int nShapeType)
993
0
{
994
0
    SAHooks sHooks;
995
996
0
    SASetupDefaultHooks(&sHooks);
997
998
0
    return SHPCreateLL(pszLayer, nShapeType, &sHooks);
999
0
}
1000
1001
/************************************************************************/
1002
/*                             SHPCreate()                              */
1003
/*                                                                      */
1004
/*      Create a new shape file and return a handle to the open         */
1005
/*      shape file with read/write access.                              */
1006
/************************************************************************/
1007
1008
SHPHandle SHPAPI_CALL SHPCreateLL(const char *pszLayer, int nShapeType,
1009
                                  const SAHooks *psHooks)
1010
641
{
1011
1012
641
    SHPHandle psSHP = STATIC_CAST(SHPHandle, calloc(1, sizeof(SHPInfo)));
1013
641
    if (!psSHP)
1014
0
        return SHPLIB_NULLPTR;
1015
1016
    /* -------------------------------------------------------------------- */
1017
    /*      Open the two files so we can write their headers.               */
1018
    /* -------------------------------------------------------------------- */
1019
641
    const int nLenWithoutExtension = SHPGetLenWithoutExtension(pszLayer);
1020
641
    char *pszFullname = STATIC_CAST(char *, malloc(nLenWithoutExtension + 5));
1021
641
    if (!pszFullname)
1022
0
    {
1023
0
        free(psSHP);
1024
0
        return SHPLIB_NULLPTR;
1025
0
    }
1026
641
    memcpy(pszFullname, pszLayer, nLenWithoutExtension);
1027
641
    memcpy(pszFullname + nLenWithoutExtension, ".shp", 5);
1028
641
    SAFile fpSHP = psHooks->FOpen(pszFullname, "w+b", psHooks->pvUserData);
1029
641
    if (fpSHP == SHPLIB_NULLPTR)
1030
0
    {
1031
0
        const size_t nMessageLen = strlen(pszFullname) + 256;
1032
0
        char *pszMessage = STATIC_CAST(char *, malloc(nMessageLen));
1033
0
        if (pszMessage)
1034
0
        {
1035
0
            snprintf(pszMessage, nMessageLen, "Failed to create file %s: %s",
1036
0
                     pszFullname, strerror(errno));
1037
0
            psHooks->Error(pszMessage);
1038
0
            free(pszMessage);
1039
0
        }
1040
0
        free(pszFullname);
1041
0
        free(psSHP);
1042
0
        return SHPLIB_NULLPTR;
1043
0
    }
1044
1045
641
    memcpy(pszFullname + nLenWithoutExtension, ".shx", 5);
1046
641
    SAFile fpSHX = psHooks->FOpen(pszFullname, "w+b", psHooks->pvUserData);
1047
641
    if (fpSHX == SHPLIB_NULLPTR)
1048
0
    {
1049
0
        const size_t nMessageLen = strlen(pszFullname) + 256;
1050
0
        char *pszMessage = STATIC_CAST(char *, malloc(nMessageLen));
1051
0
        if (pszMessage)
1052
0
        {
1053
0
            snprintf(pszMessage, nMessageLen, "Failed to create file %s: %s",
1054
0
                     pszFullname, strerror(errno));
1055
0
            psHooks->Error(pszMessage);
1056
0
            free(pszMessage);
1057
0
        }
1058
1059
0
        free(pszFullname);
1060
0
        psHooks->FClose(fpSHP);
1061
0
        free(psSHP);
1062
0
        return SHPLIB_NULLPTR;
1063
0
    }
1064
1065
641
    free(pszFullname);
1066
641
    pszFullname = SHPLIB_NULLPTR;
1067
1068
    /* -------------------------------------------------------------------- */
1069
    /*      Prepare header block for .shp file.                             */
1070
    /* -------------------------------------------------------------------- */
1071
641
    unsigned char abyHeader[100];
1072
641
    memset(abyHeader, 0, sizeof(abyHeader));
1073
1074
641
    abyHeader[2] = 0x27; /* magic cookie */
1075
641
    abyHeader[3] = 0x0a;
1076
1077
641
    uint32_t i32 = 50; /* file size */
1078
641
    ByteCopy(&i32, abyHeader + 24, 4);
1079
641
#if !defined(SHP_BIG_ENDIAN)
1080
641
    SHP_SWAP32(abyHeader + 24);
1081
641
#endif
1082
1083
641
    i32 = 1000; /* version */
1084
641
    ByteCopy(&i32, abyHeader + 28, 4);
1085
#if defined(SHP_BIG_ENDIAN)
1086
    SHP_SWAP32(abyHeader + 28);
1087
#endif
1088
1089
641
    i32 = nShapeType; /* shape type */
1090
641
    ByteCopy(&i32, abyHeader + 32, 4);
1091
#if defined(SHP_BIG_ENDIAN)
1092
    SHP_SWAP32(abyHeader + 32);
1093
#endif
1094
1095
641
    double dValue = 0.0; /* set bounds */
1096
641
    ByteCopy(&dValue, abyHeader + 36, 8);
1097
641
    ByteCopy(&dValue, abyHeader + 44, 8);
1098
641
    ByteCopy(&dValue, abyHeader + 52, 8);
1099
641
    ByteCopy(&dValue, abyHeader + 60, 8);
1100
1101
    /* -------------------------------------------------------------------- */
1102
    /*      Write .shp file header.                                         */
1103
    /* -------------------------------------------------------------------- */
1104
641
    if (psHooks->FWrite(abyHeader, 100, 1, fpSHP) != 1)
1105
0
    {
1106
0
        char szErrorMsg[200];
1107
1108
0
        snprintf(szErrorMsg, sizeof(szErrorMsg),
1109
0
                 "Failed to write .shp header: %s", strerror(errno));
1110
0
        szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
1111
0
        psHooks->Error(szErrorMsg);
1112
1113
0
        free(pszFullname);
1114
0
        psHooks->FClose(fpSHP);
1115
0
        psHooks->FClose(fpSHX);
1116
0
        free(psSHP);
1117
0
        return SHPLIB_NULLPTR;
1118
0
    }
1119
1120
    /* -------------------------------------------------------------------- */
1121
    /*      Prepare, and write .shx file header.                            */
1122
    /* -------------------------------------------------------------------- */
1123
641
    i32 = 50; /* file size */
1124
641
    ByteCopy(&i32, abyHeader + 24, 4);
1125
641
#if !defined(SHP_BIG_ENDIAN)
1126
641
    SHP_SWAP32(abyHeader + 24);
1127
641
#endif
1128
1129
641
    if (psHooks->FWrite(abyHeader, 100, 1, fpSHX) != 1)
1130
0
    {
1131
0
        char szErrorMsg[200];
1132
1133
0
        snprintf(szErrorMsg, sizeof(szErrorMsg),
1134
0
                 "Failure writing .shx header: %s", strerror(errno));
1135
0
        szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
1136
0
        psHooks->Error(szErrorMsg);
1137
1138
0
        free(pszFullname);
1139
0
        psHooks->FClose(fpSHP);
1140
0
        psHooks->FClose(fpSHX);
1141
0
        free(psSHP);
1142
0
        return SHPLIB_NULLPTR;
1143
0
    }
1144
1145
641
    psSHP->bUpdated = FALSE;
1146
641
    memcpy(&(psSHP->sHooks), psHooks, sizeof(SAHooks));
1147
1148
641
    psSHP->fpSHP = fpSHP;
1149
641
    psSHP->fpSHX = fpSHX;
1150
641
    psSHP->nShapeType = nShapeType;
1151
641
    psSHP->nFileSize = 100;
1152
641
    psSHP->panRecOffset =
1153
641
        STATIC_CAST(unsigned int *, malloc(sizeof(unsigned int)));
1154
641
    psSHP->panRecSize =
1155
641
        STATIC_CAST(unsigned int *, malloc(sizeof(unsigned int)));
1156
1157
641
    if (psSHP->panRecOffset == SHPLIB_NULLPTR ||
1158
641
        psSHP->panRecSize == SHPLIB_NULLPTR)
1159
0
    {
1160
0
        psSHP->sHooks.Error("Not enough memory to allocate requested memory");
1161
0
        psSHP->sHooks.FClose(psSHP->fpSHP);
1162
0
        psSHP->sHooks.FClose(psSHP->fpSHX);
1163
0
        if (psSHP->panRecOffset)
1164
0
            free(psSHP->panRecOffset);
1165
0
        if (psSHP->panRecSize)
1166
0
            free(psSHP->panRecSize);
1167
0
        free(psSHP);
1168
0
        return SHPLIB_NULLPTR;
1169
0
    }
1170
1171
641
    return psSHP;
1172
641
}
1173
1174
/************************************************************************/
1175
/*                           _SHPSetBounds()                            */
1176
/*                                                                      */
1177
/*      Compute a bounds rectangle for a shape, and set it into the     */
1178
/*      indicated location in the record.                               */
1179
/************************************************************************/
1180
1181
static void _SHPSetBounds(unsigned char *pabyRec, const SHPObject *psShape)
1182
30
{
1183
30
    ByteCopy(&(psShape->dfXMin), pabyRec + 0, 8);
1184
30
    ByteCopy(&(psShape->dfYMin), pabyRec + 8, 8);
1185
30
    ByteCopy(&(psShape->dfXMax), pabyRec + 16, 8);
1186
30
    ByteCopy(&(psShape->dfYMax), pabyRec + 24, 8);
1187
1188
#if defined(SHP_BIG_ENDIAN)
1189
    SHP_SWAP64(pabyRec + 0);
1190
    SHP_SWAP64(pabyRec + 8);
1191
    SHP_SWAP64(pabyRec + 16);
1192
    SHP_SWAP64(pabyRec + 24);
1193
#endif
1194
30
}
1195
1196
/************************************************************************/
1197
/*                         SHPComputeExtents()                          */
1198
/*                                                                      */
1199
/*      Recompute the extents of a shape.  Automatically done by        */
1200
/*      SHPCreateObject().                                              */
1201
/************************************************************************/
1202
1203
void SHPAPI_CALL SHPComputeExtents(SHPObject *psObject)
1204
971k
{
1205
    /* -------------------------------------------------------------------- */
1206
    /*      Build extents for this object.                                  */
1207
    /* -------------------------------------------------------------------- */
1208
971k
    if (psObject->nVertices > 0)
1209
39
    {
1210
39
        psObject->dfXMin = psObject->dfXMax = psObject->padfX[0];
1211
39
        psObject->dfYMin = psObject->dfYMax = psObject->padfY[0];
1212
39
        psObject->dfZMin = psObject->dfZMax = psObject->padfZ[0];
1213
39
        psObject->dfMMin = psObject->dfMMax = psObject->padfM[0];
1214
39
    }
1215
1216
972k
    for (int i = 0; i < psObject->nVertices; i++)
1217
135
    {
1218
135
        psObject->dfXMin = MIN(psObject->dfXMin, psObject->padfX[i]);
1219
135
        psObject->dfYMin = MIN(psObject->dfYMin, psObject->padfY[i]);
1220
135
        psObject->dfZMin = MIN(psObject->dfZMin, psObject->padfZ[i]);
1221
135
        psObject->dfMMin = MIN(psObject->dfMMin, psObject->padfM[i]);
1222
1223
135
        psObject->dfXMax = MAX(psObject->dfXMax, psObject->padfX[i]);
1224
135
        psObject->dfYMax = MAX(psObject->dfYMax, psObject->padfY[i]);
1225
135
        psObject->dfZMax = MAX(psObject->dfZMax, psObject->padfZ[i]);
1226
135
        psObject->dfMMax = MAX(psObject->dfMMax, psObject->padfM[i]);
1227
135
    }
1228
971k
}
1229
1230
/************************************************************************/
1231
/*                          SHPCreateObject()                           */
1232
/*                                                                      */
1233
/*      Create a shape object.  It should be freed with                 */
1234
/*      SHPDestroyObject().                                             */
1235
/************************************************************************/
1236
1237
SHPObject SHPAPI_CALL1(*)
1238
    SHPCreateObject(int nSHPType, int nShapeId, int nParts,
1239
                    const int *panPartStart, const int *panPartType,
1240
                    int nVertices, const double *padfX, const double *padfY,
1241
                    const double *padfZ, const double *padfM)
1242
971k
{
1243
971k
    SHPObject *psObject =
1244
971k
        STATIC_CAST(SHPObject *, calloc(1, sizeof(SHPObject)));
1245
971k
    if (!psObject)
1246
0
        return SHPLIB_NULLPTR;
1247
971k
    psObject->nSHPType = nSHPType;
1248
971k
    psObject->nShapeId = nShapeId;
1249
971k
    psObject->bMeasureIsUsed = FALSE;
1250
1251
    /* -------------------------------------------------------------------- */
1252
    /*      Establish whether this shape type has M, and Z values.          */
1253
    /* -------------------------------------------------------------------- */
1254
971k
    bool bHasM;
1255
971k
    bool bHasZ;
1256
1257
971k
    if (nSHPType == SHPT_ARCM || nSHPType == SHPT_POINTM ||
1258
971k
        nSHPType == SHPT_POLYGONM || nSHPType == SHPT_MULTIPOINTM)
1259
0
    {
1260
0
        bHasM = true;
1261
0
        bHasZ = false;
1262
0
    }
1263
971k
    else if (nSHPType == SHPT_ARCZ || nSHPType == SHPT_POINTZ ||
1264
971k
             nSHPType == SHPT_POLYGONZ || nSHPType == SHPT_MULTIPOINTZ ||
1265
971k
             nSHPType == SHPT_MULTIPATCH)
1266
3
    {
1267
3
        bHasM = true;
1268
3
        bHasZ = true;
1269
3
    }
1270
971k
    else
1271
971k
    {
1272
971k
        bHasM = false;
1273
971k
        bHasZ = false;
1274
971k
    }
1275
1276
    /* -------------------------------------------------------------------- */
1277
    /*      Capture parts.  Note that part type is optional, and            */
1278
    /*      defaults to ring.                                               */
1279
    /* -------------------------------------------------------------------- */
1280
971k
    if (nSHPType == SHPT_ARC || nSHPType == SHPT_POLYGON ||
1281
971k
        nSHPType == SHPT_ARCM || nSHPType == SHPT_POLYGONM ||
1282
971k
        nSHPType == SHPT_ARCZ || nSHPType == SHPT_POLYGONZ ||
1283
971k
        nSHPType == SHPT_MULTIPATCH)
1284
27
    {
1285
27
        psObject->nParts = MAX(1, nParts);
1286
1287
27
        psObject->panPartStart =
1288
27
            STATIC_CAST(int *, calloc(psObject->nParts, sizeof(int)));
1289
27
        psObject->panPartType =
1290
27
            STATIC_CAST(int *, malloc(sizeof(int) * psObject->nParts));
1291
27
        if (!psObject->panPartStart || !psObject->panPartType)
1292
0
        {
1293
0
            free(psObject->panPartStart);
1294
0
            free(psObject->panPartType);
1295
0
            free(psObject);
1296
0
            return SHPLIB_NULLPTR;
1297
0
        }
1298
1299
27
        psObject->panPartStart[0] = 0;
1300
27
        psObject->panPartType[0] = SHPP_RING;
1301
1302
54
        for (int i = 0; i < nParts; i++)
1303
27
        {
1304
27
            if (panPartStart != SHPLIB_NULLPTR)
1305
27
                psObject->panPartStart[i] = panPartStart[i];
1306
1307
27
            if (panPartType != SHPLIB_NULLPTR)
1308
0
                psObject->panPartType[i] = panPartType[i];
1309
27
            else
1310
27
                psObject->panPartType[i] = SHPP_RING;
1311
27
        }
1312
1313
27
        psObject->panPartStart[0] = 0;
1314
27
    }
1315
1316
    /* -------------------------------------------------------------------- */
1317
    /*      Capture vertices.  Note that X, Y, Z and M are optional.        */
1318
    /* -------------------------------------------------------------------- */
1319
971k
    if (nVertices > 0)
1320
39
    {
1321
39
        const size_t nSize = sizeof(double) * nVertices;
1322
39
        psObject->padfX =
1323
39
            STATIC_CAST(double *, padfX ? malloc(nSize)
1324
39
                                        : calloc(nVertices, sizeof(double)));
1325
39
        psObject->padfY =
1326
39
            STATIC_CAST(double *, padfY ? malloc(nSize)
1327
39
                                        : calloc(nVertices, sizeof(double)));
1328
39
        psObject->padfZ = STATIC_CAST(
1329
39
            double *,
1330
39
            padfZ &&bHasZ ? malloc(nSize) : calloc(nVertices, sizeof(double)));
1331
39
        psObject->padfM = STATIC_CAST(
1332
39
            double *,
1333
39
            padfM &&bHasM ? malloc(nSize) : calloc(nVertices, sizeof(double)));
1334
39
        if (!psObject->padfX || !psObject->padfY || !psObject->padfZ ||
1335
39
            !psObject->padfM)
1336
0
        {
1337
0
            free(psObject->panPartStart);
1338
0
            free(psObject->panPartType);
1339
0
            free(psObject->padfX);
1340
0
            free(psObject->padfY);
1341
0
            free(psObject->padfZ);
1342
0
            free(psObject->padfM);
1343
0
            free(psObject);
1344
0
            return SHPLIB_NULLPTR;
1345
0
        }
1346
39
        if (padfX != SHPLIB_NULLPTR)
1347
39
            memcpy(psObject->padfX, padfX, nSize);
1348
39
        if (padfY != SHPLIB_NULLPTR)
1349
39
            memcpy(psObject->padfY, padfY, nSize);
1350
39
        if (padfZ != SHPLIB_NULLPTR && bHasZ)
1351
3
            memcpy(psObject->padfZ, padfZ, nSize);
1352
39
        if (padfM != SHPLIB_NULLPTR && bHasM)
1353
0
        {
1354
0
            memcpy(psObject->padfM, padfM, nSize);
1355
0
            psObject->bMeasureIsUsed = TRUE;
1356
0
        }
1357
39
    }
1358
1359
    /* -------------------------------------------------------------------- */
1360
    /*      Compute the extents.                                            */
1361
    /* -------------------------------------------------------------------- */
1362
971k
    psObject->nVertices = nVertices;
1363
971k
    SHPComputeExtents(psObject);
1364
1365
971k
    return (psObject);
1366
971k
}
1367
1368
/************************************************************************/
1369
/*                       SHPCreateSimpleObject()                        */
1370
/*                                                                      */
1371
/*      Create a simple (common) shape object.  Destroy with            */
1372
/*      SHPDestroyObject().                                             */
1373
/************************************************************************/
1374
1375
SHPObject SHPAPI_CALL1(*)
1376
    SHPCreateSimpleObject(int nSHPType, int nVertices, const double *padfX,
1377
                          const double *padfY, const double *padfZ)
1378
0
{
1379
0
    return (SHPCreateObject(nSHPType, -1, 0, SHPLIB_NULLPTR, SHPLIB_NULLPTR,
1380
0
                            nVertices, padfX, padfY, padfZ, SHPLIB_NULLPTR));
1381
0
}
1382
1383
/************************************************************************/
1384
/*                           SHPWriteObject()                           */
1385
/*                                                                      */
1386
/*      Write out the vertices of a new structure.  Note that it is     */
1387
/*      only possible to write vertices at the end of the file.         */
1388
/************************************************************************/
1389
1390
int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId,
1391
                               const SHPObject *psObject)
1392
971k
{
1393
971k
    psSHP->bUpdated = TRUE;
1394
1395
    /* -------------------------------------------------------------------- */
1396
    /*      Ensure that shape object matches the type of the file it is     */
1397
    /*      being written to.                                               */
1398
    /* -------------------------------------------------------------------- */
1399
971k
    assert(psObject->nSHPType == psSHP->nShapeType ||
1400
971k
           psObject->nSHPType == SHPT_NULL);
1401
1402
    /* -------------------------------------------------------------------- */
1403
    /*      Ensure that -1 is used for appends.  Either blow an             */
1404
    /*      assertion, or if they are disabled, set the shapeid to -1       */
1405
    /*      for appends.                                                    */
1406
    /* -------------------------------------------------------------------- */
1407
971k
    assert(nShapeId == -1 || (nShapeId >= 0 && nShapeId < psSHP->nRecords));
1408
1409
971k
    if (nShapeId != -1 && nShapeId >= psSHP->nRecords)
1410
0
        nShapeId = -1;
1411
1412
    /* -------------------------------------------------------------------- */
1413
    /*      Add the new entity to the in memory index.                      */
1414
    /* -------------------------------------------------------------------- */
1415
971k
    if (nShapeId == -1 && psSHP->nRecords + 1 > psSHP->nMaxRecords)
1416
2.70k
    {
1417
        /* This cannot overflow given that we check that the file size does
1418
         * not grow over 4 GB, and the minimum size of a record is 12 bytes,
1419
         * hence the maximm value for nMaxRecords is 357,913,941
1420
         */
1421
2.70k
        int nNewMaxRecords = psSHP->nMaxRecords + psSHP->nMaxRecords / 3 + 100;
1422
2.70k
        unsigned int *panRecOffsetNew;
1423
2.70k
        unsigned int *panRecSizeNew;
1424
1425
2.70k
        panRecOffsetNew = STATIC_CAST(
1426
2.70k
            unsigned int *, realloc(psSHP->panRecOffset,
1427
2.70k
                                    sizeof(unsigned int) * nNewMaxRecords));
1428
2.70k
        if (panRecOffsetNew == SHPLIB_NULLPTR)
1429
0
        {
1430
0
            psSHP->sHooks.Error("Failed to write shape object. "
1431
0
                                "Memory allocation error.");
1432
0
            return -1;
1433
0
        }
1434
2.70k
        psSHP->panRecOffset = panRecOffsetNew;
1435
1436
2.70k
        panRecSizeNew = STATIC_CAST(
1437
2.70k
            unsigned int *,
1438
2.70k
            realloc(psSHP->panRecSize, sizeof(unsigned int) * nNewMaxRecords));
1439
2.70k
        if (panRecSizeNew == SHPLIB_NULLPTR)
1440
0
        {
1441
0
            psSHP->sHooks.Error("Failed to write shape object. "
1442
0
                                "Memory allocation error.");
1443
0
            return -1;
1444
0
        }
1445
2.70k
        psSHP->panRecSize = panRecSizeNew;
1446
1447
2.70k
        psSHP->nMaxRecords = nNewMaxRecords;
1448
2.70k
    }
1449
1450
    /* -------------------------------------------------------------------- */
1451
    /*      Initialize record.                                              */
1452
    /* -------------------------------------------------------------------- */
1453
1454
    /* The following computation cannot overflow on 32-bit platforms given that
1455
     * the user had to allocate arrays of at least that size. */
1456
971k
    size_t nRecMaxSize =
1457
971k
        psObject->nVertices * 4 * sizeof(double) + psObject->nParts * 8;
1458
    /* But the following test could trigger on 64-bit platforms on huge
1459
     * geometries. */
1460
971k
    const unsigned nExtraSpaceForGeomHeader = 128;
1461
971k
    if (nRecMaxSize > UINT_MAX - nExtraSpaceForGeomHeader)
1462
0
    {
1463
0
        psSHP->sHooks.Error("Failed to write shape object. Too big geometry.");
1464
0
        return -1;
1465
0
    }
1466
971k
    nRecMaxSize += nExtraSpaceForGeomHeader;
1467
971k
    unsigned char *pabyRec = STATIC_CAST(unsigned char *, malloc(nRecMaxSize));
1468
971k
    if (pabyRec == SHPLIB_NULLPTR)
1469
0
    {
1470
0
        psSHP->sHooks.Error("Failed to write shape object. "
1471
0
                            "Memory allocation error.");
1472
0
        return -1;
1473
0
    }
1474
1475
    /* -------------------------------------------------------------------- */
1476
    /*      Extract vertices for a Polygon or Arc.                          */
1477
    /* -------------------------------------------------------------------- */
1478
971k
    unsigned int nRecordSize = 0;
1479
971k
    const bool bFirstFeature = psSHP->nRecords == 0;
1480
1481
971k
    if (psObject->nSHPType == SHPT_POLYGON ||
1482
971k
        psObject->nSHPType == SHPT_POLYGONZ ||
1483
971k
        psObject->nSHPType == SHPT_POLYGONM || psObject->nSHPType == SHPT_ARC ||
1484
971k
        psObject->nSHPType == SHPT_ARCZ || psObject->nSHPType == SHPT_ARCM ||
1485
971k
        psObject->nSHPType == SHPT_MULTIPATCH)
1486
27
    {
1487
27
        uint32_t nPoints = psObject->nVertices;
1488
27
        uint32_t nParts = psObject->nParts;
1489
1490
27
        _SHPSetBounds(pabyRec + 12, psObject);
1491
1492
#if defined(SHP_BIG_ENDIAN)
1493
        SHP_SWAP32(&nPoints);
1494
        SHP_SWAP32(&nParts);
1495
#endif
1496
1497
27
        ByteCopy(&nPoints, pabyRec + 40 + 8, 4);
1498
27
        ByteCopy(&nParts, pabyRec + 36 + 8, 4);
1499
1500
27
        nRecordSize = 52;
1501
1502
        /*
1503
         * Write part start positions.
1504
         */
1505
27
        ByteCopy(psObject->panPartStart, pabyRec + 44 + 8,
1506
27
                 4 * psObject->nParts);
1507
54
        for (int i = 0; i < psObject->nParts; i++)
1508
27
        {
1509
#if defined(SHP_BIG_ENDIAN)
1510
            SHP_SWAP32(pabyRec + 44 + 8 + 4 * i);
1511
#endif
1512
27
            nRecordSize += 4;
1513
27
        }
1514
1515
        /*
1516
         * Write multipatch part types if needed.
1517
         */
1518
27
        if (psObject->nSHPType == SHPT_MULTIPATCH)
1519
0
        {
1520
0
            memcpy(pabyRec + nRecordSize, psObject->panPartType,
1521
0
                   4 * psObject->nParts);
1522
0
            for (int i = 0; i < psObject->nParts; i++)
1523
0
            {
1524
#if defined(SHP_BIG_ENDIAN)
1525
                SHP_SWAP32(pabyRec + nRecordSize);
1526
#endif
1527
0
                nRecordSize += 4;
1528
0
            }
1529
0
        }
1530
1531
        /*
1532
         * Write the (x,y) vertex values.
1533
         */
1534
150
        for (int i = 0; i < psObject->nVertices; i++)
1535
123
        {
1536
123
            ByteCopy(psObject->padfX + i, pabyRec + nRecordSize, 8);
1537
123
            ByteCopy(psObject->padfY + i, pabyRec + nRecordSize + 8, 8);
1538
1539
#if defined(SHP_BIG_ENDIAN)
1540
            SHP_SWAP64(pabyRec + nRecordSize);
1541
            SHP_SWAP64(pabyRec + nRecordSize + 8);
1542
#endif
1543
1544
123
            nRecordSize += 2 * 8;
1545
123
        }
1546
1547
        /*
1548
         * Write the Z coordinates (if any).
1549
         */
1550
27
        if (psObject->nSHPType == SHPT_POLYGONZ ||
1551
27
            psObject->nSHPType == SHPT_ARCZ ||
1552
27
            psObject->nSHPType == SHPT_MULTIPATCH)
1553
1
        {
1554
1
            ByteCopy(&(psObject->dfZMin), pabyRec + nRecordSize, 8);
1555
#if defined(SHP_BIG_ENDIAN)
1556
            SHP_SWAP64(pabyRec + nRecordSize);
1557
#endif
1558
1
            nRecordSize += 8;
1559
1560
1
            ByteCopy(&(psObject->dfZMax), pabyRec + nRecordSize, 8);
1561
#if defined(SHP_BIG_ENDIAN)
1562
            SHP_SWAP64(pabyRec + nRecordSize);
1563
#endif
1564
1
            nRecordSize += 8;
1565
1566
55
            for (int i = 0; i < psObject->nVertices; i++)
1567
54
            {
1568
54
                ByteCopy(psObject->padfZ + i, pabyRec + nRecordSize, 8);
1569
#if defined(SHP_BIG_ENDIAN)
1570
                SHP_SWAP64(pabyRec + nRecordSize);
1571
#endif
1572
54
                nRecordSize += 8;
1573
54
            }
1574
1
        }
1575
1576
        /*
1577
         * Write the M values, if any.
1578
         */
1579
27
        if (psObject->bMeasureIsUsed &&
1580
27
            (psObject->nSHPType == SHPT_POLYGONM ||
1581
0
             psObject->nSHPType == SHPT_ARCM
1582
#ifndef DISABLE_MULTIPATCH_MEASURE
1583
             || psObject->nSHPType == SHPT_MULTIPATCH
1584
#endif
1585
0
             || psObject->nSHPType == SHPT_POLYGONZ ||
1586
0
             psObject->nSHPType == SHPT_ARCZ))
1587
0
        {
1588
0
            ByteCopy(&(psObject->dfMMin), pabyRec + nRecordSize, 8);
1589
#if defined(SHP_BIG_ENDIAN)
1590
            SHP_SWAP64(pabyRec + nRecordSize);
1591
#endif
1592
0
            nRecordSize += 8;
1593
1594
0
            ByteCopy(&(psObject->dfMMax), pabyRec + nRecordSize, 8);
1595
#if defined(SHP_BIG_ENDIAN)
1596
            SHP_SWAP64(pabyRec + nRecordSize);
1597
#endif
1598
0
            nRecordSize += 8;
1599
1600
0
            for (int i = 0; i < psObject->nVertices; i++)
1601
0
            {
1602
0
                ByteCopy(psObject->padfM + i, pabyRec + nRecordSize, 8);
1603
#if defined(SHP_BIG_ENDIAN)
1604
                SHP_SWAP64(pabyRec + nRecordSize);
1605
#endif
1606
0
                nRecordSize += 8;
1607
0
            }
1608
0
        }
1609
27
    }
1610
1611
    /* -------------------------------------------------------------------- */
1612
    /*      Extract vertices for a MultiPoint.                              */
1613
    /* -------------------------------------------------------------------- */
1614
971k
    else if (psObject->nSHPType == SHPT_MULTIPOINT ||
1615
971k
             psObject->nSHPType == SHPT_MULTIPOINTZ ||
1616
971k
             psObject->nSHPType == SHPT_MULTIPOINTM)
1617
3
    {
1618
3
        uint32_t nPoints = psObject->nVertices;
1619
1620
3
        _SHPSetBounds(pabyRec + 12, psObject);
1621
1622
#if defined(SHP_BIG_ENDIAN)
1623
        SHP_SWAP32(&nPoints);
1624
#endif
1625
3
        ByteCopy(&nPoints, pabyRec + 44, 4);
1626
1627
6
        for (int i = 0; i < psObject->nVertices; i++)
1628
3
        {
1629
3
            ByteCopy(psObject->padfX + i, pabyRec + 48 + i * 16, 8);
1630
3
            ByteCopy(psObject->padfY + i, pabyRec + 48 + i * 16 + 8, 8);
1631
1632
#if defined(SHP_BIG_ENDIAN)
1633
            SHP_SWAP64(pabyRec + 48 + i * 16);
1634
            SHP_SWAP64(pabyRec + 48 + i * 16 + 8);
1635
#endif
1636
3
        }
1637
1638
3
        nRecordSize = 48 + 16 * psObject->nVertices;
1639
1640
3
        if (psObject->nSHPType == SHPT_MULTIPOINTZ)
1641
0
        {
1642
0
            ByteCopy(&(psObject->dfZMin), pabyRec + nRecordSize, 8);
1643
#if defined(SHP_BIG_ENDIAN)
1644
            SHP_SWAP64(pabyRec + nRecordSize);
1645
#endif
1646
0
            nRecordSize += 8;
1647
1648
0
            ByteCopy(&(psObject->dfZMax), pabyRec + nRecordSize, 8);
1649
#if defined(SHP_BIG_ENDIAN)
1650
            SHP_SWAP64(pabyRec + nRecordSize);
1651
#endif
1652
0
            nRecordSize += 8;
1653
1654
0
            for (int i = 0; i < psObject->nVertices; i++)
1655
0
            {
1656
0
                ByteCopy(psObject->padfZ + i, pabyRec + nRecordSize, 8);
1657
#if defined(SHP_BIG_ENDIAN)
1658
                SHP_SWAP64(pabyRec + nRecordSize);
1659
#endif
1660
0
                nRecordSize += 8;
1661
0
            }
1662
0
        }
1663
1664
3
        if (psObject->bMeasureIsUsed &&
1665
3
            (psObject->nSHPType == SHPT_MULTIPOINTZ ||
1666
0
             psObject->nSHPType == SHPT_MULTIPOINTM))
1667
0
        {
1668
0
            ByteCopy(&(psObject->dfMMin), pabyRec + nRecordSize, 8);
1669
#if defined(SHP_BIG_ENDIAN)
1670
            SHP_SWAP64(pabyRec + nRecordSize);
1671
#endif
1672
0
            nRecordSize += 8;
1673
1674
0
            ByteCopy(&(psObject->dfMMax), pabyRec + nRecordSize, 8);
1675
#if defined(SHP_BIG_ENDIAN)
1676
            SHP_SWAP64(pabyRec + nRecordSize);
1677
#endif
1678
0
            nRecordSize += 8;
1679
1680
0
            for (int i = 0; i < psObject->nVertices; i++)
1681
0
            {
1682
0
                ByteCopy(psObject->padfM + i, pabyRec + nRecordSize, 8);
1683
#if defined(SHP_BIG_ENDIAN)
1684
                SHP_SWAP64(pabyRec + nRecordSize);
1685
#endif
1686
0
                nRecordSize += 8;
1687
0
            }
1688
0
        }
1689
3
    }
1690
1691
    /* -------------------------------------------------------------------- */
1692
    /*      Write point.                                                    */
1693
    /* -------------------------------------------------------------------- */
1694
971k
    else if (psObject->nSHPType == SHPT_POINT ||
1695
971k
             psObject->nSHPType == SHPT_POINTZ ||
1696
971k
             psObject->nSHPType == SHPT_POINTM)
1697
9
    {
1698
9
        ByteCopy(psObject->padfX, pabyRec + 12, 8);
1699
9
        ByteCopy(psObject->padfY, pabyRec + 20, 8);
1700
1701
#if defined(SHP_BIG_ENDIAN)
1702
        SHP_SWAP64(pabyRec + 12);
1703
        SHP_SWAP64(pabyRec + 20);
1704
#endif
1705
1706
9
        nRecordSize = 28;
1707
1708
9
        if (psObject->nSHPType == SHPT_POINTZ)
1709
2
        {
1710
2
            ByteCopy(psObject->padfZ, pabyRec + nRecordSize, 8);
1711
#if defined(SHP_BIG_ENDIAN)
1712
            SHP_SWAP64(pabyRec + nRecordSize);
1713
#endif
1714
2
            nRecordSize += 8;
1715
2
        }
1716
1717
9
        if (psObject->bMeasureIsUsed && (psObject->nSHPType == SHPT_POINTZ ||
1718
0
                                         psObject->nSHPType == SHPT_POINTM))
1719
0
        {
1720
0
            ByteCopy(psObject->padfM, pabyRec + nRecordSize, 8);
1721
#if defined(SHP_BIG_ENDIAN)
1722
            SHP_SWAP64(pabyRec + nRecordSize);
1723
#endif
1724
0
            nRecordSize += 8;
1725
0
        }
1726
9
    }
1727
1728
    /* -------------------------------------------------------------------- */
1729
    /*      Not much to do for null geometries.                             */
1730
    /* -------------------------------------------------------------------- */
1731
971k
    else if (psObject->nSHPType == SHPT_NULL)
1732
971k
    {
1733
971k
        nRecordSize = 12;
1734
971k
    }
1735
0
    else
1736
0
    {
1737
        /* unknown type */
1738
0
        assert(false);
1739
0
    }
1740
1741
    /* -------------------------------------------------------------------- */
1742
    /*      Establish where we are going to put this record. If we are      */
1743
    /*      rewriting the last record of the file, then we can update it in */
1744
    /*      place. Otherwise if rewriting an existing record, and it will   */
1745
    /*      fit, then put it  back where the original came from.  Otherwise */
1746
    /*      write at the end.                                               */
1747
    /* -------------------------------------------------------------------- */
1748
971k
    SAOffset nRecordOffset;
1749
971k
    bool bAppendToLastRecord = false;
1750
971k
    bool bAppendToFile = false;
1751
971k
    if (nShapeId != -1 &&
1752
971k
        psSHP->panRecOffset[nShapeId] + psSHP->panRecSize[nShapeId] + 8 ==
1753
0
            psSHP->nFileSize)
1754
0
    {
1755
0
        nRecordOffset = psSHP->panRecOffset[nShapeId];
1756
0
        bAppendToLastRecord = true;
1757
0
    }
1758
971k
    else if (nShapeId == -1 || psSHP->panRecSize[nShapeId] < nRecordSize - 8)
1759
971k
    {
1760
971k
        if (psSHP->nFileSize > UINT_MAX - nRecordSize)
1761
0
        {
1762
0
            char str[255];
1763
0
            snprintf(str, sizeof(str),
1764
0
                     "Failed to write shape object. "
1765
0
                     "The maximum file size of %u has been reached. "
1766
0
                     "The current record of size %u cannot be added.",
1767
0
                     psSHP->nFileSize, nRecordSize);
1768
0
            str[sizeof(str) - 1] = '\0';
1769
0
            psSHP->sHooks.Error(str);
1770
0
            free(pabyRec);
1771
0
            return -1;
1772
0
        }
1773
1774
971k
        bAppendToFile = true;
1775
971k
        nRecordOffset = psSHP->nFileSize;
1776
971k
    }
1777
0
    else
1778
0
    {
1779
0
        nRecordOffset = psSHP->panRecOffset[nShapeId];
1780
0
    }
1781
1782
    /* -------------------------------------------------------------------- */
1783
    /*      Set the shape type, record number, and record size.             */
1784
    /* -------------------------------------------------------------------- */
1785
971k
    uint32_t i32 =
1786
971k
        (nShapeId < 0) ? psSHP->nRecords + 1 : nShapeId + 1; /* record # */
1787
971k
#if !defined(SHP_BIG_ENDIAN)
1788
971k
    SHP_SWAP32(&i32);
1789
971k
#endif
1790
971k
    ByteCopy(&i32, pabyRec, 4);
1791
1792
971k
    i32 = (nRecordSize - 8) / 2; /* record size */
1793
971k
#if !defined(SHP_BIG_ENDIAN)
1794
971k
    SHP_SWAP32(&i32);
1795
971k
#endif
1796
971k
    ByteCopy(&i32, pabyRec + 4, 4);
1797
1798
971k
    i32 = psObject->nSHPType; /* shape type */
1799
#if defined(SHP_BIG_ENDIAN)
1800
    SHP_SWAP32(&i32);
1801
#endif
1802
971k
    ByteCopy(&i32, pabyRec + 8, 4);
1803
1804
    /* -------------------------------------------------------------------- */
1805
    /*      Write out record.                                               */
1806
    /* -------------------------------------------------------------------- */
1807
1808
    /* -------------------------------------------------------------------- */
1809
    /*      Guard FSeek with check for whether we're already at position;   */
1810
    /*      no-op FSeeks defeat network filesystems' write buffering.       */
1811
    /* -------------------------------------------------------------------- */
1812
971k
    if (psSHP->sHooks.FTell(psSHP->fpSHP) != nRecordOffset)
1813
0
    {
1814
0
        if (psSHP->sHooks.FSeek(psSHP->fpSHP, nRecordOffset, 0) != 0)
1815
0
        {
1816
0
            char szErrorMsg[200];
1817
1818
0
            snprintf(szErrorMsg, sizeof(szErrorMsg),
1819
0
                     "Error in psSHP->sHooks.FSeek() while writing object to "
1820
0
                     ".shp file: %s",
1821
0
                     strerror(errno));
1822
0
            szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
1823
0
            psSHP->sHooks.Error(szErrorMsg);
1824
1825
0
            free(pabyRec);
1826
0
            return -1;
1827
0
        }
1828
0
    }
1829
971k
    if (psSHP->sHooks.FWrite(pabyRec, nRecordSize, 1, psSHP->fpSHP) < 1)
1830
0
    {
1831
0
        char szErrorMsg[200];
1832
1833
0
        snprintf(szErrorMsg, sizeof(szErrorMsg),
1834
0
                 "Error in psSHP->sHooks.FWrite() while writing object of %u "
1835
0
                 "bytes to .shp file: %s",
1836
0
                 nRecordSize, strerror(errno));
1837
0
        szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
1838
0
        psSHP->sHooks.Error(szErrorMsg);
1839
1840
0
        free(pabyRec);
1841
0
        return -1;
1842
0
    }
1843
1844
971k
    free(pabyRec);
1845
1846
971k
    if (bAppendToLastRecord)
1847
0
    {
1848
0
        psSHP->nFileSize = psSHP->panRecOffset[nShapeId] + nRecordSize;
1849
0
    }
1850
971k
    else if (bAppendToFile)
1851
971k
    {
1852
971k
        if (nShapeId == -1)
1853
971k
            nShapeId = psSHP->nRecords++;
1854
1855
971k
        psSHP->panRecOffset[nShapeId] = psSHP->nFileSize;
1856
971k
        psSHP->nFileSize += nRecordSize;
1857
971k
    }
1858
971k
    psSHP->panRecSize[nShapeId] = nRecordSize - 8;
1859
1860
    /* -------------------------------------------------------------------- */
1861
    /*      Expand file wide bounds based on this shape.                    */
1862
    /* -------------------------------------------------------------------- */
1863
971k
    if (bFirstFeature)
1864
571
    {
1865
571
        if (psObject->nSHPType == SHPT_NULL || psObject->nVertices == 0)
1866
561
        {
1867
561
            psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = 0.0;
1868
561
            psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = 0.0;
1869
561
            psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = 0.0;
1870
561
            psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = 0.0;
1871
561
        }
1872
10
        else
1873
10
        {
1874
10
            psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = psObject->padfX[0];
1875
10
            psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = psObject->padfY[0];
1876
10
            psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] =
1877
10
                psObject->padfZ ? psObject->padfZ[0] : 0.0;
1878
10
            psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] =
1879
10
                psObject->padfM ? psObject->padfM[0] : 0.0;
1880
10
        }
1881
571
    }
1882
1883
972k
    for (int i = 0; i < psObject->nVertices; i++)
1884
135
    {
1885
135
        psSHP->adBoundsMin[0] = MIN(psSHP->adBoundsMin[0], psObject->padfX[i]);
1886
135
        psSHP->adBoundsMin[1] = MIN(psSHP->adBoundsMin[1], psObject->padfY[i]);
1887
135
        psSHP->adBoundsMax[0] = MAX(psSHP->adBoundsMax[0], psObject->padfX[i]);
1888
135
        psSHP->adBoundsMax[1] = MAX(psSHP->adBoundsMax[1], psObject->padfY[i]);
1889
135
        if (psObject->padfZ)
1890
135
        {
1891
135
            psSHP->adBoundsMin[2] =
1892
135
                MIN(psSHP->adBoundsMin[2], psObject->padfZ[i]);
1893
135
            psSHP->adBoundsMax[2] =
1894
135
                MAX(psSHP->adBoundsMax[2], psObject->padfZ[i]);
1895
135
        }
1896
135
        if (psObject->padfM)
1897
135
        {
1898
135
            psSHP->adBoundsMin[3] =
1899
135
                MIN(psSHP->adBoundsMin[3], psObject->padfM[i]);
1900
135
            psSHP->adBoundsMax[3] =
1901
135
                MAX(psSHP->adBoundsMax[3], psObject->padfM[i]);
1902
135
        }
1903
135
    }
1904
1905
971k
    return (nShapeId);
1906
971k
}
1907
1908
/************************************************************************/
1909
/*                         SHPAllocBuffer()                             */
1910
/************************************************************************/
1911
1912
static void *SHPAllocBuffer(unsigned char **pBuffer, int nSize)
1913
4.50k
{
1914
4.50k
    if (pBuffer == SHPLIB_NULLPTR)
1915
0
        return calloc(1, nSize);
1916
1917
4.50k
    unsigned char *pRet = *pBuffer;
1918
4.50k
    if (pRet == SHPLIB_NULLPTR)
1919
0
        return SHPLIB_NULLPTR;
1920
1921
4.50k
    (*pBuffer) += nSize;
1922
4.50k
    return pRet;
1923
4.50k
}
1924
1925
/************************************************************************/
1926
/*                    SHPReallocObjectBufIfNecessary()                  */
1927
/************************************************************************/
1928
1929
static unsigned char *SHPReallocObjectBufIfNecessary(SHPHandle psSHP,
1930
                                                     int nObjectBufSize)
1931
1.01k
{
1932
1.01k
    if (nObjectBufSize == 0)
1933
71
    {
1934
71
        nObjectBufSize = 4 * sizeof(double);
1935
71
    }
1936
1937
1.01k
    unsigned char *pBuffer;
1938
1.01k
    if (nObjectBufSize > psSHP->nObjectBufSize)
1939
240
    {
1940
240
        pBuffer = STATIC_CAST(unsigned char *,
1941
240
                              realloc(psSHP->pabyObjectBuf, nObjectBufSize));
1942
240
        if (pBuffer != SHPLIB_NULLPTR)
1943
240
        {
1944
240
            psSHP->pabyObjectBuf = pBuffer;
1945
240
            psSHP->nObjectBufSize = nObjectBufSize;
1946
240
        }
1947
240
    }
1948
770
    else
1949
770
    {
1950
770
        pBuffer = psSHP->pabyObjectBuf;
1951
770
    }
1952
1953
1.01k
    return pBuffer;
1954
1.01k
}
1955
1956
/************************************************************************/
1957
/*                          SHPReadObject()                             */
1958
/*                                                                      */
1959
/*      Read the vertices, parts, and other non-attribute information   */
1960
/*      for one shape.                                                  */
1961
/************************************************************************/
1962
1963
SHPObject SHPAPI_CALL1(*) SHPReadObject(const SHPHandle psSHP, int hEntity)
1964
37.8k
{
1965
    /* -------------------------------------------------------------------- */
1966
    /*      Validate the record/entity number.                              */
1967
    /* -------------------------------------------------------------------- */
1968
37.8k
    if (hEntity < 0 || hEntity >= psSHP->nRecords)
1969
0
        return SHPLIB_NULLPTR;
1970
1971
    /* -------------------------------------------------------------------- */
1972
    /*      Read offset/length from SHX loading if necessary.               */
1973
    /* -------------------------------------------------------------------- */
1974
37.8k
    if (psSHP->panRecOffset[hEntity] == 0 && psSHP->fpSHX != SHPLIB_NULLPTR)
1975
0
    {
1976
0
        unsigned int nOffset;
1977
0
        unsigned int nLength;
1978
1979
0
        if (psSHP->sHooks.FSeek(psSHP->fpSHX, 100 + 8 * hEntity, 0) != 0 ||
1980
0
            psSHP->sHooks.FRead(&nOffset, 1, 4, psSHP->fpSHX) != 4 ||
1981
0
            psSHP->sHooks.FRead(&nLength, 1, 4, psSHP->fpSHX) != 4)
1982
0
        {
1983
0
            char str[128];
1984
0
            snprintf(str, sizeof(str),
1985
0
                     "Error in fseek()/fread() reading object from .shx file "
1986
0
                     "at offset %d",
1987
0
                     100 + 8 * hEntity);
1988
0
            str[sizeof(str) - 1] = '\0';
1989
1990
0
            psSHP->sHooks.Error(str);
1991
0
            return SHPLIB_NULLPTR;
1992
0
        }
1993
0
#if !defined(SHP_BIG_ENDIAN)
1994
0
        SHP_SWAP32(&nOffset);
1995
0
        SHP_SWAP32(&nLength);
1996
0
#endif
1997
1998
0
        if (nOffset > STATIC_CAST(unsigned int, INT_MAX))
1999
0
        {
2000
0
            char str[128];
2001
0
            snprintf(str, sizeof(str), "Invalid offset for entity %d", hEntity);
2002
0
            str[sizeof(str) - 1] = '\0';
2003
2004
0
            psSHP->sHooks.Error(str);
2005
0
            return SHPLIB_NULLPTR;
2006
0
        }
2007
0
        if (nLength > STATIC_CAST(unsigned int, INT_MAX / 2 - 4))
2008
0
        {
2009
0
            char str[128];
2010
0
            snprintf(str, sizeof(str), "Invalid length for entity %d", hEntity);
2011
0
            str[sizeof(str) - 1] = '\0';
2012
2013
0
            psSHP->sHooks.Error(str);
2014
0
            return SHPLIB_NULLPTR;
2015
0
        }
2016
2017
0
        psSHP->panRecOffset[hEntity] = nOffset * 2;
2018
0
        psSHP->panRecSize[hEntity] = nLength * 2;
2019
0
    }
2020
2021
    /* -------------------------------------------------------------------- */
2022
    /*      Ensure our record buffer is large enough.                       */
2023
    /* -------------------------------------------------------------------- */
2024
37.8k
    const int nEntitySize = psSHP->panRecSize[hEntity] + 8;
2025
37.8k
    if (nEntitySize > psSHP->nBufSize)
2026
12.9k
    {
2027
12.9k
        int nNewBufSize = nEntitySize;
2028
12.9k
        if (nNewBufSize < INT_MAX - nNewBufSize / 3)
2029
8.59k
            nNewBufSize += nNewBufSize / 3;
2030
4.38k
        else
2031
4.38k
            nNewBufSize = INT_MAX;
2032
2033
        /* Before allocating too much memory, check that the file is big enough */
2034
        /* and do not trust the file size in the header the first time we */
2035
        /* need to allocate more than 10 MB */
2036
12.9k
        if (nNewBufSize >= 10 * 1024 * 1024)
2037
10.6k
        {
2038
10.6k
            if (psSHP->nBufSize < 10 * 1024 * 1024)
2039
10.6k
            {
2040
10.6k
                SAOffset nFileSize;
2041
10.6k
                psSHP->sHooks.FSeek(psSHP->fpSHP, 0, 2);
2042
10.6k
                nFileSize = psSHP->sHooks.FTell(psSHP->fpSHP);
2043
10.6k
                if (nFileSize >= UINT_MAX)
2044
0
                    psSHP->nFileSize = UINT_MAX;
2045
10.6k
                else
2046
10.6k
                    psSHP->nFileSize = STATIC_CAST(unsigned int, nFileSize);
2047
10.6k
            }
2048
2049
10.6k
            if (psSHP->panRecOffset[hEntity] >= psSHP->nFileSize ||
2050
                /* We should normally use nEntitySize instead of*/
2051
                /* psSHP->panRecSize[hEntity] in the below test, but because of */
2052
                /* the case of non conformant .shx files detailed a bit below, */
2053
                /* let be more tolerant */
2054
10.6k
                psSHP->panRecSize[hEntity] >
2055
2.00k
                    psSHP->nFileSize - psSHP->panRecOffset[hEntity])
2056
10.6k
            {
2057
10.6k
                char str[128];
2058
10.6k
                snprintf(str, sizeof(str),
2059
10.6k
                         "Error in fread() reading object of size %d at offset "
2060
10.6k
                         "%u from .shp file",
2061
10.6k
                         nEntitySize, psSHP->panRecOffset[hEntity]);
2062
10.6k
                str[sizeof(str) - 1] = '\0';
2063
2064
10.6k
                psSHP->sHooks.Error(str);
2065
10.6k
                return SHPLIB_NULLPTR;
2066
10.6k
            }
2067
10.6k
        }
2068
2069
2.32k
        unsigned char *pabyRecNew =
2070
2.32k
            STATIC_CAST(unsigned char *, realloc(psSHP->pabyRec, nNewBufSize));
2071
2.32k
        if (pabyRecNew == SHPLIB_NULLPTR)
2072
0
        {
2073
0
            char szErrorMsg[160];
2074
0
            snprintf(szErrorMsg, sizeof(szErrorMsg),
2075
0
                     "Not enough memory to allocate requested memory "
2076
0
                     "(nNewBufSize=%d). "
2077
0
                     "Probably broken SHP file",
2078
0
                     nNewBufSize);
2079
0
            szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
2080
0
            psSHP->sHooks.Error(szErrorMsg);
2081
0
            return SHPLIB_NULLPTR;
2082
0
        }
2083
2084
        /* Only set new buffer size after successful alloc */
2085
2.32k
        psSHP->pabyRec = pabyRecNew;
2086
2.32k
        psSHP->nBufSize = nNewBufSize;
2087
2.32k
    }
2088
2089
    /* In case we were not able to reallocate the buffer on a previous step */
2090
27.1k
    if (psSHP->pabyRec == SHPLIB_NULLPTR)
2091
0
    {
2092
0
        return SHPLIB_NULLPTR;
2093
0
    }
2094
2095
    /* -------------------------------------------------------------------- */
2096
    /*      Read the record.                                                */
2097
    /* -------------------------------------------------------------------- */
2098
27.1k
    if (psSHP->sHooks.FSeek(psSHP->fpSHP, psSHP->panRecOffset[hEntity], 0) != 0)
2099
0
    {
2100
        /*
2101
         * TODO - mloskot: Consider detailed diagnostics of shape file,
2102
         * for example to detect if file is truncated.
2103
         */
2104
0
        char str[128];
2105
0
        snprintf(str, sizeof(str),
2106
0
                 "Error in fseek() reading object from .shp file at offset %u",
2107
0
                 psSHP->panRecOffset[hEntity]);
2108
0
        str[sizeof(str) - 1] = '\0';
2109
2110
0
        psSHP->sHooks.Error(str);
2111
0
        return SHPLIB_NULLPTR;
2112
0
    }
2113
2114
27.1k
    const int nBytesRead = STATIC_CAST(
2115
27.1k
        int, psSHP->sHooks.FRead(psSHP->pabyRec, 1, nEntitySize, psSHP->fpSHP));
2116
2117
    /* Special case for a shapefile whose .shx content length field is not equal */
2118
    /* to the content length field of the .shp, which is a violation of "The */
2119
    /* content length stored in the index record is the same as the value stored in the main */
2120
    /* file record header." (http://www.esri.com/library/whitepapers/pdfs/shapefile.pdf, page 24) */
2121
    /* Actually in that case the .shx content length is equal to the .shp content length + */
2122
    /* 4 (16 bit words), representing the 8 bytes of the record header... */
2123
27.1k
    if (nBytesRead >= 8 && nBytesRead == nEntitySize - 8)
2124
9
    {
2125
        /* Do a sanity check */
2126
9
        int nSHPContentLength;
2127
9
        memcpy(&nSHPContentLength, psSHP->pabyRec + 4, 4);
2128
9
#if !defined(SHP_BIG_ENDIAN)
2129
9
        SHP_SWAP32(&(nSHPContentLength));
2130
9
#endif
2131
9
        if (nSHPContentLength < 0 || nSHPContentLength > INT_MAX / 2 - 4 ||
2132
9
            2 * nSHPContentLength + 8 != nBytesRead)
2133
9
        {
2134
9
            char str[128];
2135
9
            snprintf(str, sizeof(str),
2136
9
                     "Sanity check failed when trying to recover from "
2137
9
                     "inconsistent .shx/.shp with shape %d",
2138
9
                     hEntity);
2139
9
            str[sizeof(str) - 1] = '\0';
2140
2141
9
            psSHP->sHooks.Error(str);
2142
9
            return SHPLIB_NULLPTR;
2143
9
        }
2144
9
    }
2145
27.1k
    else if (nBytesRead != nEntitySize)
2146
10.1k
    {
2147
        /*
2148
         * TODO - mloskot: Consider detailed diagnostics of shape file,
2149
         * for example to detect if file is truncated.
2150
         */
2151
10.1k
        char str[128];
2152
10.1k
        snprintf(str, sizeof(str),
2153
10.1k
                 "Error in fread() reading object of size %d at offset %u from "
2154
10.1k
                 ".shp file",
2155
10.1k
                 nEntitySize, psSHP->panRecOffset[hEntity]);
2156
10.1k
        str[sizeof(str) - 1] = '\0';
2157
2158
10.1k
        psSHP->sHooks.Error(str);
2159
10.1k
        return SHPLIB_NULLPTR;
2160
10.1k
    }
2161
2162
16.9k
    if (8 + 4 > nEntitySize)
2163
12.8k
    {
2164
12.8k
        char szErrorMsg[160];
2165
12.8k
        snprintf(szErrorMsg, sizeof(szErrorMsg),
2166
12.8k
                 "Corrupted .shp file : shape %d : nEntitySize = %d", hEntity,
2167
12.8k
                 nEntitySize);
2168
12.8k
        szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
2169
12.8k
        psSHP->sHooks.Error(szErrorMsg);
2170
12.8k
        return SHPLIB_NULLPTR;
2171
12.8k
    }
2172
4.13k
    int nSHPType;
2173
4.13k
    memcpy(&nSHPType, psSHP->pabyRec + 8, 4);
2174
2175
#if defined(SHP_BIG_ENDIAN)
2176
    SHP_SWAP32(&(nSHPType));
2177
#endif
2178
2179
    /* -------------------------------------------------------------------- */
2180
    /*      Allocate and minimally initialize the object.                   */
2181
    /* -------------------------------------------------------------------- */
2182
4.13k
    SHPObject *psShape;
2183
4.13k
    if (psSHP->bFastModeReadObject)
2184
4.13k
    {
2185
4.13k
        if (psSHP->psCachedObject->bFastModeReadObject)
2186
0
        {
2187
0
            psSHP->sHooks.Error("Invalid read pattern in fast read mode. "
2188
0
                                "SHPDestroyObject() should be called.");
2189
0
            return SHPLIB_NULLPTR;
2190
0
        }
2191
2192
4.13k
        psShape = psSHP->psCachedObject;
2193
4.13k
        memset(psShape, 0, sizeof(SHPObject));
2194
4.13k
    }
2195
0
    else
2196
0
    {
2197
0
        psShape = STATIC_CAST(SHPObject *, calloc(1, sizeof(SHPObject)));
2198
0
        if (!psShape)
2199
0
        {
2200
0
            psSHP->sHooks.Error("Out of memory.");
2201
0
            return SHPLIB_NULLPTR;
2202
0
        }
2203
0
    }
2204
4.13k
    psShape->nShapeId = hEntity;
2205
4.13k
    psShape->nSHPType = nSHPType;
2206
4.13k
    psShape->bMeasureIsUsed = FALSE;
2207
4.13k
    psShape->bFastModeReadObject = psSHP->bFastModeReadObject;
2208
2209
    /* ==================================================================== */
2210
    /*  Extract vertices for a Polygon or Arc.                              */
2211
    /* ==================================================================== */
2212
4.13k
    if (psShape->nSHPType == SHPT_POLYGON || psShape->nSHPType == SHPT_ARC ||
2213
4.13k
        psShape->nSHPType == SHPT_POLYGONZ ||
2214
4.13k
        psShape->nSHPType == SHPT_POLYGONM || psShape->nSHPType == SHPT_ARCZ ||
2215
4.13k
        psShape->nSHPType == SHPT_ARCM || psShape->nSHPType == SHPT_MULTIPATCH)
2216
283
    {
2217
283
        if (40 + 8 + 4 > nEntitySize)
2218
9
        {
2219
9
            char szErrorMsg[160];
2220
9
            snprintf(szErrorMsg, sizeof(szErrorMsg),
2221
9
                     "Corrupted .shp file : shape %d : nEntitySize = %d",
2222
9
                     hEntity, nEntitySize);
2223
9
            szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
2224
9
            psSHP->sHooks.Error(szErrorMsg);
2225
9
            SHPDestroyObject(psShape);
2226
9
            return SHPLIB_NULLPTR;
2227
9
        }
2228
        /* -------------------------------------------------------------------- */
2229
        /*      Get the X/Y bounds.                                             */
2230
        /* -------------------------------------------------------------------- */
2231
#if defined(SHP_BIG_ENDIAN)
2232
        SHP_SWAPDOUBLE_CPY(&psShape->dfXMin, psSHP->pabyRec + 8 + 4);
2233
        SHP_SWAPDOUBLE_CPY(&psShape->dfYMin, psSHP->pabyRec + 8 + 12);
2234
        SHP_SWAPDOUBLE_CPY(&psShape->dfXMax, psSHP->pabyRec + 8 + 20);
2235
        SHP_SWAPDOUBLE_CPY(&psShape->dfYMax, psSHP->pabyRec + 8 + 28);
2236
#else
2237
274
        memcpy(&psShape->dfXMin, psSHP->pabyRec + 8 + 4, 8);
2238
274
        memcpy(&psShape->dfYMin, psSHP->pabyRec + 8 + 12, 8);
2239
274
        memcpy(&psShape->dfXMax, psSHP->pabyRec + 8 + 20, 8);
2240
274
        memcpy(&psShape->dfYMax, psSHP->pabyRec + 8 + 28, 8);
2241
274
#endif
2242
2243
        /* -------------------------------------------------------------------- */
2244
        /*      Extract part/point count, and build vertex and part arrays      */
2245
        /*      to proper size.                                                 */
2246
        /* -------------------------------------------------------------------- */
2247
274
        uint32_t nPoints;
2248
274
        memcpy(&nPoints, psSHP->pabyRec + 40 + 8, 4);
2249
274
        uint32_t nParts;
2250
274
        memcpy(&nParts, psSHP->pabyRec + 36 + 8, 4);
2251
2252
#if defined(SHP_BIG_ENDIAN)
2253
        SHP_SWAP32(&nPoints);
2254
        SHP_SWAP32(&nParts);
2255
#endif
2256
2257
        /* nPoints and nParts are unsigned */
2258
274
        if (/* nPoints < 0 || nParts < 0 || */
2259
274
            nPoints > 50 * 1000 * 1000 || nParts > 10 * 1000 * 1000)
2260
11
        {
2261
11
            char szErrorMsg[160];
2262
11
            snprintf(szErrorMsg, sizeof(szErrorMsg),
2263
11
                     "Corrupted .shp file : shape %d, nPoints=%u, nParts=%u.",
2264
11
                     hEntity, nPoints, nParts);
2265
11
            szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
2266
11
            psSHP->sHooks.Error(szErrorMsg);
2267
11
            SHPDestroyObject(psShape);
2268
11
            return SHPLIB_NULLPTR;
2269
11
        }
2270
2271
        /* With the previous checks on nPoints and nParts, */
2272
        /* we should not overflow here and after */
2273
        /* since 50 M * (16 + 8 + 8) = 1 600 MB */
2274
263
        int nRequiredSize = 44 + 8 + 4 * nParts + 16 * nPoints;
2275
263
        if (psShape->nSHPType == SHPT_POLYGONZ ||
2276
263
            psShape->nSHPType == SHPT_ARCZ ||
2277
263
            psShape->nSHPType == SHPT_MULTIPATCH)
2278
108
        {
2279
108
            nRequiredSize += 16 + 8 * nPoints;
2280
108
        }
2281
263
        if (psShape->nSHPType == SHPT_MULTIPATCH)
2282
59
        {
2283
59
            nRequiredSize += 4 * nParts;
2284
59
        }
2285
263
        if (nRequiredSize > nEntitySize)
2286
29
        {
2287
29
            char szErrorMsg[160];
2288
29
            snprintf(szErrorMsg, sizeof(szErrorMsg),
2289
29
                     "Corrupted .shp file : shape %d, nPoints=%u, nParts=%u, "
2290
29
                     "nEntitySize=%d.",
2291
29
                     hEntity, nPoints, nParts, nEntitySize);
2292
29
            szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
2293
29
            psSHP->sHooks.Error(szErrorMsg);
2294
29
            SHPDestroyObject(psShape);
2295
29
            return SHPLIB_NULLPTR;
2296
29
        }
2297
2298
234
        unsigned char *pBuffer = SHPLIB_NULLPTR;
2299
234
        unsigned char **ppBuffer = SHPLIB_NULLPTR;
2300
2301
234
        if (psShape->bFastModeReadObject)
2302
234
        {
2303
234
            const int nObjectBufSize =
2304
234
                4 * sizeof(double) * nPoints + 2 * sizeof(int) * nParts;
2305
234
            pBuffer = SHPReallocObjectBufIfNecessary(psSHP, nObjectBufSize);
2306
234
            ppBuffer = &pBuffer;
2307
234
        }
2308
2309
234
        psShape->nVertices = nPoints;
2310
234
        psShape->padfX = STATIC_CAST(
2311
234
            double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints));
2312
234
        psShape->padfY = STATIC_CAST(
2313
234
            double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints));
2314
234
        psShape->padfZ = STATIC_CAST(
2315
234
            double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints));
2316
234
        psShape->padfM = STATIC_CAST(
2317
234
            double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints));
2318
2319
234
        psShape->nParts = nParts;
2320
234
        psShape->panPartStart =
2321
234
            STATIC_CAST(int *, SHPAllocBuffer(ppBuffer, nParts * sizeof(int)));
2322
234
        psShape->panPartType =
2323
234
            STATIC_CAST(int *, SHPAllocBuffer(ppBuffer, nParts * sizeof(int)));
2324
2325
234
        if (psShape->padfX == SHPLIB_NULLPTR ||
2326
234
            psShape->padfY == SHPLIB_NULLPTR ||
2327
234
            psShape->padfZ == SHPLIB_NULLPTR ||
2328
234
            psShape->padfM == SHPLIB_NULLPTR ||
2329
234
            psShape->panPartStart == SHPLIB_NULLPTR ||
2330
234
            psShape->panPartType == SHPLIB_NULLPTR)
2331
0
        {
2332
0
            char szErrorMsg[160];
2333
0
            snprintf(szErrorMsg, sizeof(szErrorMsg),
2334
0
                     "Not enough memory to allocate requested memory "
2335
0
                     "(nPoints=%u, nParts=%u) for shape %d. "
2336
0
                     "Probably broken SHP file",
2337
0
                     nPoints, nParts, hEntity);
2338
0
            szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
2339
0
            psSHP->sHooks.Error(szErrorMsg);
2340
0
            SHPDestroyObject(psShape);
2341
0
            return SHPLIB_NULLPTR;
2342
0
        }
2343
2344
792
        for (int i = 0; STATIC_CAST(uint32_t, i) < nParts; i++)
2345
558
            psShape->panPartType[i] = SHPP_RING;
2346
2347
        /* -------------------------------------------------------------------- */
2348
        /*      Copy out the part array from the record.                        */
2349
        /* -------------------------------------------------------------------- */
2350
234
        memcpy(psShape->panPartStart, psSHP->pabyRec + 44 + 8, 4 * nParts);
2351
494
        for (int i = 0; STATIC_CAST(uint32_t, i) < nParts; i++)
2352
288
        {
2353
#if defined(SHP_BIG_ENDIAN)
2354
            SHP_SWAP32(psShape->panPartStart + i);
2355
#endif
2356
2357
            /* We check that the offset is inside the vertex array */
2358
288
            if (psShape->panPartStart[i] < 0 ||
2359
288
                (psShape->panPartStart[i] >= psShape->nVertices &&
2360
277
                 psShape->nVertices > 0) ||
2361
288
                (psShape->panPartStart[i] > 0 && psShape->nVertices == 0))
2362
22
            {
2363
22
                char szErrorMsg[160];
2364
22
                snprintf(szErrorMsg, sizeof(szErrorMsg),
2365
22
                         "Corrupted .shp file : shape %d : panPartStart[%d] = "
2366
22
                         "%d, nVertices = %d",
2367
22
                         hEntity, i, psShape->panPartStart[i],
2368
22
                         psShape->nVertices);
2369
22
                szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
2370
22
                psSHP->sHooks.Error(szErrorMsg);
2371
22
                SHPDestroyObject(psShape);
2372
22
                return SHPLIB_NULLPTR;
2373
22
            }
2374
266
            if (i > 0 &&
2375
266
                psShape->panPartStart[i] <= psShape->panPartStart[i - 1])
2376
6
            {
2377
6
                char szErrorMsg[160];
2378
6
                snprintf(szErrorMsg, sizeof(szErrorMsg),
2379
6
                         "Corrupted .shp file : shape %d : panPartStart[%d] = "
2380
6
                         "%d, panPartStart[%d] = %d",
2381
6
                         hEntity, i, psShape->panPartStart[i], i - 1,
2382
6
                         psShape->panPartStart[i - 1]);
2383
6
                szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
2384
6
                psSHP->sHooks.Error(szErrorMsg);
2385
6
                SHPDestroyObject(psShape);
2386
6
                return SHPLIB_NULLPTR;
2387
6
            }
2388
266
        }
2389
2390
206
        int nOffset = 44 + 8 + 4 * nParts;
2391
2392
        /* -------------------------------------------------------------------- */
2393
        /*      If this is a multipatch, we will also have parts types.         */
2394
        /* -------------------------------------------------------------------- */
2395
206
        if (psShape->nSHPType == SHPT_MULTIPATCH)
2396
54
        {
2397
54
            memcpy(psShape->panPartType, psSHP->pabyRec + nOffset, 4 * nParts);
2398
118
            for (int i = 0; STATIC_CAST(uint32_t, i) < nParts; i++)
2399
64
            {
2400
#if defined(SHP_BIG_ENDIAN)
2401
                SHP_SWAP32(psShape->panPartType + i);
2402
#endif
2403
64
            }
2404
2405
54
            nOffset += 4 * nParts;
2406
54
        }
2407
2408
        /* -------------------------------------------------------------------- */
2409
        /*      Copy out the vertices from the record.                          */
2410
        /* -------------------------------------------------------------------- */
2411
1.93k
        for (int i = 0; STATIC_CAST(uint32_t, i) < nPoints; i++)
2412
1.72k
        {
2413
#if defined(SHP_BIG_ENDIAN)
2414
            SHP_SWAPDOUBLE_CPY(psShape->padfX + i,
2415
                               psSHP->pabyRec + nOffset + i * 16);
2416
            SHP_SWAPDOUBLE_CPY(psShape->padfY + i,
2417
                               psSHP->pabyRec + nOffset + i * 16 + 8);
2418
#else
2419
1.72k
            memcpy(psShape->padfX + i, psSHP->pabyRec + nOffset + i * 16, 8);
2420
1.72k
            memcpy(psShape->padfY + i, psSHP->pabyRec + nOffset + i * 16 + 8,
2421
1.72k
                   8);
2422
1.72k
#endif
2423
1.72k
        }
2424
2425
206
        nOffset += 16 * nPoints;
2426
2427
        /* -------------------------------------------------------------------- */
2428
        /*      If we have a Z coordinate, collect that now.                    */
2429
        /* -------------------------------------------------------------------- */
2430
206
        if (psShape->nSHPType == SHPT_POLYGONZ ||
2431
206
            psShape->nSHPType == SHPT_ARCZ ||
2432
206
            psShape->nSHPType == SHPT_MULTIPATCH)
2433
81
        {
2434
#if defined(SHP_BIG_ENDIAN)
2435
            SHP_SWAPDOUBLE_CPY(&psShape->dfZMin, psSHP->pabyRec + nOffset);
2436
            SHP_SWAPDOUBLE_CPY(&psShape->dfZMax, psSHP->pabyRec + nOffset + 8);
2437
#else
2438
81
            memcpy(&psShape->dfZMin, psSHP->pabyRec + nOffset, 8);
2439
81
            memcpy(&psShape->dfZMax, psSHP->pabyRec + nOffset + 8, 8);
2440
2441
81
#endif
2442
2443
452
            for (int i = 0; STATIC_CAST(uint32_t, i) < nPoints; i++)
2444
371
            {
2445
#if defined(SHP_BIG_ENDIAN)
2446
                SHP_SWAPDOUBLE_CPY(psShape->padfZ + i,
2447
                                   psSHP->pabyRec + nOffset + 16 + i * 8);
2448
#else
2449
371
                memcpy(psShape->padfZ + i,
2450
371
                       psSHP->pabyRec + nOffset + 16 + i * 8, 8);
2451
371
#endif
2452
371
            }
2453
2454
81
            nOffset += 16 + 8 * nPoints;
2455
81
        }
2456
125
        else if (psShape->bFastModeReadObject)
2457
125
        {
2458
125
            psShape->padfZ = SHPLIB_NULLPTR;
2459
125
        }
2460
2461
        /* -------------------------------------------------------------------- */
2462
        /*      If we have a M measure value, then read it now.  We assume      */
2463
        /*      that the measure can be present for any shape if the size is    */
2464
        /*      big enough, but really it will only occur for the Z shapes      */
2465
        /*      (options), and the M shapes.                                    */
2466
        /* -------------------------------------------------------------------- */
2467
206
        if (nEntitySize >= STATIC_CAST(int, nOffset + 16 + 8 * nPoints))
2468
144
        {
2469
#if defined(SHP_BIG_ENDIAN)
2470
            SHP_SWAPDOUBLE_CPY(&psShape->dfMMin, psSHP->pabyRec + nOffset);
2471
            SHP_SWAPDOUBLE_CPY(&psShape->dfMMax, psSHP->pabyRec + nOffset + 8);
2472
#else
2473
144
            memcpy(&psShape->dfMMin, psSHP->pabyRec + nOffset, 8);
2474
144
            memcpy(&psShape->dfMMax, psSHP->pabyRec + nOffset + 8, 8);
2475
144
#endif
2476
2477
690
            for (int i = 0; STATIC_CAST(uint32_t, i) < nPoints; i++)
2478
546
            {
2479
#if defined(SHP_BIG_ENDIAN)
2480
                SHP_SWAPDOUBLE_CPY(psShape->padfM + i,
2481
                                   psSHP->pabyRec + nOffset + 16 + i * 8);
2482
#else
2483
546
                memcpy(psShape->padfM + i,
2484
546
                       psSHP->pabyRec + nOffset + 16 + i * 8, 8);
2485
546
#endif
2486
546
            }
2487
144
            psShape->bMeasureIsUsed = TRUE;
2488
144
        }
2489
62
        else if (psShape->bFastModeReadObject)
2490
62
        {
2491
62
            psShape->padfM = SHPLIB_NULLPTR;
2492
62
        }
2493
206
    }
2494
2495
    /* ==================================================================== */
2496
    /*  Extract vertices for a MultiPoint.                                  */
2497
    /* ==================================================================== */
2498
3.84k
    else if (psShape->nSHPType == SHPT_MULTIPOINT ||
2499
3.84k
             psShape->nSHPType == SHPT_MULTIPOINTM ||
2500
3.84k
             psShape->nSHPType == SHPT_MULTIPOINTZ)
2501
1.97k
    {
2502
1.97k
        if (44 + 4 > nEntitySize)
2503
676
        {
2504
676
            char szErrorMsg[160];
2505
676
            snprintf(szErrorMsg, sizeof(szErrorMsg),
2506
676
                     "Corrupted .shp file : shape %d : nEntitySize = %d",
2507
676
                     hEntity, nEntitySize);
2508
676
            szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
2509
676
            psSHP->sHooks.Error(szErrorMsg);
2510
676
            SHPDestroyObject(psShape);
2511
676
            return SHPLIB_NULLPTR;
2512
676
        }
2513
1.30k
        uint32_t nPoints;
2514
1.30k
        memcpy(&nPoints, psSHP->pabyRec + 44, 4);
2515
2516
#if defined(SHP_BIG_ENDIAN)
2517
        SHP_SWAP32(&nPoints);
2518
#endif
2519
2520
        /* nPoints is unsigned */
2521
1.30k
        if (/* nPoints < 0 || */ nPoints > 50 * 1000 * 1000)
2522
119
        {
2523
119
            char szErrorMsg[160];
2524
119
            snprintf(szErrorMsg, sizeof(szErrorMsg),
2525
119
                     "Corrupted .shp file : shape %d : nPoints = %u", hEntity,
2526
119
                     nPoints);
2527
119
            szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
2528
119
            psSHP->sHooks.Error(szErrorMsg);
2529
119
            SHPDestroyObject(psShape);
2530
119
            return SHPLIB_NULLPTR;
2531
119
        }
2532
2533
1.18k
        int nRequiredSize = 48 + nPoints * 16;
2534
1.18k
        if (psShape->nSHPType == SHPT_MULTIPOINTZ)
2535
1.03k
        {
2536
1.03k
            nRequiredSize += 16 + nPoints * 8;
2537
1.03k
        }
2538
1.18k
        if (nRequiredSize > nEntitySize)
2539
406
        {
2540
406
            char szErrorMsg[160];
2541
406
            snprintf(szErrorMsg, sizeof(szErrorMsg),
2542
406
                     "Corrupted .shp file : shape %d : nPoints = %u, "
2543
406
                     "nEntitySize = %d",
2544
406
                     hEntity, nPoints, nEntitySize);
2545
406
            szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
2546
406
            psSHP->sHooks.Error(szErrorMsg);
2547
406
            SHPDestroyObject(psShape);
2548
406
            return SHPLIB_NULLPTR;
2549
406
        }
2550
2551
776
        unsigned char *pBuffer = SHPLIB_NULLPTR;
2552
776
        unsigned char **ppBuffer = SHPLIB_NULLPTR;
2553
2554
776
        if (psShape->bFastModeReadObject)
2555
776
        {
2556
776
            const int nObjectBufSize = 4 * sizeof(double) * nPoints;
2557
776
            pBuffer = SHPReallocObjectBufIfNecessary(psSHP, nObjectBufSize);
2558
776
            ppBuffer = &pBuffer;
2559
776
        }
2560
2561
776
        psShape->nVertices = nPoints;
2562
2563
776
        psShape->padfX = STATIC_CAST(
2564
776
            double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints));
2565
776
        psShape->padfY = STATIC_CAST(
2566
776
            double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints));
2567
776
        psShape->padfZ = STATIC_CAST(
2568
776
            double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints));
2569
776
        psShape->padfM = STATIC_CAST(
2570
776
            double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints));
2571
2572
776
        if (psShape->padfX == SHPLIB_NULLPTR ||
2573
776
            psShape->padfY == SHPLIB_NULLPTR ||
2574
776
            psShape->padfZ == SHPLIB_NULLPTR ||
2575
776
            psShape->padfM == SHPLIB_NULLPTR)
2576
0
        {
2577
0
            char szErrorMsg[160];
2578
0
            snprintf(szErrorMsg, sizeof(szErrorMsg),
2579
0
                     "Not enough memory to allocate requested memory "
2580
0
                     "(nPoints=%u) for shape %d. "
2581
0
                     "Probably broken SHP file",
2582
0
                     nPoints, hEntity);
2583
0
            szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
2584
0
            psSHP->sHooks.Error(szErrorMsg);
2585
0
            SHPDestroyObject(psShape);
2586
0
            return SHPLIB_NULLPTR;
2587
0
        }
2588
2589
106k
        for (int i = 0; STATIC_CAST(uint32_t, i) < nPoints; i++)
2590
105k
        {
2591
#if defined(SHP_BIG_ENDIAN)
2592
            SHP_SWAPDOUBLE_CPY(psShape->padfX + i,
2593
                               psSHP->pabyRec + 48 + 16 * i);
2594
            SHP_SWAPDOUBLE_CPY(psShape->padfY + i,
2595
                               psSHP->pabyRec + 48 + 16 * i + 8);
2596
#else
2597
105k
            memcpy(psShape->padfX + i, psSHP->pabyRec + 48 + 16 * i, 8);
2598
105k
            memcpy(psShape->padfY + i, psSHP->pabyRec + 48 + 16 * i + 8, 8);
2599
105k
#endif
2600
105k
        }
2601
2602
776
        int nOffset = 48 + 16 * nPoints;
2603
2604
        /* -------------------------------------------------------------------- */
2605
        /*      Get the X/Y bounds.                                             */
2606
        /* -------------------------------------------------------------------- */
2607
#if defined(SHP_BIG_ENDIAN)
2608
        SHP_SWAPDOUBLE_CPY(&psShape->dfXMin, psSHP->pabyRec + 8 + 4);
2609
        SHP_SWAPDOUBLE_CPY(&psShape->dfYMin, psSHP->pabyRec + 8 + 12);
2610
        SHP_SWAPDOUBLE_CPY(&psShape->dfXMax, psSHP->pabyRec + 8 + 20);
2611
        SHP_SWAPDOUBLE_CPY(&psShape->dfYMax, psSHP->pabyRec + 8 + 28);
2612
#else
2613
776
        memcpy(&psShape->dfXMin, psSHP->pabyRec + 8 + 4, 8);
2614
776
        memcpy(&psShape->dfYMin, psSHP->pabyRec + 8 + 12, 8);
2615
776
        memcpy(&psShape->dfXMax, psSHP->pabyRec + 8 + 20, 8);
2616
776
        memcpy(&psShape->dfYMax, psSHP->pabyRec + 8 + 28, 8);
2617
776
#endif
2618
2619
        /* -------------------------------------------------------------------- */
2620
        /*      If we have a Z coordinate, collect that now.                    */
2621
        /* -------------------------------------------------------------------- */
2622
776
        if (psShape->nSHPType == SHPT_MULTIPOINTZ)
2623
669
        {
2624
#if defined(SHP_BIG_ENDIAN)
2625
            SHP_SWAPDOUBLE_CPY(&psShape->dfZMin, psSHP->pabyRec + nOffset);
2626
            SHP_SWAPDOUBLE_CPY(&psShape->dfZMax, psSHP->pabyRec + nOffset + 8);
2627
#else
2628
669
            memcpy(&psShape->dfZMin, psSHP->pabyRec + nOffset, 8);
2629
669
            memcpy(&psShape->dfZMax, psSHP->pabyRec + nOffset + 8, 8);
2630
669
#endif
2631
2632
100k
            for (int i = 0; STATIC_CAST(uint32_t, i) < nPoints; i++)
2633
99.5k
            {
2634
#if defined(SHP_BIG_ENDIAN)
2635
                SHP_SWAPDOUBLE_CPY(psShape->padfZ + i,
2636
                                   psSHP->pabyRec + nOffset + 16 + i * 8);
2637
#else
2638
99.5k
                memcpy(psShape->padfZ + i,
2639
99.5k
                       psSHP->pabyRec + nOffset + 16 + i * 8, 8);
2640
99.5k
#endif
2641
99.5k
            }
2642
2643
669
            nOffset += 16 + 8 * nPoints;
2644
669
        }
2645
107
        else if (psShape->bFastModeReadObject)
2646
107
            psShape->padfZ = SHPLIB_NULLPTR;
2647
2648
        /* -------------------------------------------------------------------- */
2649
        /*      If we have a M measure value, then read it now.  We assume      */
2650
        /*      that the measure can be present for any shape if the size is    */
2651
        /*      big enough, but really it will only occur for the Z shapes      */
2652
        /*      (options), and the M shapes.                                    */
2653
        /* -------------------------------------------------------------------- */
2654
776
        if (nEntitySize >= STATIC_CAST(int, nOffset + 16 + 8 * nPoints))
2655
646
        {
2656
#if defined(SHP_BIG_ENDIAN)
2657
            SHP_SWAPDOUBLE_CPY(&psShape->dfMMin, psSHP->pabyRec + nOffset);
2658
            SHP_SWAPDOUBLE_CPY(&psShape->dfMMax, psSHP->pabyRec + nOffset + 8);
2659
#else
2660
646
            memcpy(&psShape->dfMMin, psSHP->pabyRec + nOffset, 8);
2661
646
            memcpy(&psShape->dfMMax, psSHP->pabyRec + nOffset + 8, 8);
2662
646
#endif
2663
2664
91.2k
            for (int i = 0; STATIC_CAST(uint32_t, i) < nPoints; i++)
2665
90.6k
            {
2666
#if defined(SHP_BIG_ENDIAN)
2667
                SHP_SWAPDOUBLE_CPY(psShape->padfM + i,
2668
                                   psSHP->pabyRec + nOffset + 16 + i * 8);
2669
#else
2670
90.6k
                memcpy(psShape->padfM + i,
2671
90.6k
                       psSHP->pabyRec + nOffset + 16 + i * 8, 8);
2672
90.6k
#endif
2673
90.6k
            }
2674
646
            psShape->bMeasureIsUsed = TRUE;
2675
646
        }
2676
130
        else if (psShape->bFastModeReadObject)
2677
130
            psShape->padfM = SHPLIB_NULLPTR;
2678
776
    }
2679
2680
    /* ==================================================================== */
2681
    /*      Extract vertices for a point.                                   */
2682
    /* ==================================================================== */
2683
1.87k
    else if (psShape->nSHPType == SHPT_POINT ||
2684
1.87k
             psShape->nSHPType == SHPT_POINTM ||
2685
1.87k
             psShape->nSHPType == SHPT_POINTZ)
2686
120
    {
2687
120
        psShape->nVertices = 1;
2688
120
        if (psShape->bFastModeReadObject)
2689
120
        {
2690
120
            psShape->padfX = &(psShape->dfXMin);
2691
120
            psShape->padfY = &(psShape->dfYMin);
2692
120
            psShape->padfZ = &(psShape->dfZMin);
2693
120
            psShape->padfM = &(psShape->dfMMin);
2694
120
            psShape->padfZ[0] = 0.0;
2695
120
            psShape->padfM[0] = 0.0;
2696
120
        }
2697
0
        else
2698
0
        {
2699
0
            psShape->padfX = STATIC_CAST(double *, calloc(1, sizeof(double)));
2700
0
            psShape->padfY = STATIC_CAST(double *, calloc(1, sizeof(double)));
2701
0
            psShape->padfZ = STATIC_CAST(double *, calloc(1, sizeof(double)));
2702
0
            psShape->padfM = STATIC_CAST(double *, calloc(1, sizeof(double)));
2703
0
        }
2704
2705
120
        if (20 + 8 + ((psShape->nSHPType == SHPT_POINTZ) ? 8 : 0) > nEntitySize)
2706
3
        {
2707
3
            char szErrorMsg[160];
2708
3
            snprintf(szErrorMsg, sizeof(szErrorMsg),
2709
3
                     "Corrupted .shp file : shape %d : nEntitySize = %d",
2710
3
                     hEntity, nEntitySize);
2711
3
            szErrorMsg[sizeof(szErrorMsg) - 1] = '\0';
2712
3
            psSHP->sHooks.Error(szErrorMsg);
2713
3
            SHPDestroyObject(psShape);
2714
3
            return SHPLIB_NULLPTR;
2715
3
        }
2716
#if defined(SHP_BIG_ENDIAN)
2717
        SHP_SWAPDOUBLE_CPY(psShape->padfX, psSHP->pabyRec + 12);
2718
        SHP_SWAPDOUBLE_CPY(psShape->padfY, psSHP->pabyRec + 20);
2719
#else
2720
117
        memcpy(psShape->padfX, psSHP->pabyRec + 12, 8);
2721
117
        memcpy(psShape->padfY, psSHP->pabyRec + 20, 8);
2722
117
#endif
2723
2724
117
        int nOffset = 20 + 8;
2725
2726
        /* -------------------------------------------------------------------- */
2727
        /*      If we have a Z coordinate, collect that now.                    */
2728
        /* -------------------------------------------------------------------- */
2729
117
        if (psShape->nSHPType == SHPT_POINTZ)
2730
22
        {
2731
#if defined(SHP_BIG_ENDIAN)
2732
            SHP_SWAPDOUBLE_CPY(psShape->padfZ, psSHP->pabyRec + nOffset);
2733
#else
2734
22
            memcpy(psShape->padfZ, psSHP->pabyRec + nOffset, 8);
2735
22
#endif
2736
2737
22
            nOffset += 8;
2738
22
        }
2739
2740
        /* -------------------------------------------------------------------- */
2741
        /*      If we have a M measure value, then read it now.  We assume      */
2742
        /*      that the measure can be present for any shape if the size is    */
2743
        /*      big enough, but really it will only occur for the Z shapes      */
2744
        /*      (options), and the M shapes.                                    */
2745
        /* -------------------------------------------------------------------- */
2746
117
        if (nEntitySize >= nOffset + 8)
2747
114
        {
2748
#if defined(SHP_BIG_ENDIAN)
2749
            SHP_SWAPDOUBLE_CPY(psShape->padfM, psSHP->pabyRec + nOffset);
2750
#else
2751
114
            memcpy(psShape->padfM, psSHP->pabyRec + nOffset, 8);
2752
114
#endif
2753
114
            psShape->bMeasureIsUsed = TRUE;
2754
114
        }
2755
2756
        /* -------------------------------------------------------------------- */
2757
        /*      Since no extents are supplied in the record, we will apply      */
2758
        /*      them from the single vertex.                                    */
2759
        /* -------------------------------------------------------------------- */
2760
117
        psShape->dfXMin = psShape->dfXMax = psShape->padfX[0];
2761
117
        psShape->dfYMin = psShape->dfYMax = psShape->padfY[0];
2762
117
        psShape->dfZMin = psShape->dfZMax = psShape->padfZ[0];
2763
117
        psShape->dfMMin = psShape->dfMMax = psShape->padfM[0];
2764
117
    }
2765
2766
2.85k
    return (psShape);
2767
4.13k
}
2768
2769
/************************************************************************/
2770
/*                            SHPTypeName()                             */
2771
/************************************************************************/
2772
2773
const char SHPAPI_CALL1(*) SHPTypeName(int nSHPType)
2774
0
{
2775
0
    switch (nSHPType)
2776
0
    {
2777
0
        case SHPT_NULL:
2778
0
            return "NullShape";
2779
2780
0
        case SHPT_POINT:
2781
0
            return "Point";
2782
2783
0
        case SHPT_ARC:
2784
0
            return "Arc";
2785
2786
0
        case SHPT_POLYGON:
2787
0
            return "Polygon";
2788
2789
0
        case SHPT_MULTIPOINT:
2790
0
            return "MultiPoint";
2791
2792
0
        case SHPT_POINTZ:
2793
0
            return "PointZ";
2794
2795
0
        case SHPT_ARCZ:
2796
0
            return "ArcZ";
2797
2798
0
        case SHPT_POLYGONZ:
2799
0
            return "PolygonZ";
2800
2801
0
        case SHPT_MULTIPOINTZ:
2802
0
            return "MultiPointZ";
2803
2804
0
        case SHPT_POINTM:
2805
0
            return "PointM";
2806
2807
0
        case SHPT_ARCM:
2808
0
            return "ArcM";
2809
2810
0
        case SHPT_POLYGONM:
2811
0
            return "PolygonM";
2812
2813
0
        case SHPT_MULTIPOINTM:
2814
0
            return "MultiPointM";
2815
2816
0
        case SHPT_MULTIPATCH:
2817
0
            return "MultiPatch";
2818
2819
0
        default:
2820
0
            return "UnknownShapeType";
2821
0
    }
2822
0
}
2823
2824
/************************************************************************/
2825
/*                          SHPPartTypeName()                           */
2826
/************************************************************************/
2827
2828
const char SHPAPI_CALL1(*) SHPPartTypeName(int nPartType)
2829
0
{
2830
0
    switch (nPartType)
2831
0
    {
2832
0
        case SHPP_TRISTRIP:
2833
0
            return "TriangleStrip";
2834
2835
0
        case SHPP_TRIFAN:
2836
0
            return "TriangleFan";
2837
2838
0
        case SHPP_OUTERRING:
2839
0
            return "OuterRing";
2840
2841
0
        case SHPP_INNERRING:
2842
0
            return "InnerRing";
2843
2844
0
        case SHPP_FIRSTRING:
2845
0
            return "FirstRing";
2846
2847
0
        case SHPP_RING:
2848
0
            return "Ring";
2849
2850
0
        default:
2851
0
            return "UnknownPartType";
2852
0
    }
2853
0
}
2854
2855
/************************************************************************/
2856
/*                          SHPDestroyObject()                          */
2857
/************************************************************************/
2858
2859
void SHPAPI_CALL SHPDestroyObject(SHPObject *psShape)
2860
976k
{
2861
976k
    if (psShape == SHPLIB_NULLPTR)
2862
0
        return;
2863
2864
976k
    if (psShape->bFastModeReadObject)
2865
4.13k
    {
2866
4.13k
        psShape->bFastModeReadObject = FALSE;
2867
4.13k
        return;
2868
4.13k
    }
2869
2870
971k
    if (psShape->padfX != SHPLIB_NULLPTR)
2871
39
        free(psShape->padfX);
2872
971k
    if (psShape->padfY != SHPLIB_NULLPTR)
2873
39
        free(psShape->padfY);
2874
971k
    if (psShape->padfZ != SHPLIB_NULLPTR)
2875
39
        free(psShape->padfZ);
2876
971k
    if (psShape->padfM != SHPLIB_NULLPTR)
2877
39
        free(psShape->padfM);
2878
2879
971k
    if (psShape->panPartStart != SHPLIB_NULLPTR)
2880
27
        free(psShape->panPartStart);
2881
971k
    if (psShape->panPartType != SHPLIB_NULLPTR)
2882
27
        free(psShape->panPartType);
2883
2884
971k
    free(psShape);
2885
971k
}
2886
2887
/************************************************************************/
2888
/*                       SHPGetPartVertexCount()                        */
2889
/************************************************************************/
2890
2891
static int SHPGetPartVertexCount(const SHPObject *psObject, int iPart)
2892
0
{
2893
0
    if (iPart == psObject->nParts - 1)
2894
0
        return psObject->nVertices - psObject->panPartStart[iPart];
2895
0
    else
2896
0
        return psObject->panPartStart[iPart + 1] -
2897
0
               psObject->panPartStart[iPart];
2898
0
}
2899
2900
/************************************************************************/
2901
/*                      SHPRewindIsInnerRing()                          */
2902
/************************************************************************/
2903
2904
/* Return -1 in case of ambiguity */
2905
static int SHPRewindIsInnerRing(const SHPObject *psObject, int iOpRing,
2906
                                double dfTestX, double dfTestY,
2907
                                double dfRelativeTolerance, int bSameZ,
2908
                                double dfTestZ)
2909
0
{
2910
    /* -------------------------------------------------------------------- */
2911
    /*      Determine if this ring is an inner ring or an outer ring        */
2912
    /*      relative to all the other rings.  For now we assume the         */
2913
    /*      first ring is outer and all others are inner, but eventually    */
2914
    /*      we need to fix this to handle multiple island polygons and      */
2915
    /*      unordered sets of rings.                                        */
2916
    /*                                                                      */
2917
    /* -------------------------------------------------------------------- */
2918
2919
0
    bool bInner = false;
2920
0
    for (int iCheckRing = 0; iCheckRing < psObject->nParts; iCheckRing++)
2921
0
    {
2922
0
        if (iCheckRing == iOpRing)
2923
0
            continue;
2924
2925
0
        const int nVertStartCheck = psObject->panPartStart[iCheckRing];
2926
0
        const int nVertCountCheck = SHPGetPartVertexCount(psObject, iCheckRing);
2927
2928
        /* Ignore rings that don't have the same (constant) Z value as the
2929
         * point. */
2930
        /* As noted in SHPRewindObject(), this is a simplification */
2931
        /* of what we should ideally do. */
2932
0
        if (!bSameZ)
2933
0
        {
2934
0
            int bZTestOK = TRUE;
2935
0
            for (int iVert = nVertStartCheck + 1;
2936
0
                 iVert < nVertStartCheck + nVertCountCheck; ++iVert)
2937
0
            {
2938
0
                if (psObject->padfZ[iVert] != dfTestZ)
2939
0
                {
2940
0
                    bZTestOK = FALSE;
2941
0
                    break;
2942
0
                }
2943
0
            }
2944
0
            if (!bZTestOK)
2945
0
                continue;
2946
0
        }
2947
2948
0
        for (int iEdge = 0; iEdge < nVertCountCheck; iEdge++)
2949
0
        {
2950
0
            int iNext;
2951
0
            if (iEdge < nVertCountCheck - 1)
2952
0
                iNext = iEdge + 1;
2953
0
            else
2954
0
                iNext = 0;
2955
2956
0
            const double y0 = psObject->padfY[iEdge + nVertStartCheck];
2957
0
            const double y1 = psObject->padfY[iNext + nVertStartCheck];
2958
            /* Rule #1:
2959
             * Test whether the edge 'straddles' the horizontal ray from
2960
             * the test point (dfTestY,dfTestY)
2961
             * The rule #1 also excludes edges colinear with the ray.
2962
             */
2963
0
            if ((y0 < dfTestY && dfTestY <= y1) ||
2964
0
                (y1 < dfTestY && dfTestY <= y0))
2965
0
            {
2966
                /* Rule #2:
2967
                 * Test if edge-ray intersection is on the right from the
2968
                 * test point (dfTestY,dfTestY)
2969
                 */
2970
0
                const double x0 = psObject->padfX[iEdge + nVertStartCheck];
2971
0
                const double x1 = psObject->padfX[iNext + nVertStartCheck];
2972
0
                const double intersect_minus_testX =
2973
0
                    (x0 - dfTestX) + (dfTestY - y0) / (y1 - y0) * (x1 - x0);
2974
2975
0
                if (fabs(intersect_minus_testX) <=
2976
0
                    dfRelativeTolerance * fabs(dfTestX))
2977
0
                {
2978
                    /* Potential shared edge, or slightly overlapping polygons
2979
                     */
2980
0
                    return -1;
2981
0
                }
2982
0
                else if (intersect_minus_testX < 0)
2983
0
                {
2984
0
                    bInner = !bInner;
2985
0
                }
2986
0
            }
2987
0
        }
2988
0
    } /* for iCheckRing */
2989
0
    return bInner;
2990
0
}
2991
2992
/************************************************************************/
2993
/*                          SHPRewindObject()                           */
2994
/*                                                                      */
2995
/*      Reset the winding of polygon objects to adhere to the           */
2996
/*      specification.                                                  */
2997
/************************************************************************/
2998
2999
int SHPAPI_CALL SHPRewindObject(const SHPHandle hSHP, SHPObject *psObject)
3000
0
{
3001
0
    (void)hSHP;
3002
    /* -------------------------------------------------------------------- */
3003
    /*      Do nothing if this is not a polygon object.                     */
3004
    /* -------------------------------------------------------------------- */
3005
0
    if (psObject->nSHPType != SHPT_POLYGON &&
3006
0
        psObject->nSHPType != SHPT_POLYGONZ &&
3007
0
        psObject->nSHPType != SHPT_POLYGONM)
3008
0
        return 0;
3009
3010
0
    if (psObject->nVertices == 0 || psObject->nParts == 0)
3011
0
        return 0;
3012
3013
    /* -------------------------------------------------------------------- */
3014
    /*      Test if all points have the same Z value.                       */
3015
    /* -------------------------------------------------------------------- */
3016
0
    int bSameZ = TRUE;
3017
0
    if (psObject->nSHPType == SHPT_POLYGONZ ||
3018
0
        psObject->nSHPType == SHPT_POLYGONM)
3019
0
    {
3020
0
        for (int iVert = 1; iVert < psObject->nVertices; ++iVert)
3021
0
        {
3022
0
            if (psObject->padfZ[iVert] != psObject->padfZ[0])
3023
0
            {
3024
0
                bSameZ = FALSE;
3025
0
                break;
3026
0
            }
3027
0
        }
3028
0
    }
3029
3030
    /* -------------------------------------------------------------------- */
3031
    /*      Process each of the rings.                                      */
3032
    /* -------------------------------------------------------------------- */
3033
0
    int bAltered = 0;
3034
0
    for (int iOpRing = 0; iOpRing < psObject->nParts; iOpRing++)
3035
0
    {
3036
0
        const int nVertStart = psObject->panPartStart[iOpRing];
3037
0
        const int nVertCount = SHPGetPartVertexCount(psObject, iOpRing);
3038
3039
0
        if (nVertCount < 2)
3040
0
            continue;
3041
3042
        /* If a ring has a non-constant Z value, then consider it as an outer */
3043
        /* ring. */
3044
        /* NOTE: this is a rough approximation. If we were smarter, */
3045
        /* we would check that all points of the ring are coplanar, and compare
3046
         */
3047
        /* that to other rings in the same (oblique) plane. */
3048
0
        int bDoIsInnerRingTest = TRUE;
3049
0
        if (!bSameZ)
3050
0
        {
3051
0
            int bPartSameZ = TRUE;
3052
0
            for (int iVert = nVertStart + 1; iVert < nVertStart + nVertCount;
3053
0
                 ++iVert)
3054
0
            {
3055
0
                if (psObject->padfZ[iVert] != psObject->padfZ[nVertStart])
3056
0
                {
3057
0
                    bPartSameZ = FALSE;
3058
0
                    break;
3059
0
                }
3060
0
            }
3061
0
            if (!bPartSameZ)
3062
0
                bDoIsInnerRingTest = FALSE;
3063
0
        }
3064
3065
0
        int bInner = FALSE;
3066
0
        if (bDoIsInnerRingTest)
3067
0
        {
3068
0
            for (int iTolerance = 0; iTolerance < 2; iTolerance++)
3069
0
            {
3070
                /* In a first attempt, use a relaxed criterion to decide if a
3071
                 * point */
3072
                /* is inside another ring. If all points of the current ring are
3073
                 * in the */
3074
                /* "grey" zone w.r.t that criterion, which seems really
3075
                 * unlikely, */
3076
                /* then use the strict criterion for another pass. */
3077
0
                const double dfRelativeTolerance = (iTolerance == 0) ? 1e-9 : 0;
3078
0
                for (int iVert = nVertStart;
3079
0
                     iVert + 1 < nVertStart + nVertCount; ++iVert)
3080
0
                {
3081
                    /* Use point in the middle of segment to avoid testing
3082
                     * common points of rings.
3083
                     */
3084
0
                    const double dfTestX =
3085
0
                        (psObject->padfX[iVert] + psObject->padfX[iVert + 1]) /
3086
0
                        2;
3087
0
                    const double dfTestY =
3088
0
                        (psObject->padfY[iVert] + psObject->padfY[iVert + 1]) /
3089
0
                        2;
3090
0
                    const double dfTestZ =
3091
0
                        !bSameZ ? psObject->padfZ[nVertStart] : 0;
3092
3093
0
                    bInner = SHPRewindIsInnerRing(psObject, iOpRing, dfTestX,
3094
0
                                                  dfTestY, dfRelativeTolerance,
3095
0
                                                  bSameZ, dfTestZ);
3096
0
                    if (bInner >= 0)
3097
0
                        break;
3098
0
                }
3099
0
                if (bInner >= 0)
3100
0
                    break;
3101
0
            }
3102
0
            if (bInner < 0)
3103
0
            {
3104
                /* Completely degenerate case. Do not bother touching order. */
3105
0
                continue;
3106
0
            }
3107
0
        }
3108
3109
        /* -------------------------------------------------------------------- */
3110
        /*      Determine the current order of this ring so we will know if     */
3111
        /*      it has to be reversed.                                          */
3112
        /* -------------------------------------------------------------------- */
3113
3114
0
        double dfSum = psObject->padfX[nVertStart] *
3115
0
                       (psObject->padfY[nVertStart + 1] -
3116
0
                        psObject->padfY[nVertStart + nVertCount - 1]);
3117
0
        int iVert = nVertStart + 1;
3118
0
        for (; iVert < nVertStart + nVertCount - 1; iVert++)
3119
0
        {
3120
0
            dfSum += psObject->padfX[iVert] *
3121
0
                     (psObject->padfY[iVert + 1] - psObject->padfY[iVert - 1]);
3122
0
        }
3123
3124
0
        dfSum += psObject->padfX[iVert] *
3125
0
                 (psObject->padfY[nVertStart] - psObject->padfY[iVert - 1]);
3126
3127
        /* -------------------------------------------------------------------- */
3128
        /*      Reverse if necessary.                                           */
3129
        /* -------------------------------------------------------------------- */
3130
0
        if ((dfSum < 0.0 && bInner) || (dfSum > 0.0 && !bInner))
3131
0
        {
3132
0
            bAltered++;
3133
0
            for (int i = 0; i < nVertCount / 2; i++)
3134
0
            {
3135
                /* Swap X */
3136
0
                double dfSaved = psObject->padfX[nVertStart + i];
3137
0
                psObject->padfX[nVertStart + i] =
3138
0
                    psObject->padfX[nVertStart + nVertCount - i - 1];
3139
0
                psObject->padfX[nVertStart + nVertCount - i - 1] = dfSaved;
3140
3141
                /* Swap Y */
3142
0
                dfSaved = psObject->padfY[nVertStart + i];
3143
0
                psObject->padfY[nVertStart + i] =
3144
0
                    psObject->padfY[nVertStart + nVertCount - i - 1];
3145
0
                psObject->padfY[nVertStart + nVertCount - i - 1] = dfSaved;
3146
3147
                /* Swap Z */
3148
0
                if (psObject->padfZ)
3149
0
                {
3150
0
                    dfSaved = psObject->padfZ[nVertStart + i];
3151
0
                    psObject->padfZ[nVertStart + i] =
3152
0
                        psObject->padfZ[nVertStart + nVertCount - i - 1];
3153
0
                    psObject->padfZ[nVertStart + nVertCount - i - 1] = dfSaved;
3154
0
                }
3155
3156
                /* Swap M */
3157
0
                if (psObject->padfM)
3158
0
                {
3159
0
                    dfSaved = psObject->padfM[nVertStart + i];
3160
0
                    psObject->padfM[nVertStart + i] =
3161
0
                        psObject->padfM[nVertStart + nVertCount - i - 1];
3162
0
                    psObject->padfM[nVertStart + nVertCount - i - 1] = dfSaved;
3163
0
                }
3164
0
            }
3165
0
        }
3166
0
    }
3167
3168
0
    return bAltered;
3169
0
}