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