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