//---------------------------------------------------------------------------------
//
//  Little Color Management System
//  Copyright (c) 1998-2020 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//---------------------------------------------------------------------------------
//
 
#include "lcms2_internal.h"
 
 
// ----------------------------------------------------------------------------------
// Encoding & Decoding support functions
// ----------------------------------------------------------------------------------
 
//      Little-Endian to Big-Endian
 
// Adjust a word value after being read/ before being written from/to an ICC profile
cmsUInt16Number CMSEXPORT  _cmsAdjustEndianess16(cmsUInt16Number Word)
{
#ifndef CMS_USE_BIG_ENDIAN
 
    cmsUInt8Number* pByte = (cmsUInt8Number*) &Word;
    cmsUInt8Number tmp;
 
    tmp = pByte[0];
    pByte[0] = pByte[1];
    pByte[1] = tmp;
#endif
 
    return Word;
}
 
 
// Transports to properly encoded values - note that icc profiles does use big endian notation.
 
// 1 2 3 4
// 4 3 2 1
 
cmsUInt32Number CMSEXPORT  _cmsAdjustEndianess32(cmsUInt32Number DWord)
{
#ifndef CMS_USE_BIG_ENDIAN
 
    cmsUInt8Number* pByte = (cmsUInt8Number*) &DWord;
    cmsUInt8Number temp1;
    cmsUInt8Number temp2;
 
    temp1 = *pByte++;
    temp2 = *pByte++;
    *(pByte-1) = *pByte;
    *pByte++ = temp2;
    *(pByte-3) = *pByte;
    *pByte = temp1;
#endif
    return DWord;
}
 
// 1 2 3 4 5 6 7 8
// 8 7 6 5 4 3 2 1
 
void CMSEXPORT  _cmsAdjustEndianess64(cmsUInt64Number* Result, cmsUInt64Number* QWord)
{
 
#ifndef CMS_USE_BIG_ENDIAN
 
    cmsUInt8Number* pIn  = (cmsUInt8Number*) QWord;
    cmsUInt8Number* pOut = (cmsUInt8Number*) Result;
 
    _cmsAssert(Result != NULL);
 
    pOut[7] = pIn[0];
    pOut[6] = pIn[1];
    pOut[5] = pIn[2];
    pOut[4] = pIn[3];
    pOut[3] = pIn[4];
    pOut[2] = pIn[5];
    pOut[1] = pIn[6];
    pOut[0] = pIn[7];
 
#else
    _cmsAssert(Result != NULL);
 
#  ifdef CMS_DONT_USE_INT64
    (*Result)[0] = (*QWord)[0];
    (*Result)[1] = (*QWord)[1];
#  else
    *Result = *QWord;
#  endif
#endif
}
 
// Auxiliary -- read 8, 16 and 32-bit numbers
cmsBool CMSEXPORT  _cmsReadUInt8Number(cmsIOHANDLER* io, cmsUInt8Number* n)
{
    cmsUInt8Number tmp;
 
    _cmsAssert(io != NULL);
 
    if (io -> Read(io, &tmp, sizeof(cmsUInt8Number), 1) != 1)
            return FALSE;
 
    if (n != NULL) *n = tmp;
    return TRUE;
}
 
cmsBool CMSEXPORT  _cmsReadUInt16Number(cmsIOHANDLER* io, cmsUInt16Number* n)
{
    cmsUInt16Number tmp;
 
    _cmsAssert(io != NULL);
 
    if (io -> Read(io, &tmp, sizeof(cmsUInt16Number), 1) != 1)
            return FALSE;
 
    if (n != NULL) *n = _cmsAdjustEndianess16(tmp);
    return TRUE;
}
 
cmsBool CMSEXPORT  _cmsReadUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, cmsUInt16Number* Array)
{
    cmsUInt32Number i;
 
    _cmsAssert(io != NULL);
 
    for (i=0; i < n; i++) {
 
        if (Array != NULL) {
            if (!_cmsReadUInt16Number(io, Array + i)) return FALSE;
        }
        else {
            if (!_cmsReadUInt16Number(io, NULL)) return FALSE;
        }
 
    }
    return TRUE;
}
 
cmsBool CMSEXPORT  _cmsReadUInt32Number(cmsIOHANDLER* io, cmsUInt32Number* n)
{
    cmsUInt32Number tmp;
 
    _cmsAssert(io != NULL);
 
    if (io -> Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1)
            return FALSE;
 
    if (n != NULL) *n = _cmsAdjustEndianess32(tmp);
    return TRUE;
}
 
cmsBool CMSEXPORT  _cmsReadFloat32Number(cmsIOHANDLER* io, cmsFloat32Number* n)
{
    cmsUInt32Number tmp;
 
    _cmsAssert(io != NULL);
 
    if (io->Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1)
        return FALSE;
 
    if (n != NULL) {
 
        tmp = _cmsAdjustEndianess32(tmp);
        *n = *(cmsFloat32Number*)(void*)&tmp;
        
        // Safeguard which covers against absurd values
        if (*n > 1E+20 || *n < -1E+20) return FALSE;
 
        #if defined(_MSC_VER) && _MSC_VER < 1800
           return TRUE;
        #elif defined (__BORLANDC__)
           return TRUE;
        #elif !defined(_MSC_VER) && (defined(__STDC_VERSION__) && __STDC_VERSION__ < 199901L)
           return TRUE;
        #else
 
           // fpclassify() required by C99 (only provided by MSVC >= 1800, VS2013 onwards)
           return ((fpclassify(*n) == FP_ZERO) || (fpclassify(*n) == FP_NORMAL));
        #endif        
    }
 
    return TRUE;
}
 
 
cmsBool CMSEXPORT   _cmsReadUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n)
{
    cmsUInt64Number tmp;
 
    _cmsAssert(io != NULL);
 
    if (io -> Read(io, &tmp, sizeof(cmsUInt64Number), 1) != 1)
            return FALSE;
 
    if (n != NULL) {
 
        _cmsAdjustEndianess64(n, &tmp);
    }
 
    return TRUE;
}
 
 
cmsBool CMSEXPORT  _cmsRead15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number* n)
{
    cmsUInt32Number tmp;
 
    _cmsAssert(io != NULL);
 
    if (io -> Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1)
            return FALSE;
 
    if (n != NULL) {
        *n = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32(tmp));
    }
 
    return TRUE;
}
 
 
cmsBool CMSEXPORT  _cmsReadXYZNumber(cmsIOHANDLER* io, cmsCIEXYZ* XYZ)
{
    cmsEncodedXYZNumber xyz;
 
    _cmsAssert(io != NULL);
 
    if (io ->Read(io, &xyz, sizeof(cmsEncodedXYZNumber), 1) != 1) return FALSE;
 
    if (XYZ != NULL) {
 
        XYZ->X = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.X));
        XYZ->Y = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.Y));
        XYZ->Z = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.Z));
    }
    return TRUE;
}
 
cmsBool CMSEXPORT  _cmsWriteUInt8Number(cmsIOHANDLER* io, cmsUInt8Number n)
{
    _cmsAssert(io != NULL);
 
    if (io -> Write(io, sizeof(cmsUInt8Number), &n) != 1)
            return FALSE;
 
    return TRUE;
}
 
cmsBool CMSEXPORT  _cmsWriteUInt16Number(cmsIOHANDLER* io, cmsUInt16Number n)
{
    cmsUInt16Number tmp;
 
    _cmsAssert(io != NULL);
 
    tmp = _cmsAdjustEndianess16(n);
    if (io -> Write(io, sizeof(cmsUInt16Number), &tmp) != 1)
            return FALSE;
 
    return TRUE;
}
 
cmsBool CMSEXPORT  _cmsWriteUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, const cmsUInt16Number* Array)
{
    cmsUInt32Number i;
 
    _cmsAssert(io != NULL);
    _cmsAssert(Array != NULL);
 
    for (i=0; i < n; i++) {
        if (!_cmsWriteUInt16Number(io, Array[i])) return FALSE;
    }
 
    return TRUE;
}
 
cmsBool CMSEXPORT  _cmsWriteUInt32Number(cmsIOHANDLER* io, cmsUInt32Number n)
{
    cmsUInt32Number tmp;
 
    _cmsAssert(io != NULL);
 
    tmp = _cmsAdjustEndianess32(n);
    if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1)
            return FALSE;
 
    return TRUE;
}
 
 
cmsBool CMSEXPORT  _cmsWriteFloat32Number(cmsIOHANDLER* io, cmsFloat32Number n)
{
    cmsUInt32Number tmp;
 
    _cmsAssert(io != NULL);
 
    tmp = *(cmsUInt32Number*) (void*) &n;
    tmp = _cmsAdjustEndianess32(tmp);
    if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1)
            return FALSE;
 
    return TRUE;
}
 
cmsBool CMSEXPORT  _cmsWriteUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n)
{
    cmsUInt64Number tmp;
 
    _cmsAssert(io != NULL);
 
    _cmsAdjustEndianess64(&tmp, n);
    if (io -> Write(io, sizeof(cmsUInt64Number), &tmp) != 1)
            return FALSE;
 
    return TRUE;
}
 
cmsBool CMSEXPORT  _cmsWrite15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number n)
{
    cmsUInt32Number tmp;
 
    _cmsAssert(io != NULL);
 
    tmp = _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(n));
    if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1)
            return FALSE;
 
    return TRUE;
}
 
cmsBool CMSEXPORT  _cmsWriteXYZNumber(cmsIOHANDLER* io, const cmsCIEXYZ* XYZ)
{
    cmsEncodedXYZNumber xyz;
 
    _cmsAssert(io != NULL);
    _cmsAssert(XYZ != NULL);
 
    xyz.X = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(XYZ->X));
    xyz.Y = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(XYZ->Y));
    xyz.Z = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(XYZ->Z));
 
    return io -> Write(io,  sizeof(cmsEncodedXYZNumber), &xyz);
}
 
// from Fixed point 8.8 to double
cmsFloat64Number CMSEXPORT _cms8Fixed8toDouble(cmsUInt16Number fixed8)
{
       cmsUInt8Number  msb, lsb;
 
       lsb = (cmsUInt8Number) (fixed8 & 0xff);
       msb = (cmsUInt8Number) (((cmsUInt16Number) fixed8 >> 8) & 0xff);
 
       return (cmsFloat64Number) ((cmsFloat64Number) msb + ((cmsFloat64Number) lsb / 256.0));
}
 
cmsUInt16Number CMSEXPORT _cmsDoubleTo8Fixed8(cmsFloat64Number val)
{
    cmsS15Fixed16Number GammaFixed32 = _cmsDoubleTo15Fixed16(val);
    return  (cmsUInt16Number) ((GammaFixed32 >> 8) & 0xFFFF);
}
 
// from Fixed point 15.16 to double
cmsFloat64Number CMSEXPORT _cms15Fixed16toDouble(cmsS15Fixed16Number fix32)
{
    cmsFloat64Number floater, sign, mid;
    int Whole, FracPart;
 
    sign  = (fix32 < 0 ? -1 : 1);
    fix32 = abs(fix32);
 
    Whole     = (cmsUInt16Number)(fix32 >> 16) & 0xffff;
    FracPart  = (cmsUInt16Number)(fix32 & 0xffff);
 
    mid     = (cmsFloat64Number) FracPart / 65536.0;
    floater = (cmsFloat64Number) Whole + mid;
 
    return sign * floater;
}
 
// from double to Fixed point 15.16
cmsS15Fixed16Number CMSEXPORT _cmsDoubleTo15Fixed16(cmsFloat64Number v)
{
    return ((cmsS15Fixed16Number) floor((v)*65536.0 + 0.5));
}
 
// Date/Time functions
 
void CMSEXPORT _cmsDecodeDateTimeNumber(const cmsDateTimeNumber *Source, struct tm *Dest)
{
 
    _cmsAssert(Dest != NULL);
    _cmsAssert(Source != NULL);
 
    Dest->tm_sec   = _cmsAdjustEndianess16(Source->seconds);
    Dest->tm_min   = _cmsAdjustEndianess16(Source->minutes);
    Dest->tm_hour  = _cmsAdjustEndianess16(Source->hours);
    Dest->tm_mday  = _cmsAdjustEndianess16(Source->day);
    Dest->tm_mon   = _cmsAdjustEndianess16(Source->month) - 1;
    Dest->tm_year  = _cmsAdjustEndianess16(Source->year) - 1900;
    Dest->tm_wday  = -1;
    Dest->tm_yday  = -1;
    Dest->tm_isdst = 0;
}
 
void CMSEXPORT _cmsEncodeDateTimeNumber(cmsDateTimeNumber *Dest, const struct tm *Source)
{
    _cmsAssert(Dest != NULL);
    _cmsAssert(Source != NULL);
 
    Dest->seconds = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_sec);
    Dest->minutes = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_min);
    Dest->hours   = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_hour);
    Dest->day     = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_mday);
    Dest->month   = _cmsAdjustEndianess16((cmsUInt16Number) (Source->tm_mon + 1));
    Dest->year    = _cmsAdjustEndianess16((cmsUInt16Number) (Source->tm_year + 1900));
}
 
// Read base and return type base
cmsTagTypeSignature CMSEXPORT _cmsReadTypeBase(cmsIOHANDLER* io)
{
    _cmsTagBase Base;
 
    _cmsAssert(io != NULL);
 
    if (io -> Read(io, &Base, sizeof(_cmsTagBase), 1) != 1)
        return (cmsTagTypeSignature) 0;
 
    return (cmsTagTypeSignature) _cmsAdjustEndianess32(Base.sig);
}
 
// Setup base marker
cmsBool  CMSEXPORT _cmsWriteTypeBase(cmsIOHANDLER* io, cmsTagTypeSignature sig)
{
    _cmsTagBase  Base;
 
    _cmsAssert(io != NULL);
 
    Base.sig = (cmsTagTypeSignature) _cmsAdjustEndianess32(sig);
    memset(&Base.reserved, 0, sizeof(Base.reserved));
    return io -> Write(io, sizeof(_cmsTagBase), &Base);
}
 
cmsBool CMSEXPORT _cmsReadAlignment(cmsIOHANDLER* io)
{
    cmsUInt8Number  Buffer[4];
    cmsUInt32Number NextAligned, At;
    cmsUInt32Number BytesToNextAlignedPos;
 
    _cmsAssert(io != NULL);
 
    At = io -> Tell(io);
    NextAligned = _cmsALIGNLONG(At);
    BytesToNextAlignedPos = NextAligned - At;
    if (BytesToNextAlignedPos == 0) return TRUE;
    if (BytesToNextAlignedPos > 4)  return FALSE;
 
    return (io ->Read(io, Buffer, BytesToNextAlignedPos, 1) == 1);
}
 
cmsBool CMSEXPORT _cmsWriteAlignment(cmsIOHANDLER* io)
{
    cmsUInt8Number  Buffer[4];
    cmsUInt32Number NextAligned, At;
    cmsUInt32Number BytesToNextAlignedPos;
 
    _cmsAssert(io != NULL);
 
    At = io -> Tell(io);
    NextAligned = _cmsALIGNLONG(At);
    BytesToNextAlignedPos = NextAligned - At;
    if (BytesToNextAlignedPos == 0) return TRUE;
    if (BytesToNextAlignedPos > 4)  return FALSE;
 
    memset(Buffer, 0, BytesToNextAlignedPos);
    return io -> Write(io, BytesToNextAlignedPos, Buffer);
}
 
 
// To deal with text streams. 2K at most
cmsBool CMSEXPORT _cmsIOPrintf(cmsIOHANDLER* io, const char* frm, ...)
{
    va_list args;
    int len;
    cmsUInt8Number Buffer[2048];
    cmsBool rc;
    cmsUInt8Number* ptr;
 
    _cmsAssert(io != NULL);
    _cmsAssert(frm != NULL);
 
    va_start(args, frm);
 
    len = vsnprintf((char*) Buffer, 2047, frm, args);
    if (len < 0) {
        va_end(args);
        return FALSE;   // Truncated, which is a fatal error for us
    }
 
    // setlocale may be active, no commas are needed in PS generator
    // and PS generator is our only client
    for (ptr = Buffer; *ptr; ptr++)
    {
        if (*ptr == ',') *ptr = '.';
    }
 
    rc = io ->Write(io, (cmsUInt32Number) len, Buffer);
 
    va_end(args);
 
    return rc;
}
 
 
// Plugin memory management -------------------------------------------------------------------------------------------------
 
// Specialized malloc for plug-ins, that is freed upon exit.
void* _cmsPluginMalloc(cmsContext ContextID, cmsUInt32Number size)
{
    struct _cmsContext_struct* ctx = _cmsGetContext(ContextID);
 
    if (ctx ->MemPool == NULL) {
 
        if (ContextID == NULL) {
 
            ctx->MemPool = _cmsCreateSubAlloc(0, 2*1024);
            if (ctx->MemPool == NULL) return NULL;
        }
        else {
            cmsSignalError(ContextID, cmsERROR_CORRUPTION_DETECTED, "NULL memory pool on context");
            return NULL;
        }
    }
 
    return _cmsSubAlloc(ctx->MemPool, size);
}
 
 
// Main plug-in dispatcher
cmsBool CMSEXPORT cmsPlugin(void* Plug_in)
{
    return cmsPluginTHR(NULL, Plug_in);
}
 
cmsBool CMSEXPORT cmsPluginTHR(cmsContext id, void* Plug_in)
{
    cmsPluginBase* Plugin;
 
    for (Plugin = (cmsPluginBase*) Plug_in;
         Plugin != NULL;
         Plugin = Plugin -> Next) {
 
            if (Plugin -> Magic != cmsPluginMagicNumber) {
                cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin");
                return FALSE;
            }
 
            if (Plugin ->ExpectedVersion > LCMS_VERSION) {
                cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "plugin needs Little CMS %d, current version is %d",
                    Plugin ->ExpectedVersion, LCMS_VERSION);
                return FALSE;
            }
 
            switch (Plugin -> Type) {
 
                case cmsPluginMemHandlerSig:
                    if (!_cmsRegisterMemHandlerPlugin(id, Plugin)) return FALSE;
                    break;
 
                case cmsPluginInterpolationSig:
                    if (!_cmsRegisterInterpPlugin(id, Plugin)) return FALSE;
                    break;
 
                case cmsPluginTagTypeSig:
                    if (!_cmsRegisterTagTypePlugin(id, Plugin)) return FALSE;
                    break;
 
                case cmsPluginTagSig:
                    if (!_cmsRegisterTagPlugin(id, Plugin)) return FALSE;
                    break;
 
                case cmsPluginFormattersSig:
                    if (!_cmsRegisterFormattersPlugin(id, Plugin)) return FALSE;
                    break;
 
                case cmsPluginRenderingIntentSig:
                    if (!_cmsRegisterRenderingIntentPlugin(id, Plugin)) return FALSE;
                    break;
 
                case cmsPluginParametricCurveSig:
                    if (!_cmsRegisterParametricCurvesPlugin(id, Plugin)) return FALSE;
                    break;
 
                case cmsPluginMultiProcessElementSig:
                    if (!_cmsRegisterMultiProcessElementPlugin(id, Plugin)) return FALSE;
                    break;
 
                case cmsPluginOptimizationSig:
                    if (!_cmsRegisterOptimizationPlugin(id, Plugin)) return FALSE;
                    break;
 
                case cmsPluginTransformSig:
                    if (!_cmsRegisterTransformPlugin(id, Plugin)) return FALSE;
                    break;
 
                case cmsPluginMutexSig:
                    if (!_cmsRegisterMutexPlugin(id, Plugin)) return FALSE;
                    break;
 
                default:
                    cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin type '%X'", Plugin -> Type);
                    return FALSE;
            }
    }
 
    // Keep a reference to the plug-in
    return TRUE;
}
 
 
// Revert all plug-ins to default
void CMSEXPORT cmsUnregisterPlugins(void)
{
    cmsUnregisterPluginsTHR(NULL);
}
 
 
// The Global storage for system context. This is the one and only global variable
// pointers structure. All global vars are referenced here.
static struct _cmsContext_struct globalContext = {
 
    NULL,                              // Not in the linked list
    NULL,                              // No suballocator
    {
        NULL,                          //  UserPtr,            
        &_cmsLogErrorChunk,            //  Logger,
        &_cmsAlarmCodesChunk,          //  AlarmCodes,
        &_cmsAdaptationStateChunk,     //  AdaptationState, 
        &_cmsMemPluginChunk,           //  MemPlugin,
        &_cmsInterpPluginChunk,        //  InterpPlugin,
        &_cmsCurvesPluginChunk,        //  CurvesPlugin,
        &_cmsFormattersPluginChunk,    //  FormattersPlugin,
        &_cmsTagTypePluginChunk,       //  TagTypePlugin,
        &_cmsTagPluginChunk,           //  TagPlugin,
        &_cmsIntentsPluginChunk,       //  IntentPlugin,
        &_cmsMPETypePluginChunk,       //  MPEPlugin,
        &_cmsOptimizationPluginChunk,  //  OptimizationPlugin,
        &_cmsTransformPluginChunk,     //  TransformPlugin,
        &_cmsMutexPluginChunk          //  MutexPlugin
    },
    
    { NULL, NULL, NULL, NULL, NULL, NULL } // The default memory allocator is not used for context 0
};
 
 
// The context pool (linked list head)
static _cmsMutex _cmsContextPoolHeadMutex = CMS_MUTEX_INITIALIZER;
static struct _cmsContext_struct* _cmsContextPoolHead = NULL;
 
// Internal, get associated pointer, with guessing. Never returns NULL.
struct _cmsContext_struct* _cmsGetContext(cmsContext ContextID)
{
    struct _cmsContext_struct* id = (struct _cmsContext_struct*) ContextID;
    struct _cmsContext_struct* ctx;
 
 
    // On 0, use global settings
    if (id == NULL) 
        return &globalContext;
 
    // Search
    _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
 
    for (ctx = _cmsContextPoolHead;
         ctx != NULL;
         ctx = ctx ->Next) {
 
            // Found it?
        if (id == ctx)
        {
            _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
            return ctx; // New-style context
        }
    }
 
    _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
    return &globalContext;
}
 
 
// Internal: get the memory area associanted with each context client
// Returns the block assigned to the specific zone. Never return NULL.
void* _cmsContextGetClientChunk(cmsContext ContextID, _cmsMemoryClient mc)
{
    struct _cmsContext_struct* ctx;
    void *ptr;
 
    if ((int) mc < 0 || mc >= MemoryClientMax) {
        
           cmsSignalError(ContextID, cmsERROR_INTERNAL, "Bad context client -- possible corruption");
 
           // This is catastrophic. Should never reach here
           _cmsAssert(0);
 
           // Reverts to global context
           return globalContext.chunks[UserPtr];
    }
    
    ctx = _cmsGetContext(ContextID);
    ptr = ctx ->chunks[mc];
 
    if (ptr != NULL)
        return ptr;
 
    // A null ptr means no special settings for that context, and this 
    // reverts to Context0 globals
    return globalContext.chunks[mc];    
}
 
 
// This function returns the given context its default pristine state,
// as no plug-ins were declared. There is no way to unregister a single 
// plug-in, as a single call to cmsPluginTHR() function may register 
// many different plug-ins simultaneously, then there is no way to 
// identify which plug-in to unregister.
void CMSEXPORT cmsUnregisterPluginsTHR(cmsContext ContextID)
{
    _cmsRegisterMemHandlerPlugin(ContextID, NULL);
    _cmsRegisterInterpPlugin(ContextID, NULL);
    _cmsRegisterTagTypePlugin(ContextID, NULL);
    _cmsRegisterTagPlugin(ContextID, NULL);
    _cmsRegisterFormattersPlugin(ContextID, NULL);
    _cmsRegisterRenderingIntentPlugin(ContextID, NULL);
    _cmsRegisterParametricCurvesPlugin(ContextID, NULL);
    _cmsRegisterMultiProcessElementPlugin(ContextID, NULL);
    _cmsRegisterOptimizationPlugin(ContextID, NULL);
    _cmsRegisterTransformPlugin(ContextID, NULL);    
    _cmsRegisterMutexPlugin(ContextID, NULL);
}
 
 
// Returns the memory manager plug-in, if any, from the Plug-in bundle
static
cmsPluginMemHandler* _cmsFindMemoryPlugin(void* PluginBundle)
{
    cmsPluginBase* Plugin;
 
    for (Plugin = (cmsPluginBase*) PluginBundle;
        Plugin != NULL;
        Plugin = Plugin -> Next) {
 
            if (Plugin -> Magic == cmsPluginMagicNumber && 
                Plugin -> ExpectedVersion <= LCMS_VERSION && 
                Plugin -> Type == cmsPluginMemHandlerSig) {
 
                    // Found!
                    return (cmsPluginMemHandler*) Plugin;  
            }
    }
 
    // Nope, revert to defaults 
    return NULL;
}
 
 
// Creates a new context with optional associated plug-ins. Caller may also specify an optional pointer to user-defined 
// data that will be forwarded to plug-ins and logger.
cmsContext CMSEXPORT cmsCreateContext(void* Plugin, void* UserData)
{
    struct _cmsContext_struct* ctx;
    struct _cmsContext_struct  fakeContext;
        
    // See the comments regarding locking in lcms2_internal.h
    // for an explanation of why we need the following code.
#ifndef CMS_NO_PTHREADS
#ifdef CMS_IS_WINDOWS_
#ifndef CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT
    {
        static HANDLE _cmsWindowsInitMutex = NULL;
        static volatile HANDLE* mutex = &_cmsWindowsInitMutex;
 
        if (*mutex == NULL)
        {
            HANDLE p = CreateMutex(NULL, FALSE, NULL);
            if (p && InterlockedCompareExchangePointer((void **)mutex, (void*)p, NULL) != NULL)
                CloseHandle(p);
        }
        if (*mutex == NULL || WaitForSingleObject(*mutex, INFINITE) == WAIT_FAILED)
            return NULL;
        if (((void **)&_cmsContextPoolHeadMutex)[0] == NULL)
            InitializeCriticalSection(&_cmsContextPoolHeadMutex);
        if (*mutex == NULL || !ReleaseMutex(*mutex))
            return NULL;
    }
#endif
#endif
#endif
 
    _cmsInstallAllocFunctions(_cmsFindMemoryPlugin(Plugin), &fakeContext.DefaultMemoryManager);
    
    fakeContext.chunks[UserPtr]     = UserData;
    fakeContext.chunks[MemPlugin]   = &fakeContext.DefaultMemoryManager;
 
    // Create the context structure.
    ctx = (struct _cmsContext_struct*) _cmsMalloc(&fakeContext, sizeof(struct _cmsContext_struct));
    if (ctx == NULL)   
        return NULL;     // Something very wrong happened!
 
    // Init the structure and the memory manager
    memset(ctx, 0, sizeof(struct _cmsContext_struct));
 
    // Keep memory manager
    memcpy(&ctx->DefaultMemoryManager, &fakeContext.DefaultMemoryManager, sizeof(_cmsMemPluginChunk)); 
   
    // Maintain the linked list (with proper locking)
    _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
       ctx ->Next = _cmsContextPoolHead;
       _cmsContextPoolHead = ctx;
    _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
 
    ctx ->chunks[UserPtr]     = UserData;
    ctx ->chunks[MemPlugin]   = &ctx->DefaultMemoryManager;
   
    // Now we can allocate the pool by using default memory manager
    ctx ->MemPool = _cmsCreateSubAlloc(ctx, 22 * sizeof(void*));  // default size about 22 pointers
    if (ctx ->MemPool == NULL) {
 
         cmsDeleteContext(ctx);
        return NULL;
    }
 
    _cmsAllocLogErrorChunk(ctx, NULL);
    _cmsAllocAlarmCodesChunk(ctx, NULL);
    _cmsAllocAdaptationStateChunk(ctx, NULL);
    _cmsAllocMemPluginChunk(ctx, NULL);
    _cmsAllocInterpPluginChunk(ctx, NULL);
    _cmsAllocCurvesPluginChunk(ctx, NULL);
    _cmsAllocFormattersPluginChunk(ctx, NULL);
    _cmsAllocTagTypePluginChunk(ctx, NULL);
    _cmsAllocMPETypePluginChunk(ctx, NULL);
    _cmsAllocTagPluginChunk(ctx, NULL);
    _cmsAllocIntentsPluginChunk(ctx, NULL);
    _cmsAllocOptimizationPluginChunk(ctx, NULL);
    _cmsAllocTransformPluginChunk(ctx, NULL);
    _cmsAllocMutexPluginChunk(ctx, NULL);
 
    // Setup the plug-ins
    if (!cmsPluginTHR(ctx, Plugin)) {
    
        cmsDeleteContext(ctx);
        return NULL;
    }
 
    return (cmsContext) ctx;  
}
 
// Duplicates a context with all associated plug-ins. 
// Caller may specify an optional pointer to user-defined 
// data that will be forwarded to plug-ins and logger. 
cmsContext CMSEXPORT cmsDupContext(cmsContext ContextID, void* NewUserData)
{
    int i;
    struct _cmsContext_struct* ctx;
    const struct _cmsContext_struct* src = _cmsGetContext(ContextID);
 
    void* userData = (NewUserData != NULL) ? NewUserData : src -> chunks[UserPtr];
    
    
    ctx = (struct _cmsContext_struct*) _cmsMalloc(ContextID, sizeof(struct _cmsContext_struct));
    if (ctx == NULL)   
        return NULL;     // Something very wrong happened
 
    // Setup default memory allocators
    memcpy(&ctx->DefaultMemoryManager, &src->DefaultMemoryManager, sizeof(ctx->DefaultMemoryManager));
 
    // Maintain the linked list
    _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
       ctx ->Next = _cmsContextPoolHead;
       _cmsContextPoolHead = ctx;
    _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
 
    ctx ->chunks[UserPtr]    = userData;
    ctx ->chunks[MemPlugin]  = &ctx->DefaultMemoryManager;
 
    ctx ->MemPool = _cmsCreateSubAlloc(ctx, 22 * sizeof(void*));
    if (ctx ->MemPool == NULL) {
 
         cmsDeleteContext(ctx);
        return NULL;
    }
 
    // Allocate all required chunks.
    _cmsAllocLogErrorChunk(ctx, src);
    _cmsAllocAlarmCodesChunk(ctx, src);
    _cmsAllocAdaptationStateChunk(ctx, src);
    _cmsAllocMemPluginChunk(ctx, src);
    _cmsAllocInterpPluginChunk(ctx, src);
    _cmsAllocCurvesPluginChunk(ctx, src);
    _cmsAllocFormattersPluginChunk(ctx, src);
    _cmsAllocTagTypePluginChunk(ctx, src);
    _cmsAllocMPETypePluginChunk(ctx, src);
    _cmsAllocTagPluginChunk(ctx, src);
    _cmsAllocIntentsPluginChunk(ctx, src);
    _cmsAllocOptimizationPluginChunk(ctx, src);
    _cmsAllocTransformPluginChunk(ctx, src);
    _cmsAllocMutexPluginChunk(ctx, src);
 
    // Make sure no one failed
    for (i=Logger; i < MemoryClientMax; i++) {
 
        if (src ->chunks[i] == NULL) {
            cmsDeleteContext((cmsContext) ctx);
            return NULL;
        }
    }
 
    return (cmsContext) ctx;
}
 
 
// Frees any resources associated with the given context, 
// and destroys the context placeholder. 
// The ContextID can no longer be used in any THR operation.  
void CMSEXPORT cmsDeleteContext(cmsContext ContextID)
{
    if (ContextID != NULL) {
 
        struct _cmsContext_struct* ctx = (struct _cmsContext_struct*) ContextID;              
        struct _cmsContext_struct  fakeContext;  
        struct _cmsContext_struct* prev;
 
        memcpy(&fakeContext.DefaultMemoryManager, &ctx->DefaultMemoryManager, sizeof(ctx->DefaultMemoryManager));
 
        fakeContext.chunks[UserPtr]     = ctx ->chunks[UserPtr];
        fakeContext.chunks[MemPlugin]   = &fakeContext.DefaultMemoryManager;
 
        // Get rid of plugins
        cmsUnregisterPluginsTHR(ContextID); 
 
        // Since all memory is allocated in the private pool, all what we need to do is destroy the pool
        if (ctx -> MemPool != NULL)
              _cmsSubAllocDestroy(ctx ->MemPool);
        ctx -> MemPool = NULL;
 
        // Maintain list
        _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
        if (_cmsContextPoolHead == ctx) { 
 
            _cmsContextPoolHead = ctx->Next;
        }
        else {
 
            // Search for previous
            for (prev = _cmsContextPoolHead; 
                 prev != NULL;
                 prev = prev ->Next)
            {
                if (prev -> Next == ctx) {
                    prev -> Next = ctx ->Next;
                    break;
                }
            }
        }
        _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
 
        // free the memory block itself
        _cmsFree(&fakeContext, ctx);
    }
}
 
// Returns the user data associated to the given ContextID, or NULL if no user data was attached on context creation
void* CMSEXPORT cmsGetContextUserData(cmsContext ContextID)
{
    return _cmsContextGetClientChunk(ContextID, UserPtr);
}
 
 
// Use context mutex to provide thread-safe time
cmsBool _cmsGetTime(struct tm* ptr_time)
{
    struct tm* t;
#if defined(HAVE_GMTIME_R) || defined(HAVE_GMTIME_S)
    struct tm tm;
#endif
 
    time_t now = time(NULL);
 
#ifdef HAVE_GMTIME_R
    t = gmtime_r(&now, &tm);
#elif defined(HAVE_GMTIME_S)
    t = gmtime_s(&tm, &now) == 0 ? &tm : NULL;
#else
    _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
    t = gmtime(&now);
    _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
#endif
 
    if (t == NULL) 
        return FALSE;
    else {
        *ptr_time = *t;
        return TRUE;
    }
}

V580 An odd explicit type casting: (void * *) & _cmsContextPoolHeadMutex. Consider verifying it.

V676 It is incorrect to compare the variable of BOOL type with TRUE.

V676 It is incorrect to compare the variable of BOOL type with TRUE.

V676 It is incorrect to compare the variable of BOOL type with TRUE.

V676 It is incorrect to compare the variable of BOOL type with TRUE.

V676 It is incorrect to compare the variable of BOOL type with TRUE.

V676 It is incorrect to compare the variable of BOOL type with TRUE.