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