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