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