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 52typedef 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 78typedef 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 89typedef 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) 102typedef struct _OwnedMem { 103 104 struct _OwnedMem* Next; 105 void * Ptr; // Point to value 106 107 } OWNEDMEM; 108 109// Suballocator 110typedef 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 119typedef 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 134typedef 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. 140typedef 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 182typedef 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 198typedef struct { 199 200 const char *id; 201 SYMBOL sy; 202 203 } KEYWORD; 204 205// The keyword->symbol translation table. Sorting is required. 206static 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 224typedef struct { 225 const char *id; // The identifier 226 WRITEMODE as; // How is supposed to be written 227 } PROPERTY; 228 229static 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 308static 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 359static void* AllocChunk(cmsIT8* it8, cmsUInt32Number size); 360 361// Checks whatever c is a separator 362static 363cmsBool isseparator(int c) 364{ 365 return (c == ' ') || (c == '\t') ; 366} 367 368// Checks whatever c is a valid identifier char 369static 370cmsBool 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. 376static 377cmsBool isidchar(int c) 378{ 379 return isalnum(c) || ismiddle(c); 380} 381 382// Checks whatsever c is a valid identifier first char. 383static 384cmsBool isfirstidchar(int c) 385{ 386 return !isdigit(c) && ismiddle(c); 387} 388 389// Guess whether the supplied path looks like an absolute path 390static 391cmsBool 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 416static 417cmsBool 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 448static 449const 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 458static 459cmsBool 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. 477static 478cmsBool 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 486static 487void 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 512static 513SYMBOL 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 533static 534cmsFloat64Number xpow10(int n) 535{ 536 return pow(10, (cmsFloat64Number) n); 537} 538 539 540// Reads a Real number, tries to follow from integer number 541static 542void 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 605static 606cmsFloat64Number 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 683static 684void 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 946static 947cmsBool 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 958static 959void Skip(cmsIT8* it8, SYMBOL sy) 960{ 961 if (it8->sy == sy && it8->sy != SEOF) 962 InSymbol(it8); 963} 964 965 966// Skip multiple EOLN 967static 968void SkipEOLN(cmsIT8* it8) 969{ 970 while (it8->sy == SEOLN) { 971 InSymbol(it8); 972 } 973} 974 975 976// Returns a string holding current value 977static 978cmsBool 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 1005static 1006TABLE* 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 1021void 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 1049static 1050void* 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. 1075static 1076void* 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 1107static 1108char *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 1122static 1123cmsBool 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 1160static 1161KEYVALUE* 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 1228static 1229KEYVALUE* AddAvailableProperty(cmsIT8* it8, const char* Key, WRITEMODE as) 1230{ 1231 return AddToList(it8, &it8->ValidKeywords, Key, NULL, NULL, as); 1232} 1233 1234 1235static 1236KEYVALUE* AddAvailableSampleID(cmsIT8* it8, const char* Key) 1237{ 1238 return AddToList(it8, &it8->ValidSampleID, Key, NULL, NULL, WRITE_UNCOOKED); 1239} 1240 1241 1242static 1243void 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 1257cmsInt32Number 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 1281cmsHANDLE 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 1330const char* CMSEXPORT cmsIT8GetSheetType(cmsHANDLE hIT8) 1331{ 1332 return GetTable((cmsIT8*) hIT8)->SheetType; 1333} 1334 1335cmsBool 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 1344cmsBool 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 1355cmsBool 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 1365cmsBool 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 1375cmsBool 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 1385cmsBool 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 1392cmsBool 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 1400const 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 1413cmsFloat64Number 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 1422const 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 1436static 1437void 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 1459static 1460const 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 1470static 1471cmsBool 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 1491cmsBool CMSEXPORT cmsIT8SetDataFormat(cmsHANDLE h, int n, const char *Sample) 1492{ 1493 cmsIT8* it8 = (cmsIT8*) h; 1494 return SetDataFormat(it8, n, Sample); 1495} 1496 1497static 1498void 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 1515static 1516char* 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 1529static 1530cmsBool 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 1558static 1559void 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 1599static 1600void 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 1614static 1615void 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 1694static 1695void 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 1717static 1718void 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 1760cmsBool 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 1786cmsBool 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 1826static 1827cmsBool 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 1867static 1868cmsBool 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 1919static 1920cmsBool 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 2028static 2029void 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 2046static 2047cmsBool 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 2129static 2130void 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? 2230static 2231int 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 2269static 2270cmsBool 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 2295cmsHANDLE 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 2337cmsHANDLE 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 2384int 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 2399cmsUInt32Number 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 2431cmsUInt32Number 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 2471static 2472int 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 2494static 2495int 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 2513static 2514int 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 2532int 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 2543const 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 2553cmsFloat64Number 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 2565cmsBool 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 2575cmsBool 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 2589const 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 2610cmsFloat64Number 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 2621cmsBool 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 2663cmsBool 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 2678const 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 2697int CMSEXPORT cmsIT8GetPatchByName(cmsHANDLE hIT8, const char *cPatch) 2698{ 2699 _cmsAssert(hIT8 != NULL); 2700 2701 return LocatePatch((cmsIT8*)hIT8, cPatch); 2702} 2703 2704cmsUInt32Number 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 2716int 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 2748cmsBool 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 2764void 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