Path: blob/master/src/java.desktop/share/native/liblcms/cmscgats.c
41152 views
/*1* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.2*3* This code is free software; you can redistribute it and/or modify it4* under the terms of the GNU General Public License version 2 only, as5* published by the Free Software Foundation. Oracle designates this6* particular file as subject to the "Classpath" exception as provided7* by Oracle in the LICENSE file that accompanied this code.8*9* This code is distributed in the hope that it will be useful, but WITHOUT10* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or11* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License12* version 2 for more details (a copy is included in the LICENSE file that13* accompanied this code).14*15* You should have received a copy of the GNU General Public License version16* 2 along with this work; if not, write to the Free Software Foundation,17* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.18*19* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA20* or visit www.oracle.com if you need additional information or have any21* questions.22*/2324// This file is available under and governed by the GNU General Public25// License version 2 only, as published by the Free Software Foundation.26// However, the following notice accompanied the original version of this27// file:28//29//---------------------------------------------------------------------------------30//31// Little Color Management System32// Copyright (c) 1998-2020 Marti Maria Saguer33//34// Permission is hereby granted, free of charge, to any person obtaining35// a copy of this software and associated documentation files (the "Software"),36// to deal in the Software without restriction, including without limitation37// the rights to use, copy, modify, merge, publish, distribute, sublicense,38// and/or sell copies of the Software, and to permit persons to whom the Software39// is furnished to do so, subject to the following conditions:40//41// The above copyright notice and this permission notice shall be included in42// all copies or substantial portions of the Software.43//44// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,45// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO46// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND47// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE48// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION49// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION50// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.51//52//---------------------------------------------------------------------------------53//5455#include "lcms2_internal.h"565758// IT8.7 / CGATS.17-200x handling -----------------------------------------------------------------------------596061#define MAXID 128 // Max length of identifier62#define MAXSTR 1024 // Max length of string63#define MAXTABLES 255 // Max Number of tables in a single stream64#define MAXINCLUDE 20 // Max number of nested includes6566#define DEFAULT_DBL_FORMAT "%.10g" // Double formatting6768#ifdef CMS_IS_WINDOWS_69# include <io.h>70# define DIR_CHAR '\\'71#else72# define DIR_CHAR '/'73#endif747576// Symbols77typedef enum {7879SUNDEFINED,80SINUM, // Integer81SDNUM, // Real82SIDENT, // Identifier83SSTRING, // string84SCOMMENT, // comment85SEOLN, // End of line86SEOF, // End of stream87SSYNERROR, // Syntax error found on stream8889// Keywords9091SBEGIN_DATA,92SBEGIN_DATA_FORMAT,93SEND_DATA,94SEND_DATA_FORMAT,95SKEYWORD,96SDATA_FORMAT_ID,97SINCLUDE9899} SYMBOL;100101102// How to write the value103typedef enum {104105WRITE_UNCOOKED,106WRITE_STRINGIFY,107WRITE_HEXADECIMAL,108WRITE_BINARY,109WRITE_PAIR110111} WRITEMODE;112113// Linked list of variable names114typedef struct _KeyVal {115116struct _KeyVal* Next;117char* Keyword; // Name of variable118struct _KeyVal* NextSubkey; // If key is a dictionary, points to the next item119char* Subkey; // If key is a dictionary, points to the subkey name120char* Value; // Points to value121WRITEMODE WriteAs; // How to write the value122123} KEYVALUE;124125126// Linked list of memory chunks (Memory sink)127typedef struct _OwnedMem {128129struct _OwnedMem* Next;130void * Ptr; // Point to value131132} OWNEDMEM;133134// Suballocator135typedef struct _SubAllocator {136137cmsUInt8Number* Block;138cmsUInt32Number BlockSize;139cmsUInt32Number Used;140141} SUBALLOCATOR;142143// Table. Each individual table can hold properties and rows & cols144typedef struct _Table {145146char SheetType[MAXSTR]; // The first row of the IT8 (the type)147148int nSamples, nPatches; // Cols, Rows149int SampleID; // Pos of ID150151KEYVALUE* HeaderList; // The properties152153char** DataFormat; // The binary stream descriptor154char** Data; // The binary stream155156} TABLE;157158// File stream being parsed159typedef struct _FileContext {160char FileName[cmsMAX_PATH]; // File name if being read from file161FILE* Stream; // File stream or NULL if holded in memory162} FILECTX;163164// This struct hold all information about an open IT8 handler.165typedef struct {166167168cmsUInt32Number TablesCount; // How many tables in this stream169cmsUInt32Number nTable; // The actual table170171TABLE Tab[MAXTABLES];172173// Memory management174OWNEDMEM* MemorySink; // The storage backend175SUBALLOCATOR Allocator; // String suballocator -- just to keep it fast176177// Parser state machine178SYMBOL sy; // Current symbol179int ch; // Current character180181cmsInt32Number inum; // integer value182cmsFloat64Number dnum; // real value183184char id[MAXID]; // identifier185char str[MAXSTR]; // string186187// Allowed keywords & datasets. They have visibility on whole stream188KEYVALUE* ValidKeywords;189KEYVALUE* ValidSampleID;190191char* Source; // Points to loc. being parsed192cmsInt32Number lineno; // line counter for error reporting193194FILECTX* FileStack[MAXINCLUDE]; // Stack of files being parsed195cmsInt32Number IncludeSP; // Include Stack Pointer196197char* MemoryBlock; // The stream if holded in memory198199char DoubleFormatter[MAXID];// Printf-like 'cmsFloat64Number' formatter200201cmsContext ContextID; // The threading context202203} cmsIT8;204205206// The stream for save operations207typedef struct {208209FILE* stream; // For save-to-file behaviour210211cmsUInt8Number* Base;212cmsUInt8Number* Ptr; // For save-to-mem behaviour213cmsUInt32Number Used;214cmsUInt32Number Max;215216} SAVESTREAM;217218219// ------------------------------------------------------ cmsIT8 parsing routines220221222// A keyword223typedef struct {224225const char *id;226SYMBOL sy;227228} KEYWORD;229230// The keyword->symbol translation table. Sorting is required.231static const KEYWORD TabKeys[] = {232233{"$INCLUDE", SINCLUDE}, // This is an extension!234{".INCLUDE", SINCLUDE}, // This is an extension!235236{"BEGIN_DATA", SBEGIN_DATA },237{"BEGIN_DATA_FORMAT", SBEGIN_DATA_FORMAT },238{"DATA_FORMAT_IDENTIFIER", SDATA_FORMAT_ID},239{"END_DATA", SEND_DATA},240{"END_DATA_FORMAT", SEND_DATA_FORMAT},241{"KEYWORD", SKEYWORD}242};243244#define NUMKEYS (sizeof(TabKeys)/sizeof(KEYWORD))245246// Predefined properties247248// A property249typedef struct {250const char *id; // The identifier251WRITEMODE as; // How is supposed to be written252} PROPERTY;253254static PROPERTY PredefinedProperties[] = {255256{"NUMBER_OF_FIELDS", WRITE_UNCOOKED}, // Required - NUMBER OF FIELDS257{"NUMBER_OF_SETS", WRITE_UNCOOKED}, // Required - NUMBER OF SETS258{"ORIGINATOR", WRITE_STRINGIFY}, // Required - Identifies the specific system, organization or individual that created the data file.259{"FILE_DESCRIPTOR", WRITE_STRINGIFY}, // Required - Describes the purpose or contents of the data file.260{"CREATED", WRITE_STRINGIFY}, // Required - Indicates date of creation of the data file.261{"DESCRIPTOR", WRITE_STRINGIFY}, // Required - Describes the purpose or contents of the data file.262{"DIFFUSE_GEOMETRY", WRITE_STRINGIFY}, // The diffuse geometry used. Allowed values are "sphere" or "opal".263{"MANUFACTURER", WRITE_STRINGIFY},264{"MANUFACTURE", WRITE_STRINGIFY}, // Some broken Fuji targets does store this value265{"PROD_DATE", WRITE_STRINGIFY}, // Identifies year and month of production of the target in the form yyyy:mm.266{"SERIAL", WRITE_STRINGIFY}, // Uniquely identifies individual physical target.267268{"MATERIAL", WRITE_STRINGIFY}, // Identifies the material on which the target was produced using a code269// uniquely identifying th e material. This is intend ed to be used for IT8.7270// physical targets only (i.e . IT8.7/1 a nd IT8.7/2).271272{"INSTRUMENTATION", WRITE_STRINGIFY}, // Used to report the specific instrumentation used (manufacturer and273// model number) to generate the data reported. This data will often274// provide more information about the particular data collected than an275// extensive list of specific details. This is particularly important for276// spectral data or data derived from spectrophotometry.277278{"MEASUREMENT_SOURCE", WRITE_STRINGIFY}, // Illumination used for spectral measurements. This data helps provide279// a guide to the potential for issues of paper fluorescence, etc.280281{"PRINT_CONDITIONS", WRITE_STRINGIFY}, // Used to define the characteristics of the printed sheet being reported.282// Where standard conditions have been defined (e.g., SWOP at nominal)283// named conditions may suffice. Otherwise, detailed information is284// needed.285286{"SAMPLE_BACKING", WRITE_STRINGIFY}, // Identifies the backing material used behind the sample during287// measurement. Allowed values are "black", "white", or {"na".288289{"CHISQ_DOF", WRITE_STRINGIFY}, // Degrees of freedom associated with the Chi squared statistic290// below properties are new in recent specs:291292{"MEASUREMENT_GEOMETRY", WRITE_STRINGIFY}, // The type of measurement, either reflection or transmission, should be indicated293// along with details of the geometry and the aperture size and shape. For example,294// for transmission measurements it is important to identify 0/diffuse, diffuse/0,295// opal or integrating sphere, etc. For reflection it is important to identify 0/45,296// 45/0, sphere (specular included or excluded), etc.297298{"FILTER", WRITE_STRINGIFY}, // Identifies the use of physical filter(s) during measurement. Typically used to299// denote the use of filters such as none, D65, Red, Green or Blue.300301{"POLARIZATION", WRITE_STRINGIFY}, // Identifies the use of a physical polarization filter during measurement. Allowed302// values are {"yes", "white", "none" or "na".303304{"WEIGHTING_FUNCTION", WRITE_PAIR}, // Indicates such functions as: the CIE standard observer functions used in the305// calculation of various data parameters (2 degree and 10 degree), CIE standard306// illuminant functions used in the calculation of various data parameters (e.g., D50,307// D65, etc.), density status response, etc. If used there shall be at least one308// name-value pair following the WEIGHTING_FUNCTION tag/keyword. The first attribute309// in the set shall be {"name" and shall identify the particular parameter used.310// The second shall be {"value" and shall provide the value associated with that name.311// For ASCII data, a string containing the Name and Value attribute pairs shall follow312// the weighting function keyword. A semi-colon separates attribute pairs from each313// other and within the attribute the name and value are separated by a comma.314315{"COMPUTATIONAL_PARAMETER", WRITE_PAIR}, // Parameter that is used in computing a value from measured data. Name is the name316// of the calculation, parameter is the name of the parameter used in the calculation317// and value is the value of the parameter.318319{"TARGET_TYPE", WRITE_STRINGIFY}, // The type of target being measured, e.g. IT8.7/1, IT8.7/3, user defined, etc.320321{"COLORANT", WRITE_STRINGIFY}, // Identifies the colorant(s) used in creating the target.322323{"TABLE_DESCRIPTOR", WRITE_STRINGIFY}, // Describes the purpose or contents of a data table.324325{"TABLE_NAME", WRITE_STRINGIFY} // Provides a short name for a data table.326};327328#define NUMPREDEFINEDPROPS (sizeof(PredefinedProperties)/sizeof(PROPERTY))329330331// Predefined sample types on dataset332static const char* PredefinedSampleID[] = {333"SAMPLE_ID", // Identifies sample that data represents334"STRING", // Identifies label, or other non-machine readable value.335// Value must begin and end with a " symbol336337"CMYK_C", // Cyan component of CMYK data expressed as a percentage338"CMYK_M", // Magenta component of CMYK data expressed as a percentage339"CMYK_Y", // Yellow component of CMYK data expressed as a percentage340"CMYK_K", // Black component of CMYK data expressed as a percentage341"D_RED", // Red filter density342"D_GREEN", // Green filter density343"D_BLUE", // Blue filter density344"D_VIS", // Visual filter density345"D_MAJOR_FILTER", // Major filter d ensity346"RGB_R", // Red component of RGB data347"RGB_G", // Green component of RGB data348"RGB_B", // Blue com ponent of RGB data349"SPECTRAL_NM", // Wavelength of measurement expressed in nanometers350"SPECTRAL_PCT", // Percentage reflectance/transmittance351"SPECTRAL_DEC", // Reflectance/transmittance352"XYZ_X", // X component of tristimulus data353"XYZ_Y", // Y component of tristimulus data354"XYZ_Z", // Z component of tristimulus data355"XYY_X", // x component of chromaticity data356"XYY_Y", // y component of chromaticity data357"XYY_CAPY", // Y component of tristimulus data358"LAB_L", // L* component of Lab data359"LAB_A", // a* component of Lab data360"LAB_B", // b* component of Lab data361"LAB_C", // C*ab component of Lab data362"LAB_H", // hab component of Lab data363"LAB_DE", // CIE dE364"LAB_DE_94", // CIE dE using CIE 94365"LAB_DE_CMC", // dE using CMC366"LAB_DE_2000", // CIE dE using CIE DE 2000367"MEAN_DE", // Mean Delta E (LAB_DE) of samples compared to batch average368// (Used for data files for ANSI IT8.7/1 and IT8.7/2 targets)369"STDEV_X", // Standard deviation of X (tristimulus data)370"STDEV_Y", // Standard deviation of Y (tristimulus data)371"STDEV_Z", // Standard deviation of Z (tristimulus data)372"STDEV_L", // Standard deviation of L*373"STDEV_A", // Standard deviation of a*374"STDEV_B", // Standard deviation of b*375"STDEV_DE", // Standard deviation of CIE dE376"CHI_SQD_PAR"}; // The average of the standard deviations of L*, a* and b*. It is377// used to derive an estimate of the chi-squared parameter which is378// recommended as the predictor of the variability of dE379380#define NUMPREDEFINEDSAMPLEID (sizeof(PredefinedSampleID)/sizeof(char *))381382//Forward declaration of some internal functions383static void* AllocChunk(cmsIT8* it8, cmsUInt32Number size);384385// Checks whatever c is a separator386static387cmsBool isseparator(int c)388{389return (c == ' ') || (c == '\t') ;390}391392// Checks whatever c is a valid identifier char393static394cmsBool ismiddle(int c)395{396return (!isseparator(c) && (c != '#') && (c !='\"') && (c != '\'') && (c > 32) && (c < 127));397}398399// Checks whatsever c is a valid identifier middle char.400static401cmsBool isidchar(int c)402{403return isalnum(c) || ismiddle(c);404}405406// Checks whatsever c is a valid identifier first char.407static408cmsBool isfirstidchar(int c)409{410return !isdigit(c) && ismiddle(c);411}412413// Guess whether the supplied path looks like an absolute path414static415cmsBool isabsolutepath(const char *path)416{417char ThreeChars[4];418419if(path == NULL)420return FALSE;421if (path[0] == 0)422return FALSE;423424strncpy(ThreeChars, path, 3);425ThreeChars[3] = 0;426427if(ThreeChars[0] == DIR_CHAR)428return TRUE;429430#ifdef CMS_IS_WINDOWS_431if (isalpha((int) ThreeChars[0]) && ThreeChars[1] == ':')432return TRUE;433#endif434return FALSE;435}436437438// Makes a file path based on a given reference path439// NOTE: this function doesn't check if the path exists or even if it's legal440static441cmsBool BuildAbsolutePath(const char *relPath, const char *basePath, char *buffer, cmsUInt32Number MaxLen)442{443char *tail;444cmsUInt32Number len;445446// Already absolute?447if (isabsolutepath(relPath)) {448449strncpy(buffer, relPath, MaxLen);450buffer[MaxLen-1] = 0;451return TRUE;452}453454// No, search for last455strncpy(buffer, basePath, MaxLen);456buffer[MaxLen-1] = 0;457458tail = strrchr(buffer, DIR_CHAR);459if (tail == NULL) return FALSE; // Is not absolute and has no separators??460461len = (cmsUInt32Number) (tail - buffer);462if (len >= MaxLen) return FALSE;463464// No need to assure zero terminator over here465strncpy(tail + 1, relPath, MaxLen - len);466467return TRUE;468}469470471// Make sure no exploit is being even tried472static473const char* NoMeta(const char* str)474{475if (strchr(str, '%') != NULL)476return "**** CORRUPTED FORMAT STRING ***";477478return str;479}480481// Syntax error482static483cmsBool SynError(cmsIT8* it8, const char *Txt, ...)484{485char Buffer[256], ErrMsg[1024];486va_list args;487488va_start(args, Txt);489vsnprintf(Buffer, 255, Txt, args);490Buffer[255] = 0;491va_end(args);492493snprintf(ErrMsg, 1023, "%s: Line %d, %s", it8->FileStack[it8 ->IncludeSP]->FileName, it8->lineno, Buffer);494ErrMsg[1023] = 0;495it8->sy = SSYNERROR;496cmsSignalError(it8 ->ContextID, cmsERROR_CORRUPTION_DETECTED, "%s", ErrMsg);497return FALSE;498}499500// Check if current symbol is same as specified. issue an error else.501static502cmsBool Check(cmsIT8* it8, SYMBOL sy, const char* Err)503{504if (it8 -> sy != sy)505return SynError(it8, NoMeta(Err));506return TRUE;507}508509// Read Next character from stream510static511void NextCh(cmsIT8* it8)512{513if (it8 -> FileStack[it8 ->IncludeSP]->Stream) {514515it8 ->ch = fgetc(it8 ->FileStack[it8 ->IncludeSP]->Stream);516517if (feof(it8 -> FileStack[it8 ->IncludeSP]->Stream)) {518519if (it8 ->IncludeSP > 0) {520521fclose(it8 ->FileStack[it8->IncludeSP--]->Stream);522it8 -> ch = ' '; // Whitespace to be ignored523524} else525it8 ->ch = 0; // EOF526}527}528else {529it8->ch = *it8->Source;530if (it8->ch) it8->Source++;531}532}533534535// Try to see if current identifier is a keyword, if so return the referred symbol536static537SYMBOL BinSrchKey(const char *id)538{539int l = 1;540int r = NUMKEYS;541int x, res;542543while (r >= l)544{545x = (l+r)/2;546res = cmsstrcasecmp(id, TabKeys[x-1].id);547if (res == 0) return TabKeys[x-1].sy;548if (res < 0) r = x - 1;549else l = x + 1;550}551552return SUNDEFINED;553}554555556// 10 ^n557static558cmsFloat64Number xpow10(int n)559{560return pow(10, (cmsFloat64Number) n);561}562563564// Reads a Real number, tries to follow from integer number565static566void ReadReal(cmsIT8* it8, cmsInt32Number inum)567{568it8->dnum = (cmsFloat64Number)inum;569570while (isdigit(it8->ch)) {571572it8->dnum = (cmsFloat64Number)it8->dnum * 10.0 + (cmsFloat64Number)(it8->ch - '0');573NextCh(it8);574}575576if (it8->ch == '.') { // Decimal point577578cmsFloat64Number frac = 0.0; // fraction579int prec = 0; // precision580581NextCh(it8); // Eats dec. point582583while (isdigit(it8->ch)) {584585frac = frac * 10.0 + (cmsFloat64Number)(it8->ch - '0');586prec++;587NextCh(it8);588}589590it8->dnum = it8->dnum + (frac / xpow10(prec));591}592593// Exponent, example 34.00E+20594if (toupper(it8->ch) == 'E') {595596cmsInt32Number e;597cmsInt32Number sgn;598599NextCh(it8); sgn = 1;600601if (it8->ch == '-') {602603sgn = -1; NextCh(it8);604}605else606if (it8->ch == '+') {607608sgn = +1;609NextCh(it8);610}611612e = 0;613while (isdigit(it8->ch)) {614615cmsInt32Number digit = (it8->ch - '0');616617if ((cmsFloat64Number)e * 10.0 + (cmsFloat64Number)digit < (cmsFloat64Number)+2147483647.0)618e = e * 10 + digit;619620NextCh(it8);621}622623e = sgn*e;624it8->dnum = it8->dnum * xpow10(e);625}626}627628// Parses a float number629// This can not call directly atof because it uses locale dependent630// parsing, while CCMX files always use . as decimal separator631static632cmsFloat64Number ParseFloatNumber(const char *Buffer)633{634cmsFloat64Number dnum = 0.0;635int sign = 1;636637// keep safe638if (Buffer == NULL) return 0.0;639640if (*Buffer == '-' || *Buffer == '+') {641642sign = (*Buffer == '-') ? -1 : 1;643Buffer++;644}645646647while (*Buffer && isdigit((int)*Buffer)) {648649dnum = dnum * 10.0 + (*Buffer - '0');650if (*Buffer) Buffer++;651}652653if (*Buffer == '.') {654655cmsFloat64Number frac = 0.0; // fraction656int prec = 0; // precision657658if (*Buffer) Buffer++;659660while (*Buffer && isdigit((int)*Buffer)) {661662frac = frac * 10.0 + (*Buffer - '0');663prec++;664if (*Buffer) Buffer++;665}666667dnum = dnum + (frac / xpow10(prec));668}669670// Exponent, example 34.00E+20671if (*Buffer && toupper(*Buffer) == 'E') {672673int e;674int sgn;675676if (*Buffer) Buffer++;677sgn = 1;678679if (*Buffer == '-') {680681sgn = -1;682if (*Buffer) Buffer++;683}684else685if (*Buffer == '+') {686687sgn = +1;688if (*Buffer) Buffer++;689}690691e = 0;692while (*Buffer && isdigit((int)*Buffer)) {693694cmsInt32Number digit = (*Buffer - '0');695696if ((cmsFloat64Number)e * 10.0 + digit < (cmsFloat64Number)+2147483647.0)697e = e * 10 + digit;698699if (*Buffer) Buffer++;700}701702e = sgn*e;703dnum = dnum * xpow10(e);704}705706return sign * dnum;707}708709710// Reads next symbol711static712void InSymbol(cmsIT8* it8)713{714CMSREGISTER char *idptr;715CMSREGISTER int k;716SYMBOL key;717int sng;718719do {720721while (isseparator(it8->ch))722NextCh(it8);723724if (isfirstidchar(it8->ch)) { // Identifier725726k = 0;727idptr = it8->id;728729do {730731if (++k < MAXID) *idptr++ = (char) it8->ch;732733NextCh(it8);734735} while (isidchar(it8->ch));736737*idptr = '\0';738739740key = BinSrchKey(it8->id);741if (key == SUNDEFINED) it8->sy = SIDENT;742else it8->sy = key;743744}745else // Is a number?746if (isdigit(it8->ch) || it8->ch == '.' || it8->ch == '-' || it8->ch == '+')747{748int sign = 1;749750if (it8->ch == '-') {751sign = -1;752NextCh(it8);753}754755it8->inum = 0;756it8->sy = SINUM;757758if (it8->ch == '0') { // 0xnnnn (Hexa) or 0bnnnn (Binary)759760NextCh(it8);761if (toupper(it8->ch) == 'X') {762763int j;764765NextCh(it8);766while (isxdigit(it8->ch))767{768it8->ch = toupper(it8->ch);769if (it8->ch >= 'A' && it8->ch <= 'F') j = it8->ch -'A'+10;770else j = it8->ch - '0';771772if ((cmsFloat64Number) it8->inum * 16.0 + (cmsFloat64Number) j > (cmsFloat64Number)+2147483647.0)773{774SynError(it8, "Invalid hexadecimal number");775return;776}777778it8->inum = it8->inum * 16 + j;779NextCh(it8);780}781return;782}783784if (toupper(it8->ch) == 'B') { // Binary785786int j;787788NextCh(it8);789while (it8->ch == '0' || it8->ch == '1')790{791j = it8->ch - '0';792793if ((cmsFloat64Number) it8->inum * 2.0 + j > (cmsFloat64Number)+2147483647.0)794{795SynError(it8, "Invalid binary number");796return;797}798799it8->inum = it8->inum * 2 + j;800NextCh(it8);801}802return;803}804}805806807while (isdigit(it8->ch)) {808809cmsInt32Number digit = (it8->ch - '0');810811if ((cmsFloat64Number) it8->inum * 10.0 + (cmsFloat64Number) digit > (cmsFloat64Number) +2147483647.0) {812ReadReal(it8, it8->inum);813it8->sy = SDNUM;814it8->dnum *= sign;815return;816}817818it8->inum = it8->inum * 10 + digit;819NextCh(it8);820}821822if (it8->ch == '.') {823824ReadReal(it8, it8->inum);825it8->sy = SDNUM;826it8->dnum *= sign;827return;828}829830it8 -> inum *= sign;831832// Special case. Numbers followed by letters are taken as identifiers833834if (isidchar(it8 ->ch)) {835836if (it8 ->sy == SINUM) {837838snprintf(it8->id, 127, "%d", it8->inum);839}840else {841842snprintf(it8->id, 127, it8 ->DoubleFormatter, it8->dnum);843}844845k = (int) strlen(it8 ->id);846idptr = it8 ->id + k;847do {848849if (++k < MAXID) *idptr++ = (char) it8->ch;850851NextCh(it8);852853} while (isidchar(it8->ch));854855*idptr = '\0';856it8->sy = SIDENT;857}858return;859860}861else862switch ((int) it8->ch) {863864// EOF marker -- ignore it865case '\x1a':866NextCh(it8);867break;868869// Eof stream markers870case 0:871case -1:872it8->sy = SEOF;873break;874875876// Next line877case '\r':878NextCh(it8);879if (it8 ->ch == '\n')880NextCh(it8);881it8->sy = SEOLN;882it8->lineno++;883break;884885case '\n':886NextCh(it8);887it8->sy = SEOLN;888it8->lineno++;889break;890891// Comment892case '#':893NextCh(it8);894while (it8->ch && it8->ch != '\n' && it8->ch != '\r')895NextCh(it8);896897it8->sy = SCOMMENT;898break;899900// String.901case '\'':902case '\"':903idptr = it8->str;904sng = it8->ch;905k = 0;906NextCh(it8);907908while (k < (MAXSTR-1) && it8->ch != sng) {909910if (it8->ch == '\n'|| it8->ch == '\r') k = MAXSTR+1;911else {912*idptr++ = (char) it8->ch;913NextCh(it8);914k++;915}916}917918it8->sy = SSTRING;919*idptr = '\0';920NextCh(it8);921break;922923924default:925SynError(it8, "Unrecognized character: 0x%x", it8 ->ch);926return;927}928929} while (it8->sy == SCOMMENT);930931// Handle the include special token932933if (it8 -> sy == SINCLUDE) {934935FILECTX* FileNest;936937if(it8 -> IncludeSP >= (MAXINCLUDE-1)) {938939SynError(it8, "Too many recursion levels");940return;941}942943InSymbol(it8);944if (!Check(it8, SSTRING, "Filename expected")) return;945946FileNest = it8 -> FileStack[it8 -> IncludeSP + 1];947if(FileNest == NULL) {948949FileNest = it8 ->FileStack[it8 -> IncludeSP + 1] = (FILECTX*)AllocChunk(it8, sizeof(FILECTX));950//if(FileNest == NULL)951// TODO: how to manage out-of-memory conditions?952}953954if (BuildAbsolutePath(it8->str,955it8->FileStack[it8->IncludeSP]->FileName,956FileNest->FileName, cmsMAX_PATH-1) == FALSE) {957SynError(it8, "File path too long");958return;959}960961FileNest->Stream = fopen(FileNest->FileName, "rt");962if (FileNest->Stream == NULL) {963964SynError(it8, "File %s not found", FileNest->FileName);965return;966}967it8->IncludeSP++;968969it8 ->ch = ' ';970InSymbol(it8);971}972973}974975// Checks end of line separator976static977cmsBool CheckEOLN(cmsIT8* it8)978{979if (!Check(it8, SEOLN, "Expected separator")) return FALSE;980while (it8 -> sy == SEOLN)981InSymbol(it8);982return TRUE;983984}985986// Skip a symbol987988static989void Skip(cmsIT8* it8, SYMBOL sy)990{991if (it8->sy == sy && it8->sy != SEOF)992InSymbol(it8);993}994995996// Skip multiple EOLN997static998void SkipEOLN(cmsIT8* it8)999{1000while (it8->sy == SEOLN) {1001InSymbol(it8);1002}1003}100410051006// Returns a string holding current value1007static1008cmsBool GetVal(cmsIT8* it8, char* Buffer, cmsUInt32Number max, const char* ErrorTitle)1009{1010switch (it8->sy) {10111012case SEOLN: // Empty value1013Buffer[0]=0;1014break;1015case SIDENT: strncpy(Buffer, it8->id, max);1016Buffer[max-1]=0;1017break;1018case SINUM: snprintf(Buffer, max, "%d", it8 -> inum); break;1019case SDNUM: snprintf(Buffer, max, it8->DoubleFormatter, it8 -> dnum); break;1020case SSTRING: strncpy(Buffer, it8->str, max);1021Buffer[max-1] = 0;1022break;102310241025default:1026return SynError(it8, "%s", ErrorTitle);1027}10281029Buffer[max] = 0;1030return TRUE;1031}10321033// ---------------------------------------------------------- Table10341035static1036TABLE* GetTable(cmsIT8* it8)1037{1038if ((it8 -> nTable >= it8 ->TablesCount)) {10391040SynError(it8, "Table %d out of sequence", it8 -> nTable);1041return it8 -> Tab;1042}10431044return it8 ->Tab + it8 ->nTable;1045}10461047// ---------------------------------------------------------- Memory management104810491050// Frees an allocator and owned memory1051void CMSEXPORT cmsIT8Free(cmsHANDLE hIT8)1052{1053cmsIT8* it8 = (cmsIT8*) hIT8;10541055if (it8 == NULL)1056return;10571058if (it8->MemorySink) {10591060OWNEDMEM* p;1061OWNEDMEM* n;10621063for (p = it8->MemorySink; p != NULL; p = n) {10641065n = p->Next;1066if (p->Ptr) _cmsFree(it8 ->ContextID, p->Ptr);1067_cmsFree(it8 ->ContextID, p);1068}1069}10701071if (it8->MemoryBlock)1072_cmsFree(it8 ->ContextID, it8->MemoryBlock);10731074_cmsFree(it8 ->ContextID, it8);1075}107610771078// Allocates a chunk of data, keep linked list1079static1080void* AllocBigBlock(cmsIT8* it8, cmsUInt32Number size)1081{1082OWNEDMEM* ptr1;1083void* ptr = _cmsMallocZero(it8->ContextID, size);10841085if (ptr != NULL) {10861087ptr1 = (OWNEDMEM*) _cmsMallocZero(it8 ->ContextID, sizeof(OWNEDMEM));10881089if (ptr1 == NULL) {10901091_cmsFree(it8 ->ContextID, ptr);1092return NULL;1093}10941095ptr1-> Ptr = ptr;1096ptr1-> Next = it8 -> MemorySink;1097it8 -> MemorySink = ptr1;1098}10991100return ptr;1101}110211031104// Suballocator.1105static1106void* AllocChunk(cmsIT8* it8, cmsUInt32Number size)1107{1108cmsUInt32Number Free = it8 ->Allocator.BlockSize - it8 ->Allocator.Used;1109cmsUInt8Number* ptr;11101111size = _cmsALIGNMEM(size);11121113if (size > Free) {11141115if (it8 -> Allocator.BlockSize == 0)11161117it8 -> Allocator.BlockSize = 20*1024;1118else1119it8 ->Allocator.BlockSize *= 2;11201121if (it8 ->Allocator.BlockSize < size)1122it8 ->Allocator.BlockSize = size;11231124it8 ->Allocator.Used = 0;1125it8 ->Allocator.Block = (cmsUInt8Number*) AllocBigBlock(it8, it8 ->Allocator.BlockSize);1126}11271128ptr = it8 ->Allocator.Block + it8 ->Allocator.Used;1129it8 ->Allocator.Used += size;11301131return (void*) ptr;11321133}113411351136// Allocates a string1137static1138char *AllocString(cmsIT8* it8, const char* str)1139{1140cmsUInt32Number Size = (cmsUInt32Number) strlen(str)+1;1141char *ptr;114211431144ptr = (char *) AllocChunk(it8, Size);1145if (ptr) strncpy (ptr, str, Size-1);11461147return ptr;1148}11491150// Searches through linked list11511152static1153cmsBool IsAvailableOnList(KEYVALUE* p, const char* Key, const char* Subkey, KEYVALUE** LastPtr)1154{1155if (LastPtr) *LastPtr = p;11561157for (; p != NULL; p = p->Next) {11581159if (LastPtr) *LastPtr = p;11601161if (*Key != '#') { // Comments are ignored11621163if (cmsstrcasecmp(Key, p->Keyword) == 0)1164break;1165}1166}11671168if (p == NULL)1169return FALSE;11701171if (Subkey == 0)1172return TRUE;11731174for (; p != NULL; p = p->NextSubkey) {11751176if (p ->Subkey == NULL) continue;11771178if (LastPtr) *LastPtr = p;11791180if (cmsstrcasecmp(Subkey, p->Subkey) == 0)1181return TRUE;1182}11831184return FALSE;1185}1186118711881189// Add a property into a linked list1190static1191KEYVALUE* AddToList(cmsIT8* it8, KEYVALUE** Head, const char *Key, const char *Subkey, const char* xValue, WRITEMODE WriteAs)1192{1193KEYVALUE* p;1194KEYVALUE* last;119511961197// Check if property is already in list11981199if (IsAvailableOnList(*Head, Key, Subkey, &p)) {12001201// This may work for editing properties12021203// return SynError(it8, "duplicate key <%s>", Key);1204}1205else {12061207last = p;12081209// Allocate the container1210p = (KEYVALUE*) AllocChunk(it8, sizeof(KEYVALUE));1211if (p == NULL)1212{1213SynError(it8, "AddToList: out of memory");1214return NULL;1215}12161217// Store name and value1218p->Keyword = AllocString(it8, Key);1219p->Subkey = (Subkey == NULL) ? NULL : AllocString(it8, Subkey);12201221// Keep the container in our list1222if (*Head == NULL) {1223*Head = p;1224}1225else1226{1227if (Subkey != NULL && last != NULL) {12281229last->NextSubkey = p;12301231// If Subkey is not null, then last is the last property with the same key,1232// but not necessarily is the last property in the list, so we need to move1233// to the actual list end1234while (last->Next != NULL)1235last = last->Next;1236}12371238if (last != NULL) last->Next = p;1239}12401241p->Next = NULL;1242p->NextSubkey = NULL;1243}12441245p->WriteAs = WriteAs;12461247if (xValue != NULL) {12481249p->Value = AllocString(it8, xValue);1250}1251else {1252p->Value = NULL;1253}12541255return p;1256}12571258static1259KEYVALUE* AddAvailableProperty(cmsIT8* it8, const char* Key, WRITEMODE as)1260{1261return AddToList(it8, &it8->ValidKeywords, Key, NULL, NULL, as);1262}126312641265static1266KEYVALUE* AddAvailableSampleID(cmsIT8* it8, const char* Key)1267{1268return AddToList(it8, &it8->ValidSampleID, Key, NULL, NULL, WRITE_UNCOOKED);1269}127012711272static1273void AllocTable(cmsIT8* it8)1274{1275TABLE* t;12761277t = it8 ->Tab + it8 ->TablesCount;12781279t->HeaderList = NULL;1280t->DataFormat = NULL;1281t->Data = NULL;12821283it8 ->TablesCount++;1284}128512861287cmsInt32Number CMSEXPORT cmsIT8SetTable(cmsHANDLE IT8, cmsUInt32Number nTable)1288{1289cmsIT8* it8 = (cmsIT8*) IT8;12901291if (nTable >= it8 ->TablesCount) {12921293if (nTable == it8 ->TablesCount) {12941295AllocTable(it8);1296}1297else {1298SynError(it8, "Table %d is out of sequence", nTable);1299return -1;1300}1301}13021303it8 ->nTable = nTable;13041305return (cmsInt32Number) nTable;1306}1307130813091310// Init an empty container1311cmsHANDLE CMSEXPORT cmsIT8Alloc(cmsContext ContextID)1312{1313cmsIT8* it8;1314cmsUInt32Number i;13151316it8 = (cmsIT8*) _cmsMallocZero(ContextID, sizeof(cmsIT8));1317if (it8 == NULL) return NULL;13181319AllocTable(it8);13201321it8->MemoryBlock = NULL;1322it8->MemorySink = NULL;13231324it8 ->nTable = 0;13251326it8->ContextID = ContextID;1327it8->Allocator.Used = 0;1328it8->Allocator.Block = NULL;1329it8->Allocator.BlockSize = 0;13301331it8->ValidKeywords = NULL;1332it8->ValidSampleID = NULL;13331334it8 -> sy = SUNDEFINED;1335it8 -> ch = ' ';1336it8 -> Source = NULL;1337it8 -> inum = 0;1338it8 -> dnum = 0.0;13391340it8->FileStack[0] = (FILECTX*)AllocChunk(it8, sizeof(FILECTX));1341it8->IncludeSP = 0;1342it8 -> lineno = 1;13431344strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT);1345cmsIT8SetSheetType((cmsHANDLE) it8, "CGATS.17");13461347// Initialize predefined properties & data13481349for (i=0; i < NUMPREDEFINEDPROPS; i++)1350AddAvailableProperty(it8, PredefinedProperties[i].id, PredefinedProperties[i].as);13511352for (i=0; i < NUMPREDEFINEDSAMPLEID; i++)1353AddAvailableSampleID(it8, PredefinedSampleID[i]);135413551356return (cmsHANDLE) it8;1357}135813591360const char* CMSEXPORT cmsIT8GetSheetType(cmsHANDLE hIT8)1361{1362return GetTable((cmsIT8*) hIT8)->SheetType;1363}13641365cmsBool CMSEXPORT cmsIT8SetSheetType(cmsHANDLE hIT8, const char* Type)1366{1367TABLE* t = GetTable((cmsIT8*) hIT8);13681369strncpy(t ->SheetType, Type, MAXSTR-1);1370t ->SheetType[MAXSTR-1] = 0;1371return TRUE;1372}13731374cmsBool CMSEXPORT cmsIT8SetComment(cmsHANDLE hIT8, const char* Val)1375{1376cmsIT8* it8 = (cmsIT8*) hIT8;13771378if (!Val) return FALSE;1379if (!*Val) return FALSE;13801381return AddToList(it8, &GetTable(it8)->HeaderList, "# ", NULL, Val, WRITE_UNCOOKED) != NULL;1382}13831384// Sets a property1385cmsBool CMSEXPORT cmsIT8SetPropertyStr(cmsHANDLE hIT8, const char* Key, const char *Val)1386{1387cmsIT8* it8 = (cmsIT8*) hIT8;13881389if (!Val) return FALSE;1390if (!*Val) return FALSE;13911392return AddToList(it8, &GetTable(it8)->HeaderList, Key, NULL, Val, WRITE_STRINGIFY) != NULL;1393}13941395cmsBool CMSEXPORT cmsIT8SetPropertyDbl(cmsHANDLE hIT8, const char* cProp, cmsFloat64Number Val)1396{1397cmsIT8* it8 = (cmsIT8*) hIT8;1398char Buffer[1024];13991400snprintf(Buffer, 1023, it8->DoubleFormatter, Val);14011402return AddToList(it8, &GetTable(it8)->HeaderList, cProp, NULL, Buffer, WRITE_UNCOOKED) != NULL;1403}14041405cmsBool CMSEXPORT cmsIT8SetPropertyHex(cmsHANDLE hIT8, const char* cProp, cmsUInt32Number Val)1406{1407cmsIT8* it8 = (cmsIT8*) hIT8;1408char Buffer[1024];14091410snprintf(Buffer, 1023, "%u", Val);14111412return AddToList(it8, &GetTable(it8)->HeaderList, cProp, NULL, Buffer, WRITE_HEXADECIMAL) != NULL;1413}14141415cmsBool CMSEXPORT cmsIT8SetPropertyUncooked(cmsHANDLE hIT8, const char* Key, const char* Buffer)1416{1417cmsIT8* it8 = (cmsIT8*) hIT8;14181419return AddToList(it8, &GetTable(it8)->HeaderList, Key, NULL, Buffer, WRITE_UNCOOKED) != NULL;1420}14211422cmsBool CMSEXPORT cmsIT8SetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char* SubKey, const char *Buffer)1423{1424cmsIT8* it8 = (cmsIT8*) hIT8;14251426return AddToList(it8, &GetTable(it8)->HeaderList, Key, SubKey, Buffer, WRITE_PAIR) != NULL;1427}14281429// Gets a property1430const char* CMSEXPORT cmsIT8GetProperty(cmsHANDLE hIT8, const char* Key)1431{1432cmsIT8* it8 = (cmsIT8*) hIT8;1433KEYVALUE* p;14341435if (IsAvailableOnList(GetTable(it8) -> HeaderList, Key, NULL, &p))1436{1437return p -> Value;1438}1439return NULL;1440}144114421443cmsFloat64Number CMSEXPORT cmsIT8GetPropertyDbl(cmsHANDLE hIT8, const char* cProp)1444{1445const char *v = cmsIT8GetProperty(hIT8, cProp);14461447if (v == NULL) return 0.0;14481449return ParseFloatNumber(v);1450}14511452const char* CMSEXPORT cmsIT8GetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char *SubKey)1453{1454cmsIT8* it8 = (cmsIT8*) hIT8;1455KEYVALUE* p;14561457if (IsAvailableOnList(GetTable(it8) -> HeaderList, Key, SubKey, &p)) {1458return p -> Value;1459}1460return NULL;1461}14621463// ----------------------------------------------------------------- Datasets146414651466static1467void AllocateDataFormat(cmsIT8* it8)1468{1469TABLE* t = GetTable(it8);14701471if (t -> DataFormat) return; // Already allocated14721473t -> nSamples = (int) cmsIT8GetPropertyDbl(it8, "NUMBER_OF_FIELDS");14741475if (t -> nSamples <= 0) {14761477SynError(it8, "AllocateDataFormat: Unknown NUMBER_OF_FIELDS");1478t -> nSamples = 10;1479}14801481t -> DataFormat = (char**) AllocChunk (it8, ((cmsUInt32Number) t->nSamples + 1) * sizeof(char *));1482if (t->DataFormat == NULL) {14831484SynError(it8, "AllocateDataFormat: Unable to allocate dataFormat array");1485}14861487}14881489static1490const char *GetDataFormat(cmsIT8* it8, int n)1491{1492TABLE* t = GetTable(it8);14931494if (t->DataFormat)1495return t->DataFormat[n];14961497return NULL;1498}14991500static1501cmsBool SetDataFormat(cmsIT8* it8, int n, const char *label)1502{1503TABLE* t = GetTable(it8);15041505if (!t->DataFormat)1506AllocateDataFormat(it8);15071508if (n > t -> nSamples) {1509SynError(it8, "More than NUMBER_OF_FIELDS fields.");1510return FALSE;1511}15121513if (t->DataFormat) {1514t->DataFormat[n] = AllocString(it8, label);1515}15161517return TRUE;1518}151915201521cmsBool CMSEXPORT cmsIT8SetDataFormat(cmsHANDLE h, int n, const char *Sample)1522{1523cmsIT8* it8 = (cmsIT8*)h;1524return SetDataFormat(it8, n, Sample);1525}15261527// A safe atoi that returns 0 when NULL input is given1528static1529cmsInt32Number satoi(const char* b)1530{1531if (b == NULL) return 0;1532return atoi(b);1533}15341535static1536void AllocateDataSet(cmsIT8* it8)1537{1538TABLE* t = GetTable(it8);15391540if (t -> Data) return; // Already allocated15411542t-> nSamples = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));1543t-> nPatches = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS"));15441545if (t -> nSamples < 0 || t->nSamples > 0x7ffe || t->nPatches < 0 || t->nPatches > 0x7ffe)1546{1547SynError(it8, "AllocateDataSet: too much data");1548}1549else {1550// Some dumb analizers warns of possible overflow here, just take a look couple of lines above.1551t->Data = (char**)AllocChunk(it8, ((cmsUInt32Number)t->nSamples + 1) * ((cmsUInt32Number)t->nPatches + 1) * sizeof(char*));1552if (t->Data == NULL) {15531554SynError(it8, "AllocateDataSet: Unable to allocate data array");1555}1556}15571558}15591560static1561char* GetData(cmsIT8* it8, int nSet, int nField)1562{1563TABLE* t = GetTable(it8);1564int nSamples = t -> nSamples;1565int nPatches = t -> nPatches;15661567if (nSet >= nPatches || nField >= nSamples)1568return NULL;15691570if (!t->Data) return NULL;1571return t->Data [nSet * nSamples + nField];1572}15731574static1575cmsBool SetData(cmsIT8* it8, int nSet, int nField, const char *Val)1576{1577TABLE* t = GetTable(it8);15781579if (!t->Data)1580AllocateDataSet(it8);15811582if (!t->Data) return FALSE;15831584if (nSet > t -> nPatches || nSet < 0) {15851586return SynError(it8, "Patch %d out of range, there are %d patches", nSet, t -> nPatches);1587}15881589if (nField > t ->nSamples || nField < 0) {1590return SynError(it8, "Sample %d out of range, there are %d samples", nField, t ->nSamples);15911592}15931594t->Data [nSet * t -> nSamples + nField] = AllocString(it8, Val);1595return TRUE;1596}159715981599// --------------------------------------------------------------- File I/O160016011602// Writes a string to file1603static1604void WriteStr(SAVESTREAM* f, const char *str)1605{1606cmsUInt32Number len;16071608if (str == NULL)1609str = " ";16101611// Length to write1612len = (cmsUInt32Number) strlen(str);1613f ->Used += len;161416151616if (f ->stream) { // Should I write it to a file?16171618if (fwrite(str, 1, len, f->stream) != len) {1619cmsSignalError(0, cmsERROR_WRITE, "Write to file error in CGATS parser");1620return;1621}16221623}1624else { // Or to a memory block?16251626if (f ->Base) { // Am I just counting the bytes?16271628if (f ->Used > f ->Max) {16291630cmsSignalError(0, cmsERROR_WRITE, "Write to memory overflows in CGATS parser");1631return;1632}16331634memmove(f ->Ptr, str, len);1635f->Ptr += len;1636}16371638}1639}164016411642// Write formatted16431644static1645void Writef(SAVESTREAM* f, const char* frm, ...)1646{1647char Buffer[4096];1648va_list args;16491650va_start(args, frm);1651vsnprintf(Buffer, 4095, frm, args);1652Buffer[4095] = 0;1653WriteStr(f, Buffer);1654va_end(args);16551656}16571658// Writes full header1659static1660void WriteHeader(cmsIT8* it8, SAVESTREAM* fp)1661{1662KEYVALUE* p;1663TABLE* t = GetTable(it8);16641665// Writes the type1666WriteStr(fp, t->SheetType);1667WriteStr(fp, "\n");16681669for (p = t->HeaderList; (p != NULL); p = p->Next)1670{1671if (*p ->Keyword == '#') {16721673char* Pt;16741675WriteStr(fp, "#\n# ");1676for (Pt = p ->Value; *Pt; Pt++) {167716781679Writef(fp, "%c", *Pt);16801681if (*Pt == '\n') {1682WriteStr(fp, "# ");1683}1684}16851686WriteStr(fp, "\n#\n");1687continue;1688}168916901691if (!IsAvailableOnList(it8-> ValidKeywords, p->Keyword, NULL, NULL)) {16921693#ifdef CMS_STRICT_CGATS1694WriteStr(fp, "KEYWORD\t\"");1695WriteStr(fp, p->Keyword);1696WriteStr(fp, "\"\n");1697#endif16981699AddAvailableProperty(it8, p->Keyword, WRITE_UNCOOKED);1700}17011702WriteStr(fp, p->Keyword);1703if (p->Value) {17041705switch (p ->WriteAs) {17061707case WRITE_UNCOOKED:1708Writef(fp, "\t%s", p ->Value);1709break;17101711case WRITE_STRINGIFY:1712Writef(fp, "\t\"%s\"", p->Value );1713break;17141715case WRITE_HEXADECIMAL:1716Writef(fp, "\t0x%X", satoi(p ->Value));1717break;17181719case WRITE_BINARY:1720Writef(fp, "\t0x%B", satoi(p ->Value));1721break;17221723case WRITE_PAIR:1724Writef(fp, "\t\"%s,%s\"", p->Subkey, p->Value);1725break;17261727default: SynError(it8, "Unknown write mode %d", p ->WriteAs);1728return;1729}1730}17311732WriteStr (fp, "\n");1733}17341735}173617371738// Writes the data format1739static1740void WriteDataFormat(SAVESTREAM* fp, cmsIT8* it8)1741{1742int i, nSamples;1743TABLE* t = GetTable(it8);17441745if (!t -> DataFormat) return;17461747WriteStr(fp, "BEGIN_DATA_FORMAT\n");1748WriteStr(fp, " ");1749nSamples = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));17501751for (i = 0; i < nSamples; i++) {17521753WriteStr(fp, t->DataFormat[i]);1754WriteStr(fp, ((i == (nSamples-1)) ? "\n" : "\t"));1755}17561757WriteStr (fp, "END_DATA_FORMAT\n");1758}175917601761// Writes data array1762static1763void WriteData(SAVESTREAM* fp, cmsIT8* it8)1764{1765int i, j;1766TABLE* t = GetTable(it8);17671768if (!t->Data) return;17691770WriteStr (fp, "BEGIN_DATA\n");17711772t->nPatches = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS"));17731774for (i = 0; i < t-> nPatches; i++) {17751776WriteStr(fp, " ");17771778for (j = 0; j < t->nSamples; j++) {17791780char *ptr = t->Data[i*t->nSamples+j];17811782if (ptr == NULL) WriteStr(fp, "\"\"");1783else {1784// If value contains whitespace, enclose within quote17851786if (strchr(ptr, ' ') != NULL) {17871788WriteStr(fp, "\"");1789WriteStr(fp, ptr);1790WriteStr(fp, "\"");1791}1792else1793WriteStr(fp, ptr);1794}17951796WriteStr(fp, ((j == (t->nSamples-1)) ? "\n" : "\t"));1797}1798}1799WriteStr (fp, "END_DATA\n");1800}1801180218031804// Saves whole file1805cmsBool CMSEXPORT cmsIT8SaveToFile(cmsHANDLE hIT8, const char* cFileName)1806{1807SAVESTREAM sd;1808cmsUInt32Number i;1809cmsIT8* it8 = (cmsIT8*) hIT8;18101811memset(&sd, 0, sizeof(sd));18121813sd.stream = fopen(cFileName, "wt");1814if (!sd.stream) return FALSE;18151816for (i=0; i < it8 ->TablesCount; i++) {18171818cmsIT8SetTable(hIT8, i);1819WriteHeader(it8, &sd);1820WriteDataFormat(&sd, it8);1821WriteData(&sd, it8);1822}18231824if (fclose(sd.stream) != 0) return FALSE;18251826return TRUE;1827}182818291830// Saves to memory1831cmsBool CMSEXPORT cmsIT8SaveToMem(cmsHANDLE hIT8, void *MemPtr, cmsUInt32Number* BytesNeeded)1832{1833SAVESTREAM sd;1834cmsUInt32Number i;1835cmsIT8* it8 = (cmsIT8*) hIT8;18361837memset(&sd, 0, sizeof(sd));18381839sd.stream = NULL;1840sd.Base = (cmsUInt8Number*) MemPtr;1841sd.Ptr = sd.Base;18421843sd.Used = 0;18441845if (sd.Base)1846sd.Max = *BytesNeeded; // Write to memory?1847else1848sd.Max = 0; // Just counting the needed bytes18491850for (i=0; i < it8 ->TablesCount; i++) {18511852cmsIT8SetTable(hIT8, i);1853WriteHeader(it8, &sd);1854WriteDataFormat(&sd, it8);1855WriteData(&sd, it8);1856}18571858sd.Used++; // The \0 at the very end18591860if (sd.Base)1861*sd.Ptr = 0;18621863*BytesNeeded = sd.Used;18641865return TRUE;1866}186718681869// -------------------------------------------------------------- Higher level parsing18701871static1872cmsBool DataFormatSection(cmsIT8* it8)1873{1874int iField = 0;1875TABLE* t = GetTable(it8);18761877InSymbol(it8); // Eats "BEGIN_DATA_FORMAT"1878CheckEOLN(it8);18791880while (it8->sy != SEND_DATA_FORMAT &&1881it8->sy != SEOLN &&1882it8->sy != SEOF &&1883it8->sy != SSYNERROR) {18841885if (it8->sy != SIDENT) {18861887return SynError(it8, "Sample type expected");1888}18891890if (!SetDataFormat(it8, iField, it8->id)) return FALSE;1891iField++;18921893InSymbol(it8);1894SkipEOLN(it8);1895}18961897SkipEOLN(it8);1898Skip(it8, SEND_DATA_FORMAT);1899SkipEOLN(it8);19001901if (iField != t ->nSamples) {1902SynError(it8, "Count mismatch. NUMBER_OF_FIELDS was %d, found %d\n", t ->nSamples, iField);190319041905}19061907return TRUE;1908}1909191019111912static1913cmsBool DataSection (cmsIT8* it8)1914{1915int iField = 0;1916int iSet = 0;1917char Buffer[256];1918TABLE* t = GetTable(it8);19191920InSymbol(it8); // Eats "BEGIN_DATA"1921CheckEOLN(it8);19221923if (!t->Data)1924AllocateDataSet(it8);19251926while (it8->sy != SEND_DATA && it8->sy != SEOF)1927{1928if (iField >= t -> nSamples) {1929iField = 0;1930iSet++;19311932}19331934if (it8->sy != SEND_DATA && it8->sy != SEOF) {19351936if (!GetVal(it8, Buffer, 255, "Sample data expected"))1937return FALSE;19381939if (!SetData(it8, iSet, iField, Buffer))1940return FALSE;19411942iField++;19431944InSymbol(it8);1945SkipEOLN(it8);1946}1947}19481949SkipEOLN(it8);1950Skip(it8, SEND_DATA);1951SkipEOLN(it8);19521953// Check for data completion.19541955if ((iSet+1) != t -> nPatches)1956return SynError(it8, "Count mismatch. NUMBER_OF_SETS was %d, found %d\n", t ->nPatches, iSet+1);19571958return TRUE;1959}19601961196219631964static1965cmsBool HeaderSection(cmsIT8* it8)1966{1967char VarName[MAXID];1968char Buffer[MAXSTR];1969KEYVALUE* Key;19701971while (it8->sy != SEOF &&1972it8->sy != SSYNERROR &&1973it8->sy != SBEGIN_DATA_FORMAT &&1974it8->sy != SBEGIN_DATA) {197519761977switch (it8 -> sy) {19781979case SKEYWORD:1980InSymbol(it8);1981if (!GetVal(it8, Buffer, MAXSTR-1, "Keyword expected")) return FALSE;1982if (!AddAvailableProperty(it8, Buffer, WRITE_UNCOOKED)) return FALSE;1983InSymbol(it8);1984break;198519861987case SDATA_FORMAT_ID:1988InSymbol(it8);1989if (!GetVal(it8, Buffer, MAXSTR-1, "Keyword expected")) return FALSE;1990if (!AddAvailableSampleID(it8, Buffer)) return FALSE;1991InSymbol(it8);1992break;199319941995case SIDENT:1996strncpy(VarName, it8->id, MAXID - 1);1997VarName[MAXID - 1] = 0;19981999if (!IsAvailableOnList(it8->ValidKeywords, VarName, NULL, &Key)) {20002001#ifdef CMS_STRICT_CGATS2002return SynError(it8, "Undefined keyword '%s'", VarName);2003#else2004Key = AddAvailableProperty(it8, VarName, WRITE_UNCOOKED);2005if (Key == NULL) return FALSE;2006#endif2007}20082009InSymbol(it8);2010if (!GetVal(it8, Buffer, MAXSTR - 1, "Property data expected")) return FALSE;20112012if (Key->WriteAs != WRITE_PAIR) {2013AddToList(it8, &GetTable(it8)->HeaderList, VarName, NULL, Buffer,2014(it8->sy == SSTRING) ? WRITE_STRINGIFY : WRITE_UNCOOKED);2015}2016else {2017const char *Subkey;2018char *Nextkey;2019if (it8->sy != SSTRING)2020return SynError(it8, "Invalid value '%s' for property '%s'.", Buffer, VarName);20212022// chop the string as a list of "subkey, value" pairs, using ';' as a separator2023for (Subkey = Buffer; Subkey != NULL; Subkey = Nextkey)2024{2025char *Value, *temp;20262027// identify token pair boundary2028Nextkey = (char*)strchr(Subkey, ';');2029if (Nextkey)2030*Nextkey++ = '\0';20312032// for each pair, split the subkey and the value2033Value = (char*)strrchr(Subkey, ',');2034if (Value == NULL)2035return SynError(it8, "Invalid value for property '%s'.", VarName);20362037// gobble the spaces before the coma, and the coma itself2038temp = Value++;2039do *temp-- = '\0'; while (temp >= Subkey && *temp == ' ');20402041// gobble any space at the right2042temp = Value + strlen(Value) - 1;2043while (*temp == ' ') *temp-- = '\0';20442045// trim the strings from the left2046Subkey += strspn(Subkey, " ");2047Value += strspn(Value, " ");20482049if (Subkey[0] == 0 || Value[0] == 0)2050return SynError(it8, "Invalid value for property '%s'.", VarName);2051AddToList(it8, &GetTable(it8)->HeaderList, VarName, Subkey, Value, WRITE_PAIR);2052}2053}20542055InSymbol(it8);2056break;205720582059case SEOLN: break;20602061default:2062return SynError(it8, "expected keyword or identifier");2063}20642065SkipEOLN(it8);2066}20672068return TRUE;20692070}207120722073static2074void ReadType(cmsIT8* it8, char* SheetTypePtr)2075{2076cmsInt32Number cnt = 0;20772078// First line is a very special case.20792080while (isseparator(it8->ch))2081NextCh(it8);20822083while (it8->ch != '\r' && it8 ->ch != '\n' && it8->ch != '\t' && it8 -> ch != 0) {20842085if (cnt++ < MAXSTR)2086*SheetTypePtr++= (char) it8 ->ch;2087NextCh(it8);2088}20892090*SheetTypePtr = 0;2091}209220932094static2095cmsBool ParseIT8(cmsIT8* it8, cmsBool nosheet)2096{2097char* SheetTypePtr = it8 ->Tab[0].SheetType;20982099if (nosheet == 0) {2100ReadType(it8, SheetTypePtr);2101}21022103InSymbol(it8);21042105SkipEOLN(it8);21062107while (it8-> sy != SEOF &&2108it8-> sy != SSYNERROR) {21092110switch (it8 -> sy) {21112112case SBEGIN_DATA_FORMAT:2113if (!DataFormatSection(it8)) return FALSE;2114break;21152116case SBEGIN_DATA:21172118if (!DataSection(it8)) return FALSE;21192120if (it8 -> sy != SEOF) {21212122AllocTable(it8);2123it8 ->nTable = it8 ->TablesCount - 1;21242125// Read sheet type if present. We only support identifier and string.2126// <ident> <eoln> is a type string2127// anything else, is not a type string2128if (nosheet == 0) {21292130if (it8 ->sy == SIDENT) {21312132// May be a type sheet or may be a prop value statement. We cannot use insymbol in2133// this special case...2134while (isseparator(it8->ch))2135NextCh(it8);21362137// If a newline is found, then this is a type string2138if (it8 ->ch == '\n' || it8->ch == '\r') {21392140cmsIT8SetSheetType(it8, it8 ->id);2141InSymbol(it8);2142}2143else2144{2145// It is not. Just continue2146cmsIT8SetSheetType(it8, "");2147}2148}2149else2150// Validate quoted strings2151if (it8 ->sy == SSTRING) {2152cmsIT8SetSheetType(it8, it8 ->str);2153InSymbol(it8);2154}2155}21562157}2158break;21592160case SEOLN:2161SkipEOLN(it8);2162break;21632164default:2165if (!HeaderSection(it8)) return FALSE;2166}21672168}21692170return (it8 -> sy != SSYNERROR);2171}2172217321742175// Init useful pointers21762177static2178void CookPointers(cmsIT8* it8)2179{2180int idField, i;2181char* Fld;2182cmsUInt32Number j;2183cmsUInt32Number nOldTable = it8 ->nTable;21842185for (j=0; j < it8 ->TablesCount; j++) {21862187TABLE* t = it8 ->Tab + j;21882189t -> SampleID = 0;2190it8 ->nTable = j;21912192for (idField = 0; idField < t -> nSamples; idField++)2193{2194if (t ->DataFormat == NULL){2195SynError(it8, "Undefined DATA_FORMAT");2196return;2197}21982199Fld = t->DataFormat[idField];2200if (!Fld) continue;220122022203if (cmsstrcasecmp(Fld, "SAMPLE_ID") == 0) {22042205t -> SampleID = idField;2206}22072208// "LABEL" is an extension. It keeps references to forward tables22092210if ((cmsstrcasecmp(Fld, "LABEL") == 0) || Fld[0] == '$') {22112212// Search for table references...2213for (i = 0; i < t->nPatches; i++) {22142215char* Label = GetData(it8, i, idField);22162217if (Label) {22182219cmsUInt32Number k;22202221// This is the label, search for a table containing2222// this property22232224for (k = 0; k < it8->TablesCount; k++) {22252226TABLE* Table = it8->Tab + k;2227KEYVALUE* p;22282229if (IsAvailableOnList(Table->HeaderList, Label, NULL, &p)) {22302231// Available, keep type and table2232char Buffer[256];22332234char* Type = p->Value;2235int nTable = (int)k;22362237snprintf(Buffer, 255, "%s %d %s", Label, nTable, Type);22382239SetData(it8, i, idField, Buffer);2240}2241}224222432244}22452246}224722482249}22502251}2252}22532254it8 ->nTable = nOldTable;2255}22562257// Try to infere if the file is a CGATS/IT8 file at all. Read first line2258// that should be something like some printable characters plus a \n2259// returns 0 if this is not like a CGATS, or an integer otherwise. This integer is the number of words in first line?2260static2261int IsMyBlock(const cmsUInt8Number* Buffer, cmsUInt32Number n)2262{2263int words = 1, space = 0, quot = 0;2264cmsUInt32Number i;22652266if (n < 10) return 0; // Too small22672268if (n > 132)2269n = 132;22702271for (i = 1; i < n; i++) {22722273switch(Buffer[i])2274{2275case '\n':2276case '\r':2277return ((quot == 1) || (words > 2)) ? 0 : words;2278case '\t':2279case ' ':2280if(!quot && !space)2281space = 1;2282break;2283case '\"':2284quot = !quot;2285break;2286default:2287if (Buffer[i] < 32) return 0;2288if (Buffer[i] > 127) return 0;2289words += space;2290space = 0;2291break;2292}2293}22942295return 0;2296}229722982299static2300cmsBool IsMyFile(const char* FileName)2301{2302FILE *fp;2303cmsUInt32Number Size;2304cmsUInt8Number Ptr[133];23052306fp = fopen(FileName, "rt");2307if (!fp) {2308cmsSignalError(0, cmsERROR_FILE, "File '%s' not found", FileName);2309return FALSE;2310}23112312Size = (cmsUInt32Number) fread(Ptr, 1, 132, fp);23132314if (fclose(fp) != 0)2315return FALSE;23162317Ptr[Size] = '\0';23182319return IsMyBlock(Ptr, Size);2320}23212322// ---------------------------------------------------------- Exported routines232323242325cmsHANDLE CMSEXPORT cmsIT8LoadFromMem(cmsContext ContextID, const void *Ptr, cmsUInt32Number len)2326{2327cmsHANDLE hIT8;2328cmsIT8* it8;2329int type;23302331_cmsAssert(Ptr != NULL);2332_cmsAssert(len != 0);23332334type = IsMyBlock((const cmsUInt8Number*)Ptr, len);2335if (type == 0) return NULL;23362337hIT8 = cmsIT8Alloc(ContextID);2338if (!hIT8) return NULL;23392340it8 = (cmsIT8*) hIT8;2341it8 ->MemoryBlock = (char*) _cmsMalloc(ContextID, len + 1);2342if (it8->MemoryBlock == NULL)2343{2344cmsIT8Free(hIT8);2345return FALSE;2346}23472348strncpy(it8 ->MemoryBlock, (const char*) Ptr, len);2349it8 ->MemoryBlock[len] = 0;23502351strncpy(it8->FileStack[0]->FileName, "", cmsMAX_PATH-1);2352it8-> Source = it8 -> MemoryBlock;23532354if (!ParseIT8(it8, type-1)) {23552356cmsIT8Free(hIT8);2357return FALSE;2358}23592360CookPointers(it8);2361it8 ->nTable = 0;23622363_cmsFree(ContextID, it8->MemoryBlock);2364it8 -> MemoryBlock = NULL;23652366return hIT8;236723682369}237023712372cmsHANDLE CMSEXPORT cmsIT8LoadFromFile(cmsContext ContextID, const char* cFileName)2373{23742375cmsHANDLE hIT8;2376cmsIT8* it8;2377int type;23782379_cmsAssert(cFileName != NULL);23802381type = IsMyFile(cFileName);2382if (type == 0) return NULL;23832384hIT8 = cmsIT8Alloc(ContextID);2385it8 = (cmsIT8*) hIT8;2386if (!hIT8) return NULL;238723882389it8 ->FileStack[0]->Stream = fopen(cFileName, "rt");23902391if (!it8 ->FileStack[0]->Stream) {2392cmsIT8Free(hIT8);2393return NULL;2394}239523962397strncpy(it8->FileStack[0]->FileName, cFileName, cmsMAX_PATH-1);2398it8->FileStack[0]->FileName[cmsMAX_PATH-1] = 0;23992400if (!ParseIT8(it8, type-1)) {24012402fclose(it8 ->FileStack[0]->Stream);2403cmsIT8Free(hIT8);2404return NULL;2405}24062407CookPointers(it8);2408it8 ->nTable = 0;24092410if (fclose(it8 ->FileStack[0]->Stream)!= 0) {2411cmsIT8Free(hIT8);2412return NULL;2413}24142415return hIT8;24162417}24182419int CMSEXPORT cmsIT8EnumDataFormat(cmsHANDLE hIT8, char ***SampleNames)2420{2421cmsIT8* it8 = (cmsIT8*) hIT8;2422TABLE* t;24232424_cmsAssert(hIT8 != NULL);24252426t = GetTable(it8);24272428if (SampleNames)2429*SampleNames = t -> DataFormat;2430return t -> nSamples;2431}243224332434cmsUInt32Number CMSEXPORT cmsIT8EnumProperties(cmsHANDLE hIT8, char ***PropertyNames)2435{2436cmsIT8* it8 = (cmsIT8*) hIT8;2437KEYVALUE* p;2438cmsUInt32Number n;2439char **Props;2440TABLE* t;24412442_cmsAssert(hIT8 != NULL);24432444t = GetTable(it8);24452446// Pass#1 - count properties24472448n = 0;2449for (p = t -> HeaderList; p != NULL; p = p->Next) {2450n++;2451}245224532454Props = (char **) AllocChunk(it8, sizeof(char *) * n);24552456// Pass#2 - Fill pointers2457n = 0;2458for (p = t -> HeaderList; p != NULL; p = p->Next) {2459Props[n++] = p -> Keyword;2460}24612462*PropertyNames = Props;2463return n;2464}24652466cmsUInt32Number CMSEXPORT cmsIT8EnumPropertyMulti(cmsHANDLE hIT8, const char* cProp, const char ***SubpropertyNames)2467{2468cmsIT8* it8 = (cmsIT8*) hIT8;2469KEYVALUE *p, *tmp;2470cmsUInt32Number n;2471const char **Props;2472TABLE* t;24732474_cmsAssert(hIT8 != NULL);247524762477t = GetTable(it8);24782479if(!IsAvailableOnList(t->HeaderList, cProp, NULL, &p)) {2480*SubpropertyNames = 0;2481return 0;2482}24832484// Pass#1 - count properties24852486n = 0;2487for (tmp = p; tmp != NULL; tmp = tmp->NextSubkey) {2488if(tmp->Subkey != NULL)2489n++;2490}249124922493Props = (const char **) AllocChunk(it8, sizeof(char *) * n);24942495// Pass#2 - Fill pointers2496n = 0;2497for (tmp = p; tmp != NULL; tmp = tmp->NextSubkey) {2498if(tmp->Subkey != NULL)2499Props[n++] = p ->Subkey;2500}25012502*SubpropertyNames = Props;2503return n;2504}25052506static2507int LocatePatch(cmsIT8* it8, const char* cPatch)2508{2509int i;2510const char *data;2511TABLE* t = GetTable(it8);25122513for (i=0; i < t-> nPatches; i++) {25142515data = GetData(it8, i, t->SampleID);25162517if (data != NULL) {25182519if (cmsstrcasecmp(data, cPatch) == 0)2520return i;2521}2522}25232524// SynError(it8, "Couldn't find patch '%s'\n", cPatch);2525return -1;2526}252725282529static2530int LocateEmptyPatch(cmsIT8* it8)2531{2532int i;2533const char *data;2534TABLE* t = GetTable(it8);25352536for (i=0; i < t-> nPatches; i++) {25372538data = GetData(it8, i, t->SampleID);25392540if (data == NULL)2541return i;25422543}25442545return -1;2546}25472548static2549int LocateSample(cmsIT8* it8, const char* cSample)2550{2551int i;2552const char *fld;2553TABLE* t = GetTable(it8);25542555for (i=0; i < t->nSamples; i++) {25562557fld = GetDataFormat(it8, i);2558if (fld != NULL) {2559if (cmsstrcasecmp(fld, cSample) == 0)2560return i;2561}2562}25632564return -1;25652566}256725682569int CMSEXPORT cmsIT8FindDataFormat(cmsHANDLE hIT8, const char* cSample)2570{2571cmsIT8* it8 = (cmsIT8*) hIT8;25722573_cmsAssert(hIT8 != NULL);25742575return LocateSample(it8, cSample);2576}2577257825792580const char* CMSEXPORT cmsIT8GetDataRowCol(cmsHANDLE hIT8, int row, int col)2581{2582cmsIT8* it8 = (cmsIT8*) hIT8;25832584_cmsAssert(hIT8 != NULL);25852586return GetData(it8, row, col);2587}258825892590cmsFloat64Number CMSEXPORT cmsIT8GetDataRowColDbl(cmsHANDLE hIT8, int row, int col)2591{2592const char* Buffer;25932594Buffer = cmsIT8GetDataRowCol(hIT8, row, col);25952596if (Buffer == NULL) return 0.0;25972598return ParseFloatNumber(Buffer);2599}260026012602cmsBool CMSEXPORT cmsIT8SetDataRowCol(cmsHANDLE hIT8, int row, int col, const char* Val)2603{2604cmsIT8* it8 = (cmsIT8*) hIT8;26052606_cmsAssert(hIT8 != NULL);26072608return SetData(it8, row, col, Val);2609}261026112612cmsBool CMSEXPORT cmsIT8SetDataRowColDbl(cmsHANDLE hIT8, int row, int col, cmsFloat64Number Val)2613{2614cmsIT8* it8 = (cmsIT8*) hIT8;2615char Buff[256];26162617_cmsAssert(hIT8 != NULL);26182619snprintf(Buff, 255, it8->DoubleFormatter, Val);26202621return SetData(it8, row, col, Buff);2622}2623262426252626const char* CMSEXPORT cmsIT8GetData(cmsHANDLE hIT8, const char* cPatch, const char* cSample)2627{2628cmsIT8* it8 = (cmsIT8*) hIT8;2629int iField, iSet;26302631_cmsAssert(hIT8 != NULL);26322633iField = LocateSample(it8, cSample);2634if (iField < 0) {2635return NULL;2636}26372638iSet = LocatePatch(it8, cPatch);2639if (iSet < 0) {2640return NULL;2641}26422643return GetData(it8, iSet, iField);2644}264526462647cmsFloat64Number CMSEXPORT cmsIT8GetDataDbl(cmsHANDLE it8, const char* cPatch, const char* cSample)2648{2649const char* Buffer;26502651Buffer = cmsIT8GetData(it8, cPatch, cSample);26522653return ParseFloatNumber(Buffer);2654}2655265626572658cmsBool CMSEXPORT cmsIT8SetData(cmsHANDLE hIT8, const char* cPatch, const char* cSample, const char *Val)2659{2660cmsIT8* it8 = (cmsIT8*) hIT8;2661int iField, iSet;2662TABLE* t;26632664_cmsAssert(hIT8 != NULL);26652666t = GetTable(it8);26672668iField = LocateSample(it8, cSample);26692670if (iField < 0)2671return FALSE;26722673if (t-> nPatches == 0) {26742675AllocateDataFormat(it8);2676AllocateDataSet(it8);2677CookPointers(it8);2678}26792680if (cmsstrcasecmp(cSample, "SAMPLE_ID") == 0) {26812682iSet = LocateEmptyPatch(it8);2683if (iSet < 0) {2684return SynError(it8, "Couldn't add more patches '%s'\n", cPatch);2685}26862687iField = t -> SampleID;2688}2689else {2690iSet = LocatePatch(it8, cPatch);2691if (iSet < 0) {2692return FALSE;2693}2694}26952696return SetData(it8, iSet, iField, Val);2697}269826992700cmsBool CMSEXPORT cmsIT8SetDataDbl(cmsHANDLE hIT8, const char* cPatch,2701const char* cSample,2702cmsFloat64Number Val)2703{2704cmsIT8* it8 = (cmsIT8*) hIT8;2705char Buff[256];27062707_cmsAssert(hIT8 != NULL);27082709snprintf(Buff, 255, it8->DoubleFormatter, Val);2710return cmsIT8SetData(hIT8, cPatch, cSample, Buff);2711}27122713// Buffer should get MAXSTR at least27142715const char* CMSEXPORT cmsIT8GetPatchName(cmsHANDLE hIT8, int nPatch, char* buffer)2716{2717cmsIT8* it8 = (cmsIT8*) hIT8;2718TABLE* t;2719char* Data;27202721_cmsAssert(hIT8 != NULL);27222723t = GetTable(it8);2724Data = GetData(it8, nPatch, t->SampleID);27252726if (!Data) return NULL;2727if (!buffer) return Data;27282729strncpy(buffer, Data, MAXSTR-1);2730buffer[MAXSTR-1] = 0;2731return buffer;2732}27332734int CMSEXPORT cmsIT8GetPatchByName(cmsHANDLE hIT8, const char *cPatch)2735{2736_cmsAssert(hIT8 != NULL);27372738return LocatePatch((cmsIT8*)hIT8, cPatch);2739}27402741cmsUInt32Number CMSEXPORT cmsIT8TableCount(cmsHANDLE hIT8)2742{2743cmsIT8* it8 = (cmsIT8*) hIT8;27442745_cmsAssert(hIT8 != NULL);27462747return it8 ->TablesCount;2748}27492750// This handles the "LABEL" extension.2751// Label, nTable, Type27522753int CMSEXPORT cmsIT8SetTableByLabel(cmsHANDLE hIT8, const char* cSet, const char* cField, const char* ExpectedType)2754{2755const char* cLabelFld;2756char Type[256], Label[256];2757cmsUInt32Number nTable;27582759_cmsAssert(hIT8 != NULL);27602761if (cField != NULL && *cField == 0)2762cField = "LABEL";27632764if (cField == NULL)2765cField = "LABEL";27662767cLabelFld = cmsIT8GetData(hIT8, cSet, cField);2768if (!cLabelFld) return -1;27692770if (sscanf(cLabelFld, "%255s %u %255s", Label, &nTable, Type) != 3)2771return -1;27722773if (ExpectedType != NULL && *ExpectedType == 0)2774ExpectedType = NULL;27752776if (ExpectedType) {27772778if (cmsstrcasecmp(Type, ExpectedType) != 0) return -1;2779}27802781return cmsIT8SetTable(hIT8, nTable);2782}278327842785cmsBool CMSEXPORT cmsIT8SetIndexColumn(cmsHANDLE hIT8, const char* cSample)2786{2787cmsIT8* it8 = (cmsIT8*) hIT8;2788int pos;27892790_cmsAssert(hIT8 != NULL);27912792pos = LocateSample(it8, cSample);2793if(pos == -1)2794return FALSE;27952796it8->Tab[it8->nTable].SampleID = pos;2797return TRUE;2798}279928002801void CMSEXPORT cmsIT8DefineDblFormat(cmsHANDLE hIT8, const char* Formatter)2802{2803cmsIT8* it8 = (cmsIT8*) hIT8;28042805_cmsAssert(hIT8 != NULL);28062807if (Formatter == NULL)2808strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT);2809else2810strncpy(it8->DoubleFormatter, Formatter, sizeof(it8->DoubleFormatter));28112812it8 ->DoubleFormatter[sizeof(it8 ->DoubleFormatter)-1] = 0;2813}2814281528162817