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