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