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