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