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