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