Backout bug 449422.
[wine-gecko.git] / modules / lcms / src / cmscgats.c
blob01fba2233770859a904bcc239e196cd7a17ddcf7
1 //
2 // Little cms
3 // Copyright (C) 1998-2007 Marti Maria
4 //
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.
22 //
23 // IT8.7 / CGATS.17-200x handling
25 #include "lcms.h"
28 LCMSAPI LCMSHANDLE LCMSEXPORT cmsIT8Alloc(void);
29 LCMSAPI void LCMSEXPORT cmsIT8Free(LCMSHANDLE IT8);
31 // Tables
33 LCMSAPI int LCMSEXPORT cmsIT8TableCount(LCMSHANDLE IT8);
34 LCMSAPI int LCMSEXPORT cmsIT8SetTable(LCMSHANDLE IT8, int nTable);
36 // Persistence
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);
41 // Properties
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);
56 // Datasets
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,
64 const char* Val);
66 LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetDataRowColDbl(LCMSHANDLE hIT8, int row, int col,
67 double Val);
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,
75 const char* cSample,
76 const char *Val);
78 LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetDataDbl(LCMSHANDLE hIT8, const char* cPatch,
79 const char* cSample,
80 double Val);
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,
88 const char* cField,
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
106 #include <ctype.h>
107 #include <limits.h>
109 #ifndef NON_WINDOWS
110 #include <io.h>
111 #define DIR_CHAR '\\'
112 #else
113 #define DIR_CHAR '/'
114 #endif
116 // Symbols
118 typedef enum {
120 SNONE,
121 SINUM, // Integer
122 SDNUM, // Real
123 SIDENT, // Identifier
124 SSTRING, // string
125 SCOMMENT, // comment
126 SEOLN, // End of line
127 SEOF, // End of stream
128 SSYNERROR, // Syntax error found on stream
130 // Keywords
132 SBEGIN_DATA,
133 SBEGIN_DATA_FORMAT,
134 SEND_DATA,
135 SEND_DATA_FORMAT,
136 SKEYWORD,
137 SINCLUDE
139 } SYMBOL;
142 // How to write the value
144 typedef enum {
145 WRITE_UNCOOKED,
146 WRITE_STRINGIFY,
147 WRITE_HEXADECIMAL,
148 WRITE_BINARY
150 } WRITEMODE;
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;
173 // Suballocator
175 typedef struct _SubAllocator {
177 LPBYTE Block;
178 size_t BlockSize;
179 size_t Used;
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
195 } TABLE, *LPTABLE;
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.
207 typedef struct {
209 char SheetType[MAXSTR];
211 int TablesCount; // How many tables in this stream
212 int nTable; // The actual table
214 TABLE Tab[MAXTABLES];
216 // Memory management
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
246 } IT8, *LPIT8;
250 typedef struct {
252 FILE* stream; // For save-to-file behaviour
254 LPBYTE Base;
255 LPBYTE Ptr; // For save-to-mem behaviour
256 size_t Used;
257 size_t Max;
259 } SAVESTREAM, FAR* LPSAVESTREAM;
262 // ------------------------------------------------------ IT8 parsing routines
265 // A keyword
266 typedef struct {
268 const char *id;
269 SYMBOL sy;
271 } KEYWORD;
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".
299 "MANUFACTURER",
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
320 // needed.
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
360 "LAB_DE" // CIE dE
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
383 static
384 LCMSBOOL isseparator(int c)
386 return (c == ' ') || (c == '\t') || (c == '\r');
389 // Checks whatever if c is a valid identifier char
390 static
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.
397 static
398 LCMSBOOL isidchar(int c)
400 return isalnum(c) || ismiddle(c);
403 // Checks whatsever if c is a valid identifier first char.
404 static
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
412 static
413 LCMSBOOL isabsolutepath(const char *path)
415 if(path == NULL)
416 return FALSE;
418 if(path[0] == DIR_CHAR)
419 return TRUE;
421 #ifndef NON_WINDOWS
422 if(isalpha(path[0]) && path[1] == ':')
423 return TRUE;
424 #endif
425 return FALSE;
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
432 static
433 LCMSBOOL _cmsMakePath(const char *relPath, const char *basePath, char *buffer)
435 if (!isabsolutepath(relPath)) {
437 char *tail;
439 strncpy(buffer, basePath, MAX_PATH-1);
440 tail = strrchr(buffer, DIR_CHAR);
441 if (tail != NULL) {
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!
446 return TRUE;
449 strncpy(buffer, relPath, MAX_PATH - 1);
450 return TRUE;
454 // Make sure no exploit is being even tried
456 static
457 const char* NoMeta(const char* str)
459 if (strchr(str, '%') != NULL)
460 return "**** CORRUPTED FORMAT STRING ***";
462 return str;
466 // Syntax error
467 static
468 LCMSBOOL SynError(LPIT8 it8, const char *Txt, ...)
470 char Buffer[256], ErrMsg[1024];
471 va_list args;
473 va_start(args, Txt);
474 vsnprintf(Buffer, 255, Txt, args);
475 Buffer[255] = 0;
476 va_end(args);
478 snprintf(ErrMsg, 1023, "%s: Line %d, %s", it8->FileStack[it8 ->IncludeSP]->FileName, it8->lineno, Buffer);
479 ErrMsg[1023] = 0;
480 it8->sy = SSYNERROR;
481 cmsSignalError(LCMS_ERRC_ABORTED, "%s", ErrMsg);
482 return FALSE;
485 // Check if current symbol is same as specified. issue an error else.
486 static
487 LCMSBOOL Check(LPIT8 it8, SYMBOL sy, const char* Err)
489 if (it8 -> sy != sy)
490 return SynError(it8, NoMeta(Err));
491 return TRUE;
496 // Read Next character from stream
497 static
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
511 } else
512 it8 ->ch = 0; // EOF
518 else {
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
526 static
527 SYMBOL BinSrchKey(const char *id)
529 int l = 1;
530 int r = NUMKEYS;
531 int x, res;
533 while (r >= l)
535 x = (l+r)/2;
536 res = stricmp(id, TabKeys[x-1].id);
537 if (res == 0) return TabKeys[x-1].sy;
538 if (res < 0) r = x - 1;
539 else l = x + 1;
542 return SNONE;
546 // 10 ^n
547 static
548 double xpow10(int n)
550 return pow(10, (double) n);
554 // Reads a Real number, tries to follow from integer number
555 static
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');
563 NextCh(it8);
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');
576 prec++;
577 NextCh(it8);
580 it8->dnum = it8->dnum + (frac / xpow10(prec));
583 // Exponent, example 34.00E+20
584 if (toupper(it8->ch) == 'E') {
586 int e;
587 int sgn;
589 NextCh(it8); sgn = 1;
591 if (it8->ch == '-') {
593 sgn = -1; NextCh(it8);
595 else
596 if (it8->ch == '+') {
598 sgn = +1;
599 NextCh(it8);
603 e = 0;
604 while (isdigit(it8->ch)) {
606 if ((double) e * 10L < INT_MAX)
607 e = e * 10 + (it8->ch - '0');
609 NextCh(it8);
612 e = sgn*e;
614 it8 -> dnum = it8 -> dnum * xpow10(e);
620 // Reads next symbol
621 static
622 void InSymbol(LPIT8 it8)
624 register char *idptr;
625 register int k;
626 SYMBOL key;
627 int sng;
629 do {
631 while (isseparator(it8->ch))
632 NextCh(it8);
634 if (isfirstidchar(it8->ch)) { // Identifier
637 k = 0;
638 idptr = it8->id;
640 do {
642 if (++k < MAXID) *idptr++ = (char) it8->ch;
644 NextCh(it8);
646 } while (isidchar(it8->ch));
648 *idptr = '\0';
651 key = BinSrchKey(it8->id);
652 if (key == SNONE) it8->sy = SIDENT;
653 else it8->sy = key;
656 else // Is a number?
657 if (isdigit(it8->ch) || it8->ch == '.' || it8->ch == '-' || it8->ch == '+')
659 int sign = 1;
661 if (it8->ch == '-') {
662 sign = -1;
663 NextCh(it8);
666 it8->inum = 0;
667 it8->sy = SINUM;
669 if (it8->ch == '0') { // 0xnnnn (Hexa) or 0bnnnn (Binary)
671 NextCh(it8);
672 if (toupper(it8->ch) == 'X') {
674 int j;
676 NextCh(it8);
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");
686 return;
689 it8->inum = it8->inum * 16 + j;
690 NextCh(it8);
692 return;
695 if (toupper(it8->ch) == 'B') { // Binary
697 int j;
699 NextCh(it8);
700 while (it8->ch == '0' || it8->ch == '1')
702 j = it8->ch - '0';
704 if ((long) it8->inum * 2L > (long) INT_MAX)
706 SynError(it8, "Invalid binary number");
707 return;
710 it8->inum = it8->inum * 2 + j;
711 NextCh(it8);
713 return;
718 while (isdigit(it8->ch)) {
720 if ((long) it8->inum * 10L > (long) INT_MAX) {
721 ReadReal(it8, it8->inum);
722 it8->sy = SDNUM;
723 it8->dnum *= sign;
724 return;
727 it8->inum = it8->inum * 10 + (it8->ch - '0');
728 NextCh(it8);
731 if (it8->ch == '.') {
733 ReadReal(it8, it8->inum);
734 it8->sy = SDNUM;
735 it8->dnum *= sign;
736 return;
739 it8 -> inum *= sign;
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);
749 else {
751 sprintf(it8->id, it8 ->DoubleFormatter, it8->dnum);
754 k = (int) strlen(it8 ->id);
755 idptr = it8 ->id + k;
756 do {
758 if (++k < MAXID) *idptr++ = (char) it8->ch;
760 NextCh(it8);
762 } while (isidchar(it8->ch));
764 *idptr = '\0';
766 it8->sy = SIDENT;
768 return;
771 else
772 switch ((int) it8->ch) {
774 // EOF marker -- ignore it
775 case '\x1a':
776 NextCh(it8);
777 break;
779 // Eof stream markers
781 case 0:
782 case -1:
783 it8->sy = SEOF;
784 break;
787 // Next line
789 case '\n':
790 NextCh(it8);
791 it8->sy = SEOLN;
792 it8->lineno++;
793 break;
795 // Comment
797 case '#':
798 NextCh(it8);
799 while (it8->ch && it8->ch != '\n')
800 NextCh(it8);
802 it8->sy = SCOMMENT;
803 break;
805 // String.
807 case '\'':
808 case '\"':
809 idptr = it8->str;
810 sng = it8->ch;
811 k = 0;
812 NextCh(it8);
814 while (k < MAXSTR && it8->ch != sng) {
816 if (it8->ch == '\n'|| it8->ch == '\r') k = MAXSTR+1;
817 else {
818 *idptr++ = (char) it8->ch;
819 NextCh(it8);
820 k++;
824 it8->sy = SSTRING;
825 *idptr = '\0';
826 NextCh(it8);
827 break;
830 default:
831 SynError(it8, "Unrecognized character: 0x%x", it8 ->ch);
832 return;
835 } while (it8->sy == SCOMMENT);
837 // Handle the include special token
839 if (it8 -> sy == SINCLUDE) {
841 LPFILECTX FileNest;
843 if(it8 -> IncludeSP >= (MAXINCLUDE-1))
845 SynError(it8, "Too many recursion levels");
846 return;
849 InSymbol(it8);
850 if (!Check(it8, SSTRING, "Filename expected")) return;
852 FileNest = it8 -> FileStack[it8 -> IncludeSP + 1];
853 if(FileNest == NULL)
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");
863 return;
866 FileNest->Stream = fopen(FileNest->FileName, "rt");
867 if (FileNest->Stream == NULL) {
869 SynError(it8, "File %s not found", FileNest->FileName);
870 return;
872 it8->IncludeSP++;
874 it8 ->ch = ' ';
875 InSymbol(it8);
880 // Checks end of line separator
881 static
882 LCMSBOOL CheckEOLN(LPIT8 it8)
884 if (!Check(it8, SEOLN, "Expected separator")) return FALSE;
885 while (it8 -> sy == SEOLN)
886 InSymbol(it8);
887 return TRUE;
891 // Skip a symbol
893 static
894 void Skip(LPIT8 it8, SYMBOL sy)
896 if (it8->sy == sy && it8->sy != SEOF)
897 InSymbol(it8);
901 // Skip multiple EOLN
902 static
903 void SkipEOLN(LPIT8 it8)
905 while (it8->sy == SEOLN) {
906 InSymbol(it8);
911 // Returns a string holding current value
912 static
913 LCMSBOOL GetVal(LPIT8 it8, char* Buffer, size_t max, const char* ErrorTitle)
915 switch (it8->sy) {
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;
923 default:
924 return SynError(it8, "%s", ErrorTitle);
927 Buffer[max] = 0;
928 return TRUE;
931 // ---------------------------------------------------------- Table
933 static
934 LPTABLE GetTable(LPIT8 it8)
936 if ((it8 -> nTable >= it8 ->TablesCount) || (it8 -> nTable < 0)) {
938 SynError(it8, "Table %d out of sequence", it8 -> nTable);
939 return it8 -> Tab;
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;
954 if (it8 == NULL)
955 return;
958 if (it8->MemorySink) {
960 LPOWNEDMEM p;
961 LPOWNEDMEM n;
963 for (p = it8->MemorySink; p != NULL; p = n) {
965 n = p->Next;
966 if (p->Ptr) _cmsFree(p->Ptr);
967 _cmsFree(p);
971 if (it8->MemoryBlock)
972 _cmsFree(it8->MemoryBlock);
974 _cmsFree(it8);
978 // Allocates a chunk of data, keep linked list
979 static
980 void* AllocBigBlock(LPIT8 it8, size_t size)
982 LPOWNEDMEM ptr1;
983 void* ptr = _cmsMalloc(size);
985 if (ptr) {
987 ZeroMemory(ptr, size);
988 ptr1 = (LPOWNEDMEM) _cmsMalloc(sizeof(OWNEDMEM));
990 if (ptr1 == NULL) {
992 _cmsFree(ptr);
993 return NULL;
996 ZeroMemory(ptr1, sizeof(OWNEDMEM));
998 ptr1-> Ptr = ptr;
999 ptr1-> Next = it8 -> MemorySink;
1000 it8 -> MemorySink = ptr1;
1003 return ptr;
1007 // Suballocator.
1008 static
1009 void* AllocChunk(LPIT8 it8, size_t size)
1011 size_t free = it8 ->Allocator.BlockSize - it8 ->Allocator.Used;
1012 LPBYTE ptr;
1014 size = ALIGNLONG(size);
1016 if (size > free) {
1018 if (it8 -> Allocator.BlockSize == 0)
1020 it8 -> Allocator.BlockSize = 20*1024;
1021 else
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;
1034 return (void*) ptr;
1039 // Allocates a string
1040 static
1041 char *AllocString(LPIT8 it8, const char* str)
1043 size_t Size = strlen(str)+1;
1044 char *ptr;
1047 ptr = (char *) AllocChunk(it8, Size);
1048 if (ptr) strncpy (ptr, str, Size-1);
1050 return ptr;
1053 // Searches through linked list
1055 static
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)
1066 return TRUE;
1070 return FALSE;
1075 // Add a property into a linked list
1076 static
1077 LCMSBOOL AddToList(LPIT8 it8, LPKEYVALUE* Head, const char *Key, const char* xValue, WRITEMODE WriteAs)
1079 LPKEYVALUE p;
1080 LPKEYVALUE last;
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;
1091 return TRUE;
1094 // Allocate the container
1095 p = (LPKEYVALUE) AllocChunk(it8, sizeof(KEYVALUE));
1096 if (p == NULL)
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);
1108 else {
1109 p->Value = NULL;
1112 p->Next = NULL;
1113 p->WriteAs = WriteAs;
1115 // Keep the container in our list
1116 if (*Head == NULL)
1117 *Head = p;
1118 else
1119 last->Next = p;
1121 return TRUE;
1124 static
1125 LCMSBOOL AddAvailableProperty(LPIT8 it8, const char* Key)
1127 return AddToList(it8, &it8->ValidKeywords, Key, NULL, WRITE_UNCOOKED);
1131 static
1132 LCMSBOOL AddAvailableSampleID(LPIT8 it8, const char* Key)
1134 return AddToList(it8, &it8->ValidSampleID, Key, NULL, WRITE_UNCOOKED);
1138 static
1139 void AllocTable(LPIT8 it8)
1141 LPTABLE t;
1143 t = it8 ->Tab + it8 ->TablesCount;
1145 t->HeaderList = NULL;
1146 t->DataFormat = NULL;
1147 t->Data = 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) {
1161 AllocTable(it8);
1163 else {
1164 SynError(it8, "Table %d is out of sequence", nTable);
1165 return -1;
1169 it8 ->nTable = nTable;
1171 return nTable;
1176 // Init an empty container
1177 LCMSHANDLE LCMSEXPORT cmsIT8Alloc(void)
1179 LPIT8 it8;
1180 int i;
1182 it8 = (LPIT8) _cmsMalloc(sizeof(IT8));
1183 if (it8 == NULL) return NULL;
1185 ZeroMemory(it8, sizeof(IT8));
1187 AllocTable(it8);
1189 it8->MemoryBlock = NULL;
1190 it8->MemorySink = NULL;
1192 it8 ->nTable = 0;
1194 it8->Allocator.Used = 0;
1195 it8->Allocator.Block = NULL;
1196 it8->Allocator.BlockSize = 0;
1198 it8->ValidKeywords = NULL;
1199 it8->ValidSampleID = NULL;
1201 it8 -> sy = SNONE;
1202 it8 -> ch = ' ';
1203 it8 -> Source = NULL;
1204 it8 -> inum = 0;
1205 it8 -> dnum = 0.0;
1207 it8->FileStack[0] = (LPFILECTX)AllocChunk(it8, sizeof(FILECTX));
1208 it8->IncludeSP = 0;
1209 it8 -> lineno = 1;
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);
1240 return TRUE;
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);
1255 // Sets a property
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;
1270 char Buffer[1024];
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;
1280 char Buffer[1024];
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);
1295 // Gets a property
1296 const char* LCMSEXPORT cmsIT8GetProperty(LCMSHANDLE hIT8, const char* Key)
1298 LPIT8 it8 = (LPIT8) hIT8;
1299 LPKEYVALUE p;
1301 if (IsAvailableOnList(GetTable(it8) -> HeaderList, Key, &p))
1303 return p -> Value;
1305 return NULL;
1309 double LCMSEXPORT cmsIT8GetPropertyDbl(LCMSHANDLE hIT8, const char* cProp)
1311 const char *v = cmsIT8GetProperty(hIT8, cProp);
1313 if (v) return atof(v);
1314 else return 0.0;
1317 // ----------------------------------------------------------------- Datasets
1320 static
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");
1332 t -> nSamples = 10;
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");
1343 static
1344 const char *GetDataFormat(LPIT8 it8, int n)
1346 LPTABLE t = GetTable(it8);
1348 if (t->DataFormat)
1349 return t->DataFormat[n];
1351 return NULL;
1354 static
1355 LCMSBOOL SetDataFormat(LPIT8 it8, int n, const char *label)
1357 LPTABLE t = GetTable(it8);
1359 if (!t->DataFormat)
1360 AllocateDataFormat(it8);
1362 if (n > t -> nSamples) {
1363 SynError(it8, "More than NUMBER_OF_FIELDS fields.");
1364 return FALSE;
1368 if (t->DataFormat) {
1369 t->DataFormat[n] = AllocString(it8, label);
1372 return TRUE;
1376 LCMSBOOL LCMSEXPORT cmsIT8SetDataFormat(LCMSHANDLE h, int n, const char *Sample)
1378 LPIT8 it8 = (LPIT8) h;
1379 return SetDataFormat(it8, n, Sample);
1382 static
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");
1400 static
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)
1409 return NULL;
1411 if (!t->Data) return NULL;
1412 return t->Data [nSet * nSamples + nField];
1415 static
1416 LCMSBOOL SetData(LPIT8 it8, int nSet, int nField, const char *Val)
1418 LPTABLE t = GetTable(it8);
1420 if (!t->Data)
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);
1439 return TRUE;
1443 // --------------------------------------------------------------- File I/O
1446 // Writes a string to file
1447 static
1448 void WriteStr(LPSAVESTREAM f, const char *str)
1451 size_t len;
1453 if (str == NULL)
1454 str = " ";
1456 // Lenghth to write
1457 len = strlen(str);
1458 f ->Used += len;
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");
1474 return;
1477 CopyMemory(f ->Ptr, str, len);
1478 f->Ptr += len;
1486 // Write formatted
1488 static
1489 void Writef(LPSAVESTREAM f, const char* frm, ...)
1491 char Buffer[4096];
1492 va_list args;
1494 va_start(args, frm);
1495 vsnprintf(Buffer, 4095, frm, args);
1496 Buffer[4095] = 0;
1497 WriteStr(f, Buffer);
1498 va_end(args);
1502 // Writes full header
1503 static
1504 void WriteHeader(LPIT8 it8, LPSAVESTREAM fp)
1506 LPKEYVALUE p;
1507 LPTABLE t = GetTable(it8);
1510 for (p = t->HeaderList; (p != NULL); p = p->Next)
1512 if (*p ->Keyword == '#') {
1514 char* Pt;
1516 WriteStr(fp, "#\n# ");
1517 for (Pt = p ->Value; *Pt; Pt++) {
1520 Writef(fp, "%c", *Pt);
1522 if (*Pt == '\n') {
1523 WriteStr(fp, "# ");
1527 WriteStr(fp, "\n#\n");
1528 continue;
1532 if (!IsAvailableOnList(it8-> ValidKeywords, p->Keyword, NULL)) {
1534 #ifdef STRICT_CGATS
1535 WriteStr(fp, "KEYWORD\t\"");
1536 WriteStr(fp, p->Keyword);
1537 WriteStr(fp, "\"\n");
1538 #endif
1540 AddAvailableProperty(it8, p->Keyword);
1544 WriteStr(fp, p->Keyword);
1545 if (p->Value) {
1547 switch (p ->WriteAs) {
1549 case WRITE_UNCOOKED:
1550 Writef(fp, "\t%s", p ->Value);
1551 break;
1553 case WRITE_STRINGIFY:
1554 Writef(fp, "\t\"%s\"", p->Value );
1555 break;
1557 case WRITE_HEXADECIMAL:
1558 Writef(fp, "\t0x%X", atoi(p ->Value));
1559 break;
1561 case WRITE_BINARY:
1562 Writef(fp, "\t0x%B", atoi(p ->Value));
1563 break;
1565 default: SynError(it8, "Unknown write mode %d", p ->WriteAs);
1566 return;
1570 WriteStr (fp, "\n");
1576 // Writes the data format
1577 static
1578 void WriteDataFormat(LPSAVESTREAM fp, LPIT8 it8)
1580 int i, nSamples;
1581 LPTABLE t = GetTable(it8);
1583 if (!t -> DataFormat) return;
1585 WriteStr(fp, "BEGIN_DATA_FORMAT\n");
1586 WriteStr(fp, " ");
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
1600 static
1601 void WriteData(LPSAVESTREAM fp, LPIT8 it8)
1603 int i, j;
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++) {
1614 WriteStr(fp, " ");
1616 for (j = 0; j < t->nSamples; j++) {
1618 char *ptr = t->Data[i*t->nSamples+j];
1620 if (ptr == NULL) WriteStr(fp, "\"\"");
1621 else {
1622 // If value contains whitespace, enclose within quote
1624 if (strchr(ptr, ' ') != NULL) {
1626 WriteStr(fp, "\"");
1627 WriteStr(fp, ptr);
1628 WriteStr(fp, "\"");
1630 else
1631 WriteStr(fp, ptr);
1634 WriteStr(fp, ((j == (t->nSamples-1)) ? "\n" : "\t"));
1637 WriteStr (fp, "END_DATA\n");
1642 // Saves whole file
1643 LCMSBOOL LCMSEXPORT cmsIT8SaveToFile(LCMSHANDLE hIT8, const char* cFileName)
1645 SAVESTREAM sd;
1646 int i;
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);
1664 fclose(sd.stream);
1666 return TRUE;
1670 // Saves to memory
1671 LCMSBOOL LCMSEXPORT cmsIT8SaveToMem(LCMSHANDLE hIT8, void *MemPtr, size_t* BytesNeeded)
1673 SAVESTREAM sd;
1674 int i;
1675 LPIT8 it8 = (LPIT8) hIT8;
1677 ZeroMemory(&sd, sizeof(SAVESTREAM));
1679 sd.stream = NULL;
1680 sd.Base = (LPBYTE) MemPtr;
1681 sd.Ptr = sd.Base;
1683 sd.Used = 0;
1685 if (sd.Base)
1686 sd.Max = *BytesNeeded; // Write to memory?
1687 else
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
1702 if (sd.Base)
1703 sd.Ptr = 0;
1705 *BytesNeeded = sd.Used;
1707 return TRUE;
1711 // -------------------------------------------------------------- Higer level parsing
1713 static
1714 LCMSBOOL DataFormatSection(LPIT8 it8)
1716 int iField = 0;
1717 LPTABLE t = GetTable(it8);
1719 InSymbol(it8); // Eats "BEGIN_DATA_FORMAT"
1720 CheckEOLN(it8);
1722 while (it8->sy != SEND_DATA_FORMAT &&
1723 it8->sy != SEOLN &&
1724 it8->sy != SEOF &&
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;
1733 iField++;
1735 InSymbol(it8);
1736 SkipEOLN(it8);
1739 SkipEOLN(it8);
1740 Skip(it8, SEND_DATA_FORMAT);
1741 SkipEOLN(it8);
1743 if (iField != t ->nSamples) {
1744 SynError(it8, "Count mismatch. NUMBER_OF_FIELDS was %d, found %d\n", t ->nSamples, iField);
1749 return TRUE;
1754 static
1755 LCMSBOOL DataSection (LPIT8 it8)
1757 int iField = 0;
1758 int iSet = 0;
1759 char Buffer[256];
1760 LPTABLE t = GetTable(it8);
1762 InSymbol(it8); // Eats "BEGIN_DATA"
1763 CheckEOLN(it8);
1765 if (!t->Data)
1766 AllocateDataSet(it8);
1768 while (it8->sy != SEND_DATA && it8->sy != SEOF)
1770 if (iField >= t -> nSamples) {
1771 iField = 0;
1772 iSet++;
1776 if (it8->sy != SEND_DATA && it8->sy != SEOF) {
1778 if (!GetVal(it8, Buffer, 255, "Sample data expected"))
1779 return FALSE;
1781 if (!SetData(it8, iSet, iField, Buffer))
1782 return FALSE;
1784 iField++;
1786 InSymbol(it8);
1787 SkipEOLN(it8);
1791 SkipEOLN(it8);
1792 Skip(it8, SEND_DATA);
1793 SkipEOLN(it8);
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);
1800 return TRUE;
1806 static
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) {
1820 case SKEYWORD:
1821 InSymbol(it8);
1822 if (!GetVal(it8, Buffer, MAXSTR-1, "Keyword expected")) return FALSE;
1823 if (!AddAvailableProperty(it8, Buffer)) return FALSE;
1824 InSymbol(it8);
1825 break;
1828 case SIDENT:
1829 strncpy(VarName, it8->id, MAXID-1);
1831 if (!IsAvailableOnList(it8-> ValidKeywords, VarName, NULL)) {
1833 #ifdef STRICT_CGATS
1834 return SynError(it8, "Undefined keyword '%s'", VarName);
1835 #else
1836 if (!AddAvailableProperty(it8, VarName)) return FALSE;
1837 #endif
1840 InSymbol(it8);
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);
1847 InSymbol(it8);
1848 break;
1851 case SEOLN: break;
1853 default:
1854 return SynError(it8, "expected keyword or identifier");
1857 SkipEOLN(it8);
1860 return TRUE;
1865 static
1866 LCMSBOOL ParseIT8(LPIT8 it8)
1868 char* SheetTypePtr;
1870 // First line is a very special case.
1872 while (isseparator(it8->ch))
1873 NextCh(it8);
1875 SheetTypePtr = it8 ->SheetType;
1877 while (it8->ch != '\r' && it8 ->ch != '\n' && it8->ch != '\t' && it8 -> ch != -1) {
1879 *SheetTypePtr++= (char) it8 ->ch;
1880 NextCh(it8);
1883 *SheetTypePtr = 0;
1884 InSymbol(it8);
1886 SkipEOLN(it8);
1888 while (it8-> sy != SEOF &&
1889 it8-> sy != SSYNERROR) {
1891 switch (it8 -> sy) {
1893 case SBEGIN_DATA_FORMAT:
1894 if (!DataFormatSection(it8)) return FALSE;
1895 break;
1897 case SBEGIN_DATA:
1899 if (!DataSection(it8)) return FALSE;
1901 if (it8 -> sy != SEOF) {
1903 AllocTable(it8);
1904 it8 ->nTable = it8 ->TablesCount - 1;
1906 break;
1908 case SEOLN:
1909 SkipEOLN(it8);
1910 break;
1912 default:
1913 if (!HeaderSection(it8)) return FALSE;
1918 return (it8 -> sy != SSYNERROR);
1923 // Init usefull pointers
1925 static
1926 void CookPointers(LPIT8 it8)
1928 int idField, i;
1929 char* Fld;
1930 int j;
1931 int nOldTable = it8 ->nTable;
1933 for (j=0; j < it8 ->TablesCount; j++) {
1935 LPTABLE t = it8 ->Tab + j;
1937 t -> SampleID = 0;
1938 it8 ->nTable = j;
1940 for (idField = 0; idField < t -> nSamples; idField++)
1942 if (t ->DataFormat == NULL) {
1943 SynError(it8, "Undefined DATA_FORMAT");
1944 return;
1948 Fld = t->DataFormat[idField];
1949 if (!Fld) continue;
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);
1959 if (Data) {
1960 char Buffer[256];
1962 strncpy(Buffer, Data, 255);
1964 if (strlen(Buffer) <= strlen(Data))
1965 strcpy(Data, Buffer);
1966 else
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);
1983 if (Label) {
1985 int k;
1987 // This is the label, search for a table containing
1988 // this property
1990 for (k=0; k < it8 ->TablesCount; k++) {
1992 LPTABLE Table = it8 ->Tab + k;
1993 LPKEYVALUE p;
1995 if (IsAvailableOnList(Table->HeaderList, Label, &p)) {
1997 // Available, keep type and table
1998 char Buffer[256];
2000 char *Type = p ->Value;
2001 int nTable = k;
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
2026 static
2027 LCMSBOOL IsMyBlock(LPBYTE Buffer, size_t n)
2029 size_t i;
2031 if (n < 10) return FALSE; // Too small
2033 if (n > 132)
2034 n = 132;
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;
2043 return FALSE;
2048 static
2049 LCMSBOOL IsMyFile(const char* FileName)
2051 FILE *fp;
2052 size_t Size;
2053 BYTE Ptr[133];
2055 fp = fopen(FileName, "rt");
2056 if (!fp) {
2057 cmsSignalError(LCMS_ERRC_ABORTED, "File '%s' not found", FileName);
2058 return FALSE;
2061 Size = fread(Ptr, 1, 132, fp);
2062 fclose(fp);
2064 Ptr[Size] = '\0';
2066 return IsMyBlock(Ptr, Size);
2069 // ---------------------------------------------------------- Exported routines
2072 LCMSHANDLE LCMSEXPORT cmsIT8LoadFromMem(void *Ptr, size_t len)
2074 LCMSHANDLE hIT8;
2075 LPIT8 it8;
2077 if (!IsMyBlock((LPBYTE) Ptr, len)) return NULL;
2079 hIT8 = cmsIT8Alloc();
2080 if (!hIT8) return NULL;
2082 it8 = (LPIT8) hIT8;
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)) {
2093 cmsIT8Free(hIT8);
2094 return FALSE;
2097 CookPointers(it8);
2098 it8 ->nTable = 0;
2100 _cmsFree(it8->MemoryBlock);
2101 it8 -> MemoryBlock = NULL;
2103 return hIT8;
2109 LCMSHANDLE LCMSEXPORT cmsIT8LoadFromFile(const char* cFileName)
2112 LCMSHANDLE hIT8;
2113 LPIT8 it8;
2115 if (!IsMyFile(cFileName)) return NULL;
2117 hIT8 = cmsIT8Alloc();
2118 it8 = (LPIT8) hIT8;
2119 if (!hIT8) return NULL;
2122 it8 ->FileStack[0]->Stream = fopen(cFileName, "rt");
2124 if (!it8 ->FileStack[0]->Stream) {
2125 cmsIT8Free(hIT8);
2126 return NULL;
2130 strncpy(it8->FileStack[0]->FileName, cFileName, MAX_PATH-1);
2132 if (!ParseIT8(it8)) {
2134 fclose(it8 ->FileStack[0]->Stream);
2135 cmsIT8Free(hIT8);
2136 return NULL;
2139 CookPointers(it8);
2140 it8 ->nTable = 0;
2142 fclose(it8 ->FileStack[0]->Stream);
2143 return hIT8;
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;
2160 LPKEYVALUE p;
2161 int n;
2162 char **Props;
2163 LPTABLE t = GetTable(it8);
2165 // Pass#1 - count properties
2167 n = 0;
2168 for (p = t -> HeaderList; p != NULL; p = p->Next) {
2169 n++;
2173 Props = (char **) AllocChunk(it8, sizeof(char *) * n);
2175 // Pass#2 - Fill pointers
2176 n = 0;
2177 for (p = t -> HeaderList; p != NULL; p = p->Next) {
2178 Props[n++] = p -> Keyword;
2181 *PropertyNames = Props;
2182 return n;
2185 static
2186 int LocatePatch(LPIT8 it8, const char* cPatch)
2188 int i;
2189 const char *data;
2190 LPTABLE t = GetTable(it8);
2192 for (i=0; i < t-> nPatches; i++) {
2194 data = GetData(it8, i, t->SampleID);
2196 if (data != NULL) {
2198 if (stricmp(data, cPatch) == 0)
2199 return i;
2203 // SynError(it8, "Couldn't find patch '%s'\n", cPatch);
2204 return -1;
2208 static
2209 int LocateEmptyPatch(LPIT8 it8)
2211 int i;
2212 const char *data;
2213 LPTABLE t = GetTable(it8);
2215 for (i=0; i < t-> nPatches; i++) {
2217 data = GetData(it8, i, t->SampleID);
2219 if (data == NULL)
2220 return i;
2224 return -1;
2227 static
2228 int LocateSample(LPIT8 it8, const char* cSample)
2230 int i;
2231 const char *fld;
2232 LPTABLE t = GetTable(it8);
2234 for (i=0; i < t->nSamples; i++) {
2236 fld = GetDataFormat(it8, i);
2237 if (stricmp(fld, cSample) == 0)
2238 return i;
2242 // SynError(it8, "Couldn't find data field %s\n", cSample);
2243 return -1;
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)
2266 const char* Buffer;
2268 Buffer = cmsIT8GetDataRowCol(hIT8, row, col);
2270 if (Buffer) {
2272 return atof(Buffer);
2274 } else
2275 return 0;
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;
2291 char Buff[256];
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;
2303 int iField, iSet;
2306 iField = LocateSample(it8, cSample);
2307 if (iField < 0) {
2308 return NULL;
2312 iSet = LocatePatch(it8, cPatch);
2313 if (iSet < 0) {
2314 return NULL;
2317 return GetData(it8, iSet, iField);
2321 double LCMSEXPORT cmsIT8GetDataDbl(LCMSHANDLE it8, const char* cPatch, const char* cSample)
2323 const char* Buffer;
2325 Buffer = cmsIT8GetData(it8, cPatch, cSample);
2327 if (Buffer) {
2329 return atof(Buffer);
2331 } else {
2333 return 0;
2339 LCMSBOOL LCMSEXPORT cmsIT8SetData(LCMSHANDLE hIT8, const char* cPatch,
2340 const char* cSample,
2341 const char *Val)
2343 LPIT8 it8 = (LPIT8) hIT8;
2344 int iField, iSet;
2345 LPTABLE t = GetTable(it8);
2348 iField = LocateSample(it8, cSample);
2350 if (iField < 0)
2351 return FALSE;
2355 if (t-> nPatches == 0) {
2357 AllocateDataFormat(it8);
2358 AllocateDataSet(it8);
2359 CookPointers(it8);
2363 if (stricmp(cSample, "SAMPLE_ID") == 0)
2366 iSet = LocateEmptyPatch(it8);
2367 if (iSet < 0) {
2368 return SynError(it8, "Couldn't add more patches '%s'\n", cPatch);
2371 iField = t -> SampleID;
2373 else {
2374 iSet = LocatePatch(it8, cPatch);
2375 if (iSet < 0) {
2376 return FALSE;
2380 return SetData(it8, iSet, iField, Val);
2384 LCMSBOOL LCMSEXPORT cmsIT8SetDataDbl(LCMSHANDLE hIT8, const char* cPatch,
2385 const char* cSample,
2386 double Val)
2388 LPIT8 it8 = (LPIT8) hIT8;
2389 char Buff[256];
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);
2408 return buffer;
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];
2425 int nTable;
2427 if (cField != NULL && *cField == 0)
2428 cField = "LABEL";
2430 if (cField == NULL)
2431 cField = "LABEL";
2433 cLabelFld = cmsIT8GetData(hIT8, cSet, cField);
2434 if (!cLabelFld) return -1;
2436 if (sscanf(cLabelFld, "%255s %d %255s", Label, &nTable, Type) != 3)
2437 return -1;
2439 if (ExpectedType != NULL && *ExpectedType == 0)
2440 ExpectedType = NULL;
2442 if (ExpectedType) {
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);
2457 else
2458 strcpy(it8->DoubleFormatter, Formatter);