cmscgats.c revision 11948:691879ceb811
129088Smarkm/*
229088Smarkm * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
329088Smarkm *
429088Smarkm * This code is free software; you can redistribute it and/or modify it
529088Smarkm * under the terms of the GNU General Public License version 2 only, as
629088Smarkm * published by the Free Software Foundation.  Oracle designates this
729088Smarkm * particular file as subject to the "Classpath" exception as provided
829088Smarkm * by Oracle in the LICENSE file that accompanied this code.
929088Smarkm *
1029088Smarkm * This code is distributed in the hope that it will be useful, but WITHOUT
1129088Smarkm * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1229088Smarkm * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1329088Smarkm * version 2 for more details (a copy is included in the LICENSE file that
1429088Smarkm * accompanied this code).
1529088Smarkm *
1629088Smarkm * You should have received a copy of the GNU General Public License version
1729088Smarkm * 2 along with this work; if not, write to the Free Software Foundation,
1829088Smarkm * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
1929088Smarkm *
2029088Smarkm * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2129088Smarkm * or visit www.oracle.com if you need additional information or have any
2229088Smarkm * questions.
2329088Smarkm */
2429088Smarkm
2529088Smarkm// This file is available under and governed by the GNU General Public
2629088Smarkm// License version 2 only, as published by the Free Software Foundation.
2729088Smarkm// However, the following notice accompanied the original version of this
2829088Smarkm// file:
2929088Smarkm//
3029088Smarkm//---------------------------------------------------------------------------------
3129088Smarkm//
3229088Smarkm//  Little Color Management System
3329088Smarkm//  Copyright (c) 1998-2012 Marti Maria Saguer
3484305Smarkm//
3587139Smarkm// Permission is hereby granted, free of charge, to any person obtaining
3684305Smarkm// a copy of this software and associated documentation files (the "Software"),
3784305Smarkm// to deal in the Software without restriction, including without limitation
3829088Smarkm// the rights to use, copy, modify, merge, publish, distribute, sublicense,
3963248Speter// and/or sell copies of the Software, and to permit persons to whom the Software
4029181Smarkm// is furnished to do so, subject to the following conditions:
4163248Speter//
4229088Smarkm// The above copyright notice and this permission notice shall be included in
4329088Smarkm// all copies or substantial portions of the Software.
4429088Smarkm//
4529088Smarkm// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
4629088Smarkm// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
4729088Smarkm// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
4829088Smarkm// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
4929088Smarkm// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
5029088Smarkm// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
5129088Smarkm// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
5229088Smarkm//
5329088Smarkm//---------------------------------------------------------------------------------
5429088Smarkm//
5529088Smarkm
5629088Smarkm#include "lcms2_internal.h"
5729088Smarkm
5829088Smarkm
5929088Smarkm// IT8.7 / CGATS.17-200x handling -----------------------------------------------------------------------------
6029088Smarkm
6129088Smarkm
6229088Smarkm#define MAXID        128     // Max length of identifier
6329088Smarkm#define MAXSTR      1024     // Max length of string
6429088Smarkm#define MAXTABLES    255     // Max Number of tables in a single stream
6529088Smarkm#define MAXINCLUDE    20     // Max number of nested includes
6698884Smarkm
6729088Smarkm#define DEFAULT_DBL_FORMAT  "%.10g" // Double formatting
6887139Smarkm
6929181Smarkm#ifdef CMS_IS_WINDOWS_
7087139Smarkm#    include <io.h>
7187139Smarkm#    define DIR_CHAR    '\\'
7229088Smarkm#else
7329088Smarkm#    define DIR_CHAR    '/'
7429088Smarkm#endif
7529088Smarkm
7629088Smarkm
7729088Smarkm// Symbols
7829088Smarkmtypedef enum {
7929088Smarkm
8087155Smarkm        SUNDEFINED,
8187155Smarkm        SINUM,      // Integer
8229088Smarkm        SDNUM,      // Real
8329181Smarkm        SIDENT,     // Identifier
8429181Smarkm        SSTRING,    // string
8529181Smarkm        SCOMMENT,   // comment
8629181Smarkm        SEOLN,      // End of line
8729181Smarkm        SEOF,       // End of stream
8829181Smarkm        SSYNERROR,  // Syntax error found on stream
8929181Smarkm
9029181Smarkm        // Keywords
9129088Smarkm
9229088Smarkm        SBEGIN_DATA,
9329088Smarkm        SBEGIN_DATA_FORMAT,
9429088Smarkm        SEND_DATA,
9529088Smarkm        SEND_DATA_FORMAT,
9629088Smarkm        SKEYWORD,
9729088Smarkm        SDATA_FORMAT_ID,
9829088Smarkm        SINCLUDE
9987139Smarkm
10029088Smarkm    } SYMBOL;
10129088Smarkm
10229088Smarkm
10398884Smarkm// How to write the value
10449887Snsayertypedef enum {
10549887Snsayer
10698884Smarkm        WRITE_UNCOOKED,
10749887Snsayer        WRITE_STRINGIFY,
10849887Snsayer        WRITE_HEXADECIMAL,
10949887Snsayer        WRITE_BINARY,
11098884Smarkm        WRITE_PAIR
11198884Smarkm
11229088Smarkm    } WRITEMODE;
11329088Smarkm
11429088Smarkm// Linked list of variable names
11598884Smarkmtypedef struct _KeyVal {
11698884Smarkm
11729088Smarkm        struct _KeyVal*  Next;
11829088Smarkm        char*            Keyword;       // Name of variable
11929088Smarkm        struct _KeyVal*  NextSubkey;    // If key is a dictionary, points to the next item
12029088Smarkm        char*            Subkey;        // If key is a dictionary, points to the subkey name
12129088Smarkm        char*            Value;         // Points to value
12229088Smarkm        WRITEMODE        WriteAs;       // How to write the value
12329088Smarkm
12429088Smarkm   } KEYVALUE;
12529088Smarkm
12629088Smarkm
12729088Smarkm// Linked list of memory chunks (Memory sink)
12829088Smarkmtypedef struct _OwnedMem {
12929088Smarkm
13029088Smarkm        struct _OwnedMem* Next;
13129088Smarkm        void *            Ptr;          // Point to value
13229088Smarkm
13329088Smarkm   } OWNEDMEM;
13429088Smarkm
13529088Smarkm// Suballocator
13629088Smarkmtypedef struct _SubAllocator {
13729088Smarkm
13829088Smarkm         cmsUInt8Number* Block;
13987139Smarkm         cmsUInt32Number BlockSize;
14029088Smarkm         cmsUInt32Number Used;
14129088Smarkm
14229088Smarkm    } SUBALLOCATOR;
14329088Smarkm
14429088Smarkm// Table. Each individual table can hold properties and rows & cols
14529088Smarkmtypedef struct _Table {
14629088Smarkm
14729088Smarkm        char SheetType[MAXSTR];               // The first row of the IT8 (the type)
14887139Smarkm
14987139Smarkm        int            nSamples, nPatches;    // Cols, Rows
15029088Smarkm        int            SampleID;              // Pos of ID
15129088Smarkm
15229088Smarkm        KEYVALUE*      HeaderList;            // The properties
15387139Smarkm
15429088Smarkm        char**         DataFormat;            // The binary stream descriptor
15529088Smarkm        char**         Data;                  // The binary stream
15629088Smarkm
15729088Smarkm    } TABLE;
15829088Smarkm
15929088Smarkm// File stream being parsed
16087139Smarkmtypedef struct _FileContext {
16187139Smarkm        char           FileName[cmsMAX_PATH];    // File name if being readed from file
16229088Smarkm        FILE*          Stream;                   // File stream or NULL if holded in memory
16329088Smarkm    } FILECTX;
16429088Smarkm
16587139Smarkm// This struct hold all information about an open IT8 handler.
16629088Smarkmtypedef struct {
16729088Smarkm
16829088Smarkm
16929088Smarkm        cmsUInt32Number  TablesCount;                     // How many tables in this stream
17029088Smarkm        cmsUInt32Number  nTable;                          // The actual table
17129088Smarkm
17229088Smarkm        TABLE Tab[MAXTABLES];
17329088Smarkm
17429088Smarkm        // Memory management
17529088Smarkm        OWNEDMEM*      MemorySink;            // The storage backend
17629088Smarkm        SUBALLOCATOR   Allocator;             // String suballocator -- just to keep it fast
17729088Smarkm
17829088Smarkm        // Parser state machine
17987139Smarkm        SYMBOL         sy;                    // Current symbol
18029088Smarkm        int            ch;                    // Current character
18129088Smarkm
18229088Smarkm        int            inum;                  // integer value
18329088Smarkm        cmsFloat64Number         dnum;                  // real value
18429088Smarkm        char           id[MAXID];             // identifier
18529181Smarkm        char           str[MAXSTR];           // string
18629181Smarkm
18787139Smarkm        // Allowed keywords & datasets. They have visibility on whole stream
18887139Smarkm        KEYVALUE*     ValidKeywords;
18929088Smarkm        KEYVALUE*     ValidSampleID;
19029088Smarkm
19129088Smarkm        char*          Source;                // Points to loc. being parsed
19229088Smarkm        int            lineno;                // line counter for error reporting
19329088Smarkm
19429088Smarkm        FILECTX*       FileStack[MAXINCLUDE]; // Stack of files being parsed
19529088Smarkm        int            IncludeSP;             // Include Stack Pointer
19629088Smarkm
19729088Smarkm        char*          MemoryBlock;           // The stream if holded in memory
19829088Smarkm
19929088Smarkm        char           DoubleFormatter[MAXID];// Printf-like 'cmsFloat64Number' formatter
20029088Smarkm
20129088Smarkm        cmsContext    ContextID;              // The threading context
20229088Smarkm
20329088Smarkm   } cmsIT8;
20429088Smarkm
20529088Smarkm
20629088Smarkm// The stream for save operations
20729088Smarkmtypedef struct {
20829088Smarkm
20929088Smarkm        FILE* stream;   // For save-to-file behaviour
21029088Smarkm
21129088Smarkm        cmsUInt8Number* Base;
21229088Smarkm        cmsUInt8Number* Ptr;        // For save-to-mem behaviour
21329088Smarkm        cmsUInt32Number Used;
21429088Smarkm        cmsUInt32Number Max;
21529088Smarkm
21629088Smarkm    } SAVESTREAM;
21729088Smarkm
21829088Smarkm
21929088Smarkm// ------------------------------------------------------ cmsIT8 parsing routines
22087139Smarkm
22187139Smarkm
22229088Smarkm// A keyword
22329088Smarkmtypedef struct {
22429088Smarkm
22529088Smarkm        const char *id;
22629088Smarkm        SYMBOL sy;
22729088Smarkm
22829088Smarkm   } KEYWORD;
22929088Smarkm
23029088Smarkm// The keyword->symbol translation table. Sorting is required.
23129088Smarkmstatic const KEYWORD TabKeys[] = {
23287139Smarkm
23387139Smarkm        {"$INCLUDE",               SINCLUDE},   // This is an extension!
23429088Smarkm        {".INCLUDE",               SINCLUDE},   // This is an extension!
23529088Smarkm
23629088Smarkm        {"BEGIN_DATA",             SBEGIN_DATA },
23729088Smarkm        {"BEGIN_DATA_FORMAT",      SBEGIN_DATA_FORMAT },
23829088Smarkm        {"DATA_FORMAT_IDENTIFIER", SDATA_FORMAT_ID},
23929088Smarkm        {"END_DATA",               SEND_DATA},
24029088Smarkm        {"END_DATA_FORMAT",        SEND_DATA_FORMAT},
24129088Smarkm        {"KEYWORD",                SKEYWORD}
24229088Smarkm        };
24329088Smarkm
24429088Smarkm#define NUMKEYS (sizeof(TabKeys)/sizeof(KEYWORD))
24587139Smarkm
24687139Smarkm// Predefined properties
24729088Smarkm
24887139Smarkm// A property
24929088Smarkmtypedef struct {
25029088Smarkm        const char *id;    // The identifier
25129088Smarkm        WRITEMODE as;      // How is supposed to be written
25229088Smarkm    } PROPERTY;
25329088Smarkm
25429181Smarkmstatic PROPERTY PredefinedProperties[] = {
25529088Smarkm
25629088Smarkm        {"NUMBER_OF_FIELDS", WRITE_UNCOOKED},    // Required - NUMBER OF FIELDS
25729181Smarkm        {"NUMBER_OF_SETS",   WRITE_UNCOOKED},    // Required - NUMBER OF SETS
25829088Smarkm        {"ORIGINATOR",       WRITE_STRINGIFY},   // Required - Identifies the specific system, organization or individual that created the data file.
25929088Smarkm        {"FILE_DESCRIPTOR",  WRITE_STRINGIFY},   // Required - Describes the purpose or contents of the data file.
26029088Smarkm        {"CREATED",          WRITE_STRINGIFY},   // Required - Indicates date of creation of the data file.
26129088Smarkm        {"DESCRIPTOR",       WRITE_STRINGIFY},   // Required  - Describes the purpose or contents of the data file.
26229088Smarkm        {"DIFFUSE_GEOMETRY", WRITE_STRINGIFY},   // The diffuse geometry used. Allowed values are "sphere" or "opal".
26329088Smarkm        {"MANUFACTURER",     WRITE_STRINGIFY},
26429088Smarkm        {"MANUFACTURE",      WRITE_STRINGIFY},   // Some broken Fuji targets does store this value
26529088Smarkm        {"PROD_DATE",        WRITE_STRINGIFY},   // Identifies year and month of production of the target in the form yyyy:mm.
26629088Smarkm        {"SERIAL",           WRITE_STRINGIFY},   // Uniquely identifies individual physical target.
26729088Smarkm
26829088Smarkm        {"MATERIAL",         WRITE_STRINGIFY},   // Identifies the material on which the target was produced using a code
26929088Smarkm                               // uniquely identifying th e material. This is intend ed to be used for IT8.7
27029088Smarkm                               // physical targets only (i.e . IT8.7/1 a nd IT8.7/2).
27129088Smarkm
27229088Smarkm        {"INSTRUMENTATION",  WRITE_STRINGIFY},   // Used to report the specific instrumentation used (manufacturer and
27329088Smarkm                               // model number) to generate the data reported. This data will often
27429088Smarkm                               // provide more information about the particular data collected than an
27529088Smarkm                               // extensive list of specific details. This is particularly important for
27629088Smarkm                               // spectral data or data derived from spectrophotometry.
27729088Smarkm
27887139Smarkm        {"MEASUREMENT_SOURCE", WRITE_STRINGIFY}, // Illumination used for spectral measurements. This data helps provide
27987139Smarkm                               // a guide to the potential for issues of paper fluorescence, etc.
28029088Smarkm
28187139Smarkm        {"PRINT_CONDITIONS", WRITE_STRINGIFY},   // Used to define the characteristics of the printed sheet being reported.
28229088Smarkm                               // Where standard conditions have been defined (e.g., SWOP at nominal)
28329088Smarkm                               // named conditions may suffice. Otherwise, detailed information is
28429088Smarkm                               // needed.
28529088Smarkm
28629088Smarkm        {"SAMPLE_BACKING",   WRITE_STRINGIFY},   // Identifies the backing material used behind the sample during
28729181Smarkm                               // measurement. Allowed values are �black�, �white�, or {"na".
28829088Smarkm
28929088Smarkm        {"CHISQ_DOF",        WRITE_STRINGIFY},   // Degrees of freedom associated with the Chi squared statistic
29029181Smarkm
29129088Smarkm       // below properties are new in recent specs:
29229088Smarkm
29329088Smarkm        {"MEASUREMENT_GEOMETRY", WRITE_STRINGIFY}, // The type of measurement, either reflection or transmission, should be indicated
29429088Smarkm                               // along with details of the geometry and the aperture size and shape. For example,
29529088Smarkm                               // for transmission measurements it is important to identify 0/diffuse, diffuse/0,
29629088Smarkm                               // opal or integrating sphere, etc. For reflection it is important to identify 0/45,
29729088Smarkm                               // 45/0, sphere (specular included or excluded), etc.
29829088Smarkm
29929088Smarkm       {"FILTER",            WRITE_STRINGIFY},   // Identifies the use of physical filter(s) during measurement. Typically used to
30029088Smarkm                               // denote the use of filters such as none, D65, Red, Green or Blue.
30129088Smarkm
30229088Smarkm       {"POLARIZATION",      WRITE_STRINGIFY},   // Identifies the use of a physical polarization filter during measurement. Allowed
30329088Smarkm                               // values are {"yes�, �white�, �none� or �na�.
30429088Smarkm
30529088Smarkm       {"WEIGHTING_FUNCTION", WRITE_PAIR},   // Indicates such functions as: the CIE standard observer functions used in the
30629088Smarkm                               // calculation of various data parameters (2 degree and 10 degree), CIE standard
30729088Smarkm                               // illuminant functions used in the calculation of various data parameters (e.g., D50,
30829088Smarkm                               // D65, etc.), density status response, etc. If used there shall be at least one
30987139Smarkm                               // name-value pair following the WEIGHTING_FUNCTION tag/keyword. The first attribute
31087139Smarkm                               // in the set shall be {"name" and shall identify the particular parameter used.
31129088Smarkm                               // The second shall be {"value" and shall provide the value associated with that name.
31287139Smarkm                               // For ASCII data, a string containing the Name and Value attribute pairs shall follow
31329088Smarkm                               // the weighting function keyword. A semi-colon separates attribute pairs from each
31429088Smarkm                               // other and within the attribute the name and value are separated by a comma.
31529088Smarkm
31629088Smarkm       {"COMPUTATIONAL_PARAMETER", WRITE_PAIR}, // Parameter that is used in computing a value from measured data. Name is the name
31729088Smarkm                               // of the calculation, parameter is the name of the parameter used in the calculation
31829088Smarkm                               // and value is the value of the parameter.
31929088Smarkm
32029088Smarkm       {"TARGET_TYPE",        WRITE_STRINGIFY},  // The type of target being measured, e.g. IT8.7/1, IT8.7/3, user defined, etc.
32129088Smarkm
32229088Smarkm       {"COLORANT",           WRITE_STRINGIFY},  // Identifies the colorant(s) used in creating the target.
32329088Smarkm
32429088Smarkm       {"TABLE_DESCRIPTOR",   WRITE_STRINGIFY},  // Describes the purpose or contents of a data table.
32529088Smarkm
32629088Smarkm       {"TABLE_NAME",         WRITE_STRINGIFY}   // Provides a short name for a data table.
32729088Smarkm};
32829088Smarkm
32929088Smarkm#define NUMPREDEFINEDPROPS (sizeof(PredefinedProperties)/sizeof(PROPERTY))
33087139Smarkm
33187139Smarkm
33229088Smarkm// Predefined sample types on dataset
33329088Smarkmstatic const char* PredefinedSampleID[] = {
33429088Smarkm        "SAMPLE_ID",      // Identifies sample that data represents
33529088Smarkm        "STRING",         // Identifies label, or other non-machine readable value.
33629088Smarkm                          // Value must begin and end with a " symbol
33729088Smarkm
33829088Smarkm        "CMYK_C",         // Cyan component of CMYK data expressed as a percentage
33929088Smarkm        "CMYK_M",         // Magenta component of CMYK data expressed as a percentage
34029088Smarkm        "CMYK_Y",         // Yellow component of CMYK data expressed as a percentage
34187139Smarkm        "CMYK_K",         // Black component of CMYK data expressed as a percentage
34287139Smarkm        "D_RED",          // Red filter density
34329088Smarkm        "D_GREEN",        // Green filter density
34429088Smarkm        "D_BLUE",         // Blue filter density
34529088Smarkm        "D_VIS",          // Visual filter density
34629088Smarkm        "D_MAJOR_FILTER", // Major filter d ensity
34729088Smarkm        "RGB_R",          // Red component of RGB data
34829088Smarkm        "RGB_G",          // Green component of RGB data
34929088Smarkm        "RGB_B",          // Blue com ponent of RGB data
35029088Smarkm        "SPECTRAL_NM",    // Wavelength of measurement expressed in nanometers
35129088Smarkm        "SPECTRAL_PCT",   // Percentage reflectance/transmittance
35287139Smarkm        "SPECTRAL_DEC",   // Reflectance/transmittance
35387139Smarkm        "XYZ_X",          // X component of tristimulus data
35429088Smarkm        "XYZ_Y",          // Y component of tristimulus data
35529088Smarkm        "XYZ_Z",          // Z component of tristimulus data
35629088Smarkm        "XYY_X",          // x component of chromaticity data
35729088Smarkm        "XYY_Y",          // y component of chromaticity data
35829088Smarkm        "XYY_CAPY",       // Y component of tristimulus data
35929088Smarkm        "LAB_L",          // L* component of Lab data
36029088Smarkm        "LAB_A",          // a* component of Lab data
36129088Smarkm        "LAB_B",          // b* component of Lab data
36229088Smarkm        "LAB_C",          // C*ab component of Lab data
36329088Smarkm        "LAB_H",          // hab component of Lab data
36429088Smarkm        "LAB_DE",         // CIE dE
36529088Smarkm        "LAB_DE_94",      // CIE dE using CIE 94
36629088Smarkm        "LAB_DE_CMC",     // dE using CMC
36729088Smarkm        "LAB_DE_2000",    // CIE dE using CIE DE 2000
36829088Smarkm        "MEAN_DE",        // Mean Delta E (LAB_DE) of samples compared to batch average
36929088Smarkm                          // (Used for data files for ANSI IT8.7/1 and IT8.7/2 targets)
37029088Smarkm        "STDEV_X",        // Standard deviation of X (tristimulus data)
37129088Smarkm        "STDEV_Y",        // Standard deviation of Y (tristimulus data)
37229088Smarkm        "STDEV_Z",        // Standard deviation of Z (tristimulus data)
37387139Smarkm        "STDEV_L",        // Standard deviation of L*
37487139Smarkm        "STDEV_A",        // Standard deviation of a*
37529088Smarkm        "STDEV_B",        // Standard deviation of b*
37629088Smarkm        "STDEV_DE",       // Standard deviation of CIE dE
37729088Smarkm        "CHI_SQD_PAR"};   // The average of the standard deviations of L*, a* and b*. It is
37829088Smarkm                          // used to derive an estimate of the chi-squared parameter which is
37929088Smarkm                          // recommended as the predictor of the variability of dE
38087139Smarkm
38187139Smarkm#define NUMPREDEFINEDSAMPLEID (sizeof(PredefinedSampleID)/sizeof(char *))
38229088Smarkm
38329088Smarkm//Forward declaration of some internal functions
38429088Smarkmstatic void* AllocChunk(cmsIT8* it8, cmsUInt32Number size);
38529088Smarkm
38629088Smarkm// Checks whatever c is a separator
38787139Smarkmstatic
38887139SmarkmcmsBool isseparator(int c)
38929088Smarkm{
39029088Smarkm    return (c == ' ') || (c == '\t') ;
39129088Smarkm}
39229088Smarkm
39329088Smarkm// Checks whatever c is a valid identifier char
39429088Smarkmstatic
39529088SmarkmcmsBool ismiddle(int c)
39629088Smarkm{
39729088Smarkm   return (!isseparator(c) && (c != '#') && (c !='\"') && (c != '\'') && (c > 32) && (c < 127));
39887139Smarkm}
39987139Smarkm
40029088Smarkm// Checks whatsever c is a valid identifier middle char.
40129088Smarkmstatic
40229088SmarkmcmsBool isidchar(int c)
40329088Smarkm{
40429088Smarkm   return isalnum(c) || ismiddle(c);
40529088Smarkm}
40629088Smarkm
40729088Smarkm// Checks whatsever c is a valid identifier first char.
40829088Smarkmstatic
40929088SmarkmcmsBool isfirstidchar(int c)
41029088Smarkm{
41129088Smarkm     return !isdigit(c) && ismiddle(c);
41229088Smarkm}
41329088Smarkm
41429088Smarkm// Guess whether the supplied path looks like an absolute path
41529088Smarkmstatic
41629088SmarkmcmsBool isabsolutepath(const char *path)
41729088Smarkm{
41829088Smarkm    char ThreeChars[4];
41929088Smarkm
42087139Smarkm    if(path == NULL)
42187139Smarkm        return FALSE;
42229088Smarkm    if (path[0] == 0)
42329088Smarkm        return FALSE;
42429088Smarkm
42529088Smarkm    strncpy(ThreeChars, path, 3);
42629088Smarkm    ThreeChars[3] = 0;
42729088Smarkm
42829088Smarkm    if(ThreeChars[0] == DIR_CHAR)
42929088Smarkm        return TRUE;
43029088Smarkm
43129088Smarkm#ifdef  CMS_IS_WINDOWS_
43229088Smarkm    if (isalpha((int) ThreeChars[0]) && ThreeChars[1] == ':')
43329088Smarkm        return TRUE;
43429088Smarkm#endif
43529088Smarkm    return FALSE;
43629088Smarkm}
43787139Smarkm
43887139Smarkm
43929088Smarkm// Makes a file path based on a given reference path
44029088Smarkm// NOTE: this function doesn't check if the path exists or even if it's legal
44129088Smarkmstatic
44229088SmarkmcmsBool BuildAbsolutePath(const char *relPath, const char *basePath, char *buffer, cmsUInt32Number MaxLen)
44329088Smarkm{
44429088Smarkm    char *tail;
44529088Smarkm    cmsUInt32Number len;
44629088Smarkm
44729088Smarkm    // Already absolute?
44829088Smarkm    if (isabsolutepath(relPath)) {
44987139Smarkm
45087139Smarkm        strncpy(buffer, relPath, MaxLen);
45129088Smarkm        buffer[MaxLen-1] = 0;
45229088Smarkm        return TRUE;
45329088Smarkm    }
45429088Smarkm
45529088Smarkm    // No, search for last
45629088Smarkm    strncpy(buffer, basePath, MaxLen);
45729088Smarkm    buffer[MaxLen-1] = 0;
45829088Smarkm
45929088Smarkm    tail = strrchr(buffer, DIR_CHAR);
46029088Smarkm    if (tail == NULL) return FALSE;    // Is not absolute and has no separators??
46187139Smarkm
46287139Smarkm    len = (cmsUInt32Number) (tail - buffer);
46329088Smarkm    if (len >= MaxLen) return FALSE;
46429088Smarkm
46529088Smarkm    // No need to assure zero terminator over here
46629088Smarkm    strncpy(tail + 1, relPath, MaxLen - len);
46729088Smarkm
46829088Smarkm    return TRUE;
46929088Smarkm}
47087139Smarkm
47187139Smarkm
47229088Smarkm// Make sure no exploit is being even tried
47329088Smarkmstatic
47429088Smarkmconst char* NoMeta(const char* str)
47529088Smarkm{
47629088Smarkm    if (strchr(str, '%') != NULL)
47729088Smarkm        return "**** CORRUPTED FORMAT STRING ***";
47829088Smarkm
47929088Smarkm    return str;
48029088Smarkm}
48129088Smarkm
48287139Smarkm// Syntax error
48387139Smarkmstatic
48429088SmarkmcmsBool SynError(cmsIT8* it8, const char *Txt, ...)
48587139Smarkm{
48629088Smarkm    char Buffer[256], ErrMsg[1024];
48729088Smarkm    va_list args;
48829088Smarkm
48929088Smarkm    va_start(args, Txt);
49029088Smarkm    vsnprintf(Buffer, 255, Txt, args);
49129088Smarkm    Buffer[255] = 0;
49229088Smarkm    va_end(args);
49329088Smarkm
49429088Smarkm    snprintf(ErrMsg, 1023, "%s: Line %d, %s", it8->FileStack[it8 ->IncludeSP]->FileName, it8->lineno, Buffer);
49529088Smarkm    ErrMsg[1023] = 0;
49629088Smarkm    it8->sy = SSYNERROR;
49729088Smarkm    cmsSignalError(it8 ->ContextID, cmsERROR_CORRUPTION_DETECTED, "%s", ErrMsg);
49829088Smarkm    return FALSE;
49929088Smarkm}
50029088Smarkm
50129088Smarkm// Check if current symbol is same as specified. issue an error else.
50229088Smarkmstatic
50329088SmarkmcmsBool Check(cmsIT8* it8, SYMBOL sy, const char* Err)
50429088Smarkm{
50529088Smarkm        if (it8 -> sy != sy)
50629088Smarkm                return SynError(it8, NoMeta(Err));
50729088Smarkm        return TRUE;
50829088Smarkm}
50929088Smarkm
51029088Smarkm// Read Next character from stream
51129088Smarkmstatic
51229088Smarkmvoid NextCh(cmsIT8* it8)
51329088Smarkm{
51429088Smarkm    if (it8 -> FileStack[it8 ->IncludeSP]->Stream) {
51529088Smarkm
51629088Smarkm        it8 ->ch = fgetc(it8 ->FileStack[it8 ->IncludeSP]->Stream);
51729088Smarkm
51829088Smarkm        if (feof(it8 -> FileStack[it8 ->IncludeSP]->Stream))  {
51929088Smarkm
52029088Smarkm            if (it8 ->IncludeSP > 0) {
52129088Smarkm
52287139Smarkm                fclose(it8 ->FileStack[it8->IncludeSP--]->Stream);
52387139Smarkm                it8 -> ch = ' ';                            // Whitespace to be ignored
52429088Smarkm
52529088Smarkm            } else
52687139Smarkm                it8 ->ch = 0;   // EOF
52729088Smarkm        }
52829088Smarkm    }
52929088Smarkm    else {
53029088Smarkm        it8->ch = *it8->Source;
53129088Smarkm        if (it8->ch) it8->Source++;
53229088Smarkm    }
53329088Smarkm}
53429088Smarkm
53529088Smarkm
53629088Smarkm// Try to see if current identifier is a keyword, if so return the referred symbol
53729088Smarkmstatic
53829088SmarkmSYMBOL BinSrchKey(const char *id)
53929088Smarkm{
54029088Smarkm    int l = 1;
54129088Smarkm    int r = NUMKEYS;
54229088Smarkm    int x, res;
54329088Smarkm
54429088Smarkm    while (r >= l)
54529088Smarkm    {
54629088Smarkm        x = (l+r)/2;
54729088Smarkm        res = cmsstrcasecmp(id, TabKeys[x-1].id);
54829088Smarkm        if (res == 0) return TabKeys[x-1].sy;
54929088Smarkm        if (res < 0) r = x - 1;
55029088Smarkm        else l = x + 1;
55129088Smarkm    }
55229088Smarkm
55329181Smarkm    return SUNDEFINED;
55429088Smarkm}
55529088Smarkm
55629088Smarkm
55729088Smarkm// 10 ^n
55829088Smarkmstatic
55929088SmarkmcmsFloat64Number xpow10(int n)
56029088Smarkm{
56129088Smarkm    return pow(10, (cmsFloat64Number) n);
56229088Smarkm}
56329088Smarkm
56429088Smarkm
56529088Smarkm//  Reads a Real number, tries to follow from integer number
56687139Smarkmstatic
56787139Smarkmvoid ReadReal(cmsIT8* it8, int inum)
56829088Smarkm{
56929088Smarkm    it8->dnum = (cmsFloat64Number) inum;
57087139Smarkm
57129088Smarkm    while (isdigit(it8->ch)) {
57229088Smarkm
57329088Smarkm        it8->dnum = it8->dnum * 10.0 + (it8->ch - '0');
57429088Smarkm        NextCh(it8);
57529088Smarkm    }
57629088Smarkm
57729088Smarkm    if (it8->ch == '.') {        // Decimal point
57829088Smarkm
57929088Smarkm        cmsFloat64Number frac = 0.0;      // fraction
58029088Smarkm        int prec = 0;                     // precision
58129088Smarkm
58229088Smarkm        NextCh(it8);               // Eats dec. point
58329088Smarkm
58429088Smarkm        while (isdigit(it8->ch)) {
58529088Smarkm
58629088Smarkm            frac = frac * 10.0 + (it8->ch - '0');
58729088Smarkm            prec++;
58829088Smarkm            NextCh(it8);
58929088Smarkm        }
59029088Smarkm
59129088Smarkm        it8->dnum = it8->dnum + (frac / xpow10(prec));
59229088Smarkm    }
59329088Smarkm
59429088Smarkm    // Exponent, example 34.00E+20
59529181Smarkm    if (toupper(it8->ch) == 'E') {
59629088Smarkm
59729088Smarkm        int e;
59829088Smarkm        int sgn;
59929088Smarkm
60029088Smarkm        NextCh(it8); sgn = 1;
60129088Smarkm
60229088Smarkm        if (it8->ch == '-') {
60329088Smarkm
60429088Smarkm            sgn = -1; NextCh(it8);
60529088Smarkm        }
60629088Smarkm        else
60729088Smarkm            if (it8->ch == '+') {
60829088Smarkm
60929088Smarkm                sgn = +1;
61029088Smarkm                NextCh(it8);
61129088Smarkm            }
61229088Smarkm
61329088Smarkm            e = 0;
61487139Smarkm            while (isdigit(it8->ch)) {
61587139Smarkm
61629088Smarkm                if ((cmsFloat64Number) e * 10L < INT_MAX)
61729088Smarkm                    e = e * 10 + (it8->ch - '0');
61829088Smarkm
61929088Smarkm                NextCh(it8);
62029088Smarkm            }
62129088Smarkm
62229088Smarkm            e = sgn*e;
62329088Smarkm            it8 -> dnum = it8 -> dnum * xpow10(e);
62429088Smarkm    }
62529088Smarkm}
62629088Smarkm
62729088Smarkm// Parses a float number
62829088Smarkm// This can not call directly atof because it uses locale dependant
62929088Smarkm// parsing, while CCMX files always use . as decimal separator
63029088Smarkmstatic
63129181SmarkmcmsFloat64Number ParseFloatNumber(const char *Buffer)
63229088Smarkm{
63329088Smarkm    cmsFloat64Number dnum = 0.0;
63429088Smarkm    int sign = 1;
63529088Smarkm
63629088Smarkm    // keep safe
63729088Smarkm    if (Buffer == NULL) return 0.0;
63829088Smarkm
63929088Smarkm    if (*Buffer == '-' || *Buffer == '+') {
64029088Smarkm
64129088Smarkm         sign = (*Buffer == '-') ? -1 : 1;
64229088Smarkm         Buffer++;
64329088Smarkm    }
64429088Smarkm
64529088Smarkm
64629088Smarkm    while (*Buffer && isdigit((int) *Buffer)) {
64729088Smarkm
64829088Smarkm        dnum = dnum * 10.0 + (*Buffer - '0');
64929088Smarkm        if (*Buffer) Buffer++;
65087139Smarkm    }
65187139Smarkm
65229088Smarkm    if (*Buffer == '.') {
65329088Smarkm
65429088Smarkm        cmsFloat64Number frac = 0.0;      // fraction
65529088Smarkm        int prec = 0;                     // precission
65629088Smarkm
65729088Smarkm        if (*Buffer) Buffer++;
65829088Smarkm
65929088Smarkm        while (*Buffer && isdigit((int) *Buffer)) {
66029088Smarkm
66129088Smarkm            frac = frac * 10.0 + (*Buffer - '0');
66229088Smarkm            prec++;
66329088Smarkm            if (*Buffer) Buffer++;
66429088Smarkm        }
66529088Smarkm
66629088Smarkm        dnum = dnum + (frac / xpow10(prec));
66787139Smarkm    }
66887139Smarkm
66929088Smarkm    // Exponent, example 34.00E+20
67029088Smarkm    if (*Buffer && toupper(*Buffer) == 'E') {
67129088Smarkm
67229088Smarkm        int e;
67329088Smarkm        int sgn;
67429088Smarkm
67529088Smarkm        if (*Buffer) Buffer++;
67629088Smarkm        sgn = 1;
67729088Smarkm
67829088Smarkm        if (*Buffer == '-') {
67929088Smarkm
68087139Smarkm            sgn = -1;
68187139Smarkm            if (*Buffer) Buffer++;
68229088Smarkm        }
68329088Smarkm        else
68429088Smarkm            if (*Buffer == '+') {
68529088Smarkm
68629088Smarkm                sgn = +1;
68729088Smarkm                if (*Buffer) Buffer++;
68829088Smarkm            }
68929088Smarkm
69029088Smarkm            e = 0;
69129088Smarkm            while (*Buffer && isdigit((int) *Buffer)) {
69287139Smarkm
69387139Smarkm                if ((cmsFloat64Number) e * 10L < INT_MAX)
69429088Smarkm                    e = e * 10 + (*Buffer - '0');
69529088Smarkm
69629088Smarkm                if (*Buffer) Buffer++;
69729088Smarkm            }
69829088Smarkm
69929088Smarkm            e = sgn*e;
70029088Smarkm            dnum = dnum * xpow10(e);
70129088Smarkm    }
70229088Smarkm
70329088Smarkm    return sign * dnum;
70429088Smarkm}
70587139Smarkm
70687139Smarkm
70729088Smarkm// Reads next symbol
70829088Smarkmstatic
70929088Smarkmvoid InSymbol(cmsIT8* it8)
71029088Smarkm{
71187139Smarkm    register char *idptr;
71287139Smarkm    register int k;
71329088Smarkm    SYMBOL key;
71429088Smarkm    int sng;
71529088Smarkm
71629088Smarkm    do {
71787139Smarkm
71887139Smarkm        while (isseparator(it8->ch))
71929088Smarkm            NextCh(it8);
72029088Smarkm
72129088Smarkm        if (isfirstidchar(it8->ch)) {          // Identifier
72287139Smarkm
72329088Smarkm            k = 0;
724228843Scperciva            idptr = it8->id;
725228843Scperciva
726228843Scperciva            do {
72729088Smarkm
72829088Smarkm                if (++k < MAXID) *idptr++ = (char) it8->ch;
72929088Smarkm
73029088Smarkm                NextCh(it8);
73129088Smarkm
73229088Smarkm            } while (isidchar(it8->ch));
73329088Smarkm
73429088Smarkm            *idptr = '\0';
73529088Smarkm
73629088Smarkm
73729088Smarkm            key = BinSrchKey(it8->id);
73829088Smarkm            if (key == SUNDEFINED) it8->sy = SIDENT;
73929088Smarkm            else it8->sy = key;
74029088Smarkm
74129088Smarkm        }
74229088Smarkm        else                         // Is a number?
74329088Smarkm            if (isdigit(it8->ch) || it8->ch == '.' || it8->ch == '-' || it8->ch == '+')
74429088Smarkm            {
74529088Smarkm                int sign = 1;
74629088Smarkm
74729088Smarkm                if (it8->ch == '-') {
74829088Smarkm                    sign = -1;
74929088Smarkm                    NextCh(it8);
75029088Smarkm                }
75129088Smarkm
75229088Smarkm                it8->inum = 0;
75329088Smarkm                it8->sy   = SINUM;
75429088Smarkm
75529088Smarkm                if (it8->ch == '0') {          // 0xnnnn (Hexa) or 0bnnnn (Binary)
75629088Smarkm
75729088Smarkm                    NextCh(it8);
75829088Smarkm                    if (toupper(it8->ch) == 'X') {
75929088Smarkm
76029088Smarkm                        int j;
76187139Smarkm
76287139Smarkm                        NextCh(it8);
76329088Smarkm                        while (isxdigit(it8->ch))
76429088Smarkm                        {
76529088Smarkm                            it8->ch = toupper(it8->ch);
76629088Smarkm                            if (it8->ch >= 'A' && it8->ch <= 'F')  j = it8->ch -'A'+10;
76729088Smarkm                            else j = it8->ch - '0';
76829088Smarkm
76929088Smarkm                            if ((long) it8->inum * 16L > (long) INT_MAX)
77029088Smarkm                            {
77129088Smarkm                                SynError(it8, "Invalid hexadecimal number");
77229088Smarkm                                return;
77329088Smarkm                            }
77429088Smarkm
77529088Smarkm                            it8->inum = it8->inum * 16 + j;
77629088Smarkm                            NextCh(it8);
77729088Smarkm                        }
77829088Smarkm                        return;
77929088Smarkm                    }
78029088Smarkm
78129088Smarkm                    if (toupper(it8->ch) == 'B') {  // Binary
78229088Smarkm
78329088Smarkm                        int j;
78487139Smarkm
78587139Smarkm                        NextCh(it8);
78629088Smarkm                        while (it8->ch == '0' || it8->ch == '1')
78729088Smarkm                        {
78829088Smarkm                            j = it8->ch - '0';
78929088Smarkm
79029088Smarkm                            if ((long) it8->inum * 2L > (long) INT_MAX)
79129088Smarkm                            {
79229088Smarkm                                SynError(it8, "Invalid binary number");
79387139Smarkm                                return;
79487139Smarkm                            }
79529088Smarkm
79629088Smarkm                            it8->inum = it8->inum * 2 + j;
79729088Smarkm                            NextCh(it8);
79829088Smarkm                        }
79929088Smarkm                        return;
80029088Smarkm                    }
80129088Smarkm                }
80287139Smarkm
80387139Smarkm
80429088Smarkm                while (isdigit(it8->ch)) {
80529088Smarkm
80687139Smarkm                    if ((long) it8->inum * 10L > (long) INT_MAX) {
80787139Smarkm                        ReadReal(it8, it8->inum);
80829088Smarkm                        it8->sy = SDNUM;
80929088Smarkm                        it8->dnum *= sign;
81029088Smarkm                        return;
81129088Smarkm                    }
81229088Smarkm
81329088Smarkm                    it8->inum = it8->inum * 10 + (it8->ch - '0');
81429088Smarkm                    NextCh(it8);
81529088Smarkm                }
81629088Smarkm
81729088Smarkm                if (it8->ch == '.') {
81829088Smarkm
81929088Smarkm                    ReadReal(it8, it8->inum);
82029088Smarkm                    it8->sy = SDNUM;
82129088Smarkm                    it8->dnum *= sign;
82229088Smarkm                    return;
82329088Smarkm                }
82429088Smarkm
82529088Smarkm                it8 -> inum *= sign;
82629088Smarkm
82729088Smarkm                // Special case. Numbers followed by letters are taken as identifiers
82829088Smarkm
82929088Smarkm                if (isidchar(it8 ->ch)) {
83029088Smarkm
83129088Smarkm                    if (it8 ->sy == SINUM) {
83229088Smarkm
83329088Smarkm                        sprintf(it8->id, "%d", it8->inum);
83429088Smarkm                    }
83529088Smarkm                    else {
83629088Smarkm
83729088Smarkm                        sprintf(it8->id, it8 ->DoubleFormatter, it8->dnum);
83829088Smarkm                    }
83929088Smarkm
84029088Smarkm                    k = (int) strlen(it8 ->id);
84129088Smarkm                    idptr = it8 ->id + k;
84229088Smarkm                    do {
84329088Smarkm
84429088Smarkm                        if (++k < MAXID) *idptr++ = (char) it8->ch;
84529088Smarkm
84629088Smarkm                        NextCh(it8);
84729088Smarkm
84829088Smarkm                    } while (isidchar(it8->ch));
84929088Smarkm
85029088Smarkm                    *idptr = '\0';
85129088Smarkm                    it8->sy = SIDENT;
85229088Smarkm                }
85329088Smarkm                return;
85429088Smarkm
85529088Smarkm            }
85629088Smarkm            else
85729088Smarkm                switch ((int) it8->ch) {
85887139Smarkm
85987139Smarkm        // EOF marker -- ignore it
86029088Smarkm        case '\x1a':
86129088Smarkm            NextCh(it8);
86229088Smarkm            break;
86329088Smarkm
86429088Smarkm        // Eof stream markers
86529088Smarkm        case 0:
86629088Smarkm        case -1:
86729088Smarkm            it8->sy = SEOF;
86829088Smarkm            break;
86929088Smarkm
87029088Smarkm
87129088Smarkm        // Next line
87229088Smarkm        case '\r':
87329088Smarkm            NextCh(it8);
87429088Smarkm            if (it8 ->ch == '\n')
87529088Smarkm                NextCh(it8);
87629088Smarkm            it8->sy = SEOLN;
87729088Smarkm            it8->lineno++;
87829088Smarkm            break;
87987139Smarkm
88087139Smarkm        case '\n':
88129088Smarkm            NextCh(it8);
88287139Smarkm            it8->sy = SEOLN;
88387139Smarkm            it8->lineno++;
88429088Smarkm            break;
88529088Smarkm
88629088Smarkm        // Comment
88729088Smarkm        case '#':
88829088Smarkm            NextCh(it8);
88929088Smarkm            while (it8->ch && it8->ch != '\n' && it8->ch != '\r')
89029088Smarkm                NextCh(it8);
89129088Smarkm
89229088Smarkm            it8->sy = SCOMMENT;
89329088Smarkm            break;
89429088Smarkm
89529088Smarkm        // String.
89629088Smarkm        case '\'':
89729088Smarkm        case '\"':
89829088Smarkm            idptr = it8->str;
89987139Smarkm            sng = it8->ch;
90087139Smarkm            k = 0;
90129088Smarkm            NextCh(it8);
90229088Smarkm
90329088Smarkm            while (k < MAXSTR && it8->ch != sng) {
90429088Smarkm
90529088Smarkm                if (it8->ch == '\n'|| it8->ch == '\r') k = MAXSTR+1;
90629088Smarkm                else {
90729088Smarkm                    *idptr++ = (char) it8->ch;
90829088Smarkm                    NextCh(it8);
90929088Smarkm                    k++;
91087139Smarkm                }
91187139Smarkm            }
91229088Smarkm
91329088Smarkm            it8->sy = SSTRING;
91429088Smarkm            *idptr = '\0';
91529088Smarkm            NextCh(it8);
91629088Smarkm            break;
91729088Smarkm
91829088Smarkm
91929088Smarkm        default:
92029088Smarkm            SynError(it8, "Unrecognized character: 0x%x", it8 ->ch);
92129088Smarkm            return;
92287139Smarkm            }
92387139Smarkm
92429088Smarkm    } while (it8->sy == SCOMMENT);
92529088Smarkm
92629088Smarkm    // Handle the include special token
92729088Smarkm
92829088Smarkm    if (it8 -> sy == SINCLUDE) {
92929088Smarkm
93029088Smarkm                FILECTX* FileNest;
93129088Smarkm
93229088Smarkm                if(it8 -> IncludeSP >= (MAXINCLUDE-1)) {
93329088Smarkm
93429088Smarkm                    SynError(it8, "Too many recursion levels");
93529088Smarkm                    return;
93629088Smarkm                }
93729088Smarkm
93829088Smarkm                InSymbol(it8);
93929088Smarkm                if (!Check(it8, SSTRING, "Filename expected")) return;
94029088Smarkm
94129088Smarkm                FileNest = it8 -> FileStack[it8 -> IncludeSP + 1];
94287139Smarkm                if(FileNest == NULL) {
94387139Smarkm
94429088Smarkm                    FileNest = it8 ->FileStack[it8 -> IncludeSP + 1] = (FILECTX*)AllocChunk(it8, sizeof(FILECTX));
94529088Smarkm                    //if(FileNest == NULL)
94687139Smarkm                    //  TODO: how to manage out-of-memory conditions?
94729088Smarkm                }
94829088Smarkm
94929088Smarkm                if (BuildAbsolutePath(it8->str,
95029088Smarkm                                      it8->FileStack[it8->IncludeSP]->FileName,
95129088Smarkm                                      FileNest->FileName, cmsMAX_PATH-1) == FALSE) {
95229088Smarkm                    SynError(it8, "File path too long");
95329088Smarkm                    return;
95429088Smarkm                }
95529088Smarkm
95629088Smarkm                FileNest->Stream = fopen(FileNest->FileName, "rt");
957                if (FileNest->Stream == NULL) {
958
959                        SynError(it8, "File %s not found", FileNest->FileName);
960                        return;
961                }
962                it8->IncludeSP++;
963
964                it8 ->ch = ' ';
965                InSymbol(it8);
966    }
967
968}
969
970// Checks end of line separator
971static
972cmsBool CheckEOLN(cmsIT8* it8)
973{
974        if (!Check(it8, SEOLN, "Expected separator")) return FALSE;
975        while (it8 -> sy == SEOLN)
976                        InSymbol(it8);
977        return TRUE;
978
979}
980
981// Skip a symbol
982
983static
984void Skip(cmsIT8* it8, SYMBOL sy)
985{
986        if (it8->sy == sy && it8->sy != SEOF)
987                        InSymbol(it8);
988}
989
990
991// Skip multiple EOLN
992static
993void SkipEOLN(cmsIT8* it8)
994{
995    while (it8->sy == SEOLN) {
996             InSymbol(it8);
997    }
998}
999
1000
1001// Returns a string holding current value
1002static
1003cmsBool GetVal(cmsIT8* it8, char* Buffer, cmsUInt32Number max, const char* ErrorTitle)
1004{
1005    switch (it8->sy) {
1006
1007    case SEOLN:   // Empty value
1008                  Buffer[0]=0;
1009                  break;
1010    case SIDENT:  strncpy(Buffer, it8->id, max);
1011                  Buffer[max-1]=0;
1012                  break;
1013    case SINUM:   snprintf(Buffer, max, "%d", it8 -> inum); break;
1014    case SDNUM:   snprintf(Buffer, max, it8->DoubleFormatter, it8 -> dnum); break;
1015    case SSTRING: strncpy(Buffer, it8->str, max);
1016                  Buffer[max-1] = 0;
1017                  break;
1018
1019
1020    default:
1021         return SynError(it8, "%s", ErrorTitle);
1022    }
1023
1024    Buffer[max] = 0;
1025    return TRUE;
1026}
1027
1028// ---------------------------------------------------------- Table
1029
1030static
1031TABLE* GetTable(cmsIT8* it8)
1032{
1033   if ((it8 -> nTable >= it8 ->TablesCount)) {
1034
1035           SynError(it8, "Table %d out of sequence", it8 -> nTable);
1036           return it8 -> Tab;
1037   }
1038
1039   return it8 ->Tab + it8 ->nTable;
1040}
1041
1042// ---------------------------------------------------------- Memory management
1043
1044
1045// Frees an allocator and owned memory
1046void CMSEXPORT cmsIT8Free(cmsHANDLE hIT8)
1047{
1048   cmsIT8* it8 = (cmsIT8*) hIT8;
1049
1050    if (it8 == NULL)
1051        return;
1052
1053    if (it8->MemorySink) {
1054
1055        OWNEDMEM* p;
1056        OWNEDMEM* n;
1057
1058        for (p = it8->MemorySink; p != NULL; p = n) {
1059
1060            n = p->Next;
1061            if (p->Ptr) _cmsFree(it8 ->ContextID, p->Ptr);
1062            _cmsFree(it8 ->ContextID, p);
1063        }
1064    }
1065
1066    if (it8->MemoryBlock)
1067        _cmsFree(it8 ->ContextID, it8->MemoryBlock);
1068
1069    _cmsFree(it8 ->ContextID, it8);
1070}
1071
1072
1073// Allocates a chunk of data, keep linked list
1074static
1075void* AllocBigBlock(cmsIT8* it8, cmsUInt32Number size)
1076{
1077    OWNEDMEM* ptr1;
1078    void* ptr = _cmsMallocZero(it8->ContextID, size);
1079
1080    if (ptr != NULL) {
1081
1082        ptr1 = (OWNEDMEM*) _cmsMallocZero(it8 ->ContextID, sizeof(OWNEDMEM));
1083
1084        if (ptr1 == NULL) {
1085
1086            _cmsFree(it8 ->ContextID, ptr);
1087            return NULL;
1088        }
1089
1090        ptr1-> Ptr        = ptr;
1091        ptr1-> Next       = it8 -> MemorySink;
1092        it8 -> MemorySink = ptr1;
1093    }
1094
1095    return ptr;
1096}
1097
1098
1099// Suballocator.
1100static
1101void* AllocChunk(cmsIT8* it8, cmsUInt32Number size)
1102{
1103    cmsUInt32Number Free = it8 ->Allocator.BlockSize - it8 ->Allocator.Used;
1104    cmsUInt8Number* ptr;
1105
1106    size = _cmsALIGNMEM(size);
1107
1108    if (size > Free) {
1109
1110        if (it8 -> Allocator.BlockSize == 0)
1111
1112                it8 -> Allocator.BlockSize = 20*1024;
1113        else
1114                it8 ->Allocator.BlockSize *= 2;
1115
1116        if (it8 ->Allocator.BlockSize < size)
1117                it8 ->Allocator.BlockSize = size;
1118
1119        it8 ->Allocator.Used = 0;
1120        it8 ->Allocator.Block = (cmsUInt8Number*)  AllocBigBlock(it8, it8 ->Allocator.BlockSize);
1121    }
1122
1123    ptr = it8 ->Allocator.Block + it8 ->Allocator.Used;
1124    it8 ->Allocator.Used += size;
1125
1126    return (void*) ptr;
1127
1128}
1129
1130
1131// Allocates a string
1132static
1133char *AllocString(cmsIT8* it8, const char* str)
1134{
1135    cmsUInt32Number Size = (cmsUInt32Number) strlen(str)+1;
1136    char *ptr;
1137
1138
1139    ptr = (char *) AllocChunk(it8, Size);
1140    if (ptr) strncpy (ptr, str, Size-1);
1141
1142    return ptr;
1143}
1144
1145// Searches through linked list
1146
1147static
1148cmsBool IsAvailableOnList(KEYVALUE* p, const char* Key, const char* Subkey, KEYVALUE** LastPtr)
1149{
1150    if (LastPtr) *LastPtr = p;
1151
1152    for (;  p != NULL; p = p->Next) {
1153
1154        if (LastPtr) *LastPtr = p;
1155
1156        if (*Key != '#') { // Comments are ignored
1157
1158            if (cmsstrcasecmp(Key, p->Keyword) == 0)
1159                break;
1160        }
1161    }
1162
1163    if (p == NULL)
1164        return FALSE;
1165
1166    if (Subkey == 0)
1167        return TRUE;
1168
1169    for (; p != NULL; p = p->NextSubkey) {
1170
1171        if (p ->Subkey == NULL) continue;
1172
1173        if (LastPtr) *LastPtr = p;
1174
1175        if (cmsstrcasecmp(Subkey, p->Subkey) == 0)
1176            return TRUE;
1177    }
1178
1179    return FALSE;
1180}
1181
1182
1183
1184// Add a property into a linked list
1185static
1186KEYVALUE* AddToList(cmsIT8* it8, KEYVALUE** Head, const char *Key, const char *Subkey, const char* xValue, WRITEMODE WriteAs)
1187{
1188    KEYVALUE* p;
1189    KEYVALUE* last;
1190
1191
1192    // Check if property is already in list
1193
1194    if (IsAvailableOnList(*Head, Key, Subkey, &p)) {
1195
1196        // This may work for editing properties
1197
1198        //     return SynError(it8, "duplicate key <%s>", Key);
1199    }
1200    else {
1201
1202        last = p;
1203
1204        // Allocate the container
1205        p = (KEYVALUE*) AllocChunk(it8, sizeof(KEYVALUE));
1206        if (p == NULL)
1207        {
1208            SynError(it8, "AddToList: out of memory");
1209            return NULL;
1210        }
1211
1212        // Store name and value
1213        p->Keyword = AllocString(it8, Key);
1214        p->Subkey = (Subkey == NULL) ? NULL : AllocString(it8, Subkey);
1215
1216        // Keep the container in our list
1217        if (*Head == NULL) {
1218            *Head = p;
1219        }
1220        else
1221        {
1222            if (Subkey != NULL && last != NULL) {
1223
1224                last->NextSubkey = p;
1225
1226                // If Subkey is not null, then last is the last property with the same key,
1227                // but not necessarily is the last property in the list, so we need to move
1228                // to the actual list end
1229                while (last->Next != NULL)
1230                         last = last->Next;
1231            }
1232
1233            if (last != NULL) last->Next = p;
1234        }
1235
1236        p->Next    = NULL;
1237        p->NextSubkey = NULL;
1238    }
1239
1240    p->WriteAs = WriteAs;
1241
1242    if (xValue != NULL) {
1243
1244        p->Value   = AllocString(it8, xValue);
1245    }
1246    else {
1247        p->Value   = NULL;
1248    }
1249
1250    return p;
1251}
1252
1253static
1254KEYVALUE* AddAvailableProperty(cmsIT8* it8, const char* Key, WRITEMODE as)
1255{
1256    return AddToList(it8, &it8->ValidKeywords, Key, NULL, NULL, as);
1257}
1258
1259
1260static
1261KEYVALUE* AddAvailableSampleID(cmsIT8* it8, const char* Key)
1262{
1263    return AddToList(it8, &it8->ValidSampleID, Key, NULL, NULL, WRITE_UNCOOKED);
1264}
1265
1266
1267static
1268void AllocTable(cmsIT8* it8)
1269{
1270    TABLE* t;
1271
1272    t = it8 ->Tab + it8 ->TablesCount;
1273
1274    t->HeaderList = NULL;
1275    t->DataFormat = NULL;
1276    t->Data       = NULL;
1277
1278    it8 ->TablesCount++;
1279}
1280
1281
1282cmsInt32Number CMSEXPORT cmsIT8SetTable(cmsHANDLE  IT8, cmsUInt32Number nTable)
1283{
1284     cmsIT8* it8 = (cmsIT8*) IT8;
1285
1286     if (nTable >= it8 ->TablesCount) {
1287
1288         if (nTable == it8 ->TablesCount) {
1289
1290             AllocTable(it8);
1291         }
1292         else {
1293             SynError(it8, "Table %d is out of sequence", nTable);
1294             return -1;
1295         }
1296     }
1297
1298     it8 ->nTable = nTable;
1299
1300     return (cmsInt32Number) nTable;
1301}
1302
1303
1304
1305// Init an empty container
1306cmsHANDLE  CMSEXPORT cmsIT8Alloc(cmsContext ContextID)
1307{
1308    cmsIT8* it8;
1309    cmsUInt32Number i;
1310
1311    it8 = (cmsIT8*) _cmsMallocZero(ContextID, sizeof(cmsIT8));
1312    if (it8 == NULL) return NULL;
1313
1314    AllocTable(it8);
1315
1316    it8->MemoryBlock = NULL;
1317    it8->MemorySink  = NULL;
1318
1319    it8 ->nTable = 0;
1320
1321    it8->ContextID = ContextID;
1322    it8->Allocator.Used = 0;
1323    it8->Allocator.Block = NULL;
1324    it8->Allocator.BlockSize = 0;
1325
1326    it8->ValidKeywords = NULL;
1327    it8->ValidSampleID = NULL;
1328
1329    it8 -> sy = SUNDEFINED;
1330    it8 -> ch = ' ';
1331    it8 -> Source = NULL;
1332    it8 -> inum = 0;
1333    it8 -> dnum = 0.0;
1334
1335    it8->FileStack[0] = (FILECTX*)AllocChunk(it8, sizeof(FILECTX));
1336    it8->IncludeSP   = 0;
1337    it8 -> lineno = 1;
1338
1339    strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT);
1340    cmsIT8SetSheetType((cmsHANDLE) it8, "CGATS.17");
1341
1342    // Initialize predefined properties & data
1343
1344    for (i=0; i < NUMPREDEFINEDPROPS; i++)
1345            AddAvailableProperty(it8, PredefinedProperties[i].id, PredefinedProperties[i].as);
1346
1347    for (i=0; i < NUMPREDEFINEDSAMPLEID; i++)
1348            AddAvailableSampleID(it8, PredefinedSampleID[i]);
1349
1350
1351   return (cmsHANDLE) it8;
1352}
1353
1354
1355const char* CMSEXPORT cmsIT8GetSheetType(cmsHANDLE hIT8)
1356{
1357        return GetTable((cmsIT8*) hIT8)->SheetType;
1358}
1359
1360cmsBool CMSEXPORT cmsIT8SetSheetType(cmsHANDLE hIT8, const char* Type)
1361{
1362        TABLE* t = GetTable((cmsIT8*) hIT8);
1363
1364        strncpy(t ->SheetType, Type, MAXSTR-1);
1365        t ->SheetType[MAXSTR-1] = 0;
1366        return TRUE;
1367}
1368
1369cmsBool CMSEXPORT cmsIT8SetComment(cmsHANDLE hIT8, const char* Val)
1370{
1371    cmsIT8* it8 = (cmsIT8*) hIT8;
1372
1373    if (!Val) return FALSE;
1374    if (!*Val) return FALSE;
1375
1376    return AddToList(it8, &GetTable(it8)->HeaderList, "# ", NULL, Val, WRITE_UNCOOKED) != NULL;
1377}
1378
1379// Sets a property
1380cmsBool CMSEXPORT cmsIT8SetPropertyStr(cmsHANDLE hIT8, const char* Key, const char *Val)
1381{
1382    cmsIT8* it8 = (cmsIT8*) hIT8;
1383
1384    if (!Val) return FALSE;
1385    if (!*Val) return FALSE;
1386
1387    return AddToList(it8, &GetTable(it8)->HeaderList, Key, NULL, Val, WRITE_STRINGIFY) != NULL;
1388}
1389
1390cmsBool CMSEXPORT cmsIT8SetPropertyDbl(cmsHANDLE hIT8, const char* cProp, cmsFloat64Number Val)
1391{
1392    cmsIT8* it8 = (cmsIT8*) hIT8;
1393    char Buffer[1024];
1394
1395    sprintf(Buffer, it8->DoubleFormatter, Val);
1396
1397    return AddToList(it8, &GetTable(it8)->HeaderList, cProp, NULL, Buffer, WRITE_UNCOOKED) != NULL;
1398}
1399
1400cmsBool CMSEXPORT cmsIT8SetPropertyHex(cmsHANDLE hIT8, const char* cProp, cmsUInt32Number Val)
1401{
1402    cmsIT8* it8 = (cmsIT8*) hIT8;
1403    char Buffer[1024];
1404
1405    sprintf(Buffer, "%u", Val);
1406
1407    return AddToList(it8, &GetTable(it8)->HeaderList, cProp, NULL, Buffer, WRITE_HEXADECIMAL) != NULL;
1408}
1409
1410cmsBool CMSEXPORT cmsIT8SetPropertyUncooked(cmsHANDLE hIT8, const char* Key, const char* Buffer)
1411{
1412    cmsIT8* it8 = (cmsIT8*) hIT8;
1413
1414    return AddToList(it8, &GetTable(it8)->HeaderList, Key, NULL, Buffer, WRITE_UNCOOKED) != NULL;
1415}
1416
1417cmsBool CMSEXPORT cmsIT8SetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char* SubKey, const char *Buffer)
1418{
1419    cmsIT8* it8 = (cmsIT8*) hIT8;
1420
1421    return AddToList(it8, &GetTable(it8)->HeaderList, Key, SubKey, Buffer, WRITE_PAIR) != NULL;
1422}
1423
1424// Gets a property
1425const char* CMSEXPORT cmsIT8GetProperty(cmsHANDLE hIT8, const char* Key)
1426{
1427    cmsIT8* it8 = (cmsIT8*) hIT8;
1428    KEYVALUE* p;
1429
1430    if (IsAvailableOnList(GetTable(it8) -> HeaderList, Key, NULL, &p))
1431    {
1432        return p -> Value;
1433    }
1434    return NULL;
1435}
1436
1437
1438cmsFloat64Number CMSEXPORT cmsIT8GetPropertyDbl(cmsHANDLE hIT8, const char* cProp)
1439{
1440    const char *v = cmsIT8GetProperty(hIT8, cProp);
1441
1442    if (v == NULL) return 0.0;
1443
1444    return ParseFloatNumber(v);
1445}
1446
1447const char* CMSEXPORT cmsIT8GetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char *SubKey)
1448{
1449    cmsIT8* it8 = (cmsIT8*) hIT8;
1450    KEYVALUE* p;
1451
1452    if (IsAvailableOnList(GetTable(it8) -> HeaderList, Key, SubKey, &p)) {
1453        return p -> Value;
1454    }
1455    return NULL;
1456}
1457
1458// ----------------------------------------------------------------- Datasets
1459
1460
1461static
1462void AllocateDataFormat(cmsIT8* it8)
1463{
1464    TABLE* t = GetTable(it8);
1465
1466    if (t -> DataFormat) return;    // Already allocated
1467
1468    t -> nSamples  = (int) cmsIT8GetPropertyDbl(it8, "NUMBER_OF_FIELDS");
1469
1470    if (t -> nSamples <= 0) {
1471
1472        SynError(it8, "AllocateDataFormat: Unknown NUMBER_OF_FIELDS");
1473        t -> nSamples = 10;
1474        }
1475
1476    t -> DataFormat = (char**) AllocChunk (it8, ((cmsUInt32Number) t->nSamples + 1) * sizeof(char *));
1477    if (t->DataFormat == NULL) {
1478
1479        SynError(it8, "AllocateDataFormat: Unable to allocate dataFormat array");
1480    }
1481
1482}
1483
1484static
1485const char *GetDataFormat(cmsIT8* it8, int n)
1486{
1487    TABLE* t = GetTable(it8);
1488
1489    if (t->DataFormat)
1490        return t->DataFormat[n];
1491
1492    return NULL;
1493}
1494
1495static
1496cmsBool SetDataFormat(cmsIT8* it8, int n, const char *label)
1497{
1498    TABLE* t = GetTable(it8);
1499
1500    if (!t->DataFormat)
1501        AllocateDataFormat(it8);
1502
1503    if (n > t -> nSamples) {
1504        SynError(it8, "More than NUMBER_OF_FIELDS fields.");
1505        return FALSE;
1506    }
1507
1508    if (t->DataFormat) {
1509        t->DataFormat[n] = AllocString(it8, label);
1510    }
1511
1512    return TRUE;
1513}
1514
1515
1516cmsBool CMSEXPORT cmsIT8SetDataFormat(cmsHANDLE  h, int n, const char *Sample)
1517{
1518        cmsIT8* it8 = (cmsIT8*) h;
1519        return SetDataFormat(it8, n, Sample);
1520}
1521
1522static
1523void AllocateDataSet(cmsIT8* it8)
1524{
1525    TABLE* t = GetTable(it8);
1526
1527    if (t -> Data) return;    // Already allocated
1528
1529    t-> nSamples   = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
1530    t-> nPatches   = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS"));
1531
1532    t-> Data = (char**)AllocChunk (it8, ((cmsUInt32Number) t->nSamples + 1) * ((cmsUInt32Number) t->nPatches + 1) *sizeof (char*));
1533    if (t->Data == NULL) {
1534
1535        SynError(it8, "AllocateDataSet: Unable to allocate data array");
1536    }
1537
1538}
1539
1540static
1541char* GetData(cmsIT8* it8, int nSet, int nField)
1542{
1543    TABLE* t = GetTable(it8);
1544    int  nSamples   = t -> nSamples;
1545    int  nPatches   = t -> nPatches;
1546
1547    if (nSet >= nPatches || nField >= nSamples)
1548        return NULL;
1549
1550    if (!t->Data) return NULL;
1551    return t->Data [nSet * nSamples + nField];
1552}
1553
1554static
1555cmsBool SetData(cmsIT8* it8, int nSet, int nField, const char *Val)
1556{
1557    TABLE* t = GetTable(it8);
1558
1559    if (!t->Data)
1560        AllocateDataSet(it8);
1561
1562    if (!t->Data) return FALSE;
1563
1564    if (nSet > t -> nPatches || nSet < 0) {
1565
1566            return SynError(it8, "Patch %d out of range, there are %d patches", nSet, t -> nPatches);
1567    }
1568
1569    if (nField > t ->nSamples || nField < 0) {
1570            return SynError(it8, "Sample %d out of range, there are %d samples", nField, t ->nSamples);
1571
1572    }
1573
1574    t->Data [nSet * t -> nSamples + nField] = AllocString(it8, Val);
1575    return TRUE;
1576}
1577
1578
1579// --------------------------------------------------------------- File I/O
1580
1581
1582// Writes a string to file
1583static
1584void WriteStr(SAVESTREAM* f, const char *str)
1585{
1586    cmsUInt32Number len;
1587
1588    if (str == NULL)
1589        str = " ";
1590
1591    // Length to write
1592    len = (cmsUInt32Number) strlen(str);
1593    f ->Used += len;
1594
1595
1596    if (f ->stream) {   // Should I write it to a file?
1597
1598        if (fwrite(str, 1, len, f->stream) != len) {
1599            cmsSignalError(0, cmsERROR_WRITE, "Write to file error in CGATS parser");
1600            return;
1601        }
1602
1603    }
1604    else {  // Or to a memory block?
1605
1606        if (f ->Base) {   // Am I just counting the bytes?
1607
1608            if (f ->Used > f ->Max) {
1609
1610                 cmsSignalError(0, cmsERROR_WRITE, "Write to memory overflows in CGATS parser");
1611                 return;
1612            }
1613
1614            memmove(f ->Ptr, str, len);
1615            f->Ptr += len;
1616        }
1617
1618    }
1619}
1620
1621
1622// Write formatted
1623
1624static
1625void Writef(SAVESTREAM* f, const char* frm, ...)
1626{
1627    char Buffer[4096];
1628    va_list args;
1629
1630    va_start(args, frm);
1631    vsnprintf(Buffer, 4095, frm, args);
1632    Buffer[4095] = 0;
1633    WriteStr(f, Buffer);
1634    va_end(args);
1635
1636}
1637
1638// Writes full header
1639static
1640void WriteHeader(cmsIT8* it8, SAVESTREAM* fp)
1641{
1642    KEYVALUE* p;
1643    TABLE* t = GetTable(it8);
1644
1645    // Writes the type
1646    WriteStr(fp, t->SheetType);
1647    WriteStr(fp, "\n");
1648
1649    for (p = t->HeaderList; (p != NULL); p = p->Next)
1650    {
1651        if (*p ->Keyword == '#') {
1652
1653            char* Pt;
1654
1655            WriteStr(fp, "#\n# ");
1656            for (Pt = p ->Value; *Pt; Pt++) {
1657
1658
1659                Writef(fp, "%c", *Pt);
1660
1661                if (*Pt == '\n') {
1662                    WriteStr(fp, "# ");
1663                }
1664            }
1665
1666            WriteStr(fp, "\n#\n");
1667            continue;
1668        }
1669
1670
1671        if (!IsAvailableOnList(it8-> ValidKeywords, p->Keyword, NULL, NULL)) {
1672
1673#ifdef CMS_STRICT_CGATS
1674            WriteStr(fp, "KEYWORD\t\"");
1675            WriteStr(fp, p->Keyword);
1676            WriteStr(fp, "\"\n");
1677#endif
1678
1679            AddAvailableProperty(it8, p->Keyword, WRITE_UNCOOKED);
1680        }
1681
1682        WriteStr(fp, p->Keyword);
1683        if (p->Value) {
1684
1685            switch (p ->WriteAs) {
1686
1687            case WRITE_UNCOOKED:
1688                    Writef(fp, "\t%s", p ->Value);
1689                    break;
1690
1691            case WRITE_STRINGIFY:
1692                    Writef(fp, "\t\"%s\"", p->Value );
1693                    break;
1694
1695            case WRITE_HEXADECIMAL:
1696                    Writef(fp, "\t0x%X", atoi(p ->Value));
1697                    break;
1698
1699            case WRITE_BINARY:
1700                    Writef(fp, "\t0x%B", atoi(p ->Value));
1701                    break;
1702
1703            case WRITE_PAIR:
1704                    Writef(fp, "\t\"%s,%s\"", p->Subkey, p->Value);
1705                    break;
1706
1707            default: SynError(it8, "Unknown write mode %d", p ->WriteAs);
1708                     return;
1709            }
1710        }
1711
1712        WriteStr (fp, "\n");
1713    }
1714
1715}
1716
1717
1718// Writes the data format
1719static
1720void WriteDataFormat(SAVESTREAM* fp, cmsIT8* it8)
1721{
1722    int i, nSamples;
1723    TABLE* t = GetTable(it8);
1724
1725    if (!t -> DataFormat) return;
1726
1727       WriteStr(fp, "BEGIN_DATA_FORMAT\n");
1728       WriteStr(fp, " ");
1729       nSamples = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
1730
1731       for (i = 0; i < nSamples; i++) {
1732
1733              WriteStr(fp, t->DataFormat[i]);
1734              WriteStr(fp, ((i == (nSamples-1)) ? "\n" : "\t"));
1735          }
1736
1737       WriteStr (fp, "END_DATA_FORMAT\n");
1738}
1739
1740
1741// Writes data array
1742static
1743void WriteData(SAVESTREAM* fp, cmsIT8* it8)
1744{
1745       int  i, j;
1746       TABLE* t = GetTable(it8);
1747
1748       if (!t->Data) return;
1749
1750       WriteStr (fp, "BEGIN_DATA\n");
1751
1752       t->nPatches = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS"));
1753
1754       for (i = 0; i < t-> nPatches; i++) {
1755
1756              WriteStr(fp, " ");
1757
1758              for (j = 0; j < t->nSamples; j++) {
1759
1760                     char *ptr = t->Data[i*t->nSamples+j];
1761
1762                     if (ptr == NULL) WriteStr(fp, "\"\"");
1763                     else {
1764                         // If value contains whitespace, enclose within quote
1765
1766                         if (strchr(ptr, ' ') != NULL) {
1767
1768                             WriteStr(fp, "\"");
1769                             WriteStr(fp, ptr);
1770                             WriteStr(fp, "\"");
1771                         }
1772                         else
1773                            WriteStr(fp, ptr);
1774                     }
1775
1776                     WriteStr(fp, ((j == (t->nSamples-1)) ? "\n" : "\t"));
1777              }
1778       }
1779       WriteStr (fp, "END_DATA\n");
1780}
1781
1782
1783
1784// Saves whole file
1785cmsBool CMSEXPORT cmsIT8SaveToFile(cmsHANDLE hIT8, const char* cFileName)
1786{
1787    SAVESTREAM sd;
1788    cmsUInt32Number i;
1789    cmsIT8* it8 = (cmsIT8*) hIT8;
1790
1791    memset(&sd, 0, sizeof(sd));
1792
1793    sd.stream = fopen(cFileName, "wt");
1794    if (!sd.stream) return FALSE;
1795
1796    for (i=0; i < it8 ->TablesCount; i++) {
1797
1798            cmsIT8SetTable(hIT8, i);
1799            WriteHeader(it8, &sd);
1800            WriteDataFormat(&sd, it8);
1801            WriteData(&sd, it8);
1802    }
1803
1804    if (fclose(sd.stream) != 0) return FALSE;
1805
1806    return TRUE;
1807}
1808
1809
1810// Saves to memory
1811cmsBool CMSEXPORT cmsIT8SaveToMem(cmsHANDLE hIT8, void *MemPtr, cmsUInt32Number* BytesNeeded)
1812{
1813    SAVESTREAM sd;
1814    cmsUInt32Number i;
1815    cmsIT8* it8 = (cmsIT8*) hIT8;
1816
1817    memset(&sd, 0, sizeof(sd));
1818
1819    sd.stream = NULL;
1820    sd.Base   = (cmsUInt8Number*)  MemPtr;
1821    sd.Ptr    = sd.Base;
1822
1823    sd.Used = 0;
1824
1825    if (sd.Base)
1826        sd.Max  = *BytesNeeded;     // Write to memory?
1827    else
1828        sd.Max  = 0;                // Just counting the needed bytes
1829
1830    for (i=0; i < it8 ->TablesCount; i++) {
1831
1832        cmsIT8SetTable(hIT8, i);
1833        WriteHeader(it8, &sd);
1834        WriteDataFormat(&sd, it8);
1835        WriteData(&sd, it8);
1836    }
1837
1838    sd.Used++;  // The \0 at the very end
1839
1840    if (sd.Base)
1841        *sd.Ptr = 0;
1842
1843    *BytesNeeded = sd.Used;
1844
1845    return TRUE;
1846}
1847
1848
1849// -------------------------------------------------------------- Higer level parsing
1850
1851static
1852cmsBool DataFormatSection(cmsIT8* it8)
1853{
1854    int iField = 0;
1855    TABLE* t = GetTable(it8);
1856
1857    InSymbol(it8);   // Eats "BEGIN_DATA_FORMAT"
1858    CheckEOLN(it8);
1859
1860    while (it8->sy != SEND_DATA_FORMAT &&
1861        it8->sy != SEOLN &&
1862        it8->sy != SEOF &&
1863        it8->sy != SSYNERROR)  {
1864
1865            if (it8->sy != SIDENT) {
1866
1867                return SynError(it8, "Sample type expected");
1868            }
1869
1870            if (!SetDataFormat(it8, iField, it8->id)) return FALSE;
1871            iField++;
1872
1873            InSymbol(it8);
1874            SkipEOLN(it8);
1875       }
1876
1877       SkipEOLN(it8);
1878       Skip(it8, SEND_DATA_FORMAT);
1879       SkipEOLN(it8);
1880
1881       if (iField != t ->nSamples) {
1882           SynError(it8, "Count mismatch. NUMBER_OF_FIELDS was %d, found %d\n", t ->nSamples, iField);
1883
1884
1885       }
1886
1887       return TRUE;
1888}
1889
1890
1891
1892static
1893cmsBool DataSection (cmsIT8* it8)
1894{
1895    int  iField = 0;
1896    int  iSet   = 0;
1897    char Buffer[256];
1898    TABLE* t = GetTable(it8);
1899
1900    InSymbol(it8);   // Eats "BEGIN_DATA"
1901    CheckEOLN(it8);
1902
1903    if (!t->Data)
1904        AllocateDataSet(it8);
1905
1906    while (it8->sy != SEND_DATA && it8->sy != SEOF)
1907    {
1908        if (iField >= t -> nSamples) {
1909            iField = 0;
1910            iSet++;
1911
1912        }
1913
1914        if (it8->sy != SEND_DATA && it8->sy != SEOF) {
1915
1916            if (!GetVal(it8, Buffer, 255, "Sample data expected"))
1917                return FALSE;
1918
1919            if (!SetData(it8, iSet, iField, Buffer))
1920                return FALSE;
1921
1922            iField++;
1923
1924            InSymbol(it8);
1925            SkipEOLN(it8);
1926        }
1927    }
1928
1929    SkipEOLN(it8);
1930    Skip(it8, SEND_DATA);
1931    SkipEOLN(it8);
1932
1933    // Check for data completion.
1934
1935    if ((iSet+1) != t -> nPatches)
1936        return SynError(it8, "Count mismatch. NUMBER_OF_SETS was %d, found %d\n", t ->nPatches, iSet+1);
1937
1938    return TRUE;
1939}
1940
1941
1942
1943
1944static
1945cmsBool HeaderSection(cmsIT8* it8)
1946{
1947    char VarName[MAXID];
1948    char Buffer[MAXSTR];
1949    KEYVALUE* Key;
1950
1951        while (it8->sy != SEOF &&
1952               it8->sy != SSYNERROR &&
1953               it8->sy != SBEGIN_DATA_FORMAT &&
1954               it8->sy != SBEGIN_DATA) {
1955
1956
1957        switch (it8 -> sy) {
1958
1959        case SKEYWORD:
1960                InSymbol(it8);
1961                if (!GetVal(it8, Buffer, MAXSTR-1, "Keyword expected")) return FALSE;
1962                if (!AddAvailableProperty(it8, Buffer, WRITE_UNCOOKED)) return FALSE;
1963                InSymbol(it8);
1964                break;
1965
1966
1967        case SDATA_FORMAT_ID:
1968                InSymbol(it8);
1969                if (!GetVal(it8, Buffer, MAXSTR-1, "Keyword expected")) return FALSE;
1970                if (!AddAvailableSampleID(it8, Buffer)) return FALSE;
1971                InSymbol(it8);
1972                break;
1973
1974
1975        case SIDENT:
1976                strncpy(VarName, it8->id, MAXID-1);
1977                VarName[MAXID-1] = 0;
1978
1979                if (!IsAvailableOnList(it8-> ValidKeywords, VarName, NULL, &Key)) {
1980
1981#ifdef CMS_STRICT_CGATS
1982                 return SynError(it8, "Undefined keyword '%s'", VarName);
1983#else
1984                    Key = AddAvailableProperty(it8, VarName, WRITE_UNCOOKED);
1985                    if (Key == NULL) return FALSE;
1986#endif
1987                }
1988
1989                InSymbol(it8);
1990                if (!GetVal(it8, Buffer, MAXSTR-1, "Property data expected")) return FALSE;
1991
1992                if(Key->WriteAs != WRITE_PAIR) {
1993                    AddToList(it8, &GetTable(it8)->HeaderList, VarName, NULL, Buffer,
1994                                (it8->sy == SSTRING) ? WRITE_STRINGIFY : WRITE_UNCOOKED);
1995                }
1996                else {
1997                    const char *Subkey;
1998                    char *Nextkey;
1999                    if (it8->sy != SSTRING)
2000                        return SynError(it8, "Invalid value '%s' for property '%s'.", Buffer, VarName);
2001
2002                    // chop the string as a list of "subkey, value" pairs, using ';' as a separator
2003                    for (Subkey = Buffer; Subkey != NULL; Subkey = Nextkey)
2004                    {
2005                        char *Value, *temp;
2006
2007                        //  identify token pair boundary
2008                        Nextkey = (char*) strchr(Subkey, ';');
2009                        if(Nextkey)
2010                            *Nextkey++ = '\0';
2011
2012                        // for each pair, split the subkey and the value
2013                        Value = (char*) strrchr(Subkey, ',');
2014                        if(Value == NULL)
2015                            return SynError(it8, "Invalid value for property '%s'.", VarName);
2016
2017                        // gobble the spaces before the coma, and the coma itself
2018                        temp = Value++;
2019                        do *temp-- = '\0'; while(temp >= Subkey && *temp == ' ');
2020
2021                        // gobble any space at the right
2022                        temp = Value + strlen(Value) - 1;
2023                        while(*temp == ' ') *temp-- = '\0';
2024
2025                        // trim the strings from the left
2026                        Subkey += strspn(Subkey, " ");
2027                        Value += strspn(Value, " ");
2028
2029                        if(Subkey[0] == 0 || Value[0] == 0)
2030                            return SynError(it8, "Invalid value for property '%s'.", VarName);
2031                        AddToList(it8, &GetTable(it8)->HeaderList, VarName, Subkey, Value, WRITE_PAIR);
2032                    }
2033                }
2034
2035                InSymbol(it8);
2036                break;
2037
2038
2039        case SEOLN: break;
2040
2041        default:
2042                return SynError(it8, "expected keyword or identifier");
2043        }
2044
2045    SkipEOLN(it8);
2046    }
2047
2048    return TRUE;
2049
2050}
2051
2052
2053static
2054void ReadType(cmsIT8* it8, char* SheetTypePtr)
2055{
2056    // First line is a very special case.
2057
2058    while (isseparator(it8->ch))
2059            NextCh(it8);
2060
2061    while (it8->ch != '\r' && it8 ->ch != '\n' && it8->ch != '\t' && it8 -> ch != -1) {
2062
2063        *SheetTypePtr++= (char) it8 ->ch;
2064        NextCh(it8);
2065    }
2066
2067    *SheetTypePtr = 0;
2068}
2069
2070
2071static
2072cmsBool ParseIT8(cmsIT8* it8, cmsBool nosheet)
2073{
2074    char* SheetTypePtr = it8 ->Tab[0].SheetType;
2075
2076    if (nosheet == 0) {
2077        ReadType(it8, SheetTypePtr);
2078    }
2079
2080    InSymbol(it8);
2081
2082    SkipEOLN(it8);
2083
2084    while (it8-> sy != SEOF &&
2085           it8-> sy != SSYNERROR) {
2086
2087            switch (it8 -> sy) {
2088
2089            case SBEGIN_DATA_FORMAT:
2090                    if (!DataFormatSection(it8)) return FALSE;
2091                    break;
2092
2093            case SBEGIN_DATA:
2094
2095                    if (!DataSection(it8)) return FALSE;
2096
2097                    if (it8 -> sy != SEOF) {
2098
2099                            AllocTable(it8);
2100                            it8 ->nTable = it8 ->TablesCount - 1;
2101
2102                            // Read sheet type if present. We only support identifier and string.
2103                            // <ident> <eoln> is a type string
2104                            // anything else, is not a type string
2105                            if (nosheet == 0) {
2106
2107                                if (it8 ->sy == SIDENT) {
2108
2109                                    // May be a type sheet or may be a prop value statement. We cannot use insymbol in
2110                                    // this special case...
2111                                     while (isseparator(it8->ch))
2112                                         NextCh(it8);
2113
2114                                     // If a newline is found, then this is a type string
2115                                    if (it8 ->ch == '\n' || it8->ch == '\r') {
2116
2117                                         cmsIT8SetSheetType(it8, it8 ->id);
2118                                         InSymbol(it8);
2119                                    }
2120                                    else
2121                                    {
2122                                        // It is not. Just continue
2123                                        cmsIT8SetSheetType(it8, "");
2124                                    }
2125                                }
2126                                else
2127                                    // Validate quoted strings
2128                                    if (it8 ->sy == SSTRING) {
2129                                        cmsIT8SetSheetType(it8, it8 ->str);
2130                                        InSymbol(it8);
2131                                    }
2132                           }
2133
2134                    }
2135                    break;
2136
2137            case SEOLN:
2138                    SkipEOLN(it8);
2139                    break;
2140
2141            default:
2142                    if (!HeaderSection(it8)) return FALSE;
2143           }
2144
2145    }
2146
2147    return (it8 -> sy != SSYNERROR);
2148}
2149
2150
2151
2152// Init usefull pointers
2153
2154static
2155void CookPointers(cmsIT8* it8)
2156{
2157    int idField, i;
2158    char* Fld;
2159    cmsUInt32Number j;
2160    cmsUInt32Number nOldTable = it8 ->nTable;
2161
2162    for (j=0; j < it8 ->TablesCount; j++) {
2163
2164    TABLE* t = it8 ->Tab + j;
2165
2166    t -> SampleID = 0;
2167    it8 ->nTable = j;
2168
2169    for (idField = 0; idField < t -> nSamples; idField++)
2170    {
2171        if (t ->DataFormat == NULL){
2172            SynError(it8, "Undefined DATA_FORMAT");
2173            return;
2174        }
2175
2176        Fld = t->DataFormat[idField];
2177        if (!Fld) continue;
2178
2179
2180        if (cmsstrcasecmp(Fld, "SAMPLE_ID") == 0) {
2181
2182            t -> SampleID = idField;
2183
2184            for (i=0; i < t -> nPatches; i++) {
2185
2186                char *Data = GetData(it8, i, idField);
2187                if (Data) {
2188                    char Buffer[256];
2189
2190                    strncpy(Buffer, Data, 255);
2191                    Buffer[255] = 0;
2192
2193                    if (strlen(Buffer) <= strlen(Data))
2194                        strcpy(Data, Buffer);
2195                    else
2196                        SetData(it8, i, idField, Buffer);
2197
2198                }
2199            }
2200
2201        }
2202
2203        // "LABEL" is an extension. It keeps references to forward tables
2204
2205        if ((cmsstrcasecmp(Fld, "LABEL") == 0) || Fld[0] == '$' ) {
2206
2207                    // Search for table references...
2208                    for (i=0; i < t -> nPatches; i++) {
2209
2210                            char *Label = GetData(it8, i, idField);
2211
2212                            if (Label) {
2213
2214                                cmsUInt32Number k;
2215
2216                                // This is the label, search for a table containing
2217                                // this property
2218
2219                                for (k=0; k < it8 ->TablesCount; k++) {
2220
2221                                    TABLE* Table = it8 ->Tab + k;
2222                                    KEYVALUE* p;
2223
2224                                    if (IsAvailableOnList(Table->HeaderList, Label, NULL, &p)) {
2225
2226                                        // Available, keep type and table
2227                                        char Buffer[256];
2228
2229                                        char *Type  = p ->Value;
2230                                        int  nTable = (int) k;
2231
2232                                        snprintf(Buffer, 255, "%s %d %s", Label, nTable, Type );
2233
2234                                        SetData(it8, i, idField, Buffer);
2235                                    }
2236                                }
2237
2238
2239                            }
2240
2241                    }
2242
2243
2244        }
2245
2246    }
2247    }
2248
2249    it8 ->nTable = nOldTable;
2250}
2251
2252// Try to infere if the file is a CGATS/IT8 file at all. Read first line
2253// that should be something like some printable characters plus a \n
2254// returns 0 if this is not like a CGATS, or an integer otherwise. This integer is the number of words in first line?
2255static
2256int IsMyBlock(cmsUInt8Number* Buffer, int n)
2257{
2258    int words = 1, space = 0, quot = 0;
2259    int i;
2260
2261    if (n < 10) return 0;   // Too small
2262
2263    if (n > 132)
2264        n = 132;
2265
2266    for (i = 1; i < n; i++) {
2267
2268        switch(Buffer[i])
2269        {
2270        case '\n':
2271        case '\r':
2272            return ((quot == 1) || (words > 2)) ? 0 : words;
2273        case '\t':
2274        case ' ':
2275            if(!quot && !space)
2276                space = 1;
2277            break;
2278        case '\"':
2279            quot = !quot;
2280            break;
2281        default:
2282            if (Buffer[i] < 32) return 0;
2283            if (Buffer[i] > 127) return 0;
2284            words += space;
2285            space = 0;
2286            break;
2287        }
2288    }
2289
2290    return 0;
2291}
2292
2293
2294static
2295cmsBool IsMyFile(const char* FileName)
2296{
2297   FILE *fp;
2298   cmsUInt32Number Size;
2299   cmsUInt8Number Ptr[133];
2300
2301   fp = fopen(FileName, "rt");
2302   if (!fp) {
2303       cmsSignalError(0, cmsERROR_FILE, "File '%s' not found", FileName);
2304       return FALSE;
2305   }
2306
2307   Size = (cmsUInt32Number) fread(Ptr, 1, 132, fp);
2308
2309   if (fclose(fp) != 0)
2310       return FALSE;
2311
2312   Ptr[Size] = '\0';
2313
2314   return IsMyBlock(Ptr, Size);
2315}
2316
2317// ---------------------------------------------------------- Exported routines
2318
2319
2320cmsHANDLE  CMSEXPORT cmsIT8LoadFromMem(cmsContext ContextID, void *Ptr, cmsUInt32Number len)
2321{
2322    cmsHANDLE hIT8;
2323    cmsIT8*  it8;
2324    int type;
2325
2326    _cmsAssert(Ptr != NULL);
2327    _cmsAssert(len != 0);
2328
2329    type = IsMyBlock((cmsUInt8Number*)Ptr, len);
2330    if (type == 0) return NULL;
2331
2332    hIT8 = cmsIT8Alloc(ContextID);
2333    if (!hIT8) return NULL;
2334
2335    it8 = (cmsIT8*) hIT8;
2336    it8 ->MemoryBlock = (char*) _cmsMalloc(ContextID, len + 1);
2337
2338    strncpy(it8 ->MemoryBlock, (const char*) Ptr, len);
2339    it8 ->MemoryBlock[len] = 0;
2340
2341    strncpy(it8->FileStack[0]->FileName, "", cmsMAX_PATH-1);
2342    it8-> Source = it8 -> MemoryBlock;
2343
2344    if (!ParseIT8(it8, type-1)) {
2345
2346        cmsIT8Free(hIT8);
2347        return FALSE;
2348    }
2349
2350    CookPointers(it8);
2351    it8 ->nTable = 0;
2352
2353    _cmsFree(ContextID, it8->MemoryBlock);
2354    it8 -> MemoryBlock = NULL;
2355
2356    return hIT8;
2357
2358
2359}
2360
2361
2362cmsHANDLE  CMSEXPORT cmsIT8LoadFromFile(cmsContext ContextID, const char* cFileName)
2363{
2364
2365     cmsHANDLE hIT8;
2366     cmsIT8*  it8;
2367     int type;
2368
2369     _cmsAssert(cFileName != NULL);
2370
2371     type = IsMyFile(cFileName);
2372     if (type == 0) return NULL;
2373
2374     hIT8 = cmsIT8Alloc(ContextID);
2375     it8 = (cmsIT8*) hIT8;
2376     if (!hIT8) return NULL;
2377
2378
2379     it8 ->FileStack[0]->Stream = fopen(cFileName, "rt");
2380
2381     if (!it8 ->FileStack[0]->Stream) {
2382         cmsIT8Free(hIT8);
2383         return NULL;
2384     }
2385
2386
2387    strncpy(it8->FileStack[0]->FileName, cFileName, cmsMAX_PATH-1);
2388    it8->FileStack[0]->FileName[cmsMAX_PATH-1] = 0;
2389
2390    if (!ParseIT8(it8, type-1)) {
2391
2392            fclose(it8 ->FileStack[0]->Stream);
2393            cmsIT8Free(hIT8);
2394            return NULL;
2395    }
2396
2397    CookPointers(it8);
2398    it8 ->nTable = 0;
2399
2400    if (fclose(it8 ->FileStack[0]->Stream)!= 0) {
2401            cmsIT8Free(hIT8);
2402            return NULL;
2403    }
2404
2405    return hIT8;
2406
2407}
2408
2409int CMSEXPORT cmsIT8EnumDataFormat(cmsHANDLE hIT8, char ***SampleNames)
2410{
2411    cmsIT8* it8 = (cmsIT8*) hIT8;
2412    TABLE* t;
2413
2414    _cmsAssert(hIT8 != NULL);
2415
2416    t = GetTable(it8);
2417
2418    if (SampleNames)
2419        *SampleNames = t -> DataFormat;
2420    return t -> nSamples;
2421}
2422
2423
2424cmsUInt32Number CMSEXPORT cmsIT8EnumProperties(cmsHANDLE hIT8, char ***PropertyNames)
2425{
2426    cmsIT8* it8 = (cmsIT8*) hIT8;
2427    KEYVALUE* p;
2428    cmsUInt32Number n;
2429    char **Props;
2430    TABLE* t;
2431
2432    _cmsAssert(hIT8 != NULL);
2433
2434    t = GetTable(it8);
2435
2436    // Pass#1 - count properties
2437
2438    n = 0;
2439    for (p = t -> HeaderList;  p != NULL; p = p->Next) {
2440        n++;
2441    }
2442
2443
2444    Props = (char **) AllocChunk(it8, sizeof(char *) * n);
2445
2446    // Pass#2 - Fill pointers
2447    n = 0;
2448    for (p = t -> HeaderList;  p != NULL; p = p->Next) {
2449        Props[n++] = p -> Keyword;
2450    }
2451
2452    *PropertyNames = Props;
2453    return n;
2454}
2455
2456cmsUInt32Number CMSEXPORT cmsIT8EnumPropertyMulti(cmsHANDLE hIT8, const char* cProp, const char ***SubpropertyNames)
2457{
2458    cmsIT8* it8 = (cmsIT8*) hIT8;
2459    KEYVALUE *p, *tmp;
2460    cmsUInt32Number n;
2461    const char **Props;
2462    TABLE* t;
2463
2464    _cmsAssert(hIT8 != NULL);
2465
2466
2467    t = GetTable(it8);
2468
2469    if(!IsAvailableOnList(t->HeaderList, cProp, NULL, &p)) {
2470        *SubpropertyNames = 0;
2471        return 0;
2472    }
2473
2474    // Pass#1 - count properties
2475
2476    n = 0;
2477    for (tmp = p;  tmp != NULL; tmp = tmp->NextSubkey) {
2478        if(tmp->Subkey != NULL)
2479            n++;
2480    }
2481
2482
2483    Props = (const char **) AllocChunk(it8, sizeof(char *) * n);
2484
2485    // Pass#2 - Fill pointers
2486    n = 0;
2487    for (tmp = p;  tmp != NULL; tmp = tmp->NextSubkey) {
2488        if(tmp->Subkey != NULL)
2489            Props[n++] = p ->Subkey;
2490    }
2491
2492    *SubpropertyNames = Props;
2493    return n;
2494}
2495
2496static
2497int LocatePatch(cmsIT8* it8, const char* cPatch)
2498{
2499    int i;
2500    const char *data;
2501    TABLE* t = GetTable(it8);
2502
2503    for (i=0; i < t-> nPatches; i++) {
2504
2505        data = GetData(it8, i, t->SampleID);
2506
2507        if (data != NULL) {
2508
2509                if (cmsstrcasecmp(data, cPatch) == 0)
2510                        return i;
2511                }
2512        }
2513
2514        // SynError(it8, "Couldn't find patch '%s'\n", cPatch);
2515        return -1;
2516}
2517
2518
2519static
2520int LocateEmptyPatch(cmsIT8* it8)
2521{
2522    int i;
2523    const char *data;
2524    TABLE* t = GetTable(it8);
2525
2526    for (i=0; i < t-> nPatches; i++) {
2527
2528        data = GetData(it8, i, t->SampleID);
2529
2530        if (data == NULL)
2531            return i;
2532
2533    }
2534
2535    return -1;
2536}
2537
2538static
2539int LocateSample(cmsIT8* it8, const char* cSample)
2540{
2541    int i;
2542    const char *fld;
2543    TABLE* t = GetTable(it8);
2544
2545    for (i=0; i < t->nSamples; i++) {
2546
2547        fld = GetDataFormat(it8, i);
2548        if (cmsstrcasecmp(fld, cSample) == 0)
2549            return i;
2550    }
2551
2552    return -1;
2553
2554}
2555
2556
2557int CMSEXPORT cmsIT8FindDataFormat(cmsHANDLE hIT8, const char* cSample)
2558{
2559    cmsIT8* it8 = (cmsIT8*) hIT8;
2560
2561    _cmsAssert(hIT8 != NULL);
2562
2563    return LocateSample(it8, cSample);
2564}
2565
2566
2567
2568const char* CMSEXPORT cmsIT8GetDataRowCol(cmsHANDLE hIT8, int row, int col)
2569{
2570    cmsIT8* it8 = (cmsIT8*) hIT8;
2571
2572    _cmsAssert(hIT8 != NULL);
2573
2574    return GetData(it8, row, col);
2575}
2576
2577
2578cmsFloat64Number CMSEXPORT cmsIT8GetDataRowColDbl(cmsHANDLE hIT8, int row, int col)
2579{
2580    const char* Buffer;
2581
2582    Buffer = cmsIT8GetDataRowCol(hIT8, row, col);
2583
2584    if (Buffer == NULL) return 0.0;
2585
2586    return ParseFloatNumber(Buffer);
2587}
2588
2589
2590cmsBool CMSEXPORT cmsIT8SetDataRowCol(cmsHANDLE hIT8, int row, int col, const char* Val)
2591{
2592    cmsIT8* it8 = (cmsIT8*) hIT8;
2593
2594    _cmsAssert(hIT8 != NULL);
2595
2596    return SetData(it8, row, col, Val);
2597}
2598
2599
2600cmsBool CMSEXPORT cmsIT8SetDataRowColDbl(cmsHANDLE hIT8, int row, int col, cmsFloat64Number Val)
2601{
2602    cmsIT8* it8 = (cmsIT8*) hIT8;
2603    char Buff[256];
2604
2605    _cmsAssert(hIT8 != NULL);
2606
2607    sprintf(Buff, it8->DoubleFormatter, Val);
2608
2609    return SetData(it8, row, col, Buff);
2610}
2611
2612
2613
2614const char* CMSEXPORT cmsIT8GetData(cmsHANDLE hIT8, const char* cPatch, const char* cSample)
2615{
2616    cmsIT8* it8 = (cmsIT8*) hIT8;
2617    int iField, iSet;
2618
2619    _cmsAssert(hIT8 != NULL);
2620
2621    iField = LocateSample(it8, cSample);
2622    if (iField < 0) {
2623        return NULL;
2624    }
2625
2626    iSet = LocatePatch(it8, cPatch);
2627    if (iSet < 0) {
2628            return NULL;
2629    }
2630
2631    return GetData(it8, iSet, iField);
2632}
2633
2634
2635cmsFloat64Number CMSEXPORT cmsIT8GetDataDbl(cmsHANDLE  it8, const char* cPatch, const char* cSample)
2636{
2637    const char* Buffer;
2638
2639    Buffer = cmsIT8GetData(it8, cPatch, cSample);
2640
2641    return ParseFloatNumber(Buffer);
2642}
2643
2644
2645
2646cmsBool CMSEXPORT cmsIT8SetData(cmsHANDLE hIT8, const char* cPatch, const char* cSample, const char *Val)
2647{
2648    cmsIT8* it8 = (cmsIT8*) hIT8;
2649    int iField, iSet;
2650    TABLE* t;
2651
2652    _cmsAssert(hIT8 != NULL);
2653
2654    t = GetTable(it8);
2655
2656    iField = LocateSample(it8, cSample);
2657
2658    if (iField < 0)
2659        return FALSE;
2660
2661    if (t-> nPatches == 0) {
2662
2663        AllocateDataFormat(it8);
2664        AllocateDataSet(it8);
2665        CookPointers(it8);
2666    }
2667
2668    if (cmsstrcasecmp(cSample, "SAMPLE_ID") == 0) {
2669
2670        iSet   = LocateEmptyPatch(it8);
2671        if (iSet < 0) {
2672            return SynError(it8, "Couldn't add more patches '%s'\n", cPatch);
2673        }
2674
2675        iField = t -> SampleID;
2676    }
2677    else {
2678        iSet = LocatePatch(it8, cPatch);
2679        if (iSet < 0) {
2680            return FALSE;
2681        }
2682    }
2683
2684    return SetData(it8, iSet, iField, Val);
2685}
2686
2687
2688cmsBool CMSEXPORT cmsIT8SetDataDbl(cmsHANDLE hIT8, const char* cPatch,
2689                                   const char* cSample,
2690                                   cmsFloat64Number Val)
2691{
2692    cmsIT8* it8 = (cmsIT8*) hIT8;
2693    char Buff[256];
2694
2695    _cmsAssert(hIT8 != NULL);
2696
2697    snprintf(Buff, 255, it8->DoubleFormatter, Val);
2698    return cmsIT8SetData(hIT8, cPatch, cSample, Buff);
2699}
2700
2701// Buffer should get MAXSTR at least
2702
2703const char* CMSEXPORT cmsIT8GetPatchName(cmsHANDLE hIT8, int nPatch, char* buffer)
2704{
2705    cmsIT8* it8 = (cmsIT8*) hIT8;
2706    TABLE* t;
2707    char* Data;
2708
2709    _cmsAssert(hIT8 != NULL);
2710
2711    t = GetTable(it8);
2712    Data = GetData(it8, nPatch, t->SampleID);
2713
2714    if (!Data) return NULL;
2715    if (!buffer) return Data;
2716
2717    strncpy(buffer, Data, MAXSTR-1);
2718    buffer[MAXSTR-1] = 0;
2719    return buffer;
2720}
2721
2722int CMSEXPORT cmsIT8GetPatchByName(cmsHANDLE hIT8, const char *cPatch)
2723{
2724    _cmsAssert(hIT8 != NULL);
2725
2726    return LocatePatch((cmsIT8*)hIT8, cPatch);
2727}
2728
2729cmsUInt32Number CMSEXPORT cmsIT8TableCount(cmsHANDLE hIT8)
2730{
2731    cmsIT8* it8 = (cmsIT8*) hIT8;
2732
2733    _cmsAssert(hIT8 != NULL);
2734
2735    return it8 ->TablesCount;
2736}
2737
2738// This handles the "LABEL" extension.
2739// Label, nTable, Type
2740
2741int CMSEXPORT cmsIT8SetTableByLabel(cmsHANDLE hIT8, const char* cSet, const char* cField, const char* ExpectedType)
2742{
2743    const char* cLabelFld;
2744    char Type[256], Label[256];
2745    int nTable;
2746
2747    _cmsAssert(hIT8 != NULL);
2748
2749    if (cField != NULL && *cField == 0)
2750            cField = "LABEL";
2751
2752    if (cField == NULL)
2753            cField = "LABEL";
2754
2755    cLabelFld = cmsIT8GetData(hIT8, cSet, cField);
2756    if (!cLabelFld) return -1;
2757
2758    if (sscanf(cLabelFld, "%255s %d %255s", Label, &nTable, Type) != 3)
2759            return -1;
2760
2761    if (ExpectedType != NULL && *ExpectedType == 0)
2762        ExpectedType = NULL;
2763
2764    if (ExpectedType) {
2765
2766        if (cmsstrcasecmp(Type, ExpectedType) != 0) return -1;
2767    }
2768
2769    return cmsIT8SetTable(hIT8, nTable);
2770}
2771
2772
2773cmsBool CMSEXPORT cmsIT8SetIndexColumn(cmsHANDLE hIT8, const char* cSample)
2774{
2775    cmsIT8* it8 = (cmsIT8*) hIT8;
2776    int pos;
2777
2778    _cmsAssert(hIT8 != NULL);
2779
2780    pos = LocateSample(it8, cSample);
2781    if(pos == -1)
2782        return FALSE;
2783
2784    it8->Tab[it8->nTable].SampleID = pos;
2785    return TRUE;
2786}
2787
2788
2789void CMSEXPORT cmsIT8DefineDblFormat(cmsHANDLE hIT8, const char* Formatter)
2790{
2791    cmsIT8* it8 = (cmsIT8*) hIT8;
2792
2793    _cmsAssert(hIT8 != NULL);
2794
2795    if (Formatter == NULL)
2796        strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT);
2797    else
2798        strncpy(it8->DoubleFormatter, Formatter, sizeof(it8->DoubleFormatter));
2799
2800    it8 ->DoubleFormatter[sizeof(it8 ->DoubleFormatter)-1] = 0;
2801}
2802
2803