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