1 /*
   2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   3  *
   4  * This code is free software; you can redistribute it and/or modify it
   5  * under the terms of the GNU General Public License version 2 only, as
   6  * published by the Free Software Foundation.  Oracle designates this
   7  * particular file as subject to the "Classpath" exception as provided
   8  * by Oracle in the LICENSE file that accompanied this code.
   9  *
  10  * This code is distributed in the hope that it will be useful, but WITHOUT
  11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  13  * version 2 for more details (a copy is included in the LICENSE file that
  14  * accompanied this code).
  15  *
  16  * You should have received a copy of the GNU General Public License version
  17  * 2 along with this work; if not, write to the Free Software Foundation,
  18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  19  *
  20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  21  * or visit www.oracle.com if you need additional information or have any
  22  * questions.
  23  */
  24 
  25 // This file is available under and governed by the GNU General Public
  26 // License version 2 only, as published by the Free Software Foundation.
  27 // However, the following notice accompanied the original version of this
  28 // file:
  29 //
  30 //---------------------------------------------------------------------------------
  31 //
  32 //  Little Color Management System
  33 //  Copyright (c) 1998-2023 Marti Maria Saguer
  34 //
  35 // Permission is hereby granted, free of charge, to any person obtaining
  36 // a copy of this software and associated documentation files (the "Software"),
  37 // to deal in the Software without restriction, including without limitation
  38 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
  39 // and/or sell copies of the Software, and to permit persons to whom the Software
  40 // is furnished to do so, subject to the following conditions:
  41 //
  42 // The above copyright notice and this permission notice shall be included in
  43 // all copies or substantial portions of the Software.
  44 //
  45 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  46 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  47 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  48 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  49 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  50 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  51 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  52 //
  53 //---------------------------------------------------------------------------------
  54 //
  55 
  56 #include "lcms2_internal.h"
  57 
  58 
  59 // ----------------------------------------------------------------------------------
  60 // Encoding & Decoding support functions
  61 // ----------------------------------------------------------------------------------
  62 
  63 //      Little-Endian to Big-Endian
  64 
  65 // Adjust a word value after being read/ before being written from/to an ICC profile
  66 cmsUInt16Number CMSEXPORT  _cmsAdjustEndianess16(cmsUInt16Number Word)
  67 {
  68 #ifndef CMS_USE_BIG_ENDIAN
  69 
  70     cmsUInt8Number* pByte = (cmsUInt8Number*) &Word;
  71     cmsUInt8Number tmp;
  72 
  73     tmp = pByte[0];
  74     pByte[0] = pByte[1];
  75     pByte[1] = tmp;
  76 #endif
  77 
  78     return Word;
  79 }
  80 
  81 
  82 // Transports to properly encoded values - note that icc profiles does use big endian notation.
  83 
  84 // 1 2 3 4
  85 // 4 3 2 1
  86 
  87 cmsUInt32Number CMSEXPORT  _cmsAdjustEndianess32(cmsUInt32Number DWord)
  88 {
  89 #ifndef CMS_USE_BIG_ENDIAN
  90 
  91     cmsUInt8Number* pByte = (cmsUInt8Number*) &DWord;
  92     cmsUInt8Number temp1;
  93     cmsUInt8Number temp2;
  94 
  95     temp1 = *pByte++;
  96     temp2 = *pByte++;
  97     *(pByte-1) = *pByte;
  98     *pByte++ = temp2;
  99     *(pByte-3) = *pByte;
 100     *pByte = temp1;
 101 #endif
 102     return DWord;
 103 }
 104 
 105 // 1 2 3 4 5 6 7 8
 106 // 8 7 6 5 4 3 2 1
 107 
 108 void CMSEXPORT  _cmsAdjustEndianess64(cmsUInt64Number* Result, cmsUInt64Number* QWord)
 109 {
 110 
 111 #ifndef CMS_USE_BIG_ENDIAN
 112 
 113     cmsUInt8Number* pIn  = (cmsUInt8Number*) QWord;
 114     cmsUInt8Number* pOut = (cmsUInt8Number*) Result;
 115 
 116     _cmsAssert(Result != NULL);
 117 
 118     pOut[7] = pIn[0];
 119     pOut[6] = pIn[1];
 120     pOut[5] = pIn[2];
 121     pOut[4] = pIn[3];
 122     pOut[3] = pIn[4];
 123     pOut[2] = pIn[5];
 124     pOut[1] = pIn[6];
 125     pOut[0] = pIn[7];
 126 
 127 #else
 128     _cmsAssert(Result != NULL);
 129 
 130 #  ifdef CMS_DONT_USE_INT64
 131     (*Result)[0] = (*QWord)[0];
 132     (*Result)[1] = (*QWord)[1];
 133 #  else
 134     *Result = *QWord;
 135 #  endif
 136 #endif
 137 }
 138 
 139 // Auxiliary -- read 8, 16 and 32-bit numbers
 140 cmsBool CMSEXPORT  _cmsReadUInt8Number(cmsIOHANDLER* io, cmsUInt8Number* n)
 141 {
 142     cmsUInt8Number tmp;
 143 
 144     _cmsAssert(io != NULL);
 145 
 146     if (io -> Read(io, &tmp, sizeof(cmsUInt8Number), 1) != 1)
 147             return FALSE;
 148 
 149     if (n != NULL) *n = tmp;
 150     return TRUE;
 151 }
 152 
 153 cmsBool CMSEXPORT  _cmsReadUInt16Number(cmsIOHANDLER* io, cmsUInt16Number* n)
 154 {
 155     cmsUInt16Number tmp;
 156 
 157     _cmsAssert(io != NULL);
 158 
 159     if (io -> Read(io, &tmp, sizeof(cmsUInt16Number), 1) != 1)
 160             return FALSE;
 161 
 162     if (n != NULL) *n = _cmsAdjustEndianess16(tmp);
 163     return TRUE;
 164 }
 165 
 166 cmsBool CMSEXPORT  _cmsReadUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, cmsUInt16Number* Array)
 167 {
 168     cmsUInt32Number i;
 169 
 170     _cmsAssert(io != NULL);
 171 
 172     for (i=0; i < n; i++) {
 173 
 174         if (Array != NULL) {
 175             if (!_cmsReadUInt16Number(io, Array + i)) return FALSE;
 176         }
 177         else {
 178             if (!_cmsReadUInt16Number(io, NULL)) return FALSE;
 179         }
 180 
 181     }
 182     return TRUE;
 183 }
 184 
 185 cmsBool CMSEXPORT  _cmsReadUInt32Number(cmsIOHANDLER* io, cmsUInt32Number* n)
 186 {
 187     cmsUInt32Number tmp;
 188 
 189     _cmsAssert(io != NULL);
 190 
 191     if (io -> Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1)
 192             return FALSE;
 193 
 194     if (n != NULL) *n = _cmsAdjustEndianess32(tmp);
 195     return TRUE;
 196 }
 197 
 198 cmsBool CMSEXPORT  _cmsReadFloat32Number(cmsIOHANDLER* io, cmsFloat32Number* n)
 199 {
 200     union typeConverter {
 201         cmsUInt32Number integer;
 202         cmsFloat32Number floating_point;
 203     } tmp;
 204 
 205     _cmsAssert(io != NULL);
 206 
 207     if (io->Read(io, &tmp.integer, sizeof(cmsUInt32Number), 1) != 1)
 208         return FALSE;
 209 
 210     if (n != NULL) {
 211 
 212         tmp.integer = _cmsAdjustEndianess32(tmp.integer);
 213         *n = tmp.floating_point;
 214 
 215         // Safeguard which covers against absurd values
 216         if (*n > 1E+20 || *n < -1E+20) return FALSE;
 217 
 218         #if defined(_MSC_VER) && _MSC_VER < 1800
 219            return TRUE;
 220         #elif defined (__BORLANDC__)
 221            return TRUE;
 222         #elif !defined(_MSC_VER) && (defined(__STDC_VERSION__) && __STDC_VERSION__ < 199901L)
 223            return TRUE;
 224         #else
 225 
 226            // fpclassify() required by C99 (only provided by MSVC >= 1800, VS2013 onwards)
 227            return ((fpclassify(*n) == FP_ZERO) || (fpclassify(*n) == FP_NORMAL));
 228         #endif
 229     }
 230 
 231     return TRUE;
 232 }
 233 
 234 
 235 cmsBool CMSEXPORT   _cmsReadUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n)
 236 {
 237     cmsUInt64Number tmp;
 238 
 239     _cmsAssert(io != NULL);
 240 
 241     if (io -> Read(io, &tmp, sizeof(cmsUInt64Number), 1) != 1)
 242             return FALSE;
 243 
 244     if (n != NULL) {
 245 
 246         _cmsAdjustEndianess64(n, &tmp);
 247     }
 248 
 249     return TRUE;
 250 }
 251 
 252 
 253 cmsBool CMSEXPORT  _cmsRead15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number* n)
 254 {
 255     cmsUInt32Number tmp;
 256 
 257     _cmsAssert(io != NULL);
 258 
 259     if (io -> Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1)
 260             return FALSE;
 261 
 262     if (n != NULL) {
 263         *n = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32(tmp));
 264     }
 265 
 266     return TRUE;
 267 }
 268 
 269 
 270 cmsBool CMSEXPORT  _cmsReadXYZNumber(cmsIOHANDLER* io, cmsCIEXYZ* XYZ)
 271 {
 272     cmsEncodedXYZNumber xyz;
 273 
 274     _cmsAssert(io != NULL);
 275 
 276     if (io ->Read(io, &xyz, sizeof(cmsEncodedXYZNumber), 1) != 1) return FALSE;
 277 
 278     if (XYZ != NULL) {
 279 
 280         XYZ->X = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.X));
 281         XYZ->Y = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.Y));
 282         XYZ->Z = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.Z));
 283     }
 284     return TRUE;
 285 }
 286 
 287 cmsBool CMSEXPORT  _cmsWriteUInt8Number(cmsIOHANDLER* io, cmsUInt8Number n)
 288 {
 289     _cmsAssert(io != NULL);
 290 
 291     if (io -> Write(io, sizeof(cmsUInt8Number), &n) != 1)
 292             return FALSE;
 293 
 294     return TRUE;
 295 }
 296 
 297 cmsBool CMSEXPORT  _cmsWriteUInt16Number(cmsIOHANDLER* io, cmsUInt16Number n)
 298 {
 299     cmsUInt16Number tmp;
 300 
 301     _cmsAssert(io != NULL);
 302 
 303     tmp = _cmsAdjustEndianess16(n);
 304     if (io -> Write(io, sizeof(cmsUInt16Number), &tmp) != 1)
 305             return FALSE;
 306 
 307     return TRUE;
 308 }
 309 
 310 cmsBool CMSEXPORT  _cmsWriteUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, const cmsUInt16Number* Array)
 311 {
 312     cmsUInt32Number i;
 313 
 314     _cmsAssert(io != NULL);
 315     _cmsAssert(Array != NULL);
 316 
 317     for (i=0; i < n; i++) {
 318         if (!_cmsWriteUInt16Number(io, Array[i])) return FALSE;
 319     }
 320 
 321     return TRUE;
 322 }
 323 
 324 cmsBool CMSEXPORT  _cmsWriteUInt32Number(cmsIOHANDLER* io, cmsUInt32Number n)
 325 {
 326     cmsUInt32Number tmp;
 327 
 328     _cmsAssert(io != NULL);
 329 
 330     tmp = _cmsAdjustEndianess32(n);
 331     if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1)
 332             return FALSE;
 333 
 334     return TRUE;
 335 }
 336 
 337 
 338 cmsBool CMSEXPORT  _cmsWriteFloat32Number(cmsIOHANDLER* io, cmsFloat32Number n)
 339 {
 340     union typeConverter {
 341         cmsUInt32Number integer;
 342         cmsFloat32Number floating_point;
 343     } tmp;
 344 
 345     tmp.floating_point = n;
 346     tmp.integer = _cmsAdjustEndianess32(tmp.integer);
 347     if (io -> Write(io, sizeof(cmsUInt32Number), &tmp.integer) != 1)
 348             return FALSE;
 349 
 350     return TRUE;
 351 }
 352 
 353 cmsBool CMSEXPORT  _cmsWriteUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n)
 354 {
 355     cmsUInt64Number tmp;
 356 
 357     _cmsAssert(io != NULL);
 358 
 359     _cmsAdjustEndianess64(&tmp, n);
 360     if (io -> Write(io, sizeof(cmsUInt64Number), &tmp) != 1)
 361             return FALSE;
 362 
 363     return TRUE;
 364 }
 365 
 366 cmsBool CMSEXPORT  _cmsWrite15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number n)
 367 {
 368     cmsUInt32Number tmp;
 369 
 370     _cmsAssert(io != NULL);
 371 
 372     tmp = _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(n));
 373     if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1)
 374             return FALSE;
 375 
 376     return TRUE;
 377 }
 378 
 379 cmsBool CMSEXPORT  _cmsWriteXYZNumber(cmsIOHANDLER* io, const cmsCIEXYZ* XYZ)
 380 {
 381     cmsEncodedXYZNumber xyz;
 382 
 383     _cmsAssert(io != NULL);
 384     _cmsAssert(XYZ != NULL);
 385 
 386     xyz.X = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(XYZ->X));
 387     xyz.Y = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(XYZ->Y));
 388     xyz.Z = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(XYZ->Z));
 389 
 390     return io -> Write(io,  sizeof(cmsEncodedXYZNumber), &xyz);
 391 }
 392 
 393 // from Fixed point 8.8 to double
 394 cmsFloat64Number CMSEXPORT _cms8Fixed8toDouble(cmsUInt16Number fixed8)
 395 {
 396     return fixed8 / 256.0;
 397 }
 398 
 399 cmsUInt16Number CMSEXPORT _cmsDoubleTo8Fixed8(cmsFloat64Number val)
 400 {
 401     cmsS15Fixed16Number GammaFixed32 = _cmsDoubleTo15Fixed16(val);
 402     return  (cmsUInt16Number) ((GammaFixed32 >> 8) & 0xFFFF);
 403 }
 404 
 405 // from Fixed point 15.16 to double
 406 cmsFloat64Number CMSEXPORT _cms15Fixed16toDouble(cmsS15Fixed16Number fix32)
 407 {
 408     return fix32 / 65536.0;
 409 }
 410 
 411 // from double to Fixed point 15.16
 412 cmsS15Fixed16Number CMSEXPORT _cmsDoubleTo15Fixed16(cmsFloat64Number v)
 413 {
 414     return ((cmsS15Fixed16Number) floor((v)*65536.0 + 0.5));
 415 }
 416 
 417 // Date/Time functions
 418 
 419 void CMSEXPORT _cmsDecodeDateTimeNumber(const cmsDateTimeNumber *Source, struct tm *Dest)
 420 {
 421 
 422     _cmsAssert(Dest != NULL);
 423     _cmsAssert(Source != NULL);
 424 
 425     Dest->tm_sec   = _cmsAdjustEndianess16(Source->seconds);
 426     Dest->tm_min   = _cmsAdjustEndianess16(Source->minutes);
 427     Dest->tm_hour  = _cmsAdjustEndianess16(Source->hours);
 428     Dest->tm_mday  = _cmsAdjustEndianess16(Source->day);
 429     Dest->tm_mon   = _cmsAdjustEndianess16(Source->month) - 1;
 430     Dest->tm_year  = _cmsAdjustEndianess16(Source->year) - 1900;
 431     Dest->tm_wday  = -1;
 432     Dest->tm_yday  = -1;
 433     Dest->tm_isdst = 0;
 434 }
 435 
 436 void CMSEXPORT _cmsEncodeDateTimeNumber(cmsDateTimeNumber *Dest, const struct tm *Source)
 437 {
 438     _cmsAssert(Dest != NULL);
 439     _cmsAssert(Source != NULL);
 440 
 441     Dest->seconds = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_sec);
 442     Dest->minutes = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_min);
 443     Dest->hours   = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_hour);
 444     Dest->day     = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_mday);
 445     Dest->month   = _cmsAdjustEndianess16((cmsUInt16Number) (Source->tm_mon + 1));
 446     Dest->year    = _cmsAdjustEndianess16((cmsUInt16Number) (Source->tm_year + 1900));
 447 }
 448 
 449 // Read base and return type base
 450 cmsTagTypeSignature CMSEXPORT _cmsReadTypeBase(cmsIOHANDLER* io)
 451 {
 452     _cmsTagBase Base;
 453 
 454     _cmsAssert(io != NULL);
 455 
 456     if (io -> Read(io, &Base, sizeof(_cmsTagBase), 1) != 1)
 457         return (cmsTagTypeSignature) 0;
 458 
 459     return (cmsTagTypeSignature) _cmsAdjustEndianess32(Base.sig);
 460 }
 461 
 462 // Setup base marker
 463 cmsBool  CMSEXPORT _cmsWriteTypeBase(cmsIOHANDLER* io, cmsTagTypeSignature sig)
 464 {
 465     _cmsTagBase  Base;
 466 
 467     _cmsAssert(io != NULL);
 468 
 469     Base.sig = (cmsTagTypeSignature) _cmsAdjustEndianess32(sig);
 470     memset(&Base.reserved, 0, sizeof(Base.reserved));
 471     return io -> Write(io, sizeof(_cmsTagBase), &Base);
 472 }
 473 
 474 cmsBool CMSEXPORT _cmsReadAlignment(cmsIOHANDLER* io)
 475 {
 476     cmsUInt8Number  Buffer[4];
 477     cmsUInt32Number NextAligned, At;
 478     cmsUInt32Number BytesToNextAlignedPos;
 479 
 480     _cmsAssert(io != NULL);
 481 
 482     At = io -> Tell(io);
 483     NextAligned = _cmsALIGNLONG(At);
 484     BytesToNextAlignedPos = NextAligned - At;
 485     if (BytesToNextAlignedPos == 0) return TRUE;
 486     if (BytesToNextAlignedPos > 4)  return FALSE;
 487 
 488     return (io ->Read(io, Buffer, BytesToNextAlignedPos, 1) == 1);
 489 }
 490 
 491 cmsBool CMSEXPORT _cmsWriteAlignment(cmsIOHANDLER* io)
 492 {
 493     cmsUInt8Number  Buffer[4];
 494     cmsUInt32Number NextAligned, At;
 495     cmsUInt32Number BytesToNextAlignedPos;
 496 
 497     _cmsAssert(io != NULL);
 498 
 499     At = io -> Tell(io);
 500     NextAligned = _cmsALIGNLONG(At);
 501     BytesToNextAlignedPos = NextAligned - At;
 502     if (BytesToNextAlignedPos == 0) return TRUE;
 503     if (BytesToNextAlignedPos > 4)  return FALSE;
 504 
 505     memset(Buffer, 0, BytesToNextAlignedPos);
 506     return io -> Write(io, BytesToNextAlignedPos, Buffer);
 507 }
 508 
 509 
 510 // To deal with text streams. 2K at most
 511 cmsBool CMSEXPORT _cmsIOPrintf(cmsIOHANDLER* io, const char* frm, ...)
 512 {
 513     va_list args;
 514     int len;
 515     cmsUInt8Number Buffer[2048];
 516     cmsBool rc;
 517     cmsUInt8Number* ptr;
 518 
 519     _cmsAssert(io != NULL);
 520     _cmsAssert(frm != NULL);
 521 
 522     va_start(args, frm);
 523 
 524     len = vsnprintf((char*) Buffer, 2047, frm, args);
 525     if (len < 0) {
 526         va_end(args);
 527         return FALSE;   // Truncated, which is a fatal error for us
 528     }
 529 
 530     // setlocale may be active, no commas are needed in PS generator
 531     // and PS generator is our only client
 532     for (ptr = Buffer; *ptr; ptr++)
 533     {
 534         if (*ptr == ',') *ptr = '.';
 535     }
 536 
 537     rc = io ->Write(io, (cmsUInt32Number) len, Buffer);
 538 
 539     va_end(args);
 540 
 541     return rc;
 542 }
 543 
 544 
 545 // Plugin memory management -------------------------------------------------------------------------------------------------
 546 
 547 // Specialized malloc for plug-ins, that is freed upon exit.
 548 void* _cmsPluginMalloc(cmsContext ContextID, cmsUInt32Number size)
 549 {
 550     struct _cmsContext_struct* ctx = _cmsGetContext(ContextID);
 551 
 552     if (ctx ->MemPool == NULL) {
 553 
 554         if (ContextID == NULL) {
 555 
 556             ctx->MemPool = _cmsCreateSubAlloc(0, 2*1024);
 557             if (ctx->MemPool == NULL) return NULL;
 558         }
 559         else {
 560             cmsSignalError(ContextID, cmsERROR_CORRUPTION_DETECTED, "NULL memory pool on context");
 561             return NULL;
 562         }
 563     }
 564 
 565     return _cmsSubAlloc(ctx->MemPool, size);
 566 }
 567 
 568 
 569 // Main plug-in dispatcher
 570 cmsBool CMSEXPORT cmsPlugin(void* Plug_in)
 571 {
 572     return cmsPluginTHR(NULL, Plug_in);
 573 }
 574 
 575 cmsBool CMSEXPORT cmsPluginTHR(cmsContext id, void* Plug_in)
 576 {
 577     cmsPluginBase* Plugin;
 578 
 579     for (Plugin = (cmsPluginBase*) Plug_in;
 580          Plugin != NULL;
 581          Plugin = Plugin -> Next) {
 582 
 583             if (Plugin -> Magic != cmsPluginMagicNumber) {
 584                 cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin");
 585                 return FALSE;
 586             }
 587 
 588             if (Plugin ->ExpectedVersion > LCMS_VERSION) {
 589                 cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "plugin needs Little CMS %d, current version is %d",
 590                     Plugin ->ExpectedVersion, LCMS_VERSION);
 591                 return FALSE;
 592             }
 593 
 594             switch (Plugin -> Type) {
 595 
 596                 case cmsPluginMemHandlerSig:
 597                     if (!_cmsRegisterMemHandlerPlugin(id, Plugin)) return FALSE;
 598                     break;
 599 
 600                 case cmsPluginInterpolationSig:
 601                     if (!_cmsRegisterInterpPlugin(id, Plugin)) return FALSE;
 602                     break;
 603 
 604                 case cmsPluginTagTypeSig:
 605                     if (!_cmsRegisterTagTypePlugin(id, Plugin)) return FALSE;
 606                     break;
 607 
 608                 case cmsPluginTagSig:
 609                     if (!_cmsRegisterTagPlugin(id, Plugin)) return FALSE;
 610                     break;
 611 
 612                 case cmsPluginFormattersSig:
 613                     if (!_cmsRegisterFormattersPlugin(id, Plugin)) return FALSE;
 614                     break;
 615 
 616                 case cmsPluginRenderingIntentSig:
 617                     if (!_cmsRegisterRenderingIntentPlugin(id, Plugin)) return FALSE;
 618                     break;
 619 
 620                 case cmsPluginParametricCurveSig:
 621                     if (!_cmsRegisterParametricCurvesPlugin(id, Plugin)) return FALSE;
 622                     break;
 623 
 624                 case cmsPluginMultiProcessElementSig:
 625                     if (!_cmsRegisterMultiProcessElementPlugin(id, Plugin)) return FALSE;
 626                     break;
 627 
 628                 case cmsPluginOptimizationSig:
 629                     if (!_cmsRegisterOptimizationPlugin(id, Plugin)) return FALSE;
 630                     break;
 631 
 632                 case cmsPluginTransformSig:
 633                     if (!_cmsRegisterTransformPlugin(id, Plugin)) return FALSE;
 634                     break;
 635 
 636                 case cmsPluginMutexSig:
 637                     if (!_cmsRegisterMutexPlugin(id, Plugin)) return FALSE;
 638                     break;
 639 
 640                 case cmsPluginParalellizationSig:
 641                     if (!_cmsRegisterParallelizationPlugin(id, Plugin)) return FALSE;
 642                     break;
 643 
 644                 default:
 645                     cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin type '%X'", Plugin -> Type);
 646                     return FALSE;
 647             }
 648     }
 649 
 650     // Keep a reference to the plug-in
 651     return TRUE;
 652 }
 653 
 654 
 655 // Revert all plug-ins to default
 656 void CMSEXPORT cmsUnregisterPlugins(void)
 657 {
 658     cmsUnregisterPluginsTHR(NULL);
 659 }
 660 
 661 
 662 // The Global storage for system context. This is the one and only global variable
 663 // pointers structure. All global vars are referenced here.
 664 static struct _cmsContext_struct globalContext = {
 665 
 666     NULL,                                // Not in the linked list
 667     NULL,                                // No suballocator
 668     {
 669         NULL,                            //  UserPtr,
 670         &_cmsLogErrorChunk,              //  Logger,
 671         &_cmsAlarmCodesChunk,            //  AlarmCodes,
 672         &_cmsAdaptationStateChunk,       //  AdaptationState,
 673         &_cmsMemPluginChunk,             //  MemPlugin,
 674         &_cmsInterpPluginChunk,          //  InterpPlugin,
 675         &_cmsCurvesPluginChunk,          //  CurvesPlugin,
 676         &_cmsFormattersPluginChunk,      //  FormattersPlugin,
 677         &_cmsTagTypePluginChunk,         //  TagTypePlugin,
 678         &_cmsTagPluginChunk,             //  TagPlugin,
 679         &_cmsIntentsPluginChunk,         //  IntentPlugin,
 680         &_cmsMPETypePluginChunk,         //  MPEPlugin,
 681         &_cmsOptimizationPluginChunk,    //  OptimizationPlugin,
 682         &_cmsTransformPluginChunk,       //  TransformPlugin,
 683         &_cmsMutexPluginChunk,           //  MutexPlugin,
 684         &_cmsParallelizationPluginChunk  //  ParallelizationPlugin
 685     },
 686 
 687     { NULL, NULL, NULL, NULL, NULL, NULL } // The default memory allocator is not used for context 0
 688 };
 689 
 690 
 691 // The context pool (linked list head)
 692 static _cmsMutex _cmsContextPoolHeadMutex = CMS_MUTEX_INITIALIZER;
 693 static struct _cmsContext_struct* _cmsContextPoolHead = NULL;
 694 
 695 
 696 // Make sure context is initialized (needed on windows)
 697 static
 698 cmsBool InitContextMutex(void)
 699 {
 700     // See the comments regarding locking in lcms2_internal.h
 701     // for an explanation of why we need the following code.
 702 #ifndef CMS_NO_PTHREADS
 703 #ifdef CMS_IS_WINDOWS_
 704 #ifndef CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT
 705 
 706     static cmsBool already_initialized = FALSE;
 707 
 708     if (!already_initialized)
 709     {
 710         static HANDLE _cmsWindowsInitMutex = NULL;
 711         static volatile HANDLE* mutex = &_cmsWindowsInitMutex;
 712 
 713         if (*mutex == NULL)
 714         {
 715             HANDLE p = CreateMutex(NULL, FALSE, NULL);
 716             if (p && InterlockedCompareExchangePointer((void**)mutex, (void*)p, NULL) != NULL)
 717                 CloseHandle(p);
 718         }
 719         if (*mutex == NULL || WaitForSingleObject(*mutex, INFINITE) == WAIT_FAILED)
 720         {
 721             cmsSignalError(0, cmsERROR_INTERNAL, "Mutex lock failed");
 722             return FALSE;
 723         }
 724         if (((void**)&_cmsContextPoolHeadMutex)[0] == NULL)
 725             InitializeCriticalSection(&_cmsContextPoolHeadMutex);
 726         if (*mutex == NULL || !ReleaseMutex(*mutex))
 727         {
 728             cmsSignalError(0, cmsERROR_INTERNAL, "Mutex unlock failed");
 729             return FALSE;
 730         }
 731         already_initialized = TRUE;
 732     }
 733 #endif
 734 #endif
 735 #endif
 736 
 737     return TRUE;
 738 }
 739 
 740 
 741 
 742 // Internal, get associated pointer, with guessing. Never returns NULL.
 743 struct _cmsContext_struct* _cmsGetContext(cmsContext ContextID)
 744 {
 745     struct _cmsContext_struct* id = (struct _cmsContext_struct*) ContextID;
 746     struct _cmsContext_struct* ctx;
 747 
 748     // On 0, use global settings
 749     if (id == NULL)
 750         return &globalContext;
 751 
 752     InitContextMutex();
 753 
 754     // Search
 755     _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
 756 
 757     for (ctx = _cmsContextPoolHead;
 758          ctx != NULL;
 759          ctx = ctx ->Next) {
 760 
 761             // Found it?
 762         if (id == ctx)
 763         {
 764             _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
 765             return ctx; // New-style context
 766         }
 767     }
 768 
 769     _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
 770     return &globalContext;
 771 }
 772 
 773 
 774 // Internal: get the memory area associanted with each context client
 775 // Returns the block assigned to the specific zone. Never return NULL.
 776 void* _cmsContextGetClientChunk(cmsContext ContextID, _cmsMemoryClient mc)
 777 {
 778     struct _cmsContext_struct* ctx;
 779     void *ptr;
 780 
 781     if ((int) mc < 0 || mc >= MemoryClientMax) {
 782 
 783            cmsSignalError(ContextID, cmsERROR_INTERNAL, "Bad context client -- possible corruption");
 784 
 785            // This is catastrophic. Should never reach here
 786            _cmsAssert(0);
 787 
 788            // Reverts to global context
 789            return globalContext.chunks[UserPtr];
 790     }
 791 
 792     ctx = _cmsGetContext(ContextID);
 793     ptr = ctx ->chunks[mc];
 794 
 795     if (ptr != NULL)
 796         return ptr;
 797 
 798     // A null ptr means no special settings for that context, and this
 799     // reverts to Context0 globals
 800     return globalContext.chunks[mc];
 801 }
 802 
 803 
 804 // This function returns the given context its default pristine state,
 805 // as no plug-ins were declared. There is no way to unregister a single
 806 // plug-in, as a single call to cmsPluginTHR() function may register
 807 // many different plug-ins simultaneously, then there is no way to
 808 // identify which plug-in to unregister.
 809 void CMSEXPORT cmsUnregisterPluginsTHR(cmsContext ContextID)
 810 {
 811     _cmsRegisterMemHandlerPlugin(ContextID, NULL);
 812     _cmsRegisterInterpPlugin(ContextID, NULL);
 813     _cmsRegisterTagTypePlugin(ContextID, NULL);
 814     _cmsRegisterTagPlugin(ContextID, NULL);
 815     _cmsRegisterFormattersPlugin(ContextID, NULL);
 816     _cmsRegisterRenderingIntentPlugin(ContextID, NULL);
 817     _cmsRegisterParametricCurvesPlugin(ContextID, NULL);
 818     _cmsRegisterMultiProcessElementPlugin(ContextID, NULL);
 819     _cmsRegisterOptimizationPlugin(ContextID, NULL);
 820     _cmsRegisterTransformPlugin(ContextID, NULL);
 821     _cmsRegisterMutexPlugin(ContextID, NULL);
 822     _cmsRegisterParallelizationPlugin(ContextID, NULL);
 823 
 824 }
 825 
 826 
 827 // Returns the memory manager plug-in, if any, from the Plug-in bundle
 828 static
 829 cmsPluginMemHandler* _cmsFindMemoryPlugin(void* PluginBundle)
 830 {
 831     cmsPluginBase* Plugin;
 832 
 833     for (Plugin = (cmsPluginBase*) PluginBundle;
 834         Plugin != NULL;
 835         Plugin = Plugin -> Next) {
 836 
 837             if (Plugin -> Magic == cmsPluginMagicNumber &&
 838                 Plugin -> ExpectedVersion <= LCMS_VERSION &&
 839                 Plugin -> Type == cmsPluginMemHandlerSig) {
 840 
 841                     // Found!
 842                     return (cmsPluginMemHandler*) Plugin;
 843             }
 844     }
 845 
 846     // Nope, revert to defaults
 847     return NULL;
 848 }
 849 
 850 
 851 // Creates a new context with optional associated plug-ins. Caller may also specify an optional pointer to user-defined
 852 // data that will be forwarded to plug-ins and logger.
 853 cmsContext CMSEXPORT cmsCreateContext(void* Plugin, void* UserData)
 854 {
 855     struct _cmsContext_struct* ctx;
 856     struct _cmsContext_struct  fakeContext;
 857 
 858     if (!InitContextMutex()) return NULL;
 859 
 860     _cmsInstallAllocFunctions(_cmsFindMemoryPlugin(Plugin), &fakeContext.DefaultMemoryManager);
 861 
 862     fakeContext.chunks[UserPtr]     = UserData;
 863     fakeContext.chunks[MemPlugin]   = &fakeContext.DefaultMemoryManager;
 864 
 865     // Create the context structure.
 866     ctx = (struct _cmsContext_struct*) _cmsMalloc(&fakeContext, sizeof(struct _cmsContext_struct));
 867     if (ctx == NULL)
 868         return NULL;     // Something very wrong happened!
 869 
 870     // Init the structure and the memory manager
 871     memset(ctx, 0, sizeof(struct _cmsContext_struct));
 872 
 873     // Keep memory manager
 874     memcpy(&ctx->DefaultMemoryManager, &fakeContext.DefaultMemoryManager, sizeof(_cmsMemPluginChunk));
 875 
 876     // Maintain the linked list (with proper locking)
 877     _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
 878        ctx ->Next = _cmsContextPoolHead;
 879        _cmsContextPoolHead = ctx;
 880     _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
 881 
 882     ctx ->chunks[UserPtr]     = UserData;
 883     ctx ->chunks[MemPlugin]   = &ctx->DefaultMemoryManager;
 884 
 885     // Now we can allocate the pool by using default memory manager
 886     ctx ->MemPool = _cmsCreateSubAlloc(ctx, 22 * sizeof(void*));  // default size about 22 pointers
 887     if (ctx ->MemPool == NULL) {
 888 
 889          cmsDeleteContext(ctx);
 890         return NULL;
 891     }
 892 
 893     _cmsAllocLogErrorChunk(ctx, NULL);
 894     _cmsAllocAlarmCodesChunk(ctx, NULL);
 895     _cmsAllocAdaptationStateChunk(ctx, NULL);
 896     _cmsAllocMemPluginChunk(ctx, NULL);
 897     _cmsAllocInterpPluginChunk(ctx, NULL);
 898     _cmsAllocCurvesPluginChunk(ctx, NULL);
 899     _cmsAllocFormattersPluginChunk(ctx, NULL);
 900     _cmsAllocTagTypePluginChunk(ctx, NULL);
 901     _cmsAllocMPETypePluginChunk(ctx, NULL);
 902     _cmsAllocTagPluginChunk(ctx, NULL);
 903     _cmsAllocIntentsPluginChunk(ctx, NULL);
 904     _cmsAllocOptimizationPluginChunk(ctx, NULL);
 905     _cmsAllocTransformPluginChunk(ctx, NULL);
 906     _cmsAllocMutexPluginChunk(ctx, NULL);
 907     _cmsAllocParallelizationPluginChunk(ctx, NULL);
 908 
 909     // Setup the plug-ins
 910     if (!cmsPluginTHR(ctx, Plugin)) {
 911 
 912         cmsDeleteContext(ctx);
 913         return NULL;
 914     }
 915 
 916     return (cmsContext) ctx;
 917 }
 918 
 919 // Duplicates a context with all associated plug-ins.
 920 // Caller may specify an optional pointer to user-defined
 921 // data that will be forwarded to plug-ins and logger.
 922 cmsContext CMSEXPORT cmsDupContext(cmsContext ContextID, void* NewUserData)
 923 {
 924     int i;
 925     struct _cmsContext_struct* ctx;
 926     const struct _cmsContext_struct* src = _cmsGetContext(ContextID);
 927 
 928     void* userData = (NewUserData != NULL) ? NewUserData : src -> chunks[UserPtr];
 929 
 930 
 931     ctx = (struct _cmsContext_struct*) _cmsMalloc(ContextID, sizeof(struct _cmsContext_struct));
 932     if (ctx == NULL)
 933         return NULL;     // Something very wrong happened
 934 
 935     if (!InitContextMutex()) return NULL;
 936 
 937     // Setup default memory allocators
 938     memcpy(&ctx->DefaultMemoryManager, &src->DefaultMemoryManager, sizeof(ctx->DefaultMemoryManager));
 939 
 940     // Maintain the linked list
 941     _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
 942        ctx ->Next = _cmsContextPoolHead;
 943        _cmsContextPoolHead = ctx;
 944     _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
 945 
 946     ctx ->chunks[UserPtr]    = userData;
 947     ctx ->chunks[MemPlugin]  = &ctx->DefaultMemoryManager;
 948 
 949     ctx ->MemPool = _cmsCreateSubAlloc(ctx, 22 * sizeof(void*));
 950     if (ctx ->MemPool == NULL) {
 951 
 952          cmsDeleteContext(ctx);
 953         return NULL;
 954     }
 955 
 956     // Allocate all required chunks.
 957     _cmsAllocLogErrorChunk(ctx, src);
 958     _cmsAllocAlarmCodesChunk(ctx, src);
 959     _cmsAllocAdaptationStateChunk(ctx, src);
 960     _cmsAllocMemPluginChunk(ctx, src);
 961     _cmsAllocInterpPluginChunk(ctx, src);
 962     _cmsAllocCurvesPluginChunk(ctx, src);
 963     _cmsAllocFormattersPluginChunk(ctx, src);
 964     _cmsAllocTagTypePluginChunk(ctx, src);
 965     _cmsAllocMPETypePluginChunk(ctx, src);
 966     _cmsAllocTagPluginChunk(ctx, src);
 967     _cmsAllocIntentsPluginChunk(ctx, src);
 968     _cmsAllocOptimizationPluginChunk(ctx, src);
 969     _cmsAllocTransformPluginChunk(ctx, src);
 970     _cmsAllocMutexPluginChunk(ctx, src);
 971     _cmsAllocParallelizationPluginChunk(ctx, src);
 972 
 973     // Make sure no one failed
 974     for (i=Logger; i < MemoryClientMax; i++) {
 975 
 976         if (src ->chunks[i] == NULL) {
 977             cmsDeleteContext((cmsContext) ctx);
 978             return NULL;
 979         }
 980     }
 981 
 982     return (cmsContext) ctx;
 983 }
 984 
 985 
 986 // Frees any resources associated with the given context,
 987 // and destroys the context placeholder.
 988 // The ContextID can no longer be used in any THR operation.
 989 void CMSEXPORT cmsDeleteContext(cmsContext ContextID)
 990 {
 991     if (ContextID == NULL) {
 992 
 993         cmsUnregisterPlugins();
 994         if (globalContext.MemPool != NULL)
 995             _cmsSubAllocDestroy(globalContext.MemPool);
 996         globalContext.MemPool = NULL;
 997     }
 998     else {
 999 
1000         struct _cmsContext_struct* ctx = (struct _cmsContext_struct*) ContextID;
1001         struct _cmsContext_struct  fakeContext;
1002         struct _cmsContext_struct* prev;
1003 
1004 
1005         InitContextMutex();
1006 
1007         memcpy(&fakeContext.DefaultMemoryManager, &ctx->DefaultMemoryManager, sizeof(ctx->DefaultMemoryManager));
1008 
1009         fakeContext.chunks[UserPtr]     = ctx ->chunks[UserPtr];
1010         fakeContext.chunks[MemPlugin]   = &fakeContext.DefaultMemoryManager;
1011 
1012         // Get rid of plugins
1013         cmsUnregisterPluginsTHR(ContextID);
1014 
1015         // Since all memory is allocated in the private pool, all what we need to do is destroy the pool
1016         if (ctx -> MemPool != NULL)
1017               _cmsSubAllocDestroy(ctx ->MemPool);
1018         ctx -> MemPool = NULL;
1019 
1020         // Maintain list
1021         _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
1022         if (_cmsContextPoolHead == ctx) {
1023 
1024             _cmsContextPoolHead = ctx->Next;
1025         }
1026         else {
1027 
1028             // Search for previous
1029             for (prev = _cmsContextPoolHead;
1030                  prev != NULL;
1031                  prev = prev ->Next)
1032             {
1033                 if (prev -> Next == ctx) {
1034                     prev -> Next = ctx ->Next;
1035                     break;
1036                 }
1037             }
1038         }
1039         _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
1040 
1041         // free the memory block itself
1042         _cmsFree(&fakeContext, ctx);
1043     }
1044 }
1045 
1046 // Returns the user data associated to the given ContextID, or NULL if no user data was attached on context creation
1047 void* CMSEXPORT cmsGetContextUserData(cmsContext ContextID)
1048 {
1049     return _cmsContextGetClientChunk(ContextID, UserPtr);
1050 }
1051 
1052 
1053 // Use context mutex to provide thread-safe time
1054 cmsBool _cmsGetTime(struct tm* ptr_time)
1055 {
1056     struct tm* t;
1057 #if defined(HAVE_GMTIME_R) || defined(HAVE_GMTIME_S)
1058     struct tm tm;
1059 #endif
1060 
1061     time_t now = time(NULL);
1062 
1063 #ifdef HAVE_GMTIME_R
1064     t = gmtime_r(&now, &tm);
1065 #elif defined(HAVE_GMTIME_S)
1066     t = gmtime_s(&tm, &now) == 0 ? &tm : NULL;
1067 #else
1068     if (!InitContextMutex()) return FALSE;
1069 
1070     _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
1071     t = gmtime(&now);
1072     _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
1073 #endif
1074 
1075     if (t == NULL)
1076         return FALSE;
1077     else {
1078         *ptr_time = *t;
1079         return TRUE;
1080     }
1081 }