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 362 cmsBool isseparator(int c) 363 { 364 return (c == ' ') || (c == '\t') ; 365 } 366 367 // Checks whatever c is a valid identifier char 368 static 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 1329 const char* CMSEXPORT cmsIT8GetSheetType(cmsHANDLE hIT8) 1330 { 1331 return GetTable((cmsIT8*) hIT8)->SheetType; 1332 } 1333 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 2696 int CMSEXPORT cmsIT8GetPatchByName(cmsHANDLE hIT8, const char *cPatch) 2697 { 2698 _cmsAssert(hIT8 != NULL); 2699 2700 return LocatePatch((cmsIT8*)hIT8, cPatch); 2701 } 2702 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 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 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 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