|
|
/* */
|
|
|
/* Little cms - profiler construction set */
|
|
|
/* Copyright (C) 1998-2001 Marti Maria <marti@littlecms.com> */
|
|
|
/* */
|
|
|
/* THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, */
|
|
|
/* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY */
|
|
|
/* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */
|
|
|
/* */
|
|
|
/* IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, */
|
|
|
/* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, */
|
|
|
/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */
|
|
|
/* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF */
|
|
|
/* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE */
|
|
|
/* OF THIS SOFTWARE. */
|
|
|
/* */
|
|
|
/* This file is free software; you can redistribute it and/or modify it */
|
|
|
/* under the terms of the GNU General Public License as published by */
|
|
|
/* the Free Software Foundation; either version 2 of the License, or */
|
|
|
/* (at your option) any later version. */
|
|
|
/* */
|
|
|
/* This program is distributed in the hope that it will be useful, but */
|
|
|
/* WITHOUT ANY WARRANTY; without even the implied warranty of */
|
|
|
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
|
|
|
/* General Public License for more details. */
|
|
|
/* */
|
|
|
/* You should have received a copy of the GNU General Public License */
|
|
|
/* along with this program; if not, write to the Free Software */
|
|
|
/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
|
|
|
/* */
|
|
|
/* As a special exception to the GNU General Public License, if you */
|
|
|
/* distribute this file as part of a program that contains a */
|
|
|
/* configuration script generated by Autoconf, you may include it under */
|
|
|
/* the same distribution terms that you use for the rest of that program. */
|
|
|
/* */
|
|
|
/* Version 1.08a */
|
|
|
|
|
|
|
|
|
#include "lcmsprf.h"
|
|
|
|
|
|
/* #define _DEBUG 1 */
|
|
|
|
|
|
|
|
|
|
|
|
/* IT8.7 / CGATS.17-200x handling */
|
|
|
LCMSHANDLE cdecl cmsxIT8Alloc(void);
|
|
|
void cdecl cmsxIT8Free(LCMSHANDLE IT8);
|
|
|
|
|
|
/* Persistence */
|
|
|
LCMSHANDLE cdecl cmsxIT8LoadFromFile(const char* cFileName);
|
|
|
LCMSHANDLE cdecl cmsxIT8LoadFromMem(void *Ptr, size_t len);
|
|
|
BOOL cdecl cmsxIT8SaveToFile(LCMSHANDLE IT8, const char* cFileName);
|
|
|
|
|
|
/* Properties */
|
|
|
const char* cdecl cmsxIT8GetSheetType(LCMSHANDLE hIT8);
|
|
|
BOOL cdecl cmsxIT8SetSheetType(LCMSHANDLE hIT8, const char* Type);
|
|
|
|
|
|
BOOL cdecl cmsxIT8SetProperty(LCMSHANDLE hIT8, const char* cProp, const char *Str);
|
|
|
BOOL cdecl cmsxIT8SetPropertyDbl(LCMSHANDLE hIT8, const char* cProp, double Val);
|
|
|
|
|
|
const char* cdecl cmsxIT8GetProperty(LCMSHANDLE hIT8, const char* cProp);
|
|
|
double cdecl cmsxIT8GetPropertyDbl(LCMSHANDLE hIT8, const char* cProp);
|
|
|
int cdecl cmsxIT8EnumProperties(LCMSHANDLE IT8, char ***PropertyNames);
|
|
|
|
|
|
|
|
|
/* Datasets */
|
|
|
|
|
|
BOOL cdecl cmsxIT8GetDataSetByPos(LCMSHANDLE IT8, int col, int row, char* Val, int ValBufferLen);
|
|
|
|
|
|
BOOL cdecl cmsxIT8GetDataSet(LCMSHANDLE IT8, const char* cPatch,
|
|
|
const char* cSample,
|
|
|
char* Val, int ValBufferLen);
|
|
|
|
|
|
|
|
|
BOOL cdecl cmsxIT8GetDataSetDbl(LCMSHANDLE IT8, const char* cPatch, const char* cSample, double* d);
|
|
|
|
|
|
BOOL cdecl cmsxIT8SetDataSet(LCMSHANDLE IT8, const char* cPatch,
|
|
|
const char* cSample,
|
|
|
char *Val);
|
|
|
|
|
|
BOOL cdecl cmsxIT8SetDataFormat(LCMSHANDLE IT8, int n, const char *Sample);
|
|
|
int cdecl cmsxIT8EnumDataFormat(LCMSHANDLE IT8, char ***SampleNames);
|
|
|
|
|
|
const char *cdecl cmsxIT8GenericPatchName(int nPatch, char* buffer);
|
|
|
int cdecl cmsxIT8GenericPatchNum(const char *Name);
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------- Implementation */
|
|
|
|
|
|
|
|
|
#define MAXID 128 /* Max length of identifier */
|
|
|
#define MAXSTR 255 /* Max length of string */
|
|
|
|
|
|
#ifndef NON_WINDOWS
|
|
|
#include <io.h>
|
|
|
#endif
|
|
|
|
|
|
/* Symbols */
|
|
|
|
|
|
typedef enum { SNONE,
|
|
|
SINUM, /* Integer */
|
|
|
SDNUM, /* Real */
|
|
|
SIDENT, /* Identifier */
|
|
|
SSTRING, /* string */
|
|
|
SCOMMENT, /* comment */
|
|
|
SEOLN, /* End of line */
|
|
|
SEOF, /* End of stream */
|
|
|
SSYNERROR, /* Syntax error found on stream */
|
|
|
|
|
|
/* Keywords */
|
|
|
|
|
|
SBEGIN_DATA,
|
|
|
SBEGIN_DATA_FORMAT,
|
|
|
SEND_DATA,
|
|
|
SEND_DATA_FORMAT,
|
|
|
SKEYWORD,
|
|
|
SSTRING_SY
|
|
|
|
|
|
} SYMBOL;
|
|
|
|
|
|
|
|
|
/* Linked list of variable names */
|
|
|
|
|
|
typedef struct _KeyVal {
|
|
|
|
|
|
struct _KeyVal* Next;
|
|
|
char* Keyword; /* Name of variable */
|
|
|
char* Value; /* Points to value */
|
|
|
|
|
|
} KEYVALUE, FAR* LPKEYVALUE;
|
|
|
|
|
|
/* Linked list of values (Memory sink) */
|
|
|
|
|
|
typedef struct _OwnedMem {
|
|
|
|
|
|
struct _OwnedMem* Next;
|
|
|
void * Ptr; /* Point to value */
|
|
|
|
|
|
} OWNEDMEM, FAR* LPOWNEDMEM;
|
|
|
|
|
|
|
|
|
/* This struct hold all information about an openened */
|
|
|
/* IT8 handler. Only one dataset is allowed. */
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
|
int nSamples, nPatches; /* Rows, Cols */
|
|
|
int SampleID; /* Pos of ID */
|
|
|
LPKEYVALUE HeaderList; /* The properties */
|
|
|
char* FileBuffer; /* The ASCII stream */
|
|
|
char** DataFormat; /* The binary stream descriptor */
|
|
|
char** Data; /* The binary stream */
|
|
|
LPOWNEDMEM MemorySink; /* The storage bakend */
|
|
|
|
|
|
/* Parser state machine */
|
|
|
|
|
|
SYMBOL sy; /* Current symbol */
|
|
|
int ch; /* Current character */
|
|
|
char* Source; /* Points to loc. being parsed */
|
|
|
int inum; /* integer value */
|
|
|
double dnum; /* real value */
|
|
|
char id[MAXID]; /* identifier */
|
|
|
char str[MAXSTR]; /* string */
|
|
|
|
|
|
/* Allowed keywords & datasets */
|
|
|
|
|
|
LPKEYVALUE ValidKeywords;
|
|
|
LPKEYVALUE ValidSampleID;
|
|
|
|
|
|
char FileName[MAX_PATH];
|
|
|
int lineno; /* line counter for error reporting */
|
|
|
|
|
|
char SheetType[MAXSTR]; /* New 1.09 */
|
|
|
|
|
|
} IT8, FAR* LPIT8;
|
|
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------ IT8 parsing routines */
|
|
|
|
|
|
|
|
|
/* A keyword */
|
|
|
typedef struct {
|
|
|
const char *id;
|
|
|
SYMBOL sy;
|
|
|
|
|
|
} KEYWORD;
|
|
|
|
|
|
/* The keyword->symbol translation table. Sorting is required. */
|
|
|
static const KEYWORD TabKeys[] = {
|
|
|
|
|
|
{"BEGIN_DATA", SBEGIN_DATA },
|
|
|
{"BEGIN_DATA_FORMAT", SBEGIN_DATA_FORMAT },
|
|
|
{"END_DATA", SEND_DATA},
|
|
|
{"END_DATA_FORMAT", SEND_DATA_FORMAT},
|
|
|
{"KEYWORD", SKEYWORD},
|
|
|
{"STRING", SSTRING_SY}};
|
|
|
|
|
|
#define NUMKEYS (sizeof(TabKeys)/sizeof(KEYWORD))
|
|
|
|
|
|
/* Predefined properties */
|
|
|
static char* PredefinedProperties[] = {
|
|
|
|
|
|
"NUMBER_OF_FIELDS", /* Required - NUMBER OF FIELDS */
|
|
|
"NUMBER_OF_SETS", /* Required - NUMBER OF SETS */
|
|
|
"ORIGINATOR", /* Required - Identifies the specific system, organization or individual that created the data file. */
|
|
|
"CREATED", /* Required - Indicates date of creation of the data file. */
|
|
|
"DESCRIPTOR", /* Required - Describes the purpose or contents of the data file. */
|
|
|
"DIFFUSE_GEOMETRY", /* The diffuse geometry used. Allowed values are "sphere" or "opal". */
|
|
|
"MANUFACTURER",
|
|
|
"MANUFACTURE", /* Some broken Fuji targets does store this value */
|
|
|
"PROD_DATE", /* Identifies year and month of production of the target in the form yyyy:mm. */
|
|
|
"SERIAL", /* Uniquely identifies individual physical target. */
|
|
|
|
|
|
"MATERIAL", /* Identifies the material on which the target was produced using a code */
|
|
|
/* uniquely identifying th e material. Th is is intend ed to be used for IT8.7 */
|
|
|
/* physical targets only (i.e . IT8.7/1 a nd IT8.7/2). */
|
|
|
|
|
|
"INSTRUMENTATION", /* Used to report the specific instrumentation used (manufacturer and */
|
|
|
/* model number) to generate the data reported. This data will often */
|
|
|
/* provide more information about the particular data collected than an */
|
|
|
/* extensive list of specific details. This is particularly important for */
|
|
|
/* spectral data or data derived from spectrophotometry. */
|
|
|
|
|
|
"MEASUREMENT_SOURCE", /* Illumination used for spectral measurements. This data helps provide */
|
|
|
/* a guide to the potential for issues of paper fluorescence, etc. */
|
|
|
|
|
|
"PRINT_CONDITIONS", /* Used to define the ch aracteristics of the printed sheet being reported. */
|
|
|
/* Where standard conditions have been defined (e.g., SW OP at nominal) */
|
|
|
/* named conditions may suffice. Otherwise, detailed in formation is */
|
|
|
/* needed. */
|
|
|
|
|
|
"SAMPLE_BACKING", /* Identifies the backing material used behind the sample during */
|
|
|
/* measurement. Allowed values are <20>black<63>, <20>white<74>, or "na". */
|
|
|
|
|
|
"CHISQ_DOF" /* Degrees of freedom associated with the Chi squared statistic */
|
|
|
};
|
|
|
|
|
|
#define NUMPREDEFINEDPROPS (sizeof(PredefinedProperties)/sizeof(char *))
|
|
|
|
|
|
|
|
|
/* Predefined sample types on dataset */
|
|
|
static char* PredefinedSampleID[] = {
|
|
|
|
|
|
"CMYK_C", /* Cyan component of CMYK data expressed as a percentage */
|
|
|
"CMYK_M", /* Magenta component of CMYK data expressed as a percentage */
|
|
|
"CMYK_Y", /* Yellow component of CMYK data expressed as a percentage */
|
|
|
"CMYK_K", /* Black component of CMYK data expressed as a percentage */
|
|
|
"D_RED", /* Red filter density */
|
|
|
"D_GREEN", /* Green filter density */
|
|
|
"D_BLUE", /* Blue filter density */
|
|
|
"D_VIS", /* Visual filter density */
|
|
|
"D_MAJOR_FILTER", /* Major filter d ensity */
|
|
|
"RGB_R", /* Red component of RGB data */
|
|
|
"RGB_G", /* Green component of RGB data */
|
|
|
"RGB_B", /* Blue com ponent of RGB data */
|
|
|
"SPECTRAL_NM", /* Wavelength of measurement expressed in nanometers */
|
|
|
"SPECTRAL_PCT", /* Percentage reflectance/transmittance */
|
|
|
"SPECTRAL_DEC", /* Reflectance/transmittance */
|
|
|
"XYZ_X", /* X component of tristimulus data */
|
|
|
"XYZ_Y", /* Y component of tristimulus data */
|
|
|
"XYZ_Z", /* Z component of tristimulus data */
|
|
|
"XYY_X" /* x component of chromaticity data */
|
|
|
"XYY_Y", /* y component of chromaticity data */
|
|
|
"XYY_CAPY", /* Y component of tristimulus data */
|
|
|
"LAB_L", /* L* component of Lab data */
|
|
|
"LAB_A", /* a* component of Lab data */
|
|
|
"LAB_B", /* b* component of Lab data */
|
|
|
"LAB_C", /* C*ab component of Lab data */
|
|
|
"LAB_H", /* hab component of Lab data */
|
|
|
"LAB_DE" /* CIE dE */
|
|
|
"LAB_DE_94", /* CIE dE using CIE 94 */
|
|
|
"LAB_DE_CMC", /* dE using CMC */
|
|
|
"LAB_DE_2000", /* CIE dE using CIE DE 2000 */
|
|
|
"MEAN_DE", /* Mean Delta E (LAB_DE) of samples compared to batch average */
|
|
|
/* (Used for data files for ANSI IT8.7/1 and IT8.7/2 targets) */
|
|
|
"STDEV_X", /* Standard deviation of X (tristimulus data) */
|
|
|
"STDEV_Y", /* Standard deviation of Y (tristimulus data) */
|
|
|
"STDEV_Z", /* Standard deviation of Z (tristimulus data) */
|
|
|
"STDEV_L", /* Standard deviation of L* */
|
|
|
"STDEV_A" /* Standard deviation of a* */
|
|
|
"STDEV_B", /* Standard deviation of b* */
|
|
|
"STDEV_DE", /* Standard deviation of CIE dE */
|
|
|
"CHI_STQD_PAR"}; /* The average of the standard deviations of L*, a* and b*. It is */
|
|
|
/* used to derive an estimate of the chi-squared parameter which is */
|
|
|
/* recommended as the predictor of the variability of dE */
|
|
|
|
|
|
#define NUMPREDEFINEDSAMPLEID (sizeof(PredefinedSampleID)/sizeof(char *))
|
|
|
|
|
|
|
|
|
/* Checks whatsever if c is a valid identifier middle char. */
|
|
|
static
|
|
|
BOOL isidchar(int c)
|
|
|
{
|
|
|
return (isalnum(c) || c == '$' || c == '%' || c == '&' || c == '/' || c == '.' || c == '_');
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Checks whatsever if c is a valid identifier first char. */
|
|
|
static
|
|
|
BOOL isfirstidchar(int c)
|
|
|
{
|
|
|
return !isdigit(c) && isidchar(c);
|
|
|
}
|
|
|
|
|
|
/* Checks if c is a separator */
|
|
|
static
|
|
|
BOOL isseparator(int c)
|
|
|
{
|
|
|
return (c == ' ' || c == '\t' || c == '\r');
|
|
|
}
|
|
|
|
|
|
/* a replacement for strupr(), just for compatibility sake */
|
|
|
|
|
|
static
|
|
|
void xstrupr(char *cp)
|
|
|
{
|
|
|
for (;*cp;cp++)
|
|
|
if (*cp >= 'a' && *cp <= 'z')
|
|
|
*cp += 'A'-'a';
|
|
|
}
|
|
|
|
|
|
/* A replacement for (the nonstandard) filelength */
|
|
|
|
|
|
static
|
|
|
int xfilelength(int fd)
|
|
|
{
|
|
|
#ifdef _MSC_VER
|
|
|
return _filelength(fd);
|
|
|
#else
|
|
|
struct stat sb;
|
|
|
if (fstat(fd, &sb) < 0)
|
|
|
return(-1);
|
|
|
return(sb.st_size);
|
|
|
#endif
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
static
|
|
|
BOOL SynError(LPIT8 it8, const char *Txt, ...)
|
|
|
{
|
|
|
char Buffer[256], ErrMsg[1024];
|
|
|
va_list args;
|
|
|
|
|
|
va_start(args, Txt);
|
|
|
vsprintf(Buffer, Txt, args);
|
|
|
va_end(args);
|
|
|
|
|
|
sprintf(ErrMsg, "%s: Line %d, %s", it8->FileName, it8->lineno, Buffer);
|
|
|
it8->sy = SSYNERROR;
|
|
|
cmsSignalError(LCMS_ERRC_ABORTED, ErrMsg);
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
static
|
|
|
BOOL Check(LPIT8 it8, SYMBOL sy, const char* Err)
|
|
|
{
|
|
|
if (it8 -> sy != sy)
|
|
|
return SynError(it8, Err);
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Read Next character from stream */
|
|
|
static
|
|
|
void NextCh(LPIT8 it8)
|
|
|
{
|
|
|
it8->ch = *it8->Source;
|
|
|
if (it8->ch) it8->Source++;
|
|
|
}
|
|
|
|
|
|
|
|
|
/* Try to see if current identifier is a keyword, if so return the referred symbol */
|
|
|
static
|
|
|
SYMBOL BinSrchKey(const char *id)
|
|
|
{
|
|
|
int l = 1;
|
|
|
int r = NUMKEYS;
|
|
|
int x, res;
|
|
|
|
|
|
while (r >= l)
|
|
|
{
|
|
|
x = (l+r)/2;
|
|
|
res = strcmp(id, TabKeys[x-1].id);
|
|
|
if (res == 0) return TabKeys[x-1].sy;
|
|
|
if (res < 0) r = x - 1;
|
|
|
else l = x + 1;
|
|
|
}
|
|
|
|
|
|
return SNONE;
|
|
|
}
|
|
|
|
|
|
|
|
|
/* 10 ^n */
|
|
|
static
|
|
|
double pow10(int n)
|
|
|
{
|
|
|
return pow(10, n);
|
|
|
}
|
|
|
|
|
|
|
|
|
/* Reads a Real number, tries to follow from integer number */
|
|
|
static
|
|
|
void ReadReal(LPIT8 it8, int inum)
|
|
|
{
|
|
|
it8->dnum = (double) inum;
|
|
|
|
|
|
while (isdigit(it8->ch)) {
|
|
|
|
|
|
it8->dnum = it8->dnum * 10.0 + (it8->ch - '0');
|
|
|
NextCh(it8);
|
|
|
}
|
|
|
|
|
|
if (it8->ch == '.') { /* Decimal point */
|
|
|
|
|
|
double frac = 0.0; /* fraction */
|
|
|
int prec = 0; /* precission */
|
|
|
|
|
|
NextCh(it8); /* Eats dec. point */
|
|
|
|
|
|
while (isdigit(it8->ch)) {
|
|
|
|
|
|
frac = frac * 10.0 + (it8->ch - '0');
|
|
|
prec++;
|
|
|
NextCh(it8);
|
|
|
}
|
|
|
|
|
|
it8->dnum = it8->dnum + (frac / pow10(prec));
|
|
|
}
|
|
|
|
|
|
/* Exponent, example 34.00E+20 */
|
|
|
if (toupper(it8->ch) == 'E') {
|
|
|
|
|
|
int e;
|
|
|
int sgn;
|
|
|
|
|
|
NextCh(it8); sgn = 1;
|
|
|
|
|
|
if (it8->ch == '-') {
|
|
|
|
|
|
sgn = -1; NextCh(it8);
|
|
|
}
|
|
|
else
|
|
|
if (it8->ch == '+') {
|
|
|
|
|
|
sgn = +1;
|
|
|
NextCh(it8);
|
|
|
}
|
|
|
|
|
|
|
|
|
e = 0;
|
|
|
while (isdigit(it8->ch)) {
|
|
|
|
|
|
if ((double) e * 10L < INT_MAX)
|
|
|
e = e * 10 + (it8->ch - '0');
|
|
|
|
|
|
NextCh(it8);
|
|
|
}
|
|
|
|
|
|
e = sgn*e;
|
|
|
|
|
|
it8 -> dnum = it8 -> dnum * pow10(e);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Reads next symbol */
|
|
|
static
|
|
|
void InSymbol(LPIT8 it8)
|
|
|
{
|
|
|
char *idptr;
|
|
|
int k;
|
|
|
SYMBOL key;
|
|
|
int sng;
|
|
|
|
|
|
do {
|
|
|
|
|
|
while (isseparator(it8->ch))
|
|
|
NextCh(it8);
|
|
|
|
|
|
if (isfirstidchar(it8->ch)) { /* Identifier */
|
|
|
|
|
|
|
|
|
k = 0;
|
|
|
idptr = it8->id;
|
|
|
|
|
|
do {
|
|
|
|
|
|
if (++k < MAXID) *idptr++ = (char) it8->ch;
|
|
|
|
|
|
NextCh(it8);
|
|
|
|
|
|
} while (isidchar(it8->ch));
|
|
|
|
|
|
*idptr = '\0';
|
|
|
xstrupr(it8->id);
|
|
|
|
|
|
key = BinSrchKey(it8->id);
|
|
|
if (key == SNONE) it8->sy = SIDENT;
|
|
|
else it8->sy = key;
|
|
|
|
|
|
}
|
|
|
else /* Is a number? */
|
|
|
if (isdigit(it8->ch) || it8->ch == '.' || it8->ch == '-' || it8->ch == '+')
|
|
|
{
|
|
|
int sign = 1;
|
|
|
|
|
|
if (it8->ch == '-') {
|
|
|
sign = -1;
|
|
|
NextCh(it8);
|
|
|
}
|
|
|
|
|
|
it8->inum = 0;
|
|
|
it8->sy = SINUM;
|
|
|
|
|
|
while (isdigit(it8->ch))
|
|
|
{
|
|
|
if ((long) it8->inum * 10L > (long) INT_MAX)
|
|
|
{
|
|
|
ReadReal(it8, it8->inum);
|
|
|
it8->sy = SDNUM;
|
|
|
it8->dnum *= sign;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
it8->inum = it8->inum * 10 + (it8->ch - '0');
|
|
|
NextCh(it8);
|
|
|
}
|
|
|
|
|
|
if (it8->ch == '.') {
|
|
|
|
|
|
ReadReal(it8, it8->inum);
|
|
|
it8->sy = SDNUM;
|
|
|
it8->dnum *= sign;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
it8 -> inum *= sign;
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
else
|
|
|
switch ((int) it8->ch) {
|
|
|
|
|
|
case '\0':
|
|
|
case '\x1a':
|
|
|
it8->sy = SEOF;
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case '\n':
|
|
|
NextCh(it8);
|
|
|
it8->sy = SEOLN;
|
|
|
it8->lineno++;
|
|
|
break;
|
|
|
|
|
|
/* Comment */
|
|
|
|
|
|
case '#':
|
|
|
NextCh(it8);
|
|
|
while (it8->ch && it8->ch != '\n')
|
|
|
NextCh(it8);
|
|
|
|
|
|
it8->sy = SCOMMENT;
|
|
|
break;
|
|
|
|
|
|
/* String. I will support \", \n, \t and \\. */
|
|
|
/* But otherwise I hardly doubt these will be used ... */
|
|
|
|
|
|
case '\'':
|
|
|
case '\"':
|
|
|
idptr = it8->str;
|
|
|
sng = it8->ch;
|
|
|
k = 0;
|
|
|
NextCh(it8);
|
|
|
|
|
|
while (k < MAXSTR && it8->ch != sng) {
|
|
|
|
|
|
if (it8->ch == '\n'|| it8->ch == '\r') k = MAXSTR+1;
|
|
|
else {
|
|
|
|
|
|
if (it8->ch == '\\')
|
|
|
{
|
|
|
NextCh(it8);
|
|
|
|
|
|
switch (it8->ch) {
|
|
|
|
|
|
case 'n': *idptr++ = '\n'; break;
|
|
|
case 'r': *idptr++ = '\r'; break;
|
|
|
case 't': *idptr++ = '\t'; break;
|
|
|
case '\\': *idptr++ = '\\'; break;
|
|
|
|
|
|
default:
|
|
|
*idptr++ = (char) it8->ch;
|
|
|
}
|
|
|
|
|
|
NextCh(it8);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
*idptr++ = (char) it8->ch;
|
|
|
NextCh(it8);
|
|
|
}
|
|
|
|
|
|
k++;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
it8->sy = SSTRING;
|
|
|
*idptr = '\0';
|
|
|
NextCh(it8);
|
|
|
break;
|
|
|
|
|
|
|
|
|
default:
|
|
|
it8->sy = SSYNERROR;
|
|
|
NextCh(it8);
|
|
|
|
|
|
}
|
|
|
|
|
|
} while (it8->sy == SCOMMENT);
|
|
|
}
|
|
|
|
|
|
/* Checks end of line separator */
|
|
|
static
|
|
|
BOOL CheckEOLN(LPIT8 it8)
|
|
|
{
|
|
|
if (!Check(it8, SEOLN, "Expected separator")) return false;
|
|
|
while (it8 -> sy == SEOLN)
|
|
|
InSymbol(it8);
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Skip a symbol */
|
|
|
|
|
|
static
|
|
|
void Skip(LPIT8 it8, SYMBOL sy)
|
|
|
{
|
|
|
if (it8->sy == sy && it8->sy != SEOF)
|
|
|
InSymbol(it8);
|
|
|
}
|
|
|
|
|
|
/* Returns a string holding current value */
|
|
|
static
|
|
|
BOOL GetVal(LPIT8 it8, char* Buffer)
|
|
|
{
|
|
|
switch (it8->sy) {
|
|
|
|
|
|
case SIDENT: strncpy(Buffer, it8->id, MAXID-1); break;
|
|
|
case SINUM: sprintf(Buffer, "%d", it8 -> inum); break;
|
|
|
case SDNUM: sprintf(Buffer, "%g", it8 -> dnum); break;
|
|
|
case SSTRING: strncpy(Buffer, it8->str, MAXSTR-1); break;
|
|
|
|
|
|
|
|
|
default:
|
|
|
return SynError(it8, "Sample data expected");
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------- Memory management */
|
|
|
|
|
|
|
|
|
|
|
|
/* Frees an allocator and owned memory */
|
|
|
void cmsxIT8Free(LCMSHANDLE hIT8)
|
|
|
{
|
|
|
LPIT8 it8 = (LPIT8) hIT8;
|
|
|
|
|
|
if (it8 == NULL)
|
|
|
return;
|
|
|
|
|
|
if (it8->MemorySink) {
|
|
|
|
|
|
LPOWNEDMEM p;
|
|
|
LPOWNEDMEM n;
|
|
|
|
|
|
for (p = it8->MemorySink; p != NULL; p = n) {
|
|
|
|
|
|
n = p->Next;
|
|
|
if (p->Ptr) free(p->Ptr);
|
|
|
free(p);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (it8->FileBuffer)
|
|
|
free(it8->FileBuffer);
|
|
|
|
|
|
free(it8);
|
|
|
}
|
|
|
|
|
|
|
|
|
/* Allocates a chunk of data, keep linked list */
|
|
|
static
|
|
|
void* AllocChunk(LPIT8 it8, size_t size)
|
|
|
{
|
|
|
LPOWNEDMEM ptr1;
|
|
|
void* ptr = malloc(size);
|
|
|
|
|
|
if (ptr) {
|
|
|
|
|
|
ZeroMemory(ptr, size);
|
|
|
ptr1 = (LPOWNEDMEM) malloc(sizeof(OWNEDMEM));
|
|
|
|
|
|
if (ptr1 == NULL) {
|
|
|
|
|
|
free(ptr);
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
ZeroMemory(ptr1, sizeof(OWNEDMEM));
|
|
|
|
|
|
ptr1-> Ptr = ptr;
|
|
|
ptr1-> Next = it8 -> MemorySink;
|
|
|
it8 -> MemorySink = ptr1;
|
|
|
}
|
|
|
|
|
|
return ptr;
|
|
|
}
|
|
|
|
|
|
|
|
|
/* Allocates a string */
|
|
|
static
|
|
|
char *AllocString(LPIT8 it8, const char* str)
|
|
|
{
|
|
|
int Size = strlen(str)+1;
|
|
|
char *ptr;
|
|
|
ptr = (char *) AllocChunk(it8, Size);
|
|
|
if (ptr) strncpy (ptr, str, Size);
|
|
|
return ptr;
|
|
|
}
|
|
|
|
|
|
/* Searches through linked list */
|
|
|
|
|
|
static
|
|
|
BOOL IsAvailableOnList(LPKEYVALUE p, const char* Key, LPKEYVALUE* LastPtr)
|
|
|
{
|
|
|
for (; p != NULL; p = p->Next) {
|
|
|
|
|
|
if (LastPtr) *LastPtr = p;
|
|
|
if (stricmp(Key, p->Keyword) == 0)
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Add a property into a linked list */
|
|
|
static
|
|
|
BOOL AddToList(LPIT8 it8, LPKEYVALUE* Head, const char *Key, const char* Value)
|
|
|
{
|
|
|
LPKEYVALUE p;
|
|
|
LPKEYVALUE last;
|
|
|
|
|
|
|
|
|
/* Check if property is already in list (this is an error) */
|
|
|
|
|
|
if (IsAvailableOnList(*Head, Key, &last)) {
|
|
|
cmsSignalError(LCMS_ERRC_ABORTED, "duplicate key <%s>", Key);
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
/* Allocate the container */
|
|
|
p = (LPKEYVALUE) AllocChunk(it8, sizeof(KEYVALUE));
|
|
|
if (p == NULL)
|
|
|
{
|
|
|
cmsSignalError(LCMS_ERRC_ABORTED, "AddToList: out of memory");
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
/* Store name and value */
|
|
|
p->Keyword = AllocString(it8, Key);
|
|
|
|
|
|
if (Value)
|
|
|
p->Value = AllocString(it8, Value);
|
|
|
else
|
|
|
p->Value = NULL;
|
|
|
|
|
|
p->Next = NULL;
|
|
|
|
|
|
/* Keep the container in our list */
|
|
|
if (*Head == NULL)
|
|
|
*Head = p;
|
|
|
else
|
|
|
last->Next = p;
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
static
|
|
|
BOOL AddAvailableProperty(LPIT8 it8, const char* Key)
|
|
|
{
|
|
|
return AddToList(it8, &it8->ValidKeywords, Key, NULL);
|
|
|
}
|
|
|
|
|
|
|
|
|
static
|
|
|
BOOL AddAvailableSampleID(LPIT8 it8, const char* Key)
|
|
|
{
|
|
|
return AddToList(it8, &it8->ValidSampleID, Key, NULL);
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Init an empty container */
|
|
|
LCMSHANDLE cmsxIT8Alloc(void)
|
|
|
{
|
|
|
LPIT8 it8;
|
|
|
int i;
|
|
|
|
|
|
it8 = (LPIT8) malloc(sizeof(IT8));
|
|
|
if (it8 == NULL) return NULL;
|
|
|
|
|
|
ZeroMemory(it8, sizeof(IT8));
|
|
|
|
|
|
it8->HeaderList = NULL;
|
|
|
it8->FileBuffer = NULL;
|
|
|
it8->DataFormat = NULL;
|
|
|
it8->Data = NULL;
|
|
|
it8->MemorySink = NULL;
|
|
|
it8->ValidKeywords = NULL;
|
|
|
it8->ValidSampleID = NULL;
|
|
|
|
|
|
it8 -> sy = SNONE;
|
|
|
it8 -> ch = ' ';
|
|
|
it8 -> Source = NULL;
|
|
|
it8 -> inum = 0;
|
|
|
it8 -> dnum = 0.0;
|
|
|
|
|
|
it8 -> lineno = 1;
|
|
|
|
|
|
strcpy(it8->SheetType, "IT8.7/2");
|
|
|
|
|
|
/* Initialize predefined properties & data */
|
|
|
|
|
|
for (i=0; i < NUMPREDEFINEDPROPS; i++)
|
|
|
AddAvailableProperty(it8, PredefinedProperties[i]);
|
|
|
|
|
|
for (i=0; i < NUMPREDEFINEDSAMPLEID; i++)
|
|
|
AddAvailableSampleID(it8, PredefinedSampleID[i]);
|
|
|
|
|
|
|
|
|
return (LCMSHANDLE) it8;
|
|
|
}
|
|
|
|
|
|
|
|
|
const char* cdecl cmsxIT8GetSheetType(LCMSHANDLE hIT8)
|
|
|
{
|
|
|
LPIT8 it8 = (LPIT8) hIT8;
|
|
|
|
|
|
return it8 ->SheetType;
|
|
|
|
|
|
}
|
|
|
|
|
|
BOOL cmsxIT8SetSheetType(LCMSHANDLE hIT8, const char* Type)
|
|
|
{
|
|
|
LPIT8 it8 = (LPIT8) hIT8;
|
|
|
|
|
|
strncpy(it8 ->SheetType, Type, MAXSTR-1);
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Sets a property */
|
|
|
BOOL cmsxIT8SetProperty(LCMSHANDLE hIT8, const char* Key, const char *Val)
|
|
|
{
|
|
|
LPIT8 it8 = (LPIT8) hIT8;
|
|
|
|
|
|
if (!Val) return false;
|
|
|
if (!*Val) return false;
|
|
|
|
|
|
return AddToList(it8, &it8 -> HeaderList, Key, Val);
|
|
|
}
|
|
|
|
|
|
|
|
|
BOOL cmsxIT8SetPropertyDbl(LCMSHANDLE hIT8, const char* cProp, double Val)
|
|
|
{
|
|
|
char Buffer[256];
|
|
|
|
|
|
sprintf(Buffer, "%g", Val);
|
|
|
return cmsxIT8SetProperty(hIT8, cProp, Buffer);
|
|
|
}
|
|
|
|
|
|
/* Gets a property */
|
|
|
const char* cmsxIT8GetProperty(LCMSHANDLE hIT8, const char* Key)
|
|
|
{
|
|
|
LPIT8 it8 = (LPIT8) hIT8;
|
|
|
LPKEYVALUE p;
|
|
|
|
|
|
if (IsAvailableOnList(it8 -> HeaderList, Key, &p))
|
|
|
{
|
|
|
return p -> Value;
|
|
|
}
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
|
|
|
double cmsxIT8GetPropertyDbl(LCMSHANDLE hIT8, const char* cProp)
|
|
|
{
|
|
|
const char *v = cmsxIT8GetProperty(hIT8, cProp);
|
|
|
if (v) return atof(v);
|
|
|
else return 0.0;
|
|
|
}
|
|
|
|
|
|
/* ----------------------------------------------------------------- Datasets */
|
|
|
|
|
|
|
|
|
static
|
|
|
void AllocateDataFormat(LPIT8 it8)
|
|
|
{
|
|
|
if (it8 -> DataFormat) return; /* Already allocated */
|
|
|
|
|
|
it8 -> nSamples = atoi(cmsxIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
|
|
|
|
|
|
if (it8 -> nSamples <= 0) {
|
|
|
|
|
|
cmsSignalError(LCMS_ERRC_WARNING, "AllocateDataFormat: Unknown NUMBER_OF_FIELDS, assuming 10");
|
|
|
it8 -> nSamples = 10;
|
|
|
}
|
|
|
|
|
|
it8 -> DataFormat = (char**) AllocChunk (it8, (it8->nSamples + 1) * sizeof(char *));
|
|
|
if (it8->DataFormat == NULL)
|
|
|
{
|
|
|
cmsSignalError(LCMS_ERRC_ABORTED, "AllocateDataFormat: Unable to allocate dataFormat array");
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
static
|
|
|
const char *GetDataFormat(LPIT8 it8, int n)
|
|
|
{
|
|
|
if (it8->DataFormat)
|
|
|
return it8->DataFormat[n];
|
|
|
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
static
|
|
|
BOOL SetDataFormat(LPIT8 it8, int n, const char *label)
|
|
|
{
|
|
|
if (n > it8 -> nSamples) return false;
|
|
|
|
|
|
if (!it8->DataFormat)
|
|
|
AllocateDataFormat(it8);
|
|
|
|
|
|
if (it8->DataFormat) {
|
|
|
|
|
|
it8->DataFormat[n] = AllocString(it8, label);
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
|
|
|
BOOL cmsxIT8SetDataFormat(LCMSHANDLE h, int n, const char *Sample)
|
|
|
{
|
|
|
LPIT8 it8 = (LPIT8) h;
|
|
|
return SetDataFormat(it8, n, Sample);
|
|
|
}
|
|
|
|
|
|
static
|
|
|
void AllocateDataSet(LPIT8 it8)
|
|
|
{
|
|
|
if (it8 -> Data) return; /* Already allocated */
|
|
|
|
|
|
it8-> nSamples = atoi(cmsxIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
|
|
|
it8-> nPatches = atoi(cmsxIT8GetProperty(it8, "NUMBER_OF_SETS"));
|
|
|
it8-> Data = (char**)AllocChunk (it8, (it8->nSamples + 1) * (it8->nPatches + 1) *sizeof (char*));
|
|
|
if (it8->Data == NULL)
|
|
|
{
|
|
|
cmsSignalError(-1, "AllocateDataSet: Unable to allocate data array");
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
static
|
|
|
char* GetData(LPIT8 it8, int nSet, int nField)
|
|
|
{
|
|
|
int nSamples = it8 -> nSamples;
|
|
|
int nPatches = it8 -> nPatches;
|
|
|
|
|
|
if (nSet >= nPatches || nField >= nSamples)
|
|
|
return NULL;
|
|
|
|
|
|
if (!it8->Data) return NULL;
|
|
|
return it8->Data [nSet * nSamples + nField];
|
|
|
}
|
|
|
|
|
|
static
|
|
|
BOOL SetData(LPIT8 it8, int nSet, int nField, char *Val)
|
|
|
{
|
|
|
if (!it8->Data)
|
|
|
AllocateDataSet(it8);
|
|
|
|
|
|
if (!it8->Data) return false;
|
|
|
|
|
|
|
|
|
if (nSet > it8 -> nPatches) {
|
|
|
|
|
|
SynError(it8, "Patch %d out of range, there are %d datasets", nSet, it8 -> nPatches);
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
if (nField > it8 ->nSamples) {
|
|
|
SynError(it8, "Sample %d out of range, there are %d datasets", nField, it8 ->nSamples);
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
|
|
|
it8->Data [nSet * it8 -> nSamples + nField] = AllocString(it8, Val);
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
|
|
|
/* --------------------------------------------------------------- File I/O */
|
|
|
|
|
|
|
|
|
/* Writes a string to file */
|
|
|
static
|
|
|
void WriteStr(FILE *f, char *str)
|
|
|
{
|
|
|
if (str == NULL)
|
|
|
fwrite(" ", 1, 1, f);
|
|
|
else
|
|
|
fwrite(str, 1, strlen(str), f);
|
|
|
}
|
|
|
|
|
|
|
|
|
/* Writes full header */
|
|
|
static
|
|
|
void WriteHeader(LPIT8 it8, FILE *fp)
|
|
|
{
|
|
|
LPKEYVALUE p;
|
|
|
|
|
|
WriteStr(fp, it8->SheetType);
|
|
|
WriteStr(fp, "\n");
|
|
|
for (p = it8->HeaderList; (p != NULL); p = p->Next)
|
|
|
{
|
|
|
if (!IsAvailableOnList(it8-> ValidKeywords, p->Keyword, NULL)) {
|
|
|
|
|
|
WriteStr(fp, "KEYWORD\t\"");
|
|
|
WriteStr(fp, p->Keyword);
|
|
|
WriteStr(fp, "\"\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
WriteStr(fp, p->Keyword);
|
|
|
if (p->Value) {
|
|
|
|
|
|
WriteStr(fp, "\t\"");
|
|
|
WriteStr(fp, p->Value);
|
|
|
WriteStr(fp, "\"");
|
|
|
}
|
|
|
WriteStr (fp, "\n");
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
/* Writes the data format */
|
|
|
static
|
|
|
void WriteDataFormat(FILE *fp, LPIT8 it8)
|
|
|
{
|
|
|
int i, nSamples;
|
|
|
|
|
|
if (!it8 -> DataFormat) return;
|
|
|
|
|
|
WriteStr(fp, "BEGIN_DATA_FORMAT\n");
|
|
|
nSamples = atoi(cmsxIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
|
|
|
|
|
|
for (i = 0; i < nSamples; i++) {
|
|
|
|
|
|
WriteStr(fp, it8->DataFormat[i]);
|
|
|
WriteStr(fp, (char*)((i == (nSamples-1)) ? "\n" : "\t")); // C->C++ : cast
|
|
|
}
|
|
|
|
|
|
WriteStr (fp, "END_DATA_FORMAT\n");
|
|
|
}
|
|
|
|
|
|
|
|
|
/* Writes data array */
|
|
|
static
|
|
|
void WriteData(FILE *fp, LPIT8 it8)
|
|
|
{
|
|
|
int i, j;
|
|
|
|
|
|
if (!it8->Data) return;
|
|
|
|
|
|
WriteStr (fp, "BEGIN_DATA\n");
|
|
|
|
|
|
it8->nPatches = atoi(cmsxIT8GetProperty(it8, "NUMBER_OF_SETS"));
|
|
|
|
|
|
for (i = 0; i < it8-> nPatches; i++) {
|
|
|
|
|
|
for (j = 0; j < it8->nSamples; j++) {
|
|
|
|
|
|
char *ptr = it8->Data[i*it8->nSamples+j];
|
|
|
|
|
|
WriteStr(fp, (char*)((ptr == NULL) ? "0.00" : ptr)); // C->C++ : cast
|
|
|
WriteStr(fp, (char*)((j == (it8->nSamples-1)) ? "\n" : "\t")); // C->C++ : cast
|
|
|
}
|
|
|
}
|
|
|
WriteStr (fp, "END_DATA\n");
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Saves whole file */
|
|
|
BOOL cmsxIT8SaveToFile(LCMSHANDLE hIT8, const char* cFileName)
|
|
|
{
|
|
|
FILE *fp;
|
|
|
LPIT8 it8 = (LPIT8) hIT8;
|
|
|
|
|
|
fp = fopen(cFileName, "wt");
|
|
|
if (!fp) return false;
|
|
|
WriteHeader(it8, fp);
|
|
|
WriteDataFormat(fp, it8);
|
|
|
WriteData(fp, it8);
|
|
|
fclose(fp);
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
|
|
|
/* Reads whole file in a memory block */
|
|
|
static
|
|
|
BOOL ReadFileInMemory(const char *cFileName, char **Buffer, size_t *Len)
|
|
|
{
|
|
|
FILE *fp;
|
|
|
size_t Size;
|
|
|
char *Ptr;
|
|
|
|
|
|
fp = fopen(cFileName, "rt");
|
|
|
if (!fp) return false;
|
|
|
|
|
|
Size = xfilelength(fileno(fp));
|
|
|
if (Size <= 0) {
|
|
|
fclose(fp);
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
Ptr = (char*)malloc(Size+1); // C->C++ : cast
|
|
|
|
|
|
Size = fread(Ptr, 1, Size, fp);
|
|
|
fclose(fp);
|
|
|
Ptr[Size] = '\0';
|
|
|
|
|
|
*Buffer = Ptr;
|
|
|
*Len = Size;
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------- Higer lever parsing */
|
|
|
|
|
|
static
|
|
|
BOOL DataFormatSection(LPIT8 it8)
|
|
|
{
|
|
|
int iField = 0;
|
|
|
BOOL Ignoring = false;
|
|
|
|
|
|
InSymbol(it8); /* Eats "BEGIN_DATA_FORMAT" */
|
|
|
CheckEOLN(it8);
|
|
|
|
|
|
while (it8->sy != SEND_DATA_FORMAT &&
|
|
|
it8->sy != SEOLN &&
|
|
|
it8->sy != SEOF &&
|
|
|
it8->sy != SSYNERROR)
|
|
|
{
|
|
|
|
|
|
if (it8->sy != SIDENT) {
|
|
|
|
|
|
cmsSignalError(LCMS_ERRC_ABORTED, "Sample type expected");
|
|
|
it8->sy = SSYNERROR;
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
if (!Ignoring && iField > it8->nSamples) {
|
|
|
cmsSignalError(LCMS_ERRC_WARNING, "More than NUMBER_OF_FIELDS fields. Extra is ignored\n");
|
|
|
Ignoring = true;
|
|
|
}
|
|
|
else {
|
|
|
if (!SetDataFormat(it8, iField, it8->id)) return false;
|
|
|
iField++;
|
|
|
}
|
|
|
|
|
|
InSymbol(it8);
|
|
|
Skip(it8, SEOLN);
|
|
|
}
|
|
|
|
|
|
Skip(it8, SEOLN);
|
|
|
Skip(it8, SEND_DATA_FORMAT);
|
|
|
Skip(it8, SEOLN);
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static
|
|
|
BOOL DataSection (LPIT8 it8)
|
|
|
{
|
|
|
int iField = 0;
|
|
|
int iSet = 0;
|
|
|
char Buffer[256];
|
|
|
|
|
|
InSymbol(it8); /* Eats "BEGIN_DATA" */
|
|
|
CheckEOLN(it8);
|
|
|
|
|
|
while (it8->sy != SEND_DATA && it8->sy != SEOF)
|
|
|
{
|
|
|
if (iField >= it8 -> nSamples) {
|
|
|
iField = 0;
|
|
|
iSet++;
|
|
|
if (!CheckEOLN(it8))
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
if (it8->sy != SEND_DATA && it8->sy != SEOF) {
|
|
|
|
|
|
if (!GetVal(it8, Buffer))
|
|
|
return false;
|
|
|
|
|
|
if (!SetData(it8, iSet, iField, Buffer))
|
|
|
return false;
|
|
|
|
|
|
iField++;
|
|
|
|
|
|
Skip(it8, SEOLN);
|
|
|
InSymbol(it8);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
Skip(it8, SEOLN);
|
|
|
Skip(it8, SEND_DATA);
|
|
|
Skip(it8, SEOLN);
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static
|
|
|
BOOL HeaderSection (LPIT8 it8)
|
|
|
{
|
|
|
char VarName[MAXID];
|
|
|
char Buffer[MAXSTR];
|
|
|
|
|
|
while (it8->sy != SEOF &&
|
|
|
it8->sy != SSYNERROR &&
|
|
|
it8->sy != SBEGIN_DATA_FORMAT &&
|
|
|
it8->sy != SBEGIN_DATA) {
|
|
|
|
|
|
|
|
|
switch (it8 -> sy) {
|
|
|
|
|
|
case SKEYWORD:
|
|
|
InSymbol(it8);
|
|
|
if (!Check(it8, SSTRING, "Keyword expected")) return false;
|
|
|
if (!AddAvailableProperty(it8, it8 -> str)) return false;
|
|
|
InSymbol(it8);
|
|
|
break;
|
|
|
|
|
|
|
|
|
case SIDENT:
|
|
|
strncpy(VarName, it8->id, MAXID-1);
|
|
|
if (!IsAvailableOnList(it8-> ValidKeywords, VarName, NULL))
|
|
|
return SynError(it8, "Undefined keyword '%s'", VarName);
|
|
|
|
|
|
InSymbol(it8);
|
|
|
GetVal(it8, Buffer);
|
|
|
cmsxIT8SetProperty((LCMSHANDLE) it8, VarName, Buffer);
|
|
|
InSymbol(it8);
|
|
|
break;
|
|
|
|
|
|
|
|
|
case SEOLN: break;
|
|
|
|
|
|
default:
|
|
|
return SynError(it8, "expected keyword or identifier");
|
|
|
}
|
|
|
|
|
|
Skip(it8, SEOLN);
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
static
|
|
|
BOOL ParseIT8(LPIT8 it8)
|
|
|
{
|
|
|
|
|
|
InSymbol(it8);
|
|
|
|
|
|
if (it8->sy == SIDENT) {
|
|
|
|
|
|
strncpy(it8->SheetType, it8->id, MAXSTR-1);
|
|
|
InSymbol(it8);
|
|
|
|
|
|
/* if (!AddAvailableProperty(it8, it8 -> id)) return false; */
|
|
|
/* cmsxIT8SetProperty((LCMSHANDLE) it8, it8->id, NULL); */
|
|
|
}
|
|
|
|
|
|
Skip(it8, SEOLN);
|
|
|
|
|
|
while (it8-> sy != SEOF &&
|
|
|
it8-> sy != SSYNERROR) {
|
|
|
|
|
|
switch (it8 -> sy) {
|
|
|
|
|
|
case SBEGIN_DATA_FORMAT:
|
|
|
if (!DataFormatSection(it8)) return false;
|
|
|
break;
|
|
|
|
|
|
case SBEGIN_DATA:
|
|
|
if (!DataSection(it8)) return false;
|
|
|
break;
|
|
|
|
|
|
case SEOLN:
|
|
|
Skip(it8, SEOLN);
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
if (!HeaderSection(it8)) return false;
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
|
|
|
static
|
|
|
void CleanPatchName(char *cell)
|
|
|
{
|
|
|
char cleaned[256], Buffer[256], ident[256];
|
|
|
char *orig = cell, *id;
|
|
|
int n, lOneNum;
|
|
|
|
|
|
|
|
|
id = ident;
|
|
|
while (*cell && isalpha(*cell))
|
|
|
{
|
|
|
*id++ = (char) toupper(*cell);
|
|
|
cell++;
|
|
|
}
|
|
|
*id = 0;
|
|
|
strcpy(cleaned, ident);
|
|
|
|
|
|
|
|
|
n = 0;
|
|
|
lOneNum = false;
|
|
|
while (*cell && isdigit(*cell))
|
|
|
{
|
|
|
n = n * 10 + (*cell -'0');
|
|
|
cell++;
|
|
|
lOneNum = true;
|
|
|
}
|
|
|
|
|
|
if (lOneNum) {
|
|
|
|
|
|
sprintf(Buffer, "%d", n);
|
|
|
strcat(cleaned, Buffer);
|
|
|
}
|
|
|
|
|
|
if (strcmp(cleaned, "GS0") == 0)
|
|
|
strcpy(orig, "DMIN");
|
|
|
else
|
|
|
if (strcmp(cleaned, "GS23") == 0)
|
|
|
strcpy(orig, "DMAX");
|
|
|
else
|
|
|
strcpy(orig, cleaned);
|
|
|
}
|
|
|
|
|
|
|
|
|
/* Init useful pointers */
|
|
|
|
|
|
static
|
|
|
void CookPointers(LPIT8 it8)
|
|
|
{
|
|
|
int idField, i;
|
|
|
char* Fld;
|
|
|
|
|
|
it8 -> SampleID = 0;
|
|
|
for (idField = 0; idField < it8 -> nSamples; idField++)
|
|
|
{
|
|
|
Fld = it8->DataFormat[idField];
|
|
|
if (!Fld) continue;
|
|
|
|
|
|
if (strcmp(Fld, "SAMPLE_ID") == 0) {
|
|
|
it8 -> SampleID = idField;
|
|
|
|
|
|
|
|
|
for (i=0; i < it8 -> nPatches; i++) {
|
|
|
|
|
|
char *Data = GetData(it8, i, idField);
|
|
|
if (Data) {
|
|
|
char Buffer[256];
|
|
|
|
|
|
strncpy(Buffer, Data, 255);
|
|
|
CleanPatchName(Buffer);
|
|
|
|
|
|
if (strlen(Buffer) <= strlen(Data))
|
|
|
strcpy(Data, Buffer);
|
|
|
else
|
|
|
SetData(it8, i, idField, Buffer);
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------- Exported routines */
|
|
|
|
|
|
|
|
|
LCMSHANDLE cmsxIT8LoadFromMem(void *Ptr, size_t len)
|
|
|
{
|
|
|
LCMSHANDLE hIT8 = cmsxIT8Alloc();
|
|
|
LPIT8 it8 = (LPIT8) hIT8;
|
|
|
|
|
|
if (!hIT8) return NULL;
|
|
|
it8 ->FileBuffer = (char*) malloc(len + 1);
|
|
|
|
|
|
strncpy(it8 ->FileBuffer, (const char*)Ptr, len); // C->C++ : cast
|
|
|
strncpy(it8->FileName, "", MAX_PATH-1);
|
|
|
it8-> Source = it8 -> FileBuffer;
|
|
|
|
|
|
ParseIT8(it8);
|
|
|
CookPointers(it8);
|
|
|
|
|
|
free(it8->FileBuffer);
|
|
|
it8 -> FileBuffer = NULL;
|
|
|
|
|
|
return hIT8;
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
LCMSHANDLE cmsxIT8LoadFromFile(const char* cFileName)
|
|
|
{
|
|
|
|
|
|
LCMSHANDLE hIT8 = cmsxIT8Alloc();
|
|
|
LPIT8 it8 = (LPIT8) hIT8;
|
|
|
size_t Len;
|
|
|
|
|
|
if (!hIT8) return NULL;
|
|
|
if (!ReadFileInMemory(cFileName, &it8->FileBuffer, &Len)) return NULL;
|
|
|
|
|
|
strncpy(it8->FileName, cFileName, MAX_PATH-1);
|
|
|
it8-> Source = it8 -> FileBuffer;
|
|
|
|
|
|
ParseIT8(it8);
|
|
|
CookPointers(it8);
|
|
|
|
|
|
free(it8->FileBuffer);
|
|
|
it8 -> FileBuffer = NULL;
|
|
|
|
|
|
return hIT8;
|
|
|
|
|
|
}
|
|
|
|
|
|
int cmsxIT8EnumDataFormat(LCMSHANDLE hIT8, char ***SampleNames)
|
|
|
{
|
|
|
LPIT8 it8 = (LPIT8) hIT8;
|
|
|
|
|
|
*SampleNames = it8 -> DataFormat;
|
|
|
return it8 -> nSamples;
|
|
|
}
|
|
|
|
|
|
|
|
|
int cmsxIT8EnumProperties(LCMSHANDLE hIT8, char ***PropertyNames)
|
|
|
{
|
|
|
LPIT8 it8 = (LPIT8) hIT8;
|
|
|
LPKEYVALUE p;
|
|
|
int n;
|
|
|
char **Props;
|
|
|
|
|
|
/* Pass#1 - count properties */
|
|
|
|
|
|
n = 0;
|
|
|
for (p = it8 -> HeaderList; p != NULL; p = p->Next) {
|
|
|
n++;
|
|
|
}
|
|
|
|
|
|
|
|
|
Props = (char **) malloc(sizeof(char *) * n);
|
|
|
|
|
|
/* Pass#2 - Fill pointers */
|
|
|
n = 0;
|
|
|
for (p = it8 -> HeaderList; p != NULL; p = p->Next) {
|
|
|
Props[n++] = p -> Keyword;
|
|
|
}
|
|
|
|
|
|
*PropertyNames = Props;
|
|
|
return n;
|
|
|
}
|
|
|
|
|
|
static
|
|
|
int LocatePatch(LPIT8 it8, const char* cPatch)
|
|
|
{
|
|
|
int i;
|
|
|
const char *data;
|
|
|
|
|
|
for (i=0; i < it8-> nPatches; i++) {
|
|
|
|
|
|
data = GetData(it8, i, it8->SampleID);
|
|
|
|
|
|
if (data != NULL) {
|
|
|
|
|
|
if (stricmp(data, cPatch) == 0)
|
|
|
return i;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
|
|
|
static
|
|
|
int LocateEmptyPatch(LPIT8 it8, const char* cPatch)
|
|
|
{
|
|
|
int i;
|
|
|
const char *data;
|
|
|
|
|
|
for (i=0; i < it8-> nPatches; i++) {
|
|
|
|
|
|
data = GetData(it8, i, it8->SampleID);
|
|
|
|
|
|
if (data == NULL)
|
|
|
return i;
|
|
|
|
|
|
}
|
|
|
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
static
|
|
|
int LocateSample(LPIT8 it8, const char* cSample)
|
|
|
{
|
|
|
int i;
|
|
|
const char *fld;
|
|
|
|
|
|
for (i=0; i < it8->nSamples; i++) {
|
|
|
|
|
|
fld = GetDataFormat(it8, i);
|
|
|
if (stricmp(fld, cSample) == 0)
|
|
|
return i;
|
|
|
}
|
|
|
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
|
|
|
BOOL cmsxIT8GetDataSetByPos(LCMSHANDLE hIT8, int col, int row, char* Val, int ValBufferLen)
|
|
|
{
|
|
|
LPIT8 it8 = (LPIT8) hIT8;
|
|
|
const char *data = GetData(it8, row, col);
|
|
|
|
|
|
if (!data)
|
|
|
{
|
|
|
*Val = '\0';
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
strncpy(Val, data, ValBufferLen-1);
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
BOOL cmsxIT8GetDataSet(LCMSHANDLE hIT8, const char* cPatch,
|
|
|
const char* cSample,
|
|
|
char* Val, int ValBuffLen)
|
|
|
{
|
|
|
LPIT8 it8 = (LPIT8) hIT8;
|
|
|
int iField, iSet;
|
|
|
|
|
|
|
|
|
iField = LocateSample(it8, cSample);
|
|
|
if (iField < 0) {
|
|
|
/* cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't find data field %s\n", cSample); */
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
|
|
|
iSet = LocatePatch(it8, cPatch);
|
|
|
if (iSet < 0) {
|
|
|
|
|
|
/* cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't find patch '%s'\n", cPatch); */
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
strncpy(Val, GetData(it8, iSet, iField), ValBuffLen-1);
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
|
|
|
BOOL cmsxIT8GetDataSetDbl(LCMSHANDLE it8, const char* cPatch, const char* cSample, double* v)
|
|
|
{
|
|
|
char Buffer[20];
|
|
|
|
|
|
if (cmsxIT8GetDataSet(it8, cPatch, cSample, Buffer, 20)) {
|
|
|
|
|
|
*v = atof(Buffer);
|
|
|
return true;
|
|
|
} else
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
BOOL cmsxIT8SetDataSet(LCMSHANDLE hIT8, const char* cPatch,
|
|
|
const char* cSample,
|
|
|
char *Val)
|
|
|
{
|
|
|
LPIT8 it8 = (LPIT8) hIT8;
|
|
|
int iField, iSet;
|
|
|
|
|
|
|
|
|
iField = LocateSample(it8, cSample);
|
|
|
|
|
|
if (iField < 0) {
|
|
|
|
|
|
cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't find data field %s\n", cSample);
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
|
|
|
if (it8-> nPatches == 0) {
|
|
|
|
|
|
AllocateDataFormat(it8);
|
|
|
AllocateDataSet(it8);
|
|
|
CookPointers(it8);
|
|
|
}
|
|
|
|
|
|
|
|
|
if (stricmp(cSample, "SAMPLE_ID") == 0)
|
|
|
{
|
|
|
|
|
|
iSet = LocateEmptyPatch(it8, cPatch);
|
|
|
if (iSet < 0) {
|
|
|
cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't add more patches '%s'\n", cPatch);
|
|
|
return false;
|
|
|
}
|
|
|
iField = it8 -> SampleID;
|
|
|
}
|
|
|
else {
|
|
|
iSet = LocatePatch(it8, cPatch);
|
|
|
if (iSet < 0) {
|
|
|
|
|
|
cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't find patch '%s'\n", cPatch);
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return SetData(it8, iSet, iField, Val);
|
|
|
}
|
|
|
|
|
|
|
|
|
BOOL cmsxIT8SetDataSetDbl(LCMSHANDLE hIT8, const char* cPatch,
|
|
|
const char* cSample,
|
|
|
double Val)
|
|
|
{
|
|
|
char Buff[256];
|
|
|
|
|
|
sprintf(Buff, "%g", Val);
|
|
|
return cmsxIT8SetDataSet(hIT8, cPatch, cSample, Buff);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
const char* cmsxIT8GetPatchName(LCMSHANDLE hIT8, int nPatch, char* buffer)
|
|
|
{
|
|
|
LPIT8 it8 = (LPIT8) hIT8;
|
|
|
char* Data = GetData(it8, nPatch, it8->SampleID);
|
|
|
if (!Data) return NULL;
|
|
|
|
|
|
strcpy(buffer, Data);
|
|
|
return buffer;
|
|
|
}
|
|
|
|
|
|
|
|
|
const char* cmsxIT8GenericPatchName(int nPatch, char* buffer)
|
|
|
{
|
|
|
int row, col;
|
|
|
|
|
|
if (nPatch >= cmsxIT8_NORMAL_PATCHES)
|
|
|
return "$CUSTOM";
|
|
|
|
|
|
if (nPatch >= (cmsxIT8_ROWS * cmsxIT8_COLS)) {
|
|
|
|
|
|
nPatch -= cmsxIT8_ROWS * cmsxIT8_COLS;
|
|
|
if (nPatch == 0)
|
|
|
return "DMIN";
|
|
|
else
|
|
|
if (nPatch == cmsxIT8_GRAYCOLS - 1)
|
|
|
return "DMAX";
|
|
|
else
|
|
|
sprintf(buffer, "GS%d", nPatch);
|
|
|
return buffer;
|
|
|
}
|
|
|
|
|
|
|
|
|
row = nPatch / cmsxIT8_COLS;
|
|
|
col = nPatch % cmsxIT8_COLS;
|
|
|
|
|
|
sprintf (buffer, "%c%d", 'A'+row, col+1);
|
|
|
return buffer;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int cmsxIT8GenericPatchNum(const char *name)
|
|
|
{
|
|
|
int i;
|
|
|
char Buff[256];
|
|
|
|
|
|
|
|
|
for (i=0; i < cmsxIT8_TOTAL_PATCHES; i++)
|
|
|
if (stricmp(cmsxIT8GenericPatchName(i, Buff), name) == 0)
|
|
|
return i;
|
|
|
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|