diff --git a/ChangeLog b/ChangeLog
index 986b38979bbaa9ec99c3b4710940aa7cce2e164a..f827a9562b6c261101a686727c0e4fac1a100fb2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -149,6 +149,15 @@ CVS code -
 	  get the value of totsize in a few more places.  Changes to
 	  read_line(), read_file(), do_delete(), do_input(),
 	  get_totals(), and do_cursorpos(). (DLR)
+	- Overhaul the tab completion code and a few related functions
+	  to increase efficiency and support multibyte characters.  New
+	  functions strrchrn() and is_dir(); changes to diralphasort(),
+	  username_tab_completion(), cwd_tab_completion(), input_tab(),
+	  tail(), and striponedir(); removal of append_slash_if_dir()
+	  and check_wildcard_match(). (David Benbennick)  DLR: Move the
+	  routine to get the current user's home directory into the new
+	  function get_homedir(), and use it where necessary.  Also add
+	  a few miscellaneous tweaks.
 - cut.c:
   do_cut_text()
 	- If keep_cutbuffer is FALSE, only blow away the text in the
diff --git a/src/chars.c b/src/chars.c
index 356ecc5073d7148663936396950b1532e4eaf2f0..dc7303ce040898e14748eee9fb8b66e87f4e99eb 100644
--- a/src/chars.c
+++ b/src/chars.c
@@ -41,13 +41,13 @@
 
 /* Return TRUE if the value of c is in byte range, and FALSE
  * otherwise. */
-bool is_byte(unsigned int c)
+bool is_byte(int c)
 {
-    return (c == (unsigned char)c);
+    return ((unsigned int)c == (unsigned char)c);
 }
 
 /* This function is equivalent to isalnum(). */
-bool is_alnum_char(unsigned int c)
+bool is_alnum_char(int c)
 {
     return isalnum(c);
 }
@@ -82,7 +82,7 @@ bool is_alnum_wchar(wchar_t wc)
 #endif
 
 /* This function is equivalent to isblank(). */
-bool is_blank_char(unsigned int c)
+bool is_blank_char(int c)
 {
     return
 #ifdef HAVE_ISBLANK
@@ -130,9 +130,10 @@ bool is_blank_wchar(wchar_t wc)
 
 /* This function is equivalent to iscntrl(), except in that it also
  * handles control characters with their high bits set. */
-bool is_cntrl_char(unsigned int c)
+bool is_cntrl_char(int c)
 {
-    return (0 <= c && c < 32) || (127 <= c && c < 160);
+    return (-128 <= c && c < -96) || (0 <= c && c < 32) ||
+	(127 <= c && c < 160);
 }
 
 /* This function is equivalent to iscntrl() for multibyte characters,
@@ -273,7 +274,7 @@ int mb_cur_max(void)
 /* Convert the value in chr to a multibyte character with the same
  * wide character value as chr.  Return the multibyte character and its
  * length. */
-char *make_mbchar(unsigned int chr, char *chr_mb, int *chr_mb_len)
+char *make_mbchar(int chr, char *chr_mb, int *chr_mb_len)
 {
     assert(chr_mb != NULL && chr_mb_len != NULL);
 
@@ -412,7 +413,7 @@ size_t move_mbleft(const char *buf, size_t pos)
 #endif
 		, NULL);
 
-	if (pos_prev <= buf_mb_len)
+	if (pos_prev <= (size_t)buf_mb_len)
 	    break;
 
 	pos_prev -= buf_mb_len;
@@ -760,3 +761,17 @@ size_t mbstrnlen(const char *s, size_t maxlen)
 		nstrnlen(s, maxlen);
 #endif
 }
+
+/* Find the one-based position of the last occurrence of character c in
+ * the first n characters of s.  Return 0 if c is not found. */
+size_t strrchrn(const char *s, int c, size_t n)
+{
+    assert(n <= strlen(s));
+
+    for (s += n - 1; n >= 1; n--, s--) {
+	if (c == *s)
+	    return n;
+    }
+
+    return 0;
+}
diff --git a/src/files.c b/src/files.c
index 1a50b483e91a79f70b8cae0d9bf8ef954af8b03a..73446e79f5b8764e13fd197aacee8c09ccc9bb7b 100644
--- a/src/files.c
+++ b/src/files.c
@@ -1911,6 +1911,7 @@ int do_writeout(bool exiting)
     } /* while (TRUE) */
 
     free(ans);
+
     return retval;
 }
 
@@ -1920,71 +1921,96 @@ void do_writeout_void(void)
     display_main_list();
 }
 
-/* Return a malloc()ed string containing the actual directory, used
- * to convert ~user and ~/ notation... */
+/* Return a malloc()ed string containing the actual directory, used to
+ * convert ~user/ and ~/ notation. */
 char *real_dir_from_tilde(const char *buf)
 {
     char *dirtmp = NULL;
 
+    if (buf == NULL)
+    	return NULL;
+
     if (buf[0] == '~') {
 	size_t i;
-	const struct passwd *userdata;
+	const char *tilde_dir;
 
-	/* Figure how how much of the str we need to compare */
+	/* Figure out how much of the str we need to compare. */
 	for (i = 1; buf[i] != '/' && buf[i] != '\0'; i++)
 	    ;
 
-	/* Determine home directory using getpwuid() or getpwent(),
-	   don't rely on $HOME */
-	if (i == 1)
-	    userdata = getpwuid(geteuid());
-	else {
+	/* Get the home directory. */
+	if (i == 1) {
+	    get_homedir();
+	    tilde_dir = homedir;
+	} else {
+	    const struct passwd *userdata;
+
 	    do {
 		userdata = getpwent();
 	    } while (userdata != NULL &&
 		strncmp(userdata->pw_name, buf + 1, i - 1) != 0);
+	    endpwent();
+	    tilde_dir = userdata->pw_dir;
 	}
-	endpwent();
 
-	if (userdata != NULL) {	/* User found */
-	    dirtmp = charalloc(strlen(userdata->pw_dir) +
-		strlen(buf + i) + 1);
-	    sprintf(dirtmp, "%s%s", userdata->pw_dir, &buf[i]);
+	if (tilde_dir != NULL) {
+	    dirtmp = charalloc(strlen(tilde_dir) + strlen(buf + i) + 1);
+	    sprintf(dirtmp, "%s%s", tilde_dir, buf + i);
 	}
     }
 
+    /* Set a default value for dirtmp, in case the user's home directory
+     * isn't found. */
     if (dirtmp == NULL)
-	dirtmp = mallocstrcpy(dirtmp, buf);
+	dirtmp = mallocstrcpy(NULL, buf);
 
     return dirtmp;
 }
 
+#if !defined(DISABLE_TABCOMP) || !defined(DISABLE_BROWSER)
+/* Our sort routine for file listings.  Sort directories before
+ * filenames, alphabetically and ignoring case differences.  Sort
+ * filenames the same way, except for ignoring an initial dot. */
+int diralphasort(const void *va, const void *vb)
+{
+    struct stat fileinfo;
+    const char *a = *(const char *const *)va;
+    const char *b = *(const char *const *)vb;
+    bool aisdir = stat(a, &fileinfo) != -1 && S_ISDIR(fileinfo.st_mode);
+    bool bisdir = stat(b, &fileinfo) != -1 && S_ISDIR(fileinfo.st_mode);
+
+    if (aisdir && !bisdir)
+	return -1;
+    if (!aisdir && bisdir)
+	return 1;
+
+    if (*a == '.')
+	a++;
+    if (*b == '.')
+	b++;
+
+    return strcasecmp(a, b);
+}
+#endif
+
 #ifndef DISABLE_TABCOMP
-/* Tack a slash onto the string we're completing if it's a directory.
- * We assume there is room for one more character on the end of buf.
- * The return value says whether buf is a directory. */
-int append_slash_if_dir(char *buf, bool *lastwastab, int *place)
+/* Is the given file a directory? */
+int is_dir(const char *buf)
 {
     char *dirptr = real_dir_from_tilde(buf);
     struct stat fileinfo;
-    int ret = 0;
 
-    assert(dirptr != buf);
+    int ret = (stat(dirptr, &fileinfo) != -1 &&
+		S_ISDIR(fileinfo.st_mode));
 
-    if (stat(dirptr, &fileinfo) != -1 && S_ISDIR(fileinfo.st_mode)) {
-	strncat(buf, "/", 1);
-	(*place)++;
-	/* now we start over again with # of tabs so far */
-	*lastwastab = FALSE;
-	ret = 1;
-    }
+    assert(buf != NULL && dirptr != buf);
 
     free(dirptr);
+
     return ret;
 }
 
-/*
- * These functions (username_tab_completion(), cwd_tab_completion(), and
+/* These functions (username_tab_completion(), cwd_tab_completion(), and
  * input_tab()) were taken from busybox 0.46 (cmdedit.c).  Here is the
  * notice from that file:
  *
@@ -1999,46 +2025,38 @@ int append_slash_if_dir(char *buf, bool *lastwastab, int *place)
  * You may use this code as you wish, so long as the original author(s)
  * are attributed in any redistributions of the source code.
  * This code is 'as is' with no warranty.
- * This code may safely be consumed by a BSD or GPL license.
- */
+ * This code may safely be consumed by a BSD or GPL license. */
 
-char **username_tab_completion(char *buf, int *num_matches)
+/* We consider the first buflen characters of buf for ~username tab
+ * completion. */
+char **username_tab_completion(const char *buf, size_t *num_matches,
+	size_t buflen)
 {
-    char **matches = (char **)NULL;
-    char *matchline = NULL;
-    struct passwd *userdata;
+    char **matches = NULL;
+    const struct passwd *userdata;
 
-    *num_matches = 0;
-    matches = (char **)nmalloc(BUFSIZ * sizeof(char *));
+    assert(buf != NULL && num_matches != NULL && buflen > 0);
 
-    strcat(buf, "*");
+    *num_matches = 0;
 
     while ((userdata = getpwent()) != NULL) {
-
-	if (check_wildcard_match(userdata->pw_name, &buf[1])) {
-
-	    /* Cool, found a match.  Add it to the list
-	     * This makes a lot more sense to me (Chris) this way...
-	     */
+	if (strncmp(userdata->pw_name, buf + 1, buflen - 1) == 0) {
+	    /* Cool, found a match.  Add it to the list.  This makes a
+	     * lot more sense to me (Chris) this way... */
 
 #ifndef DISABLE_OPERATINGDIR
 	    /* ...unless the match exists outside the operating
-               directory, in which case just go to the next match */
-
-	    if (operating_dir != NULL) {
-		if (check_operating_dir(userdata->pw_dir, TRUE) != 0)
-		    continue;
-	    }
+	     * directory, in which case just go to the next match. */
+	    if (check_operating_dir(userdata->pw_dir, TRUE))
+		continue;
 #endif
 
-	    matchline = charalloc(strlen(userdata->pw_name) + 2);
-	    sprintf(matchline, "~%s", userdata->pw_name);
-	    matches[*num_matches] = matchline;
+	    matches = (char **)nrealloc(matches, (*num_matches + 1) *
+		sizeof(char *));
+	    matches[*num_matches] =
+		charalloc(strlen(userdata->pw_name) + 2);
+	    sprintf(matches[*num_matches], "~%s", userdata->pw_name);
 	    ++(*num_matches);
-
-	    /* If there's no more room, bail out */
-	    if (*num_matches == BUFSIZ)
-		break;
 	}
     }
     endpwent();
@@ -2047,365 +2065,271 @@ char **username_tab_completion(char *buf, int *num_matches)
 }
 
 /* This was originally called exe_n_cwd_tab_completion, but we're not
-   worried about executables, only filenames :> */
-
-char **cwd_tab_completion(char *buf, int *num_matches)
+ * worried about executables, only filenames :> */
+char **cwd_tab_completion(const char *buf, size_t *num_matches, size_t
+	buflen)
 {
-    char *dirname, *dirtmp = NULL, *tmp = NULL, *tmp2 = NULL;
-    char **matches = (char **)NULL;
+    char *dirname = mallocstrcpy(NULL, buf);
+    char *filename;
+#ifndef DISABLE_OPERATINGDIR
+    size_t dirnamelen;
+#endif
+    size_t filenamelen;
+    char **matches = NULL;
     DIR *dir;
-    struct dirent *next;
+    const struct dirent *next;
 
-    matches = (char **)nmalloc(BUFSIZ * sizeof(char *));
-
-    /* Stick a wildcard onto the buf, for later use */
-    strcat(buf, "*");
-
-    /* Okie, if there's a / in the buffer, strip out the directory part */
-    if (buf[0] != '\0' && strstr(buf, "/") != NULL) {
-	dirname = charalloc(strlen(buf) + 1);
-	tmp = buf + strlen(buf);
-	while (*tmp != '/' && tmp != buf)
-	    tmp--;
-
-	tmp++;
-
-	strncpy(dirname, buf, tmp - buf + 1);
-	dirname[tmp - buf] = '\0';
+    assert(dirname != NULL && num_matches != NULL && buflen >= 0);
 
+    *num_matches = 0;
+    null_at(&dirname, buflen);
+
+    /* Okie, if there's a / in the buffer, strip out the directory
+     * part. */
+    filename = strrchr(dirname, '/');
+    if (filename != NULL) {
+	char *tmpdirname = filename + 1;
+
+	filename = mallocstrcpy(NULL, tmpdirname);
+	*tmpdirname = '\0';
+	tmpdirname = dirname;
+	dirname = real_dir_from_tilde(dirname);
+	free(tmpdirname);
     } else {
-
-	if ((dirname = getcwd(NULL, PATH_MAX + 1)) == NULL)
-	    return matches;
-	else
-	    tmp = buf;
+	filename = dirname;
+	dirname = mallocstrcpy(NULL, "./");
     }
 
-#ifdef DEBUG
-    fprintf(stderr, "\nDir = %s\n", dirname);
-    fprintf(stderr, "\nbuf = %s\n", buf);
-    fprintf(stderr, "\ntmp = %s\n", tmp);
-#endif
-
-    dirtmp = real_dir_from_tilde(dirname);
-    free(dirname);
-    dirname = dirtmp;
-
-#ifdef DEBUG
-    fprintf(stderr, "\nDir = %s\n", dirname);
-    fprintf(stderr, "\nbuf = %s\n", buf);
-    fprintf(stderr, "\ntmp = %s\n", tmp);
-#endif
-
+    assert(dirname[strlen(dirname) - 1] == '/');
 
     dir = opendir(dirname);
+
     if (dir == NULL) {
-	/* Don't print an error, just shut up and return */
-	*num_matches = 0;
+	/* Don't print an error, just shut up and return. */
 	beep();
-	return matches;
+	free(filename);
+	free(dirname);
+	return NULL;
     }
+
+#ifndef DISABLE_OPERATINGDIR
+    dirnamelen = strlen(dirname);
+#endif
+    filenamelen = strlen(filename);
+
     while ((next = readdir(dir)) != NULL) {
 
 #ifdef DEBUG
 	fprintf(stderr, "Comparing \'%s\'\n", next->d_name);
 #endif
-	/* See if this matches */
-	if (check_wildcard_match(next->d_name, tmp)) {
-
-	    /* Cool, found a match.  Add it to the list
-	     * This makes a lot more sense to me (Chris) this way...
-	     */
+	/* See if this matches. */
+	if (strncmp(next->d_name, filename, filenamelen) == 0 &&
+		(*filename == '.' || (strcmp(next->d_name, ".") != 0 &&
+		strcmp(next->d_name, "..") != 0))) {
+	    /* Cool, found a match.  Add it to the list.  This makes a
+	     * lot more sense to me (Chris) this way... */
 
 #ifndef DISABLE_OPERATINGDIR
 	    /* ...unless the match exists outside the operating
-               directory, in which case just go to the next match; to
-	       properly do operating directory checking, we have to add the
-	       directory name to the beginning of the proposed match
-	       before we check it */
-
-	    if (operating_dir != NULL) {
-		tmp2 = charalloc(strlen(dirname) + strlen(next->d_name) + 2);
-		strcpy(tmp2, dirname);
-		strcat(tmp2, "/");
-		strcat(tmp2, next->d_name);
-		if (check_operating_dir(tmp2, TRUE) != 0) {
-		    free(tmp2);
-		    continue;
-		}
-	        free(tmp2);
+	     * directory, in which case just go to the next match.  To
+	     * properly do operating directory checking, we have to add
+	     * the directory name to the beginning of the proposed match
+	     * before we check it. */
+	    char *tmp2 = charalloc(strlen(dirname) +
+		strlen(next->d_name) + 1);
+
+	    sprintf(tmp2, "%s%s", dirname, next->d_name);
+	    if (check_operating_dir(tmp2, TRUE)) {
+		free(tmp2);
+		continue;
 	    }
+	    free(tmp2);
 #endif
 
-	    tmp2 = NULL;
-	    tmp2 = charalloc(strlen(next->d_name) + 1);
-	    strcpy(tmp2, next->d_name);
-	    matches[*num_matches] = tmp2;
-	    ++*num_matches;
-
-	    /* If there's no more room, bail out */
-	    if (*num_matches == BUFSIZ)
-		break;
+	    matches = (char **)nrealloc(matches, (*num_matches + 1) *
+		sizeof(char *));
+	    matches[*num_matches] = mallocstrcpy(NULL, next->d_name);
+	    ++(*num_matches);
 	}
     }
     closedir(dir);
     free(dirname);
+    free(filename);
 
     return matches;
 }
 
-/* This function now has an arg which refers to how much the statusbar
- * (place) should be advanced, i.e. the new cursor pos. */
-char *input_tab(char *buf, int place, bool *lastwastab, int *newplace,
-	bool *list)
+/* Do tab completion.  This function now has an arg which refers to how
+ * much the statusbar cursor position (place) should be advanced. */
+char *input_tab(char *buf, size_t *place, bool *lastwastab, bool *list)
 {
-    /* Do TAB completion */
-    static int num_matches = 0, match_matches = 0;
-    static char **matches = (char **)NULL;
-    int pos = place, i = 0, col = 0, editline = 0;
-    int longestname = 0, is_dir = 0;
-    char *foo;
-
-    *list = FALSE;
-
-    if (*lastwastab == FALSE) {
-	char *tmp, *copyto, *matchbuf;
-
-	*lastwastab = TRUE;
-
-	/* Make a local copy of the string -- up to the position of the
-	   cursor */
-	matchbuf = charalloc(strlen(buf) + 2);
-	memset(matchbuf, '\0', strlen(buf) + 2);
-
-	strncpy(matchbuf, buf, place);
-	tmp = matchbuf;
-
-	/* skip any leading white space */
-	while (*tmp && is_blank_char(*tmp))
-	    ++tmp;
-
-	/* Free up any memory already allocated */
-	if (matches != NULL) {
-	    for (i = i; i < num_matches; i++)
-		free(matches[i]);
-	    free(matches);
-	    matches = (char **)NULL;
-	    num_matches = 0;
-	}
+    size_t num_matches = 0;
+    char **matches = NULL;
 
-	/* If the word starts with `~' and there is no slash in the word, 
-	 * then try completing this word as a username. */
-
-	/* If the original string begins with a tilde, and the part
-	   we're trying to tab-complete doesn't contain a slash, copy
-	   the part we're tab-completing into buf, so tab completion
-	   will result in buf's containing only the tab-completed
-	   username. */
-	if (buf[0] == '~' && strchr(tmp, '/') == NULL) {
-	    buf = mallocstrcpy(buf, tmp);
-	    matches = username_tab_completion(tmp, &num_matches);
-	}
-	/* If we're in the middle of the original line, copy the string
-	   only up to the cursor position into buf, so tab completion
-	   will result in buf's containing only the tab-completed
-	   path/filename. */
-	else if (strlen(buf) > strlen(tmp))
-	    buf = mallocstrcpy(buf, tmp);
+    assert(buf != NULL && place != NULL && *place <= strlen(buf) && lastwastab != NULL && list != NULL);
 
-	/* Try to match everything in the current working directory that
-	 * matches.  */
-	if (matches == NULL)
-	    matches = cwd_tab_completion(tmp, &num_matches);
+    *list = 0;
 
-	/* Don't leak memory */
-	free(matchbuf);
+    /* If the word starts with `~' and there is no slash in the word,
+     * then try completing this word as a username. */
+    if (*place > 0 && *buf == '~') {
+	const char *bob = strchr(buf, '/');
 
-#ifdef DEBUG
-	fprintf(stderr, "%d matches found...\n", num_matches);
-#endif
-	/* Did we find exactly one match? */
-	switch (num_matches) {
-	case 0:
-	    blank_edit();
-	    wrefresh(edit);
-	    break;
-	case 1:
+	if (bob == NULL || bob >= buf + *place)
+	    matches = username_tab_completion(buf, &num_matches,
+		*place);
+    }
 
-	    buf = charealloc(buf, strlen(buf) + strlen(matches[0]) + 1);
+    /* Match against files relative to the current working directory. */
+    if (matches == NULL)
+	matches = cwd_tab_completion(buf, &num_matches, *place);
 
-	    if (buf[0] != '\0' && strstr(buf, "/") != NULL) {
-		for (tmp = buf + strlen(buf); *tmp != '/' && tmp != buf;
-		     tmp--);
-		tmp++;
-	    } else
-		tmp = buf;
-
-	    if (strcmp(tmp, matches[0]) == 0)
-		is_dir = append_slash_if_dir(buf, lastwastab, newplace);
+    if (num_matches <= 0)
+	beep();
+    else {
+	size_t match, common_len = 0;
+	size_t lastslash = strrchrn(buf, '/', *place);
+	    /* Ignore the first match_strip characters of matches
+	     * entries.  The entries of matches are tilde expanded. */
+	char *mzero;
+
+	while (TRUE) {
+	    for (match = 1; match < num_matches; match++) {
+		if (matches[0][common_len] !=
+			matches[match][common_len])
+		    break;
+	    }
 
-	    if (is_dir != 0)
+	    if (match < num_matches || matches[0][common_len] == '\0')
 		break;
 
-	    copyto = tmp;
-	    for (pos = 0; *tmp == matches[0][pos] &&
-		 pos <= strlen(matches[0]); pos++)
-		tmp++;
+	    common_len++;
+	}
 
-	    /* write out the matched name */
-	    strncpy(copyto, matches[0], strlen(matches[0]) + 1);
-	    *newplace += strlen(matches[0]) - pos;
+	mzero = charalloc(lastslash + common_len + 1);
+	sprintf(mzero, "%.*s%.*s", lastslash, buf, common_len,
+		matches[0]);
 
-	    /* if an exact match is typed in and Tab is pressed,
-	       *newplace will now be negative; in that case, make it
-	       zero, so that the cursor will stay where it is instead of
-	       moving backward */
-	    if (*newplace < 0)
-		*newplace = 0;
+	common_len += lastslash;
 
-	    /* Is it a directory? */
-	    append_slash_if_dir(buf, lastwastab, newplace);
+	assert(common_len >= *place);
 
-	    break;
-	default:
-	    /* Check to see if all matches share a beginning, and, if so,
-	       tack it onto buf and then beep */
-
-	    if (buf[0] != '\0' && strstr(buf, "/") != NULL) {
-		for (tmp = buf + strlen(buf); *tmp != '/' && tmp != buf;
-		     tmp--);
-		tmp++;
-	    } else
-		tmp = buf;
+	if (num_matches == 1 && is_dir(mzero)) {
+	    mzero[common_len] = '/';
+	    common_len++;
+	    assert(common_len > *place);
+	}
 
-	    for (pos = 0; *tmp == matches[0][pos] && *tmp != '\0' &&
-		 pos <= strlen(matches[0]); pos++)
-		tmp++;
+	if (num_matches > 1 && (common_len != *place ||
+		*lastwastab == FALSE))
+	    beep();
 
-	    while (TRUE) {
-		match_matches = 0;
+	/* If there is more match to display on the statusbar, show it.
+	 * We reset lastwastab to FALSE: it requires hitting Tab twice
+	 * in succession with no statusbar changes to see a match
+	 * list. */
+	if (common_len != *place) {
+	    size_t buflen = strlen(buf);
+
+	    *lastwastab = FALSE;
+	    buf = charealloc(buf, common_len + buflen - *place + 1);
+	    charmove(buf + common_len, buf + *place, buflen - *place + 1);
+	    strncpy(buf, mzero, common_len);
+	    *place = common_len;
+	} else if (*lastwastab == FALSE || num_matches < 2)
+	    *lastwastab = TRUE;
+	else {
+	    int longest_name = 0, editline = 0;
+	    size_t columns;
 
-		for (i = 0; i < num_matches; i++) {
-		    if (matches[i][pos] == 0)
-			break;
-		    else if (matches[i][pos] == matches[0][pos])
-			match_matches++;
-		}
-		if (match_matches == num_matches &&
-		    (i == num_matches || matches[i] != 0)) {
-		    /* All the matches have the same character at pos+1,
-		       so paste it into buf... */
-		    buf = charealloc(buf, strlen(buf) + 2);
-		    strncat(buf, matches[0] + pos, 1);
-		    *newplace += 1;
-		    pos++;
-		} else {
-		    beep();
+	    /* Now we show a list of the available choices. */
+	    assert(num_matches > 1);
+
+	    /* Sort the list. */
+	    qsort(matches, num_matches, sizeof(char *), diralphasort);
+
+	    for (match = 0; match < num_matches; match++) {
+		common_len = strnlenpt(matches[match], COLS - 1);
+		if (common_len > COLS - 1) {
+		    longest_name = COLS - 1;
 		    break;
 		}
+		if (common_len > longest_name)
+		    longest_name = common_len;
 	    }
-	}
-    } else {
-	/* Ok -- the last char was a TAB.  Since they
-	 * just hit TAB again, print a list of all the
-	 * available choices... */
-	if (matches != NULL && num_matches > 1) {
-
-	    /* Blank the edit window, and print the matches out there */
-	    blank_edit();
-	    wmove(edit, 0, 0);
 
-	    editline = 0;
+	    assert(longest_name <= COLS - 1);
 
-	    /* Figure out the length of the longest filename */
-	    for (i = 0; i < num_matches; i++)
-		if (strlen(matches[i]) > longestname)
-		    longestname = strlen(matches[i]);
+	    /* Each column will be longest_name + 2 characters wide,
+	     * i.e, two spaces between columns, except that there will
+	     * be only one space after the last column. */
+	    columns = (COLS + 1) / (longest_name + 2);
 
-	    if (longestname > COLS - 1)
-		longestname = COLS - 1;
+	    /* Blank the edit window, and print the matches out
+	     * there. */
+	    blank_edit();
+	    wmove(edit, 0, 0);
 
-	    foo = charalloc(longestname + 5);
+	    /* Disable el cursor. */
+	    curs_set(0);
 
-	    /* Print the list of matches */
-	    for (i = 0, col = 0; i < num_matches; i++) {
+	    for (match = 0; match < num_matches; match++) {
+		char *disp;
 
-		/* make each filename shown be the same length as the
-		   longest filename, with two spaces at the end */
-		snprintf(foo, longestname + 1, "%s", matches[i]);
-		while (strlen(foo) < longestname)
-		    strcat(foo, " ");
+		wmove(edit, editline, (longest_name + 2) *
+			(match % columns));
 
-		strcat(foo, "  ");
+		if (match % columns == 0 && editline == editwinrows - 1
+			&& num_matches - match > columns) {
+		    waddstr(edit, _("(more)"));
+		    break;
+		}
 
-		/* Disable el cursor */
-		curs_set(0);
-		/* now, put the match on the screen */
-		waddnstr(edit, foo, strlen(foo));
-		col += strlen(foo);
+		disp = display_string(matches[match], 0, longest_name,
+			FALSE);
+		waddstr(edit, disp);
+		free(disp);
 
-		/* And if the next match isn't going to fit on the
-		   line, move to the next one */
-		if (col > COLS - longestname && i + 1 < num_matches) {
+		if ((match + 1) % columns == 0)
 		    editline++;
-		    wmove(edit, editline, 0);
-		    if (editline == editwinrows - 1) {
-			waddstr(edit, _("(more)"));
-			break;
-		    }
-		    col = 0;
-		}
 	    }
-	    free(foo);
 	    wrefresh(edit);
 	    *list = TRUE;
-	} else
-	    beep();
+	}
+
+	free(mzero);
     }
 
+    free_charptrarray(matches, num_matches);
+
     /* Only refresh the edit window if we don't have a list of filename
-       matches on it */
+     * matches on it. */
     if (*list == FALSE)
 	edit_refresh();
+
+    /* Enable el cursor. */
     curs_set(1);
+
     return buf;
 }
 #endif /* !DISABLE_TABCOMP */
 
-/* Only print the last part of a path; isn't there a shell
- * command for this? */
+/* Only print the last part of a path.  Isn't there a shell command for
+ * this? */
 const char *tail(const char *foo)
 {
-    const char *tmp = foo + strlen(foo);
+    const char *tmp = strrchr(foo, '/');
 
-    while (*tmp != '/' && tmp != foo)
-	tmp--;
-
-    if (*tmp == '/')
+    if (tmp == NULL)
+	tmp = foo;
+    else if (*tmp == '/')
 	tmp++;
 
     return tmp;
 }
 
 #ifndef DISABLE_BROWSER
-/* Our sort routine for file listings -- sort directories before
- * files, and then alphabetically. */ 
-int diralphasort(const void *va, const void *vb)
-{
-    struct stat fileinfo;
-    const char *a = *(char *const *)va, *b = *(char *const *)vb;
-    int aisdir = stat(a, &fileinfo) != -1 && S_ISDIR(fileinfo.st_mode);
-    int bisdir = stat(b, &fileinfo) != -1 && S_ISDIR(fileinfo.st_mode);
-
-    if (aisdir != 0 && bisdir == 0)
-	return -1;
-    if (aisdir == 0 && bisdir != 0)
-	return 1;
-
-    return strcasecmp(a, b);
-}
-
-/* Free our malloc()ed memory */
+/* Free our malloc()ed memory. */
 void free_charptrarray(char **array, size_t len)
 {
     for (; len > 0; len--)
@@ -2419,25 +2343,10 @@ void striponedir(char *foo)
     char *tmp;
 
     assert(foo != NULL);
-    /* Don't strip the root dir */
-    if (*foo == '\0' || strcmp(foo, "/") == 0)
-	return;
 
-    tmp = foo + strlen(foo) - 1;
-    assert(tmp >= foo);
-    if (*tmp == '/')
-	*tmp = '\0';
-
-    while (*tmp != '/' && tmp != foo)
-	tmp--;
-
-    if (tmp != foo)
-	*tmp = '\0';
-    else { /* SPK may need to make a 'default' path here */
-        if (*tmp != '/')
-	    *tmp = '.';
-	*(tmp + 1) = '\0';
-    }
+    tmp = strrchr(foo, '/');
+    if (tmp != NULL)
+ 	*tmp = '\0';
 }
 
 int readable_dir(const char *path)
@@ -2528,7 +2437,7 @@ char *do_browser(const char *inpath)
     filelist = browser_init(path, &longest, &numents);
     foo = charalloc(longest + 8);
 
-    /* Sort the list by directory first, then alphabetically */
+    /* Sort the list. */
     qsort(filelist, numents, sizeof(char *), diralphasort);
 
     titlebar(path);
diff --git a/src/global.c b/src/global.c
index b714c74470fc6afec54e1cc4b32036567e9a9afc..7d5ce48f23a1a91cd8081905b28ac5629de8fe37 100644
--- a/src/global.c
+++ b/src/global.c
@@ -183,9 +183,7 @@ bool curses_ended = FALSE;	/* Indicates to statusbar() to simply
 				 * write to stderr, since endwin() has
 				 * ended curses mode. */
 
-#ifdef ENABLE_NANORC
 char *homedir = NULL;		/* $HOME or from /etc/passwd. */
-#endif
 
 size_t length_of_list(const shortcut *s)
 {
diff --git a/src/proto.h b/src/proto.h
index 145e381ba73df7bb17a9c105a9e6e9dea67fb5ec..26940c1b17d52375b626c8752f0a9ce18db0539c 100644
--- a/src/proto.h
+++ b/src/proto.h
@@ -144,25 +144,23 @@ extern historyheadtype replace_history;
 
 extern bool curses_ended;
 
-#ifdef ENABLE_NANORC
 extern char *homedir;
-#endif
 
 /* Functions we want available. */
 
 /* Public functions in chars.c. */
-bool is_byte(unsigned int c);
-bool is_alnum_char(unsigned int c);
+bool is_byte(int c);
+bool is_alnum_char(int c);
 bool is_alnum_mbchar(const char *c);
 #ifdef NANO_WIDE
 bool is_alnum_wchar(wchar_t wc);
 #endif
-bool is_blank_char(unsigned int c);
+bool is_blank_char(int c);
 bool is_blank_mbchar(const char *c);
 #ifdef NANO_WIDE
 bool is_blank_wchar(wchar_t wc);
 #endif
-bool is_cntrl_char(unsigned int c);
+bool is_cntrl_char(int c);
 bool is_cntrl_mbchar(const char *c);
 #ifdef NANO_WIDE
 bool is_cntrl_wchar(wchar_t wc);
@@ -174,7 +172,7 @@ wchar_t control_wrep(wchar_t c);
 #endif
 int mbwidth(const char *c);
 int mb_cur_max(void);
-char *make_mbchar(unsigned int chr, char *chr_mb, int *chr_mb_len);
+char *make_mbchar(int chr, char *chr_mb, int *chr_mb_len);
 int parse_mbchar(const char *buf, char *chr
 #ifdef NANO_WIDE
 	, bool *bad_chr
@@ -207,6 +205,7 @@ size_t mbstrlen(const char *s);
 size_t nstrnlen(const char *s, size_t maxlen);
 #endif
 size_t mbstrnlen(const char *s, size_t maxlen);
+size_t strrchrn(const char *s, int c, size_t n);
 
 /* Public functions in color.c. */
 #ifdef ENABLE_COLOR
@@ -287,16 +286,18 @@ int write_marked(const char *name, bool tmp, int append);
 int do_writeout(bool exiting);
 void do_writeout_void(void);
 char *real_dir_from_tilde(const char *buf);
+#if !defined(DISABLE_TABCOMP) || !defined(DISABLE_BROWSER)
+int diralphasort(const void *va, const void *vb);
+#endif
 #ifndef DISABLE_TABCOMP
-int append_slash_if_dir(char *buf, bool *lastwastab, int *place);
-char **username_tab_completion(char *buf, int *num_matches);
-char **cwd_tab_completion(char *buf, int *num_matches);
-char *input_tab(char *buf, int place, bool *lastwastab, int *newplace,
-	bool *list);
+char **username_tab_completion(const char *buf, size_t *num_matches,
+	size_t buflen);
+char **cwd_tab_completion(const char *buf, size_t *num_matches, size_t
+	buflen);
+char *input_tab(char *buf, size_t *place, bool *lastwastab, bool *list);
 #endif
 const char *tail(const char *foo);
 #ifndef DISABLE_BROWSER
-int diralphasort(const void *va, const void *vb);
 void free_charptrarray(char **array, size_t len);
 void striponedir(char *foo);
 int readable_dir(const char *path);
@@ -531,6 +532,7 @@ int regexec_safe(const regex_t *preg, const char *string, size_t nmatch,
 int regexp_bol_or_eol(const regex_t *preg, const char *string);
 #endif
 int num_of_digits(int n);
+void get_homedir(void);
 bool parse_num(const char *str, ssize_t *val);
 void align(char **strp);
 void null_at(char **data, size_t index);
@@ -560,9 +562,6 @@ void mark_order(const filestruct **top, size_t *top_x, const filestruct
 #endif
 void get_totals(const filestruct *begin, const filestruct *end, int
 	*lines, size_t *size);
-#ifndef DISABLE_TABCOMP
-int check_wildcard_match(const char *text, const char *pattern);
-#endif
 
 /* Public functions in winio.c. */
 #ifndef NANO_SMALL
diff --git a/src/rcfile.c b/src/rcfile.c
index 649e72e45254b449b9fb8f8bb38e9587ecb1209b..16999c2075fc025e653fd1b76afe9181200f0405 100644
--- a/src/rcfile.c
+++ b/src/rcfile.c
@@ -30,7 +30,6 @@
 #include <errno.h>
 #include <unistd.h>
 #include <fcntl.h>
-#include <pwd.h>
 #include <ctype.h>
 #include <assert.h>
 #include "proto.h"
@@ -666,18 +665,7 @@ void do_rcfile(void)
 
     lineno = 0;
 
-    {
-	const char *homenv = getenv("HOME");
-
-	/* Rely on $HOME, fall back on getpwuid() */
-	if (homenv == NULL) {
-	    const struct passwd *userage = getpwuid(geteuid());
-
-	    if (userage != NULL)
-		homenv = userage->pw_dir;
-	}
-	homedir = mallocstrcpy(NULL, homenv);
-    }
+    get_homedir();
 
     if (homedir == NULL) {
 	rcfile_error(N_("I can't find my home directory!  Wah!"));
diff --git a/src/utils.c b/src/utils.c
index b0a3301935daec5146011f16f8502706856859df..7739cec126a4c5685e3c9fe356653c49a60169e0 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -27,6 +27,7 @@
 #include <string.h>
 #include <stdio.h>
 #include <unistd.h>
+#include <pwd.h>
 #include <ctype.h>
 #include <errno.h>
 #include <assert.h>
@@ -67,6 +68,23 @@ int num_of_digits(int n)
     return i;
 }
 
+/* Return the user's home directory.  We use $HOME, and if that fails,
+ * we fall back on getpwuid(). */
+void get_homedir(void)
+{
+    if (homedir == NULL) {
+	const char *homenv = getenv("HOME");
+
+	if (homenv == NULL) {
+	    const struct passwd *userage = getpwuid(geteuid());
+
+	    if (userage != NULL)
+		homenv = userage->pw_dir;
+	}
+	homedir = mallocstrcpy(NULL, homenv);
+    }
+}
+
 /* Read a ssize_t from str, and store it in *val (if val is not NULL).
  * On error, we return FALSE and don't change *val.  Otherwise, we
  * return TRUE. */
@@ -414,105 +432,3 @@ void get_totals(const filestruct *begin, const filestruct *end, int
 	}
     }
 }
-
-#ifndef DISABLE_TABCOMP
-/*
- * Routine to see if a text string is matched by a wildcard pattern.
- * Returns TRUE if the text is matched, or FALSE if it is not matched
- * or if the pattern is invalid.
- *  *		matches zero or more characters
- *  ?		matches a single character
- *  [abc]	matches 'a', 'b' or 'c'
- *  \c		quotes character c
- * Adapted from code written by Ingo Wilken, and
- * then taken from sash, Copyright (c) 1999 by David I. Bell
- * Permission is granted to use, distribute, or modify this source,
- * provided that this copyright notice remains intact.
- * Permission to distribute this code under the GPL has been granted.
- */
-int check_wildcard_match(const char *text, const char *pattern)
-{
-    const char *retrypat;
-    const char *retrytext;
-    int ch;
-    int found;
-    int len;
-
-    retrypat = NULL;
-    retrytext = NULL;
-
-    while (*text != '\0' || *pattern != '\0') {
-	ch = *pattern++;
-
-	switch (ch) {
-	case '*':
-	    retrypat = pattern;
-	    retrytext = text;
-	    break;
-
-	case '[':
-	    found = FALSE;
-
-	    while ((ch = *pattern++) != ']') {
-		if (ch == '\\')
-		    ch = *pattern++;
-
-		if (ch == '\0')
-		    return FALSE;
-
-		if (*text == ch)
-		    found = TRUE;
-	    }
-	    len = strlen(text);
-	    if (!found && len != 0) {
-		return FALSE;
-	    }
-	    if (found) {
-		if (strlen(pattern) == 0 && len == 1) {
-		    return TRUE;
-		}
-		if (len != 0) {
-		    text++;
-		    continue;
-		}
-	    }
-
-	    /* fall into next case */
-
-	case '?':
-	    if (*text++ == '\0')
-		return FALSE;
-
-	    break;
-
-	case '\\':
-	    ch = *pattern++;
-
-	    if (ch == '\0')
-		return FALSE;
-
-	    /* fall into next case */
-
-	default:
-	    if (*text == ch) {
-		if (*text != '\0')
-		    text++;
-		break;
-	    }
-
-	    if (*text != '\0') {
-		pattern = retrypat;
-		text = ++retrytext;
-		break;
-	    }
-
-	    return FALSE;
-	}
-
-	if (pattern == NULL)
-	    return FALSE;
-    }
-
-    return TRUE;
-}
-#endif
diff --git a/src/winio.c b/src/winio.c
index 225c02997b6a99fad3bda92f610705beeecef43b..fe54fa1e4ba34214aa830f7e23d2999383b1b4a7 100644
--- a/src/winio.c
+++ b/src/winio.c
@@ -2530,17 +2530,11 @@ int nanogetstr(bool allow_tabs, const char *buf, const char *def,
 #endif
 #ifndef DISABLE_TABCOMP
 	    if (allow_tabs) {
-		int shift = 0;
-
-		answer = input_tab(answer, statusbar_x, &tabbed, &shift,
-			list);
-		statusbar_xend = strlen(answer);
-		statusbar_x += shift;
-		if (statusbar_x > statusbar_xend)
-		    statusbar_x = statusbar_xend;
+		answer = input_tab(answer, &statusbar_x, &tabbed, list);
+		statusbar_xend = statusbar_x;
 	    }
-#endif
 	    break;
+#endif
 	case NANO_PREVLINE_KEY:
 #ifndef NANO_SMALL
 	    if (history_list != NULL) {