alpar@9: /* glpmpl06.c */ alpar@9: alpar@9: /*********************************************************************** alpar@9: * This code is part of GLPK (GNU Linear Programming Kit). alpar@9: * alpar@9: * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, alpar@9: * 2009, 2010, 2011 Andrew Makhorin, Department for Applied Informatics, alpar@9: * Moscow Aviation Institute, Moscow, Russia. All rights reserved. alpar@9: * E-mail: . alpar@9: * alpar@9: * GLPK is free software: you can redistribute it and/or modify it alpar@9: * under the terms of the GNU General Public License as published by alpar@9: * the Free Software Foundation, either version 3 of the License, or alpar@9: * (at your option) any later version. alpar@9: * alpar@9: * GLPK is distributed in the hope that it will be useful, but WITHOUT alpar@9: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY alpar@9: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public alpar@9: * License for more details. alpar@9: * alpar@9: * You should have received a copy of the GNU General Public License alpar@9: * along with GLPK. If not, see . alpar@9: ***********************************************************************/ alpar@9: alpar@9: #define _GLPSTD_ERRNO alpar@9: #define _GLPSTD_STDIO alpar@9: #include "glpmpl.h" alpar@9: #include "glpsql.h" alpar@9: alpar@9: /**********************************************************************/ alpar@9: alpar@9: #define CSV_FIELD_MAX 50 alpar@9: /* maximal number of fields in record */ alpar@9: alpar@9: #define CSV_FDLEN_MAX 100 alpar@9: /* maximal field length */ alpar@9: alpar@9: struct csv alpar@9: { /* comma-separated values file */ alpar@9: int mode; alpar@9: /* 'R' = reading; 'W' = writing */ alpar@9: char *fname; alpar@9: /* name of csv file */ alpar@9: FILE *fp; alpar@9: /* stream assigned to csv file */ alpar@9: jmp_buf jump; alpar@9: /* address for non-local go to in case of error */ alpar@9: int count; alpar@9: /* record count */ alpar@9: /*--------------------------------------------------------------*/ alpar@9: /* used only for input csv file */ alpar@9: int c; alpar@9: /* current character or EOF */ alpar@9: int what; alpar@9: /* current marker: */ alpar@9: #define CSV_EOF 0 /* end-of-file */ alpar@9: #define CSV_EOR 1 /* end-of-record */ alpar@9: #define CSV_NUM 2 /* floating-point number */ alpar@9: #define CSV_STR 3 /* character string */ alpar@9: char field[CSV_FDLEN_MAX+1]; alpar@9: /* current field just read */ alpar@9: int nf; alpar@9: /* number of fields in the csv file */ alpar@9: int ref[1+CSV_FIELD_MAX]; alpar@9: /* ref[k] = k', if k-th field of the csv file corresponds to alpar@9: k'-th field in the table statement; if ref[k] = 0, k-th field alpar@9: of the csv file is ignored */ alpar@9: #if 1 /* 01/VI-2010 */ alpar@9: int nskip; alpar@9: /* number of comment records preceding the header record */ alpar@9: #endif alpar@9: }; alpar@9: alpar@9: #undef read_char alpar@9: alpar@9: static void read_char(struct csv *csv) alpar@9: { /* read character from csv data file */ alpar@9: int c; alpar@9: xassert(csv->c != EOF); alpar@9: if (csv->c == '\n') csv->count++; alpar@9: loop: c = fgetc(csv->fp); alpar@9: if (ferror(csv->fp)) alpar@9: { xprintf("%s:%d: read error - %s\n", csv->fname, csv->count, alpar@9: strerror(errno)); alpar@9: longjmp(csv->jump, 0); alpar@9: } alpar@9: if (feof(csv->fp)) alpar@9: { if (csv->c == '\n') alpar@9: { csv->count--; alpar@9: c = EOF; alpar@9: } alpar@9: else alpar@9: { xprintf("%s:%d: warning: missing final end-of-line\n", alpar@9: csv->fname, csv->count); alpar@9: c = '\n'; alpar@9: } alpar@9: } alpar@9: else if (c == '\r') alpar@9: goto loop; alpar@9: else if (c == '\n') alpar@9: ; alpar@9: else if (iscntrl(c)) alpar@9: { xprintf("%s:%d: invalid control character 0x%02X\n", alpar@9: csv->fname, csv->count, c); alpar@9: longjmp(csv->jump, 0); alpar@9: } alpar@9: csv->c = c; alpar@9: return; alpar@9: } alpar@9: alpar@9: static void read_field(struct csv *csv) alpar@9: { /* read field from csv data file */ alpar@9: /* check for end of file */ alpar@9: if (csv->c == EOF) alpar@9: { csv->what = CSV_EOF; alpar@9: strcpy(csv->field, "EOF"); alpar@9: goto done; alpar@9: } alpar@9: /* check for end of record */ alpar@9: if (csv->c == '\n') alpar@9: { csv->what = CSV_EOR; alpar@9: strcpy(csv->field, "EOR"); alpar@9: read_char(csv); alpar@9: if (csv->c == ',') alpar@9: err1: { xprintf("%s:%d: empty field not allowed\n", csv->fname, alpar@9: csv->count); alpar@9: longjmp(csv->jump, 0); alpar@9: } alpar@9: if (csv->c == '\n') alpar@9: { xprintf("%s:%d: empty record not allowed\n", csv->fname, alpar@9: csv->count); alpar@9: longjmp(csv->jump, 0); alpar@9: } alpar@9: #if 1 /* 01/VI-2010 */ alpar@9: /* skip comment records; may appear only before the very first alpar@9: record containing field names */ alpar@9: if (csv->c == '#' && csv->count == 1) alpar@9: { while (csv->c == '#') alpar@9: { while (csv->c != '\n') alpar@9: read_char(csv); alpar@9: read_char(csv); alpar@9: csv->nskip++; alpar@9: } alpar@9: } alpar@9: #endif alpar@9: goto done; alpar@9: } alpar@9: /* skip comma before next field */ alpar@9: if (csv->c == ',') alpar@9: read_char(csv); alpar@9: /* read field */ alpar@9: if (csv->c == '\'' || csv->c == '"') alpar@9: { /* read a field enclosed in quotes */ alpar@9: int quote = csv->c, len = 0; alpar@9: csv->what = CSV_STR; alpar@9: /* skip opening quote */ alpar@9: read_char(csv); alpar@9: /* read field characters within quotes */ alpar@9: for (;;) alpar@9: { /* check for closing quote and read it */ alpar@9: if (csv->c == quote) alpar@9: { read_char(csv); alpar@9: if (csv->c == quote) alpar@9: ; alpar@9: else if (csv->c == ',' || csv->c == '\n') alpar@9: break; alpar@9: else alpar@9: { xprintf("%s:%d: invalid field\n", csv->fname, alpar@9: csv->count); alpar@9: longjmp(csv->jump, 0); alpar@9: } alpar@9: } alpar@9: /* check the current field length */ alpar@9: if (len == CSV_FDLEN_MAX) alpar@9: err2: { xprintf("%s:%d: field too long\n", csv->fname, alpar@9: csv->count); alpar@9: longjmp(csv->jump, 0); alpar@9: } alpar@9: /* add the current character to the field */ alpar@9: csv->field[len++] = (char)csv->c; alpar@9: /* read the next character */ alpar@9: read_char(csv); alpar@9: } alpar@9: /* the field has been read */ alpar@9: if (len == 0) goto err1; alpar@9: csv->field[len] = '\0'; alpar@9: } alpar@9: else alpar@9: { /* read a field not enclosed in quotes */ alpar@9: int len = 0; alpar@9: double temp; alpar@9: csv->what = CSV_NUM; alpar@9: while (!(csv->c == ',' || csv->c == '\n')) alpar@9: { /* quotes within the field are not allowed */ alpar@9: if (csv->c == '\'' || csv->c == '"') alpar@9: { xprintf("%s:%d: invalid use of single or double quote wi" alpar@9: "thin field\n", csv->fname, csv->count); alpar@9: longjmp(csv->jump, 0); alpar@9: } alpar@9: /* check the current field length */ alpar@9: if (len == CSV_FDLEN_MAX) goto err2; alpar@9: /* add the current character to the field */ alpar@9: csv->field[len++] = (char)csv->c; alpar@9: /* read the next character */ alpar@9: read_char(csv); alpar@9: } alpar@9: /* the field has been read */ alpar@9: if (len == 0) goto err1; alpar@9: csv->field[len] = '\0'; alpar@9: /* check the field type */ alpar@9: if (str2num(csv->field, &temp)) csv->what = CSV_STR; alpar@9: } alpar@9: done: return; alpar@9: } alpar@9: alpar@9: static struct csv *csv_open_file(TABDCA *dca, int mode) alpar@9: { /* open csv data file */ alpar@9: struct csv *csv; alpar@9: /* create control structure */ alpar@9: csv = xmalloc(sizeof(struct csv)); alpar@9: csv->mode = mode; alpar@9: csv->fname = NULL; alpar@9: csv->fp = NULL; alpar@9: if (setjmp(csv->jump)) goto fail; alpar@9: csv->count = 0; alpar@9: csv->c = '\n'; alpar@9: csv->what = 0; alpar@9: csv->field[0] = '\0'; alpar@9: csv->nf = 0; alpar@9: /* try to open the csv data file */ alpar@9: if (mpl_tab_num_args(dca) < 2) alpar@9: { xprintf("csv_driver: file name not specified\n"); alpar@9: longjmp(csv->jump, 0); alpar@9: } alpar@9: csv->fname = xmalloc(strlen(mpl_tab_get_arg(dca, 2))+1); alpar@9: strcpy(csv->fname, mpl_tab_get_arg(dca, 2)); alpar@9: if (mode == 'R') alpar@9: { /* open the file for reading */ alpar@9: int k; alpar@9: csv->fp = fopen(csv->fname, "r"); alpar@9: if (csv->fp == NULL) alpar@9: { xprintf("csv_driver: unable to open %s - %s\n", alpar@9: csv->fname, strerror(errno)); alpar@9: longjmp(csv->jump, 0); alpar@9: } alpar@9: #if 1 /* 01/VI-2010 */ alpar@9: csv->nskip = 0; alpar@9: #endif alpar@9: /* skip fake new-line */ alpar@9: read_field(csv); alpar@9: xassert(csv->what == CSV_EOR); alpar@9: /* read field names */ alpar@9: xassert(csv->nf == 0); alpar@9: for (;;) alpar@9: { read_field(csv); alpar@9: if (csv->what == CSV_EOR) alpar@9: break; alpar@9: if (csv->what != CSV_STR) alpar@9: { xprintf("%s:%d: invalid field name\n", csv->fname, alpar@9: csv->count); alpar@9: longjmp(csv->jump, 0); alpar@9: } alpar@9: if (csv->nf == CSV_FIELD_MAX) alpar@9: { xprintf("%s:%d: too many fields\n", csv->fname, alpar@9: csv->count); alpar@9: longjmp(csv->jump, 0); alpar@9: } alpar@9: csv->nf++; alpar@9: /* find corresponding field in the table statement */ alpar@9: for (k = mpl_tab_num_flds(dca); k >= 1; k--) alpar@9: { if (strcmp(mpl_tab_get_name(dca, k), csv->field) == 0) alpar@9: break; alpar@9: } alpar@9: csv->ref[csv->nf] = k; alpar@9: } alpar@9: /* find dummy RECNO field in the table statement */ alpar@9: for (k = mpl_tab_num_flds(dca); k >= 1; k--) alpar@9: if (strcmp(mpl_tab_get_name(dca, k), "RECNO") == 0) break; alpar@9: csv->ref[0] = k; alpar@9: } alpar@9: else if (mode == 'W') alpar@9: { /* open the file for writing */ alpar@9: int k, nf; alpar@9: csv->fp = fopen(csv->fname, "w"); alpar@9: if (csv->fp == NULL) alpar@9: { xprintf("csv_driver: unable to create %s - %s\n", alpar@9: csv->fname, strerror(errno)); alpar@9: longjmp(csv->jump, 0); alpar@9: } alpar@9: /* write field names */ alpar@9: nf = mpl_tab_num_flds(dca); alpar@9: for (k = 1; k <= nf; k++) alpar@9: fprintf(csv->fp, "%s%c", mpl_tab_get_name(dca, k), alpar@9: k < nf ? ',' : '\n'); alpar@9: csv->count++; alpar@9: } alpar@9: else alpar@9: xassert(mode != mode); alpar@9: /* the file has been open */ alpar@9: return csv; alpar@9: fail: /* the file cannot be open */ alpar@9: if (csv->fname != NULL) xfree(csv->fname); alpar@9: if (csv->fp != NULL) fclose(csv->fp); alpar@9: xfree(csv); alpar@9: return NULL; alpar@9: } alpar@9: alpar@9: static int csv_read_record(TABDCA *dca, struct csv *csv) alpar@9: { /* read next record from csv data file */ alpar@9: int k, ret = 0; alpar@9: xassert(csv->mode == 'R'); alpar@9: if (setjmp(csv->jump)) alpar@9: { ret = 1; alpar@9: goto done; alpar@9: } alpar@9: /* read dummy RECNO field */ alpar@9: if (csv->ref[0] > 0) alpar@9: #if 0 /* 01/VI-2010 */ alpar@9: mpl_tab_set_num(dca, csv->ref[0], csv->count-1); alpar@9: #else alpar@9: mpl_tab_set_num(dca, csv->ref[0], csv->count-csv->nskip-1); alpar@9: #endif alpar@9: /* read fields */ alpar@9: for (k = 1; k <= csv->nf; k++) alpar@9: { read_field(csv); alpar@9: if (csv->what == CSV_EOF) alpar@9: { /* end-of-file reached */ alpar@9: xassert(k == 1); alpar@9: ret = -1; alpar@9: goto done; alpar@9: } alpar@9: else if (csv->what == CSV_EOR) alpar@9: { /* end-of-record reached */ alpar@9: int lack = csv->nf - k + 1; alpar@9: if (lack == 1) alpar@9: xprintf("%s:%d: one field missing\n", csv->fname, alpar@9: csv->count); alpar@9: else alpar@9: xprintf("%s:%d: %d fields missing\n", csv->fname, alpar@9: csv->count, lack); alpar@9: longjmp(csv->jump, 0); alpar@9: } alpar@9: else if (csv->what == CSV_NUM) alpar@9: { /* floating-point number */ alpar@9: if (csv->ref[k] > 0) alpar@9: { double num; alpar@9: xassert(str2num(csv->field, &num) == 0); alpar@9: mpl_tab_set_num(dca, csv->ref[k], num); alpar@9: } alpar@9: } alpar@9: else if (csv->what == CSV_STR) alpar@9: { /* character string */ alpar@9: if (csv->ref[k] > 0) alpar@9: mpl_tab_set_str(dca, csv->ref[k], csv->field); alpar@9: } alpar@9: else alpar@9: xassert(csv != csv); alpar@9: } alpar@9: /* now there must be NL */ alpar@9: read_field(csv); alpar@9: xassert(csv->what != CSV_EOF); alpar@9: if (csv->what != CSV_EOR) alpar@9: { xprintf("%s:%d: too many fields\n", csv->fname, csv->count); alpar@9: longjmp(csv->jump, 0); alpar@9: } alpar@9: done: return ret; alpar@9: } alpar@9: alpar@9: static int csv_write_record(TABDCA *dca, struct csv *csv) alpar@9: { /* write next record to csv data file */ alpar@9: int k, nf, ret = 0; alpar@9: const char *c; alpar@9: xassert(csv->mode == 'W'); alpar@9: nf = mpl_tab_num_flds(dca); alpar@9: for (k = 1; k <= nf; k++) alpar@9: { switch (mpl_tab_get_type(dca, k)) alpar@9: { case 'N': alpar@9: fprintf(csv->fp, "%.*g", DBL_DIG, alpar@9: mpl_tab_get_num(dca, k)); alpar@9: break; alpar@9: case 'S': alpar@9: fputc('"', csv->fp); alpar@9: for (c = mpl_tab_get_str(dca, k); *c != '\0'; c++) alpar@9: { if (*c == '"') alpar@9: fputc('"', csv->fp), fputc('"', csv->fp); alpar@9: else alpar@9: fputc(*c, csv->fp); alpar@9: } alpar@9: fputc('"', csv->fp); alpar@9: break; alpar@9: default: alpar@9: xassert(dca != dca); alpar@9: } alpar@9: fputc(k < nf ? ',' : '\n', csv->fp); alpar@9: } alpar@9: csv->count++; alpar@9: if (ferror(csv->fp)) alpar@9: { xprintf("%s:%d: write error - %s\n", csv->fname, csv->count, alpar@9: strerror(errno)); alpar@9: ret = 1; alpar@9: } alpar@9: return ret; alpar@9: } alpar@9: alpar@9: static int csv_close_file(TABDCA *dca, struct csv *csv) alpar@9: { /* close csv data file */ alpar@9: int ret = 0; alpar@9: xassert(dca == dca); alpar@9: if (csv->mode == 'W') alpar@9: { fflush(csv->fp); alpar@9: if (ferror(csv->fp)) alpar@9: { xprintf("%s:%d: write error - %s\n", csv->fname, alpar@9: csv->count, strerror(errno)); alpar@9: ret = 1; alpar@9: } alpar@9: } alpar@9: xfree(csv->fname); alpar@9: fclose(csv->fp); alpar@9: xfree(csv); alpar@9: return ret; alpar@9: } alpar@9: alpar@9: /**********************************************************************/ alpar@9: alpar@9: #define DBF_FIELD_MAX 50 alpar@9: /* maximal number of fields in record */ alpar@9: alpar@9: #define DBF_FDLEN_MAX 100 alpar@9: /* maximal field length */ alpar@9: alpar@9: struct dbf alpar@9: { /* xBASE data file */ alpar@9: int mode; alpar@9: /* 'R' = reading; 'W' = writing */ alpar@9: char *fname; alpar@9: /* name of xBASE file */ alpar@9: FILE *fp; alpar@9: /* stream assigned to xBASE file */ alpar@9: jmp_buf jump; alpar@9: /* address for non-local go to in case of error */ alpar@9: int offset; alpar@9: /* offset of a byte to be read next */ alpar@9: int count; alpar@9: /* record count */ alpar@9: int nf; alpar@9: /* number of fields */ alpar@9: int ref[1+DBF_FIELD_MAX]; alpar@9: /* ref[k] = k', if k-th field of the csv file corresponds to alpar@9: k'-th field in the table statement; if ref[k] = 0, k-th field alpar@9: of the csv file is ignored */ alpar@9: int type[1+DBF_FIELD_MAX]; alpar@9: /* type[k] is type of k-th field */ alpar@9: int len[1+DBF_FIELD_MAX]; alpar@9: /* len[k] is length of k-th field */ alpar@9: int prec[1+DBF_FIELD_MAX]; alpar@9: /* prec[k] is precision of k-th field */ alpar@9: }; alpar@9: alpar@9: static int read_byte(struct dbf *dbf) alpar@9: { /* read byte from xBASE data file */ alpar@9: int b; alpar@9: b = fgetc(dbf->fp); alpar@9: if (ferror(dbf->fp)) alpar@9: { xprintf("%s:0x%X: read error - %s\n", dbf->fname, alpar@9: dbf->offset, strerror(errno)); alpar@9: longjmp(dbf->jump, 0); alpar@9: } alpar@9: if (feof(dbf->fp)) alpar@9: { xprintf("%s:0x%X: unexpected end of file\n", dbf->fname, alpar@9: dbf->offset); alpar@9: longjmp(dbf->jump, 0); alpar@9: } alpar@9: xassert(0x00 <= b && b <= 0xFF); alpar@9: dbf->offset++; alpar@9: return b; alpar@9: } alpar@9: alpar@9: static void read_header(TABDCA *dca, struct dbf *dbf) alpar@9: { /* read xBASE data file header */ alpar@9: int b, j, k, recl; alpar@9: char name[10+1]; alpar@9: /* (ignored) */ alpar@9: for (j = 1; j <= 10; j++) alpar@9: read_byte(dbf); alpar@9: /* length of each record, in bytes */ alpar@9: recl = read_byte(dbf); alpar@9: recl += read_byte(dbf) << 8; alpar@9: /* (ignored) */ alpar@9: for (j = 1; j <= 20; j++) alpar@9: read_byte(dbf); alpar@9: /* field descriptor array */ alpar@9: xassert(dbf->nf == 0); alpar@9: for (;;) alpar@9: { /* check for end of array */ alpar@9: b = read_byte(dbf); alpar@9: if (b == 0x0D) break; alpar@9: if (dbf->nf == DBF_FIELD_MAX) alpar@9: { xprintf("%s:0x%X: too many fields\n", dbf->fname, alpar@9: dbf->offset); alpar@9: longjmp(dbf->jump, 0); alpar@9: } alpar@9: dbf->nf++; alpar@9: /* field name */ alpar@9: name[0] = (char)b; alpar@9: for (j = 1; j < 10; j++) alpar@9: { b = read_byte(dbf); alpar@9: name[j] = (char)b; alpar@9: } alpar@9: name[10] = '\0'; alpar@9: b = read_byte(dbf); alpar@9: if (b != 0x00) alpar@9: { xprintf("%s:0x%X: invalid field name\n", dbf->fname, alpar@9: dbf->offset); alpar@9: longjmp(dbf->jump, 0); alpar@9: } alpar@9: /* find corresponding field in the table statement */ alpar@9: for (k = mpl_tab_num_flds(dca); k >= 1; k--) alpar@9: if (strcmp(mpl_tab_get_name(dca, k), name) == 0) break; alpar@9: dbf->ref[dbf->nf] = k; alpar@9: /* field type */ alpar@9: b = read_byte(dbf); alpar@9: if (!(b == 'C' || b == 'N')) alpar@9: { xprintf("%s:0x%X: invalid field type\n", dbf->fname, alpar@9: dbf->offset); alpar@9: longjmp(dbf->jump, 0); alpar@9: } alpar@9: dbf->type[dbf->nf] = b; alpar@9: /* (ignored) */ alpar@9: for (j = 1; j <= 4; j++) alpar@9: read_byte(dbf); alpar@9: /* field length */ alpar@9: b = read_byte(dbf); alpar@9: if (b == 0) alpar@9: { xprintf("%s:0x%X: invalid field length\n", dbf->fname, alpar@9: dbf->offset); alpar@9: longjmp(dbf->jump, 0); alpar@9: } alpar@9: if (b > DBF_FDLEN_MAX) alpar@9: { xprintf("%s:0x%X: field too long\n", dbf->fname, alpar@9: dbf->offset); alpar@9: longjmp(dbf->jump, 0); alpar@9: } alpar@9: dbf->len[dbf->nf] = b; alpar@9: recl -= b; alpar@9: /* (ignored) */ alpar@9: for (j = 1; j <= 15; j++) alpar@9: read_byte(dbf); alpar@9: } alpar@9: if (recl != 1) alpar@9: { xprintf("%s:0x%X: invalid file header\n", dbf->fname, alpar@9: dbf->offset); alpar@9: longjmp(dbf->jump, 0); alpar@9: } alpar@9: /* find dummy RECNO field in the table statement */ alpar@9: for (k = mpl_tab_num_flds(dca); k >= 1; k--) alpar@9: if (strcmp(mpl_tab_get_name(dca, k), "RECNO") == 0) break; alpar@9: dbf->ref[0] = k; alpar@9: return; alpar@9: } alpar@9: alpar@9: static void parse_third_arg(TABDCA *dca, struct dbf *dbf) alpar@9: { /* parse xBASE file format (third argument) */ alpar@9: int j, k, temp; alpar@9: const char *arg; alpar@9: dbf->nf = mpl_tab_num_flds(dca); alpar@9: arg = mpl_tab_get_arg(dca, 3), j = 0; alpar@9: for (k = 1; k <= dbf->nf; k++) alpar@9: { /* parse specification of k-th field */ alpar@9: if (arg[j] == '\0') alpar@9: { xprintf("xBASE driver: field %s: specification missing\n", alpar@9: mpl_tab_get_name(dca, k)); alpar@9: longjmp(dbf->jump, 0); alpar@9: } alpar@9: /* parse field type */ alpar@9: if (arg[j] == 'C' || arg[j] == 'N') alpar@9: dbf->type[k] = arg[j], j++; alpar@9: else alpar@9: { xprintf("xBASE driver: field %s: invalid field type\n", alpar@9: mpl_tab_get_name(dca, k)); alpar@9: longjmp(dbf->jump, 0); alpar@9: } alpar@9: /* check for left parenthesis */ alpar@9: if (arg[j] == '(') alpar@9: j++; alpar@9: else alpar@9: err: { xprintf("xBASE driver: field %s: invalid field format\n", alpar@9: mpl_tab_get_name(dca, k)); alpar@9: longjmp(dbf->jump, 0); alpar@9: } alpar@9: /* parse field length */ alpar@9: temp = 0; alpar@9: while (isdigit(arg[j])) alpar@9: { if (temp > DBF_FDLEN_MAX) break; alpar@9: temp = 10 * temp + (arg[j] - '0'), j++; alpar@9: } alpar@9: if (!(1 <= temp && temp <= DBF_FDLEN_MAX)) alpar@9: { xprintf("xBASE driver: field %s: invalid field length\n", alpar@9: mpl_tab_get_name(dca, k)); alpar@9: longjmp(dbf->jump, 0); alpar@9: } alpar@9: dbf->len[k] = temp; alpar@9: /* parse optional field precision */ alpar@9: if (dbf->type[k] == 'N' && arg[j] == ',') alpar@9: { j++; alpar@9: temp = 0; alpar@9: while (isdigit(arg[j])) alpar@9: { if (temp > dbf->len[k]) break; alpar@9: temp = 10 * temp + (arg[j] - '0'), j++; alpar@9: } alpar@9: if (temp > dbf->len[k]) alpar@9: { xprintf("xBASE driver: field %s: invalid field precision" alpar@9: "\n", mpl_tab_get_name(dca, k)); alpar@9: longjmp(dbf->jump, 0); alpar@9: } alpar@9: dbf->prec[k] = temp; alpar@9: } alpar@9: else alpar@9: dbf->prec[k] = 0; alpar@9: /* check for right parenthesis */ alpar@9: if (arg[j] == ')') alpar@9: j++; alpar@9: else alpar@9: goto err; alpar@9: } alpar@9: /* ignore other specifications */ alpar@9: return; alpar@9: } alpar@9: alpar@9: static void write_byte(struct dbf *dbf, int b) alpar@9: { /* write byte to xBASE data file */ alpar@9: fputc(b, dbf->fp); alpar@9: dbf->offset++; alpar@9: return; alpar@9: } alpar@9: alpar@9: static void write_header(TABDCA *dca, struct dbf *dbf) alpar@9: { /* write xBASE data file header */ alpar@9: int j, k, temp; alpar@9: const char *name; alpar@9: /* version number */ alpar@9: write_byte(dbf, 0x03 /* file without DBT */); alpar@9: /* date of last update (YYMMDD) */ alpar@9: write_byte(dbf, 70 /* 1970 */); alpar@9: write_byte(dbf, 1 /* January */); alpar@9: write_byte(dbf, 1 /* 1st */); alpar@9: /* number of records (unknown so far) */ alpar@9: for (j = 1; j <= 4; j++) alpar@9: write_byte(dbf, 0xFF); alpar@9: /* length of the header, in bytes */ alpar@9: temp = 32 + dbf->nf * 32 + 1; alpar@9: write_byte(dbf, temp); alpar@9: write_byte(dbf, temp >> 8); alpar@9: /* length of each record, in bytes */ alpar@9: temp = 1; alpar@9: for (k = 1; k <= dbf->nf; k++) alpar@9: temp += dbf->len[k]; alpar@9: write_byte(dbf, temp); alpar@9: write_byte(dbf, temp >> 8); alpar@9: /* (reserved) */ alpar@9: for (j = 1; j <= 20; j++) alpar@9: write_byte(dbf, 0x00); alpar@9: /* field descriptor array */ alpar@9: for (k = 1; k <= dbf->nf; k++) alpar@9: { /* field name (terminated by 0x00) */ alpar@9: name = mpl_tab_get_name(dca, k); alpar@9: for (j = 0; j < 10 && name[j] != '\0'; j++) alpar@9: write_byte(dbf, name[j]); alpar@9: for (j = j; j < 11; j++) alpar@9: write_byte(dbf, 0x00); alpar@9: /* field type */ alpar@9: write_byte(dbf, dbf->type[k]); alpar@9: /* (reserved) */ alpar@9: for (j = 1; j <= 4; j++) alpar@9: write_byte(dbf, 0x00); alpar@9: /* field length */ alpar@9: write_byte(dbf, dbf->len[k]); alpar@9: /* field precision */ alpar@9: write_byte(dbf, dbf->prec[k]); alpar@9: /* (reserved) */ alpar@9: for (j = 1; j <= 14; j++) alpar@9: write_byte(dbf, 0x00); alpar@9: } alpar@9: /* end of header */ alpar@9: write_byte(dbf, 0x0D); alpar@9: return; alpar@9: } alpar@9: alpar@9: static struct dbf *dbf_open_file(TABDCA *dca, int mode) alpar@9: { /* open xBASE data file */ alpar@9: struct dbf *dbf; alpar@9: /* create control structure */ alpar@9: dbf = xmalloc(sizeof(struct dbf)); alpar@9: dbf->mode = mode; alpar@9: dbf->fname = NULL; alpar@9: dbf->fp = NULL; alpar@9: if (setjmp(dbf->jump)) goto fail; alpar@9: dbf->offset = 0; alpar@9: dbf->count = 0; alpar@9: dbf->nf = 0; alpar@9: /* try to open the xBASE data file */ alpar@9: if (mpl_tab_num_args(dca) < 2) alpar@9: { xprintf("xBASE driver: file name not specified\n"); alpar@9: longjmp(dbf->jump, 0); alpar@9: } alpar@9: dbf->fname = xmalloc(strlen(mpl_tab_get_arg(dca, 2))+1); alpar@9: strcpy(dbf->fname, mpl_tab_get_arg(dca, 2)); alpar@9: if (mode == 'R') alpar@9: { /* open the file for reading */ alpar@9: dbf->fp = fopen(dbf->fname, "rb"); alpar@9: if (dbf->fp == NULL) alpar@9: { xprintf("xBASE driver: unable to open %s - %s\n", alpar@9: dbf->fname, strerror(errno)); alpar@9: longjmp(dbf->jump, 0); alpar@9: } alpar@9: read_header(dca, dbf); alpar@9: } alpar@9: else if (mode == 'W') alpar@9: { /* open the file for writing */ alpar@9: if (mpl_tab_num_args(dca) < 3) alpar@9: { xprintf("xBASE driver: file format not specified\n"); alpar@9: longjmp(dbf->jump, 0); alpar@9: } alpar@9: parse_third_arg(dca, dbf); alpar@9: dbf->fp = fopen(dbf->fname, "wb"); alpar@9: if (dbf->fp == NULL) alpar@9: { xprintf("xBASE driver: unable to create %s - %s\n", alpar@9: dbf->fname, strerror(errno)); alpar@9: longjmp(dbf->jump, 0); alpar@9: } alpar@9: write_header(dca, dbf); alpar@9: } alpar@9: else alpar@9: xassert(mode != mode); alpar@9: /* the file has been open */ alpar@9: return dbf; alpar@9: fail: /* the file cannot be open */ alpar@9: if (dbf->fname != NULL) xfree(dbf->fname); alpar@9: if (dbf->fp != NULL) fclose(dbf->fp); alpar@9: xfree(dbf); alpar@9: return NULL; alpar@9: } alpar@9: alpar@9: static int dbf_read_record(TABDCA *dca, struct dbf *dbf) alpar@9: { /* read next record from xBASE data file */ alpar@9: int b, j, k, ret = 0; alpar@9: char buf[DBF_FDLEN_MAX+1]; alpar@9: xassert(dbf->mode == 'R'); alpar@9: if (setjmp(dbf->jump)) alpar@9: { ret = 1; alpar@9: goto done; alpar@9: } alpar@9: /* check record flag */ alpar@9: b = read_byte(dbf); alpar@9: if (b == 0x1A) alpar@9: { /* end of data */ alpar@9: ret = -1; alpar@9: goto done; alpar@9: } alpar@9: if (b != 0x20) alpar@9: { xprintf("%s:0x%X: invalid record flag\n", dbf->fname, alpar@9: dbf->offset); alpar@9: longjmp(dbf->jump, 0); alpar@9: } alpar@9: /* read dummy RECNO field */ alpar@9: if (dbf->ref[0] > 0) alpar@9: mpl_tab_set_num(dca, dbf->ref[0], dbf->count+1); alpar@9: /* read fields */ alpar@9: for (k = 1; k <= dbf->nf; k++) alpar@9: { /* read k-th field */ alpar@9: for (j = 0; j < dbf->len[k]; j++) alpar@9: buf[j] = (char)read_byte(dbf); alpar@9: buf[dbf->len[k]] = '\0'; alpar@9: /* set field value */ alpar@9: if (dbf->type[k] == 'C') alpar@9: { /* character field */ alpar@9: if (dbf->ref[k] > 0) alpar@9: mpl_tab_set_str(dca, dbf->ref[k], strtrim(buf)); alpar@9: } alpar@9: else if (dbf->type[k] == 'N') alpar@9: { /* numeric field */ alpar@9: if (dbf->ref[k] > 0) alpar@9: { double num; alpar@9: strspx(buf); alpar@9: xassert(str2num(buf, &num) == 0); alpar@9: mpl_tab_set_num(dca, dbf->ref[k], num); alpar@9: } alpar@9: } alpar@9: else alpar@9: xassert(dbf != dbf); alpar@9: } alpar@9: /* increase record count */ alpar@9: dbf->count++; alpar@9: done: return ret; alpar@9: } alpar@9: alpar@9: static int dbf_write_record(TABDCA *dca, struct dbf *dbf) alpar@9: { /* write next record to xBASE data file */ alpar@9: int j, k, ret = 0; alpar@9: char buf[255+1]; alpar@9: xassert(dbf->mode == 'W'); alpar@9: if (setjmp(dbf->jump)) alpar@9: { ret = 1; alpar@9: goto done; alpar@9: } alpar@9: /* record flag */ alpar@9: write_byte(dbf, 0x20); alpar@9: xassert(dbf->nf == mpl_tab_num_flds(dca)); alpar@9: for (k = 1; k <= dbf->nf; k++) alpar@9: { if (dbf->type[k] == 'C') alpar@9: { /* character field */ alpar@9: const char *str; alpar@9: if (mpl_tab_get_type(dca, k) == 'N') alpar@9: { sprintf(buf, "%.*g", DBL_DIG, mpl_tab_get_num(dca, k)); alpar@9: str = buf; alpar@9: } alpar@9: else if (mpl_tab_get_type(dca, k) == 'S') alpar@9: str = mpl_tab_get_str(dca, k); alpar@9: else alpar@9: xassert(dca != dca); alpar@9: if ((int)strlen(str) > dbf->len[k]) alpar@9: { xprintf("xBASE driver: field %s: cannot convert %.15s..." alpar@9: " to field format\n", mpl_tab_get_name(dca, k), str); alpar@9: longjmp(dbf->jump, 0); alpar@9: } alpar@9: for (j = 0; j < dbf->len[k] && str[j] != '\0'; j++) alpar@9: write_byte(dbf, str[j]); alpar@9: for (j = j; j < dbf->len[k]; j++) alpar@9: write_byte(dbf, ' '); alpar@9: } alpar@9: else if (dbf->type[k] == 'N') alpar@9: { /* numeric field */ alpar@9: double num = mpl_tab_get_num(dca, k); alpar@9: if (fabs(num) > 1e20) alpar@9: err: { xprintf("xBASE driver: field %s: cannot convert %g to fi" alpar@9: "eld format\n", mpl_tab_get_name(dca, k), num); alpar@9: longjmp(dbf->jump, 0); alpar@9: } alpar@9: sprintf(buf, "%*.*f", dbf->len[k], dbf->prec[k], num); alpar@9: xassert(strlen(buf) < sizeof(buf)); alpar@9: if ((int)strlen(buf) != dbf->len[k]) goto err; alpar@9: for (j = 0; j < dbf->len[k]; j++) alpar@9: write_byte(dbf, buf[j]); alpar@9: } alpar@9: else alpar@9: xassert(dbf != dbf); alpar@9: } alpar@9: /* increase record count */ alpar@9: dbf->count++; alpar@9: done: return ret; alpar@9: } alpar@9: alpar@9: static int dbf_close_file(TABDCA *dca, struct dbf *dbf) alpar@9: { /* close xBASE data file */ alpar@9: int ret = 0; alpar@9: xassert(dca == dca); alpar@9: if (dbf->mode == 'W') alpar@9: { if (setjmp(dbf->jump)) alpar@9: { ret = 1; alpar@9: goto skip; alpar@9: } alpar@9: /* end-of-file flag */ alpar@9: write_byte(dbf, 0x1A); alpar@9: /* number of records */ alpar@9: dbf->offset = 4; alpar@9: if (fseek(dbf->fp, dbf->offset, SEEK_SET)) alpar@9: { xprintf("%s:0x%X: seek error - %s\n", dbf->fname, alpar@9: dbf->offset, strerror(errno)); alpar@9: longjmp(dbf->jump, 0); alpar@9: } alpar@9: write_byte(dbf, dbf->count); alpar@9: write_byte(dbf, dbf->count >> 8); alpar@9: write_byte(dbf, dbf->count >> 16); alpar@9: write_byte(dbf, dbf->count >> 24); alpar@9: fflush(dbf->fp); alpar@9: if (ferror(dbf->fp)) alpar@9: { xprintf("%s:0x%X: write error - %s\n", dbf->fname, alpar@9: dbf->offset, strerror(errno)); alpar@9: longjmp(dbf->jump, 0); alpar@9: } alpar@9: skip: ; alpar@9: } alpar@9: xfree(dbf->fname); alpar@9: fclose(dbf->fp); alpar@9: xfree(dbf); alpar@9: return ret; alpar@9: } alpar@9: alpar@9: /**********************************************************************/ alpar@9: alpar@9: #define TAB_CSV 1 alpar@9: #define TAB_XBASE 2 alpar@9: #define TAB_ODBC 3 alpar@9: #define TAB_MYSQL 4 alpar@9: alpar@9: void mpl_tab_drv_open(MPL *mpl, int mode) alpar@9: { TABDCA *dca = mpl->dca; alpar@9: xassert(dca->id == 0); alpar@9: xassert(dca->link == NULL); alpar@9: xassert(dca->na >= 1); alpar@9: if (strcmp(dca->arg[1], "CSV") == 0) alpar@9: { dca->id = TAB_CSV; alpar@9: dca->link = csv_open_file(dca, mode); alpar@9: } alpar@9: else if (strcmp(dca->arg[1], "xBASE") == 0) alpar@9: { dca->id = TAB_XBASE; alpar@9: dca->link = dbf_open_file(dca, mode); alpar@9: } alpar@9: else if (strcmp(dca->arg[1], "ODBC") == 0 || alpar@9: strcmp(dca->arg[1], "iODBC") == 0) alpar@9: { dca->id = TAB_ODBC; alpar@9: dca->link = db_iodbc_open(dca, mode); alpar@9: } alpar@9: else if (strcmp(dca->arg[1], "MySQL") == 0) alpar@9: { dca->id = TAB_MYSQL; alpar@9: dca->link = db_mysql_open(dca, mode); alpar@9: } alpar@9: else alpar@9: xprintf("Invalid table driver `%s'\n", dca->arg[1]); alpar@9: if (dca->link == NULL) alpar@9: error(mpl, "error on opening table %s", alpar@9: mpl->stmt->u.tab->name); alpar@9: return; alpar@9: } alpar@9: alpar@9: int mpl_tab_drv_read(MPL *mpl) alpar@9: { TABDCA *dca = mpl->dca; alpar@9: int ret; alpar@9: switch (dca->id) alpar@9: { case TAB_CSV: alpar@9: ret = csv_read_record(dca, dca->link); alpar@9: break; alpar@9: case TAB_XBASE: alpar@9: ret = dbf_read_record(dca, dca->link); alpar@9: break; alpar@9: case TAB_ODBC: alpar@9: ret = db_iodbc_read(dca, dca->link); alpar@9: break; alpar@9: case TAB_MYSQL: alpar@9: ret = db_mysql_read(dca, dca->link); alpar@9: break; alpar@9: default: alpar@9: xassert(dca != dca); alpar@9: } alpar@9: if (ret > 0) alpar@9: error(mpl, "error on reading data from table %s", alpar@9: mpl->stmt->u.tab->name); alpar@9: return ret; alpar@9: } alpar@9: alpar@9: void mpl_tab_drv_write(MPL *mpl) alpar@9: { TABDCA *dca = mpl->dca; alpar@9: int ret; alpar@9: switch (dca->id) alpar@9: { case TAB_CSV: alpar@9: ret = csv_write_record(dca, dca->link); alpar@9: break; alpar@9: case TAB_XBASE: alpar@9: ret = dbf_write_record(dca, dca->link); alpar@9: break; alpar@9: case TAB_ODBC: alpar@9: ret = db_iodbc_write(dca, dca->link); alpar@9: break; alpar@9: case TAB_MYSQL: alpar@9: ret = db_mysql_write(dca, dca->link); alpar@9: break; alpar@9: default: alpar@9: xassert(dca != dca); alpar@9: } alpar@9: if (ret) alpar@9: error(mpl, "error on writing data to table %s", alpar@9: mpl->stmt->u.tab->name); alpar@9: return; alpar@9: } alpar@9: alpar@9: void mpl_tab_drv_close(MPL *mpl) alpar@9: { TABDCA *dca = mpl->dca; alpar@9: int ret; alpar@9: switch (dca->id) alpar@9: { case TAB_CSV: alpar@9: ret = csv_close_file(dca, dca->link); alpar@9: break; alpar@9: case TAB_XBASE: alpar@9: ret = dbf_close_file(dca, dca->link); alpar@9: break; alpar@9: case TAB_ODBC: alpar@9: ret = db_iodbc_close(dca, dca->link); alpar@9: break; alpar@9: case TAB_MYSQL: alpar@9: ret = db_mysql_close(dca, dca->link); alpar@9: break; alpar@9: default: alpar@9: xassert(dca != dca); alpar@9: } alpar@9: dca->id = 0; alpar@9: dca->link = NULL; alpar@9: if (ret) alpar@9: error(mpl, "error on closing table %s", alpar@9: mpl->stmt->u.tab->name); alpar@9: return; alpar@9: } alpar@9: alpar@9: /* eof */