patch 9.1.0882: too many strlen() calls in insexpand.c

Problem:  too many strlen() calls in insexpand.c
Solution: Refactor insexpand.c and reduce number of calls to STRLEN(),
          fix a warning get_next_filename_completion(), add new function
          ins_compl_leader_len() (John Marriott)

closes: #16095

Signed-off-by: John Marriott <basilisk@internode.on.net>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/insexpand.c b/src/insexpand.c
index 9fe9fc5..6bc5075 100644
--- a/src/insexpand.c
+++ b/src/insexpand.c
@@ -95,7 +95,7 @@
 {
     compl_T	*cp_next;
     compl_T	*cp_prev;
-    char_u	*cp_str;	// matched text
+    string_T	cp_str;			// matched text
     char_u	*(cp_text[CPT_COUNT]);	// text for the menu
 #ifdef FEAT_EVAL
     typval_T	cp_user_data;
@@ -136,7 +136,7 @@
 
 // When "compl_leader" is not NULL only matches that start with this string
 // are used.
-static char_u	  *compl_leader = NULL;
+static string_T	  compl_leader = {NULL, 0};
 
 static int	  compl_get_longest = FALSE;	// put longest common string
 						// in compl_leader
@@ -162,8 +162,7 @@
 static int	  ctrl_x_mode = CTRL_X_NORMAL;
 
 static int	  compl_matches = 0;	    // number of completion matches
-static char_u	  *compl_pattern = NULL;
-static size_t	  compl_patternlen = 0;
+static string_T	  compl_pattern = {NULL, 0};
 static int	  compl_direction = FORWARD;
 static int	  compl_shows_dir = FORWARD;
 static int	  compl_pending = 0;	    // > 1 for postponed CTRL-N
@@ -173,7 +172,7 @@
 static int	  compl_length = 0;
 static colnr_T	  compl_col = 0;	    // column where the text starts
 					    // that is being completed
-static char_u	  *compl_orig_text = NULL;  // text as it was before
+static string_T	  compl_orig_text = {NULL, 0};  // text as it was before
 					    // completion started
 static int	  compl_cont_mode = 0;
 static expand_T	  compl_xp;
@@ -208,7 +207,7 @@
 static void ins_compl_new_leader(void);
 static int  get_compl_len(void);
 static void ins_compl_restart(void);
-static void ins_compl_set_original_text(char_u *str);
+static void ins_compl_set_original_text(char_u *str, size_t len);
 static void ins_compl_fixRedoBufForLeader(char_u *ptr_arg);
 # if defined(FEAT_COMPL_FUNC) || defined(FEAT_EVAL)
 static void ins_compl_add_list(list_T *list);
@@ -560,7 +559,7 @@
 	    wca[i] = *(p++);
 
     // Rule 1: Were any chars converted to lower?
-    p = compl_orig_text;
+    p = compl_orig_text.string;
     for (i = 0; i < min_len; ++i)
     {
 	if (has_mbyte)
@@ -584,7 +583,7 @@
     // upper case.
     if (!has_lower)
     {
-	p = compl_orig_text;
+	p = compl_orig_text.string;
 	for (i = 0; i < min_len; ++i)
 	{
 	    if (has_mbyte)
@@ -603,7 +602,7 @@
     }
 
     // Copy the original case of the part we typed.
-    p = compl_orig_text;
+    p = compl_orig_text.string;
     for (i = 0; i < min_len; ++i)
     {
 	if (has_mbyte)
@@ -651,7 +650,7 @@
 	    }
 	    *p = NUL;
 	    STRCPY(gap.ga_data, IObuff);
-	    gap.ga_len = (int)STRLEN(IObuff);
+	    gap.ga_len = (int)(p - IObuff);
 	}
 	else if (has_mbyte)
 	    p += (*mb_char2bytes)(wca[i++], p);
@@ -715,7 +714,7 @@
 	// Find actual length of original text.
 	if (has_mbyte)
 	{
-	    p = compl_orig_text;
+	    p = compl_orig_text.string;
 	    compl_char_len = 0;
 	    while (*p != NUL)
 	    {
@@ -795,9 +794,9 @@
 	do
 	{
 	    if (!match_at_original_text(match)
-		    && STRNCMP(match->cp_str, str, len) == 0
-		    && ((int)STRLEN(match->cp_str) <= len
-						 || match->cp_str[len] == NUL))
+		    && STRNCMP(match->cp_str.string, str, len) == 0
+		    && ((int)match->cp_str.length <= len
+						 || match->cp_str.string[len] == NUL))
 		return NOTDONE;
 	    match = match->cp_next;
 	} while (match != NULL && !is_first_match(match));
@@ -814,12 +813,14 @@
     match->cp_number = -1;
     if (flags & CP_ORIGINAL_TEXT)
 	match->cp_number = 0;
-    if ((match->cp_str = vim_strnsave(str, len)) == NULL)
+    if ((match->cp_str.string = vim_strnsave(str, len)) == NULL)
     {
 	vim_free(match);
 	return FAIL;
     }
 
+    match->cp_str.length = len;
+
     // match-fname is:
     // - compl_curr_match->cp_fname if it is a string equal to fname.
     // - a copy of fname, CP_FREE_FNAME is set to free later THE allocated mem.
@@ -892,8 +893,8 @@
     if (match->cp_flags & CP_EQUAL)
 	return TRUE;
     if (match->cp_flags & CP_ICASE)
-	return STRNICMP(match->cp_str, str, (size_t)len) == 0;
-    return STRNCMP(match->cp_str, str, (size_t)len) == 0;
+	return STRNICMP(match->cp_str.string, str, (size_t)len) == 0;
+    return STRNCMP(match->cp_str.string, str, (size_t)len) == 0;
 }
 
 /*
@@ -906,16 +907,17 @@
     int		c1, c2;
     int		had_match;
 
-    if (compl_leader == NULL)
+    if (compl_leader.string == NULL)
     {
 	// First match, use it as a whole.
-	compl_leader = vim_strsave(match->cp_str);
-	if (compl_leader == NULL)
+	compl_leader.string = vim_strnsave(match->cp_str.string, match->cp_str.length);
+	if (compl_leader.string == NULL)
 	    return;
 
+	compl_leader.length = match->cp_str.length;
 	had_match = (curwin->w_cursor.col > compl_col);
 	ins_compl_delete();
-	ins_bytes(compl_leader + get_compl_len());
+	ins_bytes(compl_leader.string + get_compl_len());
 	ins_redraw(FALSE);
 
 	// When the match isn't there (to avoid matching itself) remove it
@@ -928,8 +930,8 @@
     }
 
     // Reduce the text if this match differs from compl_leader.
-    p = compl_leader;
-    s = match->cp_str;
+    p = compl_leader.string;
+    s = match->cp_str.string;
     while (*p != NUL)
     {
 	if (has_mbyte)
@@ -961,9 +963,11 @@
     {
 	// Leader was shortened, need to change the inserted text.
 	*p = NUL;
+	compl_leader.length = (size_t)(p - compl_leader.string);
+
 	had_match = (curwin->w_cursor.col > compl_col);
 	ins_compl_delete();
-	ins_bytes(compl_leader + get_compl_len());
+	ins_bytes(compl_leader.string + get_compl_len());
 	ins_redraw(FALSE);
 
 	// When the match isn't there (to avoid matching itself) remove it
@@ -1040,7 +1044,7 @@
     int
 ins_compl_long_shown_match(void)
 {
-    return (int)STRLEN(compl_shown_match->cp_str)
+    return (int)compl_shown_match->cp_str.length
 					    > curwin->w_cursor.col - compl_col;
 }
 
@@ -1149,7 +1153,7 @@
     if (dict == NULL)
 	return NULL;
 
-    dict_add_string(dict, "word", match->cp_str);
+    dict_add_string(dict, "word", match->cp_str.string);
     dict_add_string(dict, "abbr", match->cp_text[CPT_ABBR]);
     dict_add_string(dict, "menu", match->cp_text[CPT_MENU]);
     dict_add_string(dict, "kind", match->cp_text[CPT_KIND]);
@@ -1225,7 +1229,6 @@
     int		shown_match_ok = FALSE;
     int		i;
     int		cur = -1;
-    int		lead_len = 0;
     int		max_fuzzy_score = 0;
     unsigned int cur_cot_flags = get_cot_flags();
     int		compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0;
@@ -1234,19 +1237,17 @@
     // Need to build the popup menu list.
     compl_match_arraysize = 0;
     compl = compl_first_match;
-    if (compl_leader != NULL)
-	lead_len = (int)STRLEN(compl_leader);
 
     do
     {
 	// When 'completeopt' contains "fuzzy" and leader is not NULL or empty,
 	// set the cp_score for later comparisons.
-	if (compl_fuzzy_match && compl_leader != NULL && lead_len > 0)
-	    compl->cp_score = fuzzy_match_str(compl->cp_str, compl_leader);
+	if (compl_fuzzy_match && compl_leader.string != NULL && compl_leader.length > 0)
+	    compl->cp_score = fuzzy_match_str(compl->cp_str.string, compl_leader.string);
 
 	if (!match_at_original_text(compl)
-		&& (compl_leader == NULL
-		    || ins_compl_equal(compl, compl_leader, lead_len)
+		&& (compl_leader.string == NULL
+		    || ins_compl_equal(compl, compl_leader.string, (int)compl_leader.length)
 		    || (compl_fuzzy_match && compl->cp_score > 0)))
 	    ++compl_match_arraysize;
 	compl = compl->cp_next;
@@ -1264,8 +1265,8 @@
     if (match_at_original_text(compl_shown_match))
 	shown_match_ok = TRUE;
 
-    if (compl_leader != NULL
-	    && STRCMP(compl_leader, compl_orig_text) == 0
+    if (compl_leader.string != NULL
+	    && STRCMP(compl_leader.string, compl_orig_text.string) == 0
 	    && shown_match_ok == FALSE)
 	compl_shown_match = compl_no_select ? compl_first_match
 					    : compl_first_match->cp_next;
@@ -1275,8 +1276,8 @@
     do
     {
 	if (!match_at_original_text(compl)
-		&& (compl_leader == NULL
-		    || ins_compl_equal(compl, compl_leader, lead_len)
+		&& (compl_leader.string == NULL
+		    || ins_compl_equal(compl, compl_leader.string, (int)compl_leader.length)
 		    || (compl_fuzzy_match && compl->cp_score > 0)))
 	{
 	    if (!shown_match_ok && !compl_fuzzy_match)
@@ -1321,7 +1322,7 @@
 		// reset the current index.
 		if (!compl_no_select
 			&& (max_fuzzy_score > 0
-				|| (compl_leader == NULL || lead_len == 0)))
+				|| (compl_leader.string == NULL || compl_leader.length == 0)))
 		{
 		    if (match_at_original_text(compl_shown_match))
 		      compl_shown_match = shown_compl;
@@ -1331,7 +1332,7 @@
 	    if (compl->cp_text[CPT_ABBR] != NULL)
 		compl_match_array[i].pum_text = compl->cp_text[CPT_ABBR];
 	    else
-		compl_match_array[i].pum_text = compl->cp_str;
+		compl_match_array[i].pum_text = compl->cp_str.string;
 	    compl_match_array[i].pum_kind = compl->cp_text[CPT_KIND];
 	    compl_match_array[i].pum_info = compl->cp_text[CPT_INFO];
 	    compl_match_array[i].pum_score = compl->cp_score;
@@ -1364,7 +1365,7 @@
 	compl = compl->cp_next;
     } while (compl != NULL && !is_first_match(compl));
 
-    if (compl_fuzzy_match && compl_leader != NULL && lead_len > 0)
+    if (compl_fuzzy_match && compl_leader.string != NULL && compl_leader.length > 0)
     {
 	for (i = 0; i < compl_match_arraysize; i++)
 	    compl_match_array[i].pum_idx = i;
@@ -1404,7 +1405,7 @@
     {
 	// popup menu already exists, only need to find the current item.
 	for (i = 0; i < compl_match_arraysize; ++i)
-	    if (compl_match_array[i].pum_text == compl_shown_match->cp_str
+	    if (compl_match_array[i].pum_text == compl_shown_match->cp_str.string
 		    || compl_match_array[i].pum_text
 				      == compl_shown_match->cp_text[CPT_ABBR])
 	    {
@@ -1453,7 +1454,16 @@
     char_u *
 ins_compl_leader(void)
 {
-    return compl_leader != NULL ? compl_leader : compl_orig_text;
+    return compl_leader.string != NULL ? compl_leader.string : compl_orig_text.string;
+}
+
+/*
+ * Get current completion leader length
+ */
+    size_t
+ins_compl_leader_len(void)
+{
+    return compl_leader.string != NULL ? compl_leader.length : compl_orig_text.length;
 }
 
 /*
@@ -1778,9 +1788,8 @@
     compl_T *match;
     int	    i;
 
-    VIM_CLEAR(compl_pattern);
-    compl_patternlen = 0;
-    VIM_CLEAR(compl_leader);
+    VIM_CLEAR_STRING(compl_pattern);
+    VIM_CLEAR_STRING(compl_leader);
 
     if (compl_first_match == NULL)
 	return;
@@ -1793,7 +1802,7 @@
     {
 	match = compl_curr_match;
 	compl_curr_match = compl_curr_match->cp_next;
-	vim_free(match->cp_str);
+	VIM_CLEAR_STRING(match->cp_str);
 	// several entries may use the same fname, free it just once.
 	if (match->cp_flags & CP_FREE_FNAME)
 	    vim_free(match->cp_fname);
@@ -1818,11 +1827,10 @@
     compl_cont_status = 0;
     compl_started = FALSE;
     compl_matches = 0;
-    VIM_CLEAR(compl_pattern);
-    compl_patternlen = 0;
-    VIM_CLEAR(compl_leader);
+    VIM_CLEAR_STRING(compl_pattern);
+    VIM_CLEAR_STRING(compl_leader);
     edit_submode_extra = NULL;
-    VIM_CLEAR(compl_orig_text);
+    VIM_CLEAR_STRING(compl_orig_text);
     compl_enter_selects = FALSE;
 #ifdef FEAT_EVAL
     // clear v:completed_item
@@ -1927,10 +1935,14 @@
 						  || ins_compl_need_restart())
 	ins_compl_restart();
 
-    vim_free(compl_leader);
-    compl_leader = vim_strnsave(line + compl_col, (p - line) - compl_col);
-    if (compl_leader == NULL)
+    VIM_CLEAR_STRING(compl_leader);
+    compl_leader.length = (size_t)((p - line) - compl_col);
+    compl_leader.string = vim_strnsave(line + compl_col, compl_leader.length);
+    if (compl_leader.string == NULL)
+    {
+	compl_leader.length = 0;
 	return K_BS;
+    }
 
     ins_compl_new_leader();
     if (compl_shown_match != NULL)
@@ -1963,11 +1975,11 @@
 {
     ins_compl_del_pum();
     ins_compl_delete();
-    ins_bytes(compl_leader + get_compl_len());
+    ins_bytes(compl_leader.string + get_compl_len());
     compl_used_match = FALSE;
 
     if (compl_started)
-	ins_compl_set_original_text(compl_leader);
+	ins_compl_set_original_text(compl_leader.string, compl_leader.length);
     else
     {
 #ifdef FEAT_SPELL
@@ -2052,11 +2064,17 @@
     // break redo.
     if (!compl_opt_refresh_always)
     {
-	vim_free(compl_leader);
-	compl_leader = vim_strnsave(ml_get_curline() + compl_col,
-					     curwin->w_cursor.col - compl_col);
-	if (compl_leader != NULL)
-	    ins_compl_new_leader();
+	VIM_CLEAR_STRING(compl_leader);
+	compl_leader.length = (size_t)(curwin->w_cursor.col - compl_col);
+	compl_leader.string = vim_strnsave(ml_get_curline() + compl_col,
+					     compl_leader.length);
+	if (compl_leader.string == NULL)
+	{
+	    compl_leader.length = 0;
+	    return;
+	}
+
+	ins_compl_new_leader();
     }
 }
 
@@ -2078,31 +2096,31 @@
  * Set the first match, the original text.
  */
     static void
-ins_compl_set_original_text(char_u *str)
+ins_compl_set_original_text(char_u *str, size_t len)
 {
-    char_u	*p;
-
     // Replace the original text entry.
     // The CP_ORIGINAL_TEXT flag is either at the first item or might possibly
     // be at the last item for backward completion
     if (match_at_original_text(compl_first_match))	// safety check
     {
-	p = vim_strsave(str);
+	char_u	*p = vim_strnsave(str, len);
 	if (p != NULL)
 	{
-	    vim_free(compl_first_match->cp_str);
-	    compl_first_match->cp_str = p;
+	    VIM_CLEAR_STRING(compl_first_match->cp_str);
+	    compl_first_match->cp_str.string = p;
+	    compl_first_match->cp_str.length = len;
 	}
     }
     else if (compl_first_match->cp_prev != NULL
 	    && match_at_original_text(compl_first_match->cp_prev))
     {
-       p = vim_strsave(str);
-       if (p != NULL)
-       {
-	   vim_free(compl_first_match->cp_prev->cp_str);
-	   compl_first_match->cp_prev->cp_str = p;
-       }
+	char_u *p = vim_strnsave(str, len);
+	if (p != NULL)
+	{
+	    VIM_CLEAR_STRING(compl_first_match->cp_prev->cp_str);
+	    compl_first_match->cp_prev->cp_str.string = p;
+	    compl_first_match->cp_prev->cp_str.length = len;
+	}
     }
 }
 
@@ -2116,29 +2134,33 @@
     char_u	*p;
     int		len = (int)curwin->w_cursor.col - (int)compl_col;
     int		c;
-    compl_T	*cp;
 
-    p = compl_shown_match->cp_str;
-    if ((int)STRLEN(p) <= len)   // the match is too short
+    p = compl_shown_match->cp_str.string;
+    if ((int)compl_shown_match->cp_str.length <= len)   // the match is too short
     {
+	size_t	plen;
+	compl_T	*cp;
+
 	// When still at the original match use the first entry that matches
 	// the leader.
 	if (!match_at_original_text(compl_shown_match))
 	    return;
 
 	p = NULL;
+	plen = 0;
 	for (cp = compl_shown_match->cp_next; cp != NULL
 		&& !is_first_match(cp); cp = cp->cp_next)
 	{
-	    if (compl_leader == NULL
-		    || ins_compl_equal(cp, compl_leader,
-			(int)STRLEN(compl_leader)))
+	    if (compl_leader.string == NULL
+		    || ins_compl_equal(cp, compl_leader.string,
+			(int)compl_leader.length))
 	    {
-		p = cp->cp_str;
+		p = cp->cp_str.string;
+		plen = cp->cp_str.length;
 		break;
 	    }
 	}
-	if (p == NULL || (int)STRLEN(p) <= len)
+	if (p == NULL || (int)plen <= len)
 	    return;
     }
     p += len;
@@ -2284,14 +2306,15 @@
     static int
 ins_compl_stop(int c, int prev_mode, int retval)
 {
-    char_u	*ptr;
     int		want_cindent;
 
     // Get here when we have finished typing a sequence of ^N and
     // ^P or other completion characters in CTRL-X mode.  Free up
     // memory that was used, and make sure we can redo the insert.
-    if (compl_curr_match != NULL || compl_leader != NULL || c == Ctrl_E)
+    if (compl_curr_match != NULL || compl_leader.string != NULL || c == Ctrl_E)
     {
+	char_u	*ptr;
+
 	// If any of the original typed text has been changed, eg when
 	// ignorecase is set, we must add back-spaces to the redo
 	// buffer.  We add as few as necessary to delete just the part
@@ -2299,7 +2322,7 @@
 	// When using the longest match, edited the match or used
 	// CTRL-E then don't use the current match.
 	if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E)
-	    ptr = compl_curr_match->cp_str;
+	    ptr = compl_curr_match->cp_str.string;
 	else
 	    ptr = NULL;
 	ins_compl_fixRedoBufForLeader(ptr);
@@ -2346,19 +2369,25 @@
     if (c == Ctrl_E)
     {
 	char_u *p = NULL;
+	size_t	plen = 0;
 
 	ins_compl_delete();
-	if (compl_leader != NULL)
-	    p = compl_leader;
+	if (compl_leader.string != NULL)
+	{
+	    p = compl_leader.string;
+	    plen = compl_leader.length;
+	}
 	else if (compl_first_match != NULL)
-	    p = compl_orig_text;
+	{
+	    p = compl_orig_text.string;
+	    plen = compl_orig_text.length;
+	}
 	if (p != NULL)
 	{
 	    int	    compl_len = get_compl_len();
-	    int	    len = (int)STRLEN(p);
 
-	    if (len > compl_len)
-		ins_bytes_len(p + compl_len, len - compl_len);
+	    if ((int)plen > compl_len)
+		ins_bytes_len(p + compl_len, (int)(plen - compl_len));
 	}
 	retval = TRUE;
     }
@@ -2536,20 +2565,20 @@
     static void
 ins_compl_fixRedoBufForLeader(char_u *ptr_arg)
 {
-    int	    len;
+    int	    len = 0;
     char_u  *p;
     char_u  *ptr = ptr_arg;
 
     if (ptr == NULL)
     {
-	if (compl_leader != NULL)
-	    ptr = compl_leader;
+	if (compl_leader.string != NULL)
+	    ptr = compl_leader.string;
 	else
 	    return;  // nothing to do
     }
-    if (compl_orig_text != NULL)
+    if (compl_orig_text.string != NULL)
     {
-	p = compl_orig_text;
+	p = compl_orig_text.string;
 	for (len = 0; p[len] != NUL && p[len] == ptr[len]; ++len)
 	    ;
 	if (len > 0)
@@ -2557,8 +2586,6 @@
 	for (p += len; *p != NUL; MB_PTR_ADV(p))
 	    AppendCharToRedobuff(K_BS);
     }
-    else
-	len = 0;
     if (ptr != NULL)
 	AppendToRedobuffLit(ptr + len, -1);
 }
@@ -2988,12 +3015,18 @@
     compl_col = startcol;
     compl_length = (int)curwin->w_cursor.col - (int)startcol;
     // compl_pattern doesn't need to be set
-    compl_orig_text = vim_strnsave(ml_get_curline() + compl_col, compl_length);
+    compl_orig_text.string = vim_strnsave(ml_get_curline() + compl_col, (size_t)compl_length);
     if (p_ic)
 	flags |= CP_ICASE;
-    if (compl_orig_text == NULL || ins_compl_add(compl_orig_text,
-					      -1, NULL, NULL, NULL, 0,
-					      flags | CP_FAST, FALSE, NULL) != OK)
+    if (compl_orig_text.string == NULL)
+    {
+	compl_orig_text.length = 0;
+	return;
+    }
+    compl_orig_text.length = (size_t)compl_length;
+    if (ins_compl_add(compl_orig_text.string,
+		  (int)compl_orig_text.length, NULL, NULL, NULL, 0,
+		  flags | CP_FAST, FALSE, NULL) != OK)
 	return;
 
     ctrl_x_mode = CTRL_X_EVAL;
@@ -3223,7 +3256,7 @@
 			ret = list_append_dict(li, di);
 			if (ret != OK)
 			    return;
-			dict_add_string(di, "word", match->cp_str);
+			dict_add_string(di, "word", match->cp_str.string);
 			dict_add_string(di, "abbr", match->cp_text[CPT_ABBR]);
 			dict_add_string(di, "menu", match->cp_text[CPT_MENU]);
 			dict_add_string(di, "kind", match->cp_text[CPT_KIND]);
@@ -3470,8 +3503,8 @@
     static void
 get_next_include_file_completion(int compl_type)
 {
-    find_pattern_in_path(compl_pattern, compl_direction,
-	    (int)compl_patternlen, FALSE, FALSE,
+    find_pattern_in_path(compl_pattern.string, compl_direction,
+	    (int)compl_pattern.length, FALSE, FALSE,
 	    (compl_type == CTRL_X_PATH_DEFINES
 	     && !(compl_cont_status & CONT_SOL))
 	    ? FIND_DEFINE : FIND_ANY, 1L, ACTION_EXPAND,
@@ -3488,7 +3521,7 @@
 {
 #ifdef FEAT_COMPL_FUNC
     if (thesaurus_func_complete(compl_type))
-	expand_by_function(compl_type, compl_pattern);
+	expand_by_function(compl_type, compl_pattern.string);
     else
 #endif
 	ins_compl_dictionaries(
@@ -3496,7 +3529,7 @@
 		: (compl_type == CTRL_X_THESAURUS
 		    ? (*curbuf->b_p_tsr == NUL ? p_tsr : curbuf->b_p_tsr)
 		    : (*curbuf->b_p_dict == NUL ? p_dict : curbuf->b_p_dict)),
-		compl_pattern,
+		compl_pattern.string,
 		dict != NULL ? dict_f : 0,
 		compl_type == CTRL_X_THESAURUS);
 }
@@ -3513,12 +3546,12 @@
 
     // set p_ic according to p_ic, p_scs and pat for find_tags().
     save_p_ic = p_ic;
-    p_ic = ignorecase(compl_pattern);
+    p_ic = ignorecase(compl_pattern.string);
 
     // Find up to TAG_MANY matches.  Avoids that an enormous number
     // of matches is found when compl_pattern is empty
     g_tag_at_cursor = TRUE;
-    if (find_tags(compl_pattern, &num_matches, &matches,
+    if (find_tags(compl_pattern.string, &num_matches, &matches,
 		TAG_REGEXP | TAG_NAMES | TAG_NOIC | TAG_INS_COMP
 		| (ctrl_x_mode_not_default() ? TAG_VERBOSE : 0),
 		TAG_MANY, curbuf->b_ffname) == OK && num_matches > 0)
@@ -3553,13 +3586,11 @@
     int		i;
     int		score;
     char_u	*leader = ins_compl_leader();
-    size_t	leader_len = STRLEN(leader);
+    size_t	leader_len = ins_compl_leader_len();;
     int		in_fuzzy = ((get_cot_flags() & COT_FUZZY) != 0 && leader_len > 0);
     char_u	**sorted_matches;
     int		*fuzzy_indices_data;
     char_u	*last_sep = NULL;
-    size_t	path_with_wildcard_len;
-    char_u	*path_with_wildcard;
 
 #ifdef BACKSLASH_IN_FILENAME
     char pathsep = (curbuf->b_p_csl[0] == 's') ?
@@ -3573,7 +3604,7 @@
 #ifdef BACKSLASH_IN_FILENAME
 	if (curbuf->b_p_csl[0] == 's')
 	{
-	    for (i = 0; i < leader_len; i++)
+	    for (i = 0; i < (int)leader_len; i++)
 	    {
 		if (leader[i] == '\\')
 		    leader[i] = '/';
@@ -3581,7 +3612,7 @@
 	}
 	else if (curbuf->b_p_csl[0] == 'b')
 	{
-	    for (i = 0; i < leader_len; i++)
+	    for (i = 0; i < (int)leader_len; i++)
 	    {
 		if (leader[i] == '/')
 		    leader[i] = '\\';
@@ -3593,9 +3624,11 @@
 	{
 	    // No path separator or separator is the last character,
 	    // fuzzy match the whole leader
-	    vim_free(compl_pattern);
-	    compl_pattern = vim_strsave((char_u *)"*");
-	    compl_patternlen = STRLEN(compl_pattern);
+	    VIM_CLEAR_STRING(compl_pattern);
+	    compl_pattern.string = vim_strnsave((char_u *)"*", 1);
+	    if (compl_pattern.string == NULL)
+		return;
+	    compl_pattern.length = 1;
 	}
 	else if (*(last_sep + 1) == '\0')
 	    in_fuzzy = FALSE;
@@ -3603,29 +3636,29 @@
 	{
 	    // Split leader into path and file parts
 	    int path_len = last_sep - leader + 1;
-	    path_with_wildcard_len = path_len + 2;
-	    path_with_wildcard = alloc(path_with_wildcard_len);
+	    char_u  *path_with_wildcard;
+
+	    path_with_wildcard = alloc(path_len + 2);
 	    if (path_with_wildcard != NULL)
 	    {
-		vim_strncpy(path_with_wildcard, leader, path_len);
-		vim_strcat(path_with_wildcard, (char_u *)"*", path_with_wildcard_len);
-		vim_free(compl_pattern);
-		compl_pattern = path_with_wildcard;
-		compl_patternlen = STRLEN(compl_pattern);
+		vim_snprintf((char *)path_with_wildcard, path_len + 2, "%*.*s*", path_len, path_len, leader);
+		VIM_CLEAR_STRING(compl_pattern);
+		compl_pattern.string = path_with_wildcard;
+		compl_pattern.length = path_len + 1;
 
 		// Move leader to the file part
 		leader = last_sep + 1;
-		leader_len = STRLEN(leader);
+		leader_len -= path_len;
 	    }
 	}
     }
 
-    if (expand_wildcards(1, &compl_pattern, &num_matches, &matches,
+    if (expand_wildcards(1, &compl_pattern.string, &num_matches, &matches,
 		EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) != OK)
 	return;
 
     // May change home directory back to "~".
-    tilde_replace(compl_pattern, num_matches, matches);
+    tilde_replace(compl_pattern.string, num_matches, matches);
 #ifdef BACKSLASH_IN_FILENAME
     if (curbuf->b_p_csl[0] != NUL)
     {
@@ -3701,8 +3734,8 @@
     char_u	**matches;
     int		num_matches;
 
-    if (expand_cmdline(&compl_xp, compl_pattern,
-		(int)compl_patternlen, &num_matches, &matches) == EXPAND_OK)
+    if (expand_cmdline(&compl_xp, compl_pattern.string,
+		(int)compl_pattern.length, &num_matches, &matches) == EXPAND_OK)
 	ins_compl_add_matches(num_matches, matches, FALSE);
 }
 
@@ -3716,7 +3749,7 @@
     char_u	**matches;
     int		num_matches;
 
-    num_matches = expand_spelling(lnum, compl_pattern, &matches);
+    num_matches = expand_spelling(lnum, compl_pattern.string, &matches);
     if (num_matches > 0)
 	ins_compl_add_matches(num_matches, matches, p_ic);
     else
@@ -3741,6 +3774,7 @@
     *match_len = 0;
     ptr = ml_get_buf(ins_buf, cur_match_pos->lnum, FALSE) +
 	cur_match_pos->col;
+    len = (int)ml_get_buf_len(ins_buf, cur_match_pos->lnum) - cur_match_pos->col;
     if (ctrl_x_mode_line_or_eval())
     {
 	if (compl_status_adding())
@@ -3748,16 +3782,21 @@
 	    if (cur_match_pos->lnum >= ins_buf->b_ml.ml_line_count)
 		return NULL;
 	    ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1, FALSE);
+	    len = ml_get_buf_len(ins_buf, cur_match_pos->lnum + 1);
 	    if (!p_paste)
-		ptr = skipwhite(ptr);
+	    {
+		char_u	*tmp_ptr = ptr;
+
+		ptr = skipwhite(tmp_ptr);
+		len -= (int)(ptr - tmp_ptr);
+	    }
 	}
-	len = (int)STRLEN(ptr);
     }
     else
     {
 	char_u	*tmp_ptr = ptr;
 
-	if (compl_status_adding() && compl_length <= (int)STRLEN(tmp_ptr))
+	if (compl_status_adding() && compl_length <= len)
 	{
 	    tmp_ptr += compl_length;
 	    // Skip if already inside a word.
@@ -3866,14 +3905,14 @@
 	// has added a word that was at the beginning of the line
 	if ((ctrl_x_mode_whole_line() && !in_fuzzy) || ctrl_x_mode_eval() || (compl_cont_status & CONT_SOL))
 	    found_new_match = search_for_exact_line(st->ins_buf,
-			    st->cur_match_pos, compl_direction, compl_pattern);
+			    st->cur_match_pos, compl_direction, compl_pattern.string);
 	else if (in_fuzzy)
 	     found_new_match = search_for_fuzzy_match(st->ins_buf,
 			    st->cur_match_pos, leader, compl_direction,
 			    start_pos, &len, &ptr, ctrl_x_mode_whole_line());
 	else
 	    found_new_match = searchit(NULL, st->ins_buf, st->cur_match_pos,
-				NULL, compl_direction, compl_pattern, compl_patternlen,
+				NULL, compl_direction, compl_pattern.string, (int)compl_pattern.length,
 				1L, SEARCH_KEEP + SEARCH_NFMSG, RE_LAST, NULL);
 	--msg_silent;
 	if (!compl_started || st->set_match_pos)
@@ -3981,7 +4020,7 @@
 #ifdef FEAT_COMPL_FUNC
 	case CTRL_X_FUNCTION:
 	case CTRL_X_OMNI:
-	    expand_by_function(type, compl_pattern);
+	    expand_by_function(type, compl_pattern.string);
 	    break;
 #endif
 
@@ -4071,7 +4110,7 @@
 
 	// If complete() was called then compl_pattern has been reset.  The
 	// following won't work then, bail out.
-	if (compl_pattern == NULL)
+	if (compl_pattern.string == NULL)
 	    break;
 
 	// get the next set of completion matches
@@ -4137,7 +4176,7 @@
 ins_compl_update_shown_match(void)
 {
     while (!ins_compl_equal(compl_shown_match,
-		compl_leader, (int)STRLEN(compl_leader))
+		compl_leader.string, (int)compl_leader.length)
 	    && compl_shown_match->cp_next != NULL
 	    && !is_first_match(compl_shown_match->cp_next))
 	compl_shown_match = compl_shown_match->cp_next;
@@ -4146,12 +4185,12 @@
     // backward, find the last match.
     if (compl_shows_dir_backward()
 	    && !ins_compl_equal(compl_shown_match,
-		compl_leader, (int)STRLEN(compl_leader))
+		compl_leader.string, (int)compl_leader.length)
 	    && (compl_shown_match->cp_next == NULL
 		|| is_first_match(compl_shown_match->cp_next)))
     {
 	while (!ins_compl_equal(compl_shown_match,
-		    compl_leader, (int)STRLEN(compl_leader))
+		    compl_leader.string, (int)compl_leader.length)
 		&& compl_shown_match->cp_prev != NULL
 		&& !is_first_match(compl_shown_match->cp_prev))
 	    compl_shown_match = compl_shown_match->cp_prev;
@@ -4196,8 +4235,8 @@
 
     // Make sure we don't go over the end of the string, this can happen with
     // illegal bytes.
-    if (compl_len < (int)STRLEN(compl_shown_match->cp_str))
-	ins_bytes(compl_shown_match->cp_str + compl_len);
+    if (compl_len < (int)compl_shown_match->cp_str.length)
+	ins_bytes(compl_shown_match->cp_str.string + compl_len);
     if (match_at_original_text(compl_shown_match))
 	compl_used_match = FALSE;
     else
@@ -4277,7 +4316,7 @@
 
     comp = compl_first_match;
     do {
-      if (comp->cp_score == score && (str == comp->cp_str || str == comp->cp_text[CPT_ABBR]))
+      if (comp->cp_score == score && (str == comp->cp_str.string || str == comp->cp_text[CPT_ABBR]))
 	  return comp;
       comp = comp->cp_next;
     } while (comp != NULL && !is_first_match(comp));
@@ -4374,9 +4413,9 @@
 	    found_end = FALSE;
 	}
 	if (!match_at_original_text(compl_shown_match)
-		&& compl_leader != NULL
+		&& compl_leader.string != NULL
 		&& !ins_compl_equal(compl_shown_match,
-		    compl_leader, (int)STRLEN(compl_leader))
+		    compl_leader.string, (int)compl_leader.length)
 		&& !(compl_fuzzy_match && compl_shown_match->cp_score > 0))
 	    ++todo;
 	else
@@ -4436,7 +4475,7 @@
     if (compl_shown_match == NULL)
 	return -1;
 
-    if (compl_leader != NULL
+    if (compl_leader.string != NULL
 	    && !match_at_original_text(compl_shown_match)
 	    && !compl_fuzzy_match)
 	// Update "compl_shown_match" to the actually shown match
@@ -4474,7 +4513,7 @@
     // Insert the text of the new completion, or the compl_leader.
     if (compl_no_insert && !started)
     {
-	ins_bytes(compl_orig_text + get_compl_len());
+	ins_bytes(compl_orig_text.string + get_compl_len());
 	compl_used_match = FALSE;
     }
     else if (insert_match)
@@ -4482,7 +4521,7 @@
 	if (!compl_get_longest || compl_used_match)
 	    ins_compl_insert(in_compl_func);
 	else
-	    ins_bytes(compl_leader + get_compl_len());
+	    ins_bytes(compl_leader.string + get_compl_len());
     }
     else
 	compl_used_match = FALSE;
@@ -4662,8 +4701,7 @@
 /*
  * Get the pattern, column and length for normal completion (CTRL-N CTRL-P
  * completion)
- * Sets the global variables: compl_col, compl_length, compl_pattern and
- * compl_patternlen.
+ * Sets the global variables: compl_col, compl_length and compl_pattern.
  * Uses the global variables: compl_cont_status and ctrl_x_mode
  */
     static int
@@ -4679,29 +4717,33 @@
 	    compl_length = curs_col - startcol;
 	}
 	if (p_ic)
-	    compl_pattern = str_foldcase(line + compl_col,
-		    compl_length, NULL, 0);
-	else
-	    compl_pattern = vim_strnsave(line + compl_col, compl_length);
-	if (compl_pattern == NULL)
 	{
-	    compl_patternlen = 0;
-	    return FAIL;
+	    compl_pattern.string = str_foldcase(line + compl_col,
+		    compl_length, NULL, 0);
+	    if (compl_pattern.string == NULL)
+	    {
+		compl_pattern.length = 0;
+		return FAIL;
+	    }
+	    compl_pattern.length = STRLEN(compl_pattern.string);
+	}
+	else
+	{
+	    compl_pattern.string = vim_strnsave(line + compl_col, (size_t)compl_length);
+	    if (compl_pattern.string == NULL)
+	    {
+		compl_pattern.length = 0;
+		return FAIL;
+	    }
+	    compl_pattern.length = (size_t)compl_length;
 	}
     }
     else if (compl_status_adding())
     {
 	char_u	    *prefix = (char_u *)"\\<";
 	size_t	    prefixlen = STRLEN_LITERAL("\\<");
+	size_t	    n;
 
-	// we need up to 2 extra chars for the prefix
-	compl_pattern = alloc(quote_meta(NULL, line + compl_col,
-		    compl_length) + prefixlen);
-	if (compl_pattern == NULL)
-	{
-	    compl_patternlen = 0;
-	    return FAIL;
-	}
 	if (!vim_iswordp(line + compl_col)
 		|| (compl_col > 0
 		    && (vim_iswordp(mb_prevptr(line, line + compl_col)))))
@@ -4709,20 +4751,33 @@
 	    prefix = (char_u *)"";
 	    prefixlen = 0;
 	}
-	STRCPY((char *)compl_pattern, prefix);
-	(void)quote_meta(compl_pattern + prefixlen,
+
+	// we need up to 2 extra chars for the prefix
+	n = quote_meta(NULL, line + compl_col, compl_length) + prefixlen;
+	compl_pattern.string = alloc(n);
+	if (compl_pattern.string == NULL)
+	{
+	    compl_pattern.length = 0;
+	    return FAIL;
+	}
+	STRCPY((char *)compl_pattern.string, prefix);
+	(void)quote_meta(compl_pattern.string + prefixlen,
 		line + compl_col, compl_length);
+	compl_pattern.length = n - 1;
     }
     else if (--startcol < 0
 	    || !vim_iswordp(mb_prevptr(line, line + startcol + 1)))
     {
+	size_t	len = STRLEN_LITERAL("\\<\\k\\k");
+
 	// Match any word of at least two chars
-	compl_pattern = vim_strnsave((char_u *)"\\<\\k\\k", STRLEN_LITERAL("\\<\\k\\k"));
-	if (compl_pattern == NULL)
+	compl_pattern.string = vim_strnsave((char_u *)"\\<\\k\\k", len);
+	if (compl_pattern.string == NULL)
 	{
-	    compl_patternlen = 0;
+	    compl_pattern.length = 0;
 	    return FAIL;
 	}
+	compl_pattern.length = len;
 	compl_col += curs_col;
 	compl_length = 0;
     }
@@ -4756,32 +4811,34 @@
 	    // Only match word with at least two chars -- webb
 	    // there's no need to call quote_meta,
 	    // alloc(7) is enough  -- Acevedo
-	    compl_pattern = alloc(7);
-	    if (compl_pattern == NULL)
+	    compl_pattern.string = alloc(7);
+	    if (compl_pattern.string == NULL)
 	    {
-		compl_patternlen = 0;
+		compl_pattern.length = 0;
 		return FAIL;
 	    }
-	    STRCPY((char *)compl_pattern, "\\<");
-	    (void)quote_meta(compl_pattern + 2, line + compl_col, 1);
-	    STRCAT((char *)compl_pattern, "\\k");
+	    STRCPY((char *)compl_pattern.string, "\\<");
+	    (void)quote_meta(compl_pattern.string + 2, line + compl_col, 1);
+	    STRCAT((char *)compl_pattern.string, "\\k");
+	    compl_pattern.length = STRLEN(compl_pattern.string);
 	}
 	else
 	{
-	    compl_pattern = alloc(quote_meta(NULL, line + compl_col,
-			compl_length) + 2);
-	    if (compl_pattern == NULL)
+	    size_t  n = quote_meta(NULL, line + compl_col, compl_length) + 2;
+
+	    compl_pattern.string = alloc(n);
+	    if (compl_pattern.string == NULL)
 	    {
-		compl_patternlen = 0;
+		compl_pattern.length = 0;
 		return FAIL;
 	    }
-	    STRCPY((char *)compl_pattern, "\\<");
-	    (void)quote_meta(compl_pattern + 2, line + compl_col,
+	    STRCPY((char *)compl_pattern.string, "\\<");
+	    (void)quote_meta(compl_pattern.string + 2, line + compl_col,
 		    compl_length);
+	    compl_pattern.length = n - 1;
 	}
     }
 
-    compl_patternlen = STRLEN(compl_pattern);
     return OK;
 }
 
@@ -4798,17 +4855,26 @@
     if (compl_length < 0)	// cursor in indent: empty pattern
 	compl_length = 0;
     if (p_ic)
-	compl_pattern = str_foldcase(line + compl_col, compl_length,
-		NULL, 0);
-    else
-	compl_pattern = vim_strnsave(line + compl_col, compl_length);
-    if (compl_pattern == NULL)
     {
-	compl_patternlen = 0;
-	return FAIL;
+	compl_pattern.string = str_foldcase(line + compl_col, compl_length,
+		NULL, 0);
+	if (compl_pattern.string == NULL)
+	{
+	    compl_pattern.length = 0;
+	    return FAIL;
+	}
+	compl_pattern.length = STRLEN(compl_pattern.string);
     }
-
-    compl_patternlen = STRLEN(compl_pattern);
+    else
+    {
+	compl_pattern.string = vim_strnsave(line + compl_col, (size_t)compl_length);
+	if (compl_pattern.string == NULL)
+	{
+	    compl_pattern.length = 0;
+	    return FAIL;
+	}
+	compl_pattern.length = (size_t)compl_length;
+    }
 
     return OK;
 }
@@ -4836,14 +4902,14 @@
 
     compl_col += startcol;
     compl_length = (int)curs_col - startcol;
-    compl_pattern = addstar(line + compl_col, compl_length, EXPAND_FILES);
-    if (compl_pattern == NULL)
+    compl_pattern.string = addstar(line + compl_col, compl_length, EXPAND_FILES);
+    if (compl_pattern.string == NULL)
     {
-	compl_patternlen = 0;
+	compl_pattern.length = 0;
 	return FAIL;
     }
 
-    compl_patternlen = STRLEN(compl_pattern);
+    compl_pattern.length = STRLEN(compl_pattern.string);
 
     return OK;
 }
@@ -4855,22 +4921,22 @@
     static int
 get_cmdline_compl_info(char_u *line, colnr_T curs_col)
 {
-    compl_pattern = vim_strnsave(line, curs_col);
-    if (compl_pattern == NULL)
+    compl_pattern.string = vim_strnsave(line, curs_col);
+    if (compl_pattern.string == NULL)
     {
-	compl_patternlen = 0;
+	compl_pattern.length = 0;
 	return FAIL;
     }
-    compl_patternlen = curs_col;
-    set_cmd_context(&compl_xp, compl_pattern,
-	    (int)compl_patternlen, curs_col, FALSE);
+    compl_pattern.length = curs_col;
+    set_cmd_context(&compl_xp, compl_pattern.string,
+	    (int)compl_pattern.length, curs_col, FALSE);
     if (compl_xp.xp_context == EXPAND_UNSUCCESSFUL
 	    || compl_xp.xp_context == EXPAND_NOTHING)
 	// No completion possible, use an empty pattern to get a
 	// "pattern not found" message.
 	compl_col = curs_col;
     else
-	compl_col = (int)(compl_xp.xp_pattern - compl_pattern);
+	compl_col = (int)(compl_xp.xp_pattern - compl_pattern.string);
     compl_length = curs_col - compl_col;
 
     return OK;
@@ -4959,14 +5025,14 @@
     // it may have become invalid.
     line = ml_get(curwin->w_cursor.lnum);
     compl_length = curs_col - compl_col;
-    compl_pattern = vim_strnsave(line + compl_col, compl_length);
-    if (compl_pattern == NULL)
+    compl_pattern.string = vim_strnsave(line + compl_col, (size_t)compl_length);
+    if (compl_pattern.string == NULL)
     {
-	compl_patternlen = 0;
+	compl_pattern.length = 0;
 	return FAIL;
     }
 
-    compl_patternlen = compl_length;
+    compl_pattern.length = (size_t)compl_length;
     ret = OK;
 #endif
 
@@ -5001,14 +5067,14 @@
     }
     // Need to obtain "line" again, it may have become invalid.
     line = ml_get(curwin->w_cursor.lnum);
-    compl_pattern = vim_strnsave(line + compl_col, compl_length);
-    if (compl_pattern == NULL)
+    compl_pattern.string = vim_strnsave(line + compl_col, (size_t)compl_length);
+    if (compl_pattern.string == NULL)
     {
-	compl_patternlen = 0;
+	compl_pattern.length = 0;
 	return FAIL;
     }
 
-    compl_patternlen = compl_length;
+    compl_pattern.length = (size_t)compl_length;
     ret = OK;
 #endif
 
@@ -5227,16 +5293,16 @@
     ins_compl_fixRedoBufForLeader(NULL);
 
     // Always add completion for the original text.
-    vim_free(compl_orig_text);
-    compl_orig_text = vim_strnsave(line + compl_col, compl_length);
+    VIM_CLEAR_STRING(compl_orig_text);
+    compl_orig_text.length = (size_t)compl_length;
+    compl_orig_text.string = vim_strnsave(line + compl_col, (size_t)compl_length);
     if (p_ic)
 	flags |= CP_ICASE;
-    if (compl_orig_text == NULL || ins_compl_add(compl_orig_text,
-		-1, NULL, NULL, NULL, 0, flags, FALSE, NULL) != OK)
+    if (compl_orig_text.string == NULL || ins_compl_add(compl_orig_text.string,
+		(int)compl_orig_text.length, NULL, NULL, NULL, 0, flags, FALSE, NULL) != OK)
     {
-	VIM_CLEAR(compl_pattern);
-	compl_patternlen = 0;
-	VIM_CLEAR(compl_orig_text);
+	VIM_CLEAR_STRING(compl_pattern);
+	VIM_CLEAR_STRING(compl_orig_text);
 	return FAIL;
     }
 
@@ -5505,7 +5571,7 @@
     void
 free_insexpand_stuff(void)
 {
-    VIM_CLEAR(compl_orig_text);
+    VIM_CLEAR_STRING(compl_orig_text);
 # ifdef FEAT_EVAL
     free_callback(&cfu_cb);
     free_callback(&ofu_cb);
diff --git a/src/proto/insexpand.pro b/src/proto/insexpand.pro
index dbd5ef7..4feab85 100644
--- a/src/proto/insexpand.pro
+++ b/src/proto/insexpand.pro
@@ -31,6 +31,7 @@
 int pum_wanted(void);
 void ins_compl_show_pum(void);
 char_u *ins_compl_leader(void);
+size_t ins_compl_leader_len(void);
 char_u *find_word_start(char_u *ptr);
 char_u *find_word_end(char_u *ptr);
 void ins_compl_clear(void);
diff --git a/src/version.c b/src/version.c
index 4eea94a..9adbf9b 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    882,
+/**/
     881,
 /**/
     880,