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