src/glpmpl06.c
changeset 2 4c8956a7bdf4
equal deleted inserted replaced
-1:000000000000 0:78f5bbf05a50
       
     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 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 */