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