changeset 15613:90f01701ecad v8.1.0814

patch 8.1.0814: :mksession cannot handle a very long 'runtimepath' commit https://github.com/vim/vim/commit/ed18f2c03ae4786b489943cb575bb781a70356e4 Author: Bram Moolenaar <Bram@vim.org> Date: Thu Jan 24 20:30:52 2019 +0100 patch 8.1.0814: :mksession cannot handle a very long 'runtimepath' Problem: :mksession cannot handle a very long 'runtimepath'. (Timothy Madden) Solution: Expand each part separately, instead of the whole option at once. (Christian Brabandt, closes #3466)
author Bram Moolenaar <Bram@vim.org>
date Thu, 24 Jan 2019 20:45:06 +0100
parents e08d1aa308d8
children 19317f8fdd2c
files src/option.c src/testdir/test_mksession.vim src/version.c
diffstat 3 files changed, 79 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/src/option.c
+++ b/src/option.c
@@ -3243,7 +3243,7 @@ static int find_key_option(char_u *arg_a
 static void showoptions(int all, int opt_flags);
 static int optval_default(struct vimoption *, char_u *varp);
 static void showoneopt(struct vimoption *, int opt_flags);
-static int put_setstring(FILE *fd, char *cmd, char *name, char_u **valuep, int expand);
+static int put_setstring(FILE *fd, char *cmd, char *name, char_u **valuep, long_u flags);
 static int put_setnum(FILE *fd, char *cmd, char *name, long *valuep);
 static int put_setbool(FILE *fd, char *cmd, char *name, int value);
 static int  istermoption(struct vimoption *);
@@ -10297,7 +10297,7 @@ makeset(FILE *fd, int opt_flags, int loc
 			do_endif = TRUE;
 		    }
 		    if (put_setstring(fd, cmd, p->fullname, (char_u **)varp,
-					  (p->flags & P_EXPAND) != 0) == FAIL)
+							     p->flags) == FAIL)
 			return FAIL;
 		    if (do_endif)
 		    {
@@ -10319,14 +10319,14 @@ makeset(FILE *fd, int opt_flags, int loc
     int
 makefoldset(FILE *fd)
 {
-    if (put_setstring(fd, "setlocal", "fdm", &curwin->w_p_fdm, FALSE) == FAIL
+    if (put_setstring(fd, "setlocal", "fdm", &curwin->w_p_fdm, 0) == FAIL
 # ifdef FEAT_EVAL
-	    || put_setstring(fd, "setlocal", "fde", &curwin->w_p_fde, FALSE)
+	    || put_setstring(fd, "setlocal", "fde", &curwin->w_p_fde, 0)
 								       == FAIL
 # endif
-	    || put_setstring(fd, "setlocal", "fmr", &curwin->w_p_fmr, FALSE)
+	    || put_setstring(fd, "setlocal", "fmr", &curwin->w_p_fmr, 0)
 								       == FAIL
-	    || put_setstring(fd, "setlocal", "fdi", &curwin->w_p_fdi, FALSE)
+	    || put_setstring(fd, "setlocal", "fdi", &curwin->w_p_fdi, 0)
 								       == FAIL
 	    || put_setnum(fd, "setlocal", "fdl", &curwin->w_p_fdl) == FAIL
 	    || put_setnum(fd, "setlocal", "fml", &curwin->w_p_fml) == FAIL
@@ -10345,10 +10345,12 @@ put_setstring(
     char	*cmd,
     char	*name,
     char_u	**valuep,
-    int		expand)
+    long_u	flags)
 {
     char_u	*s;
-    char_u	*buf;
+    char_u	*buf = NULL;
+    char_u	*part = NULL;
+    char_u	*p;
 
     if (fprintf(fd, "%s %s=", cmd, name) < 0)
 	return FAIL;
@@ -10364,12 +10366,46 @@ put_setstring(
 		if (put_escstr(fd, str2special(&s, FALSE), 2) == FAIL)
 		    return FAIL;
 	}
-	else if (expand)
-	{
-	    buf = alloc(MAXPATHL);
+	// expand the option value, replace $HOME by ~
+	else if ((flags & P_EXPAND) != 0)
+	{
+	    int  size = (int)STRLEN(*valuep) + 1;
+
+	    // replace home directory in the whole option value into "buf"
+	    buf = alloc(size);
 	    if (buf == NULL)
-		return FAIL;
-	    home_replace(NULL, *valuep, buf, MAXPATHL, FALSE);
+		goto fail;
+	    home_replace(NULL, *valuep, buf, size, FALSE);
+
+	    // If the option value is longer than MAXPATHL, we need to append
+	    // earch comma separated part of the option separately, so that it
+	    // can be expanded when read back.
+	    if (size >= MAXPATHL && (flags & P_COMMA) != 0
+					   && vim_strchr(*valuep, ',') != NULL)
+	    {
+		part = alloc(size);
+		if (part == NULL)
+		    goto fail;
+
+		// write line break to clear the option, e.g. ':set rtp='
+		if (put_eol(fd) == FAIL)
+		    goto fail;
+
+		p = buf;
+		while (*p != NUL)
+		{
+		    // for each comma separated option part, append value to
+		    // the option, :set rtp+=value
+		    if (fprintf(fd, "%s %s+=", cmd, name) < 0)
+			goto fail;
+		    (void)copy_option_part(&p, part, size,  ",");
+		    if (put_escstr(fd, part, 2) == FAIL || put_eol(fd) == FAIL)
+			goto fail;
+		}
+		vim_free(buf);
+		vim_free(part);
+		return OK;
+	    }
 	    if (put_escstr(fd, buf, 2) == FAIL)
 	    {
 		vim_free(buf);
@@ -10383,6 +10419,10 @@ put_setstring(
     if (put_eol(fd) < 0)
 	return FAIL;
     return OK;
+fail:
+    vim_free(buf);
+    vim_free(part);
+    return FAIL;
 }
 
     static int
--- a/src/testdir/test_mksession.vim
+++ b/src/testdir/test_mksession.vim
@@ -3,7 +3,7 @@
 set encoding=latin1
 scriptencoding latin1
 
-if !has('multi_byte') || !has('mksession')
+if !has('mksession')
   finish
 endif
 
@@ -126,6 +126,29 @@ func Test_mksession_large_winheight()
   call delete('Xtest_mks_winheight.out')
 endfunc
 
+func Test_mksession_rtp()
+  new
+  let _rtp=&rtp
+  " Make a real long (invalid) runtimepath value,
+  " that should exceed PATH_MAX (hopefully)
+  let newrtp=&rtp.',~'.repeat('/foobar', 1000)
+  let newrtp.=",".expand("$HOME")."/.vim"
+  let &rtp=newrtp
+
+  " determine expected value
+  let expected=split(&rtp, ',')
+  let expected = map(expected, '"set runtimepath+=".v:val')
+  let expected = ['set runtimepath='] + expected
+  let expected = map(expected, {v,w -> substitute(w, $HOME, "~", "g")})
+
+  mksession! Xtest_mks.out
+  let &rtp=_rtp
+  let li = filter(readfile('Xtest_mks.out'), 'v:val =~# "runtimepath"')
+  call assert_equal(expected, li)
+
+  call delete('Xtest_mks.out')
+endfunc
+
 func Test_mksession_arglist()
   argdel *
   next file1 file2 file3 file4
--- a/src/version.c
+++ b/src/version.c
@@ -788,6 +788,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    84,
+/**/
     813,
 /**/
     812,