lemon-project-template-glpk

annotate deps/glpk/src/glpmpl06.c @ 9:33de93886c88

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