Coverage Report

Created: 2024-10-29 06:13

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