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