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