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