shapelib 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,21 @@
1
+ static method_table_entry sf_methods[] = {
2
+ { "add_field", sf_add_field, -1 },
3
+ { "close", sf_close, 0 },
4
+ { "delete_shape", sf_delete_shape, 1 },
5
+ { "each", sf_each, 0 },
6
+ { "field_count", sf_field_count, 0 },
7
+ { "field_index", sf_field_index, 1 },
8
+ { "field_name", sf_field_name, 1 },
9
+ { "field_type", sf_field_type, 1 },
10
+ { "field_width", sf_field_width, 1 },
11
+ { "field_decimals", sf_field_decimals, 1 },
12
+ { "fields", sf_fields, 0 },
13
+ { "minbound", sf_minbound, 0 },
14
+ { "maxbound", sf_maxbound, 0 },
15
+ { "read", sf_read, -1 },
16
+ { "rewind", sf_rewind, 0 },
17
+ { "shape_type", sf_shape_type, 0 },
18
+ { "size", sf_size, 0 },
19
+ { "write", sf_write, -1 },
20
+ { NULL, NULL, 0 }
21
+ };
@@ -0,0 +1,588 @@
1
+ #include <stdlib.h>
2
+ #include <ctype.h>
3
+ #include <string.h>
4
+ #if defined(HAVE_SHAPEFIL_H)
5
+ #include <shapefil.h>
6
+ #endif
7
+ #if defined(HAVE_LIBSHP_SHAPEFIL_H)
8
+ #include <libshp/shapefil.h>
9
+ #endif
10
+ #include <ruby.h>
11
+ #include "shpplus.h"
12
+
13
+ #define HAS_DBFMARKRECORDDELETED 0
14
+ #define ATTR_TABLE_DEFAULT 16
15
+
16
+ /*
17
+ * === static functions ===
18
+ */
19
+
20
+ static shape_t *shape_init(SHPObject *objp);
21
+
22
+ /*--- lower level routines ---*/
23
+
24
+ static int FieldNameEquiv(const char *a, const char *b)
25
+ {
26
+ int i;
27
+ monitor_someday(("FNE <%s> <%s>", a, b));
28
+ for (i = 0; i < 11; i++) {
29
+ int ca, cb;
30
+ if (a[i] == '\0' && b[i] == '\0') {
31
+ return 1;
32
+ } else if (a[i] == '\0' || b[i] == '\0') {
33
+ return 0;
34
+ }
35
+ ca = (isalpha(a[i]) ? toupper(a[i]) : (a[i]));
36
+ cb = (isalpha(b[i]) ? toupper(b[i]) : (b[i]));
37
+ if (ca != cb) {
38
+ return 0;
39
+ }
40
+ }
41
+ return 1;
42
+ }
43
+
44
+ static void AttribClear(attrib_t *pa)
45
+ {
46
+ pa->type = -1;
47
+ pa->val.fval = 0.0;
48
+ memset(pa->name, '\0', DBF_NAMELEN);
49
+ }
50
+
51
+ /*--- related to struct shape_t ---*/
52
+
53
+ static int ShapeSetAttrTable(shape_t *sh, int newsize)
54
+ {
55
+ int oldsize;
56
+ int i;
57
+ if (newsize == 0) {
58
+ if (sh->nattr != 0)
59
+ rb_bug("trying to emptify attr table with %d entries", sh->nattr);
60
+ return -1;
61
+ }
62
+ if (sh->attr == NULL) {
63
+ sh->attr = xmalloc(sizeof(attrib_t) * newsize);
64
+ } else {
65
+ sh->attr = xrealloc(sh->attr, sizeof(attrib_t) * newsize);
66
+ }
67
+ oldsize = sh->nattr;
68
+ sh->nattr = newsize;
69
+ for (i = oldsize; i < newsize; i++) {
70
+ AttribClear(&(sh->attr[i]));
71
+ }
72
+ return 0;
73
+ }
74
+
75
+ /*--- related to struct shapefile_t ---*/
76
+
77
+ static shapefile_t *ShapefileInit(void)
78
+ {
79
+ shapefile_t *sfile;
80
+ sfile = xmalloc(sizeof(shapefile_t));
81
+ if (sfile == NULL)
82
+ return NULL;
83
+ sfile->nrecs = sfile->nfields = -1;
84
+ sfile->ftab = NULL;
85
+ sfile->irec = 0;
86
+ return sfile;
87
+ }
88
+
89
+ static void ShapefileBuildFieldTable(shapefile_t *sfile)
90
+ {
91
+ int i;
92
+ if (sfile->ftab)
93
+ return;
94
+ sfile->nfields = shapefile_field_count(sfile);
95
+ sfile->ftab = xmalloc(sizeof(field_t) * sfile->nfields);
96
+ if (sfile->ftab == NULL)
97
+ return;
98
+ for (i = 0; i < sfile->nfields; i++) {
99
+ memset(sfile->ftab[i].name, '\0', DBF_NAMELEN);
100
+ }
101
+ for (i = 0; i < sfile->nfields; i++) {
102
+ field_t *f;
103
+ f = &(sfile->ftab[i]);
104
+ f->type = DBFGetFieldInfo(sfile->hdbf, i, f->name, NULL, NULL);
105
+ monitor(("DBFGetFieldInfo(%p, %d) => %d, \"%s\"", sfile->hdbf, i,
106
+ (int)f->type, f->name));
107
+ }
108
+ }
109
+
110
+ static void ShapefileWriteAttribute(shapefile_t *sfile,
111
+ int irec, int ifield, attrib_t *a)
112
+ {
113
+ int s;
114
+ if (a->type == -1) {
115
+ rb_bug("supposed to be rejected in shapefile_write");
116
+ } else if (a->type == FTInteger) {
117
+ s = DBFWriteIntegerAttribute(sfile->hdbf, irec, ifield,
118
+ a->val.ival);
119
+ monitor(("DBFWriteIntegerAttribute(%p %d %d %d) -> %d",
120
+ sfile->hdbf, irec, ifield, a->val.ival, s));
121
+ } else if (a->type == FTDouble) {
122
+ s = DBFWriteDoubleAttribute(sfile->hdbf, irec, ifield,
123
+ a->val.fval);
124
+ monitor(("DBFWriteDoubleAttribute(%p %d %d %g) -> %d",
125
+ sfile->hdbf, irec, ifield, a->val.fval, s));
126
+ } else {
127
+ s = DBFWriteStringAttribute(sfile->hdbf, irec, ifield,
128
+ a->val.sval);
129
+ monitor(("DBFWriteStringAttribute(%p %d %d %s) -> %d",
130
+ sfile->hdbf, irec, ifield, a->val.sval, s));
131
+ }
132
+ if (s == 0) {
133
+ rb_raise(rb_eRuntimeError, "write error or overflow");
134
+ }
135
+ }
136
+
137
+ /*
138
+ * === methods of class ShapeFile ===
139
+ */
140
+
141
+ shapefile_t *shapefile_open(const char *filename, const char *mode)
142
+ {
143
+ shapefile_t *sfile;
144
+ sfile = ShapefileInit();
145
+ sfile->hshp = SHPOpen(filename, mode);
146
+ monitor(("SHPOpen(\"%s\", \"%s\") -> %p", filename, mode, sfile->hshp));
147
+ sfile->hdbf = DBFOpen(filename, mode);
148
+ monitor(("DBFOpen(\"%s\", \"%s\") -> %p", filename, mode, sfile->hdbf));
149
+ ShapefileBuildFieldTable(sfile);
150
+ return sfile;
151
+ }
152
+
153
+ shapefile_t *shapefile_new(const char *filename, int shptype)
154
+ {
155
+ shapefile_t *sfile;
156
+ sfile = ShapefileInit();
157
+ sfile->hshp = SHPCreate(filename, shptype);
158
+ monitor(("SHPCreate(\"%s\", %d) -> %p", filename, shptype, sfile->hshp));
159
+ sfile->hdbf = DBFCreate(filename);
160
+ monitor(("DBFCreate(\"%s\") -> %p", filename, sfile->hdbf));
161
+ return sfile;
162
+ }
163
+
164
+ int shapefile_add_field(shapefile_t *sfile, const char *name,
165
+ int atype, int width, int decimals)
166
+ {
167
+ int r;
168
+ if (sfile->hdbf == NULL) {
169
+ rb_raise(rb_eRuntimeError, "dbf closed");
170
+ return -1;
171
+ }
172
+ if (sfile->ftab) {
173
+ rb_raise(rb_eRuntimeError, "add field before you write a record");
174
+ return -1;
175
+ }
176
+ monitor(("DBFAddField(%p, %s, %d, %d, %d)", sfile->hdbf, name,
177
+ atype, width, decimals));
178
+ r = DBFAddField(sfile->hdbf, name, (DBFFieldType)atype, width, decimals);
179
+ if (r == -1) {
180
+ rb_raise(rb_eRuntimeError, "DBFAddField fail");
181
+ }
182
+ return r;
183
+ }
184
+
185
+ int shapefile_close(shapefile_t *sfile)
186
+ {
187
+ if (sfile->hshp) {
188
+ monitor(("SHPClose(%p)", sfile->hshp));
189
+ SHPClose(sfile->hshp);
190
+ sfile->hshp = NULL;
191
+ }
192
+ if (sfile->hdbf) {
193
+ monitor(("DBFClose(%p)", sfile->hdbf));
194
+ DBFClose(sfile->hdbf);
195
+ sfile->hdbf = NULL;
196
+ }
197
+ if (sfile->ftab) {
198
+ xfree(sfile->ftab);
199
+ sfile->ftab = NULL;
200
+ }
201
+ return 0;
202
+ }
203
+
204
+ int shapefile_field_decimals(shapefile_t *sfile, unsigned ifield)
205
+ {
206
+ int decimals;
207
+ if (NULL == sfile->hdbf)
208
+ return -1;
209
+ DBFGetFieldInfo(sfile->hdbf, ifield, NULL, NULL, &decimals);
210
+ return decimals;
211
+ }
212
+
213
+ int shapefile_delete_shape(shapefile_t *sfile, int ishape)
214
+ {
215
+ #if HAS_DBFMARKRECORDDELETED
216
+ int r;
217
+ if (ishape < 0) {
218
+ rb_raise(rb_eArgError, "shape number %d must be nonnegative", ishape);
219
+ }
220
+ if (sfile->hshp) {
221
+ SHPObject *shape;
222
+ double d;
223
+ shape = SHPCreateSimpleObject(SHPT_NULL, 0, &d, &d, &d);
224
+ if (shape == NULL) {
225
+ rb_raise(rb_eRuntimeError,
226
+ "SHPCreateSimpleObject(SHPT_NULL, ...)");
227
+ }
228
+ r = SHPWriteObject(sfile->hshp, ishape, shape);
229
+ if (r != ishape) {
230
+ rb_raise(rb_eRuntimeError,
231
+ "SHPWriteObject(NullObj, %d) -> %d", ishape, r);
232
+ }
233
+ SHPDestroyObject(shape);
234
+ }
235
+ if (sfile->hdbf) {
236
+ r = DBFMarkRecordDeleted(sfile->hdbf, ishape, 1);
237
+ if (r) {
238
+ rb_raise(rb_eRuntimeError,
239
+ "DBFMarkRecordDeleted %d -> %d", ishape, r);
240
+ }
241
+ }
242
+ #else
243
+ rb_raise(rb_eRuntimeError, "DBFMarkRecordDeleted is disabled");
244
+ #endif
245
+ return 0;
246
+ }
247
+
248
+ int shapefile_field_index(shapefile_t *sfile, const char *name)
249
+ {
250
+ if (NULL == sfile->hdbf)
251
+ return -1;
252
+ return DBFGetFieldIndex(sfile->hdbf, name);
253
+ }
254
+
255
+ /* WARNING: result of this function is not valid after next call.
256
+ */
257
+ const char *shapefile_field_name(shapefile_t *sfile, unsigned ifield)
258
+ {
259
+ static char buf[DBF_NAMELEN];
260
+ if (NULL == sfile->hdbf)
261
+ return NULL;
262
+ DBFGetFieldInfo(sfile->hdbf, ifield, buf, NULL, NULL);
263
+ return buf;
264
+ }
265
+
266
+ DBFFieldType shapefile_field_type(shapefile_t *sfile, unsigned ifield)
267
+ {
268
+ if (NULL == sfile->hdbf) {
269
+ return -1;
270
+ }
271
+ if (sfile->ftab) {
272
+ if (ifield >= sfile->nfields) {
273
+ return -1;
274
+ }
275
+ return sfile->ftab[ifield].type;
276
+ }
277
+ return DBFGetFieldInfo(sfile->hdbf, ifield, NULL, NULL, NULL);
278
+ }
279
+
280
+ int shapefile_field_width(shapefile_t *sfile, unsigned ifield)
281
+ {
282
+ int width;
283
+ if (NULL == sfile->hdbf)
284
+ return -1;
285
+ DBFGetFieldInfo(sfile->hdbf, ifield, NULL, &width, NULL);
286
+ return width;
287
+ }
288
+
289
+ int shapefile_field_count(shapefile_t *sfile)
290
+ {
291
+ int r;
292
+ if (NULL == sfile->hdbf) {
293
+ return -1;
294
+ }
295
+ if (sfile->ftab) {
296
+ return sfile->nfields;
297
+ }
298
+ /*
299
+ * Field table is not built here
300
+ * because it would prohibit field_add() afterwards.
301
+ */
302
+ r = DBFGetFieldCount(sfile->hdbf);
303
+ monitor(("DBFGetFieldCount(%p) -> %d", sfile->hdbf, r));
304
+ return r;
305
+ }
306
+
307
+ void shapefile_bound(shapefile_t *sfile, double *minbound, double *maxbound)
308
+ {
309
+ if (NULL == sfile->hshp) {
310
+ int i;
311
+ /*
312
+ * is that right? I guess NaN should be filled - if there is
313
+ * a portable and safe way to generate it.
314
+ */
315
+ if (minbound) { for (i = 0; i < 4; i++) minbound[i] = 0.0; }
316
+ if (maxbound) { for (i = 0; i < 4; i++) maxbound[i] = 0.0; }
317
+ return;
318
+ }
319
+ SHPGetInfo(sfile->hshp, NULL, NULL, minbound, maxbound);
320
+ }
321
+
322
+ shape_t *shapefile_read(shapefile_t *sfile, int ishape)
323
+ {
324
+ shape_t *shape;
325
+ 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
+ ShapefileBuildFieldTable(sfile);
346
+ if (NULL == sfile->hdbf)
347
+ goto no_dbf;
348
+ #if HAS_DBFMARKRECORDDELETED
349
+ if (DBFIsRecordDeleted(sfile->hdbf, ishape)) {
350
+ monitor(("DBF record %d deleted", ishape))
351
+ goto no_dbf;
352
+ }
353
+ #endif
354
+ ShapeSetAttrTable(shape, sfile->nfields);
355
+ for (i = iattr = 0; i < sfile->nfields; i++) {
356
+ attrib_t *a;
357
+ field_t *f;
358
+ if (DBFIsAttributeNULL(sfile->hdbf, ishape, i)) {
359
+ goto next_field;
360
+ }
361
+ a = &(shape->attr[iattr]);
362
+ f = &(sfile->ftab[i]);
363
+ monitor(("attrib %s type %d", f->name, f->type));
364
+ if (f->type == FTInteger) {
365
+ a->val.ival = DBFReadIntegerAttribute(sfile->hdbf, ishape, i);
366
+ } else if (f->type == FTDouble) {
367
+ a->val.fval = DBFReadDoubleAttribute(sfile->hdbf, ishape, i);
368
+ #if 0
369
+ } else if (f->type == FTLogical) {
370
+ a->val.ival = DBFReadLogicalAttribute(sfile->hdbf, ishape, i);
371
+ #endif
372
+ } else {
373
+ a->val.sval = StringDup(
374
+ DBFReadStringAttribute(sfile->hdbf, ishape, i)
375
+ );
376
+ }
377
+ a->type = f->type;
378
+ memcpy(a->name, f->name, DBF_NAMELEN);
379
+ iattr++;
380
+ next_field:
381
+ ;
382
+ }
383
+ no_dbf:
384
+ return shape;
385
+ }
386
+
387
+ int shapefile_shape_type(shapefile_t *sfile)
388
+ {
389
+ int r;
390
+ if (NULL == sfile->hshp)
391
+ return -1;
392
+ SHPGetInfo(sfile->hshp, NULL, &r, NULL, NULL);
393
+ return r;
394
+ }
395
+
396
+ int shapefile_size(shapefile_t *sfile)
397
+ {
398
+ int sd, ss;
399
+ if (NULL == sfile->hshp) {
400
+ return -1;
401
+ }
402
+ SHPGetInfo(sfile->hshp, &ss, NULL, NULL, NULL);
403
+ monitor(("SHPGetInfo(%p) -> %d", sfile->hshp, ss));
404
+ if (sfile->hdbf) {
405
+ sd = DBFGetRecordCount(sfile->hdbf);
406
+ monitor(("DBFGetRecordCount(%p) -> %d", sfile->hdbf, sd));
407
+ if (sd != ss) {
408
+ if (DBFGetFieldCount(sfile->hdbf) > 0) {
409
+ rb_warn("ShapeFile/DBF size mismatch %d %d", ss, sd);
410
+ }
411
+ }
412
+ }
413
+ sfile->nrecs = ss;
414
+ return ss;
415
+ }
416
+
417
+ int shapefile_write(shapefile_t *sfile, int irec, shape_t *shape)
418
+ {
419
+ int r;
420
+ int ifield;
421
+ int iattr;
422
+ int *usedfield;
423
+ /* precondition */
424
+ if (!sfile->hshp || !shape->obj)
425
+ return -1;
426
+ ShapefileBuildFieldTable(sfile);
427
+ /* write the object */
428
+ r = SHPWriteObject(sfile->hshp, irec, shape->obj);
429
+ monitor(("SHPWriteObject(%p, %d, %p) -> %d", sfile->hshp, irec,
430
+ shape->obj, r));
431
+ if (r < 0)
432
+ 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
+ }
438
+ for (iattr = 0; iattr < shape->nattr; iattr++) {
439
+ attrib_t *a;
440
+ a = &(shape->attr[iattr]);
441
+ if (a->type == -1) {
442
+ continue;
443
+ }
444
+ 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
+ ShapefileWriteAttribute(sfile, r, ifield, a);
450
+ usedfield[ifield] = 1;
451
+ }
452
+ for (ifield = 0; ifield < sfile->nfields; ifield++) {
453
+ int r2;
454
+ if (usedfield[ifield] == 0) {
455
+ r2 = DBFWriteNULLAttribute(sfile->hdbf, r, ifield);
456
+ monitor(("DBFWriteNULLAttribute(%p %d %d -> %d",
457
+ sfile->hdbf, r, ifield, r2));
458
+ if (r2 == 0)
459
+ rb_raise(rb_eRuntimeError, "write error");
460
+ }
461
+ }
462
+ free(usedfield);
463
+ return r;
464
+ }
465
+
466
+ static shape_t *shape_init(SHPObject *objp)
467
+ {
468
+ shape_t *sh;
469
+ if (objp == NULL)
470
+ return NULL;
471
+ sh = xmalloc(sizeof(shape_t));
472
+ if (sh == NULL) {
473
+ SHPDestroyObject(objp);
474
+ return NULL;
475
+ }
476
+ sh->obj = objp;
477
+ sh->attr = NULL;
478
+ sh->nattr = 0;
479
+ return sh;
480
+ }
481
+
482
+ shape_t *shape_new(int shape_type, int shape_id,
483
+ int n_parts, int *part_type, int *part_start, int n_vertices,
484
+ double *xvals, double *yvals, double *zvals, double *mvals)
485
+ {
486
+ SHPObject *objp;
487
+ shape_t *sh;
488
+ objp = SHPCreateObject(shape_type, shape_id,
489
+ n_parts, part_start, part_type, n_vertices,
490
+ xvals, yvals, zvals, mvals);
491
+ monitor(("SHPCreateObject(%d, %d, ...) -> %p",
492
+ shape_type, shape_id, objp));
493
+ sh = shape_init(objp);
494
+ return sh;
495
+ }
496
+
497
+ shape_t *shape_new_point(int shapetype, double x, double y, double m, double z)
498
+ {
499
+ shape_t *sh;
500
+ if (m == 0.0) {
501
+ sh = shape_init(SHPCreateSimpleObject(shapetype, 1, &x, &y, &z));
502
+ monitor(("SHPCreateSimpleObject(%d, 1 %g, %g, %g) -> %p",
503
+ shapetype, x, y, z, sh));
504
+ } else {
505
+ sh = shape_init(SHPCreateObject(shapetype,
506
+ -1, /* iShape: dummy because shapefile_write determines it */
507
+ 0, /* nParts: 0 indicates panPartStart is not given */
508
+ NULL, /* panPartStart: list of part start address */
509
+ NULL, /* panPartType: list of MultiPatch part type */
510
+ 1, /* nVertices */
511
+ &x, &y, &z, &m));
512
+ monitor(("SHPCreateObject(%d, ..., 1 %g, %g, %g, %g) -> %p",
513
+ shapetype, x, y, m, z, sh));
514
+ }
515
+ return sh;
516
+ }
517
+
518
+ shape_t *shape_new_obj(int shapetype,
519
+ int n_parts, int *part_start, int *part_type, int n_vert,
520
+ double *x, double *y, double *m, double *z)
521
+ {
522
+ shape_t *sh;
523
+ sh = shape_init(SHPCreateObject(shapetype,
524
+ -1, /* iShape: dummy because shapefile_write determines it */
525
+ n_parts, part_start, part_type, n_vert, x, y, z, m));
526
+ monitor(("SHPCreateObject(%d, -1, %d, ... %d, [%g, %g, %g, %g], ...) -> %p",
527
+ shapetype, n_parts, n_vert, x, y, m, z, sh));
528
+ return sh;
529
+ }
530
+
531
+ int shape_attrib_index(shape_t *sh, const char *key, int extend)
532
+ {
533
+ int i;
534
+ monitor(("shape_attrib_index(%p, %s)", sh, key));
535
+ if (sh->attr == NULL || sh->nattr == 0) {
536
+ if (!extend) {
537
+ return -1;
538
+ }
539
+ ShapeSetAttrTable(sh, ATTR_TABLE_DEFAULT);
540
+ }
541
+ for (i = 0; i < sh->nattr; i++) {
542
+ if (FieldNameEquiv(sh->attr[i].name, key)) {
543
+ return i;
544
+ }
545
+ }
546
+ for (i = 0; i < sh->nattr; i++) {
547
+ if (sh->attr[i].type == -1) {
548
+ strcpy(sh->attr[i].name, key);
549
+ return i;
550
+ }
551
+ }
552
+ if (extend) {
553
+ i = sh->nattr;
554
+ ShapeSetAttrTable(sh, sh->nattr * 2);
555
+ strcpy(sh->attr[i].name, key);
556
+ return i;
557
+ } else {
558
+ return -1;
559
+ }
560
+ }
561
+
562
+ int shape_close(shape_t *shape)
563
+ {
564
+ if (shape->obj) {
565
+ monitor(("SHPDestroyObject(%p)", shape->obj));
566
+ SHPDestroyObject(shape->obj);
567
+ shape->obj = NULL;
568
+ }
569
+ if (shape->attr) {
570
+ int i;
571
+ 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
+ }
582
+ }
583
+ free(shape->attr);
584
+ shape->attr = NULL;
585
+ }
586
+ xfree(shape);
587
+ return 0;
588
+ }