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