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