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