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