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