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