Coverage Report

Created: 2025-06-22 06:59

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