Coverage Report

Created: 2026-07-20 07:19

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