diff -uNrw asterisk-1.8.19.0/.cleancount asterisk-1.8.20.0-rc1/.cleancount
--- asterisk-1.8.19.0/.cleancount	2011-01-31 07:41:36.000000000 +0100
+++ asterisk-1.8.20.0-rc1/.cleancount	2012-11-06 19:18:32.000000000 +0100
@@ -1,3 +1 @@
-39
-
-
+40
diff -uNrw asterisk-1.8.19.0/include/asterisk/astmm.h asterisk-1.8.20.0-rc1/include/asterisk/astmm.h
--- asterisk-1.8.19.0/include/asterisk/astmm.h	2010-06-08 16:38:18.000000000 +0200
+++ asterisk-1.8.20.0-rc1/include/asterisk/astmm.h	2012-11-08 17:58:52.000000000 +0100
@@ -65,7 +65,8 @@
 	__attribute__((format(printf, 5, 6)));
 int __ast_vasprintf(char **strp, const char *format, va_list ap, const char *file, int lineno, const char *func)
 	__attribute__((format(printf, 2, 0)));
-void __ast_mm_init(void);
+void __ast_mm_init_phase_1(void);
+void __ast_mm_init_phase_2(void);
 
 
 /* Provide our own definitions */
diff -uNrw asterisk-1.8.19.0/include/asterisk/channel.h asterisk-1.8.20.0-rc1/include/asterisk/channel.h
--- asterisk-1.8.19.0/include/asterisk/channel.h	2012-09-13 20:39:40.000000000 +0200
+++ asterisk-1.8.20.0-rc1/include/asterisk/channel.h	2012-11-07 18:01:13.000000000 +0100
@@ -870,6 +870,8 @@
 	char macrocontext[AST_MAX_CONTEXT];		/*!< Macro: Current non-macro context. See app_macro.c */
 	char macroexten[AST_MAX_EXTENSION];		/*!< Macro: Current non-macro extension. See app_macro.c */
 	char emulate_dtmf_digit;			/*!< Digit being emulated */
+	char sending_dtmf_digit;			/*!< Digit this channel is currently sending out. (zero if not sending) */
+	struct timeval sending_dtmf_tv;		/*!< The time this channel started sending the current digit. (Invalid if sending_dtmf_digit is zero.) */
 };
 
 /*! \brief ast_channel_tech Properties */
@@ -1686,7 +1688,7 @@
 /*!
  * \brief Wait for a specified amount of time, looking for hangups
  * \param chan channel to wait for
- * \param ms length of time in milliseconds to sleep
+ * \param ms length of time in milliseconds to sleep. This should never be less than zero.
  * \details
  * Waits for a specified amount of time, servicing the channel as required.
  * \return returns -1 on hangup, otherwise 0.
@@ -1696,7 +1698,7 @@
 /*!
  * \brief Wait for a specified amount of time, looking for hangups and a condition argument
  * \param chan channel to wait for
- * \param ms length of time in milliseconds to sleep
+ * \param ms length of time in milliseconds to sleep.
  * \param cond a function pointer for testing continue condition
  * \param data argument to be passed to the condition test function
  * \return returns -1 on hangup, otherwise 0.
diff -uNrw asterisk-1.8.19.0/include/asterisk/features.h asterisk-1.8.20.0-rc1/include/asterisk/features.h
--- asterisk-1.8.19.0/include/asterisk/features.h	2011-10-20 23:54:11.000000000 +0200
+++ asterisk-1.8.20.0-rc1/include/asterisk/features.h	2012-11-06 19:18:32.000000000 +0100
@@ -169,6 +169,18 @@
 /*! \brief Determine system call pickup extension */
 const char *ast_pickup_ext(void);
 
+/*!
+ * \brief Simulate a DTMF end on a broken bridge channel.
+ *
+ * \param chan Channel sending DTMF that has not ended.
+ * \param digit DTMF digit to stop.
+ * \param start DTMF digit start time.
+ * \param why Reason bridge broken.
+ *
+ * \return Nothing
+ */
+void ast_bridge_end_dtmf(struct ast_channel *chan, char digit, struct timeval start, const char *why);
+
 /*! \brief Bridge a call, optionally allowing redirection */
 int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer,struct ast_bridge_config *config);
 
diff -uNrw asterisk-1.8.19.0/include/asterisk/hashtab.h asterisk-1.8.20.0-rc1/include/asterisk/hashtab.h
--- asterisk-1.8.19.0/include/asterisk/hashtab.h	2009-03-11 01:29:59.000000000 +0100
+++ asterisk-1.8.20.0-rc1/include/asterisk/hashtab.h	2012-11-15 23:27:37.000000000 +0100
@@ -23,6 +23,9 @@
  * \brief Generic (perhaps overly so) hashtable implementation
  * \ref AstHash
  */
+
+#include "asterisk/lock.h"
+
 /*! \page AstHash Hash Table support in Asterisk
 
 A hash table is a structure that allows for an exact-match search
diff -uNrw asterisk-1.8.19.0/include/asterisk/linkedlists.h asterisk-1.8.20.0-rc1/include/asterisk/linkedlists.h
--- asterisk-1.8.19.0/include/asterisk/linkedlists.h	2011-10-29 06:19:42.000000000 +0200
+++ asterisk-1.8.20.0-rc1/include/asterisk/linkedlists.h	2012-11-27 18:28:43.000000000 +0100
@@ -834,33 +834,37 @@
  * \param elm This is a pointer to the entry to be removed.
  * \param field This is the name of the field (declared using AST_LIST_ENTRY())
  * used to link entries of this list together.
- * \warning The removed entry is \b not freed nor modified in any way.
+ * \retval elm if elm was in the list.
+ * \retval NULL if elm was not in the list or elm was NULL.
+ * \warning The removed entry is \b not freed.
  */
-#define AST_LIST_REMOVE(head, elm, field) ({			        \
-	__typeof(elm) __res = NULL; \
-	__typeof(elm) __tmp = elm; \
-	if (!__tmp) { \
-		__res = NULL; \
-	} else if ((head)->first == (elm)) {					\
-		__res = (head)->first;                      \
-		(head)->first = (elm)->field.next;			\
-		if ((head)->last == (elm))			\
+#define AST_LIST_REMOVE(head, elm, field)						\
+	({															\
+		__typeof(elm) __elm = (elm);							\
+		if (__elm) {											\
+			if ((head)->first == __elm) {						\
+				(head)->first = __elm->field.next;				\
+				__elm->field.next = NULL;						\
+				if ((head)->last == __elm) {					\
 			(head)->last = NULL;			\
+				}												\
+			} else {											\
+				typeof(elm) __prev = (head)->first;				\
+				while (__prev && __prev->field.next != __elm) {	\
+					__prev = __prev->field.next;				\
+				}												\
+				if (__prev) {									\
+					__prev->field.next = __elm->field.next;		\
+					__elm->field.next = NULL;					\
+					if ((head)->last == __elm) {				\
+						(head)->last = __prev;					\
+					}											\
 	} else {								\
-		typeof(elm) curelm = (head)->first;			\
-		while (curelm && (curelm->field.next != (elm)))			\
-			curelm = curelm->field.next;			\
-		if (curelm) { \
-			__res = (elm); \
-			curelm->field.next = (elm)->field.next;			\
-			if ((head)->last == (elm))				\
-				(head)->last = curelm;				\
+					__elm = NULL;								\
 		} \
 	}								\
-	if (__res) { \
-		(__res)->field.next = NULL; \
 	} \
-	(__res); \
+		__elm;													\
 })
 
 #define AST_RWLIST_REMOVE AST_LIST_REMOVE
diff -uNrw asterisk-1.8.19.0/include/asterisk/lock.h asterisk-1.8.20.0-rc1/include/asterisk/lock.h
--- asterisk-1.8.19.0/include/asterisk/lock.h	2011-01-31 08:51:40.000000000 +0100
+++ asterisk-1.8.20.0-rc1/include/asterisk/lock.h	2012-11-23 00:51:02.000000000 +0100
@@ -428,12 +428,24 @@
 
 static inline void ast_reentrancy_lock(struct ast_lock_track *lt)
 {
-	pthread_mutex_lock(&lt->reentr_mutex);
+	int res;
+	if ((res = pthread_mutex_lock(&lt->reentr_mutex))) {
+		fprintf(stderr, "ast_reentrancy_lock failed: '%s' (%d)\n", strerror(res), res);
+#if defined(DO_CRASH) || defined(THREAD_CRASH)
+		abort();
+#endif
+	}
 }
 
 static inline void ast_reentrancy_unlock(struct ast_lock_track *lt)
 {
-	pthread_mutex_unlock(&lt->reentr_mutex);
+	int res;
+	if ((res = pthread_mutex_unlock(&lt->reentr_mutex))) {
+		fprintf(stderr, "ast_reentrancy_unlock failed: '%s' (%d)\n", strerror(res), res);
+#if defined(DO_CRASH) || defined(THREAD_CRASH)
+		abort();
+#endif
+	}
 }
 
 static inline void ast_reentrancy_init(struct ast_lock_track **plt)
diff -uNrw asterisk-1.8.19.0/include/asterisk/_private.h asterisk-1.8.20.0-rc1/include/asterisk/_private.h
--- asterisk-1.8.19.0/include/asterisk/_private.h	2010-05-19 23:29:08.000000000 +0200
+++ asterisk-1.8.20.0-rc1/include/asterisk/_private.h	2012-12-03 21:26:37.000000000 +0100
@@ -19,6 +19,7 @@
 int load_pbx(void);			/*!< Provided by pbx.c */
 int init_logger(void);			/*!< Provided by logger.c */
 void close_logger(void);		/*!< Provided by logger.c */
+void clean_time_zones(void);			/*!< Provided by localtime.c */
 int init_framer(void);			/*!< Provided by frame.c */
 int ast_term_init(void);		/*!< Provided by term.c */
 int astdb_init(void);			/*!< Provided by db.c */
diff -uNrw asterisk-1.8.19.0/include/asterisk/time.h asterisk-1.8.20.0-rc1/include/asterisk/time.h
--- asterisk-1.8.19.0/include/asterisk/time.h	2012-04-27 21:26:37.000000000 +0200
+++ asterisk-1.8.20.0-rc1/include/asterisk/time.h	2012-11-07 18:01:13.000000000 +0100
@@ -152,6 +152,20 @@
 struct timeval ast_tvsub(struct timeval a, struct timeval b);
 
 /*!
+ * \brief Calculate remaining milliseconds given a starting timestamp
+ * and upper bound
+ *
+ * If the upper bound is negative, then this indicates that there is no
+ * upper bound on the amount of time to wait. This will result in a
+ * negative return.
+ *
+ * \param start When timing started being calculated
+ * \param max_ms The maximum number of milliseconds to wait from start. May be negative.
+ * \return The number of milliseconds left to wait for. May be negative.
+ */
+int ast_remaining_ms(struct timeval start, int max_ms);
+
+/*!
  * \brief Returns a timeval from sec, usec
  */
 AST_INLINE_API(
diff -uNrw asterisk-1.8.19.0/include/asterisk/utils.h asterisk-1.8.20.0-rc1/include/asterisk/utils.h
--- asterisk-1.8.19.0/include/asterisk/utils.h	2012-07-31 21:31:42.000000000 +0200
+++ asterisk-1.8.20.0-rc1/include/asterisk/utils.h	2012-11-08 17:58:52.000000000 +0100
@@ -706,32 +706,27 @@
 
 
 #ifdef AST_DEVMODE
+void __ast_assert_failed(int condition, const char *condition_str, const char *file, int line, const char *function);
 #define ast_assert(a) _ast_assert(a, # a, __FILE__, __LINE__, __PRETTY_FUNCTION__)
-static void force_inline _ast_assert(int condition, const char *condition_str, 
-	const char *file, int line, const char *function)
+static void force_inline _ast_assert(int condition, const char *condition_str, const char *file, int line, const char *function)
 {
 	if (__builtin_expect(!condition, 1)) {
-		/* Attempt to put it into the logger, but hope that at least someone saw the
-		 * message on stderr ... */
-		ast_log(__LOG_ERROR, file, line, function, "FRACK!, Failed assertion %s (%d)\n",
-			condition_str, condition);
-		fprintf(stderr, "FRACK!, Failed assertion %s (%d) at line %d in %s of %s\n",
-			condition_str, condition, line, function, file);
-		/* Give the logger a chance to get the message out, just in case we abort(), or
-		 * Asterisk crashes due to whatever problem just happened after we exit ast_assert(). */
-		usleep(1);
-#ifdef DO_CRASH
-		abort();
-		/* Just in case abort() doesn't work or something else super silly,
-		 * and for Qwell's amusement. */
-		*((int*)0)=0;
-#endif
+		__ast_assert_failed(condition, condition_str, file, line, function);
 	}
 }
 #else
 #define ast_assert(a)
 #endif
 
+/*!
+ * \brief Force a crash if DO_CRASH is defined.
+ *
+ * \note If DO_CRASH is not defined then the function returns.
+ *
+ * \return Nothing
+ */
+void ast_do_crash(void);
+
 #include "asterisk/strings.h"
 
 /*!
diff -uNrw asterisk-1.8.19.0/.lastclean asterisk-1.8.20.0-rc1/.lastclean
--- asterisk-1.8.19.0/.lastclean	2012-11-06 17:23:40.000000000 +0100
+++ asterisk-1.8.20.0-rc1/.lastclean	2012-12-10 02:48:00.000000000 +0100
@@ -1,3 +1 @@
-39
-
-
+40
diff -uNrw asterisk-1.8.19.0/main/asterisk.c asterisk-1.8.20.0-rc1/main/asterisk.c
--- asterisk-1.8.19.0/main/asterisk.c	2012-10-02 22:54:16.000000000 +0200
+++ asterisk-1.8.20.0-rc1/main/asterisk.c	2012-12-03 23:51:41.000000000 +0100
@@ -65,7 +65,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 374230 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 377165 $")
 
 #include "asterisk/_private.h"
 
@@ -209,10 +209,10 @@
 
 struct ast_atexit {
 	void (*func)(void);
-	AST_RWLIST_ENTRY(ast_atexit) list;
+	AST_LIST_ENTRY(ast_atexit) list;
 };
 
-static AST_RWLIST_HEAD_STATIC(atexits, ast_atexit);
+static AST_LIST_HEAD_STATIC(atexits, ast_atexit);
 
 struct timeval ast_startuptime;
 struct timeval ast_lastreloadtime;
@@ -294,6 +294,7 @@
 static pthread_t mon_sig_flags;
 static int canary_pid = 0;
 static char canary_filename[128];
+static int multi_thread_safe;
 
 static char randompool[256];
 
@@ -402,6 +403,8 @@
 
 	if (!new)
 		return;
+
+	ast_assert(multi_thread_safe);
 	new->id = pthread_self();
 	new->name = name; /* steal the allocated memory for the thread name */
 	AST_RWLIST_WRLOCK(&thread_list);
@@ -942,39 +945,57 @@
 
 #endif /* ! LOW_MEMORY */
 
+static void ast_run_atexits(void)
+{
+	struct ast_atexit *ae;
+
+	AST_LIST_LOCK(&atexits);
+	while ((ae = AST_LIST_REMOVE_HEAD(&atexits, list))) {
+		if (ae->func) {
+			ae->func();
+		}
+		ast_free(ae);
+	}
+	AST_LIST_UNLOCK(&atexits);
+}
+
+static void __ast_unregister_atexit(void (*func)(void))
+{
+	struct ast_atexit *ae;
+
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&atexits, ae, list) {
+		if (ae->func == func) {
+			AST_LIST_REMOVE_CURRENT(list);
+			ast_free(ae);
+			break;
+		}
+	}
+	AST_LIST_TRAVERSE_SAFE_END;
+}
+
 int ast_register_atexit(void (*func)(void))
 {
 	struct ast_atexit *ae;
 
-	if (!(ae = ast_calloc(1, sizeof(*ae))))
+	ae = ast_calloc(1, sizeof(*ae));
+	if (!ae) {
 		return -1;
-
+	}
 	ae->func = func;
 
-	ast_unregister_atexit(func);	
-
-	AST_RWLIST_WRLOCK(&atexits);
-	AST_RWLIST_INSERT_HEAD(&atexits, ae, list);
-	AST_RWLIST_UNLOCK(&atexits);
+	AST_LIST_LOCK(&atexits);
+	__ast_unregister_atexit(func);
+	AST_LIST_INSERT_HEAD(&atexits, ae, list);
+	AST_LIST_UNLOCK(&atexits);
 
 	return 0;
 }
 
 void ast_unregister_atexit(void (*func)(void))
 {
-	struct ast_atexit *ae = NULL;
-
-	AST_RWLIST_WRLOCK(&atexits);
-	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&atexits, ae, list) {
-		if (ae->func == func) {
-			AST_RWLIST_REMOVE_CURRENT(list);
-			break;
-		}
-	}
-	AST_RWLIST_TRAVERSE_SAFE_END;
-	AST_RWLIST_UNLOCK(&atexits);
-
-	free(ae);
+	AST_LIST_LOCK(&atexits);
+	__ast_unregister_atexit(func);
+	AST_LIST_UNLOCK(&atexits);
 }
 
 /* Sending commands from consoles back to the daemon requires a terminating NULL */
@@ -1502,7 +1523,7 @@
 	int res;
 	ast_consock = socket(PF_LOCAL, SOCK_STREAM, 0);
 	if (ast_consock < 0) {
-		ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
+		fprintf(stderr, "Unable to create socket: %s\n", strerror(errno));
 		return 0;
 	}
 	memset(&sunaddr, 0, sizeof(sunaddr));
@@ -1646,17 +1667,6 @@
 	return 0;
 }
 
-static void ast_run_atexits(void)
-{
-	struct ast_atexit *ae;
-	AST_RWLIST_RDLOCK(&atexits);
-	AST_RWLIST_TRAVERSE(&atexits, ae, list) {
-		if (ae->func) 
-			ae->func();
-	}
-	AST_RWLIST_UNLOCK(&atexits);
-}
-
 static int can_safely_quit(shutdown_nice_t niceness, int restart);
 static void really_quit(int num, shutdown_nice_t niceness, int restart);
 
@@ -1735,8 +1745,11 @@
 	return 1;
 }
 
+/*! Called when exiting is certain. */
 static void really_quit(int num, shutdown_nice_t niceness, int restart)
 {
+	int active_channels;
+
 	if (niceness >= SHUTDOWN_NICE) {
 		ast_module_shutdown();
 	}
@@ -1763,21 +1776,24 @@
 			}
 		}
 	}
+	active_channels = ast_active_channels();
 	/* The manager event for shutdown must happen prior to ast_run_atexits, as
 	 * the manager interface will dispose of its sessions as part of its
 	 * shutdown.
 	 */
 	manager_event(EVENT_FLAG_SYSTEM, "Shutdown", "Shutdown: %s\r\n"
 			"Restart: %s\r\n",
-			ast_active_channels() ? "Uncleanly" : "Cleanly",
+			active_channels ? "Uncleanly" : "Cleanly",
 			restart ? "True" : "False");
+	if (option_verbose && ast_opt_console) {
+		ast_verbose("Asterisk %s ending (%d).\n",
+			active_channels ? "uncleanly" : "cleanly", num);
+	}
 
 	if (option_verbose)
 		ast_verbose("Executing last minute cleanups\n");
 	ast_run_atexits();
-	/* Called on exit */
-	if (option_verbose && ast_opt_console)
-		ast_verbose("Asterisk %s ending (%d).\n", ast_active_channels() ? "uncleanly" : "cleanly", num);
+
 	ast_debug(1, "Asterisk ending (%d).\n", num);
 	if (ast_socket > -1) {
 		pthread_cancel(lthread);
@@ -1804,6 +1820,7 @@
 
 		/* close logger */
 		close_logger();
+		clean_time_zones();
 
 		/* If there is a consolethread running send it a SIGHUP 
 		   so it can execvp, otherwise we can do it ourselves */
@@ -1817,6 +1834,7 @@
 	} else {
 		/* close logger */
 		close_logger();
+		clean_time_zones();
 	}
 
 	exit(0);
@@ -2215,14 +2233,25 @@
 
 #define ASTERISK_PROMPT2 "%s*CLI> "
 
-static struct ast_cli_entry cli_asterisk[] = {
-	AST_CLI_DEFINE(handle_abort_shutdown, "Cancel a running shutdown"),
+/*!
+ * \brief Shutdown Asterisk CLI commands.
+ *
+ * \note These CLI commands cannot be unregistered at shutdown
+ * because one of them is likely the reason for the shutdown.
+ * The CLI generates a warning if a command is in-use when it is
+ * unregistered.
+ */
+static struct ast_cli_entry cli_asterisk_shutdown[] = {
 	AST_CLI_DEFINE(handle_stop_now, "Shut down Asterisk immediately"),
 	AST_CLI_DEFINE(handle_stop_gracefully, "Gracefully shut down Asterisk"),
 	AST_CLI_DEFINE(handle_stop_when_convenient, "Shut down Asterisk at empty call volume"),
 	AST_CLI_DEFINE(handle_restart_now, "Restart Asterisk immediately"), 
 	AST_CLI_DEFINE(handle_restart_gracefully, "Restart Asterisk gracefully"),
 	AST_CLI_DEFINE(handle_restart_when_convenient, "Restart Asterisk at empty call volume"),
+};
+
+static struct ast_cli_entry cli_asterisk[] = {
+	AST_CLI_DEFINE(handle_abort_shutdown, "Cancel a running shutdown"),
 	AST_CLI_DEFINE(show_warranty, "Show the warranty (if any) for this copy of Asterisk"),
 	AST_CLI_DEFINE(show_license, "Show the license(s) for this copy of Asterisk"),
 	AST_CLI_DEFINE(handle_version, "Display version info"),
@@ -2264,7 +2293,7 @@
 				break;
 			if (errno == EINTR)
 				continue;
-			ast_log(LOG_ERROR, "poll failed: %s\n", strerror(errno));
+			fprintf(stderr, "poll failed: %s\n", strerror(errno));
 			break;
 		}
 
@@ -2978,10 +3007,12 @@
 
 	if (ast_opt_override_config) {
 		cfg = ast_config_load2(ast_config_AST_CONFIG_FILE, "" /* core, can't reload */, config_flags);
-		if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID)
-			ast_log(LOG_WARNING, "Unable to open specified master config file '%s', using built-in defaults\n", ast_config_AST_CONFIG_FILE);
-	} else 
+		if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
+			fprintf(stderr, "Unable to open specified master config file '%s', using built-in defaults\n", ast_config_AST_CONFIG_FILE);
+		}
+	} else {
 		cfg = ast_config_load2(config, "" /* core, can't reload */, config_flags);
+	}
 
 	/* init with buildtime config */
 	ast_copy_string(cfg_paths.config_dir, DEFAULT_CONFIG_DIR, sizeof(cfg_paths.config_dir));
@@ -3187,7 +3218,7 @@
 	for (v = ast_variable_browse(cfg, "compat"); v; v = v->next) {
 		float version;
 		if (sscanf(v->value, "%30f", &version) != 1) {
-			ast_log(LOG_WARNING, "Compatibility version for option '%s' is not a number: '%s'\n", v->name, v->value);
+			fprintf(stderr, "Compatibility version for option '%s' is not a number: '%s'\n", v->name, v->value);
 			continue;
 		}
 		if (!strcasecmp(v->name, "app_set")) {
@@ -3302,6 +3333,28 @@
 	setenv("AST_VERSION", ast_get_version(), 1);
 }
 
+static void print_intro_message(const char *runuser, const char *rungroup)
+{
+	if (ast_opt_console || option_verbose) {
+		if (ast_register_verbose(console_verboser)) {
+			fprintf(stderr, "Unable to register console verboser?\n");
+			return;
+		}
+		WELCOME_MESSAGE;
+		if (runuser) {
+			ast_verbose("Running as user '%s'\n", runuser);
+		}
+		if (rungroup) {
+			ast_verbose("Running under group '%s'\n", rungroup);
+		}
+	}
+}
+
+static void main_atexit(void)
+{
+	ast_cli_unregister_multiple(cli_asterisk, ARRAY_LEN(cli_asterisk));
+}
+
 int main(int argc, char *argv[])
 {
 	int c;
@@ -3339,15 +3392,6 @@
 	if (gethostname(hostname, sizeof(hostname)-1))
 		ast_copy_string(hostname, "<Unknown>", sizeof(hostname));
 	ast_mainpid = getpid();
-	ast_ulaw_init();
-	ast_alaw_init();
-	callerid_init();
-	ast_builtins_init();
-	ast_utils_init();
-	tdd_init();
-	ast_tps_init();
-	ast_fd_init();
-	ast_pbx_init();
 
 	if (getenv("HOME")) 
 		snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
@@ -3468,16 +3512,6 @@
 		}
 	}
 
-	if (ast_opt_console || option_verbose || (ast_opt_remote && !ast_opt_exec)) {
-		if (ast_register_verbose(console_verboser)) {
-			ast_log(LOG_WARNING, "Unable to register console verboser?\n");
-		}
-		WELCOME_MESSAGE;
-	}
-
-	if (ast_opt_console && !option_verbose) 
-		ast_verbose("[ Booting...\n");
-
 	/* For remote connections, change the name of the remote connection.
 	 * We do this for the benefit of init scripts (which need to know if/when
 	 * the main asterisk process has died yet). */
@@ -3488,21 +3522,18 @@
 		}
 	}
 
-	if (ast_opt_console && !option_verbose) {
-		ast_verbose("[ Reading Master Configuration ]\n");
-	}
-
 	ast_readconfig();
 	env_init();
 
 	if (ast_opt_remote && remotesock != NULL)
 		ast_copy_string((char *) cfg_paths.socket_path, remotesock, sizeof(cfg_paths.socket_path));
 
-	if (!ast_language_is_prefix && !ast_opt_remote)
-		ast_log(LOG_WARNING, "The 'languageprefix' option in asterisk.conf is deprecated; in a future release it will be removed, and your sound files will need to be organized in the 'new style' language layout.\n");
+	if (!ast_language_is_prefix && !ast_opt_remote) {
+		fprintf(stderr, "The 'languageprefix' option in asterisk.conf is deprecated; in a future release it will be removed, and your sound files will need to be organized in the 'new style' language layout.\n");
+	}
 
 	if (ast_opt_always_fork && (ast_opt_remote || ast_opt_console)) {
-		ast_log(LOG_WARNING, "'alwaysfork' is not compatible with console or remote console mode; ignored\n");
+		fprintf(stderr, "'alwaysfork' is not compatible with console or remote console mode; ignored\n");
 		ast_clear_flag(&ast_options, AST_OPT_FLAG_ALWAYS_FORK);
 	}
 
@@ -3511,12 +3542,12 @@
 		l.rlim_cur = RLIM_INFINITY;
 		l.rlim_max = RLIM_INFINITY;
 		if (setrlimit(RLIMIT_CORE, &l)) {
-			ast_log(LOG_WARNING, "Unable to disable core size resource limit: %s\n", strerror(errno));
+			fprintf(stderr, "Unable to disable core size resource limit: %s\n", strerror(errno));
 		}
 	}
 
 	if (getrlimit(RLIMIT_NOFILE, &l)) {
-		ast_log(LOG_WARNING, "Unable to check file descriptor limit: %s\n", strerror(errno));
+		fprintf(stderr, "Unable to check file descriptor limit: %s\n", strerror(errno));
 	}
 
 #if !defined(CONFIGURE_RAN_AS_ROOT)
@@ -3533,13 +3564,13 @@
 		}
 
 		if (!(fd = open("/dev/null", O_RDONLY))) {
-			ast_log(LOG_ERROR, "Cannot open a file descriptor at boot? %s\n", strerror(errno));
+			fprintf(stderr, "Cannot open a file descriptor at boot? %s\n", strerror(errno));
 			break; /* XXX Should we exit() here? XXX */
 		}
 
 		fd2 = (l.rlim_cur > sizeof(readers) * 8 ? sizeof(readers) * 8 : l.rlim_cur) - 1;
 		if (dup2(fd, fd2) < 0) {
-			ast_log(LOG_WARNING, "Cannot open maximum file descriptor %d at boot? %s\n", fd2, strerror(errno));
+			fprintf(stderr, "Cannot open maximum file descriptor %d at boot? %s\n", fd2, strerror(errno));
 			close(fd);
 			break;
 		}
@@ -3547,7 +3578,7 @@
 		FD_ZERO(&readers);
 		FD_SET(fd2, &readers);
 		if (ast_select(fd2 + 1, &readers, NULL, NULL, &tv) < 0) {
-			ast_log(LOG_WARNING, "Maximum select()able file descriptor is %d\n", FD_SETSIZE);
+			fprintf(stderr, "Maximum select()able file descriptor is %d\n", FD_SETSIZE);
 		}
 		ast_FD_SETSIZE = l.rlim_cur > ast_FDMAX ? ast_FDMAX : l.rlim_cur;
 		close(fd);
@@ -3573,7 +3604,7 @@
 		if (errno == EEXIST) {
 			rundir_exists = 1;
 		} else {
-			ast_log(LOG_WARNING, "Unable to create socket file directory.  Remote consoles will not be able to connect! (%s)\n", strerror(x));
+			fprintf(stderr, "Unable to create socket file directory.  Remote consoles will not be able to connect! (%s)\n", strerror(x));
 		}
 	}
 
@@ -3587,22 +3618,20 @@
 		struct group *gr;
 		gr = getgrnam(rungroup);
 		if (!gr) {
-			ast_log(LOG_WARNING, "No such group '%s'!\n", rungroup);
+			fprintf(stderr, "No such group '%s'!\n", rungroup);
 			exit(1);
 		}
 		if (!rundir_exists && chown(ast_config_AST_RUN_DIR, -1, gr->gr_gid)) {
-			ast_log(LOG_WARNING, "Unable to chgrp run directory to %d (%s)\n", (int) gr->gr_gid, rungroup);
+			fprintf(stderr, "Unable to chgrp run directory to %d (%s)\n", (int) gr->gr_gid, rungroup);
 		}
 		if (setgid(gr->gr_gid)) {
-			ast_log(LOG_WARNING, "Unable to setgid to %d (%s)\n", (int)gr->gr_gid, rungroup);
+			fprintf(stderr, "Unable to setgid to %d (%s)\n", (int)gr->gr_gid, rungroup);
 			exit(1);
 		}
 		if (setgroups(0, NULL)) {
-			ast_log(LOG_WARNING, "Unable to drop unneeded groups\n");
+			fprintf(stderr, "Unable to drop unneeded groups\n");
 			exit(1);
 		}
-		if (option_verbose)
-			ast_verbose("Running as group '%s'\n", rungroup);
 	}
 
 	if (runuser && !ast_test_flag(&ast_options, AST_OPT_FLAG_REMOTE)) {
@@ -3612,11 +3641,11 @@
 		struct passwd *pw;
 		pw = getpwnam(runuser);
 		if (!pw) {
-			ast_log(LOG_WARNING, "No such user '%s'!\n", runuser);
+			fprintf(stderr, "No such user '%s'!\n", runuser);
 			exit(1);
 		}
 		if (chown(ast_config_AST_RUN_DIR, pw->pw_uid, -1)) {
-			ast_log(LOG_WARNING, "Unable to chown run directory to %d (%s)\n", (int) pw->pw_uid, runuser);
+			fprintf(stderr, "Unable to chown run directory to %d (%s)\n", (int) pw->pw_uid, runuser);
 		}
 #ifdef HAVE_CAP
 		if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) {
@@ -3625,36 +3654,35 @@
 		}
 #endif /* HAVE_CAP */
 		if (!isroot && pw->pw_uid != geteuid()) {
-			ast_log(LOG_ERROR, "Asterisk started as nonroot, but runuser '%s' requested.\n", runuser);
+			fprintf(stderr, "Asterisk started as nonroot, but runuser '%s' requested.\n", runuser);
 			exit(1);
 		}
 		if (!rungroup) {
 			if (setgid(pw->pw_gid)) {
-				ast_log(LOG_WARNING, "Unable to setgid to %d!\n", (int)pw->pw_gid);
+				fprintf(stderr, "Unable to setgid to %d!\n", (int)pw->pw_gid);
 				exit(1);
 			}
 			if (isroot && initgroups(pw->pw_name, pw->pw_gid)) {
-				ast_log(LOG_WARNING, "Unable to init groups for '%s'\n", runuser);
+				fprintf(stderr, "Unable to init groups for '%s'\n", runuser);
 				exit(1);
 			}
 		}
 		if (setuid(pw->pw_uid)) {
-			ast_log(LOG_WARNING, "Unable to setuid to %d (%s)\n", (int)pw->pw_uid, runuser);
+			fprintf(stderr, "Unable to setuid to %d (%s)\n", (int)pw->pw_uid, runuser);
 			exit(1);
 		}
-		if (option_verbose)
-			ast_verbose("Running as user '%s'\n", runuser);
 #ifdef HAVE_CAP
 		if (has_cap) {
 			cap_t cap;
 
 			cap = cap_from_text("cap_net_admin=eip");
 
-			if (cap_set_proc(cap))
-				ast_log(LOG_WARNING, "Unable to install capabilities.\n");
-
-			if (cap_free(cap))
-				ast_log(LOG_WARNING, "Unable to drop capabilities.\n");
+			if (cap_set_proc(cap)) {
+				fprintf(stderr, "Unable to install capabilities.\n");
+			}
+			if (cap_free(cap)) {
+				fprintf(stderr, "Unable to drop capabilities.\n");
+			}
 		}
 #endif /* HAVE_CAP */
 	}
@@ -3664,7 +3692,7 @@
 #ifdef linux
 	if (geteuid() && ast_opt_dump_core) {
 		if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) {
-			ast_log(LOG_WARNING, "Unable to set the process for core dumps after changing to a non-root user. %s\n", strerror(errno));
+			fprintf(stderr, "Unable to set the process for core dumps after changing to a non-root user. %s\n", strerror(errno));
 		}
 	}
 #endif
@@ -3676,93 +3704,74 @@
 #endif
 		char dir[PATH_MAX];
 		if (!getcwd(dir, sizeof(dir)) || eaccess(dir, R_OK | X_OK | F_OK)) {
-			ast_log(LOG_ERROR, "Unable to access the running directory (%s).  Changing to '/' for compatibility.\n", strerror(errno));
+			fprintf(stderr, "Unable to access the running directory (%s).  Changing to '/' for compatibility.\n", strerror(errno));
 			/* If we cannot access the CWD, then we couldn't dump core anyway,
 			 * so chdir("/") won't break anything. */
 			if (chdir("/")) {
 				/* chdir(/) should never fail, so this ends up being a no-op */
-				ast_log(LOG_ERROR, "chdir(\"/\") failed?!! %s\n", strerror(errno));
+				fprintf(stderr, "chdir(\"/\") failed?!! %s\n", strerror(errno));
 			}
 		} else
 #endif /* defined(HAVE_EACCESS) || defined(HAVE_EUIDACCESS) */
 		if (!ast_opt_no_fork && !ast_opt_dump_core) {
 			/* Backgrounding, but no cores, so chdir won't break anything. */
 			if (chdir("/")) {
-				ast_log(LOG_ERROR, "Unable to chdir(\"/\") ?!! %s\n", strerror(errno));
+				fprintf(stderr, "Unable to chdir(\"/\") ?!! %s\n", strerror(errno));
 			}
 		}
 	}
 
-	ast_term_init();
-	printf("%s", term_end());
-	fflush(stdout);
-
-	if (ast_opt_console && !option_verbose) 
-		ast_verbose("[ Initializing Custom Configuration Options ]\n");
-	/* custom config setup */
-	register_config_cli();
-	read_config_maps();
-	
-	if (ast_opt_console) {
-		if (el_hist == NULL || el == NULL)
-			ast_el_initialize();
-
-		if (!ast_strlen_zero(filename))
-			ast_el_read_history(filename);
-	}
-
 	if (ast_tryconnect()) {
 		/* One is already running */
 		if (ast_opt_remote) {
+			multi_thread_safe = 1;
 			if (ast_opt_exec) {
 				ast_remotecontrol(xarg);
 				quit_handler(0, SHUTDOWN_FAST, 0);
 				exit(0);
 			}
+			print_intro_message(runuser, rungroup);
 			printf("%s", term_quit());
 			ast_remotecontrol(NULL);
 			quit_handler(0, SHUTDOWN_FAST, 0);
 			exit(0);
 		} else {
-			ast_log(LOG_ERROR, "Asterisk already running on %s.  Use 'asterisk -r' to connect.\n", ast_config_AST_SOCKET);
+			fprintf(stderr, "Asterisk already running on %s.  Use 'asterisk -r' to connect.\n", ast_config_AST_SOCKET);
 			printf("%s", term_quit());
 			exit(1);
 		}
 	} else if (ast_opt_remote || ast_opt_exec) {
-		ast_log(LOG_ERROR, "Unable to connect to remote asterisk (does %s exist?)\n", ast_config_AST_SOCKET);
+		fprintf(stderr, "Unable to connect to remote asterisk (does %s exist?)\n", ast_config_AST_SOCKET);
 		printf("%s", term_quit());
 		exit(1);
 	}
-	/* Blindly write pid file since we couldn't connect */
-	unlink(ast_config_AST_PID);
-	f = fopen(ast_config_AST_PID, "w");
-	if (f) {
-		fprintf(f, "%ld\n", (long)getpid());
-		fclose(f);
-	} else
-		ast_log(LOG_WARNING, "Unable to open pid file '%s': %s\n", ast_config_AST_PID, strerror(errno));
 
+	/* This needs to remain as high up in the initial start up as possible.
+	 * daemon causes a fork to occur, which has all sorts of unintended
+	 * consequences for things that interact with threads.  This call *must*
+	 * occur before anything in Asterisk spawns or manipulates thread related
+	 * primitives. */
 #if HAVE_WORKING_FORK
 	if (ast_opt_always_fork || !ast_opt_no_fork) {
 #ifndef HAVE_SBIN_LAUNCHD
 		if (daemon(1, 0) < 0) {
-			ast_log(LOG_ERROR, "daemon() failed: %s\n", strerror(errno));
+			fprintf(stderr, "daemon() failed: %s\n", strerror(errno));
 		}
-		ast_mainpid = getpid();
-		/* Blindly re-write pid file since we are forking */
-		unlink(ast_config_AST_PID);
-		f = fopen(ast_config_AST_PID, "w");
-		if (f) {
-			fprintf(f, "%ld\n", (long)ast_mainpid);
-			fclose(f);
-		} else
-			ast_log(LOG_WARNING, "Unable to open pid file '%s': %s\n", ast_config_AST_PID, strerror(errno));
 #else
-		ast_log(LOG_WARNING, "Mac OS X detected.  Use 'launchctl load /Library/LaunchDaemon/org.asterisk.asterisk.plist'.\n");
+		fprintf(stderr, "Mac OS X detected.  Use 'launchctl load /Library/LaunchDaemon/org.asterisk.asterisk.plist'.\n");
 #endif
 	}
 #endif
 
+	/* At this point everything has been forked successfully,
+	 * we have determined that we aren't attempting to connect to
+	 * an Asterisk instance, and that there isn't one already running. */
+	multi_thread_safe = 1;
+
+#if defined(__AST_DEBUG_MALLOC)
+	__ast_mm_init_phase_1();
+#endif	/* defined(__AST_DEBUG_MALLOC) */
+
 	/* Spawning of astcanary must happen AFTER the call to daemon(3) */
 	if (isroot && ast_opt_high_priority) {
 		snprintf(canary_filename, sizeof(canary_filename), "%s/alt.asterisk.canary.tweet.tweet.tweet", ast_config_AST_RUN_DIR);
@@ -3802,6 +3811,67 @@
 		ast_register_atexit(canary_exit);
 	}
 
+	/* Blindly write the PID file. */
+	ast_mainpid = getpid();
+	unlink(ast_config_AST_PID);
+	f = fopen(ast_config_AST_PID, "w");
+	if (f) {
+		fprintf(f, "%ld\n", (long)getpid());
+		fclose(f);
+	} else {
+		fprintf(stderr, "Unable to open pid file '%s': %s\n", ast_config_AST_PID, strerror(errno));
+	}
+
+	/* Initialize the terminal.  Since all processes have been forked,
+	 * we can now start using the standard log messages.
+	 */
+	ast_term_init();
+	printf("%s", term_end());
+	fflush(stdout);
+
+	print_intro_message(runuser, rungroup);
+
+	if (ast_opt_console && !option_verbose) {
+		ast_verbose("[ Initializing Custom Configuration Options ]\n");
+	}
+	/* custom config setup */
+	register_config_cli();
+	read_config_maps();
+
+	if (ast_opt_console) {
+		if (el_hist == NULL || el == NULL)
+			ast_el_initialize();
+
+		if (!ast_strlen_zero(filename))
+			ast_el_read_history(filename);
+	}
+
+	ast_ulaw_init();
+	ast_alaw_init();
+	tdd_init();
+	callerid_init();
+	ast_builtins_init();
+
+	if (ast_utils_init()) {
+		printf("%s", term_quit());
+		exit(1);
+	}
+
+	if (ast_tps_init()) {
+		printf("%s", term_quit());
+		exit(1);
+	}
+
+	if (ast_fd_init()) {
+		printf("%s", term_quit());
+		exit(1);
+	}
+
+	if (ast_pbx_init()) {
+		printf("%s", term_quit());
+		exit(1);
+	}
+
 	if (ast_event_init()) {
 		printf("%s", term_quit());
 		exit(1);
@@ -3981,12 +4051,14 @@
 
 	pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
 
-#ifdef __AST_DEBUG_MALLOC
-	__ast_mm_init();
-#endif	
+#if defined(__AST_DEBUG_MALLOC)
+	__ast_mm_init_phase_2();
+#endif	/* defined(__AST_DEBUG_MALLOC) */
 
 	ast_lastreloadtime = ast_startuptime = ast_tvnow();
+	ast_cli_register_multiple(cli_asterisk_shutdown, ARRAY_LEN(cli_asterisk_shutdown));
 	ast_cli_register_multiple(cli_asterisk, ARRAY_LEN(cli_asterisk));
+	ast_register_atexit(main_atexit);
 
 	run_startup_commands();
 
diff -uNrw asterisk-1.8.19.0/main/astmm.c asterisk-1.8.20.0-rc1/main/astmm.c
--- asterisk-1.8.19.0/main/astmm.c	2012-09-08 01:05:21.000000000 +0200
+++ asterisk-1.8.20.0-rc1/main/astmm.c	2012-12-08 00:40:19.000000000 +0100
@@ -1,7 +1,7 @@
 /*
  * Asterisk -- An open source telephony toolkit.
  *
- * Copyright (C) 1999 - 2006, Digium, Inc.
+ * Copyright (C) 1999 - 2012, Digium, Inc.
  *
  * Mark Spencer <markster@digium.com>
  *
@@ -21,6 +21,7 @@
  * \brief Memory Management
  *
  * \author Mark Spencer <markster@digium.com>
+ * \author Richard Mudgett <rmudgett@digium.com>
  */
 
 /*** MODULEINFO
@@ -29,9 +30,9 @@
 
 #include "asterisk.h"
 
-#ifdef __AST_DEBUG_MALLOC
+#if defined(__AST_DEBUG_MALLOC)
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 372655 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 377398 $")
 
 #include "asterisk/paths.h"	/* use ast_config_AST_LOG_DIR */
 #include <stddef.h>
@@ -42,7 +43,12 @@
 #include "asterisk/strings.h"
 #include "asterisk/unaligned.h"
 
-#define SOME_PRIME 563
+/*!
+ * The larger the number the faster memory can be freed.
+ * However, more memory then is used for the regions[] hash
+ * table.
+ */
+#define SOME_PRIME 1567
 
 enum func_type {
 	FUNC_CALLOC = 1,
@@ -64,36 +70,79 @@
 #undef vasprintf
 #undef asprintf
 
-#define FENCE_MAGIC 0xdeadbeef
+#define FENCE_MAGIC		0xdeadbeef	/*!< Allocated memory high/low fence overwrite check. */
+#define FREED_MAGIC		0xdeaddead	/*!< Freed memory wipe filler. */
+#define MALLOC_FILLER	0x55		/*!< Malloced memory filler.  Must not be zero. */
 
 static FILE *mmlog;
 
-/* NOTE: Be EXTREMELY careful with modifying this structure; the total size of this structure
-   must result in 'automatic' alignment so that the 'fence' field lands exactly at the end of
-   the structure in memory (and thus immediately before the allocated region the fence is
-   supposed to be used to monitor). In other words, we cannot allow the compiler to insert
-   any padding between this structure and anything following it, so add up the sizes of all the
-   fields and compare to sizeof(struct ast_region)... if they don't match, then the compiler
-   is padding the structure and either the fields need to be rearranged to eliminate internal
-   padding, or a dummy field will need to be inserted before the 'fence' field to push it to
-   the end of the actual space it will consume. Note that this must be checked for both 32-bit
-   and 64-bit platforms, as the sizes of pointers and 'size_t' differ on these platforms.
-*/
-
-static struct ast_region {
-	struct ast_region *next;
+struct ast_region {
+	AST_LIST_ENTRY(ast_region) node;
 	size_t len;
-	char file[64];
-	char func[40];
+	unsigned int cache;		/* region was allocated as part of a cache pool */
 	unsigned int lineno;
 	enum func_type which;
-	unsigned int cache;		/* region was allocated as part of a cache pool */
+	char file[64];
+	char func[40];
+
+	/*!
+	 * \brief Lower guard fence.
+	 *
+	 * \note Must be right before data[].
+	 *
+	 * \note Padding between fence and data[] is irrelevent because
+	 * data[] is used to fill in the lower fence check value and not
+	 * the fence member.  The fence member is to ensure that there
+	 * is space reserved for the fence check value.
+	 */
 	unsigned int fence;
-	unsigned char data[0];
-} *regions[SOME_PRIME];
+	/*!
+	 * \brief Location of the requested malloc block to return.
+	 *
+	 * \note Must have the same alignment that malloc returns.
+	 * i.e., It is suitably aligned for any kind of varible.
+	 */
+	unsigned char data[0] __attribute__((aligned));
+};
+
+/*! Hash table of lists of active allocated memory regions. */
+static struct ast_region *regions[SOME_PRIME];
+
+/*! Number of freed regions to keep around to delay actually freeing them. */
+#define FREED_MAX_COUNT		1500
+
+/*! Maximum size of a minnow block */
+#define MINNOWS_MAX_SIZE	50
+
+struct ast_freed_regions {
+	/*! Memory regions that have been freed. */
+	struct ast_region *regions[FREED_MAX_COUNT];
+	/*! Next index into freed regions[] to use. */
+	int index;
+};
+
+/*! Large memory blocks that have been freed. */
+static struct ast_freed_regions whales;
+/*! Small memory blocks that have been freed. */
+static struct ast_freed_regions minnows;
+
+enum summary_opts {
+	/*! No summary at exit. */
+	SUMMARY_OFF,
+	/*! Bit set if summary by line at exit. */
+	SUMMARY_BY_LINE = (1 << 0),
+	/*! Bit set if summary by function at exit. */
+	SUMMARY_BY_FUNC = (1 << 1),
+	/*! Bit set if summary by file at exit. */
+	SUMMARY_BY_FILE = (1 << 2),
+};
+
+/*! Summary options of unfreed regions at exit. */
+static enum summary_opts atexit_summary;
+/*! Nonzero if the unfreed regions are listed at exit. */
+static int atexit_list;
 
-#define HASH(a) \
-	(((unsigned long)(a)) % SOME_PRIME)
+#define HASH(a)		(((unsigned long)(a)) % ARRAY_LEN(regions))
 
 /*! Tracking this mutex will cause infinite recursion, as the mutex tracking
  *  code allocates memory */
@@ -108,95 +157,285 @@
 		}                                    \
 	} while (0)
 
-static inline void *__ast_alloc_region(size_t size, const enum func_type which, const char *file, int lineno, const char *func, unsigned int cache)
+/*!
+ * \internal
+ *
+ * \note If DO_CRASH is not defined then the function returns.
+ *
+ * \return Nothing
+ */
+static void my_do_crash(void)
+{
+	/*
+	 * Give the logger a chance to get the message out, just in case
+	 * we abort(), or Asterisk crashes due to whatever problem just
+	 * happened.
+	 */
+	usleep(1);
+	ast_do_crash();
+}
+
+static void *__ast_alloc_region(size_t size, const enum func_type which, const char *file, int lineno, const char *func, unsigned int cache)
 {
 	struct ast_region *reg;
-	void *ptr = NULL;
 	unsigned int *fence;
 	int hash;
 
 	if (!(reg = malloc(size + sizeof(*reg) + sizeof(*fence)))) {
-		astmm_log("Memory Allocation Failure - '%d' bytes in function %s "
-			  "at line %d of %s\n", (int) size, func, lineno, file);
+		astmm_log("Memory Allocation Failure - '%d' bytes at %s %s() line %d\n",
+			(int) size, file, func, lineno);
 		return NULL;
 	}
 
-	ast_copy_string(reg->file, file, sizeof(reg->file));
-	ast_copy_string(reg->func, func, sizeof(reg->func));
-	reg->lineno = lineno;
 	reg->len = size;
-	reg->which = which;
 	reg->cache = cache;
-	ptr = reg->data;
-	hash = HASH(ptr);
-	reg->fence = FENCE_MAGIC;
-	fence = (ptr + reg->len);
+	reg->lineno = lineno;
+	reg->which = which;
+	ast_copy_string(reg->file, file, sizeof(reg->file));
+	ast_copy_string(reg->func, func, sizeof(reg->func));
+
+	/*
+	 * Init lower fence.
+	 *
+	 * We use the bytes just preceeding reg->data and not reg->fence
+	 * because there is likely to be padding between reg->fence and
+	 * reg->data for reg->data alignment.
+	 */
+	fence = (unsigned int *) (reg->data - sizeof(*fence));
+	*fence = FENCE_MAGIC;
+
+	/* Init higher fence. */
+	fence = (unsigned int *) (reg->data + reg->len);
 	put_unaligned_uint32(fence, FENCE_MAGIC);
 
+	hash = HASH(reg->data);
 	ast_mutex_lock(&reglock);
-	reg->next = regions[hash];
+	AST_LIST_NEXT(reg, node) = regions[hash];
 	regions[hash] = reg;
 	ast_mutex_unlock(&reglock);
 
-	return ptr;
+	return reg->data;
 }
 
-static inline size_t __ast_sizeof_region(void *ptr)
+/*!
+ * \internal
+ * \brief Wipe the region payload data with a known value.
+ *
+ * \param reg Region block to be wiped.
+ *
+ * \return Nothing
+ */
+static void region_data_wipe(struct ast_region *reg)
 {
-	int hash = HASH(ptr);
-	struct ast_region *reg;
-	size_t len = 0;
+	void *end;
+	unsigned int *pos;
 	
-	ast_mutex_lock(&reglock);
-	for (reg = regions[hash]; reg; reg = reg->next) {
-		if (reg->data == ptr) {
-			len = reg->len;
+	/*
+	 * Wipe the lower fence, the payload, and whatever amount of the
+	 * higher fence that falls into alignment with the payload.
+	 */
+	end = reg->data + reg->len;
+	for (pos = &reg->fence; (void *) pos <= end; ++pos) {
+		*pos = FREED_MAGIC;
+	}
+}
+
+/*!
+ * \internal
+ * \brief Check the region payload data for memory corruption.
+ *
+ * \param reg Region block to be checked.
+ *
+ * \return Nothing
+ */
+static void region_data_check(struct ast_region *reg)
+{
+	void *end;
+	unsigned int *pos;
+
+	/*
+	 * Check the lower fence, the payload, and whatever amount of
+	 * the higher fence that falls into alignment with the payload.
+	 */
+	end = reg->data + reg->len;
+	for (pos = &reg->fence; (void *) pos <= end; ++pos) {
+		if (*pos != FREED_MAGIC) {
+			astmm_log("WARNING: Memory corrupted after free of %p allocated at %s %s() line %d\n",
+				reg->data, reg->file, reg->func, reg->lineno);
+			my_do_crash();
 			break;
 		}
 	}
+}
+
+/*!
+ * \internal
+ * \brief Flush the circular array of freed regions.
+ *
+ * \param freed Already freed region blocks storage.
+ *
+ * \return Nothing
+ */
+static void freed_regions_flush(struct ast_freed_regions *freed)
+{
+	int idx;
+	struct ast_region *old;
+
+	ast_mutex_lock(&reglock);
+	for (idx = 0; idx < ARRAY_LEN(freed->regions); ++idx) {
+		old = freed->regions[idx];
+		freed->regions[idx] = NULL;
+		if (old) {
+			region_data_check(old);
+			free(old);
+		}
+	}
+	freed->index = 0;
+	ast_mutex_unlock(&reglock);
+}
+
+/*!
+ * \internal
+ * \brief Delay freeing a region block.
+ *
+ * \param freed Already freed region blocks storage.
+ * \param reg Region block to be freed.
+ *
+ * \return Nothing
+ */
+static void region_free(struct ast_freed_regions *freed, struct ast_region *reg)
+{
+	struct ast_region *old;
+
+	region_data_wipe(reg);
+
+	ast_mutex_lock(&reglock);
+	old = freed->regions[freed->index];
+	freed->regions[freed->index] = reg;
+
+	++freed->index;
+	if (ARRAY_LEN(freed->regions) <= freed->index) {
+		freed->index = 0;
+	}
 	ast_mutex_unlock(&reglock);
 
-	return len;
+	if (old) {
+		region_data_check(old);
+		free(old);
+	}
 }
 
-static void __ast_free_region(void *ptr, const char *file, int lineno, const char *func)
+/*!
+ * \internal
+ * \brief Remove a region from the active regions.
+ *
+ * \param ptr Region payload data pointer.
+ *
+ * \retval region on success.
+ * \retval NULL if not found.
+ */
+static struct ast_region *region_remove(void *ptr)
 {
 	int hash;
-	struct ast_region *reg, *prev = NULL;
-	unsigned int *fence;
-
-	if (!ptr)
-		return;
+	struct ast_region *reg;
+	struct ast_region *prev = NULL;
 
 	hash = HASH(ptr);
 
 	ast_mutex_lock(&reglock);
-	for (reg = regions[hash]; reg; reg = reg->next) {
+	for (reg = regions[hash]; reg; reg = AST_LIST_NEXT(reg, node)) {
 		if (reg->data == ptr) {
-			if (prev)
-				prev->next = reg->next;
-			else
-				regions[hash] = reg->next;
+			if (prev) {
+				AST_LIST_NEXT(prev, node) = AST_LIST_NEXT(reg, node);
+			} else {
+				regions[hash] = AST_LIST_NEXT(reg, node);
+			}
 			break;
 		}
 		prev = reg;
 	}
 	ast_mutex_unlock(&reglock);
 
-	if (reg) {
-		fence = (unsigned int *)(reg->data + reg->len);
-		if (reg->fence != FENCE_MAGIC) {
-			astmm_log("WARNING: Low fence violation at %p, in %s of %s, "
-				"line %d\n", reg->data, reg->func, reg->file, reg->lineno);
+	return reg;
 		}
+
+/*!
+ * \internal
+ * \brief Check the fences of a region.
+ *
+ * \param reg Region block to check.
+ *
+ * \return Nothing
+ */
+static void region_check_fences(struct ast_region *reg)
+{
+	unsigned int *fence;
+
+	/*
+	 * We use the bytes just preceeding reg->data and not reg->fence
+	 * because there is likely to be padding between reg->fence and
+	 * reg->data for reg->data alignment.
+	 */
+	fence = (unsigned int *) (reg->data - sizeof(*fence));
+	if (*fence != FENCE_MAGIC) {
+		astmm_log("WARNING: Low fence violation of %p allocated at %s %s() line %d\n",
+			reg->data, reg->file, reg->func, reg->lineno);
+		my_do_crash();
+	}
+	fence = (unsigned int *) (reg->data + reg->len);
 		if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
-			astmm_log("WARNING: High fence violation at %p, in %s of %s, "
-				"line %d\n", reg->data, reg->func, reg->file, reg->lineno);
+		astmm_log("WARNING: High fence violation of %p allocated at %s %s() line %d\n",
+			reg->data, reg->file, reg->func, reg->lineno);
+		my_do_crash();
+	}
+}
+
+/*!
+ * \internal
+ * \brief Check the fences of all regions currently allocated.
+ *
+ * \return Nothing
+ */
+static void regions_check_all_fences(void)
+{
+	int idx;
+	struct ast_region *reg;
+
+	ast_mutex_lock(&reglock);
+	for (idx = 0; idx < ARRAY_LEN(regions); ++idx) {
+		for (reg = regions[idx]; reg; reg = AST_LIST_NEXT(reg, node)) {
+			region_check_fences(reg);
+		}
+	}
+	ast_mutex_unlock(&reglock);
+}
+
+static void __ast_free_region(void *ptr, const char *file, int lineno, const char *func)
+{
+	struct ast_region *reg;
+
+	if (!ptr) {
+		return;
+	}
+
+	reg = region_remove(ptr);
+	if (reg) {
+		region_check_fences(reg);
+
+		if (reg->len <= MINNOWS_MAX_SIZE) {
+			region_free(&minnows, reg);
+		} else {
+			region_free(&whales, reg);
 		}
-		free(reg);
 	} else {
-		astmm_log("WARNING: Freeing unused memory at %p, in %s of %s, line %d\n",	
-			ptr, func, file, lineno);
+		/*
+		 * This memory region is not registered.  It could be because of
+		 * a double free or the memory block was not allocated by the
+		 * malloc debug code.
+		 */
+		astmm_log("WARNING: Freeing unregistered memory %p by %s %s() line %d\n",
+			ptr, file, func, lineno);
+		my_do_crash();
 	}
 }
 
@@ -204,8 +443,10 @@
 {
 	void *ptr;
 
-	if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 0))) 
+	ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 0);
+	if (ptr) {
 		memset(ptr, 0, size * nmemb);
+	}
 
 	return ptr;
 }
@@ -214,15 +455,25 @@
 {
 	void *ptr;
 
-	if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 1))) 
+	ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 1);
+	if (ptr) {
 		memset(ptr, 0, size * nmemb);
+	}
 
 	return ptr;
 }
 
 void *__ast_malloc(size_t size, const char *file, int lineno, const char *func) 
 {
-	return __ast_alloc_region(size, FUNC_MALLOC, file, lineno, func, 0);
+	void *ptr;
+
+	ptr = __ast_alloc_region(size, FUNC_MALLOC, file, lineno, func, 0);
+	if (ptr) {
+		/* Make sure that the malloced memory is not zero. */
+		memset(ptr, MALLOC_FILLER, size);
+	}
+
+	return ptr;
 }
 
 void __ast_free(void *ptr, const char *file, int lineno, const char *func) 
@@ -230,28 +481,71 @@
 	__ast_free_region(ptr, file, lineno, func);
 }
 
+/*!
+ * \note reglock must be locked before calling.
+ */
+static struct ast_region *region_find(void *ptr)
+{
+	int hash;
+	struct ast_region *reg;
+
+	hash = HASH(ptr);
+	for (reg = regions[hash]; reg; reg = AST_LIST_NEXT(reg, node)) {
+		if (reg->data == ptr) {
+			break;
+		}
+	}
+
+	return reg;
+}
+
 void *__ast_realloc(void *ptr, size_t size, const char *file, int lineno, const char *func) 
 {
-	void *tmp;
-	size_t len = 0;
+	size_t len;
+	struct ast_region *found;
+	void *new_mem;
 
-	if (ptr && !(len = __ast_sizeof_region(ptr))) {
-		astmm_log("WARNING: Realloc of unalloced memory at %p, in %s of %s, "
-			"line %d\n", ptr, func, file, lineno);
+	if (ptr) {
+		ast_mutex_lock(&reglock);
+		found = region_find(ptr);
+		if (!found) {
+			ast_mutex_unlock(&reglock);
+			astmm_log("WARNING: Realloc of unregistered memory %p by %s %s() line %d\n",
+				ptr, file, func, lineno);
+			my_do_crash();
 		return NULL;
 	}
+		len = found->len;
+		ast_mutex_unlock(&reglock);
+	} else {
+		found = NULL;
+		len = 0;
+	}
 
-	if (!(tmp = __ast_alloc_region(size, FUNC_REALLOC, file, lineno, func, 0)))
+	if (!size) {
+		__ast_free_region(ptr, file, lineno, func);
 		return NULL;
+	}
 
-	if (len > size)
-		len = size;
-	if (ptr) {
-		memcpy(tmp, ptr, len);
+	new_mem = __ast_alloc_region(size, FUNC_REALLOC, file, lineno, func, 0);
+	if (new_mem) {
+		if (found) {
+			/* Copy the old data to the new malloced memory. */
+			if (size <= len) {
+				memcpy(new_mem, ptr, size);
+			} else {
+				memcpy(new_mem, ptr, len);
+				/* Make sure that the added memory is not zero. */
+				memset(new_mem + len, MALLOC_FILLER, size - len);
+			}
 		__ast_free_region(ptr, file, lineno, func);
+		} else {
+			/* Make sure that the malloced memory is not zero. */
+			memset(new_mem, MALLOC_FILLER, size);
+		}
 	}
 	
-	return tmp;
+	return new_mem;
 }
 
 char *__ast_strdup(const char *s, const char *file, int lineno, const char *func) 
@@ -327,83 +621,195 @@
 	return size;
 }
 
-static char *handle_memory_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+static char *handle_memory_atexit_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "memory atexit list";
+		e->usage =
+			"Usage: memory atexit list {on|off}\n"
+			"       Enable dumping a list of still allocated memory segments at exit.\n";
+		return NULL;
+	case CLI_GENERATE:
+		if (a->pos == 3) {
+			const char * const options[] = { "off", "on", NULL };
+
+			return ast_cli_complete(a->word, options, a->n);
+		}
+		return NULL;
+	}
+
+	if (a->argc != 4) {
+		return CLI_SHOWUSAGE;
+	}
+
+	if (ast_true(a->argv[3])) {
+		atexit_list = 1;
+	} else if (ast_false(a->argv[3])) {
+		atexit_list = 0;
+	} else {
+		return CLI_SHOWUSAGE;
+	}
+
+	ast_cli(a->fd, "The atexit list is: %s\n", atexit_list ? "On" : "Off");
+
+	return CLI_SUCCESS;
+}
+
+static char *handle_memory_atexit_summary(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	char buf[80];
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "memory atexit summary";
+		e->usage =
+			"Usage: memory atexit summary {off|byline|byfunc|byfile}\n"
+			"       Summary of still allocated memory segments at exit options.\n"
+			"       off - Disable at exit summary.\n"
+			"       byline - Enable at exit summary by file line number.\n"
+			"       byfunc - Enable at exit summary by function name.\n"
+			"       byfile - Enable at exit summary by file.\n"
+			"\n"
+			"       Note: byline, byfunc, and byfile are cumulative enables.\n";
+		return NULL;
+	case CLI_GENERATE:
+		if (a->pos == 3) {
+			const char * const options[] = { "off", "byline", "byfunc", "byfile", NULL };
+
+			return ast_cli_complete(a->word, options, a->n);
+		}
+		return NULL;
+	}
+
+	if (a->argc != 4) {
+		return CLI_SHOWUSAGE;
+	}
+
+	if (ast_false(a->argv[3])) {
+		atexit_summary = SUMMARY_OFF;
+	} else if (!strcasecmp(a->argv[3], "byline")) {
+		atexit_summary |= SUMMARY_BY_LINE;
+	} else if (!strcasecmp(a->argv[3], "byfunc")) {
+		atexit_summary |= SUMMARY_BY_FUNC;
+	} else if (!strcasecmp(a->argv[3], "byfile")) {
+		atexit_summary |= SUMMARY_BY_FILE;
+	} else {
+		return CLI_SHOWUSAGE;
+	}
+
+	if (atexit_summary) {
+		buf[0] = '\0';
+		if (atexit_summary & SUMMARY_BY_LINE) {
+			strcat(buf, "byline");
+		}
+		if (atexit_summary & SUMMARY_BY_FUNC) {
+			if (buf[0]) {
+				strcat(buf, " | ");
+			}
+			strcat(buf, "byfunc");
+		}
+		if (atexit_summary & SUMMARY_BY_FILE) {
+			if (buf[0]) {
+				strcat(buf, " | ");
+			}
+			strcat(buf, "byfile");
+		}
+	} else {
+		strcpy(buf, "Off");
+	}
+	ast_cli(a->fd, "The atexit summary is: %s\n", buf);
+
+	return CLI_SUCCESS;
+}
+
+static char *handle_memory_show_allocations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
 	const char *fn = NULL;
 	struct ast_region *reg;
-	unsigned int x;
+	unsigned int idx;
 	unsigned int len = 0;
 	unsigned int cache_len = 0;
 	unsigned int count = 0;
-	unsigned int *fence;
 
 	switch (cmd) {
 	case CLI_INIT:
 		e->command = "memory show allocations";
 		e->usage =
-			"Usage: memory show allocations [<file>]\n"
-			"       Dumps a list of all segments of allocated memory, optionally\n"
-			"       limited to those from a specific file\n";
+			"Usage: memory show allocations [<file>|anomalies]\n"
+			"       Dumps a list of segments of allocated memory.\n"
+			"       Defaults to listing all memory allocations.\n"
+			"       <file> - Restricts output to memory allocated by the file.\n"
+			"       anomalies - Only check for fence violations.\n";
 		return NULL;
 	case CLI_GENERATE:
 		return NULL;
 	}
 
-
-	if (a->argc > 3)
+	if (a->argc == 4) {
 		fn = a->argv[3];
-
-	ast_mutex_lock(&reglock);
-	for (x = 0; x < SOME_PRIME; x++) {
-		for (reg = regions[x]; reg; reg = reg->next) {
-			if (!fn || !strcasecmp(fn, reg->file) || !strcasecmp(fn, "anomolies")) {
-				fence = (unsigned int *)(reg->data + reg->len);
-				if (reg->fence != FENCE_MAGIC) {
-					astmm_log("WARNING: Low fence violation at %p, "
-						"in %s of %s, line %d\n", reg->data, 
-						reg->func, reg->file, reg->lineno);
+	} else if (a->argc != 3) {
+		return CLI_SHOWUSAGE;
 				}
-				if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
-					astmm_log("WARNING: High fence violation at %p, in %s of %s, "
-						"line %d\n", reg->data, reg->func, reg->file, reg->lineno);
+
+	/* Look for historical misspelled option as well. */
+	if (fn && (!strcasecmp(fn, "anomalies") || !strcasecmp(fn, "anomolies"))) {
+		regions_check_all_fences();
+		ast_cli(a->fd, "Anomaly check complete.\n");
+		return CLI_SUCCESS;
 				}
+
+	ast_mutex_lock(&reglock);
+	for (idx = 0; idx < ARRAY_LEN(regions); ++idx) {
+		for (reg = regions[idx]; reg; reg = AST_LIST_NEXT(reg, node)) {
+			if (fn && strcasecmp(fn, reg->file)) {
+				continue;
 			}
-			if (!fn || !strcasecmp(fn, reg->file)) {
-				ast_cli(a->fd, "%10d bytes allocated%s in %20s at line %5d of %s\n", 
-					(int) reg->len, reg->cache ? " (cache)" : "", 
+
+			region_check_fences(reg);
+
+			ast_cli(a->fd, "%10u bytes allocated%s by %20s() line %5u of %s\n",
+				(unsigned int) reg->len, reg->cache ? " (cache)" : "",
 					reg->func, reg->lineno, reg->file);
+
 				len += reg->len;
-				if (reg->cache)
+			if (reg->cache) {
 					cache_len += reg->len;
-				count++;
 			}
+			++count;
 		}
 	}
 	ast_mutex_unlock(&reglock);
 	
-	if (cache_len)
-		ast_cli(a->fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
-	else
-		ast_cli(a->fd, "%d bytes allocated in %d allocations\n", len, count);
+	if (cache_len) {
+		ast_cli(a->fd, "%u bytes allocated (%u in caches) in %u allocations\n",
+			len, cache_len, count);
+	} else {
+		ast_cli(a->fd, "%u bytes allocated in %u allocations\n", len, count);
+	}
 	
 	return CLI_SUCCESS;
 }
 
 static char *handle_memory_show_summary(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
+#define my_max(a, b) ((a) >= (b) ? (a) : (b))
+
 	const char *fn = NULL;
-	int x;
+	int idx;
+	int cmp;
 	struct ast_region *reg;
 	unsigned int len = 0;
 	unsigned int cache_len = 0;
-	int count = 0;
+	unsigned int count = 0;
 	struct file_summary {
-		char fn[80];
-		int len;
-		int cache_len;
-		int count;
 		struct file_summary *next;
-	} *list = NULL, *cur;
+		unsigned int len;
+		unsigned int cache_len;
+		unsigned int count;
+		unsigned int lineno;
+		char name[my_max(sizeof(reg->file), sizeof(reg->func))];
+	} *list = NULL, *cur, **prev;
 	
 	switch (cmd) {
 	case CLI_INIT:
@@ -411,37 +817,77 @@
 		e->usage =
 			"Usage: memory show summary [<file>]\n"
 			"       Summarizes heap memory allocations by file, or optionally\n"
-			"by function, if a file is specified\n";
+			"       by line, if a file is specified.\n";
 		return NULL;
 	case CLI_GENERATE:
 		return NULL;
 	}
 
-	if (a->argc > 3) 
+	if (a->argc == 4) {
 		fn = a->argv[3];
+	} else if (a->argc != 3) {
+		return CLI_SHOWUSAGE;
+	}
 
 	ast_mutex_lock(&reglock);
-	for (x = 0; x < SOME_PRIME; x++) {
-		for (reg = regions[x]; reg; reg = reg->next) {
-			if (fn && strcasecmp(fn, reg->file))
+	for (idx = 0; idx < ARRAY_LEN(regions); ++idx) {
+		for (reg = regions[idx]; reg; reg = AST_LIST_NEXT(reg, node)) {
+			if (fn) {
+				if (strcasecmp(fn, reg->file)) {
 				continue;
+				}
 
-			for (cur = list; cur; cur = cur->next) {
-				if ((!fn && !strcmp(cur->fn, reg->file)) || (fn && !strcmp(cur->fn, reg->func)))
+				/* Sort list by func/lineno.  Find existing or place to insert. */
+				for (prev = &list; (cur = *prev); prev = &cur->next) {
+					cmp = strcmp(cur->name, reg->func);
+					if (cmp < 0) {
+						continue;
+					}
+					if (cmp > 0) {
+						/* Insert before current */
+						cur = NULL;
+						break;
+					}
+					cmp = cur->lineno - reg->lineno;
+					if (cmp < 0) {
+						continue;
+					}
+					if (cmp > 0) {
+						/* Insert before current */
+						cur = NULL;
+					}
 					break;
 			}
+			} else {
+				/* Sort list by filename.  Find existing or place to insert. */
+				for (prev = &list; (cur = *prev); prev = &cur->next) {
+					cmp = strcmp(cur->name, reg->file);
+					if (cmp < 0) {
+						continue;
+					}
+					if (cmp > 0) {
+						/* Insert before current */
+						cur = NULL;
+					}
+					break;
+				}
+			}
+
 			if (!cur) {
 				cur = ast_alloca(sizeof(*cur));
 				memset(cur, 0, sizeof(*cur));
-				ast_copy_string(cur->fn, fn ? reg->func : reg->file, sizeof(cur->fn));
-				cur->next = list;
-				list = cur;
+				cur->lineno = reg->lineno;
+				ast_copy_string(cur->name, fn ? reg->func : reg->file, sizeof(cur->name));
+
+				cur->next = *prev;
+				*prev = cur;
 			}
 
 			cur->len += reg->len;
-			if (reg->cache)
+			if (reg->cache) {
 				cur->cache_len += reg->len;
-			cur->count++;
+			}
+			++cur->count;
 		}
 	}
 	ast_mutex_unlock(&reglock);
@@ -453,55 +899,512 @@
 		count += cur->count;
 		if (cur->cache_len) {
 			if (fn) {
-				ast_cli(a->fd, "%10d bytes (%10d cache) in %d allocations in function '%s' of '%s'\n", 
-					cur->len, cur->cache_len, cur->count, cur->fn, fn);
+				ast_cli(a->fd, "%10u bytes (%10u cache) in %10u allocations by %20s() line %5u of %s\n",
+					cur->len, cur->cache_len, cur->count, cur->name, cur->lineno, fn);
 			} else {
-				ast_cli(a->fd, "%10d bytes (%10d cache) in %d allocations in file '%s'\n", 
-					cur->len, cur->cache_len, cur->count, cur->fn);
+				ast_cli(a->fd, "%10u bytes (%10u cache) in %10u allocations in file %s\n",
+					cur->len, cur->cache_len, cur->count, cur->name);
 			}
 		} else {
 			if (fn) {
-				ast_cli(a->fd, "%10d bytes in %d allocations in function '%s' of '%s'\n", 
-					cur->len, cur->count, cur->fn, fn);
+				ast_cli(a->fd, "%10u bytes in %10u allocations by %20s() line %5u of %s\n",
+					cur->len, cur->count, cur->name, cur->lineno, fn);
 			} else {
-				ast_cli(a->fd, "%10d bytes in %d allocations in file '%s'\n", 
-					cur->len, cur->count, cur->fn);
+				ast_cli(a->fd, "%10u bytes in %10u allocations in file %s\n",
+					cur->len, cur->count, cur->name);
 			}
 		}
 	}
 
-	if (cache_len)
-		ast_cli(a->fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
-	else
-		ast_cli(a->fd, "%d bytes allocated in %d allocations\n", len, count);
+	if (cache_len) {
+		ast_cli(a->fd, "%u bytes allocated (%u in caches) in %u allocations\n",
+			len, cache_len, count);
+	} else {
+		ast_cli(a->fd, "%u bytes allocated in %u allocations\n", len, count);
+	}
 
 	return CLI_SUCCESS;
 }
 
 static struct ast_cli_entry cli_memory[] = {
-	AST_CLI_DEFINE(handle_memory_show, "Display outstanding memory allocations"),
+	AST_CLI_DEFINE(handle_memory_atexit_list, "Enable memory allocations not freed at exit list."),
+	AST_CLI_DEFINE(handle_memory_atexit_summary, "Enable memory allocations not freed at exit summary."),
+	AST_CLI_DEFINE(handle_memory_show_allocations, "Display outstanding memory allocations"),
 	AST_CLI_DEFINE(handle_memory_show_summary, "Summarize outstanding memory allocations"),
 };
 
-void __ast_mm_init(void)
+AST_LIST_HEAD_NOLOCK(region_list, ast_region);
+
+/*!
+ * \internal
+ * \brief Convert the allocated regions hash table to a list.
+ *
+ * \param list Fill list with the allocated regions.
+ *
+ * \details
+ * Take all allocated regions from the regions[] and put them
+ * into the list.
+ *
+ * \note reglock must be locked before calling.
+ *
+ * \note This function is destructive to the regions[] lists.
+ *
+ * \return Length of list created.
+ */
+static size_t mm_atexit_hash_list(struct region_list *list)
+{
+	struct ast_region *reg;
+	size_t total_length;
+	int idx;
+
+	total_length = 0;
+	for (idx = 0; idx < ARRAY_LEN(regions); ++idx) {
+		while ((reg = regions[idx])) {
+			regions[idx] = AST_LIST_NEXT(reg, node);
+			AST_LIST_NEXT(reg, node) = NULL;
+			AST_LIST_INSERT_HEAD(list, reg, node);
+			++total_length;
+		}
+	}
+	return total_length;
+}
+
+/*!
+ * \internal
+ * \brief Put the regions list into the allocated regions hash table.
+ *
+ * \param list List to put into the allocated regions hash table.
+ *
+ * \note reglock must be locked before calling.
+ *
+ * \return Nothing
+ */
+static void mm_atexit_hash_restore(struct region_list *list)
 {
-	char filename[PATH_MAX];
-	size_t pad = sizeof(struct ast_region) - offsetof(struct ast_region, data);
+	struct ast_region *reg;
+	int hash;
+
+	while ((reg = AST_LIST_REMOVE_HEAD(list, node))) {
+		hash = HASH(reg->data);
+		AST_LIST_NEXT(reg, node) = regions[hash];
+		regions[hash] = reg;
+	}
+}
+
+/*!
+ * \internal
+ * \brief Sort regions comparision.
+ *
+ * \param left Region to compare.
+ * \param right Region to compare.
+ *
+ * \retval <0 if left < right
+ * \retval =0 if left == right
+ * \retval >0 if left > right
+ */
+static int mm_atexit_cmp(struct ast_region *left, struct ast_region *right)
+{
+	int cmp;
+	ptrdiff_t cmp_ptr;
+	ssize_t cmp_size;
+
+	/* Sort by filename. */
+	cmp = strcmp(left->file, right->file);
+	if (cmp) {
+		return cmp;
+	}
+
+	/* Sort by line number. */
+	cmp = left->lineno - right->lineno;
+	if (cmp) {
+		return cmp;
+	}
+
+	/* Sort by allocated size. */
+	cmp_size = left->len - right->len;
+	if (cmp_size) {
+		if (cmp_size < 0) {
+			return -1;
+		}
+		return 1;
+	}
+
+	/* Sort by allocated pointers just because. */
+	cmp_ptr = left->data - right->data;
+	if (cmp_ptr) {
+		if (cmp_ptr < 0) {
+			return -1;
+		}
+		return 1;
+	}
+
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief Merge the given sorted sublists into sorted order onto the end of the list.
+ *
+ * \param list Merge sublists onto this list.
+ * \param sub1 First sublist to merge.
+ * \param sub2 Second sublist to merge.
+ *
+ * \return Nothing
+ */
+static void mm_atexit_list_merge(struct region_list *list, struct region_list *sub1, struct region_list *sub2)
+{
+	struct ast_region *reg;
+
+	for (;;) {
+		if (AST_LIST_EMPTY(sub1)) {
+			/* The remaining sublist goes onto the list. */
+			AST_LIST_APPEND_LIST(list, sub2, node);
+			break;
+		}
+		if (AST_LIST_EMPTY(sub2)) {
+			/* The remaining sublist goes onto the list. */
+			AST_LIST_APPEND_LIST(list, sub1, node);
+			break;
+		}
+
+		if (mm_atexit_cmp(AST_LIST_FIRST(sub1), AST_LIST_FIRST(sub2)) <= 0) {
+			reg = AST_LIST_REMOVE_HEAD(sub1, node);
+		} else {
+			reg = AST_LIST_REMOVE_HEAD(sub2, node);
+		}
+		AST_LIST_INSERT_TAIL(list, reg, node);
+	}
+}
+
+/*!
+ * \internal
+ * \brief Take sublists off of the given list.
+ *
+ * \param list Source list to remove sublists from the beginning of list.
+ * \param sub Array of sublists to fill. (Lists are empty on entry.)
+ * \param num_lists Number of lists to remove from the source list.
+ * \param size Size of the sublists to remove.
+ * \param remaining Remaining number of elements on the source list.
+ *
+ * \return Nothing
+ */
+static void mm_atexit_list_split(struct region_list *list, struct region_list sub[], size_t num_lists, size_t size, size_t *remaining)
+{
+	int idx;
+
+	for (idx = 0; idx < num_lists; ++idx) {
+		size_t count;
+
+		if (*remaining < size) {
+			/* The remaining source list goes onto the sublist. */
+			AST_LIST_APPEND_LIST(&sub[idx], list, node);
+			*remaining = 0;
+			break;
+		}
+
+		/* Take a sublist off the beginning of the source list. */
+		*remaining -= size;
+		for (count = size; count--;) {
+			struct ast_region *reg;
+
+			reg = AST_LIST_REMOVE_HEAD(list, node);
+			AST_LIST_INSERT_TAIL(&sub[idx], reg, node);
+		}
+	}
+}
+
+/*!
+ * \internal
+ * \brief Sort the regions list using mergesort.
+ *
+ * \param list Allocated regions list to sort.
+ * \param length Length of the list.
+ *
+ * \return Nothing
+ */
+static void mm_atexit_list_sort(struct region_list *list, size_t length)
+{
+	/*! Semi-sorted merged list. */
+	struct region_list merged = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
+	/*! Sublists to merge. (Can only merge two sublists at this time.) */
+	struct region_list sub[2] = {
+		AST_LIST_HEAD_NOLOCK_INIT_VALUE,
+		AST_LIST_HEAD_NOLOCK_INIT_VALUE
+	};
+	/*! Sublist size. */
+	size_t size = 1;
+	/*! Remaining elements in the list. */
+	size_t remaining;
+	/*! Number of sublist merge passes to process the list. */
+	int passes;
+
+	for (;;) {
+		remaining = length;
+
+		passes = 0;
+		while (!AST_LIST_EMPTY(list)) {
+			mm_atexit_list_split(list, sub, ARRAY_LEN(sub), size, &remaining);
+			mm_atexit_list_merge(&merged, &sub[0], &sub[1]);
+			++passes;
+		}
+		AST_LIST_APPEND_LIST(list, &merged, node);
+		if (passes <= 1) {
+			/* The list is now sorted. */
+			break;
+		}
+
+		/* Double the sublist size to remove for next round. */
+		size <<= 1;
+	}
+}
+
+/*!
+ * \internal
+ * \brief List all regions currently allocated.
+ *
+ * \param alloced regions list.
+ *
+ * \return Nothing
+ */
+static void mm_atexit_regions_list(struct region_list *alloced)
+{
+	struct ast_region *reg;
+
+	AST_LIST_TRAVERSE(alloced, reg, node) {
+		astmm_log("%s %s() line %u: %u bytes%s at %p\n",
+			reg->file, reg->func, reg->lineno,
+			(unsigned int) reg->len, reg->cache ? " (cache)" : "", reg->data);
+	}
+}
+
+/*!
+ * \internal
+ * \brief Summarize all regions currently allocated.
+ *
+ * \param alloced Sorted regions list.
+ *
+ * \return Nothing
+ */
+static void mm_atexit_regions_summary(struct region_list *alloced)
+{
+	struct ast_region *reg;
+	struct ast_region *next;
+	struct {
+		unsigned int count;
+		unsigned int len;
+		unsigned int cache_len;
+	} by_line, by_func, by_file, total;
+
+	by_line.count = 0;
+	by_line.len = 0;
+	by_line.cache_len = 0;
+
+	by_func.count = 0;
+	by_func.len = 0;
+	by_func.cache_len = 0;
+
+	by_file.count = 0;
+	by_file.len = 0;
+	by_file.cache_len = 0;
+
+	total.count = 0;
+	total.len = 0;
+	total.cache_len = 0;
+
+	AST_LIST_TRAVERSE(alloced, reg, node) {
+		next = AST_LIST_NEXT(reg, node);
+
+		++by_line.count;
+		by_line.len += reg->len;
+		if (reg->cache) {
+			by_line.cache_len += reg->len;
+		}
+		if (next && !strcmp(reg->file, next->file) && reg->lineno == next->lineno) {
+			continue;
+		}
+		if (atexit_summary & SUMMARY_BY_LINE) {
+			if (by_line.cache_len) {
+				astmm_log("%10u bytes (%u in caches) in %u allocations. %s %s() line %u\n",
+					by_line.len, by_line.cache_len, by_line.count, reg->file, reg->func, reg->lineno);
+			} else {
+				astmm_log("%10u bytes in %5u allocations. %s %s() line %u\n",
+					by_line.len, by_line.count, reg->file, reg->func, reg->lineno);
+			}
+		}
+
+		by_func.count += by_line.count;
+		by_func.len += by_line.len;
+		by_func.cache_len += by_line.cache_len;
+		by_line.count = 0;
+		by_line.len = 0;
+		by_line.cache_len = 0;
+		if (next && !strcmp(reg->file, next->file) && !strcmp(reg->func, next->func)) {
+			continue;
+		}
+		if (atexit_summary & SUMMARY_BY_FUNC) {
+			if (by_func.cache_len) {
+				astmm_log("%10u bytes (%u in caches) in %u allocations. %s %s()\n",
+					by_func.len, by_func.cache_len, by_func.count, reg->file, reg->func);
+			} else {
+				astmm_log("%10u bytes in %5u allocations. %s %s()\n",
+					by_func.len, by_func.count, reg->file, reg->func);
+			}
+		}
+
+		by_file.count += by_func.count;
+		by_file.len += by_func.len;
+		by_file.cache_len += by_func.cache_len;
+		by_func.count = 0;
+		by_func.len = 0;
+		by_func.cache_len = 0;
+		if (next && !strcmp(reg->file, next->file)) {
+			continue;
+		}
+		if (atexit_summary & SUMMARY_BY_FILE) {
+			if (by_file.cache_len) {
+				astmm_log("%10u bytes (%u in caches) in %u allocations. %s\n",
+					by_file.len, by_file.cache_len, by_file.count, reg->file);
+			} else {
+				astmm_log("%10u bytes in %5u allocations. %s\n",
+					by_file.len, by_file.count, reg->file);
+			}
+		}
+
+		total.count += by_file.count;
+		total.len += by_file.len;
+		total.cache_len += by_file.cache_len;
+		by_file.count = 0;
+		by_file.len = 0;
+		by_file.cache_len = 0;
+	}
+
+	if (total.cache_len) {
+		astmm_log("%u bytes (%u in caches) in %u allocations.\n",
+			total.len, total.cache_len, total.count);
+	} else {
+		astmm_log("%u bytes in %u allocations.\n", total.len, total.count);
+	}
+}
+
+/*!
+ * \internal
+ * \brief Dump the memory allocations atexit.
+ *
+ * \note reglock must be locked before calling.
+ *
+ * \return Nothing
+ */
+static void mm_atexit_dump(void)
+{
+	struct region_list alloced_atexit = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
+	size_t length;
+
+	length = mm_atexit_hash_list(&alloced_atexit);
+	if (!length) {
+		/* Wow!  This is amazing! */
+		astmm_log("Exiting with all memory freed.\n");
+		return;
+	}
+
+	mm_atexit_list_sort(&alloced_atexit, length);
+
+	astmm_log("Exiting with the following memory not freed:\n");
+	if (atexit_list) {
+		mm_atexit_regions_list(&alloced_atexit);
+	}
+	if (atexit_summary) {
+		mm_atexit_regions_summary(&alloced_atexit);
+	}
+
+	/*
+	 * Put the alloced list back into regions[].
+	 *
+	 * We have do do this because we can get called before all other
+	 * threads have terminated.
+	 */
+	mm_atexit_hash_restore(&alloced_atexit);
+}
+
+/*!
+ * \internal
+ * \return Nothing
+ */
+static void mm_atexit_final(void)
+{
+	FILE *log;
+
+	/* Only wait if we want atexit allocation dumps. */
+	if (atexit_list || atexit_summary) {
+		fprintf(stderr, "Waiting 10 seconds to let other threads die.\n");
+		sleep(10);
+	}
+
+	regions_check_all_fences();
+
+	/* Flush all delayed memory free circular arrays. */
+	freed_regions_flush(&whales);
+	freed_regions_flush(&minnows);
+
+	/* Peform atexit allocation dumps. */
+	if (atexit_list || atexit_summary) {
+		ast_mutex_lock(&reglock);
+		mm_atexit_dump();
+		ast_mutex_unlock(&reglock);
+	}
 
-	if (pad) {
-		ast_log(LOG_ERROR, "struct ast_region has %d bytes of padding! This must be eliminated for low-fence checking to work properly!\n", (int) pad);
+	/* Close the log file. */
+	log = mmlog;
+	mmlog = NULL;
+	if (log) {
+		fclose(log);
+	}
 	}
 
+/*!
+ * \brief Initialize malloc debug phase 1.
+ *
+ * \note Must be called first thing in main().
+ *
+ * \return Nothing
+ */
+void __ast_mm_init_phase_1(void)
+{
+	atexit(mm_atexit_final);
+}
+
+/*!
+ * \internal
+ * \return Nothing
+ */
+static void mm_atexit_ast(void)
+{
+	ast_cli_unregister_multiple(cli_memory, ARRAY_LEN(cli_memory));
+}
+
+/*!
+ * \brief Initialize malloc debug phase 2.
+ *
+ * \return Nothing
+ */
+void __ast_mm_init_phase_2(void)
+{
+	char filename[PATH_MAX];
+
 	ast_cli_register_multiple(cli_memory, ARRAY_LEN(cli_memory));
 	
 	snprintf(filename, sizeof(filename), "%s/mmlog", ast_config_AST_LOG_DIR);
 	
 	ast_verb(1, "Asterisk Malloc Debugger Started (see %s))\n", filename);
 	
-	if ((mmlog = fopen(filename, "a+"))) {
+	mmlog = fopen(filename, "a+");
+	if (mmlog) {
 		fprintf(mmlog, "%ld - New session\n", (long)time(NULL));
 		fflush(mmlog);
+	} else {
+		ast_log(LOG_ERROR, "Could not open malloc debug log file: %s\n", filename);
 	}
+
+	ast_register_atexit(mm_atexit_ast);
 }
 
-#endif
+#endif	/* defined(__AST_DEBUG_MALLOC) */
diff -uNrw asterisk-1.8.19.0/main/ccss.c asterisk-1.8.20.0-rc1/main/ccss.c
--- asterisk-1.8.19.0/main/ccss.c	2012-11-02 21:48:03.000000000 +0100
+++ asterisk-1.8.20.0-rc1/main/ccss.c	2012-12-03 18:05:40.000000000 +0100
@@ -27,7 +27,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 375658 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 377037 $")
 
 #include "asterisk/astobj2.h"
 #include "asterisk/strings.h"
@@ -4317,6 +4317,8 @@
 	ast_cc_monitor_unregister(&generic_monitor_cbs);
 	ast_unregister_application(cccancel_app);
 	ast_unregister_application(ccreq_app);
+	ast_logger_unregister_level(CC_LOGGER_LEVEL_NAME);
+	ast_cli_unregister_multiple(cc_cli, ARRAY_LEN(cc_cli));
 
 	if (cc_sched_thread) {
 		cc_sched_thread = ast_sched_thread_destroy(cc_sched_thread);
diff -uNrw asterisk-1.8.19.0/main/cdr.c asterisk-1.8.20.0-rc1/main/cdr.c
--- asterisk-1.8.19.0/main/cdr.c	2012-11-04 00:44:30.000000000 +0100
+++ asterisk-1.8.20.0-rc1/main/cdr.c	2012-12-03 19:38:21.000000000 +0100
@@ -37,7 +37,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 375727 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 377069 $")
 
 #include <signal.h>
 
@@ -1481,7 +1481,7 @@
 static struct ast_cli_entry cli_submit = AST_CLI_DEFINE(handle_cli_submit, "Posts all pending batched CDR data");
 static struct ast_cli_entry cli_status = AST_CLI_DEFINE(handle_cli_status, "Display the CDR status");
 
-static int do_reload(int reload)
+static void do_reload(int reload)
 {
 	struct ast_config *config;
 	const char *enabled_value;
@@ -1497,11 +1497,10 @@
 	int cfg_time;
 	int was_enabled;
 	int was_batchmode;
-	int res=0;
 	struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
 
 	if ((config = ast_config_load2("cdr.conf", "cdr", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
-		return 0;
+		return;
 	}
 
 	ast_mutex_lock(&cdr_batch_lock);
@@ -1519,7 +1518,7 @@
 
 	if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) {
 		ast_mutex_unlock(&cdr_batch_lock);
-		return 0;
+		return;
 	}
 
 	/* don't run the next scheduled CDR posting while reloading */
@@ -1582,7 +1581,6 @@
 		} else {
 			ast_cli_register(&cli_submit);
 			ast_register_atexit(ast_cdr_engine_term);
-			res = 0;
 		}
 	/* if this reload disabled the CDR and/or batch mode and there is a background thread,
 	   kill it */
@@ -1595,27 +1593,39 @@
 		ast_cond_destroy(&cdr_pending_cond);
 		ast_cli_unregister(&cli_submit);
 		ast_unregister_atexit(ast_cdr_engine_term);
-		res = 0;
 		/* if leaving batch mode, then post the CDRs in the batch,
 		   and don't reschedule, since we are stopping CDR logging */
 		if (!batchmode && was_batchmode) {
 			ast_cdr_engine_term();
 		}
-	} else {
-		res = 0;
 	}
 
 	ast_mutex_unlock(&cdr_batch_lock);
 	ast_config_destroy(config);
 	manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: CDR\r\nMessage: CDR subsystem reload requested\r\n");
+}
 
-	return res;
+static void cdr_engine_shutdown(void)
+{
+	if (cdr_thread != AST_PTHREADT_NULL) {
+		/* wake up the thread so it will exit */
+		pthread_cancel(cdr_thread);
+		pthread_kill(cdr_thread, SIGURG);
+		pthread_join(cdr_thread, NULL);
+		cdr_thread = AST_PTHREADT_NULL;
+		ast_cond_destroy(&cdr_pending_cond);
+	}
+	ast_cli_unregister(&cli_submit);
+
+	ast_cli_unregister(&cli_status);
+	sched_context_destroy(sched);
+	sched = NULL;
+	ast_free(batch);
+	batch = NULL;
 }
 
 int ast_cdr_engine_init(void)
 {
-	int res;
-
 	sched = sched_context_create();
 	if (!sched) {
 		ast_log(LOG_ERROR, "Unable to create schedule context.\n");
@@ -1623,15 +1633,10 @@
 	}
 
 	ast_cli_register(&cli_status);
+	do_reload(0);
+	ast_register_atexit(cdr_engine_shutdown);
 
-	res = do_reload(0);
-	if (res) {
-		ast_mutex_lock(&cdr_batch_lock);
-		res = init_batch();
-		ast_mutex_unlock(&cdr_batch_lock);
-	}
-
-	return res;
+	return 0;
 }
 
 /* \note This actually gets called a couple of times at shutdown.  Once, before we start
@@ -1643,7 +1648,8 @@
 
 int ast_cdr_engine_reload(void)
 {
-	return do_reload(1);
+	do_reload(1);
+	return 0;
 }
 
 int ast_cdr_data_add_structure(struct ast_data *tree, struct ast_cdr *cdr, int recur)
diff -uNrw asterisk-1.8.19.0/main/channel.c asterisk-1.8.20.0-rc1/main/channel.c
--- asterisk-1.8.19.0/main/channel.c	2012-11-05 23:50:21.000000000 +0100
+++ asterisk-1.8.20.0-rc1/main/channel.c	2012-11-13 19:10:22.000000000 +0100
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 375893 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 376199 $")
 
 #include "asterisk/_private.h"
 
@@ -71,6 +71,7 @@
 #include "asterisk/stringfields.h"
 #include "asterisk/global_datastores.h"
 #include "asterisk/data.h"
+#include "asterisk/features.h"
 
 #ifdef HAVE_EPOLL
 #include <sys/epoll.h>
@@ -1836,11 +1837,13 @@
 }
 
 /*! \brief Wait, look for hangups and condition arg */
-int ast_safe_sleep_conditional(struct ast_channel *chan, int ms, int (*cond)(void*), void *data)
+int ast_safe_sleep_conditional(struct ast_channel *chan, int timeout_ms, int (*cond)(void*), void *data)
 {
 	struct ast_frame *f;
 	struct ast_silence_generator *silgen = NULL;
 	int res = 0;
+	struct timeval start;
+	int ms;
 	AST_LIST_HEAD_NOLOCK(, ast_frame) deferred_frames;
 
 	AST_LIST_HEAD_INIT_NOLOCK(&deferred_frames);
@@ -1850,8 +1853,10 @@
 		silgen = ast_channel_start_silence_generator(chan);
 	}
 
-	while (ms > 0) {
+	start = ast_tvnow();
+	while ((ms = ast_remaining_ms(start, timeout_ms))) {
 		struct ast_frame *dup_f = NULL;
+
 		if (cond && ((*cond)(data) == 0)) {
 			break;
 		}
@@ -2988,12 +2993,15 @@
 		do {
 			AST_LIST_HEAD_NOLOCK(, ast_frame) frames;
 			struct ast_frame *cur, *new;
-			int ms = MAX(delay, 500);
+			int timeout_ms = MAX(delay, 500);
 			unsigned int done = 0;
+			struct timeval start;
 
 			AST_LIST_HEAD_INIT_NOLOCK(&frames);
 
+			start = ast_tvnow();
 			for (;;) {
+				int ms = ast_remaining_ms(start, timeout_ms);
 				ms = ast_waitfor(chan, ms);
 				if (ms < 0) {
 					ast_log(LOG_WARNING, "Error condition occurred when polling channel %s for a voice frame: %s\n", chan->name, strerror(errno));
@@ -3110,7 +3118,11 @@
 
 	res = generate(chan, tmp, 0, ast_format_rate(chan->writeformat & AST_FORMAT_AUDIO_MASK) / 50);
 
+	ast_channel_lock(chan);
+        if (chan->generator && generate == chan->generator->generate) {
 	chan->generatordata = tmp;
+        }
+	ast_channel_unlock(chan);
 
 	if (res) {
 		ast_debug(1, "Auto-deactivating generator\n");
@@ -3508,11 +3520,14 @@
 
 int ast_waitfor(struct ast_channel *c, int ms)
 {
-	int oldms = ms;	/* -1 if no timeout */
-
+	if (ms < 0) {
+		do {
+			ms = 100000;
+			ast_waitfor_nandfds(&c, 1, NULL, 0, NULL, NULL, &ms);
+		} while (!ms);
+	} else {
 	ast_waitfor_nandfds(&c, 1, NULL, 0, NULL, NULL, &ms);
-	if ((ms < 0) && (oldms < 0))
-		ms = 0;
+	}
 	return ms;
 }
 
@@ -3567,6 +3582,7 @@
 int ast_waitfordigit_full(struct ast_channel *c, int timeout_ms, int audiofd, int cmdfd)
 {
 	struct timeval start = ast_tvnow();
+	int ms;
 
 	/* Stop if we're a zombie or need a soft hangup */
 	if (ast_test_flag(c, AST_FLAG_ZOMBIE) || ast_check_hangup(c))
@@ -3578,19 +3594,9 @@
 	/* Wait for a digit, no more than timeout_ms milliseconds total.
 	 * Or, wait indefinitely if timeout_ms is <0.
 	 */
-	while (ast_tvdiff_ms(ast_tvnow(), start) < timeout_ms || timeout_ms < 0) {
+	while ((ms = ast_remaining_ms(start, timeout_ms))) {
 		struct ast_channel *rchan;
 		int outfd=-1;
-		int ms;
-
-		if (timeout_ms < 0) {
-			ms = timeout_ms;
-		} else {
-			ms = timeout_ms - ast_tvdiff_ms(ast_tvnow(), start);
-			if (ms < 0) {
-				ms = 0;
-			}
-		}
 
 		errno = 0;
 		/* While ast_waitfor_nandfds tries to help by reducing the timeout by how much was waited,
@@ -4596,25 +4602,32 @@
 
 char *ast_recvtext(struct ast_channel *chan, int timeout)
 {
-	int res, done = 0;
+	int res;
 	char *buf = NULL;
+	struct timeval start = ast_tvnow();
+	int ms;
 	
-	while (!done) {
+	while ((ms = ast_remaining_ms(start, timeout))) {
 		struct ast_frame *f;
-		if (ast_check_hangup(chan))
+
+		if (ast_check_hangup(chan)) {
 			break;
-		res = ast_waitfor(chan, timeout);
-		if (res <= 0) /* timeout or error */
+		}
+		res = ast_waitfor(chan, ms);
+		if (res <= 0)  {/* timeout or error */
 			break;
-		timeout = res;	/* update timeout */
+		}
 		f = ast_read(chan);
-		if (f == NULL)
+		if (f == NULL) {
 			break; /* no frame */
-		if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_HANGUP)
-			done = 1;	/* force a break */
-		else if (f->frametype == AST_FRAME_TEXT) {		/* what we want */
+		}
+		if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_HANGUP) {
+			ast_frfree(f);
+			break;
+		} else if (f->frametype == AST_FRAME_TEXT) {		/* what we want */
 			buf = ast_strndup((char *) f->data.ptr, f->datalen);	/* dup and break */
-			done = 1;
+			ast_frfree(f);
+			break;
 		}
 		ast_frfree(f);
 	}
@@ -4665,6 +4678,11 @@
 	if (!chan->tech->send_digit_begin)
 		return 0;
 
+	ast_channel_lock(chan);
+	chan->sending_dtmf_digit = digit;
+	chan->sending_dtmf_tv = ast_tvnow();
+	ast_channel_unlock(chan);
+
 	if (!chan->tech->send_digit_begin(chan, digit))
 		return 0;
 
@@ -4688,6 +4706,12 @@
 {
 	int res = -1;
 
+	ast_channel_lock(chan);
+	if (chan->sending_dtmf_digit == digit) {
+		chan->sending_dtmf_digit = 0;
+	}
+	ast_channel_unlock(chan);
+
 	if (chan->tech->send_digit_end)
 		res = chan->tech->send_digit_end(chan, digit, duration);
 
@@ -5454,18 +5478,19 @@
 	if (ast_call(chan, data, 0)) {	/* ast_call failed... */
 		ast_log(LOG_NOTICE, "Unable to call channel %s/%s\n", type, (char *)data);
 	} else {
+		struct timeval start = ast_tvnow();
 		res = 1;	/* mark success in case chan->_state is already AST_STATE_UP */
 		while (timeout && chan->_state != AST_STATE_UP) {
 			struct ast_frame *f;
-			res = ast_waitfor(chan, timeout);
+			int ms = ast_remaining_ms(start, timeout);
+
+			res = ast_waitfor(chan, ms);
 			if (res == 0) { /* timeout, treat it like ringing */
 				*outstate = AST_CONTROL_RINGING;
 				break;
 			}
 			if (res < 0) /* error or done */
 				break;
-			if (timeout > -1)
-				timeout = res;
 			if (!ast_strlen_zero(chan->call_forward)) {
 				if (!(chan = ast_call_forward(NULL, chan, NULL, format, oh, outstate))) {
 					return NULL;
@@ -6495,6 +6520,8 @@
 	char orig[AST_CHANNEL_NAME];
 	char masqn[AST_CHANNEL_NAME];
 	char zombn[AST_CHANNEL_NAME];
+	char clone_sending_dtmf_digit;
+	struct timeval clone_sending_dtmf_tv;
 
 	/* XXX This operation is a bit odd.  We're essentially putting the guts of
 	 * the clone channel into the original channel.  Start by killing off the
@@ -6604,6 +6631,10 @@
 	free_translation(clonechan);
 	free_translation(original);
 
+	/* Save the current DTMF digit being sent if any. */
+	clone_sending_dtmf_digit = clonechan->sending_dtmf_digit;
+	clone_sending_dtmf_tv = clonechan->sending_dtmf_tv;
+
 	/* Save the original name */
 	ast_copy_string(orig, original->name, sizeof(orig));
 	/* Save the new name */
@@ -6848,6 +6879,15 @@
 
 	ast_channel_unlock(clonechan);
 
+	if (clone_sending_dtmf_digit) {
+		/*
+		 * The clonechan was sending a DTMF digit that was not completed
+		 * before the masquerade.
+		 */
+		ast_bridge_end_dtmf(original, clone_sending_dtmf_digit, clone_sending_dtmf_tv,
+			"masquerade");
+	}
+
 	/*
 	 * If an indication is currently playing, maintain it on the
 	 * channel that is taking the place of original.
@@ -8186,18 +8226,46 @@
 	return state;
 }
 
+static int internal_deactivate_generator(struct ast_channel *chan, void* generator)
+{
+	ast_channel_lock(chan);
+
+	if (!chan->generatordata) {
+		ast_debug(1, "Trying to stop silence generator when there is no "
+		    "generator on '%s'\n", chan->name);
+		ast_channel_unlock(chan);
+		return 0;
+	}
+	if (chan->generator != generator) {
+		ast_debug(1, "Trying to stop silence generator when it is not the current "
+		    "generator on '%s'\n", chan->name);
+		ast_channel_unlock(chan);
+		return 0;
+	}
+	if (chan->generator && chan->generator->release) {
+		chan->generator->release(chan, chan->generatordata);
+	}
+	chan->generatordata = NULL;
+	chan->generator = NULL;
+	ast_channel_set_fd(chan, AST_GENERATOR_FD, -1);
+	ast_clear_flag(chan, AST_FLAG_WRITE_INT);
+	ast_settimeout(chan, 0, NULL, NULL);
+	ast_channel_unlock(chan);
+
+	return 1;
+}
+
 void ast_channel_stop_silence_generator(struct ast_channel *chan, struct ast_silence_generator *state)
 {
 	if (!state)
 		return;
 
-	ast_deactivate_generator(chan);
-
+	if (internal_deactivate_generator(chan, &silence_generator)) {
 	ast_debug(1, "Stopped silence generator on '%s'\n", chan->name);
 
 	if (ast_set_write_format(chan, state->old_write_format) < 0)
 		ast_log(LOG_ERROR, "Could not return write format to its original state\n");
-
+	}
 	ast_free(state);
 }
 
diff -uNrw asterisk-1.8.19.0/main/cli.c asterisk-1.8.20.0-rc1/main/cli.c
--- asterisk-1.8.19.0/main/cli.c	2012-06-15 17:56:08.000000000 +0200
+++ asterisk-1.8.20.0-rc1/main/cli.c	2012-12-03 20:13:01.000000000 +0100
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 369001 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 377073 $")
 
 #include "asterisk/_private.h"
 #include "asterisk/paths.h"	/* use ast_config_AST_MODULE_DIR */
@@ -1871,10 +1871,16 @@
 	return 0;
 }
 
+static void cli_shutdown(void)
+{
+	ast_cli_unregister_multiple(cli_cli, ARRAY_LEN(cli_cli));
+}
+
 /*! \brief initialize the _full_cmd string in * each of the builtins. */
 void ast_builtins_init(void)
 {
 	ast_cli_register_multiple(cli_cli, ARRAY_LEN(cli_cli));
+	ast_register_atexit(cli_shutdown);
 }
 
 /*!
@@ -2039,6 +2045,18 @@
 	return cmdline;
 }
 
+static int cli_is_registered(struct ast_cli_entry *e)
+{
+	struct ast_cli_entry *cur = NULL;
+
+	while ((cur = cli_next(cur))) {
+		if (cur == e) {
+			return 1;
+		}
+	}
+	return 0;
+}
+
 static int __ast_cli_unregister(struct ast_cli_entry *e, struct ast_cli_entry *ed)
 {
 	if (e->inuse) {
@@ -2070,6 +2088,15 @@
 	char **dst = (char **)e->cmda;	/* need to cast as the entry is readonly */
 	char *s;
 
+	AST_RWLIST_WRLOCK(&helpers);
+
+	if (cli_is_registered(e)) {
+		ast_log(LOG_WARNING, "Command '%s' already registered (the same ast_cli_entry)\n",
+			S_OR(e->_full_cmd, e->command));
+		ret = 0;  /* report success */
+		goto done;
+	}
+
 	memset(&a, '\0', sizeof(a));
 	e->handler(e, CLI_INIT, &a);
 	/* XXX check that usage and command are filled up */
@@ -2085,14 +2112,16 @@
 	}
 	*dst++ = NULL;
 	
-	AST_RWLIST_WRLOCK(&helpers);
-	
 	if (find_cli(e->cmda, 1)) {
-		ast_log(LOG_WARNING, "Command '%s' already registered (or something close enough)\n", S_OR(e->_full_cmd, e->command));
+		ast_log(LOG_WARNING, "Command '%s' already registered (or something close enough)\n",
+			S_OR(e->_full_cmd, e->command));
 		goto done;
 	}
-	if (set_full_cmd(e))
+	if (set_full_cmd(e)) {
+		ast_log(LOG_WARNING, "Error registering CLI Command '%s'\n",
+			S_OR(e->_full_cmd, e->command));
 		goto done;
+	}
 
 	lf = e->cmdlen;
 	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&helpers, cur, list) {
@@ -2112,6 +2141,10 @@
 
 done:
 	AST_RWLIST_UNLOCK(&helpers);
+	if (ret) {
+		ast_free(e->command);
+		e->command = NULL;
+	}
 
 	return ret;
 }
diff -uNrw asterisk-1.8.19.0/main/config.c asterisk-1.8.20.0-rc1/main/config.c
--- asterisk-1.8.19.0/main/config.c	2012-08-17 00:30:12.000000000 +0200
+++ asterisk-1.8.20.0-rc1/main/config.c	2012-12-03 20:29:17.000000000 +0100
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 371392 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 377104 $")
 
 #include "asterisk/paths.h"	/* use ast_config_AST_CONFIG_DIR */
 #include "asterisk/network.h"	/* we do some sockaddr manipulation here */
@@ -2952,8 +2952,22 @@
 	AST_CLI_DEFINE(handle_cli_config_list, "Show all files that have loaded a configuration file"),
 };
 
+static void config_shutdown(void)
+{
+	struct cache_file_mtime *cfmtime;
+
+	AST_LIST_LOCK(&cfmtime_head);
+	while ((cfmtime = AST_LIST_REMOVE_HEAD(&cfmtime_head, list))) {
+		ast_free(cfmtime);
+	}
+	AST_LIST_UNLOCK(&cfmtime_head);
+
+	ast_cli_unregister_multiple(cli_config, ARRAY_LEN(cli_config));
+}
+
 int register_config_cli(void)
 {
 	ast_cli_register_multiple(cli_config, ARRAY_LEN(cli_config));
+	ast_register_atexit(config_shutdown);
 	return 0;
 }
diff -uNrw asterisk-1.8.19.0/main/features.c asterisk-1.8.20.0-rc1/main/features.c
--- asterisk-1.8.19.0/main/features.c	2012-10-29 20:26:52.000000000 +0100
+++ asterisk-1.8.20.0-rc1/main/features.c	2012-11-06 19:18:32.000000000 +0100
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 375388 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 375964 $")
 
 #include "asterisk/_private.h"
 
@@ -3896,6 +3896,24 @@
 	ast_channel_unlock(chan);
 }
 
+void ast_bridge_end_dtmf(struct ast_channel *chan, char digit, struct timeval start, const char *why)
+{
+	int dead;
+	long duration;
+
+	ast_channel_lock(chan);
+	dead = ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan);
+	ast_channel_unlock(chan);
+	if (dead) {
+		return;
+	}
+
+	duration = ast_tvdiff_ms(ast_tvnow(), start);
+	ast_senddigit_end(chan, digit, duration);
+	ast_log(LOG_DTMF, "DTMF end '%c' simulated on %s due to %s, duration %ld ms\n",
+		digit, chan->name, why, duration);
+}
+
 /*!
  * \brief bridge the call and set CDR
  *
@@ -4342,6 +4360,15 @@
 	ast_cel_report_event(chan, AST_CEL_BRIDGE_END, NULL, NULL, peer);
 
 before_you_go:
+	if (chan->sending_dtmf_digit) {
+		ast_bridge_end_dtmf(chan, chan->sending_dtmf_digit, chan->sending_dtmf_tv,
+			"bridge end");
+	}
+	if (peer->sending_dtmf_digit) {
+		ast_bridge_end_dtmf(peer, peer->sending_dtmf_digit, peer->sending_dtmf_tv,
+			"bridge end");
+	}
+
 	/* Just in case something weird happened and we didn't clean up the silence generator... */
 	if (silgen) {
 		ast_channel_stop_silence_generator(who == chan ? peer : chan, silgen);
diff -uNrw asterisk-1.8.19.0/main/indications.c asterisk-1.8.20.0-rc1/main/indications.c
--- asterisk-1.8.19.0/main/indications.c	2012-10-02 02:31:14.000000000 +0200
+++ asterisk-1.8.20.0-rc1/main/indications.c	2012-11-19 20:30:58.000000000 +0100
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 374177 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 376469 $")
 
 #include <math.h>
 
@@ -873,7 +873,7 @@
 					(j == tz->nrringcadence - 1) ? "" : ",");
 		}
 		ast_str_append(&buf, 0, "\n");
-		ast_cli(a->fd, "%s", buf->str);
+		ast_cli(a->fd, "%s", ast_str_buffer(buf));
 
 		AST_LIST_TRAVERSE(&tz->tones, ts, entry) {
 			ast_cli(a->fd, "%-7.7s %-15.15s %s\n", tz->country, ts->name, ts->data);
diff -uNrw asterisk-1.8.19.0/main/lock.c asterisk-1.8.20.0-rc1/main/lock.c
--- asterisk-1.8.19.0/main/lock.c	2012-08-27 18:40:45.000000000 +0200
+++ asterisk-1.8.20.0-rc1/main/lock.c	2012-11-23 00:51:02.000000000 +0100
@@ -27,7 +27,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 371718 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 376586 $")
 
 #include "asterisk/lock.h"
 
@@ -51,8 +51,8 @@
 	int res;
 	pthread_mutexattr_t  attr;
 
-	t->track = NULL;
 #ifdef DEBUG_THREADS
+	t->track = NULL;
 #if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) && defined(CAN_COMPARE_MUTEX_TO_INIT_VALUE)
 	if ((t->mutex) != ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
 /*
diff -uNrw asterisk-1.8.19.0/main/logger.c asterisk-1.8.20.0-rc1/main/logger.c
--- asterisk-1.8.19.0/main/logger.c	2012-07-31 21:31:42.000000000 +0200
+++ asterisk-1.8.20.0-rc1/main/logger.c	2012-11-23 00:51:02.000000000 +0100
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 370642 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 376586 $")
 
 /* When we include logger.h again it will trample on some stuff in syslog.h, but
  * nothing we care about in here. */
@@ -1133,8 +1133,17 @@
 	/* auto rotate if sig SIGXFSZ comes a-knockin */
 	sigaction(SIGXFSZ, &handle_SIGXFSZ, NULL);
 
-	/* start logger thread */
+	/* Re-initialize the logmsgs mutex.  The recursive mutex can be accessed prior
+ 	 * to Asterisk being forked into the background, which can cause the thread
+ 	 * ID tracked by the underlying pthread mutex to be different than the ID of
+ 	 * the thread that unlocks the mutex.  Since init_logger is called after the
+ 	 * fork, it is safe to initialize the mutex here for future accesses.
+ 	 */
+	ast_mutex_destroy(&logmsgs.lock);
+	ast_mutex_init(&logmsgs.lock);
 	ast_cond_init(&logcond, NULL);
+
+	/* start logger thread */
 	if (ast_pthread_create(&logthread, NULL, logger_thread, NULL) < 0) {
 		ast_cond_destroy(&logcond);
 		return -1;
diff -uNrw asterisk-1.8.19.0/main/manager.c asterisk-1.8.20.0-rc1/main/manager.c
--- asterisk-1.8.19.0/main/manager.c	2012-11-04 04:06:49.000000000 +0100
+++ asterisk-1.8.20.0-rc1/main/manager.c	2012-11-28 17:23:47.000000000 +0100
@@ -47,7 +47,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 375800 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 376725 $")
 
 #include "asterisk/_private.h"
 #include "asterisk/paths.h"	/* use various ast_config_AST_* */
@@ -4696,7 +4696,7 @@
 			|| !strcasecmp(action, "Challenge"))) {
 		user = astman_get_header(m, "Username");
 
-		if (check_manager_session_inuse(user)) {
+		if (!ast_strlen_zero(user) && check_manager_session_inuse(user)) {
 			report_session_limit(s);
 			sleep(1);
 			mansession_lock(s);
diff -uNrw asterisk-1.8.19.0/main/pbx.c asterisk-1.8.20.0-rc1/main/pbx.c
--- asterisk-1.8.19.0/main/pbx.c	2012-10-10 00:16:34.000000000 +0200
+++ asterisk-1.8.20.0-rc1/main/pbx.c	2012-11-28 00:47:27.000000000 +0100
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 374758 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 376688 $")
 
 #include "asterisk/_private.h"
 #include "asterisk/paths.h"	/* use ast_config_AST_SYSTEM_NAME */
@@ -2282,10 +2282,122 @@
 	ast_free(pattern_tree);
 }
 
+/*!
+ * \internal
+ * \brief Get the length of the exten string.
+ *
+ * \param str Exten to get length.
+ *
+ * \retval strlen of exten.
+ */
+static int ext_cmp_exten_strlen(const char *str)
+{
+	int len;
+
+	len = 0;
+	for (;;) {
+		/* Ignore '-' chars as eye candy fluff. */
+		while (*str == '-') {
+			++str;
+		}
+		if (!*str) {
+			break;
+		}
+		++str;
+		++len;
+	}
+	return len;
+}
+
+/*!
+ * \internal
+ * \brief Partial comparison of non-pattern extens.
+ *
+ * \param left Exten to compare.
+ * \param right Exten to compare.  Also matches if this string ends first.
+ *
+ * \retval <0 if left < right
+ * \retval =0 if left == right
+ * \retval >0 if left > right
+ */
+static int ext_cmp_exten_partial(const char *left, const char *right)
+{
+	int cmp;
+
+	for (;;) {
+		/* Ignore '-' chars as eye candy fluff. */
+		while (*left == '-') {
+			++left;
+		}
+		while (*right == '-') {
+			++right;
+		}
+
+		if (!*right) {
+			/*
+			 * Right ended first for partial match or both ended at the same
+			 * time for a match.
+			 */
+			cmp = 0;
+			break;
+		}
+
+		cmp = *left - *right;
+		if (cmp) {
+			break;
+		}
+		++left;
+		++right;
+	}
+	return cmp;
+}
+
+/*!
+ * \internal
+ * \brief Comparison of non-pattern extens.
+ *
+ * \param left Exten to compare.
+ * \param right Exten to compare.
+ *
+ * \retval <0 if left < right
+ * \retval =0 if left == right
+ * \retval >0 if left > right
+ */
+static int ext_cmp_exten(const char *left, const char *right)
+{
+	int cmp;
+
+	for (;;) {
+		/* Ignore '-' chars as eye candy fluff. */
+		while (*left == '-') {
+			++left;
+		}
+		while (*right == '-') {
+			++right;
+		}
+
+		cmp = *left - *right;
+		if (cmp) {
+			break;
+		}
+		if (!*left) {
+			/*
+			 * Get here only if both strings ended at the same time.  cmp
+			 * would be non-zero if only one string ended.
+			 */
+			break;
+		}
+		++left;
+		++right;
+	}
+	return cmp;
+}
+
 /*
  * Special characters used in patterns:
  *	'_'	underscore is the leading character of a pattern.
  *		In other position it is treated as a regular char.
+ *	'-' The '-' is a separator and ignored.  Why?  So patterns like NXX-XXX-XXXX work.
  *	.	one or more of any character. Only allowed at the end of
  *		a pattern.
  *	!	zero or more of anything. Also impacts the result of CANMATCH
@@ -2314,83 +2426,106 @@
  */
 
 /*!
- * \brief helper functions to sort extensions and patterns in the desired way,
+ * \brief helper functions to sort extension patterns in the desired way,
  * so that more specific patterns appear first.
  *
- * ext_cmp1 compares individual characters (or sets of), returning
+ * \details
+ * The function compares individual characters (or sets of), returning
  * an int where bits 0-7 are the ASCII code of the first char in the set,
- * while bit 8-15 are the cardinality of the set minus 1.
- * This way more specific patterns (smaller cardinality) appear first.
+ * bits 8-15 are the number of characters in the set, and bits 16-20 are
+ * for special cases.
+ * This way more specific patterns (smaller character sets) appear first.
  * Wildcards have a special value, so that we can directly compare them to
  * sets by subtracting the two values. In particular:
- *  0x000xx		one character, xx
- *  0x0yyxx		yy character set starting with xx
- *  0x10000		'.' (one or more of anything)
- *  0x20000		'!' (zero or more of anything)
+ *  0x001xx     one character, character set starting with xx
+ *  0x0yyxx     yy characters, character set starting with xx
+ *  0x18000     '.' (one or more of anything)
+ *  0x28000     '!' (zero or more of anything)
  *  0x30000		NUL (end of string)
  *  0x40000		error in set.
  * The pointer to the string is advanced according to needs.
  * NOTES:
- *	1. the empty set is equivalent to NUL.
+ *  1. the empty set is ignored.
  *	2. given that a full set has always 0 as the first element,
  *	   we could encode the special cases as 0xffXX where XX
  *	   is 1, 2, 3, 4 as used above.
  */
-static int ext_cmp1(const char **p, unsigned char *bitwise)
+static int ext_cmp_pattern_pos(const char **p, unsigned char *bitwise)
 {
-	int c, cmin = 0xff, count = 0;
+#define BITS_PER	8	/* Number of bits per unit (byte). */
+	unsigned char c;
+	unsigned char cmin;
+	int count;
 	const char *end;
 
-	/* load value and advance pointer */
+	do {
+		/* Get character and advance. (Ignore '-' chars as eye candy fluff.) */
+		do {
 	c = *(*p)++;
+		} while (c == '-');
 
 	/* always return unless we have a set of chars */
-	switch (toupper(c)) {
-	default:	/* ordinary character */
-		bitwise[c / 8] = 1 << (c % 8);
-		return 0x0100 | (c & 0xff);
-
-	case 'N':	/* 2..9 */
-		bitwise[6] = 0xfc;
-		bitwise[7] = 0x03;
+		switch (c) {
+		default:
+			/* ordinary character */
+			bitwise[c / BITS_PER] = 1 << ((BITS_PER - 1) - (c % BITS_PER));
+			return 0x0100 | c;
+
+		case 'n':
+		case 'N':
+			/* 2..9 */
+			bitwise[6] = 0x3f;
+			bitwise[7] = 0xc0;
 		return 0x0800 | '2';
 
-	case 'X':	/* 0..9 */
+		case 'x':
+		case 'X':
+			/* 0..9 */
 		bitwise[6] = 0xff;
-		bitwise[7] = 0x03;
+			bitwise[7] = 0xc0;
 		return 0x0A00 | '0';
 
-	case 'Z':	/* 1..9 */
-		bitwise[6] = 0xfe;
-		bitwise[7] = 0x03;
+		case 'z':
+		case 'Z':
+			/* 1..9 */
+			bitwise[6] = 0x7f;
+			bitwise[7] = 0xc0;
 		return 0x0900 | '1';
 
-	case '.':	/* wildcard */
+		case '.':
+			/* wildcard */
 		return 0x18000;
 
-	case '!':	/* earlymatch */
-		return 0x28000;	/* less specific than NULL */
+		case '!':
+			/* earlymatch */
+			return 0x28000;	/* less specific than '.' */
 
-	case '\0':	/* empty string */
+		case '\0':
+			/* empty string */
 		*p = NULL;
 		return 0x30000;
 
-	case '[':	/* pattern */
+		case '[':
+			/* char set */
 		break;
 	}
 	/* locate end of set */
 	end = strchr(*p, ']');
 
-	if (end == NULL) {
+		if (!end) {
 		ast_log(LOG_WARNING, "Wrong usage of [] in the extension\n");
 		return 0x40000;	/* XXX make this entry go last... */
 	}
 
-	for (; *p < end  ; (*p)++) {
-		unsigned char c1, c2;	/* first-last char in range */
-		c1 = (unsigned char)((*p)[0]);
+		count = 0;
+		cmin = 0xFF;
+		for (; *p < end; ++*p) {
+			unsigned char c1;	/* first char in range */
+			unsigned char c2;	/* last char in range */
+
+			c1 = (*p)[0];
 		if (*p + 2 < end && (*p)[1] == '-') { /* this is a range */
-			c2 = (unsigned char)((*p)[2]);
+				c2 = (*p)[2];
 			*p += 2;    /* skip a total of 3 chars */
 		} else {        /* individual character */
 			c2 = c1;
@@ -2398,60 +2533,120 @@
 		if (c1 < cmin) {
 			cmin = c1;
 		}
-		for (; c1 <= c2; c1++) {
-			unsigned char mask = 1 << (c1 % 8);
-			/*!\note If two patterns score the same, the one with the lowest
-			 * ascii values will compare as coming first. */
-			/* Flag the character as included (used) and count it. */
-			if (!(bitwise[ c1 / 8 ] & mask)) {
-				bitwise[ c1 / 8 ] |= mask;
+			for (; c1 <= c2; ++c1) {
+				unsigned char mask = 1 << ((BITS_PER - 1) - (c1 % BITS_PER));
+
+				/*
+				 * Note: If two character sets score the same, the one with the
+				 * lowest ASCII values will compare as coming first.  Must fill
+				 * in most significant bits for lower ASCII values to accomplish
+				 * the desired sort order.
+				 */
+				if (!(bitwise[c1 / BITS_PER] & mask)) {
+					/* Add the character to the set. */
+					bitwise[c1 / BITS_PER] |= mask;
 				count += 0x100;
 			}
 		}
 	}
-	(*p)++;
-	return count == 0 ? 0x30000 : (count | cmin);
+		++*p;
+	} while (!count);/* While the char set was empty. */
+	return count | cmin;
 }
 
 /*!
- * \brief the full routine to compare extensions in rules.
+ * \internal
+ * \brief Comparison of exten patterns.
+ *
+ * \param left Pattern to compare.
+ * \param right Pattern to compare.
+ *
+ * \retval <0 if left < right
+ * \retval =0 if left == right
+ * \retval >0 if left > right
  */
-static int ext_cmp(const char *a, const char *b)
+static int ext_cmp_pattern(const char *left, const char *right)
 {
-	/* make sure non-patterns come first.
-	 * If a is not a pattern, it either comes first or
-	 * we do a more complex pattern comparison.
-	 */
-	int ret = 0;
+	int cmp;
+	int left_pos;
+	int right_pos;
 
-	if (a[0] != '_')
-		return (b[0] == '_') ? -1 : strcmp(a, b);
+	for (;;) {
+		unsigned char left_bitwise[32] = { 0, };
+		unsigned char right_bitwise[32] = { 0, };
 
-	/* Now we know a is a pattern; if b is not, a comes first */
-	if (b[0] != '_')
-		return 1;
+		left_pos = ext_cmp_pattern_pos(&left, left_bitwise);
+		right_pos = ext_cmp_pattern_pos(&right, right_bitwise);
+		cmp = left_pos - right_pos;
+		if (!cmp) {
+			/*
+			 * Are the character sets different, even though they score the same?
+			 *
+			 * Note: Must swap left and right to get the sense of the
+			 * comparison correct.  Otherwise, we would need to multiply by
+			 * -1 instead.
+			 */
+			cmp = memcmp(right_bitwise, left_bitwise, ARRAY_LEN(left_bitwise));
+		}
+		if (cmp) {
+			break;
+		}
+		if (!left) {
+			/*
+			 * Get here only if both patterns ended at the same time.  cmp
+			 * would be non-zero if only one pattern ended.
+			 */
+			break;
+		}
+	}
+	return cmp;
+}
 
-	/* ok we need full pattern sorting routine.
-	 * skip past the underscores */
-	++a; ++b;
-	do {
-		unsigned char bitwise[2][32] = { { 0, } };
-		ret = ext_cmp1(&a, bitwise[0]) - ext_cmp1(&b, bitwise[1]);
-		if (ret == 0) {
-			/* Are the classes different, even though they score the same? */
-			ret = memcmp(bitwise[0], bitwise[1], 32);
+/*!
+ * \internal
+ * \brief Comparison of dialplan extens for sorting purposes.
+ *
+ * \param left Exten/pattern to compare.
+ * \param right Exten/pattern to compare.
+ *
+ * \retval <0 if left < right
+ * \retval =0 if left == right
+ * \retval >0 if left > right
+ */
+static int ext_cmp(const char *left, const char *right)
+{
+	/* Make sure non-pattern extens come first. */
+	if (left[0] != '_') {
+		if (right[0] == '_') {
+			return -1;
 		}
-	} while (!ret && a && b);
-	if (ret == 0) {
-		return 0;
-	} else {
-		return (ret > 0) ? 1 : -1;
+		/* Compare two non-pattern extens. */
+		return ext_cmp_exten(left, right);
 	}
+	if (right[0] != '_') {
+		return 1;
+	}
+
+	/*
+	 * OK, we need full pattern sorting routine.
+	 *
+	 * Skip past the underscores
+	 */
+	return ext_cmp_pattern(left + 1, right + 1);
 }
 
 int ast_extension_cmp(const char *a, const char *b)
 {
-	return ext_cmp(a, b);
+	int cmp;
+
+	cmp = ext_cmp(a, b);
+	if (cmp < 0) {
+		return -1;
+	}
+	if (cmp > 0) {
+		return 1;
+	}
+	return 0;
 }
 
 /*!
@@ -2474,15 +2669,9 @@
 	ast_log(LOG_NOTICE,"match core: pat: '%s', dat: '%s', mode=%d\n", pattern, data, (int)mode);
 #endif
 
-	if ( (mode == E_MATCH) && (pattern[0] == '_') && (!strcasecmp(pattern,data)) ) { /* note: if this test is left out, then _x. will not match _x. !!! */
-#ifdef NEED_DEBUG_HERE
-		ast_log(LOG_NOTICE,"return (1) - pattern matches pattern\n");
-#endif
-		return 1;
-	}
-
 	if (pattern[0] != '_') { /* not a pattern, try exact or partial match */
-		int ld = strlen(data), lp = strlen(pattern);
+		int lp = ext_cmp_exten_strlen(pattern);
+		int ld = ext_cmp_exten_strlen(data);
 
 		if (lp < ld) {		/* pattern too short, cannot match */
 #ifdef NEED_DEBUG_HERE
@@ -2493,11 +2682,11 @@
 		/* depending on the mode, accept full or partial match or both */
 		if (mode == E_MATCH) {
 #ifdef NEED_DEBUG_HERE
-			ast_log(LOG_NOTICE,"return (!strcmp(%s,%s) when mode== E_MATCH)\n", pattern, data);
+			ast_log(LOG_NOTICE,"return (!ext_cmp_exten(%s,%s) when mode== E_MATCH)\n", pattern, data);
 #endif
-			return !strcmp(pattern, data); /* 1 on match, 0 on fail */
+			return !ext_cmp_exten(pattern, data); /* 1 on match, 0 on fail */
 		}
-		if (ld == 0 || !strncasecmp(pattern, data, ld)) { /* partial or full match */
+		if (ld == 0 || !ext_cmp_exten_partial(pattern, data)) { /* partial or full match */
 #ifdef NEED_DEBUG_HERE
 			ast_log(LOG_NOTICE,"return (mode(%d) == E_MATCHMORE ? lp(%d) > ld(%d) : 1)\n", mode, lp, ld);
 #endif
@@ -2509,26 +2698,60 @@
 			return 0;
 		}
 	}
-	pattern++; /* skip leading _ */
+	if (mode == E_MATCH && data[0] == '_') {
+		/*
+		 * XXX It is bad design that we don't know if we should be
+		 * comparing data and pattern as patterns or comparing data if
+		 * it conforms to pattern when the function is called.  First,
+		 * assume they are both patterns.  If they don't match then try
+		 * to see if data conforms to the given pattern.
+		 *
+		 * note: if this test is left out, then _x. will not match _x. !!!
+		 */
+#ifdef NEED_DEBUG_HERE
+		ast_log(LOG_NOTICE, "Comparing as patterns first. pattern:%s data:%s\n", pattern, data);
+#endif
+		if (!ext_cmp_pattern(pattern + 1, data + 1)) {
+#ifdef NEED_DEBUG_HERE
+			ast_log(LOG_NOTICE,"return (1) - pattern matches pattern\n");
+#endif
+			return 1;
+		}
+	}
+
+	++pattern; /* skip leading _ */
 	/*
 	 * XXX below we stop at '/' which is a separator for the CID info. However we should
 	 * not store '/' in the pattern at all. When we insure it, we can remove the checks.
 	 */
-	while (*data && *pattern && *pattern != '/') {
+	for (;;) {
 		const char *end;
 
-		if (*data == '-') { /* skip '-' in data (just a separator) */
-			data++;
-			continue;
+		/* Ignore '-' chars as eye candy fluff. */
+		while (*data == '-') {
+			++data;
+		}
+		while (*pattern == '-') {
+			++pattern;
 		}
-		switch (toupper(*pattern)) {
+		if (!*data || !*pattern || *pattern == '/') {
+			break;
+		}
+
+		switch (*pattern) {
 		case '[':	/* a range */
-			end = strchr(pattern+1, ']'); /* XXX should deal with escapes ? */
-			if (end == NULL) {
+			++pattern;
+			end = strchr(pattern, ']'); /* XXX should deal with escapes ? */
+			if (!end) {
 				ast_log(LOG_WARNING, "Wrong usage of [] in the extension\n");
 				return 0;	/* unconditional failure */
 			}
-			for (pattern++; pattern != end; pattern++) {
+			if (pattern == end) {
+				/* Ignore empty character sets. */
+				++pattern;
+				continue;
+			}
+			for (; pattern < end; ++pattern) {
 				if (pattern+2 < end && pattern[1] == '-') { /* this is a range */
 					if (*data >= pattern[0] && *data <= pattern[2])
 						break;	/* match found */
@@ -2539,34 +2762,37 @@
 				} else if (*data == pattern[0])
 					break;	/* match found */
 			}
-			if (pattern == end) {
+			if (pattern >= end) {
 #ifdef NEED_DEBUG_HERE
-				ast_log(LOG_NOTICE,"return (0) when pattern==end\n");
+				ast_log(LOG_NOTICE,"return (0) when pattern>=end\n");
 #endif
 				return 0;
 			}
 			pattern = end;	/* skip and continue */
 			break;
+		case 'n':
 		case 'N':
 			if (*data < '2' || *data > '9') {
 #ifdef NEED_DEBUG_HERE
-				ast_log(LOG_NOTICE,"return (0) N is matched\n");
+				ast_log(LOG_NOTICE,"return (0) N is not matched\n");
 #endif
 				return 0;
 			}
 			break;
+		case 'x':
 		case 'X':
 			if (*data < '0' || *data > '9') {
 #ifdef NEED_DEBUG_HERE
-				ast_log(LOG_NOTICE,"return (0) X is matched\n");
+				ast_log(LOG_NOTICE,"return (0) X is not matched\n");
 #endif
 				return 0;
 			}
 			break;
+		case 'z':
 		case 'Z':
 			if (*data < '1' || *data > '9') {
 #ifdef NEED_DEBUG_HERE
-				ast_log(LOG_NOTICE,"return (0) Z is matched\n");
+				ast_log(LOG_NOTICE,"return (0) Z is not matched\n");
 #endif
 				return 0;
 			}
@@ -2581,10 +2807,6 @@
 			ast_log(LOG_NOTICE, "return (2) when '!' is matched\n");
 #endif
 			return 2;
-		case ' ':
-		case '-':	/* Ignore these in patterns */
-			data--; /* compensate the final data++ */
-			break;
 		default:
 			if (*data != *pattern) {
 #ifdef NEED_DEBUG_HERE
@@ -2592,9 +2814,10 @@
 #endif
 				return 0;
 			}
+			break;
 		}
-		data++;
-		pattern++;
+		++data;
+		++pattern;
 	}
 	if (*data)			/* data longer than pattern, no match */ {
 #ifdef NEED_DEBUG_HERE
@@ -2604,7 +2827,7 @@
 	}
 
 	/*
-	 * match so far, but ran off the end of the data.
+	 * match so far, but ran off the end of data.
 	 * Depending on what is next, determine match or not.
 	 */
 	if (*pattern == '\0' || *pattern == '/') {	/* exact match */
@@ -6880,17 +7103,17 @@
 
 	manager_show_dialplan_helper(s, m, idtext, context, exten, &counters, NULL);
 
-	if (context && !counters.context_existence) {
+	if (!ast_strlen_zero(context) && !counters.context_existence) {
 		char errorbuf[BUFSIZ];
 
 		snprintf(errorbuf, sizeof(errorbuf), "Did not find context %s", context);
 		astman_send_error(s, m, errorbuf);
 		return 0;
 	}
-	if (exten && !counters.extension_existence) {
+	if (!ast_strlen_zero(exten) && !counters.extension_existence) {
 		char errorbuf[BUFSIZ];
 
-		if (context)
+		if (!ast_strlen_zero(context))
 			snprintf(errorbuf, sizeof(errorbuf), "Did not find extension %s@%s", exten, context);
 		else
 			snprintf(errorbuf, sizeof(errorbuf), "Did not find extension %s in any context", exten);
@@ -6898,6 +7121,10 @@
 		return 0;
 	}
 
+	if (!counters.total_items) {
+		manager_dpsendack(s, m);
+	}
+
 	astman_append(s, "Event: ShowDialPlanComplete\r\n"
 		"EventList: Complete\r\n"
 		"ListItems: %d\r\n"
@@ -8308,8 +8535,15 @@
 
 	for (ep = NULL; e ; ep = e, e = e->peer) {
 		if (e->label && tmp->label && e->priority != tmp->priority && !strcmp(e->label, tmp->label)) {
-			ast_log(LOG_WARNING, "Extension '%s', priority %d in '%s', label '%s' already in use at "
-					"priority %d\n", tmp->exten, tmp->priority, con->name, tmp->label, e->priority);
+			if (strcmp(e->exten, tmp->exten)) {
+				ast_log(LOG_WARNING,
+					"Extension '%s' priority %d in '%s', label '%s' already in use at aliased extension '%s' priority %d\n",
+					tmp->exten, tmp->priority, con->name, tmp->label, e->exten, e->priority);
+			} else {
+				ast_log(LOG_WARNING,
+					"Extension '%s' priority %d in '%s', label '%s' already in use at priority %d\n",
+					tmp->exten, tmp->priority, con->name, tmp->label, e->priority);
+			}
 			repeated_label = 1;
 		}
 		if (e->priority >= tmp->priority) {
@@ -8334,7 +8568,15 @@
 		/* Can't have something exactly the same.  Is this a
 		   replacement?  If so, replace, otherwise, bonk. */
 		if (!replace) {
-			ast_log(LOG_WARNING, "Unable to register extension '%s', priority %d in '%s', already in use\n", tmp->exten, tmp->priority, con->name);
+			if (strcmp(e->exten, tmp->exten)) {
+				ast_log(LOG_WARNING,
+					"Unable to register extension '%s' priority %d in '%s', already in use by aliased extension '%s'\n",
+					tmp->exten, tmp->priority, con->name, e->exten);
+			} else {
+				ast_log(LOG_WARNING,
+					"Unable to register extension '%s' priority %d in '%s', already in use\n",
+					tmp->exten, tmp->priority, con->name);
+			}
 			if (tmp->datad) {
 				tmp->datad(tmp->data);
 				/* if you free this, null it out */
@@ -8522,7 +8764,7 @@
 	}
 
 	/* If we are adding a hint evalulate in variables and global variables */
-	if (priority == PRIORITY_HINT && strstr(application, "${") && !strstr(extension, "_")) {
+	if (priority == PRIORITY_HINT && strstr(application, "${") && extension[0] != '_') {
 		struct ast_channel *c = ast_dummy_channel_alloc();
 
 		if (c) {
@@ -8721,13 +8963,15 @@
 	int res;
 	struct ast_frame *f;
 	struct ast_app *app;
+	struct timeval start = ast_tvnow();
+	int ms;
 
-	while (timeout && (chan->_state != AST_STATE_UP)) {
-		res = ast_waitfor(chan, timeout);
+	while ((ms = ast_remaining_ms(start, timeout)) &&
+			chan->_state != AST_STATE_UP) {
+		res = ast_waitfor(chan, ms);
 		if (res < 1)
 			break;
-		if (timeout > -1)
-			timeout = res;
+
 		f = ast_read(chan);
 		if (!f)
 			break;
diff -uNrw asterisk-1.8.19.0/main/rtp_engine.c asterisk-1.8.20.0-rc1/main/rtp_engine.c
--- asterisk-1.8.19.0/main/rtp_engine.c	2012-06-15 17:56:08.000000000 +0200
+++ asterisk-1.8.20.0-rc1/main/rtp_engine.c	2012-11-07 18:01:13.000000000 +0100
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 369001 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 375993 $")
 
 #include <math.h>
 
@@ -810,6 +810,7 @@
 	enum ast_bridge_result res = AST_BRIDGE_FAILED;
 	struct ast_channel *who = NULL, *other = NULL, *cs[3] = { NULL, };
 	struct ast_frame *fr = NULL;
+	struct timeval start;
 
 	/* Start locally bridging both instances */
 	if (instance0->engine->local_bridge && instance0->engine->local_bridge(instance0, instance1)) {
@@ -840,7 +841,9 @@
 	cs[0] = c0;
 	cs[1] = c1;
 	cs[2] = NULL;
+	start = ast_tvnow();
 	for (;;) {
+		int ms;
 		/* If the underlying formats have changed force this bridge to break */
 		if ((c0->rawreadformat != c1->rawwriteformat) || (c1->rawreadformat != c0->rawwriteformat)) {
 			ast_debug(1, "rtp-engine-local-bridge: Oooh, formats changed, backing out\n");
@@ -865,8 +868,9 @@
 			break;
 		}
 		/* Wait on a channel to feed us a frame */
-		if (!(who = ast_waitfor_n(cs, 2, &timeoutms))) {
-			if (!timeoutms) {
+		ms = ast_remaining_ms(start, timeoutms);
+		if (!(who = ast_waitfor_n(cs, 2, &ms))) {
+			if (!ms) {
 				res = AST_BRIDGE_RETRY;
 				break;
 			}
@@ -985,6 +989,7 @@
 	struct ast_sockaddr ac1 = {{0,}}, vac1 = {{0,}}, tac1 = {{0,}}, ac0 = {{0,}}, vac0 = {{0,}}, tac0 = {{0,}};
 	struct ast_sockaddr t1 = {{0,}}, vt1 = {{0,}}, tt1 = {{0,}}, t0 = {{0,}}, vt0 = {{0,}}, tt0 = {{0,}};
 	struct ast_frame *fr = NULL;
+	struct timeval start;
 
 	/* Test the first channel */
 	if (!(glue0->update_peer(c0, instance1, vinstance1, tinstance1, codec1, 0))) {
@@ -1024,7 +1029,9 @@
 	cs[0] = c0;
 	cs[1] = c1;
 	cs[2] = NULL;
+	start = ast_tvnow();
 	for (;;) {
+		int ms;
 		/* Check if anything changed */
 		if ((c0->tech_pvt != pvt0) ||
 		    (c1->tech_pvt != pvt1) ||
@@ -1115,9 +1122,10 @@
 			oldcodec0 = codec0;
 		}
 
+		ms = ast_remaining_ms(start, timeoutms);
 		/* Wait for frame to come in on the channels */
-		if (!(who = ast_waitfor_n(cs, 2, &timeoutms))) {
-			if (!timeoutms) {
+		if (!(who = ast_waitfor_n(cs, 2, &ms))) {
+			if (!ms) {
 				res = AST_BRIDGE_RETRY;
 				break;
 			}
diff -uNrw asterisk-1.8.19.0/main/security_events.c asterisk-1.8.20.0-rc1/main/security_events.c
--- asterisk-1.8.19.0/main/security_events.c	2012-06-15 17:56:08.000000000 +0200
+++ asterisk-1.8.20.0-rc1/main/security_events.c	2012-11-19 20:30:58.000000000 +0100
@@ -30,7 +30,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 369001 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 376469 $")
 
 #include "asterisk/utils.h"
 #include "asterisk/strings.h"
@@ -440,7 +440,7 @@
 	return ast_event_new(AST_EVENT_SECURITY,
 		AST_EVENT_IE_SECURITY_EVENT, AST_EVENT_IE_PLTYPE_UINT, sec->event_type,
 		AST_EVENT_IE_EVENT_VERSION, AST_EVENT_IE_PLTYPE_UINT, sec->version,
-		AST_EVENT_IE_EVENT_TV, AST_EVENT_IE_PLTYPE_STR, str->str,
+		AST_EVENT_IE_EVENT_TV, AST_EVENT_IE_PLTYPE_STR, ast_str_buffer(str),
 		AST_EVENT_IE_SERVICE, AST_EVENT_IE_PLTYPE_STR, sec->service,
 		AST_EVENT_IE_SEVERITY, AST_EVENT_IE_PLTYPE_STR, severity_str,
 		AST_EVENT_IE_END);
diff -uNrw asterisk-1.8.19.0/main/stdtime/localtime.c asterisk-1.8.20.0-rc1/main/stdtime/localtime.c
--- asterisk-1.8.19.0/main/stdtime/localtime.c	2012-06-15 17:56:08.000000000 +0200
+++ asterisk-1.8.20.0-rc1/main/stdtime/localtime.c	2012-12-03 21:26:37.000000000 +0100
@@ -50,7 +50,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 369001 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 377135 $")
 
 #include <signal.h>
 #include <sys/stat.h>
@@ -71,6 +71,7 @@
 #include "private.h"
 #include "tzfile.h"
 
+#include "asterisk/_private.h"
 #include "asterisk/lock.h"
 #include "asterisk/localtime.h"
 #include "asterisk/strings.h"
@@ -427,7 +428,7 @@
 			closedir(sp->dir);
 		}
 #endif
-		free(sp);
+		ast_free(sp);
 
 		/* Just in case the signal was sent late */
 		AST_LIST_LOCK(&zonelist);
@@ -707,8 +708,15 @@
 			return -1;
 		if ((fid = open(name, OPEN_MODE)) == -1)
 			return -1;
+		if (ast_fully_booted) {
+			/* If we don't wait until Asterisk is fully booted, it's possible
+			 * that the watcher thread gets started in the parent process,
+			 * before daemon(3) is called, and the thread won't propagate to
+			 * the child.  Given that bootup only takes a few seconds, it's
+			 * reasonable to only start the watcher later. */
 		add_notify(sp, name);
 	}
+	}
 	nread = read(fid, u.buf, sizeof u.buf);
 	if (close(fid) < 0 || nread <= 0)
 		return -1;
@@ -1410,6 +1418,17 @@
 		return -1;
 }
 
+void clean_time_zones(void)
+{
+	struct state *sp;
+
+	AST_LIST_LOCK(&zonelist);
+	while ((sp = AST_LIST_REMOVE_HEAD(&zonelist, list))) {
+		ast_free(sp);
+	}
+	AST_LIST_UNLOCK(&zonelist);
+}
+
 static const struct state *ast_tzset(const char *zone)
 {
 	struct state *sp;
diff -uNrw asterisk-1.8.19.0/main/utils.c asterisk-1.8.20.0-rc1/main/utils.c
--- asterisk-1.8.19.0/main/utils.c	2012-08-27 15:43:23.000000000 +0200
+++ asterisk-1.8.20.0-rc1/main/utils.c	2012-11-18 21:11:18.000000000 +0100
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 371690 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 376428 $")
 
 #include <ctype.h>
 #include <sys/stat.h>
@@ -981,18 +981,7 @@
 #ifdef DEBUG_THREADS
 	struct thr_lock_info *lock_info;
 	pthread_mutexattr_t mutex_attr;
-#endif
-
-	/* note that even though data->name is a pointer to allocated memory,
-	   we are not freeing it here because ast_register_thread is going to
-	   keep a copy of the pointer and then ast_unregister_thread will
-	   free the memory
-	*/
-	ast_free(data);
-	ast_register_thread(a.name);
-	pthread_cleanup_push(ast_unregister_thread, (void *) pthread_self());
 
-#ifdef DEBUG_THREADS
 	if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
 		return NULL;
 
@@ -1009,6 +998,15 @@
 	pthread_mutex_unlock(&lock_infos_lock.mutex); /* Intentionally not the wrapper */
 #endif /* DEBUG_THREADS */
 
+	/* note that even though data->name is a pointer to allocated memory,
+	   we are not freeing it here because ast_register_thread is going to
+	   keep a copy of the pointer and then ast_unregister_thread will
+	   free the memory
+	*/
+	ast_free(data);
+	ast_register_thread(a.name);
+	pthread_cleanup_push(ast_unregister_thread, (void *) pthread_self());
+
 	ret = a.start_routine(a.data);
 
 	pthread_cleanup_pop(1);
@@ -1432,6 +1430,23 @@
 	}
 	return a;
 }
+
+int ast_remaining_ms(struct timeval start, int max_ms)
+{
+	int ms;
+
+	if (max_ms < 0) {
+		ms = max_ms;
+	} else {
+		ms = max_ms - ast_tvdiff_ms(ast_tvnow(), start);
+		if (ms < 0) {
+			ms = 0;
+		}
+	}
+
+	return ms;
+}
+
 #undef ONE_MILLION
 
 /*! \brief glibc puts a lock inside random(3), so that the results are thread-safe.
@@ -2137,3 +2152,35 @@
 	return NULL;
 }
 
+void ast_do_crash(void)
+{
+#if defined(DO_CRASH)
+	abort();
+	/*
+	 * Just in case abort() doesn't work or something else super
+	 * silly, and for Qwell's amusement.
+	 */
+	*((int *) 0) = 0;
+#endif	/* defined(DO_CRASH) */
+}
+
+#if defined(AST_DEVMODE)
+void __ast_assert_failed(int condition, const char *condition_str, const char *file, int line, const char *function)
+{
+	/*
+	 * Attempt to put it into the logger, but hope that at least
+	 * someone saw the message on stderr ...
+	 */
+	ast_log(__LOG_ERROR, file, line, function, "FRACK!, Failed assertion %s (%d)\n",
+		condition_str, condition);
+	fprintf(stderr, "FRACK!, Failed assertion %s (%d) at line %d in %s of %s\n",
+		condition_str, condition, line, function, file);
+	/*
+	 * Give the logger a chance to get the message out, just in case
+	 * we abort(), or Asterisk crashes due to whatever problem just
+	 * happened after we exit ast_assert().
+	 */
+	usleep(1);
+	ast_do_crash();
+}
+#endif	/* defined(AST_DEVMODE) */
diff -uNrw asterisk-1.8.19.0/main/xmldoc.c asterisk-1.8.20.0-rc1/main/xmldoc.c
--- asterisk-1.8.19.0/main/xmldoc.c	2012-11-04 01:54:19.000000000 +0100
+++ asterisk-1.8.20.0-rc1/main/xmldoc.c	2012-12-10 02:38:52.000000000 +0100
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 375758 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 377509 $")
 
 #include "asterisk/_private.h"
 #include "asterisk/paths.h"
@@ -354,7 +354,13 @@
 
 			/* Setup color */
 			if (withcolors) {
-				ast_term_color_code(&colorized, colorized_tags[c].colorfg, 0);
+				if (ast_opt_light_background) {
+					/* Turn off *bright* colors */
+					ast_term_color_code(&colorized, colorized_tags[c].colorfg & 0x7f, 0);
+				} else {
+					/* Turn on *bright* colors */
+					ast_term_color_code(&colorized, colorized_tags[c].colorfg | 0x80, 0);
+				}
 				if (!colorized) {
 					return NULL;
 				}
diff -uNrw asterisk-1.8.19.0/pbx/pbx_dundi.c asterisk-1.8.20.0-rc1/pbx/pbx_dundi.c
--- asterisk-1.8.19.0/pbx/pbx_dundi.c	2012-09-06 04:48:58.000000000 +0200
+++ asterisk-1.8.20.0-rc1/pbx/pbx_dundi.c	2012-11-27 21:32:31.000000000 +0100
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 372417 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 376657 $")
 
 #include "asterisk/network.h"
 #include <sys/ioctl.h>
@@ -3886,7 +3886,6 @@
 {
 	int results;
 	int x;
-	struct ast_module_user *u;
 	struct dundi_result dr[MAX_RESULTS];
 	AST_DECLARE_APP_ARGS(args,
 		AST_APP_ARG(number);
@@ -3903,8 +3902,6 @@
 		return -1;
 	}
 
-	u = ast_module_user_add(chan);
-
 	parse = ast_strdupa(num);
 
 	AST_STANDARD_APP_ARGS(args, parse);
@@ -3927,8 +3924,6 @@
 		}
 	}
 
-	ast_module_user_remove(u);
-
 	return 0;
 }
 
@@ -3967,7 +3962,6 @@
 
 static int dundi_query_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 {
-	struct ast_module_user *u;
 	AST_DECLARE_APP_ARGS(args,
 		AST_APP_ARG(number);
 		AST_APP_ARG(context);
@@ -3978,17 +3972,13 @@
 	struct dundi_result_datastore *drds;
 	struct ast_datastore *datastore;
 
-	u = ast_module_user_add(chan);
-
 	if (ast_strlen_zero(data)) {
 		ast_log(LOG_WARNING, "DUNDIQUERY requires an argument (number)\n");
-		ast_module_user_remove(u);
 		return -1;
 	}
 
 	if (!chan) {
 		ast_log(LOG_ERROR, "DUNDIQUERY can not be used without a channel!\n");
-		ast_module_user_remove(u);
 		return -1;
 	}
 
@@ -4003,7 +3993,6 @@
 		args.context = "e164";
 
 	if (!(drds = ast_calloc(1, sizeof(*drds)))) {
-		ast_module_user_remove(u);
 		return -1;
 	}
 
@@ -4012,7 +4001,6 @@
 
 	if (!(datastore = ast_datastore_alloc(&dundi_result_datastore_info, buf))) {
 		drds_destroy(drds);
-		ast_module_user_remove(u);
 		return -1;
 	}
 
@@ -4028,8 +4016,6 @@
 	ast_channel_datastore_add(chan, datastore);
 	ast_channel_unlock(chan);
 
-	ast_module_user_remove(u);
-
 	return 0;
 }
 
@@ -4040,7 +4026,6 @@
 
 static int dundi_result_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 {
-	struct ast_module_user *u;
 	AST_DECLARE_APP_ARGS(args,
 		AST_APP_ARG(id);
 		AST_APP_ARG(resultnum);
@@ -4051,8 +4036,6 @@
 	struct ast_datastore *datastore;
 	int res = -1;
 
-	u = ast_module_user_add(chan);
-
 	if (ast_strlen_zero(data)) {
 		ast_log(LOG_WARNING, "DUNDIRESULT requires an argument (id and resultnum)\n");
 		goto finish;
@@ -4107,8 +4090,6 @@
 		ast_log(LOG_WARNING, "Result number %u is not valid for DUNDi query results for ID %s!\n", num, args.id);
 
 finish:
-	ast_module_user_remove(u);
-
 	return res;
 }
 
@@ -4776,7 +4757,12 @@
 static int unload_module(void)
 {
 	pthread_t previous_netthreadid = netthreadid, previous_precachethreadid = precachethreadid, previous_clearcachethreadid = clearcachethreadid;
-	ast_module_user_hangup_all();
+
+	ast_cli_unregister_multiple(cli_dundi, ARRAY_LEN(cli_dundi));
+	ast_unregister_switch(&dundi_switch);
+	ast_custom_function_unregister(&dundi_function);
+	ast_custom_function_unregister(&dundi_query_function);
+	ast_custom_function_unregister(&dundi_result_function);
 
 	/* Stop all currently running threads */
 	dundi_shutdown = 1;
@@ -4793,11 +4779,6 @@
  		pthread_join(previous_clearcachethreadid, NULL);
  	}
 
-	ast_cli_unregister_multiple(cli_dundi, ARRAY_LEN(cli_dundi));
-	ast_unregister_switch(&dundi_switch);
-	ast_custom_function_unregister(&dundi_function);
-	ast_custom_function_unregister(&dundi_query_function);
-	ast_custom_function_unregister(&dundi_result_function);
 	close(netsocket);
 	io_context_destroy(io);
 	sched_context_destroy(sched);
diff -uNrw asterisk-1.8.19.0/pbx/pbx_spool.c asterisk-1.8.20.0-rc1/pbx/pbx_spool.c
--- asterisk-1.8.19.0/pbx/pbx_spool.c	2012-10-08 22:45:25.000000000 +0200
+++ asterisk-1.8.20.0-rc1/pbx/pbx_spool.c	2012-11-14 20:48:13.000000000 +0100
@@ -28,7 +28,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 374686 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 376232 $")
 
 #include <sys/stat.h>
 #include <time.h>
@@ -492,7 +492,7 @@
 	int res;
 	time_t now = time(NULL);
 
-	if (filename[0] != '/') {
+	if (!strchr(filename, '/')) {
 		char *fn = ast_alloca(strlen(qdir) + strlen(filename) + 2);
 		sprintf(fn, "%s/%s", qdir, filename); /* SAFE */
 		filename = fn;
diff -uNrw asterisk-1.8.19.0/.version asterisk-1.8.20.0-rc1/.version
--- asterisk-1.8.19.0/.version	2012-12-10 01:49:02.000000000 +0100
+++ asterisk-1.8.20.0-rc1/.version	2012-12-10 02:48:00.000000000 +0100
@@ -1 +1 @@
-1.8.19.0
+1.8.20.0-rc1
