2280 lines
64 KiB
C
2280 lines
64 KiB
C
|
|
/*
|
|
* Committee for Graphics Arts Technologies Standards
|
|
* CGATS.5 and IT8.7 family file I/O class
|
|
* Version 2.05
|
|
*/
|
|
|
|
/*
|
|
* Author: Graeme W. Gill
|
|
* Date: 20/12/95
|
|
*
|
|
* Copyright 1995, 1996, 2002, Graeme W. Gill
|
|
* All rights reserved.
|
|
*
|
|
* This material is licensed with an "MIT" free use license:-
|
|
* see the License4.txt file in this directory for licensing details.
|
|
*/
|
|
|
|
|
|
/*
|
|
|
|
Should add a function to promote a field type, ie.
|
|
promote integer to float, to avoid misrecognition
|
|
problems.
|
|
|
|
|
|
To make this more portable for independent use,
|
|
should save/set/restore LC_NUMERIC locale before
|
|
printf/scanf from file. e.g.
|
|
|
|
include <locale.h>
|
|
char *old_locale, *saved_locale;
|
|
|
|
old_locale = setlocale (LC_NUMERIC, NULL);
|
|
saved_locale = strdup (old_locale);
|
|
if (saved_locale == NULL)
|
|
error ("Out of memory");
|
|
setlocale (LC_NUMERIC, "C");
|
|
|
|
.... read or write ...
|
|
|
|
setlocale (LC_NUMERIC, saved_locale);
|
|
free (saved_locale);
|
|
|
|
Also apply to pars.c
|
|
*/
|
|
|
|
#define _CGATS_C_ /* Turn on implimentation code */
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
|
|
#undef DEBUG /* Debug only in slected places */
|
|
|
|
#ifdef DEBUG
|
|
# define DBGA stderr
|
|
# define DBGF(xx) fprintf xx
|
|
#else
|
|
# define DBGF(xx)
|
|
#endif
|
|
|
|
#ifdef STANDALONE_TEST
|
|
extern void error(const char *fmt, ...), warning(const char *fmt, ...);
|
|
#endif
|
|
|
|
#include <sys/types.h>
|
|
#include <time.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include "pars.h"
|
|
#include "cgats.h"
|
|
|
|
#undef EMIT_KEYWORDS /* [und] Emit unknown keywords by default */
|
|
#define REAL_SIGDIG 6 /* [6] Number of significant digits in real representation */
|
|
|
|
static int cgats_read(cgats *p, cgatsFile *fp);
|
|
static int find_kword(cgats *p, int table, const char *ksym);
|
|
static int find_field(cgats *p, int table, const char *fsym);
|
|
static int add_table(cgats *p, table_type tt, int oi);
|
|
static int set_table_type(cgats *p, int table, table_type tt, int oi);
|
|
static int set_table_flags(cgats *p, int table, int sup_id, int sup_kwords, int sup_fields);
|
|
static int del_table(cgats *p, int table);
|
|
static int set_cgats_type(cgats *p, const char *osym);
|
|
static int add_other(cgats *p, const char *osym);
|
|
static int get_oi(cgats *p, const char *osym);
|
|
static int add_kword(cgats *p, int table, const char *ksym, const char *kdata, const char *kcom);
|
|
static int add_field(cgats *p, int table, const char *fsym, data_type ftype);
|
|
static int add_set(cgats *p, int table, ...);
|
|
static int add_setarr(cgats *p, int table, cgats_set_elem *args);
|
|
static int get_setarr(cgats *p, int table, int set_index, cgats_set_elem *args);
|
|
static int cgats_write(cgats *p, cgatsFile *fp);
|
|
static int cgats_error(cgats *p, char **mes);
|
|
static void cgats_del(cgats *p);
|
|
|
|
static void cgats_table_free(cgats_table *t);
|
|
static void *alloc_copy_data_type(cgatsAlloc *al, data_type ktype, void *dpoint);
|
|
static int reserved_kword(const char *ksym);
|
|
static int standard_kword(const char *ksym);
|
|
static data_type standard_field(const char *fsym);
|
|
static int cs_has_ws(const char *cs);
|
|
static char *quote_cs(cgatsAlloc *al, const char *cs);
|
|
static int clear_fields(cgats *p, int table);
|
|
static int add_kword_at(cgats *p, int table, int pos, const char *ksym, const char *kdatak, const char *kcom);
|
|
static int add_data_item(cgats *p, int table, void *data);
|
|
static void unquote_cs(char *cs);
|
|
static data_type guess_type(const char *cs);
|
|
static void real_format(double value, int nsd, char *fmt);
|
|
|
|
#ifdef COMBINED_STD
|
|
static int cgats_read_name(cgats *p, const char *filename);
|
|
static int cgats_write_name(cgats *p, const char *filename);
|
|
#endif
|
|
|
|
static const char *data_type_desc[] =
|
|
{ "real", "integer", "char string", "non-quoted char string", "no type" };
|
|
|
|
/* Create an empty cgats object */
|
|
/* Return NULL on error */
|
|
cgats *new_cgats_al(
|
|
cgatsAlloc *al /* memory allocator */
|
|
) {
|
|
cgats *p;
|
|
|
|
if ((p = (cgats *) al->calloc(al, 1, sizeof(cgats))) == NULL) {
|
|
return NULL;
|
|
}
|
|
p->al = al; /* Heap allocator */
|
|
|
|
/* Initialize the methods */
|
|
p->find_kword = find_kword;
|
|
p->find_field = find_field;
|
|
p->read = cgats_read;
|
|
p->add_table = add_table;
|
|
p->set_table_type = set_table_type;
|
|
p->set_table_flags = set_table_flags;
|
|
p->del_table = del_table;
|
|
p->set_cgats_type = set_cgats_type;
|
|
p->add_other = add_other;
|
|
p->get_oi = get_oi;
|
|
p->add_kword = add_kword;
|
|
p->add_kword_at = add_kword_at;
|
|
p->add_field = add_field;
|
|
p->add_set = add_set;
|
|
p->add_setarr = add_setarr;
|
|
p->get_setarr = get_setarr;
|
|
p->write = cgats_write;
|
|
p->error = cgats_error;
|
|
p->del = cgats_del;
|
|
|
|
#ifndef SEPARATE_STD
|
|
p->read_name = cgats_read_name;
|
|
p->write_name = cgats_write_name;
|
|
#else
|
|
p->read_name = NULL;
|
|
p->write_name = NULL;
|
|
#endif
|
|
|
|
#ifdef EMIT_KEYWORDS
|
|
p->emit_keywords = 1;
|
|
#endif
|
|
|
|
return p;
|
|
}
|
|
|
|
static int err(cgats *p, int errc, const char *fmt, ...);
|
|
|
|
/* new_cgats() with default malloc allocator */
|
|
|
|
#ifndef SEPARATE_STD
|
|
#define COMBINED_STD
|
|
|
|
#include "cgatsstd.c"
|
|
|
|
#undef COMBINED_STD
|
|
#endif /* SEPARATE_STD */
|
|
|
|
/* ------------------------------------------- */
|
|
|
|
/* Implimentation function - register an error */
|
|
/* Return the error number */
|
|
static int
|
|
err(cgats *p, int errc, const char *fmt, ...) {
|
|
va_list args;
|
|
|
|
p->e.c = errc;
|
|
va_start(args, fmt);
|
|
vsprintf(p->e.m, fmt, args);
|
|
va_end(args);
|
|
|
|
/* If this is the first registered error */
|
|
if (p->fe.c != 0) {
|
|
p->fe.c = p->e.c;
|
|
strcpy(p->fe.m, p->e.m);
|
|
}
|
|
|
|
return errc;
|
|
}
|
|
|
|
/* Define methods */
|
|
|
|
/* Return error code and message */
|
|
/* for the first error, if any error */
|
|
/* has occured since object creation. */
|
|
static int cgats_error(
|
|
cgats *p,
|
|
char **mes
|
|
) {
|
|
if (p->fe.c != 0) {
|
|
if (mes != NULL)
|
|
*mes = p->fe.m;
|
|
return p->fe.c;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* ------------------------------------------- */
|
|
|
|
/* Free the cgats object */
|
|
static void
|
|
cgats_del(cgats *p) {
|
|
int i;
|
|
cgatsAlloc *al = p->al;
|
|
int del_al = p->del_al;
|
|
|
|
/* Free all the user defined file identifiers */
|
|
if (p->cgats_type != NULL) {
|
|
al->free(al, p->cgats_type);
|
|
}
|
|
|
|
if (p->others != NULL) {
|
|
for (i = 0; i < p->nothers; i++)
|
|
if(p->others[i] != NULL)
|
|
al->free(al, p->others[i]);
|
|
al->free(al, p->others);
|
|
}
|
|
|
|
/* Free contents of all the tables */
|
|
for (i = 0; i < p->ntables; i++)
|
|
cgats_table_free(&p->t[i]);
|
|
|
|
/* Free the table structures */
|
|
if (p->t != NULL)
|
|
al->free(al, p->t);
|
|
|
|
al->free(al, p);
|
|
|
|
if (del_al) /* We are responsible for deleting allocator */
|
|
al->del(al);
|
|
}
|
|
|
|
/* Free up the contents of a cgats_table struct */
|
|
static void
|
|
cgats_table_free(cgats_table *t) {
|
|
cgatsAlloc *al = t->al;
|
|
int i,j;
|
|
|
|
/* Free all the keyword symbols */
|
|
if (t->ksym != NULL) {
|
|
for (i = 0; i < t->nkwords; i++)
|
|
if(t->ksym[i] != NULL)
|
|
al->free(al, t->ksym[i]);
|
|
al->free(al, t->ksym);
|
|
}
|
|
/* Free all the keyword values */
|
|
if (t->kdata != NULL) {
|
|
for (i = 0; i < t->nkwords; i++)
|
|
if(t->kdata[i] != NULL)
|
|
al->free(al, t->kdata[i]);
|
|
al->free(al, t->kdata);
|
|
}
|
|
/* Free all the keyword comments */
|
|
if (t->kcom != NULL) {
|
|
for (i = 0; i < t->nkwords; i++)
|
|
if(t->kcom[i] != NULL)
|
|
al->free(al, t->kcom[i]);
|
|
al->free(al, t->kcom);
|
|
}
|
|
|
|
/* Free all the field symbols */
|
|
if (t->fsym != NULL) {
|
|
for (i = 0; i < t->nfields; i++)
|
|
if(t->fsym[i] != NULL)
|
|
al->free(al, t->fsym[i]);
|
|
al->free(al, t->fsym);
|
|
}
|
|
/* Free array of field types */
|
|
if (t->ftype != NULL)
|
|
al->free(al, t->ftype);
|
|
/* Free all the original fields text values */
|
|
if (t->rfdata != NULL) {
|
|
for (j = 0; j < t->nsets; j++)
|
|
if (t->rfdata[j] != NULL) {
|
|
for (i = 0; i < t->nfields; i++)
|
|
if(t->rfdata[j][i] != NULL)
|
|
al->free(al, t->rfdata[j][i]);
|
|
al->free(al, t->rfdata[j]);
|
|
}
|
|
al->free(al, t->rfdata);
|
|
}
|
|
/* Free all the fields values */
|
|
if (t->fdata != NULL) {
|
|
for (j = 0; j < t->nsets; j++)
|
|
if (t->fdata[j] != NULL) {
|
|
for (i = 0; i < t->nfields; i++)
|
|
if(t->fdata[j][i] != NULL)
|
|
al->free(al, t->fdata[j][i]);
|
|
al->free(al, t->fdata[j]);
|
|
}
|
|
al->free(al, t->fdata);
|
|
}
|
|
}
|
|
|
|
/* Return index of the keyword, -1 on fail */
|
|
/* -2 on illegal table index, message in err & errc */
|
|
static int
|
|
find_kword(cgats *p, int table, const char *ksym) {
|
|
int i;
|
|
cgats_table *t;
|
|
|
|
p->e.c = 0;
|
|
p->e.m[0] = '\000';
|
|
|
|
if (table < 0 || table >= p->ntables)
|
|
return err(p, -2, "cgats.find_kword(), table number '%d' is out of range",table);
|
|
t = &p->t[table];
|
|
|
|
if (ksym == NULL || ksym[0] == '\000')
|
|
return -1;
|
|
|
|
for (i = 0; i < t->nkwords; i ++) {
|
|
if (t->ksym[i] != NULL && t->kdata[i] != NULL
|
|
&& strcmp(t->ksym[i],ksym) == 0)
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/* Return index of the field, -1 on fail */
|
|
/* -2 on illegal table index, message in err & errc */
|
|
static int
|
|
find_field(cgats *p, int table, const char *fsym) {
|
|
int i;
|
|
cgats_table *t;
|
|
|
|
p->e.c = 0;
|
|
p->e.m[0] = '\000';
|
|
|
|
if (table < 0 || table >= p->ntables)
|
|
return err(p, -2, "cgats.find_field(), table number '%d' is out of range",table);
|
|
t = &p->t[table];
|
|
|
|
if (fsym == NULL || fsym[0] == '\000')
|
|
return -1;
|
|
|
|
for (i = 0; i < t->nfields; i ++)
|
|
if (strcmp(t->fsym[i],fsym) == 0)
|
|
return i;
|
|
|
|
return -1;
|
|
}
|
|
|
|
/* Read a cgats file into structure */
|
|
/* returns 0 normally, -ve if there was an error, */
|
|
/* and p->e.c and p->e.m will be valid */
|
|
static int
|
|
cgats_read(cgats *p, cgatsFile *fp) {
|
|
parse *pp;
|
|
/* Read states */
|
|
#define R_IDENT 0 /* Reading file identifier */
|
|
#define R_KWORDS 1 /* Reading keywords */
|
|
#define R_KWORD_VALUE 2 /* Reading keywords values */
|
|
#define R_FIELDS 3 /* Reading field declarations */
|
|
#define R_DATA 4 /* Reading data in set */
|
|
int rstate = R_IDENT;
|
|
int tablef = 0; /* Current table we should be filling */
|
|
int expsets = 0; /* Expected number of sets */
|
|
char *kw = NULL; /* keyword symbol */
|
|
|
|
p->e.c = 0;
|
|
p->e.m[0] = '\000';
|
|
|
|
if ((pp = new_parse_al(p->al, fp)) == NULL) {
|
|
DBGF((DBGA,"Failed to open parser for file\n"));
|
|
return err(p, -1, "Unable to create file parser for file '%s'",fp->fname(fp));
|
|
}
|
|
|
|
/* Setup our token parsing charaters */
|
|
/* Terminators, Not Read, Comment start, Quote characters */
|
|
pp->add_del(pp, " \t"," \t", "#", "\"");
|
|
|
|
/* Read in the file */
|
|
for (;;) {
|
|
char *tp; /* Token string */
|
|
|
|
/* Fetch the next token */
|
|
while ((tp = pp->get_token(pp)) == NULL) {
|
|
int rc;
|
|
if (pp->e.c != 0) { /* get_token got an error */
|
|
err(p, -1, "%s", pp->e.m);
|
|
pp->del(pp);
|
|
DBGF((DBGA,"Get token got error '%s'\n",pp->e.m));
|
|
return p->e.c;
|
|
}
|
|
if ((rc = pp->read_line(pp)) == 0)
|
|
break; /* End of file */
|
|
else if (rc == -1) { /* read_line got an error */
|
|
err(p, -1, "%s", pp->e.m);
|
|
pp->del(pp);
|
|
DBGF((DBGA,"Read line got error '%s'\n",pp->e.m));
|
|
return p->e.c;
|
|
}
|
|
}
|
|
if (tp == NULL)
|
|
break; /* EOF */
|
|
|
|
/* This seems unlikely and will cause err() to barf */
|
|
if (strlen(tp) > CGATS_ERRM_LENGTH/2) {
|
|
tp[CGATS_ERRM_LENGTH/2] = '\000';
|
|
err(p,-1,"Read line got symbol '%s' that's too long\n",tp);
|
|
pp->del(pp);
|
|
return p->e.c;
|
|
}
|
|
|
|
switch(rstate) {
|
|
case R_IDENT: /* Expecting file identifier */
|
|
case R_KWORDS: { /* Expecting keyword, field def or data */
|
|
table_type tt = tt_none;
|
|
int oi = 0; /* Index if tt_other */
|
|
|
|
DBGF((DBGA,"Got kword '%s'\n",tp));
|
|
if (rstate == R_IDENT) {
|
|
DBGF((DBGA,"Expecting file identifier\n"));
|
|
}
|
|
|
|
/* The standard says that keywords have to be at the start of a line */
|
|
if (pp->token != 1) /* Be robust and ignore any problems */
|
|
break;
|
|
|
|
/* See if we have a file identifier */
|
|
if(strcmp(tp,"IT8.7/1") == 0)
|
|
tt = it8_7_1;
|
|
else if(strcmp(tp,"IT8.7/2") == 0)
|
|
tt = it8_7_2;
|
|
else if(strcmp(tp,"IT8.7/3") == 0)
|
|
tt = it8_7_3;
|
|
else if(strcmp(tp,"IT8.7/4") == 0)
|
|
tt = it8_7_4;
|
|
else if(strcmp(tp,"CGATS.5") == 0)
|
|
tt = cgats_5;
|
|
else if(strncmp(tp,"CGATS.",6) == 0) { /* Variable CGATS type */
|
|
tt = cgats_X;
|
|
if (p->cgats_type != NULL)
|
|
p->al->free(p->al, p->cgats_type);
|
|
if ((p->cgats_type = (char *)p->al->calloc(p->al,
|
|
(strlen(tp)+1), sizeof(char))) == NULL) {
|
|
err(p,-1,"Failed to malloc space for CGATS.X keyword");
|
|
pp->del(pp);
|
|
return p->e.c;
|
|
}
|
|
strcpy(p->cgats_type,tp);
|
|
DBGF((DBGA,"Found CGATS file identifier\n"));
|
|
rstate = R_KWORDS;
|
|
} else { /* See if it is an 'other' file identifier */
|
|
int iswild = 0;
|
|
DBGF((DBGA,"Checking for 'other' identifier\n"));
|
|
|
|
/* Check for non-wildcard "other" */
|
|
for (oi = 0; oi < p->nothers; oi++) {
|
|
|
|
if (p->others[oi][0] == '\000') { /* Wild card */
|
|
iswild = 1;
|
|
continue;
|
|
}
|
|
/* If "other" is a specific string */
|
|
if(strcmp(tp,p->others[oi]) == 0) {
|
|
DBGF((DBGA,"Matches 'other' %s\n",p->others[oi]));
|
|
tt = tt_other;
|
|
rstate = R_KWORDS;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (tt == tt_none
|
|
&& iswild
|
|
&& rstate == R_IDENT /* First token after a table */
|
|
&& standard_kword(tp) == 0 /* And not an obvious kword */
|
|
&& reserved_kword(tp) == 0) {
|
|
DBGF((DBGA,"Matches 'other' wildcard\n"));
|
|
if ((oi = add_other(p, tp)) == -2) {
|
|
pp->del(pp);
|
|
DBGF((DBGA,"add_other for wilidcard failed\n"));
|
|
return p->e.c;
|
|
}
|
|
tt = tt_other;
|
|
rstate = R_KWORDS;
|
|
}
|
|
}
|
|
|
|
/* First ever token must be file identifier */
|
|
if (tt == tt_none && p->ntables == 0) {
|
|
err(p,-1,"Error at line %d of file '%s': No CGATS file identifier found",pp->line,fp->fname(fp));
|
|
pp->del(pp);
|
|
DBGF((DBGA,"Failed to match file identifier\n"));
|
|
return p->e.c;
|
|
}
|
|
|
|
/* Any token after previous table has data finished */
|
|
/* causes a new table to be created. */
|
|
if (p->ntables == tablef) {
|
|
|
|
if (tt != tt_none) { /* Current token is a file identifier */
|
|
DBGF((DBGA,"Got file identifier, adding plain table\n"));
|
|
if (add_table(p, tt, oi) < 0) {
|
|
pp->del(pp);
|
|
DBGF((DBGA,"Add table failed\n"));
|
|
return p->e.c;
|
|
}
|
|
} else { /* Carry everything over from previous table the table type */
|
|
int i;
|
|
cgats_table *pt;
|
|
int ct;
|
|
|
|
DBGF((DBGA,"No file identifier, adding table copy of previous\n"));
|
|
|
|
if (add_table(p, p->t[p->ntables-1].tt, p->t[p->ntables-1].oi) < 0) {
|
|
pp->del(pp);
|
|
DBGF((DBGA,"Add table failed\n"));
|
|
return p->e.c;
|
|
}
|
|
|
|
pt = &p->t[p->ntables-2];
|
|
ct = p->ntables-1;
|
|
|
|
for (i = 0; i < pt->nkwords; i++) {
|
|
if (p->add_kword(p, ct, pt->ksym[i], pt->kdata[i], pt->kcom[i]) < 0) {
|
|
pp->del(pp);
|
|
DBGF((DBGA,"Add keyword failed\n"));
|
|
return p->e.c;
|
|
}
|
|
}
|
|
for (i = 0; i < pt->nfields; i++)
|
|
if (p->add_field(p, ct, pt->fsym[i], none_t) < 0) {
|
|
pp->del(pp);
|
|
DBGF((DBGA,"Add field failed\n"));
|
|
return p->e.c;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* If not a file identifier */
|
|
if (tt == tt_none) {
|
|
/* See if we're starting the field declarations */
|
|
if(strcmp(tp,"BEGIN_DATA_FORMAT") == 0) {
|
|
rstate = R_FIELDS;
|
|
if (clear_fields(p, p->ntables-1) < 0) {
|
|
pp->del(pp);
|
|
DBGF((DBGA,"Clear field failed\n"));
|
|
return p->e.c;
|
|
}
|
|
break;
|
|
}
|
|
if(strcmp(tp,"SAMPLE_ID") == 0) { /* Faulty table - cope gracefully */
|
|
rstate = R_FIELDS;
|
|
if (clear_fields(p, p->ntables-1) < 0) {
|
|
pp->del(pp);
|
|
DBGF((DBGA,"Clear field failed\n"));
|
|
return p->e.c;
|
|
}
|
|
goto first_field;
|
|
}
|
|
|
|
if(strcmp(tp,"BEGIN_DATA") == 0) {
|
|
rstate = R_DATA;
|
|
break;
|
|
}
|
|
/* Else must be a keyword */
|
|
if ((kw = (char *)alloc_copy_data_type(p->al, cs_t, (void *)tp)) == NULL) {
|
|
err(p, -2, "cgats.alloc_copy_data_type() malloc fail");
|
|
pp->del(pp);
|
|
DBGF((DBGA,"Alloc data type failed\n"));
|
|
return p->e.c;
|
|
}
|
|
rstate = R_KWORD_VALUE;
|
|
}
|
|
break;
|
|
}
|
|
case R_KWORD_VALUE: {
|
|
/* Add a keyword and its value */
|
|
|
|
DBGF((DBGA,"Got keyword value '%s'\n",kw));
|
|
|
|
/* Special case for read() use */
|
|
if(strcmp(kw,"NUMBER_OF_SETS") == 0)
|
|
expsets = atoi(tp);
|
|
|
|
if (!reserved_kword(kw)) { /* Don't add reserved keywords */
|
|
int ix;
|
|
|
|
/* Replace keyword if it already exists */
|
|
unquote_cs(tp);
|
|
if ((ix = find_kword(p, p->ntables-1, kw)) < -1) {
|
|
pp->del(pp);
|
|
DBGF((DBGA,"Failed to find keyword\n"));
|
|
return p->e.c;
|
|
}
|
|
if (add_kword_at(p, p->ntables-1, ix, kw, tp, NULL) < 0) {
|
|
pp->del(pp);
|
|
DBGF((DBGA,"Failed to add keyword '%s'\n",kw));
|
|
return p->e.c;
|
|
}
|
|
}
|
|
p->al->free(p->al, kw);
|
|
rstate = R_KWORDS;
|
|
break;
|
|
}
|
|
case R_FIELDS: {
|
|
DBGF((DBGA,"Got fields value '%s'\n",tp));
|
|
|
|
/* Add a list of field name declarations */
|
|
if(strcmp(tp,"END_DATA_FORMAT") == 0) {
|
|
rstate = R_KWORDS;
|
|
break;
|
|
}
|
|
if(strcmp(tp,"BEGIN_DATA") == 0) { /* Faulty table - cope gracefully */
|
|
rstate = R_DATA;
|
|
break;
|
|
}
|
|
if(strcmp(tp,"DEVICE_NAME") == 0) { /* Faulty CB table - cope gracefully */
|
|
/* It's unlikely anyone will use DEVICE_NAME as a field name */
|
|
/* Assume this is a keyword */
|
|
if ((kw = (char *)alloc_copy_data_type(p->al, cs_t, (void *)tp)) == NULL) {
|
|
err(p, -2, "cgats.alloc_copy_data_type() malloc fail");
|
|
pp->del(pp);
|
|
DBGF((DBGA,"Alloc data type failed\n"));
|
|
return p->e.c;
|
|
}
|
|
rstate = R_KWORD_VALUE;
|
|
break;
|
|
}
|
|
first_field:; /* Direct leap - cope with faulty table */
|
|
if (p->add_field(p, p->ntables-1, tp, none_t) < 0) /* none == cs untill figure type */ {
|
|
pp->del(pp);
|
|
DBGF((DBGA,"Add field failed\n"));
|
|
return p->e.c;
|
|
}
|
|
break;
|
|
}
|
|
case R_DATA: {
|
|
cgats_table *ct = &p->t[p->ntables-1];
|
|
|
|
DBGF((DBGA,"Got data value '%s'\n",tp));
|
|
if(strcmp(tp,"END_DATA") == 0) {
|
|
int i,j;
|
|
#ifdef NEVER
|
|
if (ct->nsets == 0) {
|
|
err(p,-1,"Error at line %d of file '%s': End of data without any data being read",pp->line,fp->fname(fp));
|
|
pp->del(pp);
|
|
DBGF((DBGA,"End of data without any data being read\n"));
|
|
return p->e.c;
|
|
}
|
|
#endif // NEVER
|
|
if (expsets != 0 && ct->nsets != expsets) {
|
|
err(p,-1,"Error at line %d of file '%s': Read %d sets, expected %d sets",pp->line,fp->fname(fp),ct->nsets,expsets);
|
|
pp->del(pp);
|
|
DBGF((DBGA,"End of mimatch in number of sets\n"));
|
|
return p->e.c;
|
|
}
|
|
if (ct->ndf != 0) {
|
|
err(p,-1,"Error at line %d of file '%s': Data was not an integer multiple of fields (remainder %d out of %d)",pp->line,fp->fname(fp),ct->ndf,ct->nfields);
|
|
pp->del(pp);
|
|
DBGF((DBGA,"Not an interger multiple of fields\n"));
|
|
return p->e.c;
|
|
}
|
|
|
|
/* We now need to determine the data types */
|
|
/* and convert them appropriately */
|
|
for (i = 0; i < ct->nfields; i++) {
|
|
data_type bt = i_t, st;
|
|
for (j = 0; j < ct->nsets; j++) {
|
|
data_type ty;
|
|
ty = guess_type(((char *)ct->rfdata[j][i]));
|
|
|
|
if (ty == cs_t) {
|
|
bt = cs_t;
|
|
break; /* Early out */
|
|
} else if (ty == nqcs_t) {
|
|
if (bt == i_t || bt == r_t)
|
|
bt = ty;
|
|
} else if (ty == r_t) {
|
|
if (bt == i_t)
|
|
bt = ty;
|
|
} else { /* ty == i_t */
|
|
/* This is the default */
|
|
}
|
|
}
|
|
/* Got guessed type bt. Sanity check against known field types */
|
|
/* and promote if that seems reasonable */
|
|
st = standard_field(ct->fsym[i]);
|
|
if ((st == r_t && bt == i_t) /* If ambiguous, use standard field */
|
|
|| ((st == cs_t || st == nqcs_t) && bt == i_t) /* Promote any to string */
|
|
|| ((st == cs_t || st == nqcs_t) && bt == r_t) /* Promote any to string */
|
|
|| (st == nqcs_t && bt == cs_t)
|
|
|| (st == cs_t && bt == nqcs_t))
|
|
bt = st;
|
|
|
|
/* If standard type doesn't match what it should, throw an error */
|
|
if (st != none_t && st != bt) {
|
|
err(p, -1,"Error in file '%s': Field '%s' has unexpected type, should be '%s', is '%s'",fp->fname(fp),ct->fsym[i],data_type_desc[st],data_type_desc[bt]);
|
|
pp->del(pp);
|
|
DBGF((DBGA,"Standard field has unexpected data type\n"));
|
|
return p->e.c;
|
|
}
|
|
|
|
/* Set field type, and then convert the fields to correct type. */
|
|
ct->ftype[i] = bt;
|
|
for (j = 0; j < ct->nsets; j++) {
|
|
switch(bt) {
|
|
case r_t: {
|
|
double dv;
|
|
dv = atof((char *)ct->rfdata[j][i]);
|
|
if ((ct->fdata[j][i] = alloc_copy_data_type(p->al, bt, (void *)&dv)) == NULL) {
|
|
err(p, -2, "cgats.alloc_copy_data_type() malloc fail");
|
|
pp->del(pp);
|
|
DBGF((DBGA,"Alloc copy data type failed\n"));
|
|
return p->e.c;
|
|
}
|
|
break;
|
|
}
|
|
case i_t: {
|
|
int iv;
|
|
iv = atoi((char *)ct->rfdata[j][i]);
|
|
if ((ct->fdata[j][i] = alloc_copy_data_type(p->al, bt, (void *)&iv)) == NULL) {
|
|
err(p, -2, "cgats.alloc_copy_data_type() malloc fail");
|
|
pp->del(pp);
|
|
DBGF((DBGA,"Alloc copy data type failed\n"));
|
|
return p->e.c = -2;
|
|
}
|
|
break;
|
|
}
|
|
case cs_t:
|
|
case nqcs_t: {
|
|
char *cv;
|
|
cv = ct->rfdata[j][i];
|
|
if ((ct->fdata[j][i]
|
|
= alloc_copy_data_type(p->al, bt, (void *)cv)) == NULL) {
|
|
err(p, -2, "cgats.alloc_copy_data_type() malloc fail");
|
|
pp->del(pp);
|
|
DBGF((DBGA,"Alloc copy data type failed\n"));
|
|
return p->e.c = -2;
|
|
}
|
|
unquote_cs((char *)ct->fdata[j][i]);
|
|
break;
|
|
}
|
|
case none_t:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
tablef = p->ntables; /* Finished data for current table */
|
|
rstate = R_IDENT;
|
|
break;
|
|
}
|
|
|
|
/* Make sure fields have been decalared */
|
|
if (ct->nfields == 0) {
|
|
err(p, -1,"Error at line %d of file '%s': Found data without field definitions",pp->line,fp->fname(fp));
|
|
pp->del(pp);
|
|
DBGF((DBGA,"Found data without field definition\n"));
|
|
return p->e.c;
|
|
}
|
|
/* Add the data item */
|
|
if (add_data_item(p, p->ntables-1, tp) < 0) {
|
|
pp->del(pp);
|
|
DBGF((DBGA,"Adding data item failed\n"));
|
|
return p->e.c;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
pp->del(pp); /* Clean up the parse file */
|
|
|
|
if (p->ntables == 0)
|
|
return -1; /* Failed to load any table */
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Define the (one) variable CGATS type */
|
|
/* Return -2 & set errc and err on system error */
|
|
static int
|
|
set_cgats_type(cgats *p, const char *osym) {
|
|
cgatsAlloc *al = p->al;
|
|
|
|
p->e.c = 0;
|
|
p->e.m[0] = '\000';
|
|
if (p->cgats_type != NULL)
|
|
al->free(al, p->cgats_type);
|
|
if ((p->cgats_type = (char *)al->calloc(al, (strlen(osym)+1), sizeof(char))) == NULL)
|
|
return err(p,-2,"cgats.add_cgats_type(), malloc failed!");
|
|
strcpy(p->cgats_type,osym);
|
|
return 0;
|
|
}
|
|
|
|
/* Add an 'other' file identifier string, and return the oi. */
|
|
/* Use a zero length string to indicate a wildcard. */
|
|
/* Returns existing if already registered. */
|
|
/* Return -2 & set errc and err on system error */
|
|
static int
|
|
add_other(cgats *p, const char *osym) {
|
|
int oi;
|
|
cgatsAlloc *al = p->al;
|
|
|
|
p->e.c = 0;
|
|
p->e.m[0] = '\000';
|
|
|
|
if ((oi = get_oi(p, osym)) >= 0)
|
|
return oi;
|
|
|
|
p->nothers++;
|
|
if ((p->others = (char **)al->realloc(al, p->others, p->nothers * sizeof(char *))) == NULL)
|
|
return err(p,-2, "cgats.add_other(), realloc failed!");
|
|
if ((p->others[p->nothers-1] =
|
|
(char *)al->calloc(al, (strlen(osym)+1), sizeof(char))) == NULL)
|
|
return err(p,-2,"cgats.add_other(), malloc failed!");
|
|
strcpy(p->others[p->nothers-1],osym);
|
|
return p->nothers-1;
|
|
}
|
|
|
|
/* Return the oi of the given other type */
|
|
/* return -ve and errc and err set on error */
|
|
static int get_oi(cgats *p, const char *osym) {
|
|
int oi;
|
|
|
|
p->e.c = 0;
|
|
p->e.m[0] = '\000';
|
|
|
|
for (oi = 0; oi < p->nothers; oi++) {
|
|
if (strcmp(p->others[oi], osym) == 0)
|
|
return oi;
|
|
}
|
|
return err(p,-1,"cgats.get_oi(), failed to find '%s'!",osym);
|
|
}
|
|
|
|
/* Add a new (empty) table to the structure */
|
|
/* Return the index of the table. */
|
|
/* tt defines the table type, and oi is used if tt = tt_other */
|
|
/* Return -2 & set errc and err on system error */
|
|
static int
|
|
add_table(cgats *p, table_type tt, int oi) {
|
|
cgatsAlloc *al = p->al;
|
|
cgats_table *t;
|
|
|
|
p->e.c = 0;
|
|
p->e.m[0] = '\000';
|
|
p->ntables++;
|
|
if ((p->t = (cgats_table *) al->realloc(al, p->t, p->ntables * sizeof(cgats_table))) == NULL)
|
|
return err(p,-2, "cgats.add_table(), realloc failed!");
|
|
memset(&p->t[p->ntables-1],0,sizeof(cgats_table));
|
|
t = &p->t[p->ntables-1];
|
|
|
|
t->al = al; /* Pointer to allocator */
|
|
t->tt = tt;
|
|
t->oi = oi;
|
|
|
|
return p->ntables-1;
|
|
}
|
|
|
|
/* Override the table type */
|
|
static int
|
|
set_table_type(cgats *p, int table, table_type tt, int oi) {
|
|
cgats_table *t = &p->t[table];
|
|
|
|
t->tt = tt;
|
|
t->oi = oi;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* set or reset table flags */
|
|
/* The sup_id flag suppreses the writing of the file identifier string for the table */
|
|
/* The sup_kwords flag suppreses the writing of the standard keyword definitions for the table */
|
|
/* The sup_fields flag suppreses the writing of the field definitions for the table */
|
|
/* The assumption is that the previous tables id and/or fields will be correct for */
|
|
/* the table that have these flags set. */
|
|
/* Return -1 & set errc and err on error */
|
|
static int set_table_flags(cgats *p, int table, int sup_id, int sup_kwords, int sup_fields) {
|
|
cgats_table *t;
|
|
|
|
p->e.c = 0;
|
|
p->e.m[0] = '\000';
|
|
if (table < 0 || table >= p->ntables)
|
|
return err(p,-1, "cgats.set_table_flags(), table number '%d' is out of range",table);
|
|
t = &p->t[table];
|
|
|
|
if (sup_id == 0 && (sup_kwords != 0 || sup_fields != 0))
|
|
return err(p,-1, "cgats.set_table_flags(), Can't suppress kwords or fields if ID is not suppressed");
|
|
t->sup_id = sup_id;
|
|
t->sup_kwords = sup_kwords;
|
|
t->sup_fields = sup_fields;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* delete a table */
|
|
/* Note that any following tables will be shuffled down. */
|
|
static int del_table(cgats *p, int table) {
|
|
cgats_table *t;
|
|
|
|
p->e.c = 0;
|
|
p->e.m[0] = '\000';
|
|
if (table < 0 || table >= p->ntables)
|
|
return err(p,-1, "cgats.del_table(), table number '%d' is out of range",table);
|
|
|
|
cgats_table_free(&p->t[table]);
|
|
|
|
while ((table+1) < p->ntables) {
|
|
p->t[table] = p->t[table+1];
|
|
table++;
|
|
}
|
|
p->ntables--;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Append a new keyword/value + optional comment pair to the table */
|
|
/* If no comment is provided, kcom should be set to NULL */
|
|
/* A comment only line can be inserted amongst the keywords by providing */
|
|
/* NULL values for ksym and kdata, and the comment in kcom */
|
|
/* Return the index of the new keyword, or -1, err & errc on error */
|
|
static int
|
|
add_kword(cgats *p, int table, const char *ksym, const char *kdata, const char *kcom) {
|
|
cgats_table *t;
|
|
|
|
p->e.c = 0;
|
|
p->e.m[0] = '\000';
|
|
if (table < 0 || table >= p->ntables)
|
|
return err(p,-1, "cgats.add_kword(), table number '%d' is out of range",table);
|
|
t = &p->t[table];
|
|
|
|
return add_kword_at(p, table, t->nkwords, ksym, kdata, kcom);
|
|
}
|
|
|
|
/* Replace or append a new keyword/value pair + optional comment */
|
|
/* to the table in the given position. The keyword will be appended */
|
|
/* if it is < 0. */
|
|
/* If no comment is provided, kcom should be set to NULL */
|
|
/* A comment only line can be inserted amongst the keywords by providing */
|
|
/* NULL values for ksym and kdata, and the comment in kcom */
|
|
/* Return the index of the keyword, or -1, err & errc on error */
|
|
static int
|
|
add_kword_at(cgats *p, int table, int pos, const char *ksym, const char *kdata, const char *kcom) {
|
|
cgatsAlloc *al = p->al;
|
|
cgats_table *t;
|
|
|
|
p->e.c = 0;
|
|
p->e.m[0] = '\000';
|
|
if (table < 0 || table >= p->ntables) {
|
|
DBGF((DBGA,"add_kword_at: table is invalid\n"));
|
|
return err(p,-1, "cgats.add_kword(), table number '%d' is out of range",table);
|
|
}
|
|
t = &p->t[table];
|
|
|
|
if (ksym != NULL && cs_has_ws(ksym)) { /* oops */
|
|
DBGF((DBGA,"add_kword_at: keyword '%s' is illegal (embedded white space, quote or comment character)\n",ksym));
|
|
return err(p,-1, "cgats.add_kword(), keyword '%s'is illegal",ksym);
|
|
}
|
|
|
|
if (ksym != NULL && reserved_kword(ksym)) { /* oops */
|
|
DBGF((DBGA,"add_kword_at: keyword '%s' is illegal (reserved)\n",ksym));
|
|
return err(p,-1, "cgats.add_kword(), keyword '%s'is generated automatically",ksym);
|
|
}
|
|
|
|
if (pos < 0 || pos >= t->nkwords) { /* This is an append */
|
|
t->nkwords++;
|
|
if (t->nkwords > t->nkwordsa) { /* Allocate keyword pointers in groups of 8 */
|
|
t->nkwordsa += 8;
|
|
if ((t->ksym = (char **)al->realloc(al, t->ksym, t->nkwordsa * sizeof(char *))) == NULL)
|
|
return err(p,-2, "cgats.add_kword(), realloc failed!");
|
|
if ((t->kdata = (char **)al->realloc(al, t->kdata, t->nkwordsa * sizeof(char *)))
|
|
== NULL)
|
|
return err(p,-2, "cgats.add_kword(), realloc failed!");
|
|
if ((t->kcom = (char **)al->realloc(al, t->kcom, t->nkwordsa * sizeof(char *)))
|
|
== NULL)
|
|
return err(p,-2, "cgats.add_kword(), realloc failed!");
|
|
}
|
|
pos = t->nkwords-1;
|
|
} else { /* This is a replacement */
|
|
if (t->ksym[pos] != NULL)
|
|
al->free(al, t->ksym[pos]);
|
|
if (t->kdata[pos] != NULL)
|
|
al->free(al, t->kdata[pos]);
|
|
if (t->kcom[pos] != NULL)
|
|
al->free(al, t->kcom[pos]);
|
|
}
|
|
|
|
if (ksym != NULL) {
|
|
if ((t->ksym[pos] = (char *)alloc_copy_data_type(al, cs_t, (void *)ksym)) == NULL)
|
|
return err(p,-2,"cgats.alloc_copy_data_type() malloc fail");
|
|
} else
|
|
t->ksym[pos] = NULL;
|
|
|
|
if (kdata != NULL) {
|
|
if ((t->kdata[pos] = (char *)alloc_copy_data_type(al, cs_t, (void *)kdata)) == NULL)
|
|
return err(p,-2,"cgats.alloc_copy_data_type() malloc fail");
|
|
} else
|
|
t->kdata[pos] = NULL;
|
|
|
|
if (kcom != NULL) {
|
|
if ((t->kcom[pos] = (char *)alloc_copy_data_type(al, cs_t, (void *)kcom)) == NULL)
|
|
return err(p,-2,"cgats.alloc_copy_data_type() malloc fail");
|
|
} else
|
|
t->kcom[pos] = NULL;
|
|
return pos;
|
|
}
|
|
|
|
/* Add a new field to the table */
|
|
/* Return the index of the field */
|
|
/* return -1 or -2, errc & err on error */
|
|
static int
|
|
add_field(cgats *p, int table, const char *fsym, data_type ftype) {
|
|
cgatsAlloc *al = p->al;
|
|
cgats_table *t;
|
|
data_type st;
|
|
|
|
p->e.c = 0;
|
|
p->e.m[0] = '\000';
|
|
if (table < 0 || table >= p->ntables) {
|
|
return err(p,-1,"cgats.add_field(), table parameter out of range");
|
|
}
|
|
t = &p->t[table];
|
|
|
|
if (t->nsets != 0) {
|
|
return err(p,-1,"cgats.add_field(), attempt to add field to non-empty table");
|
|
}
|
|
|
|
/* Check the field name is reasonable */
|
|
if (cs_has_ws(fsym)) {
|
|
return err(p,-1,"cgats.add_kword(), field name '%s'is illegal",fsym);
|
|
}
|
|
|
|
if (ftype == none_t)
|
|
ftype = cs_t; /* Fudge - unknown type yet, used for reads */
|
|
else {
|
|
/* Check that the data type is reasonable */
|
|
st = standard_field(fsym);
|
|
if (st == nqcs_t && ftype == cs_t) /* Fudge - standard type to non-quoted if normal */
|
|
ftype = nqcs_t;
|
|
if (st != none_t && st != ftype) {
|
|
return err(p,-1,"cgats.add_field(): unexpected data type for standard field name");
|
|
}
|
|
}
|
|
|
|
t->nfields++;
|
|
if (t->nfields > t->nfieldsa) {
|
|
/* Allocate fields in groups of 32 */
|
|
t->nfieldsa += 32;
|
|
if ((t->fsym = (char **)al->realloc(al, t->fsym, t->nfieldsa * sizeof(char *))) == NULL) {
|
|
return err(p,-2,"cgats.add_field(), realloc failed!");
|
|
}
|
|
if ((t->ftype = (data_type *)al->realloc(al, t->ftype, t->nfieldsa * sizeof(data_type)))
|
|
== NULL) {
|
|
return err(p,-2,"cgats.add_field(), realloc failed!");
|
|
}
|
|
}
|
|
if ((t->fsym[t->nfields-1] = (char *)alloc_copy_data_type(al, cs_t, (void *)fsym)) == NULL) {
|
|
return err(p,-2,"cgats.alloc_copy_data_type() malloc fail");
|
|
}
|
|
|
|
t->ftype[t->nfields-1] = ftype;
|
|
|
|
return t->nfields-1;
|
|
}
|
|
|
|
/* Clear all fields in the table */
|
|
/* return 0 if OK */
|
|
/* return -1 or -2, errc & err on error */
|
|
static int
|
|
clear_fields(cgats *p, int table) {
|
|
cgatsAlloc *al = p->al;
|
|
int i;
|
|
cgats_table *t;
|
|
|
|
p->e.c = 0;
|
|
p->e.m[0] = '\000';
|
|
if (table < 0 || table >= p->ntables)
|
|
return err(p,-1,"cgats.clear_field(), table parameter out of range");
|
|
t = &p->t[table];
|
|
|
|
if (t->nsets != 0)
|
|
return err(p,-1,"cgats.clear_field(), attempt to clear fields in a non-empty table");
|
|
|
|
/* Free all the field symbols */
|
|
if (t->fsym != NULL) {
|
|
for (i = 0; i < t->nfields; i++)
|
|
if(t->fsym[i] != NULL)
|
|
al->free(al, t->fsym[i]);
|
|
al->free(al, t->fsym);
|
|
t->fsym = NULL;
|
|
}
|
|
|
|
/* Free array of field types */
|
|
if (t->ftype != NULL)
|
|
al->free(al, t->ftype);
|
|
t->ftype = NULL;
|
|
|
|
/* Zero all the field counters */
|
|
t->nfields = 0;
|
|
t->nfieldsa = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Add a set of data */
|
|
/* return 0 normally. */
|
|
/* return -2, -1, errc & err on error */
|
|
static int
|
|
add_set(cgats *p, int table, ...) {
|
|
cgatsAlloc *al = p->al;
|
|
va_list args;
|
|
int i;
|
|
cgats_table *t;
|
|
|
|
va_start(args, table);
|
|
|
|
p->e.c = 0;
|
|
p->e.m[0] = '\000';
|
|
if (table < 0 || table >= p->ntables)
|
|
return err(p,-1,"cgats.add_kword(), table parameter out of range");
|
|
t = &p->t[table];
|
|
|
|
if (t->nfields == 0)
|
|
return err(p,-1,"cgats.add_set(), attempt to add set when no fields are defined");
|
|
|
|
t->nsets++;
|
|
|
|
if (t->nsets > t->nsetsa) /* Allocate space for more sets */ {
|
|
/* Allocate set pointers in groups of 100 */
|
|
t->nsetsa += 100;
|
|
if ((t->fdata = (void ***)al->realloc(al, t->fdata, t->nsetsa * sizeof(void **))) == NULL)
|
|
return err(p,-2,"cgats.add_set(), realloc failed!");
|
|
}
|
|
/* Allocate set pointer to data element values */
|
|
if ((t->fdata[t->nsets-1] = (void **)al->calloc(al, t->nfields, sizeof(void *))) == NULL)
|
|
return err(p,-2,"cgats.add_set(), malloc failed!");
|
|
|
|
/* Allocate and copy data to new set */
|
|
for (i = 0; i < t->nfields; i++) {
|
|
switch(t->ftype[i]) {
|
|
case r_t: {
|
|
double dv;
|
|
dv = va_arg(args, double);
|
|
if ((t->fdata[t->nsets-1][i] = alloc_copy_data_type(al, t->ftype[i], (void *)&dv)) == NULL)
|
|
return err(p,-2,"cgats.alloc_copy_data_type() malloc fail");
|
|
break;
|
|
}
|
|
case i_t: {
|
|
int iv;
|
|
iv = va_arg(args, int);
|
|
if ((t->fdata[t->nsets-1][i] = alloc_copy_data_type(al, t->ftype[i], (void *)&iv)) == NULL)
|
|
return err(p,-2,"cgats.alloc_copy_data_type() malloc fail");
|
|
break;
|
|
}
|
|
case cs_t:
|
|
case nqcs_t: {
|
|
char *sv;
|
|
sv = va_arg(args, char *);
|
|
if ((t->fdata[t->nsets-1][i] = alloc_copy_data_type(al, t->ftype[i], (void *)sv)) == NULL)
|
|
return err(p,-2,"cgats.alloc_copy_data_type() malloc fail");
|
|
break;
|
|
}
|
|
default:
|
|
return err(p,-1,"cgats.add_set(), field has unknown data type");
|
|
}
|
|
}
|
|
va_end(args);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Add a set of data from void array */
|
|
/* (Courtesy of Neil Okamoto) */
|
|
/* return 0 normally. */
|
|
/* return -2, -1, errc & err on error */
|
|
static int
|
|
add_setarr(cgats *p, int table, cgats_set_elem *args) {
|
|
cgatsAlloc *al = p->al;
|
|
int i;
|
|
cgats_table *t;
|
|
|
|
p->e.c = 0;
|
|
p->e.m[0] = '\000';
|
|
if (table < 0 || table >= p->ntables)
|
|
return err(p,-1,"cgats.add_setarr(), table parameter out of range");
|
|
t = &p->t[table];
|
|
|
|
if (t->nfields == 0)
|
|
return err(p,-1,"cgats.add_setarr(), attempt to add set when no fields are defined");
|
|
|
|
t->nsets++;
|
|
|
|
if (t->nsets > t->nsetsa) /* Allocate space for more sets */ {
|
|
/* Allocate set pointers in groups of 100 */
|
|
t->nsetsa += 100;
|
|
if ((t->fdata = (void ***)al->realloc(al,t->fdata, t->nsetsa * sizeof(void **))) == NULL)
|
|
return err(p,-2,"cgats.add_set(), realloc failed!");
|
|
}
|
|
/* Allocate set pointer to data element values */
|
|
if ((t->fdata[t->nsets-1] = (void **)al->calloc(al, t->nfields, sizeof(void *))) == NULL)
|
|
return err(p,-2,"cgats.add_set(), malloc failed!");
|
|
|
|
/* Allocate and copy data to new set */
|
|
for (i = 0; i < t->nfields; i++) {
|
|
switch(t->ftype[i]) {
|
|
case r_t: {
|
|
double dv;
|
|
dv = args[i].d;
|
|
if ((t->fdata[t->nsets-1][i] = alloc_copy_data_type(al, t->ftype[i], (void *)&dv)) == NULL)
|
|
return err(p,-2,"cgats.alloc_copy_data_type() malloc fail");
|
|
break;
|
|
}
|
|
case i_t: {
|
|
int iv;
|
|
iv = args[i].i;
|
|
if ((t->fdata[t->nsets-1][i] = alloc_copy_data_type(al, t->ftype[i], (void *)&iv)) == NULL)
|
|
return err(p,-2,"cgats.alloc_copy_data_type() malloc fail");
|
|
break;
|
|
}
|
|
case cs_t:
|
|
case nqcs_t: {
|
|
char *sv;
|
|
sv = args[i].c;
|
|
if ((t->fdata[t->nsets-1][i] = alloc_copy_data_type(al, t->ftype[i], (void *)sv)) == NULL)
|
|
return err(p,-2,"cgats.alloc_copy_data_type() malloc fail");
|
|
break;
|
|
}
|
|
default:
|
|
return err(p,-1,"cgats.add_set(), field has unknown data type");
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Fill a suitable set_element with a set of data. */
|
|
/* Note a returned char pointer is to a string in *p */
|
|
/* return 0 normally. */
|
|
/* return -2, -1, errc & err on error */
|
|
static int
|
|
get_setarr(cgats *p, int table, int set_index, cgats_set_elem *args) {
|
|
int i;
|
|
cgats_table *t;
|
|
|
|
p->e.c = 0;
|
|
p->e.m[0] = '\000';
|
|
if (table < 0 || table >= p->ntables)
|
|
return err(p,-1,"cgats.get_setarr(), table parameter out of range");
|
|
t = &p->t[table];
|
|
if (set_index < 0 || set_index >= t->nsets)
|
|
return err(p,-1,"cgats.get_setarr(), set parameter out of range");
|
|
|
|
for (i = 0; i < t->nfields; i++) {
|
|
switch(t->ftype[i]) {
|
|
case r_t:
|
|
args[i].d = *((double *)t->fdata[set_index][i]);
|
|
break;
|
|
case i_t:
|
|
args[i].i = *((int *)t->fdata[set_index][i]);
|
|
break;
|
|
case cs_t:
|
|
case nqcs_t:
|
|
args[i].c = ((char *)t->fdata[set_index][i]);
|
|
break;
|
|
default:
|
|
return err(p,-1,"cgats.get_setarr(), field has unknown data type");
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Add an item of data to rgdata[][] from the read file. */
|
|
/* return 0 normally. */
|
|
/* return -2, -1, errc & err on error */
|
|
static int
|
|
add_data_item(cgats *p, int table, void *data) {
|
|
cgatsAlloc *al = p->al;
|
|
cgats_table *t;
|
|
|
|
p->e.c = 0;
|
|
p->e.m[0] = '\000';
|
|
if (table < 0 || table >= p->ntables)
|
|
return err(p,-1,"cgats.add_kword(), table parameter out of range");
|
|
t = &p->t[table];
|
|
|
|
if (t->nfields == 0)
|
|
return err(p,-1,"cgats.add_item(), attempt to add data when no fields are defined");
|
|
|
|
if (t->ndf == 0) { /* We're about to do the first element of a new set */
|
|
t->nsets++;
|
|
|
|
if (t->nsets > t->nsetsa) { /* Allocate space for more sets */
|
|
/* Allocate set pointers in groups of 100 */
|
|
t->nsetsa += 100;
|
|
if ((t->rfdata = (char ***)al->realloc(al, t->rfdata, t->nsetsa * sizeof(void **)))
|
|
== NULL)
|
|
return err(p,-2,"cgats.add_item(), realloc failed!");
|
|
if ((t->fdata = (void ***)al->realloc(al, t->fdata, t->nsetsa * sizeof(void **)))
|
|
== NULL)
|
|
return err(p,-2,"cgats.add_item(), realloc failed!");
|
|
}
|
|
/* Allocate set pointer to data element values */
|
|
if ((t->rfdata[t->nsets-1] = (char **)al->calloc(al, t->nfields, sizeof(void *))) == NULL)
|
|
return err(p,-2,"cgats.add_item(), malloc failed!");
|
|
if ((t->fdata[t->nsets-1] = (void **)al->calloc(al, t->nfields, sizeof(void *))) == NULL)
|
|
return err(p,-2,"cgats.add_item(), malloc failed!");
|
|
}
|
|
|
|
/* Data type is always cs_t at this point, because we haven't decided the type */
|
|
if ((t->rfdata[t->nsets-1][t->ndf] = alloc_copy_data_type(al, cs_t, data)) == NULL)
|
|
return err(p,-2,"cgats.alloc_copy_data_type() malloc fail");
|
|
|
|
if (++t->ndf >= t->nfields)
|
|
t->ndf = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Write structure into cgats file */
|
|
/* Return -ve, errc & err if there was an error */
|
|
static int
|
|
cgats_write(cgats *p, cgatsFile *fp) {
|
|
cgatsAlloc *al = p->al;
|
|
int i;
|
|
int table,set,field;
|
|
int *sfield = NULL; /* Standard field flag */
|
|
p->e.c = 0;
|
|
p->e.m[0] = '\000';
|
|
|
|
DBGF((DBGA,"CGATS write called, ntables = %d\n",p->ntables));
|
|
for (table = 0; table < p->ntables; table++) {
|
|
cgats_table *t = &p->t[table];
|
|
|
|
DBGF((DBGA,"CGATS writing table %d\n",table));
|
|
|
|
/* Figure out the standard and non-standard fields */
|
|
if (t->nfields > 0)
|
|
if ((sfield = (int *)al->calloc(al, t->nfields, sizeof(int))) == NULL)
|
|
return err(p,-2,"cgats.write(), malloc failed!");
|
|
for (field = 0; field < t->nfields; field++) {
|
|
if (standard_field(t->fsym[field]) != none_t)
|
|
sfield[field] = 1; /* Is standard */
|
|
else
|
|
sfield[field] = 0;
|
|
}
|
|
|
|
if (!t->sup_kwords) /* If not suppressed */ {
|
|
/* Make sure table has basic keywords */
|
|
if ((i = p->find_kword(p,table,"ORIGINATOR")) < 0) /* Create it */
|
|
if (p->add_kword(p,table,"ORIGINATOR","Not specified", NULL) < 0) {
|
|
al->free(al, sfield);
|
|
return p->e.c;
|
|
}
|
|
if ((i = p->find_kword(p,table,"DESCRIPTOR")) < 0) /* Create it */
|
|
if (p->add_kword(p,table,"DESCRIPTOR","Not specified", NULL) < 0) {
|
|
al->free(al, sfield);
|
|
return p->e.c;
|
|
}
|
|
if ((i = p->find_kword(p,table,"CREATED")) < 0) { /* Create it */
|
|
static char *amonths[] = {"January","February","March","April",
|
|
"May","June","July","August","September",
|
|
"October","November","December"};
|
|
time_t ctime;
|
|
struct tm *ptm;
|
|
char tcs[100];
|
|
ctime = time(NULL);
|
|
ptm = localtime(&ctime);
|
|
sprintf(tcs,"%s %d, %d",amonths[ptm->tm_mon],ptm->tm_mday,1900+ptm->tm_year);
|
|
if (p->add_kword(p,table,"CREATED",tcs, NULL) < 0) {
|
|
al->free(al, sfield);
|
|
return p->e.c;
|
|
}
|
|
}
|
|
|
|
/* And table type specific keywords */
|
|
/* (Not sure this is correct - CGATS.5 appendix J is not specific enough) */
|
|
switch(t->tt) {
|
|
case it8_7_1:
|
|
case it8_7_2: /* Physical target reference files */
|
|
if ((i = p->find_kword(p,table,"MANUFACTURER")) < 0) /* Create it */
|
|
if (p->add_kword(p,table,"MANUFACTURER","Not specified", NULL) < 0) {
|
|
al->free(al, sfield);
|
|
return p->e.c;
|
|
}
|
|
if ((i = p->find_kword(p,table,"PROD_DATE")) < 0) /* Create it */
|
|
if (p->add_kword(p,table,"PROD_DATE","Not specified", NULL) < 0) {
|
|
al->free(al, sfield);
|
|
return p->e.c;
|
|
}
|
|
if ((i = p->find_kword(p,table,"SERIAL")) < 0) /* Create it */
|
|
if (p->add_kword(p,table,"SERIAL","Not specified", NULL) < 0) {
|
|
al->free(al, sfield);
|
|
return p->e.c;
|
|
}
|
|
if ((i = p->find_kword(p,table,"MATERIAL")) < 0) /* Create it */
|
|
if (p->add_kword(p,table,"MATERIAL","Not specified", NULL) < 0) {
|
|
al->free(al, sfield);
|
|
return p->e.c;
|
|
}
|
|
break;
|
|
case it8_7_3: /* Target measurement files */
|
|
case it8_7_4:
|
|
case cgats_5:
|
|
case cgats_X:
|
|
if ((i = p->find_kword(p,table,"INSTRUMENTATION")) < 0) /* Create it */
|
|
if (p->add_kword(p,table,"INSTRUMENTATION","Not specified", NULL) < 0) {
|
|
al->free(al, sfield);
|
|
return p->e.c;
|
|
}
|
|
if ((i = p->find_kword(p,table,"MEASUREMENT_SOURCE")) < 0) /* Create it */
|
|
if (p->add_kword(p,table,"MEASUREMENT_SOURCE","Not specified", NULL) < 0) {
|
|
al->free(al, sfield);
|
|
return p->e.c;
|
|
}
|
|
if ((i = p->find_kword(p,table,"PRINT_CONDITIONS")) < 0) /* Create it */
|
|
if (p->add_kword(p,table,"PRINT_CONDITIONS","Not specified", NULL) < 0) {
|
|
al->free(al, sfield);
|
|
return p->e.c;
|
|
}
|
|
break;
|
|
case tt_other:
|
|
/* We enforce no pre-defined keywords for user defined file types */
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Output the table */
|
|
|
|
/* First the table identifier */
|
|
if (!t->sup_id) /* If not suppressed */ {
|
|
switch(t->tt) {
|
|
case it8_7_1:
|
|
if (fp->gprintf(fp,"IT8.7/1\n\n") < 0)
|
|
goto write_error;
|
|
break;
|
|
case it8_7_2:
|
|
if (fp->gprintf(fp,"IT8.7/2\n\n") < 0)
|
|
goto write_error;
|
|
break;
|
|
case it8_7_3:
|
|
if (fp->gprintf(fp,"IT8.7/3\n\n") < 0)
|
|
goto write_error;
|
|
break;
|
|
case it8_7_4:
|
|
if (fp->gprintf(fp,"IT8.7/4\n\n") < 0)
|
|
goto write_error;
|
|
break;
|
|
case cgats_5:
|
|
if (fp->gprintf(fp,"CGATS.5\n\n") < 0)
|
|
goto write_error;
|
|
break;
|
|
case cgats_X: /* variable CGATS type */
|
|
if (p->cgats_type == NULL)
|
|
goto write_error;
|
|
if (fp->gprintf(fp,"%-7s\n\n", p->cgats_type) < 0)
|
|
goto write_error;
|
|
break;
|
|
case tt_other: /* User defined file identifier */
|
|
if (fp->gprintf(fp,"%-7s\n\n",p->others[t->oi]) < 0)
|
|
goto write_error;
|
|
break;
|
|
case tt_none:
|
|
break;
|
|
}
|
|
} else { /* At least space the next table out a bit */
|
|
if (table == 0) {
|
|
al->free(al, sfield);
|
|
return err(p,-1,"cgats_write(), ID should not be suppressed on first table");
|
|
}
|
|
if (t->tt != p->t[table-1].tt || (t->tt == tt_other && t->oi != p->t[table-1].oi)) {
|
|
al->free(al, sfield);
|
|
return err(p,-1,"cgats_write(), ID should not be suppressed when table %d type is not the same as previous table",table);
|
|
}
|
|
if (fp->gprintf(fp,"\n\n") < 0)
|
|
goto write_error;
|
|
}
|
|
|
|
/* Then all the keywords */
|
|
for (i = 0; i < t->nkwords; i++) {
|
|
char *qs = NULL;
|
|
|
|
DBGF((DBGA,"CGATS writing keyword %d\n",i));
|
|
|
|
/* Keyword and data if it is present */
|
|
if (t->ksym[i] != NULL && t->kdata[i] != NULL) {
|
|
if (p->emit_keywords && !standard_kword(t->ksym[i])) { /* Do the right thing */
|
|
if ((qs = quote_cs(al, t->ksym[i])) == NULL) {
|
|
al->free(al, sfield);
|
|
return err(p,-2,"quote_cs() malloc failed!");
|
|
}
|
|
if (fp->gprintf(fp,"KEYWORD %s\n",qs) < 0) {
|
|
al->free(al, qs);
|
|
goto write_error;
|
|
}
|
|
al->free(al, qs);
|
|
}
|
|
|
|
if ((qs = quote_cs(al, t->kdata[i])) == NULL) {
|
|
al->free(al, sfield);
|
|
return err(p,-2,"quote_cs() malloc failed!");
|
|
}
|
|
if (fp->gprintf(fp,"%s %s%s",t->ksym[i],qs,
|
|
t->kcom[i] == NULL ? "\n":"\t") < 0) {
|
|
al->free(al, qs);
|
|
goto write_error;
|
|
}
|
|
al->free(al, qs);
|
|
}
|
|
/* Comment if its present */
|
|
if (t->kcom[i] != NULL) {
|
|
if (fp->gprintf(fp,"# %s\n",t->kcom[i]) < 0) {
|
|
al->free(al, qs);
|
|
goto write_error;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Then the field specification */
|
|
if (!t->sup_fields) { /* If not suppressed */
|
|
if (fp->gprintf(fp,"\n") < 0)
|
|
goto write_error;
|
|
|
|
/* Declare any non-standard fields */
|
|
for (field = 0; field < t->nfields; field++) {
|
|
if (p->emit_keywords && !sfield[field]) /* Non-standard */ {
|
|
char *qs;
|
|
if ((qs = quote_cs(al, t->fsym[field])) == NULL) {
|
|
al->free(al, sfield);
|
|
return err(p,-2,"quote_cs() malloc failed!");
|
|
}
|
|
if (fp->gprintf(fp,"KEYWORD %s\n",qs) < 0) {
|
|
al->free(al, qs);
|
|
goto write_error;
|
|
}
|
|
al->free(al, qs);
|
|
}
|
|
}
|
|
|
|
if (fp->gprintf(fp,"NUMBER_OF_FIELDS %d\n",t->nfields) < 0)
|
|
goto write_error;
|
|
if (fp->gprintf(fp,"BEGIN_DATA_FORMAT\n") < 0)
|
|
goto write_error;
|
|
for (field = 0; field < t->nfields; field ++) {
|
|
DBGF((DBGA,"CGATS writing field %d\n",field));
|
|
if (fp->gprintf(fp,"%s ",t->fsym[field]) < 0)
|
|
goto write_error;
|
|
}
|
|
if (fp->gprintf(fp,"\nEND_DATA_FORMAT\n") < 0)
|
|
goto write_error;
|
|
} else { /* Check that it is safe to suppress fields */
|
|
cgats_table *pt = &p->t[table-1];
|
|
if (table == 0) {
|
|
al->free(al, sfield);
|
|
return err(p,-1,"cgats_write(), Fields should not be suppressed on first table");
|
|
}
|
|
if (t->nfields != pt->nfields) {
|
|
al->free(al, sfield);
|
|
return err(p,-1,"cgats_write(), Fields should not be suppressed when table %d different number than previous table",table);
|
|
}
|
|
for (field = 0; field < t->nfields; field ++)
|
|
if (strcmp(t->fsym[field],pt->fsym[field]) != 0
|
|
|| t->ftype[field] != pt->ftype[field]) {
|
|
al->free(al, sfield);
|
|
return err(p,-1,"cgats_write(), Fields should not be suppressed when table %d types is not the same as previous table",table);
|
|
}
|
|
}
|
|
|
|
/* Then the actual data */
|
|
if (fp->gprintf(fp,"\nNUMBER_OF_SETS %d\n",t->nsets) < 0)
|
|
goto write_error;
|
|
if (fp->gprintf(fp,"BEGIN_DATA\n") < 0)
|
|
goto write_error;
|
|
for (set = 0; set < t->nsets; set++) {
|
|
DBGF((DBGA,"CGATS writing set %d\n",set));
|
|
for (field = 0; field < t->nfields; field++) {
|
|
data_type tt;
|
|
if (t->ftype[field] == r_t) {
|
|
char fmt[30];
|
|
double val = *((double *)t->fdata[set][field]);
|
|
real_format(val, REAL_SIGDIG, fmt);
|
|
strcat(fmt," ");
|
|
if (fp->gprintf(fp,fmt,val) < 0)
|
|
goto write_error;
|
|
} else if (t->ftype[field] == i_t) {
|
|
if (fp->gprintf(fp,"%d ",*((int *)t->fdata[set][field])) < 0)
|
|
goto write_error;
|
|
} else if (t->ftype[field] == nqcs_t
|
|
&& !cs_has_ws((char *)t->fdata[set][field])
|
|
&& (sfield[field] || (tt = guess_type((char *)t->fdata[set][field]),
|
|
tt != i_t && tt != r_t))) {
|
|
/* We can only print a non-quote string if it doesn't contain white space, */
|
|
/* quote or comment characters, and if it is a standard field or */
|
|
/* can't be mistaken for a number. */
|
|
if (fp->gprintf(fp,"%s ",(char *)t->fdata[set][field]) < 0)
|
|
goto write_error;
|
|
} else if (t->ftype[field] == nqcs_t
|
|
|| t->ftype[field] == cs_t) {
|
|
char *qs;
|
|
if ((qs = quote_cs(al, (char *)t->fdata[set][field])) == NULL) {
|
|
al->free(al, sfield);
|
|
return err(p,-2,"quote_cs() malloc failed!");
|
|
}
|
|
if (fp->gprintf(fp,"%s ",qs) < 0) {
|
|
al->free(al, qs);
|
|
goto write_error;
|
|
}
|
|
al->free(al, qs);
|
|
} else {
|
|
al->free(al, sfield);
|
|
return err(p,-1,"cgats_write(), illegal data type found");
|
|
}
|
|
}
|
|
if (fp->gprintf(fp,"\n") < 0)
|
|
goto write_error;
|
|
}
|
|
if (fp->gprintf(fp,"END_DATA\n") < 0)
|
|
goto write_error;
|
|
|
|
if (sfield != NULL)
|
|
al->free(al, sfield);
|
|
sfield = NULL;
|
|
}
|
|
return 0;
|
|
|
|
write_error:
|
|
err(p,-1,"Write error to file '%s'",fp->fname(fp));
|
|
if (sfield != NULL)
|
|
al->free(al, sfield);
|
|
return p->e.c;
|
|
}
|
|
|
|
/* Allocate space for data with given type, and copy it from source */
|
|
/* Return NULL if alloc failed, or unknown data type */
|
|
static void *
|
|
alloc_copy_data_type(cgatsAlloc *al, data_type dtype, void *dpoint) {
|
|
switch(dtype) {
|
|
case r_t: { /* Real value */
|
|
double *p;
|
|
if ((p = (double *)al->calloc(al, 1, sizeof(double))) == NULL)
|
|
return NULL;
|
|
*p = *((double *)dpoint);
|
|
return (void *)p;
|
|
}
|
|
case i_t: { /* Integer value */
|
|
int *p;
|
|
if ((p = (int *)al->calloc(al, 1, sizeof(int))) == NULL)
|
|
return NULL;
|
|
*p = *((int *)dpoint);
|
|
return (void *)p;
|
|
}
|
|
case cs_t: /* Character string */
|
|
case nqcs_t: { /* Character string */
|
|
char *p;
|
|
if ((p = (char *)al->calloc(al, (strlen(((char *)dpoint))+1), sizeof(char))) == NULL)
|
|
return NULL;
|
|
strcpy(p, (char *)dpoint);
|
|
return (void *)p;
|
|
}
|
|
case none_t:
|
|
default:
|
|
return NULL;
|
|
}
|
|
return NULL; /* Shut the compiler up */
|
|
}
|
|
|
|
/* See if the keyword name is a standard one */
|
|
/* Return non-zero if it is standard */
|
|
static int
|
|
standard_kword(const char *ksym) {
|
|
if (ksym == NULL)
|
|
return 0;
|
|
if (strcmp(ksym,"ORIGINATOR") == 0)
|
|
return 1;
|
|
if (strcmp(ksym,"DESCRIPTOR") == 0)
|
|
return 1;
|
|
if (strcmp(ksym,"CREATED") == 0)
|
|
return 1;
|
|
if (strcmp(ksym,"MANUFACTURER") == 0)
|
|
return 1;
|
|
if (strcmp(ksym,"PROD_DATE") == 0)
|
|
return 1;
|
|
if (strcmp(ksym,"SERIAL") == 0)
|
|
return 1;
|
|
if (strcmp(ksym,"MATERIAL") == 0)
|
|
return 1;
|
|
if (strcmp(ksym,"INSTRUMENTATION") == 0)
|
|
return 1;
|
|
if (strcmp(ksym,"MEASUREMENT_SOURCE") == 0)
|
|
return 1;
|
|
if (strcmp(ksym,"PRINT_CONDITIONS") == 0)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/* See if the keyword name is reserved. */
|
|
/* (code generates it automatically) */
|
|
/* Return non-zero if it is reserved */
|
|
static int
|
|
reserved_kword(const char *ksym) {
|
|
if (ksym == NULL)
|
|
return 0;
|
|
if (strcmp(ksym,"NUMBER_OF_FIELDS") == 0)
|
|
return 1;
|
|
if (strcmp(ksym,"BEGIN_DATA_FORMAT") == 0)
|
|
return 1;
|
|
if (strcmp(ksym,"END_DATA_FORMAT") == 0)
|
|
return 1;
|
|
if (strcmp(ksym,"NUMBER_OF_SETS") == 0)
|
|
return 1;
|
|
if (strcmp(ksym,"BEGIN_DATA") == 0)
|
|
return 1;
|
|
if (strcmp(ksym,"END_DATA") == 0)
|
|
return 1;
|
|
if (strcmp(ksym,"KEYWORD") == 0)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/* See if the field name is a standard one */
|
|
/* with an expected data type */
|
|
static data_type
|
|
standard_field(const char *fsym) {
|
|
if (strcmp(fsym,"SAMPLE_ID") == 0)
|
|
return nqcs_t;
|
|
if (strcmp(fsym,"STRING") == 0)
|
|
return cs_t;
|
|
if (strncmp(fsym,"CMYK_",5) == 0) {
|
|
if (fsym[5] == 'C')
|
|
return r_t;
|
|
if (fsym[5] == 'M')
|
|
return r_t;
|
|
if (fsym[5] == 'Y')
|
|
return r_t;
|
|
if (fsym[5] == 'K')
|
|
return r_t;
|
|
return none_t;
|
|
}
|
|
/* Non standard, but logical */
|
|
if (strncmp(fsym,"CMY_",4) == 0) {
|
|
if (fsym[4] == 'C')
|
|
return r_t;
|
|
if (fsym[4] == 'M')
|
|
return r_t;
|
|
if (fsym[4] == 'Y')
|
|
return r_t;
|
|
return none_t;
|
|
}
|
|
if (strncmp(fsym,"D_",2) == 0) {
|
|
if (strcmp(fsym + 2, "RED") == 0)
|
|
return r_t;
|
|
if (strcmp(fsym + 2, "GREEN") == 0)
|
|
return r_t;
|
|
if (strcmp(fsym + 2, "BLUE") == 0)
|
|
return r_t;
|
|
if (strcmp(fsym + 2, "VIS") == 0)
|
|
return r_t;
|
|
return none_t;
|
|
}
|
|
if (strncmp(fsym,"RGB_",4) == 0) {
|
|
if (fsym[4] == 'R')
|
|
return r_t;
|
|
if (fsym[4] == 'G')
|
|
return r_t;
|
|
if (fsym[4] == 'B')
|
|
return r_t;
|
|
return none_t;
|
|
}
|
|
if (strncmp(fsym,"SPECTRAL_",9) == 0) {
|
|
if (strcmp(fsym + 9, "NM") == 0)
|
|
return r_t;
|
|
if (strcmp(fsym + 9, "PCT") == 0)
|
|
return r_t;
|
|
return none_t;
|
|
}
|
|
if (strncmp(fsym,"XYZ_",4) == 0) {
|
|
if (fsym[4] == 'X')
|
|
return r_t;
|
|
if (fsym[4] == 'Y')
|
|
return r_t;
|
|
if (fsym[4] == 'Z')
|
|
return r_t;
|
|
return none_t;
|
|
}
|
|
if (strncmp(fsym,"XYY_",4) == 0) {
|
|
if (fsym[4] == 'X')
|
|
return r_t;
|
|
if (fsym[4] == 'Y')
|
|
return r_t;
|
|
if (strcmp(fsym + 4, "CAPY") == 0)
|
|
return r_t;
|
|
return none_t;
|
|
}
|
|
if (strncmp(fsym,"LAB_",4) == 0) {
|
|
if (fsym[4] == 'L')
|
|
return r_t;
|
|
if (fsym[4] == 'A')
|
|
return r_t;
|
|
if (fsym[4] == 'B')
|
|
return r_t;
|
|
if (fsym[4] == 'C')
|
|
return r_t;
|
|
if (fsym[4] == 'H')
|
|
return r_t;
|
|
if (strcmp(fsym + 4, "DE") == 0)
|
|
return r_t;
|
|
return none_t;
|
|
}
|
|
if (strncmp(fsym,"STDEV_",6) == 0) {
|
|
if (fsym[6] == 'X')
|
|
return r_t;
|
|
if (fsym[6] == 'Y')
|
|
return r_t;
|
|
if (fsym[6] == 'Z')
|
|
return r_t;
|
|
if (fsym[6] == 'L')
|
|
return r_t;
|
|
if (fsym[6] == 'A')
|
|
return r_t;
|
|
if (fsym[6] == 'B')
|
|
return r_t;
|
|
if (strcmp(fsym + 6, "DE") == 0)
|
|
return r_t;
|
|
return none_t;
|
|
}
|
|
return none_t;
|
|
}
|
|
|
|
/* Return non-zero if char string has an embedded */
|
|
/* white space, quote or comment character. */
|
|
static int
|
|
cs_has_ws(const char *cs)
|
|
{
|
|
int i;
|
|
for (i = 0; cs[i] != '\000'; i++)
|
|
{
|
|
switch (cs[i])
|
|
{
|
|
case ' ':
|
|
case '\r':
|
|
case '\n':
|
|
case '\t':
|
|
case '"':
|
|
case '#':
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Return a string with quotes added */
|
|
/* Return NULL if malloc failed */
|
|
/* (Returned string should be free()'d after use) */
|
|
static char *
|
|
quote_cs(cgatsAlloc *al, const char *cs) {
|
|
int i,j;
|
|
char *rs;
|
|
/* see how much space we need for returned string */
|
|
for (i = 0, j = 3; cs[i] != '\000'; i++, j++)
|
|
if (cs[i] == '"')
|
|
j++;
|
|
if ((rs = (char *)al->calloc(al, j, sizeof(char))) == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
j = 0;
|
|
rs[j++] = '"';
|
|
for (i = 0; cs[i] != '\000'; i++, j++) {
|
|
if (cs[i] == '"')
|
|
rs[j++] = '"';
|
|
rs[j] = cs[i];
|
|
}
|
|
rs[j++] = '"';
|
|
rs[j] = '\000';
|
|
return rs;
|
|
}
|
|
|
|
/* Remove quotes from the string */
|
|
static void
|
|
unquote_cs(char *cs) {
|
|
int sl,i,j;
|
|
int s; /* flag - set if skipped last character */
|
|
|
|
sl = strlen(cs);
|
|
|
|
if (sl < 2 || cs[0] != '"' || cs[sl-1] != '"')
|
|
return;
|
|
|
|
for (i = 1, j = 0, s = 1; i < (sl-1); i++) {
|
|
if (s == 1 || cs[i-1] != '"' || cs[i] != '"') {
|
|
cs[j++] = cs[i]; /* skip second " in a row */
|
|
s = 0;
|
|
} else
|
|
s = 1;
|
|
}
|
|
cs[j] = '\000';
|
|
|
|
return;
|
|
}
|
|
|
|
/* Guess the data type from the string */
|
|
static data_type
|
|
guess_type(const char *cs)
|
|
{
|
|
int i;
|
|
int rf = 0;
|
|
|
|
/* See if its a quoted string */
|
|
if (cs[0] == '"')
|
|
{
|
|
for (i = 1;cs[i] != '\000';i++);
|
|
if (i >= 2 && cs[i-1] == '"')
|
|
return cs_t;
|
|
return nqcs_t;
|
|
}
|
|
/* See if its an integer or real */
|
|
for (i = 0;;i++)
|
|
{
|
|
char c = cs[i];
|
|
if (c == '\000')
|
|
break;
|
|
if (c >= '0' && c <= '9') /* Strictly numeric */
|
|
continue;
|
|
if (c == '-' && /* Allow appropriate '-' */
|
|
( i == 0 ||
|
|
( i > 0 && (cs[i-1] == 'e' || cs[i-1] == 'E'))))
|
|
continue;
|
|
if (c == '+' && /* Allow appropriate '+' */
|
|
( i == 0 ||
|
|
( i > 0 && (cs[i-1] == 'e' || cs[i-1] == 'E'))))
|
|
continue;
|
|
if (!(rf & 3) && c == '.') /* Allow one '.' before 'e' in real */
|
|
{
|
|
rf |= 1;
|
|
continue;
|
|
}
|
|
if (!(rf & 2) && (c == 'e' || c == 'E')) /* Allow one 'e' in real */
|
|
{
|
|
rf |= 2;
|
|
continue;
|
|
}
|
|
/* Not numeric or incorrect 'e' or '.' or '-' or '+' */
|
|
return nqcs_t;
|
|
}
|
|
if (rf)
|
|
return r_t;
|
|
return i_t;
|
|
}
|
|
|
|
/* Set the character format to the appropriate printf() */
|
|
/* format given the real value and the desired number of significant digits. */
|
|
/* We try to do this while not using the %e format for normal values. */
|
|
/* The fmt string space is assumed to be big enough to contain the format */
|
|
static void
|
|
real_format(double value, int nsd, char *fmt) {
|
|
int ndigs;
|
|
int tot = nsd + 1;
|
|
int xtot = tot;
|
|
if (value == 0.0) {
|
|
sprintf(fmt,"%%%d.%df",tot,tot-2);
|
|
return;
|
|
}
|
|
if (value != value) { /* Hmm. A nan */
|
|
sprintf(fmt,"%%f");
|
|
return;
|
|
}
|
|
if (value < 0.0) {
|
|
value = -value;
|
|
xtot++;
|
|
}
|
|
if (value < 1.0) {
|
|
int thr = -5;
|
|
ndigs = (int)(log10(value));
|
|
if (ndigs > 310 || ndigs < -310) { /* Protect against silliness.. */
|
|
strcpy(fmt,"%g");
|
|
return;
|
|
}
|
|
if (ndigs <= thr) {
|
|
sprintf(fmt,"%%%d.%de",xtot,tot-2);
|
|
return;
|
|
}
|
|
sprintf(fmt,"%%%d.%df",xtot-ndigs,nsd-ndigs);
|
|
return;
|
|
} else {
|
|
int thr = -0;
|
|
ndigs = (int)(log10(value));
|
|
if (ndigs > 310 || ndigs < -310) { /* Protect against silliness.. */
|
|
strcpy(fmt,"%g");
|
|
return;
|
|
}
|
|
if (ndigs >= (nsd + thr)) {
|
|
sprintf(fmt,"%%%d.%de",xtot,tot-2);
|
|
return;
|
|
}
|
|
sprintf(fmt,"%%%d.%df",xtot,(nsd + thr)-ndigs);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* ---------------------------------------------------------- */
|
|
/* Debug and test code */
|
|
/* ---------------------------------------------------------- */
|
|
|
|
#ifdef STANDALONE_TEST
|
|
|
|
/* Dump the contents of a cgats structure to the given output */
|
|
static void
|
|
cgats_dump(cgats *p, cgatsFile *fp) {
|
|
int tn;
|
|
|
|
fp->gprintf(fp,"Number of tables = %d\n",p->ntables);
|
|
for (tn = 0; tn < p->ntables; tn++) {
|
|
cgats_table *t;
|
|
int i,j;
|
|
t = &p->t[tn];
|
|
|
|
|
|
fp->gprintf(fp,"\nTable %d:\n",tn);
|
|
|
|
switch(t->tt) /* Table identifier */
|
|
{
|
|
case it8_7_1:
|
|
fp->gprintf(fp,"Identifier = 'IT8.7/1'\n");
|
|
break;
|
|
case it8_7_2:
|
|
fp->gprintf(fp,"Identifier = 'IT8.7/2'\n");
|
|
break;
|
|
case it8_7_3:
|
|
fp->gprintf(fp,"Identifier = 'IT8.7/3'\n");
|
|
break;
|
|
case it8_7_4:
|
|
fp->gprintf(fp,"Identifier = 'IT8.7/4'\n");
|
|
break;
|
|
case cgats_5:
|
|
fp->gprintf(fp,"Identifier = 'CGATS.5'\n");
|
|
break;
|
|
case cgats_X:
|
|
fp->gprintf(fp,"Identifier = '%s'\n",p->cgats_type);
|
|
break;
|
|
case tt_other: /* User defined file identifier */
|
|
fp->gprintf(fp,"Identifier = '%s'\n",p->others[t->oi]);
|
|
break;
|
|
default:
|
|
fp->gprintf(fp,"**ILLEGAL**\n");
|
|
break;
|
|
}
|
|
|
|
fp->gprintf(fp,"\nNumber of keywords = %d\n",t->nkwords);
|
|
|
|
/* Dump all the keyword symbols and values */
|
|
for (i = 0; i < t->nkwords; i++) {
|
|
if (t->ksym[i] != NULL && t->kdata[i] != NULL)
|
|
{
|
|
if (t->kcom[i] != NULL)
|
|
fp->gprintf(fp,"Keyword '%s' has value '%s' and comment '%s'\n",
|
|
t->ksym[i],t->kdata[i],t->kcom[i]);
|
|
else
|
|
fp->gprintf(fp,"Keyword '%s' has value '%s'\n",t->ksym[i],t->kdata[i]);
|
|
}
|
|
if (t->kcom[i] != NULL)
|
|
fp->gprintf(fp,"Comment '%s'\n",t->kcom[i]);
|
|
}
|
|
|
|
fp->gprintf(fp,"\nNumber of field defs = %d\n",t->nfields);
|
|
|
|
/* Dump all the field symbols */
|
|
for (i = 0; i < t->nfields; i++) {
|
|
char *fname;
|
|
switch(t->ftype[i]) {
|
|
case r_t:
|
|
fname = "real";
|
|
break;
|
|
case i_t:
|
|
fname = "integer";
|
|
break;
|
|
case cs_t:
|
|
fname = "character string";
|
|
break;
|
|
case nqcs_t:
|
|
fname = "non-quoted char string";
|
|
break;
|
|
default:
|
|
fname = "illegal";
|
|
break;
|
|
}
|
|
fp->gprintf(fp,"Field '%s' has type '%s'\n",t->fsym[i],fname);
|
|
}
|
|
|
|
fp->gprintf(fp,"\nNumber of sets = %d\n",t->nsets);
|
|
|
|
/* Dump all the set values */
|
|
for (j = 0; j < t->nsets; j++) {
|
|
for (i = 0; i < t->nfields; i++) {
|
|
switch(t->ftype[i]) {
|
|
case r_t: {
|
|
char fmt[30];
|
|
double val = *((double *)t->fdata[j][i]);
|
|
fmt[0] = ' ';
|
|
real_format(val, REAL_SIGDIG, fmt+1);
|
|
fp->gprintf(fp,fmt,*((double *)t->fdata[j][i]));
|
|
break;
|
|
}
|
|
case i_t:
|
|
fp->gprintf(fp," %d",*((int *)t->fdata[j][i]));
|
|
break;
|
|
case cs_t:
|
|
case nqcs_t:
|
|
fp->gprintf(fp," %s",((char *)t->fdata[j][i]));
|
|
break;
|
|
default:
|
|
fp->gprintf(fp," illegal");
|
|
break;
|
|
}
|
|
}
|
|
fp->gprintf(fp,"\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[]) {
|
|
char *fn;
|
|
cgatsFile *fp;
|
|
cgats *pp;
|
|
|
|
if (argc == 1) {
|
|
/* Write test */
|
|
pp = new_cgats(); /* Create a CGATS structure */
|
|
|
|
if (pp->add_table(pp, cgats_5, 0) < 0 /* Start the first table */
|
|
|| pp->add_kword(pp, 0, NULL, NULL, "Comment only test comment") < 0
|
|
|| pp->add_kword(pp, 0, "TEST_KEY_WORD", "try this out", "Keyword comment") < 0
|
|
|| pp->add_kword(pp, 0, "TEST_KEY_WORD2", "try this\" out \"\" huh !", NULL) < 0
|
|
|
|
|| pp->add_field(pp, 0, "SAMPLE_ID", cs_t) < 0
|
|
|| pp->add_field(pp, 0, "SAMPLE_LOC", nqcs_t) < 0
|
|
|| pp->add_field(pp, 0, "XYZ_X", r_t) < 0)
|
|
error("Initial error: '%s'",pp->e.m);
|
|
|
|
if (pp->add_set(pp, 0, "1", "A1", 0.000000012345678) < 0
|
|
|| pp->add_set(pp, 0, "2", "A1", 0.00000012345678) < 0
|
|
|| pp->add_set(pp, 0, "3", "A1", 0.0000012345678) < 0
|
|
|| pp->add_set(pp, 0, "4", "A1", 0.000012345678) < 0
|
|
|| pp->add_set(pp, 0, "5", "A1", 0.000012345678) < 0
|
|
|| pp->add_set(pp, 0, "6", "A2", 0.00012345678) < 0
|
|
|| pp->add_set(pp, 0, "7", "A5",0.0012345678) < 0
|
|
|| pp->add_set(pp, 0, "8", "A5", 0.012345678) < 0
|
|
|| pp->add_set(pp, 0, "9", "A5", 0.12345678) < 0
|
|
|| pp->add_set(pp, 0, "10", "A5", 0.00000000) < 0
|
|
|| pp->add_set(pp, 0, "11", "A5", 1.2345678) < 0
|
|
|| pp->add_set(pp, 0, "12", "A5", 12.345678) < 0
|
|
|| pp->add_set(pp, 0, "13", "A5", 123.45678) < 0
|
|
|| pp->add_set(pp, 0, "14", "A5", 1234.5678) < 0
|
|
|| pp->add_set(pp, 0, "15", "A5", 12345.678) < 0
|
|
|| pp->add_set(pp, 0, "16", "A5", 123456.78) < 0
|
|
|| pp->add_set(pp, 0, "17", "A5", 1234567.8) < 0
|
|
|| pp->add_set(pp, 0, "18", "A5", 12345678.0) < 0
|
|
|| pp->add_set(pp, 0, "19", "A5", 123456780.0) < 0
|
|
|| pp->add_set(pp, 0, "20", "A5", 1234567800.0) < 0
|
|
|| pp->add_set(pp, 0, "21", "A5", 12345678000.0) < 0
|
|
|| pp->add_set(pp, 0, "22", "A5", 123456780000.0) < 0
|
|
|| pp->add_set(pp, 0, "23", "A5", 1234567800000.0) < 0)
|
|
error("Adding set error '%s'",pp->e.m);
|
|
|
|
if (pp->add_table(pp, cgats_5, 0) < 0 /* Start the second table */
|
|
|| pp->set_table_flags(pp, 1, 1, 1, 1) < 0 /* Suppress id, kwords and fields */
|
|
|| pp->add_kword(pp, 1, NULL, NULL, "Second Comment only test comment") < 0)
|
|
error("Adding table error '%s'",pp->e.m);
|
|
|
|
if (pp->add_field(pp, 1, "SAMPLE_ID", cs_t) < 0 /* Need to define fields same as table 0 */
|
|
|| pp->add_field(pp, 1, "SAMPLE_LOC", nqcs_t) < 0
|
|
|| pp->add_field(pp, 1, "XYZ_X", r_t) < 0)
|
|
error("Adding field error '%s'",pp->e.m);
|
|
|
|
if (pp->add_set(pp, 1, "4", "A4", -0.000012345678) < 0
|
|
|| pp->add_set(pp, 1, "5", "A5", -0.00012345678) < 0
|
|
|| pp->add_set(pp, 1, "6", "A5", -0.0012345678) < 0
|
|
|| pp->add_set(pp, 1, "7", "A5", -0.012345678) < 0
|
|
|| pp->add_set(pp, 1, "8", "A5", -0.12345678) < 0
|
|
|| pp->add_set(pp, 1, "9", "A5", -1.2345678) < 0
|
|
|| pp->add_set(pp, 1, "10", "A5", -12.345678) < 0
|
|
|| pp->add_set(pp, 1, "11", "A5", -123.45678) < 0
|
|
|| pp->add_set(pp, 1, "12", "A5", -1234.5678) < 0
|
|
|| pp->add_set(pp, 1, "13", "A5", -12345.678) < 0
|
|
|| pp->add_set(pp, 1, "14", "A5", -123456.78) < 0
|
|
|| pp->add_set(pp, 1, "15", "A5", -1234567.8) < 0
|
|
|| pp->add_set(pp, 1, "16", "A5", -12345678.0) < 0
|
|
|| pp->add_set(pp, 1, "17", "A5", -123456780.0) < 0
|
|
|| pp->add_set(pp, 1, "18", "A5", -1234567800.0) < 0
|
|
|| pp->add_set(pp, 1, "19", "A5", -12345678000.0) < 0
|
|
|| pp->add_set(pp, 1, "20", "A5", -123456780000.0) < 0)
|
|
error("Adding set 2 error '%s'",pp->e.m);
|
|
|
|
if ((fp = new_cgatsFileStd_name("fred.it8", "w")) == NULL)
|
|
error("Error opening '%s' for writing","fred.it8");
|
|
|
|
if (pp->write(pp, fp))
|
|
error("Write error : %s",pp->e.m);
|
|
|
|
fp->del(fp); /* Close file */
|
|
pp->del(pp); /* Clean up */
|
|
}
|
|
|
|
/* Read test */
|
|
pp = new_cgats(); /* Create a CGATS structure */
|
|
|
|
/* Setup to cope with Argyll files */
|
|
if (pp->add_other(pp, "CTI1") == -2
|
|
|| pp->add_other(pp, "CTI2") == -2
|
|
|| pp->add_other(pp, "CTI3") == -2)
|
|
error("Adding other error '%s'",pp->e.m);
|
|
|
|
/* Setup to cope with colorblind files */
|
|
if (pp->add_other(pp, "CBSC") == -2
|
|
|| pp->add_other(pp, "CBTA") == -2
|
|
|| pp->add_other(pp, "CBPR") == -2)
|
|
error("Adding other 2 error '%s'",pp->e.m);
|
|
|
|
if (argc == 2)
|
|
fn = argv[1];
|
|
else
|
|
fn = "fred.it8";
|
|
|
|
if ((fp = new_cgatsFileStd_name(fn, "r")) == NULL)
|
|
error("Error opening '%s' for reading",fn);
|
|
if (pp->read(pp, fp))
|
|
error("Read error : %s",pp->e.m);
|
|
fp->del(fp); /* Close file */
|
|
|
|
if ((fp = new_cgatsFileStd_fp(stdout)) == NULL)
|
|
error("Error opening stdout");
|
|
cgats_dump(pp, fp);
|
|
fp->del(fp); /* delete file object */
|
|
|
|
pp->del(pp); /* Clean up */
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Basic printf type error() and warning() routines */
|
|
|
|
void
|
|
error(const char *fmt, ...) {
|
|
va_list args;
|
|
|
|
fprintf(stderr,"cgats: Error - ");
|
|
va_start(args, fmt);
|
|
vfprintf(stderr, fmt, args);
|
|
va_end(args);
|
|
fprintf(stderr, "\n");
|
|
exit (-1);
|
|
}
|
|
|
|
void
|
|
warning(const char *fmt, ...) {
|
|
va_list args;
|
|
|
|
fprintf(stderr,"cgats: Warning - ");
|
|
va_start(args, fmt);
|
|
vfprintf(stderr, fmt, args);
|
|
va_end(args);
|
|
fprintf(stderr, "\n");
|
|
}
|
|
|
|
#endif /* STANDALONE_TEST */
|
|
/* ---------------------------------------------------------- */
|