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