1
2 /*****************************************************************************
3 *
4 * MODULE: DBF driver
5 *
6 * AUTHOR(S): Radim Blazek
7 *
8 * PURPOSE: Simple driver for reading and writing dbf files
9 *
10 * COPYRIGHT: (C) 2000 by the GRASS Development Team
11 *
12 * This program is free software under the GNU General Public
13 * License (>=v2). Read the file COPYING that comes with GRASS
14 * for details.
15 *
16 * DBF API: http://shapelib.maptools.org/dbf_api.html
17 *
18 * DBFFieldType: FTString, FTInteger, FTDouble, FTLogical, FTInvalid
19 * 0 1 2 4 5
20 * DBF_CHAR DBF_INT DBF_DOUBLE
21 * 1 2 3
22 *****************************************************************************/
23 #ifdef __MINGW32__
24 # include <windows.h>
25 #endif
26
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <dirent.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <unistd.h>
34 #include <grass/dbmi.h>
35 #include <grass/shapefil.h>
36 #include <grass/gis.h>
37 #include <grass/glocale.h>
38 #include "globals.h"
39 #include "proto.h"
40
41 /* add table to database */
add_table(char * table,char * name)42 int add_table(char *table, char *name)
43 {
44 G_debug(2, "add_table(): table = %s name = %s", table, name);
45
46 if (db.atables == db.ntables) {
47 db.atables += 15;
48 db.tables =
49 (TABLE *) G_realloc(db.tables, db.atables * sizeof(TABLE));
50 }
51
52 strcpy(db.tables[db.ntables].name, table);
53
54 #ifdef __MINGW32__
55 sprintf(db.tables[db.ntables].file, "%s\\%s", db.name, name);
56 #else
57 sprintf(db.tables[db.ntables].file, "%s/%s", db.name, name);
58 #endif
59
60 db.tables[db.ntables].alive = TRUE;
61 db.tables[db.ntables].described = FALSE;
62 db.tables[db.ntables].loaded = FALSE;
63 db.tables[db.ntables].updated = FALSE;
64 db.tables[db.ntables].cols = NULL;
65 db.tables[db.ntables].rows = NULL;
66 db.tables[db.ntables].acols = 0;
67 db.tables[db.ntables].ncols = 0;
68 db.tables[db.ntables].arows = 0;
69 db.tables[db.ntables].nrows = 0;
70
71 db.ntables++;
72
73 return DB_OK;
74 }
75
76
77 /* returns table index or -1 */
find_table(char * table)78 int find_table(char *table)
79 {
80 int i;
81
82 G_debug(2, "find_table(): table = %s", table);
83
84 for (i = 0; i < db.ntables; i++) {
85 G_debug(2, " ? %s", db.tables[i].name);
86 if (G_strcasecmp(db.tables[i].name, table) == 0)
87 return (i);
88 }
89
90 return (-1);
91 }
92
load_table_head(int t)93 int load_table_head(int t)
94 {
95 int i, ncol, dtype, type, width, decimals;
96 DBFHandle dbf;
97 char fname[20];
98
99 G_debug(2, "load_table_head(): tab = %d, %s", t, db.tables[t].file);
100
101 if (db.tables[t].described == TRUE) /*already described */
102 return DB_OK;
103
104 if (access(db.tables[t].file, R_OK) == 0)
105 db.tables[t].read = TRUE;
106 else
107 db.tables[t].read = FALSE;
108
109 if (access(db.tables[t].file, W_OK) == 0)
110 db.tables[t].write = TRUE;
111 else
112 db.tables[t].write = FALSE;
113
114 /* load */
115 dbf = DBFOpen(db.tables[t].file, "r");
116 if (dbf == NULL) {
117 db_d_append_error(_("Unable to open DBF file."));
118 return DB_FAILED;
119 }
120
121 ncol = DBFGetFieldCount(dbf);
122 G_debug(2, " ncols = %d", ncol);
123
124 for (i = 0; i < ncol; i++) {
125 dtype = DBFGetFieldInfo(dbf, i, fname, &width, &decimals);
126 G_debug(2, " DBFFieldType %d", dtype);
127
128 switch (dtype) {
129 case FTString:
130 type = DBF_CHAR;
131 break;
132 case FTInteger:
133 type = DBF_INT;
134 break;
135 case FTDouble:
136 type = DBF_DOUBLE;
137 break;
138 case FTInvalid:
139 G_warning("invalid/unsupported DBFFieldType");
140 break;
141 default:
142 G_warning("unknown DBFFieldType");
143 break;
144 }
145
146 add_column(t, type, fname, width, decimals);
147 }
148
149 DBFClose(dbf);
150 db.tables[t].described = TRUE;
151
152 return DB_OK;
153 }
154
load_table(int t)155 int load_table(int t)
156 {
157 int i, j, ncols, nrows, dbfcol;
158 DBFHandle dbf;
159 char *buf;
160 ROW *rows;
161 VALUE *val;
162
163 G_debug(2, "load_table(): tab = %d", t);
164
165 if (db.tables[t].loaded == TRUE) /*already loaded */
166 return DB_OK;
167
168 dbf = DBFOpen(db.tables[t].file, "r");
169 if (dbf == NULL) {
170 db_d_append_error(_("Unable to open DBF file."));
171 return DB_FAILED;
172 }
173
174 ncols = db.tables[t].ncols;
175 nrows = DBFGetRecordCount(dbf);
176 rows = db.tables[t].rows;
177 rows = (ROW *) G_malloc(nrows * sizeof(ROW));
178 db.tables[t].arows = nrows;
179
180 G_debug(2, " ncols = %d nrows = %d", ncols, nrows);
181
182 for (i = 0; i < nrows; i++) {
183 rows[i].alive = TRUE;
184 rows[i].values = (VALUE *) G_calloc(ncols, sizeof(VALUE));
185
186 for (j = 0; j < ncols; j++) {
187 val = &(rows[i].values[j]);
188
189 dbfcol = j;
190
191 val->is_null = DBFIsAttributeNULL(dbf, i, dbfcol);
192 if (!(val->is_null)) {
193 switch (db.tables[t].cols[j].type) {
194 case DBF_INT:
195 val->i = DBFReadIntegerAttribute(dbf, i, dbfcol);
196 break;
197 case DBF_CHAR:
198 buf = (char *)DBFReadStringAttribute(dbf, i, dbfcol);
199 save_string(val, buf);
200 break;
201 case DBF_DOUBLE:
202 val->d = DBFReadDoubleAttribute(dbf, i, dbfcol);
203 break;
204 }
205 }
206 }
207 }
208
209 DBFClose(dbf);
210
211 db.tables[t].rows = rows;
212 db.tables[t].nrows = nrows;
213 db.tables[t].loaded = TRUE;
214
215 return DB_OK;
216 }
217
save_table(int t)218 int save_table(int t)
219 {
220 int i, j, ncols, nrows, ret, field, rec;
221 char name[2000], fname[20], element[100];
222 DBFHandle dbf;
223 ROW *rows;
224 VALUE *val;
225 int dbftype, width, decimals;
226
227 G_debug(2, "save_table %d", t);
228
229 /* Note: because if driver is killed during the time the table is written, the process
230 * is not completed and DATA ARE LOST. To minimize this, data are first written
231 * to temporary file and then this file is renamed to 'database/table.dbf'.
232 * Hopefully both file are on the same disk/partition */
233
234 if (!(db.tables[t].alive) || !(db.tables[t].updated))
235 return DB_OK;
236
237 /* Construct our temp name because shapelib doesn't like '.' in name */
238 G_temp_element(element);
239 sprintf(fname, "%d.dbf", getpid());
240 G_file_name(name, element, fname, G_mapset());
241 G_debug(2, "Write table to tempfile: '%s'", name);
242
243 dbf = DBFCreate(name);
244 if (dbf == NULL)
245 return DB_FAILED;
246
247 ncols = db.tables[t].ncols;
248 rows = db.tables[t].rows;
249 nrows = db.tables[t].nrows;
250
251 for (i = 0; i < ncols; i++) {
252 switch (db.tables[t].cols[i].type) {
253 case DBF_INT:
254 dbftype = FTInteger;
255 break;
256 case DBF_CHAR:
257 dbftype = FTString;
258 break;
259 case DBF_DOUBLE:
260 dbftype = FTDouble;
261 break;
262 default:
263 G_warning("invalid/unsupported DBFFieldType");
264 break;
265 }
266
267 width = db.tables[t].cols[i].width;
268 decimals = db.tables[t].cols[i].decimals;
269 DBFAddField(dbf, db.tables[t].cols[i].name, dbftype, width, decimals);
270
271 }
272
273 G_debug(2, "Write %d rows", nrows);
274 rec = 0;
275 for (i = 0; i < nrows; i++) {
276 if (rows[i].alive == FALSE)
277 continue;
278
279 for (j = 0; j < ncols; j++) {
280 field = j;
281
282 val = &(rows[i].values[j]);
283 if (val->is_null) {
284 DBFWriteNULLAttribute(dbf, rec, field);
285 }
286 else {
287 switch (db.tables[t].cols[j].type) {
288 case DBF_INT:
289 ret = DBFWriteIntegerAttribute(dbf, rec, field, val->i);
290 break;
291 case DBF_CHAR:
292 if (val->c != NULL)
293 ret =
294 DBFWriteStringAttribute(dbf, rec, field, val->c);
295 else
296 ret = DBFWriteStringAttribute(dbf, rec, field, "");
297 break;
298 case DBF_DOUBLE:
299 ret = DBFWriteDoubleAttribute(dbf, rec, field, val->d);
300 break;
301 }
302 }
303 }
304 rec++;
305 }
306 G_debug(2, "Written %d records", rec);
307
308 DBFClose(dbf);
309
310 /* Copy */
311 if (G_rename_file(name, db.tables[t].file)) {
312 db_d_append_error(_("Unable to move '%s' to '%s'."),
313 name, db.tables[t].file);
314 return DB_FAILED;
315 };
316
317 return DB_OK;
318 }
319
free_table(int tab)320 int free_table(int tab)
321 {
322 int i, j;
323
324 for (i = 0; i < db.tables[tab].nrows; i++) {
325 for (j = 0; j < db.tables[tab].ncols; j++) {
326 if (db.tables[tab].cols[j].type == DBF_CHAR &&
327 db.tables[tab].rows[i].values[j].c != NULL) {
328 G_free(db.tables[tab].rows[i].values[j].c);
329 }
330 }
331 G_free(db.tables[tab].rows[i].values);
332 }
333
334 G_free(db.tables[tab].rows);
335
336 return DB_OK;
337 }
338