Coverage Report

Created: 2026-05-16 09:25

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