Coverage Report

Created: 2025-06-13 06:18

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