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