3 // Copyright (C) 1998-2007 Marti Maria
5 // Permission is hereby granted, free of charge, to any person obtaining
6 // a copy of this software and associated documentation files (the "Software"),
7 // to deal in the Software without restriction, including without limitation
8 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 // and/or sell copies of the Software, and to permit persons to whom the Software
10 // is furnished to do so, subject to the following conditions:
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
17 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 // IT8.7 / CGATS.17-200x handling
28 LCMSAPI LCMSHANDLE LCMSEXPORT
cmsIT8Alloc(void);
29 LCMSAPI
void LCMSEXPORT
cmsIT8Free(LCMSHANDLE IT8
);
33 LCMSAPI
int LCMSEXPORT
cmsIT8TableCount(LCMSHANDLE IT8
);
34 LCMSAPI
int LCMSEXPORT
cmsIT8SetTable(LCMSHANDLE IT8
, int nTable
);
37 LCMSAPI LCMSHANDLE LCMSEXPORT
cmsIT8LoadFromFile(const char* cFileName
);
38 LCMSAPI LCMSHANDLE LCMSEXPORT
cmsIT8LoadFromMem(void *Ptr
, size_t len
);
39 LCMSAPI LCMSBOOL LCMSEXPORT
cmsIT8SaveToFile(LCMSHANDLE IT8
, const char* cFileName
);
42 LCMSAPI
const char* LCMSEXPORT
cmsIT8GetSheetType(LCMSHANDLE hIT8
);
43 LCMSAPI LCMSBOOL LCMSEXPORT
cmsIT8SetSheetType(LCMSHANDLE hIT8
, const char* Type
);
45 LCMSAPI LCMSBOOL LCMSEXPORT
cmsIT8SetComment(LCMSHANDLE hIT8
, const char* cComment
);
47 LCMSAPI LCMSBOOL LCMSEXPORT
cmsIT8SetPropertyStr(LCMSHANDLE hIT8
, const char* cProp
, const char *Str
);
48 LCMSAPI LCMSBOOL LCMSEXPORT
cmsIT8SetPropertyDbl(LCMSHANDLE hIT8
, const char* cProp
, double Val
);
49 LCMSAPI LCMSBOOL LCMSEXPORT
cmsIT8SetPropertyHex(LCMSHANDLE hIT8
, const char* cProp
, int Val
);
50 LCMSAPI LCMSBOOL LCMSEXPORT
cmsIT8SetPropertyUncooked(LCMSHANDLE hIT8
, const char* Key
, const char* Buffer
);
52 LCMSAPI
const char* LCMSEXPORT
cmsIT8GetProperty(LCMSHANDLE hIT8
, const char* cProp
);
53 LCMSAPI
double LCMSEXPORT
cmsIT8GetPropertyDbl(LCMSHANDLE hIT8
, const char* cProp
);
54 LCMSAPI
int LCMSEXPORT
cmsIT8EnumProperties(LCMSHANDLE IT8
, char ***PropertyNames
);
58 LCMSAPI
const char* LCMSEXPORT
cmsIT8GetPatchName(LCMSHANDLE hIT8
, int nPatch
, char* buffer
);
60 LCMSAPI
const char* LCMSEXPORT
cmsIT8GetDataRowCol(LCMSHANDLE IT8
, int row
, int col
);
61 LCMSAPI
double LCMSEXPORT
cmsIT8GetDataRowColDbl(LCMSHANDLE IT8
, int col
, int row
);
63 LCMSAPI LCMSBOOL LCMSEXPORT
cmsIT8SetDataRowCol(LCMSHANDLE hIT8
, int row
, int col
,
66 LCMSAPI LCMSBOOL LCMSEXPORT
cmsIT8SetDataRowColDbl(LCMSHANDLE hIT8
, int row
, int col
,
69 LCMSAPI
const char* LCMSEXPORT
cmsIT8GetData(LCMSHANDLE IT8
, const char* cPatch
, const char* cSample
);
72 LCMSAPI
double LCMSEXPORT
cmsIT8GetDataDbl(LCMSHANDLE IT8
, const char* cPatch
, const char* cSample
);
74 LCMSAPI LCMSBOOL LCMSEXPORT
cmsIT8SetData(LCMSHANDLE IT8
, const char* cPatch
,
78 LCMSAPI LCMSBOOL LCMSEXPORT
cmsIT8SetDataDbl(LCMSHANDLE hIT8
, const char* cPatch
,
82 LCMSAPI LCMSBOOL LCMSEXPORT
cmsIT8SetDataFormat(LCMSHANDLE IT8
, int n
, const char *Sample
);
83 LCMSAPI
int LCMSEXPORT
cmsIT8EnumDataFormat(LCMSHANDLE IT8
, char ***SampleNames
);
85 LCMSAPI
void LCMSEXPORT
cmsIT8DefineDblFormat(LCMSHANDLE IT8
, const char* Formatter
);
87 LCMSAPI
int LCMSEXPORT
cmsIT8SetTableByLabel(LCMSHANDLE hIT8
, const char* cSet
,
89 const char* ExpectedType
);
91 // ------------------------------------------------------------- Implementation
94 #define SIZEOFLONGMINUS1 (sizeof(long)-1)
95 #define ALIGNLONG(x) (((x)+SIZEOFLONGMINUS1) & ~(SIZEOFLONGMINUS1))
97 // #define STRICT_CGATS 1
99 #define MAXID 128 // Max lenght of identifier
100 #define MAXSTR 255 // Max lenght of string
101 #define MAXTABLES 255 // Max Number of tables in a single stream
102 #define MAXINCLUDE 20 // Max number of nested includes
104 #define DEFAULT_DBL_FORMAT "%.10g" // Double formatting
111 #define DIR_CHAR '\\'
123 SIDENT
, // Identifier
126 SEOLN
, // End of line
127 SEOF
, // End of stream
128 SSYNERROR
, // Syntax error found on stream
142 // How to write the value
152 // Linked list of variable names
154 typedef struct _KeyVal
{
156 struct _KeyVal
* Next
;
157 char* Keyword
; // Name of variable
158 char* Value
; // Points to value
159 WRITEMODE WriteAs
; // How to write the value
161 } KEYVALUE
, *LPKEYVALUE
;
164 // Linked list of memory chunks (Memory sink)
166 typedef struct _OwnedMem
{
168 struct _OwnedMem
* Next
;
169 void * Ptr
; // Point to value
171 } OWNEDMEM
, *LPOWNEDMEM
;
175 typedef struct _SubAllocator
{
181 } SUBALLOCATOR
, *LPSUBALLOCATOR
;
183 // Table. Each individual table can hold properties and rows & cols
185 typedef struct _Table
{
187 int nSamples
, nPatches
; // Cols, Rows
188 int SampleID
; // Pos of ID
190 LPKEYVALUE HeaderList
; // The properties
192 char** DataFormat
; // The binary stream descriptor
193 char** Data
; // The binary stream
197 // File stream being parsed
199 typedef struct _FileContext
{
200 char FileName
[MAX_PATH
]; // File name if being readed from file
201 FILE* Stream
; // File stream or NULL if holded in memory
202 } FILECTX
, *LPFILECTX
;
204 // This struct hold all information about an openened
205 // IT8 handler. Only one dataset is allowed.
209 char SheetType
[MAXSTR
];
211 int TablesCount
; // How many tables in this stream
212 int nTable
; // The actual table
214 TABLE Tab
[MAXTABLES
];
218 LPOWNEDMEM MemorySink
; // The storage backend
219 SUBALLOCATOR Allocator
; // String suballocator -- just to keep it fast
221 // Parser state machine
223 SYMBOL sy
; // Current symbol
224 int ch
; // Current character
226 int inum
; // integer value
227 double dnum
; // real value
228 char id
[MAXID
]; // identifier
229 char str
[MAXSTR
]; // string
231 // Allowed keywords & datasets. They have visibility on whole stream
233 LPKEYVALUE ValidKeywords
;
234 LPKEYVALUE ValidSampleID
;
236 char* Source
; // Points to loc. being parsed
237 int lineno
; // line counter for error reporting
239 LPFILECTX FileStack
[MAXINCLUDE
]; // Stack of files being parsed
240 int IncludeSP
; // Include Stack Pointer
242 char* MemoryBlock
; // The stream if holded in memory
244 char DoubleFormatter
[MAXID
]; // Printf-like 'double' formatter
252 FILE* stream
; // For save-to-file behaviour
255 LPBYTE Ptr
; // For save-to-mem behaviour
259 } SAVESTREAM
, FAR
* LPSAVESTREAM
;
262 // ------------------------------------------------------ IT8 parsing routines
273 // The keyword->symbol translation table. Sorting is required.
274 static const KEYWORD TabKeys
[] = {
276 {"$INCLUDE", SINCLUDE
},
277 {".INCLUDE", SINCLUDE
},
278 {"BEGIN_DATA", SBEGIN_DATA
},
279 {"BEGIN_DATA_FORMAT", SBEGIN_DATA_FORMAT
},
280 {"END_DATA", SEND_DATA
},
281 {"END_DATA_FORMAT", SEND_DATA_FORMAT
},
282 {"KEYWORD", SKEYWORD
}
286 #define NUMKEYS (sizeof(TabKeys)/sizeof(KEYWORD))
288 // Predefined properties
290 static const char* PredefinedProperties
[] = {
292 "NUMBER_OF_FIELDS", // Required - NUMBER OF FIELDS
293 "NUMBER_OF_SETS", // Required - NUMBER OF SETS
294 "ORIGINATOR", // Required - Identifies the specific system, organization or individual that created the data file.
295 "FILE_DESCRIPTOR", // Required - Describes the purpose or contents of the data file.
296 "CREATED", // Required - Indicates date of creation of the data file.
297 "DESCRIPTOR", // Required - Describes the purpose or contents of the data file.
298 "DIFFUSE_GEOMETRY", // The diffuse geometry used. Allowed values are "sphere" or "opal".
300 "MANUFACTURE", // Some broken Fuji targets does store this value
301 "PROD_DATE", // Identifies year and month of production of the target in the form yyyy:mm.
302 "SERIAL", // Uniquely identifies individual physical target.
304 "MATERIAL", // Identifies the material on which the target was produced using a code
305 // uniquely identifying th e material. This is intend ed to be used for IT8.7
306 // physical targets only (i.e . IT8.7/1 a nd IT8.7/2).
308 "INSTRUMENTATION", // Used to report the specific instrumentation used (manufacturer and
309 // model number) to generate the data reported. This data will often
310 // provide more information about the particular data collected than an
311 // extensive list of specific details. This is particularly important for
312 // spectral data or data derived from spectrophotometry.
314 "MEASUREMENT_SOURCE", // Illumination used for spectral measurements. This data helps provide
315 // a guide to the potential for issues of paper fluorescence, etc.
317 "PRINT_CONDITIONS", // Used to define the characteristics of the printed sheet being reported.
318 // Where standard conditions have been defined (e.g., SWOP at nominal)
319 // named conditions may suffice. Otherwise, detailed information is
322 "SAMPLE_BACKING", // Identifies the backing material used behind the sample during
323 // measurement. Allowed values are “black”, “white”, or "na".
325 "CHISQ_DOF" // Degrees of freedom associated with the Chi squared statistic
328 #define NUMPREDEFINEDPROPS (sizeof(PredefinedProperties)/sizeof(char *))
331 // Predefined sample types on dataset
332 static const char* PredefinedSampleID
[] = {
334 "CMYK_C", // Cyan component of CMYK data expressed as a percentage
335 "CMYK_M", // Magenta component of CMYK data expressed as a percentage
336 "CMYK_Y", // Yellow component of CMYK data expressed as a percentage
337 "CMYK_K", // Black component of CMYK data expressed as a percentage
338 "D_RED", // Red filter density
339 "D_GREEN", // Green filter density
340 "D_BLUE", // Blue filter density
341 "D_VIS", // Visual filter density
342 "D_MAJOR_FILTER", // Major filter d ensity
343 "RGB_R", // Red component of RGB data
344 "RGB_G", // Green component of RGB data
345 "RGB_B", // Blue com ponent of RGB data
346 "SPECTRAL_NM", // Wavelength of measurement expressed in nanometers
347 "SPECTRAL_PCT", // Percentage reflectance/transmittance
348 "SPECTRAL_DEC", // Reflectance/transmittance
349 "XYZ_X", // X component of tristimulus data
350 "XYZ_Y", // Y component of tristimulus data
351 "XYZ_Z", // Z component of tristimulus data
352 "XYY_X" // x component of chromaticity data
353 "XYY_Y", // y component of chromaticity data
354 "XYY_CAPY", // Y component of tristimulus data
355 "LAB_L", // L* component of Lab data
356 "LAB_A", // a* component of Lab data
357 "LAB_B", // b* component of Lab data
358 "LAB_C", // C*ab component of Lab data
359 "LAB_H", // hab component of Lab data
361 "LAB_DE_94", // CIE dE using CIE 94
362 "LAB_DE_CMC", // dE using CMC
363 "LAB_DE_2000", // CIE dE using CIE DE 2000
364 "MEAN_DE", // Mean Delta E (LAB_DE) of samples compared to batch average
365 // (Used for data files for ANSI IT8.7/1 and IT8.7/2 targets)
366 "STDEV_X", // Standard deviation of X (tristimulus data)
367 "STDEV_Y", // Standard deviation of Y (tristimulus data)
368 "STDEV_Z", // Standard deviation of Z (tristimulus data)
369 "STDEV_L", // Standard deviation of L*
370 "STDEV_A" // Standard deviation of a*
371 "STDEV_B", // Standard deviation of b*
372 "STDEV_DE", // Standard deviation of CIE dE
373 "CHI_SQD_PAR"}; // The average of the standard deviations of L*, a* and b*. It is
374 // used to derive an estimate of the chi-squared parameter which is
375 // recommended as the predictor of the variability of dE
377 #define NUMPREDEFINEDSAMPLEID (sizeof(PredefinedSampleID)/sizeof(char *))
379 //Forward declaration of some internal functions
380 static void* AllocChunk(LPIT8 it8
, size_t size
);
382 // Checks if c is a separator
384 LCMSBOOL
isseparator(int c
)
386 return (c
== ' ') || (c
== '\t') || (c
== '\r');
389 // Checks whatever if c is a valid identifier char
391 LCMSBOOL
ismiddle(int c
)
393 return (!isseparator(c
) && (c
!= '#') && (c
!='\"') && (c
!= '\'') && (c
> 32) && (c
< 127));
396 // Checks whatsever if c is a valid identifier middle char.
398 LCMSBOOL
isidchar(int c
)
400 return isalnum(c
) || ismiddle(c
);
403 // Checks whatsever if c is a valid identifier first char.
405 LCMSBOOL
isfirstidchar(int c
)
407 return !isdigit(c
) && ismiddle(c
);
410 // checks whether the supplied path looks like an absolute path
411 // NOTE: this function doesn't checks if the path exists or even if it's legal
413 LCMSBOOL
isabsolutepath(const char *path
)
418 if(path
[0] == DIR_CHAR
)
422 if(isalpha(path
[0]) && path
[1] == ':')
428 // Makes a file path based on a given reference path
429 // NOTE: buffer is assumed to point to at least MAX_PATH bytes
430 // NOTE: both relPath and basePath are assumed to be no more than MAX_PATH characters long (including the null terminator!)
431 // NOTE: this function doesn't check if the path exists or even if it's legal
433 LCMSBOOL
_cmsMakePath(const char *relPath
, const char *basePath
, char *buffer
)
435 if (!isabsolutepath(relPath
)) {
439 strncpy(buffer
, basePath
, MAX_PATH
-1);
440 tail
= strrchr(buffer
, DIR_CHAR
);
443 size_t len
= tail
- buffer
;
444 strncpy(tail
+ 1, relPath
, MAX_PATH
- len
-1);
445 // TODO: if combined path is longer than MAX_PATH, this should return FALSE!
449 strncpy(buffer
, relPath
, MAX_PATH
- 1);
454 // Make sure no exploit is being even tried
457 const char* NoMeta(const char* str
)
459 if (strchr(str
, '%') != NULL
)
460 return "**** CORRUPTED FORMAT STRING ***";
468 LCMSBOOL
SynError(LPIT8 it8
, const char *Txt
, ...)
470 char Buffer
[256], ErrMsg
[1024];
474 vsnprintf(Buffer
, 255, Txt
, args
);
478 snprintf(ErrMsg
, 1023, "%s: Line %d, %s", it8
->FileStack
[it8
->IncludeSP
]->FileName
, it8
->lineno
, Buffer
);
481 cmsSignalError(LCMS_ERRC_ABORTED
, "%s", ErrMsg
);
485 // Check if current symbol is same as specified. issue an error else.
487 LCMSBOOL
Check(LPIT8 it8
, SYMBOL sy
, const char* Err
)
490 return SynError(it8
, NoMeta(Err
));
496 // Read Next character from stream
498 void NextCh(LPIT8 it8
)
500 if (it8
-> FileStack
[it8
->IncludeSP
]->Stream
) {
502 it8
->ch
= fgetc(it8
->FileStack
[it8
->IncludeSP
]->Stream
);
504 if (feof(it8
-> FileStack
[it8
->IncludeSP
]->Stream
)) {
506 if (it8
->IncludeSP
> 0) {
508 fclose(it8
->FileStack
[it8
->IncludeSP
--]->Stream
);
509 it8
-> ch
= ' '; // Whitespace to be ignored
519 it8
->ch
= *it8
->Source
;
520 if (it8
->ch
) it8
->Source
++;
525 // Try to see if current identifier is a keyword, if so return the referred symbol
527 SYMBOL
BinSrchKey(const char *id
)
536 res
= stricmp(id
, TabKeys
[x
-1].id
);
537 if (res
== 0) return TabKeys
[x
-1].sy
;
538 if (res
< 0) r
= x
- 1;
550 return pow(10, (double) n
);
554 // Reads a Real number, tries to follow from integer number
556 void ReadReal(LPIT8 it8
, int inum
)
558 it8
->dnum
= (double) inum
;
560 while (isdigit(it8
->ch
)) {
562 it8
->dnum
= it8
->dnum
* 10.0 + (it8
->ch
- '0');
566 if (it8
->ch
== '.') { // Decimal point
568 double frac
= 0.0; // fraction
569 int prec
= 0; // precission
571 NextCh(it8
); // Eats dec. point
573 while (isdigit(it8
->ch
)) {
575 frac
= frac
* 10.0 + (it8
->ch
- '0');
580 it8
->dnum
= it8
->dnum
+ (frac
/ xpow10(prec
));
583 // Exponent, example 34.00E+20
584 if (toupper(it8
->ch
) == 'E') {
589 NextCh(it8
); sgn
= 1;
591 if (it8
->ch
== '-') {
593 sgn
= -1; NextCh(it8
);
596 if (it8
->ch
== '+') {
604 while (isdigit(it8
->ch
)) {
606 if ((double) e
* 10L < INT_MAX
)
607 e
= e
* 10 + (it8
->ch
- '0');
614 it8
-> dnum
= it8
-> dnum
* xpow10(e
);
622 void InSymbol(LPIT8 it8
)
624 register char *idptr
;
631 while (isseparator(it8
->ch
))
634 if (isfirstidchar(it8
->ch
)) { // Identifier
642 if (++k
< MAXID
) *idptr
++ = (char) it8
->ch
;
646 } while (isidchar(it8
->ch
));
651 key
= BinSrchKey(it8
->id
);
652 if (key
== SNONE
) it8
->sy
= SIDENT
;
657 if (isdigit(it8
->ch
) || it8
->ch
== '.' || it8
->ch
== '-' || it8
->ch
== '+')
661 if (it8
->ch
== '-') {
669 if (it8
->ch
== '0') { // 0xnnnn (Hexa) or 0bnnnn (Binary)
672 if (toupper(it8
->ch
) == 'X') {
677 while (isxdigit(it8
->ch
))
679 it8
->ch
= toupper(it8
->ch
);
680 if (it8
->ch
>= 'A' && it8
->ch
<= 'F') j
= it8
->ch
-'A'+10;
681 else j
= it8
->ch
- '0';
683 if ((long) it8
->inum
* 16L > (long) INT_MAX
)
685 SynError(it8
, "Invalid hexadecimal number");
689 it8
->inum
= it8
->inum
* 16 + j
;
695 if (toupper(it8
->ch
) == 'B') { // Binary
700 while (it8
->ch
== '0' || it8
->ch
== '1')
704 if ((long) it8
->inum
* 2L > (long) INT_MAX
)
706 SynError(it8
, "Invalid binary number");
710 it8
->inum
= it8
->inum
* 2 + j
;
718 while (isdigit(it8
->ch
)) {
720 if ((long) it8
->inum
* 10L > (long) INT_MAX
) {
721 ReadReal(it8
, it8
->inum
);
727 it8
->inum
= it8
->inum
* 10 + (it8
->ch
- '0');
731 if (it8
->ch
== '.') {
733 ReadReal(it8
, it8
->inum
);
741 // Special case. Numbers followed by letters are taken as identifiers
743 if (isidchar(it8
->ch
)) {
745 if (it8
->sy
== SINUM
) {
747 sprintf(it8
->id
, "%d", it8
->inum
);
751 sprintf(it8
->id
, it8
->DoubleFormatter
, it8
->dnum
);
754 k
= (int) strlen(it8
->id
);
755 idptr
= it8
->id
+ k
;
758 if (++k
< MAXID
) *idptr
++ = (char) it8
->ch
;
762 } while (isidchar(it8
->ch
));
772 switch ((int) it8
->ch
) {
774 // EOF marker -- ignore it
779 // Eof stream markers
799 while (it8
->ch
&& it8
->ch
!= '\n')
814 while (k
< MAXSTR
&& it8
->ch
!= sng
) {
816 if (it8
->ch
== '\n'|| it8
->ch
== '\r') k
= MAXSTR
+1;
818 *idptr
++ = (char) it8
->ch
;
831 SynError(it8
, "Unrecognized character: 0x%x", it8
->ch
);
835 } while (it8
->sy
== SCOMMENT
);
837 // Handle the include special token
839 if (it8
-> sy
== SINCLUDE
) {
843 if(it8
-> IncludeSP
>= (MAXINCLUDE
-1))
845 SynError(it8
, "Too many recursion levels");
850 if (!Check(it8
, SSTRING
, "Filename expected")) return;
852 FileNest
= it8
-> FileStack
[it8
-> IncludeSP
+ 1];
855 FileNest
= it8
->FileStack
[it8
-> IncludeSP
+ 1] = (LPFILECTX
)AllocChunk(it8
, sizeof(FILECTX
));
856 //if(FileNest == NULL)
857 // TODO: how to manage out-of-memory conditions?
860 if(_cmsMakePath(it8
->str
, it8
->FileStack
[it8
->IncludeSP
]->FileName
, FileNest
->FileName
) == FALSE
)
862 SynError(it8
, "File path too long");
866 FileNest
->Stream
= fopen(FileNest
->FileName
, "rt");
867 if (FileNest
->Stream
== NULL
) {
869 SynError(it8
, "File %s not found", FileNest
->FileName
);
880 // Checks end of line separator
882 LCMSBOOL
CheckEOLN(LPIT8 it8
)
884 if (!Check(it8
, SEOLN
, "Expected separator")) return FALSE
;
885 while (it8
-> sy
== SEOLN
)
894 void Skip(LPIT8 it8
, SYMBOL sy
)
896 if (it8
->sy
== sy
&& it8
->sy
!= SEOF
)
901 // Skip multiple EOLN
903 void SkipEOLN(LPIT8 it8
)
905 while (it8
->sy
== SEOLN
) {
911 // Returns a string holding current value
913 LCMSBOOL
GetVal(LPIT8 it8
, char* Buffer
, size_t max
, const char* ErrorTitle
)
917 case SIDENT
: strncpy(Buffer
, it8
->id
, max
); break;
918 case SINUM
: snprintf(Buffer
, max
, "%d", it8
-> inum
); break;
919 case SDNUM
: snprintf(Buffer
, max
, it8
->DoubleFormatter
, it8
-> dnum
); break;
920 case SSTRING
: strncpy(Buffer
, it8
->str
, max
); break;
924 return SynError(it8
, "%s", ErrorTitle
);
931 // ---------------------------------------------------------- Table
934 LPTABLE
GetTable(LPIT8 it8
)
936 if ((it8
-> nTable
>= it8
->TablesCount
) || (it8
-> nTable
< 0)) {
938 SynError(it8
, "Table %d out of sequence", it8
-> nTable
);
942 return it8
->Tab
+ it8
->nTable
;
945 // ---------------------------------------------------------- Memory management
949 // Frees an allocator and owned memory
950 void LCMSEXPORT
cmsIT8Free(LCMSHANDLE hIT8
)
952 LPIT8 it8
= (LPIT8
) hIT8
;
958 if (it8
->MemorySink
) {
963 for (p
= it8
->MemorySink
; p
!= NULL
; p
= n
) {
966 if (p
->Ptr
) _cmsFree(p
->Ptr
);
971 if (it8
->MemoryBlock
)
972 _cmsFree(it8
->MemoryBlock
);
978 // Allocates a chunk of data, keep linked list
980 void* AllocBigBlock(LPIT8 it8
, size_t size
)
983 void* ptr
= _cmsMalloc(size
);
987 ZeroMemory(ptr
, size
);
988 ptr1
= (LPOWNEDMEM
) _cmsMalloc(sizeof(OWNEDMEM
));
996 ZeroMemory(ptr1
, sizeof(OWNEDMEM
));
999 ptr1
-> Next
= it8
-> MemorySink
;
1000 it8
-> MemorySink
= ptr1
;
1009 void* AllocChunk(LPIT8 it8
, size_t size
)
1011 size_t free
= it8
->Allocator
.BlockSize
- it8
->Allocator
.Used
;
1014 size
= ALIGNLONG(size
);
1018 if (it8
-> Allocator
.BlockSize
== 0)
1020 it8
-> Allocator
.BlockSize
= 20*1024;
1022 it8
->Allocator
.BlockSize
*= 2;
1024 if (it8
->Allocator
.BlockSize
< size
)
1025 it8
->Allocator
.BlockSize
= size
;
1027 it8
->Allocator
.Used
= 0;
1028 it8
->Allocator
.Block
= (LPBYTE
) AllocBigBlock(it8
, it8
->Allocator
.BlockSize
);
1031 ptr
= it8
->Allocator
.Block
+ it8
->Allocator
.Used
;
1032 it8
->Allocator
.Used
+= size
;
1039 // Allocates a string
1041 char *AllocString(LPIT8 it8
, const char* str
)
1043 size_t Size
= strlen(str
)+1;
1047 ptr
= (char *) AllocChunk(it8
, Size
);
1048 if (ptr
) strncpy (ptr
, str
, Size
-1);
1053 // Searches through linked list
1056 LCMSBOOL
IsAvailableOnList(LPKEYVALUE p
, const char* Key
, LPKEYVALUE
* LastPtr
)
1059 for (; p
!= NULL
; p
= p
->Next
) {
1061 if (LastPtr
) *LastPtr
= p
;
1063 if (*Key
!= '#') { // Comments are ignored
1065 if (stricmp(Key
, p
->Keyword
) == 0)
1075 // Add a property into a linked list
1077 LCMSBOOL
AddToList(LPIT8 it8
, LPKEYVALUE
* Head
, const char *Key
, const char* xValue
, WRITEMODE WriteAs
)
1083 // Check if property is already in list (this is an error)
1085 if (IsAvailableOnList(*Head
, Key
, &last
)) {
1087 // This may work for editing properties
1089 last
->Value
= AllocString(it8
, xValue
);
1090 last
->WriteAs
= WriteAs
;
1094 // Allocate the container
1095 p
= (LPKEYVALUE
) AllocChunk(it8
, sizeof(KEYVALUE
));
1098 return SynError(it8
, "AddToList: out of memory");
1101 // Store name and value
1102 p
->Keyword
= AllocString(it8
, Key
);
1104 if (xValue
!= NULL
) {
1106 p
->Value
= AllocString(it8
, xValue
);
1113 p
->WriteAs
= WriteAs
;
1115 // Keep the container in our list
1125 LCMSBOOL
AddAvailableProperty(LPIT8 it8
, const char* Key
)
1127 return AddToList(it8
, &it8
->ValidKeywords
, Key
, NULL
, WRITE_UNCOOKED
);
1132 LCMSBOOL
AddAvailableSampleID(LPIT8 it8
, const char* Key
)
1134 return AddToList(it8
, &it8
->ValidSampleID
, Key
, NULL
, WRITE_UNCOOKED
);
1139 void AllocTable(LPIT8 it8
)
1143 t
= it8
->Tab
+ it8
->TablesCount
;
1145 t
->HeaderList
= NULL
;
1146 t
->DataFormat
= NULL
;
1149 it8
->TablesCount
++;
1153 int LCMSEXPORT
cmsIT8SetTable(LCMSHANDLE IT8
, int nTable
)
1155 LPIT8 it8
= (LPIT8
) IT8
;
1157 if (nTable
>= it8
->TablesCount
) {
1159 if (nTable
== it8
->TablesCount
) {
1164 SynError(it8
, "Table %d is out of sequence", nTable
);
1169 it8
->nTable
= nTable
;
1176 // Init an empty container
1177 LCMSHANDLE LCMSEXPORT
cmsIT8Alloc(void)
1182 it8
= (LPIT8
) _cmsMalloc(sizeof(IT8
));
1183 if (it8
== NULL
) return NULL
;
1185 ZeroMemory(it8
, sizeof(IT8
));
1189 it8
->MemoryBlock
= NULL
;
1190 it8
->MemorySink
= NULL
;
1194 it8
->Allocator
.Used
= 0;
1195 it8
->Allocator
.Block
= NULL
;
1196 it8
->Allocator
.BlockSize
= 0;
1198 it8
->ValidKeywords
= NULL
;
1199 it8
->ValidSampleID
= NULL
;
1203 it8
-> Source
= NULL
;
1207 it8
->FileStack
[0] = (LPFILECTX
)AllocChunk(it8
, sizeof(FILECTX
));
1211 strcpy(it8
->DoubleFormatter
, DEFAULT_DBL_FORMAT
);
1212 strcpy(it8
->SheetType
, "CGATS.17");
1214 // Initialize predefined properties & data
1216 for (i
=0; i
< NUMPREDEFINEDPROPS
; i
++)
1217 AddAvailableProperty(it8
, PredefinedProperties
[i
]);
1219 for (i
=0; i
< NUMPREDEFINEDSAMPLEID
; i
++)
1220 AddAvailableSampleID(it8
, PredefinedSampleID
[i
]);
1223 return (LCMSHANDLE
) it8
;
1227 const char* LCMSEXPORT
cmsIT8GetSheetType(LCMSHANDLE hIT8
)
1229 LPIT8 it8
= (LPIT8
) hIT8
;
1231 return it8
->SheetType
;
1235 LCMSBOOL LCMSEXPORT
cmsIT8SetSheetType(LCMSHANDLE hIT8
, const char* Type
)
1237 LPIT8 it8
= (LPIT8
) hIT8
;
1239 strncpy(it8
->SheetType
, Type
, MAXSTR
-1);
1243 LCMSBOOL LCMSEXPORT
cmsIT8SetComment(LCMSHANDLE hIT8
, const char* Val
)
1245 LPIT8 it8
= (LPIT8
) hIT8
;
1247 if (!Val
) return FALSE
;
1248 if (!*Val
) return FALSE
;
1250 return AddToList(it8
, &GetTable(it8
)->HeaderList
, "# ", Val
, WRITE_UNCOOKED
);
1256 LCMSBOOL LCMSEXPORT
cmsIT8SetPropertyStr(LCMSHANDLE hIT8
, const char* Key
, const char *Val
)
1258 LPIT8 it8
= (LPIT8
) hIT8
;
1260 if (!Val
) return FALSE
;
1261 if (!*Val
) return FALSE
;
1263 return AddToList(it8
, &GetTable(it8
)->HeaderList
, Key
, Val
, WRITE_STRINGIFY
);
1267 LCMSBOOL LCMSEXPORT
cmsIT8SetPropertyDbl(LCMSHANDLE hIT8
, const char* cProp
, double Val
)
1269 LPIT8 it8
= (LPIT8
) hIT8
;
1272 sprintf(Buffer
, it8
->DoubleFormatter
, Val
);
1274 return AddToList(it8
, &GetTable(it8
)->HeaderList
, cProp
, Buffer
, WRITE_UNCOOKED
);
1277 LCMSBOOL LCMSEXPORT
cmsIT8SetPropertyHex(LCMSHANDLE hIT8
, const char* cProp
, int Val
)
1279 LPIT8 it8
= (LPIT8
) hIT8
;
1282 sprintf(Buffer
, "%d", Val
);
1284 return AddToList(it8
, &GetTable(it8
)->HeaderList
, cProp
, Buffer
, WRITE_HEXADECIMAL
);
1287 LCMSBOOL LCMSEXPORT
cmsIT8SetPropertyUncooked(LCMSHANDLE hIT8
, const char* Key
, const char* Buffer
)
1289 LPIT8 it8
= (LPIT8
) hIT8
;
1291 return AddToList(it8
, &GetTable(it8
)->HeaderList
, Key
, Buffer
, WRITE_UNCOOKED
);
1296 const char* LCMSEXPORT
cmsIT8GetProperty(LCMSHANDLE hIT8
, const char* Key
)
1298 LPIT8 it8
= (LPIT8
) hIT8
;
1301 if (IsAvailableOnList(GetTable(it8
) -> HeaderList
, Key
, &p
))
1309 double LCMSEXPORT
cmsIT8GetPropertyDbl(LCMSHANDLE hIT8
, const char* cProp
)
1311 const char *v
= cmsIT8GetProperty(hIT8
, cProp
);
1313 if (v
) return atof(v
);
1317 // ----------------------------------------------------------------- Datasets
1321 void AllocateDataFormat(LPIT8 it8
)
1323 LPTABLE t
= GetTable(it8
);
1325 if (t
-> DataFormat
) return; // Already allocated
1327 t
-> nSamples
= (int) cmsIT8GetPropertyDbl(it8
, "NUMBER_OF_FIELDS");
1329 if (t
-> nSamples
<= 0) {
1331 SynError(it8
, "AllocateDataFormat: Unknown NUMBER_OF_FIELDS");
1335 t
-> DataFormat
= (char**) AllocChunk (it8
, (t
->nSamples
+ 1) * sizeof(char *));
1336 if (t
->DataFormat
== NULL
)
1338 SynError(it8
, "AllocateDataFormat: Unable to allocate dataFormat array");
1344 const char *GetDataFormat(LPIT8 it8
, int n
)
1346 LPTABLE t
= GetTable(it8
);
1349 return t
->DataFormat
[n
];
1355 LCMSBOOL
SetDataFormat(LPIT8 it8
, int n
, const char *label
)
1357 LPTABLE t
= GetTable(it8
);
1360 AllocateDataFormat(it8
);
1362 if (n
> t
-> nSamples
) {
1363 SynError(it8
, "More than NUMBER_OF_FIELDS fields.");
1368 if (t
->DataFormat
) {
1369 t
->DataFormat
[n
] = AllocString(it8
, label
);
1376 LCMSBOOL LCMSEXPORT
cmsIT8SetDataFormat(LCMSHANDLE h
, int n
, const char *Sample
)
1378 LPIT8 it8
= (LPIT8
) h
;
1379 return SetDataFormat(it8
, n
, Sample
);
1383 void AllocateDataSet(LPIT8 it8
)
1385 LPTABLE t
= GetTable(it8
);
1387 if (t
-> Data
) return; // Already allocated
1389 t
-> nSamples
= atoi(cmsIT8GetProperty(it8
, "NUMBER_OF_FIELDS"));
1390 t
-> nPatches
= atoi(cmsIT8GetProperty(it8
, "NUMBER_OF_SETS"));
1392 t
-> Data
= (char**)AllocChunk (it8
, (t
->nSamples
+ 1) * (t
->nPatches
+ 1) *sizeof (char*));
1393 if (t
->Data
== NULL
)
1395 SynError(it8
, "AllocateDataSet: Unable to allocate data array");
1401 char* GetData(LPIT8 it8
, int nSet
, int nField
)
1403 LPTABLE t
= GetTable(it8
);
1404 int nSamples
= t
-> nSamples
;
1405 int nPatches
= t
-> nPatches
;
1408 if (nSet
>= nPatches
|| nField
>= nSamples
)
1411 if (!t
->Data
) return NULL
;
1412 return t
->Data
[nSet
* nSamples
+ nField
];
1416 LCMSBOOL
SetData(LPIT8 it8
, int nSet
, int nField
, const char *Val
)
1418 LPTABLE t
= GetTable(it8
);
1421 AllocateDataSet(it8
);
1423 if (!t
->Data
) return FALSE
;
1427 if (nSet
> t
-> nPatches
|| nSet
< 0) {
1429 return SynError(it8
, "Patch %d out of range, there are %d patches", nSet
, t
-> nPatches
);
1432 if (nField
> t
->nSamples
|| nField
< 0) {
1433 return SynError(it8
, "Sample %d out of range, there are %d samples", nField
, t
->nSamples
);
1438 t
->Data
[nSet
* t
-> nSamples
+ nField
] = AllocString(it8
, Val
);
1443 // --------------------------------------------------------------- File I/O
1446 // Writes a string to file
1448 void WriteStr(LPSAVESTREAM f
, const char *str
)
1461 if (f
->stream
) { // Should I write it to a file?
1463 fwrite(str
, 1, len
, f
->stream
);
1466 else { // Or to a memory block?
1469 if (f
->Base
) { // Am I just counting the bytes?
1471 if (f
->Used
> f
->Max
) {
1473 cmsSignalError(LCMS_ERRC_ABORTED
, "Write to memory overflows in CGATS parser");
1477 CopyMemory(f
->Ptr
, str
, len
);
1489 void Writef(LPSAVESTREAM f
, const char* frm
, ...)
1494 va_start(args
, frm
);
1495 vsnprintf(Buffer
, 4095, frm
, args
);
1497 WriteStr(f
, Buffer
);
1502 // Writes full header
1504 void WriteHeader(LPIT8 it8
, LPSAVESTREAM fp
)
1507 LPTABLE t
= GetTable(it8
);
1510 for (p
= t
->HeaderList
; (p
!= NULL
); p
= p
->Next
)
1512 if (*p
->Keyword
== '#') {
1516 WriteStr(fp
, "#\n# ");
1517 for (Pt
= p
->Value
; *Pt
; Pt
++) {
1520 Writef(fp
, "%c", *Pt
);
1527 WriteStr(fp
, "\n#\n");
1532 if (!IsAvailableOnList(it8
-> ValidKeywords
, p
->Keyword
, NULL
)) {
1535 WriteStr(fp
, "KEYWORD\t\"");
1536 WriteStr(fp
, p
->Keyword
);
1537 WriteStr(fp
, "\"\n");
1540 AddAvailableProperty(it8
, p
->Keyword
);
1544 WriteStr(fp
, p
->Keyword
);
1547 switch (p
->WriteAs
) {
1549 case WRITE_UNCOOKED
:
1550 Writef(fp
, "\t%s", p
->Value
);
1553 case WRITE_STRINGIFY
:
1554 Writef(fp
, "\t\"%s\"", p
->Value
);
1557 case WRITE_HEXADECIMAL
:
1558 Writef(fp
, "\t0x%X", atoi(p
->Value
));
1562 Writef(fp
, "\t0x%B", atoi(p
->Value
));
1565 default: SynError(it8
, "Unknown write mode %d", p
->WriteAs
);
1570 WriteStr (fp
, "\n");
1576 // Writes the data format
1578 void WriteDataFormat(LPSAVESTREAM fp
, LPIT8 it8
)
1581 LPTABLE t
= GetTable(it8
);
1583 if (!t
-> DataFormat
) return;
1585 WriteStr(fp
, "BEGIN_DATA_FORMAT\n");
1587 nSamples
= atoi(cmsIT8GetProperty(it8
, "NUMBER_OF_FIELDS"));
1589 for (i
= 0; i
< nSamples
; i
++) {
1591 WriteStr(fp
, t
->DataFormat
[i
]);
1592 WriteStr(fp
, ((i
== (nSamples
-1)) ? "\n" : "\t"));
1595 WriteStr (fp
, "END_DATA_FORMAT\n");
1599 // Writes data array
1601 void WriteData(LPSAVESTREAM fp
, LPIT8 it8
)
1604 LPTABLE t
= GetTable(it8
);
1606 if (!t
->Data
) return;
1608 WriteStr (fp
, "BEGIN_DATA\n");
1610 t
->nPatches
= atoi(cmsIT8GetProperty(it8
, "NUMBER_OF_SETS"));
1612 for (i
= 0; i
< t
-> nPatches
; i
++) {
1616 for (j
= 0; j
< t
->nSamples
; j
++) {
1618 char *ptr
= t
->Data
[i
*t
->nSamples
+j
];
1620 if (ptr
== NULL
) WriteStr(fp
, "\"\"");
1622 // If value contains whitespace, enclose within quote
1624 if (strchr(ptr
, ' ') != NULL
) {
1634 WriteStr(fp
, ((j
== (t
->nSamples
-1)) ? "\n" : "\t"));
1637 WriteStr (fp
, "END_DATA\n");
1643 LCMSBOOL LCMSEXPORT
cmsIT8SaveToFile(LCMSHANDLE hIT8
, const char* cFileName
)
1647 LPIT8 it8
= (LPIT8
) hIT8
;
1649 ZeroMemory(&sd
, sizeof(SAVESTREAM
));
1651 sd
.stream
= fopen(cFileName
, "wt");
1652 if (!sd
.stream
) return FALSE
;
1654 WriteStr(&sd
, it8
->SheetType
);
1655 WriteStr(&sd
, "\n");
1656 for (i
=0; i
< it8
->TablesCount
; i
++) {
1658 cmsIT8SetTable(hIT8
, i
);
1659 WriteHeader(it8
, &sd
);
1660 WriteDataFormat(&sd
, it8
);
1661 WriteData(&sd
, it8
);
1671 LCMSBOOL LCMSEXPORT
cmsIT8SaveToMem(LCMSHANDLE hIT8
, void *MemPtr
, size_t* BytesNeeded
)
1675 LPIT8 it8
= (LPIT8
) hIT8
;
1677 ZeroMemory(&sd
, sizeof(SAVESTREAM
));
1680 sd
.Base
= (LPBYTE
) MemPtr
;
1686 sd
.Max
= *BytesNeeded
; // Write to memory?
1688 sd
.Max
= 0; // Just counting the needed bytes
1690 WriteStr(&sd
, it8
->SheetType
);
1691 WriteStr(&sd
, "\n");
1692 for (i
=0; i
< it8
->TablesCount
; i
++) {
1694 cmsIT8SetTable(hIT8
, i
);
1695 WriteHeader(it8
, &sd
);
1696 WriteDataFormat(&sd
, it8
);
1697 WriteData(&sd
, it8
);
1700 sd
.Used
++; // The \0 at the very end
1705 *BytesNeeded
= sd
.Used
;
1711 // -------------------------------------------------------------- Higer level parsing
1714 LCMSBOOL
DataFormatSection(LPIT8 it8
)
1717 LPTABLE t
= GetTable(it8
);
1719 InSymbol(it8
); // Eats "BEGIN_DATA_FORMAT"
1722 while (it8
->sy
!= SEND_DATA_FORMAT
&&
1725 it8
->sy
!= SSYNERROR
) {
1727 if (it8
->sy
!= SIDENT
) {
1729 return SynError(it8
, "Sample type expected");
1732 if (!SetDataFormat(it8
, iField
, it8
->id
)) return FALSE
;
1740 Skip(it8
, SEND_DATA_FORMAT
);
1743 if (iField
!= t
->nSamples
) {
1744 SynError(it8
, "Count mismatch. NUMBER_OF_FIELDS was %d, found %d\n", t
->nSamples
, iField
);
1755 LCMSBOOL
DataSection (LPIT8 it8
)
1760 LPTABLE t
= GetTable(it8
);
1762 InSymbol(it8
); // Eats "BEGIN_DATA"
1766 AllocateDataSet(it8
);
1768 while (it8
->sy
!= SEND_DATA
&& it8
->sy
!= SEOF
)
1770 if (iField
>= t
-> nSamples
) {
1776 if (it8
->sy
!= SEND_DATA
&& it8
->sy
!= SEOF
) {
1778 if (!GetVal(it8
, Buffer
, 255, "Sample data expected"))
1781 if (!SetData(it8
, iSet
, iField
, Buffer
))
1792 Skip(it8
, SEND_DATA
);
1795 // Check for data completion.
1797 if ((iSet
+1) != t
-> nPatches
)
1798 return SynError(it8
, "Count mismatch. NUMBER_OF_SETS was %d, found %d\n", t
->nPatches
, iSet
+1);
1807 LCMSBOOL
HeaderSection(LPIT8 it8
)
1809 char VarName
[MAXID
];
1810 char Buffer
[MAXSTR
];
1812 while (it8
->sy
!= SEOF
&&
1813 it8
->sy
!= SSYNERROR
&&
1814 it8
->sy
!= SBEGIN_DATA_FORMAT
&&
1815 it8
->sy
!= SBEGIN_DATA
) {
1818 switch (it8
-> sy
) {
1822 if (!GetVal(it8
, Buffer
, MAXSTR
-1, "Keyword expected")) return FALSE
;
1823 if (!AddAvailableProperty(it8
, Buffer
)) return FALSE
;
1829 strncpy(VarName
, it8
->id
, MAXID
-1);
1831 if (!IsAvailableOnList(it8
-> ValidKeywords
, VarName
, NULL
)) {
1834 return SynError(it8
, "Undefined keyword '%s'", VarName
);
1836 if (!AddAvailableProperty(it8
, VarName
)) return FALSE
;
1841 if (!GetVal(it8
, Buffer
, MAXSTR
-1, "Property data expected")) return FALSE
;
1844 AddToList(it8
, &GetTable(it8
)->HeaderList
, VarName
, Buffer
,
1845 (it8
->sy
== SSTRING
) ? WRITE_STRINGIFY
: WRITE_UNCOOKED
);
1854 return SynError(it8
, "expected keyword or identifier");
1866 LCMSBOOL
ParseIT8(LPIT8 it8
)
1870 // First line is a very special case.
1872 while (isseparator(it8
->ch
))
1875 SheetTypePtr
= it8
->SheetType
;
1877 while (it8
->ch
!= '\r' && it8
->ch
!= '\n' && it8
->ch
!= '\t' && it8
-> ch
!= -1) {
1879 *SheetTypePtr
++= (char) it8
->ch
;
1888 while (it8
-> sy
!= SEOF
&&
1889 it8
-> sy
!= SSYNERROR
) {
1891 switch (it8
-> sy
) {
1893 case SBEGIN_DATA_FORMAT
:
1894 if (!DataFormatSection(it8
)) return FALSE
;
1899 if (!DataSection(it8
)) return FALSE
;
1901 if (it8
-> sy
!= SEOF
) {
1904 it8
->nTable
= it8
->TablesCount
- 1;
1913 if (!HeaderSection(it8
)) return FALSE
;
1918 return (it8
-> sy
!= SSYNERROR
);
1923 // Init usefull pointers
1926 void CookPointers(LPIT8 it8
)
1931 int nOldTable
= it8
->nTable
;
1933 for (j
=0; j
< it8
->TablesCount
; j
++) {
1935 LPTABLE t
= it8
->Tab
+ j
;
1940 for (idField
= 0; idField
< t
-> nSamples
; idField
++)
1942 if (t
->DataFormat
== NULL
) {
1943 SynError(it8
, "Undefined DATA_FORMAT");
1948 Fld
= t
->DataFormat
[idField
];
1952 if (stricmp(Fld
, "SAMPLE_ID") == 0) {
1954 t
-> SampleID
= idField
;
1956 for (i
=0; i
< t
-> nPatches
; i
++) {
1958 char *Data
= GetData(it8
, i
, idField
);
1962 strncpy(Buffer
, Data
, 255);
1964 if (strlen(Buffer
) <= strlen(Data
))
1965 strcpy(Data
, Buffer
);
1967 SetData(it8
, i
, idField
, Buffer
);
1974 // "LABEL" is an extension. It keeps references to forward tables
1976 if ((stricmp(Fld
, "LABEL") == 0) || Fld
[0] == '$' ) {
1978 // Search for table references...
1979 for (i
=0; i
< t
-> nPatches
; i
++) {
1981 char *Label
= GetData(it8
, i
, idField
);
1987 // This is the label, search for a table containing
1990 for (k
=0; k
< it8
->TablesCount
; k
++) {
1992 LPTABLE Table
= it8
->Tab
+ k
;
1995 if (IsAvailableOnList(Table
->HeaderList
, Label
, &p
)) {
1997 // Available, keep type and table
2000 char *Type
= p
->Value
;
2003 snprintf(Buffer
, 255, "%s %d %s", Label
, nTable
, Type
);
2005 SetData(it8
, i
, idField
, Buffer
);
2020 it8
->nTable
= nOldTable
;
2023 // Try to infere if the file is a CGATS/IT8 file at all. Read first line
2024 // that should be something like some printable characters plus a \n
2027 LCMSBOOL
IsMyBlock(LPBYTE Buffer
, size_t n
)
2031 if (n
< 10) return FALSE
; // Too small
2036 for (i
= 1; i
< n
; i
++) {
2038 if (Buffer
[i
] == '\n' || Buffer
[i
] == '\r' || Buffer
[i
] == '\t') return TRUE
;
2039 if (Buffer
[i
] < 32) return FALSE
;
2040 if (Buffer
[i
] > 127) return FALSE
;
2049 LCMSBOOL
IsMyFile(const char* FileName
)
2055 fp
= fopen(FileName
, "rt");
2057 cmsSignalError(LCMS_ERRC_ABORTED
, "File '%s' not found", FileName
);
2061 Size
= fread(Ptr
, 1, 132, fp
);
2066 return IsMyBlock(Ptr
, Size
);
2069 // ---------------------------------------------------------- Exported routines
2072 LCMSHANDLE LCMSEXPORT
cmsIT8LoadFromMem(void *Ptr
, size_t len
)
2077 if (!IsMyBlock((LPBYTE
) Ptr
, len
)) return NULL
;
2079 hIT8
= cmsIT8Alloc();
2080 if (!hIT8
) return NULL
;
2083 it8
->MemoryBlock
= (char*) _cmsMalloc(len
+ 1);
2085 strncpy(it8
->MemoryBlock
, (const char*) Ptr
, len
);
2086 it8
->MemoryBlock
[len
] = 0;
2088 strncpy(it8
->FileStack
[0]->FileName
, "", MAX_PATH
-1);
2089 it8
-> Source
= it8
-> MemoryBlock
;
2091 if (!ParseIT8(it8
)) {
2100 _cmsFree(it8
->MemoryBlock
);
2101 it8
-> MemoryBlock
= NULL
;
2109 LCMSHANDLE LCMSEXPORT
cmsIT8LoadFromFile(const char* cFileName
)
2115 if (!IsMyFile(cFileName
)) return NULL
;
2117 hIT8
= cmsIT8Alloc();
2119 if (!hIT8
) return NULL
;
2122 it8
->FileStack
[0]->Stream
= fopen(cFileName
, "rt");
2124 if (!it8
->FileStack
[0]->Stream
) {
2130 strncpy(it8
->FileStack
[0]->FileName
, cFileName
, MAX_PATH
-1);
2132 if (!ParseIT8(it8
)) {
2134 fclose(it8
->FileStack
[0]->Stream
);
2142 fclose(it8
->FileStack
[0]->Stream
);
2147 int LCMSEXPORT
cmsIT8EnumDataFormat(LCMSHANDLE hIT8
, char ***SampleNames
)
2149 LPIT8 it8
= (LPIT8
) hIT8
;
2150 LPTABLE t
= GetTable(it8
);
2152 *SampleNames
= t
-> DataFormat
;
2153 return t
-> nSamples
;
2157 int LCMSEXPORT
cmsIT8EnumProperties(LCMSHANDLE hIT8
, char ***PropertyNames
)
2159 LPIT8 it8
= (LPIT8
) hIT8
;
2163 LPTABLE t
= GetTable(it8
);
2165 // Pass#1 - count properties
2168 for (p
= t
-> HeaderList
; p
!= NULL
; p
= p
->Next
) {
2173 Props
= (char **) AllocChunk(it8
, sizeof(char *) * n
);
2175 // Pass#2 - Fill pointers
2177 for (p
= t
-> HeaderList
; p
!= NULL
; p
= p
->Next
) {
2178 Props
[n
++] = p
-> Keyword
;
2181 *PropertyNames
= Props
;
2186 int LocatePatch(LPIT8 it8
, const char* cPatch
)
2190 LPTABLE t
= GetTable(it8
);
2192 for (i
=0; i
< t
-> nPatches
; i
++) {
2194 data
= GetData(it8
, i
, t
->SampleID
);
2198 if (stricmp(data
, cPatch
) == 0)
2203 // SynError(it8, "Couldn't find patch '%s'\n", cPatch);
2209 int LocateEmptyPatch(LPIT8 it8
)
2213 LPTABLE t
= GetTable(it8
);
2215 for (i
=0; i
< t
-> nPatches
; i
++) {
2217 data
= GetData(it8
, i
, t
->SampleID
);
2228 int LocateSample(LPIT8 it8
, const char* cSample
)
2232 LPTABLE t
= GetTable(it8
);
2234 for (i
=0; i
< t
->nSamples
; i
++) {
2236 fld
= GetDataFormat(it8
, i
);
2237 if (stricmp(fld
, cSample
) == 0)
2242 // SynError(it8, "Couldn't find data field %s\n", cSample);
2248 int LCMSEXPORT
cmsIT8GetDataFormat(LCMSHANDLE hIT8
, const char* cSample
)
2250 LPIT8 it8
= (LPIT8
) hIT8
;
2251 return LocateSample(it8
, cSample
);
2256 const char* LCMSEXPORT
cmsIT8GetDataRowCol(LCMSHANDLE hIT8
, int row
, int col
)
2258 LPIT8 it8
= (LPIT8
) hIT8
;
2260 return GetData(it8
, row
, col
);
2264 double LCMSEXPORT
cmsIT8GetDataRowColDbl(LCMSHANDLE hIT8
, int row
, int col
)
2268 Buffer
= cmsIT8GetDataRowCol(hIT8
, row
, col
);
2272 return atof(Buffer
);
2280 LCMSBOOL LCMSEXPORT
cmsIT8SetDataRowCol(LCMSHANDLE hIT8
, int row
, int col
, const char* Val
)
2282 LPIT8 it8
= (LPIT8
) hIT8
;
2284 return SetData(it8
, row
, col
, Val
);
2288 LCMSBOOL LCMSEXPORT
cmsIT8SetDataRowColDbl(LCMSHANDLE hIT8
, int row
, int col
, double Val
)
2290 LPIT8 it8
= (LPIT8
) hIT8
;
2293 sprintf(Buff
, it8
->DoubleFormatter
, Val
);
2295 return SetData(it8
, row
, col
, Buff
);
2300 const char* LCMSEXPORT
cmsIT8GetData(LCMSHANDLE hIT8
, const char* cPatch
, const char* cSample
)
2302 LPIT8 it8
= (LPIT8
) hIT8
;
2306 iField
= LocateSample(it8
, cSample
);
2312 iSet
= LocatePatch(it8
, cPatch
);
2317 return GetData(it8
, iSet
, iField
);
2321 double LCMSEXPORT
cmsIT8GetDataDbl(LCMSHANDLE it8
, const char* cPatch
, const char* cSample
)
2325 Buffer
= cmsIT8GetData(it8
, cPatch
, cSample
);
2329 return atof(Buffer
);
2339 LCMSBOOL LCMSEXPORT
cmsIT8SetData(LCMSHANDLE hIT8
, const char* cPatch
,
2340 const char* cSample
,
2343 LPIT8 it8
= (LPIT8
) hIT8
;
2345 LPTABLE t
= GetTable(it8
);
2348 iField
= LocateSample(it8
, cSample
);
2355 if (t
-> nPatches
== 0) {
2357 AllocateDataFormat(it8
);
2358 AllocateDataSet(it8
);
2363 if (stricmp(cSample
, "SAMPLE_ID") == 0)
2366 iSet
= LocateEmptyPatch(it8
);
2368 return SynError(it8
, "Couldn't add more patches '%s'\n", cPatch
);
2371 iField
= t
-> SampleID
;
2374 iSet
= LocatePatch(it8
, cPatch
);
2380 return SetData(it8
, iSet
, iField
, Val
);
2384 LCMSBOOL LCMSEXPORT
cmsIT8SetDataDbl(LCMSHANDLE hIT8
, const char* cPatch
,
2385 const char* cSample
,
2388 LPIT8 it8
= (LPIT8
) hIT8
;
2391 snprintf(Buff
, 255, it8
->DoubleFormatter
, Val
);
2392 return cmsIT8SetData(hIT8
, cPatch
, cSample
, Buff
);
2396 // Buffer should get MAXSTR at least
2398 const char* LCMSEXPORT
cmsIT8GetPatchName(LCMSHANDLE hIT8
, int nPatch
, char* buffer
)
2400 LPIT8 it8
= (LPIT8
) hIT8
;
2401 LPTABLE t
= GetTable(it8
);
2402 char* Data
= GetData(it8
, nPatch
, t
->SampleID
);
2404 if (!Data
) return NULL
;
2405 if (!buffer
) return Data
;
2407 strncpy(buffer
, Data
, MAXSTR
-1);
2411 int LCMSEXPORT
cmsIT8TableCount(LCMSHANDLE hIT8
)
2413 LPIT8 it8
= (LPIT8
) hIT8
;
2415 return it8
->TablesCount
;
2418 // This handles the "LABEL" extension.
2419 // Label, nTable, Type
2421 int LCMSEXPORT
cmsIT8SetTableByLabel(LCMSHANDLE hIT8
, const char* cSet
, const char* cField
, const char* ExpectedType
)
2423 const char* cLabelFld
;
2424 char Type
[256], Label
[256];
2427 if (cField
!= NULL
&& *cField
== 0)
2433 cLabelFld
= cmsIT8GetData(hIT8
, cSet
, cField
);
2434 if (!cLabelFld
) return -1;
2436 if (sscanf(cLabelFld
, "%255s %d %255s", Label
, &nTable
, Type
) != 3)
2439 if (ExpectedType
!= NULL
&& *ExpectedType
== 0)
2440 ExpectedType
= NULL
;
2444 if (stricmp(Type
, ExpectedType
) != 0) return -1;
2447 return cmsIT8SetTable(hIT8
, nTable
);
2451 void LCMSEXPORT
cmsIT8DefineDblFormat(LCMSHANDLE hIT8
, const char* Formatter
)
2453 LPIT8 it8
= (LPIT8
) hIT8
;
2455 if (Formatter
== NULL
)
2456 strcpy(it8
->DoubleFormatter
, DEFAULT_DBL_FORMAT
);
2458 strcpy(it8
->DoubleFormatter
, Formatter
);