LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/shape - shpopen.c (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 838 1289 65.0 %
Date: 2024-11-21 22:18:42 Functions: 19 25 76.0 %

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

Generated by: LCOV version 1.14