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