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