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