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 // Multilocalized unicode objects. That is an attempt to encapsulate i18n.
  59 
  60 
  61 // Allocates an empty multi localizad unicode object
  62 cmsMLU* CMSEXPORT cmsMLUalloc(cmsContext ContextID, cmsUInt32Number nItems)
  63 {
  64     cmsMLU* mlu;
  65 
  66     // nItems should be positive if given
  67     if (nItems <= 0) nItems = 2;
  68 
  69     // Create the container
  70     mlu = (cmsMLU*) _cmsMallocZero(ContextID, sizeof(cmsMLU));
  71     if (mlu == NULL) return NULL;
  72 
  73     mlu ->ContextID = ContextID;
  74 
  75     // Create entry array
  76     mlu ->Entries = (_cmsMLUentry*) _cmsCalloc(ContextID, nItems, sizeof(_cmsMLUentry));
  77     if (mlu ->Entries == NULL) {
  78         _cmsFree(ContextID, mlu);
  79         return NULL;
  80     }
  81 
  82     // Ok, keep indexes up to date
  83     mlu ->AllocatedEntries    = nItems;
  84     mlu ->UsedEntries         = 0;
  85 
  86     return mlu;
  87 }
  88 
  89 
  90 // Grows a mempool table for a MLU. Each time this function is called, mempool size is multiplied times two.
  91 static
  92 cmsBool GrowMLUpool(cmsMLU* mlu)
  93 {
  94     cmsUInt32Number size;
  95     void *NewPtr;
  96 
  97     // Sanity check
  98     if (mlu == NULL) return FALSE;
  99 
 100     if (mlu ->PoolSize == 0)
 101         size = 256;
 102     else
 103         size = mlu ->PoolSize * 2;
 104 
 105     // Check for overflow
 106     if (size < mlu ->PoolSize) return FALSE;
 107 
 108     // Reallocate the pool
 109     NewPtr = _cmsRealloc(mlu ->ContextID, mlu ->MemPool, size);
 110     if (NewPtr == NULL) return FALSE;
 111 
 112 
 113     mlu ->MemPool  = NewPtr;
 114     mlu ->PoolSize = size;
 115 
 116     return TRUE;
 117 }
 118 
 119 
 120 // Grows a entry table for a MLU. Each time this function is called, table size is multiplied times two.
 121 static
 122 cmsBool GrowMLUtable(cmsMLU* mlu)
 123 {
 124     cmsUInt32Number AllocatedEntries;
 125     _cmsMLUentry *NewPtr;
 126 
 127     // Sanity check
 128     if (mlu == NULL) return FALSE;
 129 
 130     AllocatedEntries = mlu ->AllocatedEntries * 2;
 131 
 132     // Check for overflow
 133     if (AllocatedEntries / 2 != mlu ->AllocatedEntries) return FALSE;
 134 
 135     // Reallocate the memory
 136     NewPtr = (_cmsMLUentry*)_cmsRealloc(mlu ->ContextID, mlu ->Entries, AllocatedEntries*sizeof(_cmsMLUentry));
 137     if (NewPtr == NULL) return FALSE;
 138 
 139     mlu ->Entries          = NewPtr;
 140     mlu ->AllocatedEntries = AllocatedEntries;
 141 
 142     return TRUE;
 143 }
 144 
 145 
 146 // Search for a specific entry in the structure. Language and Country are used.
 147 static
 148 int SearchMLUEntry(cmsMLU* mlu, cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode)
 149 {
 150     cmsUInt32Number i;
 151 
 152     // Sanity check
 153     if (mlu == NULL) return -1;
 154 
 155     // Iterate whole table
 156     for (i=0; i < mlu ->UsedEntries; i++) {
 157 
 158         if (mlu ->Entries[i].Country  == CountryCode &&
 159             mlu ->Entries[i].Language == LanguageCode) return (int) i;
 160     }
 161 
 162     // Not found
 163     return -1;
 164 }
 165 
 166 // Add a block of characters to the intended MLU. Language and country are specified.
 167 // Only one entry for Language/country pair is allowed.
 168 static
 169 cmsBool AddMLUBlock(cmsMLU* mlu, cmsUInt32Number size, const wchar_t *Block,
 170                      cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode)
 171 {
 172     cmsUInt32Number Offset;
 173     cmsUInt8Number* Ptr;
 174 
 175     // Sanity check
 176     if (mlu == NULL) return FALSE;
 177 
 178     // Is there any room available?
 179     if (mlu ->UsedEntries >= mlu ->AllocatedEntries) {
 180         if (!GrowMLUtable(mlu)) return FALSE;
 181     }
 182 
 183     // Only one ASCII string
 184     if (SearchMLUEntry(mlu, LanguageCode, CountryCode) >= 0) return FALSE;  // Only one  is allowed!
 185 
 186     // Check for size
 187     while ((mlu ->PoolSize - mlu ->PoolUsed) < size) {
 188 
 189             if (!GrowMLUpool(mlu)) return FALSE;
 190     }
 191 
 192     Offset = mlu ->PoolUsed;
 193 
 194     Ptr = (cmsUInt8Number*) mlu ->MemPool;
 195     if (Ptr == NULL) return FALSE;
 196 
 197     // Set the entry
 198     memmove(Ptr + Offset, Block, size);
 199     mlu ->PoolUsed += size;
 200 
 201     mlu ->Entries[mlu ->UsedEntries].StrW     = Offset;
 202     mlu ->Entries[mlu ->UsedEntries].Len      = size;
 203     mlu ->Entries[mlu ->UsedEntries].Country  = CountryCode;
 204     mlu ->Entries[mlu ->UsedEntries].Language = LanguageCode;
 205     mlu ->UsedEntries++;
 206 
 207     return TRUE;
 208 }
 209 
 210 // Convert from a 3-char code to a cmsUInt16Number. It is done in this way because some
 211 // compilers don't properly align beginning of strings
 212 static
 213 cmsUInt16Number strTo16(const char str[3])
 214 {
 215     const cmsUInt8Number* ptr8;
 216     cmsUInt16Number n;
 217 
 218     // For non-existent strings
 219     if (str == NULL) return 0;
 220     ptr8 = (const cmsUInt8Number*)str;
 221     n = (cmsUInt16Number)(((cmsUInt16Number)ptr8[0] << 8) | ptr8[1]);
 222 
 223     return n;
 224 }
 225 
 226 static
 227 void strFrom16(char str[3], cmsUInt16Number n)
 228 {
 229     str[0] = (char)(n >> 8);
 230     str[1] = (char)n;
 231     str[2] = (char)0;
 232 }
 233 
 234 
 235 // Convert from UTF8 to wchar, returns len.
 236 static
 237 cmsUInt32Number decodeUTF8(wchar_t* out, const char* in)
 238 {
 239     cmsUInt32Number codepoint = 0;
 240     cmsUInt32Number size = 0;
 241 
 242     while (*in)
 243     {
 244         cmsUInt8Number ch = (cmsUInt8Number) *in;
 245 
 246         if (ch <= 0x7f)
 247         {
 248             codepoint = ch;
 249         }
 250         else if (ch <= 0xbf)
 251         {
 252             codepoint = (codepoint << 6) | (ch & 0x3f);
 253         }
 254         else if (ch <= 0xdf)
 255         {
 256             codepoint = ch & 0x1f;
 257         }
 258         else if (ch <= 0xef)
 259         {
 260             codepoint = ch & 0x0f;
 261         }
 262         else
 263         {
 264             codepoint = ch & 0x07;
 265         }
 266 
 267         in++;
 268 
 269         if (((*in & 0xc0) != 0x80) && (codepoint <= 0x10ffff))
 270         {
 271             if (sizeof(wchar_t) > 2)
 272             {
 273                 if (out) *out++ = (wchar_t) codepoint;
 274                 size++;
 275             }
 276             else
 277                 if (codepoint > 0xffff)
 278                 {
 279                     if (out)
 280                     {
 281                         *out++ = (wchar_t)(0xd800 + (codepoint >> 10));
 282                         *out++ = (wchar_t)(0xdc00 + (codepoint & 0x03ff));
 283                         size += 2;
 284                     }
 285                 }
 286                 else
 287                     if (codepoint < 0xd800 || codepoint >= 0xe000)
 288                     {
 289                         if (out) *out++ = (wchar_t) codepoint;
 290                         size++;
 291                     }
 292         }
 293     }
 294 
 295     return size;
 296 }
 297 
 298 // Convert from wchar_t to UTF8
 299 static
 300 cmsUInt32Number encodeUTF8(char* out, const wchar_t* in, cmsUInt32Number max_wchars, cmsUInt32Number max_chars)
 301 {
 302     cmsUInt32Number codepoint = 0;
 303     cmsUInt32Number size = 0;
 304     cmsUInt32Number len_w = 0;
 305 
 306     while (*in && len_w < max_wchars)
 307     {
 308         if (*in >= 0xd800 && *in <= 0xdbff)
 309             codepoint = ((*in - 0xd800) << 10) + 0x10000;
 310         else
 311         {
 312             if (*in >= 0xdc00 && *in <= 0xdfff)
 313                 codepoint |= *in - 0xdc00;
 314             else
 315                 codepoint = *in;
 316 
 317             if (codepoint <= 0x7f)
 318             {
 319                 if (out && (size + 1 < max_chars)) *out++ = (char)codepoint;
 320                 size++;
 321             }
 322 
 323             else if (codepoint <= 0x7ff)
 324             {
 325                 if (out && (max_chars > 0) && (size + 2 < max_chars))
 326                 {
 327                     *out++ = (char)(cmsUInt32Number)(0xc0 | ((codepoint >> 6) & 0x1f));
 328                     *out++ = (char)(cmsUInt32Number)(0x80 | (codepoint & 0x3f));
 329                 }
 330                 size += 2;
 331             }
 332             else if (codepoint <= 0xffff)
 333             {
 334                 if (out && (max_chars > 0) && (size + 3 < max_chars))
 335                 {
 336                     *out++ = (char)(cmsUInt32Number)(0xe0 | ((codepoint >> 12) & 0x0f));
 337                     *out++ = (char)(cmsUInt32Number)(0x80 | ((codepoint >> 6) & 0x3f));
 338                     *out++ = (char)(cmsUInt32Number)(0x80 | (codepoint & 0x3f));
 339                 }
 340                 size += 3;
 341             }
 342             else
 343             {
 344                 if (out && (max_chars > 0) && (size + 4 < max_chars))
 345                 {
 346                     *out++ = (char)(cmsUInt32Number)(0xf0 | ((codepoint >> 18) & 0x07));
 347                     *out++ = (char)(cmsUInt32Number)(0x80 | ((codepoint >> 12) & 0x3f));
 348                     *out++ = (char)(cmsUInt32Number)(0x80 | ((codepoint >> 6) & 0x3f));
 349                     *out++ = (char)(cmsUInt32Number)(0x80 | (codepoint & 0x3f));
 350                 }
 351                 size += 4;
 352             }
 353 
 354             codepoint = 0;
 355         }
 356 
 357         in++; len_w++;
 358     }
 359 
 360     return size;
 361 }
 362 
 363 // Add an ASCII entry. Do not add any \0 termination (ICC1v43_2010-12.pdf page 61)
 364 // In the case the user explicitly sets an empty string, we force a \0
 365 cmsBool CMSEXPORT cmsMLUsetASCII(cmsMLU* mlu, const char LanguageCode[3], const char CountryCode[3], const char* ASCIIString)
 366 {
 367     cmsUInt32Number i, len = (cmsUInt32Number)strlen(ASCIIString);
 368     wchar_t* WStr;
 369     cmsBool  rc;
 370     cmsUInt16Number Lang = strTo16(LanguageCode);
 371     cmsUInt16Number Cntry = strTo16(CountryCode);
 372 
 373     if (mlu == NULL) return FALSE;
 374 
 375     // len == 0 would prevent operation, so we set a empty string pointing to zero
 376     if (len == 0)
 377     {
 378         wchar_t empty = 0;
 379         return AddMLUBlock(mlu, sizeof(wchar_t), &empty, Lang, Cntry);
 380     }
 381 
 382     WStr = (wchar_t*)_cmsCalloc(mlu->ContextID, len, sizeof(wchar_t));
 383     if (WStr == NULL) return FALSE;
 384 
 385     for (i = 0; i < len; i++)
 386         WStr[i] = (wchar_t)ASCIIString[i];
 387 
 388     rc = AddMLUBlock(mlu, len * sizeof(wchar_t), WStr, Lang, Cntry);
 389 
 390     _cmsFree(mlu->ContextID, WStr);
 391     return rc;
 392 
 393 }
 394 
 395 // Add an UTF8 entry. Do not add any \0 termination (ICC1v43_2010-12.pdf page 61)
 396 // In the case the user explicitly sets an empty string, we force a \0
 397 cmsBool CMSEXPORT cmsMLUsetUTF8(cmsMLU* mlu, const char LanguageCode[3], const char CountryCode[3], const char* UTF8String)
 398 {
 399     cmsUInt32Number UTF8len;
 400     wchar_t* WStr;
 401     cmsBool  rc;
 402     cmsUInt16Number Lang  = strTo16(LanguageCode);
 403     cmsUInt16Number Cntry = strTo16(CountryCode);
 404 
 405     if (mlu == NULL) return FALSE;
 406 
 407     if (*UTF8String == '\0')
 408     {
 409         wchar_t empty = 0;
 410         return AddMLUBlock(mlu, sizeof(wchar_t), &empty, Lang, Cntry);
 411     }
 412 
 413     // Len excluding terminator 0
 414     UTF8len = decodeUTF8(NULL, UTF8String);
 415 
 416     // Get space for dest
 417     WStr = (wchar_t*) _cmsCalloc(mlu ->ContextID, UTF8len,  sizeof(wchar_t));
 418     if (WStr == NULL) return FALSE;
 419 
 420     decodeUTF8(WStr, UTF8String);
 421 
 422     rc = AddMLUBlock(mlu, UTF8len  * sizeof(wchar_t), WStr, Lang, Cntry);
 423 
 424     _cmsFree(mlu ->ContextID, WStr);
 425     return rc;
 426 }
 427 
 428 // We don't need any wcs support library
 429 static
 430 cmsUInt32Number mywcslen(const wchar_t *s)
 431 {
 432     const wchar_t *p;
 433 
 434     p = s;
 435     while (*p)
 436         p++;
 437 
 438     return (cmsUInt32Number)(p - s);
 439 }
 440 
 441 // Add a wide entry. Do not add any \0 terminator (ICC1v43_2010-12.pdf page 61)
 442 cmsBool  CMSEXPORT cmsMLUsetWide(cmsMLU* mlu, const char Language[3], const char Country[3], const wchar_t* WideString)
 443 {
 444     cmsUInt16Number Lang  = strTo16(Language);
 445     cmsUInt16Number Cntry = strTo16(Country);
 446     cmsUInt32Number len;
 447 
 448     if (mlu == NULL) return FALSE;
 449     if (WideString == NULL) return FALSE;
 450 
 451     len = (cmsUInt32Number) (mywcslen(WideString)) * sizeof(wchar_t);
 452     if (len == 0)
 453         len = sizeof(wchar_t);
 454 
 455     return AddMLUBlock(mlu, len, WideString, Lang, Cntry);
 456 }
 457 
 458 // Duplicating a MLU is as easy as copying all members
 459 cmsMLU* CMSEXPORT cmsMLUdup(const cmsMLU* mlu)
 460 {
 461     cmsMLU* NewMlu = NULL;
 462 
 463     // Duplicating a NULL obtains a NULL
 464     if (mlu == NULL) return NULL;
 465 
 466     NewMlu = cmsMLUalloc(mlu ->ContextID, mlu ->UsedEntries);
 467     if (NewMlu == NULL) return NULL;
 468 
 469     // Should never happen
 470     if (NewMlu ->AllocatedEntries < mlu ->UsedEntries)
 471         goto Error;
 472 
 473     // Sanitize...
 474     if (NewMlu ->Entries == NULL || mlu ->Entries == NULL)  goto Error;
 475 
 476     memmove(NewMlu ->Entries, mlu ->Entries, mlu ->UsedEntries * sizeof(_cmsMLUentry));
 477     NewMlu ->UsedEntries = mlu ->UsedEntries;
 478 
 479     // The MLU may be empty
 480     if (mlu ->PoolUsed == 0) {
 481         NewMlu ->MemPool = NULL;
 482     }
 483     else {
 484         // It is not empty
 485         NewMlu ->MemPool = _cmsMalloc(mlu ->ContextID, mlu ->PoolUsed);
 486         if (NewMlu ->MemPool == NULL) goto Error;
 487     }
 488 
 489     NewMlu ->PoolSize = mlu ->PoolUsed;
 490 
 491     if (NewMlu ->MemPool == NULL || mlu ->MemPool == NULL) goto Error;
 492 
 493     memmove(NewMlu ->MemPool, mlu->MemPool, mlu ->PoolUsed);
 494     NewMlu ->PoolUsed = mlu ->PoolUsed;
 495 
 496     return NewMlu;
 497 
 498 Error:
 499 
 500     if (NewMlu != NULL) cmsMLUfree(NewMlu);
 501     return NULL;
 502 }
 503 
 504 // Free any used memory
 505 void CMSEXPORT cmsMLUfree(cmsMLU* mlu)
 506 {
 507     if (mlu) {
 508 
 509         if (mlu -> Entries) _cmsFree(mlu ->ContextID, mlu->Entries);
 510         if (mlu -> MemPool) _cmsFree(mlu ->ContextID, mlu->MemPool);
 511 
 512         _cmsFree(mlu ->ContextID, mlu);
 513     }
 514 }
 515 
 516 
 517 // The algorithm first searches for an exact match of country and language, if not found it uses
 518 // the Language. If none is found, first entry is used instead.
 519 static
 520 const wchar_t* _cmsMLUgetWide(const cmsMLU* mlu,
 521                               cmsUInt32Number *len,
 522                               cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode,
 523                               cmsUInt16Number* UsedLanguageCode, cmsUInt16Number* UsedCountryCode)
 524 {
 525     cmsUInt32Number i;
 526     int Best = -1;
 527     _cmsMLUentry* v;
 528 
 529     if (mlu == NULL) return NULL;
 530 
 531     if (mlu -> AllocatedEntries <= 0) return NULL;
 532 
 533     for (i=0; i < mlu ->UsedEntries; i++) {
 534 
 535         v = mlu ->Entries + i;
 536 
 537         if (v -> Language == LanguageCode) {
 538 
 539             if (Best == -1) Best = (int) i;
 540 
 541             if (v -> Country == CountryCode) {
 542 
 543                 if (UsedLanguageCode != NULL) *UsedLanguageCode = v ->Language;
 544                 if (UsedCountryCode  != NULL) *UsedCountryCode = v ->Country;
 545 
 546                 if (len != NULL) *len = v ->Len;
 547 
 548                 return (wchar_t*) ((cmsUInt8Number*) mlu ->MemPool + v -> StrW);        // Found exact match
 549             }
 550         }
 551     }
 552 
 553     // No string found. Return First one
 554     if (Best == -1)
 555         Best = 0;
 556 
 557     v = mlu ->Entries + Best;
 558 
 559     if (UsedLanguageCode != NULL) *UsedLanguageCode = v ->Language;
 560     if (UsedCountryCode  != NULL) *UsedCountryCode = v ->Country;
 561 
 562     if (len != NULL) *len   = v ->Len;
 563 
 564     if (v->StrW + v->Len > mlu->PoolSize) return NULL;
 565 
 566     return (wchar_t*) ((cmsUInt8Number*) mlu ->MemPool + v ->StrW);
 567 }
 568 
 569 
 570 // Obtain an ASCII representation of the wide string. Setting buffer to NULL returns the len
 571 cmsUInt32Number CMSEXPORT cmsMLUgetASCII(const cmsMLU* mlu,
 572                                        const char LanguageCode[3], const char CountryCode[3],
 573                                        char* Buffer, cmsUInt32Number BufferSize)
 574 {
 575     const wchar_t *Wide;
 576     cmsUInt32Number  StrLen = 0;
 577     cmsUInt32Number ASCIIlen, i;
 578 
 579     cmsUInt16Number Lang  = strTo16(LanguageCode);
 580     cmsUInt16Number Cntry = strTo16(CountryCode);
 581 
 582     // Sanitize
 583     if (mlu == NULL) return 0;
 584 
 585     // Get WideChar
 586     Wide = _cmsMLUgetWide(mlu, &StrLen, Lang, Cntry, NULL, NULL);
 587     if (Wide == NULL) return 0;
 588 
 589     ASCIIlen = StrLen / sizeof(wchar_t);
 590 
 591     // Maybe we want only to know the len?
 592     if (Buffer == NULL) return ASCIIlen + 1; // Note the zero at the end
 593 
 594     // No buffer size means no data
 595     if (BufferSize <= 0) return 0;
 596 
 597     // Some clipping may be required
 598     if (BufferSize < ASCIIlen + 1)
 599         ASCIIlen = BufferSize - 1;
 600 
 601     // Precess each character
 602     for (i=0; i < ASCIIlen; i++) {
 603 
 604         wchar_t wc = Wide[i];
 605 
 606         if (wc < 0xff)
 607             Buffer[i] = (char)wc;
 608         else
 609             Buffer[i] = '?';
 610     }
 611 
 612     // We put a termination "\0"
 613     Buffer[ASCIIlen] = 0;
 614     return ASCIIlen + 1;
 615 }
 616 
 617 
 618 // Obtain a UTF8 representation of the wide string. Setting buffer to NULL returns the len
 619 cmsUInt32Number CMSEXPORT cmsMLUgetUTF8(const cmsMLU* mlu,
 620                                        const char LanguageCode[3], const char CountryCode[3],
 621                                        char* Buffer, cmsUInt32Number BufferSize)
 622 {
 623     const wchar_t *Wide;
 624     cmsUInt32Number  StrLen = 0;
 625     cmsUInt32Number UTF8len;
 626 
 627     cmsUInt16Number Lang  = strTo16(LanguageCode);
 628     cmsUInt16Number Cntry = strTo16(CountryCode);
 629 
 630     // Sanitize
 631     if (mlu == NULL) return 0;
 632 
 633     // Get WideChar
 634     Wide = _cmsMLUgetWide(mlu, &StrLen, Lang, Cntry, NULL, NULL);
 635     if (Wide == NULL) return 0;
 636 
 637     UTF8len = encodeUTF8(NULL, Wide, StrLen / sizeof(wchar_t), BufferSize);
 638 
 639     // Maybe we want only to know the len?
 640     if (Buffer == NULL) return UTF8len + 1; // Note the zero at the end
 641 
 642     // No buffer size means no data
 643     if (BufferSize <= 0) return 0;
 644 
 645     // Some clipping may be required
 646     if (BufferSize < UTF8len + 1)
 647         UTF8len = BufferSize - 1;
 648 
 649     // Process it
 650     encodeUTF8(Buffer, Wide, StrLen / sizeof(wchar_t), BufferSize);
 651 
 652     // We put a termination "\0"
 653     Buffer[UTF8len] = 0;
 654     return UTF8len + 1;
 655 }
 656 
 657 // Obtain a wide representation of the MLU, on depending on current locale settings
 658 cmsUInt32Number CMSEXPORT cmsMLUgetWide(const cmsMLU* mlu,
 659                                       const char LanguageCode[3], const char CountryCode[3],
 660                                       wchar_t* Buffer, cmsUInt32Number BufferSize)
 661 {
 662     const wchar_t *Wide;
 663     cmsUInt32Number  StrLen = 0;
 664 
 665     cmsUInt16Number Lang  = strTo16(LanguageCode);
 666     cmsUInt16Number Cntry = strTo16(CountryCode);
 667 
 668     // Sanitize
 669     if (mlu == NULL) return 0;
 670 
 671     Wide = _cmsMLUgetWide(mlu, &StrLen, Lang, Cntry, NULL, NULL);
 672     if (Wide == NULL) return 0;
 673 
 674     // Maybe we want only to know the len?
 675     if (Buffer == NULL) return StrLen + sizeof(wchar_t);
 676 
 677     // Invalid buffer size means no data
 678     if (BufferSize < sizeof(wchar_t)) return 0;
 679 
 680     // Some clipping may be required
 681     if (BufferSize < StrLen + sizeof(wchar_t))
 682         StrLen = BufferSize - sizeof(wchar_t);
 683 
 684     memmove(Buffer, Wide, StrLen);
 685     Buffer[StrLen / sizeof(wchar_t)] = 0;
 686 
 687     return StrLen + sizeof(wchar_t);
 688 }
 689 
 690 
 691 // Get also the language and country
 692 CMSAPI cmsBool CMSEXPORT cmsMLUgetTranslation(const cmsMLU* mlu,
 693                                               const char LanguageCode[3], const char CountryCode[3],
 694                                               char ObtainedLanguage[3], char ObtainedCountry[3])
 695 {
 696     const wchar_t *Wide;
 697 
 698     cmsUInt16Number Lang  = strTo16(LanguageCode);
 699     cmsUInt16Number Cntry = strTo16(CountryCode);
 700     cmsUInt16Number ObtLang, ObtCode;
 701 
 702     // Sanitize
 703     if (mlu == NULL) return FALSE;
 704 
 705     Wide = _cmsMLUgetWide(mlu, NULL, Lang, Cntry, &ObtLang, &ObtCode);
 706     if (Wide == NULL) return FALSE;
 707 
 708     // Get used language and code
 709     strFrom16(ObtainedLanguage, ObtLang);
 710     strFrom16(ObtainedCountry, ObtCode);
 711 
 712     return TRUE;
 713 }
 714 
 715 
 716 
 717 // Get the number of translations in the MLU object
 718 cmsUInt32Number CMSEXPORT cmsMLUtranslationsCount(const cmsMLU* mlu)
 719 {
 720     if (mlu == NULL) return 0;
 721     return mlu->UsedEntries;
 722 }
 723 
 724 // Get the language and country codes for a specific MLU index
 725 cmsBool CMSEXPORT cmsMLUtranslationsCodes(const cmsMLU* mlu,
 726                                           cmsUInt32Number idx,
 727                                           char LanguageCode[3],
 728                                           char CountryCode[3])
 729 {
 730     _cmsMLUentry *entry;
 731 
 732     if (mlu == NULL) return FALSE;
 733 
 734     if (idx >= mlu->UsedEntries) return FALSE;
 735 
 736     entry = &mlu->Entries[idx];
 737 
 738     strFrom16(LanguageCode, entry->Language);
 739     strFrom16(CountryCode, entry->Country);
 740 
 741     return TRUE;
 742 }
 743 
 744 
 745 // Named color lists --------------------------------------------------------------------------------------------
 746 
 747 // Grow the list to keep at least NumElements
 748 static
 749 cmsBool  GrowNamedColorList(cmsNAMEDCOLORLIST* v)
 750 {
 751     cmsUInt32Number size;
 752     _cmsNAMEDCOLOR * NewPtr;
 753 
 754     if (v == NULL) return FALSE;
 755 
 756     if (v ->Allocated == 0)
 757         size = 64;   // Initial guess
 758     else
 759         size = v ->Allocated * 2;
 760 
 761     // Keep a maximum color lists can grow, 100K entries seems reasonable
 762     if (size > 1024 * 100) {
 763         _cmsFree(v->ContextID, (void*) v->List);
 764         v->List = NULL;
 765         return FALSE;
 766     }
 767 
 768     NewPtr = (_cmsNAMEDCOLOR*) _cmsRealloc(v ->ContextID, v ->List, size * sizeof(_cmsNAMEDCOLOR));
 769     if (NewPtr == NULL)
 770         return FALSE;
 771 
 772     v ->List      = NewPtr;
 773     v ->Allocated = size;
 774     return TRUE;
 775 }
 776 
 777 // Allocate a list for n elements
 778 cmsNAMEDCOLORLIST* CMSEXPORT cmsAllocNamedColorList(cmsContext ContextID, cmsUInt32Number n, cmsUInt32Number ColorantCount, const char* Prefix, const char* Suffix)
 779 {
 780     cmsNAMEDCOLORLIST* v;
 781 
 782     if (ColorantCount > cmsMAXCHANNELS)
 783         return NULL;
 784 
 785     v = (cmsNAMEDCOLORLIST*)_cmsMallocZero(ContextID, sizeof(cmsNAMEDCOLORLIST));
 786     if (v == NULL) return NULL;
 787 
 788     v ->List      = NULL;
 789     v ->nColors   = 0;
 790     v ->ContextID  = ContextID;
 791 
 792     while (v -> Allocated < n) {
 793         if (!GrowNamedColorList(v)) {
 794             cmsFreeNamedColorList(v);
 795             return NULL;
 796         }
 797     }
 798 
 799     strncpy(v ->Prefix, Prefix, sizeof(v ->Prefix)-1);
 800     strncpy(v ->Suffix, Suffix, sizeof(v ->Suffix)-1);
 801     v->Prefix[32] = v->Suffix[32] = 0;
 802 
 803     v -> ColorantCount = ColorantCount;
 804 
 805     return v;
 806 }
 807 
 808 // Free a list
 809 void CMSEXPORT cmsFreeNamedColorList(cmsNAMEDCOLORLIST* v)
 810 {
 811     if (v == NULL) return;
 812     if (v ->List) _cmsFree(v ->ContextID, v ->List);
 813     _cmsFree(v ->ContextID, v);
 814 }
 815 
 816 cmsNAMEDCOLORLIST* CMSEXPORT cmsDupNamedColorList(const cmsNAMEDCOLORLIST* v)
 817 {
 818     cmsNAMEDCOLORLIST* NewNC;
 819 
 820     if (v == NULL) return NULL;
 821 
 822     NewNC= cmsAllocNamedColorList(v ->ContextID, v -> nColors, v ->ColorantCount, v ->Prefix, v ->Suffix);
 823     if (NewNC == NULL) return NULL;
 824 
 825     // For really large tables we need this
 826     while (NewNC ->Allocated < v ->Allocated){
 827         if (!GrowNamedColorList(NewNC))
 828         {
 829             cmsFreeNamedColorList(NewNC);
 830             return NULL;
 831         }
 832     }
 833 
 834     memmove(NewNC ->Prefix, v ->Prefix, sizeof(v ->Prefix));
 835     memmove(NewNC ->Suffix, v ->Suffix, sizeof(v ->Suffix));
 836     NewNC ->ColorantCount = v ->ColorantCount;
 837     memmove(NewNC->List, v ->List, v->nColors * sizeof(_cmsNAMEDCOLOR));
 838     NewNC ->nColors = v ->nColors;
 839     return NewNC;
 840 }
 841 
 842 
 843 // Append a color to a list. List pointer may change if reallocated
 844 cmsBool  CMSEXPORT cmsAppendNamedColor(cmsNAMEDCOLORLIST* NamedColorList,
 845                                        const char* Name,
 846                                        cmsUInt16Number PCS[3], cmsUInt16Number Colorant[cmsMAXCHANNELS])
 847 {
 848     cmsUInt32Number i;
 849 
 850     if (NamedColorList == NULL) return FALSE;
 851 
 852     if (NamedColorList ->nColors + 1 > NamedColorList ->Allocated) {
 853         if (!GrowNamedColorList(NamedColorList)) return FALSE;
 854     }
 855 
 856     for (i=0; i < NamedColorList ->ColorantCount; i++)
 857         NamedColorList ->List[NamedColorList ->nColors].DeviceColorant[i] = Colorant == NULL ? (cmsUInt16Number)0 : Colorant[i];
 858 
 859     for (i=0; i < 3; i++)
 860         NamedColorList ->List[NamedColorList ->nColors].PCS[i] = PCS == NULL ? (cmsUInt16Number) 0 : PCS[i];
 861 
 862     if (Name != NULL) {
 863 
 864         strncpy(NamedColorList ->List[NamedColorList ->nColors].Name, Name, cmsMAX_PATH-1);
 865         NamedColorList ->List[NamedColorList ->nColors].Name[cmsMAX_PATH-1] = 0;
 866 
 867     }
 868     else
 869         NamedColorList ->List[NamedColorList ->nColors].Name[0] = 0;
 870 
 871 
 872     NamedColorList ->nColors++;
 873     return TRUE;
 874 }
 875 
 876 // Returns number of elements
 877 cmsUInt32Number CMSEXPORT cmsNamedColorCount(const cmsNAMEDCOLORLIST* NamedColorList)
 878 {
 879      if (NamedColorList == NULL) return 0;
 880      return NamedColorList ->nColors;
 881 }
 882 
 883 // Info about a given color
 884 cmsBool  CMSEXPORT cmsNamedColorInfo(const cmsNAMEDCOLORLIST* NamedColorList, cmsUInt32Number nColor,
 885                                      char* Name,
 886                                      char* Prefix,
 887                                      char* Suffix,
 888                                      cmsUInt16Number* PCS,
 889                                      cmsUInt16Number* Colorant)
 890 {
 891     if (NamedColorList == NULL) return FALSE;
 892 
 893     if (nColor >= cmsNamedColorCount(NamedColorList)) return FALSE;
 894 
 895     // strcpy instead of strncpy because many apps are using small buffers
 896     if (Name) strcpy(Name, NamedColorList->List[nColor].Name);
 897     if (Prefix) strcpy(Prefix, NamedColorList->Prefix);
 898     if (Suffix) strcpy(Suffix, NamedColorList->Suffix);
 899     if (PCS)
 900         memmove(PCS, NamedColorList ->List[nColor].PCS, 3*sizeof(cmsUInt16Number));
 901 
 902     if (Colorant)
 903         memmove(Colorant, NamedColorList ->List[nColor].DeviceColorant,
 904                                 sizeof(cmsUInt16Number) * NamedColorList ->ColorantCount);
 905 
 906 
 907     return TRUE;
 908 }
 909 
 910 // Search for a given color name (no prefix or suffix)
 911 cmsInt32Number CMSEXPORT cmsNamedColorIndex(const cmsNAMEDCOLORLIST* NamedColorList, const char* Name)
 912 {
 913     cmsUInt32Number i;
 914     cmsUInt32Number n;
 915 
 916     if (NamedColorList == NULL) return -1;
 917     n = cmsNamedColorCount(NamedColorList);
 918     for (i=0; i < n; i++) {
 919         if (cmsstrcasecmp(Name,  NamedColorList->List[i].Name) == 0)
 920             return (cmsInt32Number) i;
 921     }
 922 
 923     return -1;
 924 }
 925 
 926 // MPE support -----------------------------------------------------------------------------------------------------------------
 927 
 928 static
 929 void FreeNamedColorList(cmsStage* mpe)
 930 {
 931     cmsNAMEDCOLORLIST* List = (cmsNAMEDCOLORLIST*) mpe ->Data;
 932     cmsFreeNamedColorList(List);
 933 }
 934 
 935 static
 936 void* DupNamedColorList(cmsStage* mpe)
 937 {
 938     cmsNAMEDCOLORLIST* List = (cmsNAMEDCOLORLIST*) mpe ->Data;
 939     return cmsDupNamedColorList(List);
 940 }
 941 
 942 static
 943 void EvalNamedColorPCS(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe)
 944 {
 945     cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) mpe ->Data;
 946     cmsUInt16Number index = (cmsUInt16Number) _cmsQuickSaturateWord(In[0] * 65535.0);
 947 
 948     if (index >= NamedColorList-> nColors) {
 949         cmsSignalError(NamedColorList ->ContextID, cmsERROR_RANGE, "Color %d out of range", index);
 950         Out[0] = Out[1] = Out[2] = 0.0f;
 951     }
 952     else {
 953 
 954             // Named color always uses Lab
 955             Out[0] = (cmsFloat32Number) (NamedColorList->List[index].PCS[0] / 65535.0);
 956             Out[1] = (cmsFloat32Number) (NamedColorList->List[index].PCS[1] / 65535.0);
 957             Out[2] = (cmsFloat32Number) (NamedColorList->List[index].PCS[2] / 65535.0);
 958     }
 959 }
 960 
 961 static
 962 void EvalNamedColor(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe)
 963 {
 964     cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) mpe ->Data;
 965     cmsUInt16Number index = (cmsUInt16Number) _cmsQuickSaturateWord(In[0] * 65535.0);
 966     cmsUInt32Number j;
 967 
 968     if (index >= NamedColorList-> nColors) {
 969         cmsSignalError(NamedColorList ->ContextID, cmsERROR_RANGE, "Color %d out of range", index);
 970         for (j = 0; j < NamedColorList->ColorantCount; j++)
 971             Out[j] = 0.0f;
 972 
 973     }
 974     else {
 975         for (j=0; j < NamedColorList ->ColorantCount; j++)
 976             Out[j] = (cmsFloat32Number) (NamedColorList->List[index].DeviceColorant[j] / 65535.0);
 977     }
 978 }
 979 
 980 
 981 // Named color lookup element
 982 cmsStage* CMSEXPORT _cmsStageAllocNamedColor(cmsNAMEDCOLORLIST* NamedColorList, cmsBool UsePCS)
 983 {
 984     return _cmsStageAllocPlaceholder(NamedColorList ->ContextID,
 985                                    cmsSigNamedColorElemType,
 986                                    1, UsePCS ? 3 : NamedColorList ->ColorantCount,
 987                                    UsePCS ? EvalNamedColorPCS : EvalNamedColor,
 988                                    DupNamedColorList,
 989                                    FreeNamedColorList,
 990                                    cmsDupNamedColorList(NamedColorList));
 991 
 992 }
 993 
 994 
 995 // Retrieve the named color list from a transform. Should be first element in the LUT
 996 cmsNAMEDCOLORLIST* CMSEXPORT cmsGetNamedColorList(cmsHTRANSFORM xform)
 997 {
 998     _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;
 999     cmsStage* mpe;
1000 
1001     if (v == NULL) return NULL;
1002     if (v->Lut == NULL) return NULL;
1003 
1004     mpe = v->Lut->Elements;
1005     if (mpe == NULL) return NULL;
1006 
1007     if (mpe ->Type != cmsSigNamedColorElemType) return NULL;
1008     return (cmsNAMEDCOLORLIST*) mpe ->Data;
1009 }
1010 
1011 
1012 // Profile sequence description routines -------------------------------------------------------------------------------------
1013 
1014 cmsSEQ* CMSEXPORT cmsAllocProfileSequenceDescription(cmsContext ContextID, cmsUInt32Number n)
1015 {
1016     cmsSEQ* Seq;
1017     cmsUInt32Number i;
1018 
1019     if (n == 0) return NULL;
1020 
1021     // In a absolutely arbitrary way, I hereby decide to allow a maxim of 255 profiles linked
1022     // in a devicelink. It makes not sense anyway and may be used for exploits, so let's close the door!
1023     if (n > 255) return NULL;
1024 
1025     Seq = (cmsSEQ*) _cmsMallocZero(ContextID, sizeof(cmsSEQ));
1026     if (Seq == NULL) return NULL;
1027 
1028     Seq -> ContextID = ContextID;
1029     Seq -> seq      = (cmsPSEQDESC*) _cmsCalloc(ContextID, n, sizeof(cmsPSEQDESC));
1030     Seq -> n        = n;
1031 
1032     if (Seq -> seq == NULL) {
1033         _cmsFree(ContextID, Seq);
1034         return NULL;
1035     }
1036 
1037     for (i=0; i < n; i++) {
1038         Seq -> seq[i].Manufacturer = NULL;
1039         Seq -> seq[i].Model        = NULL;
1040         Seq -> seq[i].Description  = NULL;
1041     }
1042 
1043     return Seq;
1044 }
1045 
1046 void CMSEXPORT cmsFreeProfileSequenceDescription(cmsSEQ* pseq)
1047 {
1048     cmsUInt32Number i;
1049 
1050     if (pseq == NULL)
1051         return;
1052 
1053     if (pseq ->seq != NULL) {
1054         for (i=0; i < pseq ->n; i++) {
1055             if (pseq ->seq[i].Manufacturer != NULL) cmsMLUfree(pseq ->seq[i].Manufacturer);
1056             if (pseq ->seq[i].Model != NULL) cmsMLUfree(pseq ->seq[i].Model);
1057             if (pseq ->seq[i].Description != NULL) cmsMLUfree(pseq ->seq[i].Description);
1058         }
1059 
1060         _cmsFree(pseq ->ContextID, pseq ->seq);
1061     }
1062 
1063     _cmsFree(pseq -> ContextID, pseq);
1064 }
1065 
1066 cmsSEQ* CMSEXPORT cmsDupProfileSequenceDescription(const cmsSEQ* pseq)
1067 {
1068     cmsSEQ *NewSeq;
1069     cmsUInt32Number i;
1070 
1071     if (pseq == NULL)
1072         return NULL;
1073 
1074     NewSeq = (cmsSEQ*) _cmsMalloc(pseq -> ContextID, sizeof(cmsSEQ));
1075     if (NewSeq == NULL) return NULL;
1076 
1077 
1078     NewSeq -> seq      = (cmsPSEQDESC*) _cmsCalloc(pseq ->ContextID, pseq ->n, sizeof(cmsPSEQDESC));
1079     if (NewSeq ->seq == NULL) goto Error;
1080 
1081     NewSeq -> ContextID = pseq ->ContextID;
1082     NewSeq -> n        = pseq ->n;
1083 
1084     for (i=0; i < pseq->n; i++) {
1085 
1086         memmove(&NewSeq ->seq[i].attributes, &pseq ->seq[i].attributes, sizeof(cmsUInt64Number));
1087 
1088         NewSeq ->seq[i].deviceMfg   = pseq ->seq[i].deviceMfg;
1089         NewSeq ->seq[i].deviceModel = pseq ->seq[i].deviceModel;
1090         memmove(&NewSeq ->seq[i].ProfileID, &pseq ->seq[i].ProfileID, sizeof(cmsProfileID));
1091         NewSeq ->seq[i].technology  = pseq ->seq[i].technology;
1092 
1093         NewSeq ->seq[i].Manufacturer = cmsMLUdup(pseq ->seq[i].Manufacturer);
1094         NewSeq ->seq[i].Model        = cmsMLUdup(pseq ->seq[i].Model);
1095         NewSeq ->seq[i].Description  = cmsMLUdup(pseq ->seq[i].Description);
1096 
1097     }
1098 
1099     return NewSeq;
1100 
1101 Error:
1102 
1103     cmsFreeProfileSequenceDescription(NewSeq);
1104     return NULL;
1105 }
1106 
1107 // Dictionaries --------------------------------------------------------------------------------------------------------
1108 
1109 // Dictionaries are just very simple linked lists
1110 
1111 
1112 typedef struct _cmsDICT_struct {
1113     cmsDICTentry* head;
1114     cmsContext ContextID;
1115 } _cmsDICT;
1116 
1117 
1118 // Allocate an empty dictionary
1119 cmsHANDLE CMSEXPORT cmsDictAlloc(cmsContext ContextID)
1120 {
1121     _cmsDICT* dict = (_cmsDICT*) _cmsMallocZero(ContextID, sizeof(_cmsDICT));
1122     if (dict == NULL) return NULL;
1123 
1124     dict ->ContextID = ContextID;
1125     return (cmsHANDLE) dict;
1126 
1127 }
1128 
1129 // Dispose resources
1130 void CMSEXPORT cmsDictFree(cmsHANDLE hDict)
1131 {
1132     _cmsDICT* dict = (_cmsDICT*) hDict;
1133     cmsDICTentry *entry, *next;
1134 
1135     _cmsAssert(dict != NULL);
1136 
1137     // Walk the list freeing all nodes
1138     entry = dict ->head;
1139     while (entry != NULL) {
1140 
1141             if (entry ->DisplayName  != NULL) cmsMLUfree(entry ->DisplayName);
1142             if (entry ->DisplayValue != NULL) cmsMLUfree(entry ->DisplayValue);
1143             if (entry ->Name != NULL) _cmsFree(dict ->ContextID, entry -> Name);
1144             if (entry ->Value != NULL) _cmsFree(dict ->ContextID, entry -> Value);
1145 
1146             // Don't fall in the habitual trap...
1147             next = entry ->Next;
1148             _cmsFree(dict ->ContextID, entry);
1149 
1150             entry = next;
1151     }
1152 
1153     _cmsFree(dict ->ContextID, dict);
1154 }
1155 
1156 
1157 // Duplicate a wide char string
1158 static
1159 wchar_t* DupWcs(cmsContext ContextID, const wchar_t* ptr)
1160 {
1161     if (ptr == NULL) return NULL;
1162     return (wchar_t*) _cmsDupMem(ContextID, ptr, (mywcslen(ptr) + 1) * sizeof(wchar_t));
1163 }
1164 
1165 // Add a new entry to the linked list
1166 cmsBool CMSEXPORT cmsDictAddEntry(cmsHANDLE hDict, const wchar_t* Name, const wchar_t* Value, const cmsMLU *DisplayName, const cmsMLU *DisplayValue)
1167 {
1168     _cmsDICT* dict = (_cmsDICT*) hDict;
1169     cmsDICTentry *entry;
1170 
1171     _cmsAssert(dict != NULL);
1172     _cmsAssert(Name != NULL);
1173 
1174     entry = (cmsDICTentry*) _cmsMallocZero(dict ->ContextID, sizeof(cmsDICTentry));
1175     if (entry == NULL) return FALSE;
1176 
1177     entry ->DisplayName  = cmsMLUdup(DisplayName);
1178     entry ->DisplayValue = cmsMLUdup(DisplayValue);
1179     entry ->Name         = DupWcs(dict ->ContextID, Name);
1180     entry ->Value        = DupWcs(dict ->ContextID, Value);
1181 
1182     entry ->Next = dict ->head;
1183     dict ->head = entry;
1184 
1185     return TRUE;
1186 }
1187 
1188 
1189 // Duplicates an existing dictionary
1190 cmsHANDLE CMSEXPORT cmsDictDup(cmsHANDLE hDict)
1191 {
1192     _cmsDICT* old_dict = (_cmsDICT*) hDict;
1193     cmsHANDLE hNew;
1194     cmsDICTentry *entry;
1195 
1196     _cmsAssert(old_dict != NULL);
1197 
1198     hNew  = cmsDictAlloc(old_dict ->ContextID);
1199     if (hNew == NULL) return NULL;
1200 
1201     // Walk the list freeing all nodes
1202     entry = old_dict ->head;
1203     while (entry != NULL) {
1204 
1205         if (!cmsDictAddEntry(hNew, entry ->Name, entry ->Value, entry ->DisplayName, entry ->DisplayValue)) {
1206 
1207             cmsDictFree(hNew);
1208             return NULL;
1209         }
1210 
1211         entry = entry -> Next;
1212     }
1213 
1214     return hNew;
1215 }
1216 
1217 // Get a pointer to the linked list
1218 const cmsDICTentry* CMSEXPORT cmsDictGetEntryList(cmsHANDLE hDict)
1219 {
1220     _cmsDICT* dict = (_cmsDICT*) hDict;
1221 
1222     if (dict == NULL) return NULL;
1223     return dict ->head;
1224 }
1225 
1226 // Helper For external languages
1227 const cmsDICTentry* CMSEXPORT cmsDictNextEntry(const cmsDICTentry* e)
1228 {
1229      if (e == NULL) return NULL;
1230      return e ->Next;
1231 }