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