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