patch 8.1.1372: when evaluating 'statusline' the current window is unknown

Problem:    When evaluating 'statusline' the current window is unknown.
            (Daniel Hahler)
Solution:   Set "g:actual_curwin" for %{} items.  Set "g:statusline_winid"
            when evaluationg %!. (closes #4406, closes #3299)
diff --git a/src/buffer.c b/src/buffer.c
index 0e45f0e..425c914 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -3893,7 +3893,8 @@
     char_u	base;
     char_u	opt;
 #define TMPLEN 70
-    char_u	tmp[TMPLEN];
+    char_u	buf_tmp[TMPLEN];
+    char_u	win_tmp[TMPLEN];
     char_u	*usefmt = fmt;
     struct stl_hlrec *sp;
     int		save_must_redraw = must_redraw;
@@ -3906,9 +3907,17 @@
      */
     if (fmt[0] == '%' && fmt[1] == '!')
     {
+	typval_T	tv;
+
+	tv.v_type = VAR_NUMBER;
+	tv.vval.v_number = wp->w_id;
+	set_var((char_u *)"g:statusline_winid", &tv, FALSE);
+
 	usefmt = eval_to_string_safe(fmt + 2, NULL, use_sandbox);
 	if (usefmt == NULL)
 	    usefmt = fmt;
+
+	do_unlet((char_u *)"g:statusline_winid", TRUE);
     }
 #endif
 
@@ -4225,8 +4234,11 @@
 	    p = t;
 
 #ifdef FEAT_EVAL
-	    vim_snprintf((char *)tmp, sizeof(tmp), "%d", curbuf->b_fnum);
-	    set_internal_string_var((char_u *)"g:actual_curbuf", tmp);
+	    vim_snprintf((char *)buf_tmp, sizeof(buf_tmp),
+							 "%d", curbuf->b_fnum);
+	    set_internal_string_var((char_u *)"g:actual_curbuf", buf_tmp);
+	    vim_snprintf((char *)win_tmp, sizeof(win_tmp), "%d", curwin->w_id);
+	    set_internal_string_var((char_u *)"g:actual_curwin", win_tmp);
 
 	    save_curbuf = curbuf;
 	    save_curwin = curwin;
@@ -4238,6 +4250,7 @@
 	    curwin = save_curwin;
 	    curbuf = save_curbuf;
 	    do_unlet((char_u *)"g:actual_curbuf", TRUE);
+	    do_unlet((char_u *)"g:actual_curwin", TRUE);
 
 	    if (str != NULL && *str != 0)
 	    {
@@ -4290,21 +4303,21 @@
 	    break;
 
 	case STL_ALTPERCENT:
-	    str = tmp;
+	    str = buf_tmp;
 	    get_rel_pos(wp, str, TMPLEN);
 	    break;
 
 	case STL_ARGLISTSTAT:
 	    fillable = FALSE;
-	    tmp[0] = 0;
-	    if (append_arg_number(wp, tmp, (int)sizeof(tmp), FALSE))
-		str = tmp;
+	    buf_tmp[0] = 0;
+	    if (append_arg_number(wp, buf_tmp, (int)sizeof(buf_tmp), FALSE))
+		str = buf_tmp;
 	    break;
 
 	case STL_KEYMAP:
 	    fillable = FALSE;
-	    if (get_keymap_str(wp, (char_u *)"<%s>", tmp, TMPLEN))
-		str = tmp;
+	    if (get_keymap_str(wp, (char_u *)"<%s>", buf_tmp, TMPLEN))
+		str = buf_tmp;
 	    break;
 	case STL_PAGENUM:
 #if defined(FEAT_PRINTER) || defined(FEAT_GUI_TABLINE)
@@ -4360,9 +4373,9 @@
 	    if (*wp->w_buffer->b_p_ft != NUL
 		    && STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 3)
 	    {
-		vim_snprintf((char *)tmp, sizeof(tmp), "[%s]",
+		vim_snprintf((char *)buf_tmp, sizeof(buf_tmp), "[%s]",
 							wp->w_buffer->b_p_ft);
-		str = tmp;
+		str = buf_tmp;
 	    }
 	    break;
 
@@ -4371,11 +4384,11 @@
 	    if (*wp->w_buffer->b_p_ft != NUL
 		    && STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 2)
 	    {
-		vim_snprintf((char *)tmp, sizeof(tmp), ",%s",
+		vim_snprintf((char *)buf_tmp, sizeof(buf_tmp), ",%s",
 							wp->w_buffer->b_p_ft);
-		for (t = tmp; *t != 0; t++)
+		for (t = buf_tmp; *t != 0; t++)
 		    *t = TOUPPER_LOC(*t);
-		str = tmp;
+		str = buf_tmp;
 	    }
 	    break;
 
diff --git a/src/testdir/test_statusline.vim b/src/testdir/test_statusline.vim
index b86340a..e1830d0 100644
--- a/src/testdir/test_statusline.vim
+++ b/src/testdir/test_statusline.vim
@@ -29,7 +29,9 @@
 
 " Function used to display syntax group.
 func SyntaxItem()
-  return synIDattr(synID(line("."),col("."),1),"name")
+  call assert_equal(s:expected_curbuf, g:actual_curbuf)
+  call assert_equal(s:expected_curwin, g:actual_curwin)
+  return synIDattr(synID(line("."), col("."),1), "name")
 endfunc
 
 func Test_caught_error_in_statusline()
@@ -218,6 +220,8 @@
 
   "%{: Evaluate expression between '%{' and '}' and substitute result.
   syntax on
+  let s:expected_curbuf = string(bufnr(''))
+  let s:expected_curwin = string(win_getid())
   set statusline=%{SyntaxItem()}
   call assert_match('^vimNumber\s*$', s:get_statusline())
   s/^/"/
@@ -332,6 +336,23 @@
   set statusline=%!2*3+1
   call assert_match('7\s*$', s:get_statusline())
 
+  func GetNested()
+    call assert_equal(string(win_getid()), g:actual_curwin)
+    call assert_equal(string(bufnr('')), g:actual_curbuf)
+    return 'nested'
+  endfunc
+  func GetStatusLine()
+    call assert_equal(win_getid(), g:statusline_winid)
+    return 'the %{GetNested()} line'
+  endfunc
+  set statusline=%!GetStatusLine()
+  call assert_match('the nested line', s:get_statusline())
+  call assert_false(exists('g:actual_curwin'))
+  call assert_false(exists('g:actual_curbuf'))
+  call assert_false(exists('g:statusline_winid'))
+  delfunc GetNested
+  delfunc GetStatusLine
+
   " Check statusline in current and non-current window
   " with the 'fillchars' option.
   set fillchars=stl:^,stlnc:=,vert:\|,fold:-,diff:-
diff --git a/src/version.c b/src/version.c
index c2ee827..7bac657 100644
--- a/src/version.c
+++ b/src/version.c
@@ -768,6 +768,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1372,
+/**/
     1371,
 /**/
     1370,