Coverage Report

Created: 2026-11-10 07:41

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