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