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