]> 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 %                                John Cristy                                  %
17 %                                 July 2003                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2011 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/semaphore.h"
54 #include "MagickCore/splay-tree.h"
55 #include "MagickCore/string_.h"
56 #include "MagickCore/token.h"
57 #include "MagickCore/utility.h"
58 #include "MagickCore/utility-private.h"
59 #include "MagickCore/xml-tree.h"
60 \f
61 /*
62   Define declarations.
63 */
64 #define LocaleFilename  "locale.xml"
65 #define MaxRecursionDepth  200
66 \f
67 /*
68   Typedef declarations.
69 */
70 #if defined(__CYGWIN__)
71 typedef struct _locale_t *locale_t;
72 #endif
73 \f
74 /*
75   Static declarations.
76 */
77 static const char
78   *LocaleMap =
79     "<?xml version=\"1.0\"?>"
80     "<localemap>"
81     "  <locale name=\"C\">"
82     "    <Exception>"
83     "     <Message name=\"\">"
84     "     </Message>"
85     "    </Exception>"
86     "  </locale>"
87     "</localemap>";
88
89 static SemaphoreInfo
90   *locale_semaphore = (SemaphoreInfo *) NULL;
91
92 static SplayTreeInfo
93   *locale_list = (SplayTreeInfo *) NULL;
94
95 #if defined(MAGICKCORE_HAVE_STRTOD_L)
96 static volatile locale_t
97   c_locale = (locale_t) NULL;
98 #endif
99
100 static volatile MagickBooleanType
101   instantiate_locale = MagickFalse;
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 MagickExport 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 MagickExport 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       (instantiate_locale == MagickFalse))
426     if (InitializeLocaleList(exception) == MagickFalse)
427       return((const LocaleInfo *) NULL);
428   if ((locale_list == (SplayTreeInfo *) NULL) ||
429       (GetNumberOfNodesInSplayTree(locale_list) == 0))
430     return((const LocaleInfo *) NULL);
431   if ((tag == (const char *) NULL) || (LocaleCompare(tag,"*") == 0))
432     {
433       ResetSplayTreeIterator(locale_list);
434       return((const LocaleInfo *) GetNextValueInSplayTree(locale_list));
435     }
436   return((const LocaleInfo *) GetValueFromSplayTree(locale_list,tag));
437 }
438 \f
439 /*
440 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
441 %                                                                             %
442 %                                                                             %
443 %                                                                             %
444 %   G e t L o c a l e I n f o L i s t                                         %
445 %                                                                             %
446 %                                                                             %
447 %                                                                             %
448 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
449 %
450 %  GetLocaleInfoList() returns any locale messages that match the
451 %  specified pattern.
452 %
453 %  The format of the GetLocaleInfoList function is:
454 %
455 %      const LocaleInfo **GetLocaleInfoList(const char *pattern,
456 %        size_t *number_messages,ExceptionInfo *exception)
457 %
458 %  A description of each parameter follows:
459 %
460 %    o pattern: Specifies a pointer to a text string containing a pattern.
461 %
462 %    o number_messages:  This integer returns the number of locale messages in
463 %    the list.
464 %
465 %    o exception: return any errors or warnings in this structure.
466 %
467 */
468
469 #if defined(__cplusplus) || defined(c_plusplus)
470 extern "C" {
471 #endif
472
473 static int LocaleInfoCompare(const void *x,const void *y)
474 {
475   const LocaleInfo
476     **p,
477     **q;
478
479   p=(const LocaleInfo **) x,
480   q=(const LocaleInfo **) y;
481   if (LocaleCompare((*p)->path,(*q)->path) == 0)
482     return(LocaleCompare((*p)->tag,(*q)->tag));
483   return(LocaleCompare((*p)->path,(*q)->path));
484 }
485
486 #if defined(__cplusplus) || defined(c_plusplus)
487 }
488 #endif
489
490 MagickExport const LocaleInfo **GetLocaleInfoList(const char *pattern,
491   size_t *number_messages,ExceptionInfo *exception)
492 {
493   const LocaleInfo
494     **messages;
495
496   register const LocaleInfo
497     *p;
498
499   register ssize_t
500     i;
501
502   /*
503     Allocate locale list.
504   */
505   assert(pattern != (char *) NULL);
506   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
507   assert(number_messages != (size_t *) NULL);
508   *number_messages=0;
509   p=GetLocaleInfo_("*",exception);
510   if (p == (const LocaleInfo *) NULL)
511     return((const LocaleInfo **) NULL);
512   messages=(const LocaleInfo **) AcquireQuantumMemory((size_t)
513     GetNumberOfNodesInSplayTree(locale_list)+1UL,sizeof(*messages));
514   if (messages == (const LocaleInfo **) NULL)
515     return((const LocaleInfo **) NULL);
516   /*
517     Generate locale list.
518   */
519   LockSemaphoreInfo(locale_semaphore);
520   ResetSplayTreeIterator(locale_list);
521   p=(const LocaleInfo *) GetNextValueInSplayTree(locale_list);
522   for (i=0; p != (const LocaleInfo *) NULL; )
523   {
524     if ((p->stealth == MagickFalse) &&
525         (GlobExpression(p->tag,pattern,MagickTrue) != MagickFalse))
526       messages[i++]=p;
527     p=(const LocaleInfo *) GetNextValueInSplayTree(locale_list);
528   }
529   UnlockSemaphoreInfo(locale_semaphore);
530   qsort((void *) messages,(size_t) i,sizeof(*messages),LocaleInfoCompare);
531   messages[i]=(LocaleInfo *) NULL;
532   *number_messages=(size_t) i;
533   return(messages);
534 }
535 \f
536 /*
537 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
538 %                                                                             %
539 %                                                                             %
540 %                                                                             %
541 %   G e t L o c a l e L i s t                                                 %
542 %                                                                             %
543 %                                                                             %
544 %                                                                             %
545 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
546 %
547 %  GetLocaleList() returns any locale messages that match the specified
548 %  pattern.
549 %
550 %  The format of the GetLocaleList function is:
551 %
552 %      char **GetLocaleList(const char *pattern,size_t *number_messages,
553 %        Exceptioninfo *exception)
554 %
555 %  A description of each parameter follows:
556 %
557 %    o pattern: Specifies a pointer to a text string containing a pattern.
558 %
559 %    o number_messages:  This integer returns the number of messages in the
560 %      list.
561 %
562 %    o exception: return any errors or warnings in this structure.
563 %
564 */
565
566 #if defined(__cplusplus) || defined(c_plusplus)
567 extern "C" {
568 #endif
569
570 static int LocaleTagCompare(const void *x,const void *y)
571 {
572   register char
573     **p,
574     **q;
575
576   p=(char **) x;
577   q=(char **) y;
578   return(LocaleCompare(*p,*q));
579 }
580
581 #if defined(__cplusplus) || defined(c_plusplus)
582 }
583 #endif
584
585 MagickExport char **GetLocaleList(const char *pattern,
586   size_t *number_messages,ExceptionInfo *exception)
587 {
588   char
589     **messages;
590
591   register const LocaleInfo
592     *p;
593
594   register ssize_t
595     i;
596
597   /*
598     Allocate locale list.
599   */
600   assert(pattern != (char *) NULL);
601   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
602   assert(number_messages != (size_t *) NULL);
603   *number_messages=0;
604   p=GetLocaleInfo_("*",exception);
605   if (p == (const LocaleInfo *) NULL)
606     return((char **) NULL);
607   messages=(char **) AcquireQuantumMemory((size_t)
608     GetNumberOfNodesInSplayTree(locale_list)+1UL,sizeof(*messages));
609   if (messages == (char **) NULL)
610     return((char **) NULL);
611   LockSemaphoreInfo(locale_semaphore);
612   p=(const LocaleInfo *) GetNextValueInSplayTree(locale_list);
613   for (i=0; p != (const LocaleInfo *) NULL; )
614   {
615     if ((p->stealth == MagickFalse) &&
616         (GlobExpression(p->tag,pattern,MagickTrue) != MagickFalse))
617       messages[i++]=ConstantString(p->tag);
618     p=(const LocaleInfo *) GetNextValueInSplayTree(locale_list);
619   }
620   UnlockSemaphoreInfo(locale_semaphore);
621   qsort((void *) messages,(size_t) i,sizeof(*messages),LocaleTagCompare);
622   messages[i]=(char *) NULL;
623   *number_messages=(size_t) i;
624   return(messages);
625 }
626 \f
627 /*
628 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
629 %                                                                             %
630 %                                                                             %
631 %                                                                             %
632 %   G e t L o c a l e M e s s a g e                                           %
633 %                                                                             %
634 %                                                                             %
635 %                                                                             %
636 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
637 %
638 %  GetLocaleMessage() returns a message in the current locale that matches the
639 %  supplied tag.
640 %
641 %  The format of the GetLocaleMessage method is:
642 %
643 %      const char *GetLocaleMessage(const char *tag)
644 %
645 %  A description of each parameter follows:
646 %
647 %    o tag: Return a message that matches this tag in the current locale.
648 %
649 */
650 MagickExport const char *GetLocaleMessage(const char *tag)
651 {
652   char
653     name[MaxTextExtent];
654
655   const LocaleInfo
656     *locale_info;
657
658   ExceptionInfo
659     *exception;
660
661   if ((tag == (const char *) NULL) || (*tag == '\0'))
662     return(tag);
663   exception=AcquireExceptionInfo();
664   (void) FormatLocaleString(name,MaxTextExtent,"%s/",tag);
665   locale_info=GetLocaleInfo_(name,exception);
666   exception=DestroyExceptionInfo(exception);
667   if (locale_info != (const LocaleInfo *) NULL)
668     return(locale_info->message);
669   return(tag);
670 }
671 \f
672 /*
673 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
674 %                                                                             %
675 %                                                                             %
676 %                                                                             %
677 %  G e t L o c a l e O p t i o n s                                            %
678 %                                                                             %
679 %                                                                             %
680 %                                                                             %
681 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
682 %
683 %  GetLocaleOptions() returns any Magick configuration messages associated
684 %  with the specified filename.
685 %
686 %  The format of the GetLocaleOptions method is:
687 %
688 %      LinkedListInfo *GetLocaleOptions(const char *filename,
689 %        ExceptionInfo *exception)
690 %
691 %  A description of each parameter follows:
692 %
693 %    o filename: the locale file tag.
694 %
695 %    o exception: return any errors or warnings in this structure.
696 %
697 */
698 MagickExport LinkedListInfo *GetLocaleOptions(const char *filename,
699   ExceptionInfo *exception)
700 {
701   char
702     path[MaxTextExtent];
703
704   const char
705     *element;
706
707   LinkedListInfo
708     *messages,
709     *paths;
710
711   StringInfo
712     *xml;
713
714   assert(filename != (const char *) NULL);
715   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
716   assert(exception != (ExceptionInfo *) NULL);
717   (void) CopyMagickString(path,filename,MaxTextExtent);
718   /*
719     Load XML from configuration files to linked-list.
720   */
721   messages=NewLinkedList(0);
722   paths=GetConfigurePaths(filename,exception);
723   if (paths != (LinkedListInfo *) NULL)
724     {
725       ResetLinkedListIterator(paths);
726       element=(const char *) GetNextValueInLinkedList(paths);
727       while (element != (const char *) NULL)
728       {
729         (void) FormatLocaleString(path,MaxTextExtent,"%s%s",element,filename);
730         (void) LogMagickEvent(LocaleEvent,GetMagickModule(),
731           "Searching for locale file: \"%s\"",path);
732         xml=ConfigureFileToStringInfo(path);
733         if (xml != (StringInfo *) NULL)
734           (void) AppendValueToLinkedList(messages,xml);
735         element=(const char *) GetNextValueInLinkedList(paths);
736       }
737       paths=DestroyLinkedList(paths,RelinquishMagickMemory);
738     }
739 #if defined(MAGICKCORE_WINDOWS_SUPPORT)
740   {
741     char
742       *blob;
743
744     blob=(char *) NTResourceToBlob(filename);
745     if (blob != (char *) NULL)
746       {
747         xml=StringToStringInfo(blob);
748         (void) AppendValueToLinkedList(messages,xml);
749         blob=DestroyString(blob);
750       }
751   }
752 #endif
753   ResetLinkedListIterator(messages);
754   return(messages);
755 }
756 \f
757 /*
758 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
759 %                                                                             %
760 %                                                                             %
761 %                                                                             %
762 %   G e t L o c a l e V a l u e                                               %
763 %                                                                             %
764 %                                                                             %
765 %                                                                             %
766 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
767 %
768 %  GetLocaleValue() returns the message associated with the locale info.
769 %
770 %  The format of the GetLocaleValue method is:
771 %
772 %      const char *GetLocaleValue(const LocaleInfo *locale_info)
773 %
774 %  A description of each parameter follows:
775 %
776 %    o locale_info:  The locale info.
777 %
778 */
779 MagickExport const char *GetLocaleValue(const LocaleInfo *locale_info)
780 {
781   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
782   assert(locale_info != (LocaleInfo *) NULL);
783   assert(locale_info->signature == MagickSignature);
784   return(locale_info->message);
785 }
786 \f
787 /*
788 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
789 %                                                                             %
790 %                                                                             %
791 %                                                                             %
792 +   I n i t i a l i z e L o c a l e L i s t                                   %
793 %                                                                             %
794 %                                                                             %
795 %                                                                             %
796 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
797 %
798 %  InitializeLocaleList() initializes the locale list.
799 %
800 %  The format of the InitializeLocaleList method is:
801 %
802 %      MagickBooleanType InitializeLocaleList(ExceptionInfo *exception)
803 %
804 %  A description of each parameter follows.
805 %
806 %    o exception: return any errors or warnings in this structure.
807 %
808 */
809 static MagickBooleanType InitializeLocaleList(ExceptionInfo *exception)
810 {
811   if ((locale_list == (SplayTreeInfo *) NULL) &&
812       (instantiate_locale == MagickFalse))
813     {
814       if (locale_semaphore == (SemaphoreInfo *) NULL)
815         AcquireSemaphoreInfo(&locale_semaphore);
816       LockSemaphoreInfo(locale_semaphore);
817       if ((locale_list == (SplayTreeInfo *) NULL) &&
818           (instantiate_locale == MagickFalse))
819         {
820           char
821             *locale;
822
823           register const char
824             *p;
825
826           locale=(char *) NULL;
827           p=setlocale(LC_CTYPE,(const char *) NULL);
828           if (p != (const char *) NULL)
829             locale=ConstantString(p);
830           if (locale == (char *) NULL)
831             locale=GetEnvironmentValue("LC_ALL");
832           if (locale == (char *) NULL)
833             locale=GetEnvironmentValue("LC_MESSAGES");
834           if (locale == (char *) NULL)
835             locale=GetEnvironmentValue("LC_CTYPE");
836           if (locale == (char *) NULL)
837             locale=GetEnvironmentValue("LANG");
838           if (locale == (char *) NULL)
839             locale=ConstantString("C");
840           (void) LoadLocaleLists(LocaleFilename,locale,exception);
841           locale=DestroyString(locale);
842           instantiate_locale=MagickTrue;
843         }
844       UnlockSemaphoreInfo(locale_semaphore);
845     }
846   return(locale_list != (SplayTreeInfo *) NULL ? MagickTrue : MagickFalse);
847 }
848 \f
849 /*
850 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
851 %                                                                             %
852 %                                                                             %
853 %                                                                             %
854 +   I n t e r p r e t L o c a l e V a l u e                                   %
855 %                                                                             %
856 %                                                                             %
857 %                                                                             %
858 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
859 %
860 %  InterpretLocaleValue() interprets the string as a floating point number in
861 %  the "C" locale and returns its value as a double. If sentinal is not a null
862 %  pointer, the method also sets the value pointed by sentinal to point to the
863 %  first character after the number.
864 %
865 %  The format of the InterpretLocaleValue method is:
866 %
867 %      double InterpretLocaleValue(const char *value,char **sentinal)
868 %
869 %  A description of each parameter follows:
870 %
871 %    o value: the string value.
872 %
873 %    o sentinal:  if sentinal is not NULL, a pointer to the character after the
874 %      last character used in the conversion is stored in the location
875 %      referenced by sentinal.
876 %
877 */
878 MagickExport double InterpretLocaleValue(const char *restrict string,
879   char **restrict sentinal)
880 {
881   double
882     value;
883
884 #if defined(MAGICKCORE_HAVE_STRTOD_L)
885   {
886     locale_t
887       locale;
888
889     locale=AcquireCLocale();
890     if (locale == (locale_t) NULL)
891       value=strtod(string,sentinal);
892     else
893       value=strtod_l(string,sentinal,locale);
894   }
895 #else
896   value=strtod(string,sentinal);
897 #endif
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
1057 static MagickBooleanType LoadLocaleList(const char *xml,const char *filename,
1058   const char *locale,const size_t depth,ExceptionInfo *exception)
1059 {
1060   char
1061     keyword[MaxTextExtent],
1062     message[MaxTextExtent],
1063     tag[MaxTextExtent],
1064     *token;
1065
1066   const char
1067     *q;
1068
1069   FatalErrorHandler
1070     fatal_handler;
1071
1072   LocaleInfo
1073     *locale_info;
1074
1075   MagickBooleanType
1076     status;
1077
1078   register char
1079     *p;
1080
1081   /*
1082     Read the locale configure file.
1083   */
1084   (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1085     "Loading locale configure file \"%s\" ...",filename);
1086   if (xml == (const char *) NULL)
1087     return(MagickFalse);
1088   if (locale_list == (SplayTreeInfo *) NULL)
1089     {
1090       locale_list=NewSplayTree(CompareSplayTreeString,(void *(*)(void *)) NULL,
1091         DestroyLocaleNode);
1092       if (locale_list == (SplayTreeInfo *) NULL)
1093         return(MagickFalse);
1094     }
1095   status=MagickTrue;
1096   locale_info=(LocaleInfo *) NULL;
1097   *tag='\0';
1098   *message='\0';
1099   *keyword='\0';
1100   fatal_handler=SetFatalErrorHandler(LocaleFatalErrorHandler);
1101   token=AcquireString(xml);
1102   for (q=(char *) xml; *q != '\0'; )
1103   {
1104     /*
1105       Interpret XML.
1106     */
1107     GetMagickToken(q,&q,token);
1108     if (*token == '\0')
1109       break;
1110     (void) CopyMagickString(keyword,token,MaxTextExtent);
1111     if (LocaleNCompare(keyword,"<!DOCTYPE",9) == 0)
1112       {
1113         /*
1114           Doctype element.
1115         */
1116         while ((LocaleNCompare(q,"]>",2) != 0) && (*q != '\0'))
1117         {
1118           GetMagickToken(q,&q,token);
1119           while (isspace((int) ((unsigned char) *q)) != 0)
1120             q++;
1121         }
1122         continue;
1123       }
1124     if (LocaleNCompare(keyword,"<!--",4) == 0)
1125       {
1126         /*
1127           Comment element.
1128         */
1129         while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0'))
1130         {
1131           GetMagickToken(q,&q,token);
1132           while (isspace((int) ((unsigned char) *q)) != 0)
1133             q++;
1134         }
1135         continue;
1136       }
1137     if (LocaleCompare(keyword,"<include") == 0)
1138       {
1139         /*
1140           Include element.
1141         */
1142         while (((*token != '/') && (*(token+1) != '>')) && (*q != '\0'))
1143         {
1144           (void) CopyMagickString(keyword,token,MaxTextExtent);
1145           GetMagickToken(q,&q,token);
1146           if (*token != '=')
1147             continue;
1148           GetMagickToken(q,&q,token);
1149           if (LocaleCompare(keyword,"locale") == 0)
1150             {
1151               if (LocaleCompare(locale,token) != 0)
1152                 break;
1153               continue;
1154             }
1155           if (LocaleCompare(keyword,"file") == 0)
1156             {
1157               if (depth > 200)
1158                 (void) ThrowMagickException(exception,GetMagickModule(),
1159                   ConfigureError,"IncludeElementNestedTooDeeply","`%s'",token);
1160               else
1161                 {
1162                   char
1163                     path[MaxTextExtent],
1164                     *xml;
1165
1166                   *path='\0';
1167                   GetPathComponent(filename,HeadPath,path);
1168                   if (*path != '\0')
1169                     (void) ConcatenateMagickString(path,DirectorySeparator,
1170                       MaxTextExtent);
1171                   if (*token == *DirectorySeparator)
1172                     (void) CopyMagickString(path,token,MaxTextExtent);
1173                   else
1174                     (void) ConcatenateMagickString(path,token,MaxTextExtent);
1175                   xml=FileToString(path,~0,exception);
1176                   if (xml != (char *) NULL)
1177                     {
1178                       status=LoadLocaleList(xml,path,locale,depth+1,exception);
1179                       xml=(char *) RelinquishMagickMemory(xml);
1180                     }
1181                 }
1182             }
1183         }
1184         continue;
1185       }
1186     if (LocaleCompare(keyword,"<locale") == 0)
1187       {
1188         /*
1189           Locale element.
1190         */
1191         while ((*token != '>') && (*q != '\0'))
1192         {
1193           (void) CopyMagickString(keyword,token,MaxTextExtent);
1194           GetMagickToken(q,&q,token);
1195           if (*token != '=')
1196             continue;
1197           GetMagickToken(q,&q,token);
1198         }
1199         continue;
1200       }
1201     if (LocaleCompare(keyword,"</locale>") == 0)
1202       {
1203         ChopLocaleComponents(tag,1);
1204         (void) ConcatenateMagickString(tag,"/",MaxTextExtent);
1205         continue;
1206       }
1207     if (LocaleCompare(keyword,"<localemap>") == 0)
1208       continue;
1209     if (LocaleCompare(keyword,"</localemap>") == 0)
1210       continue;
1211     if (LocaleCompare(keyword,"<message") == 0)
1212       {
1213         /*
1214           Message element.
1215         */
1216         while ((*token != '>') && (*q != '\0'))
1217         {
1218           (void) CopyMagickString(keyword,token,MaxTextExtent);
1219           GetMagickToken(q,&q,token);
1220           if (*token != '=')
1221             continue;
1222           GetMagickToken(q,&q,token);
1223           if (LocaleCompare(keyword,"name") == 0)
1224             {
1225               (void) ConcatenateMagickString(tag,token,MaxTextExtent);
1226               (void) ConcatenateMagickString(tag,"/",MaxTextExtent);
1227             }
1228         }
1229         for (p=(char *) q; (*q != '<') && (*q != '\0'); q++) ;
1230         while (isspace((int) ((unsigned char) *p)) != 0)
1231           p++;
1232         q--;
1233         while ((isspace((int) ((unsigned char) *q)) != 0) && (q > p))
1234           q--;
1235         (void) CopyMagickString(message,p,(size_t) (q-p+2));
1236         locale_info=(LocaleInfo *) AcquireMagickMemory(sizeof(*locale_info));
1237         if (locale_info == (LocaleInfo *) NULL)
1238           ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1239         (void) ResetMagickMemory(locale_info,0,sizeof(*locale_info));
1240         locale_info->path=ConstantString(filename);
1241         locale_info->tag=ConstantString(tag);
1242         locale_info->message=ConstantString(message);
1243         locale_info->signature=MagickSignature;
1244         status=AddValueToSplayTree(locale_list,locale_info->tag,locale_info);
1245         if (status == MagickFalse)
1246           (void) ThrowMagickException(exception,GetMagickModule(),
1247             ResourceLimitError,"MemoryAllocationFailed","`%s'",
1248             locale_info->tag);
1249         (void) ConcatenateMagickString(tag,message,MaxTextExtent);
1250         (void) ConcatenateMagickString(tag,"\n",MaxTextExtent);
1251         q++;
1252         continue;
1253       }
1254     if (LocaleCompare(keyword,"</message>") == 0)
1255       {
1256         ChopLocaleComponents(tag,2);
1257         (void) ConcatenateMagickString(tag,"/",MaxTextExtent);
1258         continue;
1259       }
1260     if (*keyword == '<')
1261       {
1262         /*
1263           Subpath element.
1264         */
1265         if (*(keyword+1) == '?')
1266           continue;
1267         if (*(keyword+1) == '/')
1268           {
1269             ChopLocaleComponents(tag,1);
1270             if (*tag != '\0')
1271               (void) ConcatenateMagickString(tag,"/",MaxTextExtent);
1272             continue;
1273           }
1274         token[strlen(token)-1]='\0';
1275         (void) CopyMagickString(token,token+1,MaxTextExtent);
1276         (void) ConcatenateMagickString(tag,token,MaxTextExtent);
1277         (void) ConcatenateMagickString(tag,"/",MaxTextExtent);
1278         continue;
1279       }
1280     GetMagickToken(q,(const char **) NULL,token);
1281     if (*token != '=')
1282       continue;
1283   }
1284   token=(char *) RelinquishMagickMemory(token);
1285   (void) SetFatalErrorHandler(fatal_handler);
1286   return(status);
1287 }
1288 \f
1289 /*
1290 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1291 %                                                                             %
1292 %                                                                             %
1293 %                                                                             %
1294 %  L o a d L o c a l e L i s t s                                              %
1295 %                                                                             %
1296 %                                                                             %
1297 %                                                                             %
1298 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1299 %
1300 %  LoadLocaleList() loads one or more locale configuration file which
1301 %  provides a mapping between locale attributes and a locale tag.
1302 %
1303 %  The format of the LoadLocaleLists method is:
1304 %
1305 %      MagickBooleanType LoadLocaleLists(const char *filename,
1306 %        ExceptionInfo *exception)
1307 %
1308 %  A description of each parameter follows:
1309 %
1310 %    o filename: the font file tag.
1311 %
1312 %    o locale: the actual locale.
1313 %
1314 %    o exception: return any errors or warnings in this structure.
1315 %
1316 */
1317 static MagickBooleanType LoadLocaleLists(const char *filename,
1318   const char *locale,ExceptionInfo *exception)
1319 {
1320 #if defined(MAGICKCORE_EMBEDDABLE_SUPPORT)
1321   return(LoadLocaleList(LocaleMap,"built-in",locale,0,exception));
1322 #else
1323   const StringInfo
1324     *option;
1325
1326   LinkedListInfo
1327     *options;
1328
1329   MagickStatusType
1330     status;
1331
1332   status=MagickFalse;
1333   options=GetLocaleOptions(filename,exception);
1334   option=(const StringInfo *) GetNextValueInLinkedList(options);
1335   while (option != (const StringInfo *) NULL)
1336   {
1337     status|=LoadLocaleList((const char *) GetStringInfoDatum(option),
1338       GetStringInfoPath(option),locale,0,exception);
1339     option=(const StringInfo *) GetNextValueInLinkedList(options);
1340   }
1341   options=DestroyLocaleOptions(options);
1342   if ((locale_list == (SplayTreeInfo *) NULL) ||
1343       (GetNumberOfNodesInSplayTree(locale_list) == 0))
1344     {
1345       options=GetLocaleOptions("english.xml",exception);
1346       option=(const StringInfo *) GetNextValueInLinkedList(options);
1347       while (option != (const StringInfo *) NULL)
1348       {
1349         status|=LoadLocaleList((const char *) GetStringInfoDatum(option),
1350           GetStringInfoPath(option),locale,0,exception);
1351         option=(const StringInfo *) GetNextValueInLinkedList(options);
1352       }
1353       options=DestroyLocaleOptions(options);
1354     }
1355   if ((locale_list == (SplayTreeInfo *) NULL) ||
1356       (GetNumberOfNodesInSplayTree(locale_list) == 0))
1357     status|=LoadLocaleList(LocaleMap,"built-in",locale,0,exception);
1358   return(status != 0 ? MagickTrue : MagickFalse);
1359 #endif
1360 }
1361 \f
1362 /*
1363 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1364 %                                                                             %
1365 %                                                                             %
1366 %                                                                             %
1367 +   L o c a l e C o m p o n e n t G e n e s i s                               %
1368 %                                                                             %
1369 %                                                                             %
1370 %                                                                             %
1371 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1372 %
1373 %  LocaleComponentGenesis() instantiates the locale component.
1374 %
1375 %  The format of the LocaleComponentGenesis method is:
1376 %
1377 %      MagickBooleanType LocaleComponentGenesis(void)
1378 %
1379 */
1380 MagickPrivate MagickBooleanType LocaleComponentGenesis(void)
1381 {
1382   AcquireSemaphoreInfo(&locale_semaphore);
1383   return(MagickTrue);
1384 }
1385 \f
1386 /*
1387 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1388 %                                                                             %
1389 %                                                                             %
1390 %                                                                             %
1391 +   L o c a l e C o m p o n e n t T e r m i n u s                             %
1392 %                                                                             %
1393 %                                                                             %
1394 %                                                                             %
1395 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1396 %
1397 %  LocaleComponentTerminus() destroys the locale component.
1398 %
1399 %  The format of the LocaleComponentTerminus method is:
1400 %
1401 %      LocaleComponentTerminus(void)
1402 %
1403 */
1404 MagickPrivate void LocaleComponentTerminus(void)
1405 {
1406   if (locale_semaphore == (SemaphoreInfo *) NULL)
1407     AcquireSemaphoreInfo(&locale_semaphore);
1408   LockSemaphoreInfo(locale_semaphore);
1409 #if defined(MAGICKCORE_HAVE_STRTOD_L)
1410   DestroyCLocale();
1411 #endif
1412   instantiate_locale=MagickFalse;
1413   UnlockSemaphoreInfo(locale_semaphore);
1414   DestroySemaphoreInfo(&locale_semaphore);
1415 }