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