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