From 22f703cab05b7cd368f4de9e03991b7664dc5022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Mon, 1 Sep 2014 13:56:46 +0200 Subject: Initial import of argyll version 1.5.1-8 --- cgats/Jamfile | 37 + cgats/License.txt | 22 + cgats/Makefile | 68 ++ cgats/Makefile.IBMNT | 41 + cgats/Makefile.OSX | 38 + cgats/Makefile.UNIX | 38 + cgats/Makefile.WNT | 38 + cgats/Makefile.am | 9 + cgats/Readme.txt | 286 +++++++ cgats/afiles | 16 + cgats/cgats.c | 2166 ++++++++++++++++++++++++++++++++++++++++++++++++++ cgats/cgats.h | 180 +++++ cgats/cgatsstd.c | 111 +++ cgats/makezip.ksh | 2 + cgats/pars.c | 642 +++++++++++++++ cgats/pars.h | 242 ++++++ cgats/parsstd.c | 476 +++++++++++ 17 files changed, 4412 insertions(+) create mode 100644 cgats/Jamfile create mode 100644 cgats/License.txt create mode 100644 cgats/Makefile create mode 100644 cgats/Makefile.IBMNT create mode 100644 cgats/Makefile.OSX create mode 100644 cgats/Makefile.UNIX create mode 100644 cgats/Makefile.WNT create mode 100644 cgats/Makefile.am create mode 100644 cgats/Readme.txt create mode 100644 cgats/afiles create mode 100644 cgats/cgats.c create mode 100644 cgats/cgats.h create mode 100644 cgats/cgatsstd.c create mode 100644 cgats/makezip.ksh create mode 100644 cgats/pars.c create mode 100644 cgats/pars.h create mode 100644 cgats/parsstd.c (limited to 'cgats') diff --git a/cgats/Jamfile b/cgats/Jamfile new file mode 100644 index 0000000..813269a --- /dev/null +++ b/cgats/Jamfile @@ -0,0 +1,37 @@ + +# Jamfile for cgats library + +#PREF_CCFLAGS = $(CCOPTFLAG) ; # Turn optimisation on +PREF_CCFLAGS = $(CCDEBUGFLAG) ; # Debugging flags +PREF_LINKFLAGS = $(LINKDEBUGFLAG) ; + +#if stdio is not wanted in icclib: +#DEFINES = SEPARATE_STD ; + +#Products +Libraries = libcgats ; +Headers = cgats.h ; + +#Install +#InstallLib $(DESTDIR)$(PREFIX)/lib : $(Libraries ; +#InstallFile $(DESTDIR)$(PREFIX)/h : $(Headers) ; + +# Stop objects that are in more than one library from being +# prematurely deleted: +ObjectKeep cgats pars ; + +# CGATS library +Library libcgats : pars.c cgats.c ; + +# Executable support if SEPARATED_STD +if SEPARATE_STD in $(DEFINES) { + Objects parsstd.c cgatsstd.c ; + LINKOBJS = cgatsstd parsstd ; # Link all tests here with these +} + +# Individual stand alone test of parser +MainVariant pars : pars.c : : STANDALONE_TEST ; + +# Individual stand alone test of cgats i/o +MainVariant cgats : cgats.c : : STANDALONE_TEST : : pars ; + diff --git a/cgats/License.txt b/cgats/License.txt new file mode 100644 index 0000000..7655233 --- /dev/null +++ b/cgats/License.txt @@ -0,0 +1,22 @@ +************************************************************************* +Copyright (c) 1995-2002 Graeme W. Gill + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +************************************************************************* diff --git a/cgats/Makefile b/cgats/Makefile new file mode 100644 index 0000000..5312104 --- /dev/null +++ b/cgats/Makefile @@ -0,0 +1,68 @@ +# UNIX style makefile, for icclib and friends. +# "include" the right environment for your system, +# by uncommenting the appropriate line: + +# Microsoft C++, WinNT setup +#include Makefile.WNT + +# IBM C++, WinNT setup +include Makefile.IBMNT + +# Generic UNIX setup +#include Makefile.UNIX + +# Apple OSX +#include Makefile.OSX + +############################### + +#Compile with separate stndard malloc & file io +#CCDEFINES = $(DEFFLAG)SEPARATE_STD + +#Set optimisation on +CCFLAGS = $(CCFLAGSDEF) $(CCOPTFLAG) $(CCDEFINES) + +#Set debugging on +#CCFLAGS = $(CCFLAGSDEF) $(CCDEBUGFLAG) $(CCDEFINES) + +STDHDRS = $(STDHDRSDEF) +LINKFLAGS = $(LINKFLAGSDEF) $(LINKDEBUGFLAG) + +all:: libcgats$(SUFLIB) pars$(SUFEXE) cgats$(SUFEXE) + + +# Separate for executables +parsstd$(SUFOBJ): parsstd.c pars.h + $(CC) $(CCOF)parsstd$(SUFOBJ) parsstd.c + +cgatsstd$(SUFOBJ): cgatsstd.c cgats.h pars.h + $(CC) $(CCOF)cgatsstd$(SUFOBJ) cgatsstd.c + + +pars$(SUFOBJ): pars.c pars.h + $(CC) $(CCOF)pars$(SUFOBJ) pars.c + +cgats$(SUFOBJ): cgats.c cgats.h pars.h + $(CC) $(CCOF)cgats$(SUFOBJ) cgats.c + + +libcgats$(SUFLIB): pars$(SUFOBJ) cgats$(SUFOBJ) + $(LIBU) $(LIBOF)libcgats$(SUFLIB) cgats$(SUFOBJ) pars$(SUFOBJ) + $(RANLIB) libcgats$(SUFLIB) + + +sa_pars$(SUFOBJ): pars.c pars.h + $(CC) $(CCOF)sa_pars$(SUFOBJ) $(DEFFLAG)STANDALONE_TEST pars.c + +pars$(SUFEXE): sa_pars$(SUFOBJ) parsstd$(SUFOBJ) + $(LINK) $(LINKOF)pars$(SUFEXE) sa_pars$(SUFOBJ) parsstd$(SUFOBJ) $(LINKLIBS) + + +sa_cgats$(SUFOBJ): cgats.c cgats.h pars.h + $(CC) $(CCOF)sa_cgats$(SUFOBJ) $(DEFFLAG)STANDALONE_TEST cgats.c + +cgats$(SUFEXE): sa_cgats$(SUFOBJ) pars$(SUFOBJ) parsstd$(SUFOBJ) cgatsstd$(SUFOBJ) + $(LINK) $(LINKOF)cgats$(SUFEXE) sa_cgats$(SUFOBJ) pars$(SUFOBJ) parsstd$(SUFOBJ) \ + cgatsstd$(SUFOBJ) $(LINKLIBS) + + diff --git a/cgats/Makefile.IBMNT b/cgats/Makefile.IBMNT new file mode 100644 index 0000000..26a2bd2 --- /dev/null +++ b/cgats/Makefile.IBMNT @@ -0,0 +1,41 @@ +# IBM C++, WinNT setup + +SLASH = \ +SUFLIB = .lib +SUFOBJ = .obj +SUFEXE = .exe +CMDSEP = & + +INCFLAG = /I +DEFFLAG = /D +UNDEFFLAG = "/U _" +CCOPTFLAG = /O+ /qtune=pentiumpro +CCDEBUGFLAG = /Ti+ /O- /Oi- /DEbug +CCPROFFLAG = /Ti+ /Gh+ +LINKDEBUGFLAG = /DEbug +LINKPROFFLAG = /DE /NOE cppwpa3.obj + +STDHDRSDEF = $(IPF_PATH32)\\include $(IPF_PATH32)\\sdk\\winh + +MAKEU = nmake +LIBU = ilib /Q +LIBOF = /OUT: +RANLIB = rem +AS = masm386 +CCFLAGSDEF = /DNT /c +CC = icc /Q $(CCFLAGS) $(INCFLAG)$(STDHDRS) +CCOF = /Fo +LINKLIBS = $(IPF_PATH32)\\sdk\\lib\\*.lib +LINKFLAGSDEF = +LINK = ilink /NOLogo $(LINKFLAGS) +LINKOF = /OUT: + +.SUFFIXES: +.SUFFIXES: .c $(SUFLIB) $(SUFOBJ) $(SUFEXE) + +.c$(SUFOBJ): + $(CC) $(CCOF)$*$(SUFOBJ) $< + + + + diff --git a/cgats/Makefile.OSX b/cgats/Makefile.OSX new file mode 100644 index 0000000..fb1c88f --- /dev/null +++ b/cgats/Makefile.OSX @@ -0,0 +1,38 @@ +# MAC OSX, derived from UNIX setup + +SLASH = / +SUFLIB = .a +SUFOBJ = .o +SUFEXE = +CMDSEP = ; + +INCFLAG = -I +DEFFLAG = -D +UNDEFFLAG = "-u _" +CCOPTFLAG = -O +CCDEBUGFLAG = -g +CCPROFFLAG = +LINKDEBUGFLAG = +LINKPROFFLAG = + +STDHDRSDEF = /usr/include + +MAKEU = make +LIBU = ar -r +LIBOF = +RANLIB = ranlib +AS = as +CCFLAGSDEF = -DUNIX -c +CC = cc $(CCFLAGS) $(INCFLAG)$(STDHDRS) +CCOF = -o +LINKFLAGSDEF = -lm +LINKLIBS = +LINK = cc $(LINKFLAGS) $(LINKLIBS) +LINKOF = -o + +.SUFFIXES: +.SUFFIXES: .c $(SUFLIB) $(SUFOBJ) $(SUFEXE) + +.c$(SUFOBJ): + $(CC) $(CCOF)$*$(SUFOBJ) $< + diff --git a/cgats/Makefile.UNIX b/cgats/Makefile.UNIX new file mode 100644 index 0000000..91d6005 --- /dev/null +++ b/cgats/Makefile.UNIX @@ -0,0 +1,38 @@ +# Generic UNIX setup + +SLASH = / +SUFLIB = .a +SUFOBJ = .o +SUFEXE = +CMDSEP = ; + +INCFLAG = -I +DEFFLAG = -D +UNDEFFLAG = "-u _" +CCOPTFLAG = -O +CCDEBUGFLAG = -g +CCPROFFLAG = +LINKDEBUGFLAG = +LINKPROFFLAG = + +STDHDRSDEF = /usr/include + +MAKEU = make +LIBU = ar -r +LIBOF = -o +RANLIB = echo +AS = as +CCFLAGSDEF = -DUNIX -c +CC = cc $(CCFLAGS) $(INCFLAG)$(STDHDRS) +CCOF = -o +LINKFLAGSDEF = -lm +LINKLIBS = +LINK = cc $(LINKFLAGS) $(LINKLIBS) +LINKOF = -o + +.SUFFIXES: +.SUFFIXES: .c $(SUFLIB) $(SUFOBJ) $(SUFEXE) + +.c$(SUFOBJ): + $(CC) $(CCOF)$*$(SUFOBJ) $< + diff --git a/cgats/Makefile.WNT b/cgats/Makefile.WNT new file mode 100644 index 0000000..4ca291d --- /dev/null +++ b/cgats/Makefile.WNT @@ -0,0 +1,38 @@ +# Microsoft C++, WinNT setup + +SLASH = \ +SUFLIB = .lib +SUFOBJ = .obj +SUFEXE = .exe +CMDSEP = & + +INCFLAG = /I +DEFFLAG = /D +UNDEFFLAG = "/u _" +CCOPTFLAG = /Ox /GB +CCDEBUGFLAG = /Z7 /Od +CCPROFFLAG = /Z7 +LINKDEBUGFLAG = /DEBUG +LINKPROFFLAG = /PROFILE + +STDHDRSDEF = $(MSVCNT)\include + +MAKEU = nmake +LIBU = lib +LIBOF = /OUT: +RANLIB = rem +AS = masm386 +CCFLAGSDEF = /DNT /c +CC = cl /nologo $(CCFLAGS) $(INCFLAG)$(STDHDRS) +CCOF = /Fo +LINKLIBS = $(MSVCNT)/lib/user32.lib $(MSVCNT)/lib/gdi32.lib +LINKFLAGSDEF = /link /INCREMENTAL:NO +LINK = link $(LINKFLAGS) +LINKOF = /OUT: + +.SUFFIXES: +.SUFFIXES: .c $(SUFLIB) $(SUFOBJ) $(SUFEXE) + +.c$(SUFOBJ): + $(CC) $(CCOF)$*$(SUFOBJ) $< + diff --git a/cgats/Makefile.am b/cgats/Makefile.am new file mode 100644 index 0000000..23db698 --- /dev/null +++ b/cgats/Makefile.am @@ -0,0 +1,9 @@ +include $(top_srcdir)/Makefile.shared + +privatelib_LTLIBRARIES = libcgats.la +privatelibdir = $(pkglibdir) + +libcgats_la_SOURCES = pars.c pars.h cgats.c cgats.h parsstd.c \ + cgatsstd.c + +EXTRA_DIST = License.txt Readme.txt diff --git a/cgats/Readme.txt b/cgats/Readme.txt new file mode 100644 index 0000000..94f3e3d --- /dev/null +++ b/cgats/Readme.txt @@ -0,0 +1,286 @@ + +CGATS file I/O library, V2.01 README file +----------------------------------------- + +Package contents: +----------------- +cgatslib.zip ZIP archive of the following files +Readme.txt This file. +License.txt Important! - Permissions for use of this package. +cgats.c CGATS Library source code. +cgatsstd.c I/O and malloc source code. +cgats.h CGATS Library include file. Note machine dependent defines. +pars.c Parser source code. +parsstd.c I/O and malloc source code. +pars.h Parser include file. Note machine dependent defines. +Jampfile JAM style "makefile" see +Makefile Makefile. Modify this to include one of the following rule sets. +Makefile.WNT Makefile defines for Microsoft C++ on Windows NT +Makefile.IBMNT Makefile defines for IBM C++ on Windows NT +Makefile.UNIX Makefile defines for generic UNIX system +Makefile.OSX Makefile defines for Apple MAC OSX + +Changes: +-------- + +Changes since V2.00 + + Removed all exit()s from code - now return error values + from all functions. + + Added abstract objects for File I/O and memory allocation + to improve system compatibility. + + Separated the implimentations of the abstract I/O and + memory objects that ise stdio and malloc into separate + files so that a library can be compiled without reference + to these system calls. + + +--------------------------------------------------------------- + DOCUMENTATION + + This library has been implemented from the CGATS.5 Data Exchange Format + specification, in Annex J, of the ANSI CGATS.5-1993 standard. + See + + This module attempts to make reading CGATS.5 and IT8.7 files + easy and convenient. Since the standard is a less than clear + on some points it is hard to know how much compatibility is + to be expected. + + The module supports non-standard keywords and fields automatically. + It does not support reading of comments. + It supports reading and writing multiple tables within one file. + Abreviated tables may be written. + Non-standard fields written by this module will be recognized + correcty when read by this module (data types are written + unambiguously - reals all have a decimal point, no non-quoted strings + are written that could be interpreted as a real or integer), but + there is no certainty that non-standard fields written by other + software will be recognized corectly (e.g. - how to you tell + whether 1234 is an integer, real, or non-quoted string ?) + + To creat a file each element needs to be built up in turn: + + Create an empty cgats structure: + new_cgats(). + + A non-standard memory allocator can be specified for use + by the cgats object by passing an object that inherits + from the cgatsAlloc class defined in parse.h, to the + new_cgats_al(cgatsAlloc *al) constructor. + + + Use the module methods to: + + + Add a user defined file identifier to augment the standard identifiers: + add_other(cgats *p, char *osym) + This can be used to read or write table that are compatible with CGATS.5 + syntax but have different file identifiers. + Use a zero length string (ie. just "") for wildcard. + Normaly returns 0, returns -ve if there was a system error, + & sets p->errc & p->err appropriately. + + + create an empty table entry: + add_table(cgats *p, table_type tt, int oi) + tt is the table type: it8_7_1, it8_7_2, it8_7_3, it8_7_4, cgats_5, cgats_X, + tt_other or tt_none. + cgat_X represents any "CGATS.XXX" table type. + If tt_other is used for creating a file, then oi must be set to the index of the + user defined table identifiers set by calls to p->add_other(); + The table type read can be identified by looking at the index + p->t[table_number].tt, which will be set to the table type. + If the type is cgats_X type, the type actually found will be in p->cgats_type. + If tt == tt_other, then the "other index" value is p->t[table_numberx].oi. + For a wildcard tt_other, the actual type found will be at p->others[oi]. + + Tables are added in turn, and are indexed from 0 in other functions. + Returns 0 normally. + It returns -2 if there was a system error, & sets p->errc & p->err appropriately. + + + Suppress the writing of the file identifier string, standard keyword definitions + and/or field definitions for a second or subsequent table: + set_table_flags(cgats *p, int table, int sup_id, int sup_kwords, int sup_fields); + This only makes sense if the subsequent table has the same identifier type, keywords + and field definitions as the preceding table. Returns 0 normally. + It returns -1 if there was an error, & sets p->errc & p->err appropriately. + + + Add keywords and their values: + int add_kword(cgats *p, int table, char *ksym, char *kdata, char *kcom); + The return value is the index of the new keyword, or -1, errc & err on error. + Any non-standard keywords will automatically be declared in the file. + The comment is optional and NULL should be passed if no comment is to be used + after the keyword/value pair. + + Standard keywords are: + ORIGINATOR System, organization or individual that created data + DESCRIPTOR Purpose or contents of the data file + CREATED Date of creation of the file + MANUFACTURER Manufacturer of physical target + PROD_DATE Year and month of physical target production yyyy:mm + SERIAL Unique physical target serial number + MATERIAL Material physical target was produced on + INSTRUMENTATION Manufacturer and model number of measuring instrument + MEASUREMENT_SOURCE Illumination used for spectral measurements + PRINT_CONDITIONS Characteristics of printed sheet being reported + + Standard keywords will be created automatically if necessary for a legal file format. + + The following kewords are supplied automatically by the module, + and cannot be used for other things: + NUMBER_OF_FIELDS + BEGIN_DATA_FORMAT + END_DATA_FORMAT + NUMBER_OF_SETS + BEGIN_DATA + END_DATA + KEYWORD + + + Add fields: + int add_field(cgats *p, int table, char *fsym, data_type ftype); + + The return value is the index of the new field, or return + -1, errc & err on error, -2, errc & err on system error. + + ftype defines the data type from: r_t, i_t, cs_t, nqcs_t. + r_t is the real (double) type, + i_t is the integer (int) type, + cs_t is the character string (char*) type, + nqcs_t is the same as cs_t except that it will be non-quoted if possible. + Note that the type must agree with the standard type if the field is + from the set default data format identifiers: + + SAMPLE_ID nqcs_t Identifies sample which data represents + STRING cs_t Identifies label, or other non-machine readable value. + CMYK_C r_t Cyan percentage of CMYK + CMYK_M r_t Magenta percentage of CMYK + CMYK_Y r_t Yellow percentage of CMYK + CMYK_K r_t Black percentage of CMYK + D_RED r_t Red filter reflection density + D_GREEN r_t Green filter reflection density + D_BLUE r_t Blue filter reflection density + D_VIS r_t Visual filter reflection density + RGB_R r_t Red component of RGB data + RGB_G r_t Green component of RGB data + RGB_B r_t Blue component of RGB data + SPECTRAL_NM r_t Wavelength of measurement in nanometers + SPECTRAL_PCT r_t Precentage reflectance/transmittance + XYZ_X r_t X component of tristimulus data + XYZ_Y r_t Y component of tristimulus data + XYZ_Z r_t Z component of tristimulus data + XYY_X r_t x component of chromaticity data + XYY_Y r_t y component of chromaticity data + XYY_CAPY r_t Y component of chromaticity data + LAB_L r_t L* component of Lab data + LAB_A r_t a* component of Lab data + LAB_B r_t b* component of Lab data + LAB_C r_t C*ab component of Lab data + LAB_H r_t hab component of Lab data + LAB_DE r_t CIA delta E + STDEV_X r_t Standard deviation of X (tristimulous data) + STDEV_Y r_t Standard deviation of Y (tristimulous data) + STDEV_Z r_t Standard deviation of Z (tristimulous data) + STDEV_L r_t Standard deviation of L* + STDEV_A r_t Standard deviation of a* + STDEV_B r_t Standard deviation of b* + STDEV_DE r_t Standard deviation of CIE delta E + + Add a set of data: + add_set(cgats *p, int table, ...) + The data should be supplied as a varargs list in the appropropriate + data format [char*, double or int]. + Returns 0 normally, -1 errc & err if parameter error, + -2 errc & err if system error. + + Add a set of data from union array: + add_setarr(cgats *p, int table, cgats_set_elem *args); + The data should be supplied as an array of cgats_set_elem unions. + Returns 0 normally, -1 errc & err if parameter error, + -2 errc & err if system error. + + Write the data out to a file. + write_name(cgats *p, char *fname); + The method will return non-zero on an error, with an error + description in the err location of the structure. + + + A non-standard destination of data can be read by passing an + object that inherits from the cgatsFile class defined in + parse.h to the write(cgats *p, cgatsFile *fp) method. + + To read in a data file, the cgats structure should be created + as usual. The read method can then be called to read in the file: + read_name(cgats *p, char *fname) + Returns 0 normally, and -ve on an error, with an error + description in the err location of the structure, + and errc set with the return code. + + A non-standard source of data can be read by passing an + object that inherits from the cgatsFile class defined in + parse.h to the read(cgats *p, cgatsFile *fp) method. + + The reader will deal automaticaly with carry over of keywords + and/or field definitions from one table to another, making each + table appear independent once read. + + The data is accessed by refering to the following read-only + structure entries: + + The number of tables will be in p->ntables + + The number of keywords will be in p->t[table_number].nkwords + The number of fields will be in p->t[table_number].nfields + The number of sets will be in p->t[table_number].nsets + + Tables, keywords, fields and sets index from 0. + + The keywords will be in + p->t[table_number].ksym[keyword_index] + + The keywords character string value be in + p->t[table_number].kdata[keyword_index] + + The field format identifiers of each field will be in + p->t[table_number].fsym[field_index] + + The data type of each field will be in + p->t[table_number].ftype[field_index] + + A void pointer to the data of each field of each set will be in + p->t[table_number].fdata[set_index][field_index] + Cast the void pointet according to its type to retrieve the data. + Alternatively the + p->get_setarr(struct _cgats *p, int table, int set_index, cgats_set_elem *ary) + method can be used to fill in a suitable sized cgats_set_elem array with + the value of all the fields at a particular index. Any character string + type will be a pointer to the data in p->t[table_number].fdata[set_index][field_index]. + + To find the index to a particular keyword, use: + find_kword(cgats *p, int table, char *ksym) + -1 will be returned if no match is found. + -2 will be returned, p->errc & p->err will be set if table is out of range. + + To find the index to a particular field, use: + find_field(cgats *p, int table, char *fsym); + -1 will be returned if no match is found. + -2 will be returned, p->errc & p->err will be set if table is out of range. + + Rather than checking the error return codes from every method, + the first error is "sticky", and recorded in the object. + This can be checked after a series of operations by calling + the error(cgats *p, char **mes) method, which will return the + error code, and (optionaly) the error message. + + Once operations are finished, the object can be deleted by calling + the delete method: + del(cgats *p) + +Graeme Gill. + +--------------------------------------------------------------- diff --git a/cgats/afiles b/cgats/afiles new file mode 100644 index 0000000..dc20cb4 --- /dev/null +++ b/cgats/afiles @@ -0,0 +1,16 @@ +Readme.txt +License.txt +afiles +cgats.c +cgats.h +pars.c +pars.h +parsstd.c +cgatsstd.c +Jamfile +Makefile +Makefile.WNT +Makefile.IBMNT +Makefile.UNIX +Makefile.OSX +makezip.ksh diff --git a/cgats/cgats.c b/cgats/cgats.c new file mode 100644 index 0000000..baf9992 --- /dev/null +++ b/cgats/cgats.c @@ -0,0 +1,2166 @@ + +/* + * 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 License.txt file in this directory for licensing details. + */ + +#define _CGATS_C_ /* Turn on implimentation code */ + +#include +#include +#include + +#undef DEBUG /* Debug only in slected places */ + +#ifdef DEBUG +# define DBGA g_log, 0 /* First argument to DBGF() */ +# define DBGF(xx) a1logd xx +#else +# define DBGF(xx) +#endif + +#ifdef STANDALONE_TEST +extern void error(const char *fmt, ...), warning(const char *fmt, ...); +#endif + +#include +#include +#include +#include +#include "pars.h" +#include "cgats.h" + +#define REAL_SIGDIG 5 /* 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_flags(cgats *p, int table, int sup_id, int sup_kwords, int sup_fields); +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, sizeof(cgats), 1)) == 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_flags = set_table_flags; + p->set_cgats_type = set_cgats_type; + p->add_other = add_other; + p->get_oi = get_oi; + p->add_kword = add_kword; + 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 + + 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->errc = errc; + va_start(args, fmt); + vsprintf(p->err, fmt, args); + va_end(args); + + /* If this is the first registered error */ + if (p->ferrc != 0) { + p->ferrc = p->errc; + strcpy(p->ferr, p->err); + } + + 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->ferrc != 0) { + if (mes != NULL) + *mes = p->ferr; + return p->ferrc; + } + 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->errc = 0; + p->err[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->errc = 0; + p->err[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->errc and p->err 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->errc = 0; + p->err[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->errc != 0) { /* get_token got an error */ + err(p, -1, "%s", pp->err); + pp->del(pp); + DBGF((DBGA,"Get token got error '%s'\n",pp->err)); + return p->errc; + } + 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->err); + pp->del(pp); + DBGF((DBGA,"Read line got error '%s'\n",pp->err)); + return p->errc; + } + } + if (tp == NULL) + break; /* EOF */ + + 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->malloc(p->al, + (strlen(tp)+1) * sizeof(char))) == NULL) { + err(p,-1,"Failed to malloc space for CGATS.X keyword"); + pp->del(pp); + return p->errc; + } + 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->errc; + } + 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->errc; + } + + /* 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->errc; + } + } 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->errc; + } + + 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->errc; + } + } + 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->errc; + } + } + } + + /* 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->errc; + } + 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->errc; + } + 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->errc; + } + 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->errc; + } + 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->errc; + } + } + 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->errc; + } + 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->errc; + } + 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->errc; + } +#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->errc; + } + 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->errc; + } + + /* 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 */ + bt = ty; + } + } + /* 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->errc; + } + + /* 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->errc; + } + 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->errc = -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->errc = -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->errc; + } + /* 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->errc; + } + break; + } + } + } + + pp->del(pp); /* Clean up the parse file */ + 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->errc = 0; + p->err[0] = '\000'; + if (p->cgats_type != NULL) + al->free(al, p->cgats_type); + if ((p->cgats_type = (char *)al->malloc(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. */ +/* Return -2 & set errc and err on system error */ +static int +add_other(cgats *p, const char *osym) { + cgatsAlloc *al = p->al; + + p->errc = 0; + p->err[0] = '\000'; + 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->malloc(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->errc = 0; + p->err[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->errc = 0; + p->err[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; +} + +/* 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->errc = 0; + p->err[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; +} + + +/* 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->errc = 0; + p->err[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->errc = 0; + p->err[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->errc = 0; + p->err[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 4 */ + t->nfieldsa += 4; + 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->errc = 0; + p->err[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->errc = 0; + p->err[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->malloc(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->errc = 0; + p->err[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->malloc(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->errc = 0; + p->err[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->errc = 0; + p->err[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->malloc(al, t->nfields * sizeof(void *))) == NULL) + return err(p,-2,"cgats.add_item(), malloc failed!"); + if ((t->fdata[t->nsets-1] = (void **)al->malloc(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->errc = 0; + p->err[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->malloc(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->errc; + } + 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->errc; + } + 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->errc; + } + } + + /* 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->errc; + } + 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->errc; + } + 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->errc; + } + 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->errc; + } + 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->errc; + } + 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->errc; + } + 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->errc; + } + 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 (!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 (!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->errc; +} + +/* 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->malloc(al, sizeof(double))) == NULL) + return NULL; + *p = *((double *)dpoint); + return (void *)p; + } + case i_t: { /* Integer value */ + int *p; + if ((p = (int *)al->malloc(al, 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->malloc(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->malloc(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) { + ndigs = (int)(log10(value)); + if (ndigs <= -2) { + sprintf(fmt,"%%%d.%de",xtot,tot-2); + return; + } + sprintf(fmt,"%%%d.%df",xtot-ndigs,nsd-ndigs); + return; + } else { + ndigs = (int)(log10(value)); + if (ndigs >= (nsd -1)) + { + sprintf(fmt,"%%%d.%de",xtot,tot-2); + return; + } + sprintf(fmt,"%%%d.%df",xtot,(nsd-1)-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->err); + + if (pp->add_set(pp, 0, "1", "A1", 0.000012345678) < 0 + || pp->add_set(pp, 0, "2", "A2", 0.00012345678) < 0 + || pp->add_set(pp, 0, "3 ", "A#5",0.0012345678) < 0 + || pp->add_set(pp, 0, "4", "A5", 0.012345678) < 0 + || pp->add_set(pp, 0, "5", "A5", 0.12345678) < 0 + || pp->add_set(pp, 0, "5", "A5", 0.00000000) < 0 + || pp->add_set(pp, 0, "6", "A5", 1.2345678) < 0 + || pp->add_set(pp, 0, "7", "A5", 12.345678) < 0 + || pp->add_set(pp, 0, "8", "A5", 123.45678) < 0 + || pp->add_set(pp, 0, "9", "A5", 1234.5678) < 0 + || pp->add_set(pp, 0, "10", "A5", 12345.678) < 0 + || pp->add_set(pp, 0, "12", "A5", 123456.78) < 0 + || pp->add_set(pp, 0, "13", "A5", 1234567.8) < 0 + || pp->add_set(pp, 0, "14", "A5", 12345678.0) < 0 + || pp->add_set(pp, 0, "15", "A5", 123456780.0) < 0 + || pp->add_set(pp, 0, "16", "A5", 1234567800.0) < 0 + || pp->add_set(pp, 0, "17", "A5", 12345678000.0) < 0 + || pp->add_set(pp, 0, "18", "A5", 123456780000.0) < 0) + error("Adding set error '%s'",pp->err); + + 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->err); + + 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->err); + + 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->err); + + 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->err); + + 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->err); + + /* 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->err); + + 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->err); + 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 */ +/* ---------------------------------------------------------- */ diff --git a/cgats/cgats.h b/cgats/cgats.h new file mode 100644 index 0000000..2a6b1fd --- /dev/null +++ b/cgats/cgats.h @@ -0,0 +1,180 @@ +#ifndef CGATS_H + +/* + * 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 License.txt file in this directory for licensing details. + */ + +/* Version of cgatslib release */ + +#define CGATSLIB_VERSION 0x020005 +#define CGATSLIB_VERSION_STR "2.05" + +#define CGATS_ERRM_LENGTH 200 + +#ifdef __cplusplus + extern "C" { +#endif + +#include "pars.h" /* We use the ASCII parsing class */ + +/* Possible table types */ +typedef enum { it8_7_1, it8_7_2, it8_7_3, it8_7_4, cgats_5, cgats_X, tt_other, tt_none } table_type; + +/* Possible data types - real, integer, char string, non-quoted char string, no value */ +typedef enum { r_t, i_t, cs_t, nqcs_t, none_t } data_type; + +union _cgats_set_elem { + int i; + double d; + char *c; +}; typedef union _cgats_set_elem cgats_set_elem; + +struct _cgats_table { + cgatsAlloc *al; /* Copy of parent memory allocator */ + table_type tt; /* Table type */ + int oi; /* other type index */ + + /* Read only */ + int nkwords; /* Number of keywords */ + int nfields; /* Number of fields of data */ + int nsets; /* Number of data sets */ + + char **ksym; /* Pointer to [nkwords] array of pointers to keyword symbols */ + char **kdata; /* Pointer to [nkwords] array of pointers to keyword values */ + + char **fsym; /* Pointer to [nfields] array of pointers to field symbols */ + data_type *ftype; /* Pointer to [nfields] array of field types */ + char ***rfdata; /* Pointer to [nsets] array of pointers */ + /* to [nfields] array of pointers to read file field text values */ + void ***fdata; /* Pointer to [nsets] array of pointers */ + /* to [nfields] array of pointers to field set values of ftype */ + /* Private */ + int nkwordsa; /* Number of keywords allocated */ + int nfieldsa; /* Number of fields allocated */ + int nsetsa; /* Number of sets allocated */ + char **kcom; /* Pointer to [nkwords] array of pointers to keyword comments */ + int ndf; /* Next data field - used by add_data_item() */ + int sup_id; /* Set to non-zero if table ID output is to be suppressed */ + int sup_kwords; /* Set to non-zero if table default keyword output is to be suppressed */ + int sup_fields; /* Set to non-zero if table field output is to be suppressed */ +}; typedef struct _cgats_table cgats_table; + +struct _cgats { + /* Private */ + cgatsAlloc *al; /* Memory allocator */ + int del_al; /* Flag to indicate we al->del() */ + + /* Read only Variables */ + int ntables; /* Number of tables */ + + cgats_table *t; /* Pointer to an array of ntable table structures */ + + /* Undefined CGATS table type */ + char *cgats_type; + + /* User defined table types */ + int nothers; /* Number of other identifiers */ + char **others; /* Other file type identifiers */ + + /* Public Methods */ + int (*set_cgats_type)(struct _cgats *p, const char *osym); + /* Define the (one) variable CGATS type */ + /* Return -2, set errc & err on system error */ + + int (*add_other)(struct _cgats *p, const char *osym); + /* Add a user defined file identifier string. */ + /* Use a zero length string for wildcard. */ + /* Return the oi */ + /* Return -2, set errc & err on system error */ + + int (*get_oi)(struct _cgats *p, const char *osym); + /* Return the oi of the given other type */ + /* return -ve and errc and err set on error */ + + int (*read)(struct _cgats *p, cgatsFile *fp); /* Read a cgats file into structure */ + /* return -ve and errc and err set on error */ + + /* NULL if SEPARATE_STD is defined: */ + int (*read_name)(struct _cgats *p, const char *filename); /* Standard file I/O */ + /* return -ve and errc and err set on error */ + + int (*find_kword)(struct _cgats *p, int table, const char *ksym); + /* Return index of the keyword, -1 on fail */ + /* -2 on illegal table index, errc & err */ + int (*find_field)(struct _cgats *p, int table, const char *fsym); + /* Return index of the field, -1 on fail */ + /* -2 on illegal table index, errc & err */ + + int (*add_table)(struct _cgats *p, table_type tt, int oi); + /* Add a new (empty) table to the structure */ + /* Return the index of the table */ + /* Return -2, set errc & err on system error */ + /* if tt is tt_other, io sets the other index */ + int (*set_table_flags)(struct _cgats *p, int table, int sup_id,int sup_kwords,int sup_fields); + /* Set or reset table output suppresion flags */ + /* Return -ve, set errc & err on error */ + int (*add_kword)(struct _cgats *p, int table, const char *ksym, const char *kdata, const char *kcom); + /* Add a new keyword/value pair + optional comment to the table */ + /* Return index of new keyword, or -1, errc & err on error */ + int (*add_field)(struct _cgats *p, int table, const char *fsym, data_type ftype); + /* Add a new field to the table */ + /* Return index of new field, or -1, -2, errc and err on error */ + int (*add_set)(struct _cgats *p, int table, ...); /* Add a set of data */ + /* Return 0 normally, -1, -2, errc & err if error */ + int (*add_setarr)(struct _cgats *p, int table, cgats_set_elem *ary); /* Add data from array */ + /* Return 0 normally, -1, -2, errc & err if error */ + int (*write)(struct _cgats *p, cgatsFile *fp); /* Write structure into cgats file */ + /* return -ve and errc and err set on error */ + + int (*get_setarr)(struct _cgats *p, int table, int set_index, cgats_set_elem *ary); + /* Fill a suitable set_element with a line of data */ + /* Return 0 normally, -1, -2, errc & err if error */ + /* NULL if SEPARATE_STD is defined: */ + int (*write_name)(struct _cgats *p, const char *filename); /* Standard file I/O */ + /* return -ve and errc and err set on error */ + + + int (*error)(struct _cgats *p, char **mes); /* Return error code and message */ + /* for the first error, if any error */ + /* has occured since object creation. */ + void (*del)(struct _cgats *p); /* Delete the object */ + + char err[CGATS_ERRM_LENGTH]; /* Error message */ + int errc; /* Error code */ + char ferr[CGATS_ERRM_LENGTH]; /* First error message */ + int ferrc; /* First error code */ +}; typedef struct _cgats cgats; + +/* Creator */ +extern cgats *new_cgats_al(cgatsAlloc *al); /* with allocator object */ + +#ifdef COMBINED_STD +#define CGATS_STATIC static +#else +#define CGATS_STATIC +#endif + +/* Available if SEPARATE_STD is not defined */ +extern cgats *new_cgats(void); /* Standard allocator */ + +/* Available from cgatsstd.obj SEPARATE_STD is defined: */ +CGATS_STATIC int cgats_read_name(cgats *p, const char *filename); +CGATS_STATIC int cgats_write_name(cgats *p, const char *filename); + +#ifdef __cplusplus + } +#endif + +#define CGATS_H +#endif /* CGATS_H */ diff --git a/cgats/cgatsstd.c b/cgats/cgatsstd.c new file mode 100644 index 0000000..d8fbb34 --- /dev/null +++ b/cgats/cgatsstd.c @@ -0,0 +1,111 @@ + +/* + * cgats library stdio and malloc utility classes. + * Version 2.05 + * + * Author: Graeme W. Gill + * Date: 2002/10/24 + * + * Copyright 2002, Graeme W. Gill + * All rights reserved. + * + * This material is licensed with an "MIT" free use license:- + * see the License.txt file in this directory for licensing details. + * + * These are kept in a separate file to allow them to be + * selectively ommitted from the cgats library. + * + */ + +#ifndef COMBINED_STD + +#include +#include +#include +#include + +#include "cgats.h" + +#ifdef NEVER /* Not sure if this is needed with some combinations */ + +/* Implimentation function - register an error */ +/* Return the error number */ +/* [Note that this is a duplicate of the one in cgats.c] */ +static int +err(cgats *p, int errc, const char *fmt, ...) { + va_list args; + + p->errc = errc; + va_start(args, fmt); + vsprintf(p->err, fmt, args); + va_end(args); + + /* If this is the first registered error */ + if (p->ferrc != 0) { + p->ferrc = p->errc; + strcpy(p->ferr, p->err); + } + + return errc; +} + +#endif /* NEVER */ + +#endif /* !COMBINED_STD */ + +#if defined(SEPARATE_STD) || defined(COMBINED_STD) + +/* Create an empty cgats object, default standard allocator */ +cgats *new_cgats(void) { + cgats *p; + cgatsAlloc *al; /* memory allocator */ + + if ((al = new_cgatsAllocStd()) == NULL) + return NULL; + + if ((p = new_cgats_al(al)) == NULL) { + al->del(al); + return NULL; + } + + p->del_al = 1; /* Get cgets->del to cleanup allocator */ + return p; +} + +/* Read a cgats file into structure */ +/* Return non-zero if there was an error */ +/* and set p->errc * p->err */ +CGATS_STATIC +int +cgats_read_name(cgats *p, const char *filename) { + int rv; + cgatsFile *fp; + + p->errc = 0; + p->err[0] = '\000'; + + if ((fp = new_cgatsFileStd_name(filename, "r")) == NULL) + return err(p,-1,"Unable to open file '%s' for reading",filename); + rv = p->read(p, fp); + fp->del(fp); + + return rv; +} + +/* Write a cgats file into structure */ +/* Return -ve, errc & err if there was an error */ +CGATS_STATIC +int +cgats_write_name(cgats *p, const char *filename) { + int rv; + cgatsFile *fp; + + if ((fp = new_cgatsFileStd_name(filename, "w")) == NULL) + return err(p,-1,"Unable to open file '%s' for writing",filename); + rv = p->write(p, fp); + fp->del(fp); + + return rv; +} + +#endif /* defined(SEPARATE_STD) || defined(COMBINED_STD) */ diff --git a/cgats/makezip.ksh b/cgats/makezip.ksh new file mode 100644 index 0000000..8cc5f6c --- /dev/null +++ b/cgats/makezip.ksh @@ -0,0 +1,2 @@ +rm cgatslib.zip +zip -9 -ll cgatslib.zip Readme.txt Licence.txt afiles cgats.c cgats.h pars.c pars.h parsstd.c cgatsstd.c Jamfile Makefile Makefile.WNT Makefile.IBMNT Makefile.UNIX Makefile.OSX diff --git a/cgats/pars.c b/cgats/pars.c new file mode 100644 index 0000000..606957c --- /dev/null +++ b/cgats/pars.c @@ -0,0 +1,642 @@ + +/* + * Simple ASCII file parsing object. + * Used as a base for the CGATS.5 and IT8.7 family file I/O class + * Version 2.05 + * + * Author: Graeme W. Gill + * Date: 20/12/95 + * + * Copyright 1996, 2002 Graeme W. Gill + * All rights reserved. + * + * This material is licensed with an "MIT" free use license:- + * see the License.txt file in this directory for licensing details. + */ + +#define _PARS_C_ /* Turn on implimentation code */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __sun +#include +#endif + +#ifdef _MSC_VER +#define vsnprintf _vsnprintf +#define snprintf _snprintf +#endif + +#ifdef STANDALONE_TEST +extern void error(const char *fmt, ...), warning(const char *fmt, ...); +#endif + +#include "pars.h" + +static void del_parse(parse *p); +static int read_line(parse *p); +static void reset_del(parse *p); +static void add_del(struct _parse *p, char *t, + char *nr, char *c, char *q); +static char *get_token(parse *p); + +/* Open the file, allocate and initialize the parse structure */ +/* Return pointer to parse structure. Return NULL on error */ +parse * +new_parse_al( +cgatsAlloc *al, /* Allocator object */ +cgatsFile *fp /* File to read from */ +) { + parse *p; + + if ((p = (parse *) al->calloc(al, sizeof(parse), 1)) == NULL) { + return NULL; + } + p->al = al; /* Heap allocator */ + + p->fp = fp; + p->b = NULL; /* Init line buffer */ + p->bs = 0; + p->bo = 0; + p->tb = NULL; /* Init token buffer */ + p->tbs = 0; + p->to = 0; + p->line = 0; + p->token = 0; + p->ltflag = 0; + p->q = 0; + p->errc = 0; + p->err[0] = '\000'; + + /* Reset the parsing delimiters */ + reset_del(p); + + /* Set default pointers to methods */ + p->del = del_parse; + p->read_line = read_line; + p->reset_del = reset_del; + p->add_del = add_del; + p->get_token = get_token; + + return p; +} + +/* new_parse() with default malloc allocator is in parsstd.c */ + +#ifndef SEPARATE_STD +#define COMBINED_STD + +#include "parsstd.c" + +#undef COMBINED_STD +#endif /* SEPARATE_STD */ + +/* --------------------------------------------- */ + +/* size_t versions of saturating arithmatic */ + +#ifndef SIZE_MAX +# define SIZE_MAX ((size_t)(-1)) +#endif + +/* a * b */ +static size_t ssat_mul(size_t a, size_t b) { + + if (a == 0 || b == 0) + return 0; + + if (a > (SIZE_MAX/b)) + return SIZE_MAX; + else + return a * b; +} + +/* --------------------------------------------- */ +/* Memory image cgatsFile compatible class */ +/* Buffer is assumed to have been allocated by the given allocator, */ +/* and will be expanded on write. */ + +/* Get the size of the file (Only valid for memory file). */ +static size_t cgatsFileMem_get_size(cgatsFile *pp) { + cgatsFileMem *p = (cgatsFileMem *)pp; + + return p->end - p->start; +} + +/* Set current position to offset. Return 0 on success, nz on failure. */ +static int cgatsFileMem_seek( +cgatsFile *pp, +unsigned int offset +) { + cgatsFileMem *p = (cgatsFileMem *)pp; + unsigned char *np; + + np = p->start + offset; + if (np < p->start || np >= p->end) + return 1; + p->cur = np; + return 0; +} + +/* Read count items of size length. Return number of items successfully read. */ +static size_t cgatsFileMem_read( +cgatsFile *pp, +void *buffer, +size_t size, +size_t count +) { + cgatsFileMem *p = (cgatsFileMem *)pp; + size_t len; + + len = ssat_mul(size, count); + if (len > (size_t)(p->end - p->cur)) { /* Too much */ + if (size > 0) + count = (p->end - p->cur)/size; + else + count = 0; + } + len = size * count; + if (len > 0) + memmove(buffer, p->cur, len); + p->cur += len; + return count; +} + +/* Read a character */ +static int cgatsFileMem_getch( +cgatsFile *pp +) { + cgatsFileMem *p = (cgatsFileMem *)pp; + int c; + + if (p->cur < p->start || p->cur >= p->end) + return EOF; + + c = (int)*p->cur; + p->cur++; + + return c; +} + +/* Expand the memory buffer file to hold up to pointer ep */ +/* Don't expand if realloc fails */ +static void cgatsFileMem_filemem_resize(cgatsFileMem *p, unsigned char *ep) { + size_t na, co, ce; + unsigned char *nstart; + + /* No need to realloc */ + if (ep <= p->aend) { + return; + } + + co = p->cur - p->start; /* Current offset */ + ce = p->end - p->start; /* Current end */ + na = ep - p->start; /* new allocatd size */ + + /* Round new allocation up */ + if (na <= 1024) + na += 1024; + else + na += 4096; + + if ((nstart = p->al->realloc(p->al, p->start, na)) != NULL) { + p->start = nstart; + p->cur = nstart + co; + p->end = nstart + ce; + p->aend = nstart + na; + } +} + +/* write count items of size length. Return number of items successfully written. */ +static size_t cgatsFileMem_write( +cgatsFile *pp, +void *buffer, +size_t size, +size_t count +) { + cgatsFileMem *p = (cgatsFileMem *)pp; + size_t len; + + len = ssat_mul(size, count); + if (len > (size_t)(p->end - p->cur)) /* Try and expand buffer */ + cgatsFileMem_filemem_resize(p, p->start + len); + + if (len > (size_t)(p->end - p->cur)) { + if (size > 0) + count = (p->end - p->cur)/size; + else + count = 0; + } + len = size * count; + if (len > 0) + memmove(p->cur, buffer, len); + p->cur += len; + if (p->end < p->cur) + p->end = p->cur; + return count; +} + +/* do a printf */ +static int cgatsFileMem_printf( +cgatsFile *pp, +const char *format, +... +) { + int rv; + va_list args; + cgatsFileMem *p = (cgatsFileMem *)pp; + int len; + + va_start(args, format); + + rv = 1; + len = 100; /* Initial allocation for printf */ + cgatsFileMem_filemem_resize(p, p->cur + len); + + /* We have to use the available printf functions to resize the buffer if needed. */ + for (;rv != 0;) { + /* vsnprintf() either returns -1 if it doesn't fit, or */ + /* returns the size-1 needed in order to fit. */ + len = vsnprintf((char *)p->cur, (p->aend - p->cur), format, args); + + if (len > -1 && ((p->cur + len +1) <= p->aend)) /* Fitted in current allocation */ + break; + + if (len > -1) /* vsnprintf returned needed size-1 */ + len = len+2; /* (In case vsnprintf returned 1 less than it needs) */ + else + len *= 2; /* We just have to guess */ + + /* Attempt to resize */ + cgatsFileMem_filemem_resize(p, p->cur + len); + + /* If resize failed */ + if ((p->aend - p->cur) < len) { + rv = 0; + break; + } + } + if (rv != 0) { + /* Figure out where end of printf is */ + len = strlen((char *)p->cur); /* Length excluding nul */ + p->cur += len; + if (p->cur > p->end) + p->end = p->cur; + rv = len; + } + va_end(args); + return rv; +} + +/* flush all write data out to secondary storage. Return nz on failure. */ +static int cgatsFileMem_flush( +cgatsFile *pp +) { + return 0; +} + +/* Return the memory buffer. Error if not cgatsFileMem */ +static int cgatsFileMem_get_buf( +cgatsFile *pp, +unsigned char **buf, +size_t *len +) { + cgatsFileMem *p = (cgatsFileMem *)pp; + if (buf != NULL) + *buf = p->start; + if (len != NULL) + *len = p->end - p->start; + return 0; +} + +/* return the filename */ +static char *cgatsFileMem_fname( +cgatsFile *pp +) { +// cgatsFileMem *p = (cgatsFileMem *)pp; + + /* Memory doesn't have a name */ + return "**Mem**"; +} + +/* we're done with the file object, return nz on failure */ +static int cgatsFileMem_delete( +cgatsFile *pp +) { + cgatsFileMem *p = (cgatsFileMem *)pp; + cgatsAlloc *al = p->al; + int del_al = p->del_al; + + if (p->del_buf) /* Free the memory buffer */ + al->free(al, p->start); + al->free(al, p); /* Free object */ + if (del_al) /* We are responsible for deleting allocator */ + al->del(al); + return 0; +} + +/* Create a memory image file access class with allocator */ +/* Buffer is used as is. */ +cgatsFile *new_cgatsFileMem_a( +void *base, /* Pointer to base of memory buffer */ +size_t length, /* Number of bytes in buffer */ +cgatsAlloc *al /* heap allocator */ +) { + cgatsFileMem *p; + + if ((p = (cgatsFileMem *) al->calloc(al, 1, sizeof(cgatsFileMem))) == NULL) { + return NULL; + } + p->al = al; /* Heap allocator */ + p->get_size = cgatsFileMem_get_size; + p->seek = cgatsFileMem_seek; + p->read = cgatsFileMem_read; + p->getch = cgatsFileMem_getch; + p->write = cgatsFileMem_write; + p->gprintf = cgatsFileMem_printf; + p->flush = cgatsFileMem_flush; + p->get_buf = cgatsFileMem_get_buf; + p->fname = cgatsFileMem_fname; + p->del = cgatsFileMem_delete; + + p->start = (unsigned char *)base; + p->cur = p->start; + p->aend = p->end = p->start + length; + + return (cgatsFile *)p; +} + +/* Create a memory image file access class with given allocator */ +/* and delete base when cgatsFile is deleted. */ +cgatsFile *new_cgatsFileMem_ad(void *base, size_t length, cgatsAlloc *al) { + cgatsFile *fp; + + if ((fp = new_cgatsFileMem_a(base, length, al)) != NULL) { + ((cgatsFileMem *)fp)->del_buf = 1; + } + + return fp; +} + +/* --------------------------------------------- */ +/* Free up the structure (doesn't close the file) */ +static void +del_parse(parse *p) { + cgatsAlloc *al = p->al; + int del_al = p->del_al; + + if (p->b != NULL) + al->free(al, p->b); + if (p->tb != NULL) + al->free(al, p->tb); + al->free(al, p); + + if (del_al) /* We are responsible for deleting allocator */ + al->del(al); +} + + +/* Read the next line from the file into the line buffer. */ +/* Return 0 if the read fails due to reaching EOF before */ +/* putting anything in the buffer. */ +/* Return -1 if there was some other sort of failure, */ +/* and the error message in parse will be valid. */ +static int +read_line(parse *p) { + int c; + p->bo = 0; /* Reset pointer to the start of the line buffer */ + p->q = 0; /* Reset quoted flag */ + p->errc = 0; /* Reset error status */ + p->err[0] = '\000'; + do { + if ((c = p->fp->getch(p->fp)) == EOF) { + if (p->bo == 0) { /* If there is nothing in the buffer */ + p->line = 0; + return 0; + } + c = 0; /* Finish the line */ + } + if (p->ltflag == 1) { /* Finished last line on '\r' */ + p->ltflag = 0; + if (c == '\n') { + if (p->q == 0) + continue; /* Ignore the following '\n' */ + else + p->line--; /* Undo double increment due to \n after \r */ + } + /* else fall through and use character */ + } else if (p->ltflag == 2) { /* Finished last line on comment character */ + /* Suck up chars till the start of the next line */ + if (c == '\r') + p->ltflag = 1; + else if (c == '\n') + p->ltflag = 0; + continue; /* Ignore characters untill we get to the end of the line */ + } + + if (c == '\r') { + p->line++; /* Increment line number */ + p->ltflag = 1; /* Remember to allow 1 of '\n' before next line */ + if (p->q == 0) + c = 0; /* Finish the line */ + } else if (p->q == 0 && (p->delf[c] & PARS_COMM) != 0) { /* Hit a comment */ + p->line++; /* Increment line number */ + p->ltflag = 2; /* Remember to flush all chars up to end of line */ + c = 0; /* Finish the line */ + } else if (c == '\n') { + p->line++; /* Increment line number */ + if (p->q == 0) + c = 0; /* Finish the the line */ + } + + /* Deal with starting/stopping a quoted section */ + if ((p->delf[c] & PARS_QUOTE) != 0) { + if (p->q == 0) /* We weren't in a quoted section */ + p->q = c; /* Start of quoted section */ + else if (c == p->q) /* If matching quote */ + p->q = 0; /* End quoted section */ + } + + /* Can put the character in the buffer now */ + if (p->bo == p->bs) { /* Run out of buffer space */ + p->bs = (p->bs + 100) * 2; /* Expand line buffer size */ + if ((p->b = (char *) p->al->realloc(p->al, p->b, p->bs)) == NULL) { + sprintf(p->err,"parse.read_line(), realloc failed!"); + return (p->errc = -1); + } + } + p->b[p->bo++] = c; /* Stash character away */ + } while (c != 0); /* Null means we've done the end of the line */ + p->to = 0; /* Reset token pointer to the start of the line buffer */ + p->q = 0; /* Reset quoted flag */ + return 1; +} + +/* Reset the delimiter character set */ +static void +reset_del(parse *p) { + int i; + for (i = 0; i < 256; i++) + p->delf[i] = 0; + p->delf[0] = PARS_TERM; +} + +/* Add to the parsing characters */ +static void +add_del( + parse *p, /* Parse structure */ + char *t, /* Terminators */ + char *nr, /* Not Read */ + char *c, /* Comment start */ + char *q) /* Quote characters */ + { + int i; + if (t != NULL) + for (i = 0; t[i] != '\000'; i++) + p->delf[(int)t[i]] |= PARS_TERM; + if (nr != NULL) + for (i = 0; nr[i] != '\000'; i++) + p->delf[(int)nr[i]] |= PARS_SKIP; + if (c != NULL) + for (i = 0; c[i] != '\000'; i++) + p->delf[(int)c[i]] |= PARS_COMM; + if (q != NULL) + for (i = 0; q[i] != '\000'; i++) + p->delf[(int)q[i]] |= PARS_QUOTE; + } + +/* Using the current token delimiter table and the current line, */ +/* parse it from the current location and return a pointer to the */ +/* null terminated token. Return NULL if there is no token found */ +/* set the parse err and errc to non-zero if there was some other error */ +static char * +get_token(parse *p) { + int tbo = 0; /* Token buffer offset */ + int term = 0; /* flag to trigger token termination */ + char c; + + p->errc = 0; /* Reset error status */ + p->err[0] = '\000'; + if (p->b == NULL) + return NULL; + p->token++; /* Increment token number */ + p->q = 0; + do { + if (term) + c = '\000'; /* end token */ + else if ((c = p->b[p->to++]) == '\000') /* Fetch next token */ + p->to--; /* Safety - don't pass end */ + + /* Deal with starting/stopping a quoted section */ + if ((p->delf[c] & PARS_QUOTE) != 0) { + if (p->q == 0) /* We weren't in a quoted section */ + p->q = c; /* Start of quoted section */ + else if (c == p->q) /* If matching quote */ + p->q = 0; /* End quoted section */ + } + + if (tbo == p->tbs) { /* Run out of buffer space */ + p->tbs = (p->tbs + 100) * 2; /* Expand token buffer size */ + if ((p->tb = (char *) p->al->realloc(p->al, p->tb, p->tbs)) == NULL) { + sprintf(p->err,"parse.get_token(), realloc failed!"); + p->errc = -1; + return NULL; + } + } + + if ((p->q != 0 && (p->q != c || (p->delf[c] & PARS_SKIP) == 0)) + /* If quoted, store if trigger quite is not being skipped */ + || (!(tbo == 0 && (p->delf[c] & PARS_TERM) != 0 && (p->delf[c] & PARS_SKIP) != 0) + /* Skip initial non-reader terminators */ + && (p->delf[c] & PARS_SKIP) == 0)) /* Skip non-readers */ + p->tb[tbo++] = c; /* Stash character away in token */ + + if (p->q == 0 /* If not quoted and if token is non-empty and we have a terminator */ + && tbo != 0 && (p->delf[c] & PARS_TERM) != 0) + term = 1; /* Finish token off next time around */ + } while (c != '\000'); /* Null means we've done the end of the token */ + p->q = 0; + if (tbo <= 1) { + p->token = 0; + return NULL; /* Haven't read anything useful */ + } + return p->tb; +} + +/* ========================================================== */ +/* Test code */ + +#ifdef STANDALONE_TEST +int +main() { + int rc; + parse *pp; + cgatsFile *fp; + + if ((fp = new_cgatsFileStd_name("test.txt", "r")) == NULL) + error("Failed to open file 'test.txt'"); + + if ((pp = new_parse(fp)) == NULL) + error("Failed to create parse object with file 'test.txt'"); + + /* Setup our token parsing charaters */ + pp->add_del(pp, " ,\to"," ,\t", "#", "\""); + + for (;;) { + char *tp; + if ((rc = pp->read_line(pp)) == -1) + error("%s",pp->err); + if (rc == 0) + break; + printf("Line %d = '%s'\n",pp->line,pp->b); + do { + tp = pp->get_token(pp); + if (pp->errc != 0) + error("%s",pp->err); + if (tp != NULL) + printf("Token %d = '%s'\n",pp->token,tp); + } while (tp != NULL); + } + printf("End of File\n"); + + pp->del(pp); /* Clean up */ + fp->del(fp); /* Close the file */ + + return 0; +} + + +/* Basic printf type error() and warning() routines for standalone test */ +void +error(const char *fmt, ...) { + va_list args; + + fprintf(stderr,"chart: 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,"chart: Warning - "); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fprintf(stderr, "\n"); +} + +#endif /* STANDALONE_TEST */ +/* ---------------------------------------------------------- */ diff --git a/cgats/pars.h b/cgats/pars.h new file mode 100644 index 0000000..83bbdad --- /dev/null +++ b/cgats/pars.h @@ -0,0 +1,242 @@ +#ifndef PARS_H +/* + * Simple ASCII file parsing object. + * Used as a base for the CGATS.5 and IT8.7 family file I/O class + * Version 2.01 + * + * 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 License.txt file in this directory for licensing details. + */ + +#undef CGATS_DEBUG_MALLOC /* Turns on partial support for filename and linenumber capture */ + +/* - - - - - - - - - - - - - - - - - - - - - */ + +/* System interface objects. The defaults can be replaced */ +/* for adaption to different system environments */ + +/* - - - - - - - - - - - - - - - - - - - - - */ + +#ifdef CGATS_DEBUG_MALLOC + +/* Heap allocator class interface definition */ +#define CGATS_ALLOC_BASE \ + /* Public: */ \ + \ + void *(*dmalloc) (struct _cgatsAlloc *p, size_t size, char *file, int line); \ + void *(*dcalloc) (struct _cgatsAlloc *p, size_t num, size_t size, char *file, int line);\ + void *(*drealloc)(struct _cgatsAlloc *p, void *ptr, size_t size, char *file, int line); \ + void (*dfree) (struct _cgatsAlloc *p, void *ptr, char *file, int line); \ + \ + /* we're done with the allocator object */ \ + void (*del)(struct _cgatsAlloc *p); \ + +#if defined(_PARS_C_) || defined(_CGATS_C_) || defined(_PARSSTD_C_) /* only inside implimentation */ +#define malloc( p, size ) dmalloc( p, size, __FILE__, __LINE__ ) +#define calloc( p, num, size ) dcalloc( p, num, size, __FILE__, __LINE__ ) +#define realloc( p, ptr, size ) drealloc( p, ptr, size, __FILE__, __LINE__ ) +#define free( p, ptr ) dfree( p, ptr , __FILE__, __LINE__ ) +#endif + +#else /* !CGATS_DEBUG_MALLOC */ + +/* Heap allocator class interface definition */ +#define CGATS_ALLOC_BASE \ + /* Public: */ \ + \ + void *(*malloc) (struct _cgatsAlloc *p, size_t size); \ + void *(*calloc) (struct _cgatsAlloc *p, size_t num, size_t size); \ + void *(*realloc)(struct _cgatsAlloc *p, void *ptr, size_t size); \ + void (*free) (struct _cgatsAlloc *p, void *ptr); \ + \ + /* we're done with the allocator object */ \ + void (*del)(struct _cgatsAlloc *p); \ + +#endif /* !CGATS_DEBUG_MALLOC */ + +/* Common heap allocator interface class */ +struct _cgatsAlloc { + CGATS_ALLOC_BASE +}; typedef struct _cgatsAlloc cgatsAlloc; + +/* - - - - - - - - - - - - - - - - - - - - - */ + +/* Available when SEPARATE_STD is not defined: */ +/* Implementation of heap class based on standard system malloc */ +struct _cgatsAllocStd { + CGATS_ALLOC_BASE +}; typedef struct _cgatsAllocStd cgatsAllocStd; + +/* Create a standard alloc object */ +cgatsAlloc *new_cgatsAllocStd(void); + + +/* - - - - - - - - - - - - - - - - - - - - - */ + +/* File access class interface definition */ +#define CGATS_FILE_BASE \ + /* Public: */ \ + \ + /* Get the size of the file (Only valid for reading file). */ \ + size_t (*get_size) (struct _cgatsFile *p); \ + \ + /* Set current position to offset. Return 0 on success, nz on failure. */ \ + int (*seek) (struct _cgatsFile *p, unsigned int offset); \ + \ + /* Read count items of size length. Return number of items successfully read. */ \ + size_t (*read) (struct _cgatsFile *p, void *buffer, size_t size, size_t count); \ + \ + /* Read a single character */ \ + int (*getch) (struct _cgatsFile *p); \ + \ + /* write count items of size length. Return number of items successfully written. */ \ + size_t (*write)(struct _cgatsFile *p, void *buffer, size_t size, size_t count); \ + \ + /* printf to the file */ \ + int (*gprintf)(struct _cgatsFile *p, const char *format, ...); \ + \ + /* flush all write data out to secondary storage. Return nz on failure. */ \ + int (*flush)(struct _cgatsFile *p); \ + \ + /* return the filename if known, NULL if not */ \ + char *(*fname)(struct _cgatsFile *p); \ + \ + /* Return the memory buffer. Error if not cgatsFileMem */ \ + int (*get_buf)(struct _cgatsFile *p, unsigned char **buf, size_t *len); \ + \ + /* we're done with the file object, close & free it */ \ + /* return nz on failure */ \ + int (*del)(struct _cgatsFile *p); \ + +/* Common file interface class */ +struct _cgatsFile { + CGATS_FILE_BASE +}; typedef struct _cgatsFile cgatsFile; + + +/* - - - - - - - - - - - - - - - - - - - - - */ + +/* Available when SEPARATE_STD is not defined: */ + +/* Implementation of file access class based on standard file I/O */ +struct _cgatsFileStd { + CGATS_FILE_BASE + + /* Private: */ + cgatsAlloc *al; /* Heap allocator */ + int del_al; /* NZ if heap allocator should be deleted */ + FILE *fp; + int doclose; /* nz if free should close */ + char *filename; /* NULL if not known */ + + /* Private: */ + size_t size; /* Size of the file (For read) */ + +}; typedef struct _cgatsFileStd cgatsFileStd; + +/* Create given a file name */ +cgatsFile *new_cgatsFileStd_name(const char *name, const char *mode); + +/* Create given a (binary) FILE* */ +cgatsFile *new_cgatsFileStd_fp(FILE *fp); + +/* Create given a file name and allocator */ +cgatsFile *new_cgatsFileStd_name_a(const char *name, const char *mode, cgatsAlloc *al); + +/* Create given a (binary) FILE* and allocator */ +cgatsFile *new_cgatsFileStd_fp_a(FILE *fp, cgatsAlloc *al); + + +/* - - - - - - - - - - - - - - - - - - - - - */ +/* Implementation of file access class based on a memory image */ +/* The buffer is assumed to be allocated with the given heap allocator */ +/* Pass base = NULL, length = 0 for no initial buffer */ + +/* ~~ should ad method that returns buffer and length */ + +struct _cgatsFileMem { + CGATS_FILE_BASE + + /* Private: */ + cgatsAlloc *al; /* Heap allocator */ + int del_al; /* NZ if heap allocator should be deleted */ + int del_buf; /* NZ if memory file buffer should be deleted */ + unsigned char *start, *cur, *end, *aend; + +}; typedef struct _cgatsFileMem cgatsFileMem; + +/* Create a memory image file access class with given allocator */ +/* The buffer is not delete with the object. */ +cgatsFile *new_cgatsFileMem_a(void *base, size_t length, cgatsAlloc *al); + +/* Create a memory image file access class with given allocator */ +/* and delete buffer when cgatsFile is deleted. */ +cgatsFile *new_cgatsFileMem_ad(void *base, size_t length, cgatsAlloc *al); + +/* This is avalailable if SEPARATE_STD is not defined: */ + +/* Create a memory image file access class with the std allocator */ +/* The buffer is not delete with the object. */ +cgatsFile *new_cgatsFileMem(void *base, size_t length); + +/* Create a memory image file access class with the std allocator */ +/* and delete buffer when cgatsFile is deleted. */ +cgatsFile *new_cgatsFileMem_d(void *base, size_t length); + + +/* - - - - - - - - - - - - - - - - - - - - - */ + +struct _parse { + /* Public Variables */ + int line; /* Current line number */ + int token; /* Current token number */ + + /* Public Methods */ + void (*del)(struct _parse *p); /* Delete the object */ + void (*reset_del)(struct _parse *p); /* Reset the parsing delimiters */ + void (*add_del)(struct _parse *p, /* Add to the parsing delimiters */ + char *t, char *nr, + char *c, char *q); + int (*read_line)(struct _parse *p); /* Read in a line. Return 0 if read failed, */ + /* -1 on other error */ + char *(*get_token)(struct _parse *p); /* Return a pointer to the next token, */ + /* NULL if no tokens. set errc NZ on other error */ + + /* Private */ + cgatsAlloc *al; /* Memory allocator */ + int del_al; /* Flag to indicate we al->del() */ + cgatsFile *fp; /* File we're dealing with */ + int ltflag; /* Last terminator flag */ + int q; /* Quote */ + char *b; /* Line buffer */ + int bs; /* Buffer size */ + int bo; /* Next buffer offset */ + int to; /* Token parsing offset into b */ + char *tb; /* Token buffer */ + int tbs; /* Token buffer size */ + char delf[256]; /* Parsing delimiter flags */ + /* Parsing flags */ +#define PARS_TERM 0x01 /* Terminates a token */ +#define PARS_SKIP 0x02 /* Character is not read */ +#define PARS_COMM 0x04 /* Character starts a comment */ +#define PARS_QUOTE 0x08 /* Character starts/ends a quoted string */ + + char err[200]; /* Error message */ + int errc; /* Error code */ +}; typedef struct _parse parse; + +/* Creator */ +extern parse *new_parse_al(cgatsAlloc *al, cgatsFile *fp); /* With allocator class */ + +/* Available when SEPARATE_STD is not defined: */ +extern parse *new_parse(cgatsFile *fp); /* Default allocator */ + +#define PARS_H +#endif /* PARS_H */ + diff --git a/cgats/parsstd.c b/cgats/parsstd.c new file mode 100644 index 0000000..4443e0a --- /dev/null +++ b/cgats/parsstd.c @@ -0,0 +1,476 @@ + +/* + * parse library stdio and malloc utility classes. + * Version 2.05 + * + * Author: Graeme W. Gill + * Date: 2002/10/24 + * + * Copyright 2002, Graeme W. Gill + * All rights reserved. + * + * This material is licensed with an "MIT" free use license:- + * see the License.txt file in this directory for licensing details. + * + * These are kept in a separate file to allow them to be + * selectively ommitted from the cgats library. + * + */ + +#define _PARSSTD_C_ + +#ifndef COMBINED_STD + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __sun +#include +#endif + +#include "pars.h" + +#endif /* !COMBINED_STD */ + +#if defined(SEPARATE_STD) || defined(COMBINED_STD) + +/* ----------------------------------------------- */ +/* Standard Heap allocator cgatsAlloc compatible class */ +/* Just call the standard system function */ + +#ifdef CGATS_DEBUG_MALLOC + +/* Make sure that inline malloc #defines are turned off for this file */ +#undef was_debug_malloc +#ifdef malloc +#undef malloc +#undef calloc +#undef realloc +#undef free +#define was_debug_malloc +#endif /* dmalloc */ + +static void *cgatsAllocStd_dmalloc( +struct _cgatsAlloc *pp, +size_t size, +char *file, +int line +) { + void *rv = malloc(size); + return rv; +} + +static void *cgatsAllocStd_dcalloc( +struct _cgatsAlloc *pp, +size_t num, +size_t size, +char *file, +int line +) { + void *rv = calloc(num, size); + return rv; +} + +static void *cgatsAllocStd_drealloc( +struct _cgatsAlloc *pp, +void *ptr, +size_t size, +char *file, +int line +) { + void *rv = realloc(ptr, size); + return rv; +} + + +static void cgatsAllocStd_dfree( +struct _cgatsAlloc *pp, +void *ptr, +char *file, +int line +) { + free(ptr); +} + +/* we're done with the AllocStd object */ +static void cgatsAllocStd_delete( +cgatsAlloc *pp +) { + cgatsAllocStd *p = (cgatsAllocStd *)pp; + + free(p); +} + +/* Create cgatsAllocStd */ +cgatsAlloc *new_cgatsAllocStd() { + cgatsAllocStd *p; + if ((p = (cgatsAllocStd *) calloc(1,sizeof(cgatsAllocStd))) == NULL) + return NULL; + p->dmalloc = cgatsAllocStd_dmalloc; + p->dcalloc = cgatsAllocStd_dcalloc; + p->drealloc = cgatsAllocStd_drealloc; + p->dfree = cgatsAllocStd_dfree; + p->del = cgatsAllocStd_delete; + + return (cgatsAlloc *)p; +} + +#ifdef was_debug_malloc +#undef was_debug_malloc +#define malloc( p, size ) dmalloc( p, size, __FILE__, __LINE__ ) +#define calloc( p, num, size ) dcalloc( p, num, size, __FILE__, __LINE__ ) +#define realloc( p, ptr, size ) drealloc( p, ptr, size, __FILE__, __LINE__ ) +#define free( p, ptr ) dfree( p, ptr , __FILE__, __LINE__ ) +#endif /* was_debug_malloc */ + +#else /* !CGATS_DEBUG_MALLOC */ + +static void *cgatsAllocStd_malloc( +struct _cgatsAlloc *pp, +size_t size +) { + void *rv = malloc(size); + return rv; +} + +static void *cgatsAllocStd_calloc( +struct _cgatsAlloc *pp, +size_t num, +size_t size +) { + void *rv = calloc(num, size); + return rv; +} + +static void *cgatsAllocStd_realloc( +struct _cgatsAlloc *pp, +void *ptr, +size_t size +) { + void *rv = realloc(ptr, size); + return rv; +} + + +static void cgatsAllocStd_free( +struct _cgatsAlloc *pp, +void *ptr +) { + free(ptr); +} + +/* we're done with the AllocStd object */ +static void cgatsAllocStd_delete( +cgatsAlloc *pp +) { + cgatsAllocStd *p = (cgatsAllocStd *)pp; + + free(p); +} + +/* Create cgatsAllocStd */ +cgatsAlloc *new_cgatsAllocStd() { + cgatsAllocStd *p; + if ((p = (cgatsAllocStd *) calloc(1,sizeof(cgatsAllocStd))) == NULL) + return NULL; + p->malloc = cgatsAllocStd_malloc; + p->calloc = cgatsAllocStd_calloc; + p->realloc = cgatsAllocStd_realloc; + p->free = cgatsAllocStd_free; + p->del = cgatsAllocStd_delete; + + return (cgatsAlloc *)p; +} + +#endif /* !CGATS_DEBUG_MALLOC */ + +/* ------------------------------------------------- */ +/* Standard Stream file I/O cgatsFile compatible class */ + +/* Get the size of the file (Only valid for reading file. */ +static size_t cgatsFileStd_get_size(cgatsFile *pp) { + cgatsFileStd *p = (cgatsFileStd *)pp; + + return p->size; +} + +/* Set current position to offset. Return 0 on success, nz on failure. */ +static int cgatsFileStd_seek( +cgatsFile *pp, +unsigned int offset +) { + cgatsFileStd *p = (cgatsFileStd *)pp; + + return fseek(p->fp, offset, SEEK_SET); +} + +/* Read count items of size length. Return number of items successfully read. */ +static size_t cgatsFileStd_read( +cgatsFile *pp, +void *buffer, +size_t size, +size_t count +) { + cgatsFileStd *p = (cgatsFileStd *)pp; + + return fread(buffer, size, count, p->fp); +} + +/* Read a character */ +static int cgatsFileStd_getch( +cgatsFile *pp +) { + + cgatsFileStd *p = (cgatsFileStd *)pp; + + return fgetc(p->fp); +} + +/* write count items of size length. Return number of items successfully written. */ +static size_t cgatsFileStd_write( +cgatsFile *pp, +void *buffer, +size_t size, +size_t count +) { + cgatsFileStd *p = (cgatsFileStd *)pp; + + return fwrite(buffer, size, count, p->fp); +} + +/* do a printf */ +static int cgatsFileStd_printf( +cgatsFile *pp, +const char *format, +... +) { + int rv; + va_list args; + cgatsFileStd *p = (cgatsFileStd *)pp; + + va_start(args, format); + rv = vfprintf(p->fp, format, args); + va_end(args); + return rv; +} + +/* flush all write data out to secondary storage. Return nz on failure. */ +static int cgatsFileStd_flush( +cgatsFile *pp +) { + cgatsFileStd *p = (cgatsFileStd *)pp; + + return fflush(p->fp); +} + +/* Return the memory buffer. Error if not cgatsFileMem */ +static int cgatsFileStd_get_buf( +cgatsFile *pp, +unsigned char **buf, +size_t *len +) { + return 1; +} + +/* return the filename */ +static char *cgatsFileStd_fname( +cgatsFile *pp +) { + cgatsFileStd *p = (cgatsFileStd *)pp; + + if (p->filename != NULL) + return p->filename; + else + return "**Unknown**"; +} + + +/* we're done with the file object, return nz on failure */ +static int cgatsFileStd_delete( +cgatsFile *pp +) { + int rv = 0; + cgatsFileStd *p = (cgatsFileStd *)pp; + cgatsAlloc *al = p->al; + int del_al = p->del_al; + + if (p->doclose != 0) { + if (fclose(p->fp) != 0) + rv = 2; + } + + if (p->filename != NULL) + al->free(al, p->filename); + + al->free(al, p); /* Free object */ + if (del_al) /* We are responsible for deleting allocator */ + al->del(al); + + return rv; +} + +/* Create cgatsFile given a (binary) FILE* */ +cgatsFile *new_cgatsFileStd_fp( +FILE *fp +) { + return new_cgatsFileStd_fp_a(fp, NULL); +} + +/* Create cgatsFile given a (binary) FILE* and allocator */ +cgatsFile *new_cgatsFileStd_fp_a( +FILE *fp, +cgatsAlloc *al /* heap allocator, NULL for default */ +) { + cgatsFileStd *p; + int del_al = 0; + struct stat sbuf; + + if (al == NULL) { /* None provided, create default */ + if ((al = new_cgatsAllocStd()) == NULL) + return NULL; + del_al = 1; /* We need to delete it */ + } + + if ((p = (cgatsFileStd *) al->calloc(al, 1, sizeof(cgatsFileStd))) == NULL) { + if (del_al) + al->del(al); + return NULL; + } + p->al = al; /* Heap allocator */ + p->del_al = del_al; /* Flag noting whether we delete it */ + p->get_size = cgatsFileStd_get_size; + p->seek = cgatsFileStd_seek; + p->read = cgatsFileStd_read; + p->getch = cgatsFileStd_getch; + p->write = cgatsFileStd_write; + p->gprintf = cgatsFileStd_printf; + p->flush = cgatsFileStd_flush; + p->get_buf = cgatsFileStd_get_buf; + p->fname = cgatsFileStd_fname; + p->del = cgatsFileStd_delete; + + if (fstat(fileno(fp), &sbuf) == 0) { + p->size = sbuf.st_size; + } else { + p->size = 0; + } + + p->fp = fp; + p->doclose = 0; + + return (cgatsFile *)p; +} + +/* Create cgatsFile given a file name */ +cgatsFile *new_cgatsFileStd_name( +const char *name, +const char *mode +) { + return new_cgatsFileStd_name_a(name, mode, NULL); +} + +/* Create given a file name and allocator */ +cgatsFile *new_cgatsFileStd_name_a( +const char *name, +const char *mode, +cgatsAlloc *al /* heap allocator, NULL for default */ +) { + FILE *fp; + cgatsFile *p; + char nmode[50]; + + strcpy(nmode, mode); +#if !defined(O_CREAT) && !defined(_O_CREAT) +# error "Need to #include fcntl.h!" +#endif +#if defined(O_BINARY) || defined(_O_BINARY) + strcat(nmode, "b"); +#endif + + if ((fp = fopen(name, nmode)) == NULL) + return NULL; + + p = new_cgatsFileStd_fp_a(fp, al); + + if (p != NULL) { + cgatsFileStd *pp = (cgatsFileStd *)p; + pp->doclose = 1; + + pp->filename = pp->al->malloc(pp->al, strlen(name) + 1); + strcpy(pp->filename, name); + } + return p; +} + +/* Create a memory image file access class with the std allocator */ +/* Don't free the buffer on delete */ +cgatsFile *new_cgatsFileMem( +void *base, /* Pointer to base of memory buffer */ +size_t length /* Number of bytes in buffer */ +) { + cgatsFile *p; + cgatsAlloc *al; /* memory allocator */ + + if ((al = new_cgatsAllocStd()) == NULL) + return NULL; + + if ((p = new_cgatsFileMem_a(base, length, al)) == NULL) { + al->del(al); + return NULL; + } + + ((cgatsFileMem *)p)->del_al = 1; /* Get cgatsFileMem->del to cleanup al */ + return p; +} + +/* Create a memory image file access class with the std allocator */ +/* Free buffer on delete */ +cgatsFile *new_cgatsFileMem_d( +void *base, /* Pointer to base of memory buffer */ +size_t length /* Number of bytes in buffer */ +) { + cgatsFile *p; + cgatsAlloc *al; /* memory allocator */ + + if ((al = new_cgatsAllocStd()) == NULL) + return NULL; + + if ((p = new_cgatsFileMem_a(base, length, al)) == NULL) { + al->del(al); + return NULL; + } + + ((cgatsFileMem *)p)->del_al = 1; /* Get cgatsFileMem->del to cleanup al */ + ((cgatsFileMem *)p)->del_buf = 1; /* Get cgatsFileMem->del to cleanup buffer */ + return p; +} + +/* ------------------------------------------------- */ + +/* Create an empty parse object, default standard allocator */ +parse * +new_parse(cgatsFile *fp) { + parse *p; + cgatsAlloc *al; /* memory allocator */ + + if ((al = new_cgatsAllocStd()) == NULL) + return NULL; + + if ((p = new_parse_al(al, fp)) == NULL) { + al->del(al); + return NULL; + } + + p->del_al = 1; /* Get parse->del to cleanup allocator */ + return p; +} + + +#endif /* defined(SEPARATE_STD) || defined(COMBINED_STD) */ -- cgit v1.2.3