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