shapelib 0.7.2 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,4 @@
1
+ #include <stdio.h>
1
2
  #include <stdlib.h>
2
3
  #include <ctype.h>
3
4
  #include <string.h>
@@ -10,6 +11,9 @@
10
11
  #include <ruby.h>
11
12
  #include "shpplus.h"
12
13
 
14
+ /* Match native shapelib's numeric formatting instead of Ruby's substitute. */
15
+ #undef snprintf
16
+
13
17
  #define HAS_DBFMARKRECORDDELETED 0
14
18
  #define ATTR_TABLE_DEFAULT 16
15
19
 
@@ -80,16 +84,26 @@ static shapefile_t *ShapefileInit(void)
80
84
  sfile = xmalloc(sizeof(shapefile_t));
81
85
  if (sfile == NULL)
82
86
  return NULL;
87
+ sfile->hshp = NULL;
88
+ sfile->hdbf = NULL;
83
89
  sfile->nrecs = sfile->nfields = -1;
84
90
  sfile->ftab = NULL;
85
91
  sfile->irec = 0;
86
92
  return sfile;
87
93
  }
88
94
 
95
+ static void ShapefileFail(shapefile_t *sfile, const char *operation,
96
+ const char *filename)
97
+ {
98
+ shapefile_close(sfile);
99
+ xfree(sfile);
100
+ rb_raise(rb_eIOError, "%s failed for %s", operation, filename);
101
+ }
102
+
89
103
  static void ShapefileBuildFieldTable(shapefile_t *sfile)
90
104
  {
91
105
  int i;
92
- if (sfile->ftab)
106
+ if (!sfile->hdbf || sfile->ftab)
93
107
  return;
94
108
  sfile->nfields = shapefile_field_count(sfile);
95
109
  sfile->ftab = xmalloc(sizeof(field_t) * sfile->nfields);
@@ -107,12 +121,22 @@ static void ShapefileBuildFieldTable(shapefile_t *sfile)
107
121
  }
108
122
  }
109
123
 
124
+ static VALUE ShapefileBuildFieldTableProtected(VALUE ptr)
125
+ {
126
+ ShapefileBuildFieldTable((shapefile_t *)ptr);
127
+ return Qnil;
128
+ }
129
+
110
130
  static void ShapefileWriteAttribute(shapefile_t *sfile,
111
131
  int irec, int ifield, attrib_t *a)
112
132
  {
113
133
  int s;
114
134
  if (a->type == -1) {
115
135
  rb_bug("supposed to be rejected in shapefile_write");
136
+ #ifdef HAVE_CONST_FTDATE
137
+ } else if (sfile->ftab[ifield].type == FTDate) {
138
+ s = DBFWriteAttributeDirectly(sfile->hdbf, irec, ifield, a->val.sval);
139
+ #endif
116
140
  } else if (a->type == FTInteger) {
117
141
  s = DBFWriteIntegerAttribute(sfile->hdbf, irec, ifield,
118
142
  a->val.ival);
@@ -141,12 +165,20 @@ static void ShapefileWriteAttribute(shapefile_t *sfile,
141
165
  shapefile_t *shapefile_open(const char *filename, const char *mode)
142
166
  {
143
167
  shapefile_t *sfile;
168
+ int state;
144
169
  sfile = ShapefileInit();
145
170
  sfile->hshp = SHPOpen(filename, mode);
146
171
  monitor(("SHPOpen(\"%s\", \"%s\") -> %p", filename, mode, sfile->hshp));
172
+ if (!sfile->hshp)
173
+ ShapefileFail(sfile, "SHPOpen", filename);
147
174
  sfile->hdbf = DBFOpen(filename, mode);
148
175
  monitor(("DBFOpen(\"%s\", \"%s\") -> %p", filename, mode, sfile->hdbf));
149
- ShapefileBuildFieldTable(sfile);
176
+ rb_protect(ShapefileBuildFieldTableProtected, (VALUE)sfile, &state);
177
+ if (state) {
178
+ shapefile_close(sfile);
179
+ xfree(sfile);
180
+ rb_jump_tag(state);
181
+ }
150
182
  return sfile;
151
183
  }
152
184
 
@@ -156,8 +188,12 @@ shapefile_t *shapefile_new(const char *filename, int shptype)
156
188
  sfile = ShapefileInit();
157
189
  sfile->hshp = SHPCreate(filename, shptype);
158
190
  monitor(("SHPCreate(\"%s\", %d) -> %p", filename, shptype, sfile->hshp));
191
+ if (!sfile->hshp)
192
+ ShapefileFail(sfile, "SHPCreate", filename);
159
193
  sfile->hdbf = DBFCreate(filename);
160
194
  monitor(("DBFCreate(\"%s\") -> %p", filename, sfile->hdbf));
195
+ if (!sfile->hdbf)
196
+ ShapefileFail(sfile, "DBFCreate", filename);
161
197
  return sfile;
162
198
  }
163
199
 
@@ -173,6 +209,16 @@ int shapefile_add_field(shapefile_t *sfile, const char *name,
173
209
  rb_raise(rb_eRuntimeError, "add field before you write a record");
174
210
  return -1;
175
211
  }
212
+ if ((atype == FTInteger || atype == FTDouble) && (decimals < 0 || decimals > 255))
213
+ rb_raise(rb_eArgError, "numeric field precision must be between 0 and 255");
214
+ #ifdef HAVE_CONST_FTDATE
215
+ if (atype == FTDate && (width != 8 || decimals != 0))
216
+ rb_raise(rb_eArgError, "Date fields require width 8 and zero decimal places");
217
+ #endif
218
+ if (strlen(name) > XBASE_FLDNAME_LEN_WRITE)
219
+ rb_raise(rb_eArgError, "field name exceeds 10 bytes");
220
+ if (DBFGetFieldIndex(sfile->hdbf, name) >= 0)
221
+ rb_raise(rb_eArgError, "field name already exists: %s", name);
176
222
  monitor(("DBFAddField(%p, %s, %d, %d, %d)", sfile->hdbf, name,
177
223
  atype, width, decimals));
178
224
  r = DBFAddField(sfile->hdbf, name, (DBFFieldType)atype, width, decimals);
@@ -206,7 +252,8 @@ int shapefile_field_decimals(shapefile_t *sfile, unsigned ifield)
206
252
  int decimals;
207
253
  if (NULL == sfile->hdbf)
208
254
  return -1;
209
- DBFGetFieldInfo(sfile->hdbf, ifield, NULL, NULL, &decimals);
255
+ if (DBFGetFieldInfo(sfile->hdbf, ifield, NULL, NULL, &decimals) == FTInvalid)
256
+ return -1;
210
257
  return decimals;
211
258
  }
212
259
 
@@ -259,7 +306,8 @@ const char *shapefile_field_name(shapefile_t *sfile, unsigned ifield)
259
306
  static char buf[DBF_NAMELEN];
260
307
  if (NULL == sfile->hdbf)
261
308
  return NULL;
262
- DBFGetFieldInfo(sfile->hdbf, ifield, buf, NULL, NULL);
309
+ if (DBFGetFieldInfo(sfile->hdbf, ifield, buf, NULL, NULL) == FTInvalid)
310
+ return NULL;
263
311
  return buf;
264
312
  }
265
313
 
@@ -282,7 +330,8 @@ int shapefile_field_width(shapefile_t *sfile, unsigned ifield)
282
330
  int width;
283
331
  if (NULL == sfile->hdbf)
284
332
  return -1;
285
- DBFGetFieldInfo(sfile->hdbf, ifield, NULL, &width, NULL);
333
+ if (DBFGetFieldInfo(sfile->hdbf, ifield, NULL, &width, NULL) == FTInvalid)
334
+ return -1;
286
335
  return width;
287
336
  }
288
337
 
@@ -319,29 +368,19 @@ void shapefile_bound(shapefile_t *sfile, double *minbound, double *maxbound)
319
368
  SHPGetInfo(sfile->hshp, NULL, NULL, minbound, maxbound);
320
369
  }
321
370
 
322
- shape_t *shapefile_read(shapefile_t *sfile, int ishape)
323
- {
371
+ struct shapefile_read_attributes_args {
372
+ shapefile_t *sfile;
324
373
  shape_t *shape;
374
+ int ishape;
375
+ };
376
+
377
+ static VALUE ShapefileReadAttributesProtected(VALUE ptr)
378
+ {
379
+ struct shapefile_read_attributes_args *args = (struct shapefile_read_attributes_args *)ptr;
380
+ shapefile_t *sfile = args->sfile;
381
+ shape_t *shape = args->shape;
382
+ int ishape = args->ishape;
325
383
  int i, iattr;
326
- if (NULL == sfile->hshp)
327
- return NULL;
328
- if (ishape < 0) {
329
- if (sfile->nrecs < 0) {
330
- shapefile_size(sfile);
331
- sfile->irec = 0;
332
- }
333
- ishape = (sfile->irec)++;
334
- } else {
335
- sfile->irec = ishape + 1;
336
- }
337
- shape = shape_init(SHPReadObject(sfile->hshp, ishape));
338
- if (shape == NULL)
339
- return NULL;
340
- monitor(("SHPReadObject(%p) -> %p", sfile->hshp, shape));
341
- if (shape->obj->nSHPType == SHPT_NULL) {
342
- monitor(("shape type NULL"));
343
- return NULL;
344
- }
345
384
  ShapefileBuildFieldTable(sfile);
346
385
  if (NULL == sfile->hdbf)
347
386
  goto no_dbf;
@@ -351,6 +390,12 @@ shape_t *shapefile_read(shapefile_t *sfile, int ishape)
351
390
  goto no_dbf;
352
391
  }
353
392
  #endif
393
+ if (sfile->nfields > 0 && DBFReadTuple(sfile->hdbf, ishape) == NULL) {
394
+ /* A partial read can overwrite the buffer without changing its record number. */
395
+ if (!sfile->hdbf->bCurrentRecordModified)
396
+ sfile->hdbf->nCurrentRecord = -1;
397
+ rb_raise(rb_eIOError, "cannot read DBF record %d", ishape);
398
+ }
354
399
  ShapeSetAttrTable(shape, sfile->nfields);
355
400
  for (i = iattr = 0; i < sfile->nfields; i++) {
356
401
  attrib_t *a;
@@ -381,6 +426,47 @@ shape_t *shapefile_read(shapefile_t *sfile, int ishape)
381
426
  ;
382
427
  }
383
428
  no_dbf:
429
+ return Qnil;
430
+ }
431
+
432
+ shape_t *shapefile_read(shapefile_t *sfile, int ishape)
433
+ {
434
+ shape_t *shape;
435
+ struct shapefile_read_attributes_args args;
436
+ int state;
437
+ int sequential = ishape < 0;
438
+ if (NULL == sfile->hshp)
439
+ return NULL;
440
+ if (ishape < 0) {
441
+ if (sfile->nrecs < 0) {
442
+ shapefile_size(sfile);
443
+ }
444
+ ishape = sfile->irec;
445
+ } else {
446
+ sfile->irec = ishape + 1;
447
+ }
448
+ while (1) {
449
+ shape = shape_init(SHPReadObject(sfile->hshp, ishape));
450
+ if (shape == NULL)
451
+ return NULL;
452
+ sfile->irec = ishape + 1;
453
+ monitor(("SHPReadObject(%p) -> %p", sfile->hshp, shape));
454
+ if (shape->obj->nSHPType != SHPT_NULL)
455
+ break;
456
+ monitor(("shape type NULL"));
457
+ shape_close(shape);
458
+ if (!sequential)
459
+ return NULL;
460
+ ishape = sfile->irec;
461
+ }
462
+ args.sfile = sfile;
463
+ args.shape = shape;
464
+ args.ishape = ishape;
465
+ rb_protect(ShapefileReadAttributesProtected, (VALUE)&args, &state);
466
+ if (state) {
467
+ shape_close(shape);
468
+ rb_jump_tag(state);
469
+ }
384
470
  return shape;
385
471
  }
386
472
 
@@ -417,23 +503,115 @@ int shapefile_size(shapefile_t *sfile)
417
503
  int shapefile_write(shapefile_t *sfile, int irec, shape_t *shape)
418
504
  {
419
505
  int r;
506
+ int shape_type, file_type, record_count;
507
+ int ipart;
420
508
  int ifield;
421
509
  int iattr;
422
510
  int *usedfield;
511
+ VALUE usedfield_storage = 0;
423
512
  /* precondition */
424
513
  if (!sfile->hshp || !shape->obj)
425
514
  return -1;
515
+ if (!sfile->hdbf)
516
+ rb_raise(rb_eIOError, "DBF file is not open for writing");
517
+ SHPGetInfo(sfile->hshp, &record_count, &file_type, NULL, NULL);
518
+ if (irec < -1 || (irec >= 0 && irec >= record_count))
519
+ rb_raise(rb_eArgError,
520
+ "write index %d must be -1 (append) or an existing record index (record count: %d)",
521
+ irec, record_count);
522
+ shape_type = shape->obj->nSHPType;
523
+ switch (shape_type) {
524
+ case SHPT_NULL:
525
+ case SHPT_POINT: case SHPT_POINTM: case SHPT_POINTZ:
526
+ case SHPT_ARC: case SHPT_ARCM: case SHPT_ARCZ:
527
+ case SHPT_POLYGON: case SHPT_POLYGONM: case SHPT_POLYGONZ:
528
+ case SHPT_MULTIPOINT: case SHPT_MULTIPOINTM: case SHPT_MULTIPOINTZ:
529
+ case SHPT_MULTIPATCH:
530
+ break;
531
+ default:
532
+ rb_raise(rb_eArgError, "unsupported shape type %d", shape_type);
533
+ }
534
+ if (shape_type != SHPT_NULL && shape_type != file_type)
535
+ rb_raise(rb_eArgError, "shape type %d does not match ShapeFile type %d", shape_type, file_type);
536
+ if (shape_type == SHPT_POINT || shape_type == SHPT_POINTM || shape_type == SHPT_POINTZ) {
537
+ if (shape->obj->nVertices < 1)
538
+ rb_raise(rb_eArgError, "point has no vertices");
539
+ if (shape->obj->nVertices > 1)
540
+ rb_raise(rb_eArgError, "point must have exactly one vertex (got %d)", shape->obj->nVertices);
541
+ }
542
+ if (shape_type == SHPT_NULL && shape->obj->nVertices != 0)
543
+ rb_raise(rb_eArgError, "null shape must have no vertices (got %d)", shape->obj->nVertices);
544
+ /* Match the part-layout checks in SHPReadObject before changing either file. */
545
+ for (ipart = 0; ipart < shape->obj->nParts; ipart++) {
546
+ int start = shape->obj->panPartStart[ipart];
547
+ int vertices = shape->obj->nVertices;
548
+ if (start < 0 || (vertices > 0 ? start >= vertices : start != 0))
549
+ rb_raise(rb_eArgError, "part_start[%d] is out of bounds for %d vertices", ipart, vertices);
550
+ if (ipart > 0 && start <= shape->obj->panPartStart[ipart - 1])
551
+ rb_raise(rb_eArgError, "part_start[%d] must be greater than part_start[%d]", ipart, ipart - 1);
552
+ if (shape_type == SHPT_MULTIPATCH &&
553
+ (shape->obj->panPartType[ipart] < 0 || shape->obj->panPartType[ipart] > 5))
554
+ rb_raise(rb_eArgError, "part_type[%d] must be between 0 and 5 (got %d)",
555
+ ipart, shape->obj->panPartType[ipart]);
556
+ }
426
557
  ShapefileBuildFieldTable(sfile);
558
+ /* Reject invalid attributes before changing either file. */
559
+ for (iattr = 0; iattr < shape->nattr; iattr++) {
560
+ attrib_t *a = &(shape->attr[iattr]);
561
+ DBFFieldType type;
562
+ if (a->type == -1)
563
+ continue;
564
+ ifield = DBFGetFieldIndex(sfile->hdbf, a->name);
565
+ if (ifield == -1)
566
+ rb_raise(rb_eArgError, "attribute %s not defined in ShapeFile", a->name);
567
+ type = sfile->ftab[ifield].type;
568
+ #ifdef HAVE_CONST_FTDATE
569
+ if (type == FTDate) {
570
+ if (a->type != FTString && a->type != FTDate)
571
+ rb_raise(rb_eTypeError, "attribute %s has incompatible type for ShapeFile field", a->name);
572
+ if (shapefile_field_width(sfile, ifield) != 8 || shapefile_field_decimals(sfile, ifield) != 0)
573
+ rb_raise(rb_eArgError, "Date fields require width 8 and zero decimal places");
574
+ if (strlen(a->val.sval) != 8 || strspn(a->val.sval, "0123456789") != 8)
575
+ rb_raise(rb_eArgError, "attribute %s must be an 8-digit YYYYMMDD string", a->name);
576
+ continue;
577
+ }
578
+ #endif
579
+ if (!(((type == FTInteger || type == FTDouble) &&
580
+ (a->type == FTInteger || a->type == FTDouble)) ||
581
+ ((type == FTString || type == FTLogical) &&
582
+ (a->type == FTString || a->type == FTLogical
583
+ #ifdef HAVE_CONST_FTDATE
584
+ || a->type == FTDate
585
+ #endif
586
+ ))))
587
+ rb_raise(rb_eTypeError, "attribute %s has incompatible type for ShapeFile field", a->name);
588
+ if (type == FTLogical && a->val.sval[0] != 'T' && a->val.sval[0] != 'F')
589
+ rb_raise(rb_eArgError, "attribute %s must start with T or F for a Logical field", a->name);
590
+ if (type == FTString && strlen(a->val.sval) > (size_t)shapefile_field_width(sfile, ifield))
591
+ rb_raise(rb_eRuntimeError, "attribute %s exceeds field width", a->name);
592
+ if (type == FTInteger || type == FTDouble) {
593
+ int width, decimals, length;
594
+ double value = a->type == FTInteger ? (double)a->val.ival : a->val.fval;
595
+ DBFGetFieldInfo(sfile->hdbf, ifield, NULL, &width, &decimals);
596
+ length = snprintf(NULL, 0, "%.*f", decimals, value);
597
+ if (length < 0)
598
+ rb_raise(rb_eRuntimeError, "cannot format numeric attribute %s", a->name);
599
+ if (length > width)
600
+ rb_raise(rb_eRuntimeError, "attribute %s exceeds field width", a->name);
601
+ }
602
+ }
603
+ /* Ruby can reclaim this temporary buffer if attribute writing raises. */
604
+ usedfield = ALLOCV_N(int, usedfield_storage, sfile->nfields);
605
+ for (ifield = 0; ifield < sfile->nfields; ifield++) {
606
+ usedfield[ifield] = 0;
607
+ }
427
608
  /* write the object */
428
609
  r = SHPWriteObject(sfile->hshp, irec, shape->obj);
429
610
  monitor(("SHPWriteObject(%p, %d, %p) -> %d", sfile->hshp, irec,
430
611
  shape->obj, r));
431
- if (r < 0)
612
+ if (r < 0) {
613
+ ALLOCV_END(usedfield_storage);
432
614
  return -1;
433
- /* make field table */
434
- usedfield = xmalloc(sizeof(int) * sfile->nfields);
435
- for (ifield = 0; ifield < sfile->nfields; ifield++) {
436
- usedfield[ifield] = 0;
437
615
  }
438
616
  for (iattr = 0; iattr < shape->nattr; iattr++) {
439
617
  attrib_t *a;
@@ -442,10 +620,6 @@ int shapefile_write(shapefile_t *sfile, int irec, shape_t *shape)
442
620
  continue;
443
621
  }
444
622
  ifield = DBFGetFieldIndex(sfile->hdbf, a->name);
445
- if (ifield == -1) {
446
- rb_raise(rb_eArgError,
447
- "attribute %s not defined in ShapeFile", a->name);
448
- }
449
623
  ShapefileWriteAttribute(sfile, r, ifield, a);
450
624
  usedfield[ifield] = 1;
451
625
  }
@@ -453,29 +627,35 @@ int shapefile_write(shapefile_t *sfile, int irec, shape_t *shape)
453
627
  int r2;
454
628
  if (usedfield[ifield] == 0) {
455
629
  r2 = DBFWriteNULLAttribute(sfile->hdbf, r, ifield);
456
- monitor(("DBFWriteNULLAttribute(%p %d %d -> %d",
457
- sfile->hdbf, r, ifield, r2));
630
+ monitor(("DBFWriteNULLAttribute(%p %d %d -> %d", sfile->hdbf, r, ifield, r2));
458
631
  if (r2 == 0)
459
632
  rb_raise(rb_eRuntimeError, "write error");
460
633
  }
461
634
  }
462
- free(usedfield);
635
+ ALLOCV_END(usedfield_storage);
463
636
  return r;
464
637
  }
465
638
 
639
+ static VALUE ShapeInitProtected(VALUE ptr)
640
+ {
641
+ shape_t *sh = xmalloc(sizeof(shape_t));
642
+ sh->obj = (SHPObject *)ptr;
643
+ sh->attr = NULL;
644
+ sh->nattr = 0;
645
+ return (VALUE)sh;
646
+ }
647
+
466
648
  static shape_t *shape_init(SHPObject *objp)
467
649
  {
468
650
  shape_t *sh;
651
+ int state;
469
652
  if (objp == NULL)
470
653
  return NULL;
471
- sh = xmalloc(sizeof(shape_t));
472
- if (sh == NULL) {
654
+ sh = (shape_t *)rb_protect(ShapeInitProtected, (VALUE)objp, &state);
655
+ if (state) {
473
656
  SHPDestroyObject(objp);
474
- return NULL;
657
+ rb_jump_tag(state);
475
658
  }
476
- sh->obj = objp;
477
- sh->attr = NULL;
478
- sh->nattr = 0;
479
659
  return sh;
480
660
  }
481
661
 
@@ -545,20 +725,37 @@ int shape_attrib_index(shape_t *sh, const char *key, int extend)
545
725
  }
546
726
  for (i = 0; i < sh->nattr; i++) {
547
727
  if (sh->attr[i].type == -1) {
548
- strcpy(sh->attr[i].name, key);
728
+ strncpy(sh->attr[i].name, key, sizeof(sh->attr[i].name) - 1);
729
+ sh->attr[i].name[sizeof(sh->attr[i].name) - 1] = '\0';
549
730
  return i;
550
731
  }
551
732
  }
552
733
  if (extend) {
553
734
  i = sh->nattr;
554
735
  ShapeSetAttrTable(sh, sh->nattr * 2);
555
- strcpy(sh->attr[i].name, key);
736
+ strncpy(sh->attr[i].name, key, sizeof(sh->attr[i].name) - 1);
737
+ sh->attr[i].name[sizeof(sh->attr[i].name) - 1] = '\0';
556
738
  return i;
557
739
  } else {
558
740
  return -1;
559
741
  }
560
742
  }
561
743
 
744
+ void shape_attrib_clear_value(attrib_t *attr)
745
+ {
746
+ switch (attr->type) {
747
+ case -1:
748
+ case FTInteger:
749
+ case FTDouble:
750
+ break;
751
+ default:
752
+ xfree(attr->val.sval);
753
+ break;
754
+ }
755
+ attr->type = -1;
756
+ memset(&attr->val, 0, sizeof(attr->val));
757
+ }
758
+
562
759
  int shape_close(shape_t *shape)
563
760
  {
564
761
  if (shape->obj) {
@@ -569,18 +766,9 @@ int shape_close(shape_t *shape)
569
766
  if (shape->attr) {
570
767
  int i;
571
768
  for (i = 0; i < shape->nattr; i++) {
572
- switch (shape->attr[i].type) {
573
- case FTInteger:
574
- case FTDouble:
575
- break;
576
- case FTString:
577
- default:
578
- free(shape->attr[i].val.sval);
579
- shape->attr[i].val.ival = 0;
580
- break;
581
- }
769
+ shape_attrib_clear_value(&shape->attr[i]);
582
770
  }
583
- free(shape->attr);
771
+ xfree(shape->attr);
584
772
  shape->attr = NULL;
585
773
  }
586
774
  xfree(shape);
@@ -62,6 +62,7 @@ extern shape_t *shape_new_obj(int shapetype,
62
62
  int n_parts, int *part_start, int *part_type, int n_vert,
63
63
  double *x, double *y, double *m, double *z);
64
64
  extern int shape_close(shape_t *shape);
65
+ extern void shape_attrib_clear_value(attrib_t *attr);
65
66
  extern int shape_attrib_index(shape_t *sh, const char *key, int extend);
66
67
 
67
68
  /*