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