/[thuban]/branches/WIP-pyshapelib-bramz/libraries/pyshapelib/dbflibmodule.c
ViewVC logotype

Annotation of /branches/WIP-pyshapelib-bramz/libraries/pyshapelib/dbflibmodule.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2751 - (hide annotations)
Wed Mar 28 23:30:15 2007 UTC (17 years, 11 months ago) by bramz
File MIME type: text/plain
File size: 15436 byte(s)
Added support for Win32 wide character file API.  Unicode filenames are now fully supported on the windows platform: for example exotic filenames like the greek letter pi (u"\u03c0").  However, this needed unofficial modifications in the C++ shapelib library.
1 bramz 2742 #include "pyshapelib_common.h"
2 jan 1611
3 bramz 2742 /* --- DBFFile ------------------------------------------------------------------------------------------------------- */
4 jan 1611
5 bramz 2742 typedef struct {
6     PyObject_HEAD
7     DBFHandle handle;
8     } DBFFileObject;
9 jan 1611
10    
11 bh 1917
12 bramz 2742 /* allocator
13     */
14     static PyObject* dbffile_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
15 bh 1917 {
16 bramz 2742 DBFFileObject* self;
17     self = (DBFFileObject*) type->tp_alloc(type, 0);
18     self->handle = NULL;
19     return (PyObject*) self;
20     }
21 bh 1917
22 bramz 2742
23    
24     /* deallocator
25     */
26     static void dbffile_dealloc(DBFFileObject* self)
27     {
28     DBFClose(self->handle);
29     self->handle = NULL;
30     self->ob_type->tp_free((PyObject*)self);
31     }
32    
33    
34    
35     /* constructor
36     */
37     static int dbffile_init(DBFFileObject* self, PyObject* args, PyObject* kwds)
38     {
39 bramz 2751 char* file = NULL;
40 bramz 2742 char* mode = "rb";
41 bramz 2745 static char *kwlist[] = {"name", "mode", NULL};
42 bramz 2751
43     DBFClose(self->handle);
44     self->handle = NULL;
45    
46     #if defined(SHPAPI_HAS_WIDE) && defined(Py_WIN_WIDE_FILENAMES)
47     if (GetVersion() < 0x80000000) { /* On NT, so wide API available */
48     PyObject *wfile;
49     if (PyArg_ParseTupleAndKeywords(args, kwds, "U|s:DBFFile", kwlist, &wfile, &mode))
50     {
51     PyObject *wmode = PyUnicode_DecodeASCII(mode, strlen(mode), NULL);
52     if (!wmode) return -1;
53     self->handle = DBFOpenW(PyUnicode_AS_UNICODE(wfile), PyUnicode_AS_UNICODE(wmode));
54     Py_DECREF(wmode);
55     if (!self->handle)
56     {
57     PyErr_SetFromErrnoWithFilenameObject(PyExc_IOError, wfile);
58     return -1;
59     }
60     }
61     else
62     {
63     /* Drop the argument parsing error as narrow
64     strings are also valid. */
65     PyErr_Clear();
66     }
67     }
68     #endif
69    
70 bramz 2749 if (!self->handle)
71     {
72 bramz 2751 if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|s:DBFFile", kwlist,
73     Py_FileSystemDefaultEncoding, &file, &mode)) return -1;
74     self->handle = DBFOpen(file, mode);
75    
76     if (!self->handle)
77     {
78     PyErr_SetFromErrnoWithFilename(PyExc_IOError, file);
79     PyMem_Free(file);
80     return -1;
81     }
82    
83     PyMem_Free(file);
84 bramz 2749 }
85    
86 bramz 2751 return 0;
87 bramz 2742 }
88 bh 1917
89    
90    
91 bramz 2742 static PyObject* dbffile_close(DBFFileObject* self)
92 bh 1917 {
93 bramz 2742 DBFClose(self->handle);
94     self->handle = NULL;
95     Py_RETURN_NONE;
96     }
97 bh 1917
98    
99 bramz 2742
100     static PyObject* dbffile_field_count(DBFFileObject* self)
101     {
102     return PyInt_FromLong((long)DBFGetFieldCount(self->handle));
103 bh 1917 }
104    
105 jan 1611
106 bramz 2742
107     static PyObject* dbffile_record_count(DBFFileObject* self)
108 jan 1611 {
109 bramz 2742 return PyInt_FromLong((long)DBFGetRecordCount(self->handle));
110     }
111 jan 1611
112    
113 bramz 2742
114     static PyObject* dbffile_field_info(DBFFileObject* self, PyObject* args)
115     {
116     char field_name[12];
117     int field, width = 0, decimals = 0, field_type;
118 jan 1611
119 bramz 2744 if (!PyArg_ParseTuple(args, "i:field_info", &field)) return NULL;
120 bramz 2742
121     field_name[0] = '\0';
122     field_type = DBFGetFieldInfo(self->handle, field, field_name, &width, &decimals);
123    
124     return Py_BuildValue("isii", field_type, field_name, width, decimals);
125     }
126 bh 1917
127 jan 1611
128 bh 1917
129 bramz 2742 static PyObject* dbffile_add_field(DBFFileObject* self, PyObject* args)
130     {
131     char* name;
132     int type, width, decimals;
133     int field;
134    
135 bramz 2744 if (!PyArg_ParseTuple(args, "siii:add_field", &name, &type, &width, &decimals)) return NULL;
136 bramz 2742
137     field = DBFAddField(self->handle, name, (DBFFieldType)type, width, decimals);
138    
139     if (field < 0)
140     {
141     PyErr_SetString(PyExc_ValueError, "Failed to add field due to inappropriate field definition");
142     return NULL;
143     }
144     return PyInt_FromLong((long)field);
145 jan 1611 }
146    
147    
148    
149 bramz 2742 /* Read one attribute from the dbf handle and return it as a new python object
150     *
151     * If an error occurs, set the appropriate Python exception and return
152     * NULL.
153     *
154     * Assume that the values of the record and field arguments are valid.
155     * The name argument will be passed to DBFGetFieldInfo as is and should
156     * thus be either NULL or a pointer to an array of at least 12 chars
157     */
158     static PyObject* do_read_attribute(DBFHandle handle, int record, int field, char * name)
159 jan 1611 {
160 bramz 2742 int type, width;
161     const char* temp;
162     type = DBFGetFieldInfo(handle, field, name, &width, NULL);
163    
164     /* For strings NULL and the empty string are indistinguishable
165     * in DBF files. We prefer empty strings instead for backwards
166     * compatibility reasons because older wrapper versions returned
167     * emtpy strings as empty strings.
168     */
169     if (type != FTString && DBFIsAttributeNULL(handle, record, field))
170 jan 1611 {
171 bramz 2742 Py_RETURN_NONE;
172 jan 1611 }
173 bramz 2742 else
174 jan 1611 {
175 bramz 2742 switch (type)
176     {
177     case FTString:
178     temp = DBFReadStringAttribute(handle, record, field);
179 bramz 2745 if (temp) return PyString_FromString(temp);
180 jan 1611
181 bramz 2742 case FTInteger:
182     return PyInt_FromLong((long)DBFReadIntegerAttribute(handle, record, field));
183 jan 1611
184 bramz 2742 case FTDouble:
185     return PyFloat_FromDouble(DBFReadDoubleAttribute(handle, record, field));
186 bramz 2745
187     case FTLogical:
188     temp = DBFReadLogicalAttribute(handle, record, field);
189     if (temp)
190     {
191     switch (temp[0])
192     {
193     case 'F':
194     case 'N':
195     Py_RETURN_FALSE;
196     case 'T':
197     case 'Y':
198     Py_RETURN_TRUE;
199     }
200     }
201     break;
202 jan 1611
203 bramz 2742 default:
204     PyErr_Format(PyExc_TypeError, "Invalid field data type %d", type);
205     return NULL;
206     }
207 jan 1611 }
208 bramz 2745
209     PyErr_Format(PyExc_IOError, "Can't read value for row %d column %d", record, field);
210     return NULL;
211 bramz 2742 }
212 jan 1611
213    
214 bramz 2742
215     /* the read_attribute method. Return the value of the given record and
216     * field as a python object of the appropriate type.
217     */
218     static PyObject* dbffile_read_attribute(DBFFileObject* self, PyObject* args)
219 jan 1611 {
220 bramz 2742 int record, field;
221 jan 1611
222 bramz 2744 if (!PyArg_ParseTuple(args, "ii:read_field", &record, &field)) return NULL;
223 bramz 2742
224     if (record < 0 || record >= DBFGetRecordCount(self->handle))
225     {
226     PyErr_Format(PyExc_ValueError,
227     "record index %d out of bounds (record count: %d)",
228     record, DBFGetRecordCount(self->handle));
229     return NULL;
230     }
231 jan 1611
232 bramz 2742 if (field < 0 || field >= DBFGetFieldCount(self->handle))
233 jan 1611 {
234 bramz 2742 PyErr_Format(PyExc_ValueError,
235     "field index %d out of bounds (field count: %d)",
236     field, DBFGetFieldCount(self->handle));
237     return NULL;
238 jan 1611 }
239 bramz 2742
240     return do_read_attribute(self->handle, record, field, NULL);
241     }
242    
243    
244    
245     /* the read_record method. Return the record record as a dictionary with
246     * whose keys are the names of the fields, and their values as the
247     * appropriate Python type.
248     */
249     static PyObject* dbffile_read_record(DBFFileObject* self, PyObject* args)
250     {
251     int record;
252     int num_fields;
253     int i;
254     char name[12];
255     PyObject *dict;
256     PyObject *value = NULL;
257    
258 bramz 2744 if (!PyArg_ParseTuple(args, "i:read_record", &record)) return NULL;
259 bramz 2742
260     if (record < 0 || record >= DBFGetRecordCount(self->handle))
261 jan 1611 {
262 bramz 2742 PyErr_Format(PyExc_ValueError,
263     "record index %d out of bounds (record count: %d)",
264     record, DBFGetRecordCount(self->handle));
265     return NULL;
266 jan 1611 }
267 bramz 2742
268     dict = PyDict_New();
269     if (!dict) return NULL;
270    
271     num_fields = DBFGetFieldCount(self->handle);
272 jan 1611 for (i = 0; i < num_fields; i++)
273     {
274 bramz 2742 value = do_read_attribute(self->handle, record, i, name);
275     if (!value || PyDict_SetItemString(dict, name, value) < 0) goto fail;
276 jan 1611 Py_DECREF(value);
277 bramz 2742 value = NULL;
278 jan 1611 }
279    
280 bramz 2742 return dict;
281 jan 1611
282 bramz 2742 fail:
283     Py_XDECREF(value);
284     Py_DECREF(dict);
285     return NULL;
286 jan 1611 }
287    
288    
289 bramz 2742
290     /* write a single field of a record. */
291     static int do_write_field(DBFHandle handle, int record, int field, int type, PyObject* value)
292 bh 2212 {
293 bramz 2742 char * string_value;
294     int int_value;
295     double double_value;
296 bramz 2745 int logical_value;
297 bh 2212
298 bramz 2742 if (value == Py_None)
299     {
300 bramz 2745 if (DBFWriteNULLAttribute(handle, record, field)) return 1;
301 bramz 2742 }
302     else
303     {
304     switch (type)
305     {
306     case FTString:
307     string_value = PyString_AsString(value);
308     if (!string_value) return 0;
309 bramz 2745 if (DBFWriteStringAttribute(handle, record, field, string_value)) return 1;
310 bramz 2742 break;
311 bh 2212
312 bramz 2742 case FTInteger:
313     int_value = PyInt_AsLong(value);
314     if (int_value == -1 && PyErr_Occurred()) return 0;
315 bramz 2745 if (DBFWriteIntegerAttribute(handle, record, field, int_value)) return 1;
316 bramz 2742 break;
317 jan 1611
318 bramz 2742 case FTDouble:
319     double_value = PyFloat_AsDouble(value);
320     if (double_value == -1 && PyErr_Occurred()) return 0;
321 bramz 2745 if (DBFWriteDoubleAttribute(handle, record, field, double_value)) return 1;
322 bramz 2742 break;
323 bramz 2745
324     case FTLogical:
325     logical_value = PyObject_IsTrue(value);
326     if (logical_value == -1) return 0;
327     if (DBFWriteLogicalAttribute(handle, record, field, logical_value ? 'T' : 'F')) return 1;
328     break;
329 jan 1611
330 bramz 2742 default:
331     PyErr_Format(PyExc_TypeError, "Invalid field data type %d", type);
332     return 0;
333     }
334     }
335 jan 1611
336 bramz 2745 PyErr_Format(PyExc_IOError, "can't write field %d of record %d", field, record);
337     return 0;
338 bramz 2742 }
339 jan 1611
340    
341    
342 bramz 2742 static PyObject* dbffile_write_field(DBFFileObject* self, PyObject* args)
343     {
344     int record, field;
345     PyObject* value;
346     int type;
347    
348 bramz 2744 if (!PyArg_ParseTuple(args, "iiO:write_field", &record, &field, &value)) return NULL;
349 bramz 2742
350     if (field < 0 || field >= DBFGetFieldCount(self->handle))
351     {
352     PyErr_Format(PyExc_ValueError,
353     "field index %d out of bounds (field count: %d)",
354     field, DBFGetFieldCount(self->handle));
355     return NULL;
356     }
357    
358     type = DBFGetFieldInfo(self->handle, field, NULL, NULL, NULL);
359     if (!do_write_field(self->handle, record, field, type, value)) return NULL;
360     Py_RETURN_NONE;
361 jan 1611 }
362    
363    
364    
365 bramz 2742 static PyObject* dbffile_write_record(DBFFileObject* self, PyObject* args)
366     {
367     int record;
368     PyObject* record_object;
369     int i, num_fields;
370    
371     int type;
372     char name[12];
373     PyObject* value = NULL;
374    
375 bramz 2744 if (!PyArg_ParseTuple(args, "iO:write_record", &record, &record_object)) return NULL;
376 bramz 2742
377     num_fields = DBFGetFieldCount(self->handle);
378    
379     /* mimic ShapeFile functionality where id = -1 means appending */
380     if (record == -1)
381     {
382     record = num_fields;
383     }
384 jan 1611
385 bramz 2742 if (PySequence_Check(record_object))
386     {
387     /* It's a sequence object. Iterate through all items in the
388     * sequence and write them to the appropriate field.
389     */
390     if (PySequence_Length(record_object) != num_fields)
391     {
392     PyErr_SetString(PyExc_TypeError, "record must have one item for each field");
393     return NULL;
394     }
395     for (i = 0; i < num_fields; ++i)
396     {
397     type = DBFGetFieldInfo(self->handle, i, NULL, NULL, NULL);
398     value = PySequence_GetItem(record_object, i);
399     if (!value) return NULL;
400     if (!do_write_field(self->handle, record, i, type, value))
401     {
402     Py_DECREF(value);
403     return NULL;
404     }
405     Py_DECREF(value);
406     }
407     }
408     else
409     {
410     /* It's a dictionary-like object. Iterate over the names of the
411     * known fields and write the corresponding item
412     */
413     for (i = 0; i < num_fields; ++i)
414     {
415     name[0] = '\0';
416     type = DBFGetFieldInfo(self->handle, i, name, NULL, NULL);
417     value = PyDict_GetItemString(record_object, name);
418     if (value && !do_write_field(self->handle, record, i, type, value)) return NULL;
419     }
420     }
421    
422     return PyInt_FromLong((long)record);
423 bh 1761 }
424 jan 1611
425    
426    
427 bramz 2742 static PyObject* dbffile_repr(DBFFileObject* self)
428     {
429     /* TODO: it would be nice to do something like "dbflib.DBFFile(filename, mode)" instead */
430     return PyString_FromFormat("<dbflib.DBFFile object at %p>", self->handle);
431 jan 1611 }
432    
433    
434    
435 bramz 2742 /* The commit method implementation
436     *
437     * The method relies on the DBFUpdateHeader method which is not
438     * available in shapelib <= 1.2.10. setup.py defines
439     * HAVE_UPDATE_HEADER's value depending on whether the function is
440     * available in the shapelib version the code is compiled with.
441     */
442     #if HAVE_UPDATE_HEADER
443     static PyObject* dbffile_commit(DBFFileObject* self)
444 jan 1611 {
445 bramz 2742 DBFUpdateHeader(self->handle);
446     Py_RETURN_NONE;
447     }
448     #endif
449 jan 1611
450    
451    
452 bramz 2742 static struct PyMethodDef dbffile_methods[] =
453     {
454 bramz 2744 {"close", (PyCFunction)dbffile_close, METH_NOARGS,
455 bramz 2745 "close() -> None\n\n"
456     "closes DBFFile"},
457 bramz 2744 {"field_count", (PyCFunction)dbffile_field_count, METH_NOARGS,
458 bramz 2745 "field_count() -> integer\n\n"
459 bramz 2744 "returns number of fields currently defined"},
460     {"record_count", (PyCFunction)dbffile_record_count, METH_NOARGS,
461 bramz 2745 "record_count() -> integer\n\n"
462 bramz 2744 "returns number of records that currently exist"},
463     {"field_info", (PyCFunction)dbffile_field_info, METH_VARARGS,
464 bramz 2745 "field_info(field_index) -> (type, name, width, decimals)\n\n"
465     "returns info of a field as a tuple with:\n"
466     "- type: the type of the field corresponding to the integer value of one "
467     " of the constants FTString, FTInteger, ...\n"
468     "- name: the name of the field as a string\n"
469     "- width: the width of the field as a number of characters\n"
470     "- decimals: the number of decimal digits" },
471 bramz 2742 {"add_field", (PyCFunction)dbffile_add_field, METH_VARARGS,
472 bramz 2745 "add_field(type, name, width, decimals) -> field_index\n\n"
473 bramz 2742 "adds a new field and returns field index if successful\n"
474 bramz 2745 "- type: the type of the field corresponding to the integer value of one "
475     " of the constants FTString, FTInteger, ...\n"
476     "- name: the name of the field as a string\n"
477     "- width: the width of the field as a number of characters\n"
478     "- decimals: the number of decimal digits" },
479 bramz 2744 {"read_attribute", (PyCFunction)dbffile_read_attribute, METH_VARARGS,
480 bramz 2745 "read_attribute(record_index, field_index) -> value\n\n"
481     "returns the value of one field of a record"},
482 bramz 2744 {"read_record", (PyCFunction)dbffile_read_record, METH_VARARGS,
483 bramz 2745 "read_record(record_index) -> dict\n\n"
484     "returns an entire record as a dictionary of field names and values"},
485 bramz 2744 {"write_field", (PyCFunction)dbffile_write_field, METH_VARARGS,
486     "write_field(record_index, field_index, new_value)\n"
487 bramz 2745 "writes a single field of a record"},
488 bramz 2744 {"write_record", (PyCFunction)dbffile_write_record, METH_VARARGS,
489 bramz 2745 "write_record(record_index, record) -> record_index\n\n"
490     "Writes an entire record as a dict or a sequence, and return index of record\n"
491     "Record can either be a dictionary in which case the keys are used as field names, "
492 bramz 2744 "or a sequence that must have an item for every field (length = field_count())"},
493 bramz 2742 #if HAVE_UPDATE_HEADER
494 bramz 2750 {"commit", (PyCFunction)dbffile_commit, METH_NOARGS,
495 bramz 2745 "commit() -> None"},
496 bramz 2742 #endif
497     {NULL}
498     };
499 jan 1611
500    
501 bh 1917
502 bramz 2742 static struct PyGetSetDef dbffile_getsetters[] =
503     {
504     {NULL}
505     };
506 jan 1611
507    
508    
509 bramz 2742 static PyTypeObject DBFFileType = PYSHAPELIB_DEFINE_TYPE(DBFFileObject, dbffile, "shapelib.DBFFile", 0);
510 bh 2453
511    
512 jan 1611
513 bramz 2742 /* --- dbflib -------------------------------------------------------------------------------------------------------- */
514 jan 1611
515 bramz 2742 static PyObject* dbflib_open(PyObject* module, PyObject* args)
516     {
517     return PyObject_CallObject((PyObject*)&DBFFileType, args);
518     }
519 jan 1611
520    
521    
522 bramz 2742 static PyObject* dbflib_create(PyObject* module, PyObject* args)
523     {
524     char* file;
525     DBFFileObject* result;
526 bramz 2751 DBFHandle handle = NULL;
527     int wideargument = 0;
528    
529     #if defined(SHPAPI_HAS_WIDE) && defined(Py_WIN_WIDE_FILENAMES)
530     if (GetVersion() < 0x80000000) { /* On NT, so wide API available */
531     PyObject *wfile;
532     if (PyArg_ParseTuple(args, "U:create", &wfile))
533     {
534     wideargument = 1;
535     handle = DBFCreateW(PyUnicode_AS_UNICODE(wfile));
536     if (!handle)
537     {
538     PyErr_SetFromErrnoWithFilenameObject(PyExc_IOError, wfile);
539     return NULL;
540     }
541     }
542     else
543     {
544     /* Drop the argument parsing error as narrow
545     strings are also valid. */
546     PyErr_Clear();
547     }
548     }
549     #endif
550 bramz 2742
551 bramz 2751 if (!handle)
552     {
553     if (!PyArg_ParseTuple(args, "et:create", Py_FileSystemDefaultEncoding, &file)) return NULL;
554     handle = DBFCreate(file);
555     if (!handle)
556     {
557     PyErr_SetFromErrnoWithFilename(PyExc_IOError, file);
558     PyMem_Free(file);
559     return NULL;
560     }
561     PyMem_Free(file);
562     }
563    
564 bramz 2742 result = PyObject_New(DBFFileObject, &DBFFileType);
565     if (!result)
566     {
567 bramz 2751 DBFClose(handle);
568 bramz 2742 return PyErr_NoMemory();
569     }
570    
571 bramz 2751 result->handle = handle;
572 bramz 2742 return (PyObject*) result;
573     }
574 jan 1611
575    
576    
577 bramz 2742 static struct PyMethodDef dbflib_methods[] =
578     {
579 bramz 2744 {"open", (PyCFunction)dbflib_open, METH_VARARGS,
580 bramz 2745 "open(name [, mode]) -> DBFFile\n\n"
581     "opens a DBFFile" },
582 bramz 2744 {"create", (PyCFunction)dbflib_create, METH_VARARGS,
583 bramz 2745 "create(name) -> DBFFile\n\n"
584 bramz 2744 "create a DBFFile" },
585 bramz 2742 {NULL}
586     };
587 jan 1611
588 bh 2212
589    
590 bramz 2742 PyMODINIT_FUNC initdbflib(void)
591     {
592     PyObject* module = Py_InitModule("dbflib", dbflib_methods);
593     if (!module) return;
594    
595     PYSHAPELIB_ADD_TYPE(DBFFileType, "DBFFile");
596    
597     PYSHAPELIB_ADD_CONSTANT(FTString);
598     PYSHAPELIB_ADD_CONSTANT(FTInteger);
599     PYSHAPELIB_ADD_CONSTANT(FTDouble);
600 bramz 2745 PYSHAPELIB_ADD_CONSTANT(FTLogical);
601 bramz 2742 PYSHAPELIB_ADD_CONSTANT(FTInvalid);
602     PyModule_AddIntConstant(module, "_have_commit", HAVE_UPDATE_HEADER);
603     }

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26