]> granicus.if.org Git - imagemagick/blob - MagickCore/locale.c
(no commit message)
[imagemagick] / MagickCore / locale.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                  L       OOO    CCCC   AAA   L      EEEEE                   %
7 %                  L      O   O  C      A   A  L      E                       %
8 %                  L      O   O  C      AAAAA  L      EEE                     %
9 %                  L      O   O  C      A   A  L      E                       %
10 %                  LLLLL   OOO    CCCC  A   A  LLLLL  EEEEE                   %
11 %                                                                             %
12 %                                                                             %
13 %                      MagickCore Image Locale Methods                        %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                   Cristy                                    %
17 %                                 July 2003                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2014 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 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 \f
39 /*
40   Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/blob.h"
44 #include "MagickCore/client.h"
45 #include "MagickCore/configure.h"
46 #include "MagickCore/exception.h"
47 #include "MagickCore/exception-private.h"
48 #include "MagickCore/hashmap.h"
49 #include "MagickCore/locale_.h"
50 #include "MagickCore/locale-private.h"
51 #include "MagickCore/log.h"
52 #include "MagickCore/memory_.h"
53 #include "MagickCore/nt-base-private.h"
54 #include "MagickCore/semaphore.h"
55 #include "MagickCore/splay-tree.h"
56 #include "MagickCore/string_.h"
57 #include "MagickCore/string-private.h"
58 #include "MagickCore/token.h"
59 #include "MagickCore/utility.h"
60 #include "MagickCore/utility-private.h"
61 #include "MagickCore/xml-tree.h"
62 \f
63 /*
64   Define declarations.
65 */
66 #define LocaleFilename  "locale.xml"
67 #define MaxRecursionDepth  200
68 \f
69 /*
70   Typedef declarations.
71 */
72 #if defined(__CYGWIN__)
73 typedef struct _locale_t
74   *locale_t;
75 #endif
76 \f
77 /*
78   Static declarations.
79 */
80 static const char
81   *LocaleMap =
82     "<?xml version=\"1.0\"?>"
83     "<localemap>"
84     "  <locale name=\"C\">"
85     "    <Exception>"
86     "     <Message name=\"\">"
87     "     </Message>"
88     "    </Exception>"
89     "  </locale>"
90     "</localemap>";
91
92 static SemaphoreInfo
93   *locale_semaphore = (SemaphoreInfo *) NULL;
94
95 static SplayTreeInfo
96   *locale_cache = (SplayTreeInfo *) NULL;
97
98 #if defined(MAGICKCORE_HAVE_STRTOD_L)
99 static volatile locale_t
100   c_locale = (locale_t) NULL;
101 #endif
102 \f
103 /*
104   Forward declarations.
105 */
106 static MagickBooleanType
107   IsLocaleTreeInstantiated(ExceptionInfo *),
108   LoadLocaleCache(const char *,const char *,const char *,const size_t,
109     ExceptionInfo *);
110 \f
111 #if defined(MAGICKCORE_HAVE_STRTOD_L)
112 /*
113 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
114 %                                                                             %
115 %                                                                             %
116 %                                                                             %
117 +   A c q u i r e C L o c a l e                                               %
118 %                                                                             %
119 %                                                                             %
120 %                                                                             %
121 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
122 %
123 %  AcquireCLocale() allocates the C locale object, or (locale_t) 0 with
124 %  errno set if it cannot be acquired.
125 %
126 %  The format of the AcquireCLocale method is:
127 %
128 %      locale_t AcquireCLocale(void)
129 %
130 */
131 static locale_t AcquireCLocale(void)
132 {
133 #if defined(MAGICKCORE_HAVE_NEWLOCALE)
134   if (c_locale == (locale_t) NULL)
135     c_locale=newlocale(LC_ALL_MASK,"C",(locale_t) 0);
136 #elif defined(MAGICKCORE_WINDOWS_SUPPORT)
137   if (c_locale == (locale_t) NULL)
138     c_locale=_create_locale(LC_ALL,"C");
139 #endif
140   return(c_locale);
141 }
142 #endif
143 \f
144 /*
145 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
146 %                                                                             %
147 %                                                                             %
148 %                                                                             %
149 %  A c q u i r e L o c a l e S p l a y T r e e                                %
150 %                                                                             %
151 %                                                                             %
152 %                                                                             %
153 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
154 %
155 %  AcquireLocaleSplayTree() caches one or more locale configurations which
156 %  provides a mapping between locale attributes and a locale tag.
157 %
158 %  The format of the AcquireLocaleSplayTree method is:
159 %
160 %      SplayTreeInfo *AcquireLocaleSplayTree(const char *filename,
161 %        ExceptionInfo *exception)
162 %
163 %  A description of each parameter follows:
164 %
165 %    o filename: the font file tag.
166 %
167 %    o locale: the actual locale.
168 %
169 %    o exception: return any errors or warnings in this structure.
170 %
171 */
172
173 static void *DestroyLocaleNode(void *locale_info)
174 {
175   register LocaleInfo
176     *p;
177
178   p=(LocaleInfo *) locale_info;
179   if (p->path != (char *) NULL)
180     p->path=DestroyString(p->path);
181   if (p->tag != (char *) NULL)
182     p->tag=DestroyString(p->tag);
183   if (p->message != (char *) NULL)
184     p->message=DestroyString(p->message);
185   return(RelinquishMagickMemory(p));
186 }
187
188 static SplayTreeInfo *AcquireLocaleSplayTree(const char *filename,
189   const char *locale,ExceptionInfo *exception)
190 {
191 #if defined(MAGICKCORE_ZERO_CONFIGURATION_SUPPORT)
192   return(LoadLocaleCache(LocaleMap,"built-in",locale,0,exception));
193 #else
194   const StringInfo
195     *option;
196
197   LinkedListInfo
198     *options;
199
200   MagickStatusType
201     status;
202
203   SplayTreeInfo
204     *locale_cache;
205
206   locale_cache=NewSplayTree(CompareSplayTreeString,(void *(*)(void *)) NULL,
207     DestroyLocaleNode);
208   if (locale_cache == (SplayTreeInfo *) NULL)
209     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
210   status=MagickTrue;
211   options=GetLocaleOptions(filename,exception);
212   option=(const StringInfo *) GetNextValueInLinkedList(options);
213   while (option != (const StringInfo *) NULL)
214   {
215     status&=LoadLocaleCache((const char *) GetStringInfoDatum(option),
216       GetStringInfoPath(option),locale,0,exception);
217     option=(const StringInfo *) GetNextValueInLinkedList(options);
218   }
219   options=DestroyLocaleOptions(options);
220   if ((locale_cache == (SplayTreeInfo *) NULL) ||
221       (GetNumberOfNodesInSplayTree(locale_cache) == 0))
222     {
223       options=GetLocaleOptions("english.xml",exception);
224       option=(const StringInfo *) GetNextValueInLinkedList(options);
225       while (option != (const StringInfo *) NULL)
226       {
227         status&=LoadLocaleCache((const char *) GetStringInfoDatum(option),
228           GetStringInfoPath(option),locale,0,exception);
229         option=(const StringInfo *) GetNextValueInLinkedList(options);
230       }
231       options=DestroyLocaleOptions(options);
232     }
233   if ((locale_cache == (SplayTreeInfo *) NULL) ||
234       (GetNumberOfNodesInSplayTree(locale_cache) == 0))
235     status&=LoadLocaleCache(LocaleMap,"built-in",locale,0,exception);
236   return(locale_cache);
237 #endif
238 }
239 \f
240 #if defined(MAGICKCORE_HAVE_STRTOD_L)
241 /*
242 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
243 %                                                                             %
244 %                                                                             %
245 %                                                                             %
246 +   D e s t r o y C L o c a l e                                               %
247 %                                                                             %
248 %                                                                             %
249 %                                                                             %
250 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
251 %
252 %  DestroyCLocale() releases the resources allocated for a locale object
253 %  returned by a call to the AcquireCLocale() method.
254 %
255 %  The format of the DestroyCLocale method is:
256 %
257 %      void DestroyCLocale(void)
258 %
259 */
260 static void DestroyCLocale(void)
261 {
262 #if defined(MAGICKCORE_HAVE_NEWLOCALE)
263   if (c_locale != (locale_t) NULL)
264     freelocale(c_locale);
265 #elif defined(MAGICKCORE_WINDOWS_SUPPORT)
266   if (c_locale != (locale_t) NULL)
267     _free_locale(c_locale);
268 #endif
269   c_locale=(locale_t) NULL;
270 }
271 #endif
272 \f
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   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
306   return(DestroyLinkedList(messages,DestroyOptions));
307 }
308 \f
309 /*
310 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
311 %                                                                             %
312 %                                                                             %
313 %                                                                             %
314 +  F o r m a t L o c a l e F i l e                                            %
315 %                                                                             %
316 %                                                                             %
317 %                                                                             %
318 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
319 %
320 %  FormatLocaleFile() prints formatted output of a variable argument list to a
321 %  file in the "C" locale.
322 %
323 %  The format of the FormatLocaleFile method is:
324 %
325 %      ssize_t FormatLocaleFile(FILE *file,const char *format,...)
326 %
327 %  A description of each parameter follows.
328 %
329 %   o file:  the file.
330 %
331 %   o format:  A file describing the format to use to write the remaining
332 %     arguments.
333 %
334 */
335
336 MagickPrivate ssize_t FormatLocaleFileList(FILE *file,
337   const char *restrict format,va_list operands)
338 {
339   ssize_t
340     n;
341
342 #if defined(MAGICKCORE_HAVE_VFPRINTF_L)
343   {
344     locale_t
345       locale;
346
347     locale=AcquireCLocale();
348     if (locale == (locale_t) NULL)
349       n=(ssize_t) vfprintf(file,format,operands);
350     else
351 #if defined(MAGICKCORE_WINDOWS_SUPPORT)
352       n=(ssize_t) vfprintf_l(file,format,locale,operands);
353 #else
354       n=(ssize_t) vfprintf_l(file,locale,format,operands);
355 #endif
356   }
357 #else
358 #if defined(MAGICKCORE_HAVE_USELOCALE)
359   {
360     locale_t
361       locale,
362       previous_locale;
363
364     locale=AcquireCLocale();
365     if (locale == (locale_t) NULL)
366       n=(ssize_t) vfprintf(file,format,operands);
367     else
368       {
369         previous_locale=uselocale(locale);
370         n=(ssize_t) vfprintf(file,format,operands);
371         uselocale(previous_locale);
372       }
373   }
374 #else
375   n=(ssize_t) vfprintf(file,format,operands);
376 #endif
377 #endif
378   return(n);
379 }
380
381 MagickExport ssize_t FormatLocaleFile(FILE *file,const char *restrict format,
382   ...)
383 {
384   ssize_t
385     n;
386
387   va_list
388     operands;
389
390   va_start(operands,format);
391   n=FormatLocaleFileList(file,format,operands);
392   va_end(operands);
393   return(n);
394 }
395 \f
396 /*
397 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
398 %                                                                             %
399 %                                                                             %
400 %                                                                             %
401 +  F o r m a t L o c a l e S t r i n g                                        %
402 %                                                                             %
403 %                                                                             %
404 %                                                                             %
405 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
406 %
407 %  FormatLocaleString() prints formatted output of a variable argument list to
408 %  a string buffer in the "C" locale.
409 %
410 %  The format of the FormatLocaleString method is:
411 %
412 %      ssize_t FormatLocaleString(char *string,const size_t length,
413 %        const char *format,...)
414 %
415 %  A description of each parameter follows.
416 %
417 %   o string:  FormatLocaleString() returns the formatted string in this
418 %     character buffer.
419 %
420 %   o length: the maximum length of the string.
421 %
422 %   o format:  A string describing the format to use to write the remaining
423 %     arguments.
424 %
425 */
426
427 MagickPrivate ssize_t FormatLocaleStringList(char *restrict string,
428   const size_t length,const char *restrict format,va_list operands)
429 {
430   ssize_t
431     n;
432
433 #if defined(MAGICKCORE_HAVE_VSNPRINTF_L)
434   {
435     locale_t
436       locale;
437
438     locale=AcquireCLocale();
439     if (locale == (locale_t) NULL)
440       n=(ssize_t) vsnprintf(string,length,format,operands);
441     else
442 #if defined(MAGICKCORE_WINDOWS_SUPPORT)
443       n=(ssize_t) vsnprintf_l(string,length,format,locale,operands);
444 #else
445       n=(ssize_t) vsnprintf_l(string,length,locale,format,operands);
446 #endif
447   }
448 #elif defined(MAGICKCORE_HAVE_VSNPRINTF)
449 #if defined(MAGICKCORE_HAVE_USELOCALE)
450   {
451     locale_t
452       locale,
453       previous_locale;
454
455     locale=AcquireCLocale();
456     if (locale == (locale_t) NULL)
457       n=(ssize_t) vsnprintf(string,length,format,operands);
458     else
459       {
460         previous_locale=uselocale(locale);
461         n=(ssize_t) vsnprintf(string,length,format,operands);
462         uselocale(previous_locale);
463       }
464   }
465 #else
466   n=(ssize_t) vsnprintf(string,length,format,operands);
467 #endif
468 #else
469   n=(ssize_t) vsprintf(string,format,operands);
470 #endif
471   if (n < 0)
472     string[length-1]='\0';
473   return(n);
474 }
475
476 MagickExport ssize_t FormatLocaleString(char *restrict string,
477   const size_t length,const char *restrict format,...)
478 {
479   ssize_t
480     n;
481
482   va_list
483     operands;
484
485   va_start(operands,format);
486   n=FormatLocaleStringList(string,length,format,operands);
487   va_end(operands);
488   return(n);
489 }
490 \f
491 /*
492 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
493 %                                                                             %
494 %                                                                             %
495 %                                                                             %
496 +   G e t L o c a l e I n f o _                                               %
497 %                                                                             %
498 %                                                                             %
499 %                                                                             %
500 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
501 %
502 %  GetLocaleInfo_() searches the locale list for the specified tag and if
503 %  found returns attributes for that element.
504 %
505 %  The format of the GetLocaleInfo method is:
506 %
507 %      const LocaleInfo *GetLocaleInfo_(const char *tag,
508 %        ExceptionInfo *exception)
509 %
510 %  A description of each parameter follows:
511 %
512 %    o tag: the locale tag.
513 %
514 %    o exception: return any errors or warnings in this structure.
515 %
516 */
517 MagickExport const LocaleInfo *GetLocaleInfo_(const char *tag,
518   ExceptionInfo *exception)
519 {
520   const LocaleInfo
521     *locale_info;
522
523   assert(exception != (ExceptionInfo *) NULL);
524   if (IsLocaleTreeInstantiated(exception) == MagickFalse)
525     return((const LocaleInfo *) NULL);
526   LockSemaphoreInfo(locale_semaphore);
527   if ((tag == (const char *) NULL) || (LocaleCompare(tag,"*") == 0))
528     {
529       ResetSplayTreeIterator(locale_cache);
530       locale_info=(const LocaleInfo *) GetNextValueInSplayTree(locale_cache);
531       UnlockSemaphoreInfo(locale_semaphore);
532       return(locale_info);
533     }
534   locale_info=(const LocaleInfo *) GetValueFromSplayTree(locale_cache,tag);
535   UnlockSemaphoreInfo(locale_semaphore);
536   return(locale_info);
537 }
538 \f
539 /*
540 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
541 %                                                                             %
542 %                                                                             %
543 %                                                                             %
544 %   G e t L o c a l e I n f o L i s t                                         %
545 %                                                                             %
546 %                                                                             %
547 %                                                                             %
548 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
549 %
550 %  GetLocaleInfoList() returns any locale messages that match the
551 %  specified pattern.
552 %
553 %  The format of the GetLocaleInfoList function is:
554 %
555 %      const LocaleInfo **GetLocaleInfoList(const char *pattern,
556 %        size_t *number_messages,ExceptionInfo *exception)
557 %
558 %  A description of each parameter follows:
559 %
560 %    o pattern: Specifies a pointer to a text string containing a pattern.
561 %
562 %    o number_messages:  This integer returns the number of locale messages in
563 %    the list.
564 %
565 %    o exception: return any errors or warnings in this structure.
566 %
567 */
568
569 #if defined(__cplusplus) || defined(c_plusplus)
570 extern "C" {
571 #endif
572
573 static int LocaleInfoCompare(const void *x,const void *y)
574 {
575   const LocaleInfo
576     **p,
577     **q;
578
579   p=(const LocaleInfo **) x,
580   q=(const LocaleInfo **) y;
581   if (LocaleCompare((*p)->path,(*q)->path) == 0)
582     return(LocaleCompare((*p)->tag,(*q)->tag));
583   return(LocaleCompare((*p)->path,(*q)->path));
584 }
585
586 #if defined(__cplusplus) || defined(c_plusplus)
587 }
588 #endif
589
590 MagickExport const LocaleInfo **GetLocaleInfoList(const char *pattern,
591   size_t *number_messages,ExceptionInfo *exception)
592 {
593   const LocaleInfo
594     **messages;
595
596   register const LocaleInfo
597     *p;
598
599   register ssize_t
600     i;
601
602   /*
603     Allocate locale list.
604   */
605   assert(pattern != (char *) NULL);
606   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
607   assert(number_messages != (size_t *) NULL);
608   *number_messages=0;
609   p=GetLocaleInfo_("*",exception);
610   if (p == (const LocaleInfo *) NULL)
611     return((const LocaleInfo **) NULL);
612   messages=(const LocaleInfo **) AcquireQuantumMemory((size_t)
613     GetNumberOfNodesInSplayTree(locale_cache)+1UL,sizeof(*messages));
614   if (messages == (const LocaleInfo **) NULL)
615     return((const LocaleInfo **) NULL);
616   /*
617     Generate locale list.
618   */
619   LockSemaphoreInfo(locale_semaphore);
620   ResetSplayTreeIterator(locale_cache);
621   p=(const LocaleInfo *) GetNextValueInSplayTree(locale_cache);
622   for (i=0; p != (const LocaleInfo *) NULL; )
623   {
624     if ((p->stealth == MagickFalse) &&
625         (GlobExpression(p->tag,pattern,MagickTrue) != MagickFalse))
626       messages[i++]=p;
627     p=(const LocaleInfo *) GetNextValueInSplayTree(locale_cache);
628   }
629   UnlockSemaphoreInfo(locale_semaphore);
630   qsort((void *) messages,(size_t) i,sizeof(*messages),LocaleInfoCompare);
631   messages[i]=(LocaleInfo *) NULL;
632   *number_messages=(size_t) i;
633   return(messages);
634 }
635 \f
636 /*
637 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
638 %                                                                             %
639 %                                                                             %
640 %                                                                             %
641 %   G e t L o c a l e L i s t                                                 %
642 %                                                                             %
643 %                                                                             %
644 %                                                                             %
645 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
646 %
647 %  GetLocaleList() returns any locale messages that match the specified
648 %  pattern.
649 %
650 %  The format of the GetLocaleList function is:
651 %
652 %      char **GetLocaleList(const char *pattern,size_t *number_messages,
653 %        Exceptioninfo *exception)
654 %
655 %  A description of each parameter follows:
656 %
657 %    o pattern: Specifies a pointer to a text string containing a pattern.
658 %
659 %    o number_messages:  This integer returns the number of messages in the
660 %      list.
661 %
662 %    o exception: return any errors or warnings in this structure.
663 %
664 */
665
666 #if defined(__cplusplus) || defined(c_plusplus)
667 extern "C" {
668 #endif
669
670 static int LocaleTagCompare(const void *x,const void *y)
671 {
672   register char
673     **p,
674     **q;
675
676   p=(char **) x;
677   q=(char **) y;
678   return(LocaleCompare(*p,*q));
679 }
680
681 #if defined(__cplusplus) || defined(c_plusplus)
682 }
683 #endif
684
685 MagickExport char **GetLocaleList(const char *pattern,
686   size_t *number_messages,ExceptionInfo *exception)
687 {
688   char
689     **messages;
690
691   register const LocaleInfo
692     *p;
693
694   register ssize_t
695     i;
696
697   /*
698     Allocate locale list.
699   */
700   assert(pattern != (char *) NULL);
701   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
702   assert(number_messages != (size_t *) NULL);
703   *number_messages=0;
704   p=GetLocaleInfo_("*",exception);
705   if (p == (const LocaleInfo *) NULL)
706     return((char **) NULL);
707   messages=(char **) AcquireQuantumMemory((size_t)
708     GetNumberOfNodesInSplayTree(locale_cache)+1UL,sizeof(*messages));
709   if (messages == (char **) NULL)
710     return((char **) NULL);
711   LockSemaphoreInfo(locale_semaphore);
712   p=(const LocaleInfo *) GetNextValueInSplayTree(locale_cache);
713   for (i=0; p != (const LocaleInfo *) NULL; )
714   {
715     if ((p->stealth == MagickFalse) &&
716         (GlobExpression(p->tag,pattern,MagickTrue) != MagickFalse))
717       messages[i++]=ConstantString(p->tag);
718     p=(const LocaleInfo *) GetNextValueInSplayTree(locale_cache);
719   }
720   UnlockSemaphoreInfo(locale_semaphore);
721   qsort((void *) messages,(size_t) i,sizeof(*messages),LocaleTagCompare);
722   messages[i]=(char *) NULL;
723   *number_messages=(size_t) i;
724   return(messages);
725 }
726 \f
727 /*
728 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
729 %                                                                             %
730 %                                                                             %
731 %                                                                             %
732 %   G e t L o c a l e M e s s a g e                                           %
733 %                                                                             %
734 %                                                                             %
735 %                                                                             %
736 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
737 %
738 %  GetLocaleMessage() returns a message in the current locale that matches the
739 %  supplied tag.
740 %
741 %  The format of the GetLocaleMessage method is:
742 %
743 %      const char *GetLocaleMessage(const char *tag)
744 %
745 %  A description of each parameter follows:
746 %
747 %    o tag: Return a message that matches this tag in the current locale.
748 %
749 */
750 MagickExport const char *GetLocaleMessage(const char *tag)
751 {
752   char
753     name[MaxTextExtent];
754
755   const LocaleInfo
756     *locale_info;
757
758   ExceptionInfo
759     *exception;
760
761   if ((tag == (const char *) NULL) || (*tag == '\0'))
762     return(tag);
763   exception=AcquireExceptionInfo();
764   (void) FormatLocaleString(name,MaxTextExtent,"%s/",tag);
765   locale_info=GetLocaleInfo_(name,exception);
766   exception=DestroyExceptionInfo(exception);
767   if (locale_info != (const LocaleInfo *) NULL)
768     return(locale_info->message);
769   return(tag);
770 }
771 \f
772 /*
773 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
774 %                                                                             %
775 %                                                                             %
776 %                                                                             %
777 %  G e t L o c a l e O p t i o n s                                            %
778 %                                                                             %
779 %                                                                             %
780 %                                                                             %
781 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
782 %
783 %  GetLocaleOptions() returns any Magick configuration messages associated
784 %  with the specified filename.
785 %
786 %  The format of the GetLocaleOptions method is:
787 %
788 %      LinkedListInfo *GetLocaleOptions(const char *filename,
789 %        ExceptionInfo *exception)
790 %
791 %  A description of each parameter follows:
792 %
793 %    o filename: the locale file tag.
794 %
795 %    o exception: return any errors or warnings in this structure.
796 %
797 */
798 MagickExport LinkedListInfo *GetLocaleOptions(const char *filename,
799   ExceptionInfo *exception)
800 {
801   char
802     path[MaxTextExtent];
803
804   const char
805     *element;
806
807   LinkedListInfo
808     *messages,
809     *paths;
810
811   StringInfo
812     *xml;
813
814   assert(filename != (const char *) NULL);
815   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
816   assert(exception != (ExceptionInfo *) NULL);
817   (void) CopyMagickString(path,filename,MaxTextExtent);
818   /*
819     Load XML from configuration files to linked-list.
820   */
821   messages=NewLinkedList(0);
822   paths=GetConfigurePaths(filename,exception);
823   if (paths != (LinkedListInfo *) NULL)
824     {
825       ResetLinkedListIterator(paths);
826       element=(const char *) GetNextValueInLinkedList(paths);
827       while (element != (const char *) NULL)
828       {
829         (void) FormatLocaleString(path,MaxTextExtent,"%s%s",element,filename);
830         (void) LogMagickEvent(LocaleEvent,GetMagickModule(),
831           "Searching for locale file: \"%s\"",path);
832         xml=ConfigureFileToStringInfo(path);
833         if (xml != (StringInfo *) NULL)
834           (void) AppendValueToLinkedList(messages,xml);
835         element=(const char *) GetNextValueInLinkedList(paths);
836       }
837       paths=DestroyLinkedList(paths,RelinquishMagickMemory);
838     }
839 #if defined(MAGICKCORE_WINDOWS_SUPPORT)
840   {
841     char
842       *blob;
843
844     blob=(char *) NTResourceToBlob(filename);
845     if (blob != (char *) NULL)
846       {
847         xml=AcquireStringInfo(0);
848         SetStringInfoLength(xml,strlen(blob)+1);
849         SetStringInfoDatum(xml,(unsigned char *) blob);
850         SetStringInfoPath(xml,filename);
851         (void) AppendValueToLinkedList(messages,xml);
852       }
853   }
854 #endif
855   ResetLinkedListIterator(messages);
856   return(messages);
857 }
858 \f
859 /*
860 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
861 %                                                                             %
862 %                                                                             %
863 %                                                                             %
864 %   G e t L o c a l e V a l u e                                               %
865 %                                                                             %
866 %                                                                             %
867 %                                                                             %
868 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
869 %
870 %  GetLocaleValue() returns the message associated with the locale info.
871 %
872 %  The format of the GetLocaleValue method is:
873 %
874 %      const char *GetLocaleValue(const LocaleInfo *locale_info)
875 %
876 %  A description of each parameter follows:
877 %
878 %    o locale_info:  The locale info.
879 %
880 */
881 MagickExport const char *GetLocaleValue(const LocaleInfo *locale_info)
882 {
883   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
884   assert(locale_info != (LocaleInfo *) NULL);
885   assert(locale_info->signature == MagickSignature);
886   return(locale_info->message);
887 }
888 \f
889 /*
890 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
891 %                                                                             %
892 %                                                                             %
893 %                                                                             %
894 +   I s L o c a l e T r e e I n s t a n t i a t e d                           %
895 %                                                                             %
896 %                                                                             %
897 %                                                                             %
898 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
899 %
900 %  IsLocaleTreeInstantiated() determines if the locale tree is instantiated.
901 %  If not, it instantiates the tree and returns it.
902 %
903 %  The format of the IsLocaleInstantiated method is:
904 %
905 %      MagickBooleanType IsLocaleTreeInstantiated(ExceptionInfo *exception)
906 %
907 %  A description of each parameter follows.
908 %
909 %    o exception: return any errors or warnings in this structure.
910 %
911 */
912 static MagickBooleanType IsLocaleTreeInstantiated(ExceptionInfo *exception)
913 {
914   if (locale_semaphore == (SemaphoreInfo *) NULL)
915     ActivateSemaphoreInfo(&locale_semaphore);
916   LockSemaphoreInfo(locale_semaphore);
917   if (locale_cache == (SplayTreeInfo *) NULL)
918     {
919       char
920         *locale;
921
922       register const char
923         *p;
924
925       locale=(char *) NULL;
926       p=setlocale(LC_CTYPE,(const char *) NULL);
927       if (p != (const char *) NULL)
928         locale=ConstantString(p);
929       if (locale == (char *) NULL)
930         locale=GetEnvironmentValue("LC_ALL");
931       if (locale == (char *) NULL)
932         locale=GetEnvironmentValue("LC_MESSAGES");
933       if (locale == (char *) NULL)
934         locale=GetEnvironmentValue("LC_CTYPE");
935       if (locale == (char *) NULL)
936         locale=GetEnvironmentValue("LANG");
937       if (locale == (char *) NULL)
938         locale=ConstantString("C");
939       locale_cache=AcquireLocaleSplayTree(LocaleFilename,locale,exception);
940       locale=DestroyString(locale);
941     }
942   UnlockSemaphoreInfo(locale_semaphore);
943   return(locale_cache != (SplayTreeInfo *) NULL ? MagickTrue : MagickFalse);
944 }
945 \f
946 /*
947 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
948 %                                                                             %
949 %                                                                             %
950 %                                                                             %
951 +   I n t e r p r e t L o c a l e V a l u e                                   %
952 %                                                                             %
953 %                                                                             %
954 %                                                                             %
955 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
956 %
957 %  InterpretLocaleValue() interprets the string as a floating point number in
958 %  the "C" locale and returns its value as a double. If sentinal is not a null
959 %  pointer, the method also sets the value pointed by sentinal to point to the
960 %  first character after the number.
961 %
962 %  The format of the InterpretLocaleValue method is:
963 %
964 %      double InterpretLocaleValue(const char *value,char **sentinal)
965 %
966 %  A description of each parameter follows:
967 %
968 %    o value: the string value.
969 %
970 %    o sentinal:  if sentinal is not NULL, a pointer to the character after the
971 %      last character used in the conversion is stored in the location
972 %      referenced by sentinal.
973 %
974 */
975 MagickExport double InterpretLocaleValue(const char *restrict string,
976   char **restrict sentinal)
977 {
978   char
979     *q;
980
981   double
982     value;
983
984   if ((*string == '0') && ((string[1] | 0x20)=='x'))
985     value=(double) strtoul(string,&q,16);
986   else
987     {
988 #if defined(MAGICKCORE_HAVE_STRTOD_L)
989       locale_t
990         locale;
991
992       locale=AcquireCLocale();
993       if (locale == (locale_t) NULL)
994         value=strtod(string,&q);
995       else
996         value=strtod_l(string,&q,locale);
997 #else
998       value=strtod(string,&q);
999 #endif
1000     }
1001   if (sentinal != (char **) NULL)
1002     *sentinal=q;
1003   return(value);
1004 }
1005 \f
1006 /*
1007 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1008 %                                                                             %
1009 %                                                                             %
1010 %                                                                             %
1011 %  L i s t L o c a l e I n f o                                                %
1012 %                                                                             %
1013 %                                                                             %
1014 %                                                                             %
1015 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1016 %
1017 %  ListLocaleInfo() lists the locale info to a file.
1018 %
1019 %  The format of the ListLocaleInfo method is:
1020 %
1021 %      MagickBooleanType ListLocaleInfo(FILE *file,ExceptionInfo *exception)
1022 %
1023 %  A description of each parameter follows.
1024 %
1025 %    o file:  An pointer to a FILE.
1026 %
1027 %    o exception: return any errors or warnings in this structure.
1028 %
1029 */
1030 MagickExport MagickBooleanType ListLocaleInfo(FILE *file,
1031   ExceptionInfo *exception)
1032 {
1033   const char
1034     *path;
1035
1036   const LocaleInfo
1037     **locale_info;
1038
1039   register ssize_t
1040     i;
1041
1042   size_t
1043     number_messages;
1044
1045   if (file == (const FILE *) NULL)
1046     file=stdout;
1047   number_messages=0;
1048   locale_info=GetLocaleInfoList("*",&number_messages,exception);
1049   if (locale_info == (const LocaleInfo **) NULL)
1050     return(MagickFalse);
1051   path=(const char *) NULL;
1052   for (i=0; i < (ssize_t) number_messages; i++)
1053   {
1054     if (locale_info[i]->stealth != MagickFalse)
1055       continue;
1056     if ((path == (const char *) NULL) ||
1057         (LocaleCompare(path,locale_info[i]->path) != 0))
1058       {
1059         if (locale_info[i]->path != (char *) NULL)
1060           (void) FormatLocaleFile(file,"\nPath: %s\n\n",locale_info[i]->path);
1061         (void) FormatLocaleFile(file,"Tag/Message\n");
1062         (void) FormatLocaleFile(file,
1063           "-------------------------------------------------"
1064           "------------------------------\n");
1065       }
1066     path=locale_info[i]->path;
1067     (void) FormatLocaleFile(file,"%s\n",locale_info[i]->tag);
1068     if (locale_info[i]->message != (char *) NULL)
1069       (void) FormatLocaleFile(file,"  %s",locale_info[i]->message);
1070     (void) FormatLocaleFile(file,"\n");
1071   }
1072   (void) fflush(file);
1073   locale_info=(const LocaleInfo **)
1074     RelinquishMagickMemory((void *) locale_info);
1075   return(MagickTrue);
1076 }
1077 \f
1078 /*
1079 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1080 %                                                                             %
1081 %                                                                             %
1082 %                                                                             %
1083 +   L o a d L o c a l e L i s t                                               %
1084 %                                                                             %
1085 %                                                                             %
1086 %                                                                             %
1087 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1088 %
1089 %  LoadLocaleCache() loads the locale configurations which provides a mapping
1090 %  between locale attributes and a locale name.
1091 %
1092 %  The format of the LoadLocaleCache method is:
1093 %
1094 %      MagickBooleanType LoadLocaleCache(const char *xml,const char *filename,
1095 %        const size_t depth,ExceptionInfo *exception)
1096 %
1097 %  A description of each parameter follows:
1098 %
1099 %    o xml:  The locale list in XML format.
1100 %
1101 %    o filename:  The locale list filename.
1102 %
1103 %    o depth: depth of <include /> statements.
1104 %
1105 %    o exception: return any errors or warnings in this structure.
1106 %
1107 */
1108
1109 static void ChopLocaleComponents(char *path,const size_t components)
1110 {
1111   register char
1112     *p;
1113
1114   ssize_t
1115     count;
1116
1117   if (*path == '\0')
1118     return;
1119   p=path+strlen(path)-1;
1120   if (*p == '/')
1121     *p='\0';
1122   for (count=0; (count < (ssize_t) components) && (p > path); p--)
1123     if (*p == '/')
1124       {
1125         *p='\0';
1126         count++;
1127       }
1128   if (count < (ssize_t) components)
1129     *path='\0';
1130 }
1131
1132 static void LocaleFatalErrorHandler(
1133   const ExceptionType magick_unused(severity),
1134   const char *reason,const char *description)
1135 {
1136   if (reason == (char *) NULL)
1137     return;
1138   (void) FormatLocaleFile(stderr,"%s: %s",GetClientName(),reason);
1139   if (description != (char *) NULL)
1140     (void) FormatLocaleFile(stderr," (%s)",description);
1141   (void) FormatLocaleFile(stderr,".\n");
1142   (void) fflush(stderr);
1143   exit(1);
1144 }
1145
1146 static inline size_t MagickMin(const size_t x,const size_t y)
1147 {
1148   if (x < y)
1149     return(x);
1150   return(y);
1151 }
1152
1153 static MagickBooleanType LoadLocaleCache(const char *xml,const char *filename,
1154   const char *locale,const size_t depth,ExceptionInfo *exception)
1155 {
1156   char
1157     keyword[MaxTextExtent],
1158     message[MaxTextExtent],
1159     tag[MaxTextExtent],
1160     *token;
1161
1162   const char
1163     *q;
1164
1165   FatalErrorHandler
1166     fatal_handler;
1167
1168   LocaleInfo
1169     *locale_info;
1170
1171   MagickBooleanType
1172     status;
1173
1174   register char
1175     *p;
1176
1177   /*
1178     Read the locale configure file.
1179   */
1180   (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1181     "Loading locale configure file \"%s\" ...",filename);
1182   if (xml == (const char *) NULL)
1183     return(MagickFalse);
1184   if (locale_cache == (SplayTreeInfo *) NULL)
1185     {
1186       locale_cache=NewSplayTree(CompareSplayTreeString,(void *(*)(void *)) NULL,
1187         DestroyLocaleNode);
1188       if (locale_cache == (SplayTreeInfo *) NULL)
1189         return(MagickFalse);
1190     }
1191   status=MagickTrue;
1192   locale_info=(LocaleInfo *) NULL;
1193   *tag='\0';
1194   *message='\0';
1195   *keyword='\0';
1196   fatal_handler=SetFatalErrorHandler(LocaleFatalErrorHandler);
1197   token=AcquireString(xml);
1198   for (q=(char *) xml; *q != '\0'; )
1199   {
1200     /*
1201       Interpret XML.
1202     */
1203     GetMagickToken(q,&q,token);
1204     if (*token == '\0')
1205       break;
1206     (void) CopyMagickString(keyword,token,MaxTextExtent);
1207     if (LocaleNCompare(keyword,"<!DOCTYPE",9) == 0)
1208       {
1209         /*
1210           Doctype element.
1211         */
1212         while ((LocaleNCompare(q,"]>",2) != 0) && (*q != '\0'))
1213         {
1214           GetMagickToken(q,&q,token);
1215           while (isspace((int) ((unsigned char) *q)) != 0)
1216             q++;
1217         }
1218         continue;
1219       }
1220     if (LocaleNCompare(keyword,"<!--",4) == 0)
1221       {
1222         /*
1223           Comment element.
1224         */
1225         while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0'))
1226         {
1227           GetMagickToken(q,&q,token);
1228           while (isspace((int) ((unsigned char) *q)) != 0)
1229             q++;
1230         }
1231         continue;
1232       }
1233     if (LocaleCompare(keyword,"<include") == 0)
1234       {
1235         /*
1236           Include element.
1237         */
1238         while (((*token != '/') && (*(token+1) != '>')) && (*q != '\0'))
1239         {
1240           (void) CopyMagickString(keyword,token,MaxTextExtent);
1241           GetMagickToken(q,&q,token);
1242           if (*token != '=')
1243             continue;
1244           GetMagickToken(q,&q,token);
1245           if (LocaleCompare(keyword,"locale") == 0)
1246             {
1247               if (LocaleCompare(locale,token) != 0)
1248                 break;
1249               continue;
1250             }
1251           if (LocaleCompare(keyword,"file") == 0)
1252             {
1253               if (depth > 200)
1254                 (void) ThrowMagickException(exception,GetMagickModule(),
1255                   ConfigureError,"IncludeElementNestedTooDeeply","`%s'",token);
1256               else
1257                 {
1258                   char
1259                     path[MaxTextExtent],
1260                     *xml;
1261
1262                   *path='\0';
1263                   GetPathComponent(filename,HeadPath,path);
1264                   if (*path != '\0')
1265                     (void) ConcatenateMagickString(path,DirectorySeparator,
1266                       MaxTextExtent);
1267                   if (*token == *DirectorySeparator)
1268                     (void) CopyMagickString(path,token,MaxTextExtent);
1269                   else
1270                     (void) ConcatenateMagickString(path,token,MaxTextExtent);
1271                   xml=FileToString(path,~0UL,exception);
1272                   if (xml != (char *) NULL)
1273                     {
1274                       status=LoadLocaleCache(xml,path,locale,depth+1,exception);
1275                       xml=(char *) RelinquishMagickMemory(xml);
1276                     }
1277                 }
1278             }
1279         }
1280         continue;
1281       }
1282     if (LocaleCompare(keyword,"<locale") == 0)
1283       {
1284         /*
1285           Locale element.
1286         */
1287         while ((*token != '>') && (*q != '\0'))
1288         {
1289           (void) CopyMagickString(keyword,token,MaxTextExtent);
1290           GetMagickToken(q,&q,token);
1291           if (*token != '=')
1292             continue;
1293           GetMagickToken(q,&q,token);
1294         }
1295         continue;
1296       }
1297     if (LocaleCompare(keyword,"</locale>") == 0)
1298       {
1299         ChopLocaleComponents(tag,1);
1300         (void) ConcatenateMagickString(tag,"/",MaxTextExtent);
1301         continue;
1302       }
1303     if (LocaleCompare(keyword,"<localemap>") == 0)
1304       continue;
1305     if (LocaleCompare(keyword,"</localemap>") == 0)
1306       continue;
1307     if (LocaleCompare(keyword,"<message") == 0)
1308       {
1309         /*
1310           Message element.
1311         */
1312         while ((*token != '>') && (*q != '\0'))
1313         {
1314           (void) CopyMagickString(keyword,token,MaxTextExtent);
1315           GetMagickToken(q,&q,token);
1316           if (*token != '=')
1317             continue;
1318           GetMagickToken(q,&q,token);
1319           if (LocaleCompare(keyword,"name") == 0)
1320             {
1321               (void) ConcatenateMagickString(tag,token,MaxTextExtent);
1322               (void) ConcatenateMagickString(tag,"/",MaxTextExtent);
1323             }
1324         }
1325         for (p=(char *) q; (*q != '<') && (*q != '\0'); q++) ;
1326         while (isspace((int) ((unsigned char) *p)) != 0)
1327           p++;
1328         q--;
1329         while ((isspace((int) ((unsigned char) *q)) != 0) && (q > p))
1330           q--;
1331         (void) CopyMagickString(message,p,MagickMin((size_t) (q-p+2),
1332           MaxTextExtent));
1333         locale_info=(LocaleInfo *) AcquireMagickMemory(sizeof(*locale_info));
1334         if (locale_info == (LocaleInfo *) NULL)
1335           ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1336         (void) ResetMagickMemory(locale_info,0,sizeof(*locale_info));
1337         locale_info->path=ConstantString(filename);
1338         locale_info->tag=ConstantString(tag);
1339         locale_info->message=ConstantString(message);
1340         locale_info->signature=MagickSignature;
1341         status=AddValueToSplayTree(locale_cache,locale_info->tag,locale_info);
1342         if (status == MagickFalse)
1343           (void) ThrowMagickException(exception,GetMagickModule(),
1344             ResourceLimitError,"MemoryAllocationFailed","`%s'",
1345             locale_info->tag);
1346         (void) ConcatenateMagickString(tag,message,MaxTextExtent);
1347         (void) ConcatenateMagickString(tag,"\n",MaxTextExtent);
1348         q++;
1349         continue;
1350       }
1351     if (LocaleCompare(keyword,"</message>") == 0)
1352       {
1353         ChopLocaleComponents(tag,2);
1354         (void) ConcatenateMagickString(tag,"/",MaxTextExtent);
1355         continue;
1356       }
1357     if (*keyword == '<')
1358       {
1359         /*
1360           Subpath element.
1361         */
1362         if (*(keyword+1) == '?')
1363           continue;
1364         if (*(keyword+1) == '/')
1365           {
1366             ChopLocaleComponents(tag,1);
1367             if (*tag != '\0')
1368               (void) ConcatenateMagickString(tag,"/",MaxTextExtent);
1369             continue;
1370           }
1371         token[strlen(token)-1]='\0';
1372         (void) CopyMagickString(token,token+1,MaxTextExtent);
1373         (void) ConcatenateMagickString(tag,token,MaxTextExtent);
1374         (void) ConcatenateMagickString(tag,"/",MaxTextExtent);
1375         continue;
1376       }
1377     GetMagickToken(q,(const char **) NULL,token);
1378     if (*token != '=')
1379       continue;
1380   }
1381   token=(char *) RelinquishMagickMemory(token);
1382   (void) SetFatalErrorHandler(fatal_handler);
1383   return(status);
1384 }
1385 \f
1386 /*
1387 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1388 %                                                                             %
1389 %                                                                             %
1390 %                                                                             %
1391 +   L o c a l e C o m p o n e n t G e n e s i s                               %
1392 %                                                                             %
1393 %                                                                             %
1394 %                                                                             %
1395 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1396 %
1397 %  LocaleComponentGenesis() instantiates the locale component.
1398 %
1399 %  The format of the LocaleComponentGenesis method is:
1400 %
1401 %      MagickBooleanType LocaleComponentGenesis(void)
1402 %
1403 */
1404 MagickPrivate MagickBooleanType LocaleComponentGenesis(void)
1405 {
1406   locale_semaphore=AcquireSemaphoreInfo();
1407   return(MagickTrue);
1408 }
1409 \f
1410 /*
1411 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1412 %                                                                             %
1413 %                                                                             %
1414 %                                                                             %
1415 +   L o c a l e C o m p o n e n t T e r m i n u s                             %
1416 %                                                                             %
1417 %                                                                             %
1418 %                                                                             %
1419 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1420 %
1421 %  LocaleComponentTerminus() destroys the locale component.
1422 %
1423 %  The format of the LocaleComponentTerminus method is:
1424 %
1425 %      LocaleComponentTerminus(void)
1426 %
1427 */
1428 MagickPrivate void LocaleComponentTerminus(void)
1429 {
1430   if (locale_semaphore == (SemaphoreInfo *) NULL)
1431     ActivateSemaphoreInfo(&locale_semaphore);
1432   LockSemaphoreInfo(locale_semaphore);
1433   if (locale_cache != (SplayTreeInfo *) NULL)
1434     locale_cache=DestroySplayTree(locale_cache);
1435 #if defined(MAGICKCORE_HAVE_STRTOD_L)
1436   DestroyCLocale();
1437 #endif
1438   UnlockSemaphoreInfo(locale_semaphore);
1439   RelinquishSemaphoreInfo(&locale_semaphore);
1440 }