1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                  L       OOO    CCCC   AAA   L      EEEEE                   %
7 %                  L      O   O  C      A   A  L      E                       %
8 %                  L      O   O  C      AAAAA  L      EEE                     %
9 %                  L      O   O  C      A   A  L      E                       %
10 %                  LLLLL   OOO    CCCC  A   A  LLLLL  EEEEE                   %
11 %                                                                             %
12 %                                                                             %
13 %                      MagickCore Image Locale Methods                        %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                   Cristy                                    %
17 %                                 July 2003                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2021 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    https://imagemagick.org/script/license.php                               %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 
39 /*
40   Include declarations.
41 */
42 #include "magick/studio.h"
43 #include "magick/blob.h"
44 #include "magick/client.h"
45 #include "magick/configure.h"
46 #include "magick/exception.h"
47 #include "magick/exception-private.h"
48 #include "magick/hashmap.h"
49 #include "magick/image-private.h"
50 #include "magick/locale_.h"
51 #include "magick/log.h"
52 #include "magick/memory_.h"
53 #include "magick/nt-base-private.h"
54 #include "magick/semaphore.h"
55 #include "magick/splay-tree.h"
56 #include "magick/string_.h"
57 #include "magick/token.h"
58 #include "magick/utility.h"
59 #include "magick/xml-tree.h"
60 #include "magick/xml-tree-private.h"
61 
62 /*
63   Define declarations.
64 */
65 #if (defined(MAGICKCORE_HAVE_NEWLOCALE) || defined(MAGICKCORE_WINDOWS_SUPPORT)) && !defined(__MINGW32__)
66 #  define MAGICKCORE_LOCALE_SUPPORT
67 #endif
68 #define LocaleFilename  "locale.xml"
69 #define MaxRecursionDepth  200
70 
71 /*
72   Static declarations.
73 */
74 static const char
75   *LocaleMap =
76     "<?xml version=\"1.0\"?>"
77     "<localemap>"
78     "  <locale name=\"C\">"
79     "    <Exception>"
80     "     <Message name=\"\">"
81     "     </Message>"
82     "    </Exception>"
83     "  </locale>"
84     "</localemap>";
85 
86 #ifdef __VMS
87 #define asciimap AsciiMap
88 #endif
89 #if !defined(MAGICKCORE_HAVE_STRCASECMP) || !defined(MAGICKCORE_HAVE_STRNCASECMP)
90 static const unsigned char
91   AsciiMap[] =
92   {
93     0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
94     0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
95     0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
96     0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
97     0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
98     0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
99     0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73,
100     0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
101     0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b,
102     0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
103     0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
104     0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
105     0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b,
106     0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
107     0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3,
108     0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
109     0xc0, 0xe1, 0xe2, 0xe3, 0xe4, 0xc5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb,
110     0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
111     0xf8, 0xf9, 0xfa, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3,
112     0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
113     0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb,
114     0xfc, 0xfd, 0xfe, 0xff,
115   };
116 #endif
117 
118 static SemaphoreInfo
119   *locale_semaphore = (SemaphoreInfo *) NULL;
120 
121 static SplayTreeInfo
122   *locale_cache = (SplayTreeInfo *) NULL;
123 
124 #if defined(MAGICKCORE_LOCALE_SUPPORT)
125 static volatile locale_t
126   c_locale = (locale_t) NULL;
127 #endif
128 
129 /*
130   Forward declarations.
131 */
132 static MagickBooleanType
133   IsLocaleTreeInstantiated(ExceptionInfo *),
134   LoadLocaleCache(SplayTreeInfo *,const char *,const char *,const char *,
135     const size_t,ExceptionInfo *);
136 
137 #if defined(MAGICKCORE_LOCALE_SUPPORT)
138 /*
139 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
140 %                                                                             %
141 %                                                                             %
142 %                                                                             %
143 +   A c q u i r e C L o c a l e                                               %
144 %                                                                             %
145 %                                                                             %
146 %                                                                             %
147 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
148 %
149 %  AcquireCLocale() allocates the C locale object, or (locale_t) 0 with
150 %  errno set if it cannot be acquired.
151 %
152 %  The format of the AcquireCLocale method is:
153 %
154 %      locale_t AcquireCLocale(void)
155 %
156 */
AcquireCLocale(void)157 static locale_t AcquireCLocale(void)
158 {
159 #if defined(MAGICKCORE_HAVE_NEWLOCALE)
160   if (c_locale == (locale_t) NULL)
161     c_locale=newlocale(LC_ALL_MASK,"C",(locale_t) 0);
162 #elif defined(MAGICKCORE_WINDOWS_SUPPORT) && !defined(__MINGW32__)
163   if (c_locale == (locale_t) NULL)
164     c_locale=_create_locale(LC_ALL,"C");
165 #endif
166   return(c_locale);
167 }
168 #endif
169 
170 /*
171 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
172 %                                                                             %
173 %                                                                             %
174 %                                                                             %
175 %  A c q u i r e L o c a l e S p l a y T r e e                                %
176 %                                                                             %
177 %                                                                             %
178 %                                                                             %
179 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
180 %
181 %  AcquireLocaleSplayTree() caches one or more locale configurations which
182 %  provides a mapping between locale attributes and a locale tag.
183 %
184 %  The format of the AcquireLocaleSplayTree method is:
185 %
186 %      SplayTreeInfo *AcquireLocaleSplayTree(const char *filename,
187 %        ExceptionInfo *exception)
188 %
189 %  A description of each parameter follows:
190 %
191 %    o filename: the font file tag.
192 %
193 %    o locale: the actual locale.
194 %
195 %    o exception: return any errors or warnings in this structure.
196 %
197 */
198 
DestroyLocaleNode(void * locale_info)199 static void *DestroyLocaleNode(void *locale_info)
200 {
201   LocaleInfo
202     *p;
203 
204   p=(LocaleInfo *) locale_info;
205   if (p->path != (char *) NULL)
206     p->path=DestroyString(p->path);
207   if (p->tag != (char *) NULL)
208     p->tag=DestroyString(p->tag);
209   if (p->message != (char *) NULL)
210     p->message=DestroyString(p->message);
211   return(RelinquishMagickMemory(p));
212 }
213 
AcquireLocaleSplayTree(const char * filename,const char * locale,ExceptionInfo * exception)214 static SplayTreeInfo *AcquireLocaleSplayTree(const char *filename,
215   const char *locale,ExceptionInfo *exception)
216 {
217   MagickStatusType
218     status;
219 
220   SplayTreeInfo
221     *cache;
222 
223   cache=NewSplayTree(CompareSplayTreeString,(void *(*)(void *)) NULL,
224     DestroyLocaleNode);
225   if (cache == (SplayTreeInfo *) NULL)
226     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
227   status=MagickTrue;
228 #if !MAGICKCORE_ZERO_CONFIGURATION_SUPPORT
229   {
230     const StringInfo
231       *option;
232 
233     LinkedListInfo
234       *options;
235 
236     options=GetLocaleOptions(filename,exception);
237     option=(const StringInfo *) GetNextValueInLinkedList(options);
238     while (option != (const StringInfo *) NULL)
239     {
240       status&=LoadLocaleCache(cache,(const char *) GetStringInfoDatum(option),
241         GetStringInfoPath(option),locale,0,exception);
242       option=(const StringInfo *) GetNextValueInLinkedList(options);
243     }
244     options=DestroyLocaleOptions(options);
245     if (GetNumberOfNodesInSplayTree(cache) == 0)
246       {
247         options=GetLocaleOptions("english.xml",exception);
248         option=(const StringInfo *) GetNextValueInLinkedList(options);
249         while (option != (const StringInfo *) NULL)
250         {
251           status&=LoadLocaleCache(cache,(const char *)
252             GetStringInfoDatum(option),GetStringInfoPath(option),locale,0,
253             exception);
254           option=(const StringInfo *) GetNextValueInLinkedList(options);
255         }
256         options=DestroyLocaleOptions(options);
257       }
258   }
259 #endif
260   if (GetNumberOfNodesInSplayTree(cache) == 0)
261     status&=LoadLocaleCache(cache,LocaleMap,"built-in",locale,0,
262       exception);
263   return(cache);
264 }
265 
266 #if defined(MAGICKCORE_LOCALE_SUPPORT)
267 /*
268 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
269 %                                                                             %
270 %                                                                             %
271 %                                                                             %
272 +   D e s t r o y C L o c a l e                                               %
273 %                                                                             %
274 %                                                                             %
275 %                                                                             %
276 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
277 %
278 %  DestroyCLocale() releases the resources allocated for a locale object
279 %  returned by a call to the AcquireCLocale() method.
280 %
281 %  The format of the DestroyCLocale method is:
282 %
283 %      void DestroyCLocale(void)
284 %
285 */
DestroyCLocale(void)286 static void DestroyCLocale(void)
287 {
288   if (c_locale != (locale_t) NULL)
289     freelocale(c_locale);
290   c_locale=(locale_t) NULL;
291 }
292 #endif
293 
294 /*
295 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
296 %                                                                             %
297 %                                                                             %
298 %                                                                             %
299 %   D e s t r o y L o c a l e O p t i o n s                                   %
300 %                                                                             %
301 %                                                                             %
302 %                                                                             %
303 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
304 %
305 %  DestroyLocaleOptions() releases memory associated with an locale
306 %  messages.
307 %
308 %  The format of the DestroyProfiles method is:
309 %
310 %      LinkedListInfo *DestroyLocaleOptions(Image *image)
311 %
312 %  A description of each parameter follows:
313 %
314 %    o image: the image.
315 %
316 */
317 
DestroyOptions(void * message)318 static void *DestroyOptions(void *message)
319 {
320   return(DestroyStringInfo((StringInfo *) message));
321 }
322 
DestroyLocaleOptions(LinkedListInfo * messages)323 MagickExport LinkedListInfo *DestroyLocaleOptions(LinkedListInfo *messages)
324 {
325   assert(messages != (LinkedListInfo *) NULL);
326   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
327   return(DestroyLinkedList(messages,DestroyOptions));
328 }
329 
330 /*
331 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
332 %                                                                             %
333 %                                                                             %
334 %                                                                             %
335 +  F o r m a t L o c a l e F i l e                                            %
336 %                                                                             %
337 %                                                                             %
338 %                                                                             %
339 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
340 %
341 %  FormatLocaleFile() prints formatted output of a variable argument list to a
342 %  file in the "C" locale.
343 %
344 %  The format of the FormatLocaleFile method is:
345 %
346 %      ssize_t FormatLocaleFile(FILE *file,const char *format,...)
347 %
348 %  A description of each parameter follows.
349 %
350 %   o file:  the file.
351 %
352 %   o format:  A file describing the format to use to write the remaining
353 %     arguments.
354 %
355 */
356 
FormatLocaleFileList(FILE * file,const char * magick_restrict format,va_list operands)357 MagickExport ssize_t FormatLocaleFileList(FILE *file,
358   const char *magick_restrict format,va_list operands)
359 {
360   ssize_t
361     n;
362 
363 #if defined(MAGICKCORE_LOCALE_SUPPORT) && defined(MAGICKCORE_HAVE_VFPRINTF_L)
364   {
365     locale_t
366       locale;
367 
368     locale=AcquireCLocale();
369     if (locale == (locale_t) NULL)
370       n=(ssize_t) vfprintf(file,format,operands);
371     else
372 #if defined(MAGICKCORE_WINDOWS_SUPPORT)
373       n=(ssize_t) vfprintf_l(file,format,locale,operands);
374 #else
375       n=(ssize_t) vfprintf_l(file,locale,format,operands);
376 #endif
377   }
378 #else
379 #if defined(MAGICKCORE_LOCALE_SUPPORT) && defined(MAGICKCORE_HAVE_USELOCALE)
380   {
381     locale_t
382       locale,
383       previous_locale;
384 
385     locale=AcquireCLocale();
386     if (locale == (locale_t) NULL)
387       n=(ssize_t) vfprintf(file,format,operands);
388     else
389       {
390         previous_locale=uselocale(locale);
391         n=(ssize_t) vfprintf(file,format,operands);
392         uselocale(previous_locale);
393       }
394   }
395 #else
396   n=(ssize_t) vfprintf(file,format,operands);
397 #endif
398 #endif
399   return(n);
400 }
401 
FormatLocaleFile(FILE * file,const char * magick_restrict format,...)402 MagickExport ssize_t FormatLocaleFile(FILE *file,
403   const char *magick_restrict format,...)
404 {
405   ssize_t
406     n;
407 
408   va_list
409     operands;
410 
411   va_start(operands,format);
412   n=FormatLocaleFileList(file,format,operands);
413   va_end(operands);
414   return(n);
415 }
416 
417 /*
418 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
419 %                                                                             %
420 %                                                                             %
421 %                                                                             %
422 +  F o r m a t L o c a l e S t r i n g                                        %
423 %                                                                             %
424 %                                                                             %
425 %                                                                             %
426 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
427 %
428 %  FormatLocaleString() prints formatted output of a variable argument list to
429 %  a string buffer in the "C" locale.
430 %
431 %  The format of the FormatLocaleString method is:
432 %
433 %      ssize_t FormatLocaleString(char *string,const size_t length,
434 %        const char *format,...)
435 %
436 %  A description of each parameter follows.
437 %
438 %   o string:  FormatLocaleString() returns the formatted string in this
439 %     character buffer.
440 %
441 %   o length: the maximum length of the string.
442 %
443 %   o format:  A string describing the format to use to write the remaining
444 %     arguments.
445 %
446 */
447 
FormatLocaleStringList(char * magick_restrict string,const size_t length,const char * magick_restrict format,va_list operands)448 MagickExport ssize_t FormatLocaleStringList(char *magick_restrict string,
449   const size_t length,const char *magick_restrict format,va_list operands)
450 {
451   ssize_t
452     n;
453 
454 #if defined(MAGICKCORE_LOCALE_SUPPORT) && defined(MAGICKCORE_HAVE_VSNPRINTF_L)
455   {
456     locale_t
457       locale;
458 
459     locale=AcquireCLocale();
460     if (locale == (locale_t) NULL)
461       n=(ssize_t) vsnprintf(string,length,format,operands);
462     else
463 #if defined(MAGICKCORE_WINDOWS_SUPPORT)
464       n=(ssize_t) vsnprintf_l(string,length,format,locale,operands);
465 #else
466       n=(ssize_t) vsnprintf_l(string,length,locale,format,operands);
467 #endif
468   }
469 #elif defined(MAGICKCORE_HAVE_VSNPRINTF)
470 #if defined(MAGICKCORE_LOCALE_SUPPORT) && defined(MAGICKCORE_HAVE_USELOCALE)
471   {
472     locale_t
473       locale,
474       previous_locale;
475 
476     locale=AcquireCLocale();
477     if (locale == (locale_t) NULL)
478       n=(ssize_t) vsnprintf(string,length,format,operands);
479     else
480       {
481         previous_locale=uselocale(locale);
482         n=(ssize_t) vsnprintf(string,length,format,operands);
483         uselocale(previous_locale);
484       }
485   }
486 #else
487   n=(ssize_t) vsnprintf(string,length,format,operands);
488 #endif
489 #else
490   n=(ssize_t) vsprintf(string,format,operands);
491 #endif
492   if (n < 0)
493     string[length-1]='\0';
494   return(n);
495 }
496 
FormatLocaleString(char * magick_restrict string,const size_t length,const char * magick_restrict format,...)497 MagickExport ssize_t FormatLocaleString(char *magick_restrict string,
498   const size_t length,const char *magick_restrict format,...)
499 {
500   ssize_t
501     n;
502 
503   va_list
504     operands;
505 
506   va_start(operands,format);
507   n=FormatLocaleStringList(string,length,format,operands);
508   va_end(operands);
509   return(n);
510 }
511 
512 /*
513 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
514 %                                                                             %
515 %                                                                             %
516 %                                                                             %
517 +   G e t L o c a l e I n f o _                                               %
518 %                                                                             %
519 %                                                                             %
520 %                                                                             %
521 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
522 %
523 %  GetLocaleInfo_() searches the locale list for the specified tag and if
524 %  found returns attributes for that element.
525 %
526 %  The format of the GetLocaleInfo method is:
527 %
528 %      const LocaleInfo *GetLocaleInfo_(const char *tag,
529 %        ExceptionInfo *exception)
530 %
531 %  A description of each parameter follows:
532 %
533 %    o tag: the locale tag.
534 %
535 %    o exception: return any errors or warnings in this structure.
536 %
537 */
GetLocaleInfo_(const char * tag,ExceptionInfo * exception)538 MagickExport const LocaleInfo *GetLocaleInfo_(const char *tag,
539   ExceptionInfo *exception)
540 {
541   const LocaleInfo
542     *locale_info;
543 
544   assert(exception != (ExceptionInfo *) NULL);
545   if (IsLocaleTreeInstantiated(exception) == MagickFalse)
546     return((const LocaleInfo *) NULL);
547   LockSemaphoreInfo(locale_semaphore);
548   if ((tag == (const char *) NULL) || (LocaleCompare(tag,"*") == 0))
549     {
550       ResetSplayTreeIterator(locale_cache);
551       locale_info=(const LocaleInfo *) GetNextValueInSplayTree(locale_cache);
552       UnlockSemaphoreInfo(locale_semaphore);
553       return(locale_info);
554     }
555   locale_info=(const LocaleInfo *) GetValueFromSplayTree(locale_cache,tag);
556   UnlockSemaphoreInfo(locale_semaphore);
557   return(locale_info);
558 }
559 
560 /*
561 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
562 %                                                                             %
563 %                                                                             %
564 %                                                                             %
565 %   G e t L o c a l e I n f o L i s t                                         %
566 %                                                                             %
567 %                                                                             %
568 %                                                                             %
569 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
570 %
571 %  GetLocaleInfoList() returns any locale messages that match the
572 %  specified pattern.
573 %
574 %  The format of the GetLocaleInfoList function is:
575 %
576 %      const LocaleInfo **GetLocaleInfoList(const char *pattern,
577 %        size_t *number_messages,ExceptionInfo *exception)
578 %
579 %  A description of each parameter follows:
580 %
581 %    o pattern: Specifies a pointer to a text string containing a pattern.
582 %
583 %    o number_messages:  This integer returns the number of locale messages in
584 %    the list.
585 %
586 %    o exception: return any errors or warnings in this structure.
587 %
588 */
589 
590 #if defined(__cplusplus) || defined(c_plusplus)
591 extern "C" {
592 #endif
593 
LocaleInfoCompare(const void * x,const void * y)594 static int LocaleInfoCompare(const void *x,const void *y)
595 {
596   const LocaleInfo
597     **p,
598     **q;
599 
600   p=(const LocaleInfo **) x,
601   q=(const LocaleInfo **) y;
602   if (LocaleCompare((*p)->path,(*q)->path) == 0)
603     return(LocaleCompare((*p)->tag,(*q)->tag));
604   return(LocaleCompare((*p)->path,(*q)->path));
605 }
606 
607 #if defined(__cplusplus) || defined(c_plusplus)
608 }
609 #endif
610 
GetLocaleInfoList(const char * pattern,size_t * number_messages,ExceptionInfo * exception)611 MagickExport const LocaleInfo **GetLocaleInfoList(const char *pattern,
612   size_t *number_messages,ExceptionInfo *exception)
613 {
614   const LocaleInfo
615     **messages;
616 
617   const LocaleInfo
618     *p;
619 
620   ssize_t
621     i;
622 
623   /*
624     Allocate locale list.
625   */
626   assert(pattern != (char *) NULL);
627   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
628   assert(number_messages != (size_t *) NULL);
629   *number_messages=0;
630   p=GetLocaleInfo_("*",exception);
631   if (p == (const LocaleInfo *) NULL)
632     return((const LocaleInfo **) NULL);
633   messages=(const LocaleInfo **) AcquireQuantumMemory((size_t)
634     GetNumberOfNodesInSplayTree(locale_cache)+1UL,sizeof(*messages));
635   if (messages == (const LocaleInfo **) NULL)
636     return((const LocaleInfo **) NULL);
637   /*
638     Generate locale list.
639   */
640   LockSemaphoreInfo(locale_semaphore);
641   ResetSplayTreeIterator(locale_cache);
642   p=(const LocaleInfo *) GetNextValueInSplayTree(locale_cache);
643   for (i=0; p != (const LocaleInfo *) NULL; )
644   {
645     if ((p->stealth == MagickFalse) &&
646         (GlobExpression(p->tag,pattern,MagickTrue) != MagickFalse))
647       messages[i++]=p;
648     p=(const LocaleInfo *) GetNextValueInSplayTree(locale_cache);
649   }
650   UnlockSemaphoreInfo(locale_semaphore);
651   qsort((void *) messages,(size_t) i,sizeof(*messages),LocaleInfoCompare);
652   messages[i]=(LocaleInfo *) NULL;
653   *number_messages=(size_t) i;
654   return(messages);
655 }
656 
657 /*
658 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
659 %                                                                             %
660 %                                                                             %
661 %                                                                             %
662 %   G e t L o c a l e L i s t                                                 %
663 %                                                                             %
664 %                                                                             %
665 %                                                                             %
666 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
667 %
668 %  GetLocaleList() returns any locale messages that match the specified
669 %  pattern.
670 %
671 %  The format of the GetLocaleList function is:
672 %
673 %      char **GetLocaleList(const char *pattern,size_t *number_messages,
674 %        Exceptioninfo *exception)
675 %
676 %  A description of each parameter follows:
677 %
678 %    o pattern: Specifies a pointer to a text string containing a pattern.
679 %
680 %    o number_messages:  This integer returns the number of messages in the
681 %      list.
682 %
683 %    o exception: return any errors or warnings in this structure.
684 %
685 */
686 
687 #if defined(__cplusplus) || defined(c_plusplus)
688 extern "C" {
689 #endif
690 
LocaleTagCompare(const void * x,const void * y)691 static int LocaleTagCompare(const void *x,const void *y)
692 {
693   char
694     **p,
695     **q;
696 
697   p=(char **) x;
698   q=(char **) y;
699   return(LocaleCompare(*p,*q));
700 }
701 
702 #if defined(__cplusplus) || defined(c_plusplus)
703 }
704 #endif
705 
GetLocaleList(const char * pattern,size_t * number_messages,ExceptionInfo * exception)706 MagickExport char **GetLocaleList(const char *pattern,
707   size_t *number_messages,ExceptionInfo *exception)
708 {
709   char
710     **messages;
711 
712   const LocaleInfo
713     *p;
714 
715   ssize_t
716     i;
717 
718   /*
719     Allocate locale list.
720   */
721   assert(pattern != (char *) NULL);
722   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
723   assert(number_messages != (size_t *) NULL);
724   *number_messages=0;
725   p=GetLocaleInfo_("*",exception);
726   if (p == (const LocaleInfo *) NULL)
727     return((char **) NULL);
728   messages=(char **) AcquireQuantumMemory((size_t)
729     GetNumberOfNodesInSplayTree(locale_cache)+1UL,sizeof(*messages));
730   if (messages == (char **) NULL)
731     return((char **) NULL);
732   LockSemaphoreInfo(locale_semaphore);
733   p=(const LocaleInfo *) GetNextValueInSplayTree(locale_cache);
734   for (i=0; p != (const LocaleInfo *) NULL; )
735   {
736     if ((p->stealth == MagickFalse) &&
737         (GlobExpression(p->tag,pattern,MagickTrue) != MagickFalse))
738       messages[i++]=ConstantString(p->tag);
739     p=(const LocaleInfo *) GetNextValueInSplayTree(locale_cache);
740   }
741   UnlockSemaphoreInfo(locale_semaphore);
742   qsort((void *) messages,(size_t) i,sizeof(*messages),LocaleTagCompare);
743   messages[i]=(char *) NULL;
744   *number_messages=(size_t) i;
745   return(messages);
746 }
747 
748 /*
749 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
750 %                                                                             %
751 %                                                                             %
752 %                                                                             %
753 %   G e t L o c a l e M e s s a g e                                           %
754 %                                                                             %
755 %                                                                             %
756 %                                                                             %
757 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
758 %
759 %  GetLocaleMessage() returns a message in the current locale that matches the
760 %  supplied tag.
761 %
762 %  The format of the GetLocaleMessage method is:
763 %
764 %      const char *GetLocaleMessage(const char *tag)
765 %
766 %  A description of each parameter follows:
767 %
768 %    o tag: Return a message that matches this tag in the current locale.
769 %
770 */
GetLocaleMessage(const char * tag)771 MagickExport const char *GetLocaleMessage(const char *tag)
772 {
773   char
774     name[MaxTextExtent];
775 
776   const LocaleInfo
777     *locale_info;
778 
779   ExceptionInfo
780     *exception;
781 
782   if ((tag == (const char *) NULL) || (*tag == '\0'))
783     return(tag);
784   exception=AcquireExceptionInfo();
785   (void) FormatLocaleString(name,MaxTextExtent,"%s/",tag);
786   locale_info=GetLocaleInfo_(name,exception);
787   exception=DestroyExceptionInfo(exception);
788   if (locale_info != (const LocaleInfo *) NULL)
789     return(locale_info->message);
790   return(tag);
791 }
792 
793 /*
794 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
795 %                                                                             %
796 %                                                                             %
797 %                                                                             %
798 %  G e t L o c a l e O p t i o n s                                            %
799 %                                                                             %
800 %                                                                             %
801 %                                                                             %
802 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
803 %
804 %  GetLocaleOptions() returns any Magick configuration messages associated
805 %  with the specified filename.
806 %
807 %  The format of the GetLocaleOptions method is:
808 %
809 %      LinkedListInfo *GetLocaleOptions(const char *filename,
810 %        ExceptionInfo *exception)
811 %
812 %  A description of each parameter follows:
813 %
814 %    o filename: the locale file tag.
815 %
816 %    o exception: return any errors or warnings in this structure.
817 %
818 */
GetLocaleOptions(const char * filename,ExceptionInfo * exception)819 MagickExport LinkedListInfo *GetLocaleOptions(const char *filename,
820   ExceptionInfo *exception)
821 {
822   char
823     path[MaxTextExtent];
824 
825   const char
826     *element;
827 
828   LinkedListInfo
829     *messages,
830     *paths;
831 
832   StringInfo
833     *xml;
834 
835   assert(filename != (const char *) NULL);
836   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
837   assert(exception != (ExceptionInfo *) NULL);
838   (void) CopyMagickString(path,filename,MaxTextExtent);
839   /*
840     Load XML from configuration files to linked-list.
841   */
842   messages=NewLinkedList(0);
843   paths=GetConfigurePaths(filename,exception);
844   if (paths != (LinkedListInfo *) NULL)
845     {
846       ResetLinkedListIterator(paths);
847       element=(const char *) GetNextValueInLinkedList(paths);
848       while (element != (const char *) NULL)
849       {
850         (void) FormatLocaleString(path,MaxTextExtent,"%s%s",element,filename);
851         (void) LogMagickEvent(LocaleEvent,GetMagickModule(),
852           "Searching for locale file: \"%s\"",path);
853         xml=ConfigureFileToStringInfo(path);
854         if (xml != (StringInfo *) NULL)
855           (void) AppendValueToLinkedList(messages,xml);
856         element=(const char *) GetNextValueInLinkedList(paths);
857       }
858       paths=DestroyLinkedList(paths,RelinquishMagickMemory);
859     }
860 #if defined(MAGICKCORE_WINDOWS_SUPPORT)
861   {
862     char
863       *blob;
864 
865     blob=(char *) NTResourceToBlob(filename);
866     if (blob != (char *) NULL)
867       {
868         xml=AcquireStringInfo(0);
869         SetStringInfoLength(xml,strlen(blob)+1);
870         SetStringInfoDatum(xml,(const unsigned char *) blob);
871         blob=(char *) RelinquishMagickMemory(blob);
872         SetStringInfoPath(xml,filename);
873         (void) AppendValueToLinkedList(messages,xml);
874       }
875   }
876 #endif
877   ResetLinkedListIterator(messages);
878   return(messages);
879 }
880 
881 /*
882 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
883 %                                                                             %
884 %                                                                             %
885 %                                                                             %
886 %   G e t L o c a l e V a l u e                                               %
887 %                                                                             %
888 %                                                                             %
889 %                                                                             %
890 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
891 %
892 %  GetLocaleValue() returns the message associated with the locale info.
893 %
894 %  The format of the GetLocaleValue method is:
895 %
896 %      const char *GetLocaleValue(const LocaleInfo *locale_info)
897 %
898 %  A description of each parameter follows:
899 %
900 %    o locale_info:  The locale info.
901 %
902 */
GetLocaleValue(const LocaleInfo * locale_info)903 MagickExport const char *GetLocaleValue(const LocaleInfo *locale_info)
904 {
905   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
906   assert(locale_info != (LocaleInfo *) NULL);
907   assert(locale_info->signature == MagickCoreSignature);
908   return(locale_info->message);
909 }
910 
911 /*
912 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
913 %                                                                             %
914 %                                                                             %
915 %                                                                             %
916 +   I s L o c a l e T r e e I n s t a n t i a t e d                           %
917 %                                                                             %
918 %                                                                             %
919 %                                                                             %
920 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
921 %
922 %  IsLocaleTreeInstantiated() determines if the locale tree is instantiated.
923 %  If not, it instantiates the tree and returns it.
924 %
925 %  The format of the IsLocaleInstantiated method is:
926 %
927 %      MagickBooleanType IsLocaleTreeInstantiated(ExceptionInfo *exception)
928 %
929 %  A description of each parameter follows.
930 %
931 %    o exception: return any errors or warnings in this structure.
932 %
933 */
IsLocaleTreeInstantiated(ExceptionInfo * exception)934 static MagickBooleanType IsLocaleTreeInstantiated(ExceptionInfo *exception)
935 {
936   if (locale_cache == (SplayTreeInfo *) NULL)
937     {
938       if (locale_semaphore == (SemaphoreInfo *) NULL)
939         ActivateSemaphoreInfo(&locale_semaphore);
940       LockSemaphoreInfo(locale_semaphore);
941       if (locale_cache == (SplayTreeInfo *) NULL)
942         {
943           char
944             *locale;
945 
946           const char
947             *p;
948 
949           locale=(char *) NULL;
950           p=setlocale(LC_CTYPE,(const char *) NULL);
951           if (p != (const char *) NULL)
952             locale=ConstantString(p);
953           if (locale == (char *) NULL)
954             locale=GetEnvironmentValue("LC_ALL");
955           if (locale == (char *) NULL)
956             locale=GetEnvironmentValue("LC_MESSAGES");
957           if (locale == (char *) NULL)
958             locale=GetEnvironmentValue("LC_CTYPE");
959           if (locale == (char *) NULL)
960             locale=GetEnvironmentValue("LANG");
961           if (locale == (char *) NULL)
962             locale=ConstantString("C");
963           locale_cache=AcquireLocaleSplayTree(LocaleFilename,locale,exception);
964           locale=DestroyString(locale);
965         }
966       UnlockSemaphoreInfo(locale_semaphore);
967     }
968   return(locale_cache != (SplayTreeInfo *) NULL ? MagickTrue : MagickFalse);
969 }
970 
971 /*
972 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
973 %                                                                             %
974 %                                                                             %
975 %                                                                             %
976 +   I n t e r p r e t L o c a l e V a l u e                                   %
977 %                                                                             %
978 %                                                                             %
979 %                                                                             %
980 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
981 %
982 %  InterpretLocaleValue() interprets the string as a floating point number in
983 %  the "C" locale and returns its value as a double. If sentinal is not a null
984 %  pointer, the method also sets the value pointed by sentinal to point to the
985 %  first character after the number.
986 %
987 %  The format of the InterpretLocaleValue method is:
988 %
989 %      double InterpretLocaleValue(const char *value,char **sentinal)
990 %
991 %  A description of each parameter follows:
992 %
993 %    o value: the string value.
994 %
995 %    o sentinal:  if sentinal is not NULL, a pointer to the character after the
996 %      last character used in the conversion is stored in the location
997 %      referenced by sentinal.
998 %
999 */
InterpretLocaleValue(const char * magick_restrict string,char ** magick_restrict sentinal)1000 MagickExport double InterpretLocaleValue(const char *magick_restrict string,
1001   char **magick_restrict sentinal)
1002 {
1003   char
1004     *q;
1005 
1006   double
1007     value;
1008 
1009   if ((*string == '0') && ((string[1] | 0x20)=='x'))
1010     value=(double) strtoul(string,&q,16);
1011   else
1012     {
1013 #if defined(MAGICKCORE_LOCALE_SUPPORT) && defined(MAGICKCORE_HAVE_STRTOD_L)
1014       locale_t
1015         locale;
1016 
1017       locale=AcquireCLocale();
1018       if (locale == (locale_t) NULL)
1019         value=strtod(string,&q);
1020       else
1021         value=strtod_l(string,&q,locale);
1022 #else
1023       value=strtod(string,&q);
1024 #endif
1025     }
1026   if (sentinal != (char **) NULL)
1027     *sentinal=q;
1028   return(value);
1029 }
1030 
1031 /*
1032 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1033 %                                                                             %
1034 %                                                                             %
1035 %                                                                             %
1036 %  L i s t L o c a l e I n f o                                                %
1037 %                                                                             %
1038 %                                                                             %
1039 %                                                                             %
1040 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1041 %
1042 %  ListLocaleInfo() lists the locale info to a file.
1043 %
1044 %  The format of the ListLocaleInfo method is:
1045 %
1046 %      MagickBooleanType ListLocaleInfo(FILE *file,ExceptionInfo *exception)
1047 %
1048 %  A description of each parameter follows.
1049 %
1050 %    o file:  An pointer to a FILE.
1051 %
1052 %    o exception: return any errors or warnings in this structure.
1053 %
1054 */
ListLocaleInfo(FILE * file,ExceptionInfo * exception)1055 MagickExport MagickBooleanType ListLocaleInfo(FILE *file,
1056   ExceptionInfo *exception)
1057 {
1058   const char
1059     *path;
1060 
1061   const LocaleInfo
1062     **locale_info;
1063 
1064   ssize_t
1065     i;
1066 
1067   size_t
1068     number_messages;
1069 
1070   if (file == (const FILE *) NULL)
1071     file=stdout;
1072   number_messages=0;
1073   locale_info=GetLocaleInfoList("*",&number_messages,exception);
1074   if (locale_info == (const LocaleInfo **) NULL)
1075     return(MagickFalse);
1076   path=(const char *) NULL;
1077   for (i=0; i < (ssize_t) number_messages; i++)
1078   {
1079     if (locale_info[i]->stealth != MagickFalse)
1080       continue;
1081     if ((path == (const char *) NULL) ||
1082         (LocaleCompare(path,locale_info[i]->path) != 0))
1083       {
1084         if (locale_info[i]->path != (char *) NULL)
1085           (void) FormatLocaleFile(file,"\nPath: %s\n\n",locale_info[i]->path);
1086         (void) FormatLocaleFile(file,"Tag/Message\n");
1087         (void) FormatLocaleFile(file,
1088           "-------------------------------------------------"
1089           "------------------------------\n");
1090       }
1091     path=locale_info[i]->path;
1092     (void) FormatLocaleFile(file,"%s\n",locale_info[i]->tag);
1093     if (locale_info[i]->message != (char *) NULL)
1094       (void) FormatLocaleFile(file,"  %s",locale_info[i]->message);
1095     (void) FormatLocaleFile(file,"\n");
1096   }
1097   (void) fflush(file);
1098   locale_info=(const LocaleInfo **)
1099     RelinquishMagickMemory((void *) locale_info);
1100   return(MagickTrue);
1101 }
1102 
1103 /*
1104 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1105 %                                                                             %
1106 %                                                                             %
1107 %                                                                             %
1108 +   L o a d L o c a l e C a c h e                                             %
1109 %                                                                             %
1110 %                                                                             %
1111 %                                                                             %
1112 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1113 %
1114 %  LoadLocaleCache() loads the locale configurations which provides a mapping
1115 %  between locale attributes and a locale name.
1116 %
1117 %  The format of the LoadLocaleCache method is:
1118 %
1119 %      MagickBooleanType LoadLocaleCache(SplayTreeInfo *cache,const char *xml,
1120 %        const char *filename,const size_t depth,ExceptionInfo *exception)
1121 %
1122 %  A description of each parameter follows:
1123 %
1124 %    o xml:  The locale list in XML format.
1125 %
1126 %    o filename:  The locale list filename.
1127 %
1128 %    o depth: depth of <include /> statements.
1129 %
1130 %    o exception: return any errors or warnings in this structure.
1131 %
1132 */
1133 
ChopLocaleComponents(char * path,const size_t components)1134 static void ChopLocaleComponents(char *path,const size_t components)
1135 {
1136   char
1137     *p;
1138 
1139   ssize_t
1140     count;
1141 
1142   if (*path == '\0')
1143     return;
1144   p=path+strlen(path)-1;
1145   if (*p == '/')
1146     *p='\0';
1147   for (count=0; (count < (ssize_t) components) && (p > path); p--)
1148     if (*p == '/')
1149       {
1150         *p='\0';
1151         count++;
1152       }
1153   if (count < (ssize_t) components)
1154     *path='\0';
1155 }
1156 
LocaleFatalErrorHandler(const ExceptionType magick_unused (severity),const char * reason,const char * description)1157 static void LocaleFatalErrorHandler(
1158   const ExceptionType magick_unused(severity),
1159   const char *reason,const char *description)
1160 {
1161   magick_unreferenced(severity);
1162 
1163   if (reason == (char *) NULL)
1164     return;
1165   (void) FormatLocaleFile(stderr,"%s: %s",GetClientName(),reason);
1166   if (description != (char *) NULL)
1167     (void) FormatLocaleFile(stderr," (%s)",description);
1168   (void) FormatLocaleFile(stderr,".\n");
1169   (void) fflush(stderr);
1170   exit(1);
1171 }
1172 
LoadLocaleCache(SplayTreeInfo * cache,const char * xml,const char * filename,const char * locale,const size_t depth,ExceptionInfo * exception)1173 static MagickBooleanType LoadLocaleCache(SplayTreeInfo *cache,const char *xml,
1174   const char *filename,const char *locale,const size_t depth,
1175   ExceptionInfo *exception)
1176 {
1177   char
1178     keyword[MaxTextExtent],
1179     message[MaxTextExtent],
1180     tag[MaxTextExtent],
1181     *token;
1182 
1183   const char
1184     *q;
1185 
1186   FatalErrorHandler
1187     fatal_handler;
1188 
1189   LocaleInfo
1190     *locale_info;
1191 
1192   MagickStatusType
1193     status;
1194 
1195   char
1196     *p;
1197 
1198   size_t
1199     extent;
1200 
1201   /*
1202     Read the locale configure file.
1203   */
1204   (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1205     "Loading locale configure file \"%s\" ...",filename);
1206   if (xml == (const char *) NULL)
1207     return(MagickFalse);
1208   status=MagickTrue;
1209   locale_info=(LocaleInfo *) NULL;
1210   *tag='\0';
1211   *message='\0';
1212   *keyword='\0';
1213   fatal_handler=SetFatalErrorHandler(LocaleFatalErrorHandler);
1214   token=AcquireString(xml);
1215   extent=strlen(token)+MaxTextExtent;
1216   for (q=(char *) xml; *q != '\0'; )
1217   {
1218     /*
1219       Interpret XML.
1220     */
1221     (void) GetNextToken(q,&q,extent,token);
1222     if (*token == '\0')
1223       break;
1224     (void) CopyMagickString(keyword,token,MaxTextExtent);
1225     if (LocaleNCompare(keyword,"<!DOCTYPE",9) == 0)
1226       {
1227         /*
1228           Doctype element.
1229         */
1230         while ((LocaleNCompare(q,"]>",2) != 0) && (*q != '\0'))
1231         {
1232           (void) GetNextToken(q,&q,extent,token);
1233           while (isspace((int) ((unsigned char) *q)) != 0)
1234             q++;
1235         }
1236         continue;
1237       }
1238     if (LocaleNCompare(keyword,"<!--",4) == 0)
1239       {
1240         /*
1241           Comment element.
1242         */
1243         while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0'))
1244         {
1245           (void) GetNextToken(q,&q,extent,token);
1246           while (isspace((int) ((unsigned char) *q)) != 0)
1247             q++;
1248         }
1249         continue;
1250       }
1251     if (LocaleCompare(keyword,"<include") == 0)
1252       {
1253         /*
1254           Include element.
1255         */
1256         while (((*token != '/') && (*(token+1) != '>')) && (*q != '\0'))
1257         {
1258           (void) CopyMagickString(keyword,token,MaxTextExtent);
1259           (void) GetNextToken(q,&q,extent,token);
1260           if (*token != '=')
1261             continue;
1262           (void) GetNextToken(q,&q,extent,token);
1263           if (LocaleCompare(keyword,"locale") == 0)
1264             {
1265               if (LocaleCompare(locale,token) != 0)
1266                 break;
1267               continue;
1268             }
1269           if (LocaleCompare(keyword,"file") == 0)
1270             {
1271               if (depth > MagickMaxRecursionDepth)
1272                 (void) ThrowMagickException(exception,GetMagickModule(),
1273                   ConfigureError,"IncludeElementNestedTooDeeply","`%s'",token);
1274               else
1275                 {
1276                   char
1277                     path[MaxTextExtent],
1278                     *xml;
1279 
1280                   *path='\0';
1281                   GetPathComponent(filename,HeadPath,path);
1282                   if (*path != '\0')
1283                     (void) ConcatenateMagickString(path,DirectorySeparator,
1284                       MaxTextExtent);
1285                   if (*token == *DirectorySeparator)
1286                     (void) CopyMagickString(path,token,MaxTextExtent);
1287                   else
1288                     (void) ConcatenateMagickString(path,token,MaxTextExtent);
1289                   xml=FileToXML(path,~0UL);
1290                   if (xml != (char *) NULL)
1291                     {
1292                       status&=LoadLocaleCache(cache,xml,path,locale,
1293                         depth+1,exception);
1294                       xml=(char *) RelinquishMagickMemory(xml);
1295                     }
1296                 }
1297             }
1298         }
1299         continue;
1300       }
1301     if (LocaleCompare(keyword,"<locale") == 0)
1302       {
1303         /*
1304           Locale element.
1305         */
1306         while ((*token != '>') && (*q != '\0'))
1307         {
1308           (void) CopyMagickString(keyword,token,MaxTextExtent);
1309           (void) GetNextToken(q,&q,extent,token);
1310           if (*token != '=')
1311             continue;
1312           (void) GetNextToken(q,&q,extent,token);
1313         }
1314         continue;
1315       }
1316     if (LocaleCompare(keyword,"</locale>") == 0)
1317       {
1318         ChopLocaleComponents(tag,1);
1319         (void) ConcatenateMagickString(tag,"/",MaxTextExtent);
1320         continue;
1321       }
1322     if (LocaleCompare(keyword,"<localemap>") == 0)
1323       continue;
1324     if (LocaleCompare(keyword,"</localemap>") == 0)
1325       continue;
1326     if (LocaleCompare(keyword,"<message") == 0)
1327       {
1328         /*
1329           Message element.
1330         */
1331         while ((*token != '>') && (*q != '\0'))
1332         {
1333           (void) CopyMagickString(keyword,token,MaxTextExtent);
1334           (void) GetNextToken(q,&q,extent,token);
1335           if (*token != '=')
1336             continue;
1337           (void) GetNextToken(q,&q,extent,token);
1338           if (LocaleCompare(keyword,"name") == 0)
1339             {
1340               (void) ConcatenateMagickString(tag,token,MaxTextExtent);
1341               (void) ConcatenateMagickString(tag,"/",MaxTextExtent);
1342             }
1343         }
1344         for (p=(char *) q; (*q != '<') && (*q != '\0'); q++) ;
1345         while (isspace((int) ((unsigned char) *p)) != 0)
1346           p++;
1347         q--;
1348         while ((isspace((int) ((unsigned char) *q)) != 0) && (q > p))
1349           q--;
1350         (void) CopyMagickString(message,p,MagickMin((size_t) (q-p+2),
1351           MaxTextExtent));
1352         locale_info=(LocaleInfo *) AcquireMagickMemory(sizeof(*locale_info));
1353         if (locale_info == (LocaleInfo *) NULL)
1354           ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1355         (void) memset(locale_info,0,sizeof(*locale_info));
1356         locale_info->path=ConstantString(filename);
1357         locale_info->tag=ConstantString(tag);
1358         locale_info->message=ConstantString(message);
1359         locale_info->signature=MagickCoreSignature;
1360         status=AddValueToSplayTree(cache,locale_info->tag,locale_info);
1361         if (status == MagickFalse)
1362           (void) ThrowMagickException(exception,GetMagickModule(),
1363             ResourceLimitError,"MemoryAllocationFailed","`%s'",
1364             locale_info->tag);
1365         (void) ConcatenateMagickString(tag,message,MaxTextExtent);
1366         (void) ConcatenateMagickString(tag,"\n",MaxTextExtent);
1367         q++;
1368         continue;
1369       }
1370     if (LocaleCompare(keyword,"</message>") == 0)
1371       {
1372         ChopLocaleComponents(tag,2);
1373         (void) ConcatenateMagickString(tag,"/",MaxTextExtent);
1374         continue;
1375       }
1376     if (*keyword == '<')
1377       {
1378         /*
1379           Subpath element.
1380         */
1381         if (*(keyword+1) == '?')
1382           continue;
1383         if (*(keyword+1) == '/')
1384           {
1385             ChopLocaleComponents(tag,1);
1386             if (*tag != '\0')
1387               (void) ConcatenateMagickString(tag,"/",MaxTextExtent);
1388             continue;
1389           }
1390         token[strlen(token)-1]='\0';
1391         (void) CopyMagickString(token,token+1,MaxTextExtent);
1392         (void) ConcatenateMagickString(tag,token,MaxTextExtent);
1393         (void) ConcatenateMagickString(tag,"/",MaxTextExtent);
1394         continue;
1395       }
1396     (void) GetNextToken(q,(const char **) NULL,extent,token);
1397     if (*token != '=')
1398       continue;
1399   }
1400   token=(char *) RelinquishMagickMemory(token);
1401   (void) SetFatalErrorHandler(fatal_handler);
1402   return(status != 0 ? MagickTrue : MagickFalse);
1403 }
1404 
1405 /*
1406 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1407 %                                                                             %
1408 %                                                                             %
1409 %                                                                             %
1410 %   L o c a l e C o m p a r e                                                 %
1411 %                                                                             %
1412 %                                                                             %
1413 %                                                                             %
1414 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1415 %
1416 %  LocaleCompare() performs a case-insensitive comparison of two strings
1417 %  byte-by-byte, according to the ordering of the current locale encoding.
1418 %  LocaleCompare returns an integer greater than, equal to, or less than 0,
1419 %  if the string pointed to by p is greater than, equal to, or less than the
1420 %  string pointed to by q respectively.  The sign of a non-zero return value
1421 %  is determined by the sign of the difference between the values of the first
1422 %  pair of bytes that differ in the strings being compared.
1423 %
1424 %  The format of the LocaleCompare method is:
1425 %
1426 %      int LocaleCompare(const char *p,const char *q)
1427 %
1428 %  A description of each parameter follows:
1429 %
1430 %    o p: A pointer to a character string.
1431 %
1432 %    o q: A pointer to a character string to compare to p.
1433 %
1434 */
LocaleCompare(const char * p,const char * q)1435 MagickExport int LocaleCompare(const char *p,const char *q)
1436 {
1437   if (p == (char *) NULL)
1438     {
1439       if (q == (char *) NULL)
1440         return(0);
1441       return(-1);
1442     }
1443   if (q == (char *) NULL)
1444     return(1);
1445 #if defined(MAGICKCORE_HAVE_STRCASECMP)
1446   return(strcasecmp(p,q));
1447 #else
1448   {
1449     int
1450       c,
1451       d;
1452 
1453     for ( ; ; )
1454     {
1455       c=(int) *((unsigned char *) p);
1456       d=(int) *((unsigned char *) q);
1457       if ((c == 0) || (AsciiMap[c] != AsciiMap[d]))
1458         break;
1459       p++;
1460       q++;
1461     }
1462     return(AsciiMap[c]-(int) AsciiMap[d]);
1463   }
1464 #endif
1465 }
1466 
1467 /*
1468 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1469 %                                                                             %
1470 %                                                                             %
1471 %                                                                             %
1472 %   L o c a l e L o w e r                                                     %
1473 %                                                                             %
1474 %                                                                             %
1475 %                                                                             %
1476 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1477 %
1478 %  LocaleLower() transforms all of the characters in the supplied
1479 %  null-terminated string, changing all uppercase letters to lowercase.
1480 %
1481 %  The format of the LocaleLower method is:
1482 %
1483 %      void LocaleLower(char *string)
1484 %
1485 %  A description of each parameter follows:
1486 %
1487 %    o string: A pointer to the string to convert to lower-case Locale.
1488 %
1489 */
LocaleLower(char * string)1490 MagickExport void LocaleLower(char *string)
1491 {
1492   char
1493     *q;
1494 
1495   assert(string != (char *) NULL);
1496   for (q=string; *q != '\0'; q++)
1497     *q=(char) LocaleLowercase((int) *q);
1498 }
1499 
1500 /*
1501 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1502 %                                                                             %
1503 %                                                                             %
1504 %                                                                             %
1505 %   L o c a l e L o w e r c a s e                                             %
1506 %                                                                             %
1507 %                                                                             %
1508 %                                                                             %
1509 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1510 %
1511 %  LocaleLowercase() convert to uppercase.
1512 %
1513 %  The format of the LocaleLowercase method is:
1514 %
1515 %      void LocaleLowercase(const int c)
1516 %
1517 %  A description of each parameter follows:
1518 %
1519 %    o If c is a uppercase letter, return its lowercase equivalent.
1520 %
1521 */
LocaleLowercase(const int c)1522 MagickExport int LocaleLowercase(const int c)
1523 {
1524   if ((c == EOF) || (c != (unsigned char) c))
1525     return(c);
1526 #if defined(MAGICKCORE_LOCALE_SUPPORT)
1527   if (c_locale != (locale_t) NULL)
1528     return(tolower_l((int) ((unsigned char) c),c_locale));
1529 #endif
1530   return(tolower((int) ((unsigned char) c)));
1531 }
1532 
1533 /*
1534 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1535 %                                                                             %
1536 %                                                                             %
1537 %                                                                             %
1538 %   L o c a l e N C o m p a r e                                               %
1539 %                                                                             %
1540 %                                                                             %
1541 %                                                                             %
1542 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1543 %
1544 %  LocaleNCompare() performs a case-insensitive comparison of two strings
1545 %  byte-by-byte, according to the ordering of the current locale encoding.
1546 %
1547 %  LocaleNCompare returns an integer greater than, equal to, or less than 0,
1548 %  if the string pointed to by p is greater than, equal to, or less than the
1549 %  string pointed to by q respectively.  The sign of a non-zero return value
1550 %  is determined by the sign of the difference between the values of the first
1551 %  pair of bytes that differ in the strings being compared.
1552 %
1553 %  The LocaleNCompare method makes the same comparison as LocaleCompare but
1554 %  looks at a maximum of n bytes.  Bytes following a null byte are not
1555 %  compared.
1556 %
1557 %  The format of the LocaleNCompare method is:
1558 %
1559 %      int LocaleNCompare(const char *p,const char *q,const size_t n)
1560 %
1561 %  A description of each parameter follows:
1562 %
1563 %    o p: A pointer to a character string.
1564 %
1565 %    o q: A pointer to a character string to compare to p.
1566 %
1567 %    o length: the number of characters to compare in strings p and q.
1568 %
1569 */
LocaleNCompare(const char * p,const char * q,const size_t length)1570 MagickExport int LocaleNCompare(const char *p,const char *q,const size_t length)
1571 {
1572   if (p == (char *) NULL)
1573     {
1574       if (q == (char *) NULL)
1575         return(0);
1576       return(-1);
1577     }
1578   if (q == (char *) NULL)
1579     return(1);
1580 #if defined(MAGICKCORE_HAVE_STRNCASECMP)
1581   return(strncasecmp(p,q,length));
1582 #else
1583   {
1584     int
1585       c,
1586       d;
1587 
1588     size_t
1589       i;
1590 
1591     for (i=length; i != 0; i--)
1592     {
1593       c=(int) *((unsigned char *) p);
1594       d=(int) *((unsigned char *) q);
1595       if (AsciiMap[c] != AsciiMap[d])
1596         return(AsciiMap[c]-(int) AsciiMap[d]);
1597       if (c == 0)
1598         return(0);
1599       p++;
1600       q++;
1601     }
1602     return(0);
1603   }
1604 #endif
1605 }
1606 
1607 /*
1608 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1609 %                                                                             %
1610 %                                                                             %
1611 %                                                                             %
1612 %   L o c a l e U p p e r                                                     %
1613 %                                                                             %
1614 %                                                                             %
1615 %                                                                             %
1616 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1617 %
1618 %  LocaleUpper() transforms all of the characters in the supplied
1619 %  null-terminated string, changing all lowercase letters to uppercase.
1620 %
1621 %  The format of the LocaleUpper method is:
1622 %
1623 %      void LocaleUpper(char *string)
1624 %
1625 %  A description of each parameter follows:
1626 %
1627 %    o string: A pointer to the string to convert to upper-case Locale.
1628 %
1629 */
LocaleUpper(char * string)1630 MagickExport void LocaleUpper(char *string)
1631 {
1632   char
1633     *q;
1634 
1635   assert(string != (char *) NULL);
1636   for (q=string; *q != '\0'; q++)
1637     *q=(char) LocaleUppercase((int) *q);
1638 }
1639 
1640 /*
1641 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1642 %                                                                             %
1643 %                                                                             %
1644 %                                                                             %
1645 %   L o c a l e U p p e r c a s e                                             %
1646 %                                                                             %
1647 %                                                                             %
1648 %                                                                             %
1649 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1650 %
1651 %  LocaleUppercase() convert to uppercase.
1652 %
1653 %  The format of the LocaleUppercase method is:
1654 %
1655 %      void LocaleUppercase(const int c)
1656 %
1657 %  A description of each parameter follows:
1658 %
1659 %    o If c is a lowercase letter, return its uppercase equivalent.
1660 %
1661 */
LocaleUppercase(const int c)1662 MagickExport int LocaleUppercase(const int c)
1663 {
1664   if (c == EOF)
1665     return(c);
1666 #if defined(MAGICKCORE_LOCALE_SUPPORT)
1667   if (c_locale != (locale_t) NULL)
1668     return(toupper_l((int) ((unsigned char) c),c_locale));
1669 #endif
1670   return(toupper((int) ((unsigned char) c)));
1671 }
1672 
1673 /*
1674 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1675 %                                                                             %
1676 %                                                                             %
1677 %                                                                             %
1678 +   L o c a l e C o m p o n e n t G e n e s i s                               %
1679 %                                                                             %
1680 %                                                                             %
1681 %                                                                             %
1682 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1683 %
1684 %  LocaleComponentGenesis() instantiates the locale component.
1685 %
1686 %  The format of the LocaleComponentGenesis method is:
1687 %
1688 %      MagickBooleanType LocaleComponentGenesis(void)
1689 %
1690 */
LocaleComponentGenesis(void)1691 MagickExport MagickBooleanType LocaleComponentGenesis(void)
1692 {
1693   if (locale_semaphore == (SemaphoreInfo *) NULL)
1694     locale_semaphore=AllocateSemaphoreInfo();
1695 #if defined(MAGICKCORE_LOCALE_SUPPORT)
1696   (void) AcquireCLocale();
1697 #endif
1698   return(MagickTrue);
1699 }
1700 
1701 /*
1702 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1703 %                                                                             %
1704 %                                                                             %
1705 %                                                                             %
1706 +   L o c a l e C o m p o n e n t T e r m i n u s                             %
1707 %                                                                             %
1708 %                                                                             %
1709 %                                                                             %
1710 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1711 %
1712 %  LocaleComponentTerminus() destroys the locale component.
1713 %
1714 %  The format of the LocaleComponentTerminus method is:
1715 %
1716 %      LocaleComponentTerminus(void)
1717 %
1718 */
LocaleComponentTerminus(void)1719 MagickExport void LocaleComponentTerminus(void)
1720 {
1721   if (locale_semaphore == (SemaphoreInfo *) NULL)
1722     ActivateSemaphoreInfo(&locale_semaphore);
1723   LockSemaphoreInfo(locale_semaphore);
1724   if (locale_cache != (SplayTreeInfo *) NULL)
1725     locale_cache=DestroySplayTree(locale_cache);
1726 #if defined(MAGICKCORE_LOCALE_SUPPORT)
1727   DestroyCLocale();
1728 #endif
1729   UnlockSemaphoreInfo(locale_semaphore);
1730   DestroySemaphoreInfo(&locale_semaphore);
1731 }
1732