Coverage Report

Created: 2026-05-14 06:54

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-2026 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
58.7k
#define MAXID        128     // Max length of identifier
34
56.8k
#define MAXSTR      1024     // Max length of string
35
11.6k
#define MAXTABLES    255     // Max Number of tables in a single stream
36
321
#define MAXINCLUDE    20     // Max number of nested includes
37
38
2.95k
#define DEFAULT_DBL_FORMAT  "%.10g" // Double formatting
39
40
#ifdef CMS_IS_WINDOWS_
41
#    include <io.h>
42
#    define DIR_CHAR    '\\'
43
#else
44
270
#    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
393k
#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
79.8k
#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
124k
#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
5.91k
{
403
5.91k
    string* s = (string*) AllocChunk(it8, sizeof(string));
404
5.91k
    if (s == NULL) return NULL;
405
406
5.91k
    s->it8 = it8;
407
5.91k
    s->max = max;
408
5.91k
    s->len = 0;
409
5.91k
    s->begin = (char*) AllocChunk(it8, s->max);
410
411
5.91k
    return s;
412
5.91k
}
413
414
static
415
void StringClear(string* s)
416
341k
{
417
341k
    s->len = 0;    
418
341k
    s->begin[0] = 0;
419
341k
}
420
421
static
422
cmsBool StringAppend(string* s, char c)
423
12.8M
{
424
12.8M
    if (s->len + 1 >= s->max)
425
141
    {
426
141
        char* new_ptr;
427
428
141
        s->max *= 10;
429
141
        new_ptr = (char*) AllocChunk(s->it8, s->max);
430
141
        if (new_ptr == NULL) return FALSE;
431
432
141
        if (s->begin != NULL)
433
141
            memcpy(new_ptr, s->begin, s->len);
434
435
141
        s->begin = new_ptr;
436
141
    }
437
438
12.8M
    if (s->begin != NULL)
439
12.8M
    {
440
12.8M
        s->begin[s->len++] = c;
441
12.8M
        s->begin[s->len] = 0;
442
12.8M
    }
443
444
12.8M
    return TRUE;
445
12.8M
}
446
447
static
448
char* StringPtr(string* s)
449
513k
{
450
513k
    return s->begin;
451
513k
}
452
453
static
454
cmsBool StringCat(string* s, const char* c)
455
3.20k
{
456
7.83k
    while (*c)
457
4.63k
    {
458
4.63k
        if (!StringAppend(s, *c)) return FALSE;
459
4.63k
        c++;
460
4.63k
    }
461
462
3.20k
    return TRUE;
463
3.20k
}
464
465
466
// Checks whatever c is a separator
467
static
468
cmsBool isseparator(int c)
469
5.06M
{
470
5.06M
    return (c == ' ') || (c == '\t');
471
5.06M
}
472
473
// Checks whatever c is a valid identifier char
474
static
475
cmsBool ismiddle(int c)
476
3.30M
{
477
3.30M
   return (!isseparator(c) && (c != '#') && (c !='\"') && (c != '\'') && (c > 32) && (c < 127));
478
3.30M
}
479
480
// Checks whatsever c is a valid identifier middle char.
481
static
482
cmsBool isidchar(int c)
483
8.50M
{
484
8.50M
   return isalnum(c) || ismiddle(c);
485
8.50M
}
486
487
// Checks whatsever c is a valid identifier first char.
488
static
489
cmsBool isfirstidchar(int c)
490
418k
{
491
418k
     return c != '-' && !isdigit(c) && ismiddle(c);
492
418k
}
493
494
// Guess whether the supplied path looks like an absolute path
495
static
496
cmsBool isabsolutepath(const char *path)
497
260
{
498
260
    char ThreeChars[4];
499
500
260
    if(path == NULL)
501
0
        return FALSE;
502
260
    if (path[0] == 0)
503
1
        return FALSE;
504
505
259
    strncpy(ThreeChars, path, 3);
506
259
    ThreeChars[3] = 0;
507
508
259
    if(ThreeChars[0] == DIR_CHAR)
509
249
        return TRUE;
510
511
#ifdef  CMS_IS_WINDOWS_
512
    if (isalpha((int) ThreeChars[0]) && ThreeChars[1] == ':')
513
        return TRUE;
514
#endif
515
10
    return FALSE;
516
259
}
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
260
{
524
260
    char *tail;
525
260
    cmsUInt32Number len;
526
527
    // Already absolute?
528
260
    if (isabsolutepath(relPath)) {
529
530
249
        memcpy(buffer, relPath, MaxLen);
531
249
        buffer[MaxLen-1] = 0;
532
249
        return TRUE;
533
249
    }
534
535
    // No, search for last
536
11
    memcpy(buffer, basePath, MaxLen);
537
11
    buffer[MaxLen-1] = 0;
538
539
11
    tail = strrchr(buffer, DIR_CHAR);
540
11
    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
453
{
556
453
    if (strchr(str, '%') != NULL)
557
0
        return "**** CORRUPTED FORMAT STRING ***";
558
559
453
    return str;
560
453
}
561
562
// Syntax error
563
static
564
cmsBool SynError(cmsIT8* it8, const char *Txt, ...)
565
8.27k
{
566
8.27k
    char Buffer[256], ErrMsg[1024];
567
8.27k
    va_list args;
568
569
8.27k
    va_start(args, Txt);
570
8.27k
    vsnprintf(Buffer, 255, Txt, args);
571
8.27k
    Buffer[255] = 0;
572
8.27k
    va_end(args);
573
574
8.27k
    snprintf(ErrMsg, 1023, "%s: Line %d, %s", it8->FileStack[it8 ->IncludeSP]->FileName, it8->lineno, Buffer);
575
8.27k
    ErrMsg[1023] = 0;
576
8.27k
    it8->sy = SSYNERROR;
577
8.27k
    cmsSignalError(it8 ->ContextID, cmsERROR_CORRUPTION_DETECTED, "%s", ErrMsg);
578
8.27k
    return FALSE;
579
8.27k
}
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
12.8k
{
585
12.8k
        if (it8 -> sy != sy)
586
453
                return SynError(it8, NoMeta(Err));
587
12.3k
        return TRUE;
588
12.8k
}
589
590
// Read Next character from stream
591
static
592
void NextCh(cmsIT8* it8)
593
14.3M
{
594
14.3M
    if (it8 -> FileStack[it8 ->IncludeSP]->Stream) {
595
596
1.27M
        it8 ->ch = fgetc(it8 ->FileStack[it8 ->IncludeSP]->Stream);
597
598
1.27M
        if (feof(it8 -> FileStack[it8 ->IncludeSP]->Stream))  {
599
600
178
            if (it8 ->IncludeSP > 0) {
601
602
81
                fclose(it8 ->FileStack[it8->IncludeSP--]->Stream);
603
81
                it8 -> ch = ' ';                            // Whitespace to be ignored
604
605
81
            } else
606
97
                it8 ->ch = 0;   // EOF
607
178
        }
608
1.27M
    }
609
13.1M
    else {
610
13.1M
        it8->ch = *it8->Source;
611
13.1M
        if (it8->ch) it8->Source++;
612
13.1M
    }
613
14.3M
}
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
196k
{
620
196k
    int l = 1;
621
196k
    int r = NumKeys;
622
196k
    int x, res;
623
624
774k
    while (r >= l)
625
602k
    {
626
602k
        x = (l+r)/2;
627
602k
        res = cmsstrcasecmp(id, TabKeys[x-1].id);
628
602k
        if (res == 0) return TabKeys[x-1].sy;
629
578k
        if (res < 0) r = x - 1;
630
274k
        else l = x + 1;
631
578k
    }
632
633
172k
    return SUNDEFINED;
634
196k
}
635
636
637
// 10 ^n
638
static
639
cmsFloat64Number xpow10(int n)
640
3.78k
{
641
3.78k
    return pow(10, (cmsFloat64Number) n);
642
3.78k
}
643
644
645
//  Reads a Real number, tries to follow from integer number
646
static
647
void ReadReal(cmsIT8* it8, cmsInt32Number inum)
648
2.68k
{
649
2.68k
    it8->dnum = (cmsFloat64Number)inum;
650
651
4.43k
    while (isdigit(it8->ch)) {
652
653
4.43k
        it8->dnum = (cmsFloat64Number)it8->dnum * 10.0 + (cmsFloat64Number)(it8->ch - '0');
654
4.43k
        NextCh(it8);
655
4.43k
    }
656
657
2.68k
    if (it8->ch == '.') {        // Decimal point
658
659
2.41k
        cmsFloat64Number frac = 0.0;      // fraction
660
2.41k
        int prec = 0;                     // precision
661
662
2.41k
        NextCh(it8);               // Eats dec. point
663
664
3.03k
        while (isdigit(it8->ch)) {
665
666
3.03k
            frac = frac * 10.0 + (cmsFloat64Number)(it8->ch - '0');
667
3.03k
            prec++;
668
3.03k
            NextCh(it8);
669
3.03k
        }
670
671
2.41k
        it8->dnum = it8->dnum + (frac / xpow10(prec));
672
2.41k
    }
673
674
    // Exponent, example 34.00E+20
675
2.68k
    if (toupper(it8->ch) == 'E') {
676
677
1.37k
        cmsInt32Number e;
678
1.37k
        cmsInt32Number sgn;
679
680
1.37k
        NextCh(it8); sgn = 1;
681
682
1.37k
        if (it8->ch == '-') {
683
684
208
            sgn = -1; NextCh(it8);
685
208
        }
686
1.16k
        else
687
1.16k
            if (it8->ch == '+') {
688
689
205
                sgn = +1;
690
205
                NextCh(it8);
691
205
            }
692
693
1.37k
        e = 0;
694
1.37k
        while (isdigit(it8->ch)) {
695
696
1.17k
            cmsInt32Number digit = (it8->ch - '0');
697
698
1.17k
            if ((cmsFloat64Number)e * 10.0 + (cmsFloat64Number)digit < (cmsFloat64Number)+2147483647.0)
699
955
                e = e * 10 + digit;
700
701
1.17k
            NextCh(it8);
702
1.17k
        }
703
704
1.37k
        e = sgn*e;
705
1.37k
        it8->dnum = it8->dnum * xpow10(e);
706
1.37k
    }
707
2.68k
}
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
198
{
715
198
    cmsFloat64Number dnum = 0.0;
716
198
    int sign = 1;
717
718
    // keep safe
719
198
    if (Buffer == NULL) return 0.0;
720
721
141
    if (*Buffer == '-' || *Buffer == '+') {
722
723
0
        sign = (*Buffer == '-') ? -1 : 1;
724
0
        Buffer++;
725
0
    }
726
727
728
571
    while (*Buffer && isdigit((int)*Buffer)) {
729
730
430
        dnum = dnum * 10.0 + (*Buffer - '0');
731
430
        if (*Buffer) Buffer++;
732
430
    }
733
734
141
    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
141
    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
141
    return sign * dnum;
788
198
}
789
790
791
// Reads a string, special case to avoid infinite recursion on .include
792
static
793
void InStringSymbol(cmsIT8* it8)
794
141k
{
795
141k
    while (isseparator(it8->ch))
796
540
        NextCh(it8);
797
798
141k
    if (it8->ch == '\'' || it8->ch == '\"')
799
141k
    {
800
141k
        int sng;
801
802
141k
        sng = it8->ch;
803
141k
        StringClear(it8->str);
804
805
141k
        NextCh(it8);
806
807
4.45M
        while (it8->ch != sng) {
808
809
4.41M
            if (it8->ch == '\n' || it8->ch == '\r' || it8->ch == 0) break;
810
4.31M
            else {
811
4.31M
                if (!StringAppend(it8->str, (char)it8->ch)) {
812
813
0
                    SynError(it8, "Out of memory");                    
814
0
                    return;
815
0
                }
816
817
4.31M
                NextCh(it8);
818
4.31M
            }
819
4.41M
        }
820
821
141k
        it8->sy = SSTRING;
822
141k
        NextCh(it8);        
823
141k
    }
824
61
    else
825
61
        SynError(it8, "String expected");
826
827
141k
}
828
829
// Reads next symbol
830
static
831
void InSymbol(cmsIT8* it8)
832
416k
{
833
416k
    SYMBOL key;
834
    
835
418k
    do {
836
837
1.60M
        while (isseparator(it8->ch))
838
1.18M
            NextCh(it8);
839
840
418k
        if (isfirstidchar(it8->ch)) {          // Identifier
841
842
196k
            StringClear(it8->id);
843
844
5.43M
            do {
845
846
5.43M
                if (!StringAppend(it8->id, (char)it8->ch)) {
847
848
0
                    SynError(it8, "Out of memory");                    
849
0
                    return;
850
0
                }
851
852
5.43M
                NextCh(it8);
853
854
5.43M
            } while (isidchar(it8->ch));
855
856
857
196k
            key = BinSrchKey(StringPtr(it8->id),
858
196k
                    it8->IsCUBE ? NUMKEYS_CUBE : NUMKEYS_IT8,
859
196k
                    it8->IsCUBE ? TabKeysCUBE : TabKeysIT8);
860
196k
            if (key == SUNDEFINED) it8->sy = SIDENT;
861
24.8k
            else it8->sy = key;
862
863
196k
        }
864
221k
        else                         // Is a number?
865
221k
            if (isdigit(it8->ch) || it8->ch == '.' || it8->ch == '-' || it8->ch == '+')
866
24.7k
            {
867
24.7k
                int sign = 1;
868
869
24.7k
                if (it8->ch == '-') {
870
674
                    sign = -1;
871
674
                    NextCh(it8);
872
674
                }
873
24.1k
                else
874
24.1k
                    if (it8->ch == '+') {
875
0
                        sign = +1;
876
0
                        NextCh(it8);
877
0
                    }
878
879
24.7k
                it8->inum = 0;
880
24.7k
                it8->sy   = SINUM;
881
882
24.7k
                if (it8->ch == '0') {          // 0xnnnn (Hexa) or 0bnnnn (Binary)
883
884
4.63k
                    NextCh(it8);
885
4.63k
                    if (toupper(it8->ch) == 'X') {
886
887
690
                        int j;
888
889
690
                        NextCh(it8);
890
690
                        while (isxdigit(it8->ch))
891
1.37k
                        {
892
1.37k
                            it8->ch = toupper(it8->ch);
893
1.37k
                            if (it8->ch >= 'A' && it8->ch <= 'F')  j = it8->ch -'A'+10;
894
947
                            else j = it8->ch - '0';
895
896
1.37k
                            if ((cmsFloat64Number) it8->inum * 16.0 + (cmsFloat64Number) j > (cmsFloat64Number)+2147483647.0)
897
7
                            {
898
7
                                SynError(it8, "Invalid hexadecimal number");
899
7
                                return;
900
7
                            }
901
902
1.37k
                            it8->inum = it8->inum * 16 + j;
903
1.37k
                            NextCh(it8);
904
1.37k
                        }
905
683
                        return;
906
690
                    }
907
908
3.94k
                    if (toupper(it8->ch) == 'B') {  // Binary
909
910
363
                        int j;
911
912
363
                        NextCh(it8);
913
840
                        while (it8->ch == '0' || it8->ch == '1')
914
478
                        {
915
478
                            j = it8->ch - '0';
916
917
478
                            if ((cmsFloat64Number) it8->inum * 2.0 + j > (cmsFloat64Number)+2147483647.0)
918
1
                            {
919
1
                                SynError(it8, "Invalid binary number");                                
920
1
                                return;
921
1
                            }
922
923
477
                            it8->inum = it8->inum * 2 + j;
924
477
                            NextCh(it8);
925
477
                        }
926
362
                        return;
927
363
                    }
928
3.94k
                }
929
930
931
33.8k
                while (isdigit(it8->ch)) {
932
933
33.8k
                    cmsInt32Number digit = (it8->ch - '0');
934
935
33.8k
                    if ((cmsFloat64Number) it8->inum * 10.0 + (cmsFloat64Number) digit > (cmsFloat64Number) +2147483647.0) {
936
271
                        ReadReal(it8, it8->inum);
937
271
                        it8->sy = SDNUM;
938
271
                        it8->dnum *= sign;
939
271
                        return;
940
271
                    }
941
942
33.6k
                    it8->inum = it8->inum * 10 + digit;
943
33.6k
                    NextCh(it8);
944
33.6k
                }
945
946
23.4k
                if (it8->ch == '.') {
947
948
2.41k
                    ReadReal(it8, it8->inum);
949
2.41k
                    it8->sy = SDNUM;
950
2.41k
                    it8->dnum *= sign;
951
2.41k
                    return;
952
2.41k
                }
953
954
21.0k
                it8 -> inum *= sign;
955
956
                // Special case. Numbers followed by letters are taken as identifiers
957
958
21.0k
                if (isidchar(it8 ->ch)) {
959
960
3.20k
                    char buffer[127];
961
962
3.20k
                    if (it8 ->sy == SINUM) {
963
964
3.20k
                        snprintf(buffer, sizeof(buffer), "%d", it8->inum);
965
3.20k
                    }
966
0
                    else {
967
968
0
                        snprintf(buffer, sizeof(buffer), it8 ->DoubleFormatter, it8->dnum);
969
0
                    }
970
971
3.20k
                    StringClear(it8->id);
972
3.20k
                    if (!StringCat(it8->id, buffer)) {
973
974
0
                        SynError(it8, "Out of memory");                        
975
0
                        return;
976
0
                    }
977
978
3.04M
                    do {
979
980
3.04M
                        if (!StringAppend(it8->id, (char)it8->ch)) {
981
982
0
                            SynError(it8, "Out of memory");                            
983
0
                            return;
984
0
                        }
985
986
3.04M
                        NextCh(it8);
987
988
3.04M
                    } while (isidchar(it8->ch));
989
990
3.20k
                    it8->sy = SIDENT;
991
3.20k
                }
992
21.0k
                return;
993
994
21.0k
            }
995
196k
            else
996
196k
                switch ((int) it8->ch) {
997
        
998
        // Eof stream markers
999
5
        case '\x1a':
1000
1.56k
        case 0:
1001
1.58k
        case -1:
1002
1.58k
            it8->sy = SEOF;
1003
1.58k
            break;
1004
1005
1006
        // Next line
1007
23.0k
        case '\r':
1008
23.0k
            NextCh(it8);
1009
23.0k
            if (it8->ch == '\n')
1010
2.62k
                NextCh(it8);
1011
23.0k
            it8->sy = SEOLN;
1012
23.0k
            it8->lineno++;
1013
23.0k
            break;
1014
1015
29.4k
        case '\n':
1016
29.4k
            NextCh(it8);
1017
29.4k
            it8->sy = SEOLN;
1018
29.4k
            it8->lineno++;
1019
29.4k
            break;
1020
1021
        // Comment
1022
1.52k
        case '#':
1023
1.52k
            NextCh(it8);
1024
2.46k
            while (it8->ch && it8->ch != '\n' && it8->ch != '\r')
1025
941
                NextCh(it8);
1026
1027
1.52k
            it8->sy = SCOMMENT;
1028
1.52k
            break;
1029
1030
        // String.
1031
135k
        case '\'':
1032
140k
        case '\"':
1033
140k
            InStringSymbol(it8);
1034
140k
            break;
1035
1036
1037
164
        default:
1038
164
            SynError(it8, "Unrecognized character: 0x%x", it8 ->ch);            
1039
164
            return;
1040
196k
            }
1041
1042
418k
    } while (it8->sy == SCOMMENT);
1043
1044
    // Handle the include special token
1045
1046
391k
    if (it8 -> sy == SINCLUDE) {
1047
1048
321
                FILECTX* FileNest;
1049
1050
321
                if(it8 -> IncludeSP >= (MAXINCLUDE-1)) {
1051
1052
0
                    SynError(it8, "Too many recursion levels");                    
1053
0
                    return;
1054
0
                }
1055
1056
321
                InStringSymbol(it8);
1057
321
                if (!Check(it8, SSTRING, "Filename expected"))                                    
1058
61
                    return;
1059
                
1060
1061
260
                FileNest = it8 -> FileStack[it8 -> IncludeSP + 1];
1062
260
                if(FileNest == NULL) {
1063
1064
207
                    FileNest = it8 ->FileStack[it8 -> IncludeSP + 1] = (FILECTX*)AllocChunk(it8, sizeof(FILECTX));
1065
207
                    if (FileNest == NULL) {
1066
1067
0
                        SynError(it8, "Out of memory");                        
1068
0
                        return;
1069
0
                    }
1070
207
                }
1071
1072
260
                if (BuildAbsolutePath(StringPtr(it8->str),
1073
260
                                      it8->FileStack[it8->IncludeSP]->FileName,
1074
260
                                      FileNest->FileName, cmsMAX_PATH-1) == FALSE) {
1075
1076
11
                    SynError(it8, "File path too long");                    
1077
11
                    return;
1078
11
                }
1079
1080
249
                FileNest->Stream = fopen(FileNest->FileName, "rt");
1081
249
                if (FileNest->Stream == NULL) {
1082
1083
160
                        SynError(it8, "File %s not found", FileNest->FileName);                        
1084
160
                        return;
1085
160
                }
1086
89
                it8->IncludeSP++;
1087
1088
89
                it8 ->ch = ' ';
1089
89
                InSymbol(it8);
1090
89
    }
1091
1092
391k
}
1093
1094
// Checks end of line separator
1095
static
1096
cmsBool CheckEOLN(cmsIT8* it8)
1097
12.4k
{
1098
12.4k
    if (!Check(it8, SEOLN, "Expected separator")) return FALSE;
1099
24.6k
    while (it8->sy == SEOLN)
1100
12.5k
        InSymbol(it8);
1101
12.0k
    return TRUE;
1102
1103
12.4k
}
1104
1105
// Skip a symbol
1106
1107
static
1108
void Skip(cmsIT8* it8, SYMBOL sy)
1109
12.0k
{
1110
12.0k
    if (it8->sy == sy && it8->sy != SEOF && it8->sy != SSYNERROR)
1111
11.3k
        InSymbol(it8);
1112
12.0k
}
1113
1114
1115
// Skip multiple EOLN
1116
static
1117
void SkipEOLN(cmsIT8* it8)
1118
338k
{
1119
374k
    while (it8->sy == SEOLN) {
1120
35.4k
        InSymbol(it8);
1121
35.4k
    }
1122
338k
}
1123
1124
1125
// Returns a string holding current value
1126
static
1127
cmsBool GetVal(cmsIT8* it8, char* Buffer, cmsUInt32Number max, const char* ErrorTitle)
1128
35.4k
{
1129
35.4k
    switch (it8->sy) {
1130
1131
4.43k
    case SEOLN:   // Empty value
1132
4.43k
                  Buffer[0]=0;
1133
4.43k
                  break;
1134
1.47k
    case SIDENT:  strncpy(Buffer, StringPtr(it8->id), max);
1135
1.47k
                  Buffer[max-1]=0;
1136
1.47k
                  break;
1137
18.7k
    case SINUM:   snprintf(Buffer, max, "%d", it8 -> inum); break;
1138
2.51k
    case SDNUM:   snprintf(Buffer, max, it8->DoubleFormatter, it8 -> dnum); break;
1139
7.97k
    case SSTRING: strncpy(Buffer, StringPtr(it8->str), max);
1140
7.97k
                  Buffer[max-1] = 0;
1141
7.97k
                  break;
1142
1143
1144
331
    default:
1145
331
         return SynError(it8, "%s", ErrorTitle);
1146
35.4k
    }
1147
1148
35.1k
    Buffer[max] = 0;
1149
35.1k
    return TRUE;
1150
35.4k
}
1151
1152
// ---------------------------------------------------------- Table
1153
1154
static
1155
TABLE* GetTable(cmsIT8* it8)
1156
415k
{
1157
415k
   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
415k
   return it8 ->Tab + it8 ->nTable;
1164
415k
}
1165
1166
// ---------------------------------------------------------- Memory management
1167
1168
1169
// Frees an allocator and owned memory
1170
void CMSEXPORT cmsIT8Free(cmsHANDLE hIT8)
1171
2.95k
{
1172
2.95k
   cmsIT8* it8 = (cmsIT8*) hIT8;
1173
1174
2.95k
    if (it8 == NULL)
1175
0
        return;
1176
1177
2.95k
    if (it8->MemorySink) {
1178
1179
2.95k
        OWNEDMEM* p;
1180
2.95k
        OWNEDMEM* n;
1181
1182
6.75k
        for (p = it8->MemorySink; p != NULL; p = n) {
1183
1184
3.79k
            n = p->Next;
1185
3.79k
            if (p->Ptr) _cmsFree(it8 ->ContextID, p->Ptr);
1186
3.79k
            _cmsFree(it8 ->ContextID, p);
1187
3.79k
        }
1188
2.95k
    }
1189
1190
2.95k
    if (it8->MemoryBlock)
1191
1.87k
        _cmsFree(it8 ->ContextID, it8->MemoryBlock);
1192
1193
2.95k
    _cmsFree(it8 ->ContextID, it8);
1194
2.95k
}
1195
1196
1197
// Allocates a chunk of data, keep linked list
1198
static
1199
void* AllocBigBlock(cmsIT8* it8, cmsUInt32Number size)
1200
3.79k
{
1201
3.79k
    OWNEDMEM* ptr1;
1202
3.79k
    void* ptr = _cmsMallocZero(it8->ContextID, size);
1203
1204
3.79k
    if (ptr != NULL) {
1205
1206
3.79k
        ptr1 = (OWNEDMEM*) _cmsMallocZero(it8 ->ContextID, sizeof(OWNEDMEM));
1207
1208
3.79k
        if (ptr1 == NULL) {
1209
1210
0
            _cmsFree(it8 ->ContextID, ptr);
1211
0
            return NULL;
1212
0
        }
1213
1214
3.79k
        ptr1-> Ptr        = ptr;
1215
3.79k
        ptr1-> Next       = it8 -> MemorySink;
1216
3.79k
        it8 -> MemorySink = ptr1;
1217
3.79k
    }
1218
1219
3.79k
    return ptr;
1220
3.79k
}
1221
1222
1223
// Suballocator.
1224
static
1225
void* AllocChunk(cmsIT8* it8, cmsUInt32Number size)
1226
790k
{
1227
790k
    cmsUInt32Number Free = it8 ->Allocator.BlockSize - it8 ->Allocator.Used;
1228
790k
    cmsUInt8Number* ptr;
1229
1230
790k
    size = _cmsALIGNMEM(size);
1231
790k
    if (size == 0) return NULL;
1232
1233
790k
    if (size > Free) {
1234
1235
3.79k
        cmsUInt8Number* new_block;
1236
1237
3.79k
        if (it8 -> Allocator.BlockSize == 0)
1238
1239
2.95k
                it8 -> Allocator.BlockSize = 20*1024;
1240
843
        else
1241
843
                it8 ->Allocator.BlockSize *= 2;
1242
1243
3.79k
        if (it8 ->Allocator.BlockSize < size)
1244
363
                it8 ->Allocator.BlockSize = size;
1245
1246
3.79k
        it8 ->Allocator.Used = 0;
1247
3.79k
        new_block = (cmsUInt8Number*)AllocBigBlock(it8, it8->Allocator.BlockSize);
1248
3.79k
        if (new_block == NULL) goto Error;            
1249
1250
3.79k
        it8->Allocator.Block = new_block;
1251
3.79k
    }
1252
1253
790k
    if (it8->Allocator.Block == NULL)
1254
0
        goto Error;
1255
1256
790k
    ptr = it8 ->Allocator.Block + it8 ->Allocator.Used;
1257
790k
    it8 ->Allocator.Used += size;
1258
1259
790k
    return (void*) ptr;
1260
1261
0
Error:
1262
1263
0
    SynError(it8, "Allocation error");
1264
0
    it8->Allocator.BlockSize = 0;
1265
0
    it8->Allocator.Used = 0;
1266
0
    it8->Allocator.Block = NULL;
1267
0
    return NULL;
1268
790k
}
1269
1270
1271
// Allocates a string
1272
static
1273
char *AllocString(cmsIT8* it8, const char* str)
1274
541k
{
1275
541k
    cmsUInt32Number Size;
1276
541k
    char *ptr;
1277
1278
541k
    if (str == NULL) return NULL;
1279
1280
541k
    Size = (cmsUInt32Number)strlen(str) + 1;
1281
1282
541k
    ptr = (char *) AllocChunk(it8, Size);
1283
541k
    if (ptr) memcpy(ptr, str, Size-1);
1284
1285
541k
    return ptr;
1286
541k
}
1287
1288
// Searches through linked list
1289
1290
static
1291
cmsBool IsAvailableOnList(KEYVALUE* p, const char* Key, const char* Subkey, KEYVALUE** LastPtr)
1292
376k
{
1293
376k
    if (LastPtr) *LastPtr = p;
1294
1295
4.79M
    for (;  p != NULL; p = p->Next) {
1296
1297
4.48M
        if (LastPtr) *LastPtr = p;
1298
1299
4.48M
        if (*Key != '#') { // Comments are ignored
1300
1301
4.48M
            if (cmsstrcasecmp(Key, p->Keyword) == 0)
1302
67.3k
                break;
1303
4.48M
        }
1304
4.48M
    }
1305
1306
376k
    if (p == NULL)
1307
309k
        return FALSE;
1308
1309
67.3k
    if (Subkey == 0)
1310
67.3k
        return TRUE;
1311
1312
0
    for (; p != NULL; p = p->NextSubkey) {
1313
1314
0
        if (p ->Subkey == NULL) continue;
1315
1316
0
        if (LastPtr) *LastPtr = p;
1317
1318
0
        if (cmsstrcasecmp(Subkey, p->Subkey) == 0)
1319
0
            return TRUE;
1320
0
    }
1321
1322
0
    return FALSE;
1323
0
}
1324
1325
1326
1327
// Add a property into a linked list
1328
static
1329
KEYVALUE* AddToList(cmsIT8* it8, KEYVALUE** Head, const char *Key, const char *Subkey, const char* xValue, WRITEMODE WriteAs)
1330
233k
{
1331
233k
    KEYVALUE* p;
1332
233k
    KEYVALUE* last;
1333
1334
1335
    // Check if property is already in list
1336
1337
233k
    if (IsAvailableOnList(*Head, Key, Subkey, &p)) {
1338
1339
        // This may work for editing properties
1340
1341
11.5k
        if (cmsstrcasecmp(Key, "NUMBER_OF_FIELDS") == 0 ||
1342
11.5k
            cmsstrcasecmp(Key, "NUMBER_OF_SETS") == 0) {
1343
1344
6
            SynError(it8, "duplicate key <%s>", Key);
1345
6
            return NULL;
1346
6
        }
1347
11.5k
    }
1348
222k
    else {
1349
1350
222k
        last = p;
1351
1352
        // Allocate the container
1353
222k
        p = (KEYVALUE*) AllocChunk(it8, sizeof(KEYVALUE));
1354
222k
        if (p == NULL)
1355
0
        {
1356
0
            SynError(it8, "AddToList: out of memory");
1357
0
            return NULL;
1358
0
        }
1359
1360
        // Store name and value
1361
222k
        p->Keyword = AllocString(it8, Key);
1362
222k
        p->Subkey = (Subkey == NULL) ? NULL : AllocString(it8, Subkey);
1363
1364
        // Keep the container in our list
1365
222k
        if (*Head == NULL) {
1366
16.6k
            *Head = p;
1367
16.6k
        }
1368
205k
        else
1369
205k
        {
1370
205k
            if (Subkey != NULL && last != NULL) {
1371
1372
0
                last->NextSubkey = p;
1373
1374
                // If Subkey is not null, then last is the last property with the same key,
1375
                // but not necessarily is the last property in the list, so we need to move
1376
                // to the actual list end
1377
0
                while (last->Next != NULL)
1378
0
                         last = last->Next;
1379
0
            }
1380
1381
205k
            if (last != NULL) last->Next = p;
1382
205k
        }
1383
1384
222k
        p->Next    = NULL;
1385
222k
        p->NextSubkey = NULL;
1386
222k
    }
1387
1388
233k
    p->WriteAs = WriteAs;
1389
1390
233k
    if (xValue != NULL) {
1391
1392
31.2k
        p->Value   = AllocString(it8, xValue);
1393
31.2k
    }
1394
202k
    else {
1395
202k
        p->Value   = NULL;
1396
202k
    }
1397
1398
233k
    return p;
1399
233k
}
1400
1401
static
1402
KEYVALUE* AddAvailableProperty(cmsIT8* it8, const char* Key, WRITEMODE as)
1403
81.4k
{
1404
81.4k
    return AddToList(it8, &it8->ValidKeywords, Key, NULL, NULL, as);
1405
81.4k
}
1406
1407
1408
static
1409
KEYVALUE* AddAvailableSampleID(cmsIT8* it8, const char* Key)
1410
121k
{
1411
121k
    return AddToList(it8, &it8->ValidSampleID, Key, NULL, NULL, WRITE_UNCOOKED);
1412
121k
}
1413
1414
1415
static
1416
cmsBool AllocTable(cmsIT8* it8)
1417
11.6k
{
1418
11.6k
    TABLE* t;
1419
1420
11.6k
    if (it8->TablesCount >= (MAXTABLES-1)) 
1421
1
        return FALSE;
1422
1423
11.6k
    t = it8 ->Tab + it8 ->TablesCount;
1424
1425
11.6k
    t->HeaderList = NULL;
1426
11.6k
    t->DataFormat = NULL;
1427
11.6k
    t->Data       = NULL;
1428
1429
11.6k
    it8 ->TablesCount++;
1430
11.6k
    return TRUE;
1431
11.6k
}
1432
1433
1434
cmsInt32Number CMSEXPORT cmsIT8SetTable(cmsHANDLE  IT8, cmsUInt32Number nTable)
1435
1.88k
{
1436
1.88k
     cmsIT8* it8 = (cmsIT8*) IT8;
1437
1438
1.88k
     if (nTable >= it8 ->TablesCount) {
1439
1440
0
         if (nTable == it8 ->TablesCount) {
1441
1442
0
             if (!AllocTable(it8)) {
1443
0
                 SynError(it8, "Too many tables");
1444
0
                 return -1;
1445
0
             }
1446
0
         }
1447
0
         else {
1448
0
             SynError(it8, "Table %d is out of sequence", nTable);
1449
0
             return -1;
1450
0
         }
1451
0
     }
1452
1453
1.88k
     it8 ->nTable = nTable;
1454
1455
1.88k
     return (cmsInt32Number) nTable;
1456
1.88k
}
1457
1458
1459
1460
// Init an empty container
1461
cmsHANDLE  CMSEXPORT cmsIT8Alloc(cmsContext ContextID)
1462
2.95k
{
1463
2.95k
    cmsIT8* it8;
1464
2.95k
    cmsUInt32Number i;
1465
1466
2.95k
    it8 = (cmsIT8*) _cmsMallocZero(ContextID, sizeof(cmsIT8));
1467
2.95k
    if (it8 == NULL) return NULL;
1468
1469
2.95k
    AllocTable(it8);
1470
1471
2.95k
    it8->MemoryBlock = NULL;
1472
2.95k
    it8->MemorySink  = NULL;
1473
1474
2.95k
    it8->IsCUBE = FALSE;
1475
1476
2.95k
    it8 ->nTable = 0;
1477
1478
2.95k
    it8->ContextID = ContextID;
1479
2.95k
    it8->Allocator.Used = 0;
1480
2.95k
    it8->Allocator.Block = NULL;
1481
2.95k
    it8->Allocator.BlockSize = 0;
1482
1483
2.95k
    it8->ValidKeywords = NULL;
1484
2.95k
    it8->ValidSampleID = NULL;
1485
1486
2.95k
    it8 -> sy = SUNDEFINED;
1487
2.95k
    it8 -> ch = ' ';
1488
2.95k
    it8 -> Source = NULL;
1489
2.95k
    it8 -> inum = 0;
1490
2.95k
    it8 -> dnum = 0.0;
1491
1492
2.95k
    it8->FileStack[0] = (FILECTX*)AllocChunk(it8, sizeof(FILECTX));
1493
2.95k
    it8->IncludeSP   = 0;
1494
2.95k
    it8 -> lineno = 1;
1495
1496
2.95k
    it8->id = StringAlloc(it8, MAXSTR);
1497
2.95k
    it8->str = StringAlloc(it8, MAXSTR);
1498
1499
2.95k
    strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT);
1500
2.95k
    cmsIT8SetSheetType((cmsHANDLE) it8, "CGATS.17");
1501
1502
    // Initialize predefined properties & data
1503
1504
79.8k
    for (i=0; i < NUMPREDEFINEDPROPS; i++)
1505
76.8k
            AddAvailableProperty(it8, PredefinedProperties[i].id, PredefinedProperties[i].as);
1506
1507
124k
    for (i=0; i < NUMPREDEFINEDSAMPLEID; i++)
1508
121k
            AddAvailableSampleID(it8, PredefinedSampleID[i]);
1509
1510
1511
2.95k
   return (cmsHANDLE) it8;
1512
2.95k
}
1513
1514
1515
const char* CMSEXPORT cmsIT8GetSheetType(cmsHANDLE hIT8)
1516
0
{
1517
0
        return GetTable((cmsIT8*) hIT8)->SheetType;
1518
0
}
1519
1520
cmsBool CMSEXPORT cmsIT8SetSheetType(cmsHANDLE hIT8, const char* Type)
1521
8.76k
{
1522
8.76k
        TABLE* t = GetTable((cmsIT8*) hIT8);
1523
1524
8.76k
        strncpy(t ->SheetType, Type, MAXSTR-1);
1525
8.76k
        t ->SheetType[MAXSTR-1] = 0;
1526
8.76k
        return TRUE;
1527
8.76k
}
1528
1529
cmsBool CMSEXPORT cmsIT8SetComment(cmsHANDLE hIT8, const char* Val)
1530
0
{
1531
0
    cmsIT8* it8 = (cmsIT8*) hIT8;
1532
1533
0
    if (!Val) return FALSE;
1534
0
    if (!*Val) return FALSE;
1535
1536
0
    return AddToList(it8, &GetTable(it8)->HeaderList, "# ", NULL, Val, WRITE_UNCOOKED) != NULL;
1537
0
}
1538
1539
// Sets a property
1540
cmsBool CMSEXPORT cmsIT8SetPropertyStr(cmsHANDLE hIT8, const char* Key, const char *Val)
1541
556
{
1542
556
    cmsIT8* it8 = (cmsIT8*) hIT8;
1543
1544
556
    if (!Val) return FALSE;
1545
556
    if (!*Val) return FALSE;
1546
1547
556
    return AddToList(it8, &GetTable(it8)->HeaderList, Key, NULL, Val, WRITE_STRINGIFY) != NULL;
1548
556
}
1549
1550
cmsBool CMSEXPORT cmsIT8SetPropertyDbl(cmsHANDLE hIT8, const char* cProp, cmsFloat64Number Val)
1551
1.11k
{
1552
1.11k
    cmsIT8* it8 = (cmsIT8*) hIT8;
1553
1.11k
    char Buffer[1024];
1554
1555
1.11k
    snprintf(Buffer, 1023, it8->DoubleFormatter, Val);
1556
1557
1.11k
    return AddToList(it8, &GetTable(it8)->HeaderList, cProp, NULL, Buffer, WRITE_UNCOOKED) != NULL;
1558
1.11k
}
1559
1560
cmsBool CMSEXPORT cmsIT8SetPropertyHex(cmsHANDLE hIT8, const char* cProp, cmsUInt32Number Val)
1561
278
{
1562
278
    cmsIT8* it8 = (cmsIT8*) hIT8;
1563
278
    char Buffer[1024];
1564
1565
278
    snprintf(Buffer, 1023, "%u", Val);
1566
1567
278
    return AddToList(it8, &GetTable(it8)->HeaderList, cProp, NULL, Buffer, WRITE_HEXADECIMAL) != NULL;
1568
278
}
1569
1570
cmsBool CMSEXPORT cmsIT8SetPropertyUncooked(cmsHANDLE hIT8, const char* Key, const char* Buffer)
1571
278
{
1572
278
    cmsIT8* it8 = (cmsIT8*) hIT8;
1573
1574
278
    return AddToList(it8, &GetTable(it8)->HeaderList, Key, NULL, Buffer, WRITE_UNCOOKED) != NULL;
1575
278
}
1576
1577
cmsBool CMSEXPORT cmsIT8SetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char* SubKey, const char *Buffer)
1578
0
{
1579
0
    cmsIT8* it8 = (cmsIT8*) hIT8;
1580
1581
0
    return AddToList(it8, &GetTable(it8)->HeaderList, Key, SubKey, Buffer, WRITE_PAIR) != NULL;
1582
0
}
1583
1584
// Gets a property
1585
const char* CMSEXPORT cmsIT8GetProperty(cmsHANDLE hIT8, const char* Key)
1586
33.0k
{
1587
33.0k
    cmsIT8* it8 = (cmsIT8*) hIT8;
1588
33.0k
    KEYVALUE* p;
1589
1590
33.0k
    if (IsAvailableOnList(GetTable(it8) -> HeaderList, Key, NULL, &p))
1591
25.8k
    {
1592
25.8k
        return p -> Value;
1593
25.8k
    }
1594
7.17k
    return NULL;
1595
33.0k
}
1596
1597
1598
cmsFloat64Number CMSEXPORT cmsIT8GetPropertyDbl(cmsHANDLE hIT8, const char* cProp)
1599
97
{
1600
97
    const char *v = cmsIT8GetProperty(hIT8, cProp);
1601
1602
97
    if (v == NULL) return 0.0;
1603
1604
97
    return ParseFloatNumber(v);
1605
97
}
1606
1607
const char* CMSEXPORT cmsIT8GetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char *SubKey)
1608
0
{
1609
0
    cmsIT8* it8 = (cmsIT8*) hIT8;
1610
0
    KEYVALUE* p;
1611
1612
0
    if (IsAvailableOnList(GetTable(it8) -> HeaderList, Key, SubKey, &p)) {
1613
0
        return p -> Value;
1614
0
    }
1615
0
    return NULL;
1616
0
}
1617
1618
// ----------------------------------------------------------------- Datasets
1619
1620
// A safe atoi that returns 0 when NULL input is given
1621
static
1622
cmsInt32Number satoi(const char* b)
1623
33.0k
{
1624
33.0k
    int n;
1625
1626
33.0k
    if (b == NULL) return 0;
1627
1628
25.9k
    n = atoi(b);
1629
25.9k
    if (n > 0x7ffffff0L) return 0x7ffffff0L;
1630
25.8k
    if (n < -0x7ffffff0L) return -0x7ffffff0L;
1631
1632
25.5k
    return (cmsInt32Number)n;
1633
25.8k
}
1634
1635
1636
static
1637
cmsBool AllocateDataFormat(cmsIT8* it8)
1638
2.40k
{
1639
2.40k
    cmsUInt32Number size;
1640
1641
2.40k
    TABLE* t = GetTable(it8);
1642
1643
2.40k
    if (t->DataFormat) return TRUE;    // Already allocated
1644
1645
2.40k
    t->nSamples = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
1646
1647
2.40k
    if (t->nSamples <= 0 || t->nSamples > 0x7ffe) {
1648
1649
509
        SynError(it8, "Wrong NUMBER_OF_FIELDS");
1650
509
        return FALSE;
1651
509
    }
1652
1653
1.90k
    size = ((cmsUInt32Number)t->nSamples + 1) * sizeof(char*);
1654
1655
1.90k
    t->DataFormat = (char**)AllocChunk(it8, size);
1656
1.90k
    if (t->DataFormat == NULL) {
1657
1658
0
        SynError(it8, "Unable to allocate dataFormat array");
1659
0
        return FALSE;
1660
0
    }
1661
1662
1.90k
    return TRUE;
1663
1.90k
}
1664
1665
static
1666
const char *GetDataFormat(cmsIT8* it8, int n)
1667
202
{
1668
202
    TABLE* t = GetTable(it8);
1669
1670
202
    if (t->DataFormat)
1671
202
        return t->DataFormat[n];
1672
1673
0
    return NULL;
1674
202
}
1675
1676
static
1677
cmsBool SetDataFormat(cmsIT8* it8, int n, const char *label)
1678
14.4k
{
1679
14.4k
    TABLE* t = GetTable(it8);
1680
1681
14.4k
    if (!t->DataFormat) {
1682
1683
2.40k
        if (!AllocateDataFormat(it8))
1684
509
            return FALSE;
1685
2.40k
    }
1686
1687
13.9k
    if (n < 0 || n >= t -> nSamples) {
1688
181
        SynError(it8, "Invalid or more than NUMBER_OF_FIELDS fields.");
1689
181
        return FALSE;
1690
181
    }
1691
1692
13.7k
    if (t->DataFormat) {
1693
13.7k
        t->DataFormat[n] = AllocString(it8, label);
1694
13.7k
        if (t->DataFormat[n] == NULL) return FALSE;
1695
13.7k
    }
1696
1697
13.7k
    return TRUE;
1698
13.7k
}
1699
1700
1701
cmsBool CMSEXPORT cmsIT8SetDataFormat(cmsHANDLE h, int n, const char *Sample)
1702
1.11k
{
1703
1.11k
    cmsIT8* it8 = (cmsIT8*)h;
1704
1705
1.11k
    _cmsAssert(n >= 0);
1706
1.11k
    return SetDataFormat(it8, n, Sample);
1707
1.11k
}
1708
1709
// Convert to binary
1710
static
1711
const char* satob(const char* v)
1712
0
{
1713
0
    cmsUInt32Number x;
1714
0
    static char buf[33];
1715
0
    char *s = buf + 33;
1716
    
1717
0
    if (v == NULL) return "0";
1718
    
1719
0
    x = atoi(v);
1720
0
    *--s = 0;
1721
0
    if (!x) *--s = '0';
1722
0
    for (; x; x /= 2) *--s = '0' + x%2;
1723
    
1724
0
    return s;
1725
0
}
1726
1727
1728
static
1729
cmsBool AllocateDataSet(cmsIT8* it8)
1730
13.9k
{
1731
13.9k
    TABLE* t = GetTable(it8);
1732
1733
13.9k
    if (t -> Data) return TRUE;    // Already allocated
1734
1735
13.9k
    t-> nSamples   = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
1736
13.9k
    t-> nPatches   = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS"));
1737
1738
13.9k
    if (t -> nSamples < 0 || t->nSamples > 0x7ffe || t->nPatches < 0 || t->nPatches > 0x7ffe || 
1739
10.1k
        (t->nPatches * t->nSamples) > 200000)
1740
4.45k
    {
1741
4.45k
        SynError(it8, "AllocateDataSet: too much data");
1742
4.45k
        return FALSE;
1743
4.45k
    }
1744
9.53k
    else {
1745
        // Some dumb analyzers warns of possible overflow here, just take a look couple of lines above.
1746
9.53k
        t->Data = (char**)AllocChunk(it8, ((cmsUInt32Number)t->nSamples + 1) * ((cmsUInt32Number)t->nPatches + 1) * sizeof(char*));
1747
9.53k
        if (t->Data == NULL) {
1748
1749
0
            SynError(it8, "AllocateDataSet: Unable to allocate data array");
1750
0
            return FALSE;
1751
0
        }
1752
9.53k
    }
1753
1754
9.53k
    return TRUE;
1755
13.9k
}
1756
1757
static
1758
char* GetData(cmsIT8* it8, int nSet, int nField)
1759
13.0k
{
1760
13.0k
    TABLE* t = GetTable(it8);
1761
13.0k
    int nSamples    = t -> nSamples;
1762
13.0k
    int nPatches    = t -> nPatches;
1763
1764
13.0k
    if (nSet < 0 || nSet >= nPatches || nField < 0 || nField >= nSamples)
1765
93
        return NULL;
1766
1767
12.9k
    if (!t->Data) return NULL;
1768
12.9k
    return t->Data [nSet * nSamples + nField];
1769
12.9k
}
1770
1771
static
1772
cmsBool SetData(cmsIT8* it8, int nSet, int nField, const char *Val)
1773
279k
{
1774
279k
    char* ptr;
1775
1776
279k
    TABLE* t = GetTable(it8);
1777
    
1778
1779
279k
    if (!t->Data) {
1780
4.41k
        if (!AllocateDataSet(it8)) return FALSE;
1781
4.41k
    }
1782
1783
275k
    if (!t->Data) return FALSE;
1784
1785
275k
    if (nSet > t -> nPatches || nSet < 0) {
1786
1787
18
            return SynError(it8, "Patch %d out of range, there are %d patches", nSet, t -> nPatches);
1788
18
    }
1789
1790
275k
    if (nField > t ->nSamples || nField < 0) {
1791
950
            return SynError(it8, "Sample %d out of range, there are %d samples", nField, t ->nSamples);
1792
1793
950
    }
1794
1795
274k
    ptr = AllocString(it8, Val);
1796
274k
    if (ptr == NULL)
1797
0
        return FALSE;
1798
1799
274k
    t->Data [nSet * t -> nSamples + nField] = ptr;
1800
274k
    return TRUE;
1801
274k
}
1802
1803
1804
// --------------------------------------------------------------- File I/O
1805
1806
1807
// Writes a string to file
1808
static
1809
void WriteStr(SAVESTREAM* f, const char *str)
1810
12.7M
{
1811
12.7M
    cmsUInt32Number len;
1812
1813
12.7M
    if (str == NULL)
1814
574k
        str = " ";
1815
1816
    // Length to write
1817
12.7M
    len = (cmsUInt32Number) strlen(str);
1818
12.7M
    f ->Used += len;
1819
1820
1821
12.7M
    if (f ->stream) {   // Should I write it to a file?
1822
1823
12.7M
        if (fwrite(str, 1, len, f->stream) != len) {
1824
0
            cmsSignalError(0, cmsERROR_WRITE, "Write to file error in CGATS parser");
1825
0
            return;
1826
0
        }
1827
1828
12.7M
    }
1829
0
    else {  // Or to a memory block?
1830
1831
0
        if (f ->Base) {   // Am I just counting the bytes?
1832
1833
0
            if (f ->Used > f ->Max) {
1834
1835
0
                 cmsSignalError(0, cmsERROR_WRITE, "Write to memory overflows in CGATS parser");
1836
0
                 return;
1837
0
            }
1838
1839
0
            memmove(f ->Ptr, str, len);
1840
0
            f->Ptr += len;
1841
0
        }
1842
1843
0
    }
1844
12.7M
}
1845
1846
1847
// Write formatted
1848
1849
static
1850
void Writef(SAVESTREAM* f, const char* frm, ...)
1851
4.20k
{
1852
4.20k
    char Buffer[4096];
1853
4.20k
    va_list args;
1854
1855
4.20k
    va_start(args, frm);
1856
4.20k
    vsnprintf(Buffer, 4095, frm, args);
1857
4.20k
    Buffer[4095] = 0;
1858
4.20k
    WriteStr(f, Buffer);
1859
4.20k
    va_end(args);
1860
1861
4.20k
}
1862
1863
// Writes full header
1864
static
1865
void WriteHeader(cmsIT8* it8, SAVESTREAM* fp)
1866
1.26k
{
1867
1.26k
    KEYVALUE* p;
1868
1.26k
    TABLE* t = GetTable(it8);
1869
1870
    // Writes the type
1871
1.26k
    WriteStr(fp, t->SheetType);
1872
1.26k
    WriteStr(fp, "\n");
1873
1874
5.46k
    for (p = t->HeaderList; (p != NULL); p = p->Next)
1875
4.20k
    {
1876
4.20k
        if (*p ->Keyword == '#') {
1877
1878
0
            char* Pt;
1879
1880
0
            WriteStr(fp, "#\n# ");
1881
0
            for (Pt = p ->Value; *Pt; Pt++) {
1882
1883
1884
0
                Writef(fp, "%c", *Pt);
1885
1886
0
                if (*Pt == '\n') {
1887
0
                    WriteStr(fp, "# ");
1888
0
                }
1889
0
            }
1890
1891
0
            WriteStr(fp, "\n#\n");
1892
0
            continue;
1893
0
        }
1894
1895
1896
4.20k
        if (!IsAvailableOnList(it8-> ValidKeywords, p->Keyword, NULL, NULL)) {
1897
1898
#ifdef CMS_STRICT_CGATS
1899
            WriteStr(fp, "KEYWORD\t\"");
1900
            WriteStr(fp, p->Keyword);
1901
            WriteStr(fp, "\"\n");
1902
#endif
1903
1904
0
            AddAvailableProperty(it8, p->Keyword, WRITE_UNCOOKED);
1905
0
        }
1906
1907
4.20k
        WriteStr(fp, p->Keyword);
1908
4.20k
        if (p->Value) {
1909
1910
4.20k
            switch (p ->WriteAs) {
1911
1912
3.46k
            case WRITE_UNCOOKED:
1913
3.46k
                    Writef(fp, "\t%s", p ->Value);
1914
3.46k
                    break;
1915
1916
572
            case WRITE_STRINGIFY:
1917
572
                    Writef(fp, "\t\"%s\"", p->Value );
1918
572
                    break;
1919
1920
170
            case WRITE_HEXADECIMAL:
1921
170
                    Writef(fp, "\t0x%X", satoi(p ->Value));
1922
170
                    break;
1923
1924
0
            case WRITE_BINARY:
1925
0
                    Writef(fp, "\t0b%s", satob(p ->Value));
1926
0
                    break;
1927
1928
0
            case WRITE_PAIR:
1929
0
                    Writef(fp, "\t\"%s,%s\"", p->Subkey, p->Value);
1930
0
                    break;
1931
1932
0
            default: SynError(it8, "Unknown write mode %d", p ->WriteAs);
1933
0
                     return;
1934
4.20k
            }
1935
4.20k
        }
1936
1937
4.20k
        WriteStr (fp, "\n");
1938
4.20k
    }
1939
1940
1.26k
}
1941
1942
1943
// Writes the data format
1944
static
1945
void WriteDataFormat(SAVESTREAM* fp, cmsIT8* it8)
1946
1.26k
{
1947
1.26k
    int i, nSamples;
1948
1.26k
    TABLE* t = GetTable(it8);
1949
1950
1.26k
    if (!t -> DataFormat) return;
1951
1952
1.26k
       WriteStr(fp, "BEGIN_DATA_FORMAT\n");
1953
1.26k
       WriteStr(fp, " ");
1954
1.26k
       nSamples = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
1955
1956
1.26k
       if (nSamples <= t->nSamples) {
1957
1958
585k
           for (i = 0; i < nSamples; i++) {
1959
1960
583k
               WriteStr(fp, t->DataFormat[i]);
1961
583k
               WriteStr(fp, ((i == (nSamples - 1)) ? "\n" : "\t"));
1962
583k
           }
1963
1.26k
       }
1964
1965
1.26k
       WriteStr (fp, "END_DATA_FORMAT\n");
1966
1.26k
}
1967
1968
1969
// Writes data array
1970
static
1971
void WriteData(SAVESTREAM* fp, cmsIT8* it8)
1972
1.26k
{
1973
1.26k
       int  i, j, nPatches;
1974
1.26k
       TABLE* t = GetTable(it8);
1975
1976
1.26k
       if (!t->Data) return;
1977
1978
1.26k
       WriteStr (fp, "BEGIN_DATA\n");
1979
1980
1.26k
       nPatches = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS"));
1981
1982
1.26k
       if (nPatches <= t->nPatches) {
1983
1984
9.52k
           for (i = 0; i < nPatches; i++) {
1985
1986
8.25k
               WriteStr(fp, " ");
1987
1988
5.77M
               for (j = 0; j < t->nSamples; j++) {
1989
1990
5.76M
                   char* ptr = t->Data[i * t->nSamples + j];
1991
1992
5.76M
                   if (ptr == NULL) WriteStr(fp, "\"\"");
1993
16.5k
                   else {
1994
                       // If value contains whitespace, enclose within quote
1995
1996
16.5k
                       if (strchr(ptr, ' ') != NULL) {
1997
1998
220
                           WriteStr(fp, "\"");
1999
220
                           WriteStr(fp, ptr);
2000
220
                           WriteStr(fp, "\"");
2001
220
                       }
2002
16.3k
                       else
2003
16.3k
                           WriteStr(fp, ptr);
2004
16.5k
                   }
2005
2006
5.76M
                   WriteStr(fp, ((j == (t->nSamples - 1)) ? "\n" : "\t"));
2007
5.76M
               }
2008
8.25k
           }
2009
1.26k
       }
2010
1.26k
       WriteStr (fp, "END_DATA\n");
2011
1.26k
}
2012
2013
2014
2015
// Saves whole file
2016
cmsBool CMSEXPORT cmsIT8SaveToFile(cmsHANDLE hIT8, const char* cFileName)
2017
915
{
2018
915
    SAVESTREAM sd;
2019
915
    cmsUInt32Number i;
2020
915
    cmsIT8* it8 = (cmsIT8*) hIT8;
2021
2022
915
    memset(&sd, 0, sizeof(sd));
2023
2024
915
    sd.stream = fopen(cFileName, "wt");
2025
915
    if (!sd.stream) return FALSE;
2026
2027
2.18k
    for (i=0; i < it8 ->TablesCount; i++) {
2028
2029
1.88k
        TABLE* t;
2030
2031
1.88k
        if (cmsIT8SetTable(hIT8, i) < 0) goto Error;
2032
        
2033
        /**
2034
        * Check for wrong data
2035
        */
2036
1.88k
        t = GetTable(it8);
2037
1.88k
        if (t->Data == NULL) goto Error;
2038
1.39k
        if (t->DataFormat == NULL) goto Error;
2039
2040
1.26k
        WriteHeader(it8, &sd);
2041
1.26k
        WriteDataFormat(&sd, it8);
2042
1.26k
        WriteData(&sd, it8);
2043
1.26k
    }
2044
2045
301
    if (fclose(sd.stream) != 0) return FALSE;
2046
301
    return TRUE;
2047
2048
614
Error:
2049
614
    fclose(sd.stream);
2050
614
    return FALSE;
2051
2052
301
}
2053
2054
2055
// Saves to memory
2056
cmsBool CMSEXPORT cmsIT8SaveToMem(cmsHANDLE hIT8, void *MemPtr, cmsUInt32Number* BytesNeeded)
2057
0
{
2058
0
    SAVESTREAM sd;
2059
0
    cmsUInt32Number i;
2060
0
    cmsIT8* it8 = (cmsIT8*) hIT8;
2061
2062
0
    memset(&sd, 0, sizeof(sd));
2063
2064
0
    sd.stream = NULL;
2065
0
    sd.Base   = (cmsUInt8Number*) MemPtr;
2066
0
    sd.Ptr    = sd.Base;
2067
2068
0
    sd.Used = 0;
2069
2070
0
    if (sd.Base && (*BytesNeeded > 0)) {
2071
2072
0
        sd.Max = (*BytesNeeded) - 1;     // Write to memory?
2073
0
    }
2074
0
    else
2075
0
        sd.Max  = 0;                // Just counting the needed bytes
2076
2077
0
    for (i=0; i < it8 ->TablesCount; i++) {
2078
2079
0
        cmsIT8SetTable(hIT8, i);
2080
0
        WriteHeader(it8, &sd);
2081
0
        WriteDataFormat(&sd, it8);
2082
0
        WriteData(&sd, it8);
2083
0
    }
2084
2085
0
    sd.Used++;  // The \0 at the very end
2086
2087
0
    if (sd.Base)
2088
0
        *sd.Ptr = 0;
2089
2090
0
    *BytesNeeded = sd.Used;
2091
2092
0
    return TRUE;
2093
0
}
2094
2095
2096
// -------------------------------------------------------------- Higher level parsing
2097
2098
static
2099
cmsBool DataFormatSection(cmsIT8* it8)
2100
2.90k
{
2101
2.90k
    int iField = 0;
2102
2.90k
    TABLE* t = GetTable(it8);
2103
2104
2.90k
    InSymbol(it8);   // Eats "BEGIN_DATA_FORMAT"
2105
2.90k
    CheckEOLN(it8);
2106
2107
16.1k
    while (it8->sy != SEND_DATA_FORMAT &&
2108
13.5k
        it8->sy != SEOLN &&
2109
13.5k
        it8->sy != SEOF &&
2110
13.4k
        it8->sy != SSYNERROR)  {
2111
2112
13.3k
            if (it8->sy != SIDENT) {
2113
2114
5
                return SynError(it8, "Sample type expected");
2115
5
            }
2116
2117
13.3k
            if (!SetDataFormat(it8, iField, StringPtr(it8->id))) return FALSE;
2118
13.2k
            iField++;
2119
2120
13.2k
            InSymbol(it8);
2121
13.2k
            SkipEOLN(it8);
2122
13.2k
       }
2123
2124
2.75k
       SkipEOLN(it8);
2125
2.75k
       Skip(it8, SEND_DATA_FORMAT);
2126
2.75k
       SkipEOLN(it8);
2127
2128
2.75k
       if (iField != t ->nSamples) {
2129
136
           SynError(it8, "Count mismatch. NUMBER_OF_FIELDS was %d, found %d\n", t ->nSamples, iField);
2130
2131
2132
136
       }
2133
2134
2.75k
       return TRUE;
2135
2.90k
}
2136
2137
2138
2139
static
2140
cmsBool DataSection (cmsIT8* it8)
2141
9.58k
{
2142
9.58k
    int  iField = 0;
2143
9.58k
    int  iSet   = 0;
2144
9.58k
    char Buffer[256];
2145
9.58k
    TABLE* t = GetTable(it8);
2146
2147
9.58k
    InSymbol(it8);   // Eats "BEGIN_DATA"
2148
9.58k
    CheckEOLN(it8);
2149
2150
9.58k
    if (!t->Data) {
2151
9.58k
        if (!AllocateDataSet(it8)) return FALSE;
2152
9.58k
    }
2153
2154
277k
    while (it8->sy != SEND_DATA && it8->sy != SEOF && it8->sy != SSYNERROR)
2155
268k
    {
2156
268k
        if (iField >= t -> nSamples) {
2157
10.6k
            iField = 0;
2158
10.6k
            iSet++;
2159
2160
10.6k
        }
2161
2162
268k
        if (it8->sy != SEND_DATA && it8->sy != SEOF && it8->sy != SSYNERROR) {
2163
2164
268k
            switch (it8->sy)
2165
268k
            {
2166
2167
            // To keep very long data
2168
130k
            case SIDENT:  
2169
130k
                if (!SetData(it8, iSet, iField, StringPtr(it8->id)))
2170
12
                    return FALSE;
2171
130k
                break;
2172
2173
132k
            case SSTRING:
2174
132k
                if (!SetData(it8, iSet, iField, StringPtr(it8->str)))
2175
5
                    return FALSE;
2176
132k
                break;
2177
2178
132k
            default:
2179
2180
5.48k
            if (!GetVal(it8, Buffer, 255, "Sample data expected"))
2181
6
                return FALSE;
2182
2183
5.47k
            if (!SetData(it8, iSet, iField, Buffer))
2184
1
                return FALSE;
2185
268k
            }
2186
2187
268k
            iField++;
2188
2189
268k
            InSymbol(it8);
2190
268k
            SkipEOLN(it8);
2191
268k
        }
2192
268k
    }
2193
2194
9.34k
    SkipEOLN(it8);
2195
9.34k
    Skip(it8, SEND_DATA);
2196
9.34k
    SkipEOLN(it8);
2197
2198
    // Check for data completion.
2199
2200
9.34k
    if ((iSet+1) != t -> nPatches)
2201
387
        return SynError(it8, "Count mismatch. NUMBER_OF_SETS was %d, found %d\n", t ->nPatches, iSet+1);
2202
2203
8.95k
    return TRUE;
2204
9.34k
}
2205
2206
2207
2208
2209
static
2210
cmsBool HeaderSection(cmsIT8* it8)
2211
12.5k
{
2212
12.5k
    char VarName[MAXID];
2213
12.5k
    char Buffer[MAXSTR];
2214
12.5k
    KEYVALUE* Key;
2215
2216
42.2k
        while (it8->sy != SEOF &&
2217
41.9k
               it8->sy != SSYNERROR &&
2218
41.8k
               it8->sy != SBEGIN_DATA_FORMAT &&
2219
39.7k
               it8->sy != SBEGIN_DATA) {
2220
2221
2222
30.3k
        switch (it8 -> sy) {
2223
2224
609
        case SKEYWORD:
2225
609
                InSymbol(it8);
2226
609
                if (!GetVal(it8, Buffer, MAXSTR-1, "Keyword expected")) return FALSE;
2227
600
                if (!AddAvailableProperty(it8, Buffer, WRITE_UNCOOKED)) return FALSE;
2228
598
                InSymbol(it8);
2229
598
                break;
2230
2231
2232
0
        case SDATA_FORMAT_ID:
2233
0
                InSymbol(it8);
2234
0
                if (!GetVal(it8, Buffer, MAXSTR-1, "Keyword expected")) return FALSE;
2235
0
                if (!AddAvailableSampleID(it8, Buffer)) return FALSE;
2236
0
                InSymbol(it8);
2237
0
                break;
2238
2239
2240
29.3k
        case SIDENT:
2241
29.3k
            strncpy(VarName, StringPtr(it8->id), MAXID - 1);
2242
29.3k
            VarName[MAXID - 1] = 0;
2243
2244
29.3k
            if (!IsAvailableOnList(it8->ValidKeywords, VarName, NULL, &Key)) {
2245
2246
#ifdef CMS_STRICT_CGATS
2247
                return SynError(it8, "Undefined keyword '%s'", VarName);
2248
#else
2249
4.01k
                Key = AddAvailableProperty(it8, VarName, WRITE_UNCOOKED);
2250
4.01k
                if (Key == NULL) return FALSE;
2251
4.01k
#endif
2252
4.01k
            }
2253
2254
29.3k
            InSymbol(it8);
2255
29.3k
            if (!GetVal(it8, Buffer, MAXSTR - 1, "Property data expected")) return FALSE;
2256
2257
29.0k
            if (Key->WriteAs != WRITE_PAIR) {
2258
29.0k
                if (AddToList(it8, &GetTable(it8)->HeaderList, VarName, NULL, Buffer,
2259
29.0k
                    (it8->sy == SSTRING) ? WRITE_STRINGIFY : WRITE_UNCOOKED) == NULL) return FALSE;
2260
29.0k
            }
2261
0
            else {
2262
0
                const char *Subkey;
2263
0
                char *Nextkey;
2264
0
                if (it8->sy != SSTRING)
2265
0
                    return SynError(it8, "Invalid value '%s' for property '%s'.", Buffer, VarName);
2266
2267
                // chop the string as a list of "subkey, value" pairs, using ';' as a separator
2268
0
                for (Subkey = Buffer; Subkey != NULL; Subkey = Nextkey)
2269
0
                {
2270
0
                    char *Value, *temp;
2271
2272
                    //  identify token pair boundary
2273
0
                    Nextkey = (char*)strchr(Subkey, ';');
2274
0
                    if (Nextkey)
2275
0
                        *Nextkey++ = '\0';
2276
2277
                    // for each pair, split the subkey and the value
2278
0
                    Value = (char*)strrchr(Subkey, ',');
2279
0
                    if (Value == NULL)
2280
0
                        return SynError(it8, "Invalid value for property '%s'.", VarName);
2281
2282
                    // gobble the spaces before the coma, and the coma itself
2283
0
                    temp = Value++;
2284
0
                    do *temp-- = '\0'; while (temp >= Subkey && *temp == ' ');
2285
2286
                    // gobble any space at the right
2287
0
                    temp = Value + strlen(Value) - 1;
2288
0
                    while (*temp == ' ') *temp-- = '\0';
2289
2290
                    // trim the strings from the left
2291
0
                    Subkey += strspn(Subkey, " ");
2292
0
                    Value += strspn(Value, " ");
2293
2294
0
                    if (Subkey[0] == 0 || Value[0] == 0)
2295
0
                        return SynError(it8, "Invalid value for property '%s'.", VarName);
2296
0
                    AddToList(it8, &GetTable(it8)->HeaderList, VarName, Subkey, Value, WRITE_PAIR);
2297
0
                }
2298
0
            }
2299
2300
29.0k
            InSymbol(it8);
2301
29.0k
            break;
2302
2303
2304
0
        case SEOLN: break;
2305
2306
416
        default:
2307
416
                return SynError(it8, "expected keyword or identifier");
2308
30.3k
        }
2309
2310
29.6k
    SkipEOLN(it8);
2311
29.6k
    }
2312
2313
11.8k
    return TRUE;
2314
2315
12.5k
}
2316
2317
2318
static
2319
void ReadType(cmsIT8* it8, char* SheetTypePtr)
2320
1.44k
{
2321
1.44k
    cmsInt32Number cnt = 0;
2322
2323
    // First line is a very special case.
2324
2325
3.34k
    while (isseparator(it8->ch))
2326
1.89k
            NextCh(it8);
2327
2328
4.90k
    while (it8->ch != '\r' && it8 ->ch != '\n' && it8->ch != '\t' && it8 -> ch != 0) {
2329
2330
3.45k
        if (cnt++ < MAXSTR) 
2331
3.45k
            *SheetTypePtr++= (char) it8 ->ch;
2332
3.45k
        NextCh(it8);
2333
3.45k
    }
2334
2335
1.44k
    *SheetTypePtr = 0;
2336
1.44k
}
2337
2338
2339
static
2340
cmsBool ParseIT8(cmsIT8* it8, cmsBool nosheet)
2341
2.67k
{
2342
2.67k
    char* SheetTypePtr = it8 ->Tab[0].SheetType;
2343
2344
2.67k
    if (nosheet == 0) {
2345
1.44k
        ReadType(it8, SheetTypePtr);
2346
1.44k
    }
2347
2348
2.67k
    InSymbol(it8);
2349
2350
2.67k
    SkipEOLN(it8);
2351
2352
27.0k
    while (it8-> sy != SEOF &&
2353
26.3k
           it8-> sy != SSYNERROR) {
2354
2355
25.9k
            switch (it8 -> sy) {
2356
2357
2.90k
            case SBEGIN_DATA_FORMAT:
2358
2.90k
                    if (!DataFormatSection(it8)) return FALSE;
2359
2.75k
                    break;
2360
2361
9.58k
            case SBEGIN_DATA:
2362
2363
9.58k
                    if (!DataSection(it8)) return FALSE;
2364
2365
8.95k
                    if (it8 -> sy != SEOF && it8->sy != SSYNERROR) {
2366
2367
8.65k
                            if (!AllocTable(it8)) return FALSE;                        
2368
2369
8.65k
                            it8 ->nTable = it8 ->TablesCount - 1;
2370
2371
                            // Read sheet type if present. We only support identifier and string.
2372
                            // <ident> <eoln> is a type string
2373
                            // anything else, is not a type string
2374
8.65k
                            if (nosheet == 0) {
2375
2376
5.72k
                                if (it8 ->sy == SIDENT) {
2377
2378
                                    // May be a type sheet or may be a prop value statement. We cannot use insymbol in
2379
                                    // this special case...
2380
10.0k
                                     while (isseparator(it8->ch))
2381
4.86k
                                         NextCh(it8);
2382
2383
                                     // If a newline is found, then this is a type string
2384
5.16k
                                    if (it8 ->ch == '\n' || it8->ch == '\r') {
2385
2386
853
                                         cmsIT8SetSheetType(it8, StringPtr(it8 ->id));
2387
853
                                         InSymbol(it8);
2388
853
                                    }
2389
4.31k
                                    else
2390
4.31k
                                    {
2391
                                        // It is not. Just continue
2392
4.31k
                                        cmsIT8SetSheetType(it8, "");
2393
4.31k
                                    }
2394
5.16k
                                }
2395
558
                                else
2396
                                    // Validate quoted strings
2397
558
                                    if (it8 ->sy == SSTRING) {
2398
358
                                        cmsIT8SetSheetType(it8, StringPtr(it8 ->str));
2399
358
                                        InSymbol(it8);
2400
358
                                    }
2401
5.72k
                           }
2402
2403
8.65k
                    }
2404
8.95k
                    break;
2405
2406
8.95k
            case SEOLN:
2407
870
                    SkipEOLN(it8);
2408
870
                    break;
2409
2410
12.5k
            default:
2411
12.5k
                    if (!HeaderSection(it8)) return FALSE;
2412
25.9k
           }
2413
2414
25.9k
    }
2415
2416
1.15k
    return (it8 -> sy != SSYNERROR);
2417
2.67k
}
2418
2419
2420
2421
// Init useful pointers
2422
2423
static
2424
void CookPointers(cmsIT8* it8)
2425
734
{
2426
734
    int idField, i;
2427
734
    char* Fld;
2428
734
    cmsUInt32Number j;
2429
734
    cmsUInt32Number nOldTable = it8->nTable;
2430
2431
5.23k
    for (j = 0; j < it8->TablesCount; j++) {
2432
2433
4.53k
        TABLE* t = it8->Tab + j;
2434
2435
4.53k
        t->SampleID = 0;
2436
4.53k
        it8->nTable = j;
2437
2438
15.2k
        for (idField = 0; idField < t->nSamples; idField++)
2439
10.7k
        {
2440
10.7k
            if (t->DataFormat == NULL) {
2441
29
                SynError(it8, "Undefined DATA_FORMAT");
2442
29
                return;
2443
29
            }
2444
2445
10.7k
            Fld = t->DataFormat[idField];
2446
10.7k
            if (!Fld) continue;
2447
2448
2449
10.7k
            if (cmsstrcasecmp(Fld, "SAMPLE_ID") == 0) {
2450
2451
292
                t->SampleID = idField;
2452
292
            }
2453
2454
            // "LABEL" is an extension. It keeps references to forward tables
2455
2456
10.7k
            if ((cmsstrcasecmp(Fld, "LABEL") == 0) || Fld[0] == '$') {
2457
2458
                // Search for table references...
2459
14.9k
                for (i = 0; i < t->nPatches; i++) {
2460
2461
12.7k
                    char* Label = GetData(it8, i, idField);
2462
2463
12.7k
                    if (Label) {
2464
2465
11.4k
                        cmsUInt32Number k;
2466
2467
                        // This is the label, search for a table containing
2468
                        // this property
2469
2470
87.3k
                        for (k = 0; k < it8->TablesCount; k++) {
2471
2472
75.9k
                            TABLE* Table = it8->Tab + k;
2473
75.9k
                            KEYVALUE* p;
2474
2475
75.9k
                            if (IsAvailableOnList(Table->HeaderList, Label, NULL, &p)) {
2476
2477
                                // Available, keep type and table
2478
360
                                char Buffer[256];
2479
2480
360
                                char* Type = p->Value;
2481
360
                                int  nTable = (int)k;
2482
2483
360
                                snprintf(Buffer, 255, "%s %d %s", Label, nTable, Type);
2484
2485
360
                                SetData(it8, i, idField, Buffer);
2486
360
                            }
2487
75.9k
                        }
2488
11.4k
                    }
2489
12.7k
                }
2490
2.19k
            }
2491
10.7k
        }
2492
4.53k
    }
2493
2494
705
    it8->nTable = nOldTable;
2495
705
}
2496
2497
// Try to infere if the file is a CGATS/IT8 file at all. Read first line
2498
// that should be something like some printable characters plus a \n
2499
// returns 0 if this is not like a CGATS, or an integer otherwise. This integer is the number of words in first line?
2500
static
2501
int IsMyBlock(const cmsUInt8Number* Buffer, cmsUInt32Number n)
2502
2.83k
{
2503
2.83k
    int words = 1, space = 0, quot = 0;
2504
2.83k
    cmsUInt32Number i;
2505
2506
2.83k
    if (n < 10) return 0;   // Too small
2507
2508
2.72k
    if (n > 132)
2509
503
        n = 132;
2510
2511
33.8k
    for (i = 1; i < n; i++) {
2512
2513
33.8k
        switch(Buffer[i])
2514
33.8k
        {
2515
1.68k
        case '\n':
2516
2.68k
        case '\r':
2517
2.68k
            return ((quot == 1) || (words > 2)) ? 0 : words;
2518
1.50k
        case '\t':
2519
2.82k
        case ' ':
2520
2.82k
            if(!quot && !space)
2521
1.41k
                space = 1;
2522
2.82k
            break;
2523
253
        case '\"':
2524
253
            quot = !quot;
2525
253
            break;
2526
28.0k
        default:
2527
28.0k
            if (Buffer[i] < 32) return 0;
2528
28.0k
            if (Buffer[i] > 127) return 0;
2529
28.0k
            words += space;
2530
28.0k
            space = 0;
2531
28.0k
            break;
2532
33.8k
        }
2533
33.8k
    }
2534
2535
22
    return 0;
2536
2.72k
}
2537
2538
2539
static
2540
cmsBool IsMyFile(const char* FileName)
2541
278
{
2542
278
   FILE *fp;
2543
278
   cmsUInt32Number Size;
2544
278
   cmsUInt8Number Ptr[133];
2545
2546
278
   fp = fopen(FileName, "rt");
2547
278
   if (!fp) {
2548
0
       cmsSignalError(0, cmsERROR_FILE, "File '%s' not found", FileName);
2549
0
       return FALSE;
2550
0
   }
2551
2552
278
   Size = (cmsUInt32Number) fread(Ptr, 1, 132, fp);
2553
2554
278
   if (fclose(fp) != 0)
2555
0
       return FALSE;
2556
2557
278
   Ptr[Size] = '\0';
2558
2559
278
   return IsMyBlock(Ptr, Size);
2560
278
}
2561
2562
// ---------------------------------------------------------- Exported routines
2563
2564
2565
cmsHANDLE  CMSEXPORT cmsIT8LoadFromMem(cmsContext ContextID, const void *Ptr, cmsUInt32Number len)
2566
2.55k
{
2567
2.55k
    cmsHANDLE hIT8;
2568
2.55k
    cmsIT8*  it8;
2569
2.55k
    int type;
2570
2571
2.55k
    _cmsAssert(Ptr != NULL);
2572
2.55k
    _cmsAssert(len != 0);
2573
2574
2.55k
    type = IsMyBlock((const cmsUInt8Number*)Ptr, len);
2575
2.55k
    if (type == 0) return NULL;
2576
2577
2.50k
    hIT8 = cmsIT8Alloc(ContextID);
2578
2.50k
    if (!hIT8) return NULL;
2579
2580
2.50k
    it8 = (cmsIT8*) hIT8;
2581
2.50k
    it8 ->MemoryBlock = (char*) _cmsMalloc(ContextID, len + 1);
2582
2.50k
    if (it8->MemoryBlock == NULL)
2583
0
    {
2584
0
        cmsIT8Free(hIT8);
2585
0
        return NULL;
2586
0
    }
2587
2588
2.50k
    strncpy(it8 ->MemoryBlock, (const char*) Ptr, len);
2589
2.50k
    it8 ->MemoryBlock[len] = 0;
2590
2591
2.50k
    strncpy(it8->FileStack[0]->FileName, "", cmsMAX_PATH-1);
2592
2.50k
    it8-> Source = it8 -> MemoryBlock;
2593
2594
2.50k
    if (!ParseIT8(it8, type-1)) {
2595
2596
1.87k
        cmsIT8Free(hIT8);
2597
1.87k
        return NULL;
2598
1.87k
    }
2599
2600
637
    CookPointers(it8);
2601
637
    it8 ->nTable = 0;
2602
2603
637
    _cmsFree(ContextID, it8->MemoryBlock);
2604
637
    it8 -> MemoryBlock = NULL;
2605
2606
637
    return hIT8;
2607
2608
2609
2.50k
}
2610
2611
2612
cmsHANDLE  CMSEXPORT cmsIT8LoadFromFile(cmsContext ContextID, const char* cFileName)
2613
278
{
2614
2615
278
     cmsHANDLE hIT8;
2616
278
     cmsIT8*  it8;
2617
278
     int type;
2618
2619
278
     _cmsAssert(cFileName != NULL);
2620
2621
278
     type = IsMyFile(cFileName);
2622
278
     if (type == 0) return NULL;
2623
2624
170
     hIT8 = cmsIT8Alloc(ContextID);
2625
170
     it8 = (cmsIT8*) hIT8;
2626
170
     if (!hIT8) return NULL;
2627
2628
2629
170
     it8 ->FileStack[0]->Stream = fopen(cFileName, "rt");
2630
2631
170
     if (!it8 ->FileStack[0]->Stream) {
2632
0
         cmsIT8Free(hIT8);
2633
0
         return NULL;
2634
0
     }
2635
2636
2637
170
    strncpy(it8->FileStack[0]->FileName, cFileName, cmsMAX_PATH-1);
2638
170
    it8->FileStack[0]->FileName[cmsMAX_PATH-1] = 0;
2639
2640
170
    if (!ParseIT8(it8, type-1)) {
2641
2642
73
            fclose(it8 ->FileStack[0]->Stream);
2643
73
            cmsIT8Free(hIT8);
2644
73
            return NULL;
2645
73
    }
2646
2647
97
    CookPointers(it8);
2648
97
    it8 ->nTable = 0;
2649
2650
97
    if (fclose(it8 ->FileStack[0]->Stream)!= 0) {
2651
0
            cmsIT8Free(hIT8);
2652
0
            return NULL;
2653
0
    }
2654
2655
97
    return hIT8;
2656
2657
97
}
2658
2659
int CMSEXPORT cmsIT8EnumDataFormat(cmsHANDLE hIT8, char ***SampleNames)
2660
0
{
2661
0
    cmsIT8* it8 = (cmsIT8*) hIT8;
2662
0
    TABLE* t;
2663
2664
0
    _cmsAssert(hIT8 != NULL);
2665
2666
0
    t = GetTable(it8);
2667
2668
0
    if (SampleNames)
2669
0
        *SampleNames = t -> DataFormat;
2670
0
    return t -> nSamples;
2671
0
}
2672
2673
2674
cmsUInt32Number CMSEXPORT cmsIT8EnumProperties(cmsHANDLE hIT8, char ***PropertyNames)
2675
0
{
2676
0
    cmsIT8* it8 = (cmsIT8*) hIT8;
2677
0
    KEYVALUE* p;
2678
0
    cmsUInt32Number n;
2679
0
    char **Props;
2680
0
    TABLE* t;
2681
2682
0
    _cmsAssert(hIT8 != NULL);
2683
2684
0
    t = GetTable(it8);
2685
2686
    // Pass#1 - count properties
2687
2688
0
    n = 0;
2689
0
    for (p = t -> HeaderList;  p != NULL; p = p->Next) {
2690
0
        n++;
2691
0
    }
2692
2693
2694
0
    Props = (char**)AllocChunk(it8, sizeof(char*) * n);
2695
0
    if (Props != NULL) {
2696
2697
        // Pass#2 - Fill pointers
2698
0
        n = 0;
2699
0
        for (p = t->HeaderList; p != NULL; p = p->Next) {
2700
0
            Props[n++] = p->Keyword;
2701
0
        }
2702
2703
0
    }
2704
0
    *PropertyNames = Props;
2705
2706
0
    return n;
2707
0
}
2708
2709
cmsUInt32Number CMSEXPORT cmsIT8EnumPropertyMulti(cmsHANDLE hIT8, const char* cProp, const char ***SubpropertyNames)
2710
0
{
2711
0
    cmsIT8* it8 = (cmsIT8*) hIT8;
2712
0
    KEYVALUE *p, *tmp;
2713
0
    cmsUInt32Number n;
2714
0
    const char **Props;
2715
0
    TABLE* t;
2716
2717
0
    _cmsAssert(hIT8 != NULL);
2718
2719
2720
0
    t = GetTable(it8);
2721
2722
0
    if(!IsAvailableOnList(t->HeaderList, cProp, NULL, &p)) {
2723
0
        *SubpropertyNames = 0;
2724
0
        return 0;
2725
0
    }
2726
2727
    // Pass#1 - count properties
2728
2729
0
    n = 0;
2730
0
    for (tmp = p;  tmp != NULL; tmp = tmp->NextSubkey) {
2731
0
        if(tmp->Subkey != NULL)
2732
0
            n++;
2733
0
    }
2734
2735
2736
0
    Props = (const char **) AllocChunk(it8, sizeof(char *) * n);
2737
0
    if (Props != NULL) {
2738
2739
        // Pass#2 - Fill pointers
2740
0
        n = 0;
2741
0
        for (tmp = p; tmp != NULL; tmp = tmp->NextSubkey) {
2742
0
            if (tmp->Subkey != NULL)
2743
0
                Props[n++] = p->Subkey;
2744
0
        }
2745
0
    }
2746
2747
0
    *SubpropertyNames = Props;
2748
0
    return n;
2749
0
}
2750
2751
static
2752
int LocatePatch(cmsIT8* it8, const char* cPatch)
2753
40
{
2754
40
    int i;
2755
40
    const char *data;
2756
40
    TABLE* t = GetTable(it8);
2757
2758
160
    for (i=0; i < t-> nPatches; i++) {
2759
2760
160
        data = GetData(it8, i, t->SampleID);
2761
2762
160
        if (data != NULL) {
2763
2764
160
                if (cmsstrcasecmp(data, cPatch) == 0)
2765
40
                        return i;
2766
160
                }
2767
160
        }
2768
2769
        // SynError(it8, "Couldn't find patch '%s'\n", cPatch);
2770
0
        return -1;
2771
40
}
2772
2773
2774
static
2775
int LocateEmptyPatch(cmsIT8* it8)
2776
0
{
2777
0
    int i;
2778
0
    const char *data;
2779
0
    TABLE* t = GetTable(it8);
2780
2781
0
    for (i=0; i < t-> nPatches; i++) {
2782
2783
0
        data = GetData(it8, i, t->SampleID);
2784
2785
0
        if (data == NULL)
2786
0
            return i;
2787
2788
0
    }
2789
2790
0
    return -1;
2791
0
}
2792
2793
static
2794
int LocateSample(cmsIT8* it8, const char* cSample)
2795
97
{
2796
97
    int i;
2797
97
    const char *fld;
2798
97
    TABLE* t = GetTable(it8);
2799
2800
259
    for (i=0; i < t->nSamples; i++) {
2801
2802
202
        fld = GetDataFormat(it8, i);
2803
202
        if (fld != NULL) {
2804
202
            if (cmsstrcasecmp(fld, cSample) == 0)
2805
40
                return i;
2806
202
        }
2807
202
    }
2808
2809
57
    return -1;
2810
2811
97
}
2812
2813
2814
int CMSEXPORT cmsIT8FindDataFormat(cmsHANDLE hIT8, const char* cSample)
2815
0
{
2816
0
    cmsIT8* it8 = (cmsIT8*) hIT8;
2817
2818
0
    _cmsAssert(hIT8 != NULL);
2819
2820
0
    return LocateSample(it8, cSample);
2821
0
}
2822
2823
2824
2825
const char* CMSEXPORT cmsIT8GetDataRowCol(cmsHANDLE hIT8, int row, int col)
2826
97
{
2827
97
    cmsIT8* it8 = (cmsIT8*) hIT8;
2828
2829
97
    _cmsAssert(hIT8 != NULL);
2830
2831
97
    return GetData(it8, row, col);
2832
97
}
2833
2834
2835
cmsFloat64Number CMSEXPORT cmsIT8GetDataRowColDbl(cmsHANDLE hIT8, int row, int col)
2836
97
{
2837
97
    const char* Buffer;
2838
2839
97
    Buffer = cmsIT8GetDataRowCol(hIT8, row, col);
2840
2841
97
    if (Buffer == NULL) return 0.0;
2842
2843
4
    return ParseFloatNumber(Buffer);
2844
97
}
2845
2846
2847
cmsBool CMSEXPORT cmsIT8SetDataRowCol(cmsHANDLE hIT8, int row, int col, const char* Val)
2848
2.78k
{
2849
2.78k
    cmsIT8* it8 = (cmsIT8*) hIT8;
2850
2851
2.78k
    _cmsAssert(hIT8 != NULL);
2852
2853
2.78k
    return SetData(it8, row, col, Val);
2854
2.78k
}
2855
2856
2857
cmsBool CMSEXPORT cmsIT8SetDataRowColDbl(cmsHANDLE hIT8, int row, int col, cmsFloat64Number Val)
2858
8.34k
{
2859
8.34k
    cmsIT8* it8 = (cmsIT8*) hIT8;
2860
8.34k
    char Buff[256];
2861
2862
8.34k
    _cmsAssert(hIT8 != NULL);
2863
2864
8.34k
    snprintf(Buff, 255, it8->DoubleFormatter, Val);
2865
2866
8.34k
    return SetData(it8, row, col, Buff);
2867
8.34k
}
2868
2869
2870
2871
const char* CMSEXPORT cmsIT8GetData(cmsHANDLE hIT8, const char* cPatch, const char* cSample)
2872
97
{
2873
97
    cmsIT8* it8 = (cmsIT8*) hIT8;
2874
97
    int iField, iSet;
2875
2876
97
    _cmsAssert(hIT8 != NULL);
2877
2878
97
    iField = LocateSample(it8, cSample);
2879
97
    if (iField < 0) {
2880
57
        return NULL;
2881
57
    }
2882
2883
40
    iSet = LocatePatch(it8, cPatch);
2884
40
    if (iSet < 0) {
2885
0
            return NULL;
2886
0
    }
2887
2888
40
    return GetData(it8, iSet, iField);
2889
40
}
2890
2891
2892
cmsFloat64Number CMSEXPORT cmsIT8GetDataDbl(cmsHANDLE  it8, const char* cPatch, const char* cSample)
2893
97
{
2894
97
    const char* Buffer;
2895
2896
97
    Buffer = cmsIT8GetData(it8, cPatch, cSample);
2897
2898
97
    return ParseFloatNumber(Buffer);
2899
97
}
2900
2901
2902
2903
cmsBool CMSEXPORT cmsIT8SetData(cmsHANDLE hIT8, const char* cPatch, const char* cSample, const char *Val)
2904
0
{
2905
0
    cmsIT8* it8 = (cmsIT8*) hIT8;
2906
0
    int iField, iSet;
2907
0
    TABLE* t;
2908
2909
0
    _cmsAssert(hIT8 != NULL);
2910
2911
0
    t = GetTable(it8);
2912
2913
0
    iField = LocateSample(it8, cSample);
2914
2915
0
    if (iField < 0)
2916
0
        return FALSE;
2917
2918
0
    if (t-> nPatches == 0) {
2919
2920
0
        if (!AllocateDataFormat(it8))
2921
0
            return FALSE;
2922
2923
0
        if (!AllocateDataSet(it8))
2924
0
            return FALSE;
2925
2926
0
        CookPointers(it8);
2927
0
    }
2928
2929
0
    if (cmsstrcasecmp(cSample, "SAMPLE_ID") == 0) {
2930
2931
0
        iSet   = LocateEmptyPatch(it8);
2932
0
        if (iSet < 0) {
2933
0
            return SynError(it8, "Couldn't add more patches '%s'\n", cPatch);
2934
0
        }
2935
2936
0
        iField = t -> SampleID;
2937
0
    }
2938
0
    else {
2939
0
        iSet = LocatePatch(it8, cPatch);
2940
0
        if (iSet < 0) {
2941
0
            return FALSE;
2942
0
        }
2943
0
    }
2944
2945
0
    return SetData(it8, iSet, iField, Val);
2946
0
}
2947
2948
2949
cmsBool CMSEXPORT cmsIT8SetDataDbl(cmsHANDLE hIT8, const char* cPatch,
2950
                                   const char* cSample,
2951
                                   cmsFloat64Number Val)
2952
0
{
2953
0
    cmsIT8* it8 = (cmsIT8*) hIT8;
2954
0
    char Buff[256];
2955
2956
0
    _cmsAssert(hIT8 != NULL);
2957
2958
0
    snprintf(Buff, 255, it8->DoubleFormatter, Val);
2959
0
    return cmsIT8SetData(hIT8, cPatch, cSample, Buff);
2960
0
}
2961
2962
// Buffer should get MAXSTR at least
2963
2964
const char* CMSEXPORT cmsIT8GetPatchName(cmsHANDLE hIT8, int nPatch, char* buffer)
2965
0
{
2966
0
    cmsIT8* it8 = (cmsIT8*) hIT8;
2967
0
    TABLE* t;
2968
0
    char* Data;
2969
2970
0
    _cmsAssert(hIT8 != NULL);
2971
2972
0
    t = GetTable(it8);
2973
0
    Data = GetData(it8, nPatch, t->SampleID);
2974
2975
0
    if (!Data) return NULL;
2976
0
    if (!buffer) return Data;
2977
2978
0
    strncpy(buffer, Data, MAXSTR-1);
2979
0
    buffer[MAXSTR-1] = 0;
2980
0
    return buffer;
2981
0
}
2982
2983
int CMSEXPORT cmsIT8GetPatchByName(cmsHANDLE hIT8, const char *cPatch)
2984
0
{
2985
0
    _cmsAssert(hIT8 != NULL);
2986
2987
0
    return LocatePatch((cmsIT8*)hIT8, cPatch);
2988
0
}
2989
2990
cmsUInt32Number CMSEXPORT cmsIT8TableCount(cmsHANDLE hIT8)
2991
0
{
2992
0
    cmsIT8* it8 = (cmsIT8*) hIT8;
2993
2994
0
    _cmsAssert(hIT8 != NULL);
2995
2996
0
    return it8 ->TablesCount;
2997
0
}
2998
2999
// This handles the "LABEL" extension.
3000
// Label, nTable, Type
3001
3002
int CMSEXPORT cmsIT8SetTableByLabel(cmsHANDLE hIT8, const char* cSet, const char* cField, const char* ExpectedType)
3003
0
{
3004
0
    const char* cLabelFld;
3005
0
    char Type[256], Label[256];
3006
0
    cmsUInt32Number nTable;
3007
3008
0
    _cmsAssert(hIT8 != NULL);
3009
3010
0
    if (cField != NULL && *cField == 0)
3011
0
            cField = "LABEL";
3012
3013
0
    if (cField == NULL)
3014
0
            cField = "LABEL";
3015
3016
0
    cLabelFld = cmsIT8GetData(hIT8, cSet, cField);
3017
0
    if (!cLabelFld) return -1;
3018
3019
0
    if (sscanf(cLabelFld, "%255s %u %255s", Label, &nTable, Type) != 3)
3020
0
            return -1;
3021
3022
0
    if (ExpectedType != NULL && *ExpectedType == 0)
3023
0
        ExpectedType = NULL;
3024
3025
0
    if (ExpectedType) {
3026
3027
0
        if (cmsstrcasecmp(Type, ExpectedType) != 0) return -1;
3028
0
    }
3029
3030
0
    return cmsIT8SetTable(hIT8, nTable);
3031
0
}
3032
3033
3034
cmsBool CMSEXPORT cmsIT8SetIndexColumn(cmsHANDLE hIT8, const char* cSample)
3035
0
{
3036
0
    cmsIT8* it8 = (cmsIT8*) hIT8;
3037
0
    int pos;
3038
3039
0
    _cmsAssert(hIT8 != NULL);
3040
3041
0
    pos = LocateSample(it8, cSample);
3042
0
    if(pos == -1)
3043
0
        return FALSE;
3044
3045
0
    it8->Tab[it8->nTable].SampleID = pos;
3046
0
    return TRUE;
3047
0
}
3048
3049
3050
void CMSEXPORT cmsIT8DefineDblFormat(cmsHANDLE hIT8, const char* Formatter)
3051
0
{
3052
0
    cmsIT8* it8 = (cmsIT8*) hIT8;
3053
3054
0
    _cmsAssert(hIT8 != NULL);
3055
3056
0
    if (Formatter == NULL)
3057
0
        strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT);
3058
0
    else
3059
0
        strncpy(it8->DoubleFormatter, Formatter, sizeof(it8->DoubleFormatter));
3060
3061
0
    it8 ->DoubleFormatter[sizeof(it8 ->DoubleFormatter)-1] = 0;
3062
0
}
3063
3064
3065
static
3066
cmsBool ReadNumbers(cmsIT8* cube, int n, cmsFloat64Number* arr)
3067
0
{
3068
0
    int i;
3069
3070
0
    for (i = 0; i < n; i++) {
3071
3072
0
        if (cube->sy == SINUM)
3073
0
            arr[i] = cube->inum;
3074
0
        else
3075
0
            if (cube->sy == SDNUM)
3076
0
                arr[i] = cube->dnum;
3077
0
            else
3078
0
                return SynError(cube, "Number expected");
3079
3080
0
        InSymbol(cube);
3081
0
    }
3082
3083
0
    return CheckEOLN(cube);
3084
0
}
3085
3086
static
3087
cmsBool ParseCube(cmsIT8* cube, cmsStage** Shaper, cmsStage** CLUT, char title[])
3088
0
{
3089
0
    cmsFloat64Number domain_min[3] = { 0, 0, 0 };
3090
0
    cmsFloat64Number domain_max[3] = { 1.0, 1.0, 1.0 };
3091
0
    cmsFloat64Number check_0_1[2] = { 0, 1.0 };
3092
0
    int shaper_size = 0;
3093
0
    int lut_size = 0;
3094
0
    int i;
3095
3096
0
    InSymbol(cube);
3097
3098
0
    while (cube->sy != SEOF && cube->sy != SSYNERROR) {
3099
3100
0
        switch (cube->sy)
3101
0
        {
3102
        // Set profile description
3103
0
        case STITLE:
3104
0
            InSymbol(cube);
3105
0
            if (!Check(cube, SSTRING, "Title string expected")) return FALSE;
3106
0
            memcpy(title, StringPtr(cube->str), MAXSTR);
3107
0
            title[MAXSTR - 1] = 0;
3108
0
            InSymbol(cube);
3109
0
            break;
3110
3111
        // Define domain
3112
0
        case SDOMAIN_MIN:
3113
0
            InSymbol(cube);
3114
0
            if (!ReadNumbers(cube, 3, domain_min)) return FALSE;
3115
0
            break;
3116
3117
0
        case SDOMAIN_MAX:
3118
0
            InSymbol(cube);
3119
0
            if (!ReadNumbers(cube, 3, domain_max)) return FALSE;
3120
0
            break;
3121
3122
        // Define shaper
3123
0
        case S_LUT1D_SIZE:
3124
0
            InSymbol(cube);
3125
0
            if (!Check(cube, SINUM, "Shaper size expected")) return FALSE;
3126
0
            shaper_size = cube->inum;
3127
0
            if (shaper_size < 2 || shaper_size > 65536)
3128
0
                 return SynError(cube, "LUT_1D_SIZE '%d' is out of bounds", shaper_size);
3129
0
            InSymbol(cube);
3130
0
            break;
3131
        
3132
        // Deefine CLUT
3133
0
        case S_LUT3D_SIZE:
3134
0
            InSymbol(cube);
3135
0
            if (!Check(cube, SINUM, "LUT size expected")) return FALSE;
3136
0
            lut_size = cube->inum;
3137
0
            InSymbol(cube);
3138
0
            break;
3139
3140
        // Range. If present, has to be 0..1.0
3141
0
        case S_LUT1D_INPUT_RANGE:
3142
0
        case S_LUT3D_INPUT_RANGE:
3143
0
            InSymbol(cube);
3144
0
            if (!ReadNumbers(cube, 2, check_0_1)) return FALSE;
3145
0
            if (check_0_1[0] != 0 || check_0_1[1] != 1.0) {
3146
0
                return SynError(cube, "Unsupported format");
3147
0
            }
3148
0
            break;
3149
3150
0
        case SEOLN:
3151
0
            InSymbol(cube);
3152
0
            break;
3153
3154
0
        default:
3155
0
        case S_LUT_IN_VIDEO_RANGE:
3156
0
        case S_LUT_OUT_VIDEO_RANGE:
3157
0
            return SynError(cube, "Unsupported format");
3158
3159
            // Read and create tables
3160
0
        case SINUM:
3161
0
        case SDNUM:
3162
3163
0
            if (shaper_size > 0) {
3164
3165
0
                cmsToneCurve* curves[3];
3166
0
                cmsFloat32Number* shapers = (cmsFloat32Number*)_cmsMalloc(cube->ContextID, 3 * shaper_size * sizeof(cmsFloat32Number));
3167
0
                if (shapers == NULL) return FALSE;
3168
3169
0
                for (i = 0; i < shaper_size; i++) {
3170
3171
0
                    cmsFloat64Number nums[3];
3172
3173
0
                    if (!ReadNumbers(cube, 3, nums)) return FALSE;
3174
3175
0
                    shapers[i + 0]               = (cmsFloat32Number) ((nums[0] - domain_min[0]) / (domain_max[0] - domain_min[0]));
3176
0
                    shapers[i + 1 * shaper_size] = (cmsFloat32Number) ((nums[1] - domain_min[1]) / (domain_max[1] - domain_min[1]));
3177
0
                    shapers[i + 2 * shaper_size] = (cmsFloat32Number) ((nums[2] - domain_min[2]) / (domain_max[2] - domain_min[2]));
3178
0
                }
3179
3180
0
                for (i = 0; i < 3; i++) {
3181
3182
0
                    curves[i] = cmsBuildTabulatedToneCurveFloat(cube->ContextID, shaper_size,
3183
0
                        &shapers[i * shaper_size]);
3184
0
                    if (curves[i] == NULL) return FALSE;
3185
0
                }
3186
3187
0
                *Shaper = cmsStageAllocToneCurves(cube->ContextID, 3, curves);
3188
3189
0
                cmsFreeToneCurveTriple(curves);
3190
0
            }
3191
3192
0
            if (lut_size > 0) {
3193
3194
0
                int nodes;
3195
                
3196
                /**
3197
                * Professional LUT generation tools (e.g., Nobe LutBake) list 65×65×65 as their highest supported size.                
3198
                */
3199
0
                if (lut_size < 2 || lut_size > 65)
3200
0
                    return SynError(cube, "LUT size '%d' is not allowed", lut_size);
3201
3202
0
                nodes = lut_size * lut_size * lut_size;
3203
3204
3205
0
                cmsFloat32Number* lut_table = (cmsFloat32Number*) _cmsMalloc(cube->ContextID, nodes * 3 * sizeof(cmsFloat32Number));
3206
0
                if (lut_table == NULL) return FALSE;
3207
3208
0
                for (i = 0; i < nodes; i++) {
3209
3210
0
                    cmsFloat64Number nums[3];
3211
3212
0
                    if (!ReadNumbers(cube, 3, nums)) return FALSE;
3213
3214
0
                    lut_table[i * 3 + 2] = (cmsFloat32Number) ((nums[0] - domain_min[0]) / (domain_max[0] - domain_min[0]));
3215
0
                    lut_table[i * 3 + 1] = (cmsFloat32Number) ((nums[1] - domain_min[1]) / (domain_max[1] - domain_min[1]));
3216
0
                    lut_table[i * 3 + 0] = (cmsFloat32Number) ((nums[2] - domain_min[2]) / (domain_max[2] - domain_min[2]));
3217
0
                }
3218
3219
0
                *CLUT = cmsStageAllocCLutFloat(cube->ContextID, lut_size, 3, 3, lut_table);
3220
0
                _cmsFree(cube->ContextID, lut_table);
3221
0
            }   
3222
3223
0
            if (!Check(cube, SEOF, "Extra symbols found in file")) return FALSE;
3224
0
        }
3225
0
    }
3226
3227
0
    return TRUE;
3228
0
}
3229
3230
// Share the parser to read .cube format and create RGB devicelink profiles
3231
cmsHPROFILE CMSEXPORT cmsCreateDeviceLinkFromCubeFileTHR(cmsContext ContextID, const char* cFileName)
3232
0
{    
3233
0
    cmsHPROFILE hProfile = NULL;
3234
0
    cmsIT8* cube = NULL;
3235
0
    cmsPipeline* Pipeline = NULL;   
3236
0
    cmsStage* CLUT = NULL;
3237
0
    cmsStage* Shaper = NULL;
3238
0
    cmsMLU* DescriptionMLU = NULL;
3239
0
    char title[MAXSTR];
3240
3241
0
    _cmsAssert(cFileName != NULL);
3242
    
3243
0
    cube = (cmsIT8*) cmsIT8Alloc(ContextID);    
3244
0
    if (!cube) return NULL;
3245
3246
0
    cube->IsCUBE = TRUE;
3247
0
    cube->FileStack[0]->Stream = fopen(cFileName, "rt");
3248
3249
0
    if (!cube->FileStack[0]->Stream) goto Done;
3250
3251
0
    strncpy(cube->FileStack[0]->FileName, cFileName, cmsMAX_PATH - 1);
3252
0
    cube->FileStack[0]->FileName[cmsMAX_PATH - 1] = 0;
3253
3254
0
    if (!ParseCube(cube, &Shaper, &CLUT, title)) goto Done;
3255
        
3256
    // Success on parsing, let's create the profile
3257
0
    hProfile = cmsCreateProfilePlaceholder(ContextID);
3258
0
    if (!hProfile) goto Done;
3259
        
3260
0
    cmsSetProfileVersion(hProfile, 4.4);
3261
3262
0
    cmsSetDeviceClass(hProfile, cmsSigLinkClass);
3263
0
    cmsSetColorSpace(hProfile,  cmsSigRgbData);
3264
0
    cmsSetPCS(hProfile,         cmsSigRgbData);
3265
3266
0
    cmsSetHeaderRenderingIntent(hProfile, INTENT_PERCEPTUAL);
3267
3268
    // Creates a Pipeline to hold CLUT and shaper
3269
0
    Pipeline = cmsPipelineAlloc(ContextID, 3, 3);
3270
0
    if (Pipeline == NULL) goto Done;
3271
3272
    // Populates the pipeline
3273
0
    if (Shaper != NULL) {
3274
0
        if (!cmsPipelineInsertStage(Pipeline, cmsAT_BEGIN, Shaper)) {
3275
0
            cmsStageFree(Shaper);
3276
0
            goto Done;
3277
0
        }
3278
0
    }
3279
3280
0
    if (CLUT != NULL) {
3281
0
        if (!cmsPipelineInsertStage(Pipeline, cmsAT_END, CLUT)) {
3282
0
            cmsStageFree(CLUT);
3283
0
            goto Done;
3284
0
        }
3285
0
    }
3286
3287
    // Propagate the description. We put no copyright because we know
3288
    // nothing on the copyrighted state of the .cube
3289
0
    DescriptionMLU = cmsMLUalloc(ContextID, 1);
3290
0
    if (!cmsMLUsetUTF8(DescriptionMLU, cmsNoLanguage, cmsNoCountry, title)) goto Done;
3291
3292
    // Flush the tags
3293
0
    if (!cmsWriteTag(hProfile, cmsSigProfileDescriptionTag, DescriptionMLU)) goto Done;
3294
0
    if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, (void*)Pipeline)) goto Done;
3295
3296
0
Done:
3297
3298
0
    if (DescriptionMLU != NULL)
3299
0
        cmsMLUfree(DescriptionMLU);
3300
3301
0
    if (Pipeline != NULL)
3302
0
        cmsPipelineFree(Pipeline);
3303
3304
0
    cmsIT8Free((cmsHANDLE) cube);
3305
3306
0
    return hProfile;
3307
0
}
3308
3309
cmsHPROFILE CMSEXPORT cmsCreateDeviceLinkFromCubeFile(const char* cFileName)
3310
0
{
3311
    return cmsCreateDeviceLinkFromCubeFileTHR(NULL, cFileName);
3312
0
}