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