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