Coverage Report

Created: 2026-09-06 06:47

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