lineedit: return prefix len from tab completion helpers

This kills horrific logic which deletes and re-adds prefix (!)

function                                             old     new   delta
complete_cmd_dir_file                                705     731     +26
complete_username                                    121     124      +3
input_tab                                           1041    1016     -25
build_match_prefix                                   838     804     -34
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/2 up/down: 29/-59)            Total: -30 bytes

Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
diff --git a/libbb/lineedit.c b/libbb/lineedit.c
index dd9d85b..e40917f 100644
--- a/libbb/lineedit.c
+++ b/libbb/lineedit.c
@@ -638,14 +638,16 @@
 	return tilde_name;
 }
 
-/* ~use<tab> - find all users with this prefix */
-static NOINLINE void complete_username(const char *ud)
+/* ~use<tab> - find all users with this prefix.
+ * Return the length of the prefix used for matching.
+ */
+static NOINLINE unsigned complete_username(const char *ud)
 {
 	/* Using _r function to avoid pulling in static buffers */
 	char line_buff[256];
 	struct passwd pwd;
 	struct passwd *result;
-	int userlen;
+	unsigned userlen;
 
 	ud++; /* skip ~ */
 	userlen = strlen(ud);
@@ -658,6 +660,8 @@
 		}
 	}
 	endpwent();
+
+	return 1 + userlen;
 }
 #endif  /* FEATURE_USERNAME_COMPLETION */
 
@@ -710,14 +714,17 @@
 	return npth;
 }
 
-static NOINLINE void complete_cmd_dir_file(char *command, int type)
+/* Complete command, directory or file name.
+ * Return the length of the prefix used for matching.
+ */
+static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
 {
 	char *path1[1];
 	char **paths = path1;
 	int npaths;
 	int i;
 	unsigned pf_len;
-	char *pfind;
+	const char *pfind;
 	char *dirbuf = NULL;
 
 	npaths = 1;
@@ -797,8 +804,12 @@
 	if (paths != path1) {
 		free(paths[0]); /* allocated memory is only in first member */
 		free(paths);
+	} else if (dirbuf) {
+		pf_len += strlen(dirbuf);
+		free(dirbuf);
 	}
-	free(dirbuf);
+
+	return pf_len;
 }
 
 /* build_match_prefix:
@@ -829,7 +840,7 @@
 		bb_putchar('\n');
 	}
 }
-static NOINLINE int build_match_prefix(char *matchBuf, int *len_with_quotes)
+static NOINLINE int build_match_prefix(char *matchBuf)
 {
 	int i, j;
 	int command_mode;
@@ -992,9 +1003,6 @@
 			pos = pos_buf[i] + 1;
 		}
 		matchBuf[j] = '\0';
-		/* old length matchBuf with quotes symbols */
-		*len_with_quotes = pos ? pos - pos_buf[0] : 0;
-		if (dbg_bmp) printf("len_with_quotes:%d\n", *len_with_quotes);
 	}
 
 	return command_mode;
@@ -1067,12 +1075,13 @@
 		return;
 
 	if (!*lastWasTab) {
-		char *tmp, *tmp1;
+		char *tmp;
 		size_t len_found;
 /*		char matchBuf[MAX_LINELEN]; */
 #define matchBuf (S.input_tab__matchBuf)
+		/* Length of string used for matching */
+		unsigned match_pfx_len = match_pfx_len;
 		int find_type;
-		int recalc_pos;
 #if ENABLE_UNICODE_SUPPORT
 		/* cursor pos in command converted to multibyte form */
 		int cursor_mb;
@@ -1095,7 +1104,7 @@
 #endif
 		tmp = matchBuf;
 
-		find_type = build_match_prefix(matchBuf, &recalc_pos);
+		find_type = build_match_prefix(matchBuf);
 
 		/* Free up any memory already allocated */
 		free_tab_completion_data();
@@ -1105,11 +1114,11 @@
 		 * then try completing this word as a username. */
 		if (state->flags & USERNAME_COMPLETION)
 			if (matchBuf[0] == '~' && strchr(matchBuf, '/') == NULL)
-				complete_username(matchBuf);
+				match_pfx_len = complete_username(matchBuf);
 #endif
 		/* Try to match a command in $PATH, or a directory, or a file */
 		if (!matches)
-			complete_cmd_dir_file(matchBuf, find_type);
+			match_pfx_len = complete_cmd_dir_file(matchBuf, find_type);
 		/* Remove duplicates */
 		if (matches) {
 			unsigned i;
@@ -1130,23 +1139,26 @@
 		}
 		/* Did we find exactly one match? */
 		if (num_matches != 1) { /* no */
+			char *tmp1;
 			beep();
 			if (!matches)
 				return; /* no matches at all */
 			/* Find common prefix */
 			tmp1 = xstrdup(matches[0]);
 			for (tmp = tmp1; *tmp; tmp++) {
-				for (len_found = 1; len_found < num_matches; len_found++) {
-					if (matches[len_found][tmp - tmp1] != *tmp) {
-						*tmp = '\0';
-						break;
+				unsigned n;
+				for (n = 1; n < num_matches; n++) {
+					if (matches[n][tmp - tmp1] != *tmp) {
+						goto stop;
 					}
 				}
 			}
-			if (*tmp1 == '\0') { /* have unique prefix? */
+ stop:
+			if (tmp1 == tmp) { /* have unique prefix? */
 				free(tmp1); /* no */
 				return;
 			}
+			*tmp = '\0';
 			tmp = add_quote_for_spec_chars(tmp1);
 			free(tmp1);
 			len_found = strlen(tmp);
@@ -1163,41 +1175,43 @@
 		}
 
 #if !ENABLE_UNICODE_SUPPORT
-		/* have space to place the match? */
+		/* Have space to place the match? */
 		/* The result consists of three parts with these lengths: */
-		/* (cursor - recalc_pos) + len_found + (command_len - cursor) */
+		/* cursor + (len_found - match_pfx_len) + (command_len - cursor) */
 		/* it simplifies into: */
-		if ((int)(len_found + command_len - recalc_pos) < S.maxsize) {
+		if ((int)(len_found - match_pfx_len + command_len) < S.maxsize) {
+			int pos;
 			/* save tail */
-			strcpy(matchBuf, command_ps + cursor);
+			strcpy(matchBuf, &command_ps[cursor]);
 			/* add match and tail */
-			sprintf(&command_ps[cursor - recalc_pos], "%s%s", tmp, matchBuf);
+			sprintf(&command_ps[cursor], "%s%s", tmp + match_pfx_len, matchBuf);
 			command_len = strlen(command_ps);
 			/* new pos */
-			recalc_pos = cursor - recalc_pos + len_found;
+			pos = cursor + len_found - match_pfx_len;
 			/* write out the matched command */
-			redraw(cmdedit_y, command_len - recalc_pos);
+			redraw(cmdedit_y, command_len - pos);
 		}
 #else
 		{
 			char command[MAX_LINELEN];
 			int len = save_string(command, sizeof(command));
-			/* have space to place the match? */
-			/* (cursor_mb - recalc_pos) + len_found + (len - cursor_mb) */
-			if ((int)(len_found + len - recalc_pos) < MAX_LINELEN) {
+			/* Have space to place the match? */
+			/* cursor_mb + (len_found - match_pfx_len) + (len - cursor_mb) */
+			if ((int)(len_found - match_pfx_len + len) < MAX_LINELEN) {
+				int pos;
 				/* save tail */
-				strcpy(matchBuf, command + cursor_mb);
+				strcpy(matchBuf, &command[cursor_mb]);
 				/* where do we want to have cursor after all? */
-				strcpy(&command[cursor_mb - recalc_pos], tmp);
+				strcpy(&command[cursor_mb], tmp + match_pfx_len);
 				len = load_string(command, S.maxsize);
 				/* add match and tail */
-				sprintf(&command[cursor_mb - recalc_pos], "%s%s", tmp, matchBuf);
+				sprintf(&command[cursor_mb], "%s%s", tmp + match_pfx_len, matchBuf);
 				command_len = load_string(command, S.maxsize);
 				/* write out the matched command */
 				/* paranoia: load_string can return 0 on conv error,
-				 * prevent passing len = (0 - 12) to redraw */
-				len = command_len - len;
-				redraw(cmdedit_y, len >= 0 ? len : 0);
+				 * prevent passing pos = (0 - 12) to redraw */
+				pos = command_len - len;
+				redraw(cmdedit_y, pos >= 0 ? pos : 0);
 			}
 		}
 #endif