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