alpar@1
|
1 |
/* glpmpl06.c */
|
alpar@1
|
2 |
|
alpar@1
|
3 |
/***********************************************************************
|
alpar@1
|
4 |
* This code is part of GLPK (GNU Linear Programming Kit).
|
alpar@1
|
5 |
*
|
alpar@1
|
6 |
* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
|
alpar@1
|
7 |
* 2009, 2010 Andrew Makhorin, Department for Applied Informatics,
|
alpar@1
|
8 |
* Moscow Aviation Institute, Moscow, Russia. All rights reserved.
|
alpar@1
|
9 |
* E-mail: <mao@gnu.org>.
|
alpar@1
|
10 |
*
|
alpar@1
|
11 |
* GLPK is free software: you can redistribute it and/or modify it
|
alpar@1
|
12 |
* under the terms of the GNU General Public License as published by
|
alpar@1
|
13 |
* the Free Software Foundation, either version 3 of the License, or
|
alpar@1
|
14 |
* (at your option) any later version.
|
alpar@1
|
15 |
*
|
alpar@1
|
16 |
* GLPK is distributed in the hope that it will be useful, but WITHOUT
|
alpar@1
|
17 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
alpar@1
|
18 |
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
alpar@1
|
19 |
* License for more details.
|
alpar@1
|
20 |
*
|
alpar@1
|
21 |
* You should have received a copy of the GNU General Public License
|
alpar@1
|
22 |
* along with GLPK. If not, see <http://www.gnu.org/licenses/>.
|
alpar@1
|
23 |
***********************************************************************/
|
alpar@1
|
24 |
|
alpar@1
|
25 |
#define _GLPSTD_ERRNO
|
alpar@1
|
26 |
#define _GLPSTD_STDIO
|
alpar@1
|
27 |
#include "glpmpl.h"
|
alpar@1
|
28 |
#include "glpsql.h"
|
alpar@1
|
29 |
|
alpar@1
|
30 |
/**********************************************************************/
|
alpar@1
|
31 |
|
alpar@1
|
32 |
#define CSV_FIELD_MAX 50
|
alpar@1
|
33 |
/* maximal number of fields in record */
|
alpar@1
|
34 |
|
alpar@1
|
35 |
#define CSV_FDLEN_MAX 100
|
alpar@1
|
36 |
/* maximal field length */
|
alpar@1
|
37 |
|
alpar@1
|
38 |
struct csv
|
alpar@1
|
39 |
{ /* comma-separated values file */
|
alpar@1
|
40 |
int mode;
|
alpar@1
|
41 |
/* 'R' = reading; 'W' = writing */
|
alpar@1
|
42 |
char *fname;
|
alpar@1
|
43 |
/* name of csv file */
|
alpar@1
|
44 |
FILE *fp;
|
alpar@1
|
45 |
/* stream assigned to csv file */
|
alpar@1
|
46 |
jmp_buf jump;
|
alpar@1
|
47 |
/* address for non-local go to in case of error */
|
alpar@1
|
48 |
int count;
|
alpar@1
|
49 |
/* record count */
|
alpar@1
|
50 |
/*--------------------------------------------------------------*/
|
alpar@1
|
51 |
/* used only for input csv file */
|
alpar@1
|
52 |
int c;
|
alpar@1
|
53 |
/* current character or EOF */
|
alpar@1
|
54 |
int what;
|
alpar@1
|
55 |
/* current marker: */
|
alpar@1
|
56 |
#define CSV_EOF 0 /* end-of-file */
|
alpar@1
|
57 |
#define CSV_EOR 1 /* end-of-record */
|
alpar@1
|
58 |
#define CSV_NUM 2 /* floating-point number */
|
alpar@1
|
59 |
#define CSV_STR 3 /* character string */
|
alpar@1
|
60 |
char field[CSV_FDLEN_MAX+1];
|
alpar@1
|
61 |
/* current field just read */
|
alpar@1
|
62 |
int nf;
|
alpar@1
|
63 |
/* number of fields in the csv file */
|
alpar@1
|
64 |
int ref[1+CSV_FIELD_MAX];
|
alpar@1
|
65 |
/* ref[k] = k', if k-th field of the csv file corresponds to
|
alpar@1
|
66 |
k'-th field in the table statement; if ref[k] = 0, k-th field
|
alpar@1
|
67 |
of the csv file is ignored */
|
alpar@1
|
68 |
#if 1 /* 01/VI-2010 */
|
alpar@1
|
69 |
int nskip;
|
alpar@1
|
70 |
/* number of comment records preceding the header record */
|
alpar@1
|
71 |
#endif
|
alpar@1
|
72 |
};
|
alpar@1
|
73 |
|
alpar@1
|
74 |
#undef read_char
|
alpar@1
|
75 |
|
alpar@1
|
76 |
static void read_char(struct csv *csv)
|
alpar@1
|
77 |
{ /* read character from csv data file */
|
alpar@1
|
78 |
int c;
|
alpar@1
|
79 |
xassert(csv->c != EOF);
|
alpar@1
|
80 |
if (csv->c == '\n') csv->count++;
|
alpar@1
|
81 |
loop: c = fgetc(csv->fp);
|
alpar@1
|
82 |
if (ferror(csv->fp))
|
alpar@1
|
83 |
{ xprintf("%s:%d: read error - %s\n", csv->fname, csv->count,
|
alpar@1
|
84 |
strerror(errno));
|
alpar@1
|
85 |
longjmp(csv->jump, 0);
|
alpar@1
|
86 |
}
|
alpar@1
|
87 |
if (feof(csv->fp))
|
alpar@1
|
88 |
{ if (csv->c == '\n')
|
alpar@1
|
89 |
{ csv->count--;
|
alpar@1
|
90 |
c = EOF;
|
alpar@1
|
91 |
}
|
alpar@1
|
92 |
else
|
alpar@1
|
93 |
{ xprintf("%s:%d: warning: missing final end-of-line\n",
|
alpar@1
|
94 |
csv->fname, csv->count);
|
alpar@1
|
95 |
c = '\n';
|
alpar@1
|
96 |
}
|
alpar@1
|
97 |
}
|
alpar@1
|
98 |
else if (c == '\r')
|
alpar@1
|
99 |
goto loop;
|
alpar@1
|
100 |
else if (c == '\n')
|
alpar@1
|
101 |
;
|
alpar@1
|
102 |
else if (iscntrl(c))
|
alpar@1
|
103 |
{ xprintf("%s:%d: invalid control character 0x%02X\n",
|
alpar@1
|
104 |
csv->fname, csv->count, c);
|
alpar@1
|
105 |
longjmp(csv->jump, 0);
|
alpar@1
|
106 |
}
|
alpar@1
|
107 |
csv->c = c;
|
alpar@1
|
108 |
return;
|
alpar@1
|
109 |
}
|
alpar@1
|
110 |
|
alpar@1
|
111 |
static void read_field(struct csv *csv)
|
alpar@1
|
112 |
{ /* read field from csv data file */
|
alpar@1
|
113 |
/* check for end of file */
|
alpar@1
|
114 |
if (csv->c == EOF)
|
alpar@1
|
115 |
{ csv->what = CSV_EOF;
|
alpar@1
|
116 |
strcpy(csv->field, "EOF");
|
alpar@1
|
117 |
goto done;
|
alpar@1
|
118 |
}
|
alpar@1
|
119 |
/* check for end of record */
|
alpar@1
|
120 |
if (csv->c == '\n')
|
alpar@1
|
121 |
{ csv->what = CSV_EOR;
|
alpar@1
|
122 |
strcpy(csv->field, "EOR");
|
alpar@1
|
123 |
read_char(csv);
|
alpar@1
|
124 |
if (csv->c == ',')
|
alpar@1
|
125 |
err1: { xprintf("%s:%d: empty field not allowed\n", csv->fname,
|
alpar@1
|
126 |
csv->count);
|
alpar@1
|
127 |
longjmp(csv->jump, 0);
|
alpar@1
|
128 |
}
|
alpar@1
|
129 |
if (csv->c == '\n')
|
alpar@1
|
130 |
{ xprintf("%s:%d: empty record not allowed\n", csv->fname,
|
alpar@1
|
131 |
csv->count);
|
alpar@1
|
132 |
longjmp(csv->jump, 0);
|
alpar@1
|
133 |
}
|
alpar@1
|
134 |
#if 1 /* 01/VI-2010 */
|
alpar@1
|
135 |
/* skip comment records; may appear only before the very first
|
alpar@1
|
136 |
record containing field names */
|
alpar@1
|
137 |
if (csv->c == '#' && csv->count == 1)
|
alpar@1
|
138 |
{ while (csv->c == '#')
|
alpar@1
|
139 |
{ while (csv->c != '\n')
|
alpar@1
|
140 |
read_char(csv);
|
alpar@1
|
141 |
read_char(csv);
|
alpar@1
|
142 |
csv->nskip++;
|
alpar@1
|
143 |
}
|
alpar@1
|
144 |
}
|
alpar@1
|
145 |
#endif
|
alpar@1
|
146 |
goto done;
|
alpar@1
|
147 |
}
|
alpar@1
|
148 |
/* skip comma before next field */
|
alpar@1
|
149 |
if (csv->c == ',')
|
alpar@1
|
150 |
read_char(csv);
|
alpar@1
|
151 |
/* read field */
|
alpar@1
|
152 |
if (csv->c == '\'' || csv->c == '"')
|
alpar@1
|
153 |
{ /* read a field enclosed in quotes */
|
alpar@1
|
154 |
int quote = csv->c, len = 0;
|
alpar@1
|
155 |
csv->what = CSV_STR;
|
alpar@1
|
156 |
/* skip opening quote */
|
alpar@1
|
157 |
read_char(csv);
|
alpar@1
|
158 |
/* read field characters within quotes */
|
alpar@1
|
159 |
for (;;)
|
alpar@1
|
160 |
{ /* check for closing quote and read it */
|
alpar@1
|
161 |
if (csv->c == quote)
|
alpar@1
|
162 |
{ read_char(csv);
|
alpar@1
|
163 |
if (csv->c == quote)
|
alpar@1
|
164 |
;
|
alpar@1
|
165 |
else if (csv->c == ',' || csv->c == '\n')
|
alpar@1
|
166 |
break;
|
alpar@1
|
167 |
else
|
alpar@1
|
168 |
{ xprintf("%s:%d: invalid field\n", csv->fname,
|
alpar@1
|
169 |
csv->count);
|
alpar@1
|
170 |
longjmp(csv->jump, 0);
|
alpar@1
|
171 |
}
|
alpar@1
|
172 |
}
|
alpar@1
|
173 |
/* check the current field length */
|
alpar@1
|
174 |
if (len == CSV_FDLEN_MAX)
|
alpar@1
|
175 |
err2: { xprintf("%s:%d: field too long\n", csv->fname,
|
alpar@1
|
176 |
csv->count);
|
alpar@1
|
177 |
longjmp(csv->jump, 0);
|
alpar@1
|
178 |
}
|
alpar@1
|
179 |
/* add the current character to the field */
|
alpar@1
|
180 |
csv->field[len++] = (char)csv->c;
|
alpar@1
|
181 |
/* read the next character */
|
alpar@1
|
182 |
read_char(csv);
|
alpar@1
|
183 |
}
|
alpar@1
|
184 |
/* the field has been read */
|
alpar@1
|
185 |
if (len == 0) goto err1;
|
alpar@1
|
186 |
csv->field[len] = '\0';
|
alpar@1
|
187 |
}
|
alpar@1
|
188 |
else
|
alpar@1
|
189 |
{ /* read a field not enclosed in quotes */
|
alpar@1
|
190 |
int len = 0;
|
alpar@1
|
191 |
double temp;
|
alpar@1
|
192 |
csv->what = CSV_NUM;
|
alpar@1
|
193 |
while (!(csv->c == ',' || csv->c == '\n'))
|
alpar@1
|
194 |
{ /* quotes within the field are not allowed */
|
alpar@1
|
195 |
if (csv->c == '\'' || csv->c == '"')
|
alpar@1
|
196 |
{ xprintf("%s:%d: invalid use of single or double quote wi"
|
alpar@1
|
197 |
"thin field\n", csv->fname, csv->count);
|
alpar@1
|
198 |
longjmp(csv->jump, 0);
|
alpar@1
|
199 |
}
|
alpar@1
|
200 |
/* check the current field length */
|
alpar@1
|
201 |
if (len == CSV_FDLEN_MAX) goto err2;
|
alpar@1
|
202 |
/* add the current character to the field */
|
alpar@1
|
203 |
csv->field[len++] = (char)csv->c;
|
alpar@1
|
204 |
/* read the next character */
|
alpar@1
|
205 |
read_char(csv);
|
alpar@1
|
206 |
}
|
alpar@1
|
207 |
/* the field has been read */
|
alpar@1
|
208 |
if (len == 0) goto err1;
|
alpar@1
|
209 |
csv->field[len] = '\0';
|
alpar@1
|
210 |
/* check the field type */
|
alpar@1
|
211 |
if (str2num(csv->field, &temp)) csv->what = CSV_STR;
|
alpar@1
|
212 |
}
|
alpar@1
|
213 |
done: return;
|
alpar@1
|
214 |
}
|
alpar@1
|
215 |
|
alpar@1
|
216 |
static struct csv *csv_open_file(TABDCA *dca, int mode)
|
alpar@1
|
217 |
{ /* open csv data file */
|
alpar@1
|
218 |
struct csv *csv;
|
alpar@1
|
219 |
/* create control structure */
|
alpar@1
|
220 |
csv = xmalloc(sizeof(struct csv));
|
alpar@1
|
221 |
csv->mode = mode;
|
alpar@1
|
222 |
csv->fname = NULL;
|
alpar@1
|
223 |
csv->fp = NULL;
|
alpar@1
|
224 |
if (setjmp(csv->jump)) goto fail;
|
alpar@1
|
225 |
csv->count = 0;
|
alpar@1
|
226 |
csv->c = '\n';
|
alpar@1
|
227 |
csv->what = 0;
|
alpar@1
|
228 |
csv->field[0] = '\0';
|
alpar@1
|
229 |
csv->nf = 0;
|
alpar@1
|
230 |
/* try to open the csv data file */
|
alpar@1
|
231 |
if (mpl_tab_num_args(dca) < 2)
|
alpar@1
|
232 |
{ xprintf("csv_driver: file name not specified\n");
|
alpar@1
|
233 |
longjmp(csv->jump, 0);
|
alpar@1
|
234 |
}
|
alpar@1
|
235 |
csv->fname = xmalloc(strlen(mpl_tab_get_arg(dca, 2))+1);
|
alpar@1
|
236 |
strcpy(csv->fname, mpl_tab_get_arg(dca, 2));
|
alpar@1
|
237 |
if (mode == 'R')
|
alpar@1
|
238 |
{ /* open the file for reading */
|
alpar@1
|
239 |
int k;
|
alpar@1
|
240 |
csv->fp = fopen(csv->fname, "r");
|
alpar@1
|
241 |
if (csv->fp == NULL)
|
alpar@1
|
242 |
{ xprintf("csv_driver: unable to open %s - %s\n",
|
alpar@1
|
243 |
csv->fname, strerror(errno));
|
alpar@1
|
244 |
longjmp(csv->jump, 0);
|
alpar@1
|
245 |
}
|
alpar@1
|
246 |
#if 1 /* 01/VI-2010 */
|
alpar@1
|
247 |
csv->nskip = 0;
|
alpar@1
|
248 |
#endif
|
alpar@1
|
249 |
/* skip fake new-line */
|
alpar@1
|
250 |
read_field(csv);
|
alpar@1
|
251 |
xassert(csv->what == CSV_EOR);
|
alpar@1
|
252 |
/* read field names */
|
alpar@1
|
253 |
xassert(csv->nf == 0);
|
alpar@1
|
254 |
for (;;)
|
alpar@1
|
255 |
{ read_field(csv);
|
alpar@1
|
256 |
if (csv->what == CSV_EOR)
|
alpar@1
|
257 |
break;
|
alpar@1
|
258 |
if (csv->what != CSV_STR)
|
alpar@1
|
259 |
{ xprintf("%s:%d: invalid field name\n", csv->fname,
|
alpar@1
|
260 |
csv->count);
|
alpar@1
|
261 |
longjmp(csv->jump, 0);
|
alpar@1
|
262 |
}
|
alpar@1
|
263 |
if (csv->nf == CSV_FIELD_MAX)
|
alpar@1
|
264 |
{ xprintf("%s:%d: too many fields\n", csv->fname,
|
alpar@1
|
265 |
csv->count);
|
alpar@1
|
266 |
longjmp(csv->jump, 0);
|
alpar@1
|
267 |
}
|
alpar@1
|
268 |
csv->nf++;
|
alpar@1
|
269 |
/* find corresponding field in the table statement */
|
alpar@1
|
270 |
for (k = mpl_tab_num_flds(dca); k >= 1; k--)
|
alpar@1
|
271 |
{ if (strcmp(mpl_tab_get_name(dca, k), csv->field) == 0)
|
alpar@1
|
272 |
break;
|
alpar@1
|
273 |
}
|
alpar@1
|
274 |
csv->ref[csv->nf] = k;
|
alpar@1
|
275 |
}
|
alpar@1
|
276 |
/* find dummy RECNO field in the table statement */
|
alpar@1
|
277 |
for (k = mpl_tab_num_flds(dca); k >= 1; k--)
|
alpar@1
|
278 |
if (strcmp(mpl_tab_get_name(dca, k), "RECNO") == 0) break;
|
alpar@1
|
279 |
csv->ref[0] = k;
|
alpar@1
|
280 |
}
|
alpar@1
|
281 |
else if (mode == 'W')
|
alpar@1
|
282 |
{ /* open the file for writing */
|
alpar@1
|
283 |
int k, nf;
|
alpar@1
|
284 |
csv->fp = fopen(csv->fname, "w");
|
alpar@1
|
285 |
if (csv->fp == NULL)
|
alpar@1
|
286 |
{ xprintf("csv_driver: unable to create %s - %s\n",
|
alpar@1
|
287 |
csv->fname, strerror(errno));
|
alpar@1
|
288 |
longjmp(csv->jump, 0);
|
alpar@1
|
289 |
}
|
alpar@1
|
290 |
/* write field names */
|
alpar@1
|
291 |
nf = mpl_tab_num_flds(dca);
|
alpar@1
|
292 |
for (k = 1; k <= nf; k++)
|
alpar@1
|
293 |
fprintf(csv->fp, "%s%c", mpl_tab_get_name(dca, k),
|
alpar@1
|
294 |
k < nf ? ',' : '\n');
|
alpar@1
|
295 |
csv->count++;
|
alpar@1
|
296 |
}
|
alpar@1
|
297 |
else
|
alpar@1
|
298 |
xassert(mode != mode);
|
alpar@1
|
299 |
/* the file has been open */
|
alpar@1
|
300 |
return csv;
|
alpar@1
|
301 |
fail: /* the file cannot be open */
|
alpar@1
|
302 |
if (csv->fname != NULL) xfree(csv->fname);
|
alpar@1
|
303 |
if (csv->fp != NULL) fclose(csv->fp);
|
alpar@1
|
304 |
xfree(csv);
|
alpar@1
|
305 |
return NULL;
|
alpar@1
|
306 |
}
|
alpar@1
|
307 |
|
alpar@1
|
308 |
static int csv_read_record(TABDCA *dca, struct csv *csv)
|
alpar@1
|
309 |
{ /* read next record from csv data file */
|
alpar@1
|
310 |
int k, ret = 0;
|
alpar@1
|
311 |
xassert(csv->mode == 'R');
|
alpar@1
|
312 |
if (setjmp(csv->jump))
|
alpar@1
|
313 |
{ ret = 1;
|
alpar@1
|
314 |
goto done;
|
alpar@1
|
315 |
}
|
alpar@1
|
316 |
/* read dummy RECNO field */
|
alpar@1
|
317 |
if (csv->ref[0] > 0)
|
alpar@1
|
318 |
#if 0 /* 01/VI-2010 */
|
alpar@1
|
319 |
mpl_tab_set_num(dca, csv->ref[0], csv->count-1);
|
alpar@1
|
320 |
#else
|
alpar@1
|
321 |
mpl_tab_set_num(dca, csv->ref[0], csv->count-csv->nskip-1);
|
alpar@1
|
322 |
#endif
|
alpar@1
|
323 |
/* read fields */
|
alpar@1
|
324 |
for (k = 1; k <= csv->nf; k++)
|
alpar@1
|
325 |
{ read_field(csv);
|
alpar@1
|
326 |
if (csv->what == CSV_EOF)
|
alpar@1
|
327 |
{ /* end-of-file reached */
|
alpar@1
|
328 |
xassert(k == 1);
|
alpar@1
|
329 |
ret = -1;
|
alpar@1
|
330 |
goto done;
|
alpar@1
|
331 |
}
|
alpar@1
|
332 |
else if (csv->what == CSV_EOR)
|
alpar@1
|
333 |
{ /* end-of-record reached */
|
alpar@1
|
334 |
int lack = csv->nf - k + 1;
|
alpar@1
|
335 |
if (lack == 1)
|
alpar@1
|
336 |
xprintf("%s:%d: one field missing\n", csv->fname,
|
alpar@1
|
337 |
csv->count);
|
alpar@1
|
338 |
else
|
alpar@1
|
339 |
xprintf("%s:%d: %d fields missing\n", csv->fname,
|
alpar@1
|
340 |
csv->count, lack);
|
alpar@1
|
341 |
longjmp(csv->jump, 0);
|
alpar@1
|
342 |
}
|
alpar@1
|
343 |
else if (csv->what == CSV_NUM)
|
alpar@1
|
344 |
{ /* floating-point number */
|
alpar@1
|
345 |
if (csv->ref[k] > 0)
|
alpar@1
|
346 |
{ double num;
|
alpar@1
|
347 |
xassert(str2num(csv->field, &num) == 0);
|
alpar@1
|
348 |
mpl_tab_set_num(dca, csv->ref[k], num);
|
alpar@1
|
349 |
}
|
alpar@1
|
350 |
}
|
alpar@1
|
351 |
else if (csv->what == CSV_STR)
|
alpar@1
|
352 |
{ /* character string */
|
alpar@1
|
353 |
if (csv->ref[k] > 0)
|
alpar@1
|
354 |
mpl_tab_set_str(dca, csv->ref[k], csv->field);
|
alpar@1
|
355 |
}
|
alpar@1
|
356 |
else
|
alpar@1
|
357 |
xassert(csv != csv);
|
alpar@1
|
358 |
}
|
alpar@1
|
359 |
/* now there must be NL */
|
alpar@1
|
360 |
read_field(csv);
|
alpar@1
|
361 |
xassert(csv->what != CSV_EOF);
|
alpar@1
|
362 |
if (csv->what != CSV_EOR)
|
alpar@1
|
363 |
{ xprintf("%s:%d: too many fields\n", csv->fname, csv->count);
|
alpar@1
|
364 |
longjmp(csv->jump, 0);
|
alpar@1
|
365 |
}
|
alpar@1
|
366 |
done: return ret;
|
alpar@1
|
367 |
}
|
alpar@1
|
368 |
|
alpar@1
|
369 |
static int csv_write_record(TABDCA *dca, struct csv *csv)
|
alpar@1
|
370 |
{ /* write next record to csv data file */
|
alpar@1
|
371 |
int k, nf, ret = 0;
|
alpar@1
|
372 |
const char *c;
|
alpar@1
|
373 |
xassert(csv->mode == 'W');
|
alpar@1
|
374 |
nf = mpl_tab_num_flds(dca);
|
alpar@1
|
375 |
for (k = 1; k <= nf; k++)
|
alpar@1
|
376 |
{ switch (mpl_tab_get_type(dca, k))
|
alpar@1
|
377 |
{ case 'N':
|
alpar@1
|
378 |
fprintf(csv->fp, "%.*g", DBL_DIG,
|
alpar@1
|
379 |
mpl_tab_get_num(dca, k));
|
alpar@1
|
380 |
break;
|
alpar@1
|
381 |
case 'S':
|
alpar@1
|
382 |
fputc('"', csv->fp);
|
alpar@1
|
383 |
for (c = mpl_tab_get_str(dca, k); *c != '\0'; c++)
|
alpar@1
|
384 |
{ if (*c == '"')
|
alpar@1
|
385 |
fputc('"', csv->fp), fputc('"', csv->fp);
|
alpar@1
|
386 |
else
|
alpar@1
|
387 |
fputc(*c, csv->fp);
|
alpar@1
|
388 |
}
|
alpar@1
|
389 |
fputc('"', csv->fp);
|
alpar@1
|
390 |
break;
|
alpar@1
|
391 |
default:
|
alpar@1
|
392 |
xassert(dca != dca);
|
alpar@1
|
393 |
}
|
alpar@1
|
394 |
fputc(k < nf ? ',' : '\n', csv->fp);
|
alpar@1
|
395 |
}
|
alpar@1
|
396 |
csv->count++;
|
alpar@1
|
397 |
if (ferror(csv->fp))
|
alpar@1
|
398 |
{ xprintf("%s:%d: write error - %s\n", csv->fname, csv->count,
|
alpar@1
|
399 |
strerror(errno));
|
alpar@1
|
400 |
ret = 1;
|
alpar@1
|
401 |
}
|
alpar@1
|
402 |
return ret;
|
alpar@1
|
403 |
}
|
alpar@1
|
404 |
|
alpar@1
|
405 |
static int csv_close_file(TABDCA *dca, struct csv *csv)
|
alpar@1
|
406 |
{ /* close csv data file */
|
alpar@1
|
407 |
int ret = 0;
|
alpar@1
|
408 |
xassert(dca == dca);
|
alpar@1
|
409 |
if (csv->mode == 'W')
|
alpar@1
|
410 |
{ fflush(csv->fp);
|
alpar@1
|
411 |
if (ferror(csv->fp))
|
alpar@1
|
412 |
{ xprintf("%s:%d: write error - %s\n", csv->fname,
|
alpar@1
|
413 |
csv->count, strerror(errno));
|
alpar@1
|
414 |
ret = 1;
|
alpar@1
|
415 |
}
|
alpar@1
|
416 |
}
|
alpar@1
|
417 |
xfree(csv->fname);
|
alpar@1
|
418 |
fclose(csv->fp);
|
alpar@1
|
419 |
xfree(csv);
|
alpar@1
|
420 |
return ret;
|
alpar@1
|
421 |
}
|
alpar@1
|
422 |
|
alpar@1
|
423 |
/**********************************************************************/
|
alpar@1
|
424 |
|
alpar@1
|
425 |
#define DBF_FIELD_MAX 50
|
alpar@1
|
426 |
/* maximal number of fields in record */
|
alpar@1
|
427 |
|
alpar@1
|
428 |
#define DBF_FDLEN_MAX 100
|
alpar@1
|
429 |
/* maximal field length */
|
alpar@1
|
430 |
|
alpar@1
|
431 |
struct dbf
|
alpar@1
|
432 |
{ /* xBASE data file */
|
alpar@1
|
433 |
int mode;
|
alpar@1
|
434 |
/* 'R' = reading; 'W' = writing */
|
alpar@1
|
435 |
char *fname;
|
alpar@1
|
436 |
/* name of xBASE file */
|
alpar@1
|
437 |
FILE *fp;
|
alpar@1
|
438 |
/* stream assigned to xBASE file */
|
alpar@1
|
439 |
jmp_buf jump;
|
alpar@1
|
440 |
/* address for non-local go to in case of error */
|
alpar@1
|
441 |
int offset;
|
alpar@1
|
442 |
/* offset of a byte to be read next */
|
alpar@1
|
443 |
int count;
|
alpar@1
|
444 |
/* record count */
|
alpar@1
|
445 |
int nf;
|
alpar@1
|
446 |
/* number of fields */
|
alpar@1
|
447 |
int ref[1+DBF_FIELD_MAX];
|
alpar@1
|
448 |
/* ref[k] = k', if k-th field of the csv file corresponds to
|
alpar@1
|
449 |
k'-th field in the table statement; if ref[k] = 0, k-th field
|
alpar@1
|
450 |
of the csv file is ignored */
|
alpar@1
|
451 |
int type[1+DBF_FIELD_MAX];
|
alpar@1
|
452 |
/* type[k] is type of k-th field */
|
alpar@1
|
453 |
int len[1+DBF_FIELD_MAX];
|
alpar@1
|
454 |
/* len[k] is length of k-th field */
|
alpar@1
|
455 |
int prec[1+DBF_FIELD_MAX];
|
alpar@1
|
456 |
/* prec[k] is precision of k-th field */
|
alpar@1
|
457 |
};
|
alpar@1
|
458 |
|
alpar@1
|
459 |
static int read_byte(struct dbf *dbf)
|
alpar@1
|
460 |
{ /* read byte from xBASE data file */
|
alpar@1
|
461 |
int b;
|
alpar@1
|
462 |
b = fgetc(dbf->fp);
|
alpar@1
|
463 |
if (ferror(dbf->fp))
|
alpar@1
|
464 |
{ xprintf("%s:0x%X: read error - %s\n", dbf->fname,
|
alpar@1
|
465 |
dbf->offset, strerror(errno));
|
alpar@1
|
466 |
longjmp(dbf->jump, 0);
|
alpar@1
|
467 |
}
|
alpar@1
|
468 |
if (feof(dbf->fp))
|
alpar@1
|
469 |
{ xprintf("%s:0x%X: unexpected end of file\n", dbf->fname,
|
alpar@1
|
470 |
dbf->offset);
|
alpar@1
|
471 |
longjmp(dbf->jump, 0);
|
alpar@1
|
472 |
}
|
alpar@1
|
473 |
xassert(0x00 <= b && b <= 0xFF);
|
alpar@1
|
474 |
dbf->offset++;
|
alpar@1
|
475 |
return b;
|
alpar@1
|
476 |
}
|
alpar@1
|
477 |
|
alpar@1
|
478 |
static void read_header(TABDCA *dca, struct dbf *dbf)
|
alpar@1
|
479 |
{ /* read xBASE data file header */
|
alpar@1
|
480 |
int b, j, k, recl;
|
alpar@1
|
481 |
char name[10+1];
|
alpar@1
|
482 |
/* (ignored) */
|
alpar@1
|
483 |
for (j = 1; j <= 10; j++)
|
alpar@1
|
484 |
read_byte(dbf);
|
alpar@1
|
485 |
/* length of each record, in bytes */
|
alpar@1
|
486 |
recl = read_byte(dbf);
|
alpar@1
|
487 |
recl += read_byte(dbf) << 8;
|
alpar@1
|
488 |
/* (ignored) */
|
alpar@1
|
489 |
for (j = 1; j <= 20; j++)
|
alpar@1
|
490 |
read_byte(dbf);
|
alpar@1
|
491 |
/* field descriptor array */
|
alpar@1
|
492 |
xassert(dbf->nf == 0);
|
alpar@1
|
493 |
for (;;)
|
alpar@1
|
494 |
{ /* check for end of array */
|
alpar@1
|
495 |
b = read_byte(dbf);
|
alpar@1
|
496 |
if (b == 0x0D) break;
|
alpar@1
|
497 |
if (dbf->nf == DBF_FIELD_MAX)
|
alpar@1
|
498 |
{ xprintf("%s:0x%X: too many fields\n", dbf->fname,
|
alpar@1
|
499 |
dbf->offset);
|
alpar@1
|
500 |
longjmp(dbf->jump, 0);
|
alpar@1
|
501 |
}
|
alpar@1
|
502 |
dbf->nf++;
|
alpar@1
|
503 |
/* field name */
|
alpar@1
|
504 |
name[0] = (char)b;
|
alpar@1
|
505 |
for (j = 1; j < 10; j++)
|
alpar@1
|
506 |
{ b = read_byte(dbf);
|
alpar@1
|
507 |
name[j] = (char)b;
|
alpar@1
|
508 |
}
|
alpar@1
|
509 |
name[10] = '\0';
|
alpar@1
|
510 |
b = read_byte(dbf);
|
alpar@1
|
511 |
if (b != 0x00)
|
alpar@1
|
512 |
{ xprintf("%s:0x%X: invalid field name\n", dbf->fname,
|
alpar@1
|
513 |
dbf->offset);
|
alpar@1
|
514 |
longjmp(dbf->jump, 0);
|
alpar@1
|
515 |
}
|
alpar@1
|
516 |
/* find corresponding field in the table statement */
|
alpar@1
|
517 |
for (k = mpl_tab_num_flds(dca); k >= 1; k--)
|
alpar@1
|
518 |
if (strcmp(mpl_tab_get_name(dca, k), name) == 0) break;
|
alpar@1
|
519 |
dbf->ref[dbf->nf] = k;
|
alpar@1
|
520 |
/* field type */
|
alpar@1
|
521 |
b = read_byte(dbf);
|
alpar@1
|
522 |
if (!(b == 'C' || b == 'N'))
|
alpar@1
|
523 |
{ xprintf("%s:0x%X: invalid field type\n", dbf->fname,
|
alpar@1
|
524 |
dbf->offset);
|
alpar@1
|
525 |
longjmp(dbf->jump, 0);
|
alpar@1
|
526 |
}
|
alpar@1
|
527 |
dbf->type[dbf->nf] = b;
|
alpar@1
|
528 |
/* (ignored) */
|
alpar@1
|
529 |
for (j = 1; j <= 4; j++)
|
alpar@1
|
530 |
read_byte(dbf);
|
alpar@1
|
531 |
/* field length */
|
alpar@1
|
532 |
b = read_byte(dbf);
|
alpar@1
|
533 |
if (b == 0)
|
alpar@1
|
534 |
{ xprintf("%s:0x%X: invalid field length\n", dbf->fname,
|
alpar@1
|
535 |
dbf->offset);
|
alpar@1
|
536 |
longjmp(dbf->jump, 0);
|
alpar@1
|
537 |
}
|
alpar@1
|
538 |
if (b > DBF_FDLEN_MAX)
|
alpar@1
|
539 |
{ xprintf("%s:0x%X: field too long\n", dbf->fname,
|
alpar@1
|
540 |
dbf->offset);
|
alpar@1
|
541 |
longjmp(dbf->jump, 0);
|
alpar@1
|
542 |
}
|
alpar@1
|
543 |
dbf->len[dbf->nf] = b;
|
alpar@1
|
544 |
recl -= b;
|
alpar@1
|
545 |
/* (ignored) */
|
alpar@1
|
546 |
for (j = 1; j <= 15; j++)
|
alpar@1
|
547 |
read_byte(dbf);
|
alpar@1
|
548 |
}
|
alpar@1
|
549 |
if (recl != 1)
|
alpar@1
|
550 |
{ xprintf("%s:0x%X: invalid file header\n", dbf->fname,
|
alpar@1
|
551 |
dbf->offset);
|
alpar@1
|
552 |
longjmp(dbf->jump, 0);
|
alpar@1
|
553 |
}
|
alpar@1
|
554 |
/* find dummy RECNO field in the table statement */
|
alpar@1
|
555 |
for (k = mpl_tab_num_flds(dca); k >= 1; k--)
|
alpar@1
|
556 |
if (strcmp(mpl_tab_get_name(dca, k), "RECNO") == 0) break;
|
alpar@1
|
557 |
dbf->ref[0] = k;
|
alpar@1
|
558 |
return;
|
alpar@1
|
559 |
}
|
alpar@1
|
560 |
|
alpar@1
|
561 |
static void parse_third_arg(TABDCA *dca, struct dbf *dbf)
|
alpar@1
|
562 |
{ /* parse xBASE file format (third argument) */
|
alpar@1
|
563 |
int j, k, temp;
|
alpar@1
|
564 |
const char *arg;
|
alpar@1
|
565 |
dbf->nf = mpl_tab_num_flds(dca);
|
alpar@1
|
566 |
arg = mpl_tab_get_arg(dca, 3), j = 0;
|
alpar@1
|
567 |
for (k = 1; k <= dbf->nf; k++)
|
alpar@1
|
568 |
{ /* parse specification of k-th field */
|
alpar@1
|
569 |
if (arg[j] == '\0')
|
alpar@1
|
570 |
{ xprintf("xBASE driver: field %s: specification missing\n",
|
alpar@1
|
571 |
mpl_tab_get_name(dca, k));
|
alpar@1
|
572 |
longjmp(dbf->jump, 0);
|
alpar@1
|
573 |
}
|
alpar@1
|
574 |
/* parse field type */
|
alpar@1
|
575 |
if (arg[j] == 'C' || arg[j] == 'N')
|
alpar@1
|
576 |
dbf->type[k] = arg[j], j++;
|
alpar@1
|
577 |
else
|
alpar@1
|
578 |
{ xprintf("xBASE driver: field %s: invalid field type\n",
|
alpar@1
|
579 |
mpl_tab_get_name(dca, k));
|
alpar@1
|
580 |
longjmp(dbf->jump, 0);
|
alpar@1
|
581 |
}
|
alpar@1
|
582 |
/* check for left parenthesis */
|
alpar@1
|
583 |
if (arg[j] == '(')
|
alpar@1
|
584 |
j++;
|
alpar@1
|
585 |
else
|
alpar@1
|
586 |
err: { xprintf("xBASE driver: field %s: invalid field format\n",
|
alpar@1
|
587 |
mpl_tab_get_name(dca, k));
|
alpar@1
|
588 |
longjmp(dbf->jump, 0);
|
alpar@1
|
589 |
}
|
alpar@1
|
590 |
/* parse field length */
|
alpar@1
|
591 |
temp = 0;
|
alpar@1
|
592 |
while (isdigit(arg[j]))
|
alpar@1
|
593 |
{ if (temp > DBF_FDLEN_MAX) break;
|
alpar@1
|
594 |
temp = 10 * temp + (arg[j] - '0'), j++;
|
alpar@1
|
595 |
}
|
alpar@1
|
596 |
if (!(1 <= temp && temp <= DBF_FDLEN_MAX))
|
alpar@1
|
597 |
{ xprintf("xBASE driver: field %s: invalid field length\n",
|
alpar@1
|
598 |
mpl_tab_get_name(dca, k));
|
alpar@1
|
599 |
longjmp(dbf->jump, 0);
|
alpar@1
|
600 |
}
|
alpar@1
|
601 |
dbf->len[k] = temp;
|
alpar@1
|
602 |
/* parse optional field precision */
|
alpar@1
|
603 |
if (dbf->type[k] == 'N' && arg[j] == ',')
|
alpar@1
|
604 |
{ j++;
|
alpar@1
|
605 |
temp = 0;
|
alpar@1
|
606 |
while (isdigit(arg[j]))
|
alpar@1
|
607 |
{ if (temp > dbf->len[k]) break;
|
alpar@1
|
608 |
temp = 10 * temp + (arg[j] - '0'), j++;
|
alpar@1
|
609 |
}
|
alpar@1
|
610 |
if (temp > dbf->len[k])
|
alpar@1
|
611 |
{ xprintf("xBASE driver: field %s: invalid field precision"
|
alpar@1
|
612 |
"\n", mpl_tab_get_name(dca, k));
|
alpar@1
|
613 |
longjmp(dbf->jump, 0);
|
alpar@1
|
614 |
}
|
alpar@1
|
615 |
dbf->prec[k] = temp;
|
alpar@1
|
616 |
}
|
alpar@1
|
617 |
else
|
alpar@1
|
618 |
dbf->prec[k] = 0;
|
alpar@1
|
619 |
/* check for right parenthesis */
|
alpar@1
|
620 |
if (arg[j] == ')')
|
alpar@1
|
621 |
j++;
|
alpar@1
|
622 |
else
|
alpar@1
|
623 |
goto err;
|
alpar@1
|
624 |
}
|
alpar@1
|
625 |
/* ignore other specifications */
|
alpar@1
|
626 |
return;
|
alpar@1
|
627 |
}
|
alpar@1
|
628 |
|
alpar@1
|
629 |
static void write_byte(struct dbf *dbf, int b)
|
alpar@1
|
630 |
{ /* write byte to xBASE data file */
|
alpar@1
|
631 |
fputc(b, dbf->fp);
|
alpar@1
|
632 |
dbf->offset++;
|
alpar@1
|
633 |
return;
|
alpar@1
|
634 |
}
|
alpar@1
|
635 |
|
alpar@1
|
636 |
static void write_header(TABDCA *dca, struct dbf *dbf)
|
alpar@1
|
637 |
{ /* write xBASE data file header */
|
alpar@1
|
638 |
int j, k, temp;
|
alpar@1
|
639 |
const char *name;
|
alpar@1
|
640 |
/* version number */
|
alpar@1
|
641 |
write_byte(dbf, 0x03 /* file without DBT */);
|
alpar@1
|
642 |
/* date of last update (YYMMDD) */
|
alpar@1
|
643 |
write_byte(dbf, 70 /* 1970 */);
|
alpar@1
|
644 |
write_byte(dbf, 1 /* January */);
|
alpar@1
|
645 |
write_byte(dbf, 1 /* 1st */);
|
alpar@1
|
646 |
/* number of records (unknown so far) */
|
alpar@1
|
647 |
for (j = 1; j <= 4; j++)
|
alpar@1
|
648 |
write_byte(dbf, 0xFF);
|
alpar@1
|
649 |
/* length of the header, in bytes */
|
alpar@1
|
650 |
temp = 32 + dbf->nf * 32 + 1;
|
alpar@1
|
651 |
write_byte(dbf, temp);
|
alpar@1
|
652 |
write_byte(dbf, temp >> 8);
|
alpar@1
|
653 |
/* length of each record, in bytes */
|
alpar@1
|
654 |
temp = 1;
|
alpar@1
|
655 |
for (k = 1; k <= dbf->nf; k++)
|
alpar@1
|
656 |
temp += dbf->len[k];
|
alpar@1
|
657 |
write_byte(dbf, temp);
|
alpar@1
|
658 |
write_byte(dbf, temp >> 8);
|
alpar@1
|
659 |
/* (reserved) */
|
alpar@1
|
660 |
for (j = 1; j <= 20; j++)
|
alpar@1
|
661 |
write_byte(dbf, 0x00);
|
alpar@1
|
662 |
/* field descriptor array */
|
alpar@1
|
663 |
for (k = 1; k <= dbf->nf; k++)
|
alpar@1
|
664 |
{ /* field name (terminated by 0x00) */
|
alpar@1
|
665 |
name = mpl_tab_get_name(dca, k);
|
alpar@1
|
666 |
for (j = 0; j < 10 && name[j] != '\0'; j++)
|
alpar@1
|
667 |
write_byte(dbf, name[j]);
|
alpar@1
|
668 |
for (j = j; j < 11; j++)
|
alpar@1
|
669 |
write_byte(dbf, 0x00);
|
alpar@1
|
670 |
/* field type */
|
alpar@1
|
671 |
write_byte(dbf, dbf->type[k]);
|
alpar@1
|
672 |
/* (reserved) */
|
alpar@1
|
673 |
for (j = 1; j <= 4; j++)
|
alpar@1
|
674 |
write_byte(dbf, 0x00);
|
alpar@1
|
675 |
/* field length */
|
alpar@1
|
676 |
write_byte(dbf, dbf->len[k]);
|
alpar@1
|
677 |
/* field precision */
|
alpar@1
|
678 |
write_byte(dbf, dbf->prec[k]);
|
alpar@1
|
679 |
/* (reserved) */
|
alpar@1
|
680 |
for (j = 1; j <= 14; j++)
|
alpar@1
|
681 |
write_byte(dbf, 0x00);
|
alpar@1
|
682 |
}
|
alpar@1
|
683 |
/* end of header */
|
alpar@1
|
684 |
write_byte(dbf, 0x0D);
|
alpar@1
|
685 |
return;
|
alpar@1
|
686 |
}
|
alpar@1
|
687 |
|
alpar@1
|
688 |
static struct dbf *dbf_open_file(TABDCA *dca, int mode)
|
alpar@1
|
689 |
{ /* open xBASE data file */
|
alpar@1
|
690 |
struct dbf *dbf;
|
alpar@1
|
691 |
/* create control structure */
|
alpar@1
|
692 |
dbf = xmalloc(sizeof(struct dbf));
|
alpar@1
|
693 |
dbf->mode = mode;
|
alpar@1
|
694 |
dbf->fname = NULL;
|
alpar@1
|
695 |
dbf->fp = NULL;
|
alpar@1
|
696 |
if (setjmp(dbf->jump)) goto fail;
|
alpar@1
|
697 |
dbf->offset = 0;
|
alpar@1
|
698 |
dbf->count = 0;
|
alpar@1
|
699 |
dbf->nf = 0;
|
alpar@1
|
700 |
/* try to open the xBASE data file */
|
alpar@1
|
701 |
if (mpl_tab_num_args(dca) < 2)
|
alpar@1
|
702 |
{ xprintf("xBASE driver: file name not specified\n");
|
alpar@1
|
703 |
longjmp(dbf->jump, 0);
|
alpar@1
|
704 |
}
|
alpar@1
|
705 |
dbf->fname = xmalloc(strlen(mpl_tab_get_arg(dca, 2))+1);
|
alpar@1
|
706 |
strcpy(dbf->fname, mpl_tab_get_arg(dca, 2));
|
alpar@1
|
707 |
if (mode == 'R')
|
alpar@1
|
708 |
{ /* open the file for reading */
|
alpar@1
|
709 |
dbf->fp = fopen(dbf->fname, "rb");
|
alpar@1
|
710 |
if (dbf->fp == NULL)
|
alpar@1
|
711 |
{ xprintf("xBASE driver: unable to open %s - %s\n",
|
alpar@1
|
712 |
dbf->fname, strerror(errno));
|
alpar@1
|
713 |
longjmp(dbf->jump, 0);
|
alpar@1
|
714 |
}
|
alpar@1
|
715 |
read_header(dca, dbf);
|
alpar@1
|
716 |
}
|
alpar@1
|
717 |
else if (mode == 'W')
|
alpar@1
|
718 |
{ /* open the file for writing */
|
alpar@1
|
719 |
if (mpl_tab_num_args(dca) < 3)
|
alpar@1
|
720 |
{ xprintf("xBASE driver: file format not specified\n");
|
alpar@1
|
721 |
longjmp(dbf->jump, 0);
|
alpar@1
|
722 |
}
|
alpar@1
|
723 |
parse_third_arg(dca, dbf);
|
alpar@1
|
724 |
dbf->fp = fopen(dbf->fname, "wb");
|
alpar@1
|
725 |
if (dbf->fp == NULL)
|
alpar@1
|
726 |
{ xprintf("xBASE driver: unable to create %s - %s\n",
|
alpar@1
|
727 |
dbf->fname, strerror(errno));
|
alpar@1
|
728 |
longjmp(dbf->jump, 0);
|
alpar@1
|
729 |
}
|
alpar@1
|
730 |
write_header(dca, dbf);
|
alpar@1
|
731 |
}
|
alpar@1
|
732 |
else
|
alpar@1
|
733 |
xassert(mode != mode);
|
alpar@1
|
734 |
/* the file has been open */
|
alpar@1
|
735 |
return dbf;
|
alpar@1
|
736 |
fail: /* the file cannot be open */
|
alpar@1
|
737 |
if (dbf->fname != NULL) xfree(dbf->fname);
|
alpar@1
|
738 |
if (dbf->fp != NULL) fclose(dbf->fp);
|
alpar@1
|
739 |
xfree(dbf);
|
alpar@1
|
740 |
return NULL;
|
alpar@1
|
741 |
}
|
alpar@1
|
742 |
|
alpar@1
|
743 |
static int dbf_read_record(TABDCA *dca, struct dbf *dbf)
|
alpar@1
|
744 |
{ /* read next record from xBASE data file */
|
alpar@1
|
745 |
int b, j, k, ret = 0;
|
alpar@1
|
746 |
char buf[DBF_FDLEN_MAX+1];
|
alpar@1
|
747 |
xassert(dbf->mode == 'R');
|
alpar@1
|
748 |
if (setjmp(dbf->jump))
|
alpar@1
|
749 |
{ ret = 1;
|
alpar@1
|
750 |
goto done;
|
alpar@1
|
751 |
}
|
alpar@1
|
752 |
/* check record flag */
|
alpar@1
|
753 |
b = read_byte(dbf);
|
alpar@1
|
754 |
if (b == 0x1A)
|
alpar@1
|
755 |
{ /* end of data */
|
alpar@1
|
756 |
ret = -1;
|
alpar@1
|
757 |
goto done;
|
alpar@1
|
758 |
}
|
alpar@1
|
759 |
if (b != 0x20)
|
alpar@1
|
760 |
{ xprintf("%s:0x%X: invalid record flag\n", dbf->fname,
|
alpar@1
|
761 |
dbf->offset);
|
alpar@1
|
762 |
longjmp(dbf->jump, 0);
|
alpar@1
|
763 |
}
|
alpar@1
|
764 |
/* read dummy RECNO field */
|
alpar@1
|
765 |
if (dbf->ref[0] > 0)
|
alpar@1
|
766 |
mpl_tab_set_num(dca, dbf->ref[0], dbf->count+1);
|
alpar@1
|
767 |
/* read fields */
|
alpar@1
|
768 |
for (k = 1; k <= dbf->nf; k++)
|
alpar@1
|
769 |
{ /* read k-th field */
|
alpar@1
|
770 |
for (j = 0; j < dbf->len[k]; j++)
|
alpar@1
|
771 |
buf[j] = (char)read_byte(dbf);
|
alpar@1
|
772 |
buf[dbf->len[k]] = '\0';
|
alpar@1
|
773 |
/* set field value */
|
alpar@1
|
774 |
if (dbf->type[k] == 'C')
|
alpar@1
|
775 |
{ /* character field */
|
alpar@1
|
776 |
if (dbf->ref[k] > 0)
|
alpar@1
|
777 |
mpl_tab_set_str(dca, dbf->ref[k], strtrim(buf));
|
alpar@1
|
778 |
}
|
alpar@1
|
779 |
else if (dbf->type[k] == 'N')
|
alpar@1
|
780 |
{ /* numeric field */
|
alpar@1
|
781 |
if (dbf->ref[k] > 0)
|
alpar@1
|
782 |
{ double num;
|
alpar@1
|
783 |
strspx(buf);
|
alpar@1
|
784 |
xassert(str2num(buf, &num) == 0);
|
alpar@1
|
785 |
mpl_tab_set_num(dca, dbf->ref[k], num);
|
alpar@1
|
786 |
}
|
alpar@1
|
787 |
}
|
alpar@1
|
788 |
else
|
alpar@1
|
789 |
xassert(dbf != dbf);
|
alpar@1
|
790 |
}
|
alpar@1
|
791 |
/* increase record count */
|
alpar@1
|
792 |
dbf->count++;
|
alpar@1
|
793 |
done: return ret;
|
alpar@1
|
794 |
}
|
alpar@1
|
795 |
|
alpar@1
|
796 |
static int dbf_write_record(TABDCA *dca, struct dbf *dbf)
|
alpar@1
|
797 |
{ /* write next record to xBASE data file */
|
alpar@1
|
798 |
int j, k, ret = 0;
|
alpar@1
|
799 |
char buf[255+1];
|
alpar@1
|
800 |
xassert(dbf->mode == 'W');
|
alpar@1
|
801 |
if (setjmp(dbf->jump))
|
alpar@1
|
802 |
{ ret = 1;
|
alpar@1
|
803 |
goto done;
|
alpar@1
|
804 |
}
|
alpar@1
|
805 |
/* record flag */
|
alpar@1
|
806 |
write_byte(dbf, 0x20);
|
alpar@1
|
807 |
xassert(dbf->nf == mpl_tab_num_flds(dca));
|
alpar@1
|
808 |
for (k = 1; k <= dbf->nf; k++)
|
alpar@1
|
809 |
{ if (dbf->type[k] == 'C')
|
alpar@1
|
810 |
{ /* character field */
|
alpar@1
|
811 |
const char *str;
|
alpar@1
|
812 |
if (mpl_tab_get_type(dca, k) == 'N')
|
alpar@1
|
813 |
{ sprintf(buf, "%.*g", DBL_DIG, mpl_tab_get_num(dca, k));
|
alpar@1
|
814 |
str = buf;
|
alpar@1
|
815 |
}
|
alpar@1
|
816 |
else if (mpl_tab_get_type(dca, k) == 'S')
|
alpar@1
|
817 |
str = mpl_tab_get_str(dca, k);
|
alpar@1
|
818 |
else
|
alpar@1
|
819 |
xassert(dca != dca);
|
alpar@1
|
820 |
if ((int)strlen(str) > dbf->len[k])
|
alpar@1
|
821 |
{ xprintf("xBASE driver: field %s: cannot convert %.15s..."
|
alpar@1
|
822 |
" to field format\n", mpl_tab_get_name(dca, k), str);
|
alpar@1
|
823 |
longjmp(dbf->jump, 0);
|
alpar@1
|
824 |
}
|
alpar@1
|
825 |
for (j = 0; j < dbf->len[k] && str[j] != '\0'; j++)
|
alpar@1
|
826 |
write_byte(dbf, str[j]);
|
alpar@1
|
827 |
for (j = j; j < dbf->len[k]; j++)
|
alpar@1
|
828 |
write_byte(dbf, ' ');
|
alpar@1
|
829 |
}
|
alpar@1
|
830 |
else if (dbf->type[k] == 'N')
|
alpar@1
|
831 |
{ /* numeric field */
|
alpar@1
|
832 |
double num = mpl_tab_get_num(dca, k);
|
alpar@1
|
833 |
if (fabs(num) > 1e20)
|
alpar@1
|
834 |
err: { xprintf("xBASE driver: field %s: cannot convert %g to fi"
|
alpar@1
|
835 |
"eld format\n", mpl_tab_get_name(dca, k), num);
|
alpar@1
|
836 |
longjmp(dbf->jump, 0);
|
alpar@1
|
837 |
}
|
alpar@1
|
838 |
sprintf(buf, "%*.*f", dbf->len[k], dbf->prec[k], num);
|
alpar@1
|
839 |
xassert(strlen(buf) < sizeof(buf));
|
alpar@1
|
840 |
if ((int)strlen(buf) != dbf->len[k]) goto err;
|
alpar@1
|
841 |
for (j = 0; j < dbf->len[k]; j++)
|
alpar@1
|
842 |
write_byte(dbf, buf[j]);
|
alpar@1
|
843 |
}
|
alpar@1
|
844 |
else
|
alpar@1
|
845 |
xassert(dbf != dbf);
|
alpar@1
|
846 |
}
|
alpar@1
|
847 |
/* increase record count */
|
alpar@1
|
848 |
dbf->count++;
|
alpar@1
|
849 |
done: return ret;
|
alpar@1
|
850 |
}
|
alpar@1
|
851 |
|
alpar@1
|
852 |
static int dbf_close_file(TABDCA *dca, struct dbf *dbf)
|
alpar@1
|
853 |
{ /* close xBASE data file */
|
alpar@1
|
854 |
int ret = 0;
|
alpar@1
|
855 |
xassert(dca == dca);
|
alpar@1
|
856 |
if (dbf->mode == 'W')
|
alpar@1
|
857 |
{ if (setjmp(dbf->jump))
|
alpar@1
|
858 |
{ ret = 1;
|
alpar@1
|
859 |
goto skip;
|
alpar@1
|
860 |
}
|
alpar@1
|
861 |
/* end-of-file flag */
|
alpar@1
|
862 |
write_byte(dbf, 0x1A);
|
alpar@1
|
863 |
/* number of records */
|
alpar@1
|
864 |
dbf->offset = 4;
|
alpar@1
|
865 |
if (fseek(dbf->fp, dbf->offset, SEEK_SET))
|
alpar@1
|
866 |
{ xprintf("%s:0x%X: seek error - %s\n", dbf->fname,
|
alpar@1
|
867 |
dbf->offset, strerror(errno));
|
alpar@1
|
868 |
longjmp(dbf->jump, 0);
|
alpar@1
|
869 |
}
|
alpar@1
|
870 |
write_byte(dbf, dbf->count);
|
alpar@1
|
871 |
write_byte(dbf, dbf->count >> 8);
|
alpar@1
|
872 |
write_byte(dbf, dbf->count >> 16);
|
alpar@1
|
873 |
write_byte(dbf, dbf->count >> 24);
|
alpar@1
|
874 |
fflush(dbf->fp);
|
alpar@1
|
875 |
if (ferror(dbf->fp))
|
alpar@1
|
876 |
{ xprintf("%s:0x%X: write error - %s\n", dbf->fname,
|
alpar@1
|
877 |
dbf->offset, strerror(errno));
|
alpar@1
|
878 |
longjmp(dbf->jump, 0);
|
alpar@1
|
879 |
}
|
alpar@1
|
880 |
skip: ;
|
alpar@1
|
881 |
}
|
alpar@1
|
882 |
xfree(dbf->fname);
|
alpar@1
|
883 |
fclose(dbf->fp);
|
alpar@1
|
884 |
xfree(dbf);
|
alpar@1
|
885 |
return ret;
|
alpar@1
|
886 |
}
|
alpar@1
|
887 |
|
alpar@1
|
888 |
/**********************************************************************/
|
alpar@1
|
889 |
|
alpar@1
|
890 |
#define TAB_CSV 1
|
alpar@1
|
891 |
#define TAB_XBASE 2
|
alpar@1
|
892 |
#define TAB_ODBC 3
|
alpar@1
|
893 |
#define TAB_MYSQL 4
|
alpar@1
|
894 |
|
alpar@1
|
895 |
void mpl_tab_drv_open(MPL *mpl, int mode)
|
alpar@1
|
896 |
{ TABDCA *dca = mpl->dca;
|
alpar@1
|
897 |
xassert(dca->id == 0);
|
alpar@1
|
898 |
xassert(dca->link == NULL);
|
alpar@1
|
899 |
xassert(dca->na >= 1);
|
alpar@1
|
900 |
if (strcmp(dca->arg[1], "CSV") == 0)
|
alpar@1
|
901 |
{ dca->id = TAB_CSV;
|
alpar@1
|
902 |
dca->link = csv_open_file(dca, mode);
|
alpar@1
|
903 |
}
|
alpar@1
|
904 |
else if (strcmp(dca->arg[1], "xBASE") == 0)
|
alpar@1
|
905 |
{ dca->id = TAB_XBASE;
|
alpar@1
|
906 |
dca->link = dbf_open_file(dca, mode);
|
alpar@1
|
907 |
}
|
alpar@1
|
908 |
else if (strcmp(dca->arg[1], "ODBC") == 0 ||
|
alpar@1
|
909 |
strcmp(dca->arg[1], "iODBC") == 0)
|
alpar@1
|
910 |
{ dca->id = TAB_ODBC;
|
alpar@1
|
911 |
dca->link = db_iodbc_open(dca, mode);
|
alpar@1
|
912 |
}
|
alpar@1
|
913 |
else if (strcmp(dca->arg[1], "MySQL") == 0)
|
alpar@1
|
914 |
{ dca->id = TAB_MYSQL;
|
alpar@1
|
915 |
dca->link = db_mysql_open(dca, mode);
|
alpar@1
|
916 |
}
|
alpar@1
|
917 |
else
|
alpar@1
|
918 |
xprintf("Invalid table driver `%s'\n", dca->arg[1]);
|
alpar@1
|
919 |
if (dca->link == NULL)
|
alpar@1
|
920 |
error(mpl, "error on opening table %s",
|
alpar@1
|
921 |
mpl->stmt->u.tab->name);
|
alpar@1
|
922 |
return;
|
alpar@1
|
923 |
}
|
alpar@1
|
924 |
|
alpar@1
|
925 |
int mpl_tab_drv_read(MPL *mpl)
|
alpar@1
|
926 |
{ TABDCA *dca = mpl->dca;
|
alpar@1
|
927 |
int ret;
|
alpar@1
|
928 |
switch (dca->id)
|
alpar@1
|
929 |
{ case TAB_CSV:
|
alpar@1
|
930 |
ret = csv_read_record(dca, dca->link);
|
alpar@1
|
931 |
break;
|
alpar@1
|
932 |
case TAB_XBASE:
|
alpar@1
|
933 |
ret = dbf_read_record(dca, dca->link);
|
alpar@1
|
934 |
break;
|
alpar@1
|
935 |
case TAB_ODBC:
|
alpar@1
|
936 |
ret = db_iodbc_read(dca, dca->link);
|
alpar@1
|
937 |
break;
|
alpar@1
|
938 |
case TAB_MYSQL:
|
alpar@1
|
939 |
ret = db_mysql_read(dca, dca->link);
|
alpar@1
|
940 |
break;
|
alpar@1
|
941 |
default:
|
alpar@1
|
942 |
xassert(dca != dca);
|
alpar@1
|
943 |
}
|
alpar@1
|
944 |
if (ret > 0)
|
alpar@1
|
945 |
error(mpl, "error on reading data from table %s",
|
alpar@1
|
946 |
mpl->stmt->u.tab->name);
|
alpar@1
|
947 |
return ret;
|
alpar@1
|
948 |
}
|
alpar@1
|
949 |
|
alpar@1
|
950 |
void mpl_tab_drv_write(MPL *mpl)
|
alpar@1
|
951 |
{ TABDCA *dca = mpl->dca;
|
alpar@1
|
952 |
int ret;
|
alpar@1
|
953 |
switch (dca->id)
|
alpar@1
|
954 |
{ case TAB_CSV:
|
alpar@1
|
955 |
ret = csv_write_record(dca, dca->link);
|
alpar@1
|
956 |
break;
|
alpar@1
|
957 |
case TAB_XBASE:
|
alpar@1
|
958 |
ret = dbf_write_record(dca, dca->link);
|
alpar@1
|
959 |
break;
|
alpar@1
|
960 |
case TAB_ODBC:
|
alpar@1
|
961 |
ret = db_iodbc_write(dca, dca->link);
|
alpar@1
|
962 |
break;
|
alpar@1
|
963 |
case TAB_MYSQL:
|
alpar@1
|
964 |
ret = db_mysql_write(dca, dca->link);
|
alpar@1
|
965 |
break;
|
alpar@1
|
966 |
default:
|
alpar@1
|
967 |
xassert(dca != dca);
|
alpar@1
|
968 |
}
|
alpar@1
|
969 |
if (ret)
|
alpar@1
|
970 |
error(mpl, "error on writing data to table %s",
|
alpar@1
|
971 |
mpl->stmt->u.tab->name);
|
alpar@1
|
972 |
return;
|
alpar@1
|
973 |
}
|
alpar@1
|
974 |
|
alpar@1
|
975 |
void mpl_tab_drv_close(MPL *mpl)
|
alpar@1
|
976 |
{ TABDCA *dca = mpl->dca;
|
alpar@1
|
977 |
int ret;
|
alpar@1
|
978 |
switch (dca->id)
|
alpar@1
|
979 |
{ case TAB_CSV:
|
alpar@1
|
980 |
ret = csv_close_file(dca, dca->link);
|
alpar@1
|
981 |
break;
|
alpar@1
|
982 |
case TAB_XBASE:
|
alpar@1
|
983 |
ret = dbf_close_file(dca, dca->link);
|
alpar@1
|
984 |
break;
|
alpar@1
|
985 |
case TAB_ODBC:
|
alpar@1
|
986 |
ret = db_iodbc_close(dca, dca->link);
|
alpar@1
|
987 |
break;
|
alpar@1
|
988 |
case TAB_MYSQL:
|
alpar@1
|
989 |
ret = db_mysql_close(dca, dca->link);
|
alpar@1
|
990 |
break;
|
alpar@1
|
991 |
default:
|
alpar@1
|
992 |
xassert(dca != dca);
|
alpar@1
|
993 |
}
|
alpar@1
|
994 |
dca->id = 0;
|
alpar@1
|
995 |
dca->link = NULL;
|
alpar@1
|
996 |
if (ret)
|
alpar@1
|
997 |
error(mpl, "error on closing table %s",
|
alpar@1
|
998 |
mpl->stmt->u.tab->name);
|
alpar@1
|
999 |
return;
|
alpar@1
|
1000 |
}
|
alpar@1
|
1001 |
|
alpar@1
|
1002 |
/* eof */
|