messages.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:20k
源码类别:

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * messages.c: messages interface
  3.  * This library provides an interface to the message queue to be used by other
  4.  * modules, especially intf modules. See vlc_config.h for output configuration.
  5.  *****************************************************************************
  6.  * Copyright (C) 1998-2005 the VideoLAN team
  7.  * $Id: a453bccd545d696eaef9d875e295779c31059268 $
  8.  *
  9.  * Authors: Vincent Seguin <seguin@via.ecp.fr>
  10.  *          Samuel Hocevar <sam@zoy.org>
  11.  *
  12.  * This program is free software; you can redistribute it and/or modify
  13.  * it under the terms of the GNU General Public License as published by
  14.  * the Free Software Foundation; either version 2 of the License, or
  15.  * (at your option) any later version.
  16.  *
  17.  * This program is distributed in the hope that it will be useful,
  18.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20.  * GNU General Public License for more details.
  21.  *
  22.  * You should have received a copy of the GNU General Public License
  23.  * along with this program; if not, write to the Free Software
  24.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  25.  *****************************************************************************/
  26. /*****************************************************************************
  27.  * Preamble
  28.  *****************************************************************************/
  29. #ifdef HAVE_CONFIG_H
  30. # include "config.h"
  31. #endif
  32. #include <vlc_common.h>
  33. #include <stdarg.h>                                       /* va_list for BSD */
  34. #ifdef HAVE_FCNTL_H
  35. #   include <fcntl.h>                  /* O_CREAT, O_TRUNC, O_WRONLY, O_SYNC */
  36. #endif
  37. #include <errno.h>                                                  /* errno */
  38. #ifdef WIN32
  39. #   include <vlc_network.h>          /* 'net_strerror' and 'WSAGetLastError' */
  40. #endif
  41. #ifdef HAVE_UNISTD_H
  42. #   include <unistd.h>                                   /* close(), write() */
  43. #endif
  44. #include <assert.h>
  45. #include <vlc_charset.h>
  46. #include "../libvlc.h"
  47. typedef struct
  48. {
  49.     int i_code;
  50.     char * psz_message;
  51. } msg_context_t;
  52. static void cleanup_msg_context (void *data)
  53. {
  54.     msg_context_t *ctx = data;
  55.     free (ctx->psz_message);
  56.     free (ctx);
  57. }
  58. static vlc_threadvar_t msg_context;
  59. static uintptr_t banks = 0;
  60. /*****************************************************************************
  61.  * Local macros
  62.  *****************************************************************************/
  63. #if defined(HAVE_VA_COPY)
  64. #   define vlc_va_copy(dest,src) va_copy(dest,src)
  65. #elif defined(HAVE___VA_COPY)
  66. #   define vlc_va_copy(dest,src) __va_copy(dest,src)
  67. #else
  68. #   define vlc_va_copy(dest,src) (dest)=(src)
  69. #endif
  70. #define QUEUE priv->msg_bank
  71. static inline msg_bank_t *libvlc_bank (libvlc_int_t *inst)
  72. {
  73.     return &(libvlc_priv (inst))->msg_bank;
  74. }
  75. /*****************************************************************************
  76.  * Local prototypes
  77.  *****************************************************************************/
  78. static void QueueMsg ( vlc_object_t *, int, const char *,
  79.                        const char *, va_list );
  80. static void PrintMsg ( vlc_object_t *, msg_item_t * );
  81. static vlc_mutex_t msg_stack_lock = VLC_STATIC_MUTEX;
  82. /**
  83.  * Initialize messages queues
  84.  * This function initializes all message queues
  85.  */
  86. void msg_Create (libvlc_int_t *p_libvlc)
  87. {
  88.     libvlc_priv_t *priv = libvlc_priv (p_libvlc);
  89.     msg_bank_t *bank = libvlc_bank (p_libvlc);
  90.     vlc_mutex_init (&bank->lock);
  91.     vlc_cond_init (&bank->wait);
  92.     vlc_dictionary_init( &priv->msg_enabled_objects, 0 );
  93.     priv->msg_all_objects_enabled = true;
  94.     QUEUE.i_sub = 0;
  95.     QUEUE.pp_sub = NULL;
  96. #ifdef UNDER_CE
  97.     QUEUE.logfile =
  98.         CreateFile( L"vlc-log.txt", GENERIC_WRITE,
  99.                     FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
  100.                     CREATE_ALWAYS, 0, NULL );
  101.     SetFilePointer( QUEUE.logfile, 0, NULL, FILE_END );
  102. #endif
  103.     vlc_mutex_lock( &msg_stack_lock );
  104.     if( banks++ == 0 )
  105.         vlc_threadvar_create( &msg_context, cleanup_msg_context );
  106.     vlc_mutex_unlock( &msg_stack_lock );
  107. }
  108. /**
  109.  * Object Printing selection
  110.  */
  111. static void const * kObjectPrintingEnabled = &kObjectPrintingEnabled;
  112. static void const * kObjectPrintingDisabled = &kObjectPrintingDisabled;
  113. void __msg_EnableObjectPrinting (vlc_object_t *p_this, char * psz_object)
  114. {
  115.     libvlc_priv_t *priv = libvlc_priv (p_this->p_libvlc);
  116.     vlc_mutex_lock( &QUEUE.lock );
  117.     if( !strcmp(psz_object, "all") )
  118.         priv->msg_all_objects_enabled = true;
  119.     else
  120.         vlc_dictionary_insert( &priv->msg_enabled_objects, psz_object, (void *)kObjectPrintingEnabled );
  121.     vlc_mutex_unlock( &QUEUE.lock );
  122. }
  123. void __msg_DisableObjectPrinting (vlc_object_t *p_this, char * psz_object)
  124. {
  125.     libvlc_priv_t *priv = libvlc_priv (p_this->p_libvlc);
  126.     vlc_mutex_lock( &QUEUE.lock );
  127.     if( !strcmp(psz_object, "all") )
  128.         priv->msg_all_objects_enabled = false;
  129.     else
  130.         vlc_dictionary_insert( &priv->msg_enabled_objects, psz_object, (void *)kObjectPrintingDisabled );
  131.     vlc_mutex_unlock( &QUEUE.lock );
  132. }
  133. /**
  134.  * Destroy the message queues
  135.  *
  136.  * This functions prints all messages remaining in the queues,
  137.  * then frees all the allocated resources
  138.  * No other messages interface functions should be called after this one.
  139.  */
  140. void msg_Destroy (libvlc_int_t *p_libvlc)
  141. {
  142.     libvlc_priv_t *priv = libvlc_priv (p_libvlc);
  143.     msg_bank_t *bank = libvlc_bank (p_libvlc);
  144.     if( QUEUE.i_sub )
  145.         msg_Err( p_libvlc, "stale interface subscribers (VLC might crash)" );
  146.     vlc_mutex_lock( &msg_stack_lock );
  147.     if( --banks == 0 )
  148.         vlc_threadvar_delete( &msg_context );
  149.     vlc_mutex_unlock( &msg_stack_lock );
  150. #ifdef UNDER_CE
  151.     CloseHandle( QUEUE.logfile );
  152. #endif
  153.     vlc_dictionary_clear( &priv->msg_enabled_objects, NULL, NULL );
  154.     vlc_cond_destroy (&bank->wait);
  155.     vlc_mutex_destroy (&bank->lock);
  156. }
  157. struct msg_subscription_t
  158. {
  159.     vlc_thread_t    thread;
  160.     libvlc_int_t   *instance;
  161.     msg_callback_t  func;
  162.     msg_cb_data_t  *opaque;
  163.     msg_item_t     *items[VLC_MSG_QSIZE];
  164.     unsigned        begin, end;
  165.     unsigned        overruns;
  166. };
  167. static void *msg_thread (void *data)
  168. {
  169.     msg_subscription_t *sub = data;
  170.     msg_bank_t *bank = libvlc_bank (sub->instance);
  171.     vlc_mutex_lock (&bank->lock);
  172.     for (;;)
  173.     {
  174.         /* Wait for messages */
  175.         assert (sub->begin < VLC_MSG_QSIZE);
  176.         assert (sub->end < VLC_MSG_QSIZE);
  177.         while (sub->begin != sub->end)
  178.         {
  179.             msg_item_t *msg = sub->items[sub->begin];
  180.             unsigned overruns = sub->overruns;
  181.             if (++sub->begin == VLC_MSG_QSIZE)
  182.                 sub->begin = 0;
  183.             sub->overruns = 0;
  184.             vlc_mutex_unlock (&bank->lock);
  185.             sub->func (sub->opaque, msg, overruns);
  186.             msg_Release (msg);
  187.             vlc_mutex_lock (&bank->lock);
  188.         }
  189.         mutex_cleanup_push (&bank->lock);
  190.         vlc_cond_wait (&bank->wait, &bank->lock);
  191.         vlc_cleanup_pop ();
  192.     }
  193.     assert (0);
  194. }
  195. /**
  196.  * Subscribe to the message queue.
  197.  * Whenever a message is emitted, a callback will be called.
  198.  * Callback invocation are serialized within a subscription.
  199.  *
  200.  * @param instance LibVLC instance to get messages from
  201.  * @param cb callback function
  202.  * @param opaque data for the callback function
  203.  * @return a subscription pointer, or NULL in case of failure
  204.  */
  205. msg_subscription_t *msg_Subscribe (libvlc_int_t *instance, msg_callback_t cb,
  206.                                    msg_cb_data_t *opaque)
  207. {
  208.     msg_subscription_t *sub = malloc (sizeof (*sub));
  209.     if (sub == NULL)
  210.         return NULL;
  211.     sub->instance = instance;
  212.     sub->func = cb;
  213.     sub->opaque = opaque;
  214.     sub->begin = sub->end = sub->overruns = 0;
  215.     if (vlc_clone (&sub->thread, msg_thread, sub, VLC_THREAD_PRIORITY_LOW))
  216.     {
  217.         free (sub);
  218.         return NULL;
  219.     }
  220.     msg_bank_t *bank = libvlc_bank (instance);
  221.     vlc_mutex_lock (&bank->lock);
  222.     TAB_APPEND (bank->i_sub, bank->pp_sub, sub);
  223.     vlc_mutex_unlock (&bank->lock);
  224.     return sub;
  225. }
  226. /**
  227.  * Unsubscribe from the message queue.
  228.  * This function waits for the message callback to return if needed.
  229.  */
  230. void msg_Unsubscribe (msg_subscription_t *sub)
  231. {
  232.     msg_bank_t *bank = libvlc_bank (sub->instance);
  233.     /* TODO: flush support? */
  234.     vlc_cancel (sub->thread);
  235.     vlc_mutex_lock (&bank->lock);
  236.     TAB_REMOVE (bank->i_sub, bank->pp_sub, sub);
  237.     vlc_mutex_unlock (&bank->lock);
  238.     vlc_join (sub->thread, NULL);
  239.     /* Free dangling (not flushed) messages. */
  240.     /* NOTE: no locking, only this thread can refer to the subscription now. */
  241.     while (sub->begin != sub->end)
  242.     {
  243.         msg_Release (sub->items[sub->begin]);
  244.         if (++sub->begin == VLC_MSG_QSIZE)
  245.             sub->begin = 0;
  246.     }
  247.     free (sub);
  248. }
  249. /*****************************************************************************
  250.  * __msg_*: print a message
  251.  *****************************************************************************
  252.  * These functions queue a message for later printing.
  253.  *****************************************************************************/
  254. void __msg_Generic( vlc_object_t *p_this, int i_type, const char *psz_module,
  255.                     const char *psz_format, ... )
  256. {
  257.     va_list args;
  258.     va_start( args, psz_format );
  259.     QueueMsg( p_this, i_type, psz_module, psz_format, args );
  260.     va_end( args );
  261. }
  262. void __msg_GenericVa( vlc_object_t *p_this, int i_type, const char *psz_module,
  263.                       const char *psz_format, va_list args )
  264. {
  265.     QueueMsg( p_this, i_type, psz_module, psz_format, args );
  266. }
  267. /**
  268.  * Destroys a message.
  269.  */
  270. static void msg_Free (gc_object_t *gc)
  271. {
  272.     msg_item_t *msg = vlc_priv (gc, msg_item_t);
  273.     free (msg->psz_module);
  274.     free (msg->psz_msg);
  275.     free (msg->psz_header);
  276.     free (msg);
  277. }
  278. /**
  279.  * Add a message to a queue
  280.  *
  281.  * This function provides basic functionnalities to other msg_* functions.
  282.  * It adds a message to a queue (after having printed all stored messages if it
  283.  * is full). If the message can't be converted to string in memory, it issues
  284.  * a warning.
  285.  */
  286. static void QueueMsg( vlc_object_t *p_this, int i_type, const char *psz_module,
  287.                       const char *psz_format, va_list _args )
  288. {
  289.     assert (p_this);
  290.     libvlc_priv_t *priv = libvlc_priv (p_this->p_libvlc);
  291.     int         i_header_size;             /* Size of the additionnal header */
  292.     vlc_object_t *p_obj;
  293.     char *       psz_str = NULL;                 /* formatted message string */
  294.     char *       psz_header = NULL;
  295.     va_list      args;
  296.     if( p_this->i_flags & OBJECT_FLAGS_QUIET ||
  297.         (p_this->i_flags & OBJECT_FLAGS_NODBG && i_type == VLC_MSG_DBG) )
  298.         return;
  299. #ifndef __GLIBC__
  300.     /* Expand %m to strerror(errno) - only once */
  301.     char buf[strlen( psz_format ) + 2001], *ptr;
  302.     strcpy( buf, psz_format );
  303.     ptr = (char*)buf;
  304.     psz_format = (const char*) buf;
  305.     for( ;; )
  306.     {
  307.         ptr = strchr( ptr, '%' );
  308.         if( ptr == NULL )
  309.             break;
  310.         if( ptr[1] == 'm' )
  311.         {
  312.             char errbuf[2001];
  313.             size_t errlen;
  314. #ifndef WIN32
  315.             strerror_r( errno, errbuf, 1001 );
  316. #else
  317.             int sockerr = WSAGetLastError( );
  318.             if( sockerr )
  319.             {
  320.                 strncpy( errbuf, net_strerror( sockerr ), 1001 );
  321.                 WSASetLastError( sockerr );
  322.             }
  323.             if ((sockerr == 0)
  324.              || (strcmp ("Unknown network stack error", errbuf) == 0))
  325.                 strncpy( errbuf, strerror( errno ), 1001 );
  326. #endif
  327.             errbuf[1000] = 0;
  328.             /* Escape '%' from the error string */
  329.             for( char *percent = strchr( errbuf, '%' );
  330.                  percent != NULL;
  331.                  percent = strchr( percent + 2, '%' ) )
  332.             {
  333.                 memmove( percent + 1, percent, strlen( percent ) + 1 );
  334.             }
  335.             errlen = strlen( errbuf );
  336.             memmove( ptr + errlen, ptr + 2, strlen( ptr + 2 ) + 1 );
  337.             memcpy( ptr, errbuf, errlen );
  338.             break; /* Only once, so we don't overflow */
  339.         }
  340.         /* Looks for conversion specifier... */
  341.         do
  342.             ptr++;
  343.         while( *ptr && ( strchr( "diouxXeEfFgGaAcspn%", *ptr ) == NULL ) );
  344.         if( *ptr )
  345.             ptr++; /* ...and skip it */
  346.     }
  347. #endif
  348.     /* Convert message to string  */
  349.     vlc_va_copy( args, _args );
  350.     if( vasprintf( &psz_str, psz_format, args ) == -1 )
  351.         psz_str = NULL;
  352.     va_end( args );
  353.     if( psz_str == NULL )
  354.     {
  355.         int canc = vlc_savecancel (); /* Do not print half of a message... */
  356. #ifdef __GLIBC__
  357.         fprintf( stderr, "main warning: can't store message (%m): " );
  358. #else
  359.         char psz_err[1001];
  360. #ifndef WIN32
  361.         /* we're not using GLIBC, so we are sure that the error description
  362.          * will be stored in the buffer we provide to strerror_r() */
  363.         strerror_r( errno, psz_err, 1001 );
  364. #else
  365.         strncpy( psz_err, strerror( errno ), 1001 );
  366. #endif
  367.         psz_err[1000] = '';
  368.         fprintf( stderr, "main warning: can't store message (%s): ", psz_err );
  369. #endif
  370.         vlc_va_copy( args, _args );
  371.         /* We should use utf8_vfprintf - but it calls malloc()... */
  372.         vfprintf( stderr, psz_format, args );
  373.         va_end( args );
  374.         fputs( "n", stderr );
  375.         vlc_restorecancel (canc);
  376.         return;
  377.     }
  378.     msg_item_t * p_item = malloc (sizeof (*p_item));
  379.     if (p_item == NULL)
  380.         return; /* Uho! */
  381.     vlc_gc_init (p_item, msg_Free);
  382.     p_item->psz_module = p_item->psz_msg = p_item->psz_header = NULL;
  383.     i_header_size = 0;
  384.     p_obj = p_this;
  385.     while( p_obj != NULL )
  386.     {
  387.         char *psz_old = NULL;
  388.         if( p_obj->psz_header )
  389.         {
  390.             i_header_size += strlen( p_obj->psz_header ) + 4;
  391.             if( psz_header )
  392.             {
  393.                 psz_old = strdup( psz_header );
  394.                 psz_header = (char*)realloc( psz_header, i_header_size );
  395.                 snprintf( psz_header, i_header_size , "[%s] %s",
  396.                           p_obj->psz_header, psz_old );
  397.             }
  398.             else
  399.             {
  400.                 psz_header = (char *)malloc( i_header_size );
  401.                 snprintf( psz_header, i_header_size, "[%s]",
  402.                           p_obj->psz_header );
  403.             }
  404.         }
  405.         free( psz_old );
  406.         p_obj = p_obj->p_parent;
  407.     }
  408.     /* Fill message information fields */
  409.     p_item->i_type =        i_type;
  410.     p_item->i_object_id =   (uintptr_t)p_this;
  411.     p_item->psz_object_type = p_this->psz_object_type;
  412.     p_item->psz_module =    strdup( psz_module );
  413.     p_item->psz_msg =       psz_str;
  414.     p_item->psz_header =    psz_header;
  415.     PrintMsg( p_this, p_item );
  416.     msg_bank_t *p_queue = &QUEUE;
  417.     vlc_mutex_lock( &p_queue->lock );
  418. #define bank p_queue
  419.     for (int i = 0; i < bank->i_sub; i++)
  420.     {
  421.         msg_subscription_t *sub = bank->pp_sub[i];
  422.         if ((sub->end + 1 - sub->begin) % VLC_MSG_QSIZE)
  423.         {
  424.             sub->items[sub->end++] = msg_Hold (p_item);
  425.             if (sub->end == VLC_MSG_QSIZE)
  426.                 sub->end = 0;
  427.         }
  428.         else
  429.             sub->overruns++;
  430.     }
  431.     vlc_cond_broadcast (&bank->wait);
  432.     vlc_mutex_unlock (&bank->lock);
  433.     msg_Release (p_item);
  434. }
  435. /*****************************************************************************
  436.  * PrintMsg: output a standard message item to stderr
  437.  *****************************************************************************
  438.  * Print a message to stderr, with colour formatting if needed.
  439.  *****************************************************************************/
  440. static void PrintMsg ( vlc_object_t * p_this, msg_item_t * p_item )
  441. {
  442. #   define COL(x)  "33[" #x ";1m"
  443. #   define RED     COL(31)
  444. #   define GREEN   COL(32)
  445. #   define YELLOW  COL(33)
  446. #   define WHITE   COL(0)
  447. #   define GRAY    "33[0m"
  448. #ifdef UNDER_CE
  449.     int i_dummy;
  450. #endif
  451.     static const char ppsz_type[4][9] = { "", " error", " warning", " debug" };
  452.     static const char ppsz_color[4][8] = { WHITE, RED, YELLOW, GRAY };
  453.     const char *psz_object;
  454.     libvlc_priv_t *priv = libvlc_priv (p_this->p_libvlc);
  455.     int i_type = p_item->i_type;
  456.     switch( i_type )
  457.     {
  458.         case VLC_MSG_ERR:
  459.             if( priv->i_verbose < 0 ) return;
  460.             break;
  461.         case VLC_MSG_INFO:
  462.             if( priv->i_verbose < 0 ) return;
  463.             break;
  464.         case VLC_MSG_WARN:
  465.             if( priv->i_verbose < 1 ) return;
  466.             break;
  467.         case VLC_MSG_DBG:
  468.             if( priv->i_verbose < 2 ) return;
  469.             break;
  470.     }
  471.     psz_object = p_item->psz_object_type;
  472.     void * val = vlc_dictionary_value_for_key( &priv->msg_enabled_objects,
  473.                                                p_item->psz_module );
  474.     if( val == kObjectPrintingDisabled )
  475.         return;
  476.     if( val == kObjectPrintingEnabled )
  477.         /* Allowed */;
  478.     else
  479.     {
  480.         val = vlc_dictionary_value_for_key( &priv->msg_enabled_objects,
  481.                                             psz_object );
  482.         if( val == kObjectPrintingDisabled )
  483.             return;
  484.         if( val == kObjectPrintingEnabled )
  485.             /* Allowed */;
  486.         else if( !priv->msg_all_objects_enabled )
  487.             return;
  488.     }
  489. #ifdef UNDER_CE
  490. #   define CE_WRITE(str) WriteFile( QUEUE.logfile, 
  491.                                     str, strlen(str), &i_dummy, NULL );
  492.     CE_WRITE( p_item->psz_module );
  493.     CE_WRITE( " " );
  494.     CE_WRITE( psz_object );
  495.     CE_WRITE( ppsz_type[i_type] );
  496.     CE_WRITE( ": " );
  497.     CE_WRITE( p_item->psz_msg );
  498.     CE_WRITE( "rn" );
  499.     FlushFileBuffers( QUEUE.logfile );
  500. #else
  501.     int canc = vlc_savecancel ();
  502.     /* Send the message to stderr */
  503.     utf8_fprintf( stderr, "[%s%p%s] %s%s%s %s%s: %s%s%sn",
  504.                   priv->b_color ? GREEN : "",
  505.                   (void *)p_item->i_object_id,
  506.                   priv->b_color ? GRAY : "",
  507.                   p_item->psz_header ? p_item->psz_header : "",
  508.                   p_item->psz_header ? " " : "",
  509.                   p_item->psz_module, psz_object,
  510.                   ppsz_type[i_type],
  511.                   priv->b_color ? ppsz_color[i_type] : "",
  512.                   p_item->psz_msg,
  513.                   priv->b_color ? GRAY : "" );
  514. #   if defined(WIN32)
  515.     fflush( stderr );
  516. #   endif
  517.     vlc_restorecancel (canc);
  518. #endif
  519. }
  520. static msg_context_t* GetContext(void)
  521. {
  522.     msg_context_t *p_ctx = vlc_threadvar_get( msg_context );
  523.     if( p_ctx == NULL )
  524.     {
  525.         p_ctx = malloc( sizeof( msg_context_t ) );
  526.         if( !p_ctx )
  527.             return NULL;
  528.         p_ctx->psz_message = NULL;
  529.         vlc_threadvar_set( msg_context, p_ctx );
  530.     }
  531.     return p_ctx;
  532. }
  533. void msg_StackDestroy (void *data)
  534. {
  535.     msg_context_t *p_ctx = data;
  536.     free (p_ctx->psz_message);
  537.     free (p_ctx);
  538. }
  539. void msg_StackSet( int i_code, const char *psz_message, ... )
  540. {
  541.     va_list ap;
  542.     msg_context_t *p_ctx = GetContext();
  543.     if( p_ctx == NULL )
  544.         return;
  545.     free( p_ctx->psz_message );
  546.     va_start( ap, psz_message );
  547.     if( vasprintf( &p_ctx->psz_message, psz_message, ap ) == -1 )
  548.         p_ctx->psz_message = NULL;
  549.     va_end( ap );
  550.     p_ctx->i_code = i_code;
  551. }
  552. void msg_StackAdd( const char *psz_message, ... )
  553. {
  554.     char *psz_tmp;
  555.     va_list ap;
  556.     msg_context_t *p_ctx = GetContext();
  557.     if( p_ctx == NULL )
  558.         return;
  559.     va_start( ap, psz_message );
  560.     if( vasprintf( &psz_tmp, psz_message, ap ) == -1 )
  561.         psz_tmp = NULL;
  562.     va_end( ap );
  563.     if( !p_ctx->psz_message )
  564.         p_ctx->psz_message = psz_tmp;
  565.     else
  566.     {
  567.         char *psz_new;
  568.         if( asprintf( &psz_new, "%s: %s", psz_tmp, p_ctx->psz_message ) == -1 )
  569.             psz_new = NULL;
  570.         free( p_ctx->psz_message );
  571.         p_ctx->psz_message = psz_new;
  572.         free( psz_tmp );
  573.     }
  574. }
  575. const char* msg_StackMsg( void )
  576. {
  577.     msg_context_t *p_ctx = GetContext();
  578.     assert( p_ctx );
  579.     return p_ctx->psz_message;
  580. }