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