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