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