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