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-2016 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
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/image-private.h"
49#include "MagickCore/linked-list.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) && !defined(__MINGW32__)
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    *cache;
193
194  cache=NewSplayTree(CompareSplayTreeString,(void *(*)(void *)) NULL,
195    DestroyLocaleNode);
196  if (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(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(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(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(cache) == 0)
233    status&=LoadLocaleCache(cache,LocaleMap,"built-in",locale,0,
234      exception);
235  return(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) && !defined(__MINGW32__)
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 *magick_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,
380  const char *magick_restrict format,...)
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 *magick_restrict string,
426  const size_t length,const char *magick_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 *magick_restrict string,
475  const size_t length,const char *magick_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 *magick_restrict string,
979  char **magick_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 C a c h e                                             %
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 *cache,const char *xml,
1098%        const char *filename,const size_t depth,ExceptionInfo *exception)
1099%
1100%  A description of each parameter follows:
1101%
1102%    o xml:  The locale list in XML format.
1103%
1104%    o filename:  The locale list filename.
1105%
1106%    o depth: depth of <include /> statements.
1107%
1108%    o exception: return any errors or warnings in this structure.
1109%
1110*/
1111
1112static void ChopLocaleComponents(char *path,const size_t components)
1113{
1114  register char
1115    *p;
1116
1117  ssize_t
1118    count;
1119
1120  if (*path == '\0')
1121    return;
1122  p=path+strlen(path)-1;
1123  if (*p == '/')
1124    *p='\0';
1125  for (count=0; (count < (ssize_t) components) && (p > path); p--)
1126    if (*p == '/')
1127      {
1128        *p='\0';
1129        count++;
1130      }
1131  if (count < (ssize_t) components)
1132    *path='\0';
1133}
1134
1135static void LocaleFatalErrorHandler(
1136  const ExceptionType magick_unused(severity),
1137  const char *reason,const char *description)
1138{
1139  magick_unreferenced(severity);
1140
1141  if (reason == (char *) NULL)
1142    return;
1143  (void) FormatLocaleFile(stderr,"%s: %s",GetClientName(),reason);
1144  if (description != (char *) NULL)
1145    (void) FormatLocaleFile(stderr," (%s)",description);
1146  (void) FormatLocaleFile(stderr,".\n");
1147  (void) fflush(stderr);
1148  exit(1);
1149}
1150
1151static MagickBooleanType LoadLocaleCache(SplayTreeInfo *cache,const char *xml,
1152  const char *filename,const char *locale,const size_t depth,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  size_t
1176    extent;
1177
1178  /*
1179    Read the locale configure file.
1180  */
1181  (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1182    "Loading locale configure file \"%s\" ...",filename);
1183  if (xml == (const char *) NULL)
1184    return(MagickFalse);
1185  status=MagickTrue;
1186  locale_info=(LocaleInfo *) NULL;
1187  *tag='\0';
1188  *message='\0';
1189  *keyword='\0';
1190  fatal_handler=SetFatalErrorHandler(LocaleFatalErrorHandler);
1191  token=AcquireString(xml);
1192  extent=strlen(token)+MagickPathExtent;
1193  for (q=(char *) xml; *q != '\0'; )
1194  {
1195    /*
1196      Interpret XML.
1197    */
1198    GetNextToken(q,&q,extent,token);
1199    if (*token == '\0')
1200      break;
1201    (void) CopyMagickString(keyword,token,MagickLocaleExtent);
1202    if (LocaleNCompare(keyword,"<!DOCTYPE",9) == 0)
1203      {
1204        /*
1205          Doctype element.
1206        */
1207        while ((LocaleNCompare(q,"]>",2) != 0) && (*q != '\0'))
1208        {
1209          GetNextToken(q,&q,extent,token);
1210          while (isspace((int) ((unsigned char) *q)) != 0)
1211            q++;
1212        }
1213        continue;
1214      }
1215    if (LocaleNCompare(keyword,"<!--",4) == 0)
1216      {
1217        /*
1218          Comment element.
1219        */
1220        while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0'))
1221        {
1222          GetNextToken(q,&q,extent,token);
1223          while (isspace((int) ((unsigned char) *q)) != 0)
1224            q++;
1225        }
1226        continue;
1227      }
1228    if (LocaleCompare(keyword,"<include") == 0)
1229      {
1230        /*
1231          Include element.
1232        */
1233        while (((*token != '/') && (*(token+1) != '>')) && (*q != '\0'))
1234        {
1235          (void) CopyMagickString(keyword,token,MagickLocaleExtent);
1236          GetNextToken(q,&q,extent,token);
1237          if (*token != '=')
1238            continue;
1239          GetNextToken(q,&q,extent,token);
1240          if (LocaleCompare(keyword,"locale") == 0)
1241            {
1242              if (LocaleCompare(locale,token) != 0)
1243                break;
1244              continue;
1245            }
1246          if (LocaleCompare(keyword,"file") == 0)
1247            {
1248              if (depth > 200)
1249                (void) ThrowMagickException(exception,GetMagickModule(),
1250                  ConfigureError,"IncludeElementNestedTooDeeply","`%s'",token);
1251              else
1252                {
1253                  char
1254                    path[MagickPathExtent],
1255                    *file_xml;
1256
1257                  *path='\0';
1258                  GetPathComponent(filename,HeadPath,path);
1259                  if (*path != '\0')
1260                    (void) ConcatenateMagickString(path,DirectorySeparator,
1261                      MagickPathExtent);
1262                  if (*token == *DirectorySeparator)
1263                    (void) CopyMagickString(path,token,MagickPathExtent);
1264                  else
1265                    (void) ConcatenateMagickString(path,token,MagickPathExtent);
1266                  file_xml=FileToXML(path,~0UL);
1267                  if (file_xml != (char *) NULL)
1268                    {
1269                      status&=LoadLocaleCache(cache,file_xml,path,locale,
1270                        depth+1,exception);
1271                      file_xml=DestroyString(file_xml);
1272                    }
1273                }
1274            }
1275        }
1276        continue;
1277      }
1278    if (LocaleCompare(keyword,"<locale") == 0)
1279      {
1280        /*
1281          Locale element.
1282        */
1283        while ((*token != '>') && (*q != '\0'))
1284        {
1285          (void) CopyMagickString(keyword,token,MagickLocaleExtent);
1286          GetNextToken(q,&q,extent,token);
1287          if (*token != '=')
1288            continue;
1289          GetNextToken(q,&q,extent,token);
1290        }
1291        continue;
1292      }
1293    if (LocaleCompare(keyword,"</locale>") == 0)
1294      {
1295        ChopLocaleComponents(tag,1);
1296        (void) ConcatenateMagickString(tag,"/",MagickLocaleExtent);
1297        continue;
1298      }
1299    if (LocaleCompare(keyword,"<localemap>") == 0)
1300      continue;
1301    if (LocaleCompare(keyword,"</localemap>") == 0)
1302      continue;
1303    if (LocaleCompare(keyword,"<message") == 0)
1304      {
1305        /*
1306          Message element.
1307        */
1308        while ((*token != '>') && (*q != '\0'))
1309        {
1310          (void) CopyMagickString(keyword,token,MagickLocaleExtent);
1311          GetNextToken(q,&q,extent,token);
1312          if (*token != '=')
1313            continue;
1314          GetNextToken(q,&q,extent,token);
1315          if (LocaleCompare(keyword,"name") == 0)
1316            {
1317              (void) ConcatenateMagickString(tag,token,MagickLocaleExtent);
1318              (void) ConcatenateMagickString(tag,"/",MagickLocaleExtent);
1319            }
1320        }
1321        for (p=(char *) q; (*q != '<') && (*q != '\0'); q++) ;
1322        while (isspace((int) ((unsigned char) *p)) != 0)
1323          p++;
1324        q--;
1325        while ((isspace((int) ((unsigned char) *q)) != 0) && (q > p))
1326          q--;
1327        (void) CopyMagickString(message,p,MagickMin((size_t) (q-p+2),
1328          MagickLocaleExtent));
1329        locale_info=(LocaleInfo *) AcquireMagickMemory(sizeof(*locale_info));
1330        if (locale_info == (LocaleInfo *) NULL)
1331          ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1332        (void) ResetMagickMemory(locale_info,0,sizeof(*locale_info));
1333        locale_info->path=ConstantString(filename);
1334        locale_info->tag=ConstantString(tag);
1335        locale_info->message=ConstantString(message);
1336        locale_info->signature=MagickCoreSignature;
1337        status=AddValueToSplayTree(cache,locale_info->tag,locale_info);
1338        if (status == MagickFalse)
1339          (void) ThrowMagickException(exception,GetMagickModule(),
1340            ResourceLimitError,"MemoryAllocationFailed","`%s'",
1341            locale_info->tag);
1342        (void) ConcatenateMagickString(tag,message,MagickLocaleExtent);
1343        (void) ConcatenateMagickString(tag,"\n",MagickLocaleExtent);
1344        q++;
1345        continue;
1346      }
1347    if (LocaleCompare(keyword,"</message>") == 0)
1348      {
1349        ChopLocaleComponents(tag,2);
1350        (void) ConcatenateMagickString(tag,"/",MagickLocaleExtent);
1351        continue;
1352      }
1353    if (*keyword == '<')
1354      {
1355        /*
1356          Subpath element.
1357        */
1358        if (*(keyword+1) == '?')
1359          continue;
1360        if (*(keyword+1) == '/')
1361          {
1362            ChopLocaleComponents(tag,1);
1363            if (*tag != '\0')
1364              (void) ConcatenateMagickString(tag,"/",MagickLocaleExtent);
1365            continue;
1366          }
1367        token[strlen(token)-1]='\0';
1368        (void) CopyMagickString(token,token+1,MagickLocaleExtent);
1369        (void) ConcatenateMagickString(tag,token,MagickLocaleExtent);
1370        (void) ConcatenateMagickString(tag,"/",MagickLocaleExtent);
1371        continue;
1372      }
1373    GetNextToken(q,(const char **) NULL,extent,token);
1374    if (*token != '=')
1375      continue;
1376  }
1377  token=(char *) RelinquishMagickMemory(token);
1378  (void) SetFatalErrorHandler(fatal_handler);
1379  return(status != 0 ? MagickTrue : MagickFalse);
1380}
1381
1382/*
1383%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1384%                                                                             %
1385%                                                                             %
1386%                                                                             %
1387%   L o c a l e C o m p a r e                                                 %
1388%                                                                             %
1389%                                                                             %
1390%                                                                             %
1391%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1392%
1393%  LocaleCompare() performs a case-insensitive comparison of two strings
1394%  byte-by-byte, according to the ordering of the current locale encoding.
1395%  LocaleCompare returns an integer greater than, equal to, or less than 0,
1396%  if the string pointed to by p is greater than, equal to, or less than the
1397%  string pointed to by q respectively.  The sign of a non-zero return value
1398%  is determined by the sign of the difference between the values of the first
1399%  pair of bytes that differ in the strings being compared.
1400%
1401%  The format of the LocaleCompare method is:
1402%
1403%      int LocaleCompare(const char *p,const char *q)
1404%
1405%  A description of each parameter follows:
1406%
1407%    o p: A pointer to a character string.
1408%
1409%    o q: A pointer to a character string to compare to p.
1410%
1411*/
1412MagickExport int LocaleCompare(const char *p,const char *q)
1413{
1414  if ((p == (char *) NULL) && (q == (char *) NULL))
1415    return(0);
1416  if (p == (char *) NULL)
1417    return(-1);
1418  if (q == (char *) NULL)
1419    return(1);
1420#if defined(MAGICKCORE_HAVE_STRCASECMP)
1421  return(strcasecmp(p,q));
1422#else
1423  {
1424    register int
1425      c,
1426      d;
1427
1428    for ( ; ; )
1429    {
1430      c=(int) *((unsigned char *) p);
1431      d=(int) *((unsigned char *) q);
1432      if ((c == 0) || (AsciiMap[c] != AsciiMap[d]))
1433        break;
1434      p++;
1435      q++;
1436    }
1437    return(AsciiMap[c]-(int) AsciiMap[d]);
1438  }
1439#endif
1440}
1441
1442/*
1443%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1444%                                                                             %
1445%                                                                             %
1446%                                                                             %
1447%   L o c a l e L o w e r                                                     %
1448%                                                                             %
1449%                                                                             %
1450%                                                                             %
1451%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1452%
1453%  LocaleLower() transforms all of the characters in the supplied
1454%  null-terminated string, changing all uppercase letters to lowercase.
1455%
1456%  The format of the LocaleLower method is:
1457%
1458%      void LocaleLower(char *string)
1459%
1460%  A description of each parameter follows:
1461%
1462%    o string: A pointer to the string to convert to lower-case Locale.
1463%
1464*/
1465MagickExport void LocaleLower(char *string)
1466{
1467  register char
1468    *q;
1469
1470  assert(string != (char *) NULL);
1471  for (q=string; *q != '\0'; q++)
1472    *q=(char) tolower((int) *q);
1473}
1474
1475/*
1476%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1477%                                                                             %
1478%                                                                             %
1479%                                                                             %
1480%   L o c a l e N C o m p a r e                                               %
1481%                                                                             %
1482%                                                                             %
1483%                                                                             %
1484%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1485%
1486%  LocaleNCompare() performs a case-insensitive comparison of two strings
1487%  byte-by-byte, according to the ordering of the current locale encoding.
1488%
1489%  LocaleNCompare returns an integer greater than, equal to, or less than 0,
1490%  if the string pointed to by p is greater than, equal to, or less than the
1491%  string pointed to by q respectively.  The sign of a non-zero return value
1492%  is determined by the sign of the difference between the values of the first
1493%  pair of bytes that differ in the strings being compared.
1494%
1495%  The LocaleNCompare method makes the same comparison as LocaleCompare but
1496%  looks at a maximum of n bytes.  Bytes following a null byte are not
1497%  compared.
1498%
1499%  The format of the LocaleNCompare method is:
1500%
1501%      int LocaleNCompare(const char *p,const char *q,const size_t n)
1502%
1503%  A description of each parameter follows:
1504%
1505%    o p: A pointer to a character string.
1506%
1507%    o q: A pointer to a character string to compare to p.
1508%
1509%    o length: the number of characters to compare in strings p and q.
1510%
1511*/
1512MagickExport int LocaleNCompare(const char *p,const char *q,const size_t length)
1513{
1514  if ((p == (char *) NULL) && (q == (char *) NULL))
1515    return(0);
1516  if (p == (char *) NULL)
1517    return(-1);
1518  if (q == (char *) NULL)
1519    return(1);
1520#if defined(MAGICKCORE_HAVE_STRNCASECMP)
1521  return(strncasecmp(p,q,length));
1522#else
1523  {
1524    register int
1525      c,
1526      d;
1527
1528    register size_t
1529      i;
1530
1531    for (i=length; i != 0; i--)
1532    {
1533      c=(int) *((unsigned char *) p);
1534      d=(int) *((unsigned char *) q);
1535      if (AsciiMap[c] != AsciiMap[d])
1536        return(AsciiMap[c]-(int) AsciiMap[d]);
1537      if (c == 0)
1538        return(0);
1539      p++;
1540      q++;
1541    }
1542    return(0);
1543  }
1544#endif
1545}
1546
1547/*
1548%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1549%                                                                             %
1550%                                                                             %
1551%                                                                             %
1552%   L o c a l e U p p e r                                                     %
1553%                                                                             %
1554%                                                                             %
1555%                                                                             %
1556%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1557%
1558%  LocaleUpper() transforms all of the characters in the supplied
1559%  null-terminated string, changing all lowercase letters to uppercase.
1560%
1561%  The format of the LocaleUpper method is:
1562%
1563%      void LocaleUpper(char *string)
1564%
1565%  A description of each parameter follows:
1566%
1567%    o string: A pointer to the string to convert to upper-case Locale.
1568%
1569*/
1570MagickExport void LocaleUpper(char *string)
1571{
1572  register char
1573    *q;
1574
1575  assert(string != (char *) NULL);
1576  for (q=string; *q != '\0'; q++)
1577    *q=(char) toupper((int) *q);
1578}
1579
1580/*
1581%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1582%                                                                             %
1583%                                                                             %
1584%                                                                             %
1585+   L o c a l e C o m p o n e n t G e n e s i s                               %
1586%                                                                             %
1587%                                                                             %
1588%                                                                             %
1589%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1590%
1591%  LocaleComponentGenesis() instantiates the locale component.
1592%
1593%  The format of the LocaleComponentGenesis method is:
1594%
1595%      MagickBooleanType LocaleComponentGenesis(void)
1596%
1597*/
1598MagickPrivate MagickBooleanType LocaleComponentGenesis(void)
1599{
1600  if (locale_semaphore == (SemaphoreInfo *) NULL)
1601    locale_semaphore=AcquireSemaphoreInfo();
1602  return(MagickTrue);
1603}
1604
1605/*
1606%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1607%                                                                             %
1608%                                                                             %
1609%                                                                             %
1610+   L o c a l e C o m p o n e n t T e r m i n u s                             %
1611%                                                                             %
1612%                                                                             %
1613%                                                                             %
1614%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1615%
1616%  LocaleComponentTerminus() destroys the locale component.
1617%
1618%  The format of the LocaleComponentTerminus method is:
1619%
1620%      LocaleComponentTerminus(void)
1621%
1622*/
1623MagickPrivate void LocaleComponentTerminus(void)
1624{
1625  if (locale_semaphore == (SemaphoreInfo *) NULL)
1626    ActivateSemaphoreInfo(&locale_semaphore);
1627  LockSemaphoreInfo(locale_semaphore);
1628  if (locale_cache != (SplayTreeInfo *) NULL)
1629    locale_cache=DestroySplayTree(locale_cache);
1630#if defined(MAGICKCORE_LOCALE_SUPPORT)
1631  DestroyCLocale();
1632#endif
1633  UnlockSemaphoreInfo(locale_semaphore);
1634  RelinquishSemaphoreInfo(&locale_semaphore);
1635}
1636