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

Generated by: LCOV version 1.14