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