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