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