changeset 165:e943e5502fc9 v7.0050

updated for version 7.0050
author vimboss
date Sat, 12 Feb 2005 14:31:42 +0000
parents 8b0ee9d57d7f
children 3a28ed993bbe
files src/eval.c src/ex_cmds.c src/memline.c
diffstat 3 files changed, 268 insertions(+), 94 deletions(-) [+]
line wrap: on
line diff
--- a/src/eval.c
+++ b/src/eval.c
@@ -335,10 +335,10 @@ static int list_append_tv __ARGS((list_T
 static int list_insert_tv __ARGS((list_T *l, typval_T *tv, listitem_T *item));
 static int list_extend __ARGS((list_T	*l1, list_T *l2, listitem_T *bef));
 static int list_concat __ARGS((list_T *l1, list_T *l2, typval_T *tv));
-static list_T *list_copy __ARGS((list_T *orig, int deep));
+static list_T *list_copy __ARGS((list_T *orig, int deep, int copyID));
 static void list_remove __ARGS((list_T *l, listitem_T *item, listitem_T *item2));
 static char_u *list2string __ARGS((typval_T *tv));
-static void list_join __ARGS((garray_T *gap, list_T *l, char_u *sep, int echo));
+static int list_join __ARGS((garray_T *gap, list_T *l, char_u *sep, int echo));
 
 static dict_T *dict_alloc __ARGS((void));
 static void dict_unref __ARGS((dict_T *d));
@@ -347,6 +347,7 @@ static dictitem_T *dictitem_alloc __ARGS
 static dictitem_T *dictitem_copy __ARGS((dictitem_T *org));
 static void dictitem_remove __ARGS((dict_T *dict, dictitem_T *item));
 static void dictitem_free __ARGS((dictitem_T *item));
+static dict_T *dict_copy __ARGS((dict_T *orig, int deep, int copyID));
 static int dict_add __ARGS((dict_T *d, dictitem_T *item));
 static long dict_len __ARGS((dict_T *d));
 static dictitem_T *dict_find __ARGS((dict_T *d, char_u *key, int len));
@@ -361,6 +362,7 @@ static int find_internal_func __ARGS((ch
 static char_u *deref_func_name __ARGS((char_u *name, int *lenp));
 static int get_func_tv __ARGS((char_u *name, int len, typval_T *rettv, char_u **arg, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, dict_T *selfdict));
 static int call_func __ARGS((char_u *name, int len, typval_T *rettv, int argcount, typval_T *argvars, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, dict_T *selfdict));
+static void emsg_funcname __ARGS((char *msg, char_u *name));
 
 static void f_add __ARGS((typval_T *argvars, typval_T *rettv));
 static void f_append __ARGS((typval_T *argvars, typval_T *rettv));
@@ -562,7 +564,7 @@ static void set_var __ARGS((char_u *name
 static int var_check_ro __ARGS((int flags, char_u *name));
 static int tv_check_lock __ARGS((int lock, char_u *name));
 static void copy_tv __ARGS((typval_T *from, typval_T *to));
-static void item_copy __ARGS((typval_T *from, typval_T *to, int deep));
+static int item_copy __ARGS((typval_T *from, typval_T *to, int deep, int copyID));
 static char_u *find_option_end __ARGS((char_u **arg, int *opt_flags));
 static char_u *trans_function_name __ARGS((char_u **pp, int skip, int flags, funcdict_T *fd));
 static int eval_fname_script __ARGS((char_u *p));
@@ -2497,10 +2499,7 @@ ex_call(eap)
 
     if (*startarg != '(')
     {
-	if (*name == K_SPECIAL)
-	    EMSG2(_("E107: Missing braces: <SNR>%s"), name + 3);
-	else
-	    EMSG2(_("E107: Missing braces: %s"), name);
+	EMSG2(_("E107: Missing braces: %s"), eap->arg);
 	goto end;
     }
 
@@ -4961,7 +4960,7 @@ list_concat(l1, l2, tv)
     list_T	*l;
 
     /* make a copy of the first list. */
-    l = list_copy(l1, FALSE);
+    l = list_copy(l1, FALSE, 0);
     if (l == NULL)
 	return FAIL;
     tv->v_type = VAR_LIST;
@@ -4974,12 +4973,14 @@ list_concat(l1, l2, tv)
 /*
  * Make a copy of list "orig".  Shallow if "deep" is FALSE.
  * The refcount of the new list is set to 1.
+ * See item_copy() for "copyID".
  * Returns NULL when out of memory.
  */
     static list_T *
-list_copy(orig, deep)
+list_copy(orig, deep, copyID)
     list_T	*orig;
     int		deep;
+    int		copyID;
 {
     list_T	*copy;
     listitem_T	*item;
@@ -4991,18 +4992,37 @@ list_copy(orig, deep)
     copy = list_alloc();
     if (copy != NULL)
     {
-	for (item = orig->lv_first; item != NULL; item = item->li_next)
+	if (copyID != 0)
+	{
+	    /* Do this before adding the items, because one of the items may
+	     * refer back to this list. */
+	    orig->lv_copyID = copyID;
+	    orig->lv_copylist = copy;
+	}
+	for (item = orig->lv_first; item != NULL && !got_int;
+							 item = item->li_next)
 	{
 	    ni = listitem_alloc();
 	    if (ni == NULL)
 		break;
 	    if (deep)
-		item_copy(&item->li_tv, &ni->li_tv, deep);
+	    {
+		if (item_copy(&item->li_tv, &ni->li_tv, deep, copyID) == FAIL)
+		{
+		    vim_free(ni);
+		    break;
+		}
+	    }
 	    else
 		copy_tv(&item->li_tv, &ni->li_tv);
 	    list_append(copy, ni);
 	}
 	++copy->lv_refcount;
+	if (item != NULL)
+	{
+	    list_unref(copy);
+	    copy = NULL;
+	}
     }
 
     return copy;
@@ -5054,7 +5074,11 @@ list2string(tv)
 	return NULL;
     ga_init2(&ga, (int)sizeof(char), 80);
     ga_append(&ga, '[');
-    list_join(&ga, tv->vval.v_list, (char_u *)", ", FALSE);
+    if (list_join(&ga, tv->vval.v_list, (char_u *)", ", FALSE) == FAIL)
+    {
+	vim_free(ga.ga_data);
+	return NULL;
+    }
     ga_append(&ga, ']');
     ga_append(&ga, NUL);
     return (char_u *)ga.ga_data;
@@ -5063,8 +5087,9 @@ list2string(tv)
 /*
  * Join list "l" into a string in "*gap", using separator "sep".
  * When "echo" is TRUE use String as echoed, otherwise as inside a List.
- */
-    static void
+ * Return FAIL or OK.
+ */
+    static int
 list_join(gap, l, sep, echo)
     garray_T	*gap;
     list_T	*l;
@@ -5077,7 +5102,7 @@ list_join(gap, l, sep, echo)
     listitem_T	*item;
     char_u	*s;
 
-    for (item = l->lv_first; item != NULL; item = item->li_next)
+    for (item = l->lv_first; item != NULL && !got_int; item = item->li_next)
     {
 	if (first)
 	    first = FALSE;
@@ -5091,7 +5116,10 @@ list_join(gap, l, sep, echo)
 	if (s != NULL)
 	    ga_concat(gap, s);
 	vim_free(tofree);
-    }
+	if (s == NULL)
+	    return FAIL;
+    }
+    return OK;
 }
 
 /*
@@ -5108,6 +5136,7 @@ dict_alloc()
 	hash_init(&d->dv_hashtab);
 	d->dv_lock = 0;
 	d->dv_refcount = 0;
+	d->dv_copyID = 0;
     }
     return d;
 }
@@ -5222,12 +5251,14 @@ dictitem_free(item)
 /*
  * Make a copy of dict "d".  Shallow if "deep" is FALSE.
  * The refcount of the new dict is set to 1.
+ * See item_copy() for "copyID".
  * Returns NULL when out of memory.
  */
     static dict_T *
-dict_copy(orig, deep)
+dict_copy(orig, deep, copyID)
     dict_T	*orig;
     int		deep;
+    int		copyID;
 {
     dict_T	*copy;
     dictitem_T	*di;
@@ -5240,8 +5271,13 @@ dict_copy(orig, deep)
     copy = dict_alloc();
     if (copy != NULL)
     {
+	if (copyID != 0)
+	{
+	    orig->dv_copyID = copyID;
+	    orig->dv_copydict = copy;
+	}
 	todo = orig->dv_hashtab.ht_used;
-	for (hi = orig->dv_hashtab.ht_array; todo > 0; ++hi)
+	for (hi = orig->dv_hashtab.ht_array; todo > 0 && !got_int; ++hi)
 	{
 	    if (!HASHITEM_EMPTY(hi))
 	    {
@@ -5251,7 +5287,14 @@ dict_copy(orig, deep)
 		if (di == NULL)
 		    break;
 		if (deep)
-		    item_copy(&HI2DI(hi)->di_tv, &di->di_tv, deep);
+		{
+		    if (item_copy(&HI2DI(hi)->di_tv, &di->di_tv, deep,
+							      copyID) == FAIL)
+		    {
+			vim_free(di);
+			break;
+		    }
+		}
 		else
 		    copy_tv(&HI2DI(hi)->di_tv, &di->di_tv);
 		if (dict_add(copy, di) == FAIL)
@@ -5263,6 +5306,11 @@ dict_copy(orig, deep)
 	}
 
 	++copy->dv_refcount;
+	if (todo > 0)
+	{
+	    dict_unref(copy);
+	    copy = NULL;
+	}
     }
 
     return copy;
@@ -5355,7 +5403,7 @@ dict2string(tv)
     ga_append(&ga, '{');
 
     todo = d->dv_hashtab.ht_used;
-    for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
+    for (hi = d->dv_hashtab.ht_array; todo > 0 && !got_int; ++hi)
     {
 	if (!HASHITEM_EMPTY(hi))
 	{
@@ -5377,7 +5425,14 @@ dict2string(tv)
 	    if (s != NULL)
 		ga_concat(&ga, s);
 	    vim_free(tofree);
-	}
+	    if (s == NULL)
+		break;
+	}
+    }
+    if (todo > 0)
+    {
+	vim_free(ga.ga_data);
+	return NULL;
     }
 
     ga_append(&ga, '}');
@@ -5721,7 +5776,7 @@ static struct fst
     {"count",		2, 4, f_count},
     {"cscope_connection",0,3, f_cscope_connection},
     {"cursor",		2, 2, f_cursor},
-    {"deepcopy",	1, 1, f_deepcopy},
+    {"deepcopy",	1, 2, f_deepcopy},
     {"delete",		1, 1, f_delete},
     {"did_filetype",	0, 0, f_did_filetype},
     {"diff_filler",	1, 1, f_diff_filler},
@@ -6043,9 +6098,9 @@ get_func_tv(name, len, rettv, arg, first
     else if (!aborting())
     {
 	if (argcount == MAX_FUNC_ARGS)
-	    EMSG2(_("E740: Too many arguments for function %s"), name);
-	else
-	    EMSG2(_("E116: Invalid arguments for function %s"), name);
+	    emsg_funcname("E740: Too many arguments for function %s", name);
+	else
+	    emsg_funcname("E116: Invalid arguments for function %s", name);
     }
 
     while (--argcount >= 0)
@@ -6247,21 +6302,21 @@ call_func(name, len, rettv, argcount, ar
 	switch (error)
 	{
 	    case ERROR_UNKNOWN:
-		    EMSG2(_("E117: Unknown function: %s"), name);
+		    emsg_funcname("E117: Unknown function: %s", name);
 		    break;
 	    case ERROR_TOOMANY:
-		    EMSG2(_(e_toomanyarg), name);
+		    emsg_funcname(e_toomanyarg, name);
 		    break;
 	    case ERROR_TOOFEW:
-		    EMSG2(_("E119: Not enough arguments for function: %s"),
+		    emsg_funcname("E119: Not enough arguments for function: %s",
 									name);
 		    break;
 	    case ERROR_SCRIPT:
-		    EMSG2(_("E120: Using <SID> not in a script context: %s"),
+		    emsg_funcname("E120: Using <SID> not in a script context: %s",
 									name);
 		    break;
 	    case ERROR_DICT:
-		    EMSG2(_("E725: Calling dict function without Dictionary: %s"),
+		    emsg_funcname("E725: Calling dict function without Dictionary: %s",
 									name);
 		    break;
 	}
@@ -6274,6 +6329,25 @@ call_func(name, len, rettv, argcount, ar
     return ret;
 }
 
+/*
+ * Give an error message with a function name.  Handle <SNR> things.
+ */
+    static void
+emsg_funcname(msg, name)
+    char	*msg;
+    char_u	*name;
+{
+    char_u	*p;
+
+    if (*name == K_SPECIAL)
+	p = concat_str((char_u *)"<SNR>", name + 3);
+    else
+	p = name;
+    EMSG2(_(msg), p);
+    if (p != name)
+	vim_free(p);
+}
+
 /*********************************************
  * Implementation of the built-in functions
  */
@@ -6906,7 +6980,7 @@ f_copy(argvars, rettv)
     typval_T	*argvars;
     typval_T	*rettv;
 {
-    item_copy(&argvars[0], rettv, FALSE);
+    item_copy(&argvars[0], rettv, FALSE, 0);
 }
 
 /*
@@ -7052,7 +7126,15 @@ f_deepcopy(argvars, rettv)
     typval_T	*argvars;
     typval_T	*rettv;
 {
-    item_copy(&argvars[0], rettv, TRUE);
+    static int copyID = 0;
+    int		noref = 0;
+
+    if (argvars[1].v_type != VAR_UNKNOWN)
+	noref = get_tv_number(&argvars[1]);
+    if (noref < 0 || noref > 1)
+	EMSG(_(e_invarg));
+    else
+	item_copy(&argvars[0], rettv, TRUE, noref == 0 ? ++copyID : 0);
 }
 
 /*
@@ -10200,7 +10282,7 @@ find_some_match(argvars, rettv, type)
     char_u	*save_cpo;
     long	start = 0;
     long	nth = 1;
-    int		match;
+    int		match = 0;
     list_T	*l = NULL;
     listitem_T	*li = NULL;
     long	idx = 0;
@@ -10275,6 +10357,8 @@ find_some_match(argvars, rettv, type)
 		}
 		vim_free(tofree);
 		str = echo_string(&li->li_tv, &tofree, strbuf);
+		if (str == NULL)
+		    break;
 	    }
 
 	    match = vim_regexec_nl(&regmatch, str, (colnr_T)0);
@@ -11394,7 +11478,7 @@ f_reverse(argvars, rettv)
 	    && !tv_check_lock(l->lv_lock, (char_u *)"reverse()"))
     {
 	li = l->lv_last;
-	l->lv_first = l->lv_last = li;
+	l->lv_first = l->lv_last = NULL;
 	l->lv_len = 0;
 	while (li != NULL)
 	{
@@ -14338,7 +14422,8 @@ set_var(name, tv, copy)
 	}
 	if (function_exists(name))
 	{
-	    EMSG2(_("705: Variable name conflicts with existing function: %s"), name);
+	    EMSG2(_("705: Variable name conflicts with existing function: %s"),
+									name);
 	    return;
 	}
     }
@@ -14520,19 +14605,24 @@ copy_tv(from, to)
 /*
  * Make a copy of an item.
  * Lists and Dictionaries are also copied.  A deep copy if "deep" is set.
- */
-    static void
-item_copy(from, to, deep)
+ * For deepcopy() "copyID" is zero for a full copy or the ID for when a
+ * reference to an already copied list/dict can be used.
+ * Returns FAIL or OK.
+ */
+    static int
+item_copy(from, to, deep, copyID)
     typval_T	*from;
     typval_T	*to;
     int		deep;
+    int		copyID;
 {
     static int	recurse = 0;
+    int		ret = OK;
 
     if (recurse >= DICT_MAXNEST)
     {
 	EMSG(_("E698: variable nested too deep for making a copy"));
-	return;
+	return FAIL;
     }
     ++recurse;
 
@@ -14546,17 +14636,41 @@ item_copy(from, to, deep)
 	case VAR_LIST:
 	    to->v_type = VAR_LIST;
 	    to->v_lock = 0;
-	    to->vval.v_list = list_copy(from->vval.v_list, deep);
+	    if (from->vval.v_list == NULL)
+		to->vval.v_list = NULL;
+	    else if (copyID != 0 && from->vval.v_list->lv_copyID == copyID)
+	    {
+		/* use the copy made earlier */
+		to->vval.v_list = from->vval.v_list->lv_copylist;
+		++to->vval.v_list->lv_refcount;
+	    }
+	    else
+		to->vval.v_list = list_copy(from->vval.v_list, deep, copyID);
+	    if (to->vval.v_list == NULL)
+		ret = FAIL;
 	    break;
 	case VAR_DICT:
 	    to->v_type = VAR_DICT;
 	    to->v_lock = 0;
-	    to->vval.v_dict = dict_copy(from->vval.v_dict, deep);
+	    if (from->vval.v_dict == NULL)
+		to->vval.v_dict = NULL;
+	    else if (copyID != 0 && from->vval.v_dict->dv_copyID == copyID)
+	    {
+		/* use the copy made earlier */
+		to->vval.v_dict = from->vval.v_dict->dv_copydict;
+		++to->vval.v_dict->dv_refcount;
+	    }
+	    else
+		to->vval.v_dict = dict_copy(from->vval.v_dict, deep, copyID);
+	    if (to->vval.v_dict == NULL)
+		ret = FAIL;
 	    break;
 	default:
 	    EMSG2(_(e_intern2), "item_copy()");
+	    ret = FAIL;
     }
     --recurse;
+    return ret;
 }
 
 /*
@@ -14604,31 +14718,34 @@ ex_echo(eap)
 	    }
 	    else if (eap->cmdidx == CMD_echo)
 		msg_puts_attr((char_u *)" ", echo_attr);
-	    for (p = echo_string(&rettv, &tofree, numbuf);
-						   *p != NUL && !got_int; ++p)
-		if (*p == '\n' || *p == '\r' || *p == TAB)
-		{
-		    if (*p != TAB && needclr)
+	    p = echo_string(&rettv, &tofree, numbuf);
+	    if (p != NULL)
+		for ( ; *p != NUL && !got_int; ++p)
+		{
+		    if (*p == '\n' || *p == '\r' || *p == TAB)
 		    {
-			/* remove any text still there from the command */
-			msg_clr_eos();
-			needclr = FALSE;
-		    }
-		    msg_putchar_attr(*p, echo_attr);
-		}
-		else
-		{
-#ifdef FEAT_MBYTE
-		    if (has_mbyte)
-		    {
-			int i = (*mb_ptr2len_check)(p);
-
-			(void)msg_outtrans_len_attr(p, i, echo_attr);
-			p += i - 1;
+			if (*p != TAB && needclr)
+			{
+			    /* remove any text still there from the command */
+			    msg_clr_eos();
+			    needclr = FALSE;
+			}
+			msg_putchar_attr(*p, echo_attr);
 		    }
 		    else
-#endif
-			(void)msg_outtrans_len_attr(p, 1, echo_attr);
+		    {
+#ifdef FEAT_MBYTE
+			if (has_mbyte)
+			{
+			    int i = (*mb_ptr2len_check)(p);
+
+			    (void)msg_outtrans_len_attr(p, i, echo_attr);
+			    p += i - 1;
+			}
+			else
+#endif
+			    (void)msg_outtrans_len_attr(p, 1, echo_attr);
+		    }
 		}
 	    vim_free(tofree);
 	}
@@ -14906,7 +15023,7 @@ ex_function(eap)
 		}
 	    }
 	    else
-		EMSG2(_("E123: Undefined function: %s"), name);
+		emsg_funcname("E123: Undefined function: %s", name);
 	}
 	goto ret_free;
     }
@@ -15019,7 +15136,7 @@ ex_function(eap)
 	    if (fudi.fd_dict != NULL && fudi.fd_newkey == NULL)
 		EMSG(_(e_funcdict));
 	    else if (name != NULL && find_func(name) != NULL)
-		EMSG2(_(e_funcexts), name);
+		emsg_funcname(e_funcexts, name);
 	}
 
 	msg_putchar('\n');	    /* don't overwrite the function name */
@@ -15146,7 +15263,8 @@ ex_function(eap)
 	v = find_var(name, NULL);
 	if (v != NULL && v->di_tv.v_type == VAR_FUNC)
 	{
-	    EMSG2(_("E707: Function name conflicts with variable: %s"), name);
+	    emsg_funcname("E707: Function name conflicts with variable: %s",
+									name);
 	    goto erret;
 	}
 
@@ -15155,12 +15273,12 @@ ex_function(eap)
 	{
 	    if (!eap->forceit)
 	    {
-		EMSG2(_(e_funcexts), name);
+		emsg_funcname(e_funcexts, name);
 		goto erret;
 	    }
 	    if (fp->calls > 0)
 	    {
-		EMSG2(_("E127: Cannot redefine function %s: It is in use"),
+		emsg_funcname("E127: Cannot redefine function %s: It is in use",
 									name);
 		goto erret;
 	    }
@@ -16192,14 +16310,14 @@ discard_pending_return(rettv)
 get_return_cmd(rettv)
     void	*rettv;
 {
-    char_u	*s;
+    char_u	*s = NULL;
     char_u	*tofree = NULL;
     char_u	numbuf[NUMBUFLEN];
 
-    if (rettv == NULL)
+    if (rettv != NULL)
+	s = echo_string((typval_T *)rettv, &tofree, numbuf);
+    if (s == NULL)
 	s = (char_u *)"";
-    else
-	s = echo_string((typval_T *)rettv, &tofree, numbuf);
 
     STRCPY(IObuff, ":return ");
     STRNCPY(IObuff + 8, s, IOSIZE - 8);
@@ -16368,6 +16486,7 @@ write_viminfo_varlist(fp)
     dictitem_T	*this_var;
     int		todo;
     char	*s;
+    char_u	*p;
     char_u	*tofree;
     char_u	numbuf[NUMBUFLEN];
 
@@ -16392,8 +16511,9 @@ write_viminfo_varlist(fp)
 		    default: continue;
 		}
 		fprintf(fp, "!%s\t%s\t", this_var->di_key, s);
-		viminfo_writestring(fp, echo_string(&this_var->di_tv,
-							    &tofree, numbuf));
+		p = echo_string(&this_var->di_tv, &tofree, numbuf);
+		if (p != NULL)
+		    viminfo_writestring(fp, p);
 		vim_free(tofree);
 	    }
 	}
--- a/src/ex_cmds.c
+++ b/src/ex_cmds.c
@@ -3250,32 +3250,57 @@ ex_append(eap)
     char_u	*theline;
     int		did_undo = FALSE;
     linenr_T	lnum = eap->line2;
+    int		indent = 0;
+    char_u	*p;
+    int		vcol;
+    int		empty = (curbuf->b_ml.ml_flags & ML_EMPTY);
 
     if (eap->cmdidx != CMD_append)
 	--lnum;
 
+    /* when the buffer is empty append to line 0 and delete the dummy line */
+    if (empty && lnum == 1)
+	lnum = 0;
+
     State = INSERT;		    /* behave like in Insert mode */
     if (curbuf->b_p_iminsert == B_IMODE_LMAP)
 	State |= LANGMAP;
+
     while (1)
     {
 	msg_scroll = TRUE;
 	need_wait_return = FALSE;
+	if (curbuf->b_p_ai && lnum > 0)
+	    indent = get_indent_lnum(lnum);
 	if (eap->getline == NULL)
 	    theline = getcmdline(
 #ifdef FEAT_EVAL
 		    eap->cstack->cs_looplevel > 0 ? -1 :
 #endif
-		    NUL, 0L, 0);
+		    NUL, 0L, indent);
 	else
 	    theline = eap->getline(
 #ifdef FEAT_EVAL
 		    eap->cstack->cs_looplevel > 0 ? -1 :
 #endif
-		    NUL, eap->cookie, 0);
+		    NUL, eap->cookie, indent);
 	lines_left = Rows - 1;
-	if (theline == NULL || (theline[0] == '.' && theline[1] == NUL)
-		|| (!did_undo && u_save(lnum, lnum + 1) == FAIL))
+	if (theline == NULL)
+	    break;
+
+	/* Look for the "." after automatic indent. */
+	vcol = 0;
+	for (p = theline; indent > vcol; ++p)
+	{
+	    if (*p == ' ')
+		++vcol;
+	    else if (*p == TAB)
+		vcol += 8 - vcol % 8;
+	    else
+		break;
+	}
+	if ((p[0] == '.' && p[1] == NUL)
+			     || (!did_undo && u_save(lnum, lnum + 1) == FAIL))
 	{
 	    vim_free(theline);
 	    break;
@@ -3288,6 +3313,12 @@ ex_append(eap)
 	vim_free(theline);
 	++lnum;
 	msg_didout = TRUE;	/* also scroll for empty line */
+
+	if (empty)
+	{
+	    ml_delete(2L, FALSE);
+	    empty = FALSE;
+	}
     }
     State = NORMAL;
 
@@ -3341,14 +3372,22 @@ ex_z(eap)
     exarg_T	*eap;
 {
     char_u	*x;
-    int		bigness = curwin->w_height - 3;
-    char_u	kind;
+    int		bigness;
+    char_u	*kind;
     int		numbered = FALSE;
     int		minus = 0;
     linenr_T	start, end, curs, i;
     int		j;
     linenr_T	lnum = eap->line2;
 
+    /* Vi compatible: ":z!" uses display height, without a count uses
+     * 'scroll' */
+    if (eap->forceit)
+	bigness = curwin->w_height;
+    else if (firstwin == lastwin)
+	bigness = curwin->w_p_scr * 2;
+    else
+	bigness = curwin->w_height - 3;
     if (bigness < 1)
 	bigness = 1;
 
@@ -3359,8 +3398,11 @@ ex_z(eap)
 	++x;
     }
 
-    kind = *x;
-    if (kind == '-' || kind == '+' || kind == '=' || kind == '^' || kind == '.')
+    kind = x;
+    if (*kind == '-' || *kind == '+' || *kind == '='
+					      || *kind == '^' || *kind == '.')
+	++x;
+    while (*x == '-' || *x == '+')
 	++x;
 
     if (*x != 0)
@@ -3371,15 +3413,23 @@ ex_z(eap)
 	    return;
 	}
 	else
+	{
 	    bigness = atoi((char *)x);
+	    p_window = bigness;
+	}
     }
 
-    switch (kind)
+    /* the number of '-' and '+' multiplies the distance */
+    if (*kind == '-' || *kind == '+')
+	for (x = kind + 1; *x == *kind; ++x)
+	    ;
+
+    switch (*kind)
     {
 	case '-':
-	    start = lnum - bigness;
-	    end = lnum;
-	    curs = lnum;
+	    start = lnum - bigness * (x - kind);
+	    end = start + bigness;
+	    curs = end;
 	    break;
 
 	case '=':
@@ -3403,7 +3453,9 @@ ex_z(eap)
 
 	default:  /* '+' */
 	    start = lnum;
-	    end = lnum + bigness;
+	    if (*kind == '+')
+		start += bigness * (x - kind - 1);
+	    end = start + bigness;
 	    curs = end;
 	    break;
     }
@@ -3526,15 +3578,15 @@ do_sub(eap)
     int		which_pat;
     char_u	*cmd;
     int		save_State;
-    linenr_T	first_line = 0;	/* first changed line */
-    linenr_T	last_line= 0;	/* below last changed line AFTER the
+    linenr_T	first_line = 0;		/* first changed line */
+    linenr_T	last_line= 0;		/* below last changed line AFTER the
 					 * change */
     linenr_T	old_line_count = curbuf->b_ml.ml_line_count;
     linenr_T	line2;
-    long	nmatch;		/* number of lines in match */
-    linenr_T	sub_firstlnum;	/* nr of first sub line */
-    char_u	*sub_firstline;	/* allocated copy of first sub line */
-    int		endcolumn;	/* put cursor in last column when done */
+    long	nmatch;			/* number of lines in match */
+    linenr_T	sub_firstlnum;		/* nr of first sub line */
+    char_u	*sub_firstline;		/* allocated copy of first sub line */
+    int		endcolumn = FALSE;	/* cursor in last column when done */
 
     cmd = eap->arg;
     if (!global_busy)
--- a/src/memline.c
+++ b/src/memline.c
@@ -609,6 +609,7 @@ ml_close(buf, del_file)
  * Close all existing memlines and memfiles.
  * Only used when exiting.
  * When 'del_file' is TRUE, delete the memfiles.
+ * But don't delete files that were ":preserve"d when we are POSIX compatible.
  */
     void
 ml_close_all(del_file)
@@ -617,7 +618,8 @@ ml_close_all(del_file)
     buf_T	*buf;
 
     for (buf = firstbuf; buf != NULL; buf = buf->b_next)
-	ml_close(buf, del_file);
+	ml_close(buf, del_file && ((buf->b_flags & BF_PRESERVED) == 0
+				 || vim_strchr(p_cpo, CPO_PRESERVE) == NULL));
 #ifdef TEMPDIRNAMES
     vim_deltempdir();	    /* delete created temp directory */
 #endif