locale.c revision e1c94d9d25db6b0dd7a5028ffee31d1057855d73
1f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org/*
2f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org%                                                                             %
4f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org%                                                                             %
5f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org%                                                                             %
6f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org%                  L       OOO    CCCC   AAA   L      EEEEE                   %
7f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org%                  L      O   O  C      A   A  L      E                       %
8f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org%                  L      O   O  C      AAAAA  L      EEE                     %
9f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org%                  L      O   O  C      A   A  L      E                       %
10f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org%                  LLLLL   OOO    CCCC  A   A  LLLLL  EEEEE                   %
11f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org%                                                                             %
12f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org%                                                                             %
13f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org%                      MagickCore Image Locale Methods                        %
14f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org%                                                                             %
15f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org%                              Software Design                                %
16f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org%                                   Cristy                                    %
17f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org%                                 July 2003                                   %
18f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org%                                                                             %
19f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org%                                                                             %
20f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org%  Copyright 1999-2015 ImageMagick Studio LLC, a non-profit organization      %
21f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org%  dedicated to making software imaging solutions freely available.           %
22f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org%                                                                             %
23f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org%  You may not use this file except in compliance with the License.  You may  %
24f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org%  obtain a copy of the License at                                            %
25f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org%                                                                             %
2667186fe00cc68cbe03aa66d17fb4962458ca96d2kwiberg@webrtc.org%    http://www.imagemagick.org/script/license.php                            %
2767186fe00cc68cbe03aa66d17fb4962458ca96d2kwiberg@webrtc.org%                                                                             %
2867186fe00cc68cbe03aa66d17fb4962458ca96d2kwiberg@webrtc.org%  Unless required by applicable law or agreed to in writing, software        %
2967186fe00cc68cbe03aa66d17fb4962458ca96d2kwiberg@webrtc.org%  distributed under the License is distributed on an "AS IS" BASIS,          %
30f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31aaf61e460b7f24dca89c572b5b6c9af865775bb5Thiago Farina%  See the License for the specific language governing permissions and        %
32f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org%  limitations under the License.                                             %
33f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org%                                                                             %
34f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org%
36f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org%
37*/
38
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
65/*
66  Define declarations.
67*/
68#if defined(MAGICKCORE_HAVE_NEWLOCALE) || defined(MAGICKCORE_WINDOWS_SUPPORT)
69#  define MAGICKCORE_LOCALE_SUPPORT
70#endif
71#define LocaleFilename  "locale.xml"
72#define MaxRecursionDepth  200
73
74/*
75  Static declarations.
76*/
77static const char
78  *LocaleMap =
79    "<?xml version=\"1.0\"?>"
80    "<localemap>"
81    "  <locale name=\"C\">"
82    "    <Exception>"
83    "     <Message name=\"\">"
84    "     </Message>"
85    "    </Exception>"
86    "  </locale>"
87    "</localemap>";
88
89static SemaphoreInfo
90  *locale_semaphore = (SemaphoreInfo *) NULL;
91
92static SplayTreeInfo
93  *locale_cache = (SplayTreeInfo *) NULL;
94
95#if defined(MAGICKCORE_LOCALE_SUPPORT)
96static volatile locale_t
97  c_locale = (locale_t) NULL;
98#endif
99
100/*
101  Forward declarations.
102*/
103static MagickBooleanType
104  IsLocaleTreeInstantiated(ExceptionInfo *),
105  LoadLocaleCache(SplayTreeInfo *,const char *,const char *,const char *,
106    const size_t,ExceptionInfo *);
107
108#if defined(MAGICKCORE_LOCALE_SUPPORT)
109/*
110%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
111%                                                                             %
112%                                                                             %
113%                                                                             %
114+   A c q u i r e C L o c a l e                                               %
115%                                                                             %
116%                                                                             %
117%                                                                             %
118%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
119%
120%  AcquireCLocale() allocates the C locale object, or (locale_t) 0 with
121%  errno set if it cannot be acquired.
122%
123%  The format of the AcquireCLocale method is:
124%
125%      locale_t AcquireCLocale(void)
126%
127*/
128static locale_t AcquireCLocale(void)
129{
130#if defined(MAGICKCORE_HAVE_NEWLOCALE)
131  if (c_locale == (locale_t) NULL)
132    c_locale=newlocale(LC_ALL_MASK,"C",(locale_t) 0);
133#elif defined(MAGICKCORE_WINDOWS_SUPPORT)
134  if (c_locale == (locale_t) NULL)
135    c_locale=_create_locale(LC_ALL,"C");
136#endif
137  return(c_locale);
138}
139#endif
140
141/*
142%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
143%                                                                             %
144%                                                                             %
145%                                                                             %
146%  A c q u i r e L o c a l e S p l a y T r e e                                %
147%                                                                             %
148%                                                                             %
149%                                                                             %
150%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
151%
152%  AcquireLocaleSplayTree() caches one or more locale configurations which
153%  provides a mapping between locale attributes and a locale tag.
154%
155%  The format of the AcquireLocaleSplayTree method is:
156%
157%      SplayTreeInfo *AcquireLocaleSplayTree(const char *filename,
158%        ExceptionInfo *exception)
159%
160%  A description of each parameter follows:
161%
162%    o filename: the font file tag.
163%
164%    o locale: the actual locale.
165%
166%    o exception: return any errors or warnings in this structure.
167%
168*/
169
170static void *DestroyLocaleNode(void *locale_info)
171{
172  register LocaleInfo
173    *p;
174
175  p=(LocaleInfo *) locale_info;
176  if (p->path != (char *) NULL)
177    p->path=DestroyString(p->path);
178  if (p->tag != (char *) NULL)
179    p->tag=DestroyString(p->tag);
180  if (p->message != (char *) NULL)
181    p->message=DestroyString(p->message);
182  return(RelinquishMagickMemory(p));
183}
184
185static SplayTreeInfo *AcquireLocaleSplayTree(const char *filename,
186  const char *locale,ExceptionInfo *exception)
187{
188  MagickStatusType
189    status;
190
191  SplayTreeInfo
192    *locale_cache;
193
194  locale_cache=NewSplayTree(CompareSplayTreeString,(void *(*)(void *)) NULL,
195    DestroyLocaleNode);
196  if (locale_cache == (SplayTreeInfo *) NULL)
197    ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
198  status=MagickTrue;
199#if !defined(MAGICKCORE_ZERO_CONFIGURATION_SUPPORT)
200  {
201    const StringInfo
202      *option;
203
204    LinkedListInfo
205      *options;
206
207    options=GetLocaleOptions(filename,exception);
208    option=(const StringInfo *) GetNextValueInLinkedList(options);
209    while (option != (const StringInfo *) NULL)
210    {
211      status&=LoadLocaleCache(locale_cache,(const char *)
212        GetStringInfoDatum(option),GetStringInfoPath(option),locale,0,
213        exception);
214      option=(const StringInfo *) GetNextValueInLinkedList(options);
215    }
216    options=DestroyLocaleOptions(options);
217    if (GetNumberOfNodesInSplayTree(locale_cache) == 0)
218      {
219        options=GetLocaleOptions("english.xml",exception);
220        option=(const StringInfo *) GetNextValueInLinkedList(options);
221        while (option != (const StringInfo *) NULL)
222        {
223          status&=LoadLocaleCache(locale_cache,(const char *)
224            GetStringInfoDatum(option),GetStringInfoPath(option),locale,0,
225            exception);
226          option=(const StringInfo *) GetNextValueInLinkedList(options);
227        }
228        options=DestroyLocaleOptions(options);
229      }
230  }
231#endif
232  if (GetNumberOfNodesInSplayTree(locale_cache) == 0)
233    status&=LoadLocaleCache(locale_cache,LocaleMap,"built-in",locale,0,
234      exception);
235  return(locale_cache);
236}
237
238#if defined(MAGICKCORE_LOCALE_SUPPORT)
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*/
258static 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
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
295static void *DestroyOptions(void *message)
296{
297  return(DestroyStringInfo((StringInfo *) message));
298}
299
300MagickExport LinkedListInfo *DestroyLocaleOptions(LinkedListInfo *messages)
301{
302  assert(messages != (LinkedListInfo *) NULL);
303  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
304  return(DestroyLinkedList(messages,DestroyOptions));
305}
306
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
334MagickPrivate ssize_t FormatLocaleFileList(FILE *file,
335  const char *restrict format,va_list operands)
336{
337  ssize_t
338    n;
339
340#if defined(MAGICKCORE_LOCALE_SUPPORT) && 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_LOCALE_SUPPORT) && 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
379MagickExport 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
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
425MagickPrivate 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_LOCALE_SUPPORT) && 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_LOCALE_SUPPORT) && 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
474MagickExport 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
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*/
515MagickExport 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
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)
568extern "C" {
569#endif
570
571static 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
588MagickExport 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
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)
665extern "C" {
666#endif
667
668static 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
683MagickExport char **GetLocaleList(const char *pattern,size_t *number_messages,
684  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
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*/
748MagickExport const char *GetLocaleMessage(const char *tag)
749{
750  char
751    name[MagickLocaleExtent];
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,MagickLocaleExtent,"%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
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*/
796MagickExport LinkedListInfo *GetLocaleOptions(const char *filename,
797  ExceptionInfo *exception)
798{
799  char
800    path[MagickPathExtent];
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,MagickPathExtent);
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,MagickPathExtent,"%s%s",element,
828          filename);
829        (void) LogMagickEvent(LocaleEvent,GetMagickModule(),
830          "Searching for locale file: \"%s\"",path);
831        xml=ConfigureFileToStringInfo(path);
832        if (xml != (StringInfo *) NULL)
833          (void) AppendValueToLinkedList(messages,xml);
834        element=(const char *) GetNextValueInLinkedList(paths);
835      }
836      paths=DestroyLinkedList(paths,RelinquishMagickMemory);
837    }
838#if defined(MAGICKCORE_WINDOWS_SUPPORT)
839  {
840    char
841      *blob;
842
843    blob=(char *) NTResourceToBlob(filename);
844    if (blob != (char *) NULL)
845      {
846        xml=AcquireStringInfo(0);
847        SetStringInfoLength(xml,strlen(blob)+1);
848        SetStringInfoDatum(xml,(const unsigned char *) blob);
849        blob=(char *) RelinquishMagickMemory(blob);
850        SetStringInfoPath(xml,filename);
851        (void) AppendValueToLinkedList(messages,xml);
852      }
853  }
854#endif
855  ResetLinkedListIterator(messages);
856  return(messages);
857}
858
859/*
860%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
861%                                                                             %
862%                                                                             %
863%                                                                             %
864%   G e t L o c a l e V a l u e                                               %
865%                                                                             %
866%                                                                             %
867%                                                                             %
868%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
869%
870%  GetLocaleValue() returns the message associated with the locale info.
871%
872%  The format of the GetLocaleValue method is:
873%
874%      const char *GetLocaleValue(const LocaleInfo *locale_info)
875%
876%  A description of each parameter follows:
877%
878%    o locale_info:  The locale info.
879%
880*/
881MagickExport const char *GetLocaleValue(const LocaleInfo *locale_info)
882{
883  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
884  assert(locale_info != (LocaleInfo *) NULL);
885  assert(locale_info->signature == MagickCoreSignature);
886  return(locale_info->message);
887}
888
889/*
890%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
891%                                                                             %
892%                                                                             %
893%                                                                             %
894+   I s L o c a l e T r e e I n s t a n t i a t e d                           %
895%                                                                             %
896%                                                                             %
897%                                                                             %
898%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
899%
900%  IsLocaleTreeInstantiated() determines if the locale tree is instantiated.
901%  If not, it instantiates the tree and returns it.
902%
903%  The format of the IsLocaleInstantiated method is:
904%
905%      MagickBooleanType IsLocaleTreeInstantiated(ExceptionInfo *exception)
906%
907%  A description of each parameter follows.
908%
909%    o exception: return any errors or warnings in this structure.
910%
911*/
912static MagickBooleanType IsLocaleTreeInstantiated(ExceptionInfo *exception)
913{
914  if (locale_cache == (SplayTreeInfo *) NULL)
915    {
916      if (locale_semaphore == (SemaphoreInfo *) NULL)
917        ActivateSemaphoreInfo(&locale_semaphore);
918      LockSemaphoreInfo(locale_semaphore);
919      if (locale_cache == (SplayTreeInfo *) NULL)
920        {
921          char
922            *locale;
923
924          register const char
925            *p;
926
927          locale=(char *) NULL;
928          p=setlocale(LC_CTYPE,(const char *) NULL);
929          if (p != (const char *) NULL)
930            locale=ConstantString(p);
931          if (locale == (char *) NULL)
932            locale=GetEnvironmentValue("LC_ALL");
933          if (locale == (char *) NULL)
934            locale=GetEnvironmentValue("LC_MESSAGES");
935          if (locale == (char *) NULL)
936            locale=GetEnvironmentValue("LC_CTYPE");
937          if (locale == (char *) NULL)
938            locale=GetEnvironmentValue("LANG");
939          if (locale == (char *) NULL)
940            locale=ConstantString("C");
941          locale_cache=AcquireLocaleSplayTree(LocaleFilename,locale,exception);
942          locale=DestroyString(locale);
943        }
944      UnlockSemaphoreInfo(locale_semaphore);
945    }
946  return(locale_cache != (SplayTreeInfo *) NULL ? MagickTrue : MagickFalse);
947}
948
949/*
950%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
951%                                                                             %
952%                                                                             %
953%                                                                             %
954+   I n t e r p r e t L o c a l e V a l u e                                   %
955%                                                                             %
956%                                                                             %
957%                                                                             %
958%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
959%
960%  InterpretLocaleValue() interprets the string as a floating point number in
961%  the "C" locale and returns its value as a double. If sentinal is not a null
962%  pointer, the method also sets the value pointed by sentinal to point to the
963%  first character after the number.
964%
965%  The format of the InterpretLocaleValue method is:
966%
967%      double InterpretLocaleValue(const char *value,char **sentinal)
968%
969%  A description of each parameter follows:
970%
971%    o value: the string value.
972%
973%    o sentinal:  if sentinal is not NULL, a pointer to the character after the
974%      last character used in the conversion is stored in the location
975%      referenced by sentinal.
976%
977*/
978MagickExport double InterpretLocaleValue(const char *restrict string,
979  char **restrict sentinal)
980{
981  char
982    *q;
983
984  double
985    value;
986
987  if ((*string == '0') && ((string[1] | 0x20)=='x'))
988    value=(double) strtoul(string,&q,16);
989  else
990    {
991#if defined(MAGICKCORE_LOCALE_SUPPORT) && defined(MAGICKCORE_HAVE_STRTOD_L)
992      locale_t
993        locale;
994
995      locale=AcquireCLocale();
996      if (locale == (locale_t) NULL)
997        value=strtod(string,&q);
998      else
999        value=strtod_l(string,&q,locale);
1000#else
1001      value=strtod(string,&q);
1002#endif
1003    }
1004  if (sentinal != (char **) NULL)
1005    *sentinal=q;
1006  return(value);
1007}
1008
1009/*
1010%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1011%                                                                             %
1012%                                                                             %
1013%                                                                             %
1014%  L i s t L o c a l e I n f o                                                %
1015%                                                                             %
1016%                                                                             %
1017%                                                                             %
1018%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1019%
1020%  ListLocaleInfo() lists the locale info to a file.
1021%
1022%  The format of the ListLocaleInfo method is:
1023%
1024%      MagickBooleanType ListLocaleInfo(FILE *file,ExceptionInfo *exception)
1025%
1026%  A description of each parameter follows.
1027%
1028%    o file:  An pointer to a FILE.
1029%
1030%    o exception: return any errors or warnings in this structure.
1031%
1032*/
1033MagickExport MagickBooleanType ListLocaleInfo(FILE *file,
1034  ExceptionInfo *exception)
1035{
1036  const char
1037    *path;
1038
1039  const LocaleInfo
1040    **locale_info;
1041
1042  register ssize_t
1043    i;
1044
1045  size_t
1046    number_messages;
1047
1048  if (file == (const FILE *) NULL)
1049    file=stdout;
1050  number_messages=0;
1051  locale_info=GetLocaleInfoList("*",&number_messages,exception);
1052  if (locale_info == (const LocaleInfo **) NULL)
1053    return(MagickFalse);
1054  path=(const char *) NULL;
1055  for (i=0; i < (ssize_t) number_messages; i++)
1056  {
1057    if (locale_info[i]->stealth != MagickFalse)
1058      continue;
1059    if ((path == (const char *) NULL) ||
1060        (LocaleCompare(path,locale_info[i]->path) != 0))
1061      {
1062        if (locale_info[i]->path != (char *) NULL)
1063          (void) FormatLocaleFile(file,"\nPath: %s\n\n",locale_info[i]->path);
1064        (void) FormatLocaleFile(file,"Tag/Message\n");
1065        (void) FormatLocaleFile(file,
1066          "-------------------------------------------------"
1067          "------------------------------\n");
1068      }
1069    path=locale_info[i]->path;
1070    (void) FormatLocaleFile(file,"%s\n",locale_info[i]->tag);
1071    if (locale_info[i]->message != (char *) NULL)
1072      (void) FormatLocaleFile(file,"  %s",locale_info[i]->message);
1073    (void) FormatLocaleFile(file,"\n");
1074  }
1075  (void) fflush(file);
1076  locale_info=(const LocaleInfo **)
1077    RelinquishMagickMemory((void *) locale_info);
1078  return(MagickTrue);
1079}
1080
1081/*
1082%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1083%                                                                             %
1084%                                                                             %
1085%                                                                             %
1086+   L o a d L o c a l e L i s t                                               %
1087%                                                                             %
1088%                                                                             %
1089%                                                                             %
1090%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1091%
1092%  LoadLocaleCache() loads the locale configurations which provides a mapping
1093%  between locale attributes and a locale name.
1094%
1095%  The format of the LoadLocaleCache method is:
1096%
1097%      MagickBooleanType LoadLocaleCache(SplayTreeInfo *locale_cache,
1098%        const char *xml,const char *filename,const size_t depth,
1099%        ExceptionInfo *exception)
1100%
1101%  A description of each parameter follows:
1102%
1103%    o xml:  The locale list in XML format.
1104%
1105%    o filename:  The locale list filename.
1106%
1107%    o depth: depth of <include /> statements.
1108%
1109%    o exception: return any errors or warnings in this structure.
1110%
1111*/
1112
1113static void ChopLocaleComponents(char *path,const size_t components)
1114{
1115  register char
1116    *p;
1117
1118  ssize_t
1119    count;
1120
1121  if (*path == '\0')
1122    return;
1123  p=path+strlen(path)-1;
1124  if (*p == '/')
1125    *p='\0';
1126  for (count=0; (count < (ssize_t) components) && (p > path); p--)
1127    if (*p == '/')
1128      {
1129        *p='\0';
1130        count++;
1131      }
1132  if (count < (ssize_t) components)
1133    *path='\0';
1134}
1135
1136static void LocaleFatalErrorHandler(
1137  const ExceptionType magick_unused(severity),
1138  const char *reason,const char *description)
1139{
1140  if (reason == (char *) NULL)
1141    return;
1142  (void) FormatLocaleFile(stderr,"%s: %s",GetClientName(),reason);
1143  if (description != (char *) NULL)
1144    (void) FormatLocaleFile(stderr," (%s)",description);
1145  (void) FormatLocaleFile(stderr,".\n");
1146  (void) fflush(stderr);
1147  exit(1);
1148}
1149
1150static MagickBooleanType LoadLocaleCache(SplayTreeInfo *locale_cache,
1151  const char *xml,const char *filename,const char *locale,const size_t depth,
1152  ExceptionInfo *exception)
1153{
1154  char
1155    keyword[MagickLocaleExtent],
1156    message[MagickLocaleExtent],
1157    tag[MagickLocaleExtent],
1158    *token;
1159
1160  const char
1161    *q;
1162
1163  FatalErrorHandler
1164    fatal_handler;
1165
1166  LocaleInfo
1167    *locale_info;
1168
1169  MagickStatusType
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  status=MagickTrue;
1183  locale_info=(LocaleInfo *) NULL;
1184  *tag='\0';
1185  *message='\0';
1186  *keyword='\0';
1187  fatal_handler=SetFatalErrorHandler(LocaleFatalErrorHandler);
1188  token=AcquireString(xml);
1189  for (q=(char *) xml; *q != '\0'; )
1190  {
1191    /*
1192      Interpret XML.
1193    */
1194    GetMagickToken(q,&q,token);
1195    if (*token == '\0')
1196      break;
1197    (void) CopyMagickString(keyword,token,MagickLocaleExtent);
1198    if (LocaleNCompare(keyword,"<!DOCTYPE",9) == 0)
1199      {
1200        /*
1201          Doctype element.
1202        */
1203        while ((LocaleNCompare(q,"]>",2) != 0) && (*q != '\0'))
1204        {
1205          GetMagickToken(q,&q,token);
1206          while (isspace((int) ((unsigned char) *q)) != 0)
1207            q++;
1208        }
1209        continue;
1210      }
1211    if (LocaleNCompare(keyword,"<!--",4) == 0)
1212      {
1213        /*
1214          Comment element.
1215        */
1216        while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0'))
1217        {
1218          GetMagickToken(q,&q,token);
1219          while (isspace((int) ((unsigned char) *q)) != 0)
1220            q++;
1221        }
1222        continue;
1223      }
1224    if (LocaleCompare(keyword,"<include") == 0)
1225      {
1226        /*
1227          Include element.
1228        */
1229        while (((*token != '/') && (*(token+1) != '>')) && (*q != '\0'))
1230        {
1231          (void) CopyMagickString(keyword,token,MagickLocaleExtent);
1232          GetMagickToken(q,&q,token);
1233          if (*token != '=')
1234            continue;
1235          GetMagickToken(q,&q,token);
1236          if (LocaleCompare(keyword,"locale") == 0)
1237            {
1238              if (LocaleCompare(locale,token) != 0)
1239                break;
1240              continue;
1241            }
1242          if (LocaleCompare(keyword,"file") == 0)
1243            {
1244              if (depth > 200)
1245                (void) ThrowMagickException(exception,GetMagickModule(),
1246                  ConfigureError,"IncludeElementNestedTooDeeply","`%s'",token);
1247              else
1248                {
1249                  char
1250                    path[MagickPathExtent],
1251                    *xml;
1252
1253                  *path='\0';
1254                  GetPathComponent(filename,HeadPath,path);
1255                  if (*path != '\0')
1256                    (void) ConcatenateMagickString(path,DirectorySeparator,
1257                      MagickPathExtent);
1258                  if (*token == *DirectorySeparator)
1259                    (void) CopyMagickString(path,token,MagickPathExtent);
1260                  else
1261                    (void) ConcatenateMagickString(path,token,MagickPathExtent);
1262                  xml=FileToXML(path,~0UL);
1263                  if (xml != (char *) NULL)
1264                    {
1265                      status&=LoadLocaleCache(locale_cache,xml,path,locale,
1266                        depth+1,exception);
1267                      xml=(char *) RelinquishMagickMemory(xml);
1268                    }
1269                }
1270            }
1271        }
1272        continue;
1273      }
1274    if (LocaleCompare(keyword,"<locale") == 0)
1275      {
1276        /*
1277          Locale element.
1278        */
1279        while ((*token != '>') && (*q != '\0'))
1280        {
1281          (void) CopyMagickString(keyword,token,MagickLocaleExtent);
1282          GetMagickToken(q,&q,token);
1283          if (*token != '=')
1284            continue;
1285          GetMagickToken(q,&q,token);
1286        }
1287        continue;
1288      }
1289    if (LocaleCompare(keyword,"</locale>") == 0)
1290      {
1291        ChopLocaleComponents(tag,1);
1292        (void) ConcatenateMagickString(tag,"/",MagickLocaleExtent);
1293        continue;
1294      }
1295    if (LocaleCompare(keyword,"<localemap>") == 0)
1296      continue;
1297    if (LocaleCompare(keyword,"</localemap>") == 0)
1298      continue;
1299    if (LocaleCompare(keyword,"<message") == 0)
1300      {
1301        /*
1302          Message element.
1303        */
1304        while ((*token != '>') && (*q != '\0'))
1305        {
1306          (void) CopyMagickString(keyword,token,MagickLocaleExtent);
1307          GetMagickToken(q,&q,token);
1308          if (*token != '=')
1309            continue;
1310          GetMagickToken(q,&q,token);
1311          if (LocaleCompare(keyword,"name") == 0)
1312            {
1313              (void) ConcatenateMagickString(tag,token,MagickLocaleExtent);
1314              (void) ConcatenateMagickString(tag,"/",MagickLocaleExtent);
1315            }
1316        }
1317        for (p=(char *) q; (*q != '<') && (*q != '\0'); q++) ;
1318        while (isspace((int) ((unsigned char) *p)) != 0)
1319          p++;
1320        q--;
1321        while ((isspace((int) ((unsigned char) *q)) != 0) && (q > p))
1322          q--;
1323        (void) CopyMagickString(message,p,MagickMin((size_t) (q-p+2),
1324          MagickLocaleExtent));
1325        locale_info=(LocaleInfo *) AcquireMagickMemory(sizeof(*locale_info));
1326        if (locale_info == (LocaleInfo *) NULL)
1327          ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1328        (void) ResetMagickMemory(locale_info,0,sizeof(*locale_info));
1329        locale_info->path=ConstantString(filename);
1330        locale_info->tag=ConstantString(tag);
1331        locale_info->message=ConstantString(message);
1332        locale_info->signature=MagickCoreSignature;
1333        status=AddValueToSplayTree(locale_cache,locale_info->tag,locale_info);
1334        if (status == MagickFalse)
1335          (void) ThrowMagickException(exception,GetMagickModule(),
1336            ResourceLimitError,"MemoryAllocationFailed","`%s'",
1337            locale_info->tag);
1338        (void) ConcatenateMagickString(tag,message,MagickLocaleExtent);
1339        (void) ConcatenateMagickString(tag,"\n",MagickLocaleExtent);
1340        q++;
1341        continue;
1342      }
1343    if (LocaleCompare(keyword,"</message>") == 0)
1344      {
1345        ChopLocaleComponents(tag,2);
1346        (void) ConcatenateMagickString(tag,"/",MagickLocaleExtent);
1347        continue;
1348      }
1349    if (*keyword == '<')
1350      {
1351        /*
1352          Subpath element.
1353        */
1354        if (*(keyword+1) == '?')
1355          continue;
1356        if (*(keyword+1) == '/')
1357          {
1358            ChopLocaleComponents(tag,1);
1359            if (*tag != '\0')
1360              (void) ConcatenateMagickString(tag,"/",MagickLocaleExtent);
1361            continue;
1362          }
1363        token[strlen(token)-1]='\0';
1364        (void) CopyMagickString(token,token+1,MagickLocaleExtent);
1365        (void) ConcatenateMagickString(tag,token,MagickLocaleExtent);
1366        (void) ConcatenateMagickString(tag,"/",MagickLocaleExtent);
1367        continue;
1368      }
1369    GetMagickToken(q,(const char **) NULL,token);
1370    if (*token != '=')
1371      continue;
1372  }
1373  token=(char *) RelinquishMagickMemory(token);
1374  (void) SetFatalErrorHandler(fatal_handler);
1375  return(status != 0 ? MagickTrue : MagickFalse);
1376}
1377
1378/*
1379%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1380%                                                                             %
1381%                                                                             %
1382%                                                                             %
1383+   L o c a l e C o m p o n e n t G e n e s i s                               %
1384%                                                                             %
1385%                                                                             %
1386%                                                                             %
1387%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1388%
1389%  LocaleComponentGenesis() instantiates the locale component.
1390%
1391%  The format of the LocaleComponentGenesis method is:
1392%
1393%      MagickBooleanType LocaleComponentGenesis(void)
1394%
1395*/
1396MagickPrivate MagickBooleanType LocaleComponentGenesis(void)
1397{
1398  if (locale_semaphore == (SemaphoreInfo *) NULL)
1399    locale_semaphore=AcquireSemaphoreInfo();
1400  return(MagickTrue);
1401}
1402
1403/*
1404%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1405%                                                                             %
1406%                                                                             %
1407%                                                                             %
1408+   L o c a l e C o m p o n e n t T e r m i n u s                             %
1409%                                                                             %
1410%                                                                             %
1411%                                                                             %
1412%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1413%
1414%  LocaleComponentTerminus() destroys the locale component.
1415%
1416%  The format of the LocaleComponentTerminus method is:
1417%
1418%      LocaleComponentTerminus(void)
1419%
1420*/
1421MagickPrivate void LocaleComponentTerminus(void)
1422{
1423  if (locale_semaphore == (SemaphoreInfo *) NULL)
1424    ActivateSemaphoreInfo(&locale_semaphore);
1425  LockSemaphoreInfo(locale_semaphore);
1426  if (locale_cache != (SplayTreeInfo *) NULL)
1427    locale_cache=DestroySplayTree(locale_cache);
1428#if defined(MAGICKCORE_LOCALE_SUPPORT)
1429  DestroyCLocale();
1430#endif
1431  UnlockSemaphoreInfo(locale_semaphore);
1432  RelinquishSemaphoreInfo(&locale_semaphore);
1433}
1434