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