1 //---------------------------------------------------------------------------------
2 //
3 //  Little Color Management System
4 //  Copyright (c) 1998-2020 Marti Maria Saguer
5 //
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:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
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.
23 //
24 //---------------------------------------------------------------------------------
25 //
26 
27 #include "lcms2_internal.h"
28 
29 
30 // IT8.7 / CGATS.17-200x handling -----------------------------------------------------------------------------
31 
32 
33 #define MAXID        128     // Max length of identifier
34 #define MAXSTR      1024     // Max length of string
35 #define MAXTABLES    255     // Max Number of tables in a single stream
36 #define MAXINCLUDE    20     // Max number of nested includes
37 
38 #define DEFAULT_DBL_FORMAT  "%.10g" // Double formatting
39 
40 #ifdef CMS_IS_WINDOWS_
41 #    include <io.h>
42 #    define DIR_CHAR    '\\'
43 #else
44 #    define DIR_CHAR    '/'
45 #endif
46 
47 
48 // Symbols
49 typedef enum {
50 
51         SUNDEFINED,
52         SINUM,      // Integer
53         SDNUM,      // Real
54         SIDENT,     // Identifier
55         SSTRING,    // string
56         SCOMMENT,   // comment
57         SEOLN,      // End of line
58         SEOF,       // End of stream
59         SSYNERROR,  // Syntax error found on stream
60 
61         // Keywords
62 
63         SBEGIN_DATA,
64         SBEGIN_DATA_FORMAT,
65         SEND_DATA,
66         SEND_DATA_FORMAT,
67         SKEYWORD,
68         SDATA_FORMAT_ID,
69         SINCLUDE
70 
71     } SYMBOL;
72 
73 
74 // How to write the value
75 typedef enum {
76 
77         WRITE_UNCOOKED,
78         WRITE_STRINGIFY,
79         WRITE_HEXADECIMAL,
80         WRITE_BINARY,
81         WRITE_PAIR
82 
83     } WRITEMODE;
84 
85 // Linked list of variable names
86 typedef struct _KeyVal {
87 
88         struct _KeyVal*  Next;
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
94 
95    } KEYVALUE;
96 
97 
98 // Linked list of memory chunks (Memory sink)
99 typedef struct _OwnedMem {
100 
101         struct _OwnedMem* Next;
102         void *            Ptr;          // Point to value
103 
104    } OWNEDMEM;
105 
106 // Suballocator
107 typedef struct _SubAllocator {
108 
109          cmsUInt8Number* Block;
110          cmsUInt32Number BlockSize;
111          cmsUInt32Number Used;
112 
113     } SUBALLOCATOR;
114 
115 // Table. Each individual table can hold properties and rows & cols
116 typedef struct _Table {
117 
118         char SheetType[MAXSTR];               // The first row of the IT8 (the type)
119 
120         int            nSamples, nPatches;    // Cols, Rows
121         int            SampleID;              // Pos of ID
122 
123         KEYVALUE*      HeaderList;            // The properties
124 
125         char**         DataFormat;            // The binary stream descriptor
126         char**         Data;                  // The binary stream
127 
128     } TABLE;
129 
130 // File stream being parsed
131 typedef struct _FileContext {
132         char           FileName[cmsMAX_PATH];    // File name if being read from file
133         FILE*          Stream;                   // File stream or NULL if holded in memory
134     } FILECTX;
135 
136 // This struct hold all information about an open IT8 handler.
137 typedef struct {
138 
139 
140         cmsUInt32Number  TablesCount;                     // How many tables in this stream
141         cmsUInt32Number  nTable;                          // The actual table
142 
143         TABLE Tab[MAXTABLES];
144 
145         // Memory management
146         OWNEDMEM*      MemorySink;            // The storage backend
147         SUBALLOCATOR   Allocator;             // String suballocator -- just to keep it fast
148 
149         // Parser state machine
150         SYMBOL             sy;                // Current symbol
151         int                ch;                // Current character
152 
153         cmsInt32Number     inum;              // integer value
154         cmsFloat64Number   dnum;              // real value
155 
156         char           id[MAXID];             // identifier
157         char           str[MAXSTR];           // string
158 
159         // Allowed keywords & datasets. They have visibility on whole stream
160         KEYVALUE*      ValidKeywords;
161         KEYVALUE*      ValidSampleID;
162 
163         char*          Source;                // Points to loc. being parsed
164         cmsInt32Number lineno;                // line counter for error reporting
165 
166         FILECTX*       FileStack[MAXINCLUDE]; // Stack of files being parsed
167         cmsInt32Number IncludeSP;             // Include Stack Pointer
168 
169         char*          MemoryBlock;           // The stream if holded in memory
170 
171         char           DoubleFormatter[MAXID];// Printf-like 'cmsFloat64Number' formatter
172 
173         cmsContext    ContextID;              // The threading context
174 
175    } cmsIT8;
176 
177 
178 // The stream for save operations
179 typedef struct {
180 
181         FILE* stream;   // For save-to-file behaviour
182 
183         cmsUInt8Number* Base;
184         cmsUInt8Number* Ptr;        // For save-to-mem behaviour
185         cmsUInt32Number Used;
186         cmsUInt32Number Max;
187 
188     } SAVESTREAM;
189 
190 
191 // ------------------------------------------------------ cmsIT8 parsing routines
192 
193 
194 // A keyword
195 typedef struct {
196 
197         const char *id;
198         SYMBOL sy;
199 
200    } KEYWORD;
201 
202 // The keyword->symbol translation table. Sorting is required.
203 static const KEYWORD TabKeys[] = {
204 
205         {"$INCLUDE",               SINCLUDE},   // This is an extension!
206         {".INCLUDE",               SINCLUDE},   // This is an extension!
207 
208         {"BEGIN_DATA",             SBEGIN_DATA },
209         {"BEGIN_DATA_FORMAT",      SBEGIN_DATA_FORMAT },
210         {"DATA_FORMAT_IDENTIFIER", SDATA_FORMAT_ID},
211         {"END_DATA",               SEND_DATA},
212         {"END_DATA_FORMAT",        SEND_DATA_FORMAT},
213         {"KEYWORD",                SKEYWORD}
214         };
215 
216 #define NUMKEYS (sizeof(TabKeys)/sizeof(KEYWORD))
217 
218 // Predefined properties
219 
220 // A property
221 typedef struct {
222         const char *id;    // The identifier
223         WRITEMODE as;      // How is supposed to be written
224     } PROPERTY;
225 
226 static PROPERTY PredefinedProperties[] = {
227 
228         {"NUMBER_OF_FIELDS", WRITE_UNCOOKED},    // Required - NUMBER OF FIELDS
229         {"NUMBER_OF_SETS",   WRITE_UNCOOKED},    // Required - NUMBER OF SETS
230         {"ORIGINATOR",       WRITE_STRINGIFY},   // Required - Identifies the specific system, organization or individual that created the data file.
231         {"FILE_DESCRIPTOR",  WRITE_STRINGIFY},   // Required - Describes the purpose or contents of the data file.
232         {"CREATED",          WRITE_STRINGIFY},   // Required - Indicates date of creation of the data file.
233         {"DESCRIPTOR",       WRITE_STRINGIFY},   // Required  - Describes the purpose or contents of the data file.
234         {"DIFFUSE_GEOMETRY", WRITE_STRINGIFY},   // The diffuse geometry used. Allowed values are "sphere" or "opal".
235         {"MANUFACTURER",     WRITE_STRINGIFY},
236         {"MANUFACTURE",      WRITE_STRINGIFY},   // Some broken Fuji targets does store this value
237         {"PROD_DATE",        WRITE_STRINGIFY},   // Identifies year and month of production of the target in the form yyyy:mm.
238         {"SERIAL",           WRITE_STRINGIFY},   // Uniquely identifies individual physical target.
239 
240         {"MATERIAL",         WRITE_STRINGIFY},    // Identifies the material on which the target was produced using a code
241                                                   // uniquely identifying th e material. This is intend ed to be used for IT8.7
242                                                   // physical targets only (i.e . IT8.7/1 a nd IT8.7/2).
243 
244         {"INSTRUMENTATION",  WRITE_STRINGIFY},    // Used to report the specific instrumentation used (manufacturer and
245                                                   // model number) to generate the data reported. This data will often
246                                                   // provide more information about the particular data collected than an
247                                                   // extensive list of specific details. This is particularly important for
248                                                   // spectral data or data derived from spectrophotometry.
249 
250         {"MEASUREMENT_SOURCE", WRITE_STRINGIFY},  // Illumination used for spectral measurements. This data helps provide
251                                                   // a guide to the potential for issues of paper fluorescence, etc.
252 
253         {"PRINT_CONDITIONS", WRITE_STRINGIFY},     // Used to define the characteristics of the printed sheet being reported.
254                                                    // Where standard conditions have been defined (e.g., SWOP at nominal)
255                                                    // named conditions may suffice. Otherwise, detailed information is
256                                                    // needed.
257 
258         {"SAMPLE_BACKING",   WRITE_STRINGIFY},     // Identifies the backing material used behind the sample during
259                                                    // measurement. Allowed values are "black", "white", or {"na".
260 
261         {"CHISQ_DOF",        WRITE_STRINGIFY},     // Degrees of freedom associated with the Chi squared statistic
262                                                    // below properties are new in recent specs:
263 
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.
269 
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.
272 
273        {"POLARIZATION",      WRITE_STRINGIFY},     // Identifies the use of a physical polarization filter during measurement. Allowed
274                                                    // values are {"yes", "white", "none" or "na".
275 
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.
286 
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.
290 
291        {"TARGET_TYPE",        WRITE_STRINGIFY},    // The type of target being measured, e.g. IT8.7/1, IT8.7/3, user defined, etc.
292 
293        {"COLORANT",           WRITE_STRINGIFY},    // Identifies the colorant(s) used in creating the target.
294 
295        {"TABLE_DESCRIPTOR",   WRITE_STRINGIFY},    // Describes the purpose or contents of a data table.
296 
297        {"TABLE_NAME",         WRITE_STRINGIFY}     // Provides a short name for a data table.
298 };
299 
300 #define NUMPREDEFINEDPROPS (sizeof(PredefinedProperties)/sizeof(PROPERTY))
301 
302 
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
308 
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
335         "LAB_DE",         // CIE dE
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
351 
352 #define NUMPREDEFINEDSAMPLEID (sizeof(PredefinedSampleID)/sizeof(char *))
353 
354 //Forward declaration of some internal functions
355 static void* AllocChunk(cmsIT8* it8, cmsUInt32Number size);
356 
357 // Checks whatever c is a separator
358 static
isseparator(int c)359 cmsBool isseparator(int c)
360 {
361     return (c == ' ') || (c == '\t') ;
362 }
363 
364 // Checks whatever c is a valid identifier char
365 static
ismiddle(int c)366 cmsBool ismiddle(int c)
367 {
368    return (!isseparator(c) && (c != '#') && (c !='\"') && (c != '\'') && (c > 32) && (c < 127));
369 }
370 
371 // Checks whatsever c is a valid identifier middle char.
372 static
isidchar(int c)373 cmsBool isidchar(int c)
374 {
375    return isalnum(c) || ismiddle(c);
376 }
377 
378 // Checks whatsever c is a valid identifier first char.
379 static
isfirstidchar(int c)380 cmsBool isfirstidchar(int c)
381 {
382      return !isdigit(c) && ismiddle(c);
383 }
384 
385 // Guess whether the supplied path looks like an absolute path
386 static
isabsolutepath(const char * path)387 cmsBool isabsolutepath(const char *path)
388 {
389     char ThreeChars[4];
390 
391     if(path == NULL)
392         return FALSE;
393     if (path[0] == 0)
394         return FALSE;
395 
396     strncpy(ThreeChars, path, 3);
397     ThreeChars[3] = 0;
398 
399     if(ThreeChars[0] == DIR_CHAR)
400         return TRUE;
401 
402 #ifdef  CMS_IS_WINDOWS_
403     if (isalpha((int) ThreeChars[0]) && ThreeChars[1] == ':')
404         return TRUE;
405 #endif
406     return FALSE;
407 }
408 
409 
410 // Makes a file path based on a given reference path
411 // NOTE: this function doesn't check if the path exists or even if it's legal
412 static
BuildAbsolutePath(const char * relPath,const char * basePath,char * buffer,cmsUInt32Number MaxLen)413 cmsBool BuildAbsolutePath(const char *relPath, const char *basePath, char *buffer, cmsUInt32Number MaxLen)
414 {
415     char *tail;
416     cmsUInt32Number len;
417 
418     // Already absolute?
419     if (isabsolutepath(relPath)) {
420 
421         strncpy(buffer, relPath, MaxLen);
422         buffer[MaxLen-1] = 0;
423         return TRUE;
424     }
425 
426     // No, search for last
427     strncpy(buffer, basePath, MaxLen);
428     buffer[MaxLen-1] = 0;
429 
430     tail = strrchr(buffer, DIR_CHAR);
431     if (tail == NULL) return FALSE;    // Is not absolute and has no separators??
432 
433     len = (cmsUInt32Number) (tail - buffer);
434     if (len >= MaxLen) return FALSE;
435 
436     // No need to assure zero terminator over here
437     strncpy(tail + 1, relPath, MaxLen - len);
438 
439     return TRUE;
440 }
441 
442 
443 // Make sure no exploit is being even tried
444 static
NoMeta(const char * str)445 const char* NoMeta(const char* str)
446 {
447     if (strchr(str, '%') != NULL)
448         return "**** CORRUPTED FORMAT STRING ***";
449 
450     return str;
451 }
452 
453 // Syntax error
454 static
SynError(cmsIT8 * it8,const char * Txt,...)455 cmsBool SynError(cmsIT8* it8, const char *Txt, ...)
456 {
457     char Buffer[256], ErrMsg[1024];
458     va_list args;
459 
460     va_start(args, Txt);
461     vsnprintf(Buffer, 255, Txt, args);
462     Buffer[255] = 0;
463     va_end(args);
464 
465     snprintf(ErrMsg, 1023, "%s: Line %d, %s", it8->FileStack[it8 ->IncludeSP]->FileName, it8->lineno, Buffer);
466     ErrMsg[1023] = 0;
467     it8->sy = SSYNERROR;
468     cmsSignalError(it8 ->ContextID, cmsERROR_CORRUPTION_DETECTED, "%s", ErrMsg);
469     return FALSE;
470 }
471 
472 // Check if current symbol is same as specified. issue an error else.
473 static
Check(cmsIT8 * it8,SYMBOL sy,const char * Err)474 cmsBool Check(cmsIT8* it8, SYMBOL sy, const char* Err)
475 {
476         if (it8 -> sy != sy)
477                 return SynError(it8, NoMeta(Err));
478         return TRUE;
479 }
480 
481 // Read Next character from stream
482 static
NextCh(cmsIT8 * it8)483 void NextCh(cmsIT8* it8)
484 {
485     if (it8 -> FileStack[it8 ->IncludeSP]->Stream) {
486 
487         it8 ->ch = fgetc(it8 ->FileStack[it8 ->IncludeSP]->Stream);
488 
489         if (feof(it8 -> FileStack[it8 ->IncludeSP]->Stream))  {
490 
491             if (it8 ->IncludeSP > 0) {
492 
493                 fclose(it8 ->FileStack[it8->IncludeSP--]->Stream);
494                 it8 -> ch = ' ';                            // Whitespace to be ignored
495 
496             } else
497                 it8 ->ch = 0;   // EOF
498         }
499     }
500     else {
501         it8->ch = *it8->Source;
502         if (it8->ch) it8->Source++;
503     }
504 }
505 
506 
507 // Try to see if current identifier is a keyword, if so return the referred symbol
508 static
BinSrchKey(const char * id)509 SYMBOL BinSrchKey(const char *id)
510 {
511     int l = 1;
512     int r = NUMKEYS;
513     int x, res;
514 
515     while (r >= l)
516     {
517         x = (l+r)/2;
518         res = cmsstrcasecmp(id, TabKeys[x-1].id);
519         if (res == 0) return TabKeys[x-1].sy;
520         if (res < 0) r = x - 1;
521         else l = x + 1;
522     }
523 
524     return SUNDEFINED;
525 }
526 
527 
528 // 10 ^n
529 static
xpow10(int n)530 cmsFloat64Number xpow10(int n)
531 {
532     return pow(10, (cmsFloat64Number) n);
533 }
534 
535 
536 //  Reads a Real number, tries to follow from integer number
537 static
ReadReal(cmsIT8 * it8,cmsInt32Number inum)538 void ReadReal(cmsIT8* it8, cmsInt32Number inum)
539 {
540     it8->dnum = (cmsFloat64Number)inum;
541 
542     while (isdigit(it8->ch)) {
543 
544         it8->dnum = (cmsFloat64Number)it8->dnum * 10.0 + (cmsFloat64Number)(it8->ch - '0');
545         NextCh(it8);
546     }
547 
548     if (it8->ch == '.') {        // Decimal point
549 
550         cmsFloat64Number frac = 0.0;      // fraction
551         int prec = 0;                     // precision
552 
553         NextCh(it8);               // Eats dec. point
554 
555         while (isdigit(it8->ch)) {
556 
557             frac = frac * 10.0 + (cmsFloat64Number)(it8->ch - '0');
558             prec++;
559             NextCh(it8);
560         }
561 
562         it8->dnum = it8->dnum + (frac / xpow10(prec));
563     }
564 
565     // Exponent, example 34.00E+20
566     if (toupper(it8->ch) == 'E') {
567 
568         cmsInt32Number e;
569         cmsInt32Number sgn;
570 
571         NextCh(it8); sgn = 1;
572 
573         if (it8->ch == '-') {
574 
575             sgn = -1; NextCh(it8);
576         }
577         else
578             if (it8->ch == '+') {
579 
580                 sgn = +1;
581                 NextCh(it8);
582             }
583 
584         e = 0;
585         while (isdigit(it8->ch)) {
586 
587             cmsInt32Number digit = (it8->ch - '0');
588 
589             if ((cmsFloat64Number)e * 10.0 + (cmsFloat64Number)digit < (cmsFloat64Number)+2147483647.0)
590                 e = e * 10 + digit;
591 
592             NextCh(it8);
593         }
594 
595         e = sgn*e;
596         it8->dnum = it8->dnum * xpow10(e);
597     }
598 }
599 
600 // Parses a float number
601 // This can not call directly atof because it uses locale dependent
602 // parsing, while CCMX files always use . as decimal separator
603 static
ParseFloatNumber(const char * Buffer)604 cmsFloat64Number ParseFloatNumber(const char *Buffer)
605 {
606     cmsFloat64Number dnum = 0.0;
607     int sign = 1;
608 
609     // keep safe
610     if (Buffer == NULL) return 0.0;
611 
612     if (*Buffer == '-' || *Buffer == '+') {
613 
614         sign = (*Buffer == '-') ? -1 : 1;
615         Buffer++;
616     }
617 
618 
619     while (*Buffer && isdigit((int)*Buffer)) {
620 
621         dnum = dnum * 10.0 + (*Buffer - '0');
622         if (*Buffer) Buffer++;
623     }
624 
625     if (*Buffer == '.') {
626 
627         cmsFloat64Number frac = 0.0;      // fraction
628         int prec = 0;                     // precision
629 
630         if (*Buffer) Buffer++;
631 
632         while (*Buffer && isdigit((int)*Buffer)) {
633 
634             frac = frac * 10.0 + (*Buffer - '0');
635             prec++;
636             if (*Buffer) Buffer++;
637         }
638 
639         dnum = dnum + (frac / xpow10(prec));
640     }
641 
642     // Exponent, example 34.00E+20
643     if (*Buffer && toupper(*Buffer) == 'E') {
644 
645         int e;
646         int sgn;
647 
648         if (*Buffer) Buffer++;
649         sgn = 1;
650 
651         if (*Buffer == '-') {
652 
653             sgn = -1;
654             if (*Buffer) Buffer++;
655         }
656         else
657             if (*Buffer == '+') {
658 
659                 sgn = +1;
660                 if (*Buffer) Buffer++;
661             }
662 
663         e = 0;
664         while (*Buffer && isdigit((int)*Buffer)) {
665 
666             cmsInt32Number digit = (*Buffer - '0');
667 
668             if ((cmsFloat64Number)e * 10.0 + digit < (cmsFloat64Number)+2147483647.0)
669                 e = e * 10 + digit;
670 
671             if (*Buffer) Buffer++;
672         }
673 
674         e = sgn*e;
675         dnum = dnum * xpow10(e);
676     }
677 
678     return sign * dnum;
679 }
680 
681 
682 // Reads next symbol
683 static
InSymbol(cmsIT8 * it8)684 void InSymbol(cmsIT8* it8)
685 {
686     CMSREGISTER char *idptr;
687     CMSREGISTER int k;
688     SYMBOL key;
689     int sng;
690 
691     do {
692 
693         while (isseparator(it8->ch))
694             NextCh(it8);
695 
696         if (isfirstidchar(it8->ch)) {          // Identifier
697 
698             k = 0;
699             idptr = it8->id;
700 
701             do {
702 
703                 if (++k < MAXID) *idptr++ = (char) it8->ch;
704 
705                 NextCh(it8);
706 
707             } while (isidchar(it8->ch));
708 
709             *idptr = '\0';
710 
711 
712             key = BinSrchKey(it8->id);
713             if (key == SUNDEFINED) it8->sy = SIDENT;
714             else it8->sy = key;
715 
716         }
717         else                         // Is a number?
718             if (isdigit(it8->ch) || it8->ch == '.' || it8->ch == '-' || it8->ch == '+')
719             {
720                 int sign = 1;
721 
722                 if (it8->ch == '-') {
723                     sign = -1;
724                     NextCh(it8);
725                 }
726 
727                 it8->inum = 0;
728                 it8->sy   = SINUM;
729 
730                 if (it8->ch == '0') {          // 0xnnnn (Hexa) or 0bnnnn (Binary)
731 
732                     NextCh(it8);
733                     if (toupper(it8->ch) == 'X') {
734 
735                         int j;
736 
737                         NextCh(it8);
738                         while (isxdigit(it8->ch))
739                         {
740                             it8->ch = toupper(it8->ch);
741                             if (it8->ch >= 'A' && it8->ch <= 'F')  j = it8->ch -'A'+10;
742                             else j = it8->ch - '0';
743 
744                             if ((cmsFloat64Number) it8->inum * 16.0 + (cmsFloat64Number) j > (cmsFloat64Number)+2147483647.0)
745                             {
746                                 SynError(it8, "Invalid hexadecimal number");
747                                 return;
748                             }
749 
750                             it8->inum = it8->inum * 16 + j;
751                             NextCh(it8);
752                         }
753                         return;
754                     }
755 
756                     if (toupper(it8->ch) == 'B') {  // Binary
757 
758                         int j;
759 
760                         NextCh(it8);
761                         while (it8->ch == '0' || it8->ch == '1')
762                         {
763                             j = it8->ch - '0';
764 
765                             if ((cmsFloat64Number) it8->inum * 2.0 + j > (cmsFloat64Number)+2147483647.0)
766                             {
767                                 SynError(it8, "Invalid binary number");
768                                 return;
769                             }
770 
771                             it8->inum = it8->inum * 2 + j;
772                             NextCh(it8);
773                         }
774                         return;
775                     }
776                 }
777 
778 
779                 while (isdigit(it8->ch)) {
780 
781                     cmsInt32Number digit = (it8->ch - '0');
782 
783                     if ((cmsFloat64Number) it8->inum * 10.0 + (cmsFloat64Number) digit > (cmsFloat64Number) +2147483647.0) {
784                         ReadReal(it8, it8->inum);
785                         it8->sy = SDNUM;
786                         it8->dnum *= sign;
787                         return;
788                     }
789 
790                     it8->inum = it8->inum * 10 + digit;
791                     NextCh(it8);
792                 }
793 
794                 if (it8->ch == '.') {
795 
796                     ReadReal(it8, it8->inum);
797                     it8->sy = SDNUM;
798                     it8->dnum *= sign;
799                     return;
800                 }
801 
802                 it8 -> inum *= sign;
803 
804                 // Special case. Numbers followed by letters are taken as identifiers
805 
806                 if (isidchar(it8 ->ch)) {
807 
808                     if (it8 ->sy == SINUM) {
809 
810                         snprintf(it8->id, 127, "%d", it8->inum);
811                     }
812                     else {
813 
814                         snprintf(it8->id, 127, it8 ->DoubleFormatter, it8->dnum);
815                     }
816 
817                     k = (int) strlen(it8 ->id);
818                     idptr = it8 ->id + k;
819                     do {
820 
821                         if (++k < MAXID) *idptr++ = (char) it8->ch;
822 
823                         NextCh(it8);
824 
825                     } while (isidchar(it8->ch));
826 
827                     *idptr = '\0';
828                     it8->sy = SIDENT;
829                 }
830                 return;
831 
832             }
833             else
834                 switch ((int) it8->ch) {
835 
836         // EOF marker -- ignore it
837         case '\x1a':
838             NextCh(it8);
839             break;
840 
841         // Eof stream markers
842         case 0:
843         case -1:
844             it8->sy = SEOF;
845             break;
846 
847 
848         // Next line
849         case '\r':
850             NextCh(it8);
851             if (it8 ->ch == '\n')
852                 NextCh(it8);
853             it8->sy = SEOLN;
854             it8->lineno++;
855             break;
856 
857         case '\n':
858             NextCh(it8);
859             it8->sy = SEOLN;
860             it8->lineno++;
861             break;
862 
863         // Comment
864         case '#':
865             NextCh(it8);
866             while (it8->ch && it8->ch != '\n' && it8->ch != '\r')
867                 NextCh(it8);
868 
869             it8->sy = SCOMMENT;
870             break;
871 
872         // String.
873         case '\'':
874         case '\"':
875             idptr = it8->str;
876             sng = it8->ch;
877             k = 0;
878             NextCh(it8);
879 
880             while (k < (MAXSTR-1) && it8->ch != sng) {
881 
882                 if (it8->ch == '\n'|| it8->ch == '\r') k = MAXSTR+1;
883                 else {
884                     *idptr++ = (char) it8->ch;
885                     NextCh(it8);
886                     k++;
887                 }
888             }
889 
890             it8->sy = SSTRING;
891             *idptr = '\0';
892             NextCh(it8);
893             break;
894 
895 
896         default:
897             SynError(it8, "Unrecognized character: 0x%x", it8 ->ch);
898             return;
899             }
900 
901     } while (it8->sy == SCOMMENT);
902 
903     // Handle the include special token
904 
905     if (it8 -> sy == SINCLUDE) {
906 
907                 FILECTX* FileNest;
908 
909                 if(it8 -> IncludeSP >= (MAXINCLUDE-1)) {
910 
911                     SynError(it8, "Too many recursion levels");
912                     return;
913                 }
914 
915                 InSymbol(it8);
916                 if (!Check(it8, SSTRING, "Filename expected")) return;
917 
918                 FileNest = it8 -> FileStack[it8 -> IncludeSP + 1];
919                 if(FileNest == NULL) {
920 
921                     FileNest = it8 ->FileStack[it8 -> IncludeSP + 1] = (FILECTX*)AllocChunk(it8, sizeof(FILECTX));
922                     //if(FileNest == NULL)
923                     //  TODO: how to manage out-of-memory conditions?
924                 }
925 
926                 if (BuildAbsolutePath(it8->str,
927                                       it8->FileStack[it8->IncludeSP]->FileName,
928                                       FileNest->FileName, cmsMAX_PATH-1) == FALSE) {
929                     SynError(it8, "File path too long");
930                     return;
931                 }
932 
933                 FileNest->Stream = fopen(FileNest->FileName, "rt");
934                 if (FileNest->Stream == NULL) {
935 
936                         SynError(it8, "File %s not found", FileNest->FileName);
937                         return;
938                 }
939                 it8->IncludeSP++;
940 
941                 it8 ->ch = ' ';
942                 InSymbol(it8);
943     }
944 
945 }
946 
947 // Checks end of line separator
948 static
CheckEOLN(cmsIT8 * it8)949 cmsBool CheckEOLN(cmsIT8* it8)
950 {
951         if (!Check(it8, SEOLN, "Expected separator")) return FALSE;
952         while (it8 -> sy == SEOLN)
953                         InSymbol(it8);
954         return TRUE;
955 
956 }
957 
958 // Skip a symbol
959 
960 static
Skip(cmsIT8 * it8,SYMBOL sy)961 void Skip(cmsIT8* it8, SYMBOL sy)
962 {
963         if (it8->sy == sy && it8->sy != SEOF)
964                         InSymbol(it8);
965 }
966 
967 
968 // Skip multiple EOLN
969 static
SkipEOLN(cmsIT8 * it8)970 void SkipEOLN(cmsIT8* it8)
971 {
972     while (it8->sy == SEOLN) {
973              InSymbol(it8);
974     }
975 }
976 
977 
978 // Returns a string holding current value
979 static
GetVal(cmsIT8 * it8,char * Buffer,cmsUInt32Number max,const char * ErrorTitle)980 cmsBool GetVal(cmsIT8* it8, char* Buffer, cmsUInt32Number max, const char* ErrorTitle)
981 {
982     switch (it8->sy) {
983 
984     case SEOLN:   // Empty value
985                   Buffer[0]=0;
986                   break;
987     case SIDENT:  strncpy(Buffer, it8->id, max);
988                   Buffer[max-1]=0;
989                   break;
990     case SINUM:   snprintf(Buffer, max, "%d", it8 -> inum); break;
991     case SDNUM:   snprintf(Buffer, max, it8->DoubleFormatter, it8 -> dnum); break;
992     case SSTRING: strncpy(Buffer, it8->str, max);
993                   Buffer[max-1] = 0;
994                   break;
995 
996 
997     default:
998          return SynError(it8, "%s", ErrorTitle);
999     }
1000 
1001     Buffer[max] = 0;
1002     return TRUE;
1003 }
1004 
1005 // ---------------------------------------------------------- Table
1006 
1007 static
GetTable(cmsIT8 * it8)1008 TABLE* GetTable(cmsIT8* it8)
1009 {
1010    if ((it8 -> nTable >= it8 ->TablesCount)) {
1011 
1012            SynError(it8, "Table %d out of sequence", it8 -> nTable);
1013            return it8 -> Tab;
1014    }
1015 
1016    return it8 ->Tab + it8 ->nTable;
1017 }
1018 
1019 // ---------------------------------------------------------- Memory management
1020 
1021 
1022 // Frees an allocator and owned memory
cmsIT8Free(cmsHANDLE hIT8)1023 void CMSEXPORT cmsIT8Free(cmsHANDLE hIT8)
1024 {
1025    cmsIT8* it8 = (cmsIT8*) hIT8;
1026 
1027     if (it8 == NULL)
1028         return;
1029 
1030     if (it8->MemorySink) {
1031 
1032         OWNEDMEM* p;
1033         OWNEDMEM* n;
1034 
1035         for (p = it8->MemorySink; p != NULL; p = n) {
1036 
1037             n = p->Next;
1038             if (p->Ptr) _cmsFree(it8 ->ContextID, p->Ptr);
1039             _cmsFree(it8 ->ContextID, p);
1040         }
1041     }
1042 
1043     if (it8->MemoryBlock)
1044         _cmsFree(it8 ->ContextID, it8->MemoryBlock);
1045 
1046     _cmsFree(it8 ->ContextID, it8);
1047 }
1048 
1049 
1050 // Allocates a chunk of data, keep linked list
1051 static
AllocBigBlock(cmsIT8 * it8,cmsUInt32Number size)1052 void* AllocBigBlock(cmsIT8* it8, cmsUInt32Number size)
1053 {
1054     OWNEDMEM* ptr1;
1055     void* ptr = _cmsMallocZero(it8->ContextID, size);
1056 
1057     if (ptr != NULL) {
1058 
1059         ptr1 = (OWNEDMEM*) _cmsMallocZero(it8 ->ContextID, sizeof(OWNEDMEM));
1060 
1061         if (ptr1 == NULL) {
1062 
1063             _cmsFree(it8 ->ContextID, ptr);
1064             return NULL;
1065         }
1066 
1067         ptr1-> Ptr        = ptr;
1068         ptr1-> Next       = it8 -> MemorySink;
1069         it8 -> MemorySink = ptr1;
1070     }
1071 
1072     return ptr;
1073 }
1074 
1075 
1076 // Suballocator.
1077 static
AllocChunk(cmsIT8 * it8,cmsUInt32Number size)1078 void* AllocChunk(cmsIT8* it8, cmsUInt32Number size)
1079 {
1080     cmsUInt32Number Free = it8 ->Allocator.BlockSize - it8 ->Allocator.Used;
1081     cmsUInt8Number* ptr;
1082 
1083     size = _cmsALIGNMEM(size);
1084 
1085     if (size > Free) {
1086 
1087         if (it8 -> Allocator.BlockSize == 0)
1088 
1089                 it8 -> Allocator.BlockSize = 20*1024;
1090         else
1091                 it8 ->Allocator.BlockSize *= 2;
1092 
1093         if (it8 ->Allocator.BlockSize < size)
1094                 it8 ->Allocator.BlockSize = size;
1095 
1096         it8 ->Allocator.Used = 0;
1097         it8 ->Allocator.Block = (cmsUInt8Number*)  AllocBigBlock(it8, it8 ->Allocator.BlockSize);
1098     }
1099 
1100     ptr = it8 ->Allocator.Block + it8 ->Allocator.Used;
1101     it8 ->Allocator.Used += size;
1102 
1103     return (void*) ptr;
1104 
1105 }
1106 
1107 
1108 // Allocates a string
1109 static
AllocString(cmsIT8 * it8,const char * str)1110 char *AllocString(cmsIT8* it8, const char* str)
1111 {
1112     cmsUInt32Number Size = (cmsUInt32Number) strlen(str)+1;
1113     char *ptr;
1114 
1115 
1116     ptr = (char *) AllocChunk(it8, Size);
1117     if (ptr) strncpy (ptr, str, Size-1);
1118 
1119     return ptr;
1120 }
1121 
1122 // Searches through linked list
1123 
1124 static
IsAvailableOnList(KEYVALUE * p,const char * Key,const char * Subkey,KEYVALUE ** LastPtr)1125 cmsBool IsAvailableOnList(KEYVALUE* p, const char* Key, const char* Subkey, KEYVALUE** LastPtr)
1126 {
1127     if (LastPtr) *LastPtr = p;
1128 
1129     for (;  p != NULL; p = p->Next) {
1130 
1131         if (LastPtr) *LastPtr = p;
1132 
1133         if (*Key != '#') { // Comments are ignored
1134 
1135             if (cmsstrcasecmp(Key, p->Keyword) == 0)
1136                 break;
1137         }
1138     }
1139 
1140     if (p == NULL)
1141         return FALSE;
1142 
1143     if (Subkey == 0)
1144         return TRUE;
1145 
1146     for (; p != NULL; p = p->NextSubkey) {
1147 
1148         if (p ->Subkey == NULL) continue;
1149 
1150         if (LastPtr) *LastPtr = p;
1151 
1152         if (cmsstrcasecmp(Subkey, p->Subkey) == 0)
1153             return TRUE;
1154     }
1155 
1156     return FALSE;
1157 }
1158 
1159 
1160 
1161 // Add a property into a linked list
1162 static
AddToList(cmsIT8 * it8,KEYVALUE ** Head,const char * Key,const char * Subkey,const char * xValue,WRITEMODE WriteAs)1163 KEYVALUE* AddToList(cmsIT8* it8, KEYVALUE** Head, const char *Key, const char *Subkey, const char* xValue, WRITEMODE WriteAs)
1164 {
1165     KEYVALUE* p;
1166     KEYVALUE* last;
1167 
1168 
1169     // Check if property is already in list
1170 
1171     if (IsAvailableOnList(*Head, Key, Subkey, &p)) {
1172 
1173         // This may work for editing properties
1174 
1175         //     return SynError(it8, "duplicate key <%s>", Key);
1176     }
1177     else {
1178 
1179         last = p;
1180 
1181         // Allocate the container
1182         p = (KEYVALUE*) AllocChunk(it8, sizeof(KEYVALUE));
1183         if (p == NULL)
1184         {
1185             SynError(it8, "AddToList: out of memory");
1186             return NULL;
1187         }
1188 
1189         // Store name and value
1190         p->Keyword = AllocString(it8, Key);
1191         p->Subkey = (Subkey == NULL) ? NULL : AllocString(it8, Subkey);
1192 
1193         // Keep the container in our list
1194         if (*Head == NULL) {
1195             *Head = p;
1196         }
1197         else
1198         {
1199             if (Subkey != NULL && last != NULL) {
1200 
1201                 last->NextSubkey = p;
1202 
1203                 // If Subkey is not null, then last is the last property with the same key,
1204                 // but not necessarily is the last property in the list, so we need to move
1205                 // to the actual list end
1206                 while (last->Next != NULL)
1207                          last = last->Next;
1208             }
1209 
1210             if (last != NULL) last->Next = p;
1211         }
1212 
1213         p->Next    = NULL;
1214         p->NextSubkey = NULL;
1215     }
1216 
1217     p->WriteAs = WriteAs;
1218 
1219     if (xValue != NULL) {
1220 
1221         p->Value   = AllocString(it8, xValue);
1222     }
1223     else {
1224         p->Value   = NULL;
1225     }
1226 
1227     return p;
1228 }
1229 
1230 static
AddAvailableProperty(cmsIT8 * it8,const char * Key,WRITEMODE as)1231 KEYVALUE* AddAvailableProperty(cmsIT8* it8, const char* Key, WRITEMODE as)
1232 {
1233     return AddToList(it8, &it8->ValidKeywords, Key, NULL, NULL, as);
1234 }
1235 
1236 
1237 static
AddAvailableSampleID(cmsIT8 * it8,const char * Key)1238 KEYVALUE* AddAvailableSampleID(cmsIT8* it8, const char* Key)
1239 {
1240     return AddToList(it8, &it8->ValidSampleID, Key, NULL, NULL, WRITE_UNCOOKED);
1241 }
1242 
1243 
1244 static
AllocTable(cmsIT8 * it8)1245 void AllocTable(cmsIT8* it8)
1246 {
1247     TABLE* t;
1248 
1249     t = it8 ->Tab + it8 ->TablesCount;
1250 
1251     t->HeaderList = NULL;
1252     t->DataFormat = NULL;
1253     t->Data       = NULL;
1254 
1255     it8 ->TablesCount++;
1256 }
1257 
1258 
cmsIT8SetTable(cmsHANDLE IT8,cmsUInt32Number nTable)1259 cmsInt32Number CMSEXPORT cmsIT8SetTable(cmsHANDLE  IT8, cmsUInt32Number nTable)
1260 {
1261      cmsIT8* it8 = (cmsIT8*) IT8;
1262 
1263      if (nTable >= it8 ->TablesCount) {
1264 
1265          if (nTable == it8 ->TablesCount) {
1266 
1267              AllocTable(it8);
1268          }
1269          else {
1270              SynError(it8, "Table %d is out of sequence", nTable);
1271              return -1;
1272          }
1273      }
1274 
1275      it8 ->nTable = nTable;
1276 
1277      return (cmsInt32Number) nTable;
1278 }
1279 
1280 
1281 
1282 // Init an empty container
cmsIT8Alloc(cmsContext ContextID)1283 cmsHANDLE  CMSEXPORT cmsIT8Alloc(cmsContext ContextID)
1284 {
1285     cmsIT8* it8;
1286     cmsUInt32Number i;
1287 
1288     it8 = (cmsIT8*) _cmsMallocZero(ContextID, sizeof(cmsIT8));
1289     if (it8 == NULL) return NULL;
1290 
1291     AllocTable(it8);
1292 
1293     it8->MemoryBlock = NULL;
1294     it8->MemorySink  = NULL;
1295 
1296     it8 ->nTable = 0;
1297 
1298     it8->ContextID = ContextID;
1299     it8->Allocator.Used = 0;
1300     it8->Allocator.Block = NULL;
1301     it8->Allocator.BlockSize = 0;
1302 
1303     it8->ValidKeywords = NULL;
1304     it8->ValidSampleID = NULL;
1305 
1306     it8 -> sy = SUNDEFINED;
1307     it8 -> ch = ' ';
1308     it8 -> Source = NULL;
1309     it8 -> inum = 0;
1310     it8 -> dnum = 0.0;
1311 
1312     it8->FileStack[0] = (FILECTX*)AllocChunk(it8, sizeof(FILECTX));
1313     it8->IncludeSP   = 0;
1314     it8 -> lineno = 1;
1315 
1316     strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT);
1317     cmsIT8SetSheetType((cmsHANDLE) it8, "CGATS.17");
1318 
1319     // Initialize predefined properties & data
1320 
1321     for (i=0; i < NUMPREDEFINEDPROPS; i++)
1322             AddAvailableProperty(it8, PredefinedProperties[i].id, PredefinedProperties[i].as);
1323 
1324     for (i=0; i < NUMPREDEFINEDSAMPLEID; i++)
1325             AddAvailableSampleID(it8, PredefinedSampleID[i]);
1326 
1327 
1328    return (cmsHANDLE) it8;
1329 }
1330 
1331 
cmsIT8GetSheetType(cmsHANDLE hIT8)1332 const char* CMSEXPORT cmsIT8GetSheetType(cmsHANDLE hIT8)
1333 {
1334         return GetTable((cmsIT8*) hIT8)->SheetType;
1335 }
1336 
cmsIT8SetSheetType(cmsHANDLE hIT8,const char * Type)1337 cmsBool CMSEXPORT cmsIT8SetSheetType(cmsHANDLE hIT8, const char* Type)
1338 {
1339         TABLE* t = GetTable((cmsIT8*) hIT8);
1340 
1341         strncpy(t ->SheetType, Type, MAXSTR-1);
1342         t ->SheetType[MAXSTR-1] = 0;
1343         return TRUE;
1344 }
1345 
cmsIT8SetComment(cmsHANDLE hIT8,const char * Val)1346 cmsBool CMSEXPORT cmsIT8SetComment(cmsHANDLE hIT8, const char* Val)
1347 {
1348     cmsIT8* it8 = (cmsIT8*) hIT8;
1349 
1350     if (!Val) return FALSE;
1351     if (!*Val) return FALSE;
1352 
1353     return AddToList(it8, &GetTable(it8)->HeaderList, "# ", NULL, Val, WRITE_UNCOOKED) != NULL;
1354 }
1355 
1356 // Sets a property
cmsIT8SetPropertyStr(cmsHANDLE hIT8,const char * Key,const char * Val)1357 cmsBool CMSEXPORT cmsIT8SetPropertyStr(cmsHANDLE hIT8, const char* Key, const char *Val)
1358 {
1359     cmsIT8* it8 = (cmsIT8*) hIT8;
1360 
1361     if (!Val) return FALSE;
1362     if (!*Val) return FALSE;
1363 
1364     return AddToList(it8, &GetTable(it8)->HeaderList, Key, NULL, Val, WRITE_STRINGIFY) != NULL;
1365 }
1366 
cmsIT8SetPropertyDbl(cmsHANDLE hIT8,const char * cProp,cmsFloat64Number Val)1367 cmsBool CMSEXPORT cmsIT8SetPropertyDbl(cmsHANDLE hIT8, const char* cProp, cmsFloat64Number Val)
1368 {
1369     cmsIT8* it8 = (cmsIT8*) hIT8;
1370     char Buffer[1024];
1371 
1372     snprintf(Buffer, 1023, it8->DoubleFormatter, Val);
1373 
1374     return AddToList(it8, &GetTable(it8)->HeaderList, cProp, NULL, Buffer, WRITE_UNCOOKED) != NULL;
1375 }
1376 
cmsIT8SetPropertyHex(cmsHANDLE hIT8,const char * cProp,cmsUInt32Number Val)1377 cmsBool CMSEXPORT cmsIT8SetPropertyHex(cmsHANDLE hIT8, const char* cProp, cmsUInt32Number Val)
1378 {
1379     cmsIT8* it8 = (cmsIT8*) hIT8;
1380     char Buffer[1024];
1381 
1382     snprintf(Buffer, 1023, "%u", Val);
1383 
1384     return AddToList(it8, &GetTable(it8)->HeaderList, cProp, NULL, Buffer, WRITE_HEXADECIMAL) != NULL;
1385 }
1386 
cmsIT8SetPropertyUncooked(cmsHANDLE hIT8,const char * Key,const char * Buffer)1387 cmsBool CMSEXPORT cmsIT8SetPropertyUncooked(cmsHANDLE hIT8, const char* Key, const char* Buffer)
1388 {
1389     cmsIT8* it8 = (cmsIT8*) hIT8;
1390 
1391     return AddToList(it8, &GetTable(it8)->HeaderList, Key, NULL, Buffer, WRITE_UNCOOKED) != NULL;
1392 }
1393 
cmsIT8SetPropertyMulti(cmsHANDLE hIT8,const char * Key,const char * SubKey,const char * Buffer)1394 cmsBool CMSEXPORT cmsIT8SetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char* SubKey, const char *Buffer)
1395 {
1396     cmsIT8* it8 = (cmsIT8*) hIT8;
1397 
1398     return AddToList(it8, &GetTable(it8)->HeaderList, Key, SubKey, Buffer, WRITE_PAIR) != NULL;
1399 }
1400 
1401 // Gets a property
cmsIT8GetProperty(cmsHANDLE hIT8,const char * Key)1402 const char* CMSEXPORT cmsIT8GetProperty(cmsHANDLE hIT8, const char* Key)
1403 {
1404     cmsIT8* it8 = (cmsIT8*) hIT8;
1405     KEYVALUE* p;
1406 
1407     if (IsAvailableOnList(GetTable(it8) -> HeaderList, Key, NULL, &p))
1408     {
1409         return p -> Value;
1410     }
1411     return NULL;
1412 }
1413 
1414 
cmsIT8GetPropertyDbl(cmsHANDLE hIT8,const char * cProp)1415 cmsFloat64Number CMSEXPORT cmsIT8GetPropertyDbl(cmsHANDLE hIT8, const char* cProp)
1416 {
1417     const char *v = cmsIT8GetProperty(hIT8, cProp);
1418 
1419     if (v == NULL) return 0.0;
1420 
1421     return ParseFloatNumber(v);
1422 }
1423 
cmsIT8GetPropertyMulti(cmsHANDLE hIT8,const char * Key,const char * SubKey)1424 const char* CMSEXPORT cmsIT8GetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char *SubKey)
1425 {
1426     cmsIT8* it8 = (cmsIT8*) hIT8;
1427     KEYVALUE* p;
1428 
1429     if (IsAvailableOnList(GetTable(it8) -> HeaderList, Key, SubKey, &p)) {
1430         return p -> Value;
1431     }
1432     return NULL;
1433 }
1434 
1435 // ----------------------------------------------------------------- Datasets
1436 
1437 
1438 static
AllocateDataFormat(cmsIT8 * it8)1439 void AllocateDataFormat(cmsIT8* it8)
1440 {
1441     TABLE* t = GetTable(it8);
1442 
1443     if (t -> DataFormat) return;    // Already allocated
1444 
1445     t -> nSamples  = (int) cmsIT8GetPropertyDbl(it8, "NUMBER_OF_FIELDS");
1446 
1447     if (t -> nSamples <= 0) {
1448 
1449         SynError(it8, "AllocateDataFormat: Unknown NUMBER_OF_FIELDS");
1450         t -> nSamples = 10;
1451         }
1452 
1453     t -> DataFormat = (char**) AllocChunk (it8, ((cmsUInt32Number) t->nSamples + 1) * sizeof(char *));
1454     if (t->DataFormat == NULL) {
1455 
1456         SynError(it8, "AllocateDataFormat: Unable to allocate dataFormat array");
1457     }
1458 
1459 }
1460 
1461 static
GetDataFormat(cmsIT8 * it8,int n)1462 const char *GetDataFormat(cmsIT8* it8, int n)
1463 {
1464     TABLE* t = GetTable(it8);
1465 
1466     if (t->DataFormat)
1467         return t->DataFormat[n];
1468 
1469     return NULL;
1470 }
1471 
1472 static
SetDataFormat(cmsIT8 * it8,int n,const char * label)1473 cmsBool SetDataFormat(cmsIT8* it8, int n, const char *label)
1474 {
1475     TABLE* t = GetTable(it8);
1476 
1477     if (!t->DataFormat)
1478         AllocateDataFormat(it8);
1479 
1480     if (n > t -> nSamples) {
1481         SynError(it8, "More than NUMBER_OF_FIELDS fields.");
1482         return FALSE;
1483     }
1484 
1485     if (t->DataFormat) {
1486         t->DataFormat[n] = AllocString(it8, label);
1487     }
1488 
1489     return TRUE;
1490 }
1491 
1492 
cmsIT8SetDataFormat(cmsHANDLE h,int n,const char * Sample)1493 cmsBool CMSEXPORT cmsIT8SetDataFormat(cmsHANDLE  h, int n, const char *Sample)
1494 {
1495     cmsIT8* it8 = (cmsIT8*)h;
1496     return SetDataFormat(it8, n, Sample);
1497 }
1498 
1499 // A safe atoi that returns 0 when NULL input is given
1500 static
satoi(const char * b)1501 cmsInt32Number satoi(const char* b)
1502 {
1503     if (b == NULL) return 0;
1504     return atoi(b);
1505 }
1506 
1507 // Convert to binary
1508 static
satob(const char * v)1509 const char* satob(const char* v)
1510 {
1511     cmsUInt32Number x;
1512     static char buf[33];
1513     char *s = buf + 33;
1514 
1515     if (v == NULL) return "0";
1516 
1517     x = atoi(v);
1518     *--s = 0;
1519     if (!x) *--s = '0';
1520     for (; x; x /= 2) *--s = '0' + x%2;
1521 
1522     return s;
1523 }
1524 
1525 
1526 static
AllocateDataSet(cmsIT8 * it8)1527 void AllocateDataSet(cmsIT8* it8)
1528 {
1529     TABLE* t = GetTable(it8);
1530 
1531     if (t -> Data) return;    // Already allocated
1532 
1533     t-> nSamples   = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
1534     t-> nPatches   = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS"));
1535 
1536     if (t -> nSamples < 0 || t->nSamples > 0x7ffe || t->nPatches < 0 || t->nPatches > 0x7ffe)
1537     {
1538         SynError(it8, "AllocateDataSet: too much data");
1539     }
1540     else {
1541         // Some dumb analizers warns of possible overflow here, just take a look couple of lines above.
1542         t->Data = (char**)AllocChunk(it8, ((cmsUInt32Number)t->nSamples + 1) * ((cmsUInt32Number)t->nPatches + 1) * sizeof(char*));
1543         if (t->Data == NULL) {
1544 
1545             SynError(it8, "AllocateDataSet: Unable to allocate data array");
1546         }
1547     }
1548 
1549 }
1550 
1551 static
GetData(cmsIT8 * it8,int nSet,int nField)1552 char* GetData(cmsIT8* it8, int nSet, int nField)
1553 {
1554     TABLE* t = GetTable(it8);
1555     int nSamples    = t -> nSamples;
1556     int nPatches    = t -> nPatches;
1557 
1558     if (nSet >= nPatches || nField >= nSamples)
1559         return NULL;
1560 
1561     if (!t->Data) return NULL;
1562     return t->Data [nSet * nSamples + nField];
1563 }
1564 
1565 static
SetData(cmsIT8 * it8,int nSet,int nField,const char * Val)1566 cmsBool SetData(cmsIT8* it8, int nSet, int nField, const char *Val)
1567 {
1568     TABLE* t = GetTable(it8);
1569 
1570     if (!t->Data)
1571         AllocateDataSet(it8);
1572 
1573     if (!t->Data) return FALSE;
1574 
1575     if (nSet > t -> nPatches || nSet < 0) {
1576 
1577             return SynError(it8, "Patch %d out of range, there are %d patches", nSet, t -> nPatches);
1578     }
1579 
1580     if (nField > t ->nSamples || nField < 0) {
1581             return SynError(it8, "Sample %d out of range, there are %d samples", nField, t ->nSamples);
1582 
1583     }
1584 
1585     t->Data [nSet * t -> nSamples + nField] = AllocString(it8, Val);
1586     return TRUE;
1587 }
1588 
1589 
1590 // --------------------------------------------------------------- File I/O
1591 
1592 
1593 // Writes a string to file
1594 static
WriteStr(SAVESTREAM * f,const char * str)1595 void WriteStr(SAVESTREAM* f, const char *str)
1596 {
1597     cmsUInt32Number len;
1598 
1599     if (str == NULL)
1600         str = " ";
1601 
1602     // Length to write
1603     len = (cmsUInt32Number) strlen(str);
1604     f ->Used += len;
1605 
1606 
1607     if (f ->stream) {   // Should I write it to a file?
1608 
1609         if (fwrite(str, 1, len, f->stream) != len) {
1610             cmsSignalError(0, cmsERROR_WRITE, "Write to file error in CGATS parser");
1611             return;
1612         }
1613 
1614     }
1615     else {  // Or to a memory block?
1616 
1617         if (f ->Base) {   // Am I just counting the bytes?
1618 
1619             if (f ->Used > f ->Max) {
1620 
1621                  cmsSignalError(0, cmsERROR_WRITE, "Write to memory overflows in CGATS parser");
1622                  return;
1623             }
1624 
1625             memmove(f ->Ptr, str, len);
1626             f->Ptr += len;
1627         }
1628 
1629     }
1630 }
1631 
1632 
1633 // Write formatted
1634 
1635 static
Writef(SAVESTREAM * f,const char * frm,...)1636 void Writef(SAVESTREAM* f, const char* frm, ...)
1637 {
1638     char Buffer[4096];
1639     va_list args;
1640 
1641     va_start(args, frm);
1642     vsnprintf(Buffer, 4095, frm, args);
1643     Buffer[4095] = 0;
1644     WriteStr(f, Buffer);
1645     va_end(args);
1646 
1647 }
1648 
1649 // Writes full header
1650 static
WriteHeader(cmsIT8 * it8,SAVESTREAM * fp)1651 void WriteHeader(cmsIT8* it8, SAVESTREAM* fp)
1652 {
1653     KEYVALUE* p;
1654     TABLE* t = GetTable(it8);
1655 
1656     // Writes the type
1657     WriteStr(fp, t->SheetType);
1658     WriteStr(fp, "\n");
1659 
1660     for (p = t->HeaderList; (p != NULL); p = p->Next)
1661     {
1662         if (*p ->Keyword == '#') {
1663 
1664             char* Pt;
1665 
1666             WriteStr(fp, "#\n# ");
1667             for (Pt = p ->Value; *Pt; Pt++) {
1668 
1669 
1670                 Writef(fp, "%c", *Pt);
1671 
1672                 if (*Pt == '\n') {
1673                     WriteStr(fp, "# ");
1674                 }
1675             }
1676 
1677             WriteStr(fp, "\n#\n");
1678             continue;
1679         }
1680 
1681 
1682         if (!IsAvailableOnList(it8-> ValidKeywords, p->Keyword, NULL, NULL)) {
1683 
1684 #ifdef CMS_STRICT_CGATS
1685             WriteStr(fp, "KEYWORD\t\"");
1686             WriteStr(fp, p->Keyword);
1687             WriteStr(fp, "\"\n");
1688 #endif
1689 
1690             AddAvailableProperty(it8, p->Keyword, WRITE_UNCOOKED);
1691         }
1692 
1693         WriteStr(fp, p->Keyword);
1694         if (p->Value) {
1695 
1696             switch (p ->WriteAs) {
1697 
1698             case WRITE_UNCOOKED:
1699                     Writef(fp, "\t%s", p ->Value);
1700                     break;
1701 
1702             case WRITE_STRINGIFY:
1703                     Writef(fp, "\t\"%s\"", p->Value );
1704                     break;
1705 
1706             case WRITE_HEXADECIMAL:
1707                     Writef(fp, "\t0x%X", satoi(p ->Value));
1708                     break;
1709 
1710             case WRITE_BINARY:
1711                     Writef(fp, "\t0b%s", satob(p ->Value));
1712                     break;
1713 
1714             case WRITE_PAIR:
1715                     Writef(fp, "\t\"%s,%s\"", p->Subkey, p->Value);
1716                     break;
1717 
1718             default: SynError(it8, "Unknown write mode %d", p ->WriteAs);
1719                      return;
1720             }
1721         }
1722 
1723         WriteStr (fp, "\n");
1724     }
1725 
1726 }
1727 
1728 
1729 // Writes the data format
1730 static
WriteDataFormat(SAVESTREAM * fp,cmsIT8 * it8)1731 void WriteDataFormat(SAVESTREAM* fp, cmsIT8* it8)
1732 {
1733     int i, nSamples;
1734     TABLE* t = GetTable(it8);
1735 
1736     if (!t -> DataFormat) return;
1737 
1738        WriteStr(fp, "BEGIN_DATA_FORMAT\n");
1739        WriteStr(fp, " ");
1740        nSamples = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
1741 
1742        for (i = 0; i < nSamples; i++) {
1743 
1744               WriteStr(fp, t->DataFormat[i]);
1745               WriteStr(fp, ((i == (nSamples-1)) ? "\n" : "\t"));
1746           }
1747 
1748        WriteStr (fp, "END_DATA_FORMAT\n");
1749 }
1750 
1751 
1752 // Writes data array
1753 static
WriteData(SAVESTREAM * fp,cmsIT8 * it8)1754 void WriteData(SAVESTREAM* fp, cmsIT8* it8)
1755 {
1756        int  i, j;
1757        TABLE* t = GetTable(it8);
1758 
1759        if (!t->Data) return;
1760 
1761        WriteStr (fp, "BEGIN_DATA\n");
1762 
1763        t->nPatches = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS"));
1764 
1765        for (i = 0; i < t-> nPatches; i++) {
1766 
1767               WriteStr(fp, " ");
1768 
1769               for (j = 0; j < t->nSamples; j++) {
1770 
1771                      char *ptr = t->Data[i*t->nSamples+j];
1772 
1773                      if (ptr == NULL) WriteStr(fp, "\"\"");
1774                      else {
1775                          // If value contains whitespace, enclose within quote
1776 
1777                          if (strchr(ptr, ' ') != NULL) {
1778 
1779                              WriteStr(fp, "\"");
1780                              WriteStr(fp, ptr);
1781                              WriteStr(fp, "\"");
1782                          }
1783                          else
1784                             WriteStr(fp, ptr);
1785                      }
1786 
1787                      WriteStr(fp, ((j == (t->nSamples-1)) ? "\n" : "\t"));
1788               }
1789        }
1790        WriteStr (fp, "END_DATA\n");
1791 }
1792 
1793 
1794 
1795 // Saves whole file
cmsIT8SaveToFile(cmsHANDLE hIT8,const char * cFileName)1796 cmsBool CMSEXPORT cmsIT8SaveToFile(cmsHANDLE hIT8, const char* cFileName)
1797 {
1798     SAVESTREAM sd;
1799     cmsUInt32Number i;
1800     cmsIT8* it8 = (cmsIT8*) hIT8;
1801 
1802     memset(&sd, 0, sizeof(sd));
1803 
1804     sd.stream = fopen(cFileName, "wt");
1805     if (!sd.stream) return FALSE;
1806 
1807     for (i=0; i < it8 ->TablesCount; i++) {
1808 
1809             cmsIT8SetTable(hIT8, i);
1810             WriteHeader(it8, &sd);
1811             WriteDataFormat(&sd, it8);
1812             WriteData(&sd, it8);
1813     }
1814 
1815     if (fclose(sd.stream) != 0) return FALSE;
1816 
1817     return TRUE;
1818 }
1819 
1820 
1821 // Saves to memory
cmsIT8SaveToMem(cmsHANDLE hIT8,void * MemPtr,cmsUInt32Number * BytesNeeded)1822 cmsBool CMSEXPORT cmsIT8SaveToMem(cmsHANDLE hIT8, void *MemPtr, cmsUInt32Number* BytesNeeded)
1823 {
1824     SAVESTREAM sd;
1825     cmsUInt32Number i;
1826     cmsIT8* it8 = (cmsIT8*) hIT8;
1827 
1828     memset(&sd, 0, sizeof(sd));
1829 
1830     sd.stream = NULL;
1831     sd.Base   = (cmsUInt8Number*)  MemPtr;
1832     sd.Ptr    = sd.Base;
1833 
1834     sd.Used = 0;
1835 
1836     if (sd.Base)
1837         sd.Max  = *BytesNeeded;     // Write to memory?
1838     else
1839         sd.Max  = 0;                // Just counting the needed bytes
1840 
1841     for (i=0; i < it8 ->TablesCount; i++) {
1842 
1843         cmsIT8SetTable(hIT8, i);
1844         WriteHeader(it8, &sd);
1845         WriteDataFormat(&sd, it8);
1846         WriteData(&sd, it8);
1847     }
1848 
1849     sd.Used++;  // The \0 at the very end
1850 
1851     if (sd.Base)
1852         *sd.Ptr = 0;
1853 
1854     *BytesNeeded = sd.Used;
1855 
1856     return TRUE;
1857 }
1858 
1859 
1860 // -------------------------------------------------------------- Higher level parsing
1861 
1862 static
DataFormatSection(cmsIT8 * it8)1863 cmsBool DataFormatSection(cmsIT8* it8)
1864 {
1865     int iField = 0;
1866     TABLE* t = GetTable(it8);
1867 
1868     InSymbol(it8);   // Eats "BEGIN_DATA_FORMAT"
1869     CheckEOLN(it8);
1870 
1871     while (it8->sy != SEND_DATA_FORMAT &&
1872         it8->sy != SEOLN &&
1873         it8->sy != SEOF &&
1874         it8->sy != SSYNERROR)  {
1875 
1876             if (it8->sy != SIDENT) {
1877 
1878                 return SynError(it8, "Sample type expected");
1879             }
1880 
1881             if (!SetDataFormat(it8, iField, it8->id)) return FALSE;
1882             iField++;
1883 
1884             InSymbol(it8);
1885             SkipEOLN(it8);
1886        }
1887 
1888        SkipEOLN(it8);
1889        Skip(it8, SEND_DATA_FORMAT);
1890        SkipEOLN(it8);
1891 
1892        if (iField != t ->nSamples) {
1893            SynError(it8, "Count mismatch. NUMBER_OF_FIELDS was %d, found %d\n", t ->nSamples, iField);
1894 
1895 
1896        }
1897 
1898        return TRUE;
1899 }
1900 
1901 
1902 
1903 static
DataSection(cmsIT8 * it8)1904 cmsBool DataSection (cmsIT8* it8)
1905 {
1906     int  iField = 0;
1907     int  iSet   = 0;
1908     char Buffer[256];
1909     TABLE* t = GetTable(it8);
1910 
1911     InSymbol(it8);   // Eats "BEGIN_DATA"
1912     CheckEOLN(it8);
1913 
1914     if (!t->Data)
1915         AllocateDataSet(it8);
1916 
1917     while (it8->sy != SEND_DATA && it8->sy != SEOF)
1918     {
1919         if (iField >= t -> nSamples) {
1920             iField = 0;
1921             iSet++;
1922 
1923         }
1924 
1925         if (it8->sy != SEND_DATA && it8->sy != SEOF) {
1926 
1927             if (!GetVal(it8, Buffer, 255, "Sample data expected"))
1928                 return FALSE;
1929 
1930             if (!SetData(it8, iSet, iField, Buffer))
1931                 return FALSE;
1932 
1933             iField++;
1934 
1935             InSymbol(it8);
1936             SkipEOLN(it8);
1937         }
1938     }
1939 
1940     SkipEOLN(it8);
1941     Skip(it8, SEND_DATA);
1942     SkipEOLN(it8);
1943 
1944     // Check for data completion.
1945 
1946     if ((iSet+1) != t -> nPatches)
1947         return SynError(it8, "Count mismatch. NUMBER_OF_SETS was %d, found %d\n", t ->nPatches, iSet+1);
1948 
1949     return TRUE;
1950 }
1951 
1952 
1953 
1954 
1955 static
HeaderSection(cmsIT8 * it8)1956 cmsBool HeaderSection(cmsIT8* it8)
1957 {
1958     char VarName[MAXID];
1959     char Buffer[MAXSTR];
1960     KEYVALUE* Key;
1961 
1962         while (it8->sy != SEOF &&
1963                it8->sy != SSYNERROR &&
1964                it8->sy != SBEGIN_DATA_FORMAT &&
1965                it8->sy != SBEGIN_DATA) {
1966 
1967 
1968         switch (it8 -> sy) {
1969 
1970         case SKEYWORD:
1971                 InSymbol(it8);
1972                 if (!GetVal(it8, Buffer, MAXSTR-1, "Keyword expected")) return FALSE;
1973                 if (!AddAvailableProperty(it8, Buffer, WRITE_UNCOOKED)) return FALSE;
1974                 InSymbol(it8);
1975                 break;
1976 
1977 
1978         case SDATA_FORMAT_ID:
1979                 InSymbol(it8);
1980                 if (!GetVal(it8, Buffer, MAXSTR-1, "Keyword expected")) return FALSE;
1981                 if (!AddAvailableSampleID(it8, Buffer)) return FALSE;
1982                 InSymbol(it8);
1983                 break;
1984 
1985 
1986         case SIDENT:
1987             strncpy(VarName, it8->id, MAXID - 1);
1988             VarName[MAXID - 1] = 0;
1989 
1990             if (!IsAvailableOnList(it8->ValidKeywords, VarName, NULL, &Key)) {
1991 
1992 #ifdef CMS_STRICT_CGATS
1993                 return SynError(it8, "Undefined keyword '%s'", VarName);
1994 #else
1995                 Key = AddAvailableProperty(it8, VarName, WRITE_UNCOOKED);
1996                 if (Key == NULL) return FALSE;
1997 #endif
1998             }
1999 
2000             InSymbol(it8);
2001             if (!GetVal(it8, Buffer, MAXSTR - 1, "Property data expected")) return FALSE;
2002 
2003             if (Key->WriteAs != WRITE_PAIR) {
2004                 AddToList(it8, &GetTable(it8)->HeaderList, VarName, NULL, Buffer,
2005                     (it8->sy == SSTRING) ? WRITE_STRINGIFY : WRITE_UNCOOKED);
2006             }
2007             else {
2008                 const char *Subkey;
2009                 char *Nextkey;
2010                 if (it8->sy != SSTRING)
2011                     return SynError(it8, "Invalid value '%s' for property '%s'.", Buffer, VarName);
2012 
2013                 // chop the string as a list of "subkey, value" pairs, using ';' as a separator
2014                 for (Subkey = Buffer; Subkey != NULL; Subkey = Nextkey)
2015                 {
2016                     char *Value, *temp;
2017 
2018                     //  identify token pair boundary
2019                     Nextkey = (char*)strchr(Subkey, ';');
2020                     if (Nextkey)
2021                         *Nextkey++ = '\0';
2022 
2023                     // for each pair, split the subkey and the value
2024                     Value = (char*)strrchr(Subkey, ',');
2025                     if (Value == NULL)
2026                         return SynError(it8, "Invalid value for property '%s'.", VarName);
2027 
2028                     // gobble the spaces before the coma, and the coma itself
2029                     temp = Value++;
2030                     do *temp-- = '\0'; while (temp >= Subkey && *temp == ' ');
2031 
2032                     // gobble any space at the right
2033                     temp = Value + strlen(Value) - 1;
2034                     while (*temp == ' ') *temp-- = '\0';
2035 
2036                     // trim the strings from the left
2037                     Subkey += strspn(Subkey, " ");
2038                     Value += strspn(Value, " ");
2039 
2040                     if (Subkey[0] == 0 || Value[0] == 0)
2041                         return SynError(it8, "Invalid value for property '%s'.", VarName);
2042                     AddToList(it8, &GetTable(it8)->HeaderList, VarName, Subkey, Value, WRITE_PAIR);
2043                 }
2044             }
2045 
2046             InSymbol(it8);
2047             break;
2048 
2049 
2050         case SEOLN: break;
2051 
2052         default:
2053                 return SynError(it8, "expected keyword or identifier");
2054         }
2055 
2056     SkipEOLN(it8);
2057     }
2058 
2059     return TRUE;
2060 
2061 }
2062 
2063 
2064 static
ReadType(cmsIT8 * it8,char * SheetTypePtr)2065 void ReadType(cmsIT8* it8, char* SheetTypePtr)
2066 {
2067     cmsInt32Number cnt = 0;
2068 
2069     // First line is a very special case.
2070 
2071     while (isseparator(it8->ch))
2072             NextCh(it8);
2073 
2074     while (it8->ch != '\r' && it8 ->ch != '\n' && it8->ch != '\t' && it8 -> ch != 0) {
2075 
2076         if (cnt++ < MAXSTR)
2077             *SheetTypePtr++= (char) it8 ->ch;
2078         NextCh(it8);
2079     }
2080 
2081     *SheetTypePtr = 0;
2082 }
2083 
2084 
2085 static
ParseIT8(cmsIT8 * it8,cmsBool nosheet)2086 cmsBool ParseIT8(cmsIT8* it8, cmsBool nosheet)
2087 {
2088     char* SheetTypePtr = it8 ->Tab[0].SheetType;
2089 
2090     if (nosheet == 0) {
2091         ReadType(it8, SheetTypePtr);
2092     }
2093 
2094     InSymbol(it8);
2095 
2096     SkipEOLN(it8);
2097 
2098     while (it8-> sy != SEOF &&
2099            it8-> sy != SSYNERROR) {
2100 
2101             switch (it8 -> sy) {
2102 
2103             case SBEGIN_DATA_FORMAT:
2104                     if (!DataFormatSection(it8)) return FALSE;
2105                     break;
2106 
2107             case SBEGIN_DATA:
2108 
2109                     if (!DataSection(it8)) return FALSE;
2110 
2111                     if (it8 -> sy != SEOF) {
2112 
2113                             AllocTable(it8);
2114                             it8 ->nTable = it8 ->TablesCount - 1;
2115 
2116                             // Read sheet type if present. We only support identifier and string.
2117                             // <ident> <eoln> is a type string
2118                             // anything else, is not a type string
2119                             if (nosheet == 0) {
2120 
2121                                 if (it8 ->sy == SIDENT) {
2122 
2123                                     // May be a type sheet or may be a prop value statement. We cannot use insymbol in
2124                                     // this special case...
2125                                      while (isseparator(it8->ch))
2126                                          NextCh(it8);
2127 
2128                                      // If a newline is found, then this is a type string
2129                                     if (it8 ->ch == '\n' || it8->ch == '\r') {
2130 
2131                                          cmsIT8SetSheetType(it8, it8 ->id);
2132                                          InSymbol(it8);
2133                                     }
2134                                     else
2135                                     {
2136                                         // It is not. Just continue
2137                                         cmsIT8SetSheetType(it8, "");
2138                                     }
2139                                 }
2140                                 else
2141                                     // Validate quoted strings
2142                                     if (it8 ->sy == SSTRING) {
2143                                         cmsIT8SetSheetType(it8, it8 ->str);
2144                                         InSymbol(it8);
2145                                     }
2146                            }
2147 
2148                     }
2149                     break;
2150 
2151             case SEOLN:
2152                     SkipEOLN(it8);
2153                     break;
2154 
2155             default:
2156                     if (!HeaderSection(it8)) return FALSE;
2157            }
2158 
2159     }
2160 
2161     return (it8 -> sy != SSYNERROR);
2162 }
2163 
2164 
2165 
2166 // Init useful pointers
2167 
2168 static
CookPointers(cmsIT8 * it8)2169 void CookPointers(cmsIT8* it8)
2170 {
2171     int idField, i;
2172     char* Fld;
2173     cmsUInt32Number j;
2174     cmsUInt32Number nOldTable = it8 ->nTable;
2175 
2176     for (j=0; j < it8 ->TablesCount; j++) {
2177 
2178     TABLE* t = it8 ->Tab + j;
2179 
2180     t -> SampleID = 0;
2181     it8 ->nTable = j;
2182 
2183     for (idField = 0; idField < t -> nSamples; idField++)
2184     {
2185         if (t ->DataFormat == NULL){
2186             SynError(it8, "Undefined DATA_FORMAT");
2187             return;
2188         }
2189 
2190         Fld = t->DataFormat[idField];
2191         if (!Fld) continue;
2192 
2193 
2194         if (cmsstrcasecmp(Fld, "SAMPLE_ID") == 0) {
2195 
2196             t -> SampleID = idField;
2197         }
2198 
2199         // "LABEL" is an extension. It keeps references to forward tables
2200 
2201         if ((cmsstrcasecmp(Fld, "LABEL") == 0) || Fld[0] == '$') {
2202 
2203             // Search for table references...
2204             for (i = 0; i < t->nPatches; i++) {
2205 
2206                 char* Label = GetData(it8, i, idField);
2207 
2208                 if (Label) {
2209 
2210                     cmsUInt32Number k;
2211 
2212                     // This is the label, search for a table containing
2213                     // this property
2214 
2215                     for (k = 0; k < it8->TablesCount; k++) {
2216 
2217                         TABLE* Table = it8->Tab + k;
2218                         KEYVALUE* p;
2219 
2220                         if (IsAvailableOnList(Table->HeaderList, Label, NULL, &p)) {
2221 
2222                             // Available, keep type and table
2223                             char Buffer[256];
2224 
2225                             char* Type = p->Value;
2226                             int  nTable = (int)k;
2227 
2228                             snprintf(Buffer, 255, "%s %d %s", Label, nTable, Type);
2229 
2230                             SetData(it8, i, idField, Buffer);
2231                         }
2232                     }
2233 
2234 
2235                 }
2236 
2237             }
2238 
2239 
2240         }
2241 
2242     }
2243     }
2244 
2245     it8 ->nTable = nOldTable;
2246 }
2247 
2248 // Try to infere if the file is a CGATS/IT8 file at all. Read first line
2249 // that should be something like some printable characters plus a \n
2250 // returns 0 if this is not like a CGATS, or an integer otherwise. This integer is the number of words in first line?
2251 static
IsMyBlock(const cmsUInt8Number * Buffer,cmsUInt32Number n)2252 int IsMyBlock(const cmsUInt8Number* Buffer, cmsUInt32Number n)
2253 {
2254     int words = 1, space = 0, quot = 0;
2255     cmsUInt32Number i;
2256 
2257     if (n < 10) return 0;   // Too small
2258 
2259     if (n > 132)
2260         n = 132;
2261 
2262     for (i = 1; i < n; i++) {
2263 
2264         switch(Buffer[i])
2265         {
2266         case '\n':
2267         case '\r':
2268             return ((quot == 1) || (words > 2)) ? 0 : words;
2269         case '\t':
2270         case ' ':
2271             if(!quot && !space)
2272                 space = 1;
2273             break;
2274         case '\"':
2275             quot = !quot;
2276             break;
2277         default:
2278             if (Buffer[i] < 32) return 0;
2279             if (Buffer[i] > 127) return 0;
2280             words += space;
2281             space = 0;
2282             break;
2283         }
2284     }
2285 
2286     return 0;
2287 }
2288 
2289 
2290 static
IsMyFile(const char * FileName)2291 cmsBool IsMyFile(const char* FileName)
2292 {
2293    FILE *fp;
2294    cmsUInt32Number Size;
2295    cmsUInt8Number Ptr[133];
2296 
2297    fp = fopen(FileName, "rt");
2298    if (!fp) {
2299        cmsSignalError(0, cmsERROR_FILE, "File '%s' not found", FileName);
2300        return FALSE;
2301    }
2302 
2303    Size = (cmsUInt32Number) fread(Ptr, 1, 132, fp);
2304 
2305    if (fclose(fp) != 0)
2306        return FALSE;
2307 
2308    Ptr[Size] = '\0';
2309 
2310    return IsMyBlock(Ptr, Size);
2311 }
2312 
2313 // ---------------------------------------------------------- Exported routines
2314 
2315 
cmsIT8LoadFromMem(cmsContext ContextID,const void * Ptr,cmsUInt32Number len)2316 cmsHANDLE  CMSEXPORT cmsIT8LoadFromMem(cmsContext ContextID, const void *Ptr, cmsUInt32Number len)
2317 {
2318     cmsHANDLE hIT8;
2319     cmsIT8*  it8;
2320     int type;
2321 
2322     _cmsAssert(Ptr != NULL);
2323     _cmsAssert(len != 0);
2324 
2325     type = IsMyBlock((const cmsUInt8Number*)Ptr, len);
2326     if (type == 0) return NULL;
2327 
2328     hIT8 = cmsIT8Alloc(ContextID);
2329     if (!hIT8) return NULL;
2330 
2331     it8 = (cmsIT8*) hIT8;
2332     it8 ->MemoryBlock = (char*) _cmsMalloc(ContextID, len + 1);
2333     if (it8->MemoryBlock == NULL)
2334     {
2335         cmsIT8Free(hIT8);
2336         return FALSE;
2337     }
2338 
2339     strncpy(it8 ->MemoryBlock, (const char*) Ptr, len);
2340     it8 ->MemoryBlock[len] = 0;
2341 
2342     strncpy(it8->FileStack[0]->FileName, "", cmsMAX_PATH-1);
2343     it8-> Source = it8 -> MemoryBlock;
2344 
2345     if (!ParseIT8(it8, type-1)) {
2346 
2347         cmsIT8Free(hIT8);
2348         return FALSE;
2349     }
2350 
2351     CookPointers(it8);
2352     it8 ->nTable = 0;
2353 
2354     _cmsFree(ContextID, it8->MemoryBlock);
2355     it8 -> MemoryBlock = NULL;
2356 
2357     return hIT8;
2358 
2359 
2360 }
2361 
2362 
cmsIT8LoadFromFile(cmsContext ContextID,const char * cFileName)2363 cmsHANDLE  CMSEXPORT cmsIT8LoadFromFile(cmsContext ContextID, const char* cFileName)
2364 {
2365 
2366      cmsHANDLE hIT8;
2367      cmsIT8*  it8;
2368      int type;
2369 
2370      _cmsAssert(cFileName != NULL);
2371 
2372      type = IsMyFile(cFileName);
2373      if (type == 0) return NULL;
2374 
2375      hIT8 = cmsIT8Alloc(ContextID);
2376      it8 = (cmsIT8*) hIT8;
2377      if (!hIT8) return NULL;
2378 
2379 
2380      it8 ->FileStack[0]->Stream = fopen(cFileName, "rt");
2381 
2382      if (!it8 ->FileStack[0]->Stream) {
2383          cmsIT8Free(hIT8);
2384          return NULL;
2385      }
2386 
2387 
2388     strncpy(it8->FileStack[0]->FileName, cFileName, cmsMAX_PATH-1);
2389     it8->FileStack[0]->FileName[cmsMAX_PATH-1] = 0;
2390 
2391     if (!ParseIT8(it8, type-1)) {
2392 
2393             fclose(it8 ->FileStack[0]->Stream);
2394             cmsIT8Free(hIT8);
2395             return NULL;
2396     }
2397 
2398     CookPointers(it8);
2399     it8 ->nTable = 0;
2400 
2401     if (fclose(it8 ->FileStack[0]->Stream)!= 0) {
2402             cmsIT8Free(hIT8);
2403             return NULL;
2404     }
2405 
2406     return hIT8;
2407 
2408 }
2409 
cmsIT8EnumDataFormat(cmsHANDLE hIT8,char *** SampleNames)2410 int CMSEXPORT cmsIT8EnumDataFormat(cmsHANDLE hIT8, char ***SampleNames)
2411 {
2412     cmsIT8* it8 = (cmsIT8*) hIT8;
2413     TABLE* t;
2414 
2415     _cmsAssert(hIT8 != NULL);
2416 
2417     t = GetTable(it8);
2418 
2419     if (SampleNames)
2420         *SampleNames = t -> DataFormat;
2421     return t -> nSamples;
2422 }
2423 
2424 
cmsIT8EnumProperties(cmsHANDLE hIT8,char *** PropertyNames)2425 cmsUInt32Number CMSEXPORT cmsIT8EnumProperties(cmsHANDLE hIT8, char ***PropertyNames)
2426 {
2427     cmsIT8* it8 = (cmsIT8*) hIT8;
2428     KEYVALUE* p;
2429     cmsUInt32Number n;
2430     char **Props;
2431     TABLE* t;
2432 
2433     _cmsAssert(hIT8 != NULL);
2434 
2435     t = GetTable(it8);
2436 
2437     // Pass#1 - count properties
2438 
2439     n = 0;
2440     for (p = t -> HeaderList;  p != NULL; p = p->Next) {
2441         n++;
2442     }
2443 
2444 
2445     Props = (char **) AllocChunk(it8, sizeof(char *) * n);
2446 
2447     // Pass#2 - Fill pointers
2448     n = 0;
2449     for (p = t -> HeaderList;  p != NULL; p = p->Next) {
2450         Props[n++] = p -> Keyword;
2451     }
2452 
2453     *PropertyNames = Props;
2454     return n;
2455 }
2456 
cmsIT8EnumPropertyMulti(cmsHANDLE hIT8,const char * cProp,const char *** SubpropertyNames)2457 cmsUInt32Number CMSEXPORT cmsIT8EnumPropertyMulti(cmsHANDLE hIT8, const char* cProp, const char ***SubpropertyNames)
2458 {
2459     cmsIT8* it8 = (cmsIT8*) hIT8;
2460     KEYVALUE *p, *tmp;
2461     cmsUInt32Number n;
2462     const char **Props;
2463     TABLE* t;
2464 
2465     _cmsAssert(hIT8 != NULL);
2466 
2467 
2468     t = GetTable(it8);
2469 
2470     if(!IsAvailableOnList(t->HeaderList, cProp, NULL, &p)) {
2471         *SubpropertyNames = 0;
2472         return 0;
2473     }
2474 
2475     // Pass#1 - count properties
2476 
2477     n = 0;
2478     for (tmp = p;  tmp != NULL; tmp = tmp->NextSubkey) {
2479         if(tmp->Subkey != NULL)
2480             n++;
2481     }
2482 
2483 
2484     Props = (const char **) AllocChunk(it8, sizeof(char *) * n);
2485 
2486     // Pass#2 - Fill pointers
2487     n = 0;
2488     for (tmp = p;  tmp != NULL; tmp = tmp->NextSubkey) {
2489         if(tmp->Subkey != NULL)
2490             Props[n++] = p ->Subkey;
2491     }
2492 
2493     *SubpropertyNames = Props;
2494     return n;
2495 }
2496 
2497 static
LocatePatch(cmsIT8 * it8,const char * cPatch)2498 int LocatePatch(cmsIT8* it8, const char* cPatch)
2499 {
2500     int i;
2501     const char *data;
2502     TABLE* t = GetTable(it8);
2503 
2504     for (i=0; i < t-> nPatches; i++) {
2505 
2506         data = GetData(it8, i, t->SampleID);
2507 
2508         if (data != NULL) {
2509 
2510                 if (cmsstrcasecmp(data, cPatch) == 0)
2511                         return i;
2512                 }
2513         }
2514 
2515         // SynError(it8, "Couldn't find patch '%s'\n", cPatch);
2516         return -1;
2517 }
2518 
2519 
2520 static
LocateEmptyPatch(cmsIT8 * it8)2521 int LocateEmptyPatch(cmsIT8* it8)
2522 {
2523     int i;
2524     const char *data;
2525     TABLE* t = GetTable(it8);
2526 
2527     for (i=0; i < t-> nPatches; i++) {
2528 
2529         data = GetData(it8, i, t->SampleID);
2530 
2531         if (data == NULL)
2532             return i;
2533 
2534     }
2535 
2536     return -1;
2537 }
2538 
2539 static
LocateSample(cmsIT8 * it8,const char * cSample)2540 int LocateSample(cmsIT8* it8, const char* cSample)
2541 {
2542     int i;
2543     const char *fld;
2544     TABLE* t = GetTable(it8);
2545 
2546     for (i=0; i < t->nSamples; i++) {
2547 
2548         fld = GetDataFormat(it8, i);
2549         if (fld != NULL) {
2550             if (cmsstrcasecmp(fld, cSample) == 0)
2551                 return i;
2552         }
2553     }
2554 
2555     return -1;
2556 
2557 }
2558 
2559 
cmsIT8FindDataFormat(cmsHANDLE hIT8,const char * cSample)2560 int CMSEXPORT cmsIT8FindDataFormat(cmsHANDLE hIT8, const char* cSample)
2561 {
2562     cmsIT8* it8 = (cmsIT8*) hIT8;
2563 
2564     _cmsAssert(hIT8 != NULL);
2565 
2566     return LocateSample(it8, cSample);
2567 }
2568 
2569 
2570 
cmsIT8GetDataRowCol(cmsHANDLE hIT8,int row,int col)2571 const char* CMSEXPORT cmsIT8GetDataRowCol(cmsHANDLE hIT8, int row, int col)
2572 {
2573     cmsIT8* it8 = (cmsIT8*) hIT8;
2574 
2575     _cmsAssert(hIT8 != NULL);
2576 
2577     return GetData(it8, row, col);
2578 }
2579 
2580 
cmsIT8GetDataRowColDbl(cmsHANDLE hIT8,int row,int col)2581 cmsFloat64Number CMSEXPORT cmsIT8GetDataRowColDbl(cmsHANDLE hIT8, int row, int col)
2582 {
2583     const char* Buffer;
2584 
2585     Buffer = cmsIT8GetDataRowCol(hIT8, row, col);
2586 
2587     if (Buffer == NULL) return 0.0;
2588 
2589     return ParseFloatNumber(Buffer);
2590 }
2591 
2592 
cmsIT8SetDataRowCol(cmsHANDLE hIT8,int row,int col,const char * Val)2593 cmsBool CMSEXPORT cmsIT8SetDataRowCol(cmsHANDLE hIT8, int row, int col, const char* Val)
2594 {
2595     cmsIT8* it8 = (cmsIT8*) hIT8;
2596 
2597     _cmsAssert(hIT8 != NULL);
2598 
2599     return SetData(it8, row, col, Val);
2600 }
2601 
2602 
cmsIT8SetDataRowColDbl(cmsHANDLE hIT8,int row,int col,cmsFloat64Number Val)2603 cmsBool CMSEXPORT cmsIT8SetDataRowColDbl(cmsHANDLE hIT8, int row, int col, cmsFloat64Number Val)
2604 {
2605     cmsIT8* it8 = (cmsIT8*) hIT8;
2606     char Buff[256];
2607 
2608     _cmsAssert(hIT8 != NULL);
2609 
2610     snprintf(Buff, 255, it8->DoubleFormatter, Val);
2611 
2612     return SetData(it8, row, col, Buff);
2613 }
2614 
2615 
2616 
cmsIT8GetData(cmsHANDLE hIT8,const char * cPatch,const char * cSample)2617 const char* CMSEXPORT cmsIT8GetData(cmsHANDLE hIT8, const char* cPatch, const char* cSample)
2618 {
2619     cmsIT8* it8 = (cmsIT8*) hIT8;
2620     int iField, iSet;
2621 
2622     _cmsAssert(hIT8 != NULL);
2623 
2624     iField = LocateSample(it8, cSample);
2625     if (iField < 0) {
2626         return NULL;
2627     }
2628 
2629     iSet = LocatePatch(it8, cPatch);
2630     if (iSet < 0) {
2631             return NULL;
2632     }
2633 
2634     return GetData(it8, iSet, iField);
2635 }
2636 
2637 
cmsIT8GetDataDbl(cmsHANDLE it8,const char * cPatch,const char * cSample)2638 cmsFloat64Number CMSEXPORT cmsIT8GetDataDbl(cmsHANDLE  it8, const char* cPatch, const char* cSample)
2639 {
2640     const char* Buffer;
2641 
2642     Buffer = cmsIT8GetData(it8, cPatch, cSample);
2643 
2644     return ParseFloatNumber(Buffer);
2645 }
2646 
2647 
2648 
cmsIT8SetData(cmsHANDLE hIT8,const char * cPatch,const char * cSample,const char * Val)2649 cmsBool CMSEXPORT cmsIT8SetData(cmsHANDLE hIT8, const char* cPatch, const char* cSample, const char *Val)
2650 {
2651     cmsIT8* it8 = (cmsIT8*) hIT8;
2652     int iField, iSet;
2653     TABLE* t;
2654 
2655     _cmsAssert(hIT8 != NULL);
2656 
2657     t = GetTable(it8);
2658 
2659     iField = LocateSample(it8, cSample);
2660 
2661     if (iField < 0)
2662         return FALSE;
2663 
2664     if (t-> nPatches == 0) {
2665 
2666         AllocateDataFormat(it8);
2667         AllocateDataSet(it8);
2668         CookPointers(it8);
2669     }
2670 
2671     if (cmsstrcasecmp(cSample, "SAMPLE_ID") == 0) {
2672 
2673         iSet   = LocateEmptyPatch(it8);
2674         if (iSet < 0) {
2675             return SynError(it8, "Couldn't add more patches '%s'\n", cPatch);
2676         }
2677 
2678         iField = t -> SampleID;
2679     }
2680     else {
2681         iSet = LocatePatch(it8, cPatch);
2682         if (iSet < 0) {
2683             return FALSE;
2684         }
2685     }
2686 
2687     return SetData(it8, iSet, iField, Val);
2688 }
2689 
2690 
cmsIT8SetDataDbl(cmsHANDLE hIT8,const char * cPatch,const char * cSample,cmsFloat64Number Val)2691 cmsBool CMSEXPORT cmsIT8SetDataDbl(cmsHANDLE hIT8, const char* cPatch,
2692                                    const char* cSample,
2693                                    cmsFloat64Number Val)
2694 {
2695     cmsIT8* it8 = (cmsIT8*) hIT8;
2696     char Buff[256];
2697 
2698     _cmsAssert(hIT8 != NULL);
2699 
2700     snprintf(Buff, 255, it8->DoubleFormatter, Val);
2701     return cmsIT8SetData(hIT8, cPatch, cSample, Buff);
2702 }
2703 
2704 // Buffer should get MAXSTR at least
2705 
cmsIT8GetPatchName(cmsHANDLE hIT8,int nPatch,char * buffer)2706 const char* CMSEXPORT cmsIT8GetPatchName(cmsHANDLE hIT8, int nPatch, char* buffer)
2707 {
2708     cmsIT8* it8 = (cmsIT8*) hIT8;
2709     TABLE* t;
2710     char* Data;
2711 
2712     _cmsAssert(hIT8 != NULL);
2713 
2714     t = GetTable(it8);
2715     Data = GetData(it8, nPatch, t->SampleID);
2716 
2717     if (!Data) return NULL;
2718     if (!buffer) return Data;
2719 
2720     strncpy(buffer, Data, MAXSTR-1);
2721     buffer[MAXSTR-1] = 0;
2722     return buffer;
2723 }
2724 
cmsIT8GetPatchByName(cmsHANDLE hIT8,const char * cPatch)2725 int CMSEXPORT cmsIT8GetPatchByName(cmsHANDLE hIT8, const char *cPatch)
2726 {
2727     _cmsAssert(hIT8 != NULL);
2728 
2729     return LocatePatch((cmsIT8*)hIT8, cPatch);
2730 }
2731 
cmsIT8TableCount(cmsHANDLE hIT8)2732 cmsUInt32Number CMSEXPORT cmsIT8TableCount(cmsHANDLE hIT8)
2733 {
2734     cmsIT8* it8 = (cmsIT8*) hIT8;
2735 
2736     _cmsAssert(hIT8 != NULL);
2737 
2738     return it8 ->TablesCount;
2739 }
2740 
2741 // This handles the "LABEL" extension.
2742 // Label, nTable, Type
2743 
cmsIT8SetTableByLabel(cmsHANDLE hIT8,const char * cSet,const char * cField,const char * ExpectedType)2744 int CMSEXPORT cmsIT8SetTableByLabel(cmsHANDLE hIT8, const char* cSet, const char* cField, const char* ExpectedType)
2745 {
2746     const char* cLabelFld;
2747     char Type[256], Label[256];
2748     cmsUInt32Number nTable;
2749 
2750     _cmsAssert(hIT8 != NULL);
2751 
2752     if (cField != NULL && *cField == 0)
2753             cField = "LABEL";
2754 
2755     if (cField == NULL)
2756             cField = "LABEL";
2757 
2758     cLabelFld = cmsIT8GetData(hIT8, cSet, cField);
2759     if (!cLabelFld) return -1;
2760 
2761     if (sscanf(cLabelFld, "%255s %u %255s", Label, &nTable, Type) != 3)
2762             return -1;
2763 
2764     if (ExpectedType != NULL && *ExpectedType == 0)
2765         ExpectedType = NULL;
2766 
2767     if (ExpectedType) {
2768 
2769         if (cmsstrcasecmp(Type, ExpectedType) != 0) return -1;
2770     }
2771 
2772     return cmsIT8SetTable(hIT8, nTable);
2773 }
2774 
2775 
cmsIT8SetIndexColumn(cmsHANDLE hIT8,const char * cSample)2776 cmsBool CMSEXPORT cmsIT8SetIndexColumn(cmsHANDLE hIT8, const char* cSample)
2777 {
2778     cmsIT8* it8 = (cmsIT8*) hIT8;
2779     int pos;
2780 
2781     _cmsAssert(hIT8 != NULL);
2782 
2783     pos = LocateSample(it8, cSample);
2784     if(pos == -1)
2785         return FALSE;
2786 
2787     it8->Tab[it8->nTable].SampleID = pos;
2788     return TRUE;
2789 }
2790 
2791 
cmsIT8DefineDblFormat(cmsHANDLE hIT8,const char * Formatter)2792 void CMSEXPORT cmsIT8DefineDblFormat(cmsHANDLE hIT8, const char* Formatter)
2793 {
2794     cmsIT8* it8 = (cmsIT8*) hIT8;
2795 
2796     _cmsAssert(hIT8 != NULL);
2797 
2798     if (Formatter == NULL)
2799         strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT);
2800     else
2801         strncpy(it8->DoubleFormatter, Formatter, sizeof(it8->DoubleFormatter));
2802 
2803     it8 ->DoubleFormatter[sizeof(it8 ->DoubleFormatter)-1] = 0;
2804 }
2805 
2806