1 //---------------------------------------------------------------------------------
3 // Little Color Management System
4 // Copyright (c) 1998-2012 Marti Maria Saguer
6 // Permission is hereby granted, free of charge, to any person obtaining
7 // a copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the Software
11 // is furnished to do so, subject to the following conditions:
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
18 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 //---------------------------------------------------------------------------------
27 #include "lcms2_internal.h"
30 // IT8.7 / CGATS.17-200x handling -----------------------------------------------------------------------------
33 #define MAXID 128 // Max lenght of identifier
34 #define MAXSTR 1024 // Max lenght of string
35 #define MAXTABLES 255 // Max Number of tables in a single stream
36 #define MAXINCLUDE 20 // Max number of nested includes
38 #define DEFAULT_DBL_FORMAT "%.10g" // Double formatting
40 #ifdef CMS_IS_WINDOWS_
42 # define DIR_CHAR '\\'
58 SEOF, // End of stream
59 SSYNERROR, // Syntax error found on stream
74 // How to write the value
85 // Linked list of variable names
86 typedef struct _KeyVal {
89 char* Keyword; // Name of variable
90 struct _KeyVal* NextSubkey; // If key is a dictionary, points to the next item
91 char* Subkey; // If key is a dictionary, points to the subkey name
92 char* Value; // Points to value
93 WRITEMODE WriteAs; // How to write the value
98 // Linked list of memory chunks (Memory sink)
99 typedef struct _OwnedMem {
101 struct _OwnedMem* Next;
102 void * Ptr; // Point to value
107 typedef struct _SubAllocator {
109 cmsUInt8Number* Block;
110 cmsUInt32Number BlockSize;
111 cmsUInt32Number Used;
115 // Table. Each individual table can hold properties and rows & cols
116 typedef struct _Table {
118 char SheetType[MAXSTR]; // The first row of the IT8 (the type)
120 int nSamples, nPatches; // Cols, Rows
121 int SampleID; // Pos of ID
123 KEYVALUE* HeaderList; // The properties
125 char** DataFormat; // The binary stream descriptor
126 char** Data; // The binary stream
130 // File stream being parsed
131 typedef struct _FileContext {
132 char FileName[cmsMAX_PATH]; // File name if being readed from file
133 FILE* Stream; // File stream or NULL if holded in memory
136 // This struct hold all information about an open IT8 handler.
140 cmsUInt32Number TablesCount; // How many tables in this stream
141 cmsUInt32Number nTable; // The actual table
143 TABLE Tab[MAXTABLES];
146 OWNEDMEM* MemorySink; // The storage backend
147 SUBALLOCATOR Allocator; // String suballocator -- just to keep it fast
149 // Parser state machine
150 SYMBOL sy; // Current symbol
151 int ch; // Current character
153 int inum; // integer value
154 cmsFloat64Number dnum; // real value
155 char id[MAXID]; // identifier
156 char str[MAXSTR]; // string
158 // Allowed keywords & datasets. They have visibility on whole stream
159 KEYVALUE* ValidKeywords;
160 KEYVALUE* ValidSampleID;
162 char* Source; // Points to loc. being parsed
163 int lineno; // line counter for error reporting
165 FILECTX* FileStack[MAXINCLUDE]; // Stack of files being parsed
166 int IncludeSP; // Include Stack Pointer
168 char* MemoryBlock; // The stream if holded in memory
170 char DoubleFormatter[MAXID];// Printf-like 'cmsFloat64Number' formatter
172 cmsContext ContextID; // The threading context
177 // The stream for save operations
180 FILE* stream; // For save-to-file behaviour
182 cmsUInt8Number* Base;
183 cmsUInt8Number* Ptr; // For save-to-mem behaviour
184 cmsUInt32Number Used;
190 // ------------------------------------------------------ cmsIT8 parsing routines
201 // The keyword->symbol translation table. Sorting is required.
202 static const KEYWORD TabKeys[] = {
204 {"$INCLUDE", SINCLUDE}, // This is an extension!
205 {".INCLUDE", SINCLUDE}, // This is an extension!
207 {"BEGIN_DATA", SBEGIN_DATA },
208 {"BEGIN_DATA_FORMAT", SBEGIN_DATA_FORMAT },
209 {"DATA_FORMAT_IDENTIFIER", SDATA_FORMAT_ID},
210 {"END_DATA", SEND_DATA},
211 {"END_DATA_FORMAT", SEND_DATA_FORMAT},
212 {"KEYWORD", SKEYWORD}
215 #define NUMKEYS (sizeof(TabKeys)/sizeof(KEYWORD))
217 // Predefined properties
221 const char *id; // The identifier
222 WRITEMODE as; // How is supposed to be written
225 static PROPERTY PredefinedProperties[] = {
227 {"NUMBER_OF_FIELDS", WRITE_UNCOOKED}, // Required - NUMBER OF FIELDS
228 {"NUMBER_OF_SETS", WRITE_UNCOOKED}, // Required - NUMBER OF SETS
229 {"ORIGINATOR", WRITE_STRINGIFY}, // Required - Identifies the specific system, organization or individual that created the data file.
230 {"FILE_DESCRIPTOR", WRITE_STRINGIFY}, // Required - Describes the purpose or contents of the data file.
231 {"CREATED", WRITE_STRINGIFY}, // Required - Indicates date of creation of the data file.
232 {"DESCRIPTOR", WRITE_STRINGIFY}, // Required - Describes the purpose or contents of the data file.
233 {"DIFFUSE_GEOMETRY", WRITE_STRINGIFY}, // The diffuse geometry used. Allowed values are "sphere" or "opal".
234 {"MANUFACTURER", WRITE_STRINGIFY},
235 {"MANUFACTURE", WRITE_STRINGIFY}, // Some broken Fuji targets does store this value
236 {"PROD_DATE", WRITE_STRINGIFY}, // Identifies year and month of production of the target in the form yyyy:mm.
237 {"SERIAL", WRITE_STRINGIFY}, // Uniquely identifies individual physical target.
239 {"MATERIAL", WRITE_STRINGIFY}, // Identifies the material on which the target was produced using a code
240 // uniquely identifying th e material. This is intend ed to be used for IT8.7
241 // physical targets only (i.e . IT8.7/1 a nd IT8.7/2).
243 {"INSTRUMENTATION", WRITE_STRINGIFY}, // Used to report the specific instrumentation used (manufacturer and
244 // model number) to generate the data reported. This data will often
245 // provide more information about the particular data collected than an
246 // extensive list of specific details. This is particularly important for
247 // spectral data or data derived from spectrophotometry.
249 {"MEASUREMENT_SOURCE", WRITE_STRINGIFY}, // Illumination used for spectral measurements. This data helps provide
250 // a guide to the potential for issues of paper fluorescence, etc.
252 {"PRINT_CONDITIONS", WRITE_STRINGIFY}, // Used to define the characteristics of the printed sheet being reported.
253 // Where standard conditions have been defined (e.g., SWOP at nominal)
254 // named conditions may suffice. Otherwise, detailed information is
257 {"SAMPLE_BACKING", WRITE_STRINGIFY}, // Identifies the backing material used behind the sample during
258 // measurement. Allowed values are
\93black
\94,
\93white
\94, or {"na".
260 {"CHISQ_DOF", WRITE_STRINGIFY}, // Degrees of freedom associated with the Chi squared statistic
262 // below properties are new in recent specs:
264 {"MEASUREMENT_GEOMETRY", WRITE_STRINGIFY}, // The type of measurement, either reflection or transmission, should be indicated
265 // along with details of the geometry and the aperture size and shape. For example,
266 // for transmission measurements it is important to identify 0/diffuse, diffuse/0,
267 // opal or integrating sphere, etc. For reflection it is important to identify 0/45,
268 // 45/0, sphere (specular included or excluded), etc.
270 {"FILTER", WRITE_STRINGIFY}, // Identifies the use of physical filter(s) during measurement. Typically used to
271 // denote the use of filters such as none, D65, Red, Green or Blue.
273 {"POLARIZATION", WRITE_STRINGIFY}, // Identifies the use of a physical polarization filter during measurement. Allowed
274 // values are {"yes
\94,
\93white
\94,
\93none
\94 or
\93na
\94.
276 {"WEIGHTING_FUNCTION", WRITE_PAIR}, // Indicates such functions as: the CIE standard observer functions used in the
277 // calculation of various data parameters (2 degree and 10 degree), CIE standard
278 // illuminant functions used in the calculation of various data parameters (e.g., D50,
279 // D65, etc.), density status response, etc. If used there shall be at least one
280 // name-value pair following the WEIGHTING_FUNCTION tag/keyword. The first attribute
281 // in the set shall be {"name" and shall identify the particular parameter used.
282 // The second shall be {"value" and shall provide the value associated with that name.
283 // For ASCII data, a string containing the Name and Value attribute pairs shall follow
284 // the weighting function keyword. A semi-colon separates attribute pairs from each
285 // other and within the attribute the name and value are separated by a comma.
287 {"COMPUTATIONAL_PARAMETER", WRITE_PAIR}, // Parameter that is used in computing a value from measured data. Name is the name
288 // of the calculation, parameter is the name of the parameter used in the calculation
289 // and value is the value of the parameter.
291 {"TARGET_TYPE", WRITE_STRINGIFY}, // The type of target being measured, e.g. IT8.7/1, IT8.7/3, user defined, etc.
293 {"COLORANT", WRITE_STRINGIFY}, // Identifies the colorant(s) used in creating the target.
295 {"TABLE_DESCRIPTOR", WRITE_STRINGIFY}, // Describes the purpose or contents of a data table.
297 {"TABLE_NAME", WRITE_STRINGIFY} // Provides a short name for a data table.
300 #define NUMPREDEFINEDPROPS (sizeof(PredefinedProperties)/sizeof(PROPERTY))
303 // Predefined sample types on dataset
304 static const char* PredefinedSampleID[] = {
305 "SAMPLE_ID", // Identifies sample that data represents
306 "STRING", // Identifies label, or other non-machine readable value.
307 // Value must begin and end with a " symbol
309 "CMYK_C", // Cyan component of CMYK data expressed as a percentage
310 "CMYK_M", // Magenta component of CMYK data expressed as a percentage
311 "CMYK_Y", // Yellow component of CMYK data expressed as a percentage
312 "CMYK_K", // Black component of CMYK data expressed as a percentage
313 "D_RED", // Red filter density
314 "D_GREEN", // Green filter density
315 "D_BLUE", // Blue filter density
316 "D_VIS", // Visual filter density
317 "D_MAJOR_FILTER", // Major filter d ensity
318 "RGB_R", // Red component of RGB data
319 "RGB_G", // Green component of RGB data
320 "RGB_B", // Blue com ponent of RGB data
321 "SPECTRAL_NM", // Wavelength of measurement expressed in nanometers
322 "SPECTRAL_PCT", // Percentage reflectance/transmittance
323 "SPECTRAL_DEC", // Reflectance/transmittance
324 "XYZ_X", // X component of tristimulus data
325 "XYZ_Y", // Y component of tristimulus data
326 "XYZ_Z", // Z component of tristimulus data
327 "XYY_X" // x component of chromaticity data
328 "XYY_Y", // y component of chromaticity data
329 "XYY_CAPY", // Y component of tristimulus data
330 "LAB_L", // L* component of Lab data
331 "LAB_A", // a* component of Lab data
332 "LAB_B", // b* component of Lab data
333 "LAB_C", // C*ab component of Lab data
334 "LAB_H", // hab component of Lab data
336 "LAB_DE_94", // CIE dE using CIE 94
337 "LAB_DE_CMC", // dE using CMC
338 "LAB_DE_2000", // CIE dE using CIE DE 2000
339 "MEAN_DE", // Mean Delta E (LAB_DE) of samples compared to batch average
340 // (Used for data files for ANSI IT8.7/1 and IT8.7/2 targets)
341 "STDEV_X", // Standard deviation of X (tristimulus data)
342 "STDEV_Y", // Standard deviation of Y (tristimulus data)
343 "STDEV_Z", // Standard deviation of Z (tristimulus data)
344 "STDEV_L", // Standard deviation of L*
345 "STDEV_A", // Standard deviation of a*
346 "STDEV_B", // Standard deviation of b*
347 "STDEV_DE", // Standard deviation of CIE dE
348 "CHI_SQD_PAR"}; // The average of the standard deviations of L*, a* and b*. It is
349 // used to derive an estimate of the chi-squared parameter which is
350 // recommended as the predictor of the variability of dE
352 #define NUMPREDEFINEDSAMPLEID (sizeof(PredefinedSampleID)/sizeof(char *))
354 //Forward declaration of some internal functions
355 static void* AllocChunk(cmsIT8* it8, cmsUInt32Number size);
357 // Checks if c is a separator
359 cmsBool isseparator(int c)
361 return (c == ' ') || (c == '\t') || (c == '\r');
364 // Checks whatever if c is a valid identifier char
366 cmsBool ismiddle(int c)
368 return (!isseparator(c) && (c != '#') && (c !='\"') && (c != '\'') && (c > 32) && (c < 127));
371 // Checks whatsever if c is a valid identifier middle char.
373 cmsBool isidchar(int c)
375 return isalnum(c) || ismiddle(c);
378 // Checks whatsever if c is a valid identifier first char.
380 cmsBool isfirstidchar(int c)
382 return !isdigit(c) && ismiddle(c);
385 // Guess whether the supplied path looks like an absolute path
387 cmsBool isabsolutepath(const char *path)
396 strncpy(ThreeChars, path, 3);
399 if(ThreeChars[0] == DIR_CHAR)
402 #ifdef CMS_IS_WINDOWS_
403 if (isalpha((int) ThreeChars[0]) && ThreeChars[1] == ':')
411 // Makes a file path based on a given reference path
412 // NOTE: this function doesn't check if the path exists or even if it's legal
414 cmsBool BuildAbsolutePath(const char *relPath, const char *basePath, char *buffer, cmsUInt32Number MaxLen)
420 if (isabsolutepath(relPath)) {
422 strncpy(buffer, relPath, MaxLen);
423 buffer[MaxLen-1] = 0;
427 // No, search for last
428 strncpy(buffer, basePath, MaxLen);
429 buffer[MaxLen-1] = 0;
431 tail = strrchr(buffer, DIR_CHAR);
432 if (tail == NULL) return FALSE; // Is not absolute and has no separators??
434 len = (cmsUInt32Number) (tail - buffer);
435 if (len >= MaxLen) return FALSE;
437 // No need to assure zero terminator over here
438 strncpy(tail + 1, relPath, MaxLen - len);
444 // Make sure no exploit is being even tried
446 const char* NoMeta(const char* str)
448 if (strchr(str, '%') != NULL)
449 return "**** CORRUPTED FORMAT STRING ***";
456 cmsBool SynError(cmsIT8* it8, const char *Txt, ...)
458 char Buffer[256], ErrMsg[1024];
462 vsnprintf(Buffer, 255, Txt, args);
466 snprintf(ErrMsg, 1023, "%s: Line %d, %s", it8->FileStack[it8 ->IncludeSP]->FileName, it8->lineno, Buffer);
469 cmsSignalError(it8 ->ContextID, cmsERROR_CORRUPTION_DETECTED, "%s", ErrMsg);
473 // Check if current symbol is same as specified. issue an error else.
475 cmsBool Check(cmsIT8* it8, SYMBOL sy, const char* Err)
478 return SynError(it8, NoMeta(Err));
482 // Read Next character from stream
484 void NextCh(cmsIT8* it8)
486 if (it8 -> FileStack[it8 ->IncludeSP]->Stream) {
488 it8 ->ch = fgetc(it8 ->FileStack[it8 ->IncludeSP]->Stream);
490 if (feof(it8 -> FileStack[it8 ->IncludeSP]->Stream)) {
492 if (it8 ->IncludeSP > 0) {
494 fclose(it8 ->FileStack[it8->IncludeSP--]->Stream);
495 it8 -> ch = ' '; // Whitespace to be ignored
502 it8->ch = *it8->Source;
503 if (it8->ch) it8->Source++;
508 // Try to see if current identifier is a keyword, if so return the referred symbol
510 SYMBOL BinSrchKey(const char *id)
519 res = cmsstrcasecmp(id, TabKeys[x-1].id);
520 if (res == 0) return TabKeys[x-1].sy;
521 if (res < 0) r = x - 1;
531 cmsFloat64Number xpow10(int n)
533 return pow(10, (cmsFloat64Number) n);
537 // Reads a Real number, tries to follow from integer number
539 void ReadReal(cmsIT8* it8, int inum)
541 it8->dnum = (cmsFloat64Number) inum;
543 while (isdigit(it8->ch)) {
545 it8->dnum = it8->dnum * 10.0 + (it8->ch - '0');
549 if (it8->ch == '.') { // Decimal point
551 cmsFloat64Number frac = 0.0; // fraction
552 int prec = 0; // precision
554 NextCh(it8); // Eats dec. point
556 while (isdigit(it8->ch)) {
558 frac = frac * 10.0 + (it8->ch - '0');
563 it8->dnum = it8->dnum + (frac / xpow10(prec));
566 // Exponent, example 34.00E+20
567 if (toupper(it8->ch) == 'E') {
572 NextCh(it8); sgn = 1;
574 if (it8->ch == '-') {
576 sgn = -1; NextCh(it8);
579 if (it8->ch == '+') {
586 while (isdigit(it8->ch)) {
588 if ((cmsFloat64Number) e * 10L < INT_MAX)
589 e = e * 10 + (it8->ch - '0');
595 it8 -> dnum = it8 -> dnum * xpow10(e);
599 // Parses a float number
600 // This can not call directly atof because it uses locale dependant
601 // parsing, while CCMX files always use . as decimal separator
603 cmsFloat64Number ParseFloatNumber(const char *Buffer)
605 cmsFloat64Number dnum = 0.0;
608 if (*Buffer == '-' || *Buffer == '+') {
610 sign = (*Buffer == '-') ? -1 : 1;
615 while (*Buffer && isdigit((int) *Buffer)) {
617 dnum = dnum * 10.0 + (*Buffer - '0');
618 if (*Buffer) Buffer++;
621 if (*Buffer == '.') {
623 cmsFloat64Number frac = 0.0; // fraction
624 int prec = 0; // precission
626 if (*Buffer) Buffer++;
628 while (*Buffer && isdigit((int) *Buffer)) {
630 frac = frac * 10.0 + (*Buffer - '0');
632 if (*Buffer) Buffer++;
635 dnum = dnum + (frac / xpow10(prec));
638 // Exponent, example 34.00E+20
639 if (*Buffer && toupper(*Buffer) == 'E') {
644 if (*Buffer) Buffer++;
647 if (*Buffer == '-') {
650 if (*Buffer) Buffer++;
653 if (*Buffer == '+') {
656 if (*Buffer) Buffer++;
660 while (*Buffer && isdigit((int) *Buffer)) {
662 if ((cmsFloat64Number) e * 10L < INT_MAX)
663 e = e * 10 + (*Buffer - '0');
665 if (*Buffer) Buffer++;
669 dnum = dnum * xpow10(e);
678 void InSymbol(cmsIT8* it8)
680 register char *idptr;
687 while (isseparator(it8->ch))
690 if (isfirstidchar(it8->ch)) { // Identifier
697 if (++k < MAXID) *idptr++ = (char) it8->ch;
701 } while (isidchar(it8->ch));
706 key = BinSrchKey(it8->id);
707 if (key == SNONE) it8->sy = SIDENT;
712 if (isdigit(it8->ch) || it8->ch == '.' || it8->ch == '-' || it8->ch == '+')
716 if (it8->ch == '-') {
724 if (it8->ch == '0') { // 0xnnnn (Hexa) or 0bnnnn (Binary)
727 if (toupper(it8->ch) == 'X') {
732 while (isxdigit(it8->ch))
734 it8->ch = toupper(it8->ch);
735 if (it8->ch >= 'A' && it8->ch <= 'F') j = it8->ch -'A'+10;
736 else j = it8->ch - '0';
738 if ((long) it8->inum * 16L > (long) INT_MAX)
740 SynError(it8, "Invalid hexadecimal number");
744 it8->inum = it8->inum * 16 + j;
750 if (toupper(it8->ch) == 'B') { // Binary
755 while (it8->ch == '0' || it8->ch == '1')
759 if ((long) it8->inum * 2L > (long) INT_MAX)
761 SynError(it8, "Invalid binary number");
765 it8->inum = it8->inum * 2 + j;
773 while (isdigit(it8->ch)) {
775 if ((long) it8->inum * 10L > (long) INT_MAX) {
776 ReadReal(it8, it8->inum);
782 it8->inum = it8->inum * 10 + (it8->ch - '0');
786 if (it8->ch == '.') {
788 ReadReal(it8, it8->inum);
796 // Special case. Numbers followed by letters are taken as identifiers
798 if (isidchar(it8 ->ch)) {
800 if (it8 ->sy == SINUM) {
802 sprintf(it8->id, "%d", it8->inum);
806 sprintf(it8->id, it8 ->DoubleFormatter, it8->dnum);
809 k = (int) strlen(it8 ->id);
810 idptr = it8 ->id + k;
813 if (++k < MAXID) *idptr++ = (char) it8->ch;
817 } while (isidchar(it8->ch));
826 switch ((int) it8->ch) {
828 // EOF marker -- ignore it
833 // Eof stream markers
850 while (it8->ch && it8->ch != '\n')
864 while (k < MAXSTR && it8->ch != sng) {
866 if (it8->ch == '\n'|| it8->ch == '\r') k = MAXSTR+1;
868 *idptr++ = (char) it8->ch;
881 SynError(it8, "Unrecognized character: 0x%x", it8 ->ch);
885 } while (it8->sy == SCOMMENT);
887 // Handle the include special token
889 if (it8 -> sy == SINCLUDE) {
893 if(it8 -> IncludeSP >= (MAXINCLUDE-1)) {
895 SynError(it8, "Too many recursion levels");
900 if (!Check(it8, SSTRING, "Filename expected")) return;
902 FileNest = it8 -> FileStack[it8 -> IncludeSP + 1];
903 if(FileNest == NULL) {
905 FileNest = it8 ->FileStack[it8 -> IncludeSP + 1] = (FILECTX*)AllocChunk(it8, sizeof(FILECTX));
906 //if(FileNest == NULL)
907 // TODO: how to manage out-of-memory conditions?
910 if (BuildAbsolutePath(it8->str,
911 it8->FileStack[it8->IncludeSP]->FileName,
912 FileNest->FileName, cmsMAX_PATH-1) == FALSE) {
913 SynError(it8, "File path too long");
917 FileNest->Stream = fopen(FileNest->FileName, "rt");
918 if (FileNest->Stream == NULL) {
920 SynError(it8, "File %s not found", FileNest->FileName);
931 // Checks end of line separator
933 cmsBool CheckEOLN(cmsIT8* it8)
935 if (!Check(it8, SEOLN, "Expected separator")) return FALSE;
936 while (it8 -> sy == SEOLN)
945 void Skip(cmsIT8* it8, SYMBOL sy)
947 if (it8->sy == sy && it8->sy != SEOF)
952 // Skip multiple EOLN
954 void SkipEOLN(cmsIT8* it8)
956 while (it8->sy == SEOLN) {
962 // Returns a string holding current value
964 cmsBool GetVal(cmsIT8* it8, char* Buffer, cmsUInt32Number max, const char* ErrorTitle)
968 case SIDENT: strncpy(Buffer, it8->id, max);
971 case SINUM: snprintf(Buffer, max, "%d", it8 -> inum); break;
972 case SDNUM: snprintf(Buffer, max, it8->DoubleFormatter, it8 -> dnum); break;
973 case SSTRING: strncpy(Buffer, it8->str, max);
979 return SynError(it8, "%s", ErrorTitle);
986 // ---------------------------------------------------------- Table
989 TABLE* GetTable(cmsIT8* it8)
991 if ((it8 -> nTable >= it8 ->TablesCount)) {
993 SynError(it8, "Table %d out of sequence", it8 -> nTable);
997 return it8 ->Tab + it8 ->nTable;
1000 // ---------------------------------------------------------- Memory management
1003 // Frees an allocator and owned memory
1004 void CMSEXPORT cmsIT8Free(cmsHANDLE hIT8)
1006 cmsIT8* it8 = (cmsIT8*) hIT8;
1011 if (it8->MemorySink) {
1016 for (p = it8->MemorySink; p != NULL; p = n) {
1019 if (p->Ptr) _cmsFree(it8 ->ContextID, p->Ptr);
1020 _cmsFree(it8 ->ContextID, p);
1024 if (it8->MemoryBlock)
1025 _cmsFree(it8 ->ContextID, it8->MemoryBlock);
1027 _cmsFree(it8 ->ContextID, it8);
1031 // Allocates a chunk of data, keep linked list
1033 void* AllocBigBlock(cmsIT8* it8, cmsUInt32Number size)
1036 void* ptr = _cmsMallocZero(it8->ContextID, size);
1040 ptr1 = (OWNEDMEM*) _cmsMallocZero(it8 ->ContextID, sizeof(OWNEDMEM));
1044 _cmsFree(it8 ->ContextID, ptr);
1049 ptr1-> Next = it8 -> MemorySink;
1050 it8 -> MemorySink = ptr1;
1059 void* AllocChunk(cmsIT8* it8, cmsUInt32Number size)
1061 cmsUInt32Number Free = it8 ->Allocator.BlockSize - it8 ->Allocator.Used;
1062 cmsUInt8Number* ptr;
1064 size = _cmsALIGNMEM(size);
1068 if (it8 -> Allocator.BlockSize == 0)
1070 it8 -> Allocator.BlockSize = 20*1024;
1072 it8 ->Allocator.BlockSize *= 2;
1074 if (it8 ->Allocator.BlockSize < size)
1075 it8 ->Allocator.BlockSize = size;
1077 it8 ->Allocator.Used = 0;
1078 it8 ->Allocator.Block = (cmsUInt8Number*) AllocBigBlock(it8, it8 ->Allocator.BlockSize);
1081 ptr = it8 ->Allocator.Block + it8 ->Allocator.Used;
1082 it8 ->Allocator.Used += size;
1089 // Allocates a string
1091 char *AllocString(cmsIT8* it8, const char* str)
1093 cmsUInt32Number Size = (cmsUInt32Number) strlen(str)+1;
1097 ptr = (char *) AllocChunk(it8, Size);
1098 if (ptr) strncpy (ptr, str, Size-1);
1103 // Searches through linked list
1106 cmsBool IsAvailableOnList(KEYVALUE* p, const char* Key, const char* Subkey, KEYVALUE** LastPtr)
1108 if (LastPtr) *LastPtr = p;
1110 for (; p != NULL; p = p->Next) {
1112 if (LastPtr) *LastPtr = p;
1114 if (*Key != '#') { // Comments are ignored
1116 if (cmsstrcasecmp(Key, p->Keyword) == 0)
1127 for (; p != NULL; p = p->NextSubkey) {
1129 if (LastPtr) *LastPtr = p;
1131 if (cmsstrcasecmp(Subkey, p->Subkey) == 0)
1140 // Add a property into a linked list
1142 KEYVALUE* AddToList(cmsIT8* it8, KEYVALUE** Head, const char *Key, const char *Subkey, const char* xValue, WRITEMODE WriteAs)
1148 // Check if property is already in list
1150 if (IsAvailableOnList(*Head, Key, Subkey, &p)) {
1152 // This may work for editing properties
1154 // return SynError(it8, "duplicate key <%s>", Key);
1160 // Allocate the container
1161 p = (KEYVALUE*) AllocChunk(it8, sizeof(KEYVALUE));
1164 SynError(it8, "AddToList: out of memory");
1168 // Store name and value
1169 p->Keyword = AllocString(it8, Key);
1170 p->Subkey = (Subkey == NULL) ? NULL : AllocString(it8, Subkey);
1172 // Keep the container in our list
1173 if (*Head == NULL) {
1178 if (Subkey != NULL && last != NULL) {
1180 last->NextSubkey = p;
1182 // If Subkey is not null, then last is the last property with the same key,
1183 // but not necessarily is the last property in the list, so we need to move
1184 // to the actual list end
1185 while (last->Next != NULL)
1189 if (last != NULL) last->Next = p;
1193 p->NextSubkey = NULL;
1196 p->WriteAs = WriteAs;
1198 if (xValue != NULL) {
1200 p->Value = AllocString(it8, xValue);
1210 KEYVALUE* AddAvailableProperty(cmsIT8* it8, const char* Key, WRITEMODE as)
1212 return AddToList(it8, &it8->ValidKeywords, Key, NULL, NULL, as);
1217 KEYVALUE* AddAvailableSampleID(cmsIT8* it8, const char* Key)
1219 return AddToList(it8, &it8->ValidSampleID, Key, NULL, NULL, WRITE_UNCOOKED);
1224 void AllocTable(cmsIT8* it8)
1228 t = it8 ->Tab + it8 ->TablesCount;
1230 t->HeaderList = NULL;
1231 t->DataFormat = NULL;
1234 it8 ->TablesCount++;
1238 cmsInt32Number CMSEXPORT cmsIT8SetTable(cmsHANDLE IT8, cmsUInt32Number nTable)
1240 cmsIT8* it8 = (cmsIT8*) IT8;
1242 if (nTable >= it8 ->TablesCount) {
1244 if (nTable == it8 ->TablesCount) {
1249 SynError(it8, "Table %d is out of sequence", nTable);
1254 it8 ->nTable = nTable;
1261 // Init an empty container
1262 cmsHANDLE CMSEXPORT cmsIT8Alloc(cmsContext ContextID)
1267 it8 = (cmsIT8*) _cmsMallocZero(ContextID, sizeof(cmsIT8));
1268 if (it8 == NULL) return NULL;
1272 it8->MemoryBlock = NULL;
1273 it8->MemorySink = NULL;
1277 it8->ContextID = ContextID;
1278 it8->Allocator.Used = 0;
1279 it8->Allocator.Block = NULL;
1280 it8->Allocator.BlockSize = 0;
1282 it8->ValidKeywords = NULL;
1283 it8->ValidSampleID = NULL;
1287 it8 -> Source = NULL;
1291 it8->FileStack[0] = (FILECTX*)AllocChunk(it8, sizeof(FILECTX));
1295 strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT);
1296 cmsIT8SetSheetType((cmsHANDLE) it8, "CGATS.17");
1298 // Initialize predefined properties & data
1300 for (i=0; i < NUMPREDEFINEDPROPS; i++)
1301 AddAvailableProperty(it8, PredefinedProperties[i].id, PredefinedProperties[i].as);
1303 for (i=0; i < NUMPREDEFINEDSAMPLEID; i++)
1304 AddAvailableSampleID(it8, PredefinedSampleID[i]);
1307 return (cmsHANDLE) it8;
1311 const char* CMSEXPORT cmsIT8GetSheetType(cmsHANDLE hIT8)
1313 return GetTable((cmsIT8*) hIT8)->SheetType;
1316 cmsBool CMSEXPORT cmsIT8SetSheetType(cmsHANDLE hIT8, const char* Type)
1318 TABLE* t = GetTable((cmsIT8*) hIT8);
1320 strncpy(t ->SheetType, Type, MAXSTR-1);
1321 t ->SheetType[MAXSTR-1] = 0;
1325 cmsBool CMSEXPORT cmsIT8SetComment(cmsHANDLE hIT8, const char* Val)
1327 cmsIT8* it8 = (cmsIT8*) hIT8;
1329 if (!Val) return FALSE;
1330 if (!*Val) return FALSE;
1332 return AddToList(it8, &GetTable(it8)->HeaderList, "# ", NULL, Val, WRITE_UNCOOKED) != NULL;
1336 cmsBool CMSEXPORT cmsIT8SetPropertyStr(cmsHANDLE hIT8, const char* Key, const char *Val)
1338 cmsIT8* it8 = (cmsIT8*) hIT8;
1340 if (!Val) return FALSE;
1341 if (!*Val) return FALSE;
1343 return AddToList(it8, &GetTable(it8)->HeaderList, Key, NULL, Val, WRITE_STRINGIFY) != NULL;
1346 cmsBool CMSEXPORT cmsIT8SetPropertyDbl(cmsHANDLE hIT8, const char* cProp, cmsFloat64Number Val)
1348 cmsIT8* it8 = (cmsIT8*) hIT8;
1351 sprintf(Buffer, it8->DoubleFormatter, Val);
1353 return AddToList(it8, &GetTable(it8)->HeaderList, cProp, NULL, Buffer, WRITE_UNCOOKED) != NULL;
1356 cmsBool CMSEXPORT cmsIT8SetPropertyHex(cmsHANDLE hIT8, const char* cProp, cmsUInt32Number Val)
1358 cmsIT8* it8 = (cmsIT8*) hIT8;
1361 sprintf(Buffer, "%d", Val);
1363 return AddToList(it8, &GetTable(it8)->HeaderList, cProp, NULL, Buffer, WRITE_HEXADECIMAL) != NULL;
1366 cmsBool CMSEXPORT cmsIT8SetPropertyUncooked(cmsHANDLE hIT8, const char* Key, const char* Buffer)
1368 cmsIT8* it8 = (cmsIT8*) hIT8;
1370 return AddToList(it8, &GetTable(it8)->HeaderList, Key, NULL, Buffer, WRITE_UNCOOKED) != NULL;
1373 cmsBool CMSEXPORT cmsIT8SetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char* SubKey, const char *Buffer)
1375 cmsIT8* it8 = (cmsIT8*) hIT8;
1377 return AddToList(it8, &GetTable(it8)->HeaderList, Key, SubKey, Buffer, WRITE_PAIR) != NULL;
1381 const char* CMSEXPORT cmsIT8GetProperty(cmsHANDLE hIT8, const char* Key)
1383 cmsIT8* it8 = (cmsIT8*) hIT8;
1386 if (IsAvailableOnList(GetTable(it8) -> HeaderList, Key, NULL, &p))
1394 cmsFloat64Number CMSEXPORT cmsIT8GetPropertyDbl(cmsHANDLE hIT8, const char* cProp)
1396 const char *v = cmsIT8GetProperty(hIT8, cProp);
1398 return ParseFloatNumber(v);
1401 const char* CMSEXPORT cmsIT8GetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char *SubKey)
1403 cmsIT8* it8 = (cmsIT8*) hIT8;
1406 if (IsAvailableOnList(GetTable(it8) -> HeaderList, Key, SubKey, &p)) {
1412 // ----------------------------------------------------------------- Datasets
1416 void AllocateDataFormat(cmsIT8* it8)
1418 TABLE* t = GetTable(it8);
1420 if (t -> DataFormat) return; // Already allocated
1422 t -> nSamples = (int) cmsIT8GetPropertyDbl(it8, "NUMBER_OF_FIELDS");
1424 if (t -> nSamples <= 0) {
1426 SynError(it8, "AllocateDataFormat: Unknown NUMBER_OF_FIELDS");
1430 t -> DataFormat = (char**) AllocChunk (it8, (t->nSamples + 1) * sizeof(char *));
1431 if (t->DataFormat == NULL) {
1433 SynError(it8, "AllocateDataFormat: Unable to allocate dataFormat array");
1439 const char *GetDataFormat(cmsIT8* it8, int n)
1441 TABLE* t = GetTable(it8);
1444 return t->DataFormat[n];
1450 cmsBool SetDataFormat(cmsIT8* it8, int n, const char *label)
1452 TABLE* t = GetTable(it8);
1455 AllocateDataFormat(it8);
1457 if (n > t -> nSamples) {
1458 SynError(it8, "More than NUMBER_OF_FIELDS fields.");
1462 if (t->DataFormat) {
1463 t->DataFormat[n] = AllocString(it8, label);
1470 cmsBool CMSEXPORT cmsIT8SetDataFormat(cmsHANDLE h, int n, const char *Sample)
1472 cmsIT8* it8 = (cmsIT8*) h;
1473 return SetDataFormat(it8, n, Sample);
1477 void AllocateDataSet(cmsIT8* it8)
1479 TABLE* t = GetTable(it8);
1481 if (t -> Data) return; // Already allocated
1483 t-> nSamples = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
1484 t-> nPatches = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS"));
1486 t-> Data = (char**)AllocChunk (it8, (t->nSamples + 1) * (t->nPatches + 1) *sizeof (char*));
1487 if (t->Data == NULL) {
1489 SynError(it8, "AllocateDataSet: Unable to allocate data array");
1495 char* GetData(cmsIT8* it8, int nSet, int nField)
1497 TABLE* t = GetTable(it8);
1498 int nSamples = t -> nSamples;
1499 int nPatches = t -> nPatches;
1501 if (nSet >= nPatches || nField >= nSamples)
1504 if (!t->Data) return NULL;
1505 return t->Data [nSet * nSamples + nField];
1509 cmsBool SetData(cmsIT8* it8, int nSet, int nField, const char *Val)
1511 TABLE* t = GetTable(it8);
1514 AllocateDataSet(it8);
1516 if (!t->Data) return FALSE;
1518 if (nSet > t -> nPatches || nSet < 0) {
1520 return SynError(it8, "Patch %d out of range, there are %d patches", nSet, t -> nPatches);
1523 if (nField > t ->nSamples || nField < 0) {
1524 return SynError(it8, "Sample %d out of range, there are %d samples", nField, t ->nSamples);
1528 t->Data [nSet * t -> nSamples + nField] = AllocString(it8, Val);
1533 // --------------------------------------------------------------- File I/O
1536 // Writes a string to file
1538 void WriteStr(SAVESTREAM* f, const char *str)
1540 cmsUInt32Number len;
1546 len = (cmsUInt32Number) strlen(str);
1550 if (f ->stream) { // Should I write it to a file?
1552 if (fwrite(str, 1, len, f->stream) != len) {
1553 cmsSignalError(0, cmsERROR_WRITE, "Write to file error in CGATS parser");
1558 else { // Or to a memory block?
1560 if (f ->Base) { // Am I just counting the bytes?
1562 if (f ->Used > f ->Max) {
1564 cmsSignalError(0, cmsERROR_WRITE, "Write to memory overflows in CGATS parser");
1568 memmove(f ->Ptr, str, len);
1579 void Writef(SAVESTREAM* f, const char* frm, ...)
1584 va_start(args, frm);
1585 vsnprintf(Buffer, 4095, frm, args);
1587 WriteStr(f, Buffer);
1592 // Writes full header
1594 void WriteHeader(cmsIT8* it8, SAVESTREAM* fp)
1597 TABLE* t = GetTable(it8);
1600 WriteStr(fp, t->SheetType);
1603 for (p = t->HeaderList; (p != NULL); p = p->Next)
1605 if (*p ->Keyword == '#') {
1609 WriteStr(fp, "#\n# ");
1610 for (Pt = p ->Value; *Pt; Pt++) {
1613 Writef(fp, "%c", *Pt);
1620 WriteStr(fp, "\n#\n");
1625 if (!IsAvailableOnList(it8-> ValidKeywords, p->Keyword, NULL, NULL)) {
1627 #ifdef CMS_STRICT_CGATS
1628 WriteStr(fp, "KEYWORD\t\"");
1629 WriteStr(fp, p->Keyword);
1630 WriteStr(fp, "\"\n");
1633 AddAvailableProperty(it8, p->Keyword, WRITE_UNCOOKED);
1636 WriteStr(fp, p->Keyword);
1639 switch (p ->WriteAs) {
1641 case WRITE_UNCOOKED:
1642 Writef(fp, "\t%s", p ->Value);
1645 case WRITE_STRINGIFY:
1646 Writef(fp, "\t\"%s\"", p->Value );
1649 case WRITE_HEXADECIMAL:
1650 Writef(fp, "\t0x%X", atoi(p ->Value));
1654 Writef(fp, "\t0x%B", atoi(p ->Value));
1658 Writef(fp, "\t\"%s,%s\"", p->Subkey, p->Value);
1661 default: SynError(it8, "Unknown write mode %d", p ->WriteAs);
1666 WriteStr (fp, "\n");
1672 // Writes the data format
1674 void WriteDataFormat(SAVESTREAM* fp, cmsIT8* it8)
1677 TABLE* t = GetTable(it8);
1679 if (!t -> DataFormat) return;
1681 WriteStr(fp, "BEGIN_DATA_FORMAT\n");
1683 nSamples = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
1685 for (i = 0; i < nSamples; i++) {
1687 WriteStr(fp, t->DataFormat[i]);
1688 WriteStr(fp, ((i == (nSamples-1)) ? "\n" : "\t"));
1691 WriteStr (fp, "END_DATA_FORMAT\n");
1695 // Writes data array
1697 void WriteData(SAVESTREAM* fp, cmsIT8* it8)
1700 TABLE* t = GetTable(it8);
1702 if (!t->Data) return;
1704 WriteStr (fp, "BEGIN_DATA\n");
1706 t->nPatches = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS"));
1708 for (i = 0; i < t-> nPatches; i++) {
1712 for (j = 0; j < t->nSamples; j++) {
1714 char *ptr = t->Data[i*t->nSamples+j];
1716 if (ptr == NULL) WriteStr(fp, "\"\"");
1718 // If value contains whitespace, enclose within quote
1720 if (strchr(ptr, ' ') != NULL) {
1730 WriteStr(fp, ((j == (t->nSamples-1)) ? "\n" : "\t"));
1733 WriteStr (fp, "END_DATA\n");
1739 cmsBool CMSEXPORT cmsIT8SaveToFile(cmsHANDLE hIT8, const char* cFileName)
1743 cmsIT8* it8 = (cmsIT8*) hIT8;
1745 memset(&sd, 0, sizeof(sd));
1747 sd.stream = fopen(cFileName, "wt");
1748 if (!sd.stream) return FALSE;
1750 for (i=0; i < it8 ->TablesCount; i++) {
1752 cmsIT8SetTable(hIT8, i);
1753 WriteHeader(it8, &sd);
1754 WriteDataFormat(&sd, it8);
1755 WriteData(&sd, it8);
1758 if (fclose(sd.stream) != 0) return FALSE;
1765 cmsBool CMSEXPORT cmsIT8SaveToMem(cmsHANDLE hIT8, void *MemPtr, cmsUInt32Number* BytesNeeded)
1769 cmsIT8* it8 = (cmsIT8*) hIT8;
1771 memset(&sd, 0, sizeof(sd));
1774 sd.Base = (cmsUInt8Number*) MemPtr;
1780 sd.Max = *BytesNeeded; // Write to memory?
1782 sd.Max = 0; // Just counting the needed bytes
1784 for (i=0; i < it8 ->TablesCount; i++) {
1786 cmsIT8SetTable(hIT8, i);
1787 WriteHeader(it8, &sd);
1788 WriteDataFormat(&sd, it8);
1789 WriteData(&sd, it8);
1792 sd.Used++; // The \0 at the very end
1797 *BytesNeeded = sd.Used;
1803 // -------------------------------------------------------------- Higer level parsing
1806 cmsBool DataFormatSection(cmsIT8* it8)
1809 TABLE* t = GetTable(it8);
1811 InSymbol(it8); // Eats "BEGIN_DATA_FORMAT"
1814 while (it8->sy != SEND_DATA_FORMAT &&
1817 it8->sy != SSYNERROR) {
1819 if (it8->sy != SIDENT) {
1821 return SynError(it8, "Sample type expected");
1824 if (!SetDataFormat(it8, iField, it8->id)) return FALSE;
1832 Skip(it8, SEND_DATA_FORMAT);
1835 if (iField != t ->nSamples) {
1836 SynError(it8, "Count mismatch. NUMBER_OF_FIELDS was %d, found %d\n", t ->nSamples, iField);
1847 cmsBool DataSection (cmsIT8* it8)
1852 TABLE* t = GetTable(it8);
1854 InSymbol(it8); // Eats "BEGIN_DATA"
1858 AllocateDataSet(it8);
1860 while (it8->sy != SEND_DATA && it8->sy != SEOF)
1862 if (iField >= t -> nSamples) {
1868 if (it8->sy != SEND_DATA && it8->sy != SEOF) {
1870 if (!GetVal(it8, Buffer, 255, "Sample data expected"))
1873 if (!SetData(it8, iSet, iField, Buffer))
1884 Skip(it8, SEND_DATA);
1887 // Check for data completion.
1889 if ((iSet+1) != t -> nPatches)
1890 return SynError(it8, "Count mismatch. NUMBER_OF_SETS was %d, found %d\n", t ->nPatches, iSet+1);
1899 cmsBool HeaderSection(cmsIT8* it8)
1901 char VarName[MAXID];
1902 char Buffer[MAXSTR];
1905 while (it8->sy != SEOF &&
1906 it8->sy != SSYNERROR &&
1907 it8->sy != SBEGIN_DATA_FORMAT &&
1908 it8->sy != SBEGIN_DATA) {
1911 switch (it8 -> sy) {
1915 if (!GetVal(it8, Buffer, MAXSTR-1, "Keyword expected")) return FALSE;
1916 if (!AddAvailableProperty(it8, Buffer, WRITE_UNCOOKED)) return FALSE;
1921 case SDATA_FORMAT_ID:
1923 if (!GetVal(it8, Buffer, MAXSTR-1, "Keyword expected")) return FALSE;
1924 if (!AddAvailableSampleID(it8, Buffer)) return FALSE;
1930 strncpy(VarName, it8->id, MAXID-1);
1931 VarName[MAXID-1] = 0;
1933 if (!IsAvailableOnList(it8-> ValidKeywords, VarName, NULL, &Key)) {
1935 #ifdef CMS_STRICT_CGATS
1936 return SynError(it8, "Undefined keyword '%s'", VarName);
1938 Key = AddAvailableProperty(it8, VarName, WRITE_UNCOOKED);
1939 if (Key == NULL) return FALSE;
1944 if (!GetVal(it8, Buffer, MAXSTR-1, "Property data expected")) return FALSE;
1946 if(Key->WriteAs != WRITE_PAIR) {
1947 AddToList(it8, &GetTable(it8)->HeaderList, VarName, NULL, Buffer,
1948 (it8->sy == SSTRING) ? WRITE_STRINGIFY : WRITE_UNCOOKED);
1953 if (it8->sy != SSTRING)
1954 return SynError(it8, "Invalid value '%s' for property '%s'.", Buffer, VarName);
1956 // chop the string as a list of "subkey, value" pairs, using ';' as a separator
1957 for (Subkey = Buffer; Subkey != NULL; Subkey = Nextkey)
1961 // identify token pair boundary
1962 Nextkey = (char*) strchr(Subkey, ';');
1966 // for each pair, split the subkey and the value
1967 Value = (char*) strrchr(Subkey, ',');
1969 return SynError(it8, "Invalid value for property '%s'.", VarName);
1971 // gobble the spaces before the coma, and the coma itself
1973 do *temp-- = '\0'; while(temp >= Subkey && *temp == ' ');
1975 // gobble any space at the right
1976 temp = Value + strlen(Value) - 1;
1977 while(*temp == ' ') *temp-- = '\0';
1979 // trim the strings from the left
1980 Subkey += strspn(Subkey, " ");
1981 Value += strspn(Value, " ");
1983 if(Subkey[0] == 0 || Value[0] == 0)
1984 return SynError(it8, "Invalid value for property '%s'.", VarName);
1985 AddToList(it8, &GetTable(it8)->HeaderList, VarName, Subkey, Value, WRITE_PAIR);
1996 return SynError(it8, "expected keyword or identifier");
2008 void ReadType(cmsIT8* it8, char* SheetTypePtr)
2010 // First line is a very special case.
2012 while (isseparator(it8->ch))
2015 while (it8->ch != '\r' && it8 ->ch != '\n' && it8->ch != '\t' && it8 -> ch != -1) {
2017 *SheetTypePtr++= (char) it8 ->ch;
2026 cmsBool ParseIT8(cmsIT8* it8, cmsBool nosheet)
2028 char* SheetTypePtr = it8 ->Tab[0].SheetType;
2031 ReadType(it8, SheetTypePtr);
2038 while (it8-> sy != SEOF &&
2039 it8-> sy != SSYNERROR) {
2041 switch (it8 -> sy) {
2043 case SBEGIN_DATA_FORMAT:
2044 if (!DataFormatSection(it8)) return FALSE;
2049 if (!DataSection(it8)) return FALSE;
2051 if (it8 -> sy != SEOF) {
2054 it8 ->nTable = it8 ->TablesCount - 1;
2056 // Read sheet type if present. We only support identifier and string.
2057 // <ident> <eoln> is a type string
2058 // anything else, is not a type string
2061 if (it8 ->sy == SIDENT) {
2063 // May be a type sheet or may be a prop value statement. We cannot use insymbol in
2064 // this special case...
2065 while (isseparator(it8->ch))
2068 // If a newline is found, then this is a type string
2069 if (it8 ->ch == '\n') {
2071 cmsIT8SetSheetType(it8, it8 ->id);
2076 // It is not. Just continue
2077 cmsIT8SetSheetType(it8, "");
2081 // Validate quoted strings
2082 if (it8 ->sy == SSTRING) {
2083 cmsIT8SetSheetType(it8, it8 ->str);
2096 if (!HeaderSection(it8)) return FALSE;
2101 return (it8 -> sy != SSYNERROR);
2106 // Init usefull pointers
2109 void CookPointers(cmsIT8* it8)
2114 cmsUInt32Number nOldTable = it8 ->nTable;
2116 for (j=0; j < it8 ->TablesCount; j++) {
2118 TABLE* t = it8 ->Tab + j;
2123 for (idField = 0; idField < t -> nSamples; idField++)
2125 if (t ->DataFormat == NULL){
2126 SynError(it8, "Undefined DATA_FORMAT");
2130 Fld = t->DataFormat[idField];
2134 if (cmsstrcasecmp(Fld, "SAMPLE_ID") == 0) {
2136 t -> SampleID = idField;
2138 for (i=0; i < t -> nPatches; i++) {
2140 char *Data = GetData(it8, i, idField);
2144 strncpy(Buffer, Data, 255);
2147 if (strlen(Buffer) <= strlen(Data))
2148 strcpy(Data, Buffer);
2150 SetData(it8, i, idField, Buffer);
2157 // "LABEL" is an extension. It keeps references to forward tables
2159 if ((cmsstrcasecmp(Fld, "LABEL") == 0) || Fld[0] == '$' ) {
2161 // Search for table references...
2162 for (i=0; i < t -> nPatches; i++) {
2164 char *Label = GetData(it8, i, idField);
2170 // This is the label, search for a table containing
2173 for (k=0; k < it8 ->TablesCount; k++) {
2175 TABLE* Table = it8 ->Tab + k;
2178 if (IsAvailableOnList(Table->HeaderList, Label, NULL, &p)) {
2180 // Available, keep type and table
2183 char *Type = p ->Value;
2186 snprintf(Buffer, 255, "%s %d %s", Label, nTable, Type );
2188 SetData(it8, i, idField, Buffer);
2203 it8 ->nTable = nOldTable;
2206 // Try to infere if the file is a CGATS/IT8 file at all. Read first line
2207 // that should be something like some printable characters plus a \n
2208 // returns 0 if this is not like a CGATS, or an integer otherwise. This integer is the number of words in first line?
2210 int IsMyBlock(cmsUInt8Number* Buffer, int n)
2212 int words = 1, space = 0, quot = 0;
2215 if (n < 10) return 0; // Too small
2220 for (i = 1; i < n; i++) {
2226 return ((quot == 1) || (words > 2)) ? 0 : words;
2236 if (Buffer[i] < 32) return 0;
2237 if (Buffer[i] > 127) return 0;
2249 cmsBool IsMyFile(const char* FileName)
2252 cmsUInt32Number Size;
2253 cmsUInt8Number Ptr[133];
2255 fp = fopen(FileName, "rt");
2257 cmsSignalError(0, cmsERROR_FILE, "File '%s' not found", FileName);
2261 Size = (cmsUInt32Number) fread(Ptr, 1, 132, fp);
2263 if (fclose(fp) != 0)
2268 return IsMyBlock(Ptr, Size);
2271 // ---------------------------------------------------------- Exported routines
2274 cmsHANDLE CMSEXPORT cmsIT8LoadFromMem(cmsContext ContextID, void *Ptr, cmsUInt32Number len)
2280 _cmsAssert(Ptr != NULL);
2281 _cmsAssert(len != 0);
2283 type = IsMyBlock((cmsUInt8Number*)Ptr, len);
2284 if (type == 0) return NULL;
2286 hIT8 = cmsIT8Alloc(ContextID);
2287 if (!hIT8) return NULL;
2289 it8 = (cmsIT8*) hIT8;
2290 it8 ->MemoryBlock = (char*) _cmsMalloc(ContextID, len + 1);
2292 strncpy(it8 ->MemoryBlock, (const char*) Ptr, len);
2293 it8 ->MemoryBlock[len] = 0;
2295 strncpy(it8->FileStack[0]->FileName, "", cmsMAX_PATH-1);
2296 it8-> Source = it8 -> MemoryBlock;
2298 if (!ParseIT8(it8, type-1)) {
2307 _cmsFree(ContextID, it8->MemoryBlock);
2308 it8 -> MemoryBlock = NULL;
2316 cmsHANDLE CMSEXPORT cmsIT8LoadFromFile(cmsContext ContextID, const char* cFileName)
2323 _cmsAssert(cFileName != NULL);
2325 type = IsMyFile(cFileName);
2326 if (type == 0) return NULL;
2328 hIT8 = cmsIT8Alloc(ContextID);
2329 it8 = (cmsIT8*) hIT8;
2330 if (!hIT8) return NULL;
2333 it8 ->FileStack[0]->Stream = fopen(cFileName, "rt");
2335 if (!it8 ->FileStack[0]->Stream) {
2341 strncpy(it8->FileStack[0]->FileName, cFileName, cmsMAX_PATH-1);
2342 it8->FileStack[0]->FileName[cmsMAX_PATH-1] = 0;
2344 if (!ParseIT8(it8, type-1)) {
2346 fclose(it8 ->FileStack[0]->Stream);
2354 if (fclose(it8 ->FileStack[0]->Stream)!= 0) {
2363 int CMSEXPORT cmsIT8EnumDataFormat(cmsHANDLE hIT8, char ***SampleNames)
2365 cmsIT8* it8 = (cmsIT8*) hIT8;
2368 _cmsAssert(hIT8 != NULL);
2373 *SampleNames = t -> DataFormat;
2374 return t -> nSamples;
2378 cmsUInt32Number CMSEXPORT cmsIT8EnumProperties(cmsHANDLE hIT8, char ***PropertyNames)
2380 cmsIT8* it8 = (cmsIT8*) hIT8;
2386 _cmsAssert(hIT8 != NULL);
2390 // Pass#1 - count properties
2393 for (p = t -> HeaderList; p != NULL; p = p->Next) {
2398 Props = (char **) AllocChunk(it8, sizeof(char *) * n);
2400 // Pass#2 - Fill pointers
2402 for (p = t -> HeaderList; p != NULL; p = p->Next) {
2403 Props[n++] = p -> Keyword;
2406 *PropertyNames = Props;
2410 cmsUInt32Number CMSEXPORT cmsIT8EnumPropertyMulti(cmsHANDLE hIT8, const char* cProp, const char ***SubpropertyNames)
2412 cmsIT8* it8 = (cmsIT8*) hIT8;
2418 _cmsAssert(hIT8 != NULL);
2423 if(!IsAvailableOnList(t->HeaderList, cProp, NULL, &p)) {
2424 *SubpropertyNames = 0;
2428 // Pass#1 - count properties
2431 for (tmp = p; tmp != NULL; tmp = tmp->NextSubkey) {
2432 if(tmp->Subkey != NULL)
2437 Props = (const char **) AllocChunk(it8, sizeof(char *) * n);
2439 // Pass#2 - Fill pointers
2441 for (tmp = p; tmp != NULL; tmp = tmp->NextSubkey) {
2442 if(tmp->Subkey != NULL)
2443 Props[n++] = p ->Subkey;
2446 *SubpropertyNames = Props;
2451 int LocatePatch(cmsIT8* it8, const char* cPatch)
2455 TABLE* t = GetTable(it8);
2457 for (i=0; i < t-> nPatches; i++) {
2459 data = GetData(it8, i, t->SampleID);
2463 if (cmsstrcasecmp(data, cPatch) == 0)
2468 // SynError(it8, "Couldn't find patch '%s'\n", cPatch);
2474 int LocateEmptyPatch(cmsIT8* it8)
2478 TABLE* t = GetTable(it8);
2480 for (i=0; i < t-> nPatches; i++) {
2482 data = GetData(it8, i, t->SampleID);
2493 int LocateSample(cmsIT8* it8, const char* cSample)
2497 TABLE* t = GetTable(it8);
2499 for (i=0; i < t->nSamples; i++) {
2501 fld = GetDataFormat(it8, i);
2502 if (cmsstrcasecmp(fld, cSample) == 0)
2511 int CMSEXPORT cmsIT8FindDataFormat(cmsHANDLE hIT8, const char* cSample)
2513 cmsIT8* it8 = (cmsIT8*) hIT8;
2515 _cmsAssert(hIT8 != NULL);
2517 return LocateSample(it8, cSample);
2522 const char* CMSEXPORT cmsIT8GetDataRowCol(cmsHANDLE hIT8, int row, int col)
2524 cmsIT8* it8 = (cmsIT8*) hIT8;
2526 _cmsAssert(hIT8 != NULL);
2528 return GetData(it8, row, col);
2532 cmsFloat64Number CMSEXPORT cmsIT8GetDataRowColDbl(cmsHANDLE hIT8, int row, int col)
2536 Buffer = cmsIT8GetDataRowCol(hIT8, row, col);
2538 return ParseFloatNumber(Buffer);
2542 cmsBool CMSEXPORT cmsIT8SetDataRowCol(cmsHANDLE hIT8, int row, int col, const char* Val)
2544 cmsIT8* it8 = (cmsIT8*) hIT8;
2546 _cmsAssert(hIT8 != NULL);
2548 return SetData(it8, row, col, Val);
2552 cmsBool CMSEXPORT cmsIT8SetDataRowColDbl(cmsHANDLE hIT8, int row, int col, cmsFloat64Number Val)
2554 cmsIT8* it8 = (cmsIT8*) hIT8;
2557 _cmsAssert(hIT8 != NULL);
2559 sprintf(Buff, it8->DoubleFormatter, Val);
2561 return SetData(it8, row, col, Buff);
2566 const char* CMSEXPORT cmsIT8GetData(cmsHANDLE hIT8, const char* cPatch, const char* cSample)
2568 cmsIT8* it8 = (cmsIT8*) hIT8;
2571 _cmsAssert(hIT8 != NULL);
2573 iField = LocateSample(it8, cSample);
2578 iSet = LocatePatch(it8, cPatch);
2583 return GetData(it8, iSet, iField);
2587 cmsFloat64Number CMSEXPORT cmsIT8GetDataDbl(cmsHANDLE it8, const char* cPatch, const char* cSample)
2591 Buffer = cmsIT8GetData(it8, cPatch, cSample);
2593 return ParseFloatNumber(Buffer);
2598 cmsBool CMSEXPORT cmsIT8SetData(cmsHANDLE hIT8, const char* cPatch, const char* cSample, const char *Val)
2600 cmsIT8* it8 = (cmsIT8*) hIT8;
2604 _cmsAssert(hIT8 != NULL);
2608 iField = LocateSample(it8, cSample);
2613 if (t-> nPatches == 0) {
2615 AllocateDataFormat(it8);
2616 AllocateDataSet(it8);
2620 if (cmsstrcasecmp(cSample, "SAMPLE_ID") == 0) {
2622 iSet = LocateEmptyPatch(it8);
2624 return SynError(it8, "Couldn't add more patches '%s'\n", cPatch);
2627 iField = t -> SampleID;
2630 iSet = LocatePatch(it8, cPatch);
2636 return SetData(it8, iSet, iField, Val);
2640 cmsBool CMSEXPORT cmsIT8SetDataDbl(cmsHANDLE hIT8, const char* cPatch,
2641 const char* cSample,
2642 cmsFloat64Number Val)
2644 cmsIT8* it8 = (cmsIT8*) hIT8;
2647 _cmsAssert(hIT8 != NULL);
2649 snprintf(Buff, 255, it8->DoubleFormatter, Val);
2650 return cmsIT8SetData(hIT8, cPatch, cSample, Buff);
2653 // Buffer should get MAXSTR at least
2655 const char* CMSEXPORT cmsIT8GetPatchName(cmsHANDLE hIT8, int nPatch, char* buffer)
2657 cmsIT8* it8 = (cmsIT8*) hIT8;
2661 _cmsAssert(hIT8 != NULL);
2664 Data = GetData(it8, nPatch, t->SampleID);
2666 if (!Data) return NULL;
2667 if (!buffer) return Data;
2669 strncpy(buffer, Data, MAXSTR-1);
2670 buffer[MAXSTR-1] = 0;
2674 int CMSEXPORT cmsIT8GetPatchByName(cmsHANDLE hIT8, const char *cPatch)
2676 _cmsAssert(hIT8 != NULL);
2678 return LocatePatch((cmsIT8*)hIT8, cPatch);
2681 cmsUInt32Number CMSEXPORT cmsIT8TableCount(cmsHANDLE hIT8)
2683 cmsIT8* it8 = (cmsIT8*) hIT8;
2685 _cmsAssert(hIT8 != NULL);
2687 return it8 ->TablesCount;
2690 // This handles the "LABEL" extension.
2691 // Label, nTable, Type
2693 int CMSEXPORT cmsIT8SetTableByLabel(cmsHANDLE hIT8, const char* cSet, const char* cField, const char* ExpectedType)
2695 const char* cLabelFld;
2696 char Type[256], Label[256];
2699 _cmsAssert(hIT8 != NULL);
2701 if (cField != NULL && *cField == 0)
2707 cLabelFld = cmsIT8GetData(hIT8, cSet, cField);
2708 if (!cLabelFld) return -1;
2710 if (sscanf(cLabelFld, "%255s %d %255s", Label, &nTable, Type) != 3)
2713 if (ExpectedType != NULL && *ExpectedType == 0)
2714 ExpectedType = NULL;
2718 if (cmsstrcasecmp(Type, ExpectedType) != 0) return -1;
2721 return cmsIT8SetTable(hIT8, nTable);
2725 cmsBool CMSEXPORT cmsIT8SetIndexColumn(cmsHANDLE hIT8, const char* cSample)
2727 cmsIT8* it8 = (cmsIT8*) hIT8;
2730 _cmsAssert(hIT8 != NULL);
2732 pos = LocateSample(it8, cSample);
2736 it8->Tab[it8->nTable].SampleID = pos;
2741 void CMSEXPORT cmsIT8DefineDblFormat(cmsHANDLE hIT8, const char* Formatter)
2743 cmsIT8* it8 = (cmsIT8*) hIT8;
2745 _cmsAssert(hIT8 != NULL);
2747 if (Formatter == NULL)
2748 strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT);
2750 strcpy(it8->DoubleFormatter, Formatter);
2752 it8 ->DoubleFormatter[sizeof(it8 ->DoubleFormatter)-1] = 0;