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