]> granicus.if.org Git - imagemagick/blob - MagickCore/locale.c
(no commit message)
[imagemagick] / MagickCore / locale.c
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-2014 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 %    http://www.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 \f
39 /*
40   Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/blob.h"
44 #include "MagickCore/client.h"
45 #include "MagickCore/configure.h"
46 #include "MagickCore/exception.h"
47 #include "MagickCore/exception-private.h"
48 #include "MagickCore/hashmap.h"
49 #include "MagickCore/locale_.h"
50 #include "MagickCore/locale-private.h"
51 #include "MagickCore/log.h"
52 #include "MagickCore/memory_.h"
53 #include "MagickCore/nt-base-private.h"
54 #include "MagickCore/semaphore.h"
55 #include "MagickCore/splay-tree.h"
56 #include "MagickCore/string_.h"
57 #include "MagickCore/string-private.h"
58 #include "MagickCore/token.h"
59 #include "MagickCore/utility.h"
60 #include "MagickCore/utility-private.h"
61 #include "MagickCore/xml-tree.h"
62 \f
63 /*
64   Define declarations.
65 */
66 #define LocaleFilename  "locale.xml"
67 #define MaxRecursionDepth  200
68 \f
69 /*
70   Typedef declarations.
71 */
72 #if defined(__CYGWIN__)
73 typedef struct _locale_t
74   *locale_t;
75 #endif
76 \f
77 /*
78   Static declarations.
79 */
80 static const char
81   *LocaleMap =
82     "<?xml version=\"1.0\"?>"
83     "<localemap>"
84     "  <locale name=\"C\">"
85     "    <Exception>"
86     "     <Message name=\"\">"
87     "     </Message>"
88     "    </Exception>"
89     "  </locale>"
90     "</localemap>";
91
92 static SemaphoreInfo
93   *locale_semaphore = (SemaphoreInfo *) NULL;
94
95 static SplayTreeInfo
96   *locale_list = (SplayTreeInfo *) NULL;
97
98 #if defined(MAGICKCORE_HAVE_STRTOD_L)
99 static volatile locale_t
100   c_locale = (locale_t) NULL;
101 #endif
102 \f
103 /*
104   Forward declarations.
105 */
106 static MagickBooleanType
107   InitializeLocaleList(ExceptionInfo *),
108   LoadLocaleLists(const char *,const char *,ExceptionInfo *);
109 \f
110 #if defined(MAGICKCORE_HAVE_STRTOD_L)
111 /*
112 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
113 %                                                                             %
114 %                                                                             %
115 %                                                                             %
116 +   A c q u i r e C L o c a l e                                               %
117 %                                                                             %
118 %                                                                             %
119 %                                                                             %
120 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
121 %
122 %  AcquireCLocale() allocates the C locale object, or (locale_t) 0 with
123 %  errno set if it cannot be acquired.
124 %
125 %  The format of the AcquireCLocale method is:
126 %
127 %      locale_t AcquireCLocale(void)
128 %
129 */
130 static locale_t AcquireCLocale(void)
131 {
132 #if defined(MAGICKCORE_HAVE_NEWLOCALE)
133   if (c_locale == (locale_t) NULL)
134     c_locale=newlocale(LC_ALL_MASK,"C",(locale_t) 0);
135 #elif defined(MAGICKCORE_WINDOWS_SUPPORT)
136   if (c_locale == (locale_t) NULL)
137     c_locale=_create_locale(LC_ALL,"C");
138 #endif
139   return(c_locale);
140 }
141 #endif
142 \f
143 #if defined(MAGICKCORE_HAVE_STRTOD_L)
144 /*
145 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
146 %                                                                             %
147 %                                                                             %
148 %                                                                             %
149 +   D e s t r o y C L o c a l e                                               %
150 %                                                                             %
151 %                                                                             %
152 %                                                                             %
153 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
154 %
155 %  DestroyCLocale() releases the resources allocated for a locale object
156 %  returned by a call to the AcquireCLocale() method.
157 %
158 %  The format of the DestroyCLocale method is:
159 %
160 %      void DestroyCLocale(void)
161 %
162 */
163 static void DestroyCLocale(void)
164 {
165 #if defined(MAGICKCORE_HAVE_NEWLOCALE)
166   if (c_locale != (locale_t) NULL)
167     freelocale(c_locale);
168 #elif defined(MAGICKCORE_WINDOWS_SUPPORT)
169   if (c_locale != (locale_t) NULL)
170     _free_locale(c_locale);
171 #endif
172   c_locale=(locale_t) NULL;
173 }
174 #endif
175 \f
176 /*
177 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
178 %                                                                             %
179 %                                                                             %
180 %                                                                             %
181 %   D e s t r o y L o c a l e O p t i o n s                                   %
182 %                                                                             %
183 %                                                                             %
184 %                                                                             %
185 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
186 %
187 %  DestroyLocaleOptions() releases memory associated with an locale
188 %  messages.
189 %
190 %  The format of the DestroyProfiles method is:
191 %
192 %      LinkedListInfo *DestroyLocaleOptions(Image *image)
193 %
194 %  A description of each parameter follows:
195 %
196 %    o image: the image.
197 %
198 */
199
200 static void *DestroyOptions(void *message)
201 {
202   return(DestroyStringInfo((StringInfo *) message));
203 }
204
205 MagickExport LinkedListInfo *DestroyLocaleOptions(LinkedListInfo *messages)
206 {
207   assert(messages != (LinkedListInfo *) NULL);
208   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
209   return(DestroyLinkedList(messages,DestroyOptions));
210 }
211 \f
212 /*
213 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
214 %                                                                             %
215 %                                                                             %
216 %                                                                             %
217 +  F o r m a t L o c a l e F i l e                                            %
218 %                                                                             %
219 %                                                                             %
220 %                                                                             %
221 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
222 %
223 %  FormatLocaleFile() prints formatted output of a variable argument list to a
224 %  file in the "C" locale.
225 %
226 %  The format of the FormatLocaleFile method is:
227 %
228 %      ssize_t FormatLocaleFile(FILE *file,const char *format,...)
229 %
230 %  A description of each parameter follows.
231 %
232 %   o file:  the file.
233 %
234 %   o format:  A file describing the format to use to write the remaining
235 %     arguments.
236 %
237 */
238
239 MagickPrivate ssize_t FormatLocaleFileList(FILE *file,
240   const char *restrict format,va_list operands)
241 {
242   ssize_t
243     n;
244
245 #if defined(MAGICKCORE_HAVE_VFPRINTF_L)
246   {
247     locale_t
248       locale;
249
250     locale=AcquireCLocale();
251     if (locale == (locale_t) NULL)
252       n=(ssize_t) vfprintf(file,format,operands);
253     else
254 #if defined(MAGICKCORE_WINDOWS_SUPPORT)
255       n=(ssize_t) vfprintf_l(file,format,locale,operands);
256 #else
257       n=(ssize_t) vfprintf_l(file,locale,format,operands);
258 #endif
259   }
260 #else
261 #if defined(MAGICKCORE_HAVE_USELOCALE)
262   {
263     locale_t
264       locale,
265       previous_locale;
266
267     locale=AcquireCLocale();
268     if (locale == (locale_t) NULL)
269       n=(ssize_t) vfprintf(file,format,operands);
270     else
271       {
272         previous_locale=uselocale(locale);
273         n=(ssize_t) vfprintf(file,format,operands);
274         uselocale(previous_locale);
275       }
276   }
277 #else
278   n=(ssize_t) vfprintf(file,format,operands);
279 #endif
280 #endif
281   return(n);
282 }
283
284 MagickExport ssize_t FormatLocaleFile(FILE *file,const char *restrict format,
285   ...)
286 {
287   ssize_t
288     n;
289
290   va_list
291     operands;
292
293   va_start(operands,format);
294   n=FormatLocaleFileList(file,format,operands);
295   va_end(operands);
296   return(n);
297 }
298 \f
299 /*
300 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
301 %                                                                             %
302 %                                                                             %
303 %                                                                             %
304 +  F o r m a t L o c a l e S t r i n g                                        %
305 %                                                                             %
306 %                                                                             %
307 %                                                                             %
308 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
309 %
310 %  FormatLocaleString() prints formatted output of a variable argument list to
311 %  a string buffer in the "C" locale.
312 %
313 %  The format of the FormatLocaleString method is:
314 %
315 %      ssize_t FormatLocaleString(char *string,const size_t length,
316 %        const char *format,...)
317 %
318 %  A description of each parameter follows.
319 %
320 %   o string:  FormatLocaleString() returns the formatted string in this
321 %     character buffer.
322 %
323 %   o length: the maximum length of the string.
324 %
325 %   o format:  A string describing the format to use to write the remaining
326 %     arguments.
327 %
328 */
329
330 MagickPrivate ssize_t FormatLocaleStringList(char *restrict string,
331   const size_t length,const char *restrict format,va_list operands)
332 {
333   ssize_t
334     n;
335
336 #if defined(MAGICKCORE_HAVE_VSNPRINTF_L)
337   {
338     locale_t
339       locale;
340
341     locale=AcquireCLocale();
342     if (locale == (locale_t) NULL)
343       n=(ssize_t) vsnprintf(string,length,format,operands);
344     else
345 #if defined(MAGICKCORE_WINDOWS_SUPPORT)
346       n=(ssize_t) vsnprintf_l(string,length,format,locale,operands);
347 #else
348       n=(ssize_t) vsnprintf_l(string,length,locale,format,operands);
349 #endif
350   }
351 #elif defined(MAGICKCORE_HAVE_VSNPRINTF)
352 #if defined(MAGICKCORE_HAVE_USELOCALE)
353   {
354     locale_t
355       locale,
356       previous_locale;
357
358     locale=AcquireCLocale();
359     if (locale == (locale_t) NULL)
360       n=(ssize_t) vsnprintf(string,length,format,operands);
361     else
362       {
363         previous_locale=uselocale(locale);
364         n=(ssize_t) vsnprintf(string,length,format,operands);
365         uselocale(previous_locale);
366       }
367   }
368 #else
369   n=(ssize_t) vsnprintf(string,length,format,operands);
370 #endif
371 #else
372   n=(ssize_t) vsprintf(string,format,operands);
373 #endif
374   if (n < 0)
375     string[length-1]='\0';
376   return(n);
377 }
378
379 MagickExport ssize_t FormatLocaleString(char *restrict string,
380   const size_t length,const char *restrict format,...)
381 {
382   ssize_t
383     n;
384
385   va_list
386     operands;
387
388   va_start(operands,format);
389   n=FormatLocaleStringList(string,length,format,operands);
390   va_end(operands);
391   return(n);
392 }
393 \f
394 /*
395 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
396 %                                                                             %
397 %                                                                             %
398 %                                                                             %
399 +   G e t L o c a l e I n f o _                                               %
400 %                                                                             %
401 %                                                                             %
402 %                                                                             %
403 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
404 %
405 %  GetLocaleInfo_() searches the locale list for the specified tag and if
406 %  found returns attributes for that element.
407 %
408 %  The format of the GetLocaleInfo method is:
409 %
410 %      const LocaleInfo *GetLocaleInfo_(const char *tag,
411 %        ExceptionInfo *exception)
412 %
413 %  A description of each parameter follows:
414 %
415 %    o tag: the locale tag.
416 %
417 %    o exception: return any errors or warnings in this structure.
418 %
419 */
420 MagickExport const LocaleInfo *GetLocaleInfo_(const char *tag,
421   ExceptionInfo *exception)
422 {
423   assert(exception != (ExceptionInfo *) NULL);
424   if (locale_list == (SplayTreeInfo *) NULL)
425     if (InitializeLocaleList(exception) == MagickFalse)
426       return((const LocaleInfo *) NULL);
427   if ((tag == (const char *) NULL) || (LocaleCompare(tag,"*") == 0))
428     {
429       ResetSplayTreeIterator(locale_list);
430       return((const LocaleInfo *) GetNextValueInSplayTree(locale_list));
431     }
432   return((const LocaleInfo *) GetValueFromSplayTree(locale_list,tag));
433 }
434 \f
435 /*
436 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
437 %                                                                             %
438 %                                                                             %
439 %                                                                             %
440 %   G e t L o c a l e I n f o L i s t                                         %
441 %                                                                             %
442 %                                                                             %
443 %                                                                             %
444 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
445 %
446 %  GetLocaleInfoList() returns any locale messages that match the
447 %  specified pattern.
448 %
449 %  The format of the GetLocaleInfoList function is:
450 %
451 %      const LocaleInfo **GetLocaleInfoList(const char *pattern,
452 %        size_t *number_messages,ExceptionInfo *exception)
453 %
454 %  A description of each parameter follows:
455 %
456 %    o pattern: Specifies a pointer to a text string containing a pattern.
457 %
458 %    o number_messages:  This integer returns the number of locale messages in
459 %    the list.
460 %
461 %    o exception: return any errors or warnings in this structure.
462 %
463 */
464
465 #if defined(__cplusplus) || defined(c_plusplus)
466 extern "C" {
467 #endif
468
469 static int LocaleInfoCompare(const void *x,const void *y)
470 {
471   const LocaleInfo
472     **p,
473     **q;
474
475   p=(const LocaleInfo **) x,
476   q=(const LocaleInfo **) y;
477   if (LocaleCompare((*p)->path,(*q)->path) == 0)
478     return(LocaleCompare((*p)->tag,(*q)->tag));
479   return(LocaleCompare((*p)->path,(*q)->path));
480 }
481
482 #if defined(__cplusplus) || defined(c_plusplus)
483 }
484 #endif
485
486 MagickExport const LocaleInfo **GetLocaleInfoList(const char *pattern,
487   size_t *number_messages,ExceptionInfo *exception)
488 {
489   const LocaleInfo
490     **messages;
491
492   register const LocaleInfo
493     *p;
494
495   register ssize_t
496     i;
497
498   /*
499     Allocate locale list.
500   */
501   assert(pattern != (char *) NULL);
502   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
503   assert(number_messages != (size_t *) NULL);
504   *number_messages=0;
505   p=GetLocaleInfo_("*",exception);
506   if (p == (const LocaleInfo *) NULL)
507     return((const LocaleInfo **) NULL);
508   messages=(const LocaleInfo **) AcquireQuantumMemory((size_t)
509     GetNumberOfNodesInSplayTree(locale_list)+1UL,sizeof(*messages));
510   if (messages == (const LocaleInfo **) NULL)
511     return((const LocaleInfo **) NULL);
512   /*
513     Generate locale list.
514   */
515   LockSemaphoreInfo(locale_semaphore);
516   ResetSplayTreeIterator(locale_list);
517   p=(const LocaleInfo *) GetNextValueInSplayTree(locale_list);
518   for (i=0; p != (const LocaleInfo *) NULL; )
519   {
520     if ((p->stealth == MagickFalse) &&
521         (GlobExpression(p->tag,pattern,MagickTrue) != MagickFalse))
522       messages[i++]=p;
523     p=(const LocaleInfo *) GetNextValueInSplayTree(locale_list);
524   }
525   UnlockSemaphoreInfo(locale_semaphore);
526   qsort((void *) messages,(size_t) i,sizeof(*messages),LocaleInfoCompare);
527   messages[i]=(LocaleInfo *) NULL;
528   *number_messages=(size_t) i;
529   return(messages);
530 }
531 \f
532 /*
533 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
534 %                                                                             %
535 %                                                                             %
536 %                                                                             %
537 %   G e t L o c a l e L i s t                                                 %
538 %                                                                             %
539 %                                                                             %
540 %                                                                             %
541 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
542 %
543 %  GetLocaleList() returns any locale messages that match the specified
544 %  pattern.
545 %
546 %  The format of the GetLocaleList function is:
547 %
548 %      char **GetLocaleList(const char *pattern,size_t *number_messages,
549 %        Exceptioninfo *exception)
550 %
551 %  A description of each parameter follows:
552 %
553 %    o pattern: Specifies a pointer to a text string containing a pattern.
554 %
555 %    o number_messages:  This integer returns the number of messages in the
556 %      list.
557 %
558 %    o exception: return any errors or warnings in this structure.
559 %
560 */
561
562 #if defined(__cplusplus) || defined(c_plusplus)
563 extern "C" {
564 #endif
565
566 static int LocaleTagCompare(const void *x,const void *y)
567 {
568   register char
569     **p,
570     **q;
571
572   p=(char **) x;
573   q=(char **) y;
574   return(LocaleCompare(*p,*q));
575 }
576
577 #if defined(__cplusplus) || defined(c_plusplus)
578 }
579 #endif
580
581 MagickExport char **GetLocaleList(const char *pattern,
582   size_t *number_messages,ExceptionInfo *exception)
583 {
584   char
585     **messages;
586
587   register const LocaleInfo
588     *p;
589
590   register ssize_t
591     i;
592
593   /*
594     Allocate locale list.
595   */
596   assert(pattern != (char *) NULL);
597   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
598   assert(number_messages != (size_t *) NULL);
599   *number_messages=0;
600   p=GetLocaleInfo_("*",exception);
601   if (p == (const LocaleInfo *) NULL)
602     return((char **) NULL);
603   messages=(char **) AcquireQuantumMemory((size_t)
604     GetNumberOfNodesInSplayTree(locale_list)+1UL,sizeof(*messages));
605   if (messages == (char **) NULL)
606     return((char **) NULL);
607   LockSemaphoreInfo(locale_semaphore);
608   p=(const LocaleInfo *) GetNextValueInSplayTree(locale_list);
609   for (i=0; p != (const LocaleInfo *) NULL; )
610   {
611     if ((p->stealth == MagickFalse) &&
612         (GlobExpression(p->tag,pattern,MagickTrue) != MagickFalse))
613       messages[i++]=ConstantString(p->tag);
614     p=(const LocaleInfo *) GetNextValueInSplayTree(locale_list);
615   }
616   UnlockSemaphoreInfo(locale_semaphore);
617   qsort((void *) messages,(size_t) i,sizeof(*messages),LocaleTagCompare);
618   messages[i]=(char *) NULL;
619   *number_messages=(size_t) i;
620   return(messages);
621 }
622 \f
623 /*
624 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
625 %                                                                             %
626 %                                                                             %
627 %                                                                             %
628 %   G e t L o c a l e M e s s a g e                                           %
629 %                                                                             %
630 %                                                                             %
631 %                                                                             %
632 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
633 %
634 %  GetLocaleMessage() returns a message in the current locale that matches the
635 %  supplied tag.
636 %
637 %  The format of the GetLocaleMessage method is:
638 %
639 %      const char *GetLocaleMessage(const char *tag)
640 %
641 %  A description of each parameter follows:
642 %
643 %    o tag: Return a message that matches this tag in the current locale.
644 %
645 */
646 MagickExport const char *GetLocaleMessage(const char *tag)
647 {
648   char
649     name[MaxTextExtent];
650
651   const LocaleInfo
652     *locale_info;
653
654   ExceptionInfo
655     *exception;
656
657   if ((tag == (const char *) NULL) || (*tag == '\0'))
658     return(tag);
659   exception=AcquireExceptionInfo();
660   (void) FormatLocaleString(name,MaxTextExtent,"%s/",tag);
661   locale_info=GetLocaleInfo_(name,exception);
662   exception=DestroyExceptionInfo(exception);
663   if (locale_info != (const LocaleInfo *) NULL)
664     return(locale_info->message);
665   return(tag);
666 }
667 \f
668 /*
669 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
670 %                                                                             %
671 %                                                                             %
672 %                                                                             %
673 %  G e t L o c a l e O p t i o n s                                            %
674 %                                                                             %
675 %                                                                             %
676 %                                                                             %
677 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
678 %
679 %  GetLocaleOptions() returns any Magick configuration messages associated
680 %  with the specified filename.
681 %
682 %  The format of the GetLocaleOptions method is:
683 %
684 %      LinkedListInfo *GetLocaleOptions(const char *filename,
685 %        ExceptionInfo *exception)
686 %
687 %  A description of each parameter follows:
688 %
689 %    o filename: the locale file tag.
690 %
691 %    o exception: return any errors or warnings in this structure.
692 %
693 */
694 MagickExport LinkedListInfo *GetLocaleOptions(const char *filename,
695   ExceptionInfo *exception)
696 {
697   char
698     path[MaxTextExtent];
699
700   const char
701     *element;
702
703   LinkedListInfo
704     *messages,
705     *paths;
706
707   StringInfo
708     *xml;
709
710   assert(filename != (const char *) NULL);
711   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
712   assert(exception != (ExceptionInfo *) NULL);
713   (void) CopyMagickString(path,filename,MaxTextExtent);
714   /*
715     Load XML from configuration files to linked-list.
716   */
717   messages=NewLinkedList(0);
718   paths=GetConfigurePaths(filename,exception);
719   if (paths != (LinkedListInfo *) NULL)
720     {
721       ResetLinkedListIterator(paths);
722       element=(const char *) GetNextValueInLinkedList(paths);
723       while (element != (const char *) NULL)
724       {
725         (void) FormatLocaleString(path,MaxTextExtent,"%s%s",element,filename);
726         (void) LogMagickEvent(LocaleEvent,GetMagickModule(),
727           "Searching for locale file: \"%s\"",path);
728         xml=ConfigureFileToStringInfo(path);
729         if (xml != (StringInfo *) NULL)
730           (void) AppendValueToLinkedList(messages,xml);
731         element=(const char *) GetNextValueInLinkedList(paths);
732       }
733       paths=DestroyLinkedList(paths,RelinquishMagickMemory);
734     }
735 #if defined(MAGICKCORE_WINDOWS_SUPPORT)
736   {
737     char
738       *blob;
739
740     blob=(char *) NTResourceToBlob(filename);
741     if (blob != (char *) NULL)
742       {
743         xml=AcquireStringInfo(0);
744         SetStringInfoLength(xml,strlen(blob)+1);
745         SetStringInfoDatum(xml,(unsigned char *) blob);
746         SetStringInfoPath(xml,filename);
747         (void) AppendValueToLinkedList(messages,xml);
748       }
749   }
750 #endif
751   ResetLinkedListIterator(messages);
752   return(messages);
753 }
754 \f
755 /*
756 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
757 %                                                                             %
758 %                                                                             %
759 %                                                                             %
760 %   G e t L o c a l e V a l u e                                               %
761 %                                                                             %
762 %                                                                             %
763 %                                                                             %
764 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
765 %
766 %  GetLocaleValue() returns the message associated with the locale info.
767 %
768 %  The format of the GetLocaleValue method is:
769 %
770 %      const char *GetLocaleValue(const LocaleInfo *locale_info)
771 %
772 %  A description of each parameter follows:
773 %
774 %    o locale_info:  The locale info.
775 %
776 */
777 MagickExport const char *GetLocaleValue(const LocaleInfo *locale_info)
778 {
779   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
780   assert(locale_info != (LocaleInfo *) NULL);
781   assert(locale_info->signature == MagickSignature);
782   return(locale_info->message);
783 }
784 \f
785 /*
786 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
787 %                                                                             %
788 %                                                                             %
789 %                                                                             %
790 +   I n i t i a l i z e L o c a l e L i s t                                   %
791 %                                                                             %
792 %                                                                             %
793 %                                                                             %
794 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
795 %
796 %  InitializeLocaleList() initializes the locale list.
797 %
798 %  The format of the InitializeLocaleList method is:
799 %
800 %      MagickBooleanType InitializeLocaleList(ExceptionInfo *exception)
801 %
802 %  A description of each parameter follows.
803 %
804 %    o exception: return any errors or warnings in this structure.
805 %
806 */
807 static MagickBooleanType InitializeLocaleList(ExceptionInfo *exception)
808 {
809   if (locale_semaphore == (SemaphoreInfo *) NULL)
810     ActivateSemaphoreInfo(&locale_semaphore);
811   LockSemaphoreInfo(locale_semaphore);
812   if (locale_list == (SplayTreeInfo *) NULL)
813     {
814       char
815         *locale;
816
817       register const char
818         *p;
819
820       locale=(char *) NULL;
821       p=setlocale(LC_CTYPE,(const char *) NULL);
822       if (p != (const char *) NULL)
823         locale=ConstantString(p);
824       if (locale == (char *) NULL)
825         locale=GetEnvironmentValue("LC_ALL");
826       if (locale == (char *) NULL)
827         locale=GetEnvironmentValue("LC_MESSAGES");
828       if (locale == (char *) NULL)
829         locale=GetEnvironmentValue("LC_CTYPE");
830       if (locale == (char *) NULL)
831         locale=GetEnvironmentValue("LANG");
832       if (locale == (char *) NULL)
833         locale=ConstantString("C");
834       (void) LoadLocaleLists(LocaleFilename,locale,exception);
835       locale=DestroyString(locale);
836     }
837   UnlockSemaphoreInfo(locale_semaphore);
838   return(locale_list != (SplayTreeInfo *) NULL ? MagickTrue : MagickFalse);
839 }
840 \f
841 /*
842 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
843 %                                                                             %
844 %                                                                             %
845 %                                                                             %
846 +   I n t e r p r e t L o c a l e V a l u e                                   %
847 %                                                                             %
848 %                                                                             %
849 %                                                                             %
850 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
851 %
852 %  InterpretLocaleValue() interprets the string as a floating point number in
853 %  the "C" locale and returns its value as a double. If sentinal is not a null
854 %  pointer, the method also sets the value pointed by sentinal to point to the
855 %  first character after the number.
856 %
857 %  The format of the InterpretLocaleValue method is:
858 %
859 %      double InterpretLocaleValue(const char *value,char **sentinal)
860 %
861 %  A description of each parameter follows:
862 %
863 %    o value: the string value.
864 %
865 %    o sentinal:  if sentinal is not NULL, a pointer to the character after the
866 %      last character used in the conversion is stored in the location
867 %      referenced by sentinal.
868 %
869 */
870 MagickExport double InterpretLocaleValue(const char *restrict string,
871   char **restrict sentinal)
872 {
873   char
874     *q;
875
876   double
877     value;
878
879   if ((*string == '0') && ((string[1] | 0x20)=='x'))
880     value=(double) strtoul(string,&q,16);
881   else
882     {
883 #if defined(MAGICKCORE_HAVE_STRTOD_L)
884       locale_t
885         locale;
886
887       locale=AcquireCLocale();
888       if (locale == (locale_t) NULL)
889         value=strtod(string,&q);
890       else
891         value=strtod_l(string,&q,locale);
892 #else
893       value=strtod(string,&q);
894 #endif
895     }
896   if (sentinal != (char **) NULL)
897     *sentinal=q;
898   return(value);
899 }
900 \f
901 /*
902 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
903 %                                                                             %
904 %                                                                             %
905 %                                                                             %
906 %  L i s t L o c a l e I n f o                                                %
907 %                                                                             %
908 %                                                                             %
909 %                                                                             %
910 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
911 %
912 %  ListLocaleInfo() lists the locale info to a file.
913 %
914 %  The format of the ListLocaleInfo method is:
915 %
916 %      MagickBooleanType ListLocaleInfo(FILE *file,ExceptionInfo *exception)
917 %
918 %  A description of each parameter follows.
919 %
920 %    o file:  An pointer to a FILE.
921 %
922 %    o exception: return any errors or warnings in this structure.
923 %
924 */
925 MagickExport MagickBooleanType ListLocaleInfo(FILE *file,
926   ExceptionInfo *exception)
927 {
928   const char
929     *path;
930
931   const LocaleInfo
932     **locale_info;
933
934   register ssize_t
935     i;
936
937   size_t
938     number_messages;
939
940   if (file == (const FILE *) NULL)
941     file=stdout;
942   number_messages=0;
943   locale_info=GetLocaleInfoList("*",&number_messages,exception);
944   if (locale_info == (const LocaleInfo **) NULL)
945     return(MagickFalse);
946   path=(const char *) NULL;
947   for (i=0; i < (ssize_t) number_messages; i++)
948   {
949     if (locale_info[i]->stealth != MagickFalse)
950       continue;
951     if ((path == (const char *) NULL) ||
952         (LocaleCompare(path,locale_info[i]->path) != 0))
953       {
954         if (locale_info[i]->path != (char *) NULL)
955           (void) FormatLocaleFile(file,"\nPath: %s\n\n",locale_info[i]->path);
956         (void) FormatLocaleFile(file,"Tag/Message\n");
957         (void) FormatLocaleFile(file,
958           "-------------------------------------------------"
959           "------------------------------\n");
960       }
961     path=locale_info[i]->path;
962     (void) FormatLocaleFile(file,"%s\n",locale_info[i]->tag);
963     if (locale_info[i]->message != (char *) NULL)
964       (void) FormatLocaleFile(file,"  %s",locale_info[i]->message);
965     (void) FormatLocaleFile(file,"\n");
966   }
967   (void) fflush(file);
968   locale_info=(const LocaleInfo **)
969     RelinquishMagickMemory((void *) locale_info);
970   return(MagickTrue);
971 }
972 \f
973 /*
974 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
975 %                                                                             %
976 %                                                                             %
977 %                                                                             %
978 +   L o a d L o c a l e L i s t                                               %
979 %                                                                             %
980 %                                                                             %
981 %                                                                             %
982 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
983 %
984 %  LoadLocaleList() loads the locale configuration file which provides a mapping
985 %  between locale attributes and a locale name.
986 %
987 %  The format of the LoadLocaleList method is:
988 %
989 %      MagickBooleanType LoadLocaleList(const char *xml,const char *filename,
990 %        const size_t depth,ExceptionInfo *exception)
991 %
992 %  A description of each parameter follows:
993 %
994 %    o xml:  The locale list in XML format.
995 %
996 %    o filename:  The locale list filename.
997 %
998 %    o depth: depth of <include /> statements.
999 %
1000 %    o exception: return any errors or warnings in this structure.
1001 %
1002 */
1003
1004 static void ChopLocaleComponents(char *path,const size_t components)
1005 {
1006   register char
1007     *p;
1008
1009   ssize_t
1010     count;
1011
1012   if (*path == '\0')
1013     return;
1014   p=path+strlen(path)-1;
1015   if (*p == '/')
1016     *p='\0';
1017   for (count=0; (count < (ssize_t) components) && (p > path); p--)
1018     if (*p == '/')
1019       {
1020         *p='\0';
1021         count++;
1022       }
1023   if (count < (ssize_t) components)
1024     *path='\0';
1025 }
1026
1027 static void *DestroyLocaleNode(void *locale_info)
1028 {
1029   register LocaleInfo
1030     *p;
1031
1032   p=(LocaleInfo *) locale_info;
1033   if (p->path != (char *) NULL)
1034     p->path=DestroyString(p->path);
1035   if (p->tag != (char *) NULL)
1036     p->tag=DestroyString(p->tag);
1037   if (p->message != (char *) NULL)
1038     p->message=DestroyString(p->message);
1039   return(RelinquishMagickMemory(p));
1040 }
1041
1042 static void LocaleFatalErrorHandler(
1043   const ExceptionType magick_unused(severity),
1044   const char *reason,const char *description)
1045 {
1046   if (reason == (char *) NULL)
1047     return;
1048   (void) FormatLocaleFile(stderr,"%s: %s",GetClientName(),reason);
1049   if (description != (char *) NULL)
1050     (void) FormatLocaleFile(stderr," (%s)",description);
1051   (void) FormatLocaleFile(stderr,".\n");
1052   (void) fflush(stderr);
1053   exit(1);
1054 }
1055
1056 static inline size_t MagickMin(const size_t x,const size_t y)
1057 {
1058   if (x < y)
1059     return(x);
1060   return(y);
1061 }
1062
1063 static MagickBooleanType LoadLocaleList(const char *xml,const char *filename,
1064   const char *locale,const size_t depth,ExceptionInfo *exception)
1065 {
1066   char
1067     keyword[MaxTextExtent],
1068     message[MaxTextExtent],
1069     tag[MaxTextExtent],
1070     *token;
1071
1072   const char
1073     *q;
1074
1075   FatalErrorHandler
1076     fatal_handler;
1077
1078   LocaleInfo
1079     *locale_info;
1080
1081   MagickBooleanType
1082     status;
1083
1084   register char
1085     *p;
1086
1087   /*
1088     Read the locale configure file.
1089   */
1090   (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1091     "Loading locale configure file \"%s\" ...",filename);
1092   if (xml == (const char *) NULL)
1093     return(MagickFalse);
1094   if (locale_list == (SplayTreeInfo *) NULL)
1095     {
1096       locale_list=NewSplayTree(CompareSplayTreeString,(void *(*)(void *)) NULL,
1097         DestroyLocaleNode);
1098       if (locale_list == (SplayTreeInfo *) NULL)
1099         return(MagickFalse);
1100     }
1101   status=MagickTrue;
1102   locale_info=(LocaleInfo *) NULL;
1103   *tag='\0';
1104   *message='\0';
1105   *keyword='\0';
1106   fatal_handler=SetFatalErrorHandler(LocaleFatalErrorHandler);
1107   token=AcquireString(xml);
1108   for (q=(char *) xml; *q != '\0'; )
1109   {
1110     /*
1111       Interpret XML.
1112     */
1113     GetMagickToken(q,&q,token);
1114     if (*token == '\0')
1115       break;
1116     (void) CopyMagickString(keyword,token,MaxTextExtent);
1117     if (LocaleNCompare(keyword,"<!DOCTYPE",9) == 0)
1118       {
1119         /*
1120           Doctype element.
1121         */
1122         while ((LocaleNCompare(q,"]>",2) != 0) && (*q != '\0'))
1123         {
1124           GetMagickToken(q,&q,token);
1125           while (isspace((int) ((unsigned char) *q)) != 0)
1126             q++;
1127         }
1128         continue;
1129       }
1130     if (LocaleNCompare(keyword,"<!--",4) == 0)
1131       {
1132         /*
1133           Comment element.
1134         */
1135         while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0'))
1136         {
1137           GetMagickToken(q,&q,token);
1138           while (isspace((int) ((unsigned char) *q)) != 0)
1139             q++;
1140         }
1141         continue;
1142       }
1143     if (LocaleCompare(keyword,"<include") == 0)
1144       {
1145         /*
1146           Include element.
1147         */
1148         while (((*token != '/') && (*(token+1) != '>')) && (*q != '\0'))
1149         {
1150           (void) CopyMagickString(keyword,token,MaxTextExtent);
1151           GetMagickToken(q,&q,token);
1152           if (*token != '=')
1153             continue;
1154           GetMagickToken(q,&q,token);
1155           if (LocaleCompare(keyword,"locale") == 0)
1156             {
1157               if (LocaleCompare(locale,token) != 0)
1158                 break;
1159               continue;
1160             }
1161           if (LocaleCompare(keyword,"file") == 0)
1162             {
1163               if (depth > 200)
1164                 (void) ThrowMagickException(exception,GetMagickModule(),
1165                   ConfigureError,"IncludeElementNestedTooDeeply","`%s'",token);
1166               else
1167                 {
1168                   char
1169                     path[MaxTextExtent],
1170                     *xml;
1171
1172                   *path='\0';
1173                   GetPathComponent(filename,HeadPath,path);
1174                   if (*path != '\0')
1175                     (void) ConcatenateMagickString(path,DirectorySeparator,
1176                       MaxTextExtent);
1177                   if (*token == *DirectorySeparator)
1178                     (void) CopyMagickString(path,token,MaxTextExtent);
1179                   else
1180                     (void) ConcatenateMagickString(path,token,MaxTextExtent);
1181                   xml=FileToString(path,~0UL,exception);
1182                   if (xml != (char *) NULL)
1183                     {
1184                       status=LoadLocaleList(xml,path,locale,depth+1,exception);
1185                       xml=(char *) RelinquishMagickMemory(xml);
1186                     }
1187                 }
1188             }
1189         }
1190         continue;
1191       }
1192     if (LocaleCompare(keyword,"<locale") == 0)
1193       {
1194         /*
1195           Locale element.
1196         */
1197         while ((*token != '>') && (*q != '\0'))
1198         {
1199           (void) CopyMagickString(keyword,token,MaxTextExtent);
1200           GetMagickToken(q,&q,token);
1201           if (*token != '=')
1202             continue;
1203           GetMagickToken(q,&q,token);
1204         }
1205         continue;
1206       }
1207     if (LocaleCompare(keyword,"</locale>") == 0)
1208       {
1209         ChopLocaleComponents(tag,1);
1210         (void) ConcatenateMagickString(tag,"/",MaxTextExtent);
1211         continue;
1212       }
1213     if (LocaleCompare(keyword,"<localemap>") == 0)
1214       continue;
1215     if (LocaleCompare(keyword,"</localemap>") == 0)
1216       continue;
1217     if (LocaleCompare(keyword,"<message") == 0)
1218       {
1219         /*
1220           Message element.
1221         */
1222         while ((*token != '>') && (*q != '\0'))
1223         {
1224           (void) CopyMagickString(keyword,token,MaxTextExtent);
1225           GetMagickToken(q,&q,token);
1226           if (*token != '=')
1227             continue;
1228           GetMagickToken(q,&q,token);
1229           if (LocaleCompare(keyword,"name") == 0)
1230             {
1231               (void) ConcatenateMagickString(tag,token,MaxTextExtent);
1232               (void) ConcatenateMagickString(tag,"/",MaxTextExtent);
1233             }
1234         }
1235         for (p=(char *) q; (*q != '<') && (*q != '\0'); q++) ;
1236         while (isspace((int) ((unsigned char) *p)) != 0)
1237           p++;
1238         q--;
1239         while ((isspace((int) ((unsigned char) *q)) != 0) && (q > p))
1240           q--;
1241         (void) CopyMagickString(message,p,MagickMin((size_t) (q-p+2),
1242           MaxTextExtent));
1243         locale_info=(LocaleInfo *) AcquireMagickMemory(sizeof(*locale_info));
1244         if (locale_info == (LocaleInfo *) NULL)
1245           ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1246         (void) ResetMagickMemory(locale_info,0,sizeof(*locale_info));
1247         locale_info->path=ConstantString(filename);
1248         locale_info->tag=ConstantString(tag);
1249         locale_info->message=ConstantString(message);
1250         locale_info->signature=MagickSignature;
1251         status=AddValueToSplayTree(locale_list,locale_info->tag,locale_info);
1252         if (status == MagickFalse)
1253           (void) ThrowMagickException(exception,GetMagickModule(),
1254             ResourceLimitError,"MemoryAllocationFailed","`%s'",
1255             locale_info->tag);
1256         (void) ConcatenateMagickString(tag,message,MaxTextExtent);
1257         (void) ConcatenateMagickString(tag,"\n",MaxTextExtent);
1258         q++;
1259         continue;
1260       }
1261     if (LocaleCompare(keyword,"</message>") == 0)
1262       {
1263         ChopLocaleComponents(tag,2);
1264         (void) ConcatenateMagickString(tag,"/",MaxTextExtent);
1265         continue;
1266       }
1267     if (*keyword == '<')
1268       {
1269         /*
1270           Subpath element.
1271         */
1272         if (*(keyword+1) == '?')
1273           continue;
1274         if (*(keyword+1) == '/')
1275           {
1276             ChopLocaleComponents(tag,1);
1277             if (*tag != '\0')
1278               (void) ConcatenateMagickString(tag,"/",MaxTextExtent);
1279             continue;
1280           }
1281         token[strlen(token)-1]='\0';
1282         (void) CopyMagickString(token,token+1,MaxTextExtent);
1283         (void) ConcatenateMagickString(tag,token,MaxTextExtent);
1284         (void) ConcatenateMagickString(tag,"/",MaxTextExtent);
1285         continue;
1286       }
1287     GetMagickToken(q,(const char **) NULL,token);
1288     if (*token != '=')
1289       continue;
1290   }
1291   token=(char *) RelinquishMagickMemory(token);
1292   (void) SetFatalErrorHandler(fatal_handler);
1293   return(status);
1294 }
1295 \f
1296 /*
1297 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1298 %                                                                             %
1299 %                                                                             %
1300 %                                                                             %
1301 %  L o a d L o c a l e L i s t s                                              %
1302 %                                                                             %
1303 %                                                                             %
1304 %                                                                             %
1305 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1306 %
1307 %  LoadLocaleList() loads one or more locale configuration file which
1308 %  provides a mapping between locale attributes and a locale tag.
1309 %
1310 %  The format of the LoadLocaleLists method is:
1311 %
1312 %      MagickBooleanType LoadLocaleLists(const char *filename,
1313 %        ExceptionInfo *exception)
1314 %
1315 %  A description of each parameter follows:
1316 %
1317 %    o filename: the font file tag.
1318 %
1319 %    o locale: the actual locale.
1320 %
1321 %    o exception: return any errors or warnings in this structure.
1322 %
1323 */
1324 static MagickBooleanType LoadLocaleLists(const char *filename,
1325   const char *locale,ExceptionInfo *exception)
1326 {
1327 #if defined(MAGICKCORE_ZERO_CONFIGURATION_SUPPORT)
1328   return(LoadLocaleList(LocaleMap,"built-in",locale,0,exception));
1329 #else
1330   const StringInfo
1331     *option;
1332
1333   LinkedListInfo
1334     *options;
1335
1336   MagickStatusType
1337     status;
1338
1339   status=MagickFalse;
1340   options=GetLocaleOptions(filename,exception);
1341   option=(const StringInfo *) GetNextValueInLinkedList(options);
1342   while (option != (const StringInfo *) NULL)
1343   {
1344     status&=LoadLocaleList((const char *) GetStringInfoDatum(option),
1345       GetStringInfoPath(option),locale,0,exception);
1346     option=(const StringInfo *) GetNextValueInLinkedList(options);
1347   }
1348   options=DestroyLocaleOptions(options);
1349   if ((locale_list == (SplayTreeInfo *) NULL) ||
1350       (GetNumberOfNodesInSplayTree(locale_list) == 0))
1351     {
1352       options=GetLocaleOptions("english.xml",exception);
1353       option=(const StringInfo *) GetNextValueInLinkedList(options);
1354       while (option != (const StringInfo *) NULL)
1355       {
1356         status&=LoadLocaleList((const char *) GetStringInfoDatum(option),
1357           GetStringInfoPath(option),locale,0,exception);
1358         option=(const StringInfo *) GetNextValueInLinkedList(options);
1359       }
1360       options=DestroyLocaleOptions(options);
1361     }
1362   if ((locale_list == (SplayTreeInfo *) NULL) ||
1363       (GetNumberOfNodesInSplayTree(locale_list) == 0))
1364     status&=LoadLocaleList(LocaleMap,"built-in",locale,0,exception);
1365   return(status != 0 ? MagickTrue : MagickFalse);
1366 #endif
1367 }
1368 \f
1369 /*
1370 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1371 %                                                                             %
1372 %                                                                             %
1373 %                                                                             %
1374 +   L o c a l e C o m p o n e n t G e n e s i s                               %
1375 %                                                                             %
1376 %                                                                             %
1377 %                                                                             %
1378 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1379 %
1380 %  LocaleComponentGenesis() instantiates the locale component.
1381 %
1382 %  The format of the LocaleComponentGenesis method is:
1383 %
1384 %      MagickBooleanType LocaleComponentGenesis(void)
1385 %
1386 */
1387 MagickPrivate MagickBooleanType LocaleComponentGenesis(void)
1388 {
1389   locale_semaphore=AcquireSemaphoreInfo();
1390   return(MagickTrue);
1391 }
1392 \f
1393 /*
1394 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1395 %                                                                             %
1396 %                                                                             %
1397 %                                                                             %
1398 +   L o c a l e C o m p o n e n t T e r m i n u s                             %
1399 %                                                                             %
1400 %                                                                             %
1401 %                                                                             %
1402 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1403 %
1404 %  LocaleComponentTerminus() destroys the locale component.
1405 %
1406 %  The format of the LocaleComponentTerminus method is:
1407 %
1408 %      LocaleComponentTerminus(void)
1409 %
1410 */
1411 MagickPrivate void LocaleComponentTerminus(void)
1412 {
1413   if (locale_semaphore == (SemaphoreInfo *) NULL)
1414     ActivateSemaphoreInfo(&locale_semaphore);
1415   LockSemaphoreInfo(locale_semaphore);
1416   if (locale_list != (SplayTreeInfo *) NULL)
1417     locale_list=DestroySplayTree(locale_list);
1418 #if defined(MAGICKCORE_HAVE_STRTOD_L)
1419   DestroyCLocale();
1420 #endif
1421   UnlockSemaphoreInfo(locale_semaphore);
1422   RelinquishSemaphoreInfo(&locale_semaphore);
1423 }