]> granicus.if.org Git - imagemagick/blob - magick/locale.c
(no commit message)
[imagemagick] / magick / 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 %                                John Cristy                                  %
17 %                                 July 2003                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2010 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 "magick/studio.h"
43 #include "magick/blob.h"
44 #include "magick/client.h"
45 #include "magick/configure.h"
46 #include "magick/exception.h"
47 #include "magick/exception-private.h"
48 #include "magick/hashmap.h"
49 #include "magick/locale_.h"
50 #include "magick/log.h"
51 #include "magick/memory_.h"
52 #include "magick/semaphore.h"
53 #include "magick/splay-tree.h"
54 #include "magick/string_.h"
55 #include "magick/token.h"
56 #include "magick/utility.h"
57 #include "magick/xml-tree.h"
58 \f
59 /*
60   Define declarations.
61 */
62 #define LocaleFilename  "locale.xml"
63 #define MaxRecursionDepth  200
64 \f
65 /*
66   Static declarations.
67 */
68 static const char
69   *LocaleMap =
70     "<?xml version=\"1.0\"?>"
71     "<localemap>"
72     "  <locale name=\"C\">"
73     "    <Exception>"
74     "     <Message name=\"\">"
75     "     </Message>"
76     "    </Exception>"
77     "  </locale>"
78     "</localemap>";
79
80 static SemaphoreInfo
81   *locale_semaphore = (SemaphoreInfo *) NULL;
82
83 static SplayTreeInfo
84   *locale_list = (SplayTreeInfo *) NULL;
85
86 static volatile MagickBooleanType
87   instantiate_locale = MagickFalse;
88 \f
89 /*
90   Forward declarations.
91 */
92 static MagickBooleanType
93   InitializeLocaleList(ExceptionInfo *),
94   LoadLocaleLists(const char *,const char *,ExceptionInfo *);
95 \f
96 /*
97 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
98 %                                                                             %
99 %                                                                             %
100 %                                                                             %
101 %   D e s t r o y L o c a l e O p t i o n s                                   %
102 %                                                                             %
103 %                                                                             %
104 %                                                                             %
105 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
106 %
107 %  DestroyLocaleOptions() releases memory associated with an locale
108 %  messages.
109 %
110 %  The format of the DestroyProfiles method is:
111 %
112 %      LinkedListInfo *DestroyLocaleOptions(Image *image)
113 %
114 %  A description of each parameter follows:
115 %
116 %    o image: the image.
117 %
118 */
119
120 static void *DestroyOptions(void *message)
121 {
122   return(DestroyStringInfo((StringInfo *) message));
123 }
124
125 MagickExport LinkedListInfo *DestroyLocaleOptions(LinkedListInfo *messages)
126 {
127   assert(messages != (LinkedListInfo *) NULL);
128   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
129   return(DestroyLinkedList(messages,DestroyOptions));
130 }
131 \f
132 /*
133 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
134 %                                                                             %
135 %                                                                             %
136 %                                                                             %
137 +   G e t L o c a l e I n f o _                                               %
138 %                                                                             %
139 %                                                                             %
140 %                                                                             %
141 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
142 %
143 %  GetLocaleInfo_() searches the locale list for the specified tag and if
144 %  found returns attributes for that element.
145 %
146 %  The format of the GetLocaleInfo method is:
147 %
148 %      const LocaleInfo *GetLocaleInfo_(const char *tag,
149 %        ExceptionInfo *exception)
150 %
151 %  A description of each parameter follows:
152 %
153 %    o tag: the locale tag.
154 %
155 %    o exception: return any errors or warnings in this structure.
156 %
157 */
158 MagickExport const LocaleInfo *GetLocaleInfo_(const char *tag,
159   ExceptionInfo *exception)
160 {
161   assert(exception != (ExceptionInfo *) NULL);
162   if ((locale_list == (SplayTreeInfo *) NULL) ||
163       (instantiate_locale == MagickFalse))
164     if (InitializeLocaleList(exception) == MagickFalse)
165       return((const LocaleInfo *) NULL);
166   if ((locale_list == (SplayTreeInfo *) NULL) ||
167       (GetNumberOfNodesInSplayTree(locale_list) == 0))
168     return((const LocaleInfo *) NULL);
169   if ((tag == (const char *) NULL) || (LocaleCompare(tag,"*") == 0))
170     {
171       ResetSplayTreeIterator(locale_list);
172       return((const LocaleInfo *) GetNextValueInSplayTree(locale_list));
173     }
174   return((const LocaleInfo *) GetValueFromSplayTree(locale_list,tag));
175 }
176 \f
177 /*
178 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
179 %                                                                             %
180 %                                                                             %
181 %                                                                             %
182 %   G e t L o c a l e I n f o L i s t                                         %
183 %                                                                             %
184 %                                                                             %
185 %                                                                             %
186 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
187 %
188 %  GetLocaleInfoList() returns any locale messages that match the
189 %  specified pattern.
190 %
191 %  The format of the GetLocaleInfoList function is:
192 %
193 %      const LocaleInfo **GetLocaleInfoList(const char *pattern,
194 %        unsigned long *number_messages,ExceptionInfo *exception)
195 %
196 %  A description of each parameter follows:
197 %
198 %    o pattern: Specifies a pointer to a text string containing a pattern.
199 %
200 %    o number_messages:  This integer returns the number of locale messages in
201 %    the list.
202 %
203 %    o exception: return any errors or warnings in this structure.
204 %
205 */
206
207 #if defined(__cplusplus) || defined(c_plusplus)
208 extern "C" {
209 #endif
210
211 static int LocaleInfoCompare(const void *x,const void *y)
212 {
213   const LocaleInfo
214     **p,
215     **q;
216
217   p=(const LocaleInfo **) x,
218   q=(const LocaleInfo **) y;
219   if (LocaleCompare((*p)->path,(*q)->path) == 0)
220     return(LocaleCompare((*p)->tag,(*q)->tag));
221   return(LocaleCompare((*p)->path,(*q)->path));
222 }
223
224 #if defined(__cplusplus) || defined(c_plusplus)
225 }
226 #endif
227
228 MagickExport const LocaleInfo **GetLocaleInfoList(const char *pattern,
229   unsigned long *number_messages,ExceptionInfo *exception)
230 {
231   const LocaleInfo
232     **messages;
233
234   register const LocaleInfo
235     *p;
236
237   register long
238     i;
239
240   /*
241     Allocate locale list.
242   */
243   assert(pattern != (char *) NULL);
244   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
245   assert(number_messages != (unsigned long *) NULL);
246   *number_messages=0;
247   p=GetLocaleInfo_("*",exception);
248   if (p == (const LocaleInfo *) NULL)
249     return((const LocaleInfo **) NULL);
250   messages=(const LocaleInfo **) AcquireQuantumMemory((size_t)
251     GetNumberOfNodesInSplayTree(locale_list)+1UL,sizeof(*messages));
252   if (messages == (const LocaleInfo **) NULL)
253     return((const LocaleInfo **) NULL);
254   /*
255     Generate locale list.
256   */
257   LockSemaphoreInfo(locale_semaphore);
258   ResetSplayTreeIterator(locale_list);
259   p=(const LocaleInfo *) GetNextValueInSplayTree(locale_list);
260   for (i=0; p != (const LocaleInfo *) NULL; )
261   {
262     if ((p->stealth == MagickFalse) &&
263         (GlobExpression(p->tag,pattern,MagickTrue) != MagickFalse))
264       messages[i++]=p;
265     p=(const LocaleInfo *) GetNextValueInSplayTree(locale_list);
266   }
267   UnlockSemaphoreInfo(locale_semaphore);
268   qsort((void *) messages,(size_t) i,sizeof(*messages),LocaleInfoCompare);
269   messages[i]=(LocaleInfo *) NULL;
270   *number_messages=(unsigned long) i;
271   return(messages);
272 }
273 \f
274 /*
275 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
276 %                                                                             %
277 %                                                                             %
278 %                                                                             %
279 %   G e t L o c a l e L i s t                                                 %
280 %                                                                             %
281 %                                                                             %
282 %                                                                             %
283 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
284 %
285 %  GetLocaleList() returns any locale messages that match the specified
286 %  pattern.
287 %
288 %  The format of the GetLocaleList function is:
289 %
290 %      char **GetLocaleList(const char *pattern,unsigned long *number_messages,
291 %        Exceptioninfo *exception)
292 %
293 %  A description of each parameter follows:
294 %
295 %    o pattern: Specifies a pointer to a text string containing a pattern.
296 %
297 %    o number_messages:  This integer returns the number of messages in the
298 %      list.
299 %
300 %    o exception: return any errors or warnings in this structure.
301 %
302 */
303
304 #if defined(__cplusplus) || defined(c_plusplus)
305 extern "C" {
306 #endif
307
308 static int LocaleTagCompare(const void *x,const void *y)
309 {
310   register char
311     **p,
312     **q;
313
314   p=(char **) x;
315   q=(char **) y;
316   return(LocaleCompare(*p,*q));
317 }
318
319 #if defined(__cplusplus) || defined(c_plusplus)
320 }
321 #endif
322
323 MagickExport char **GetLocaleList(const char *pattern,
324   unsigned long *number_messages,ExceptionInfo *exception)
325 {
326   char
327     **messages;
328
329   register const LocaleInfo
330     *p;
331
332   register long
333     i;
334
335   /*
336     Allocate locale list.
337   */
338   assert(pattern != (char *) NULL);
339   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
340   assert(number_messages != (unsigned long *) NULL);
341   *number_messages=0;
342   p=GetLocaleInfo_("*",exception);
343   if (p == (const LocaleInfo *) NULL)
344     return((char **) NULL);
345   messages=(char **) AcquireQuantumMemory((size_t)
346     GetNumberOfNodesInSplayTree(locale_list)+1UL,sizeof(*messages));
347   if (messages == (char **) NULL)
348     return((char **) NULL);
349   LockSemaphoreInfo(locale_semaphore);
350   p=(const LocaleInfo *) GetNextValueInSplayTree(locale_list);
351   for (i=0; p != (const LocaleInfo *) NULL; )
352   {
353     if ((p->stealth == MagickFalse) &&
354         (GlobExpression(p->tag,pattern,MagickTrue) != MagickFalse))
355       messages[i++]=ConstantString(p->tag);
356     p=(const LocaleInfo *) GetNextValueInSplayTree(locale_list);
357   }
358   UnlockSemaphoreInfo(locale_semaphore);
359   qsort((void *) messages,(size_t) i,sizeof(*messages),LocaleTagCompare);
360   messages[i]=(char *) NULL;
361   *number_messages=(unsigned long) i;
362   return(messages);
363 }
364 \f
365 /*
366 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
367 %                                                                             %
368 %                                                                             %
369 %                                                                             %
370 %   G e t L o c a l e M e s s a g e                                           %
371 %                                                                             %
372 %                                                                             %
373 %                                                                             %
374 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
375 %
376 %  GetLocaleMessage() returns a message in the current locale that matches the
377 %  supplied tag.
378 %
379 %  The format of the GetLocaleMessage method is:
380 %
381 %      const char *GetLocaleMessage(const char *tag)
382 %
383 %  A description of each parameter follows:
384 %
385 %    o tag: Return a message that matches this tag in the current locale.
386 %
387 */
388 MagickExport const char *GetLocaleMessage(const char *tag)
389 {
390   char
391     name[MaxTextExtent];
392
393   const LocaleInfo
394     *locale_info;
395
396   ExceptionInfo
397     *exception;
398
399   if ((tag == (const char *) NULL) || (*tag == '\0'))
400     return(tag);
401   exception=AcquireExceptionInfo();
402   (void) FormatMagickString(name,MaxTextExtent,"%s/",tag);
403   locale_info=GetLocaleInfo_(name,exception);
404   exception=DestroyExceptionInfo(exception);
405   if (locale_info != (const LocaleInfo *) NULL)
406     return(locale_info->message);
407   return(tag);
408 }
409 \f
410 /*
411 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
412 %                                                                             %
413 %                                                                             %
414 %                                                                             %
415 %  G e t L o c a l e O p t i o n s                                            %
416 %                                                                             %
417 %                                                                             %
418 %                                                                             %
419 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
420 %
421 %  GetLocaleOptions() returns any Magick configuration messages associated
422 %  with the specified filename.
423 %
424 %  The format of the GetLocaleOptions method is:
425 %
426 %      LinkedListInfo *GetLocaleOptions(const char *filename,
427 %        ExceptionInfo *exception)
428 %
429 %  A description of each parameter follows:
430 %
431 %    o filename: the locale file tag.
432 %
433 %    o exception: return any errors or warnings in this structure.
434 %
435 */
436 MagickExport LinkedListInfo *GetLocaleOptions(const char *filename,
437   ExceptionInfo *exception)
438 {
439   char
440     path[MaxTextExtent];
441
442   const char
443     *element;
444
445   LinkedListInfo
446     *messages,
447     *paths;
448
449   StringInfo
450     *xml;
451
452   assert(filename != (const char *) NULL);
453   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
454   assert(exception != (ExceptionInfo *) NULL);
455   (void) CopyMagickString(path,filename,MaxTextExtent);
456   /*
457     Load XML from configuration files to linked-list.
458   */
459   messages=NewLinkedList(0);
460   paths=GetConfigurePaths(filename,exception);
461   if (paths != (LinkedListInfo *) NULL)
462     {
463       ResetLinkedListIterator(paths);
464       element=(const char *) GetNextValueInLinkedList(paths);
465       while (element != (const char *) NULL)
466       {
467         (void) FormatMagickString(path,MaxTextExtent,"%s%s",element,filename);
468         (void) LogMagickEvent(LocaleEvent,GetMagickModule(),
469           "Searching for locale file: \"%s\"",path);
470         xml=ConfigureFileToStringInfo(path);
471         if (xml != (StringInfo *) NULL)
472           (void) AppendValueToLinkedList(messages,xml);
473         element=(const char *) GetNextValueInLinkedList(paths);
474       }
475       paths=DestroyLinkedList(paths,RelinquishMagickMemory);
476     }
477 #if defined(MAGICKCORE_WINDOWS_SUPPORT)
478   {
479     char
480       *blob;
481
482     blob=(char *) NTResourceToBlob(filename);
483     if (blob != (char *) NULL)
484       {
485         xml=StringToStringInfo(blob);
486         (void) AppendValueToLinkedList(messages,xml);
487         blob=DestroyString(blob);
488       }
489   }
490 #endif
491   ResetLinkedListIterator(messages);
492   return(messages);
493 }
494 \f
495 /*
496 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
497 %                                                                             %
498 %                                                                             %
499 %                                                                             %
500 %   G e t L o c a l e V a l u e                                               %
501 %                                                                             %
502 %                                                                             %
503 %                                                                             %
504 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
505 %
506 %  GetLocaleValue() returns the message associated with the locale info.
507 %
508 %  The format of the GetLocaleValue method is:
509 %
510 %      const char *GetLocaleValue(const LocaleInfo *locale_info)
511 %
512 %  A description of each parameter follows:
513 %
514 %    o locale_info:  The locale info.
515 %
516 */
517 MagickExport const char *GetLocaleValue(const LocaleInfo *locale_info)
518 {
519   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
520   assert(locale_info != (LocaleInfo *) NULL);
521   assert(locale_info->signature == MagickSignature);
522   return(locale_info->message);
523 }
524 \f
525 /*
526 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
527 %                                                                             %
528 %                                                                             %
529 %                                                                             %
530 +   I n i t i a l i z e L o c a l e L i s t                                   %
531 %                                                                             %
532 %                                                                             %
533 %                                                                             %
534 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
535 %
536 %  InitializeLocaleList() initializes the locale list.
537 %
538 %  The format of the InitializeLocaleList method is:
539 %
540 %      MagickBooleanType InitializeLocaleList(ExceptionInfo *exception)
541 %
542 %  A description of each parameter follows.
543 %
544 %    o exception: return any errors or warnings in this structure.
545 %
546 */
547 static MagickBooleanType InitializeLocaleList(ExceptionInfo *exception)
548 {
549   if ((locale_list == (SplayTreeInfo *) NULL) &&
550       (instantiate_locale == MagickFalse))
551     {
552       if (locale_semaphore == (SemaphoreInfo *) NULL)
553         AcquireSemaphoreInfo(&locale_semaphore);
554       LockSemaphoreInfo(locale_semaphore);
555       if ((locale_list == (SplayTreeInfo *) NULL) &&
556           (instantiate_locale == MagickFalse))
557         {
558           char
559             *locale;
560
561           register const char
562             *p;
563
564           locale=(char *) NULL;
565           p=setlocale(LC_CTYPE,(const char *) NULL);
566           if (p != (const char *) NULL)
567             locale=ConstantString(p);
568           if (locale == (char *) NULL)
569             locale=GetEnvironmentValue("LC_ALL");
570           if (locale == (char *) NULL)
571             locale=GetEnvironmentValue("LC_MESSAGES");
572           if (locale == (char *) NULL)
573             locale=GetEnvironmentValue("LC_CTYPE");
574           if (locale == (char *) NULL)
575             locale=GetEnvironmentValue("LANG");
576           if (locale == (char *) NULL)
577             locale=ConstantString("C");
578           (void) LoadLocaleLists(LocaleFilename,locale,exception);
579           locale=DestroyString(locale);
580           instantiate_locale=MagickTrue;
581         }
582       UnlockSemaphoreInfo(locale_semaphore);
583     }
584   return(locale_list != (SplayTreeInfo *) NULL ? MagickTrue : MagickFalse);
585 }
586 \f
587 /*
588 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
589 %                                                                             %
590 %                                                                             %
591 %                                                                             %
592 %  L i s t L o c a l e I n f o                                                %
593 %                                                                             %
594 %                                                                             %
595 %                                                                             %
596 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
597 %
598 %  ListLocaleInfo() lists the locale info to a file.
599 %
600 %  The format of the ListLocaleInfo method is:
601 %
602 %      MagickBooleanType ListLocaleInfo(FILE *file,ExceptionInfo *exception)
603 %
604 %  A description of each parameter follows.
605 %
606 %    o file:  An pointer to a FILE.
607 %
608 %    o exception: return any errors or warnings in this structure.
609 %
610 */
611 MagickExport MagickBooleanType ListLocaleInfo(FILE *file,
612   ExceptionInfo *exception)
613 {
614   const char
615     *path;
616
617   const LocaleInfo
618     **locale_info;
619
620   register long
621     i;
622
623   unsigned long
624     number_messages;
625
626   if (file == (const FILE *) NULL)
627     file=stdout;
628   number_messages=0;
629   locale_info=GetLocaleInfoList("*",&number_messages,exception);
630   if (locale_info == (const LocaleInfo **) NULL)
631     return(MagickFalse);
632   path=(const char *) NULL;
633   for (i=0; i < (long) number_messages; i++)
634   {
635     if (locale_info[i]->stealth != MagickFalse)
636       continue;
637     if ((path == (const char *) NULL) ||
638         (LocaleCompare(path,locale_info[i]->path) != 0))
639       {
640         if (locale_info[i]->path != (char *) NULL)
641           (void) fprintf(file,"\nPath: %s\n\n",locale_info[i]->path);
642         (void) fprintf(file,"Tag/Message\n");
643         (void) fprintf(file,"-------------------------------------------------"
644           "------------------------------\n");
645       }
646     path=locale_info[i]->path;
647     (void) fprintf(file,"%s\n",locale_info[i]->tag);
648     if (locale_info[i]->message != (char *) NULL)
649       (void) fprintf(file,"  %s",locale_info[i]->message);
650     (void) fprintf(file,"\n");
651   }
652   (void) fflush(file);
653   locale_info=(const LocaleInfo **)
654     RelinquishMagickMemory((void *) locale_info);
655   return(MagickTrue);
656 }
657 \f
658 /*
659 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
660 %                                                                             %
661 %                                                                             %
662 %                                                                             %
663 +   L o a d L o c a l e L i s t                                               %
664 %                                                                             %
665 %                                                                             %
666 %                                                                             %
667 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
668 %
669 %  LoadLocaleList() loads the locale configuration file which provides a mapping
670 %  between locale attributes and a locale name.
671 %
672 %  The format of the LoadLocaleList method is:
673 %
674 %      MagickBooleanType LoadLocaleList(const char *xml,const char *filename,
675 %        const unsigned long depth,ExceptionInfo *exception)
676 %
677 %  A description of each parameter follows:
678 %
679 %    o xml:  The locale list in XML format.
680 %
681 %    o filename:  The locale list filename.
682 %
683 %    o depth: depth of <include /> statements.
684 %
685 %    o exception: return any errors or warnings in this structure.
686 %
687 */
688
689 static void ChopLocaleComponents(char *path,const unsigned long components)
690 {
691   long
692     count;
693
694   register char
695     *p;
696
697   if (*path == '\0')
698     return;
699   p=path+strlen(path)-1;
700   if (*p == '/')
701     *p='\0';
702   for (count=0; (count < (long) components) && (p > path); p--)
703     if (*p == '/')
704       {
705         *p='\0';
706         count++;
707       }
708   if (count < (long) components)
709     *path='\0';
710 }
711
712 static void *DestroyLocaleNode(void *locale_info)
713 {
714   register LocaleInfo
715     *p;
716
717   p=(LocaleInfo *) locale_info;
718   if (p->path != (char *) NULL)
719     p->path=DestroyString(p->path);
720   if (p->tag != (char *) NULL)
721     p->tag=DestroyString(p->tag);
722   if (p->message != (char *) NULL)
723     p->message=DestroyString(p->message);
724   return(RelinquishMagickMemory(p));
725 }
726
727 static void LocaleFatalErrorHandler(
728   const ExceptionType magick_unused(severity),
729   const char *reason,const char *description)
730 {
731   if (reason == (char *) NULL)
732     return;
733   (void) fprintf(stderr,"%s: %s",GetClientName(),reason);
734   if (description != (char *) NULL)
735     (void) fprintf(stderr," (%s)",description);
736   (void) fprintf(stderr,".\n");
737   (void) fflush(stderr);
738   exit(1);
739 }
740
741
742 static MagickBooleanType LoadLocaleList(const char *xml,const char *filename,
743   const char *locale,const unsigned long depth,ExceptionInfo *exception)
744 {
745   char
746     keyword[MaxTextExtent],
747     message[MaxTextExtent],
748     tag[MaxTextExtent],
749     *token;
750
751   const char
752     *q;
753
754   FatalErrorHandler
755     fatal_handler;
756
757   LocaleInfo
758     *locale_info;
759
760   MagickBooleanType
761     status;
762
763   register char
764     *p;
765
766   /*
767     Read the locale configure file.
768   */
769   (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
770     "Loading locale configure file \"%s\" ...",filename);
771   if (xml == (const char *) NULL)
772     return(MagickFalse);
773   if (locale_list == (SplayTreeInfo *) NULL)
774     {
775       locale_list=NewSplayTree(CompareSplayTreeString,(void *(*)(void *)) NULL,
776         DestroyLocaleNode);
777       if (locale_list == (SplayTreeInfo *) NULL)
778         return(MagickFalse);
779     }
780   status=MagickTrue;
781   locale_info=(LocaleInfo *) NULL;
782   *tag='\0';
783   *message='\0';
784   *keyword='\0';
785   fatal_handler=SetFatalErrorHandler(LocaleFatalErrorHandler);
786   token=AcquireString(xml);
787   for (q=(char *) xml; *q != '\0'; )
788   {
789     /*
790       Interpret XML.
791     */
792     GetMagickToken(q,&q,token);
793     if (*token == '\0')
794       break;
795     (void) CopyMagickString(keyword,token,MaxTextExtent);
796     if (LocaleNCompare(keyword,"<!DOCTYPE",9) == 0)
797       {
798         /*
799           Doctype element.
800         */
801         while ((LocaleNCompare(q,"]>",2) != 0) && (*q != '\0'))
802         {
803           GetMagickToken(q,&q,token);
804           while (isspace((int) ((unsigned char) *q)) != 0)
805             q++;
806         }
807         continue;
808       }
809     if (LocaleNCompare(keyword,"<!--",4) == 0)
810       {
811         /*
812           Comment element.
813         */
814         while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0'))
815         {
816           GetMagickToken(q,&q,token);
817           while (isspace((int) ((unsigned char) *q)) != 0)
818             q++;
819         }
820         continue;
821       }
822     if (LocaleCompare(keyword,"<include") == 0)
823       {
824         /*
825           Include element.
826         */
827         while (((*token != '/') && (*(token+1) != '>')) && (*q != '\0'))
828         {
829           (void) CopyMagickString(keyword,token,MaxTextExtent);
830           GetMagickToken(q,&q,token);
831           if (*token != '=')
832             continue;
833           GetMagickToken(q,&q,token);
834           if (LocaleCompare(keyword,"locale") == 0)
835             {
836               if (LocaleCompare(locale,token) != 0)
837                 break;
838               continue;
839             }
840           if (LocaleCompare(keyword,"file") == 0)
841             {
842               if (depth > 200)
843                 (void) ThrowMagickException(exception,GetMagickModule(),
844                   ConfigureError,"IncludeElementNestedTooDeeply","`%s'",token);
845               else
846                 {
847                   char
848                     path[MaxTextExtent],
849                     *xml;
850
851                   *path='\0';
852                   GetPathComponent(filename,HeadPath,path);
853                   if (*path != '\0')
854                     (void) ConcatenateMagickString(path,DirectorySeparator,
855                       MaxTextExtent);
856                   if (*token == *DirectorySeparator)
857                     (void) CopyMagickString(path,token,MaxTextExtent);
858                   else
859                     (void) ConcatenateMagickString(path,token,MaxTextExtent);
860                   xml=FileToString(path,~0,exception);
861                   if (xml != (char *) NULL)
862                     {
863                       status=LoadLocaleList(xml,path,locale,depth+1,exception);
864                       xml=(char *) RelinquishMagickMemory(xml);
865                     }
866                 }
867             }
868         }
869         continue;
870       }
871     if (LocaleCompare(keyword,"<locale") == 0)
872       {
873         /*
874           Locale element.
875         */
876         while ((*token != '>') && (*q != '\0'))
877         {
878           (void) CopyMagickString(keyword,token,MaxTextExtent);
879           GetMagickToken(q,&q,token);
880           if (*token != '=')
881             continue;
882           GetMagickToken(q,&q,token);
883         }
884         continue;
885       }
886     if (LocaleCompare(keyword,"</locale>") == 0)
887       {
888         ChopLocaleComponents(tag,1);
889         (void) ConcatenateMagickString(tag,"/",MaxTextExtent);
890         continue;
891       }
892     if (LocaleCompare(keyword,"<localemap>") == 0)
893       continue;
894     if (LocaleCompare(keyword,"</localemap>") == 0)
895       continue;
896     if (LocaleCompare(keyword,"<message") == 0)
897       {
898         /*
899           Message element.
900         */
901         while ((*token != '>') && (*q != '\0'))
902         {
903           (void) CopyMagickString(keyword,token,MaxTextExtent);
904           GetMagickToken(q,&q,token);
905           if (*token != '=')
906             continue;
907           GetMagickToken(q,&q,token);
908           if (LocaleCompare(keyword,"name") == 0)
909             {
910               (void) ConcatenateMagickString(tag,token,MaxTextExtent);
911               (void) ConcatenateMagickString(tag,"/",MaxTextExtent);
912             }
913         }
914         for (p=(char *) q; (*q != '<') && (*q != '\0'); q++) ;
915         while (isspace((int) ((unsigned char) *p)) != 0)
916           p++;
917         q--;
918         while ((isspace((int) ((unsigned char) *q)) != 0) && (q > p))
919           q--;
920         (void) CopyMagickString(message,p,(size_t) (q-p+2));
921         locale_info=(LocaleInfo *) AcquireAlignedMemory(1,sizeof(*locale_info));
922         if (locale_info == (LocaleInfo *) NULL)
923           ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
924         (void) ResetMagickMemory(locale_info,0,sizeof(*locale_info));
925         locale_info->path=ConstantString(filename);
926         locale_info->tag=ConstantString(tag);
927         locale_info->message=ConstantString(message);
928         locale_info->signature=MagickSignature;
929         status=AddValueToSplayTree(locale_list,locale_info->tag,locale_info);
930         if (status == MagickFalse)
931           (void) ThrowMagickException(exception,GetMagickModule(),
932             ResourceLimitError,"MemoryAllocationFailed","`%s'",
933             locale_info->tag);
934         (void) ConcatenateMagickString(tag,message,MaxTextExtent);
935         (void) ConcatenateMagickString(tag,"\n",MaxTextExtent);
936         q++;
937         continue;
938       }
939     if (LocaleCompare(keyword,"</message>") == 0)
940       {
941         ChopLocaleComponents(tag,2);
942         (void) ConcatenateMagickString(tag,"/",MaxTextExtent);
943         continue;
944       }
945     if (*keyword == '<')
946       {
947         /*
948           Subpath element.
949         */
950         if (*(keyword+1) == '?')
951           continue;
952         if (*(keyword+1) == '/')
953           {
954             ChopLocaleComponents(tag,1);
955             if (*tag != '\0')
956               (void) ConcatenateMagickString(tag,"/",MaxTextExtent);
957             continue;
958           }
959         token[strlen(token)-1]='\0';
960         (void) CopyMagickString(token,token+1,MaxTextExtent);
961         (void) ConcatenateMagickString(tag,token,MaxTextExtent);
962         (void) ConcatenateMagickString(tag,"/",MaxTextExtent);
963         continue;
964       }
965     GetMagickToken(q,(const char **) NULL,token);
966     if (*token != '=')
967       continue;
968   }
969   token=(char *) RelinquishMagickMemory(token);
970   (void) SetFatalErrorHandler(fatal_handler);
971   return(status);
972 }
973 \f
974 /*
975 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
976 %                                                                             %
977 %                                                                             %
978 %                                                                             %
979 %  L o a d L o c a l e L i s t s                                              %
980 %                                                                             %
981 %                                                                             %
982 %                                                                             %
983 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
984 %
985 %  LoadLocaleList() loads one or more locale configuration file which
986 %  provides a mapping between locale attributes and a locale tag.
987 %
988 %  The format of the LoadLocaleLists method is:
989 %
990 %      MagickBooleanType LoadLocaleLists(const char *filename,
991 %        ExceptionInfo *exception)
992 %
993 %  A description of each parameter follows:
994 %
995 %    o filename: the font file tag.
996 %
997 %    o locale: the actual locale.
998 %
999 %    o exception: return any errors or warnings in this structure.
1000 %
1001 */
1002 static MagickBooleanType LoadLocaleLists(const char *filename,
1003   const char *locale,ExceptionInfo *exception)
1004 {
1005 #if defined(MAGICKCORE_EMBEDDABLE_SUPPORT)
1006   return(LoadLocaleList(LocaleMap,"built-in",locale,0,exception));
1007 #else
1008   const StringInfo
1009     *option;
1010
1011   LinkedListInfo
1012     *options;
1013
1014   MagickStatusType
1015     status;
1016
1017   status=MagickFalse;
1018   options=GetLocaleOptions(filename,exception);
1019   option=(const StringInfo *) GetNextValueInLinkedList(options);
1020   while (option != (const StringInfo *) NULL)
1021   {
1022     status|=LoadLocaleList((const char *) GetStringInfoDatum(option),
1023       GetStringInfoPath(option),locale,0,exception);
1024     option=(const StringInfo *) GetNextValueInLinkedList(options);
1025   }
1026   options=DestroyLocaleOptions(options);
1027   if ((locale_list == (SplayTreeInfo *) NULL) ||
1028       (GetNumberOfNodesInSplayTree(locale_list) == 0))
1029     {
1030       options=GetLocaleOptions("english.xml",exception);
1031       option=(const StringInfo *) GetNextValueInLinkedList(options);
1032       while (option != (const StringInfo *) NULL)
1033       {
1034         status|=LoadLocaleList((const char *) GetStringInfoDatum(option),
1035           GetStringInfoPath(option),locale,0,exception);
1036         option=(const StringInfo *) GetNextValueInLinkedList(options);
1037       }
1038       options=DestroyLocaleOptions(options);
1039     }
1040   if ((locale_list == (SplayTreeInfo *) NULL) ||
1041       (GetNumberOfNodesInSplayTree(locale_list) == 0))
1042     status|=LoadLocaleList(LocaleMap,"built-in",locale,0,exception);
1043   return(status != 0 ? MagickTrue : MagickFalse);
1044 #endif
1045 }
1046 \f
1047 /*
1048 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1049 %                                                                             %
1050 %                                                                             %
1051 %                                                                             %
1052 +   L o c a l e C o m p o n e n t G e n e s i s                               %
1053 %                                                                             %
1054 %                                                                             %
1055 %                                                                             %
1056 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1057 %
1058 %  LocaleComponentGenesis() instantiates the locale component.
1059 %
1060 %  The format of the LocaleComponentGenesis method is:
1061 %
1062 %      MagickBooleanType LocaleComponentGenesis(void)
1063 %
1064 */
1065 MagickExport MagickBooleanType LocaleComponentGenesis(void)
1066 {
1067   AcquireSemaphoreInfo(&locale_semaphore);
1068   return(MagickTrue);
1069 }
1070 \f
1071 /*
1072 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1073 %                                                                             %
1074 %                                                                             %
1075 %                                                                             %
1076 +   L o c a l e C o m p o n e n t T e r m i n u s                             %
1077 %                                                                             %
1078 %                                                                             %
1079 %                                                                             %
1080 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1081 %
1082 %  LocaleComponentTerminus() destroys the locale component.
1083 %
1084 %  The format of the LocaleComponentTerminus method is:
1085 %
1086 %      LocaleComponentTerminus(void)
1087 %
1088 */
1089 MagickExport void LocaleComponentTerminus(void)
1090 {
1091   if (locale_semaphore == (SemaphoreInfo *) NULL)
1092     AcquireSemaphoreInfo(&locale_semaphore);
1093   LockSemaphoreInfo(locale_semaphore);
1094   if (locale_list != (SplayTreeInfo *) NULL)
1095     locale_list=DestroySplayTree(locale_list);
1096   instantiate_locale=MagickFalse;
1097   UnlockSemaphoreInfo(locale_semaphore);
1098   DestroySemaphoreInfo(&locale_semaphore);
1099 }