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