]> granicus.if.org Git - imagemagick/blob - MagickCore/log.c
Added missing calls to xmlFreeDoc to fix memory leak reported in #1766.
[imagemagick] / MagickCore / log.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                             L       OOO    GGGG                             %
7 %                             L      O   O  G                                 %
8 %                             L      O   O  G GG                              %
9 %                             L      O   O  G   G                             %
10 %                             LLLLL   OOO    GGG                              %
11 %                                                                             %
12 %                                                                             %
13 %                             MagickCore Log Events                           %
14 %                                                                             %
15 %                               Software Design                               %
16 %                                    Cristy                                   %
17 %                                September 2002                               %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2019 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    https://imagemagick.org/script/license.php                               %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 \f
39 /*
40   Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/blob.h"
44 #include "MagickCore/client.h"
45 #include "MagickCore/configure.h"
46 #include "MagickCore/configure-private.h"
47 #include "MagickCore/exception.h"
48 #include "MagickCore/exception-private.h"
49 #include "MagickCore/linked-list.h"
50 #include "MagickCore/log.h"
51 #include "MagickCore/log-private.h"
52 #include "MagickCore/memory_.h"
53 #include "MagickCore/memory-private.h"
54 #include "MagickCore/nt-base-private.h"
55 #include "MagickCore/option.h"
56 #include "MagickCore/semaphore.h"
57 #include "MagickCore/timer.h"
58 #include "MagickCore/string_.h"
59 #include "MagickCore/string-private.h"
60 #include "MagickCore/thread_.h"
61 #include "MagickCore/thread-private.h"
62 #include "MagickCore/timer-private.h"
63 #include "MagickCore/token.h"
64 #include "MagickCore/utility.h"
65 #include "MagickCore/utility-private.h"
66 #include "MagickCore/version.h"
67 #include "MagickCore/xml-tree.h"
68 #include "MagickCore/xml-tree-private.h"
69 \f
70 /*
71   Define declarations.
72 */
73 #define LogFilename  "log.xml"
74 \f
75 /*
76   Typedef declarations.
77 */
78 typedef enum
79 {
80   UndefinedHandler = 0x0000,
81   NoHandler = 0x0000,
82   ConsoleHandler = 0x0001,
83   StdoutHandler = 0x0002,
84   StderrHandler = 0x0004,
85   FileHandler = 0x0008,
86   DebugHandler = 0x0010,
87   EventHandler = 0x0020,
88   MethodHandler = 0x0040
89 } LogHandlerType;
90
91 typedef struct _EventInfo
92 {
93   char
94     *name;
95
96   LogEventType
97     event;
98 } EventInfo;
99
100 typedef struct _HandlerInfo
101 {
102   const char
103     name[10];
104
105   LogHandlerType
106     handler;
107 } HandlerInfo;
108
109 struct _LogInfo
110 {
111   LogEventType
112     event_mask;
113
114   LogHandlerType
115     handler_mask;
116
117   char
118     *path,
119     *name,
120     *filename,
121     *format;
122
123   size_t
124     generations,
125     limit;
126
127   FILE
128     *file;
129
130   size_t
131     generation;
132
133   MagickBooleanType
134     append,
135     stealth;
136
137   TimerInfo
138     timer;
139
140   MagickLogMethod
141     method;
142
143   SemaphoreInfo
144     *event_semaphore;
145
146   size_t
147     signature;
148 };
149
150 typedef struct _LogMapInfo
151 {
152   const LogEventType
153     event_mask;
154
155   const LogHandlerType
156     handler_mask;
157
158   const char
159     *filename,
160     *format;
161 } LogMapInfo;
162 \f
163 /*
164   Static declarations.
165 */
166 static const HandlerInfo
167   LogHandlers[32] =
168   {
169     { "Console", ConsoleHandler },
170     { "Debug", DebugHandler },
171     { "Event", EventHandler },
172     { "File", FileHandler },
173     { "None", NoHandler },
174     { "Stderr", StderrHandler },
175     { "Stdout", StdoutHandler },
176     { "", UndefinedHandler },
177     { "", UndefinedHandler },
178     { "", UndefinedHandler },
179     { "", UndefinedHandler },
180     { "", UndefinedHandler },
181     { "", UndefinedHandler },
182     { "", UndefinedHandler },
183     { "", UndefinedHandler },
184     { "", UndefinedHandler },
185     { "", UndefinedHandler },
186     { "", UndefinedHandler },
187     { "", UndefinedHandler },
188     { "", UndefinedHandler },
189     { "", UndefinedHandler },
190     { "", UndefinedHandler },
191     { "", UndefinedHandler },
192     { "", UndefinedHandler },
193     { "", UndefinedHandler },
194     { "", UndefinedHandler },
195     { "", UndefinedHandler },
196     { "", UndefinedHandler },
197     { "", UndefinedHandler },
198     { "", UndefinedHandler },
199     { "", UndefinedHandler },
200     { "", UndefinedHandler }
201   };
202
203 static const LogMapInfo
204   LogMap[] =
205   {
206     { NoEvents, ConsoleHandler, "Magick-%g.log",
207       "%t %r %u %v %d %c[%p]: %m/%f/%l/%d\\n  %e" }
208   };
209
210 static char
211   log_name[MagickPathExtent] = "Magick";
212
213 static LinkedListInfo
214   *log_cache = (LinkedListInfo *) NULL;
215
216 static MagickBooleanType
217   event_logging = MagickFalse;
218
219 static SemaphoreInfo
220   *log_semaphore = (SemaphoreInfo *) NULL;
221 \f
222 /*
223   Forward declarations.
224 */
225 static LogHandlerType
226   ParseLogHandlers(const char *) magick_attribute((__pure__));
227
228 static LogInfo
229   *GetLogInfo(const char *,ExceptionInfo *);
230
231 static MagickBooleanType
232   IsLogCacheInstantiated(ExceptionInfo *) magick_attribute((__pure__)),
233   LoadLogCache(LinkedListInfo *,const char *,const char *,const size_t,
234     ExceptionInfo *);
235 \f
236 /*
237 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
238 %                                                                             %
239 %                                                                             %
240 %                                                                             %
241 %  A c q u i r e L o g C a c h e                                              %
242 %                                                                             %
243 %                                                                             %
244 %                                                                             %
245 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
246 %
247 %  AcquireLogCache() caches one or more log configurations which provides a
248 %  mapping between log attributes and log name.
249 %
250 %  The format of the AcquireLogCache method is:
251 %
252 %      LinkedListInfo *AcquireLogCache(const char *filename,
253 %        ExceptionInfo *exception)
254 %
255 %  A description of each parameter follows:
256 %
257 %    o filename: the log configuration filename.
258 %
259 %    o exception: return any errors or warnings in this structure.
260 %
261 */
262 static LinkedListInfo *AcquireLogCache(const char *filename,
263   ExceptionInfo *exception)
264 {
265   LinkedListInfo
266     *cache;
267
268   MagickStatusType
269     status;
270
271   register ssize_t
272     i;
273
274   /*
275     Load external log map.
276   */
277   cache=NewLinkedList(0);
278   status=MagickTrue;
279 #if !defined(MAGICKCORE_ZERO_CONFIGURATION_SUPPORT)
280   {
281     const StringInfo
282       *option;
283
284     LinkedListInfo
285       *options;
286
287     options=GetConfigureOptions(filename,exception);
288     option=(const StringInfo *) GetNextValueInLinkedList(options);
289     while (option != (const StringInfo *) NULL)
290     {
291       status&=LoadLogCache(cache,(const char *) GetStringInfoDatum(option),
292         GetStringInfoPath(option),0,exception);
293       option=(const StringInfo *) GetNextValueInLinkedList(options);
294     }
295     options=DestroyConfigureOptions(options);
296   }
297 #endif
298   /*
299     Load built-in log map.
300   */
301   for (i=0; i < (ssize_t) (sizeof(LogMap)/sizeof(*LogMap)); i++)
302   {
303     LogInfo
304       *log_info;
305
306     register const LogMapInfo
307       *p;
308
309     p=LogMap+i;
310     log_info=(LogInfo *) AcquireMagickMemory(sizeof(*log_info));
311     if (log_info == (LogInfo *) NULL)
312       {
313         (void) ThrowMagickException(exception,GetMagickModule(),
314           ResourceLimitError,"MemoryAllocationFailed","`%s'",p->filename);
315         continue;
316       }
317     (void) memset(log_info,0,sizeof(*log_info));
318     log_info->path=ConstantString("[built-in]");
319     GetTimerInfo((TimerInfo *) &log_info->timer);
320     log_info->event_mask=p->event_mask;
321     log_info->handler_mask=p->handler_mask;
322     log_info->filename=ConstantString(p->filename);
323     log_info->format=ConstantString(p->format);
324     log_info->signature=MagickCoreSignature;
325     status&=AppendValueToLinkedList(cache,log_info);
326     if (status == MagickFalse)
327       (void) ThrowMagickException(exception,GetMagickModule(),
328         ResourceLimitError,"MemoryAllocationFailed","`%s'",log_info->name);
329   }
330   return(cache);
331 }
332 \f
333 /*
334 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
335 %                                                                             %
336 %                                                                             %
337 %                                                                             %
338 %   C l o s e M a g i c k L o g                                               %
339 %                                                                             %
340 %                                                                             %
341 %                                                                             %
342 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
343 %
344 %  CloseMagickLog() closes the Magick log.
345 %
346 %  The format of the CloseMagickLog method is:
347 %
348 %      CloseMagickLog(void)
349 %
350 */
351 MagickExport void CloseMagickLog(void)
352 {
353   ExceptionInfo
354     *exception;
355
356   LogInfo
357     *log_info;
358
359   if (IsEventLogging() == MagickFalse)
360     return;
361   exception=AcquireExceptionInfo();
362   log_info=GetLogInfo("*",exception);
363   exception=DestroyExceptionInfo(exception);
364   LockSemaphoreInfo(log_semaphore);
365   if (log_info->file != (FILE *) NULL)
366     {
367       (void) FormatLocaleFile(log_info->file,"</log>\n");
368       (void) fclose(log_info->file);
369       log_info->file=(FILE *) NULL;
370     }
371   UnlockSemaphoreInfo(log_semaphore);
372 }
373 \f
374 /*
375 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
376 %                                                                             %
377 %                                                                             %
378 %                                                                             %
379 +   G e t L o g I n f o                                                       %
380 %                                                                             %
381 %                                                                             %
382 %                                                                             %
383 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
384 %
385 %  GetLogInfo() searches the log list for the specified name and if found
386 %  returns attributes for that log.
387 %
388 %  The format of the GetLogInfo method is:
389 %
390 %      LogInfo *GetLogInfo(const char *name,ExceptionInfo *exception)
391 %
392 %  A description of each parameter follows:
393 %
394 %    o name: the log name.
395 %
396 %    o exception: return any errors or warnings in this structure.
397 %
398 */
399 static LogInfo *GetLogInfo(const char *name,ExceptionInfo *exception)
400 {
401   register LogInfo
402     *p;
403
404   assert(exception != (ExceptionInfo *) NULL);
405   if (IsLogCacheInstantiated(exception) == MagickFalse)
406     return((LogInfo *) NULL);
407   /*
408     Search for log tag.
409   */
410   LockSemaphoreInfo(log_semaphore);
411   ResetLinkedListIterator(log_cache);
412   p=(LogInfo *) GetNextValueInLinkedList(log_cache);
413   if ((name == (const char *) NULL) || (LocaleCompare(name,"*") == 0))
414     {
415       UnlockSemaphoreInfo(log_semaphore);
416       return(p);
417     }
418   while (p != (LogInfo *) NULL)
419   {
420     if (LocaleCompare(name,p->name) == 0)
421       break;
422     p=(LogInfo *) GetNextValueInLinkedList(log_cache);
423   }
424   if (p != (LogInfo *) NULL)
425     (void) InsertValueInLinkedList(log_cache,0,
426       RemoveElementByValueFromLinkedList(log_cache,p));
427   UnlockSemaphoreInfo(log_semaphore);
428   return(p);
429 }
430 \f
431 /*
432 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
433 %                                                                             %
434 %                                                                             %
435 %                                                                             %
436 %   G e t L o g I n f o L i s t                                               %
437 %                                                                             %
438 %                                                                             %
439 %                                                                             %
440 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
441 %
442 %  GetLogInfoList() returns any logs that match the specified pattern.
443 %
444 %  The format of the GetLogInfoList function is:
445 %
446 %      const LogInfo **GetLogInfoList(const char *pattern,
447 %        size_t *number_preferences,ExceptionInfo *exception)
448 %
449 %  A description of each parameter follows:
450 %
451 %    o pattern: Specifies a pointer to a text string containing a pattern.
452 %
453 %    o number_preferences:  This integer returns the number of logs in the list.
454 %
455 %    o exception: return any errors or warnings in this structure.
456 %
457 */
458 #if defined(__cplusplus) || defined(c_plusplus)
459 extern "C" {
460 #endif
461
462 static int LogInfoCompare(const void *x,const void *y)
463 {
464   const LogInfo
465     **p,
466     **q;
467
468   p=(const LogInfo **) x,
469   q=(const LogInfo **) y;
470   if (LocaleCompare((*p)->path,(*q)->path) == 0)
471     return(LocaleCompare((*p)->name,(*q)->name));
472   return(LocaleCompare((*p)->path,(*q)->path));
473 }
474
475 #if defined(__cplusplus) || defined(c_plusplus)
476 }
477 #endif
478
479 MagickExport const LogInfo **GetLogInfoList(const char *pattern,
480   size_t *number_preferences,ExceptionInfo *exception)
481 {
482   const LogInfo
483     **preferences;
484
485   register const LogInfo
486     *p;
487
488   register ssize_t
489     i;
490
491   /*
492     Allocate log list.
493   */
494   assert(pattern != (char *) NULL);
495   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
496   assert(number_preferences != (size_t *) NULL);
497   *number_preferences=0;
498   p=GetLogInfo("*",exception);
499   if (p == (const LogInfo *) NULL)
500     return((const LogInfo **) NULL);
501   preferences=(const LogInfo **) AcquireQuantumMemory((size_t)
502     GetNumberOfElementsInLinkedList(log_cache)+1UL,sizeof(*preferences));
503   if (preferences == (const LogInfo **) NULL)
504     return((const LogInfo **) NULL);
505   /*
506     Generate log list.
507   */
508   LockSemaphoreInfo(log_semaphore);
509   ResetLinkedListIterator(log_cache);
510   p=(const LogInfo *) GetNextValueInLinkedList(log_cache);
511   for (i=0; p != (const LogInfo *) NULL; )
512   {
513     if ((p->stealth == MagickFalse) &&
514         (GlobExpression(p->name,pattern,MagickFalse) != MagickFalse))
515       preferences[i++]=p;
516     p=(const LogInfo *) GetNextValueInLinkedList(log_cache);
517   }
518   UnlockSemaphoreInfo(log_semaphore);
519   qsort((void *) preferences,(size_t) i,sizeof(*preferences),LogInfoCompare);
520   preferences[i]=(LogInfo *) NULL;
521   *number_preferences=(size_t) i;
522   return(preferences);
523 }
524 \f
525 /*
526 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
527 %                                                                             %
528 %                                                                             %
529 %                                                                             %
530 %   G e t L o g L i s t                                                       %
531 %                                                                             %
532 %                                                                             %
533 %                                                                             %
534 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
535 %
536 %  GetLogList() returns any logs that match the specified pattern.
537 %
538 %  The format of the GetLogList function is:
539 %
540 %      char **GetLogList(const char *pattern,size_t *number_preferences,
541 %        ExceptionInfo *exception)
542 %
543 %  A description of each parameter follows:
544 %
545 %    o pattern: Specifies a pointer to a text string containing a pattern.
546 %
547 %    o number_preferences:  This integer returns the number of logs in the list.
548 %
549 %    o exception: return any errors or warnings in this structure.
550 %
551 */
552
553 #if defined(__cplusplus) || defined(c_plusplus)
554 extern "C" {
555 #endif
556
557 static int LogCompare(const void *x,const void *y)
558 {
559   register const char
560     **p,
561     **q;
562
563   p=(const char **) x;
564   q=(const char **) y;
565   return(LocaleCompare(*p,*q));
566 }
567
568 #if defined(__cplusplus) || defined(c_plusplus)
569 }
570 #endif
571
572 MagickExport char **GetLogList(const char *pattern,size_t *number_preferences,
573   ExceptionInfo *exception)
574 {
575   char
576     **preferences;
577
578   register const LogInfo
579     *p;
580
581   register ssize_t
582     i;
583
584   /*
585     Allocate log list.
586   */
587   assert(pattern != (char *) NULL);
588   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
589   assert(number_preferences != (size_t *) NULL);
590   *number_preferences=0;
591   p=GetLogInfo("*",exception);
592   if (p == (const LogInfo *) NULL)
593     return((char **) NULL);
594   preferences=(char **) AcquireQuantumMemory((size_t)
595     GetNumberOfElementsInLinkedList(log_cache)+1UL,sizeof(*preferences));
596   if (preferences == (char **) NULL)
597     return((char **) NULL);
598   /*
599     Generate log list.
600   */
601   LockSemaphoreInfo(log_semaphore);
602   ResetLinkedListIterator(log_cache);
603   p=(const LogInfo *) GetNextValueInLinkedList(log_cache);
604   for (i=0; p != (const LogInfo *) NULL; )
605   {
606     if ((p->stealth == MagickFalse) &&
607         (GlobExpression(p->name,pattern,MagickFalse) != MagickFalse))
608       preferences[i++]=ConstantString(p->name);
609     p=(const LogInfo *) GetNextValueInLinkedList(log_cache);
610   }
611   UnlockSemaphoreInfo(log_semaphore);
612   qsort((void *) preferences,(size_t) i,sizeof(*preferences),LogCompare);
613   preferences[i]=(char *) NULL;
614   *number_preferences=(size_t) i;
615   return(preferences);
616 }
617 \f
618 /*
619 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
620 %                                                                             %
621 %                                                                             %
622 %                                                                             %
623 %   G e t L o g N a m e                                                       %
624 %                                                                             %
625 %                                                                             %
626 %                                                                             %
627 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
628 %
629 %  GetLogName() returns the current log name.
630 %
631 %  The format of the GetLogName method is:
632 %
633 %      const char *GetLogName(void)
634 %
635 */
636 MagickExport const char *GetLogName(void)
637 {
638   return(log_name);
639 }
640 \f
641 /*
642 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
643 %                                                                             %
644 %                                                                             %
645 %                                                                             %
646 +   I s L o g C a c h e I n s t a n t i a t e d                               %
647 %                                                                             %
648 %                                                                             %
649 %                                                                             %
650 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
651 %
652 %  IsLogCacheInstantiated() determines if the log list is instantiated.  If
653 %  not, it instantiates the list and returns it.
654 %
655 %  The format of the IsLogInstantiated method is:
656 %
657 %      MagickBooleanType IsLogCacheInstantiated(ExceptionInfo *exception)
658 %
659 %  A description of each parameter follows.
660 %
661 %    o exception: return any errors or warnings in this structure.
662 %
663 */
664
665 static inline void CheckEventLogging()
666 {
667   /*
668     Are we logging events?
669   */
670   if (IsLinkedListEmpty(log_cache) != MagickFalse)
671     event_logging=MagickFalse;
672   else
673     {
674       LogInfo
675         *p;
676
677       ResetLinkedListIterator(log_cache);
678       p=(LogInfo *) GetNextValueInLinkedList(log_cache);
679       event_logging=(p != (LogInfo *) NULL) && (p->event_mask != NoEvents) ?
680         MagickTrue: MagickFalse;
681     }
682 }
683
684 static MagickBooleanType IsLogCacheInstantiated(ExceptionInfo *exception)
685 {
686   if (log_cache == (LinkedListInfo *) NULL)
687     {
688       if (log_semaphore == (SemaphoreInfo *) NULL)
689         ActivateSemaphoreInfo(&log_semaphore);
690       LockSemaphoreInfo(log_semaphore);
691       if (log_cache == (LinkedListInfo *) NULL)
692         {
693           log_cache=AcquireLogCache(LogFilename,exception);
694           CheckEventLogging();
695         }
696       UnlockSemaphoreInfo(log_semaphore);
697     }
698   return(log_cache != (LinkedListInfo *) NULL ? MagickTrue : MagickFalse);
699 }
700 \f
701 /*
702 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
703 %                                                                             %
704 %                                                                             %
705 %                                                                             %
706 %  I s E v e n t L o g g i n g                                                %
707 %                                                                             %
708 %                                                                             %
709 %                                                                             %
710 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
711 %
712 %  IsEventLogging() returns MagickTrue if debug of events is enabled otherwise
713 %  MagickFalse.
714 %
715 %  The format of the IsEventLogging method is:
716 %
717 %      MagickBooleanType IsEventLogging(void)
718 %
719 */
720 MagickExport MagickBooleanType IsEventLogging(void)
721 {
722   return(event_logging);
723 }
724 \f
725 /*
726 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
727 %                                                                             %
728 %                                                                             %
729 %                                                                             %
730 %  L i s t L o g I n f o                                                      %
731 %                                                                             %
732 %                                                                             %
733 %                                                                             %
734 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
735 %
736 %  ListLogInfo() lists the log info to a file.
737 %
738 %  The format of the ListLogInfo method is:
739 %
740 %      MagickBooleanType ListLogInfo(FILE *file,ExceptionInfo *exception)
741 %
742 %  A description of each parameter follows.
743 %
744 %    o file:  An pointer to a FILE.
745 %
746 %    o exception: return any errors or warnings in this structure.
747 %
748 */
749 MagickExport MagickBooleanType ListLogInfo(FILE *file,ExceptionInfo *exception)
750 {
751 #define MegabytesToBytes(value) ((MagickSizeType) (value)*1024*1024)
752
753   const char
754     *path;
755
756   const LogInfo
757     **log_info;
758
759   register ssize_t
760     i;
761
762   size_t
763     number_aliases;
764
765   ssize_t
766     j;
767
768   if (file == (const FILE *) NULL)
769     file=stdout;
770   log_info=GetLogInfoList("*",&number_aliases,exception);
771   if (log_info == (const LogInfo **) NULL)
772     return(MagickFalse);
773   j=0;
774   path=(const char *) NULL;
775   for (i=0; i < (ssize_t) number_aliases; i++)
776   {
777     if (log_info[i]->stealth != MagickFalse)
778       continue;
779     if ((path == (const char *) NULL) ||
780         (LocaleCompare(path,log_info[i]->path) != 0))
781       {
782         size_t
783           length;
784
785         if (log_info[i]->path != (char *) NULL)
786           (void) FormatLocaleFile(file,"\nPath: %s\n\n",log_info[i]->path);
787         length=0;
788         for (j=0; j < (ssize_t) (8*sizeof(LogHandlerType)); j++)
789         {
790           size_t
791             mask;
792
793           if (*LogHandlers[j].name == '\0')
794             break;
795           mask=1;
796           mask<<=j;
797           if ((log_info[i]->handler_mask & mask) != 0)
798             {
799               (void) FormatLocaleFile(file,"%s ",LogHandlers[j].name);
800               length+=strlen(LogHandlers[j].name);
801             }
802         }
803         for (j=(ssize_t) length; j <= 12; j++)
804           (void) FormatLocaleFile(file," ");
805         (void) FormatLocaleFile(file," Generations     Limit  Format\n");
806         (void) FormatLocaleFile(file,"-----------------------------------------"
807           "--------------------------------------\n");
808       }
809     path=log_info[i]->path;
810     if (log_info[i]->filename != (char *) NULL)
811       {
812         (void) FormatLocaleFile(file,"%s",log_info[i]->filename);
813         for (j=(ssize_t) strlen(log_info[i]->filename); j <= 16; j++)
814           (void) FormatLocaleFile(file," ");
815       }
816     (void) FormatLocaleFile(file,"%9g  ",(double) log_info[i]->generations);
817     (void) FormatLocaleFile(file,"%8g   ",(double) log_info[i]->limit);
818     if (log_info[i]->format != (char *) NULL)
819       (void) FormatLocaleFile(file,"%s",log_info[i]->format);
820     (void) FormatLocaleFile(file,"\n");
821   }
822   (void) fflush(file);
823   log_info=(const LogInfo **) RelinquishMagickMemory((void *) log_info);
824   return(MagickTrue);
825 }
826 \f
827 /*
828 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
829 %                                                                             %
830 %                                                                             %
831 %                                                                             %
832 +   L o g C o m p o n e n t G e n e s i s                                     %
833 %                                                                             %
834 %                                                                             %
835 %                                                                             %
836 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
837 %
838 %  LogComponentGenesis() instantiates the log component.
839 %
840 %  The format of the LogComponentGenesis method is:
841 %
842 %      MagickBooleanType LogComponentGenesis(void)
843 %
844 */
845 MagickPrivate MagickBooleanType LogComponentGenesis(void)
846 {
847   ExceptionInfo
848     *exception;
849
850   if (log_semaphore == (SemaphoreInfo *) NULL)
851     log_semaphore=AcquireSemaphoreInfo();
852   exception=AcquireExceptionInfo();
853   (void) GetLogInfo("*",exception);
854   exception=DestroyExceptionInfo(exception);
855   return(MagickTrue);
856 }
857 \f
858 /*
859 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
860 %                                                                             %
861 %                                                                             %
862 %                                                                             %
863 +   L o g C o m p o n e n t T e r m i n u s                                   %
864 %                                                                             %
865 %                                                                             %
866 %                                                                             %
867 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
868 %
869 %  LogComponentTerminus() destroys the logging component.
870 %
871 %  The format of the LogComponentTerminus method is:
872 %
873 %      LogComponentTerminus(void)
874 %
875 */
876
877 static void *DestroyLogElement(void *log_info)
878 {
879   register LogInfo
880     *p;
881
882   p=(LogInfo *) log_info;
883   if (p->file != (FILE *) NULL)
884     {
885       (void) FormatLocaleFile(p->file,"</log>\n");
886       (void) fclose(p->file);
887       p->file=(FILE *) NULL;
888     }
889   if (p->format != (char *) NULL)
890     p->format=DestroyString(p->format);
891   if (p->path != (char *) NULL)
892     p->path=DestroyString(p->path);
893   if (p->filename != (char *) NULL)
894     p->filename=DestroyString(p->filename);
895   if (p->event_semaphore != (SemaphoreInfo *) NULL)
896     RelinquishSemaphoreInfo(&p->event_semaphore);
897   p=(LogInfo *) RelinquishMagickMemory(p);
898   return((void *) NULL);
899 }
900
901 MagickPrivate void LogComponentTerminus(void)
902 {
903   if (log_semaphore == (SemaphoreInfo *) NULL)
904     ActivateSemaphoreInfo(&log_semaphore);
905   LockSemaphoreInfo(log_semaphore);
906   if (log_cache != (LinkedListInfo *) NULL)
907     log_cache=DestroyLinkedList(log_cache,DestroyLogElement);
908   event_logging=MagickFalse;
909   UnlockSemaphoreInfo(log_semaphore);
910   RelinquishSemaphoreInfo(&log_semaphore);
911 }
912 \f
913 /*
914 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
915 %                                                                             %
916 %                                                                             %
917 %                                                                             %
918 %   L o g M a g i c k E v e n t                                               %
919 %                                                                             %
920 %                                                                             %
921 %                                                                             %
922 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
923 %
924 %  LogMagickEvent() logs an event as determined by the log configuration file.
925 %  If an error occurs, MagickFalse is returned otherwise MagickTrue.
926 %
927 %  The format of the LogMagickEvent method is:
928 %
929 %      MagickBooleanType LogMagickEvent(const LogEventType type,
930 %        const char *module,const char *function,const size_t line,
931 %        const char *format,...)
932 %
933 %  A description of each parameter follows:
934 %
935 %    o type: the event type.
936 %
937 %    o filename: the source module filename.
938 %
939 %    o function: the function name.
940 %
941 %    o line: the line number of the source module.
942 %
943 %    o format: the output format.
944 %
945 */
946 static char *TranslateEvent(const char *module,const char *function,
947   const size_t line,const char *domain,const char *event)
948 {
949   char
950     *text;
951
952   double
953     elapsed_time,
954     user_time;
955
956   ExceptionInfo
957     *exception;
958
959   LogInfo
960     *log_info;
961
962   register char
963     *q;
964
965   register const char
966     *p;
967
968   size_t
969     extent;
970
971   time_t
972     seconds;
973
974   exception=AcquireExceptionInfo();
975   log_info=(LogInfo *) GetLogInfo("*",exception);
976   exception=DestroyExceptionInfo(exception);
977   seconds=GetMagickTime();
978   elapsed_time=GetElapsedTime(&log_info->timer);
979   user_time=GetUserTime(&log_info->timer);
980   text=AcquireString(event);
981   if (log_info->format == (char *) NULL)
982     return(text);
983   extent=strlen(event)+MagickPathExtent;
984   if (LocaleCompare(log_info->format,"xml") == 0)
985     {
986       char
987         timestamp[MagickPathExtent];
988
989       /*
990         Translate event in "XML" format.
991       */
992       (void) FormatMagickTime(seconds,extent,timestamp);
993       (void) FormatLocaleString(text,extent,
994         "<entry>\n"
995         "  <timestamp>%s</timestamp>\n"
996         "  <elapsed-time>%lu:%02lu.%06lu</elapsed-time>\n"
997         "  <user-time>%0.3f</user-time>\n"
998         "  <process-id>%.20g</process-id>\n"
999         "  <thread-id>%.20g</thread-id>\n"
1000         "  <module>%s</module>\n"
1001         "  <function>%s</function>\n"
1002         "  <line>%.20g</line>\n"
1003         "  <domain>%s</domain>\n"
1004         "  <event>%s</event>\n"
1005         "</entry>",timestamp,(unsigned long) (elapsed_time/60.0),
1006         (unsigned long) floor(fmod(elapsed_time,60.0)),(unsigned long)
1007         (1000000.0*(elapsed_time-floor(elapsed_time))+0.5),user_time,
1008         (double) getpid(),(double) GetMagickThreadSignature(),module,function,
1009         (double) line,domain,event);
1010       return(text);
1011     }
1012   /*
1013     Translate event in "human readable" format.
1014   */
1015   q=text;
1016   for (p=log_info->format; *p != '\0'; p++)
1017   {
1018     *q='\0';
1019     if ((size_t) (q-text+MagickPathExtent) >= extent)
1020       {
1021         extent+=MagickPathExtent;
1022         text=(char *) ResizeQuantumMemory(text,extent+MagickPathExtent,
1023           sizeof(*text));
1024         if (text == (char *) NULL)
1025           return((char *) NULL);
1026         q=text+strlen(text);
1027       }
1028     /*
1029       The format of the log is defined by embedding special format characters:
1030
1031         %c   client name
1032         %d   domain
1033         %e   event
1034         %f   function
1035         %g   generation
1036         %i   thread id
1037         %l   line
1038         %m   module
1039         %n   log name
1040         %p   process id
1041         %r   real CPU time
1042         %t   wall clock time
1043         %u   user CPU time
1044         %v   version
1045         %%   percent sign
1046         \n   newline
1047         \r   carriage return
1048     */
1049     if ((*p == '\\') && (*(p+1) == 'r'))
1050       {
1051         *q++='\r';
1052         p++;
1053         continue;
1054       }
1055     if ((*p == '\\') && (*(p+1) == 'n'))
1056       {
1057         *q++='\n';
1058         p++;
1059         continue;
1060       }
1061     if (*p != '%')
1062       {
1063         *q++=(*p);
1064         continue;
1065       }
1066     p++;
1067     if (*p == '\0')
1068       break;
1069     switch (*p)
1070     {
1071       case 'c':
1072       {
1073         q+=CopyMagickString(q,GetClientName(),extent);
1074         break;
1075       }
1076       case 'd':
1077       {
1078         q+=CopyMagickString(q,domain,extent);
1079         break;
1080       }
1081       case 'e':
1082       {
1083         q+=CopyMagickString(q,event,extent);
1084         break;
1085       }
1086       case 'f':
1087       {
1088         q+=CopyMagickString(q,function,extent);
1089         break;
1090       }
1091       case 'g':
1092       {
1093         if (log_info->generations == 0)
1094           {
1095             (void) CopyMagickString(q,"0",extent);
1096             q++;
1097             break;
1098           }
1099         q+=FormatLocaleString(q,extent,"%.20g",(double) (log_info->generation %
1100           log_info->generations));
1101         break;
1102       }
1103       case 'i':
1104       {
1105         q+=FormatLocaleString(q,extent,"%.20g",(double)
1106           GetMagickThreadSignature());
1107         break;
1108       }
1109       case 'l':
1110       {
1111         q+=FormatLocaleString(q,extent,"%.20g",(double) line);
1112         break;
1113       }
1114       case 'm':
1115       {
1116         register const char
1117           *r;
1118
1119         for (r=module+strlen(module)-1; r > module; r--)
1120           if (*r == *DirectorySeparator)
1121             {
1122               r++;
1123               break;
1124             }
1125         q+=CopyMagickString(q,r,extent);
1126         break;
1127       }
1128       case 'n':
1129       {
1130         q+=CopyMagickString(q,GetLogName(),extent);
1131         break;
1132       }
1133       case 'p':
1134       {
1135         q+=FormatLocaleString(q,extent,"%.20g",(double) getpid());
1136         break;
1137       }
1138       case 'r':
1139       {
1140         q+=FormatLocaleString(q,extent,"%lu:%02lu.%03lu",(unsigned long)
1141           (elapsed_time/60.0),(unsigned long) floor(fmod(elapsed_time,60.0)),
1142           (unsigned long) (1000.0*(elapsed_time-floor(elapsed_time))+0.5));
1143         break;
1144       }
1145       case 't':
1146       {
1147         q+=FormatMagickTime(seconds,extent,q);
1148         break;
1149       }
1150       case 'u':
1151       {
1152         q+=FormatLocaleString(q,extent,"%0.3fu",user_time);
1153         break;
1154       }
1155       case 'v':
1156       {
1157         q+=CopyMagickString(q,MagickLibVersionText,extent);
1158         break;
1159       }
1160       case '%':
1161       {
1162         *q++=(*p);
1163         break;
1164       }
1165       default:
1166       {
1167         *q++='%';
1168         *q++=(*p);
1169         break;
1170       }
1171     }
1172   }
1173   *q='\0';
1174   return(text);
1175 }
1176
1177 static char *TranslateFilename(const LogInfo *log_info)
1178 {
1179   char
1180     *filename;
1181
1182   register char
1183     *q;
1184
1185   register const char
1186     *p;
1187
1188   size_t
1189     extent;
1190
1191   /*
1192     Translate event in "human readable" format.
1193   */
1194   assert(log_info != (LogInfo *) NULL);
1195   assert(log_info->filename != (char *) NULL);
1196   filename=AcquireString((char *) NULL);
1197   extent=MagickPathExtent;
1198   q=filename;
1199   for (p=log_info->filename; *p != '\0'; p++)
1200   {
1201     *q='\0';
1202     if ((size_t) (q-filename+MagickPathExtent) >= extent)
1203       {
1204         extent+=MagickPathExtent;
1205         filename=(char *) ResizeQuantumMemory(filename,extent+MagickPathExtent,
1206           sizeof(*filename));
1207         if (filename == (char *) NULL)
1208           return((char *) NULL);
1209         q=filename+strlen(filename);
1210       }
1211     /*
1212       The format of the filename is defined by embedding special format
1213       characters:
1214
1215         %c   client name
1216         %n   log name
1217         %p   process id
1218         %v   version
1219         %%   percent sign
1220     */
1221     if (*p != '%')
1222       {
1223         *q++=(*p);
1224         continue;
1225       }
1226     p++;
1227     if (*p == '\0')
1228       break;
1229     switch (*p)
1230     {
1231       case '\0':
1232       {
1233         p--;
1234         break;
1235       }
1236       case 'c':
1237       {
1238         q+=CopyMagickString(q,GetClientName(),extent);
1239         break;
1240       }
1241       case 'g':
1242       {
1243         if (log_info->generations == 0)
1244           {
1245             (void) CopyMagickString(q,"0",extent);
1246             q++;
1247             break;
1248           }
1249         q+=FormatLocaleString(q,extent,"%.20g",(double) (log_info->generation %
1250           log_info->generations));
1251         break;
1252       }
1253       case 'n':
1254       {
1255         q+=CopyMagickString(q,GetLogName(),extent);
1256         break;
1257       }
1258       case 'p':
1259       {
1260         q+=FormatLocaleString(q,extent,"%.20g",(double) getpid());
1261         break;
1262       }
1263       case 'v':
1264       {
1265         q+=CopyMagickString(q,MagickLibVersionText,extent);
1266         break;
1267       }
1268       case '%':
1269       {
1270         *q++=(*p);
1271         break;
1272       }
1273       default:
1274       {
1275         *q++='%';
1276         *q++=(*p);
1277         break;
1278       }
1279     }
1280   }
1281   *q='\0';
1282   return(filename);
1283 }
1284
1285 MagickExport MagickBooleanType LogMagickEventList(const LogEventType type,
1286   const char *module,const char *function,const size_t line,const char *format,
1287   va_list operands)
1288 {
1289   char
1290     event[MagickPathExtent],
1291     *text;
1292
1293   const char
1294     *domain;
1295
1296   ExceptionInfo
1297     *exception;
1298
1299   int
1300     n;
1301
1302   LogInfo
1303     *log_info;
1304
1305   exception=AcquireExceptionInfo();
1306   log_info=(LogInfo *) GetLogInfo("*",exception);
1307   exception=DestroyExceptionInfo(exception);
1308   if (log_info->event_semaphore == (SemaphoreInfo *) NULL)
1309     ActivateSemaphoreInfo(&log_info->event_semaphore);
1310   LockSemaphoreInfo(log_info->event_semaphore);
1311   if ((log_info->event_mask & type) == 0)
1312     {
1313       UnlockSemaphoreInfo(log_info->event_semaphore);
1314       return(MagickTrue);
1315     }
1316   domain=CommandOptionToMnemonic(MagickLogEventOptions,type);
1317 #if defined(MAGICKCORE_HAVE_VSNPRINTF)
1318   n=vsnprintf(event,MagickPathExtent,format,operands);
1319 #else
1320   n=vsprintf(event,format,operands);
1321 #endif
1322   if (n < 0)
1323     event[MagickPathExtent-1]='\0';
1324   text=TranslateEvent(module,function,line,domain,event);
1325   if (text == (char *) NULL)
1326     {
1327       (void) ContinueTimer((TimerInfo *) &log_info->timer);
1328       UnlockSemaphoreInfo(log_info->event_semaphore);
1329       return(MagickFalse);
1330     }
1331   if ((log_info->handler_mask & ConsoleHandler) != 0)
1332     {
1333       (void) FormatLocaleFile(stderr,"%s\n",text);
1334       (void) fflush(stderr);
1335     }
1336   if ((log_info->handler_mask & DebugHandler) != 0)
1337     {
1338 #if defined(MAGICKCORE_WINDOWS_SUPPORT)
1339       OutputDebugString(text);
1340       OutputDebugString("\n");
1341 #endif
1342     }
1343   if ((log_info->handler_mask & EventHandler) != 0)
1344     {
1345 #if defined(MAGICKCORE_WINDOWS_SUPPORT)
1346       (void) NTReportEvent(text,MagickFalse);
1347 #endif
1348     }
1349   if ((log_info->handler_mask & FileHandler) != 0)
1350     {
1351       struct stat
1352         file_info;
1353
1354       file_info.st_size=0;
1355       if (log_info->file != (FILE *) NULL)
1356         (void) fstat(fileno(log_info->file),&file_info);
1357       if (file_info.st_size > (MagickOffsetType) (1024*1024*log_info->limit))
1358         {
1359           (void) FormatLocaleFile(log_info->file,"</log>\n");
1360           (void) fclose(log_info->file);
1361           log_info->file=(FILE *) NULL;
1362         }
1363       if (log_info->file == (FILE *) NULL)
1364         {
1365           char
1366             *filename;
1367
1368           filename=TranslateFilename(log_info);
1369           if (filename == (char *) NULL)
1370             {
1371               (void) ContinueTimer((TimerInfo *) &log_info->timer);
1372               UnlockSemaphoreInfo(log_info->event_semaphore);
1373               return(MagickFalse);
1374             }
1375           log_info->append=IsPathAccessible(filename);
1376           log_info->file=fopen_utf8(filename,"ab");
1377           filename=(char  *) RelinquishMagickMemory(filename);
1378           if (log_info->file == (FILE *) NULL)
1379             {
1380               UnlockSemaphoreInfo(log_info->event_semaphore);
1381               return(MagickFalse);
1382             }
1383           log_info->generation++;
1384           if (log_info->append == MagickFalse)
1385             (void) FormatLocaleFile(log_info->file,"<?xml version=\"1.0\" "
1386               "encoding=\"UTF-8\" standalone=\"yes\"?>\n");
1387           (void) FormatLocaleFile(log_info->file,"<log>\n");
1388         }
1389       (void) FormatLocaleFile(log_info->file,"  <event>%s</event>\n",text);
1390       (void) fflush(log_info->file);
1391     }
1392   if ((log_info->handler_mask & MethodHandler) != 0)
1393     {
1394       if (log_info->method != (MagickLogMethod) NULL)
1395         log_info->method(type,text);
1396     }
1397   if ((log_info->handler_mask & StdoutHandler) != 0)
1398     {
1399       (void) FormatLocaleFile(stdout,"%s\n",text);
1400       (void) fflush(stdout);
1401     }
1402   if ((log_info->handler_mask & StderrHandler) != 0)
1403     {
1404       (void) FormatLocaleFile(stderr,"%s\n",text);
1405       (void) fflush(stderr);
1406     }
1407   text=(char  *) RelinquishMagickMemory(text);
1408   (void) ContinueTimer((TimerInfo *) &log_info->timer);
1409   UnlockSemaphoreInfo(log_info->event_semaphore);
1410   return(MagickTrue);
1411 }
1412
1413 MagickExport MagickBooleanType LogMagickEvent(const LogEventType type,
1414   const char *module,const char *function,const size_t line,
1415   const char *format,...)
1416 {
1417   va_list
1418     operands;
1419
1420   MagickBooleanType
1421     status;
1422
1423   if (IsEventLogging() == MagickFalse)
1424     return(MagickFalse);
1425   va_start(operands,format);
1426   status=LogMagickEventList(type,module,function,line,format,operands);
1427   va_end(operands);
1428   return(status);
1429 }
1430 \f
1431 /*
1432 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1433 %                                                                             %
1434 %                                                                             %
1435 %                                                                             %
1436 +   L o a d L o g C a c h e                                                   %
1437 %                                                                             %
1438 %                                                                             %
1439 %                                                                             %
1440 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1441 %
1442 %  LoadLogCache() loads the log configurations which provides a
1443 %  mapping between log attributes and log name.
1444 %
1445 %  The format of the LoadLogCache method is:
1446 %
1447 %      MagickBooleanType LoadLogCache(LinkedListInfo *cache,const char *xml,
1448 %        const char *filename,const size_t depth,ExceptionInfo *exception)
1449 %
1450 %  A description of each parameter follows:
1451 %
1452 %    o xml:  The log list in XML format.
1453 %
1454 %    o filename:  The log list filename.
1455 %
1456 %    o depth: depth of <include /> statements.
1457 %
1458 %    o exception: return any errors or warnings in this structure.
1459 %
1460 */
1461 static MagickBooleanType LoadLogCache(LinkedListInfo *cache,const char *xml,
1462   const char *filename,const size_t depth,ExceptionInfo *exception)
1463 {
1464   char
1465     keyword[MagickPathExtent],
1466     *token;
1467
1468   const char
1469     *q;
1470
1471   LogInfo
1472     *log_info = (LogInfo *) NULL;
1473
1474   MagickStatusType
1475     status;
1476
1477   size_t
1478     extent;
1479
1480   /*
1481     Load the log map file.
1482   */
1483   if (xml == (const char *) NULL)
1484     return(MagickFalse);
1485   status=MagickTrue;
1486   token=AcquireString(xml);
1487   extent=strlen(token)+MagickPathExtent;
1488   for (q=(const char *) xml; *q != '\0'; )
1489   {
1490     /*
1491       Interpret XML.
1492     */
1493     (void) GetNextToken(q,&q,extent,token);
1494     if (*token == '\0')
1495       break;
1496     (void) CopyMagickString(keyword,token,MagickPathExtent);
1497     if (LocaleNCompare(keyword,"<!DOCTYPE",9) == 0)
1498       {
1499         /*
1500           Doctype element.
1501         */
1502         while ((LocaleNCompare(q,"]>",2) != 0) && (*q != '\0'))
1503           (void) GetNextToken(q,&q,extent,token);
1504         continue;
1505       }
1506     if (LocaleNCompare(keyword,"<!--",4) == 0)
1507       {
1508         /*
1509           Comment element.
1510         */
1511         while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0'))
1512           (void) GetNextToken(q,&q,extent,token);
1513         continue;
1514       }
1515     if (LocaleCompare(keyword,"<include") == 0)
1516       {
1517         /*
1518           Include element.
1519         */
1520         while (((*token != '/') && (*(token+1) != '>')) && (*q != '\0'))
1521         {
1522           (void) CopyMagickString(keyword,token,MagickPathExtent);
1523           (void) GetNextToken(q,&q,extent,token);
1524           if (*token != '=')
1525             continue;
1526           (void) GetNextToken(q,&q,extent,token);
1527           if (LocaleCompare(keyword,"file") == 0)
1528             {
1529               if (depth > MagickMaxRecursionDepth)
1530                 (void) ThrowMagickException(exception,GetMagickModule(),
1531                   ConfigureError,"IncludeElementNestedTooDeeply","`%s'",token);
1532               else
1533                 {
1534                   char
1535                     path[MagickPathExtent],
1536                     *file_xml;
1537
1538                   GetPathComponent(filename,HeadPath,path);
1539                   if (*path != '\0')
1540                     (void) ConcatenateMagickString(path,DirectorySeparator,
1541                       MagickPathExtent);
1542                   if (*token == *DirectorySeparator)
1543                     (void) CopyMagickString(path,token,MagickPathExtent);
1544                   else
1545                     (void) ConcatenateMagickString(path,token,MagickPathExtent);
1546                   file_xml=FileToXML(path,~0UL);
1547                   if (file_xml != (char *) NULL)
1548                     {
1549                       status&=LoadLogCache(cache,file_xml,path,depth+1,
1550                         exception);
1551                       file_xml=DestroyString(file_xml);
1552                     }
1553                 }
1554             }
1555         }
1556         continue;
1557       }
1558     if (LocaleCompare(keyword,"<logmap>") == 0)
1559       {
1560         /*
1561           Allocate memory for the log list.
1562         */
1563         log_info=(LogInfo *) AcquireCriticalMemory(sizeof(*log_info));
1564         (void) memset(log_info,0,sizeof(*log_info));
1565         log_info->path=ConstantString(filename);
1566         GetTimerInfo((TimerInfo *) &log_info->timer);
1567         log_info->signature=MagickCoreSignature;
1568         continue;
1569       }
1570     if (log_info == (LogInfo *) NULL)
1571       continue;
1572     if (LocaleCompare(keyword,"</logmap>") == 0)
1573       {
1574         status=AppendValueToLinkedList(cache,log_info);
1575         if (status == MagickFalse)
1576           (void) ThrowMagickException(exception,GetMagickModule(),
1577             ResourceLimitError,"MemoryAllocationFailed","`%s'",filename);
1578         log_info=(LogInfo *) NULL;
1579         continue;
1580       }
1581     (void) GetNextToken(q,(const char **) NULL,extent,token);
1582     if (*token != '=')
1583       continue;
1584     (void) GetNextToken(q,&q,extent,token);
1585     (void) GetNextToken(q,&q,extent,token);
1586     switch (*keyword)
1587     {
1588       case 'E':
1589       case 'e':
1590       {
1591         if (LocaleCompare((char *) keyword,"events") == 0)
1592           {
1593             log_info->event_mask=(LogEventType) (log_info->event_mask |
1594               ParseCommandOption(MagickLogEventOptions,MagickTrue,token));
1595             break;
1596           }
1597         break;
1598       }
1599       case 'F':
1600       case 'f':
1601       {
1602         if (LocaleCompare((char *) keyword,"filename") == 0)
1603           {
1604             if (log_info->filename != (char *) NULL)
1605               log_info->filename=(char *)
1606                 RelinquishMagickMemory(log_info->filename);
1607             log_info->filename=ConstantString(token);
1608             break;
1609           }
1610         if (LocaleCompare((char *) keyword,"format") == 0)
1611           {
1612             if (log_info->format != (char *) NULL)
1613               log_info->format=(char *)
1614                 RelinquishMagickMemory(log_info->format);
1615             log_info->format=ConstantString(token);
1616             break;
1617           }
1618         break;
1619       }
1620       case 'G':
1621       case 'g':
1622       {
1623         if (LocaleCompare((char *) keyword,"generations") == 0)
1624           {
1625             if (LocaleCompare(token,"unlimited") == 0)
1626               {
1627                 log_info->generations=(~0UL);
1628                 break;
1629               }
1630             log_info->generations=StringToUnsignedLong(token);
1631             break;
1632           }
1633         break;
1634       }
1635       case 'L':
1636       case 'l':
1637       {
1638         if (LocaleCompare((char *) keyword,"limit") == 0)
1639           {
1640             if (LocaleCompare(token,"unlimited") == 0)
1641               {
1642                 log_info->limit=(~0UL);
1643                 break;
1644               }
1645             log_info->limit=StringToUnsignedLong(token);
1646             break;
1647           }
1648         break;
1649       }
1650       case 'O':
1651       case 'o':
1652       {
1653         if (LocaleCompare((char *) keyword,"output") == 0)
1654           {
1655             log_info->handler_mask=(LogHandlerType)
1656               (log_info->handler_mask | ParseLogHandlers(token));
1657             break;
1658           }
1659         break;
1660       }
1661       default:
1662         break;
1663     }
1664   }
1665   token=DestroyString(token);
1666   if (cache == (LinkedListInfo *) NULL)
1667     return(MagickFalse);
1668   return(status != 0 ? MagickTrue : MagickFalse);
1669 }
1670 \f
1671 /*
1672 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1673 %                                                                             %
1674 %                                                                             %
1675 %                                                                             %
1676 +   P a r s e L o g H a n d l e r s                                           %
1677 %                                                                             %
1678 %                                                                             %
1679 %                                                                             %
1680 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1681 %
1682 %  ParseLogHandlers() parses a string defining which handlers takes a log
1683 %  message and exports them.
1684 %
1685 %  The format of the ParseLogHandlers method is:
1686 %
1687 %      LogHandlerType ParseLogHandlers(const char *handlers)
1688 %
1689 %  A description of each parameter follows:
1690 %
1691 %    o handlers: one or more handlers separated by commas.
1692 %
1693 */
1694 static LogHandlerType ParseLogHandlers(const char *handlers)
1695 {
1696   LogHandlerType
1697     handler_mask;
1698
1699   register const char
1700     *p;
1701
1702   register ssize_t
1703     i;
1704
1705   size_t
1706     length;
1707
1708   handler_mask=NoHandler;
1709   for (p=handlers; p != (char *) NULL; p=strchr(p,','))
1710   {
1711     while ((*p != '\0') && ((isspace((int) ((unsigned char) *p)) != 0) ||
1712            (*p == ',')))
1713       p++;
1714     for (i=0; *LogHandlers[i].name != '\0'; i++)
1715     {
1716       length=strlen(LogHandlers[i].name);
1717       if (LocaleNCompare(p,LogHandlers[i].name,length) == 0)
1718         {
1719           handler_mask=(LogHandlerType) (handler_mask | LogHandlers[i].handler);
1720           break;
1721         }
1722     }
1723     if (*LogHandlers[i].name == '\0')
1724       return(UndefinedHandler);
1725   }
1726   return(handler_mask);
1727 }
1728 \f
1729 /*
1730 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1731 %                                                                             %
1732 %                                                                             %
1733 %                                                                             %
1734 %   S e t L o g E v e n t M a s k                                             %
1735 %                                                                             %
1736 %                                                                             %
1737 %                                                                             %
1738 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1739 %
1740 %  SetLogEventMask() accepts a list that determines which events to log.  All
1741 %  other events are ignored.  By default, no debug is enabled.  This method
1742 %  returns the previous log event mask.
1743 %
1744 %  The format of the SetLogEventMask method is:
1745 %
1746 %      LogEventType SetLogEventMask(const char *events)
1747 %
1748 %  A description of each parameter follows:
1749 %
1750 %    o events: log these events.
1751 %
1752 */
1753 MagickExport LogEventType SetLogEventMask(const char *events)
1754 {
1755   ExceptionInfo
1756     *exception;
1757
1758   LogInfo
1759     *log_info;
1760
1761   ssize_t
1762     option;
1763
1764   exception=AcquireExceptionInfo();
1765   log_info=(LogInfo *) GetLogInfo("*",exception);
1766   exception=DestroyExceptionInfo(exception);
1767   option=ParseCommandOption(MagickLogEventOptions,MagickTrue,events);
1768   LockSemaphoreInfo(log_semaphore);
1769   log_info=(LogInfo *) GetValueFromLinkedList(log_cache,0);
1770   log_info->event_mask=(LogEventType) option;
1771   if (option == -1)
1772     log_info->event_mask=UndefinedEvents;
1773   CheckEventLogging();
1774   UnlockSemaphoreInfo(log_semaphore);
1775   return(log_info->event_mask);
1776 }
1777 \f
1778 /*
1779 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1780 %                                                                             %
1781 %                                                                             %
1782 %                                                                             %
1783 %   S e t L o g F o r m a t                                                   %
1784 %                                                                             %
1785 %                                                                             %
1786 %                                                                             %
1787 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1788 %
1789 %  SetLogFormat() sets the format for the "human readable" log record.
1790 %
1791 %  The format of the LogMagickFormat method is:
1792 %
1793 %      SetLogFormat(const char *format)
1794 %
1795 %  A description of each parameter follows:
1796 %
1797 %    o format: the log record format.
1798 %
1799 */
1800 MagickExport void SetLogFormat(const char *format)
1801 {
1802   LogInfo
1803     *log_info;
1804
1805   ExceptionInfo
1806     *exception;
1807
1808   exception=AcquireExceptionInfo();
1809   log_info=(LogInfo *) GetLogInfo("*",exception);
1810   exception=DestroyExceptionInfo(exception);
1811   LockSemaphoreInfo(log_semaphore);
1812   if (log_info->format != (char *) NULL)
1813     log_info->format=DestroyString(log_info->format);
1814   log_info->format=ConstantString(format);
1815   UnlockSemaphoreInfo(log_semaphore);
1816 }
1817 \f
1818 /*
1819 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1820 %                                                                             %
1821 %                                                                             %
1822 %                                                                             %
1823 %   S e t L o g M e t h o d                                                   %
1824 %                                                                             %
1825 %                                                                             %
1826 %                                                                             %
1827 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1828 %
1829 %  SetLogMethod() sets the method that will be called when an event is logged.
1830 %
1831 %  The format of the SetLogMethod method is:
1832 %
1833 %      void SetLogMethod(MagickLogMethod method)
1834 %
1835 %  A description of each parameter follows:
1836 %
1837 %    o method: pointer to a method that will be called when LogMagickEvent is
1838 %      being called.
1839 %
1840 */
1841 MagickExport void SetLogMethod(MagickLogMethod method)
1842 {
1843   ExceptionInfo
1844     *exception;
1845
1846   LogInfo
1847     *log_info;
1848
1849   exception=AcquireExceptionInfo();
1850   log_info=(LogInfo *) GetLogInfo("*",exception);
1851   exception=DestroyExceptionInfo(exception);
1852   LockSemaphoreInfo(log_semaphore);
1853   log_info=(LogInfo *) GetValueFromLinkedList(log_cache,0);
1854   log_info->handler_mask=(LogHandlerType) (log_info->handler_mask |
1855     MethodHandler);
1856   log_info->method=method;
1857   UnlockSemaphoreInfo(log_semaphore);
1858 }
1859 \f
1860 /*
1861 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1862 %                                                                             %
1863 %                                                                             %
1864 %                                                                             %
1865 %   S e t L o g N a m e                                                       %
1866 %                                                                             %
1867 %                                                                             %
1868 %                                                                             %
1869 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1870 %
1871 %  SetLogName() sets the log name and returns it.
1872 %
1873 %  The format of the SetLogName method is:
1874 %
1875 %      const char *SetLogName(const char *name)
1876 %
1877 %  A description of each parameter follows:
1878 %
1879 %    o log_name: SetLogName() returns the current client name.
1880 %
1881 %    o name: Specifies the new client name.
1882 %
1883 */
1884 MagickExport const char *SetLogName(const char *name)
1885 {
1886   if ((name != (char *) NULL) && (*name != '\0'))
1887     (void) CopyMagickString(log_name,name,MagickPathExtent);
1888   return(log_name);
1889 }