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