diff --git a/apps/app_directory.c b/apps/app_directory.c
index 6669d09704602605bfe693ac2619b71d4eacd39f..08d5cf84200c30094796454a53283de4d2c49225 100644
--- a/apps/app_directory.c
+++ b/apps/app_directory.c
@@ -88,9 +88,12 @@ enum {
 	OPT_SELECTFROMMENU =  (1 << 3),
 } directory_option_flags;
 
-struct items {
+struct directory_item {
 	char exten[AST_MAX_EXTENSION + 1];
 	char name[AST_MAX_EXTENSION + 1];
+	char key[50];				/* Text to order items. Either lastname+firstname or firstname+lastname */
+
+	AST_LIST_ENTRY(directory_item) entry;
 };
 
 AST_APP_OPTIONS(directory_app_options, {
@@ -215,73 +218,80 @@ static void retrieve_file(char *dir)
 }
 #endif
 
-static char *convert(const char *lastname)
+static int compare(const char *text, const char *template)
 {
-	char *tmp;
-	int lcount = 0;
-	tmp = ast_malloc(NUMDIGITS + 1);
-	if (tmp) {
-		while((*lastname > 32) && lcount < NUMDIGITS) {
-			switch(toupper(*lastname)) {
-			case '1':
-				tmp[lcount++] = '1';
-				break;
-			case '2':
-			case 'A':
-			case 'B':
-			case 'C':
-				tmp[lcount++] = '2';
-				break;
-			case '3':
-			case 'D':
-			case 'E':
-			case 'F':
-				tmp[lcount++] = '3';
-				break;
-			case '4':
-			case 'G':
-			case 'H':
-			case 'I':
-				tmp[lcount++] = '4';
-				break;
-			case '5':
-			case 'J':
-			case 'K':
-			case 'L':
-				tmp[lcount++] = '5';
-				break;
-			case '6':
-			case 'M':
-			case 'N':
-			case 'O':
-				tmp[lcount++] = '6';
-				break;
-			case '7':
-			case 'P':
-			case 'Q':
-			case 'R':
-			case 'S':
-				tmp[lcount++] = '7';
-				break;
-			case '8':
-			case 'T':
-			case 'U':
-			case 'V':
-				tmp[lcount++] = '8';
-				break;
-			case '9':
-			case 'W':
-			case 'X':
-			case 'Y':
-			case 'Z':
-				tmp[lcount++] = '9';
-				break;
-			}
-			lastname++;
+	char digit;
+
+	while (*template) {
+		digit = toupper(*text++);
+		switch (digit) {
+		case 0:
+			return -1;
+		case '1':
+			digit = '1';
+			break;
+		case '2':
+		case 'A':
+		case 'B':
+		case 'C':
+			digit = '2';
+			break;
+		case '3':
+		case 'D':
+		case 'E':
+		case 'F':
+			digit = '3';
+			break;
+		case '4':
+		case 'G':
+		case 'H':
+		case 'I':
+			digit = '4';
+			break;
+		case '5':
+		case 'J':
+		case 'K':
+		case 'L':
+			digit = '5';
+			break;
+		case '6':
+		case 'M':
+		case 'N':
+		case 'O':
+			digit = '6';
+			break;
+		case '7':
+		case 'P':
+		case 'Q':
+		case 'R':
+		case 'S':
+			digit = '7';
+			break;
+		case '8':
+		case 'T':
+		case 'U':
+		case 'V':
+			digit = '8';
+			break;
+		case '9':
+		case 'W':
+		case 'X':
+		case 'Y':
+		case 'Z':
+			digit = '9';
+			break;
+
+		default:
+			if (digit > ' ')
+				return -1;
+			continue;
 		}
-		tmp[lcount] = '\0';
+
+		if (*template++ != digit)
+			return -1;
 	}
-	return tmp;
+
+	return 0;
 }
 
 /* play name of mailbox owner.
@@ -333,85 +343,117 @@ static int play_mailbox_owner(struct ast_channel *chan, const char *context,
 	return res;
 }
 
-static int get_mailbox_response(struct ast_channel *chan, const char *context, const char *dialcontext, const char *ext, const char *name, struct ast_flags *flags)
+static int select_entry(struct ast_channel *chan, const char *context, const char *dialcontext, const struct directory_item *item, struct ast_flags *flags)
 {
-	int res = 0;
-	int loop = 3;
+	ast_debug(1, "Selecting '%s' - %s@%s\n", item->name, item->exten, dialcontext);
 
-	res = play_mailbox_owner(chan, context, ext, name, flags);
-	for (loop = 3 ; loop > 0; loop--) {
-		if (!res)
-			res = ast_stream_and_wait(chan, "dir-instr", AST_DIGIT_ANY);
-		if (!res)
-			res = ast_waitfordigit(chan, 3000);
-		ast_stopstream(chan);
+	if (ast_test_flag(flags, OPT_FROMVOICEMAIL)) {
+		/* We still want to set the exten though */
+		ast_copy_string(chan->exten, item->exten, sizeof(chan->exten));
+	} else if (ast_goto_if_exists(chan, dialcontext, item->exten, 1)) {
+		ast_log(LOG_WARNING,
+			"Can't find extension '%s' in context '%s'.  "
+			"Did you pass the wrong context to Directory?\n",
+			item->exten, dialcontext);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int select_item_seq(struct ast_channel *chan, struct directory_item **items, int count, const char *context, const char *dialcontext, struct ast_flags *flags)
+{
+	struct directory_item *item, **ptr;
+	int i, res, loop;
+
+	for (ptr = items, i = 0; i < count; i++, ptr++) {
+		item = *ptr;
+
+		for (loop = 3 ; loop > 0; loop--) {
+			res = play_mailbox_owner(chan, context, item->exten, item->name, flags);
+
+			if (!res)
+				res = ast_stream_and_wait(chan, "dir-instr", AST_DIGIT_ANY);
+			if (!res)
+				res = ast_waitfordigit(chan, 3000);
+			ast_stopstream(chan);
 	
-		if (res < 0) /* User hungup, so jump out now */
-			break;
-		if (res == '1') {	/* Name selected */
-			if (ast_test_flag(flags, OPT_FROMVOICEMAIL)) {
-				/* We still want to set the exten though */
-				ast_copy_string(chan->exten, ext, sizeof(chan->exten));
-			} else {
-				if (ast_goto_if_exists(chan, dialcontext, ext, 1)) {
-					ast_log(LOG_WARNING,
-						"Can't find extension '%s' in context '%s'.  "
-						"Did you pass the wrong context to Directory?\n",
-						ext, dialcontext);
-					res = -1;
-				}
+			if (res == '1') {	/* Name selected */
+				return select_entry(chan, context, dialcontext, item, flags) ? -1 : 1;
+			} else if (res == '*') {
+				/* Skip to next match in list */
+				break;
 			}
-			break;
-		}
-		if (res == '*') /* Skip to next match in list */
-			break;
 
-		/* Not '1', or '*', so decrement number of tries */
-		res = 0;
+			if (res < 0)
+				return -1;
+
+			res = 0;
+		}
 	}
 
-	return(res);
+	/* Nothing was selected */
+	return 0;
 }
 
-static int select_item(struct ast_channel *chan, const struct items *items, int itemcount, const char *context, const char *dialcontext, struct ast_flags *flags)
+static int select_item_menu(struct ast_channel *chan, struct directory_item **items, int count, const char *context, const char *dialcontext, struct ast_flags *flags)
 {
-	int i, res = 0;
+	struct directory_item **block, *item;
+	int i, limit, res = 0;
 	char buf[9];
-	for (i = 0; i < itemcount; i++) {
-		snprintf(buf, sizeof(buf), "digits/%d", i + 1);
-		/* Press <num> for <name>, [ extension <ext> ] */
-		res = ast_streamfile(chan, "dir-multi1", chan->language) ||
-			ast_waitstream(chan, AST_DIGIT_ANY) ||
-			ast_streamfile(chan, buf, chan->language) ||
-			ast_waitstream(chan, AST_DIGIT_ANY) ||
-			ast_streamfile(chan, "dir-multi2", chan->language) ||
-			ast_waitstream(chan, AST_DIGIT_ANY) ||
-			play_mailbox_owner(chan, context, items[i].exten, items[i].name, flags) ||
-			ast_waitstream(chan, AST_DIGIT_ANY) ||
-			ast_waitfordigit(chan, 800);
-		if (res)
-			break;
-	}
-	/* Press "9" for more names. */
-	if (!res)
-		res = ast_waitstream(chan, AST_DIGIT_ANY) ||
-			(itemcount == 8 && ast_streamfile(chan, "dir-multi9", chan->language)) ||
-			ast_waitstream(chan, AST_DIGIT_ANY) ||
-			ast_waitfordigit(chan, 3000);
-
-	if (res && res > '0' && res < '9') {
-		if (ast_test_flag(flags, OPT_FROMVOICEMAIL)) {
-			/* We still want to set the exten */
-			ast_copy_string(chan->exten, items[res - '0'].exten, sizeof(chan->exten));
-		} else if (ast_goto_if_exists(chan, dialcontext, items[res - '1'].exten, 1)) {
-			ast_log(LOG_WARNING,
-				"Can't find extension '%s' in context '%s'.  "
-				"Did you pass the wrong context to Directory?\n",
-				items[res - '0'].exten, dialcontext);
-			res = -1;
+
+	for (block = items; count; block += limit, count -= limit) {
+		limit = count;
+		if (limit > 8)
+			limit = 8;
+
+		for (i = 0; i < limit && !res; i++) {
+			item = block[i];
+
+			snprintf(buf, sizeof(buf), "digits/%d", i + 1);
+			/* Press <num> for <name>, [ extension <ext> ] */
+			res = ast_streamfile(chan, "dir-multi1", chan->language);
+			if (!res)
+				res = ast_waitstream(chan, AST_DIGIT_ANY);
+			if (!res)
+				res = ast_streamfile(chan, buf, chan->language);
+			if (!res)
+				res = ast_waitstream(chan, AST_DIGIT_ANY);
+			if (!res)
+				res = ast_streamfile(chan, "dir-multi2", chan->language);
+			if (!res)
+				res = ast_waitstream(chan, AST_DIGIT_ANY);
+			if (!res)
+				res = play_mailbox_owner(chan, context, item->exten, item->name, flags);
+			if (!res)
+				res = ast_waitstream(chan, AST_DIGIT_ANY);
+			if (!res)
+				res = ast_waitfordigit(chan, 800);
 		}
+
+		/* Press "9" for more names. */
+		if (!res && count > limit) {
+			res = ast_streamfile(chan, "dir-multi9", chan->language);
+			if (!res)
+				res = ast_waitstream(chan, AST_DIGIT_ANY);
+		}
+
+		if (!res) {
+			res = ast_waitfordigit(chan, 3000);
+		}
+
+		if (res && res > '0' && res < '1' + limit) {
+			return select_entry(chan, context, dialcontext, block[res - '1'], flags) ? -1 : 1;
+		}
+
+		if (res < 0)
+			return -1;
+
+		res = 0;
 	}
-	return res;
+
+	/* Nothing was selected */
+	return 0;
 }
 
 static struct ast_config *realtime_directory(char *context)
@@ -473,18 +515,148 @@ static struct ast_config *realtime_directory(char *context)
 	return cfg;
 }
 
-static int do_directory(struct ast_channel *chan, struct ast_config *vmcfg, struct ast_config *ucfg, char *context, char *dialcontext, char digit, struct ast_flags *flags)
+static int check_match(struct directory_item **result, const char *item_fullname, const char *item_ext, const char *pattern_ext, int use_first_name)
+{
+	struct directory_item *item;
+	const char *key = NULL;
+	int namelen;
+
+
+	/* Set key to last name or first name depending on search mode */
+	if (!use_first_name)
+		key = strchr(item_fullname, ' ');
+
+	if (key)
+		key++;
+	else
+		key = item_fullname;
+
+	if (compare(key, pattern_ext))
+		return 0;
+
+	/* Match */
+	item = ast_calloc(1, sizeof(*item));
+	if (!item)
+		return -1;
+	ast_copy_string(item->name, item_fullname, sizeof(item->name));
+	ast_copy_string(item->exten, item_ext, sizeof(item->exten));
+
+	ast_copy_string(item->key, key, sizeof(item->key));
+	if (key != item_fullname) {
+		/* Key is the last name. Append first name to key in order to sort Last,First */
+		namelen = key - item_fullname - 1;
+		if (namelen > sizeof(item->key) - strlen(item->key) - 1)
+			namelen = sizeof(item->key) - strlen(item->key) - 1;
+		strncat(item->key, item_fullname, namelen);
+	}
+
+	*result = item;
+	return 1;
+}
+
+typedef AST_LIST_HEAD_NOLOCK(, directory_item) itemlist;
+
+static int search_directory(const char *context, struct ast_config *vmcfg, struct ast_config *ucfg, const char *ext, int use_first_name, itemlist *alist)
 {
-	/* Read in the first three digits..  "digit" is the first digit, already read */
-	char ext[NUMDIGITS + 1], *cat;
-	char name[80] = "";
 	struct ast_variable *v;
+	char buf[AST_MAX_EXTENSION + 1], *pos, *bufptr, *cat;
+	struct directory_item *item;
 	int res;
-	int found=0;
-	int lastuserchoice = 0;
-	char *start, *conv = NULL, *stringp = NULL;
-	char *pos;
-	int breakout = 0;
+
+	ast_debug(2, "Pattern: %s\n", ext);
+
+	for (v = ast_variable_browse(vmcfg, context); v; v = v->next) {
+
+		/* Ignore hidden */
+		if (strcasestr(v->value, "hidefromdir=yes"))
+			continue;
+
+		ast_copy_string(buf, v->value, sizeof(buf));
+		bufptr = buf;
+
+		/* password,Full Name,email,pager,options */
+		strsep(&bufptr, ",");
+		pos = strsep(&bufptr, ",");
+
+		res = check_match(&item, pos, v->name, ext, use_first_name);
+		if (!res)
+			continue;
+		else if (res < 0)
+			return -1;
+
+		AST_LIST_INSERT_TAIL(alist, item, entry);
+	}
+
+
+	if (ucfg) {
+		for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
+			const char *pos;
+			if (!strcasecmp(cat, "general"))
+				continue;
+			if (!ast_true(ast_config_option(ucfg, cat, "hasdirectory")))
+				continue;
+		
+			/* Find all candidate extensions */
+			pos = ast_variable_retrieve(ucfg, cat, "fullname");
+			if (!pos)
+				continue;
+
+			res = check_match(&item, pos, cat, ext, use_first_name);
+			if (!res)
+				continue;
+			else if (res < 0)
+				return -1;
+
+			AST_LIST_INSERT_TAIL(alist, item, entry);
+		}
+	}
+
+	return 0;
+}
+
+static void sort_items(struct directory_item **sorted, int count)
+{
+	int reordered, i;
+	struct directory_item **ptr, *tmp;
+
+	if (count < 2)
+		return;
+
+	/* Bubble-sort items by the key */
+	do {
+		reordered = 0;
+		for (ptr = sorted, i = 0; i < count - 1; i++, ptr++) {
+			if (strcasecmp(ptr[0]->key, ptr[1]->key) > 0) {
+				tmp = ptr[0];
+				ptr[0] = ptr[1];
+				ptr[1] = tmp;
+				reordered++;
+			}
+		}
+	} while (reordered);
+}
+
+static int goto_exten(struct ast_channel *chan, const char *dialcontext, char *ext)
+{
+	if (!ast_goto_if_exists(chan, dialcontext, ext, 1) ||
+	    (!ast_strlen_zero(chan->macrocontext) &&
+	     !ast_goto_if_exists(chan, chan->macrocontext, ext, 1))) {
+		return 0;
+	} else {
+		ast_log(LOG_WARNING, "Can't find extension '%s' in current context.  "
+			"Not Exiting the Directory!\n", ext);
+		return -1;
+	}
+}
+
+static int do_directory(struct ast_channel *chan, struct ast_config *vmcfg, struct ast_config *ucfg, char *context, char *dialcontext, char digit, struct ast_flags *flags)
+{
+	/* Read in the first three digits..  "digit" is the first digit, already read */
+	int res = 0;
+	itemlist alist = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
+	struct directory_item *item, **ptr, **sorted = NULL;
+	int count, i;
+	char ext[NUMDIGITS + 1] = "";
 
 	if (ast_strlen_zero(context)) {
 		ast_log(LOG_WARNING,
@@ -492,263 +664,72 @@ static int do_directory(struct ast_channel *chan, struct ast_config *vmcfg, stru
 			"(context in which to interpret extensions)\n");
 		return -1;
 	}
-	if (digit == '0') {
-		if (!ast_goto_if_exists(chan, dialcontext, "o", 1) ||
-		    (!ast_strlen_zero(chan->macrocontext) &&
-		     !ast_goto_if_exists(chan, chan->macrocontext, "o", 1))) {
-			return 0;
-		} else {
-			ast_log(LOG_WARNING, "Can't find extension 'o' in current context.  "
-				"Not Exiting the Directory!\n");
-			res = 0;
-		}
+
+	if (digit == '0' && !goto_exten(chan, dialcontext, "o")) {
+		return 0;
 	}	
-	if (digit == '*') {
-		if (!ast_goto_if_exists(chan, dialcontext, "a", 1) ||
-		    (!ast_strlen_zero(chan->macrocontext) &&
-		     !ast_goto_if_exists(chan, chan->macrocontext, "a", 1))) {
-			return 0;
-		} else {
-			ast_log(LOG_WARNING, "Can't find extension 'a' in current context.  "
-				"Not Exiting the Directory!\n");
-			res = 0;
-		}
+
+	if (digit == '*' && !goto_exten(chan, dialcontext, "a")) {
+		return 0;
 	}	
-	memset(ext, 0, sizeof(ext));
+
 	ext[0] = digit;
-	res = 0;
-	if (ast_readstring(chan, ext + 1, NUMDIGITS - 1, 3000, 3000, "#") < 0) res = -1;
-	if (!res) {
-		if (ast_test_flag(flags, OPT_SELECTFROMMENU)) {
-			char buf[AST_MAX_EXTENSION + 1], *bufptr, *fullname;
-			struct items menuitems[8];
-			int menucount = 0;
-			for (v = ast_variable_browse(vmcfg, context); v; v = v->next) {
-				if (strcasestr(v->value, "hidefromdir=yes") == NULL) {
-					ast_copy_string(buf, v->value, sizeof(buf));
-					bufptr = buf;
-					/* password,Full Name,email,pager,options */
-					strsep(&bufptr, ",");
-					pos = strsep(&bufptr, ",");
-					fullname = pos;
-
-					if (ast_test_flag(flags, OPT_LISTBYFIRSTNAME) && strrchr(pos, ' '))
-						pos = strrchr(pos, ' ') + 1;
-					conv = convert(pos);
-
-					if (conv && strcmp(conv, ext) == 0) {
-						/* Match */
-						found = 1;
-						ast_copy_string(menuitems[menucount].name, fullname, sizeof(menuitems[0].name));
-						ast_copy_string(menuitems[menucount].exten, v->name, sizeof(menuitems[0].exten));
-						menucount++;
-					}
-
-					if (menucount == 8) {
-						/* We have a full menu */
-						res = select_item(chan, menuitems, menucount, context, dialcontext, flags);
-						menucount = 0;
-						if (res != '9' && res != 0) {
-							if (res != -1)
-								lastuserchoice = res;
-							break;
-						}
-					}
-				}
-			}
+	if (ast_readstring(chan, ext + 1, NUMDIGITS - 1, 3000, 3000, "#") < 0)
+		return -1;
 
-			if (menucount > 0) {
-				/* We have a partial menu left over */
-				res = select_item(chan, menuitems, menucount, context, dialcontext, flags);
-				if (res != '9') {
-					if (res != -1)
-						lastuserchoice = res;
-				}
-			}
+	res = search_directory(context, vmcfg, ucfg, ext, ast_test_flag(flags, OPT_LISTBYFIRSTNAME), &alist);
+	if (res)
+		goto exit;
 
-			/* Make this flag conform to the old expected result */
-			if (lastuserchoice > '1' && lastuserchoice < '9')
-				lastuserchoice = '1';
-		} else {
-			/* Search for all names which start with those digits */
-			v = ast_variable_browse(vmcfg, context);
-			while(v && !res) {
-				/* Find all candidate extensions */
-				while(v) {
-					/* Find a candidate extension */
-					start = ast_strdup(v->value);
-					if (start && !strcasestr(start, "hidefromdir=yes")) {
-						stringp=start;
-						strsep(&stringp, ",");
-						pos = strsep(&stringp, ",");
-						if (pos) {
-							ast_copy_string(name, pos, sizeof(name));
-							/* Grab the last name */
-							if (!ast_test_flag(flags, OPT_LISTBYFIRSTNAME) && strrchr(pos,' '))
-								pos = strrchr(pos, ' ') + 1;
-							conv = convert(pos);
-							if (conv) {
-								if (!strncmp(conv, ext, strlen(ext))) {
-									/* Match! */
-									found++;
-									ast_free(conv);
-									ast_free(start);
-									break;
-								}
-								ast_free(conv);
-							}
-						}
-						ast_free(start);
-					}
-					v = v->next;
-				}
+	/* Count items in the list */
+	count = 0;
+	AST_LIST_TRAVERSE(&alist, item, entry) {
+		count++;
+	}
 
-				if (v) {
-					/* We have a match -- play a greeting if they have it */
-					res = get_mailbox_response(chan, context, dialcontext, v->name, name, flags);
-					switch (res) {
-					case -1:
-						/* user pressed '1' but extension does not exist, or
-						 * user hungup
-						 */
-						lastuserchoice = 0;
-						break;
-					case '1':
-						/* user pressed '1' and extensions exists;
-						   get_mailbox_response will already have done
-						   a goto() on the channel
-						 */
-						lastuserchoice = res;
-						break;
-					case '*':
-						/* user pressed '*' to skip something found */
-						lastuserchoice = res;
-						res = 0;
-						break;
-					default:
-						break;
-					}
-					v = v->next;
-				}
-			}
-		}
+	if (count < 1) {
+		res = ast_streamfile(chan, "dir-nomatch", chan->language);
+		goto exit;
+	}
 
-		if (!res && ucfg) {
-			/* Search users.conf for all names which start with those digits */
-			if (ast_test_flag(flags, OPT_SELECTFROMMENU)) {
-				char *fullname = NULL;
-				struct items menuitems[8];
-				int menucount = 0;
-				for (cat = ast_category_browse(ucfg, NULL); cat && !res ; cat = ast_category_browse(ucfg, cat)) {
-					const char *pos;
-					if (!strcasecmp(cat, "general"))
-						continue;
-					if (!ast_true(ast_config_option(ucfg, cat, "hasdirectory")))
-						continue;
-				
-					/* Find all candidate extensions */
-					if ((pos = ast_variable_retrieve(ucfg, cat, "fullname"))) {
-						ast_copy_string(name, pos, sizeof(name));
-						/* Grab the last name */
-						if (!ast_test_flag(flags, OPT_LISTBYFIRSTNAME) && strrchr(pos,' '))
-							pos = strrchr(pos, ' ') + 1;
-						conv = convert(pos);
-						if (conv && strcmp(conv, ext) == 0) {
-							/* Match */
-							found = 1;
-							ast_copy_string(menuitems[menucount].name, fullname, sizeof(menuitems[0].name));
-							ast_copy_string(menuitems[menucount].exten, v->name, sizeof(menuitems[0].exten));
-							menucount++;
-
-							if (menucount == 8) {
-								/* We have a full menu */
-								res = select_item(chan, menuitems, menucount, context, dialcontext, flags);
-								menucount = 0;
-								if (res != '9' && res != 0) {
-									if (res != -1)
-										lastuserchoice = res;
-									break;
-								}
-							}
-						}
-					}
-				}
-				if (menucount > 0) {
-					/* We have a partial menu left over */
-					res = select_item(chan, menuitems, menucount, context, dialcontext, flags);
-					if (res != '9') {
-						if (res != -1)
-							lastuserchoice = res;
-					}
-				}
 
-				/* Make this flag conform to the old expected result */
-				if (lastuserchoice > '1' && lastuserchoice < '9')
-					lastuserchoice = '1';
-			} else { /* !menu */
-				for (cat = ast_category_browse(ucfg, NULL); cat && !res ; cat = ast_category_browse(ucfg, cat)) {
-					const char *pos;
-					if (!strcasecmp(cat, "general"))
-						continue;
-					if (!ast_true(ast_config_option(ucfg, cat, "hasdirectory")))
-						continue;
-				
-					/* Find all candidate extensions */
-					if ((pos = ast_variable_retrieve(ucfg, cat, "fullname"))) {
-						ast_copy_string(name, pos, sizeof(name));
-						/* Grab the last name */
-						if (ast_test_flag(flags, OPT_LISTBYFIRSTNAME) && strrchr(pos,' '))
-							pos = strrchr(pos, ' ') + 1;
-						conv = convert(pos);
-						if (conv && strcmp(conv, ext) == 0) {
-							/* Match! */
-							found++;
-							/* We have a match -- play a greeting if they have it */
-							res = get_mailbox_response(chan, context, dialcontext, cat, name, flags);
-						}
-						switch (res) {
-						case -1:
-							/* user pressed '1' but extension does not exist, or
-							 * user hungup
-							 */
-							lastuserchoice = 0;
-							breakout = 1;
-							break;
-						case '1':
-							/* user pressed '1' and extensions exists;
-							   play_mailbox_owner will already have done
-							   a goto() on the channel
-							 */
-							lastuserchoice = res;
-							breakout = 1;
-							break;
-						case '*':
-							/* user pressed '*' to skip something found */
-							lastuserchoice = res;
-							breakout = 0;
-							res = 0;
-							break;
-						default:
-							breakout = 1;
-							break;
-						}
-						ast_free(conv);
-						if (breakout)
-							break;
-					} else
-						ast_free(conv);
-				}
-			}
-		}
+	/* Create plain array of pointers to items (for sorting) */
+	sorted = ast_calloc(count, sizeof(*sorted));
 
-		if (lastuserchoice != '1') {
-			res = ast_streamfile(chan, found ? "dir-nomore" : "dir-nomatch", chan->language);
-			if (!res)
-				res = 1;
-			return res;
+	ptr = sorted;
+	AST_LIST_TRAVERSE(&alist, item, entry) {
+		*ptr++ = item;
+	}
+
+	/* Sort items */
+	sort_items(sorted, count);
+
+	if (option_debug) {
+		ast_debug(2, "Listing matching entries:\n");
+		for (ptr = sorted, i = 0; i < count; i++, ptr++) {
+			ast_log(LOG_DEBUG, "%s: %s\n", ptr[0]->exten, ptr[0]->name);
 		}
-		return 0;
 	}
+
+	if (ast_test_flag(flags, OPT_SELECTFROMMENU)) {
+		/* Offer multiple entries at the same time */
+		res = select_item_menu(chan, sorted, count, context, dialcontext, flags);
+	} else {
+		/* Offer entries one by one */
+		res = select_item_seq(chan, sorted, count, context, dialcontext, flags);
+	}
+
+	if (!res) {
+		res = ast_streamfile(chan, "dir-nomore", chan->language);
+	}
+
+exit:
+	if (sorted)
+		ast_free(sorted);
+
+	while ((item = AST_LIST_REMOVE_HEAD(&alist, entry)))
+		ast_free(item);
+
 	return res;
 }
 
@@ -804,21 +785,26 @@ static int directory_exec(struct ast_channel *chan, void *data)
 		ast_stopstream(chan);
 		if (!res)
 			res = ast_waitfordigit(chan, 5000);
-		if (res > 0) {
-			res = do_directory(chan, cfg, ucfg, args.vmcontext, args.dialcontext, res, &flags);
-			if (res > 0) {
-				res = ast_waitstream(chan, AST_DIGIT_ANY);
-				ast_stopstream(chan);
-				if (res >= 0)
-					continue;
-			}
-		}
-		break;
+
+		if (res <= 0)
+			break;
+
+		res = do_directory(chan, cfg, ucfg, args.vmcontext, args.dialcontext, res, &flags);
+		if (res)
+			break;
+
+		res = ast_waitstream(chan, AST_DIGIT_ANY);
+		ast_stopstream(chan);
+
+		if (res)
+			break;
 	}
+
 	if (ucfg)
 		ast_config_destroy(ucfg);
 	ast_config_destroy(cfg);
-	return res;
+
+	return res < 0 ? -1 : 0;
 }
 
 static int unload_module(void)