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