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