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