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