Coverage Report

Created: 2025-10-30 06:32

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