1 //---------------------------------------------------------------------------------
3 // Little Color Management System
4 // Copyright (c) 1998-2012 Marti Maria Saguer
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:
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
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.
24 //---------------------------------------------------------------------------------
27 #include "lcms2_internal.h"
30 // IT8.7 / CGATS.17-200x handling -----------------------------------------------------------------------------
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
38 #define DEFAULT_DBL_FORMAT "%.10g" // Double formatting
40 #ifdef CMS_IS_WINDOWS_
42 # define DIR_CHAR '\\'
58 SEOF
, // End of stream
59 SSYNERROR
, // Syntax error found on stream
74 // How to write the value
85 // Linked list of variable names
86 typedef struct _KeyVal
{
89 char* Keyword
; // Name of variable
90 struct _KeyVal
* NextSubkey
; // If key is a dictionary, points to the next item
91 char* Subkey
; // If key is a dictionary, points to the subkey name
92 char* Value
; // Points to value
93 WRITEMODE WriteAs
; // How to write the value
98 // Linked list of memory chunks (Memory sink)
99 typedef struct _OwnedMem
{
101 struct _OwnedMem
* Next
;
102 void * Ptr
; // Point to value
107 typedef struct _SubAllocator
{
109 cmsUInt8Number
* Block
;
110 cmsUInt32Number BlockSize
;
111 cmsUInt32Number Used
;
115 // Table. Each individual table can hold properties and rows & cols
116 typedef struct _Table
{
118 char SheetType
[MAXSTR
]; // The first row of the IT8 (the type)
120 int nSamples
, nPatches
; // Cols, Rows
121 int SampleID
; // Pos of ID
123 KEYVALUE
* HeaderList
; // The properties
125 char** DataFormat
; // The binary stream descriptor
126 char** Data
; // The binary stream
130 // File stream being parsed
131 typedef struct _FileContext
{
132 char FileName
[cmsMAX_PATH
]; // File name if being readed from file
133 FILE* Stream
; // File stream or NULL if holded in memory
136 // This struct hold all information about an open IT8 handler.
140 cmsUInt32Number TablesCount
; // How many tables in this stream
141 cmsUInt32Number nTable
; // The actual table
143 TABLE Tab
[MAXTABLES
];
146 OWNEDMEM
* MemorySink
; // The storage backend
147 SUBALLOCATOR Allocator
; // String suballocator -- just to keep it fast
149 // Parser state machine
150 SYMBOL sy
; // Current symbol
151 int ch
; // Current character
153 int inum
; // integer value
154 cmsFloat64Number dnum
; // real value
155 char id
[MAXID
]; // identifier
156 char str
[MAXSTR
]; // string
158 // Allowed keywords & datasets. They have visibility on whole stream
159 KEYVALUE
* ValidKeywords
;
160 KEYVALUE
* ValidSampleID
;
162 char* Source
; // Points to loc. being parsed
163 int lineno
; // line counter for error reporting
165 FILECTX
* FileStack
[MAXINCLUDE
]; // Stack of files being parsed
166 int IncludeSP
; // Include Stack Pointer
168 char* MemoryBlock
; // The stream if holded in memory
170 char DoubleFormatter
[MAXID
];// Printf-like 'cmsFloat64Number' formatter
172 cmsContext ContextID
; // The threading context
177 // The stream for save operations
180 FILE* stream
; // For save-to-file behaviour
182 cmsUInt8Number
* Base
;
183 cmsUInt8Number
* Ptr
; // For save-to-mem behaviour
184 cmsUInt32Number Used
;
190 // ------------------------------------------------------ cmsIT8 parsing routines
201 // The keyword->symbol translation table. Sorting is required.
202 static const KEYWORD TabKeys
[] = {
204 {"$INCLUDE", SINCLUDE
}, // This is an extension!
205 {".INCLUDE", SINCLUDE
}, // This is an extension!
207 {"BEGIN_DATA", SBEGIN_DATA
},
208 {"BEGIN_DATA_FORMAT", SBEGIN_DATA_FORMAT
},
209 {"DATA_FORMAT_IDENTIFIER", SDATA_FORMAT_ID
},
210 {"END_DATA", SEND_DATA
},
211 {"END_DATA_FORMAT", SEND_DATA_FORMAT
},
212 {"KEYWORD", SKEYWORD
}
215 #define NUMKEYS (sizeof(TabKeys)/sizeof(KEYWORD))
217 // Predefined properties
221 const char *id
; // The identifier
222 WRITEMODE as
; // How is supposed to be written
225 static PROPERTY PredefinedProperties
[] = {
227 {"NUMBER_OF_FIELDS", WRITE_UNCOOKED
}, // Required - NUMBER OF FIELDS
228 {"NUMBER_OF_SETS", WRITE_UNCOOKED
}, // Required - NUMBER OF SETS
229 {"ORIGINATOR", WRITE_STRINGIFY
}, // Required - Identifies the specific system, organization or individual that created the data file.
230 {"FILE_DESCRIPTOR", WRITE_STRINGIFY
}, // Required - Describes the purpose or contents of the data file.
231 {"CREATED", WRITE_STRINGIFY
}, // Required - Indicates date of creation of the data file.
232 {"DESCRIPTOR", WRITE_STRINGIFY
}, // Required - Describes the purpose or contents of the data file.
233 {"DIFFUSE_GEOMETRY", WRITE_STRINGIFY
}, // The diffuse geometry used. Allowed values are "sphere" or "opal".
234 {"MANUFACTURER", WRITE_STRINGIFY
},
235 {"MANUFACTURE", WRITE_STRINGIFY
}, // Some broken Fuji targets does store this value
236 {"PROD_DATE", WRITE_STRINGIFY
}, // Identifies year and month of production of the target in the form yyyy:mm.
237 {"SERIAL", WRITE_STRINGIFY
}, // Uniquely identifies individual physical target.
239 {"MATERIAL", WRITE_STRINGIFY
}, // Identifies the material on which the target was produced using a code
240 // uniquely identifying th e material. This is intend ed to be used for IT8.7
241 // physical targets only (i.e . IT8.7/1 a nd IT8.7/2).
243 {"INSTRUMENTATION", WRITE_STRINGIFY
}, // Used to report the specific instrumentation used (manufacturer and
244 // model number) to generate the data reported. This data will often
245 // provide more information about the particular data collected than an
246 // extensive list of specific details. This is particularly important for
247 // spectral data or data derived from spectrophotometry.
249 {"MEASUREMENT_SOURCE", WRITE_STRINGIFY
}, // Illumination used for spectral measurements. This data helps provide
250 // a guide to the potential for issues of paper fluorescence, etc.
252 {"PRINT_CONDITIONS", WRITE_STRINGIFY
}, // Used to define the characteristics of the printed sheet being reported.
253 // Where standard conditions have been defined (e.g., SWOP at nominal)
254 // named conditions may suffice. Otherwise, detailed information is
257 {"SAMPLE_BACKING", WRITE_STRINGIFY
}, // Identifies the backing material used behind the sample during
258 // measurement. Allowed values are “black”, “white”, or {"na".
260 {"CHISQ_DOF", WRITE_STRINGIFY
}, // Degrees of freedom associated with the Chi squared statistic
262 // below properties are new in recent specs:
264 {"MEASUREMENT_GEOMETRY", WRITE_STRINGIFY
}, // The type of measurement, either reflection or transmission, should be indicated
265 // along with details of the geometry and the aperture size and shape. For example,
266 // for transmission measurements it is important to identify 0/diffuse, diffuse/0,
267 // opal or integrating sphere, etc. For reflection it is important to identify 0/45,
268 // 45/0, sphere (specular included or excluded), etc.
270 {"FILTER", WRITE_STRINGIFY
}, // Identifies the use of physical filter(s) during measurement. Typically used to
271 // denote the use of filters such as none, D65, Red, Green or Blue.
273 {"POLARIZATION", WRITE_STRINGIFY
}, // Identifies the use of a physical polarization filter during measurement. Allowed
274 // values are {"yes”, “white”, “none” or “na”.
276 {"WEIGHTING_FUNCTION", WRITE_PAIR
}, // Indicates such functions as: the CIE standard observer functions used in the
277 // calculation of various data parameters (2 degree and 10 degree), CIE standard
278 // illuminant functions used in the calculation of various data parameters (e.g., D50,
279 // D65, etc.), density status response, etc. If used there shall be at least one
280 // name-value pair following the WEIGHTING_FUNCTION tag/keyword. The first attribute
281 // in the set shall be {"name" and shall identify the particular parameter used.
282 // The second shall be {"value" and shall provide the value associated with that name.
283 // For ASCII data, a string containing the Name and Value attribute pairs shall follow
284 // the weighting function keyword. A semi-colon separates attribute pairs from each
285 // other and within the attribute the name and value are separated by a comma.
287 {"COMPUTATIONAL_PARAMETER", WRITE_PAIR
}, // Parameter that is used in computing a value from measured data. Name is the name
288 // of the calculation, parameter is the name of the parameter used in the calculation
289 // and value is the value of the parameter.
291 {"TARGET_TYPE", WRITE_STRINGIFY
}, // The type of target being measured, e.g. IT8.7/1, IT8.7/3, user defined, etc.
293 {"COLORANT", WRITE_STRINGIFY
}, // Identifies the colorant(s) used in creating the target.
295 {"TABLE_DESCRIPTOR", WRITE_STRINGIFY
}, // Describes the purpose or contents of a data table.
297 {"TABLE_NAME", WRITE_STRINGIFY
} // Provides a short name for a data table.
300 #define NUMPREDEFINEDPROPS (sizeof(PredefinedProperties)/sizeof(PROPERTY))
303 // Predefined sample types on dataset
304 static const char* PredefinedSampleID
[] = {
305 "SAMPLE_ID", // Identifies sample that data represents
306 "STRING", // Identifies label, or other non-machine readable value.
307 // Value must begin and end with a " symbol
309 "CMYK_C", // Cyan component of CMYK data expressed as a percentage
310 "CMYK_M", // Magenta component of CMYK data expressed as a percentage
311 "CMYK_Y", // Yellow component of CMYK data expressed as a percentage
312 "CMYK_K", // Black component of CMYK data expressed as a percentage
313 "D_RED", // Red filter density
314 "D_GREEN", // Green filter density
315 "D_BLUE", // Blue filter density
316 "D_VIS", // Visual filter density
317 "D_MAJOR_FILTER", // Major filter d ensity
318 "RGB_R", // Red component of RGB data
319 "RGB_G", // Green component of RGB data
320 "RGB_B", // Blue com ponent of RGB data
321 "SPECTRAL_NM", // Wavelength of measurement expressed in nanometers
322 "SPECTRAL_PCT", // Percentage reflectance/transmittance
323 "SPECTRAL_DEC", // Reflectance/transmittance
324 "XYZ_X", // X component of tristimulus data
325 "XYZ_Y", // Y component of tristimulus data
326 "XYZ_Z", // Z component of tristimulus data
327 "XYY_X" // x component of chromaticity data
328 "XYY_Y", // y component of chromaticity data
329 "XYY_CAPY", // Y component of tristimulus data
330 "LAB_L", // L* component of Lab data
331 "LAB_A", // a* component of Lab data
332 "LAB_B", // b* component of Lab data
333 "LAB_C", // C*ab component of Lab data
334 "LAB_H", // hab component of Lab data
336 "LAB_DE_94", // CIE dE using CIE 94
337 "LAB_DE_CMC", // dE using CMC
338 "LAB_DE_2000", // CIE dE using CIE DE 2000
339 "MEAN_DE", // Mean Delta E (LAB_DE) of samples compared to batch average
340 // (Used for data files for ANSI IT8.7/1 and IT8.7/2 targets)
341 "STDEV_X", // Standard deviation of X (tristimulus data)
342 "STDEV_Y", // Standard deviation of Y (tristimulus data)
343 "STDEV_Z", // Standard deviation of Z (tristimulus data)
344 "STDEV_L", // Standard deviation of L*
345 "STDEV_A", // Standard deviation of a*
346 "STDEV_B", // Standard deviation of b*
347 "STDEV_DE", // Standard deviation of CIE dE
348 "CHI_SQD_PAR"}; // The average of the standard deviations of L*, a* and b*. It is
349 // used to derive an estimate of the chi-squared parameter which is
350 // recommended as the predictor of the variability of dE
352 #define NUMPREDEFINEDSAMPLEID (sizeof(PredefinedSampleID)/sizeof(char *))
354 //Forward declaration of some internal functions
355 static void* AllocChunk(cmsIT8
* it8
, cmsUInt32Number size
);
357 // Checks whatever c is a separator
359 cmsBool
isseparator(int c
)
361 return (c
== ' ') || (c
== '\t') ;
364 // Checks whatever c is a valid identifier char
366 cmsBool
ismiddle(int c
)
368 return (!isseparator(c
) && (c
!= '#') && (c
!='\"') && (c
!= '\'') && (c
> 32) && (c
< 127));
371 // Checks whatsever c is a valid identifier middle char.
373 cmsBool
isidchar(int c
)
375 return isalnum(c
) || ismiddle(c
);
378 // Checks whatsever c is a valid identifier first char.
380 cmsBool
isfirstidchar(int c
)
382 return !isdigit(c
) && ismiddle(c
);
385 // Guess whether the supplied path looks like an absolute path
387 cmsBool
isabsolutepath(const char *path
)
396 strncpy(ThreeChars
, path
, 3);
399 if(ThreeChars
[0] == DIR_CHAR
)
402 #ifdef CMS_IS_WINDOWS_
403 if (isalpha((int) ThreeChars
[0]) && ThreeChars
[1] == ':')
410 // Makes a file path based on a given reference path
411 // NOTE: this function doesn't check if the path exists or even if it's legal
413 cmsBool
BuildAbsolutePath(const char *relPath
, const char *basePath
, char *buffer
, cmsUInt32Number MaxLen
)
419 if (isabsolutepath(relPath
)) {
421 strncpy(buffer
, relPath
, MaxLen
);
422 buffer
[MaxLen
-1] = 0;
426 // No, search for last
427 strncpy(buffer
, basePath
, MaxLen
);
428 buffer
[MaxLen
-1] = 0;
430 tail
= strrchr(buffer
, DIR_CHAR
);
431 if (tail
== NULL
) return FALSE
; // Is not absolute and has no separators??
433 len
= (cmsUInt32Number
) (tail
- buffer
);
434 if (len
>= MaxLen
) return FALSE
;
436 // No need to assure zero terminator over here
437 strncpy(tail
+ 1, relPath
, MaxLen
- len
);
443 // Make sure no exploit is being even tried
445 const char* NoMeta(const char* str
)
447 if (strchr(str
, '%') != NULL
)
448 return "**** CORRUPTED FORMAT STRING ***";
455 cmsBool
SynError(cmsIT8
* it8
, const char *Txt
, ...)
457 char Buffer
[256], ErrMsg
[1024];
461 vsnprintf(Buffer
, 255, Txt
, args
);
465 snprintf(ErrMsg
, 1023, "%s: Line %d, %s", it8
->FileStack
[it8
->IncludeSP
]->FileName
, it8
->lineno
, Buffer
);
468 cmsSignalError(it8
->ContextID
, cmsERROR_CORRUPTION_DETECTED
, "%s", ErrMsg
);
472 // Check if current symbol is same as specified. issue an error else.
474 cmsBool
Check(cmsIT8
* it8
, SYMBOL sy
, const char* Err
)
477 return SynError(it8
, NoMeta(Err
));
481 // Read Next character from stream
483 void NextCh(cmsIT8
* it8
)
485 if (it8
-> FileStack
[it8
->IncludeSP
]->Stream
) {
487 it8
->ch
= fgetc(it8
->FileStack
[it8
->IncludeSP
]->Stream
);
489 if (feof(it8
-> FileStack
[it8
->IncludeSP
]->Stream
)) {
491 if (it8
->IncludeSP
> 0) {
493 fclose(it8
->FileStack
[it8
->IncludeSP
--]->Stream
);
494 it8
-> ch
= ' '; // Whitespace to be ignored
501 it8
->ch
= *it8
->Source
;
502 if (it8
->ch
) it8
->Source
++;
507 // Try to see if current identifier is a keyword, if so return the referred symbol
509 SYMBOL
BinSrchKey(const char *id
)
518 res
= cmsstrcasecmp(id
, TabKeys
[x
-1].id
);
519 if (res
== 0) return TabKeys
[x
-1].sy
;
520 if (res
< 0) r
= x
- 1;
530 cmsFloat64Number
xpow10(int n
)
532 return pow(10, (cmsFloat64Number
) n
);
536 // Reads a Real number, tries to follow from integer number
538 void ReadReal(cmsIT8
* it8
, int inum
)
540 it8
->dnum
= (cmsFloat64Number
) inum
;
542 while (isdigit(it8
->ch
)) {
544 it8
->dnum
= it8
->dnum
* 10.0 + (it8
->ch
- '0');
548 if (it8
->ch
== '.') { // Decimal point
550 cmsFloat64Number frac
= 0.0; // fraction
551 int prec
= 0; // precision
553 NextCh(it8
); // Eats dec. point
555 while (isdigit(it8
->ch
)) {
557 frac
= frac
* 10.0 + (it8
->ch
- '0');
562 it8
->dnum
= it8
->dnum
+ (frac
/ xpow10(prec
));
565 // Exponent, example 34.00E+20
566 if (toupper(it8
->ch
) == 'E') {
571 NextCh(it8
); sgn
= 1;
573 if (it8
->ch
== '-') {
575 sgn
= -1; NextCh(it8
);
578 if (it8
->ch
== '+') {
585 while (isdigit(it8
->ch
)) {
587 if ((cmsFloat64Number
) e
* 10L < INT_MAX
)
588 e
= e
* 10 + (it8
->ch
- '0');
594 it8
-> dnum
= it8
-> dnum
* xpow10(e
);
598 // Parses a float number
599 // This can not call directly atof because it uses locale dependant
600 // parsing, while CCMX files always use . as decimal separator
602 cmsFloat64Number
ParseFloatNumber(const char *Buffer
)
604 cmsFloat64Number dnum
= 0.0;
608 if (Buffer
== NULL
) return 0.0;
610 if (*Buffer
== '-' || *Buffer
== '+') {
612 sign
= (*Buffer
== '-') ? -1 : 1;
617 while (*Buffer
&& isdigit((int) *Buffer
)) {
619 dnum
= dnum
* 10.0 + (*Buffer
- '0');
620 if (*Buffer
) Buffer
++;
623 if (*Buffer
== '.') {
625 cmsFloat64Number frac
= 0.0; // fraction
626 int prec
= 0; // precission
628 if (*Buffer
) Buffer
++;
630 while (*Buffer
&& isdigit((int) *Buffer
)) {
632 frac
= frac
* 10.0 + (*Buffer
- '0');
634 if (*Buffer
) Buffer
++;
637 dnum
= dnum
+ (frac
/ xpow10(prec
));
640 // Exponent, example 34.00E+20
641 if (*Buffer
&& toupper(*Buffer
) == 'E') {
646 if (*Buffer
) Buffer
++;
649 if (*Buffer
== '-') {
652 if (*Buffer
) Buffer
++;
655 if (*Buffer
== '+') {
658 if (*Buffer
) Buffer
++;
662 while (*Buffer
&& isdigit((int) *Buffer
)) {
664 if ((cmsFloat64Number
) e
* 10L < INT_MAX
)
665 e
= e
* 10 + (*Buffer
- '0');
667 if (*Buffer
) Buffer
++;
671 dnum
= dnum
* xpow10(e
);
680 void InSymbol(cmsIT8
* it8
)
682 register char *idptr
;
689 while (isseparator(it8
->ch
))
692 if (isfirstidchar(it8
->ch
)) { // Identifier
699 if (++k
< MAXID
) *idptr
++ = (char) it8
->ch
;
703 } while (isidchar(it8
->ch
));
708 key
= BinSrchKey(it8
->id
);
709 if (key
== SNONE
) it8
->sy
= SIDENT
;
714 if (isdigit(it8
->ch
) || it8
->ch
== '.' || it8
->ch
== '-' || it8
->ch
== '+')
718 if (it8
->ch
== '-') {
726 if (it8
->ch
== '0') { // 0xnnnn (Hexa) or 0bnnnn (Binary)
729 if (toupper(it8
->ch
) == 'X') {
734 while (isxdigit(it8
->ch
))
736 it8
->ch
= toupper(it8
->ch
);
737 if (it8
->ch
>= 'A' && it8
->ch
<= 'F') j
= it8
->ch
-'A'+10;
738 else j
= it8
->ch
- '0';
740 if ((long) it8
->inum
* 16L > (long) INT_MAX
)
742 SynError(it8
, "Invalid hexadecimal number");
746 it8
->inum
= it8
->inum
* 16 + j
;
752 if (toupper(it8
->ch
) == 'B') { // Binary
757 while (it8
->ch
== '0' || it8
->ch
== '1')
761 if ((long) it8
->inum
* 2L > (long) INT_MAX
)
763 SynError(it8
, "Invalid binary number");
767 it8
->inum
= it8
->inum
* 2 + j
;
775 while (isdigit(it8
->ch
)) {
777 if ((long) it8
->inum
* 10L > (long) INT_MAX
) {
778 ReadReal(it8
, it8
->inum
);
784 it8
->inum
= it8
->inum
* 10 + (it8
->ch
- '0');
788 if (it8
->ch
== '.') {
790 ReadReal(it8
, it8
->inum
);
798 // Special case. Numbers followed by letters are taken as identifiers
800 if (isidchar(it8
->ch
)) {
802 if (it8
->sy
== SINUM
) {
804 sprintf(it8
->id
, "%d", it8
->inum
);
808 sprintf(it8
->id
, it8
->DoubleFormatter
, it8
->dnum
);
811 k
= (int) strlen(it8
->id
);
812 idptr
= it8
->id
+ k
;
815 if (++k
< MAXID
) *idptr
++ = (char) it8
->ch
;
819 } while (isidchar(it8
->ch
));
828 switch ((int) it8
->ch
) {
830 // EOF marker -- ignore it
835 // Eof stream markers
845 if (it8
->ch
== '\n')
860 while (it8
->ch
&& it8
->ch
!= '\n' && it8
->ch
!= '\r')
874 while (k
< MAXSTR
&& it8
->ch
!= sng
) {
876 if (it8
->ch
== '\n'|| it8
->ch
== '\r') k
= MAXSTR
+1;
878 *idptr
++ = (char) it8
->ch
;
891 SynError(it8
, "Unrecognized character: 0x%x", it8
->ch
);
895 } while (it8
->sy
== SCOMMENT
);
897 // Handle the include special token
899 if (it8
-> sy
== SINCLUDE
) {
903 if(it8
-> IncludeSP
>= (MAXINCLUDE
-1)) {
905 SynError(it8
, "Too many recursion levels");
910 if (!Check(it8
, SSTRING
, "Filename expected")) return;
912 FileNest
= it8
-> FileStack
[it8
-> IncludeSP
+ 1];
913 if(FileNest
== NULL
) {
915 FileNest
= it8
->FileStack
[it8
-> IncludeSP
+ 1] = (FILECTX
*)AllocChunk(it8
, sizeof(FILECTX
));
916 //if(FileNest == NULL)
917 // TODO: how to manage out-of-memory conditions?
920 if (BuildAbsolutePath(it8
->str
,
921 it8
->FileStack
[it8
->IncludeSP
]->FileName
,
922 FileNest
->FileName
, cmsMAX_PATH
-1) == FALSE
) {
923 SynError(it8
, "File path too long");
927 FileNest
->Stream
= fopen(FileNest
->FileName
, "rt");
928 if (FileNest
->Stream
== NULL
) {
930 SynError(it8
, "File %s not found", FileNest
->FileName
);
941 // Checks end of line separator
943 cmsBool
CheckEOLN(cmsIT8
* it8
)
945 if (!Check(it8
, SEOLN
, "Expected separator")) return FALSE
;
946 while (it8
-> sy
== SEOLN
)
955 void Skip(cmsIT8
* it8
, SYMBOL sy
)
957 if (it8
->sy
== sy
&& it8
->sy
!= SEOF
)
962 // Skip multiple EOLN
964 void SkipEOLN(cmsIT8
* it8
)
966 while (it8
->sy
== SEOLN
) {
972 // Returns a string holding current value
974 cmsBool
GetVal(cmsIT8
* it8
, char* Buffer
, cmsUInt32Number max
, const char* ErrorTitle
)
978 case SEOLN
: // Empty value
981 case SIDENT
: strncpy(Buffer
, it8
->id
, max
);
984 case SINUM
: snprintf(Buffer
, max
, "%d", it8
-> inum
); break;
985 case SDNUM
: snprintf(Buffer
, max
, it8
->DoubleFormatter
, it8
-> dnum
); break;
986 case SSTRING
: strncpy(Buffer
, it8
->str
, max
);
992 return SynError(it8
, "%s", ErrorTitle
);
999 // ---------------------------------------------------------- Table
1002 TABLE
* GetTable(cmsIT8
* it8
)
1004 if ((it8
-> nTable
>= it8
->TablesCount
)) {
1006 SynError(it8
, "Table %d out of sequence", it8
-> nTable
);
1010 return it8
->Tab
+ it8
->nTable
;
1013 // ---------------------------------------------------------- Memory management
1016 // Frees an allocator and owned memory
1017 void CMSEXPORT
cmsIT8Free(cmsHANDLE hIT8
)
1019 cmsIT8
* it8
= (cmsIT8
*) hIT8
;
1024 if (it8
->MemorySink
) {
1029 for (p
= it8
->MemorySink
; p
!= NULL
; p
= n
) {
1032 if (p
->Ptr
) _cmsFree(it8
->ContextID
, p
->Ptr
);
1033 _cmsFree(it8
->ContextID
, p
);
1037 if (it8
->MemoryBlock
)
1038 _cmsFree(it8
->ContextID
, it8
->MemoryBlock
);
1040 _cmsFree(it8
->ContextID
, it8
);
1044 // Allocates a chunk of data, keep linked list
1046 void* AllocBigBlock(cmsIT8
* it8
, cmsUInt32Number size
)
1049 void* ptr
= _cmsMallocZero(it8
->ContextID
, size
);
1053 ptr1
= (OWNEDMEM
*) _cmsMallocZero(it8
->ContextID
, sizeof(OWNEDMEM
));
1057 _cmsFree(it8
->ContextID
, ptr
);
1062 ptr1
-> Next
= it8
-> MemorySink
;
1063 it8
-> MemorySink
= ptr1
;
1072 void* AllocChunk(cmsIT8
* it8
, cmsUInt32Number size
)
1074 cmsUInt32Number Free
= it8
->Allocator
.BlockSize
- it8
->Allocator
.Used
;
1075 cmsUInt8Number
* ptr
;
1077 size
= _cmsALIGNMEM(size
);
1081 if (it8
-> Allocator
.BlockSize
== 0)
1083 it8
-> Allocator
.BlockSize
= 20*1024;
1085 it8
->Allocator
.BlockSize
*= 2;
1087 if (it8
->Allocator
.BlockSize
< size
)
1088 it8
->Allocator
.BlockSize
= size
;
1090 it8
->Allocator
.Used
= 0;
1091 it8
->Allocator
.Block
= (cmsUInt8Number
*) AllocBigBlock(it8
, it8
->Allocator
.BlockSize
);
1094 ptr
= it8
->Allocator
.Block
+ it8
->Allocator
.Used
;
1095 it8
->Allocator
.Used
+= size
;
1102 // Allocates a string
1104 char *AllocString(cmsIT8
* it8
, const char* str
)
1106 cmsUInt32Number Size
= (cmsUInt32Number
) strlen(str
)+1;
1110 ptr
= (char *) AllocChunk(it8
, Size
);
1111 if (ptr
) strncpy (ptr
, str
, Size
-1);
1116 // Searches through linked list
1119 cmsBool
IsAvailableOnList(KEYVALUE
* p
, const char* Key
, const char* Subkey
, KEYVALUE
** LastPtr
)
1121 if (LastPtr
) *LastPtr
= p
;
1123 for (; p
!= NULL
; p
= p
->Next
) {
1125 if (LastPtr
) *LastPtr
= p
;
1127 if (*Key
!= '#') { // Comments are ignored
1129 if (cmsstrcasecmp(Key
, p
->Keyword
) == 0)
1140 for (; p
!= NULL
; p
= p
->NextSubkey
) {
1142 if (p
->Subkey
== NULL
) continue;
1144 if (LastPtr
) *LastPtr
= p
;
1146 if (cmsstrcasecmp(Subkey
, p
->Subkey
) == 0)
1155 // Add a property into a linked list
1157 KEYVALUE
* AddToList(cmsIT8
* it8
, KEYVALUE
** Head
, const char *Key
, const char *Subkey
, const char* xValue
, WRITEMODE WriteAs
)
1163 // Check if property is already in list
1165 if (IsAvailableOnList(*Head
, Key
, Subkey
, &p
)) {
1167 // This may work for editing properties
1169 // return SynError(it8, "duplicate key <%s>", Key);
1175 // Allocate the container
1176 p
= (KEYVALUE
*) AllocChunk(it8
, sizeof(KEYVALUE
));
1179 SynError(it8
, "AddToList: out of memory");
1183 // Store name and value
1184 p
->Keyword
= AllocString(it8
, Key
);
1185 p
->Subkey
= (Subkey
== NULL
) ? NULL
: AllocString(it8
, Subkey
);
1187 // Keep the container in our list
1188 if (*Head
== NULL
) {
1193 if (Subkey
!= NULL
&& last
!= NULL
) {
1195 last
->NextSubkey
= p
;
1197 // If Subkey is not null, then last is the last property with the same key,
1198 // but not necessarily is the last property in the list, so we need to move
1199 // to the actual list end
1200 while (last
->Next
!= NULL
)
1204 if (last
!= NULL
) last
->Next
= p
;
1208 p
->NextSubkey
= NULL
;
1211 p
->WriteAs
= WriteAs
;
1213 if (xValue
!= NULL
) {
1215 p
->Value
= AllocString(it8
, xValue
);
1225 KEYVALUE
* AddAvailableProperty(cmsIT8
* it8
, const char* Key
, WRITEMODE as
)
1227 return AddToList(it8
, &it8
->ValidKeywords
, Key
, NULL
, NULL
, as
);
1232 KEYVALUE
* AddAvailableSampleID(cmsIT8
* it8
, const char* Key
)
1234 return AddToList(it8
, &it8
->ValidSampleID
, Key
, NULL
, NULL
, WRITE_UNCOOKED
);
1239 void AllocTable(cmsIT8
* it8
)
1243 t
= it8
->Tab
+ it8
->TablesCount
;
1245 t
->HeaderList
= NULL
;
1246 t
->DataFormat
= NULL
;
1249 it8
->TablesCount
++;
1253 cmsInt32Number CMSEXPORT
cmsIT8SetTable(cmsHANDLE IT8
, cmsUInt32Number nTable
)
1255 cmsIT8
* it8
= (cmsIT8
*) IT8
;
1257 if (nTable
>= it8
->TablesCount
) {
1259 if (nTable
== it8
->TablesCount
) {
1264 SynError(it8
, "Table %d is out of sequence", nTable
);
1269 it8
->nTable
= nTable
;
1271 return (cmsInt32Number
) nTable
;
1276 // Init an empty container
1277 cmsHANDLE CMSEXPORT
cmsIT8Alloc(cmsContext ContextID
)
1282 it8
= (cmsIT8
*) _cmsMallocZero(ContextID
, sizeof(cmsIT8
));
1283 if (it8
== NULL
) return NULL
;
1287 it8
->MemoryBlock
= NULL
;
1288 it8
->MemorySink
= NULL
;
1292 it8
->ContextID
= ContextID
;
1293 it8
->Allocator
.Used
= 0;
1294 it8
->Allocator
.Block
= NULL
;
1295 it8
->Allocator
.BlockSize
= 0;
1297 it8
->ValidKeywords
= NULL
;
1298 it8
->ValidSampleID
= NULL
;
1302 it8
-> Source
= NULL
;
1306 it8
->FileStack
[0] = (FILECTX
*)AllocChunk(it8
, sizeof(FILECTX
));
1310 strcpy(it8
->DoubleFormatter
, DEFAULT_DBL_FORMAT
);
1311 cmsIT8SetSheetType((cmsHANDLE
) it8
, "CGATS.17");
1313 // Initialize predefined properties & data
1315 for (i
=0; i
< NUMPREDEFINEDPROPS
; i
++)
1316 AddAvailableProperty(it8
, PredefinedProperties
[i
].id
, PredefinedProperties
[i
].as
);
1318 for (i
=0; i
< NUMPREDEFINEDSAMPLEID
; i
++)
1319 AddAvailableSampleID(it8
, PredefinedSampleID
[i
]);
1322 return (cmsHANDLE
) it8
;
1326 const char* CMSEXPORT
cmsIT8GetSheetType(cmsHANDLE hIT8
)
1328 return GetTable((cmsIT8
*) hIT8
)->SheetType
;
1331 cmsBool CMSEXPORT
cmsIT8SetSheetType(cmsHANDLE hIT8
, const char* Type
)
1333 TABLE
* t
= GetTable((cmsIT8
*) hIT8
);
1335 strncpy(t
->SheetType
, Type
, MAXSTR
-1);
1336 t
->SheetType
[MAXSTR
-1] = 0;
1340 cmsBool CMSEXPORT
cmsIT8SetComment(cmsHANDLE hIT8
, const char* Val
)
1342 cmsIT8
* it8
= (cmsIT8
*) hIT8
;
1344 if (!Val
) return FALSE
;
1345 if (!*Val
) return FALSE
;
1347 return AddToList(it8
, &GetTable(it8
)->HeaderList
, "# ", NULL
, Val
, WRITE_UNCOOKED
) != NULL
;
1351 cmsBool CMSEXPORT
cmsIT8SetPropertyStr(cmsHANDLE hIT8
, const char* Key
, const char *Val
)
1353 cmsIT8
* it8
= (cmsIT8
*) hIT8
;
1355 if (!Val
) return FALSE
;
1356 if (!*Val
) return FALSE
;
1358 return AddToList(it8
, &GetTable(it8
)->HeaderList
, Key
, NULL
, Val
, WRITE_STRINGIFY
) != NULL
;
1361 cmsBool CMSEXPORT
cmsIT8SetPropertyDbl(cmsHANDLE hIT8
, const char* cProp
, cmsFloat64Number Val
)
1363 cmsIT8
* it8
= (cmsIT8
*) hIT8
;
1366 sprintf(Buffer
, it8
->DoubleFormatter
, Val
);
1368 return AddToList(it8
, &GetTable(it8
)->HeaderList
, cProp
, NULL
, Buffer
, WRITE_UNCOOKED
) != NULL
;
1371 cmsBool CMSEXPORT
cmsIT8SetPropertyHex(cmsHANDLE hIT8
, const char* cProp
, cmsUInt32Number Val
)
1373 cmsIT8
* it8
= (cmsIT8
*) hIT8
;
1376 sprintf(Buffer
, "%u", Val
);
1378 return AddToList(it8
, &GetTable(it8
)->HeaderList
, cProp
, NULL
, Buffer
, WRITE_HEXADECIMAL
) != NULL
;
1381 cmsBool CMSEXPORT
cmsIT8SetPropertyUncooked(cmsHANDLE hIT8
, const char* Key
, const char* Buffer
)
1383 cmsIT8
* it8
= (cmsIT8
*) hIT8
;
1385 return AddToList(it8
, &GetTable(it8
)->HeaderList
, Key
, NULL
, Buffer
, WRITE_UNCOOKED
) != NULL
;
1388 cmsBool CMSEXPORT
cmsIT8SetPropertyMulti(cmsHANDLE hIT8
, const char* Key
, const char* SubKey
, const char *Buffer
)
1390 cmsIT8
* it8
= (cmsIT8
*) hIT8
;
1392 return AddToList(it8
, &GetTable(it8
)->HeaderList
, Key
, SubKey
, Buffer
, WRITE_PAIR
) != NULL
;
1396 const char* CMSEXPORT
cmsIT8GetProperty(cmsHANDLE hIT8
, const char* Key
)
1398 cmsIT8
* it8
= (cmsIT8
*) hIT8
;
1401 if (IsAvailableOnList(GetTable(it8
) -> HeaderList
, Key
, NULL
, &p
))
1409 cmsFloat64Number CMSEXPORT
cmsIT8GetPropertyDbl(cmsHANDLE hIT8
, const char* cProp
)
1411 const char *v
= cmsIT8GetProperty(hIT8
, cProp
);
1413 if (v
== NULL
) return 0.0;
1415 return ParseFloatNumber(v
);
1418 const char* CMSEXPORT
cmsIT8GetPropertyMulti(cmsHANDLE hIT8
, const char* Key
, const char *SubKey
)
1420 cmsIT8
* it8
= (cmsIT8
*) hIT8
;
1423 if (IsAvailableOnList(GetTable(it8
) -> HeaderList
, Key
, SubKey
, &p
)) {
1429 // ----------------------------------------------------------------- Datasets
1433 void AllocateDataFormat(cmsIT8
* it8
)
1435 TABLE
* t
= GetTable(it8
);
1437 if (t
-> DataFormat
) return; // Already allocated
1439 t
-> nSamples
= (int) cmsIT8GetPropertyDbl(it8
, "NUMBER_OF_FIELDS");
1441 if (t
-> nSamples
<= 0) {
1443 SynError(it8
, "AllocateDataFormat: Unknown NUMBER_OF_FIELDS");
1447 t
-> DataFormat
= (char**) AllocChunk (it8
, ((cmsUInt32Number
) t
->nSamples
+ 1) * sizeof(char *));
1448 if (t
->DataFormat
== NULL
) {
1450 SynError(it8
, "AllocateDataFormat: Unable to allocate dataFormat array");
1456 const char *GetDataFormat(cmsIT8
* it8
, int n
)
1458 TABLE
* t
= GetTable(it8
);
1461 return t
->DataFormat
[n
];
1467 cmsBool
SetDataFormat(cmsIT8
* it8
, int n
, const char *label
)
1469 TABLE
* t
= GetTable(it8
);
1472 AllocateDataFormat(it8
);
1474 if (n
> t
-> nSamples
) {
1475 SynError(it8
, "More than NUMBER_OF_FIELDS fields.");
1479 if (t
->DataFormat
) {
1480 t
->DataFormat
[n
] = AllocString(it8
, label
);
1487 cmsBool CMSEXPORT
cmsIT8SetDataFormat(cmsHANDLE h
, int n
, const char *Sample
)
1489 cmsIT8
* it8
= (cmsIT8
*) h
;
1490 return SetDataFormat(it8
, n
, Sample
);
1494 void AllocateDataSet(cmsIT8
* it8
)
1496 TABLE
* t
= GetTable(it8
);
1498 if (t
-> Data
) return; // Already allocated
1500 t
-> nSamples
= atoi(cmsIT8GetProperty(it8
, "NUMBER_OF_FIELDS"));
1501 t
-> nPatches
= atoi(cmsIT8GetProperty(it8
, "NUMBER_OF_SETS"));
1503 t
-> Data
= (char**)AllocChunk (it8
, ((cmsUInt32Number
) t
->nSamples
+ 1) * ((cmsUInt32Number
) t
->nPatches
+ 1) *sizeof (char*));
1504 if (t
->Data
== NULL
) {
1506 SynError(it8
, "AllocateDataSet: Unable to allocate data array");
1512 char* GetData(cmsIT8
* it8
, int nSet
, int nField
)
1514 TABLE
* t
= GetTable(it8
);
1515 int nSamples
= t
-> nSamples
;
1516 int nPatches
= t
-> nPatches
;
1518 if (nSet
>= nPatches
|| nField
>= nSamples
)
1521 if (!t
->Data
) return NULL
;
1522 return t
->Data
[nSet
* nSamples
+ nField
];
1526 cmsBool
SetData(cmsIT8
* it8
, int nSet
, int nField
, const char *Val
)
1528 TABLE
* t
= GetTable(it8
);
1531 AllocateDataSet(it8
);
1533 if (!t
->Data
) return FALSE
;
1535 if (nSet
> t
-> nPatches
|| nSet
< 0) {
1537 return SynError(it8
, "Patch %d out of range, there are %d patches", nSet
, t
-> nPatches
);
1540 if (nField
> t
->nSamples
|| nField
< 0) {
1541 return SynError(it8
, "Sample %d out of range, there are %d samples", nField
, t
->nSamples
);
1545 t
->Data
[nSet
* t
-> nSamples
+ nField
] = AllocString(it8
, Val
);
1550 // --------------------------------------------------------------- File I/O
1553 // Writes a string to file
1555 void WriteStr(SAVESTREAM
* f
, const char *str
)
1557 cmsUInt32Number len
;
1563 len
= (cmsUInt32Number
) strlen(str
);
1567 if (f
->stream
) { // Should I write it to a file?
1569 if (fwrite(str
, 1, len
, f
->stream
) != len
) {
1570 cmsSignalError(0, cmsERROR_WRITE
, "Write to file error in CGATS parser");
1575 else { // Or to a memory block?
1577 if (f
->Base
) { // Am I just counting the bytes?
1579 if (f
->Used
> f
->Max
) {
1581 cmsSignalError(0, cmsERROR_WRITE
, "Write to memory overflows in CGATS parser");
1585 memmove(f
->Ptr
, str
, len
);
1596 void Writef(SAVESTREAM
* f
, const char* frm
, ...)
1601 va_start(args
, frm
);
1602 vsnprintf(Buffer
, 4095, frm
, args
);
1604 WriteStr(f
, Buffer
);
1609 // Writes full header
1611 void WriteHeader(cmsIT8
* it8
, SAVESTREAM
* fp
)
1614 TABLE
* t
= GetTable(it8
);
1617 WriteStr(fp
, t
->SheetType
);
1620 for (p
= t
->HeaderList
; (p
!= NULL
); p
= p
->Next
)
1622 if (*p
->Keyword
== '#') {
1626 WriteStr(fp
, "#\n# ");
1627 for (Pt
= p
->Value
; *Pt
; Pt
++) {
1630 Writef(fp
, "%c", *Pt
);
1637 WriteStr(fp
, "\n#\n");
1642 if (!IsAvailableOnList(it8
-> ValidKeywords
, p
->Keyword
, NULL
, NULL
)) {
1644 #ifdef CMS_STRICT_CGATS
1645 WriteStr(fp
, "KEYWORD\t\"");
1646 WriteStr(fp
, p
->Keyword
);
1647 WriteStr(fp
, "\"\n");
1650 AddAvailableProperty(it8
, p
->Keyword
, WRITE_UNCOOKED
);
1653 WriteStr(fp
, p
->Keyword
);
1656 switch (p
->WriteAs
) {
1658 case WRITE_UNCOOKED
:
1659 Writef(fp
, "\t%s", p
->Value
);
1662 case WRITE_STRINGIFY
:
1663 Writef(fp
, "\t\"%s\"", p
->Value
);
1666 case WRITE_HEXADECIMAL
:
1667 Writef(fp
, "\t0x%X", atoi(p
->Value
));
1671 Writef(fp
, "\t0x%B", atoi(p
->Value
));
1675 Writef(fp
, "\t\"%s,%s\"", p
->Subkey
, p
->Value
);
1678 default: SynError(it8
, "Unknown write mode %d", p
->WriteAs
);
1683 WriteStr (fp
, "\n");
1689 // Writes the data format
1691 void WriteDataFormat(SAVESTREAM
* fp
, cmsIT8
* it8
)
1694 TABLE
* t
= GetTable(it8
);
1696 if (!t
-> DataFormat
) return;
1698 WriteStr(fp
, "BEGIN_DATA_FORMAT\n");
1700 nSamples
= atoi(cmsIT8GetProperty(it8
, "NUMBER_OF_FIELDS"));
1702 for (i
= 0; i
< nSamples
; i
++) {
1704 WriteStr(fp
, t
->DataFormat
[i
]);
1705 WriteStr(fp
, ((i
== (nSamples
-1)) ? "\n" : "\t"));
1708 WriteStr (fp
, "END_DATA_FORMAT\n");
1712 // Writes data array
1714 void WriteData(SAVESTREAM
* fp
, cmsIT8
* it8
)
1717 TABLE
* t
= GetTable(it8
);
1719 if (!t
->Data
) return;
1721 WriteStr (fp
, "BEGIN_DATA\n");
1723 t
->nPatches
= atoi(cmsIT8GetProperty(it8
, "NUMBER_OF_SETS"));
1725 for (i
= 0; i
< t
-> nPatches
; i
++) {
1729 for (j
= 0; j
< t
->nSamples
; j
++) {
1731 char *ptr
= t
->Data
[i
*t
->nSamples
+j
];
1733 if (ptr
== NULL
) WriteStr(fp
, "\"\"");
1735 // If value contains whitespace, enclose within quote
1737 if (strchr(ptr
, ' ') != NULL
) {
1747 WriteStr(fp
, ((j
== (t
->nSamples
-1)) ? "\n" : "\t"));
1750 WriteStr (fp
, "END_DATA\n");
1756 cmsBool CMSEXPORT
cmsIT8SaveToFile(cmsHANDLE hIT8
, const char* cFileName
)
1760 cmsIT8
* it8
= (cmsIT8
*) hIT8
;
1762 memset(&sd
, 0, sizeof(sd
));
1764 sd
.stream
= fopen(cFileName
, "wt");
1765 if (!sd
.stream
) return FALSE
;
1767 for (i
=0; i
< it8
->TablesCount
; i
++) {
1769 cmsIT8SetTable(hIT8
, i
);
1770 WriteHeader(it8
, &sd
);
1771 WriteDataFormat(&sd
, it8
);
1772 WriteData(&sd
, it8
);
1775 if (fclose(sd
.stream
) != 0) return FALSE
;
1782 cmsBool CMSEXPORT
cmsIT8SaveToMem(cmsHANDLE hIT8
, void *MemPtr
, cmsUInt32Number
* BytesNeeded
)
1786 cmsIT8
* it8
= (cmsIT8
*) hIT8
;
1788 memset(&sd
, 0, sizeof(sd
));
1791 sd
.Base
= (cmsUInt8Number
*) MemPtr
;
1797 sd
.Max
= *BytesNeeded
; // Write to memory?
1799 sd
.Max
= 0; // Just counting the needed bytes
1801 for (i
=0; i
< it8
->TablesCount
; i
++) {
1803 cmsIT8SetTable(hIT8
, i
);
1804 WriteHeader(it8
, &sd
);
1805 WriteDataFormat(&sd
, it8
);
1806 WriteData(&sd
, it8
);
1809 sd
.Used
++; // The \0 at the very end
1814 *BytesNeeded
= sd
.Used
;
1820 // -------------------------------------------------------------- Higer level parsing
1823 cmsBool
DataFormatSection(cmsIT8
* it8
)
1826 TABLE
* t
= GetTable(it8
);
1828 InSymbol(it8
); // Eats "BEGIN_DATA_FORMAT"
1831 while (it8
->sy
!= SEND_DATA_FORMAT
&&
1834 it8
->sy
!= SSYNERROR
) {
1836 if (it8
->sy
!= SIDENT
) {
1838 return SynError(it8
, "Sample type expected");
1841 if (!SetDataFormat(it8
, iField
, it8
->id
)) return FALSE
;
1849 Skip(it8
, SEND_DATA_FORMAT
);
1852 if (iField
!= t
->nSamples
) {
1853 SynError(it8
, "Count mismatch. NUMBER_OF_FIELDS was %d, found %d\n", t
->nSamples
, iField
);
1864 cmsBool
DataSection (cmsIT8
* it8
)
1869 TABLE
* t
= GetTable(it8
);
1871 InSymbol(it8
); // Eats "BEGIN_DATA"
1875 AllocateDataSet(it8
);
1877 while (it8
->sy
!= SEND_DATA
&& it8
->sy
!= SEOF
)
1879 if (iField
>= t
-> nSamples
) {
1885 if (it8
->sy
!= SEND_DATA
&& it8
->sy
!= SEOF
) {
1887 if (!GetVal(it8
, Buffer
, 255, "Sample data expected"))
1890 if (!SetData(it8
, iSet
, iField
, Buffer
))
1901 Skip(it8
, SEND_DATA
);
1904 // Check for data completion.
1906 if ((iSet
+1) != t
-> nPatches
)
1907 return SynError(it8
, "Count mismatch. NUMBER_OF_SETS was %d, found %d\n", t
->nPatches
, iSet
+1);
1916 cmsBool
HeaderSection(cmsIT8
* it8
)
1918 char VarName
[MAXID
];
1919 char Buffer
[MAXSTR
];
1922 while (it8
->sy
!= SEOF
&&
1923 it8
->sy
!= SSYNERROR
&&
1924 it8
->sy
!= SBEGIN_DATA_FORMAT
&&
1925 it8
->sy
!= SBEGIN_DATA
) {
1928 switch (it8
-> sy
) {
1932 if (!GetVal(it8
, Buffer
, MAXSTR
-1, "Keyword expected")) return FALSE
;
1933 if (!AddAvailableProperty(it8
, Buffer
, WRITE_UNCOOKED
)) return FALSE
;
1938 case SDATA_FORMAT_ID
:
1940 if (!GetVal(it8
, Buffer
, MAXSTR
-1, "Keyword expected")) return FALSE
;
1941 if (!AddAvailableSampleID(it8
, Buffer
)) return FALSE
;
1947 strncpy(VarName
, it8
->id
, MAXID
-1);
1948 VarName
[MAXID
-1] = 0;
1950 if (!IsAvailableOnList(it8
-> ValidKeywords
, VarName
, NULL
, &Key
)) {
1952 #ifdef CMS_STRICT_CGATS
1953 return SynError(it8
, "Undefined keyword '%s'", VarName
);
1955 Key
= AddAvailableProperty(it8
, VarName
, WRITE_UNCOOKED
);
1956 if (Key
== NULL
) return FALSE
;
1961 if (!GetVal(it8
, Buffer
, MAXSTR
-1, "Property data expected")) return FALSE
;
1963 if(Key
->WriteAs
!= WRITE_PAIR
) {
1964 AddToList(it8
, &GetTable(it8
)->HeaderList
, VarName
, NULL
, Buffer
,
1965 (it8
->sy
== SSTRING
) ? WRITE_STRINGIFY
: WRITE_UNCOOKED
);
1970 if (it8
->sy
!= SSTRING
)
1971 return SynError(it8
, "Invalid value '%s' for property '%s'.", Buffer
, VarName
);
1973 // chop the string as a list of "subkey, value" pairs, using ';' as a separator
1974 for (Subkey
= Buffer
; Subkey
!= NULL
; Subkey
= Nextkey
)
1978 // identify token pair boundary
1979 Nextkey
= (char*) strchr(Subkey
, ';');
1983 // for each pair, split the subkey and the value
1984 Value
= (char*) strrchr(Subkey
, ',');
1986 return SynError(it8
, "Invalid value for property '%s'.", VarName
);
1988 // gobble the spaces before the coma, and the coma itself
1990 do *temp
-- = '\0'; while(temp
>= Subkey
&& *temp
== ' ');
1992 // gobble any space at the right
1993 temp
= Value
+ strlen(Value
) - 1;
1994 while(*temp
== ' ') *temp
-- = '\0';
1996 // trim the strings from the left
1997 Subkey
+= strspn(Subkey
, " ");
1998 Value
+= strspn(Value
, " ");
2000 if(Subkey
[0] == 0 || Value
[0] == 0)
2001 return SynError(it8
, "Invalid value for property '%s'.", VarName
);
2002 AddToList(it8
, &GetTable(it8
)->HeaderList
, VarName
, Subkey
, Value
, WRITE_PAIR
);
2013 return SynError(it8
, "expected keyword or identifier");
2025 void ReadType(cmsIT8
* it8
, char* SheetTypePtr
)
2027 // First line is a very special case.
2029 while (isseparator(it8
->ch
))
2032 while (it8
->ch
!= '\r' && it8
->ch
!= '\n' && it8
->ch
!= '\t' && it8
-> ch
!= -1) {
2034 *SheetTypePtr
++= (char) it8
->ch
;
2043 cmsBool
ParseIT8(cmsIT8
* it8
, cmsBool nosheet
)
2045 char* SheetTypePtr
= it8
->Tab
[0].SheetType
;
2048 ReadType(it8
, SheetTypePtr
);
2055 while (it8
-> sy
!= SEOF
&&
2056 it8
-> sy
!= SSYNERROR
) {
2058 switch (it8
-> sy
) {
2060 case SBEGIN_DATA_FORMAT
:
2061 if (!DataFormatSection(it8
)) return FALSE
;
2066 if (!DataSection(it8
)) return FALSE
;
2068 if (it8
-> sy
!= SEOF
) {
2071 it8
->nTable
= it8
->TablesCount
- 1;
2073 // Read sheet type if present. We only support identifier and string.
2074 // <ident> <eoln> is a type string
2075 // anything else, is not a type string
2078 if (it8
->sy
== SIDENT
) {
2080 // May be a type sheet or may be a prop value statement. We cannot use insymbol in
2081 // this special case...
2082 while (isseparator(it8
->ch
))
2085 // If a newline is found, then this is a type string
2086 if (it8
->ch
== '\n' || it8
->ch
== '\r') {
2088 cmsIT8SetSheetType(it8
, it8
->id
);
2093 // It is not. Just continue
2094 cmsIT8SetSheetType(it8
, "");
2098 // Validate quoted strings
2099 if (it8
->sy
== SSTRING
) {
2100 cmsIT8SetSheetType(it8
, it8
->str
);
2113 if (!HeaderSection(it8
)) return FALSE
;
2118 return (it8
-> sy
!= SSYNERROR
);
2123 // Init usefull pointers
2126 void CookPointers(cmsIT8
* it8
)
2131 cmsUInt32Number nOldTable
= it8
->nTable
;
2133 for (j
=0; j
< it8
->TablesCount
; j
++) {
2135 TABLE
* t
= it8
->Tab
+ j
;
2140 for (idField
= 0; idField
< t
-> nSamples
; idField
++)
2142 if (t
->DataFormat
== NULL
){
2143 SynError(it8
, "Undefined DATA_FORMAT");
2147 Fld
= t
->DataFormat
[idField
];
2151 if (cmsstrcasecmp(Fld
, "SAMPLE_ID") == 0) {
2153 t
-> SampleID
= idField
;
2155 for (i
=0; i
< t
-> nPatches
; i
++) {
2157 char *Data
= GetData(it8
, i
, idField
);
2161 strncpy(Buffer
, Data
, 255);
2164 if (strlen(Buffer
) <= strlen(Data
))
2165 strcpy(Data
, Buffer
);
2167 SetData(it8
, i
, idField
, Buffer
);
2174 // "LABEL" is an extension. It keeps references to forward tables
2176 if ((cmsstrcasecmp(Fld
, "LABEL") == 0) || Fld
[0] == '$' ) {
2178 // Search for table references...
2179 for (i
=0; i
< t
-> nPatches
; i
++) {
2181 char *Label
= GetData(it8
, i
, idField
);
2187 // This is the label, search for a table containing
2190 for (k
=0; k
< it8
->TablesCount
; k
++) {
2192 TABLE
* Table
= it8
->Tab
+ k
;
2195 if (IsAvailableOnList(Table
->HeaderList
, Label
, NULL
, &p
)) {
2197 // Available, keep type and table
2200 char *Type
= p
->Value
;
2201 int nTable
= (int) k
;
2203 snprintf(Buffer
, 255, "%s %d %s", Label
, nTable
, Type
);
2205 SetData(it8
, i
, idField
, Buffer
);
2220 it8
->nTable
= nOldTable
;
2223 // Try to infere if the file is a CGATS/IT8 file at all. Read first line
2224 // that should be something like some printable characters plus a \n
2225 // returns 0 if this is not like a CGATS, or an integer otherwise. This integer is the number of words in first line?
2227 int IsMyBlock(cmsUInt8Number
* Buffer
, int n
)
2229 int words
= 1, space
= 0, quot
= 0;
2232 if (n
< 10) return 0; // Too small
2237 for (i
= 1; i
< n
; i
++) {
2243 return ((quot
== 1) || (words
> 2)) ? 0 : words
;
2253 if (Buffer
[i
] < 32) return 0;
2254 if (Buffer
[i
] > 127) return 0;
2266 cmsBool
IsMyFile(const char* FileName
)
2269 cmsUInt32Number Size
;
2270 cmsUInt8Number Ptr
[133];
2272 fp
= fopen(FileName
, "rt");
2274 cmsSignalError(0, cmsERROR_FILE
, "File '%s' not found", FileName
);
2278 Size
= (cmsUInt32Number
) fread(Ptr
, 1, 132, fp
);
2280 if (fclose(fp
) != 0)
2285 return IsMyBlock(Ptr
, Size
);
2288 // ---------------------------------------------------------- Exported routines
2291 cmsHANDLE CMSEXPORT
cmsIT8LoadFromMem(cmsContext ContextID
, void *Ptr
, cmsUInt32Number len
)
2297 _cmsAssert(Ptr
!= NULL
);
2298 _cmsAssert(len
!= 0);
2300 type
= IsMyBlock((cmsUInt8Number
*)Ptr
, len
);
2301 if (type
== 0) return NULL
;
2303 hIT8
= cmsIT8Alloc(ContextID
);
2304 if (!hIT8
) return NULL
;
2306 it8
= (cmsIT8
*) hIT8
;
2307 it8
->MemoryBlock
= (char*) _cmsMalloc(ContextID
, len
+ 1);
2309 strncpy(it8
->MemoryBlock
, (const char*) Ptr
, len
);
2310 it8
->MemoryBlock
[len
] = 0;
2312 strncpy(it8
->FileStack
[0]->FileName
, "", cmsMAX_PATH
-1);
2313 it8
-> Source
= it8
-> MemoryBlock
;
2315 if (!ParseIT8(it8
, type
-1)) {
2324 _cmsFree(ContextID
, it8
->MemoryBlock
);
2325 it8
-> MemoryBlock
= NULL
;
2333 cmsHANDLE CMSEXPORT
cmsIT8LoadFromFile(cmsContext ContextID
, const char* cFileName
)
2340 _cmsAssert(cFileName
!= NULL
);
2342 type
= IsMyFile(cFileName
);
2343 if (type
== 0) return NULL
;
2345 hIT8
= cmsIT8Alloc(ContextID
);
2346 it8
= (cmsIT8
*) hIT8
;
2347 if (!hIT8
) return NULL
;
2350 it8
->FileStack
[0]->Stream
= fopen(cFileName
, "rt");
2352 if (!it8
->FileStack
[0]->Stream
) {
2358 strncpy(it8
->FileStack
[0]->FileName
, cFileName
, cmsMAX_PATH
-1);
2359 it8
->FileStack
[0]->FileName
[cmsMAX_PATH
-1] = 0;
2361 if (!ParseIT8(it8
, type
-1)) {
2363 fclose(it8
->FileStack
[0]->Stream
);
2371 if (fclose(it8
->FileStack
[0]->Stream
)!= 0) {
2380 int CMSEXPORT
cmsIT8EnumDataFormat(cmsHANDLE hIT8
, char ***SampleNames
)
2382 cmsIT8
* it8
= (cmsIT8
*) hIT8
;
2385 _cmsAssert(hIT8
!= NULL
);
2390 *SampleNames
= t
-> DataFormat
;
2391 return t
-> nSamples
;
2395 cmsUInt32Number CMSEXPORT
cmsIT8EnumProperties(cmsHANDLE hIT8
, char ***PropertyNames
)
2397 cmsIT8
* it8
= (cmsIT8
*) hIT8
;
2403 _cmsAssert(hIT8
!= NULL
);
2407 // Pass#1 - count properties
2410 for (p
= t
-> HeaderList
; p
!= NULL
; p
= p
->Next
) {
2415 Props
= (char **) AllocChunk(it8
, sizeof(char *) * n
);
2417 // Pass#2 - Fill pointers
2419 for (p
= t
-> HeaderList
; p
!= NULL
; p
= p
->Next
) {
2420 Props
[n
++] = p
-> Keyword
;
2423 *PropertyNames
= Props
;
2427 cmsUInt32Number CMSEXPORT
cmsIT8EnumPropertyMulti(cmsHANDLE hIT8
, const char* cProp
, const char ***SubpropertyNames
)
2429 cmsIT8
* it8
= (cmsIT8
*) hIT8
;
2435 _cmsAssert(hIT8
!= NULL
);
2440 if(!IsAvailableOnList(t
->HeaderList
, cProp
, NULL
, &p
)) {
2441 *SubpropertyNames
= 0;
2445 // Pass#1 - count properties
2448 for (tmp
= p
; tmp
!= NULL
; tmp
= tmp
->NextSubkey
) {
2449 if(tmp
->Subkey
!= NULL
)
2454 Props
= (const char **) AllocChunk(it8
, sizeof(char *) * n
);
2456 // Pass#2 - Fill pointers
2458 for (tmp
= p
; tmp
!= NULL
; tmp
= tmp
->NextSubkey
) {
2459 if(tmp
->Subkey
!= NULL
)
2460 Props
[n
++] = p
->Subkey
;
2463 *SubpropertyNames
= Props
;
2468 int LocatePatch(cmsIT8
* it8
, const char* cPatch
)
2472 TABLE
* t
= GetTable(it8
);
2474 for (i
=0; i
< t
-> nPatches
; i
++) {
2476 data
= GetData(it8
, i
, t
->SampleID
);
2480 if (cmsstrcasecmp(data
, cPatch
) == 0)
2485 // SynError(it8, "Couldn't find patch '%s'\n", cPatch);
2491 int LocateEmptyPatch(cmsIT8
* it8
)
2495 TABLE
* t
= GetTable(it8
);
2497 for (i
=0; i
< t
-> nPatches
; i
++) {
2499 data
= GetData(it8
, i
, t
->SampleID
);
2510 int LocateSample(cmsIT8
* it8
, const char* cSample
)
2514 TABLE
* t
= GetTable(it8
);
2516 for (i
=0; i
< t
->nSamples
; i
++) {
2518 fld
= GetDataFormat(it8
, i
);
2519 if (cmsstrcasecmp(fld
, cSample
) == 0)
2528 int CMSEXPORT
cmsIT8FindDataFormat(cmsHANDLE hIT8
, const char* cSample
)
2530 cmsIT8
* it8
= (cmsIT8
*) hIT8
;
2532 _cmsAssert(hIT8
!= NULL
);
2534 return LocateSample(it8
, cSample
);
2539 const char* CMSEXPORT
cmsIT8GetDataRowCol(cmsHANDLE hIT8
, int row
, int col
)
2541 cmsIT8
* it8
= (cmsIT8
*) hIT8
;
2543 _cmsAssert(hIT8
!= NULL
);
2545 return GetData(it8
, row
, col
);
2549 cmsFloat64Number CMSEXPORT
cmsIT8GetDataRowColDbl(cmsHANDLE hIT8
, int row
, int col
)
2553 Buffer
= cmsIT8GetDataRowCol(hIT8
, row
, col
);
2555 if (Buffer
== NULL
) return 0.0;
2557 return ParseFloatNumber(Buffer
);
2561 cmsBool CMSEXPORT
cmsIT8SetDataRowCol(cmsHANDLE hIT8
, int row
, int col
, const char* Val
)
2563 cmsIT8
* it8
= (cmsIT8
*) hIT8
;
2565 _cmsAssert(hIT8
!= NULL
);
2567 return SetData(it8
, row
, col
, Val
);
2571 cmsBool CMSEXPORT
cmsIT8SetDataRowColDbl(cmsHANDLE hIT8
, int row
, int col
, cmsFloat64Number Val
)
2573 cmsIT8
* it8
= (cmsIT8
*) hIT8
;
2576 _cmsAssert(hIT8
!= NULL
);
2578 sprintf(Buff
, it8
->DoubleFormatter
, Val
);
2580 return SetData(it8
, row
, col
, Buff
);
2585 const char* CMSEXPORT
cmsIT8GetData(cmsHANDLE hIT8
, const char* cPatch
, const char* cSample
)
2587 cmsIT8
* it8
= (cmsIT8
*) hIT8
;
2590 _cmsAssert(hIT8
!= NULL
);
2592 iField
= LocateSample(it8
, cSample
);
2597 iSet
= LocatePatch(it8
, cPatch
);
2602 return GetData(it8
, iSet
, iField
);
2606 cmsFloat64Number CMSEXPORT
cmsIT8GetDataDbl(cmsHANDLE it8
, const char* cPatch
, const char* cSample
)
2610 Buffer
= cmsIT8GetData(it8
, cPatch
, cSample
);
2612 return ParseFloatNumber(Buffer
);
2617 cmsBool CMSEXPORT
cmsIT8SetData(cmsHANDLE hIT8
, const char* cPatch
, const char* cSample
, const char *Val
)
2619 cmsIT8
* it8
= (cmsIT8
*) hIT8
;
2623 _cmsAssert(hIT8
!= NULL
);
2627 iField
= LocateSample(it8
, cSample
);
2632 if (t
-> nPatches
== 0) {
2634 AllocateDataFormat(it8
);
2635 AllocateDataSet(it8
);
2639 if (cmsstrcasecmp(cSample
, "SAMPLE_ID") == 0) {
2641 iSet
= LocateEmptyPatch(it8
);
2643 return SynError(it8
, "Couldn't add more patches '%s'\n", cPatch
);
2646 iField
= t
-> SampleID
;
2649 iSet
= LocatePatch(it8
, cPatch
);
2655 return SetData(it8
, iSet
, iField
, Val
);
2659 cmsBool CMSEXPORT
cmsIT8SetDataDbl(cmsHANDLE hIT8
, const char* cPatch
,
2660 const char* cSample
,
2661 cmsFloat64Number Val
)
2663 cmsIT8
* it8
= (cmsIT8
*) hIT8
;
2666 _cmsAssert(hIT8
!= NULL
);
2668 snprintf(Buff
, 255, it8
->DoubleFormatter
, Val
);
2669 return cmsIT8SetData(hIT8
, cPatch
, cSample
, Buff
);
2672 // Buffer should get MAXSTR at least
2674 const char* CMSEXPORT
cmsIT8GetPatchName(cmsHANDLE hIT8
, int nPatch
, char* buffer
)
2676 cmsIT8
* it8
= (cmsIT8
*) hIT8
;
2680 _cmsAssert(hIT8
!= NULL
);
2683 Data
= GetData(it8
, nPatch
, t
->SampleID
);
2685 if (!Data
) return NULL
;
2686 if (!buffer
) return Data
;
2688 strncpy(buffer
, Data
, MAXSTR
-1);
2689 buffer
[MAXSTR
-1] = 0;
2693 int CMSEXPORT
cmsIT8GetPatchByName(cmsHANDLE hIT8
, const char *cPatch
)
2695 _cmsAssert(hIT8
!= NULL
);
2697 return LocatePatch((cmsIT8
*)hIT8
, cPatch
);
2700 cmsUInt32Number CMSEXPORT
cmsIT8TableCount(cmsHANDLE hIT8
)
2702 cmsIT8
* it8
= (cmsIT8
*) hIT8
;
2704 _cmsAssert(hIT8
!= NULL
);
2706 return it8
->TablesCount
;
2709 // This handles the "LABEL" extension.
2710 // Label, nTable, Type
2712 int CMSEXPORT
cmsIT8SetTableByLabel(cmsHANDLE hIT8
, const char* cSet
, const char* cField
, const char* ExpectedType
)
2714 const char* cLabelFld
;
2715 char Type
[256], Label
[256];
2718 _cmsAssert(hIT8
!= NULL
);
2720 if (cField
!= NULL
&& *cField
== 0)
2726 cLabelFld
= cmsIT8GetData(hIT8
, cSet
, cField
);
2727 if (!cLabelFld
) return -1;
2729 if (sscanf(cLabelFld
, "%255s %d %255s", Label
, &nTable
, Type
) != 3)
2732 if (ExpectedType
!= NULL
&& *ExpectedType
== 0)
2733 ExpectedType
= NULL
;
2737 if (cmsstrcasecmp(Type
, ExpectedType
) != 0) return -1;
2740 return cmsIT8SetTable(hIT8
, nTable
);
2744 cmsBool CMSEXPORT
cmsIT8SetIndexColumn(cmsHANDLE hIT8
, const char* cSample
)
2746 cmsIT8
* it8
= (cmsIT8
*) hIT8
;
2749 _cmsAssert(hIT8
!= NULL
);
2751 pos
= LocateSample(it8
, cSample
);
2755 it8
->Tab
[it8
->nTable
].SampleID
= pos
;
2760 void CMSEXPORT
cmsIT8DefineDblFormat(cmsHANDLE hIT8
, const char* Formatter
)
2762 cmsIT8
* it8
= (cmsIT8
*) hIT8
;
2764 _cmsAssert(hIT8
!= NULL
);
2766 if (Formatter
== NULL
)
2767 strcpy(it8
->DoubleFormatter
, DEFAULT_DBL_FORMAT
);
2769 strncpy(it8
->DoubleFormatter
, Formatter
, sizeof(it8
->DoubleFormatter
));
2771 it8
->DoubleFormatter
[sizeof(it8
->DoubleFormatter
)-1] = 0;