[svn-commits] russell: branch russell/chan_console r81444 - in /team/russell/chan_console: ...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Tue Sep 4 12:55:11 CDT 2007


Author: russell
Date: Tue Sep  4 12:55:11 2007
New Revision: 81444

URL: http://svn.digium.com/view/asterisk?view=rev&rev=81444
Log:
* Completely remove the code that manually generated sounds in chan_console.
  Some already have been, but they will all be replaced by letting the core use
  the indications API to generate the tones.  This simplifies the code and also
  allows for localized tones.
* convert the module to ast_debug() and ast_verb()
* Add more comments, most notably explaining why the read() callback should
  never be called.  (The same logic applies to chan_iax2, actually)

Modified:
    team/russell/chan_console/channels/chan_console.c
    team/russell/chan_console/include/asterisk/logger.h
    team/russell/chan_console/main/cli.c

Modified: team/russell/chan_console/channels/chan_console.c
URL: http://svn.digium.com/view/asterisk/team/russell/chan_console/channels/chan_console.c?view=diff&rev=81444&r1=81443&r2=81444
==============================================================================
--- team/russell/chan_console/channels/chan_console.c (original)
+++ team/russell/chan_console/channels/chan_console.c Tue Sep  4 12:55:11 2007
@@ -76,11 +76,6 @@
 #include "asterisk/musiconhold.h"
 #include "asterisk/callerid.h"
 
-#include "busy.h"
-#include "ringtone.h"
-#include "ring10.h"
-#include "answer.h"
-
 /*! 
  * \brief The sample rate to request from PortAudio 
  *
@@ -115,32 +110,11 @@
 #define MAX(a,b) ((a) > (b) ? (a) : (b))
 #endif
 
+/*! \brief Dance, Kirby, Dance! */
 #define V_BEGIN " --- <(\"<) --- "
 #define V_END   " --- (>\")> ---\n"
 
 static const char config_file[] = "console.conf";
-
-/*!
- * Each sound is made of 'datalen' samples of sound, repeated as needed to
- * generate 'samplen' samples of data, then followed by 'silencelen' samples
- * of silence. The loop is repeated if 'repeat' is set.
- */
-static struct sound {
-	const int ind;
-	const char *desc;
-	const short *data;
-	const int datalen;
-	const int samplen;
-	const int silencelen;
-	const int repeat;
-} sounds[] = {
-	{ AST_CONTROL_RINGING,    "RINGING",    ringtone, sizeof(ringtone) / 2, 16000, 32000, 1 },
-	{ AST_CONTROL_BUSY,       "BUSY",       busy,     sizeof(busy) / 2,     4000,  4000,  1 },
-	{ AST_CONTROL_CONGESTION, "CONGESTION", busy,     sizeof(busy) / 2,     2000,  2000,  1 },
-	{ AST_CONTROL_RING,       "RING10",     ring10,   sizeof(ring10) / 2,   16000, 32000, 1 },
-	{ AST_CONTROL_ANSWER,     "ANSWER",     answer,   sizeof(answer) / 2,   2200,  0,     0 },
-	{ -1, NULL, 0, 0, 0, 0 },	/* end marker */
-};
 
 static struct console_pvt {
 	AST_DECLARE_STRING_FIELDS(
@@ -173,8 +147,6 @@
 	char write_buf[NUM_SAMPLES * 2];
 	/*! The index for the next write into write_buf */
 	unsigned int write_dst;
-	/*! The current index into the sounds array, -1 means none */
-	int cursound;
 	/*! The number of samples sent for the current sound, mod the total number
 	 *  of samples for the sound plus trailing silence. */
 	unsigned int sampsent;
@@ -199,7 +171,6 @@
 	AST_LIST_HEAD_NOLOCK(, ast_frame) frames;
 } console_pvt = {
 	.lock = AST_MUTEX_INIT_VALUE,
-	.cursound = -1,
 };
 
 /*! Global jitterbuffer configuration - by default, jb is disabled */
@@ -254,7 +225,7 @@
 		return 0;
 
 	pvt->streamstate = 1;
-	ast_log(LOG_DEBUG, "Starting stream\n");
+	ast_debug(1, "Starting stream\n");
 
 	res = Pa_OpenDefaultStream(&pvt->stream, INPUT_CHANNELS, OUTPUT_CHANNELS, 
 		paInt16, SAMPLE_RATE, NUM_SAMPLES, console_callback, NULL);
@@ -353,56 +324,33 @@
 
 static int console_digit_begin(struct ast_channel *c, char digit)
 {
-	ast_verbose(V_BEGIN "Console Received Beginning of Digit %c" V_END, digit);
+	ast_verb(1, V_BEGIN "Console Received Beginning of Digit %c" V_END, digit);
+
+	return -1; /* non-zero to request inband audio */
+}
+
+static int console_digit_end(struct ast_channel *c, char digit, unsigned int duration)
+{
+	ast_verb(1, V_BEGIN "Console Received End of Digit %c (duration %u)" V_END, 
+		digit, duration);
+
+	return -1; /* non-zero to request inband audio */
+}
+
+static int console_text(struct ast_channel *c, const char *text)
+{
+	ast_verb(1, V_BEGIN "Console Received Text '%s'" V_END, text);
 
 	return 0;
 }
 
-static int console_digit_end(struct ast_channel *c, char digit, unsigned int duration)
-{
-	ast_verbose(V_BEGIN "Console Received End of Digit %c (duration %u)" V_END, 
-		digit, duration);
-
-	return 0;
-}
-
-static int console_text(struct ast_channel *c, const char *text)
-{
-	ast_verbose(V_BEGIN "Console Received Text '%s'" V_END, text);
-
-	return 0;
-}
-
-static void set_cursound(struct console_pvt *pvt, int sound)
-{
-	unsigned int i = 0;
-
-	if (sound == -1) {
-		pvt->cursound = -1;
-		ast_verbose(V_BEGIN "Sound stopped" V_END);
-		return;
-	}
-
-	for (; sounds[i].ind != -1; i++) {
-		if (sounds[i].ind == sound)
-			pvt->cursound = i;
-	}
-
-	if (pvt->cursound > -1)
-		ast_verbose(V_BEGIN "Sound playing should be '%s'" V_END, 
-			sounds[pvt->cursound].desc);
-	else
-		ast_verbose(V_BEGIN "Unknown sound requested on the console!" V_END);
-}
-
 static int console_hangup(struct ast_channel *c)
 {
 	struct console_pvt *pvt = &console_pvt;
 
-	ast_verbose(V_BEGIN "Hangup on Console" V_END);
+	ast_verb(1, V_BEGIN "Hangup on Console" V_END);
 
 	pvt->hookstate = 0;
-	set_cursound(pvt, -1);
 	c->tech_pvt = NULL;
 	pvt->owner = NULL;
 
@@ -425,20 +373,39 @@
 	int res;
 	struct console_pvt *pvt = &console_pvt;
 
-	ast_verbose(V_BEGIN "Call from Console has been Answered" V_END);
+	ast_verb(1, V_BEGIN "Call from Console has been Answered" V_END);
 
 	ast_mutex_lock(&pvt->lock);
 	ast_setstate(c, AST_STATE_UP);
-	set_cursound(pvt, -1);
 	res = start_stream(pvt);
 	ast_mutex_unlock(&pvt->lock);
 
 	return res;
 }
 
+/*
+ * \brief Implementation of the ast_channel_tech read() callback
+ *
+ * Calling this function is harmless.  However, if it does get called, it
+ * is an indication that something weird happened that really shouldn't
+ * have and is worth looking into.
+ * 
+ * Why should this function not get called?  Well, let me explain.  There are
+ * a couple of ways to pass on audio that has come from this channel.  The way
+ * that this channel driver uses is that once the audio is available, it is
+ * stuffed into an ast_frame and queued onto the channel using ast_queue_frame.
+ *
+ * The other method would be signalling to the core that there is audio waiting,
+ * and that it needs to call the channel's read() callback to get it.  The way
+ * the channel gets signalled is that one or more file descriptors are placed
+ * in the fds array on the ast_channel which the core will poll() on.  When the
+ * fd indicates that input is available, the read() callback is called.  This
+ * is especially useful when there is a dedicated file descriptor where the
+ * audio is read from.  An example would be the socket for an RTP stream.
+ */
 static struct ast_frame *console_read(struct ast_channel *chan)
 {
-	ast_log(LOG_WARNING, "This should never be called, mmkay?!\n");
+	ast_debug(1, "This should never be called, mmkay?!\n");
 
 	return &ast_null_frame;
 }
@@ -448,22 +415,22 @@
 	struct ast_frame f = { 0, };
 	struct console_pvt *pvt = &console_pvt;
 
-	ast_verbose(V_BEGIN "Call to device '%s' on console from '%s' <%s>" V_END,
+	ast_verb(1, V_BEGIN "Call to device '%s' on console from '%s' <%s>" V_END,
 		dest, c->cid.cid_name, c->cid.cid_num);
 
 	if (pvt->autoanswer) {
-		ast_verbose(V_BEGIN "Auto-answered" V_END);
+		ast_verb(1, V_BEGIN "Auto-answered" V_END);
 		pvt->hookstate = 1;
 		f.frametype = AST_FRAME_CONTROL;
 		f.subclass = AST_CONTROL_ANSWER;
-		ast_queue_frame(c, &f);
 	} else {
-		ast_verbose(V_BEGIN "Type 'answer' to answer, or use 'autoanswer' for future calls" V_END);
+		ast_verb(1, V_BEGIN "Type 'answer' to answer, or use 'autoanswer' "
+				"for future calls" V_END);
 		f.frametype = AST_FRAME_CONTROL;
 		f.subclass = AST_CONTROL_RINGING;
-		ast_queue_frame(c, &f);
-		set_cursound(pvt, AST_CONTROL_RING);
-	}
+	}
+	
+	ast_queue_frame(c, &f);
 
 	return start_stream(pvt);
 }
@@ -474,7 +441,8 @@
 	struct console_pvt *pvt = &console_pvt;
 
 	if (!fr)
-		return -1; 
+		return -1;
+
 	ast_mutex_lock(&pvt->lock);
 	AST_LIST_INSERT_TAIL(&pvt->frames, fr, frame_list);
 	ast_cond_signal(&pvt->cond);
@@ -486,96 +454,42 @@
 static int console_indicate(struct ast_channel *chan, int cond, const void *data, size_t datalen)
 {
 	struct console_pvt *pvt = chan->tech_pvt;
+	int res = 0;
 
 	switch (cond) {
 	case AST_CONTROL_BUSY:
 	case AST_CONTROL_CONGESTION:
 	case AST_CONTROL_RINGING:
-		set_cursound(pvt, cond);
+		res = -1;  /* Ask for inband indications */
 		break;
 	case AST_CONTROL_VIDUPDATE:
 	case -1:
-		set_cursound(pvt, -1);
 		break;
 	case AST_CONTROL_HOLD:
-		ast_verbose(V_BEGIN "Console Has Been Placed on Hold" V_END);
+		ast_verb(1, V_BEGIN "Console Has Been Placed on Hold" V_END);
 		ast_moh_start(chan, data, pvt->mohinterpret);
 		break;
 	case AST_CONTROL_UNHOLD:
-		ast_verbose(V_BEGIN "Console Has Been Retrieved from Hold" V_END);
+		ast_verb(1, V_BEGIN "Console Has Been Retrieved from Hold" V_END);
 		ast_moh_stop(chan);
 		break;
 	default:
-		ast_log(LOG_WARNING, "Don't know how to display condition %d on %s\n", cond, chan->name);
-		return -1;
-	}
+		ast_log(LOG_WARNING, "Don't know how to display condition %d on %s\n", 
+			cond, chan->name);
+		/* The core will play inband indications for us if appropriate */
+		res = -1;
+	}
+
+	return res;
+}
+
+static int console_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
+{
+	struct console_pvt *pvt = &console_pvt;
+
+	pvt->owner = newchan;
 
 	return 0;
-}
-
-static int console_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
-{
-	struct console_pvt *pvt = &console_pvt;
-
-	ast_mutex_lock(&pvt->lock);
-	pvt->owner = newchan;
-	ast_mutex_unlock(&pvt->lock);
-
-	return 0;
-}
-
-/*!
- * \brief Play the current sound
- *
- * Builds a frame from the high level description of the sounds,
- * and passes it to the audio device.
- * The actual sound is made of 1 or more sequences of sound samples
- * (s->datalen, repeated to make s->samplen samples) followed by
- * s->silencelen samples of silence. The position in the sequence is stored
- * in pvt->sampsent, which goes between 0 .. s->samplen+s->silencelen.
- * In case we fail to write a frame, don't update pvt->sampsent.
- */
-static void send_sound(struct console_pvt *pvt, void *output, unsigned long frame_count)
-{
-	int ofs, l, start;
-	int l_sampsent = pvt->sampsent;
-	struct sound *s;
-
-	if (pvt->cursound < 0)		/* no sound to send */
-		return;
-
-	s = &sounds[pvt->cursound];
-
-	for (ofs = 0; ofs < frame_count; ofs += l) {
-		l = s->samplen - l_sampsent;	/* # of available samples */
-		if (l > 0) {
-			start = l_sampsent % s->datalen;	/* source offset */
-			l = MIN(l, frame_count - ofs);	/* don't overflow the frame */
-			l = MIN(l, s->datalen - start);	/* don't overflow the source */
-			bcopy(s->data + start, output + ofs, l * 2);
-			if (0)
-				ast_log(LOG_WARNING, "send_sound sound %d/%d of %d into %d\n", l_sampsent, l, s->samplen, ofs);
-			l_sampsent += l;
-		} else {				/* end of samples, maybe some silence */
-			static const short silence[NUM_SAMPLES] = { 0, };
-
-			l += s->silencelen;
-			if (l > 0) {
-				l = MIN(l, frame_count - ofs);
-				bcopy(silence, output + ofs, l * 2);
-				l_sampsent += l;
-			} else {			/* silence is over, restart sound if loop */
-				if (s->repeat == 0) {	/* last block */
-					pvt->cursound = -1;
-					if (ofs < frame_count)	/* pad with silence */
-						bcopy(silence, output + ofs, (frame_count - ofs) * 2);
-				}
-				l_sampsent = 0;
-			}
-		}
-	}
-	if (l > 0)
-		pvt->sampsent = l_sampsent;	/* update status */
 }
 
 static int handle_output(struct console_pvt *pvt, void *output, unsigned long frame_count)
@@ -586,12 +500,6 @@
 	int res = paContinue;
 
 	ast_mutex_lock(&pvt->lock);
-
-	/* Fill the buffer with the current sound if one is set */
-	if (pvt->cursound != -1) {
-		send_sound(pvt, output, frame_count);
-		frame_count = 0;
-	}
 
 	/* Empty the write buffer */
 	if (frame_count && pvt->write_dst) {
@@ -647,6 +555,7 @@
 	fr->subclass = AST_FORMAT_SLINEAR;
 	fr->samples = frame_count;
 	fr->datalen = frame_count * 2;
+
 	if (pvt->muted) {
 		memcpy(fr->data, input, 
 			MIN(sizeof(pvt->read_buf) - AST_FRIENDLY_OFFSET, frame_count * 2) );
@@ -658,13 +567,11 @@
 
 	/* If the channel is in the process of being hung up, then it will be
 	 * locked while calling the console_hangup callback, and that could be
-	 * waiting to stop the audio stream, which will block until this
-	 * function returns.  Thus, locking the channel could result in a
-	 * deadlock.  So, we use the fact that the locks are recursive to test
-	 * locking the channel before queueing the frame. */
+	 * waiting to stop the audio stream, which will block until this function
+	 * returns.  Thus, locking the channel could result in a deadlock */
 	while (ast_channel_trylock(pvt->owner)) {
 		if (++count == 100) {
-			ast_log(LOG_DEBUG, "Dropping frame\n");
+			ast_debug(1, "Dropping frame\n");
 			pvt->incallback = 0;
 			return paContinue;	
 		}
@@ -765,7 +672,8 @@
 	if (a->argc == e->args + 1) {
 		char *ext = NULL, *con = NULL;
 		s = ast_ext_ctx(pvt, a->argv[e->args], &ext, &con);
-		ast_log(LOG_DEBUG, "provided '%s', exten '%s' context '%s'\n", a->argv[e->args], mye, myc);
+		ast_debug(1, "provided '%s', exten '%s' context '%s'\n", 
+			a->argv[e->args], mye, myc);
 		mye = ext;
 		myc = con;
 	}
@@ -799,12 +707,13 @@
 
 	if (a->argc != e->args)
 		return CLI_SHOWUSAGE;
+
 	if (!pvt->owner && !pvt->hookstate) {
 		ast_cli(a->fd, "No call to hang up\n");
 		return CLI_FAILURE;
 	}
+
 	pvt->hookstate = 0;
-	set_cursound(pvt, -1);
 	if (pvt->owner)
 		ast_queue_hangup(pvt->owner);
 
@@ -827,6 +736,7 @@
 
 	if (a->argc != e->args)
 		return CLI_SHOWUSAGE;
+
 	s = a->argv[e->args-1];
 	if (!strcasecmp(s, "mute"))
 		pvt->muted = 1;
@@ -835,7 +745,7 @@
 	else
 		return CLI_SHOWUSAGE;
 
-	ast_verbose(V_BEGIN "The Console is now %s" V_END, 
+	ast_verb(1, V_BEGIN "The Console is now %s" V_END, 
 		pvt->muted ? "Muted" : "Unmuted");
 
 	return CLI_SUCCESS;
@@ -864,6 +774,7 @@
 		ast_cli(a->fd, "(None)\n");
 		return CLI_SUCCESS;
 	}
+
 	def_input = Pa_GetDefaultInputDevice();
 	def_output = Pa_GetDefaultOutputDevice();
 	for (index = 0; index < num; index++) {
@@ -899,14 +810,16 @@
 	case CLI_GENERATE:
 		return NULL;	/* no completion */
 	}
+
 	if (a->argc != e->args)
 		return CLI_SHOWUSAGE;
+
 	if (!pvt->owner) {
 		ast_cli(a->fd, "No one is calling us\n");
 		return CLI_FAILURE;
 	}
+
 	pvt->hookstate = 1;
-	pvt->cursound = -1;
 	ast_queue_frame(pvt->owner, &f);
 
 	return CLI_SUCCESS;
@@ -934,10 +847,12 @@
 
 	if (a->argc < e->args + 1)
 		return CLI_SHOWUSAGE;
+
 	if (!pvt->owner) {
 		ast_cli(a->fd, "Not in a call\n");
 		return CLI_FAILURE;
 	}
+
 	ast_join(buf, sizeof(buf) - 1, a->argv + e->args);
 	if (!ast_strlen_zero(buf)) {
 		struct ast_frame f = { 0, };
@@ -949,6 +864,7 @@
 		f.datalen = i + 1;
 		ast_queue_frame(pvt->owner, &f);
 	}
+
 	return CLI_SUCCESS;
 }
 
@@ -961,32 +877,8 @@
 	NEW_CLI(cli_list_devices, "List available devices"),
 };
 
-/*!
- * \brief Load the configuration
- * \param reload if this was called due to a reload
- * \retval 0 succcess
- * \retval -1 failure
- */
-static int load_config(int reload)
-{
-	struct ast_config *cfg;
-	struct ast_variable *v;
-	struct console_pvt *pvt = &console_pvt;
-	struct ast_flags config_flags = { 0 };
-	int res = -1;
-
-	/* default values */
-	memcpy(&global_jbconf, &default_jbconf, sizeof(global_jbconf));
-
-	ast_mutex_lock(&pvt->lock);
-
-	if (!reload) {
-		if (ast_string_field_init(pvt, 32))
-			goto return_unlock;
-		ast_string_field_set(pvt, name, "default");
-	}
-
-	/* Set pvt struct defaults */
+static void set_pvt_defaults(struct console_pvt *pvt)
+{
 	ast_string_field_set(pvt, mohinterpret, "default");
 	ast_string_field_set(pvt, context, "default");
 	ast_string_field_set(pvt, exten, "s");
@@ -996,12 +888,40 @@
 
 	pvt->overridecontext = 0;
 	pvt->autoanswer = 0;
-	/* End defaults */
+}
+
+/*!
+ * \brief Load the configuration
+ * \param reload if this was called due to a reload
+ * \retval 0 succcess
+ * \retval -1 failure
+ */
+static int load_config(int reload)
+{
+	struct ast_config *cfg;
+	struct ast_variable *v;
+	struct console_pvt *pvt = &console_pvt;
+	struct ast_flags config_flags = { 0 };
+	int res = -1;
+
+	/* default values */
+	memcpy(&global_jbconf, &default_jbconf, sizeof(global_jbconf));
+
+	ast_mutex_lock(&pvt->lock);
+
+	if (!reload) {
+		if (ast_string_field_init(pvt, 32))
+			goto return_unlock;
+		ast_string_field_set(pvt, name, "default");
+	}
+
+	set_pvt_defaults(pvt);
 
 	if (!(cfg = ast_config_load(config_file, config_flags))) {
 		ast_log(LOG_NOTICE, "Unable to open configuration file %s!\n", config_file);
 		goto return_unlock;
 	}
+
 	v = ast_variable_browse(cfg, "general");
 	for (; v; v = v->next) {
 		if (!ast_jb_read_conf(&global_jbconf, v->name, v->value))
@@ -1044,6 +964,7 @@
 
 	if (pvt->hookstate)
 		stop_stream(pvt);
+
 	Pa_Terminate();
 
 	ast_channel_unregister(&console_tech);
@@ -1059,28 +980,45 @@
 {
 	PaError res;
 	struct console_pvt *pvt = &console_pvt;
+	struct {
+		unsigned int pa_init:1;
+		unsigned int chan_reg:1;
+		unsigned int cli_reg:1;
+	} load_flags = { 0, };
+
+	ast_cond_init(&pvt->cond, NULL);
 
 	if (load_config(0))
-		return AST_MODULE_LOAD_DECLINE;
-
-	ast_cond_init(&pvt->cond, NULL);
+		goto return_error;
 
 	res = Pa_Initialize();
 	if (res != paNoError) {
 		ast_log(LOG_WARNING, "Failed to initialize audio system - (%d) %s\n",
 			res, Pa_GetErrorText(res));
-		return AST_MODULE_LOAD_DECLINE;
-	}
+		goto return_error;
+	}
+	load_flags.pa_init = 1;
 
 	if (ast_channel_register(&console_tech)) {
 		ast_log(LOG_ERROR, "Unable to register channel type 'Console'\n");
-		ast_cond_destroy(&pvt->cond);
-		return AST_MODULE_LOAD_DECLINE;
-	}
-	
-	ast_cli_register_multiple(cli_console, sizeof(cli_console) / sizeof(cli_console[0]));
+		goto return_error;
+	}
+	load_flags.chan_reg = 1;
+
+	if (ast_cli_register_multiple(cli_console, ARRAY_LEN(cli_console)))
+		goto return_error;
 
 	return AST_MODULE_LOAD_SUCCESS;
+
+return_error:
+	ast_cli_unregister_multiple(cli_console, ARRAY_LEN(cli_console));
+	if (load_flags.chan_reg)
+		ast_channel_unregister(&console_tech);
+	if (load_flags.pa_init)
+		Pa_Terminate();
+	ast_cond_destroy(&pvt->cond);
+
+	return AST_MODULE_LOAD_DECLINE;
 }
 
 static int reload(void)

Modified: team/russell/chan_console/include/asterisk/logger.h
URL: http://svn.digium.com/view/asterisk/team/russell/chan_console/include/asterisk/logger.h?view=diff&rev=81444&r1=81443&r2=81444
==============================================================================
--- team/russell/chan_console/include/asterisk/logger.h (original)
+++ team/russell/chan_console/include/asterisk/logger.h Tue Sep  4 12:55:11 2007
@@ -26,6 +26,7 @@
 #define _ASTERISK_LOGGER_H
 
 #include "asterisk/compat.h"
+#include "asterisk/options.h"
 
 #include <stdarg.h>
 

Modified: team/russell/chan_console/main/cli.c
URL: http://svn.digium.com/view/asterisk/team/russell/chan_console/main/cli.c?view=diff&rev=81444&r1=81443&r2=81444
==============================================================================
--- team/russell/chan_console/main/cli.c (original)
+++ team/russell/chan_console/main/cli.c Tue Sep  4 12:55:11 2007
@@ -1369,8 +1369,10 @@
 		AST_RWLIST_WRLOCK(&helpers);
 		AST_RWLIST_REMOVE(&helpers, e, list);
 		AST_RWLIST_UNLOCK(&helpers);
-		ast_free(e->_full_cmd);
-		e->_full_cmd = NULL;
+		if (e->_full_cmd) {
+			ast_free(e->_full_cmd);
+			e->_full_cmd = NULL;
+		}
 		if (e->new_handler) {
 			/* this is a new-style entry. Reset fields and free memory. */
 			bzero((char **)(e->cmda), sizeof(e->cmda));




More information about the svn-commits mailing list