xref: /vim-8.2.3635/src/buffer.c (revision 3ad69532)
1edf3f97aSBram Moolenaar /* vi:set ts=8 sts=4 sw=4 noet:
2071d4279SBram Moolenaar  *
3071d4279SBram Moolenaar  * VIM - Vi IMproved	by Bram Moolenaar
4071d4279SBram Moolenaar  *
5071d4279SBram Moolenaar  * Do ":help uganda"  in Vim to read copying and usage conditions.
6071d4279SBram Moolenaar  * Do ":help credits" in Vim to see a list of people who contributed.
7071d4279SBram Moolenaar  * See README.txt for an overview of the Vim source code.
8071d4279SBram Moolenaar  */
9071d4279SBram Moolenaar 
10071d4279SBram Moolenaar /*
11071d4279SBram Moolenaar  * buffer.c: functions for dealing with the buffer structure
12071d4279SBram Moolenaar  */
13071d4279SBram Moolenaar 
14071d4279SBram Moolenaar /*
15071d4279SBram Moolenaar  * The buffer list is a double linked list of all buffers.
16071d4279SBram Moolenaar  * Each buffer can be in one of these states:
17071d4279SBram Moolenaar  * never loaded: BF_NEVERLOADED is set, only the file name is valid
18071d4279SBram Moolenaar  *   not loaded: b_ml.ml_mfp == NULL, no memfile allocated
19071d4279SBram Moolenaar  *	 hidden: b_nwindows == 0, loaded but not displayed in a window
20071d4279SBram Moolenaar  *	 normal: loaded and displayed in a window
21071d4279SBram Moolenaar  *
22071d4279SBram Moolenaar  * Instead of storing file names all over the place, each file name is
23071d4279SBram Moolenaar  * stored in the buffer list. It can be referenced by a number.
24071d4279SBram Moolenaar  *
25071d4279SBram Moolenaar  * The current implementation remembers all file names ever used.
26071d4279SBram Moolenaar  */
27071d4279SBram Moolenaar 
28071d4279SBram Moolenaar #include "vim.h"
29071d4279SBram Moolenaar 
3030e3de21Sshadmansaleh 
3130e3de21Sshadmansaleh #ifdef FEAT_EVAL
3230e3de21Sshadmansaleh // Determines how deeply nested %{} blocks will be evaluated in statusline.
3330e3de21Sshadmansaleh # define MAX_STL_EVAL_DEPTH 100
3430e3de21Sshadmansaleh #endif
3530e3de21Sshadmansaleh 
365843f5f3SBram Moolenaar static void	enter_buffer(buf_T *buf);
375843f5f3SBram Moolenaar static void	buflist_getfpos(void);
38f28dbceaSBram Moolenaar static char_u	*buflist_match(regmatch_T *rmp, buf_T *buf, int ignore_case);
39f28dbceaSBram Moolenaar static char_u	*fname_match(regmatch_T *rmp, char_u *name, int ignore_case);
40071d4279SBram Moolenaar #ifdef UNIX
418767f52fSBram Moolenaar static buf_T	*buflist_findname_stat(char_u *ffname, stat_T *st);
428767f52fSBram Moolenaar static int	otherfile_buf(buf_T *buf, char_u *ffname, stat_T *stp);
438767f52fSBram Moolenaar static int	buf_same_ino(buf_T *buf, stat_T *stp);
44071d4279SBram Moolenaar #else
45f28dbceaSBram Moolenaar static int	otherfile_buf(buf_T *buf, char_u *ffname);
46071d4279SBram Moolenaar #endif
47071d4279SBram Moolenaar #ifdef FEAT_TITLE
4884a93085SBram Moolenaar static int	value_changed(char_u *str, char_u **last);
49071d4279SBram Moolenaar #endif
50f28dbceaSBram Moolenaar static int	append_arg_number(win_T *wp, char_u *buf, int buflen, int add_file);
51f28dbceaSBram Moolenaar static void	free_buffer(buf_T *);
52f28dbceaSBram Moolenaar static void	free_buffer_stuff(buf_T *buf, int free_options);
53f28dbceaSBram Moolenaar static void	clear_wininfo(buf_T *buf);
54071d4279SBram Moolenaar 
55071d4279SBram Moolenaar #ifdef UNIX
56071d4279SBram Moolenaar # define dev_T dev_t
57071d4279SBram Moolenaar #else
58071d4279SBram Moolenaar # define dev_T unsigned
59071d4279SBram Moolenaar #endif
60071d4279SBram Moolenaar 
6100d253e2SBram Moolenaar #define FOR_ALL_BUFS_FROM_LAST(buf) \
6200d253e2SBram Moolenaar     for ((buf) = lastbuf; (buf) != NULL; (buf) = (buf)->b_prev)
6300d253e2SBram Moolenaar 
644033c55eSBram Moolenaar #if defined(FEAT_QUICKFIX)
657fd73200SBram Moolenaar static char *msg_loclist = N_("[Location List]");
667fd73200SBram Moolenaar static char *msg_qflist = N_("[Quickfix List]");
677fd73200SBram Moolenaar #endif
6842ec6565SBram Moolenaar static char *e_auabort = N_("E855: Autocommands caused command to abort");
697fd73200SBram Moolenaar 
7000b0d6d8SBram Moolenaar // Number of times free_buffer() was called.
71b25f9a97SBram Moolenaar static int	buf_free_count = 0;
72b25f9a97SBram Moolenaar 
7300b0d6d8SBram Moolenaar static int	top_file_num = 1;	// highest file number
7400b0d6d8SBram Moolenaar static garray_T buf_reuse = GA_EMPTY;	// file numbers to recycle
7500b0d6d8SBram Moolenaar 
7600b0d6d8SBram Moolenaar /*
7700b0d6d8SBram Moolenaar  * Return the highest possible buffer number.
7800b0d6d8SBram Moolenaar  */
7900b0d6d8SBram Moolenaar     int
get_highest_fnum(void)8000b0d6d8SBram Moolenaar get_highest_fnum(void)
8100b0d6d8SBram Moolenaar {
8200b0d6d8SBram Moolenaar     return top_file_num - 1;
8300b0d6d8SBram Moolenaar }
8400b0d6d8SBram Moolenaar 
85c024b466SBram Moolenaar /*
86c024b466SBram Moolenaar  * Read data from buffer for retrying.
87c024b466SBram Moolenaar  */
88f71d7b9eSBram Moolenaar     static int
read_buffer(int read_stdin,exarg_T * eap,int flags)89f71d7b9eSBram Moolenaar read_buffer(
90c667da51SBram Moolenaar     int		read_stdin,	    // read file from stdin, otherwise fifo
91c667da51SBram Moolenaar     exarg_T	*eap,		    // for forced 'ff' and 'fenc' or NULL
92c667da51SBram Moolenaar     int		flags)		    // extra flags for readfile()
93f71d7b9eSBram Moolenaar {
94f71d7b9eSBram Moolenaar     int		retval = OK;
95f71d7b9eSBram Moolenaar     linenr_T	line_count;
96f71d7b9eSBram Moolenaar 
97c5935a85SBram Moolenaar     // Read from the buffer which the text is already filled in and append at
98c5935a85SBram Moolenaar     // the end.  This makes it possible to retry when 'fileformat' or
99c5935a85SBram Moolenaar     // 'fileencoding' was guessed wrong.
100f71d7b9eSBram Moolenaar     line_count = curbuf->b_ml.ml_line_count;
101f71d7b9eSBram Moolenaar     retval = readfile(
102f71d7b9eSBram Moolenaar 	    read_stdin ? NULL : curbuf->b_ffname,
103f71d7b9eSBram Moolenaar 	    read_stdin ? NULL : curbuf->b_fname,
104dfa5e464S=?UTF-8?q?Dundar=20G=C3=B6c?= 	    line_count, (linenr_T)0, (linenr_T)MAXLNUM, eap,
105f71d7b9eSBram Moolenaar 	    flags | READ_BUFFER);
106f71d7b9eSBram Moolenaar     if (retval == OK)
107f71d7b9eSBram Moolenaar     {
108c667da51SBram Moolenaar 	// Delete the binary lines.
109f71d7b9eSBram Moolenaar 	while (--line_count >= 0)
110ca70c07bSBram Moolenaar 	    ml_delete((linenr_T)1);
111f71d7b9eSBram Moolenaar     }
112f71d7b9eSBram Moolenaar     else
113f71d7b9eSBram Moolenaar     {
114c667da51SBram Moolenaar 	// Delete the converted lines.
115f71d7b9eSBram Moolenaar 	while (curbuf->b_ml.ml_line_count > line_count)
116ca70c07bSBram Moolenaar 	    ml_delete(line_count);
117f71d7b9eSBram Moolenaar     }
118c667da51SBram Moolenaar     // Put the cursor on the first line.
119f71d7b9eSBram Moolenaar     curwin->w_cursor.lnum = 1;
120f71d7b9eSBram Moolenaar     curwin->w_cursor.col = 0;
121f71d7b9eSBram Moolenaar 
122f71d7b9eSBram Moolenaar     if (read_stdin)
123f71d7b9eSBram Moolenaar     {
124c667da51SBram Moolenaar 	// Set or reset 'modified' before executing autocommands, so that
125c667da51SBram Moolenaar 	// it can be changed there.
126b5aedf3eSBram Moolenaar 	if (!readonlymode && !BUFEMPTY())
127f71d7b9eSBram Moolenaar 	    changed();
128e13b9afeSBram Moolenaar 	else if (retval == OK)
129c024b466SBram Moolenaar 	    unchanged(curbuf, FALSE, TRUE);
130f71d7b9eSBram Moolenaar 
131e13b9afeSBram Moolenaar 	if (retval == OK)
132e13b9afeSBram Moolenaar 	{
133f71d7b9eSBram Moolenaar #ifdef FEAT_EVAL
134f71d7b9eSBram Moolenaar 	    apply_autocmds_retval(EVENT_STDINREADPOST, NULL, NULL, FALSE,
135f71d7b9eSBram Moolenaar 							      curbuf, &retval);
136f71d7b9eSBram Moolenaar #else
137f71d7b9eSBram Moolenaar 	    apply_autocmds(EVENT_STDINREADPOST, NULL, NULL, FALSE, curbuf);
138f71d7b9eSBram Moolenaar #endif
139e13b9afeSBram Moolenaar 	}
140f71d7b9eSBram Moolenaar     }
141f71d7b9eSBram Moolenaar     return retval;
142f71d7b9eSBram Moolenaar }
143f71d7b9eSBram Moolenaar 
144071d4279SBram Moolenaar /*
1455b8cfedfSBram Moolenaar  * Ensure buffer "buf" is loaded.  Does not trigger the swap-exists action.
1465b8cfedfSBram Moolenaar  */
1475b8cfedfSBram Moolenaar     void
buffer_ensure_loaded(buf_T * buf)1485b8cfedfSBram Moolenaar buffer_ensure_loaded(buf_T *buf)
1495b8cfedfSBram Moolenaar {
1505b8cfedfSBram Moolenaar     if (buf->b_ml.ml_mfp == NULL)
1515b8cfedfSBram Moolenaar     {
1525b8cfedfSBram Moolenaar 	aco_save_T	aco;
1535b8cfedfSBram Moolenaar 
1545b8cfedfSBram Moolenaar 	aucmd_prepbuf(&aco, buf);
1555b8cfedfSBram Moolenaar 	swap_exists_action = SEA_NONE;
1565b8cfedfSBram Moolenaar 	open_buffer(FALSE, NULL, 0);
1575b8cfedfSBram Moolenaar 	aucmd_restbuf(&aco);
1585b8cfedfSBram Moolenaar     }
1595b8cfedfSBram Moolenaar }
1605b8cfedfSBram Moolenaar 
1615b8cfedfSBram Moolenaar /*
16255debbe3SBram Moolenaar  * Open current buffer, that is: open the memfile and read the file into
16355debbe3SBram Moolenaar  * memory.
16455debbe3SBram Moolenaar  * Return FAIL for failure, OK otherwise.
165071d4279SBram Moolenaar  */
166071d4279SBram Moolenaar     int
open_buffer(int read_stdin,exarg_T * eap,int flags)1677454a06eSBram Moolenaar open_buffer(
168c667da51SBram Moolenaar     int		read_stdin,	    // read file from stdin
169c667da51SBram Moolenaar     exarg_T	*eap,		    // for forced 'ff' and 'fenc' or NULL
170c667da51SBram Moolenaar     int		flags)		    // extra flags for readfile()
171071d4279SBram Moolenaar {
172071d4279SBram Moolenaar     int		retval = OK;
1737c0a2f36SBram Moolenaar     bufref_T	old_curbuf;
1746d47df7cSBram Moolenaar #ifdef FEAT_SYN_HL
1756d47df7cSBram Moolenaar     long	old_tw = curbuf->b_p_tw;
1766d47df7cSBram Moolenaar #endif
177f71d7b9eSBram Moolenaar     int		read_fifo = FALSE;
178071d4279SBram Moolenaar 
179c5935a85SBram Moolenaar     // The 'readonly' flag is only set when BF_NEVERLOADED is being reset.
180c5935a85SBram Moolenaar     // When re-entering the same buffer, it should not change, because the
181c5935a85SBram Moolenaar     // user may have reset the flag by hand.
182071d4279SBram Moolenaar     if (readonlymode && curbuf->b_ffname != NULL
183071d4279SBram Moolenaar 					&& (curbuf->b_flags & BF_NEVERLOADED))
184071d4279SBram Moolenaar 	curbuf->b_p_ro = TRUE;
185071d4279SBram Moolenaar 
1864770d09aSBram Moolenaar     if (ml_open(curbuf) == FAIL)
187071d4279SBram Moolenaar     {
188c5935a85SBram Moolenaar 	// There MUST be a memfile, otherwise we can't do anything
189c5935a85SBram Moolenaar 	// If we can't create one for the current buffer, take another buffer
190a6e8f888SBram Moolenaar 	close_buffer(NULL, curbuf, 0, FALSE, FALSE);
19129323590SBram Moolenaar 	FOR_ALL_BUFFERS(curbuf)
192071d4279SBram Moolenaar 	    if (curbuf->b_ml.ml_mfp != NULL)
193071d4279SBram Moolenaar 		break;
194c5935a85SBram Moolenaar 	// If there is no memfile at all, exit.
195c5935a85SBram Moolenaar 	// This is OK, since there are no changes to lose.
196071d4279SBram Moolenaar 	if (curbuf == NULL)
197071d4279SBram Moolenaar 	{
198f9e3e09fSBram Moolenaar 	    emsg(_("E82: Cannot allocate any buffer, exiting..."));
1996f10c70bSBram Moolenaar 
2006f10c70bSBram Moolenaar 	    // Don't try to do any saving, with "curbuf" NULL almost nothing
2016f10c70bSBram Moolenaar 	    // will work.
2026f10c70bSBram Moolenaar 	    v_dying = 2;
203071d4279SBram Moolenaar 	    getout(2);
204071d4279SBram Moolenaar 	}
2056f10c70bSBram Moolenaar 
206f9e3e09fSBram Moolenaar 	emsg(_("E83: Cannot allocate buffer, using other one..."));
207071d4279SBram Moolenaar 	enter_buffer(curbuf);
2086d47df7cSBram Moolenaar #ifdef FEAT_SYN_HL
2096d47df7cSBram Moolenaar 	if (old_tw != curbuf->b_p_tw)
2106d47df7cSBram Moolenaar 	    check_colorcolumn(curwin);
2116d47df7cSBram Moolenaar #endif
212071d4279SBram Moolenaar 	return FAIL;
213071d4279SBram Moolenaar     }
214071d4279SBram Moolenaar 
215c667da51SBram Moolenaar     // The autocommands in readfile() may change the buffer, but only AFTER
216c667da51SBram Moolenaar     // reading the file.
2177c0a2f36SBram Moolenaar     set_bufref(&old_curbuf, curbuf);
218071d4279SBram Moolenaar     modified_was_set = FALSE;
219071d4279SBram Moolenaar 
220c667da51SBram Moolenaar     // mark cursor position as being invalid
22189c0ea4eSBram Moolenaar     curwin->w_valid = 0;
222071d4279SBram Moolenaar 
223071d4279SBram Moolenaar     if (curbuf->b_ffname != NULL
224071d4279SBram Moolenaar #ifdef FEAT_NETBEANS_INTG
225071d4279SBram Moolenaar 	    && netbeansReadFile
226071d4279SBram Moolenaar #endif
227071d4279SBram Moolenaar        )
228071d4279SBram Moolenaar     {
229426dd021SBram Moolenaar 	int old_msg_silent = msg_silent;
230f71d7b9eSBram Moolenaar #ifdef UNIX
231f71d7b9eSBram Moolenaar 	int save_bin = curbuf->b_p_bin;
232f71d7b9eSBram Moolenaar 	int perm;
233f71d7b9eSBram Moolenaar #endif
234071d4279SBram Moolenaar #ifdef FEAT_NETBEANS_INTG
235071d4279SBram Moolenaar 	int oldFire = netbeansFireChanges;
236071d4279SBram Moolenaar 
237071d4279SBram Moolenaar 	netbeansFireChanges = 0;
238071d4279SBram Moolenaar #endif
239f71d7b9eSBram Moolenaar #ifdef UNIX
240f71d7b9eSBram Moolenaar 	perm = mch_getperm(curbuf->b_ffname);
241d569bb02SBram Moolenaar 	if (perm >= 0 && (S_ISFIFO(perm)
242f71d7b9eSBram Moolenaar 		      || S_ISSOCK(perm)
243f04507d1SBram Moolenaar # ifdef OPEN_CHR_FILES
244f04507d1SBram Moolenaar 		      || (S_ISCHR(perm) && is_dev_fd_file(curbuf->b_ffname))
245f04507d1SBram Moolenaar # endif
246f71d7b9eSBram Moolenaar 		    ))
247f71d7b9eSBram Moolenaar 		read_fifo = TRUE;
248f71d7b9eSBram Moolenaar 	if (read_fifo)
249f71d7b9eSBram Moolenaar 	    curbuf->b_p_bin = TRUE;
250f71d7b9eSBram Moolenaar #endif
251426dd021SBram Moolenaar 	if (shortmess(SHM_FILEINFO))
252426dd021SBram Moolenaar 	    msg_silent = 1;
253071d4279SBram Moolenaar 	retval = readfile(curbuf->b_ffname, curbuf->b_fname,
25459f931efSBram Moolenaar 		  (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, eap,
255f71d7b9eSBram Moolenaar 		  flags | READ_NEW | (read_fifo ? READ_FIFO : 0));
256f71d7b9eSBram Moolenaar #ifdef UNIX
257f71d7b9eSBram Moolenaar 	if (read_fifo)
258f71d7b9eSBram Moolenaar 	{
259f71d7b9eSBram Moolenaar 	    curbuf->b_p_bin = save_bin;
260f71d7b9eSBram Moolenaar 	    if (retval == OK)
261f71d7b9eSBram Moolenaar 		retval = read_buffer(FALSE, eap, flags);
262f71d7b9eSBram Moolenaar 	}
263f71d7b9eSBram Moolenaar #endif
264426dd021SBram Moolenaar 	msg_silent = old_msg_silent;
265071d4279SBram Moolenaar #ifdef FEAT_NETBEANS_INTG
266071d4279SBram Moolenaar 	netbeansFireChanges = oldFire;
267071d4279SBram Moolenaar #endif
268c667da51SBram Moolenaar 	// Help buffer is filtered.
269d28cc3f5SBram Moolenaar 	if (bt_help(curbuf))
270071d4279SBram Moolenaar 	    fix_help_buffer();
271071d4279SBram Moolenaar     }
272071d4279SBram Moolenaar     else if (read_stdin)
273071d4279SBram Moolenaar     {
274071d4279SBram Moolenaar 	int	save_bin = curbuf->b_p_bin;
275071d4279SBram Moolenaar 
276c5935a85SBram Moolenaar 	// First read the text in binary mode into the buffer.
277c5935a85SBram Moolenaar 	// Then read from that same buffer and append at the end.  This makes
278c5935a85SBram Moolenaar 	// it possible to retry when 'fileformat' or 'fileencoding' was
279c5935a85SBram Moolenaar 	// guessed wrong.
280071d4279SBram Moolenaar 	curbuf->b_p_bin = TRUE;
281071d4279SBram Moolenaar 	retval = readfile(NULL, NULL, (linenr_T)0,
28259f931efSBram Moolenaar 		  (linenr_T)0, (linenr_T)MAXLNUM, NULL,
28359f931efSBram Moolenaar 		  flags | (READ_NEW + READ_STDIN));
284071d4279SBram Moolenaar 	curbuf->b_p_bin = save_bin;
285071d4279SBram Moolenaar 	if (retval == OK)
286f71d7b9eSBram Moolenaar 	    retval = read_buffer(TRUE, eap, flags);
287071d4279SBram Moolenaar     }
288071d4279SBram Moolenaar 
289c667da51SBram Moolenaar     // if first time loading this buffer, init b_chartab[]
290071d4279SBram Moolenaar     if (curbuf->b_flags & BF_NEVERLOADED)
2916bcbcc59SBram Moolenaar     {
292071d4279SBram Moolenaar 	(void)buf_init_chartab(curbuf, FALSE);
293dce7c91dSBram Moolenaar #ifdef FEAT_CINDENT
2946bcbcc59SBram Moolenaar 	parse_cino(curbuf);
295dce7c91dSBram Moolenaar #endif
2966bcbcc59SBram Moolenaar     }
297071d4279SBram Moolenaar 
298c5935a85SBram Moolenaar     // Set/reset the Changed flag first, autocmds may change the buffer.
299c5935a85SBram Moolenaar     // Apply the automatic commands, before processing the modelines.
300c5935a85SBram Moolenaar     // So the modelines have priority over autocommands.
301c5935a85SBram Moolenaar     //
302c667da51SBram Moolenaar     // When reading stdin, the buffer contents always needs writing, so set
303c667da51SBram Moolenaar     // the changed flag.  Unless in readonly mode: "ls | gview -".
304c667da51SBram Moolenaar     // When interrupted and 'cpoptions' contains 'i' set changed flag.
305512e6b83SBram Moolenaar     if ((got_int && vim_strchr(p_cpo, CPO_INTMOD) != NULL)
306c667da51SBram Moolenaar 		|| modified_was_set	// ":set modified" used in autocmd
307071d4279SBram Moolenaar #ifdef FEAT_EVAL
308071d4279SBram Moolenaar 		|| (aborting() && vim_strchr(p_cpo, CPO_INTMOD) != NULL)
309071d4279SBram Moolenaar #endif
310512e6b83SBram Moolenaar        )
311071d4279SBram Moolenaar 	changed();
312e13b9afeSBram Moolenaar     else if (retval == OK && !read_stdin && !read_fifo)
313c024b466SBram Moolenaar 	unchanged(curbuf, FALSE, TRUE);
314c667da51SBram Moolenaar     save_file_ff(curbuf);		// keep this fileformat
315071d4279SBram Moolenaar 
316c667da51SBram Moolenaar     // Set last_changedtick to avoid triggering a TextChanged autocommand right
317c667da51SBram Moolenaar     // after it was added.
3188c64a36eSBram Moolenaar     curbuf->b_last_changedtick = CHANGEDTICK(curbuf);
319db3b4464SChristian Brabandt     curbuf->b_last_changedtick_i = CHANGEDTICK(curbuf);
3208c64a36eSBram Moolenaar     curbuf->b_last_changedtick_pum = CHANGEDTICK(curbuf);
3218c64a36eSBram Moolenaar 
322c667da51SBram Moolenaar     // require "!" to overwrite the file, because it wasn't read completely
323071d4279SBram Moolenaar #ifdef FEAT_EVAL
324071d4279SBram Moolenaar     if (aborting())
325071d4279SBram Moolenaar #else
326071d4279SBram Moolenaar     if (got_int)
327071d4279SBram Moolenaar #endif
328071d4279SBram Moolenaar 	curbuf->b_flags |= BF_READERR;
329071d4279SBram Moolenaar 
3305eb86f91SBram Moolenaar #ifdef FEAT_FOLDING
331c667da51SBram Moolenaar     // Need to update automatic folding.  Do this before the autocommands,
332c667da51SBram Moolenaar     // they may use the fold info.
3335eb86f91SBram Moolenaar     foldUpdateAll(curwin);
3345eb86f91SBram Moolenaar #endif
3355eb86f91SBram Moolenaar 
336c667da51SBram Moolenaar     // need to set w_topline, unless some autocommand already did that.
337071d4279SBram Moolenaar     if (!(curwin->w_valid & VALID_TOPLINE))
338071d4279SBram Moolenaar     {
339071d4279SBram Moolenaar 	curwin->w_topline = 1;
340071d4279SBram Moolenaar #ifdef FEAT_DIFF
341071d4279SBram Moolenaar 	curwin->w_topfill = 0;
342071d4279SBram Moolenaar #endif
343071d4279SBram Moolenaar     }
344071d4279SBram Moolenaar #ifdef FEAT_EVAL
345071d4279SBram Moolenaar     apply_autocmds_retval(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf, &retval);
346071d4279SBram Moolenaar #else
347071d4279SBram Moolenaar     apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
348071d4279SBram Moolenaar #endif
349071d4279SBram Moolenaar 
350e13b9afeSBram Moolenaar     if (retval == OK)
351071d4279SBram Moolenaar     {
352c5935a85SBram Moolenaar 	// The autocommands may have changed the current buffer.  Apply the
353c5935a85SBram Moolenaar 	// modelines to the correct buffer, if it still exists and is loaded.
3547c0a2f36SBram Moolenaar 	if (bufref_valid(&old_curbuf) && old_curbuf.br_buf->b_ml.ml_mfp != NULL)
355071d4279SBram Moolenaar 	{
356071d4279SBram Moolenaar 	    aco_save_T	aco;
357071d4279SBram Moolenaar 
358c667da51SBram Moolenaar 	    // Go to the buffer that was opened.
3597c0a2f36SBram Moolenaar 	    aucmd_prepbuf(&aco, old_curbuf.br_buf);
360a3227e2bSBram Moolenaar 	    do_modelines(0);
361071d4279SBram Moolenaar 	    curbuf->b_flags &= ~(BF_CHECK_RO | BF_NEVERLOADED);
362071d4279SBram Moolenaar 
3631d30fde3SBram Moolenaar 	    if ((flags & READ_NOWINENTER) == 0)
364071d4279SBram Moolenaar #ifdef FEAT_EVAL
3651d30fde3SBram Moolenaar 		apply_autocmds_retval(EVENT_BUFWINENTER, NULL, NULL, FALSE,
3661d30fde3SBram Moolenaar 							      curbuf, &retval);
367071d4279SBram Moolenaar #else
368071d4279SBram Moolenaar 		apply_autocmds(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf);
369071d4279SBram Moolenaar #endif
370071d4279SBram Moolenaar 
371c667da51SBram Moolenaar 	    // restore curwin/curbuf and a few other things
372071d4279SBram Moolenaar 	    aucmd_restbuf(&aco);
373071d4279SBram Moolenaar 	}
374071d4279SBram Moolenaar     }
375071d4279SBram Moolenaar 
376071d4279SBram Moolenaar     return retval;
377071d4279SBram Moolenaar }
378071d4279SBram Moolenaar 
379071d4279SBram Moolenaar /*
380b25f9a97SBram Moolenaar  * Store "buf" in "bufref" and set the free count.
381b25f9a97SBram Moolenaar  */
382b25f9a97SBram Moolenaar     void
set_bufref(bufref_T * bufref,buf_T * buf)383b25f9a97SBram Moolenaar set_bufref(bufref_T *bufref, buf_T *buf)
384b25f9a97SBram Moolenaar {
385b25f9a97SBram Moolenaar     bufref->br_buf = buf;
386fadacf01SBram Moolenaar     bufref->br_fnum = buf == NULL ? 0 : buf->b_fnum;
387b25f9a97SBram Moolenaar     bufref->br_buf_free_count = buf_free_count;
388b25f9a97SBram Moolenaar }
389b25f9a97SBram Moolenaar 
390b25f9a97SBram Moolenaar /*
39145e5fd13SBram Moolenaar  * Return TRUE if "bufref->br_buf" points to the same buffer as when
39245e5fd13SBram Moolenaar  * set_bufref() was called and it is a valid buffer.
393b25f9a97SBram Moolenaar  * Only goes through the buffer list if buf_free_count changed.
39445e5fd13SBram Moolenaar  * Also checks if b_fnum is still the same, a :bwipe followed by :new might get
39545e5fd13SBram Moolenaar  * the same allocated memory, but it's a different buffer.
396b25f9a97SBram Moolenaar  */
397b25f9a97SBram Moolenaar     int
bufref_valid(bufref_T * bufref)398b25f9a97SBram Moolenaar bufref_valid(bufref_T *bufref)
399b25f9a97SBram Moolenaar {
400b25f9a97SBram Moolenaar     return bufref->br_buf_free_count == buf_free_count
40145e5fd13SBram Moolenaar 	? TRUE : buf_valid(bufref->br_buf)
40245e5fd13SBram Moolenaar 				  && bufref->br_fnum == bufref->br_buf->b_fnum;
403b25f9a97SBram Moolenaar }
404b25f9a97SBram Moolenaar 
405b25f9a97SBram Moolenaar /*
406071d4279SBram Moolenaar  * Return TRUE if "buf" points to a valid buffer (in the buffer list).
407b25f9a97SBram Moolenaar  * This can be slow if there are many buffers, prefer using bufref_valid().
408071d4279SBram Moolenaar  */
409071d4279SBram Moolenaar     int
buf_valid(buf_T * buf)4107454a06eSBram Moolenaar buf_valid(buf_T *buf)
411071d4279SBram Moolenaar {
412071d4279SBram Moolenaar     buf_T	*bp;
413071d4279SBram Moolenaar 
414c667da51SBram Moolenaar     // Assume that we more often have a recent buffer, start with the last
415c667da51SBram Moolenaar     // one.
41600d253e2SBram Moolenaar     FOR_ALL_BUFS_FROM_LAST(bp)
417071d4279SBram Moolenaar 	if (bp == buf)
418071d4279SBram Moolenaar 	    return TRUE;
419071d4279SBram Moolenaar     return FALSE;
420071d4279SBram Moolenaar }
421071d4279SBram Moolenaar 
422071d4279SBram Moolenaar /*
423480778b8SBram Moolenaar  * A hash table used to quickly lookup a buffer by its number.
424480778b8SBram Moolenaar  */
425480778b8SBram Moolenaar static hashtab_T buf_hashtab;
426480778b8SBram Moolenaar 
427480778b8SBram Moolenaar     static void
buf_hashtab_add(buf_T * buf)428480778b8SBram Moolenaar buf_hashtab_add(buf_T *buf)
429480778b8SBram Moolenaar {
430480778b8SBram Moolenaar     sprintf((char *)buf->b_key, "%x", buf->b_fnum);
431480778b8SBram Moolenaar     if (hash_add(&buf_hashtab, buf->b_key) == FAIL)
432f9e3e09fSBram Moolenaar 	emsg(_("E931: Buffer cannot be registered"));
433480778b8SBram Moolenaar }
434480778b8SBram Moolenaar 
435480778b8SBram Moolenaar     static void
buf_hashtab_remove(buf_T * buf)436480778b8SBram Moolenaar buf_hashtab_remove(buf_T *buf)
437480778b8SBram Moolenaar {
438480778b8SBram Moolenaar     hashitem_T *hi = hash_find(&buf_hashtab, buf->b_key);
439480778b8SBram Moolenaar 
440480778b8SBram Moolenaar     if (!HASHITEM_EMPTY(hi))
441480778b8SBram Moolenaar 	hash_remove(&buf_hashtab, hi);
442480778b8SBram Moolenaar }
443480778b8SBram Moolenaar 
44494f01956SBram Moolenaar /*
44594f01956SBram Moolenaar  * Return TRUE when buffer "buf" can be unloaded.
44694f01956SBram Moolenaar  * Give an error message and return FALSE when the buffer is locked or the
44794f01956SBram Moolenaar  * screen is being redrawn and the buffer is in a window.
44894f01956SBram Moolenaar  */
44994f01956SBram Moolenaar     static int
can_unload_buffer(buf_T * buf)45094f01956SBram Moolenaar can_unload_buffer(buf_T *buf)
45194f01956SBram Moolenaar {
45294f01956SBram Moolenaar     int	    can_unload = !buf->b_locked;
45394f01956SBram Moolenaar 
45494f01956SBram Moolenaar     if (can_unload && updating_screen)
45594f01956SBram Moolenaar     {
45694f01956SBram Moolenaar 	win_T	*wp;
45794f01956SBram Moolenaar 
45894f01956SBram Moolenaar 	FOR_ALL_WINDOWS(wp)
45994f01956SBram Moolenaar 	    if (wp->w_buffer == buf)
4609cea87c5SBram Moolenaar 	    {
46194f01956SBram Moolenaar 		can_unload = FALSE;
4629cea87c5SBram Moolenaar 		break;
4639cea87c5SBram Moolenaar 	    }
46494f01956SBram Moolenaar     }
46594f01956SBram Moolenaar     if (!can_unload)
466defa067cSBram Moolenaar 	semsg(_("E937: Attempt to delete a buffer that is in use: %s"),
467defa067cSBram Moolenaar 								 buf->b_fname);
46894f01956SBram Moolenaar     return can_unload;
46994f01956SBram Moolenaar }
470a997b45cSBram Moolenaar 
471480778b8SBram Moolenaar /*
472071d4279SBram Moolenaar  * Close the link to a buffer.
473071d4279SBram Moolenaar  * "action" is used when there is no longer a window for the buffer.
474071d4279SBram Moolenaar  * It can be:
475071d4279SBram Moolenaar  * 0			buffer becomes hidden
476071d4279SBram Moolenaar  * DOBUF_UNLOAD		buffer is unloaded
477071d4279SBram Moolenaar  * DOBUF_DELETE		buffer is unloaded and removed from buffer list
478071d4279SBram Moolenaar  * DOBUF_WIPE		buffer is unloaded and really deleted
47900b0d6d8SBram Moolenaar  * DOBUF_WIPE_REUSE	idem, and add to buf_reuse list
480071d4279SBram Moolenaar  * When doing all but the first one on the current buffer, the caller should
481071d4279SBram Moolenaar  * get a new buffer very soon!
482071d4279SBram Moolenaar  *
483071d4279SBram Moolenaar  * The 'bufhidden' option can force freeing and deleting.
48442ec6565SBram Moolenaar  *
48542ec6565SBram Moolenaar  * When "abort_if_last" is TRUE then do not close the buffer if autocommands
48642ec6565SBram Moolenaar  * cause there to be only one window with this buffer.  e.g. when ":quit" is
48742ec6565SBram Moolenaar  * supposed to close the window but autocommands close all other windows.
488a6e8f888SBram Moolenaar  *
489a6e8f888SBram Moolenaar  * When "ignore_abort" is TRUE don't abort even when aborting() returns TRUE.
490797e63b9SBram Moolenaar  *
491797e63b9SBram Moolenaar  * Return TRUE when we got to the end and b_nwindows was decremented.
492071d4279SBram Moolenaar  */
493797e63b9SBram Moolenaar     int
close_buffer(win_T * win,buf_T * buf,int action,int abort_if_last,int ignore_abort)4947454a06eSBram Moolenaar close_buffer(
495c667da51SBram Moolenaar     win_T	*win,		// if not NULL, set b_last_cursor
4967454a06eSBram Moolenaar     buf_T	*buf,
4977454a06eSBram Moolenaar     int		action,
498a6e8f888SBram Moolenaar     int		abort_if_last,
499a6e8f888SBram Moolenaar     int		ignore_abort)
500071d4279SBram Moolenaar {
501071d4279SBram Moolenaar     int		is_curbuf;
5022660c0eaSBram Moolenaar     int		nwindows;
503b25f9a97SBram Moolenaar     bufref_T	bufref;
504f9e687e0SBram Moolenaar     int		is_curwin = (curwin != NULL && curwin->w_buffer == buf);
505f9e687e0SBram Moolenaar     win_T	*the_curwin = curwin;
506f9e687e0SBram Moolenaar     tabpage_T	*the_curtab = curtab;
507071d4279SBram Moolenaar     int		unload_buf = (action != 0);
50800b0d6d8SBram Moolenaar     int		wipe_buf = (action == DOBUF_WIPE || action == DOBUF_WIPE_REUSE);
50900b0d6d8SBram Moolenaar     int		del_buf = (action == DOBUF_DEL || wipe_buf);
510071d4279SBram Moolenaar 
511cee52204SBram Moolenaar     CHECK_CURBUF;
512c5935a85SBram Moolenaar 
513c5935a85SBram Moolenaar     // Force unloading or deleting when 'bufhidden' says so.
514c5935a85SBram Moolenaar     // The caller must take care of NOT deleting/freeing when 'bufhidden' is
515c5935a85SBram Moolenaar     // "hide" (otherwise we could never free or delete a buffer).
516c667da51SBram Moolenaar     if (buf->b_p_bh[0] == 'd')		// 'bufhidden' == "delete"
5178adb0d03SBram Moolenaar     {
5188adb0d03SBram Moolenaar 	del_buf = TRUE;
5198adb0d03SBram Moolenaar 	unload_buf = TRUE;
5208adb0d03SBram Moolenaar     }
521c667da51SBram Moolenaar     else if (buf->b_p_bh[0] == 'w')	// 'bufhidden' == "wipe"
5228adb0d03SBram Moolenaar     {
5238adb0d03SBram Moolenaar 	del_buf = TRUE;
5248adb0d03SBram Moolenaar 	unload_buf = TRUE;
5258adb0d03SBram Moolenaar 	wipe_buf = TRUE;
5268adb0d03SBram Moolenaar     }
527c667da51SBram Moolenaar     else if (buf->b_p_bh[0] == 'u')	// 'bufhidden' == "unload"
5288adb0d03SBram Moolenaar 	unload_buf = TRUE;
5298adb0d03SBram Moolenaar 
53094053a51SBram Moolenaar #ifdef FEAT_TERMINAL
5318adb0d03SBram Moolenaar     if (bt_terminal(buf) && (buf->b_nwindows == 1 || del_buf))
53294053a51SBram Moolenaar     {
533cee52204SBram Moolenaar 	CHECK_CURBUF;
53494053a51SBram Moolenaar 	if (term_job_running(buf->b_term))
53594053a51SBram Moolenaar 	{
536f5be7cd0SBram Moolenaar 	    if (wipe_buf || unload_buf)
537a997b45cSBram Moolenaar 	    {
53894f01956SBram Moolenaar 		if (!can_unload_buffer(buf))
539797e63b9SBram Moolenaar 		    return FALSE;
54094f01956SBram Moolenaar 
541c667da51SBram Moolenaar 		// Wiping out or unloading a terminal buffer kills the job.
54294053a51SBram Moolenaar 		free_terminal(buf);
543a997b45cSBram Moolenaar 	    }
54494053a51SBram Moolenaar 	    else
54594053a51SBram Moolenaar 	    {
546c667da51SBram Moolenaar 		// The job keeps running, hide the buffer.
54794053a51SBram Moolenaar 		del_buf = FALSE;
54894053a51SBram Moolenaar 		unload_buf = FALSE;
54994053a51SBram Moolenaar 	    }
55094053a51SBram Moolenaar 	}
551c9f8b849SBram Moolenaar 	else if (buf->b_p_bh[0] == 'h' && !del_buf)
552c9f8b849SBram Moolenaar 	{
553c9f8b849SBram Moolenaar 	    // Hide a terminal buffer.
554c9f8b849SBram Moolenaar 	    unload_buf = FALSE;
555c9f8b849SBram Moolenaar 	}
55694053a51SBram Moolenaar 	else
55794053a51SBram Moolenaar 	{
558c667da51SBram Moolenaar 	    // A terminal buffer is wiped out if the job has finished.
55994053a51SBram Moolenaar 	    del_buf = TRUE;
56094053a51SBram Moolenaar 	    unload_buf = TRUE;
56194053a51SBram Moolenaar 	    wipe_buf = TRUE;
56294053a51SBram Moolenaar 	}
563cee52204SBram Moolenaar 	CHECK_CURBUF;
56494053a51SBram Moolenaar     }
56594053a51SBram Moolenaar #endif
566071d4279SBram Moolenaar 
567c667da51SBram Moolenaar     // Disallow deleting the buffer when it is locked (already being closed or
568c667da51SBram Moolenaar     // halfway a command that relies on it). Unloading is allowed.
56994f01956SBram Moolenaar     if ((del_buf || wipe_buf) && !can_unload_buffer(buf))
570797e63b9SBram Moolenaar 	return FALSE;
571e0ab94e7SBram Moolenaar 
572c667da51SBram Moolenaar     // check no autocommands closed the window
5734033c55eSBram Moolenaar     if (win != NULL && win_valid_any_tab(win))
574071d4279SBram Moolenaar     {
575c667da51SBram Moolenaar 	// Set b_last_cursor when closing the last window for the buffer.
576c667da51SBram Moolenaar 	// Remember the last cursor position and window options of the buffer.
577c667da51SBram Moolenaar 	// This used to be only for the current window, but then options like
578c667da51SBram Moolenaar 	// 'foldmethod' may be lost with a ":only" command.
579071d4279SBram Moolenaar 	if (buf->b_nwindows == 1)
580071d4279SBram Moolenaar 	    set_last_cursor(win);
581071d4279SBram Moolenaar 	buflist_setfpos(buf, win,
582071d4279SBram Moolenaar 		    win->w_cursor.lnum == 1 ? 0 : win->w_cursor.lnum,
583071d4279SBram Moolenaar 		    win->w_cursor.col, TRUE);
584071d4279SBram Moolenaar     }
585071d4279SBram Moolenaar 
586b25f9a97SBram Moolenaar     set_bufref(&bufref, buf);
587b25f9a97SBram Moolenaar 
588c667da51SBram Moolenaar     // When the buffer is no longer in a window, trigger BufWinLeave
589071d4279SBram Moolenaar     if (buf->b_nwindows == 1)
590071d4279SBram Moolenaar     {
591e0ab94e7SBram Moolenaar 	++buf->b_locked;
592983d83ffSBram Moolenaar 	++buf->b_locked_split;
5938240433fSBram Moolenaar 	if (apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname,
5948240433fSBram Moolenaar 								  FALSE, buf)
595b25f9a97SBram Moolenaar 		&& !bufref_valid(&bufref))
59642ec6565SBram Moolenaar 	{
597c667da51SBram Moolenaar 	    // Autocommands deleted the buffer.
598362ce480SBram Moolenaar aucmd_abort:
599f9e3e09fSBram Moolenaar 	    emsg(_(e_auabort));
600797e63b9SBram Moolenaar 	    return FALSE;
60142ec6565SBram Moolenaar 	}
602e0ab94e7SBram Moolenaar 	--buf->b_locked;
603983d83ffSBram Moolenaar 	--buf->b_locked_split;
604362ce480SBram Moolenaar 	if (abort_if_last && one_window())
605c667da51SBram Moolenaar 	    // Autocommands made this the only window.
606362ce480SBram Moolenaar 	    goto aucmd_abort;
607071d4279SBram Moolenaar 
608c667da51SBram Moolenaar 	// When the buffer becomes hidden, but is not unloaded, trigger
609c667da51SBram Moolenaar 	// BufHidden
610071d4279SBram Moolenaar 	if (!unload_buf)
611071d4279SBram Moolenaar 	{
612e0ab94e7SBram Moolenaar 	    ++buf->b_locked;
613983d83ffSBram Moolenaar 	    ++buf->b_locked_split;
6148240433fSBram Moolenaar 	    if (apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname,
6158240433fSBram Moolenaar 								  FALSE, buf)
616b25f9a97SBram Moolenaar 		    && !bufref_valid(&bufref))
617c667da51SBram Moolenaar 		// Autocommands deleted the buffer.
618362ce480SBram Moolenaar 		goto aucmd_abort;
619e0ab94e7SBram Moolenaar 	    --buf->b_locked;
620983d83ffSBram Moolenaar 	    --buf->b_locked_split;
621362ce480SBram Moolenaar 	    if (abort_if_last && one_window())
622c667da51SBram Moolenaar 		// Autocommands made this the only window.
623362ce480SBram Moolenaar 		goto aucmd_abort;
62442ec6565SBram Moolenaar 	}
625071d4279SBram Moolenaar #ifdef FEAT_EVAL
626a6e8f888SBram Moolenaar 	// autocmds may abort script processing
627a6e8f888SBram Moolenaar 	if (!ignore_abort && aborting())
628797e63b9SBram Moolenaar 	    return FALSE;
629071d4279SBram Moolenaar #endif
630071d4279SBram Moolenaar     }
631f9e687e0SBram Moolenaar 
632c667da51SBram Moolenaar     // If the buffer was in curwin and the window has changed, go back to that
633c667da51SBram Moolenaar     // window, if it still exists.  This avoids that ":edit x" triggering a
634c667da51SBram Moolenaar     // "tabnext" BufUnload autocmd leaves a window behind without a buffer.
635f9e687e0SBram Moolenaar     if (is_curwin && curwin != the_curwin &&  win_valid_any_tab(the_curwin))
636f9e687e0SBram Moolenaar     {
637f9e687e0SBram Moolenaar 	block_autocmds();
638f9e687e0SBram Moolenaar 	goto_tabpage_win(the_curtab, the_curwin);
639f9e687e0SBram Moolenaar 	unblock_autocmds();
640f9e687e0SBram Moolenaar     }
641f9e687e0SBram Moolenaar 
642071d4279SBram Moolenaar     nwindows = buf->b_nwindows;
643071d4279SBram Moolenaar 
644c667da51SBram Moolenaar     // decrease the link count from windows (unless not in any window)
645071d4279SBram Moolenaar     if (buf->b_nwindows > 0)
646071d4279SBram Moolenaar 	--buf->b_nwindows;
647071d4279SBram Moolenaar 
64897ce4192SBram Moolenaar #ifdef FEAT_DIFF
64997ce4192SBram Moolenaar     if (diffopt_hiddenoff() && !unload_buf && buf->b_nwindows == 0)
650c667da51SBram Moolenaar 	diff_buf_delete(buf);	// Clear 'diff' for hidden buffer.
65197ce4192SBram Moolenaar #endif
65297ce4192SBram Moolenaar 
653c667da51SBram Moolenaar     // Return when a window is displaying the buffer or when it's not
654c667da51SBram Moolenaar     // unloaded.
655071d4279SBram Moolenaar     if (buf->b_nwindows > 0 || !unload_buf)
656797e63b9SBram Moolenaar 	return FALSE;
657071d4279SBram Moolenaar 
658c667da51SBram Moolenaar     // Always remove the buffer when there is no file name.
659071d4279SBram Moolenaar     if (buf->b_ffname == NULL)
660071d4279SBram Moolenaar 	del_buf = TRUE;
661071d4279SBram Moolenaar 
662c667da51SBram Moolenaar     // When closing the current buffer stop Visual mode before freeing
663c667da51SBram Moolenaar     // anything.
6644930a76aSBram Moolenaar     if (buf == curbuf && VIsual_active
6659a27c7fdSBram Moolenaar #if defined(EXITFREE)
6669a27c7fdSBram Moolenaar 	    && !entered_free_all_mem
6679a27c7fdSBram Moolenaar #endif
6689a27c7fdSBram Moolenaar 	    )
669c4a908e8SBram Moolenaar 	end_visual_mode();
670c4a908e8SBram Moolenaar 
671c5935a85SBram Moolenaar     // Free all things allocated for this buffer.
672c5935a85SBram Moolenaar     // Also calls the "BufDelete" autocommands when del_buf is TRUE.
673c5935a85SBram Moolenaar     //
674c667da51SBram Moolenaar     // Remember if we are closing the current buffer.  Restore the number of
675c667da51SBram Moolenaar     // windows, so that autocommands in buf_freeall() don't get confused.
676071d4279SBram Moolenaar     is_curbuf = (buf == curbuf);
677071d4279SBram Moolenaar     buf->b_nwindows = nwindows;
678071d4279SBram Moolenaar 
679a6e8f888SBram Moolenaar     buf_freeall(buf, (del_buf ? BFA_DEL : 0)
680a6e8f888SBram Moolenaar 		   + (wipe_buf ? BFA_WIPE : 0)
681a6e8f888SBram Moolenaar 		   + (ignore_abort ? BFA_IGNORE_ABORT : 0));
682071d4279SBram Moolenaar 
683c667da51SBram Moolenaar     // Autocommands may have deleted the buffer.
684b25f9a97SBram Moolenaar     if (!bufref_valid(&bufref))
685797e63b9SBram Moolenaar 	return FALSE;
686071d4279SBram Moolenaar #ifdef FEAT_EVAL
687a6e8f888SBram Moolenaar     // autocmds may abort script processing
688a6e8f888SBram Moolenaar     if (!ignore_abort && aborting())
689797e63b9SBram Moolenaar 	return FALSE;
690071d4279SBram Moolenaar #endif
691071d4279SBram Moolenaar 
692c5935a85SBram Moolenaar     // It's possible that autocommands change curbuf to the one being deleted.
693c5935a85SBram Moolenaar     // This might cause the previous curbuf to be deleted unexpectedly.  But
694c5935a85SBram Moolenaar     // in some cases it's OK to delete the curbuf, because a new one is
695c5935a85SBram Moolenaar     // obtained anyway.  Therefore only return if curbuf changed to the
696c5935a85SBram Moolenaar     // deleted buffer.
697071d4279SBram Moolenaar     if (buf == curbuf && !is_curbuf)
698797e63b9SBram Moolenaar 	return FALSE;
69930445cb6SBram Moolenaar 
7004033c55eSBram Moolenaar     if (win_valid_any_tab(win) && win->w_buffer == buf)
701c667da51SBram Moolenaar 	win->w_buffer = NULL;  // make sure we don't use the buffer now
70230445cb6SBram Moolenaar 
703c667da51SBram Moolenaar     // Autocommands may have opened or closed windows for this buffer.
704c667da51SBram Moolenaar     // Decrement the count for the close we do here.
70530445cb6SBram Moolenaar     if (buf->b_nwindows > 0)
70630445cb6SBram Moolenaar 	--buf->b_nwindows;
707071d4279SBram Moolenaar 
708071d4279SBram Moolenaar     /*
709071d4279SBram Moolenaar      * Remove the buffer from the list.
710071d4279SBram Moolenaar      */
711071d4279SBram Moolenaar     if (wipe_buf)
712071d4279SBram Moolenaar     {
71300b0d6d8SBram Moolenaar 	if (action == DOBUF_WIPE_REUSE)
71400b0d6d8SBram Moolenaar 	{
71500b0d6d8SBram Moolenaar 	    // we can re-use this buffer number, store it
71600b0d6d8SBram Moolenaar 	    if (buf_reuse.ga_itemsize == 0)
71700b0d6d8SBram Moolenaar 		ga_init2(&buf_reuse, sizeof(int), 50);
71800b0d6d8SBram Moolenaar 	    if (ga_grow(&buf_reuse, 1) == OK)
71900b0d6d8SBram Moolenaar 		((int *)buf_reuse.ga_data)[buf_reuse.ga_len++] = buf->b_fnum;
72000b0d6d8SBram Moolenaar 	}
7213d6014f0SBram Moolenaar 	if (buf->b_sfname != buf->b_ffname)
7223d6014f0SBram Moolenaar 	    VIM_CLEAR(buf->b_sfname);
7233d6014f0SBram Moolenaar 	else
7243d6014f0SBram Moolenaar 	    buf->b_sfname = NULL;
7253d6014f0SBram Moolenaar 	VIM_CLEAR(buf->b_ffname);
726071d4279SBram Moolenaar 	if (buf->b_prev == NULL)
727071d4279SBram Moolenaar 	    firstbuf = buf->b_next;
728071d4279SBram Moolenaar 	else
729071d4279SBram Moolenaar 	    buf->b_prev->b_next = buf->b_next;
730071d4279SBram Moolenaar 	if (buf->b_next == NULL)
731071d4279SBram Moolenaar 	    lastbuf = buf->b_prev;
732071d4279SBram Moolenaar 	else
733071d4279SBram Moolenaar 	    buf->b_next->b_prev = buf->b_prev;
734071d4279SBram Moolenaar 	free_buffer(buf);
735071d4279SBram Moolenaar     }
736071d4279SBram Moolenaar     else
737071d4279SBram Moolenaar     {
738071d4279SBram Moolenaar 	if (del_buf)
739071d4279SBram Moolenaar 	{
740c667da51SBram Moolenaar 	    // Free all internal variables and reset option values, to make
741c667da51SBram Moolenaar 	    // ":bdel" compatible with Vim 5.7.
742071d4279SBram Moolenaar 	    free_buffer_stuff(buf, TRUE);
743071d4279SBram Moolenaar 
744c667da51SBram Moolenaar 	    // Make it look like a new buffer.
745071d4279SBram Moolenaar 	    buf->b_flags = BF_CHECK_RO | BF_NEVERLOADED;
746071d4279SBram Moolenaar 
747c667da51SBram Moolenaar 	    // Init the options when loaded again.
748071d4279SBram Moolenaar 	    buf->b_p_initialized = FALSE;
749071d4279SBram Moolenaar 	}
750071d4279SBram Moolenaar 	buf_clear_file(buf);
751071d4279SBram Moolenaar 	if (del_buf)
752071d4279SBram Moolenaar 	    buf->b_p_bl = FALSE;
753071d4279SBram Moolenaar     }
754cee52204SBram Moolenaar     // NOTE: at this point "curbuf" may be invalid!
755797e63b9SBram Moolenaar     return TRUE;
756071d4279SBram Moolenaar }
757071d4279SBram Moolenaar 
758071d4279SBram Moolenaar /*
759071d4279SBram Moolenaar  * Make buffer not contain a file.
760071d4279SBram Moolenaar  */
761071d4279SBram Moolenaar     void
buf_clear_file(buf_T * buf)7627454a06eSBram Moolenaar buf_clear_file(buf_T *buf)
763071d4279SBram Moolenaar {
764071d4279SBram Moolenaar     buf->b_ml.ml_line_count = 1;
765c024b466SBram Moolenaar     unchanged(buf, TRUE, TRUE);
766071d4279SBram Moolenaar     buf->b_shortname = FALSE;
767071d4279SBram Moolenaar     buf->b_p_eol = TRUE;
768071d4279SBram Moolenaar     buf->b_start_eol = TRUE;
769071d4279SBram Moolenaar     buf->b_p_bomb = FALSE;
77083eb885dSBram Moolenaar     buf->b_start_bomb = FALSE;
771071d4279SBram Moolenaar     buf->b_ml.ml_mfp = NULL;
772c667da51SBram Moolenaar     buf->b_ml.ml_flags = ML_EMPTY;		// empty buffer
773071d4279SBram Moolenaar #ifdef FEAT_NETBEANS_INTG
774071d4279SBram Moolenaar     netbeans_deleted_all_lines(buf);
775071d4279SBram Moolenaar #endif
776071d4279SBram Moolenaar }
777071d4279SBram Moolenaar 
778071d4279SBram Moolenaar /*
779071d4279SBram Moolenaar  * buf_freeall() - free all things allocated for a buffer that are related to
7805a49789aSBram Moolenaar  * the file.  Careful: get here with "curwin" NULL when exiting.
7815a49789aSBram Moolenaar  * flags:
78259f931efSBram Moolenaar  * BFA_DEL	     buffer is going to be deleted
78359f931efSBram Moolenaar  * BFA_WIPE	     buffer is going to be wiped out
78459f931efSBram Moolenaar  * BFA_KEEP_UNDO     do not free undo information
785a6e8f888SBram Moolenaar  * BFA_IGNORE_ABORT  don't abort even when aborting() returns TRUE
786071d4279SBram Moolenaar  */
787071d4279SBram Moolenaar     void
buf_freeall(buf_T * buf,int flags)7887454a06eSBram Moolenaar buf_freeall(buf_T *buf, int flags)
789071d4279SBram Moolenaar {
790071d4279SBram Moolenaar     int		is_curbuf = (buf == curbuf);
791b25f9a97SBram Moolenaar     bufref_T	bufref;
7925a49789aSBram Moolenaar     int		is_curwin = (curwin != NULL && curwin->w_buffer == buf);
7935a49789aSBram Moolenaar     win_T	*the_curwin = curwin;
7945a49789aSBram Moolenaar     tabpage_T	*the_curtab = curtab;
795071d4279SBram Moolenaar 
796c667da51SBram Moolenaar     // Make sure the buffer isn't closed by autocommands.
797e0ab94e7SBram Moolenaar     ++buf->b_locked;
798983d83ffSBram Moolenaar     ++buf->b_locked_split;
799b25f9a97SBram Moolenaar     set_bufref(&bufref, buf);
800c67e8921SBram Moolenaar     if (buf->b_ml.ml_mfp != NULL)
801c67e8921SBram Moolenaar     {
8028240433fSBram Moolenaar 	if (apply_autocmds(EVENT_BUFUNLOAD, buf->b_fname, buf->b_fname,
8038240433fSBram Moolenaar 								  FALSE, buf)
804b25f9a97SBram Moolenaar 		&& !bufref_valid(&bufref))
805c667da51SBram Moolenaar 	    // autocommands deleted the buffer
806071d4279SBram Moolenaar 	    return;
807c67e8921SBram Moolenaar     }
80859f931efSBram Moolenaar     if ((flags & BFA_DEL) && buf->b_p_bl)
809071d4279SBram Moolenaar     {
8108240433fSBram Moolenaar 	if (apply_autocmds(EVENT_BUFDELETE, buf->b_fname, buf->b_fname,
8118240433fSBram Moolenaar 								   FALSE, buf)
812b25f9a97SBram Moolenaar 		&& !bufref_valid(&bufref))
813c667da51SBram Moolenaar 	    // autocommands deleted the buffer
814071d4279SBram Moolenaar 	    return;
815071d4279SBram Moolenaar     }
81659f931efSBram Moolenaar     if (flags & BFA_WIPE)
817071d4279SBram Moolenaar     {
8188240433fSBram Moolenaar 	if (apply_autocmds(EVENT_BUFWIPEOUT, buf->b_fname, buf->b_fname,
8198240433fSBram Moolenaar 								  FALSE, buf)
820b25f9a97SBram Moolenaar 		&& !bufref_valid(&bufref))
821c667da51SBram Moolenaar 	    // autocommands deleted the buffer
822071d4279SBram Moolenaar 	    return;
823071d4279SBram Moolenaar     }
824e0ab94e7SBram Moolenaar     --buf->b_locked;
825983d83ffSBram Moolenaar     --buf->b_locked_split;
8265a49789aSBram Moolenaar 
827c667da51SBram Moolenaar     // If the buffer was in curwin and the window has changed, go back to that
828c667da51SBram Moolenaar     // window, if it still exists.  This avoids that ":edit x" triggering a
829c667da51SBram Moolenaar     // "tabnext" BufUnload autocmd leaves a window behind without a buffer.
8305a49789aSBram Moolenaar     if (is_curwin && curwin != the_curwin &&  win_valid_any_tab(the_curwin))
8315a49789aSBram Moolenaar     {
8325a49789aSBram Moolenaar 	block_autocmds();
8335a49789aSBram Moolenaar 	goto_tabpage_win(the_curtab, the_curwin);
8345a49789aSBram Moolenaar 	unblock_autocmds();
8355a49789aSBram Moolenaar     }
8365a49789aSBram Moolenaar 
837071d4279SBram Moolenaar #ifdef FEAT_EVAL
838a6e8f888SBram Moolenaar     // autocmds may abort script processing
839a6e8f888SBram Moolenaar     if ((flags & BFA_IGNORE_ABORT) == 0 && aborting())
840071d4279SBram Moolenaar 	return;
841071d4279SBram Moolenaar #endif
842071d4279SBram Moolenaar 
843c5935a85SBram Moolenaar     // It's possible that autocommands change curbuf to the one being deleted.
844c5935a85SBram Moolenaar     // This might cause curbuf to be deleted unexpectedly.  But in some cases
845c5935a85SBram Moolenaar     // it's OK to delete the curbuf, because a new one is obtained anyway.
846c5935a85SBram Moolenaar     // Therefore only return if curbuf changed to the deleted buffer.
847071d4279SBram Moolenaar     if (buf == curbuf && !is_curbuf)
848071d4279SBram Moolenaar 	return;
849071d4279SBram Moolenaar #ifdef FEAT_DIFF
850c667da51SBram Moolenaar     diff_buf_delete(buf);	    // Can't use 'diff' for unloaded buffer.
851071d4279SBram Moolenaar #endif
852a971b82bSBram Moolenaar #ifdef FEAT_SYN_HL
853c667da51SBram Moolenaar     // Remove any ownsyntax, unless exiting.
854030cddc7SBram Moolenaar     if (curwin != NULL && curwin->w_buffer == buf)
85589c7122cSBram Moolenaar 	reset_synblock(curwin);
856a971b82bSBram Moolenaar #endif
857b6799acdSBram Moolenaar 
858b6799acdSBram Moolenaar #ifdef FEAT_FOLDING
859c667da51SBram Moolenaar     // No folds in an empty buffer.
860b6799acdSBram Moolenaar     {
861b6799acdSBram Moolenaar 	win_T		*win;
862b6799acdSBram Moolenaar 	tabpage_T	*tp;
863b6799acdSBram Moolenaar 
864b6799acdSBram Moolenaar 	FOR_ALL_TAB_WINDOWS(tp, win)
865b6799acdSBram Moolenaar 	    if (win->w_buffer == buf)
866b6799acdSBram Moolenaar 		clearFolding(win);
867b6799acdSBram Moolenaar     }
868b6799acdSBram Moolenaar #endif
869b6799acdSBram Moolenaar 
870071d4279SBram Moolenaar #ifdef FEAT_TCL
871071d4279SBram Moolenaar     tcl_buffer_free(buf);
872071d4279SBram Moolenaar #endif
873c667da51SBram Moolenaar     ml_close(buf, TRUE);	    // close and delete the memline/memfile
874c667da51SBram Moolenaar     buf->b_ml.ml_line_count = 0;    // no lines in buffer
87559f931efSBram Moolenaar     if ((flags & BFA_KEEP_UNDO) == 0)
87659f931efSBram Moolenaar     {
877c667da51SBram Moolenaar 	u_blockfree(buf);	    // free the memory allocated for undo
878c667da51SBram Moolenaar 	u_clearall(buf);	    // reset all undo information
87959f931efSBram Moolenaar     }
880071d4279SBram Moolenaar #ifdef FEAT_SYN_HL
881c667da51SBram Moolenaar     syntax_clear(&buf->b_s);	    // reset syntax info
882071d4279SBram Moolenaar #endif
88305ad5ff0SBram Moolenaar #ifdef FEAT_PROP_POPUP
88498aefe7cSBram Moolenaar     clear_buf_prop_types(buf);
88598aefe7cSBram Moolenaar #endif
886c667da51SBram Moolenaar     buf->b_flags &= ~BF_READERR;    // a read error is no longer relevant
887071d4279SBram Moolenaar }
888071d4279SBram Moolenaar 
889071d4279SBram Moolenaar /*
890071d4279SBram Moolenaar  * Free a buffer structure and the things it contains related to the buffer
891071d4279SBram Moolenaar  * itself (not the file, that must have been done already).
892071d4279SBram Moolenaar  */
893071d4279SBram Moolenaar     static void
free_buffer(buf_T * buf)8947454a06eSBram Moolenaar free_buffer(buf_T *buf)
895071d4279SBram Moolenaar {
896b25f9a97SBram Moolenaar     ++buf_free_count;
897071d4279SBram Moolenaar     free_buffer_stuff(buf, TRUE);
898429fa853SBram Moolenaar #ifdef FEAT_EVAL
899c667da51SBram Moolenaar     // b:changedtick uses an item in buf_T, remove it now
900b2c8750cSBram Moolenaar     dictitem_remove(buf->b_vars, (dictitem_T *)&buf->b_ct_di);
901429fa853SBram Moolenaar     unref_var_dict(buf->b_vars);
9028617348eSBram Moolenaar     remove_listeners(buf);
903429fa853SBram Moolenaar #endif
9040ba04296SBram Moolenaar #ifdef FEAT_LUA
9050ba04296SBram Moolenaar     lua_buffer_free(buf);
9060ba04296SBram Moolenaar #endif
907325b7a2fSBram Moolenaar #ifdef FEAT_MZSCHEME
908325b7a2fSBram Moolenaar     mzscheme_buffer_free(buf);
909325b7a2fSBram Moolenaar #endif
910071d4279SBram Moolenaar #ifdef FEAT_PERL
911071d4279SBram Moolenaar     perl_buf_free(buf);
912071d4279SBram Moolenaar #endif
913071d4279SBram Moolenaar #ifdef FEAT_PYTHON
914071d4279SBram Moolenaar     python_buffer_free(buf);
915071d4279SBram Moolenaar #endif
916bd5e15fdSBram Moolenaar #ifdef FEAT_PYTHON3
917bd5e15fdSBram Moolenaar     python3_buffer_free(buf);
918bd5e15fdSBram Moolenaar #endif
919071d4279SBram Moolenaar #ifdef FEAT_RUBY
920071d4279SBram Moolenaar     ruby_buffer_free(buf);
921071d4279SBram Moolenaar #endif
922e0f76d00SBram Moolenaar #ifdef FEAT_JOB_CHANNEL
923e0f76d00SBram Moolenaar     channel_buffer_free(buf);
924e0f76d00SBram Moolenaar #endif
92596ca27a0SBram Moolenaar #ifdef FEAT_TERMINAL
926d85f271bSBram Moolenaar     free_terminal(buf);
92796ca27a0SBram Moolenaar #endif
928f273245fSBram Moolenaar #ifdef FEAT_JOB_CHANNEL
929f273245fSBram Moolenaar     vim_free(buf->b_prompt_text);
9303a97bb3fSBram Moolenaar     free_callback(&buf->b_prompt_callback);
9318617348eSBram Moolenaar     free_callback(&buf->b_prompt_interrupt);
932f273245fSBram Moolenaar #endif
933480778b8SBram Moolenaar 
934480778b8SBram Moolenaar     buf_hashtab_remove(buf);
935480778b8SBram Moolenaar 
9369280e3f9SBram Moolenaar     aubuflocal_remove(buf);
9379280e3f9SBram Moolenaar 
9384c7ab1bbSBram Moolenaar     if (autocmd_busy)
9394c7ab1bbSBram Moolenaar     {
940c667da51SBram Moolenaar 	// Do not free the buffer structure while autocommands are executing,
941c667da51SBram Moolenaar 	// it's still needed. Free it when autocmd_busy is reset.
9424c7ab1bbSBram Moolenaar 	buf->b_next = au_pending_free_buf;
9434c7ab1bbSBram Moolenaar 	au_pending_free_buf = buf;
9444c7ab1bbSBram Moolenaar     }
9454c7ab1bbSBram Moolenaar     else
946cee52204SBram Moolenaar     {
947071d4279SBram Moolenaar 	vim_free(buf);
948cee52204SBram Moolenaar 	if (curbuf == buf)
949cee52204SBram Moolenaar 	    curbuf = NULL;  // make clear it's not to be used
950cee52204SBram Moolenaar     }
951071d4279SBram Moolenaar }
952071d4279SBram Moolenaar 
953071d4279SBram Moolenaar /*
95495c526e1SBram Moolenaar  * Initializes b:changedtick.
95579518e2aSBram Moolenaar  */
95679518e2aSBram Moolenaar     static void
init_changedtick(buf_T * buf)95779518e2aSBram Moolenaar init_changedtick(buf_T *buf)
95879518e2aSBram Moolenaar {
95995c526e1SBram Moolenaar     dictitem_T *di = (dictitem_T *)&buf->b_ct_di;
96079518e2aSBram Moolenaar 
96195c526e1SBram Moolenaar     di->di_flags = DI_FLAGS_FIX | DI_FLAGS_RO;
96279518e2aSBram Moolenaar     di->di_tv.v_type = VAR_NUMBER;
96379518e2aSBram Moolenaar     di->di_tv.v_lock = VAR_FIXED;
96479518e2aSBram Moolenaar     di->di_tv.vval.v_number = 0;
96595c526e1SBram Moolenaar 
96692769c39SBram Moolenaar #ifdef FEAT_EVAL
96795c526e1SBram Moolenaar     STRCPY(buf->b_ct_di.di_key, "changedtick");
96895c526e1SBram Moolenaar     (void)dict_add(buf->b_vars, di);
96992769c39SBram Moolenaar #endif
97079518e2aSBram Moolenaar }
97179518e2aSBram Moolenaar 
97279518e2aSBram Moolenaar /*
973071d4279SBram Moolenaar  * Free stuff in the buffer for ":bdel" and when wiping out the buffer.
974071d4279SBram Moolenaar  */
975071d4279SBram Moolenaar     static void
free_buffer_stuff(buf_T * buf,int free_options)9767454a06eSBram Moolenaar free_buffer_stuff(
9777454a06eSBram Moolenaar     buf_T	*buf,
978c667da51SBram Moolenaar     int		free_options)		// free options as well
979071d4279SBram Moolenaar {
980071d4279SBram Moolenaar     if (free_options)
981071d4279SBram Moolenaar     {
982c667da51SBram Moolenaar 	clear_wininfo(buf);		// including window-local options
983071d4279SBram Moolenaar 	free_buf_options(buf, TRUE);
984beca055bSBram Moolenaar #ifdef FEAT_SPELL
985beca055bSBram Moolenaar 	ga_clear(&buf->b_s.b_langp);
986beca055bSBram Moolenaar #endif
987071d4279SBram Moolenaar     }
988071d4279SBram Moolenaar #ifdef FEAT_EVAL
98979518e2aSBram Moolenaar     {
99095c526e1SBram Moolenaar 	varnumber_T tick = CHANGEDTICK(buf);
99179518e2aSBram Moolenaar 
992c667da51SBram Moolenaar 	vars_clear(&buf->b_vars->dv_hashtab); // free all buffer variables
993429fa853SBram Moolenaar 	hash_init(&buf->b_vars->dv_hashtab);
99479518e2aSBram Moolenaar 	init_changedtick(buf);
99595c526e1SBram Moolenaar 	CHANGEDTICK(buf) = tick;
996f10997a1SBram Moolenaar 	remove_listeners(buf);
99779518e2aSBram Moolenaar     }
998071d4279SBram Moolenaar #endif
999ac9fb180SBram Moolenaar     uc_clear(&buf->b_ucmds);		// clear local user commands
1000071d4279SBram Moolenaar #ifdef FEAT_SIGNS
1001ac9fb180SBram Moolenaar     buf_delete_signs(buf, (char_u *)"*");	// delete any signs
1002071d4279SBram Moolenaar #endif
1003d7f8f5c8SBram Moolenaar #ifdef FEAT_NETBEANS_INTG
1004d7f8f5c8SBram Moolenaar     netbeans_file_killed(buf);
1005d7f8f5c8SBram Moolenaar #endif
1006c667da51SBram Moolenaar     map_clear_int(buf, MAP_ALL_MODES, TRUE, FALSE);  // clear local mappings
1007c667da51SBram Moolenaar     map_clear_int(buf, MAP_ALL_MODES, TRUE, TRUE);   // clear local abbrevs
1008d23a8236SBram Moolenaar     VIM_CLEAR(buf->b_start_fenc);
1009071d4279SBram Moolenaar }
1010071d4279SBram Moolenaar 
1011071d4279SBram Moolenaar /*
10124882d983SBram Moolenaar  * Free one wininfo_T.
10134882d983SBram Moolenaar  */
10144882d983SBram Moolenaar     void
free_wininfo(wininfo_T * wip)10154882d983SBram Moolenaar free_wininfo(wininfo_T *wip)
10164882d983SBram Moolenaar {
10174882d983SBram Moolenaar     if (wip->wi_optset)
10184882d983SBram Moolenaar     {
10194882d983SBram Moolenaar 	clear_winopt(&wip->wi_opt);
10204882d983SBram Moolenaar #ifdef FEAT_FOLDING
10214882d983SBram Moolenaar 	deleteFoldRecurse(&wip->wi_folds);
10224882d983SBram Moolenaar #endif
10234882d983SBram Moolenaar     }
10244882d983SBram Moolenaar     vim_free(wip);
10254882d983SBram Moolenaar }
10264882d983SBram Moolenaar 
10274882d983SBram Moolenaar /*
1028071d4279SBram Moolenaar  * Free the b_wininfo list for buffer "buf".
1029071d4279SBram Moolenaar  */
1030071d4279SBram Moolenaar     static void
clear_wininfo(buf_T * buf)10317454a06eSBram Moolenaar clear_wininfo(buf_T *buf)
1032071d4279SBram Moolenaar {
1033071d4279SBram Moolenaar     wininfo_T	*wip;
1034071d4279SBram Moolenaar 
1035071d4279SBram Moolenaar     while (buf->b_wininfo != NULL)
1036071d4279SBram Moolenaar     {
1037071d4279SBram Moolenaar 	wip = buf->b_wininfo;
1038071d4279SBram Moolenaar 	buf->b_wininfo = wip->wi_next;
10394882d983SBram Moolenaar 	free_wininfo(wip);
1040071d4279SBram Moolenaar     }
1041071d4279SBram Moolenaar }
1042071d4279SBram Moolenaar 
1043071d4279SBram Moolenaar /*
1044071d4279SBram Moolenaar  * Go to another buffer.  Handles the result of the ATTENTION dialog.
1045071d4279SBram Moolenaar  */
1046071d4279SBram Moolenaar     void
goto_buffer(exarg_T * eap,int start,int dir,int count)10477454a06eSBram Moolenaar goto_buffer(
10487454a06eSBram Moolenaar     exarg_T	*eap,
10497454a06eSBram Moolenaar     int		start,
10507454a06eSBram Moolenaar     int		dir,
10517454a06eSBram Moolenaar     int		count)
1052071d4279SBram Moolenaar {
10537c0a2f36SBram Moolenaar     bufref_T	old_curbuf;
10547c0a2f36SBram Moolenaar 
10557c0a2f36SBram Moolenaar     set_bufref(&old_curbuf, curbuf);
1056071d4279SBram Moolenaar 
1057071d4279SBram Moolenaar     swap_exists_action = SEA_DIALOG;
1058071d4279SBram Moolenaar     (void)do_buffer(*eap->cmd == 's' ? DOBUF_SPLIT : DOBUF_GOTO,
1059071d4279SBram Moolenaar 					     start, dir, count, eap->forceit);
1060071d4279SBram Moolenaar     if (swap_exists_action == SEA_QUIT && *eap->cmd == 's')
1061071d4279SBram Moolenaar     {
1062f2bd8ef2SBram Moolenaar #if defined(FEAT_EVAL)
1063c0197e28SBram Moolenaar 	cleanup_T   cs;
10645eb86f91SBram Moolenaar 
1065c667da51SBram Moolenaar 	// Reset the error/interrupt/exception state here so that
1066c667da51SBram Moolenaar 	// aborting() returns FALSE when closing a window.
1067c0197e28SBram Moolenaar 	enter_cleanup(&cs);
1068c0197e28SBram Moolenaar #endif
10695eb86f91SBram Moolenaar 
1070c667da51SBram Moolenaar 	// Quitting means closing the split window, nothing else.
1071071d4279SBram Moolenaar 	win_close(curwin, TRUE);
1072071d4279SBram Moolenaar 	swap_exists_action = SEA_NONE;
107312033fb4SBram Moolenaar 	swap_exists_did_quit = TRUE;
1074c0197e28SBram Moolenaar 
1075f2bd8ef2SBram Moolenaar #if defined(FEAT_EVAL)
1076c667da51SBram Moolenaar 	// Restore the error/interrupt/exception state if not discarded by a
1077c667da51SBram Moolenaar 	// new aborting error, interrupt, or uncaught exception.
1078c0197e28SBram Moolenaar 	leave_cleanup(&cs);
1079c0197e28SBram Moolenaar #endif
1080071d4279SBram Moolenaar     }
1081071d4279SBram Moolenaar     else
10827c0a2f36SBram Moolenaar 	handle_swap_exists(&old_curbuf);
1083071d4279SBram Moolenaar }
1084071d4279SBram Moolenaar 
1085071d4279SBram Moolenaar /*
1086071d4279SBram Moolenaar  * Handle the situation of swap_exists_action being set.
1087071d4279SBram Moolenaar  * It is allowed for "old_curbuf" to be NULL or invalid.
1088071d4279SBram Moolenaar  */
1089071d4279SBram Moolenaar     void
handle_swap_exists(bufref_T * old_curbuf)10907c0a2f36SBram Moolenaar handle_swap_exists(bufref_T *old_curbuf)
1091071d4279SBram Moolenaar {
1092f2bd8ef2SBram Moolenaar #if defined(FEAT_EVAL)
1093c0197e28SBram Moolenaar     cleanup_T	cs;
1094c0197e28SBram Moolenaar #endif
10956d47df7cSBram Moolenaar #ifdef FEAT_SYN_HL
10966d47df7cSBram Moolenaar     long	old_tw = curbuf->b_p_tw;
10976d47df7cSBram Moolenaar #endif
10987c0a2f36SBram Moolenaar     buf_T	*buf;
10995eb86f91SBram Moolenaar 
1100071d4279SBram Moolenaar     if (swap_exists_action == SEA_QUIT)
1101071d4279SBram Moolenaar     {
1102f2bd8ef2SBram Moolenaar #if defined(FEAT_EVAL)
1103c667da51SBram Moolenaar 	// Reset the error/interrupt/exception state here so that
1104c667da51SBram Moolenaar 	// aborting() returns FALSE when closing a buffer.
1105c0197e28SBram Moolenaar 	enter_cleanup(&cs);
1106c0197e28SBram Moolenaar #endif
1107c0197e28SBram Moolenaar 
1108c667da51SBram Moolenaar 	// User selected Quit at ATTENTION prompt.  Go back to previous
1109c667da51SBram Moolenaar 	// buffer.  If that buffer is gone or the same as the current one,
1110c667da51SBram Moolenaar 	// open a new, empty buffer.
1111c667da51SBram Moolenaar 	swap_exists_action = SEA_NONE;	// don't want it again
111212033fb4SBram Moolenaar 	swap_exists_did_quit = TRUE;
1113a6e8f888SBram Moolenaar 	close_buffer(curwin, curbuf, DOBUF_UNLOAD, FALSE, FALSE);
11147c0a2f36SBram Moolenaar 	if (old_curbuf == NULL || !bufref_valid(old_curbuf)
11157c0a2f36SBram Moolenaar 					      || old_curbuf->br_buf == curbuf)
11161d97efceSBram Moolenaar 	{
11171d97efceSBram Moolenaar 	    // Block autocommands here because curwin->w_buffer is NULL.
11181d97efceSBram Moolenaar 	    block_autocmds();
11197c0a2f36SBram Moolenaar 	    buf = buflist_new(NULL, NULL, 1L, BLN_CURBUF | BLN_LISTED);
11201d97efceSBram Moolenaar 	    unblock_autocmds();
11211d97efceSBram Moolenaar 	}
11227c0a2f36SBram Moolenaar 	else
11237c0a2f36SBram Moolenaar 	    buf = old_curbuf->br_buf;
11247c0a2f36SBram Moolenaar 	if (buf != NULL)
11256d47df7cSBram Moolenaar 	{
11262f0f8711SBram Moolenaar 	    int old_msg_silent = msg_silent;
11272f0f8711SBram Moolenaar 
11282f0f8711SBram Moolenaar 	    if (shortmess(SHM_FILEINFO))
11292f0f8711SBram Moolenaar 		msg_silent = 1;  // prevent fileinfo message
11307c0a2f36SBram Moolenaar 	    enter_buffer(buf);
11312f0f8711SBram Moolenaar 	    // restore msg_silent, so that the command line will be shown
11322f0f8711SBram Moolenaar 	    msg_silent = old_msg_silent;
11332f0f8711SBram Moolenaar 
11346d47df7cSBram Moolenaar #ifdef FEAT_SYN_HL
11356d47df7cSBram Moolenaar 	    if (old_tw != curbuf->b_p_tw)
11366d47df7cSBram Moolenaar 		check_colorcolumn(curwin);
11376d47df7cSBram Moolenaar #endif
11386d47df7cSBram Moolenaar 	}
1139c667da51SBram Moolenaar 	// If "old_curbuf" is NULL we are in big trouble here...
1140c0197e28SBram Moolenaar 
1141f2bd8ef2SBram Moolenaar #if defined(FEAT_EVAL)
1142c667da51SBram Moolenaar 	// Restore the error/interrupt/exception state if not discarded by a
1143c667da51SBram Moolenaar 	// new aborting error, interrupt, or uncaught exception.
1144c0197e28SBram Moolenaar 	leave_cleanup(&cs);
1145c0197e28SBram Moolenaar #endif
1146071d4279SBram Moolenaar     }
1147071d4279SBram Moolenaar     else if (swap_exists_action == SEA_RECOVER)
1148071d4279SBram Moolenaar     {
1149f2bd8ef2SBram Moolenaar #if defined(FEAT_EVAL)
1150c667da51SBram Moolenaar 	// Reset the error/interrupt/exception state here so that
1151c667da51SBram Moolenaar 	// aborting() returns FALSE when closing a buffer.
1152c0197e28SBram Moolenaar 	enter_cleanup(&cs);
1153c0197e28SBram Moolenaar #endif
1154c0197e28SBram Moolenaar 
1155c667da51SBram Moolenaar 	// User selected Recover at ATTENTION prompt.
1156071d4279SBram Moolenaar 	msg_scroll = TRUE;
115799499b1cSBram Moolenaar 	ml_recover(FALSE);
1158c667da51SBram Moolenaar 	msg_puts("\n");	// don't overwrite the last message
1159071d4279SBram Moolenaar 	cmdline_row = msg_row;
1160a3227e2bSBram Moolenaar 	do_modelines(0);
1161c0197e28SBram Moolenaar 
1162f2bd8ef2SBram Moolenaar #if defined(FEAT_EVAL)
1163c667da51SBram Moolenaar 	// Restore the error/interrupt/exception state if not discarded by a
1164c667da51SBram Moolenaar 	// new aborting error, interrupt, or uncaught exception.
1165c0197e28SBram Moolenaar 	leave_cleanup(&cs);
1166c0197e28SBram Moolenaar #endif
1167071d4279SBram Moolenaar     }
1168071d4279SBram Moolenaar     swap_exists_action = SEA_NONE;
1169071d4279SBram Moolenaar }
1170071d4279SBram Moolenaar 
1171071d4279SBram Moolenaar /*
1172a02471e2SBram Moolenaar  * Make the current buffer empty.
1173a02471e2SBram Moolenaar  * Used when it is wiped out and it's the last buffer.
1174a02471e2SBram Moolenaar  */
1175a02471e2SBram Moolenaar     static int
empty_curbuf(int close_others,int forceit,int action)11767454a06eSBram Moolenaar empty_curbuf(
11777454a06eSBram Moolenaar     int close_others,
11787454a06eSBram Moolenaar     int forceit,
11797454a06eSBram Moolenaar     int action)
1180a02471e2SBram Moolenaar {
1181a02471e2SBram Moolenaar     int	    retval;
1182a02471e2SBram Moolenaar     buf_T   *buf = curbuf;
1183b25f9a97SBram Moolenaar     bufref_T bufref;
1184a02471e2SBram Moolenaar 
1185a02471e2SBram Moolenaar     if (action == DOBUF_UNLOAD)
1186a02471e2SBram Moolenaar     {
1187f9e3e09fSBram Moolenaar 	emsg(_("E90: Cannot unload last buffer"));
1188a02471e2SBram Moolenaar 	return FAIL;
1189a02471e2SBram Moolenaar     }
1190a02471e2SBram Moolenaar 
1191b25f9a97SBram Moolenaar     set_bufref(&bufref, buf);
1192b25f9a97SBram Moolenaar     if (close_others)
1193c667da51SBram Moolenaar 	// Close any other windows on this buffer, then make it empty.
1194a02471e2SBram Moolenaar 	close_windows(buf, TRUE);
1195a02471e2SBram Moolenaar 
1196a02471e2SBram Moolenaar     setpcmark();
1197a02471e2SBram Moolenaar     retval = do_ecmd(0, NULL, NULL, NULL, ECMD_ONE,
1198a02471e2SBram Moolenaar 					  forceit ? ECMD_FORCEIT : 0, curwin);
1199a02471e2SBram Moolenaar 
1200c5935a85SBram Moolenaar     // do_ecmd() may create a new buffer, then we have to delete
1201c5935a85SBram Moolenaar     // the old one.  But do_ecmd() may have done that already, check
1202c5935a85SBram Moolenaar     // if the buffer still exists.
1203b25f9a97SBram Moolenaar     if (buf != curbuf && bufref_valid(&bufref) && buf->b_nwindows == 0)
1204a6e8f888SBram Moolenaar 	close_buffer(NULL, buf, action, FALSE, FALSE);
1205a02471e2SBram Moolenaar     if (!close_others)
1206a02471e2SBram Moolenaar 	need_fileinfo = FALSE;
1207a02471e2SBram Moolenaar     return retval;
1208a02471e2SBram Moolenaar }
120994f01956SBram Moolenaar 
1210071d4279SBram Moolenaar /*
1211071d4279SBram Moolenaar  * Implementation of the commands for the buffer list.
1212071d4279SBram Moolenaar  *
1213071d4279SBram Moolenaar  * action == DOBUF_GOTO	    go to specified buffer
1214071d4279SBram Moolenaar  * action == DOBUF_SPLIT    split window and go to specified buffer
1215071d4279SBram Moolenaar  * action == DOBUF_UNLOAD   unload specified buffer(s)
1216071d4279SBram Moolenaar  * action == DOBUF_DEL	    delete specified buffer(s) from buffer list
1217071d4279SBram Moolenaar  * action == DOBUF_WIPE	    delete specified buffer(s) really
121800b0d6d8SBram Moolenaar  * action == DOBUF_WIPE_REUSE idem, and add number to "buf_reuse"
1219071d4279SBram Moolenaar  *
1220071d4279SBram Moolenaar  * start == DOBUF_CURRENT   go to "count" buffer from current buffer
1221071d4279SBram Moolenaar  * start == DOBUF_FIRST	    go to "count" buffer from first buffer
1222071d4279SBram Moolenaar  * start == DOBUF_LAST	    go to "count" buffer from last buffer
1223071d4279SBram Moolenaar  * start == DOBUF_MOD	    go to "count" modified buffer from current buffer
1224071d4279SBram Moolenaar  *
1225071d4279SBram Moolenaar  * Return FAIL or OK.
1226071d4279SBram Moolenaar  */
12277b4f76c0SBram Moolenaar     static int
do_buffer_ext(int action,int start,int dir,int count,int flags)12287b4f76c0SBram Moolenaar do_buffer_ext(
12297454a06eSBram Moolenaar     int		action,
12307454a06eSBram Moolenaar     int		start,
1231c667da51SBram Moolenaar     int		dir,		// FORWARD or BACKWARD
1232c667da51SBram Moolenaar     int		count,		// buffer number or number of buffers
12337b4f76c0SBram Moolenaar     int		flags)		// DOBUF_FORCEIT etc.
1234071d4279SBram Moolenaar {
1235071d4279SBram Moolenaar     buf_T	*buf;
1236071d4279SBram Moolenaar     buf_T	*bp;
1237071d4279SBram Moolenaar     int		unload = (action == DOBUF_UNLOAD || action == DOBUF_DEL
123800b0d6d8SBram Moolenaar 			|| action == DOBUF_WIPE || action == DOBUF_WIPE_REUSE);
1239071d4279SBram Moolenaar 
1240071d4279SBram Moolenaar     switch (start)
1241071d4279SBram Moolenaar     {
1242071d4279SBram Moolenaar 	case DOBUF_FIRST:   buf = firstbuf; break;
1243071d4279SBram Moolenaar 	case DOBUF_LAST:    buf = lastbuf;  break;
1244071d4279SBram Moolenaar 	default:	    buf = curbuf;   break;
1245071d4279SBram Moolenaar     }
1246c667da51SBram Moolenaar     if (start == DOBUF_MOD)	    // find next modified buffer
1247071d4279SBram Moolenaar     {
1248071d4279SBram Moolenaar 	while (count-- > 0)
1249071d4279SBram Moolenaar 	{
1250071d4279SBram Moolenaar 	    do
1251071d4279SBram Moolenaar 	    {
1252071d4279SBram Moolenaar 		buf = buf->b_next;
1253071d4279SBram Moolenaar 		if (buf == NULL)
1254071d4279SBram Moolenaar 		    buf = firstbuf;
1255071d4279SBram Moolenaar 	    }
1256071d4279SBram Moolenaar 	    while (buf != curbuf && !bufIsChanged(buf));
1257071d4279SBram Moolenaar 	}
1258071d4279SBram Moolenaar 	if (!bufIsChanged(buf))
1259071d4279SBram Moolenaar 	{
1260f9e3e09fSBram Moolenaar 	    emsg(_("E84: No modified buffer found"));
1261071d4279SBram Moolenaar 	    return FAIL;
1262071d4279SBram Moolenaar 	}
1263071d4279SBram Moolenaar     }
1264c667da51SBram Moolenaar     else if (start == DOBUF_FIRST && count) // find specified buffer number
1265071d4279SBram Moolenaar     {
1266071d4279SBram Moolenaar 	while (buf != NULL && buf->b_fnum != count)
1267071d4279SBram Moolenaar 	    buf = buf->b_next;
1268071d4279SBram Moolenaar     }
1269071d4279SBram Moolenaar     else
1270071d4279SBram Moolenaar     {
1271071d4279SBram Moolenaar 	bp = NULL;
1272071d4279SBram Moolenaar 	while (count > 0 || (!unload && !buf->b_p_bl && bp != buf))
1273071d4279SBram Moolenaar 	{
1274c667da51SBram Moolenaar 	    // remember the buffer where we start, we come back there when all
1275c667da51SBram Moolenaar 	    // buffers are unlisted.
1276071d4279SBram Moolenaar 	    if (bp == NULL)
1277071d4279SBram Moolenaar 		bp = buf;
1278071d4279SBram Moolenaar 	    if (dir == FORWARD)
1279071d4279SBram Moolenaar 	    {
1280071d4279SBram Moolenaar 		buf = buf->b_next;
1281071d4279SBram Moolenaar 		if (buf == NULL)
1282071d4279SBram Moolenaar 		    buf = firstbuf;
1283071d4279SBram Moolenaar 	    }
1284071d4279SBram Moolenaar 	    else
1285071d4279SBram Moolenaar 	    {
1286071d4279SBram Moolenaar 		buf = buf->b_prev;
1287071d4279SBram Moolenaar 		if (buf == NULL)
1288071d4279SBram Moolenaar 		    buf = lastbuf;
1289071d4279SBram Moolenaar 	    }
1290c667da51SBram Moolenaar 	    // don't count unlisted buffers
1291071d4279SBram Moolenaar 	    if (unload || buf->b_p_bl)
1292071d4279SBram Moolenaar 	    {
1293071d4279SBram Moolenaar 		 --count;
1294c667da51SBram Moolenaar 		 bp = NULL;	// use this buffer as new starting point
1295071d4279SBram Moolenaar 	    }
1296071d4279SBram Moolenaar 	    if (bp == buf)
1297071d4279SBram Moolenaar 	    {
1298c667da51SBram Moolenaar 		// back where we started, didn't find anything.
1299f9e3e09fSBram Moolenaar 		emsg(_("E85: There is no listed buffer"));
1300071d4279SBram Moolenaar 		return FAIL;
1301071d4279SBram Moolenaar 	    }
1302071d4279SBram Moolenaar 	}
1303071d4279SBram Moolenaar     }
1304071d4279SBram Moolenaar 
1305c667da51SBram Moolenaar     if (buf == NULL)	    // could not find it
1306071d4279SBram Moolenaar     {
1307071d4279SBram Moolenaar 	if (start == DOBUF_FIRST)
1308071d4279SBram Moolenaar 	{
1309c667da51SBram Moolenaar 	    // don't warn when deleting
1310071d4279SBram Moolenaar 	    if (!unload)
1311f9e3e09fSBram Moolenaar 		semsg(_(e_nobufnr), count);
1312071d4279SBram Moolenaar 	}
1313071d4279SBram Moolenaar 	else if (dir == FORWARD)
1314f9e3e09fSBram Moolenaar 	    emsg(_("E87: Cannot go beyond last buffer"));
1315071d4279SBram Moolenaar 	else
1316f9e3e09fSBram Moolenaar 	    emsg(_("E88: Cannot go before first buffer"));
1317071d4279SBram Moolenaar 	return FAIL;
1318071d4279SBram Moolenaar     }
13197b4f76c0SBram Moolenaar #ifdef FEAT_PROP_POPUP
13207b4f76c0SBram Moolenaar     if ((flags & DOBUF_NOPOPUP) && bt_popup(buf)
13217b4f76c0SBram Moolenaar # ifdef FEAT_TERMINAL
13227b4f76c0SBram Moolenaar 				&& !bt_terminal(buf)
13237b4f76c0SBram Moolenaar #endif
13247b4f76c0SBram Moolenaar        )
13257b4f76c0SBram Moolenaar 	return OK;
13267b4f76c0SBram Moolenaar #endif
1327071d4279SBram Moolenaar 
1328071d4279SBram Moolenaar #ifdef FEAT_GUI
1329071d4279SBram Moolenaar     need_mouse_correct = TRUE;
1330071d4279SBram Moolenaar #endif
1331071d4279SBram Moolenaar 
1332071d4279SBram Moolenaar     /*
1333c5935a85SBram Moolenaar      * delete buffer "buf" from memory and/or the list
1334071d4279SBram Moolenaar      */
1335071d4279SBram Moolenaar     if (unload)
1336071d4279SBram Moolenaar     {
1337071d4279SBram Moolenaar 	int	forward;
1338b25f9a97SBram Moolenaar 	bufref_T bufref;
1339b25f9a97SBram Moolenaar 
134094f01956SBram Moolenaar 	if (!can_unload_buffer(buf))
1341a997b45cSBram Moolenaar 	    return FAIL;
1342a997b45cSBram Moolenaar 
1343b25f9a97SBram Moolenaar 	set_bufref(&bufref, buf);
1344071d4279SBram Moolenaar 
1345c667da51SBram Moolenaar 	// When unloading or deleting a buffer that's already unloaded and
1346c667da51SBram Moolenaar 	// unlisted: fail silently.
134700b0d6d8SBram Moolenaar 	if (action != DOBUF_WIPE && action != DOBUF_WIPE_REUSE
134800b0d6d8SBram Moolenaar 				   && buf->b_ml.ml_mfp == NULL && !buf->b_p_bl)
1349071d4279SBram Moolenaar 	    return FAIL;
1350071d4279SBram Moolenaar 
13517b4f76c0SBram Moolenaar 	if ((flags & DOBUF_FORCEIT) == 0 && bufIsChanged(buf))
1352071d4279SBram Moolenaar 	{
1353071d4279SBram Moolenaar #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
1354e1004401SBram Moolenaar 	    if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write)
1355071d4279SBram Moolenaar 	    {
1356071d4279SBram Moolenaar 		dialog_changed(buf, FALSE);
1357b25f9a97SBram Moolenaar 		if (!bufref_valid(&bufref))
1358c667da51SBram Moolenaar 		    // Autocommand deleted buffer, oops!  It's not changed
1359c667da51SBram Moolenaar 		    // now.
1360071d4279SBram Moolenaar 		    return FAIL;
1361c667da51SBram Moolenaar 		// If it's still changed fail silently, the dialog already
1362c667da51SBram Moolenaar 		// mentioned why it fails.
1363071d4279SBram Moolenaar 		if (bufIsChanged(buf))
1364293ee4d4SBram Moolenaar 		    return FAIL;
1365293ee4d4SBram Moolenaar 	    }
1366293ee4d4SBram Moolenaar 	    else
1367071d4279SBram Moolenaar #endif
1368071d4279SBram Moolenaar 	    {
1369b5443cc4SBram Moolenaar 		semsg(_("E89: No write since last change for buffer %d (add ! to override)"),
1370071d4279SBram Moolenaar 								 buf->b_fnum);
1371071d4279SBram Moolenaar 		return FAIL;
1372071d4279SBram Moolenaar 	    }
1373071d4279SBram Moolenaar 	}
1374071d4279SBram Moolenaar 
1375c667da51SBram Moolenaar 	// When closing the current buffer stop Visual mode.
13764930a76aSBram Moolenaar 	if (buf == curbuf && VIsual_active)
1377c4a908e8SBram Moolenaar 	    end_visual_mode();
1378c4a908e8SBram Moolenaar 
1379c5935a85SBram Moolenaar 	// If deleting the last (listed) buffer, make it empty.
1380c5935a85SBram Moolenaar 	// The last (listed) buffer cannot be unloaded.
138129323590SBram Moolenaar 	FOR_ALL_BUFFERS(bp)
1382071d4279SBram Moolenaar 	    if (bp->b_p_bl && bp != buf)
1383071d4279SBram Moolenaar 		break;
1384071d4279SBram Moolenaar 	if (bp == NULL && buf == curbuf)
13857b4f76c0SBram Moolenaar 	    return empty_curbuf(TRUE, (flags & DOBUF_FORCEIT), action);
1386071d4279SBram Moolenaar 
1387c5935a85SBram Moolenaar 	// If the deleted buffer is the current one, close the current window
1388c5935a85SBram Moolenaar 	// (unless it's the only window).  Repeat this so long as we end up in
1389c5935a85SBram Moolenaar 	// a window with this buffer.
1390f740b29aSBram Moolenaar 	while (buf == curbuf
1391e0ab94e7SBram Moolenaar 		   && !(curwin->w_closing || curwin->w_buffer->b_locked > 0)
1392459ca563SBram Moolenaar 		   && (!ONE_WINDOW || first_tabpage->tp_next != NULL))
1393c93df6b0SBram Moolenaar 	{
1394c93df6b0SBram Moolenaar 	    if (win_close(curwin, FALSE) == FAIL)
1395c93df6b0SBram Moolenaar 		break;
1396c93df6b0SBram Moolenaar 	}
1397071d4279SBram Moolenaar 
1398c5935a85SBram Moolenaar 	// If the buffer to be deleted is not the current one, delete it here.
1399071d4279SBram Moolenaar 	if (buf != curbuf)
1400071d4279SBram Moolenaar 	{
1401f740b29aSBram Moolenaar 	    close_windows(buf, FALSE);
14024033c55eSBram Moolenaar 	    if (buf != curbuf && bufref_valid(&bufref) && buf->b_nwindows <= 0)
1403a6e8f888SBram Moolenaar 		    close_buffer(NULL, buf, action, FALSE, FALSE);
1404071d4279SBram Moolenaar 	    return OK;
1405071d4279SBram Moolenaar 	}
1406071d4279SBram Moolenaar 
1407071d4279SBram Moolenaar 	/*
1408071d4279SBram Moolenaar 	 * Deleting the current buffer: Need to find another buffer to go to.
1409a02471e2SBram Moolenaar 	 * There should be another, otherwise it would have been handled
1410a02471e2SBram Moolenaar 	 * above.  However, autocommands may have deleted all buffers.
141119ff9bf4SBram Moolenaar 	 * First use au_new_curbuf.br_buf, if it is valid.
1412071d4279SBram Moolenaar 	 * Then prefer the buffer we most recently visited.
1413071d4279SBram Moolenaar 	 * Else try to find one that is loaded, after the current buffer,
1414071d4279SBram Moolenaar 	 * then before the current buffer.
1415071d4279SBram Moolenaar 	 * Finally use any buffer.
1416071d4279SBram Moolenaar 	 */
1417c667da51SBram Moolenaar 	buf = NULL;	// selected buffer
1418c667da51SBram Moolenaar 	bp = NULL;	// used when no loaded buffer found
141919ff9bf4SBram Moolenaar 	if (au_new_curbuf.br_buf != NULL && bufref_valid(&au_new_curbuf))
142019ff9bf4SBram Moolenaar 	    buf = au_new_curbuf.br_buf;
1421071d4279SBram Moolenaar #ifdef FEAT_JUMPLIST
1422f2bd8ef2SBram Moolenaar 	else if (curwin->w_jumplistlen > 0)
1423071d4279SBram Moolenaar 	{
1424071d4279SBram Moolenaar 	    int     jumpidx;
1425071d4279SBram Moolenaar 
1426071d4279SBram Moolenaar 	    jumpidx = curwin->w_jumplistidx - 1;
1427071d4279SBram Moolenaar 	    if (jumpidx < 0)
1428071d4279SBram Moolenaar 		jumpidx = curwin->w_jumplistlen - 1;
1429071d4279SBram Moolenaar 
1430071d4279SBram Moolenaar 	    forward = jumpidx;
1431071d4279SBram Moolenaar 	    while (jumpidx != curwin->w_jumplistidx)
1432071d4279SBram Moolenaar 	    {
1433071d4279SBram Moolenaar 		buf = buflist_findnr(curwin->w_jumplist[jumpidx].fmark.fnum);
1434071d4279SBram Moolenaar 		if (buf != NULL)
1435071d4279SBram Moolenaar 		{
1436071d4279SBram Moolenaar 		    if (buf == curbuf || !buf->b_p_bl)
1437c667da51SBram Moolenaar 			buf = NULL;	// skip current and unlisted bufs
1438071d4279SBram Moolenaar 		    else if (buf->b_ml.ml_mfp == NULL)
1439071d4279SBram Moolenaar 		    {
1440c667da51SBram Moolenaar 			// skip unloaded buf, but may keep it for later
1441071d4279SBram Moolenaar 			if (bp == NULL)
1442071d4279SBram Moolenaar 			    bp = buf;
1443071d4279SBram Moolenaar 			buf = NULL;
1444071d4279SBram Moolenaar 		    }
1445071d4279SBram Moolenaar 		}
1446c667da51SBram Moolenaar 		if (buf != NULL)   // found a valid buffer: stop searching
1447071d4279SBram Moolenaar 		    break;
1448c667da51SBram Moolenaar 		// advance to older entry in jump list
1449071d4279SBram Moolenaar 		if (!jumpidx && curwin->w_jumplistidx == curwin->w_jumplistlen)
1450071d4279SBram Moolenaar 		    break;
1451071d4279SBram Moolenaar 		if (--jumpidx < 0)
1452071d4279SBram Moolenaar 		    jumpidx = curwin->w_jumplistlen - 1;
1453c667da51SBram Moolenaar 		if (jumpidx == forward)		// List exhausted for sure
1454071d4279SBram Moolenaar 		    break;
1455071d4279SBram Moolenaar 	    }
1456071d4279SBram Moolenaar 	}
1457071d4279SBram Moolenaar #endif
1458071d4279SBram Moolenaar 
1459c667da51SBram Moolenaar 	if (buf == NULL)	// No previous buffer, Try 2'nd approach
1460071d4279SBram Moolenaar 	{
1461071d4279SBram Moolenaar 	    forward = TRUE;
1462071d4279SBram Moolenaar 	    buf = curbuf->b_next;
1463071d4279SBram Moolenaar 	    for (;;)
1464071d4279SBram Moolenaar 	    {
1465071d4279SBram Moolenaar 		if (buf == NULL)
1466071d4279SBram Moolenaar 		{
1467c667da51SBram Moolenaar 		    if (!forward)	// tried both directions
1468071d4279SBram Moolenaar 			break;
1469071d4279SBram Moolenaar 		    buf = curbuf->b_prev;
1470071d4279SBram Moolenaar 		    forward = FALSE;
1471071d4279SBram Moolenaar 		    continue;
1472071d4279SBram Moolenaar 		}
1473c667da51SBram Moolenaar 		// in non-help buffer, try to skip help buffers, and vv
1474071d4279SBram Moolenaar 		if (buf->b_help == curbuf->b_help && buf->b_p_bl)
1475071d4279SBram Moolenaar 		{
1476c667da51SBram Moolenaar 		    if (buf->b_ml.ml_mfp != NULL)   // found loaded buffer
1477071d4279SBram Moolenaar 			break;
1478c667da51SBram Moolenaar 		    if (bp == NULL)	// remember unloaded buf for later
1479071d4279SBram Moolenaar 			bp = buf;
1480071d4279SBram Moolenaar 		}
1481071d4279SBram Moolenaar 		if (forward)
1482071d4279SBram Moolenaar 		    buf = buf->b_next;
1483071d4279SBram Moolenaar 		else
1484071d4279SBram Moolenaar 		    buf = buf->b_prev;
1485071d4279SBram Moolenaar 	    }
1486071d4279SBram Moolenaar 	}
1487c667da51SBram Moolenaar 	if (buf == NULL)	// No loaded buffer, use unloaded one
1488071d4279SBram Moolenaar 	    buf = bp;
1489c667da51SBram Moolenaar 	if (buf == NULL)	// No loaded buffer, find listed one
1490071d4279SBram Moolenaar 	{
149129323590SBram Moolenaar 	    FOR_ALL_BUFFERS(buf)
1492071d4279SBram Moolenaar 		if (buf->b_p_bl && buf != curbuf)
1493071d4279SBram Moolenaar 		    break;
1494071d4279SBram Moolenaar 	}
1495c667da51SBram Moolenaar 	if (buf == NULL)	// Still no buffer, just take one
1496071d4279SBram Moolenaar 	{
1497071d4279SBram Moolenaar 	    if (curbuf->b_next != NULL)
1498071d4279SBram Moolenaar 		buf = curbuf->b_next;
1499071d4279SBram Moolenaar 	    else
1500071d4279SBram Moolenaar 		buf = curbuf->b_prev;
1501071d4279SBram Moolenaar 	}
1502071d4279SBram Moolenaar     }
1503071d4279SBram Moolenaar 
1504a02471e2SBram Moolenaar     if (buf == NULL)
1505a02471e2SBram Moolenaar     {
1506c667da51SBram Moolenaar 	// Autocommands must have wiped out all other buffers.  Only option
1507c667da51SBram Moolenaar 	// now is to make the current buffer empty.
15087b4f76c0SBram Moolenaar 	return empty_curbuf(FALSE, (flags & DOBUF_FORCEIT), action);
1509a02471e2SBram Moolenaar     }
1510a02471e2SBram Moolenaar 
1511071d4279SBram Moolenaar     /*
1512c5935a85SBram Moolenaar      * make "buf" the current buffer
1513071d4279SBram Moolenaar      */
1514c667da51SBram Moolenaar     if (action == DOBUF_SPLIT)	    // split window first
1515071d4279SBram Moolenaar     {
1516c667da51SBram Moolenaar 	// If 'switchbuf' contains "useopen": jump to first window containing
1517c667da51SBram Moolenaar 	// "buf" if one exists
1518f233048aSBram Moolenaar 	if ((swb_flags & SWB_USEOPEN) && buf_jump_open_win(buf))
1519779b74b2SBram Moolenaar 	    return OK;
1520c667da51SBram Moolenaar 	// If 'switchbuf' contains "usetab": jump to first window in any tab
1521c667da51SBram Moolenaar 	// page containing "buf" if one exists
1522f233048aSBram Moolenaar 	if ((swb_flags & SWB_USETAB) && buf_jump_open_tab(buf))
1523071d4279SBram Moolenaar 	    return OK;
1524071d4279SBram Moolenaar 	if (win_split(0, 0) == FAIL)
1525071d4279SBram Moolenaar 	    return FAIL;
1526071d4279SBram Moolenaar     }
1527071d4279SBram Moolenaar 
1528c667da51SBram Moolenaar     // go to current buffer - nothing to do
1529071d4279SBram Moolenaar     if (buf == curbuf)
1530071d4279SBram Moolenaar 	return OK;
1531071d4279SBram Moolenaar 
1532c5935a85SBram Moolenaar     // Check if the current buffer may be abandoned.
15337b4f76c0SBram Moolenaar     if (action == DOBUF_GOTO && !can_abandon(curbuf, (flags & DOBUF_FORCEIT)))
1534071d4279SBram Moolenaar     {
1535071d4279SBram Moolenaar #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
1536e1004401SBram Moolenaar 	if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write)
1537071d4279SBram Moolenaar 	{
1538b25f9a97SBram Moolenaar 	    bufref_T bufref;
1539b25f9a97SBram Moolenaar 
1540b25f9a97SBram Moolenaar 	    set_bufref(&bufref, buf);
1541071d4279SBram Moolenaar 	    dialog_changed(curbuf, FALSE);
1542b25f9a97SBram Moolenaar 	    if (!bufref_valid(&bufref))
1543c667da51SBram Moolenaar 		// Autocommand deleted buffer, oops!
1544071d4279SBram Moolenaar 		return FAIL;
1545071d4279SBram Moolenaar 	}
1546071d4279SBram Moolenaar 	if (bufIsChanged(curbuf))
1547071d4279SBram Moolenaar #endif
1548071d4279SBram Moolenaar 	{
1549f5be7cd0SBram Moolenaar 	    no_write_message();
1550071d4279SBram Moolenaar 	    return FAIL;
1551071d4279SBram Moolenaar 	}
1552071d4279SBram Moolenaar     }
1553071d4279SBram Moolenaar 
1554c667da51SBram Moolenaar     // Go to the other buffer.
1555071d4279SBram Moolenaar     set_curbuf(buf, action);
1556071d4279SBram Moolenaar 
1557071d4279SBram Moolenaar     if (action == DOBUF_SPLIT)
1558c667da51SBram Moolenaar 	RESET_BINDING(curwin);	// reset 'scrollbind' and 'cursorbind'
1559071d4279SBram Moolenaar 
1560f2bd8ef2SBram Moolenaar #if defined(FEAT_EVAL)
1561c667da51SBram Moolenaar     if (aborting())	    // autocmds may abort script processing
1562071d4279SBram Moolenaar 	return FAIL;
1563071d4279SBram Moolenaar #endif
1564071d4279SBram Moolenaar 
1565071d4279SBram Moolenaar     return OK;
1566071d4279SBram Moolenaar }
1567071d4279SBram Moolenaar 
15687b4f76c0SBram Moolenaar     int
do_buffer(int action,int start,int dir,int count,int forceit)15697b4f76c0SBram Moolenaar do_buffer(
15707b4f76c0SBram Moolenaar     int		action,
15717b4f76c0SBram Moolenaar     int		start,
15727b4f76c0SBram Moolenaar     int		dir,		// FORWARD or BACKWARD
15737b4f76c0SBram Moolenaar     int		count,		// buffer number or number of buffers
15747b4f76c0SBram Moolenaar     int		forceit)	// TRUE when using !
15757b4f76c0SBram Moolenaar {
15767b4f76c0SBram Moolenaar     return do_buffer_ext(action, start, dir, count,
15777b4f76c0SBram Moolenaar 						  forceit ? DOBUF_FORCEIT : 0);
15787b4f76c0SBram Moolenaar }
15797b4f76c0SBram Moolenaar 
15807b4f76c0SBram Moolenaar /*
15817b4f76c0SBram Moolenaar  * do_bufdel() - delete or unload buffer(s)
15827b4f76c0SBram Moolenaar  *
15837b4f76c0SBram Moolenaar  * addr_count == 0: ":bdel" - delete current buffer
15847b4f76c0SBram Moolenaar  * addr_count == 1: ":N bdel" or ":bdel N [N ..]" - first delete
15857b4f76c0SBram Moolenaar  *		    buffer "end_bnr", then any other arguments.
15867b4f76c0SBram Moolenaar  * addr_count == 2: ":N,N bdel" - delete buffers in range
15877b4f76c0SBram Moolenaar  *
15887b4f76c0SBram Moolenaar  * command can be DOBUF_UNLOAD (":bunload"), DOBUF_WIPE (":bwipeout") or
15897b4f76c0SBram Moolenaar  * DOBUF_DEL (":bdel")
15907b4f76c0SBram Moolenaar  *
15917b4f76c0SBram Moolenaar  * Returns error message or NULL
15927b4f76c0SBram Moolenaar  */
15937b4f76c0SBram Moolenaar     char *
do_bufdel(int command,char_u * arg,int addr_count,int start_bnr,int end_bnr,int forceit)15947b4f76c0SBram Moolenaar do_bufdel(
15957b4f76c0SBram Moolenaar     int		command,
15967b4f76c0SBram Moolenaar     char_u	*arg,		// pointer to extra arguments
15977b4f76c0SBram Moolenaar     int		addr_count,
15987b4f76c0SBram Moolenaar     int		start_bnr,	// first buffer number in a range
15997b4f76c0SBram Moolenaar     int		end_bnr,	// buffer nr or last buffer nr in a range
16007b4f76c0SBram Moolenaar     int		forceit)
16017b4f76c0SBram Moolenaar {
16027b4f76c0SBram Moolenaar     int		do_current = 0;	// delete current buffer?
16037b4f76c0SBram Moolenaar     int		deleted = 0;	// number of buffers deleted
16047b4f76c0SBram Moolenaar     char	*errormsg = NULL; // return value
16057b4f76c0SBram Moolenaar     int		bnr;		// buffer number
16067b4f76c0SBram Moolenaar     char_u	*p;
16077b4f76c0SBram Moolenaar 
16087b4f76c0SBram Moolenaar     if (addr_count == 0)
16097b4f76c0SBram Moolenaar     {
16107b4f76c0SBram Moolenaar 	(void)do_buffer(command, DOBUF_CURRENT, FORWARD, 0, forceit);
16117b4f76c0SBram Moolenaar     }
16127b4f76c0SBram Moolenaar     else
16137b4f76c0SBram Moolenaar     {
16147b4f76c0SBram Moolenaar 	if (addr_count == 2)
16157b4f76c0SBram Moolenaar 	{
16167b4f76c0SBram Moolenaar 	    if (*arg)		// both range and argument is not allowed
16177b4f76c0SBram Moolenaar 		return ex_errmsg(e_trailing_arg, arg);
16187b4f76c0SBram Moolenaar 	    bnr = start_bnr;
16197b4f76c0SBram Moolenaar 	}
16207b4f76c0SBram Moolenaar 	else	// addr_count == 1
16217b4f76c0SBram Moolenaar 	    bnr = end_bnr;
16227b4f76c0SBram Moolenaar 
16237b4f76c0SBram Moolenaar 	for ( ;!got_int; ui_breakcheck())
16247b4f76c0SBram Moolenaar 	{
1625c5935a85SBram Moolenaar 	    // Delete the current buffer last, otherwise when the
1626c5935a85SBram Moolenaar 	    // current buffer is deleted, the next buffer becomes
1627c5935a85SBram Moolenaar 	    // the current one and will be loaded, which may then
1628c5935a85SBram Moolenaar 	    // also be deleted, etc.
16297b4f76c0SBram Moolenaar 	    if (bnr == curbuf->b_fnum)
16307b4f76c0SBram Moolenaar 		do_current = bnr;
16317b4f76c0SBram Moolenaar 	    else if (do_buffer_ext(command, DOBUF_FIRST, FORWARD, (int)bnr,
16327b4f76c0SBram Moolenaar 			  DOBUF_NOPOPUP | (forceit ? DOBUF_FORCEIT : 0)) == OK)
16337b4f76c0SBram Moolenaar 		++deleted;
16347b4f76c0SBram Moolenaar 
1635c5935a85SBram Moolenaar 	    // find next buffer number to delete/unload
16367b4f76c0SBram Moolenaar 	    if (addr_count == 2)
16377b4f76c0SBram Moolenaar 	    {
16387b4f76c0SBram Moolenaar 		if (++bnr > end_bnr)
16397b4f76c0SBram Moolenaar 		    break;
16407b4f76c0SBram Moolenaar 	    }
16417b4f76c0SBram Moolenaar 	    else    // addr_count == 1
16427b4f76c0SBram Moolenaar 	    {
16437b4f76c0SBram Moolenaar 		arg = skipwhite(arg);
16447b4f76c0SBram Moolenaar 		if (*arg == NUL)
16457b4f76c0SBram Moolenaar 		    break;
16467b4f76c0SBram Moolenaar 		if (!VIM_ISDIGIT(*arg))
16477b4f76c0SBram Moolenaar 		{
16487b4f76c0SBram Moolenaar 		    p = skiptowhite_esc(arg);
16497b4f76c0SBram Moolenaar 		    bnr = buflist_findpat(arg, p,
16507b4f76c0SBram Moolenaar 			  command == DOBUF_WIPE || command == DOBUF_WIPE_REUSE,
16517b4f76c0SBram Moolenaar 								FALSE, FALSE);
16527b4f76c0SBram Moolenaar 		    if (bnr < 0)	    // failed
16537b4f76c0SBram Moolenaar 			break;
16547b4f76c0SBram Moolenaar 		    arg = p;
16557b4f76c0SBram Moolenaar 		}
16567b4f76c0SBram Moolenaar 		else
16577b4f76c0SBram Moolenaar 		    bnr = getdigits(&arg);
16587b4f76c0SBram Moolenaar 	    }
16597b4f76c0SBram Moolenaar 	}
16607b4f76c0SBram Moolenaar 	if (!got_int && do_current && do_buffer(command, DOBUF_FIRST,
16617b4f76c0SBram Moolenaar 					  FORWARD, do_current, forceit) == OK)
16627b4f76c0SBram Moolenaar 	    ++deleted;
16637b4f76c0SBram Moolenaar 
16647b4f76c0SBram Moolenaar 	if (deleted == 0)
16657b4f76c0SBram Moolenaar 	{
16667b4f76c0SBram Moolenaar 	    if (command == DOBUF_UNLOAD)
16677b4f76c0SBram Moolenaar 		STRCPY(IObuff, _("E515: No buffers were unloaded"));
16687b4f76c0SBram Moolenaar 	    else if (command == DOBUF_DEL)
16697b4f76c0SBram Moolenaar 		STRCPY(IObuff, _("E516: No buffers were deleted"));
16707b4f76c0SBram Moolenaar 	    else
16717b4f76c0SBram Moolenaar 		STRCPY(IObuff, _("E517: No buffers were wiped out"));
16727b4f76c0SBram Moolenaar 	    errormsg = (char *)IObuff;
16737b4f76c0SBram Moolenaar 	}
16747b4f76c0SBram Moolenaar 	else if (deleted >= p_report)
16757b4f76c0SBram Moolenaar 	{
16767b4f76c0SBram Moolenaar 	    if (command == DOBUF_UNLOAD)
16777b4f76c0SBram Moolenaar 		smsg(NGETTEXT("%d buffer unloaded",
16787b4f76c0SBram Moolenaar 			    "%d buffers unloaded", deleted), deleted);
16797b4f76c0SBram Moolenaar 	    else if (command == DOBUF_DEL)
16807b4f76c0SBram Moolenaar 		smsg(NGETTEXT("%d buffer deleted",
16817b4f76c0SBram Moolenaar 			    "%d buffers deleted", deleted), deleted);
16827b4f76c0SBram Moolenaar 	    else
16837b4f76c0SBram Moolenaar 		smsg(NGETTEXT("%d buffer wiped out",
16847b4f76c0SBram Moolenaar 			    "%d buffers wiped out", deleted), deleted);
16857b4f76c0SBram Moolenaar 	}
16867b4f76c0SBram Moolenaar     }
16877b4f76c0SBram Moolenaar 
16887b4f76c0SBram Moolenaar 
16897b4f76c0SBram Moolenaar     return errormsg;
16907b4f76c0SBram Moolenaar }
16917b4f76c0SBram Moolenaar 
1692071d4279SBram Moolenaar /*
1693071d4279SBram Moolenaar  * Set current buffer to "buf".  Executes autocommands and closes current
1694071d4279SBram Moolenaar  * buffer.  "action" tells how to close the current buffer:
1695071d4279SBram Moolenaar  * DOBUF_GOTO	    free or hide it
1696071d4279SBram Moolenaar  * DOBUF_SPLIT	    nothing
1697071d4279SBram Moolenaar  * DOBUF_UNLOAD	    unload it
1698071d4279SBram Moolenaar  * DOBUF_DEL	    delete it
1699071d4279SBram Moolenaar  * DOBUF_WIPE	    wipe it out
170000b0d6d8SBram Moolenaar  * DOBUF_WIPE_REUSE wipe it out and add to "buf_reuse"
1701071d4279SBram Moolenaar  */
1702071d4279SBram Moolenaar     void
set_curbuf(buf_T * buf,int action)17037454a06eSBram Moolenaar set_curbuf(buf_T *buf, int action)
1704071d4279SBram Moolenaar {
1705071d4279SBram Moolenaar     buf_T	*prevbuf;
1706071d4279SBram Moolenaar     int		unload = (action == DOBUF_UNLOAD || action == DOBUF_DEL
170700b0d6d8SBram Moolenaar 			|| action == DOBUF_WIPE || action == DOBUF_WIPE_REUSE);
17086d47df7cSBram Moolenaar #ifdef FEAT_SYN_HL
17096d47df7cSBram Moolenaar     long	old_tw = curbuf->b_p_tw;
17106d47df7cSBram Moolenaar #endif
17119bca805eSBram Moolenaar     bufref_T	newbufref;
17129bca805eSBram Moolenaar     bufref_T	prevbufref;
1713071d4279SBram Moolenaar 
1714071d4279SBram Moolenaar     setpcmark();
1715e1004401SBram Moolenaar     if ((cmdmod.cmod_flags & CMOD_KEEPALT) == 0)
1716c667da51SBram Moolenaar 	curwin->w_alt_fnum = curbuf->b_fnum; // remember alternate file
1717c667da51SBram Moolenaar     buflist_altfpos(curwin);			 // remember curpos
1718071d4279SBram Moolenaar 
1719c667da51SBram Moolenaar     // Don't restart Select mode after switching to another buffer.
1720071d4279SBram Moolenaar     VIsual_reselect = FALSE;
1721071d4279SBram Moolenaar 
1722c667da51SBram Moolenaar     // close_windows() or apply_autocmds() may change curbuf and wipe out "buf"
1723071d4279SBram Moolenaar     prevbuf = curbuf;
17249bca805eSBram Moolenaar     set_bufref(&prevbufref, prevbuf);
17259bca805eSBram Moolenaar     set_bufref(&newbufref, buf);
1726071d4279SBram Moolenaar 
1727983d83ffSBram Moolenaar     // Autocommands may delete the current buffer and/or the buffer we want to
1728983d83ffSBram Moolenaar     // go to.  In those cases don't close the buffer.
17298240433fSBram Moolenaar     if (!apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf)
17309bca805eSBram Moolenaar 	    || (bufref_valid(&prevbufref)
17319bca805eSBram Moolenaar 		&& bufref_valid(&newbufref)
1732071d4279SBram Moolenaar #ifdef FEAT_EVAL
17339bca805eSBram Moolenaar 		&& !aborting()
1734071d4279SBram Moolenaar #endif
17359bca805eSBram Moolenaar 	       ))
1736071d4279SBram Moolenaar     {
1737a971b82bSBram Moolenaar #ifdef FEAT_SYN_HL
1738a971b82bSBram Moolenaar 	if (prevbuf == curwin->w_buffer)
1739a971b82bSBram Moolenaar 	    reset_synblock(curwin);
1740a971b82bSBram Moolenaar #endif
1741071d4279SBram Moolenaar 	if (unload)
1742f740b29aSBram Moolenaar 	    close_windows(prevbuf, FALSE);
1743f2bd8ef2SBram Moolenaar #if defined(FEAT_EVAL)
17449bca805eSBram Moolenaar 	if (bufref_valid(&prevbufref) && !aborting())
1745071d4279SBram Moolenaar #else
17469bca805eSBram Moolenaar 	if (bufref_valid(&prevbufref))
1747071d4279SBram Moolenaar #endif
1748607a95edSBram Moolenaar 	{
1749e25865a7SBram Moolenaar 	    win_T  *previouswin = curwin;
17504b96df5aSBram Moolenaar 
1751607a95edSBram Moolenaar 	    if (prevbuf == curbuf)
1752779b74b2SBram Moolenaar 		u_sync(FALSE);
1753071d4279SBram Moolenaar 	    close_buffer(prevbuf == curwin->w_buffer ? curwin : NULL, prevbuf,
1754071d4279SBram Moolenaar 		    unload ? action : (action == DOBUF_GOTO
1755eb44a68bSBram Moolenaar 			&& !buf_hide(prevbuf)
1756a6e8f888SBram Moolenaar 			&& !bufIsChanged(prevbuf)) ? DOBUF_UNLOAD : 0,
1757a6e8f888SBram Moolenaar 		    FALSE, FALSE);
1758e25865a7SBram Moolenaar 	    if (curwin != previouswin && win_valid(previouswin))
1759c667da51SBram Moolenaar 	      // autocommands changed curwin, Grr!
1760e25865a7SBram Moolenaar 	      curwin = previouswin;
1761071d4279SBram Moolenaar 	}
1762607a95edSBram Moolenaar     }
1763c667da51SBram Moolenaar     // An autocommand may have deleted "buf", already entered it (e.g., when
1764c667da51SBram Moolenaar     // it did ":bunload") or aborted the script processing.
1765c667da51SBram Moolenaar     // If curwin->w_buffer is null, enter_buffer() will make it valid again
17669e931224SBram Moolenaar     if ((buf_valid(buf) && buf != curbuf
1767071d4279SBram Moolenaar #ifdef FEAT_EVAL
17689e931224SBram Moolenaar 		&& !aborting()
1769071d4279SBram Moolenaar #endif
17704033c55eSBram Moolenaar 	) || curwin->w_buffer == NULL)
17716d47df7cSBram Moolenaar     {
1772071d4279SBram Moolenaar 	enter_buffer(buf);
17736d47df7cSBram Moolenaar #ifdef FEAT_SYN_HL
17746d47df7cSBram Moolenaar 	if (old_tw != curbuf->b_p_tw)
17756d47df7cSBram Moolenaar 	    check_colorcolumn(curwin);
17766d47df7cSBram Moolenaar #endif
17776d47df7cSBram Moolenaar     }
1778071d4279SBram Moolenaar }
1779071d4279SBram Moolenaar 
1780071d4279SBram Moolenaar /*
1781071d4279SBram Moolenaar  * Enter a new current buffer.
17826d47df7cSBram Moolenaar  * Old curbuf must have been abandoned already!  This also means "curbuf" may
17836d47df7cSBram Moolenaar  * be pointing to freed memory.
1784071d4279SBram Moolenaar  */
17855843f5f3SBram Moolenaar     static void
enter_buffer(buf_T * buf)17867454a06eSBram Moolenaar enter_buffer(buf_T *buf)
1787071d4279SBram Moolenaar {
1788010ee965SBram Moolenaar     // Get the buffer in the current window.
1789010ee965SBram Moolenaar     curwin->w_buffer = buf;
1790010ee965SBram Moolenaar     curbuf = buf;
1791010ee965SBram Moolenaar     ++curbuf->b_nwindows;
1792010ee965SBram Moolenaar 
1793010ee965SBram Moolenaar     // Copy buffer and window local option values.  Not for a help buffer.
1794071d4279SBram Moolenaar     buf_copy_options(buf, BCO_ENTER | BCO_NOHELP);
1795071d4279SBram Moolenaar     if (!buf->b_help)
1796071d4279SBram Moolenaar 	get_winopts(buf);
1797071d4279SBram Moolenaar #ifdef FEAT_FOLDING
1798071d4279SBram Moolenaar     else
1799010ee965SBram Moolenaar 	// Remove all folds in the window.
1800071d4279SBram Moolenaar 	clearFolding(curwin);
1801010ee965SBram Moolenaar     foldUpdateAll(curwin);	// update folds (later).
1802071d4279SBram Moolenaar #endif
1803071d4279SBram Moolenaar 
1804071d4279SBram Moolenaar #ifdef FEAT_DIFF
180549d7bf13SBram Moolenaar     if (curwin->w_p_diff)
180649d7bf13SBram Moolenaar 	diff_buf_add(curbuf);
1807071d4279SBram Moolenaar #endif
1808071d4279SBram Moolenaar 
1809a971b82bSBram Moolenaar #ifdef FEAT_SYN_HL
181096ca27a0SBram Moolenaar     curwin->w_s = &(curbuf->b_s);
1811a971b82bSBram Moolenaar #endif
1812a971b82bSBram Moolenaar 
1813c667da51SBram Moolenaar     // Cursor on first line by default.
1814071d4279SBram Moolenaar     curwin->w_cursor.lnum = 1;
1815071d4279SBram Moolenaar     curwin->w_cursor.col = 0;
1816071d4279SBram Moolenaar     curwin->w_cursor.coladd = 0;
1817071d4279SBram Moolenaar     curwin->w_set_curswant = TRUE;
1818d4153d4aSBram Moolenaar     curwin->w_topline_was_set = FALSE;
1819071d4279SBram Moolenaar 
1820c667da51SBram Moolenaar     // mark cursor position as being invalid
182189c0ea4eSBram Moolenaar     curwin->w_valid = 0;
182289c0ea4eSBram Moolenaar 
18239cea87c5SBram Moolenaar     buflist_setfpos(curbuf, curwin, curbuf->b_last_cursor.lnum,
18249cea87c5SBram Moolenaar 					      curbuf->b_last_cursor.col, TRUE);
18259cea87c5SBram Moolenaar 
1826c667da51SBram Moolenaar     // Make sure the buffer is loaded.
1827c667da51SBram Moolenaar     if (curbuf->b_ml.ml_mfp == NULL)	// need to load the file
1828d4755bb0SBram Moolenaar     {
1829c667da51SBram Moolenaar 	// If there is no filetype, allow for detecting one.  Esp. useful for
1830c667da51SBram Moolenaar 	// ":ball" used in a autocommand.  If there already is a filetype we
1831c667da51SBram Moolenaar 	// might prefer to keep it.
1832d4755bb0SBram Moolenaar 	if (*curbuf->b_p_ft == NUL)
1833d4755bb0SBram Moolenaar 	    did_filetype = FALSE;
1834d4755bb0SBram Moolenaar 
183559f931efSBram Moolenaar 	open_buffer(FALSE, NULL, 0);
1836d4755bb0SBram Moolenaar     }
1837071d4279SBram Moolenaar     else
1838071d4279SBram Moolenaar     {
1839eda65221SBram Moolenaar 	if (!msg_silent && !shortmess(SHM_FILEINFO))
1840eda65221SBram Moolenaar 	    need_fileinfo = TRUE;	// display file info after redraw
1841eda65221SBram Moolenaar 
1842eda65221SBram Moolenaar 	// check if file changed
1843eda65221SBram Moolenaar 	(void)buf_check_timestamp(curbuf, FALSE);
1844eda65221SBram Moolenaar 
1845071d4279SBram Moolenaar 	curwin->w_topline = 1;
1846071d4279SBram Moolenaar #ifdef FEAT_DIFF
1847071d4279SBram Moolenaar 	curwin->w_topfill = 0;
1848071d4279SBram Moolenaar #endif
1849071d4279SBram Moolenaar 	apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
1850071d4279SBram Moolenaar 	apply_autocmds(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf);
1851071d4279SBram Moolenaar     }
1852071d4279SBram Moolenaar 
1853c667da51SBram Moolenaar     // If autocommands did not change the cursor position, restore cursor lnum
1854c667da51SBram Moolenaar     // and possibly cursor col.
1855071d4279SBram Moolenaar     if (curwin->w_cursor.lnum == 1 && inindent(0))
1856071d4279SBram Moolenaar 	buflist_getfpos();
1857071d4279SBram Moolenaar 
1858c667da51SBram Moolenaar     check_arg_idx(curwin);		// check for valid arg_idx
1859071d4279SBram Moolenaar #ifdef FEAT_TITLE
1860071d4279SBram Moolenaar     maketitle();
1861071d4279SBram Moolenaar #endif
1862c667da51SBram Moolenaar 	// when autocmds didn't change it
1863d4153d4aSBram Moolenaar     if (curwin->w_topline == 1 && !curwin->w_topline_was_set)
1864c667da51SBram Moolenaar 	scroll_cursor_halfway(FALSE);	// redisplay at correct position
1865071d4279SBram Moolenaar 
1866009b2592SBram Moolenaar #ifdef FEAT_NETBEANS_INTG
1867c667da51SBram Moolenaar     // Send fileOpened event because we've changed buffers.
1868009b2592SBram Moolenaar     netbeans_file_activated(curbuf);
1869009b2592SBram Moolenaar #endif
1870009b2592SBram Moolenaar 
1871c667da51SBram Moolenaar     // Change directories when the 'acd' option is set.
18726f470023SBram Moolenaar     DO_AUTOCHDIR;
1873071d4279SBram Moolenaar 
1874071d4279SBram Moolenaar #ifdef FEAT_KEYMAP
1875071d4279SBram Moolenaar     if (curbuf->b_kmap_state & KEYMAP_INIT)
18760ab2a887SBram Moolenaar 	(void)keymap_init();
1877071d4279SBram Moolenaar #endif
1878706cdebcSBram Moolenaar #ifdef FEAT_SPELL
1879c667da51SBram Moolenaar     // May need to set the spell language.  Can only do this after the buffer
1880c667da51SBram Moolenaar     // has been properly setup.
1881860cae1cSBram Moolenaar     if (!curbuf->b_help && curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL)
1882860cae1cSBram Moolenaar 	(void)did_set_spelllang(curwin);
1883706cdebcSBram Moolenaar #endif
1884ab9c89b6SBram Moolenaar #ifdef FEAT_VIMINFO
1885ab9c89b6SBram Moolenaar     curbuf->b_last_used = vim_time();
1886ab9c89b6SBram Moolenaar #endif
1887706cdebcSBram Moolenaar 
1888071d4279SBram Moolenaar     redraw_later(NOT_VALID);
1889071d4279SBram Moolenaar }
1890071d4279SBram Moolenaar 
1891498efdb7SBram Moolenaar #if defined(FEAT_AUTOCHDIR) || defined(PROTO)
1892498efdb7SBram Moolenaar /*
1893498efdb7SBram Moolenaar  * Change to the directory of the current buffer.
18946bd364e0SBram Moolenaar  * Don't do this while still starting up.
1895498efdb7SBram Moolenaar  */
1896498efdb7SBram Moolenaar     void
do_autochdir(void)18977454a06eSBram Moolenaar do_autochdir(void)
1898498efdb7SBram Moolenaar {
18995c71994fSBram Moolenaar     if ((starting == 0 || test_autochdir)
19006bd364e0SBram Moolenaar 	    && curbuf->b_ffname != NULL
1901b7407d3fSBram Moolenaar 	    && vim_chdirfile(curbuf->b_ffname, "auto") == OK)
19020526815cSBram Moolenaar     {
1903498efdb7SBram Moolenaar 	shorten_fnames(TRUE);
19040526815cSBram Moolenaar 	last_chdir_reason = "autochdir";
19050526815cSBram Moolenaar     }
1906498efdb7SBram Moolenaar }
1907498efdb7SBram Moolenaar #endif
1908498efdb7SBram Moolenaar 
1909f5be7cd0SBram Moolenaar     void
no_write_message(void)1910f5be7cd0SBram Moolenaar no_write_message(void)
1911f5be7cd0SBram Moolenaar {
1912f5be7cd0SBram Moolenaar #ifdef FEAT_TERMINAL
1913f5be7cd0SBram Moolenaar     if (term_job_running(curbuf->b_term))
1914f9e3e09fSBram Moolenaar 	emsg(_("E948: Job still running (add ! to end the job)"));
1915f5be7cd0SBram Moolenaar     else
1916f5be7cd0SBram Moolenaar #endif
1917e29a27f6SBram Moolenaar 	emsg(_(e_no_write_since_last_change_add_bang_to_override));
1918f5be7cd0SBram Moolenaar }
1919f5be7cd0SBram Moolenaar 
1920f5be7cd0SBram Moolenaar     void
no_write_message_nobang(buf_T * buf UNUSED)19217a76092aSBram Moolenaar no_write_message_nobang(buf_T *buf UNUSED)
1922f5be7cd0SBram Moolenaar {
1923f5be7cd0SBram Moolenaar #ifdef FEAT_TERMINAL
19247a76092aSBram Moolenaar     if (term_job_running(buf->b_term))
1925f9e3e09fSBram Moolenaar 	emsg(_("E948: Job still running"));
1926f5be7cd0SBram Moolenaar     else
1927f5be7cd0SBram Moolenaar #endif
1928e29a27f6SBram Moolenaar 	emsg(_(e_no_write_since_last_change));
1929f5be7cd0SBram Moolenaar }
1930f5be7cd0SBram Moolenaar 
1931071d4279SBram Moolenaar /*
1932071d4279SBram Moolenaar  * functions for dealing with the buffer list
1933071d4279SBram Moolenaar  */
1934071d4279SBram Moolenaar 
1935071d4279SBram Moolenaar /*
193646a53dfcSBram Moolenaar  * Return TRUE if the current buffer is empty, unnamed, unmodified and used in
193746a53dfcSBram Moolenaar  * only one window.  That means it can be re-used.
193846a53dfcSBram Moolenaar  */
193946a53dfcSBram Moolenaar     int
curbuf_reusable(void)194046a53dfcSBram Moolenaar curbuf_reusable(void)
194146a53dfcSBram Moolenaar {
194246a53dfcSBram Moolenaar     return (curbuf != NULL
194346a53dfcSBram Moolenaar 	&& curbuf->b_ffname == NULL
194446a53dfcSBram Moolenaar 	&& curbuf->b_nwindows <= 1
194546a53dfcSBram Moolenaar 	&& (curbuf->b_ml.ml_mfp == NULL || BUFEMPTY())
1946d85c396dSBram Moolenaar #if defined(FEAT_QUICKFIX)
194739803d82SBram Moolenaar 	&& !bt_quickfix(curbuf)
1948d85c396dSBram Moolenaar #endif
194946a53dfcSBram Moolenaar 	&& !curbufIsChanged());
195046a53dfcSBram Moolenaar }
195146a53dfcSBram Moolenaar 
195246a53dfcSBram Moolenaar /*
1953071d4279SBram Moolenaar  * Add a file name to the buffer list.  Return a pointer to the buffer.
1954071d4279SBram Moolenaar  * If the same file name already exists return a pointer to that buffer.
1955071d4279SBram Moolenaar  * If it does not exist, or if fname == NULL, a new entry is created.
1956071d4279SBram Moolenaar  * If (flags & BLN_CURBUF) is TRUE, may use current buffer.
1957071d4279SBram Moolenaar  * If (flags & BLN_LISTED) is TRUE, add new buffer to buffer list.
1958071d4279SBram Moolenaar  * If (flags & BLN_DUMMY) is TRUE, don't count it as a real buffer.
1959b127cfd7SBram Moolenaar  * If (flags & BLN_NEW) is TRUE, don't use an existing buffer.
19608240433fSBram Moolenaar  * If (flags & BLN_NOOPT) is TRUE, don't copy options from the current buffer
19618240433fSBram Moolenaar  *				    if the buffer already exists.
196200b0d6d8SBram Moolenaar  * If (flags & BLN_REUSE) is TRUE, may use buffer number from "buf_reuse".
1963071d4279SBram Moolenaar  * This is the ONLY way to create a new buffer.
1964071d4279SBram Moolenaar  */
1965071d4279SBram Moolenaar     buf_T *
buflist_new(char_u * ffname_arg,char_u * sfname_arg,linenr_T lnum,int flags)19667454a06eSBram Moolenaar buflist_new(
19673d6014f0SBram Moolenaar     char_u	*ffname_arg,	// full path of fname or relative
19683d6014f0SBram Moolenaar     char_u	*sfname_arg,	// short fname or NULL
19693d6014f0SBram Moolenaar     linenr_T	lnum,		// preferred cursor line
19703d6014f0SBram Moolenaar     int		flags)		// BLN_ defines
1971071d4279SBram Moolenaar {
19723d6014f0SBram Moolenaar     char_u	*ffname = ffname_arg;
19733d6014f0SBram Moolenaar     char_u	*sfname = sfname_arg;
1974071d4279SBram Moolenaar     buf_T	*buf;
1975071d4279SBram Moolenaar #ifdef UNIX
19768767f52fSBram Moolenaar     stat_T	st;
1977071d4279SBram Moolenaar #endif
1978071d4279SBram Moolenaar 
1979480778b8SBram Moolenaar     if (top_file_num == 1)
1980480778b8SBram Moolenaar 	hash_init(&buf_hashtab);
1981480778b8SBram Moolenaar 
19823d6014f0SBram Moolenaar     fname_expand(curbuf, &ffname, &sfname);	// will allocate ffname
1983071d4279SBram Moolenaar 
1984071d4279SBram Moolenaar     /*
1985c5935a85SBram Moolenaar      * If the file name already exists in the list, update the entry.
1986071d4279SBram Moolenaar      */
1987071d4279SBram Moolenaar #ifdef UNIX
1988c667da51SBram Moolenaar     // On Unix we can use inode numbers when the file exists.  Works better
1989c667da51SBram Moolenaar     // for hard links.
1990071d4279SBram Moolenaar     if (sfname == NULL || mch_stat((char *)sfname, &st) < 0)
1991071d4279SBram Moolenaar 	st.st_dev = (dev_T)-1;
1992071d4279SBram Moolenaar #endif
1993b127cfd7SBram Moolenaar     if (ffname != NULL && !(flags & (BLN_DUMMY | BLN_NEW)) && (buf =
1994071d4279SBram Moolenaar #ifdef UNIX
1995071d4279SBram Moolenaar 		buflist_findname_stat(ffname, &st)
1996071d4279SBram Moolenaar #else
1997071d4279SBram Moolenaar 		buflist_findname(ffname)
1998071d4279SBram Moolenaar #endif
1999071d4279SBram Moolenaar 		) != NULL)
2000071d4279SBram Moolenaar     {
2001071d4279SBram Moolenaar 	vim_free(ffname);
2002071d4279SBram Moolenaar 	if (lnum != 0)
200389b693e5SBram Moolenaar 	    buflist_setfpos(buf, (flags & BLN_NOCURWIN) ? NULL : curwin,
200489b693e5SBram Moolenaar 						      lnum, (colnr_T)0, FALSE);
20058240433fSBram Moolenaar 
20068240433fSBram Moolenaar 	if ((flags & BLN_NOOPT) == 0)
2007c667da51SBram Moolenaar 	    // copy the options now, if 'cpo' doesn't have 's' and not done
2008c667da51SBram Moolenaar 	    // already
2009071d4279SBram Moolenaar 	    buf_copy_options(buf, 0);
20108240433fSBram Moolenaar 
2011071d4279SBram Moolenaar 	if ((flags & BLN_LISTED) && !buf->b_p_bl)
2012071d4279SBram Moolenaar 	{
2013b25f9a97SBram Moolenaar 	    bufref_T bufref;
2014f2bd8ef2SBram Moolenaar 
2015071d4279SBram Moolenaar 	    buf->b_p_bl = TRUE;
2016b25f9a97SBram Moolenaar 	    set_bufref(&bufref, buf);
2017071d4279SBram Moolenaar 	    if (!(flags & BLN_DUMMY))
20184c7ab1bbSBram Moolenaar 	    {
20198240433fSBram Moolenaar 		if (apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, buf)
2020b25f9a97SBram Moolenaar 			&& !bufref_valid(&bufref))
20214c7ab1bbSBram Moolenaar 		    return NULL;
20224c7ab1bbSBram Moolenaar 	    }
2023071d4279SBram Moolenaar 	}
2024071d4279SBram Moolenaar 	return buf;
2025071d4279SBram Moolenaar     }
2026071d4279SBram Moolenaar 
2027071d4279SBram Moolenaar     /*
2028071d4279SBram Moolenaar      * If the current buffer has no name and no contents, use the current
2029071d4279SBram Moolenaar      * buffer.	Otherwise: Need to allocate a new buffer structure.
2030071d4279SBram Moolenaar      *
2031071d4279SBram Moolenaar      * This is the ONLY place where a new buffer structure is allocated!
20324770d09aSBram Moolenaar      * (A spell file buffer is allocated in spell.c, but that's not a normal
20334770d09aSBram Moolenaar      * buffer.)
2034071d4279SBram Moolenaar      */
2035071d4279SBram Moolenaar     buf = NULL;
203646a53dfcSBram Moolenaar     if ((flags & BLN_CURBUF) && curbuf_reusable())
2037071d4279SBram Moolenaar     {
2038071d4279SBram Moolenaar 	buf = curbuf;
2039c667da51SBram Moolenaar 	// It's like this buffer is deleted.  Watch out for autocommands that
2040c667da51SBram Moolenaar 	// change curbuf!  If that happens, allocate a new buffer anyway.
2041071d4279SBram Moolenaar 	if (curbuf->b_p_bl)
2042071d4279SBram Moolenaar 	    apply_autocmds(EVENT_BUFDELETE, NULL, NULL, FALSE, curbuf);
2043071d4279SBram Moolenaar 	if (buf == curbuf)
2044071d4279SBram Moolenaar 	    apply_autocmds(EVENT_BUFWIPEOUT, NULL, NULL, FALSE, curbuf);
2045071d4279SBram Moolenaar #ifdef FEAT_EVAL
2046c667da51SBram Moolenaar 	if (aborting())		// autocmds may abort script processing
204714285cb8SBram Moolenaar 	{
204814285cb8SBram Moolenaar 	    vim_free(ffname);
2049071d4279SBram Moolenaar 	    return NULL;
205014285cb8SBram Moolenaar 	}
2051071d4279SBram Moolenaar #endif
2052071d4279SBram Moolenaar 	if (buf == curbuf)
2053071d4279SBram Moolenaar 	{
2054c667da51SBram Moolenaar 	    // Make sure 'bufhidden' and 'buftype' are empty
2055071d4279SBram Moolenaar 	    clear_string_option(&buf->b_p_bh);
2056071d4279SBram Moolenaar 	    clear_string_option(&buf->b_p_bt);
2057071d4279SBram Moolenaar 	}
2058071d4279SBram Moolenaar     }
2059071d4279SBram Moolenaar     if (buf != curbuf || curbuf == NULL)
2060071d4279SBram Moolenaar     {
2061c799fe20SBram Moolenaar 	buf = ALLOC_CLEAR_ONE(buf_T);
2062071d4279SBram Moolenaar 	if (buf == NULL)
2063071d4279SBram Moolenaar 	{
2064071d4279SBram Moolenaar 	    vim_free(ffname);
2065071d4279SBram Moolenaar 	    return NULL;
2066071d4279SBram Moolenaar 	}
2067429fa853SBram Moolenaar #ifdef FEAT_EVAL
2068c667da51SBram Moolenaar 	// init b: variables
2069429fa853SBram Moolenaar 	buf->b_vars = dict_alloc();
2070429fa853SBram Moolenaar 	if (buf->b_vars == NULL)
2071429fa853SBram Moolenaar 	{
2072429fa853SBram Moolenaar 	    vim_free(ffname);
2073429fa853SBram Moolenaar 	    vim_free(buf);
2074429fa853SBram Moolenaar 	    return NULL;
2075429fa853SBram Moolenaar 	}
2076429fa853SBram Moolenaar 	init_var_dict(buf->b_vars, &buf->b_bufvar, VAR_SCOPE);
2077429fa853SBram Moolenaar #endif
207879518e2aSBram Moolenaar 	init_changedtick(buf);
2079071d4279SBram Moolenaar     }
2080071d4279SBram Moolenaar 
2081071d4279SBram Moolenaar     if (ffname != NULL)
2082071d4279SBram Moolenaar     {
2083071d4279SBram Moolenaar 	buf->b_ffname = ffname;
2084071d4279SBram Moolenaar 	buf->b_sfname = vim_strsave(sfname);
2085071d4279SBram Moolenaar     }
2086071d4279SBram Moolenaar 
2087071d4279SBram Moolenaar     clear_wininfo(buf);
2088c799fe20SBram Moolenaar     buf->b_wininfo = ALLOC_CLEAR_ONE(wininfo_T);
2089071d4279SBram Moolenaar 
2090071d4279SBram Moolenaar     if ((ffname != NULL && (buf->b_ffname == NULL || buf->b_sfname == NULL))
2091071d4279SBram Moolenaar 	    || buf->b_wininfo == NULL)
2092071d4279SBram Moolenaar     {
20933d6014f0SBram Moolenaar 	if (buf->b_sfname != buf->b_ffname)
2094d23a8236SBram Moolenaar 	    VIM_CLEAR(buf->b_sfname);
20953d6014f0SBram Moolenaar 	else
20963d6014f0SBram Moolenaar 	    buf->b_sfname = NULL;
20973d6014f0SBram Moolenaar 	VIM_CLEAR(buf->b_ffname);
2098071d4279SBram Moolenaar 	if (buf != curbuf)
2099071d4279SBram Moolenaar 	    free_buffer(buf);
2100071d4279SBram Moolenaar 	return NULL;
2101071d4279SBram Moolenaar     }
2102071d4279SBram Moolenaar 
2103071d4279SBram Moolenaar     if (buf == curbuf)
2104071d4279SBram Moolenaar     {
2105c667da51SBram Moolenaar 	// free all things allocated for this buffer
210659f931efSBram Moolenaar 	buf_freeall(buf, 0);
2107c667da51SBram Moolenaar 	if (buf != curbuf)	 // autocommands deleted the buffer!
2108071d4279SBram Moolenaar 	    return NULL;
2109f2bd8ef2SBram Moolenaar #if defined(FEAT_EVAL)
2110c667da51SBram Moolenaar 	if (aborting())		// autocmds may abort script processing
2111071d4279SBram Moolenaar 	    return NULL;
2112071d4279SBram Moolenaar #endif
2113c667da51SBram Moolenaar 	free_buffer_stuff(buf, FALSE);	// delete local variables et al.
21140ac24e1eSBram Moolenaar 
2115c667da51SBram Moolenaar 	// Init the options.
21160ac24e1eSBram Moolenaar 	buf->b_p_initialized = FALSE;
21170ac24e1eSBram Moolenaar 	buf_copy_options(buf, BCO_ENTER);
21180ac24e1eSBram Moolenaar 
2119071d4279SBram Moolenaar #ifdef FEAT_KEYMAP
2120c667da51SBram Moolenaar 	// need to reload lmaps and set b:keymap_name
2121071d4279SBram Moolenaar 	curbuf->b_kmap_state |= KEYMAP_INIT;
2122071d4279SBram Moolenaar #endif
2123071d4279SBram Moolenaar     }
2124071d4279SBram Moolenaar     else
2125071d4279SBram Moolenaar     {
2126c5935a85SBram Moolenaar 	// put the new buffer at the end of the buffer list
2127071d4279SBram Moolenaar 	buf->b_next = NULL;
2128c667da51SBram Moolenaar 	if (firstbuf == NULL)		// buffer list is empty
2129071d4279SBram Moolenaar 	{
2130071d4279SBram Moolenaar 	    buf->b_prev = NULL;
2131071d4279SBram Moolenaar 	    firstbuf = buf;
2132071d4279SBram Moolenaar 	}
2133c667da51SBram Moolenaar 	else				// append new buffer at end of list
2134071d4279SBram Moolenaar 	{
2135071d4279SBram Moolenaar 	    lastbuf->b_next = buf;
2136071d4279SBram Moolenaar 	    buf->b_prev = lastbuf;
2137071d4279SBram Moolenaar 	}
2138071d4279SBram Moolenaar 	lastbuf = buf;
2139071d4279SBram Moolenaar 
214000b0d6d8SBram Moolenaar 	if ((flags & BLN_REUSE) && buf_reuse.ga_len > 0)
214100b0d6d8SBram Moolenaar 	{
214200b0d6d8SBram Moolenaar 	    // Recycle a previously used buffer number.  Used for buffers which
214300b0d6d8SBram Moolenaar 	    // are normally hidden, e.g. in a popup window.  Avoids that the
214400b0d6d8SBram Moolenaar 	    // buffer number grows rapidly.
214500b0d6d8SBram Moolenaar 	    --buf_reuse.ga_len;
214600b0d6d8SBram Moolenaar 	    buf->b_fnum = ((int *)buf_reuse.ga_data)[buf_reuse.ga_len];
214799ebf22cSBram Moolenaar 
214899ebf22cSBram Moolenaar 	    // Move buffer to the right place in the buffer list.
214999ebf22cSBram Moolenaar 	    while (buf->b_prev != NULL && buf->b_fnum < buf->b_prev->b_fnum)
215099ebf22cSBram Moolenaar 	    {
215199ebf22cSBram Moolenaar 		buf_T	*prev = buf->b_prev;
215299ebf22cSBram Moolenaar 
215399ebf22cSBram Moolenaar 		prev->b_next = buf->b_next;
215499ebf22cSBram Moolenaar 		if (prev->b_next != NULL)
215599ebf22cSBram Moolenaar 		    prev->b_next->b_prev = prev;
215699ebf22cSBram Moolenaar 		buf->b_next = prev;
215799ebf22cSBram Moolenaar 		buf->b_prev = prev->b_prev;
215899ebf22cSBram Moolenaar 		if (buf->b_prev != NULL)
215999ebf22cSBram Moolenaar 		    buf->b_prev->b_next = buf;
216099ebf22cSBram Moolenaar 		prev->b_prev = buf;
216199ebf22cSBram Moolenaar 		if (lastbuf == buf)
216299ebf22cSBram Moolenaar 		    lastbuf = prev;
216399ebf22cSBram Moolenaar 		if (firstbuf == prev)
216499ebf22cSBram Moolenaar 		    firstbuf = buf;
216599ebf22cSBram Moolenaar 	    }
216600b0d6d8SBram Moolenaar 	}
216700b0d6d8SBram Moolenaar 	else
2168071d4279SBram Moolenaar 	    buf->b_fnum = top_file_num++;
2169c667da51SBram Moolenaar 	if (top_file_num < 0)		// wrap around (may cause duplicates)
2170071d4279SBram Moolenaar 	{
2171f9e3e09fSBram Moolenaar 	    emsg(_("W14: Warning: List of file names overflow"));
217228ee892aSBram Moolenaar 	    if (emsg_silent == 0 && !in_assert_fails)
2173071d4279SBram Moolenaar 	    {
2174071d4279SBram Moolenaar 		out_flush();
2175eda1da0cSBram Moolenaar 		ui_delay(3001L, TRUE);	// make sure it is noticed
2176071d4279SBram Moolenaar 	    }
2177071d4279SBram Moolenaar 	    top_file_num = 1;
2178071d4279SBram Moolenaar 	}
2179480778b8SBram Moolenaar 	buf_hashtab_add(buf);
2180071d4279SBram Moolenaar 
2181c5935a85SBram Moolenaar 	// Always copy the options from the current buffer.
2182071d4279SBram Moolenaar 	buf_copy_options(buf, BCO_ALWAYS);
2183071d4279SBram Moolenaar     }
2184071d4279SBram Moolenaar 
2185071d4279SBram Moolenaar     buf->b_wininfo->wi_fpos.lnum = lnum;
2186071d4279SBram Moolenaar     buf->b_wininfo->wi_win = curwin;
2187071d4279SBram Moolenaar 
21881fad5d49SBram Moolenaar #ifdef FEAT_SYN_HL
2189860cae1cSBram Moolenaar     hash_init(&buf->b_s.b_keywtab);
2190860cae1cSBram Moolenaar     hash_init(&buf->b_s.b_keywtab_ic);
2191071d4279SBram Moolenaar #endif
2192071d4279SBram Moolenaar 
2193071d4279SBram Moolenaar     buf->b_fname = buf->b_sfname;
2194071d4279SBram Moolenaar #ifdef UNIX
2195071d4279SBram Moolenaar     if (st.st_dev == (dev_T)-1)
2196f1726cc8SBram Moolenaar 	buf->b_dev_valid = FALSE;
2197071d4279SBram Moolenaar     else
2198071d4279SBram Moolenaar     {
2199f1726cc8SBram Moolenaar 	buf->b_dev_valid = TRUE;
2200071d4279SBram Moolenaar 	buf->b_dev = st.st_dev;
2201071d4279SBram Moolenaar 	buf->b_ino = st.st_ino;
2202071d4279SBram Moolenaar     }
2203071d4279SBram Moolenaar #endif
2204071d4279SBram Moolenaar     buf->b_u_synced = TRUE;
2205071d4279SBram Moolenaar     buf->b_flags = BF_CHECK_RO | BF_NEVERLOADED;
220681695250SBram Moolenaar     if (flags & BLN_DUMMY)
220781695250SBram Moolenaar 	buf->b_flags |= BF_DUMMY;
2208071d4279SBram Moolenaar     buf_clear_file(buf);
2209c667da51SBram Moolenaar     clrallmarks(buf);			// clear marks
2210c667da51SBram Moolenaar     fmarks_check_names(buf);		// check file marks for this file
2211c667da51SBram Moolenaar     buf->b_p_bl = (flags & BLN_LISTED) ? TRUE : FALSE;	// init 'buflisted'
2212071d4279SBram Moolenaar     if (!(flags & BLN_DUMMY))
2213071d4279SBram Moolenaar     {
2214b25f9a97SBram Moolenaar 	bufref_T bufref;
2215b25f9a97SBram Moolenaar 
2216c667da51SBram Moolenaar 	// Tricky: these autocommands may change the buffer list.  They could
2217c667da51SBram Moolenaar 	// also split the window with re-using the one empty buffer. This may
2218c667da51SBram Moolenaar 	// result in unexpectedly losing the empty buffer.
2219b25f9a97SBram Moolenaar 	set_bufref(&bufref, buf);
22208240433fSBram Moolenaar 	if (apply_autocmds(EVENT_BUFNEW, NULL, NULL, FALSE, buf)
2221b25f9a97SBram Moolenaar 		&& !bufref_valid(&bufref))
22224c7ab1bbSBram Moolenaar 	    return NULL;
2223071d4279SBram Moolenaar 	if (flags & BLN_LISTED)
22244c7ab1bbSBram Moolenaar 	{
22258240433fSBram Moolenaar 	    if (apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, buf)
2226b25f9a97SBram Moolenaar 		    && !bufref_valid(&bufref))
22274c7ab1bbSBram Moolenaar 		return NULL;
22284c7ab1bbSBram Moolenaar 	}
2229071d4279SBram Moolenaar #ifdef FEAT_EVAL
2230c667da51SBram Moolenaar 	if (aborting())		// autocmds may abort script processing
2231071d4279SBram Moolenaar 	    return NULL;
2232071d4279SBram Moolenaar #endif
2233071d4279SBram Moolenaar     }
2234071d4279SBram Moolenaar 
2235071d4279SBram Moolenaar     return buf;
2236071d4279SBram Moolenaar }
2237071d4279SBram Moolenaar 
2238071d4279SBram Moolenaar /*
2239071d4279SBram Moolenaar  * Free the memory for the options of a buffer.
2240071d4279SBram Moolenaar  * If "free_p_ff" is TRUE also free 'fileformat', 'buftype' and
2241071d4279SBram Moolenaar  * 'fileencoding'.
2242071d4279SBram Moolenaar  */
2243071d4279SBram Moolenaar     void
free_buf_options(buf_T * buf,int free_p_ff)22447454a06eSBram Moolenaar free_buf_options(
22457454a06eSBram Moolenaar     buf_T	*buf,
22467454a06eSBram Moolenaar     int		free_p_ff)
2247071d4279SBram Moolenaar {
2248071d4279SBram Moolenaar     if (free_p_ff)
2249071d4279SBram Moolenaar     {
2250071d4279SBram Moolenaar 	clear_string_option(&buf->b_p_fenc);
2251071d4279SBram Moolenaar 	clear_string_option(&buf->b_p_ff);
2252071d4279SBram Moolenaar 	clear_string_option(&buf->b_p_bh);
2253071d4279SBram Moolenaar 	clear_string_option(&buf->b_p_bt);
2254071d4279SBram Moolenaar     }
2255071d4279SBram Moolenaar #ifdef FEAT_FIND_ID
2256071d4279SBram Moolenaar     clear_string_option(&buf->b_p_def);
2257071d4279SBram Moolenaar     clear_string_option(&buf->b_p_inc);
2258071d4279SBram Moolenaar # ifdef FEAT_EVAL
2259071d4279SBram Moolenaar     clear_string_option(&buf->b_p_inex);
2260071d4279SBram Moolenaar # endif
2261071d4279SBram Moolenaar #endif
2262071d4279SBram Moolenaar #if defined(FEAT_CINDENT) && defined(FEAT_EVAL)
2263071d4279SBram Moolenaar     clear_string_option(&buf->b_p_inde);
2264071d4279SBram Moolenaar     clear_string_option(&buf->b_p_indk);
2265071d4279SBram Moolenaar #endif
2266371d5403SBram Moolenaar #if defined(FEAT_BEVAL) && defined(FEAT_EVAL)
2267371d5403SBram Moolenaar     clear_string_option(&buf->b_p_bexpr);
2268371d5403SBram Moolenaar #endif
226949771f4fSBram Moolenaar #if defined(FEAT_CRYPT)
227049771f4fSBram Moolenaar     clear_string_option(&buf->b_p_cm);
227149771f4fSBram Moolenaar #endif
227224a2d416SBram Moolenaar     clear_string_option(&buf->b_p_fp);
22731d2ba7faSBram Moolenaar #if defined(FEAT_EVAL)
22741d2ba7faSBram Moolenaar     clear_string_option(&buf->b_p_fex);
22751d2ba7faSBram Moolenaar #endif
2276071d4279SBram Moolenaar #ifdef FEAT_CRYPT
2277131530a5SBram Moolenaar # ifdef FEAT_SODIUM
2278131530a5SBram Moolenaar     if (buf->b_p_key != NULL && (crypt_get_method_nr(buf) == CRYPT_M_SOD))
2279131530a5SBram Moolenaar 	sodium_munlock(buf->b_p_key, STRLEN(buf->b_p_key));
2280131530a5SBram Moolenaar # endif
2281071d4279SBram Moolenaar     clear_string_option(&buf->b_p_key);
2282071d4279SBram Moolenaar #endif
2283071d4279SBram Moolenaar     clear_string_option(&buf->b_p_kp);
2284071d4279SBram Moolenaar     clear_string_option(&buf->b_p_mps);
2285071d4279SBram Moolenaar     clear_string_option(&buf->b_p_fo);
228686b68359SBram Moolenaar     clear_string_option(&buf->b_p_flp);
2287071d4279SBram Moolenaar     clear_string_option(&buf->b_p_isk);
228804958cbaSBram Moolenaar #ifdef FEAT_VARTABS
228904958cbaSBram Moolenaar     clear_string_option(&buf->b_p_vsts);
229004958cbaSBram Moolenaar     vim_free(buf->b_p_vsts_nopaste);
229104958cbaSBram Moolenaar     buf->b_p_vsts_nopaste = NULL;
229204958cbaSBram Moolenaar     vim_free(buf->b_p_vsts_array);
229304958cbaSBram Moolenaar     buf->b_p_vsts_array = NULL;
229404958cbaSBram Moolenaar     clear_string_option(&buf->b_p_vts);
229555c77cf2SBram Moolenaar     VIM_CLEAR(buf->b_p_vts_array);
229604958cbaSBram Moolenaar #endif
2297071d4279SBram Moolenaar #ifdef FEAT_KEYMAP
2298071d4279SBram Moolenaar     clear_string_option(&buf->b_p_keymap);
229950138323SBram Moolenaar     keymap_clear(&buf->b_kmap_ga);
2300071d4279SBram Moolenaar     ga_clear(&buf->b_kmap_ga);
2301071d4279SBram Moolenaar #endif
2302071d4279SBram Moolenaar     clear_string_option(&buf->b_p_com);
2303071d4279SBram Moolenaar #ifdef FEAT_FOLDING
2304071d4279SBram Moolenaar     clear_string_option(&buf->b_p_cms);
2305071d4279SBram Moolenaar #endif
2306071d4279SBram Moolenaar     clear_string_option(&buf->b_p_nf);
2307071d4279SBram Moolenaar #ifdef FEAT_SYN_HL
2308071d4279SBram Moolenaar     clear_string_option(&buf->b_p_syn);
2309b8060fe8SBram Moolenaar     clear_string_option(&buf->b_s.b_syn_isk);
2310f71a3db4SBram Moolenaar #endif
2311f71a3db4SBram Moolenaar #ifdef FEAT_SPELL
2312860cae1cSBram Moolenaar     clear_string_option(&buf->b_s.b_p_spc);
2313860cae1cSBram Moolenaar     clear_string_option(&buf->b_s.b_p_spf);
2314473de61bSBram Moolenaar     vim_regfree(buf->b_s.b_cap_prog);
2315860cae1cSBram Moolenaar     buf->b_s.b_cap_prog = NULL;
2316860cae1cSBram Moolenaar     clear_string_option(&buf->b_s.b_p_spl);
2317362b44bdSBram Moolenaar     clear_string_option(&buf->b_s.b_p_spo);
2318071d4279SBram Moolenaar #endif
2319071d4279SBram Moolenaar #ifdef FEAT_SEARCHPATH
2320071d4279SBram Moolenaar     clear_string_option(&buf->b_p_sua);
2321071d4279SBram Moolenaar #endif
2322071d4279SBram Moolenaar     clear_string_option(&buf->b_p_ft);
2323071d4279SBram Moolenaar #ifdef FEAT_CINDENT
2324071d4279SBram Moolenaar     clear_string_option(&buf->b_p_cink);
2325071d4279SBram Moolenaar     clear_string_option(&buf->b_p_cino);
2326071d4279SBram Moolenaar #endif
2327071d4279SBram Moolenaar #if defined(FEAT_CINDENT) || defined(FEAT_SMARTINDENT)
2328071d4279SBram Moolenaar     clear_string_option(&buf->b_p_cinw);
2329071d4279SBram Moolenaar #endif
2330071d4279SBram Moolenaar     clear_string_option(&buf->b_p_cpt);
2331cfbc5ee4SBram Moolenaar #ifdef FEAT_COMPL_FUNC
2332cfbc5ee4SBram Moolenaar     clear_string_option(&buf->b_p_cfu);
2333e344beadSBram Moolenaar     clear_string_option(&buf->b_p_ofu);
2334d4c4bfa0SBram Moolenaar     clear_string_option(&buf->b_p_tsrfu);
2335cfbc5ee4SBram Moolenaar #endif
2336071d4279SBram Moolenaar #ifdef FEAT_QUICKFIX
2337071d4279SBram Moolenaar     clear_string_option(&buf->b_p_gp);
2338071d4279SBram Moolenaar     clear_string_option(&buf->b_p_mp);
2339071d4279SBram Moolenaar     clear_string_option(&buf->b_p_efm);
2340071d4279SBram Moolenaar #endif
2341071d4279SBram Moolenaar     clear_string_option(&buf->b_p_ep);
2342071d4279SBram Moolenaar     clear_string_option(&buf->b_p_path);
2343071d4279SBram Moolenaar     clear_string_option(&buf->b_p_tags);
23440f6562e9SBram Moolenaar     clear_string_option(&buf->b_p_tc);
234545e18cbdSBram Moolenaar #ifdef FEAT_EVAL
234645e18cbdSBram Moolenaar     clear_string_option(&buf->b_p_tfu);
234745e18cbdSBram Moolenaar #endif
2348071d4279SBram Moolenaar     clear_string_option(&buf->b_p_dict);
2349071d4279SBram Moolenaar     clear_string_option(&buf->b_p_tsr);
2350cfbc5ee4SBram Moolenaar #ifdef FEAT_TEXTOBJ
2351cfbc5ee4SBram Moolenaar     clear_string_option(&buf->b_p_qe);
2352cfbc5ee4SBram Moolenaar #endif
2353071d4279SBram Moolenaar     buf->b_p_ar = -1;
2354f5a2fd88SBram Moolenaar     buf->b_p_ul = NO_LOCAL_UNDOLEVEL;
2355af6c131bSBram Moolenaar #ifdef FEAT_LISP
2356af6c131bSBram Moolenaar     clear_string_option(&buf->b_p_lw);
2357af6c131bSBram Moolenaar #endif
2358b8ee25acSBram Moolenaar     clear_string_option(&buf->b_p_bkc);
23592c7292dcSBram Moolenaar     clear_string_option(&buf->b_p_menc);
2360071d4279SBram Moolenaar }
2361071d4279SBram Moolenaar 
2362071d4279SBram Moolenaar /*
236345e5fd13SBram Moolenaar  * Get alternate file "n".
236445e5fd13SBram Moolenaar  * Set linenr to "lnum" or altfpos.lnum if "lnum" == 0.
236545e5fd13SBram Moolenaar  *	Also set cursor column to altfpos.col if 'startofline' is not set.
2366071d4279SBram Moolenaar  * if (options & GETF_SETMARK) call setpcmark()
2367071d4279SBram Moolenaar  * if (options & GETF_ALT) we are jumping to an alternate file.
2368071d4279SBram Moolenaar  * if (options & GETF_SWITCH) respect 'switchbuf' settings when jumping
2369071d4279SBram Moolenaar  *
237045e5fd13SBram Moolenaar  * Return FAIL for failure, OK for success.
2371071d4279SBram Moolenaar  */
2372071d4279SBram Moolenaar     int
buflist_getfile(int n,linenr_T lnum,int options,int forceit)23737454a06eSBram Moolenaar buflist_getfile(
23747454a06eSBram Moolenaar     int		n,
23757454a06eSBram Moolenaar     linenr_T	lnum,
23767454a06eSBram Moolenaar     int		options,
23777454a06eSBram Moolenaar     int		forceit)
2378071d4279SBram Moolenaar {
2379071d4279SBram Moolenaar     buf_T	*buf;
2380071d4279SBram Moolenaar     win_T	*wp = NULL;
2381071d4279SBram Moolenaar     pos_T	*fpos;
2382071d4279SBram Moolenaar     colnr_T	col;
2383071d4279SBram Moolenaar 
2384071d4279SBram Moolenaar     buf = buflist_findnr(n);
2385071d4279SBram Moolenaar     if (buf == NULL)
2386071d4279SBram Moolenaar     {
2387071d4279SBram Moolenaar 	if ((options & GETF_ALT) && n == 0)
2388108010aaSBram Moolenaar 	    emsg(_(e_no_alternate_file));
2389071d4279SBram Moolenaar 	else
2390b5443cc4SBram Moolenaar 	    semsg(_("E92: Buffer %d not found"), n);
2391071d4279SBram Moolenaar 	return FAIL;
2392071d4279SBram Moolenaar     }
2393071d4279SBram Moolenaar 
2394c667da51SBram Moolenaar     // if alternate file is the current buffer, nothing to do
2395071d4279SBram Moolenaar     if (buf == curbuf)
2396071d4279SBram Moolenaar 	return OK;
2397071d4279SBram Moolenaar 
23982d3f489eSBram Moolenaar     if (text_locked())
239905a7bb36SBram Moolenaar     {
24002d3f489eSBram Moolenaar 	text_locked_msg();
2401071d4279SBram Moolenaar 	return FAIL;
240205a7bb36SBram Moolenaar     }
2403910f66f9SBram Moolenaar     if (curbuf_locked())
2404910f66f9SBram Moolenaar 	return FAIL;
2405071d4279SBram Moolenaar 
2406c667da51SBram Moolenaar     // altfpos may be changed by getfile(), get it now
2407071d4279SBram Moolenaar     if (lnum == 0)
2408071d4279SBram Moolenaar     {
2409071d4279SBram Moolenaar 	fpos = buflist_findfpos(buf);
2410071d4279SBram Moolenaar 	lnum = fpos->lnum;
2411071d4279SBram Moolenaar 	col = fpos->col;
2412071d4279SBram Moolenaar     }
2413071d4279SBram Moolenaar     else
2414071d4279SBram Moolenaar 	col = 0;
2415071d4279SBram Moolenaar 
2416071d4279SBram Moolenaar     if (options & GETF_SWITCH)
2417071d4279SBram Moolenaar     {
2418c667da51SBram Moolenaar 	// If 'switchbuf' contains "useopen": jump to first window containing
2419c667da51SBram Moolenaar 	// "buf" if one exists
2420f233048aSBram Moolenaar 	if (swb_flags & SWB_USEOPEN)
2421071d4279SBram Moolenaar 	    wp = buf_jump_open_win(buf);
2422a594d77fSBram Moolenaar 
2423c667da51SBram Moolenaar 	// If 'switchbuf' contains "usetab": jump to first window in any tab
2424c667da51SBram Moolenaar 	// page containing "buf" if one exists
2425f233048aSBram Moolenaar 	if (wp == NULL && (swb_flags & SWB_USETAB))
2426779b74b2SBram Moolenaar 	    wp = buf_jump_open_tab(buf);
2427a594d77fSBram Moolenaar 
2428c667da51SBram Moolenaar 	// If 'switchbuf' contains "split", "vsplit" or "newtab" and the
2429c667da51SBram Moolenaar 	// current buffer isn't empty: open new tab or window
2430a594d77fSBram Moolenaar 	if (wp == NULL && (swb_flags & (SWB_VSPLIT | SWB_SPLIT | SWB_NEWTAB))
2431b5aedf3eSBram Moolenaar 							       && !BUFEMPTY())
2432071d4279SBram Moolenaar 	{
2433a594d77fSBram Moolenaar 	    if (swb_flags & SWB_NEWTAB)
2434f233048aSBram Moolenaar 		tabpage_new();
2435a594d77fSBram Moolenaar 	    else if (win_split(0, (swb_flags & SWB_VSPLIT) ? WSP_VERT : 0)
2436a594d77fSBram Moolenaar 								      == FAIL)
2437071d4279SBram Moolenaar 		return FAIL;
24383368ea21SBram Moolenaar 	    RESET_BINDING(curwin);
2439071d4279SBram Moolenaar 	}
2440071d4279SBram Moolenaar     }
2441071d4279SBram Moolenaar 
2442071d4279SBram Moolenaar     ++RedrawingDisabled;
24438ad80deaSBram Moolenaar     if (GETFILE_SUCCESS(getfile(buf->b_fnum, NULL, NULL,
24448ad80deaSBram Moolenaar 				     (options & GETF_SETMARK), lnum, forceit)))
2445071d4279SBram Moolenaar     {
2446071d4279SBram Moolenaar 	--RedrawingDisabled;
2447071d4279SBram Moolenaar 
2448c667da51SBram Moolenaar 	// cursor is at to BOL and w_cursor.lnum is checked due to getfile()
2449071d4279SBram Moolenaar 	if (!p_sol && col != 0)
2450071d4279SBram Moolenaar 	{
2451071d4279SBram Moolenaar 	    curwin->w_cursor.col = col;
2452071d4279SBram Moolenaar 	    check_cursor_col();
2453071d4279SBram Moolenaar 	    curwin->w_cursor.coladd = 0;
2454071d4279SBram Moolenaar 	    curwin->w_set_curswant = TRUE;
2455071d4279SBram Moolenaar 	}
2456071d4279SBram Moolenaar 	return OK;
2457071d4279SBram Moolenaar     }
2458071d4279SBram Moolenaar     --RedrawingDisabled;
2459071d4279SBram Moolenaar     return FAIL;
2460071d4279SBram Moolenaar }
2461071d4279SBram Moolenaar 
2462071d4279SBram Moolenaar /*
2463071d4279SBram Moolenaar  * go to the last know line number for the current buffer
2464071d4279SBram Moolenaar  */
24655843f5f3SBram Moolenaar     static void
buflist_getfpos(void)24667454a06eSBram Moolenaar buflist_getfpos(void)
2467071d4279SBram Moolenaar {
2468071d4279SBram Moolenaar     pos_T	*fpos;
2469071d4279SBram Moolenaar 
2470071d4279SBram Moolenaar     fpos = buflist_findfpos(curbuf);
2471071d4279SBram Moolenaar 
2472071d4279SBram Moolenaar     curwin->w_cursor.lnum = fpos->lnum;
2473071d4279SBram Moolenaar     check_cursor_lnum();
2474071d4279SBram Moolenaar 
2475071d4279SBram Moolenaar     if (p_sol)
2476071d4279SBram Moolenaar 	curwin->w_cursor.col = 0;
2477071d4279SBram Moolenaar     else
2478071d4279SBram Moolenaar     {
2479071d4279SBram Moolenaar 	curwin->w_cursor.col = fpos->col;
2480071d4279SBram Moolenaar 	check_cursor_col();
2481071d4279SBram Moolenaar 	curwin->w_cursor.coladd = 0;
2482071d4279SBram Moolenaar 	curwin->w_set_curswant = TRUE;
2483071d4279SBram Moolenaar     }
2484071d4279SBram Moolenaar }
2485071d4279SBram Moolenaar 
248681695250SBram Moolenaar #if defined(FEAT_QUICKFIX) || defined(FEAT_EVAL) || defined(PROTO)
248781695250SBram Moolenaar /*
248881695250SBram Moolenaar  * Find file in buffer list by name (it has to be for the current window).
248981695250SBram Moolenaar  * Returns NULL if not found.
249081695250SBram Moolenaar  */
249181695250SBram Moolenaar     buf_T *
buflist_findname_exp(char_u * fname)24927454a06eSBram Moolenaar buflist_findname_exp(char_u *fname)
249381695250SBram Moolenaar {
249481695250SBram Moolenaar     char_u	*ffname;
249581695250SBram Moolenaar     buf_T	*buf = NULL;
249681695250SBram Moolenaar 
2497c667da51SBram Moolenaar     // First make the name into a full path name
249881695250SBram Moolenaar     ffname = FullName_save(fname,
249981695250SBram Moolenaar #ifdef UNIX
2500c667da51SBram Moolenaar 	    TRUE	    // force expansion, get rid of symbolic links
250181695250SBram Moolenaar #else
250281695250SBram Moolenaar 	    FALSE
250381695250SBram Moolenaar #endif
250481695250SBram Moolenaar 	    );
250581695250SBram Moolenaar     if (ffname != NULL)
250681695250SBram Moolenaar     {
250781695250SBram Moolenaar 	buf = buflist_findname(ffname);
250881695250SBram Moolenaar 	vim_free(ffname);
250981695250SBram Moolenaar     }
251081695250SBram Moolenaar     return buf;
251181695250SBram Moolenaar }
251281695250SBram Moolenaar #endif
251381695250SBram Moolenaar 
2514071d4279SBram Moolenaar /*
2515071d4279SBram Moolenaar  * Find file in buffer list by name (it has to be for the current window).
2516071d4279SBram Moolenaar  * "ffname" must have a full path.
251781695250SBram Moolenaar  * Skips dummy buffers.
251881695250SBram Moolenaar  * Returns NULL if not found.
2519071d4279SBram Moolenaar  */
2520071d4279SBram Moolenaar     buf_T *
buflist_findname(char_u * ffname)25217454a06eSBram Moolenaar buflist_findname(char_u *ffname)
2522071d4279SBram Moolenaar {
2523071d4279SBram Moolenaar #ifdef UNIX
25248767f52fSBram Moolenaar     stat_T	st;
2525071d4279SBram Moolenaar 
2526071d4279SBram Moolenaar     if (mch_stat((char *)ffname, &st) < 0)
2527071d4279SBram Moolenaar 	st.st_dev = (dev_T)-1;
2528071d4279SBram Moolenaar     return buflist_findname_stat(ffname, &st);
2529071d4279SBram Moolenaar }
2530071d4279SBram Moolenaar 
2531071d4279SBram Moolenaar /*
2532071d4279SBram Moolenaar  * Same as buflist_findname(), but pass the stat structure to avoid getting it
2533071d4279SBram Moolenaar  * twice for the same file.
253481695250SBram Moolenaar  * Returns NULL if not found.
2535071d4279SBram Moolenaar  */
2536071d4279SBram Moolenaar     static buf_T *
buflist_findname_stat(char_u * ffname,stat_T * stp)25377454a06eSBram Moolenaar buflist_findname_stat(
25387454a06eSBram Moolenaar     char_u	*ffname,
25398767f52fSBram Moolenaar     stat_T	*stp)
2540071d4279SBram Moolenaar {
2541071d4279SBram Moolenaar #endif
2542071d4279SBram Moolenaar     buf_T	*buf;
2543071d4279SBram Moolenaar 
2544c667da51SBram Moolenaar     // Start at the last buffer, expect to find a match sooner.
254500d253e2SBram Moolenaar     FOR_ALL_BUFS_FROM_LAST(buf)
254681695250SBram Moolenaar 	if ((buf->b_flags & BF_DUMMY) == 0 && !otherfile_buf(buf, ffname
2547071d4279SBram Moolenaar #ifdef UNIX
2548071d4279SBram Moolenaar 		    , stp
2549071d4279SBram Moolenaar #endif
2550071d4279SBram Moolenaar 		    ))
2551071d4279SBram Moolenaar 	    return buf;
2552071d4279SBram Moolenaar     return NULL;
2553071d4279SBram Moolenaar }
2554071d4279SBram Moolenaar 
2555071d4279SBram Moolenaar /*
2556071d4279SBram Moolenaar  * Find file in buffer list by a regexp pattern.
2557071d4279SBram Moolenaar  * Return fnum of the found buffer.
2558071d4279SBram Moolenaar  * Return < 0 for error.
2559071d4279SBram Moolenaar  */
2560071d4279SBram Moolenaar     int
buflist_findpat(char_u * pattern,char_u * pattern_end,int unlisted,int diffmode UNUSED,int curtab_only)25617454a06eSBram Moolenaar buflist_findpat(
25627454a06eSBram Moolenaar     char_u	*pattern,
2563c667da51SBram Moolenaar     char_u	*pattern_end,	// pointer to first char after pattern
2564c667da51SBram Moolenaar     int		unlisted,	// find unlisted buffers
2565c667da51SBram Moolenaar     int		diffmode UNUSED, // find diff-mode buffers only
2566c667da51SBram Moolenaar     int		curtab_only)	// find buffers in current tab only
2567071d4279SBram Moolenaar {
2568071d4279SBram Moolenaar     buf_T	*buf;
2569071d4279SBram Moolenaar     int		match = -1;
2570071d4279SBram Moolenaar     int		find_listed;
2571071d4279SBram Moolenaar     char_u	*pat;
2572071d4279SBram Moolenaar     char_u	*patend;
2573071d4279SBram Moolenaar     int		attempt;
2574071d4279SBram Moolenaar     char_u	*p;
2575071d4279SBram Moolenaar     int		toggledollar;
2576071d4279SBram Moolenaar 
2577dfbc5fd8SBram Moolenaar     // "%" is current file, "%%" or "#" is alternate file
2578dfbc5fd8SBram Moolenaar     if ((pattern_end == pattern + 1 && (*pattern == '%' || *pattern == '#'))
2579dfbc5fd8SBram Moolenaar 	    || (in_vim9script() && pattern_end == pattern + 2
2580dfbc5fd8SBram Moolenaar 				    && pattern[0] == '%' && pattern[1] == '%'))
2581071d4279SBram Moolenaar     {
2582dfbc5fd8SBram Moolenaar 	if (*pattern == '#' || pattern_end == pattern + 2)
2583071d4279SBram Moolenaar 	    match = curwin->w_alt_fnum;
2584dfbc5fd8SBram Moolenaar 	else
2585dfbc5fd8SBram Moolenaar 	    match = curbuf->b_fnum;
2586071d4279SBram Moolenaar #ifdef FEAT_DIFF
2587071d4279SBram Moolenaar 	if (diffmode && !diff_mode_buf(buflist_findnr(match)))
2588071d4279SBram Moolenaar 	    match = -1;
2589071d4279SBram Moolenaar #endif
2590071d4279SBram Moolenaar     }
2591071d4279SBram Moolenaar 
2592071d4279SBram Moolenaar     /*
2593071d4279SBram Moolenaar      * Try four ways of matching a listed buffer:
2594071d4279SBram Moolenaar      * attempt == 0: without '^' or '$' (at any position)
2595b6799acdSBram Moolenaar      * attempt == 1: with '^' at start (only at position 0)
2596071d4279SBram Moolenaar      * attempt == 2: with '$' at end (only match at end)
2597071d4279SBram Moolenaar      * attempt == 3: with '^' at start and '$' at end (only full match)
2598071d4279SBram Moolenaar      * Repeat this for finding an unlisted buffer if there was no matching
2599071d4279SBram Moolenaar      * listed buffer.
2600071d4279SBram Moolenaar      */
2601071d4279SBram Moolenaar     else
2602071d4279SBram Moolenaar     {
2603071d4279SBram Moolenaar 	pat = file_pat_to_reg_pat(pattern, pattern_end, NULL, FALSE);
2604071d4279SBram Moolenaar 	if (pat == NULL)
2605071d4279SBram Moolenaar 	    return -1;
2606071d4279SBram Moolenaar 	patend = pat + STRLEN(pat) - 1;
2607071d4279SBram Moolenaar 	toggledollar = (patend > pat && *patend == '$');
2608071d4279SBram Moolenaar 
2609c667da51SBram Moolenaar 	// First try finding a listed buffer.  If not found and "unlisted"
2610c667da51SBram Moolenaar 	// is TRUE, try finding an unlisted buffer.
2611071d4279SBram Moolenaar 	find_listed = TRUE;
2612071d4279SBram Moolenaar 	for (;;)
2613071d4279SBram Moolenaar 	{
2614071d4279SBram Moolenaar 	    for (attempt = 0; attempt <= 3; ++attempt)
2615071d4279SBram Moolenaar 	    {
2616dffa5b8eSBram Moolenaar 		regmatch_T	regmatch;
2617dffa5b8eSBram Moolenaar 
2618c667da51SBram Moolenaar 		// may add '^' and '$'
2619071d4279SBram Moolenaar 		if (toggledollar)
2620c667da51SBram Moolenaar 		    *patend = (attempt < 2) ? NUL : '$'; // add/remove '$'
2621071d4279SBram Moolenaar 		p = pat;
2622c667da51SBram Moolenaar 		if (*p == '^' && !(attempt & 1))	 // add/remove '^'
2623071d4279SBram Moolenaar 		    ++p;
2624f4e2099eSBram Moolenaar 		regmatch.regprog = vim_regcomp(p, magic_isset() ? RE_MAGIC : 0);
2625dffa5b8eSBram Moolenaar 		if (regmatch.regprog == NULL)
2626071d4279SBram Moolenaar 		{
2627071d4279SBram Moolenaar 		    vim_free(pat);
2628071d4279SBram Moolenaar 		    return -1;
2629071d4279SBram Moolenaar 		}
2630071d4279SBram Moolenaar 
263100d253e2SBram Moolenaar 		FOR_ALL_BUFS_FROM_LAST(buf)
2632071d4279SBram Moolenaar 		    if (buf->b_p_bl == find_listed
2633071d4279SBram Moolenaar #ifdef FEAT_DIFF
2634071d4279SBram Moolenaar 			    && (!diffmode || diff_mode_buf(buf))
2635071d4279SBram Moolenaar #endif
2636dffa5b8eSBram Moolenaar 			    && buflist_match(&regmatch, buf, FALSE) != NULL)
2637071d4279SBram Moolenaar 		    {
26380c279bbbSBram Moolenaar 			if (curtab_only)
26390c279bbbSBram Moolenaar 			{
2640c667da51SBram Moolenaar 			    // Ignore the match if the buffer is not open in
2641c667da51SBram Moolenaar 			    // the current tab.
26420c279bbbSBram Moolenaar 			    win_T	*wp;
26430c279bbbSBram Moolenaar 
264429323590SBram Moolenaar 			    FOR_ALL_WINDOWS(wp)
26450c279bbbSBram Moolenaar 				if (wp->w_buffer == buf)
26460c279bbbSBram Moolenaar 				    break;
26470c279bbbSBram Moolenaar 			    if (wp == NULL)
26480c279bbbSBram Moolenaar 				continue;
26490c279bbbSBram Moolenaar 			}
2650c667da51SBram Moolenaar 			if (match >= 0)		// already found a match
2651071d4279SBram Moolenaar 			{
2652071d4279SBram Moolenaar 			    match = -2;
2653071d4279SBram Moolenaar 			    break;
2654071d4279SBram Moolenaar 			}
2655c667da51SBram Moolenaar 			match = buf->b_fnum;	// remember first match
2656071d4279SBram Moolenaar 		    }
2657071d4279SBram Moolenaar 
2658dffa5b8eSBram Moolenaar 		vim_regfree(regmatch.regprog);
2659c667da51SBram Moolenaar 		if (match >= 0)			// found one match
2660071d4279SBram Moolenaar 		    break;
2661071d4279SBram Moolenaar 	    }
2662071d4279SBram Moolenaar 
2663c667da51SBram Moolenaar 	    // Only search for unlisted buffers if there was no match with
2664c667da51SBram Moolenaar 	    // a listed buffer.
2665071d4279SBram Moolenaar 	    if (!unlisted || !find_listed || match != -1)
2666071d4279SBram Moolenaar 		break;
2667071d4279SBram Moolenaar 	    find_listed = FALSE;
2668071d4279SBram Moolenaar 	}
2669071d4279SBram Moolenaar 
2670071d4279SBram Moolenaar 	vim_free(pat);
2671071d4279SBram Moolenaar     }
2672071d4279SBram Moolenaar 
2673071d4279SBram Moolenaar     if (match == -2)
2674f9e3e09fSBram Moolenaar 	semsg(_("E93: More than one match for %s"), pattern);
2675071d4279SBram Moolenaar     else if (match < 0)
2676f9e3e09fSBram Moolenaar 	semsg(_("E94: No matching buffer for %s"), pattern);
2677071d4279SBram Moolenaar     return match;
2678071d4279SBram Moolenaar }
2679071d4279SBram Moolenaar 
268052410575SBram Moolenaar #ifdef FEAT_VIMINFO
268152410575SBram Moolenaar typedef struct {
268252410575SBram Moolenaar     buf_T   *buf;
268352410575SBram Moolenaar     char_u  *match;
268452410575SBram Moolenaar } bufmatch_T;
268552410575SBram Moolenaar #endif
268652410575SBram Moolenaar 
2687071d4279SBram Moolenaar /*
2688071d4279SBram Moolenaar  * Find all buffer names that match.
2689071d4279SBram Moolenaar  * For command line expansion of ":buf" and ":sbuf".
2690071d4279SBram Moolenaar  * Return OK if matches found, FAIL otherwise.
2691071d4279SBram Moolenaar  */
2692071d4279SBram Moolenaar     int
ExpandBufnames(char_u * pat,int * num_file,char_u *** file,int options)26937454a06eSBram Moolenaar ExpandBufnames(
26947454a06eSBram Moolenaar     char_u	*pat,
26957454a06eSBram Moolenaar     int		*num_file,
26967454a06eSBram Moolenaar     char_u	***file,
26977454a06eSBram Moolenaar     int		options)
2698071d4279SBram Moolenaar {
2699071d4279SBram Moolenaar     int		count = 0;
2700071d4279SBram Moolenaar     buf_T	*buf;
2701071d4279SBram Moolenaar     int		round;
2702071d4279SBram Moolenaar     char_u	*p;
2703071d4279SBram Moolenaar     int		attempt;
270405159a0cSBram Moolenaar     char_u	*patc;
270552410575SBram Moolenaar #ifdef FEAT_VIMINFO
270652410575SBram Moolenaar     bufmatch_T	*matches = NULL;
270752410575SBram Moolenaar #endif
2708071d4279SBram Moolenaar 
2709c667da51SBram Moolenaar     *num_file = 0;		    // return values in case of FAIL
2710071d4279SBram Moolenaar     *file = NULL;
2711071d4279SBram Moolenaar 
2712efcc3290SBram Moolenaar #ifdef FEAT_DIFF
2713efcc3290SBram Moolenaar     if ((options & BUF_DIFF_FILTER) && !curwin->w_p_diff)
2714efcc3290SBram Moolenaar 	return FAIL;
2715efcc3290SBram Moolenaar #endif
2716efcc3290SBram Moolenaar 
2717c667da51SBram Moolenaar     // Make a copy of "pat" and change "^" to "\(^\|[\/]\)".
271805159a0cSBram Moolenaar     if (*pat == '^')
2719071d4279SBram Moolenaar     {
2720964b3746SBram Moolenaar 	patc = alloc(STRLEN(pat) + 11);
272105159a0cSBram Moolenaar 	if (patc == NULL)
2722071d4279SBram Moolenaar 	    return FAIL;
272305159a0cSBram Moolenaar 	STRCPY(patc, "\\(^\\|[\\/]\\)");
272405159a0cSBram Moolenaar 	STRCPY(patc + 11, pat + 1);
272505159a0cSBram Moolenaar     }
272605159a0cSBram Moolenaar     else
272705159a0cSBram Moolenaar 	patc = pat;
272805159a0cSBram Moolenaar 
2729c5935a85SBram Moolenaar     // attempt == 0: try match with    '\<', match at start of word
2730c5935a85SBram Moolenaar     // attempt == 1: try match without '\<', match anywhere
2731d8a4e563SBram Moolenaar     for (attempt = 0; attempt <= 1; ++attempt)
273205159a0cSBram Moolenaar     {
2733dffa5b8eSBram Moolenaar 	regmatch_T	regmatch;
2734dffa5b8eSBram Moolenaar 
2735d8a4e563SBram Moolenaar 	if (attempt > 0 && patc == pat)
2736c667da51SBram Moolenaar 	    break;	// there was no anchor, no need to try again
2737dffa5b8eSBram Moolenaar 	regmatch.regprog = vim_regcomp(patc + attempt * 11, RE_MAGIC);
2738dffa5b8eSBram Moolenaar 	if (regmatch.regprog == NULL)
273905159a0cSBram Moolenaar 	{
274005159a0cSBram Moolenaar 	    if (patc != pat)
274105159a0cSBram Moolenaar 		vim_free(patc);
274205159a0cSBram Moolenaar 	    return FAIL;
274305159a0cSBram Moolenaar 	}
2744071d4279SBram Moolenaar 
2745c5935a85SBram Moolenaar 	// round == 1: Count the matches.
2746c5935a85SBram Moolenaar 	// round == 2: Build the array to keep the matches.
2747071d4279SBram Moolenaar 	for (round = 1; round <= 2; ++round)
2748071d4279SBram Moolenaar 	{
2749071d4279SBram Moolenaar 	    count = 0;
275029323590SBram Moolenaar 	    FOR_ALL_BUFFERS(buf)
2751071d4279SBram Moolenaar 	    {
2752c667da51SBram Moolenaar 		if (!buf->b_p_bl)	// skip unlisted buffers
2753071d4279SBram Moolenaar 		    continue;
2754ae7dba89SBram Moolenaar #ifdef FEAT_DIFF
2755ae7dba89SBram Moolenaar 		if (options & BUF_DIFF_FILTER)
2756ae7dba89SBram Moolenaar 		    // Skip buffers not suitable for
2757ae7dba89SBram Moolenaar 		    // :diffget or :diffput completion.
2758efcc3290SBram Moolenaar 		    if (buf == curbuf || !diff_mode_buf(buf))
2759ae7dba89SBram Moolenaar 			continue;
2760ae7dba89SBram Moolenaar #endif
2761ae7dba89SBram Moolenaar 
2762dffa5b8eSBram Moolenaar 		p = buflist_match(&regmatch, buf, p_wic);
2763071d4279SBram Moolenaar 		if (p != NULL)
2764071d4279SBram Moolenaar 		{
2765071d4279SBram Moolenaar 		    if (round == 1)
2766071d4279SBram Moolenaar 			++count;
2767071d4279SBram Moolenaar 		    else
2768071d4279SBram Moolenaar 		    {
2769071d4279SBram Moolenaar 			if (options & WILD_HOME_REPLACE)
2770071d4279SBram Moolenaar 			    p = home_replace_save(buf, p);
2771071d4279SBram Moolenaar 			else
2772071d4279SBram Moolenaar 			    p = vim_strsave(p);
277352410575SBram Moolenaar #ifdef FEAT_VIMINFO
277452410575SBram Moolenaar 			if (matches != NULL)
277552410575SBram Moolenaar 			{
277652410575SBram Moolenaar 			    matches[count].buf = buf;
277752410575SBram Moolenaar 			    matches[count].match = p;
277852410575SBram Moolenaar 			    count++;
277952410575SBram Moolenaar 			}
278052410575SBram Moolenaar 			else
278152410575SBram Moolenaar #endif
2782071d4279SBram Moolenaar 			    (*file)[count++] = p;
2783071d4279SBram Moolenaar 		    }
2784071d4279SBram Moolenaar 		}
2785071d4279SBram Moolenaar 	    }
2786c667da51SBram Moolenaar 	    if (count == 0)	// no match found, break here
2787071d4279SBram Moolenaar 		break;
2788071d4279SBram Moolenaar 	    if (round == 1)
2789071d4279SBram Moolenaar 	    {
2790c799fe20SBram Moolenaar 		*file = ALLOC_MULT(char_u *, count);
2791071d4279SBram Moolenaar 		if (*file == NULL)
2792071d4279SBram Moolenaar 		{
2793dffa5b8eSBram Moolenaar 		    vim_regfree(regmatch.regprog);
279405159a0cSBram Moolenaar 		    if (patc != pat)
279505159a0cSBram Moolenaar 			vim_free(patc);
2796071d4279SBram Moolenaar 		    return FAIL;
2797071d4279SBram Moolenaar 		}
279852410575SBram Moolenaar #ifdef FEAT_VIMINFO
279952410575SBram Moolenaar 		if (options & WILD_BUFLASTUSED)
280052410575SBram Moolenaar 		    matches = ALLOC_MULT(bufmatch_T, count);
280152410575SBram Moolenaar #endif
2802071d4279SBram Moolenaar 	    }
2803071d4279SBram Moolenaar 	}
2804dffa5b8eSBram Moolenaar 	vim_regfree(regmatch.regprog);
2805c667da51SBram Moolenaar 	if (count)		// match(es) found, break here
2806071d4279SBram Moolenaar 	    break;
2807071d4279SBram Moolenaar     }
2808071d4279SBram Moolenaar 
280905159a0cSBram Moolenaar     if (patc != pat)
281005159a0cSBram Moolenaar 	vim_free(patc);
281105159a0cSBram Moolenaar 
281252410575SBram Moolenaar #ifdef FEAT_VIMINFO
281352410575SBram Moolenaar     if (matches != NULL)
281452410575SBram Moolenaar     {
281552410575SBram Moolenaar 	int i;
281652410575SBram Moolenaar 	if (count > 1)
281752410575SBram Moolenaar 	    qsort(matches, count, sizeof(bufmatch_T), buf_compare);
281852410575SBram Moolenaar 	// if the current buffer is first in the list, place it at the end
281952410575SBram Moolenaar 	if (matches[0].buf == curbuf)
282052410575SBram Moolenaar 	{
282152410575SBram Moolenaar 	    for (i = 1; i < count; i++)
282252410575SBram Moolenaar 		(*file)[i-1] = matches[i].match;
282352410575SBram Moolenaar 	    (*file)[count-1] = matches[0].match;
282452410575SBram Moolenaar 	}
282552410575SBram Moolenaar 	else
282652410575SBram Moolenaar 	{
282752410575SBram Moolenaar 	    for (i = 0; i < count; i++)
282852410575SBram Moolenaar 		(*file)[i] = matches[i].match;
282952410575SBram Moolenaar 	}
283052410575SBram Moolenaar 	vim_free(matches);
283152410575SBram Moolenaar     }
283252410575SBram Moolenaar #endif
283352410575SBram Moolenaar 
2834071d4279SBram Moolenaar     *num_file = count;
2835071d4279SBram Moolenaar     return (count == 0 ? FAIL : OK);
2836071d4279SBram Moolenaar }
2837071d4279SBram Moolenaar 
2838071d4279SBram Moolenaar /*
2839071d4279SBram Moolenaar  * Check for a match on the file name for buffer "buf" with regprog "prog".
2840071d4279SBram Moolenaar  */
2841071d4279SBram Moolenaar     static char_u *
buflist_match(regmatch_T * rmp,buf_T * buf,int ignore_case)28427454a06eSBram Moolenaar buflist_match(
28437454a06eSBram Moolenaar     regmatch_T	*rmp,
28447454a06eSBram Moolenaar     buf_T	*buf,
2845c667da51SBram Moolenaar     int		ignore_case)  // when TRUE ignore case, when FALSE use 'fic'
2846071d4279SBram Moolenaar {
2847071d4279SBram Moolenaar     char_u	*match;
2848071d4279SBram Moolenaar 
2849c667da51SBram Moolenaar     // First try the short file name, then the long file name.
2850dffa5b8eSBram Moolenaar     match = fname_match(rmp, buf->b_sfname, ignore_case);
2851071d4279SBram Moolenaar     if (match == NULL)
2852dffa5b8eSBram Moolenaar 	match = fname_match(rmp, buf->b_ffname, ignore_case);
2853071d4279SBram Moolenaar 
2854071d4279SBram Moolenaar     return match;
2855071d4279SBram Moolenaar }
2856071d4279SBram Moolenaar 
2857071d4279SBram Moolenaar /*
2858071d4279SBram Moolenaar  * Try matching the regexp in "prog" with file name "name".
2859071d4279SBram Moolenaar  * Return "name" when there is a match, NULL when not.
2860071d4279SBram Moolenaar  */
2861071d4279SBram Moolenaar     static char_u *
fname_match(regmatch_T * rmp,char_u * name,int ignore_case)28627454a06eSBram Moolenaar fname_match(
28637454a06eSBram Moolenaar     regmatch_T	*rmp,
28647454a06eSBram Moolenaar     char_u	*name,
2865c667da51SBram Moolenaar     int		ignore_case)  // when TRUE ignore case, when FALSE use 'fic'
2866071d4279SBram Moolenaar {
2867071d4279SBram Moolenaar     char_u	*match = NULL;
2868071d4279SBram Moolenaar     char_u	*p;
2869071d4279SBram Moolenaar 
2870071d4279SBram Moolenaar     if (name != NULL)
2871071d4279SBram Moolenaar     {
2872c667da51SBram Moolenaar 	// Ignore case when 'fileignorecase' or the argument is set.
2873dffa5b8eSBram Moolenaar 	rmp->rm_ic = p_fic || ignore_case;
2874dffa5b8eSBram Moolenaar 	if (vim_regexec(rmp, name, (colnr_T)0))
2875071d4279SBram Moolenaar 	    match = name;
2876071d4279SBram Moolenaar 	else
2877071d4279SBram Moolenaar 	{
2878c667da51SBram Moolenaar 	    // Replace $(HOME) with '~' and try matching again.
2879071d4279SBram Moolenaar 	    p = home_replace_save(NULL, name);
2880dffa5b8eSBram Moolenaar 	    if (p != NULL && vim_regexec(rmp, p, (colnr_T)0))
2881071d4279SBram Moolenaar 		match = name;
2882071d4279SBram Moolenaar 	    vim_free(p);
2883071d4279SBram Moolenaar 	}
2884071d4279SBram Moolenaar     }
2885071d4279SBram Moolenaar 
2886071d4279SBram Moolenaar     return match;
2887071d4279SBram Moolenaar }
2888071d4279SBram Moolenaar 
2889071d4279SBram Moolenaar /*
2890480778b8SBram Moolenaar  * Find a file in the buffer list by buffer number.
2891071d4279SBram Moolenaar  */
2892071d4279SBram Moolenaar     buf_T *
buflist_findnr(int nr)28937454a06eSBram Moolenaar buflist_findnr(int nr)
2894071d4279SBram Moolenaar {
2895480778b8SBram Moolenaar     char_u	key[VIM_SIZEOF_INT * 2 + 1];
2896480778b8SBram Moolenaar     hashitem_T	*hi;
2897071d4279SBram Moolenaar 
2898071d4279SBram Moolenaar     if (nr == 0)
2899071d4279SBram Moolenaar 	nr = curwin->w_alt_fnum;
2900480778b8SBram Moolenaar     sprintf((char *)key, "%x", nr);
2901480778b8SBram Moolenaar     hi = hash_find(&buf_hashtab, key);
2902480778b8SBram Moolenaar 
2903480778b8SBram Moolenaar     if (!HASHITEM_EMPTY(hi))
2904480778b8SBram Moolenaar 	return (buf_T *)(hi->hi_key
2905480778b8SBram Moolenaar 			     - ((unsigned)(curbuf->b_key - (char_u *)curbuf)));
2906071d4279SBram Moolenaar     return NULL;
2907071d4279SBram Moolenaar }
2908071d4279SBram Moolenaar 
2909071d4279SBram Moolenaar /*
2910071d4279SBram Moolenaar  * Get name of file 'n' in the buffer list.
2911071d4279SBram Moolenaar  * When the file has no name an empty string is returned.
2912071d4279SBram Moolenaar  * home_replace() is used to shorten the file name (used for marks).
2913071d4279SBram Moolenaar  * Returns a pointer to allocated memory, of NULL when failed.
2914071d4279SBram Moolenaar  */
2915071d4279SBram Moolenaar     char_u *
buflist_nr2name(int n,int fullname,int helptail)29167454a06eSBram Moolenaar buflist_nr2name(
29177454a06eSBram Moolenaar     int		n,
29187454a06eSBram Moolenaar     int		fullname,
2919c667da51SBram Moolenaar     int		helptail)	// for help buffers return tail only
2920071d4279SBram Moolenaar {
2921071d4279SBram Moolenaar     buf_T	*buf;
2922071d4279SBram Moolenaar 
2923071d4279SBram Moolenaar     buf = buflist_findnr(n);
2924071d4279SBram Moolenaar     if (buf == NULL)
2925071d4279SBram Moolenaar 	return NULL;
2926071d4279SBram Moolenaar     return home_replace_save(helptail ? buf : NULL,
2927071d4279SBram Moolenaar 				     fullname ? buf->b_ffname : buf->b_fname);
2928071d4279SBram Moolenaar }
2929071d4279SBram Moolenaar 
2930071d4279SBram Moolenaar /*
2931071d4279SBram Moolenaar  * Set the "lnum" and "col" for the buffer "buf" and the current window.
2932071d4279SBram Moolenaar  * When "copy_options" is TRUE save the local window option values.
2933071d4279SBram Moolenaar  * When "lnum" is 0 only do the options.
2934071d4279SBram Moolenaar  */
2935defa067cSBram Moolenaar     void
buflist_setfpos(buf_T * buf,win_T * win,linenr_T lnum,colnr_T col,int copy_options)29367454a06eSBram Moolenaar buflist_setfpos(
29377454a06eSBram Moolenaar     buf_T	*buf,
293889b693e5SBram Moolenaar     win_T	*win,		// may be NULL when using :badd
29397454a06eSBram Moolenaar     linenr_T	lnum,
29407454a06eSBram Moolenaar     colnr_T	col,
29417454a06eSBram Moolenaar     int		copy_options)
2942071d4279SBram Moolenaar {
2943071d4279SBram Moolenaar     wininfo_T	*wip;
2944071d4279SBram Moolenaar 
2945aeea7215SBram Moolenaar     FOR_ALL_BUF_WININFO(buf, wip)
2946071d4279SBram Moolenaar 	if (wip->wi_win == win)
2947071d4279SBram Moolenaar 	    break;
2948071d4279SBram Moolenaar     if (wip == NULL)
2949071d4279SBram Moolenaar     {
2950c667da51SBram Moolenaar 	// allocate a new entry
2951c799fe20SBram Moolenaar 	wip = ALLOC_CLEAR_ONE(wininfo_T);
2952071d4279SBram Moolenaar 	if (wip == NULL)
2953071d4279SBram Moolenaar 	    return;
2954071d4279SBram Moolenaar 	wip->wi_win = win;
2955c667da51SBram Moolenaar 	if (lnum == 0)		// set lnum even when it's 0
2956071d4279SBram Moolenaar 	    lnum = 1;
2957071d4279SBram Moolenaar     }
2958071d4279SBram Moolenaar     else
2959071d4279SBram Moolenaar     {
2960c667da51SBram Moolenaar 	// remove the entry from the list
2961071d4279SBram Moolenaar 	if (wip->wi_prev)
2962071d4279SBram Moolenaar 	    wip->wi_prev->wi_next = wip->wi_next;
2963071d4279SBram Moolenaar 	else
2964071d4279SBram Moolenaar 	    buf->b_wininfo = wip->wi_next;
2965071d4279SBram Moolenaar 	if (wip->wi_next)
2966071d4279SBram Moolenaar 	    wip->wi_next->wi_prev = wip->wi_prev;
2967071d4279SBram Moolenaar 	if (copy_options && wip->wi_optset)
2968071d4279SBram Moolenaar 	{
2969071d4279SBram Moolenaar 	    clear_winopt(&wip->wi_opt);
2970071d4279SBram Moolenaar #ifdef FEAT_FOLDING
2971071d4279SBram Moolenaar 	    deleteFoldRecurse(&wip->wi_folds);
2972071d4279SBram Moolenaar #endif
2973071d4279SBram Moolenaar 	}
2974071d4279SBram Moolenaar     }
2975071d4279SBram Moolenaar     if (lnum != 0)
2976071d4279SBram Moolenaar     {
2977071d4279SBram Moolenaar 	wip->wi_fpos.lnum = lnum;
2978071d4279SBram Moolenaar 	wip->wi_fpos.col = col;
2979071d4279SBram Moolenaar     }
298089b693e5SBram Moolenaar     if (copy_options && win != NULL)
2981071d4279SBram Moolenaar     {
2982c667da51SBram Moolenaar 	// Save the window-specific option values.
2983071d4279SBram Moolenaar 	copy_winopt(&win->w_onebuf_opt, &wip->wi_opt);
2984071d4279SBram Moolenaar #ifdef FEAT_FOLDING
2985071d4279SBram Moolenaar 	wip->wi_fold_manual = win->w_fold_manual;
2986071d4279SBram Moolenaar 	cloneFoldGrowArray(&win->w_folds, &wip->wi_folds);
2987071d4279SBram Moolenaar #endif
2988071d4279SBram Moolenaar 	wip->wi_optset = TRUE;
2989071d4279SBram Moolenaar     }
2990071d4279SBram Moolenaar 
2991c667da51SBram Moolenaar     // insert the entry in front of the list
2992071d4279SBram Moolenaar     wip->wi_next = buf->b_wininfo;
2993071d4279SBram Moolenaar     buf->b_wininfo = wip;
2994071d4279SBram Moolenaar     wip->wi_prev = NULL;
2995071d4279SBram Moolenaar     if (wip->wi_next)
2996071d4279SBram Moolenaar 	wip->wi_next->wi_prev = wip;
2997071d4279SBram Moolenaar }
2998071d4279SBram Moolenaar 
2999701f7afcSBram Moolenaar #ifdef FEAT_DIFF
3000701f7afcSBram Moolenaar /*
3001701f7afcSBram Moolenaar  * Return TRUE when "wip" has 'diff' set and the diff is only for another tab
3002701f7afcSBram Moolenaar  * page.  That's because a diff is local to a tab page.
3003701f7afcSBram Moolenaar  */
3004701f7afcSBram Moolenaar     static int
wininfo_other_tab_diff(wininfo_T * wip)30057454a06eSBram Moolenaar wininfo_other_tab_diff(wininfo_T *wip)
3006701f7afcSBram Moolenaar {
3007701f7afcSBram Moolenaar     win_T	*wp;
3008701f7afcSBram Moolenaar 
3009701f7afcSBram Moolenaar     if (wip->wi_opt.wo_diff)
3010701f7afcSBram Moolenaar     {
301129323590SBram Moolenaar 	FOR_ALL_WINDOWS(wp)
3012c667da51SBram Moolenaar 	    // return FALSE when it's a window in the current tab page, thus
3013c667da51SBram Moolenaar 	    // the buffer was in diff mode here
3014701f7afcSBram Moolenaar 	    if (wip->wi_win == wp)
3015701f7afcSBram Moolenaar 		return FALSE;
3016701f7afcSBram Moolenaar 	return TRUE;
3017701f7afcSBram Moolenaar     }
3018701f7afcSBram Moolenaar     return FALSE;
3019701f7afcSBram Moolenaar }
3020701f7afcSBram Moolenaar #endif
3021701f7afcSBram Moolenaar 
3022071d4279SBram Moolenaar /*
3023071d4279SBram Moolenaar  * Find info for the current window in buffer "buf".
3024071d4279SBram Moolenaar  * If not found, return the info for the most recently used window.
302589b693e5SBram Moolenaar  * When "need_options" is TRUE skip entries where wi_optset is FALSE.
3026701f7afcSBram Moolenaar  * When "skip_diff_buffer" is TRUE avoid windows with 'diff' set that is in
3027701f7afcSBram Moolenaar  * another tab page.
3028071d4279SBram Moolenaar  * Returns NULL when there isn't any info.
3029071d4279SBram Moolenaar  */
3030071d4279SBram Moolenaar     static wininfo_T *
find_wininfo(buf_T * buf,int need_options,int skip_diff_buffer UNUSED)30317454a06eSBram Moolenaar find_wininfo(
30327454a06eSBram Moolenaar     buf_T	*buf,
303389b693e5SBram Moolenaar     int		need_options,
30347454a06eSBram Moolenaar     int		skip_diff_buffer UNUSED)
3035071d4279SBram Moolenaar {
3036071d4279SBram Moolenaar     wininfo_T	*wip;
3037071d4279SBram Moolenaar 
3038aeea7215SBram Moolenaar     FOR_ALL_BUF_WININFO(buf, wip)
3039701f7afcSBram Moolenaar 	if (wip->wi_win == curwin
3040701f7afcSBram Moolenaar #ifdef FEAT_DIFF
3041701f7afcSBram Moolenaar 		&& (!skip_diff_buffer || !wininfo_other_tab_diff(wip))
3042701f7afcSBram Moolenaar #endif
304389b693e5SBram Moolenaar 
304489b693e5SBram Moolenaar 		&& (!need_options || wip->wi_optset))
3045071d4279SBram Moolenaar 	    break;
3046701f7afcSBram Moolenaar 
3047c667da51SBram Moolenaar     // If no wininfo for curwin, use the first in the list (that doesn't have
3048c667da51SBram Moolenaar     // 'diff' set and is in another tab page).
304989b693e5SBram Moolenaar     // If "need_options" is TRUE skip entries that don't have options set,
305089b693e5SBram Moolenaar     // unless the window is editing "buf", so we can copy from the window
305189b693e5SBram Moolenaar     // itself.
3052701f7afcSBram Moolenaar     if (wip == NULL)
3053701f7afcSBram Moolenaar     {
3054701f7afcSBram Moolenaar #ifdef FEAT_DIFF
3055701f7afcSBram Moolenaar 	if (skip_diff_buffer)
3056701f7afcSBram Moolenaar 	{
3057aeea7215SBram Moolenaar 	    FOR_ALL_BUF_WININFO(buf, wip)
305889b693e5SBram Moolenaar 		if (!wininfo_other_tab_diff(wip)
305989b693e5SBram Moolenaar 			&& (!need_options || wip->wi_optset
306089b693e5SBram Moolenaar 			    || (wip->wi_win != NULL
306189b693e5SBram Moolenaar 					     && wip->wi_win->w_buffer == buf)))
3062701f7afcSBram Moolenaar 		    break;
3063701f7afcSBram Moolenaar 	}
3064701f7afcSBram Moolenaar 	else
3065701f7afcSBram Moolenaar #endif
3066071d4279SBram Moolenaar 	    wip = buf->b_wininfo;
3067701f7afcSBram Moolenaar     }
3068071d4279SBram Moolenaar     return wip;
3069071d4279SBram Moolenaar }
3070071d4279SBram Moolenaar 
3071071d4279SBram Moolenaar /*
3072071d4279SBram Moolenaar  * Reset the local window options to the values last used in this window.
3073071d4279SBram Moolenaar  * If the buffer wasn't used in this window before, use the values from
3074071d4279SBram Moolenaar  * the most recently used window.  If the values were never set, use the
3075071d4279SBram Moolenaar  * global values for the window.
3076071d4279SBram Moolenaar  */
3077071d4279SBram Moolenaar     void
get_winopts(buf_T * buf)30787454a06eSBram Moolenaar get_winopts(buf_T *buf)
3079071d4279SBram Moolenaar {
3080071d4279SBram Moolenaar     wininfo_T	*wip;
3081071d4279SBram Moolenaar 
3082071d4279SBram Moolenaar     clear_winopt(&curwin->w_onebuf_opt);
3083071d4279SBram Moolenaar #ifdef FEAT_FOLDING
3084071d4279SBram Moolenaar     clearFolding(curwin);
3085071d4279SBram Moolenaar #endif
3086071d4279SBram Moolenaar 
308789b693e5SBram Moolenaar     wip = find_wininfo(buf, TRUE, TRUE);
308825782a7fSBram Moolenaar     if (wip != NULL && wip->wi_win != NULL
308925782a7fSBram Moolenaar 	    && wip->wi_win != curwin && wip->wi_win->w_buffer == buf)
3090071d4279SBram Moolenaar     {
3091c667da51SBram Moolenaar 	// The buffer is currently displayed in the window: use the actual
3092c667da51SBram Moolenaar 	// option values instead of the saved (possibly outdated) values.
309325782a7fSBram Moolenaar 	win_T *wp = wip->wi_win;
309425782a7fSBram Moolenaar 
309525782a7fSBram Moolenaar 	copy_winopt(&wp->w_onebuf_opt, &curwin->w_onebuf_opt);
309625782a7fSBram Moolenaar #ifdef FEAT_FOLDING
309725782a7fSBram Moolenaar 	curwin->w_fold_manual = wp->w_fold_manual;
309825782a7fSBram Moolenaar 	curwin->w_foldinvalid = TRUE;
309925782a7fSBram Moolenaar 	cloneFoldGrowArray(&wp->w_folds, &curwin->w_folds);
310025782a7fSBram Moolenaar #endif
310125782a7fSBram Moolenaar     }
310225782a7fSBram Moolenaar     else if (wip != NULL && wip->wi_optset)
310325782a7fSBram Moolenaar     {
3104c667da51SBram Moolenaar 	// the buffer was displayed in the current window earlier
3105071d4279SBram Moolenaar 	copy_winopt(&wip->wi_opt, &curwin->w_onebuf_opt);
3106071d4279SBram Moolenaar #ifdef FEAT_FOLDING
3107071d4279SBram Moolenaar 	curwin->w_fold_manual = wip->wi_fold_manual;
3108071d4279SBram Moolenaar 	curwin->w_foldinvalid = TRUE;
3109071d4279SBram Moolenaar 	cloneFoldGrowArray(&wip->wi_folds, &curwin->w_folds);
3110071d4279SBram Moolenaar #endif
3111071d4279SBram Moolenaar     }
3112071d4279SBram Moolenaar     else
3113071d4279SBram Moolenaar 	copy_winopt(&curwin->w_allbuf_opt, &curwin->w_onebuf_opt);
3114071d4279SBram Moolenaar 
3115071d4279SBram Moolenaar #ifdef FEAT_FOLDING
3116c667da51SBram Moolenaar     // Set 'foldlevel' to 'foldlevelstart' if it's not negative.
3117071d4279SBram Moolenaar     if (p_fdls >= 0)
3118071d4279SBram Moolenaar 	curwin->w_p_fdl = p_fdls;
3119071d4279SBram Moolenaar #endif
3120010ee965SBram Moolenaar     after_copy_winopt(curwin);
3121071d4279SBram Moolenaar }
3122071d4279SBram Moolenaar 
3123071d4279SBram Moolenaar /*
3124071d4279SBram Moolenaar  * Find the position (lnum and col) for the buffer 'buf' for the current
3125071d4279SBram Moolenaar  * window.
3126071d4279SBram Moolenaar  * Returns a pointer to no_position if no position is found.
3127071d4279SBram Moolenaar  */
3128071d4279SBram Moolenaar     pos_T *
buflist_findfpos(buf_T * buf)31297454a06eSBram Moolenaar buflist_findfpos(buf_T *buf)
3130071d4279SBram Moolenaar {
3131071d4279SBram Moolenaar     wininfo_T	*wip;
313229ddebefSBram Moolenaar     static pos_T no_position = {1, 0, 0};
3133071d4279SBram Moolenaar 
313489b693e5SBram Moolenaar     wip = find_wininfo(buf, FALSE, FALSE);
3135071d4279SBram Moolenaar     if (wip != NULL)
3136071d4279SBram Moolenaar 	return &(wip->wi_fpos);
3137071d4279SBram Moolenaar     else
3138071d4279SBram Moolenaar 	return &no_position;
3139071d4279SBram Moolenaar }
3140071d4279SBram Moolenaar 
3141071d4279SBram Moolenaar /*
3142071d4279SBram Moolenaar  * Find the lnum for the buffer 'buf' for the current window.
3143071d4279SBram Moolenaar  */
3144071d4279SBram Moolenaar     linenr_T
buflist_findlnum(buf_T * buf)31457454a06eSBram Moolenaar buflist_findlnum(buf_T *buf)
3146071d4279SBram Moolenaar {
3147071d4279SBram Moolenaar     return buflist_findfpos(buf)->lnum;
3148071d4279SBram Moolenaar }
3149071d4279SBram Moolenaar 
3150071d4279SBram Moolenaar /*
315145e5fd13SBram Moolenaar  * List all known file names (for :files and :buffers command).
3152071d4279SBram Moolenaar  */
3153071d4279SBram Moolenaar     void
buflist_list(exarg_T * eap)31547454a06eSBram Moolenaar buflist_list(exarg_T *eap)
3155071d4279SBram Moolenaar {
315652410575SBram Moolenaar     buf_T	*buf = firstbuf;
3157071d4279SBram Moolenaar     int		len;
3158071d4279SBram Moolenaar     int		i;
3159304b64c9SBram Moolenaar     int		ro_char;
3160304b64c9SBram Moolenaar     int		changed_char;
31610751f51aSBram Moolenaar #ifdef FEAT_TERMINAL
31620751f51aSBram Moolenaar     int		job_running;
31630751f51aSBram Moolenaar     int		job_none_open;
31640751f51aSBram Moolenaar #endif
3165071d4279SBram Moolenaar 
316652410575SBram Moolenaar #ifdef FEAT_VIMINFO
316752410575SBram Moolenaar     garray_T	buflist;
316852410575SBram Moolenaar     buf_T	**buflist_data = NULL, **p;
316952410575SBram Moolenaar 
317052410575SBram Moolenaar     if (vim_strchr(eap->arg, 't'))
317152410575SBram Moolenaar     {
317252410575SBram Moolenaar 	ga_init2(&buflist, sizeof(buf_T *), 50);
3173aeea7215SBram Moolenaar 	FOR_ALL_BUFFERS(buf)
317452410575SBram Moolenaar 	{
317552410575SBram Moolenaar 	    if (ga_grow(&buflist, 1) == OK)
317652410575SBram Moolenaar 		((buf_T **)buflist.ga_data)[buflist.ga_len++] = buf;
317752410575SBram Moolenaar 	}
317852410575SBram Moolenaar 
317952410575SBram Moolenaar 	qsort(buflist.ga_data, (size_t)buflist.ga_len,
318052410575SBram Moolenaar 		sizeof(buf_T *), buf_compare);
318152410575SBram Moolenaar 
31823b991527SBram Moolenaar 	buflist_data = (buf_T **)buflist.ga_data;
31833b991527SBram Moolenaar 	buf = *buflist_data;
318452410575SBram Moolenaar     }
31853b991527SBram Moolenaar     p = buflist_data;
318652410575SBram Moolenaar 
31873b991527SBram Moolenaar     for (; buf != NULL && !got_int; buf = buflist_data != NULL
318852410575SBram Moolenaar 	    ? (++p < buflist_data + buflist.ga_len ? *p : NULL)
318952410575SBram Moolenaar 	    : buf->b_next)
319052410575SBram Moolenaar #else
3191071d4279SBram Moolenaar     for (buf = firstbuf; buf != NULL && !got_int; buf = buf->b_next)
319252410575SBram Moolenaar #endif
3193071d4279SBram Moolenaar     {
31940751f51aSBram Moolenaar #ifdef FEAT_TERMINAL
31950751f51aSBram Moolenaar 	job_running = term_job_running(buf->b_term);
31960751f51aSBram Moolenaar 	job_none_open = job_running && term_none_open(buf->b_term);
31970751f51aSBram Moolenaar #endif
3198c667da51SBram Moolenaar 	// skip unlisted buffers, unless ! was used
3199d51cb706SBram Moolenaar 	if ((!buf->b_p_bl && !eap->forceit && !vim_strchr(eap->arg, 'u'))
3200d51cb706SBram Moolenaar 		|| (vim_strchr(eap->arg, 'u') && buf->b_p_bl)
3201d51cb706SBram Moolenaar 		|| (vim_strchr(eap->arg, '+')
3202d51cb706SBram Moolenaar 			&& ((buf->b_flags & BF_READERR) || !bufIsChanged(buf)))
3203d51cb706SBram Moolenaar 		|| (vim_strchr(eap->arg, 'a')
3204d51cb706SBram Moolenaar 			&& (buf->b_ml.ml_mfp == NULL || buf->b_nwindows == 0))
3205d51cb706SBram Moolenaar 		|| (vim_strchr(eap->arg, 'h')
3206d51cb706SBram Moolenaar 			&& (buf->b_ml.ml_mfp == NULL || buf->b_nwindows != 0))
32070751f51aSBram Moolenaar #ifdef FEAT_TERMINAL
32080751f51aSBram Moolenaar 		|| (vim_strchr(eap->arg, 'R')
32090751f51aSBram Moolenaar 			&& (!job_running || (job_running && job_none_open)))
32100751f51aSBram Moolenaar 		|| (vim_strchr(eap->arg, '?')
32110751f51aSBram Moolenaar 			&& (!job_running || (job_running && !job_none_open)))
32120751f51aSBram Moolenaar 		|| (vim_strchr(eap->arg, 'F')
32130751f51aSBram Moolenaar 			&& (job_running || buf->b_term == NULL))
32140751f51aSBram Moolenaar #endif
3215d51cb706SBram Moolenaar 		|| (vim_strchr(eap->arg, '-') && buf->b_p_ma)
3216d51cb706SBram Moolenaar 		|| (vim_strchr(eap->arg, '=') && !buf->b_p_ro)
3217d51cb706SBram Moolenaar 		|| (vim_strchr(eap->arg, 'x') && !(buf->b_flags & BF_READERR))
3218d51cb706SBram Moolenaar 		|| (vim_strchr(eap->arg, '%') && buf != curbuf)
3219d51cb706SBram Moolenaar 		|| (vim_strchr(eap->arg, '#')
3220d51cb706SBram Moolenaar 		      && (buf == curbuf || curwin->w_alt_fnum != buf->b_fnum)))
3221071d4279SBram Moolenaar 	    continue;
3222071d4279SBram Moolenaar 	if (buf_spname(buf) != NULL)
3223e1704badSBram Moolenaar 	    vim_strncpy(NameBuff, buf_spname(buf), MAXPATHL - 1);
3224071d4279SBram Moolenaar 	else
3225071d4279SBram Moolenaar 	    home_replace(buf, buf->b_fname, NameBuff, MAXPATHL, TRUE);
322677401addSBram Moolenaar 	if (message_filtered(NameBuff))
322777401addSBram Moolenaar 	    continue;
3228071d4279SBram Moolenaar 
3229304b64c9SBram Moolenaar 	changed_char = (buf->b_flags & BF_READERR) ? 'x'
3230304b64c9SBram Moolenaar 					     : (bufIsChanged(buf) ? '+' : ' ');
3231304b64c9SBram Moolenaar #ifdef FEAT_TERMINAL
3232304b64c9SBram Moolenaar 	if (term_job_running(buf->b_term))
3233304b64c9SBram Moolenaar 	{
32344033c55eSBram Moolenaar 	    if (term_none_open(buf->b_term))
32354033c55eSBram Moolenaar 		ro_char = '?';
32364033c55eSBram Moolenaar 	    else
3237304b64c9SBram Moolenaar 		ro_char = 'R';
3238c667da51SBram Moolenaar 	    changed_char = ' ';  // bufIsChanged() returns TRUE to avoid
3239c667da51SBram Moolenaar 				 // closing, but it's not actually changed.
3240304b64c9SBram Moolenaar 	}
3241304b64c9SBram Moolenaar 	else if (buf->b_term != NULL)
3242304b64c9SBram Moolenaar 	    ro_char = 'F';
3243304b64c9SBram Moolenaar 	else
3244304b64c9SBram Moolenaar #endif
3245304b64c9SBram Moolenaar 	    ro_char = !buf->b_p_ma ? '-' : (buf->b_p_ro ? '=' : ' ');
3246304b64c9SBram Moolenaar 
324777401addSBram Moolenaar 	msg_putchar('\n');
324850cde827SBram Moolenaar 	len = vim_snprintf((char *)IObuff, IOSIZE - 20, "%3d%c%c%c%c%c \"%s\"",
3249071d4279SBram Moolenaar 		buf->b_fnum,
3250071d4279SBram Moolenaar 		buf->b_p_bl ? ' ' : 'u',
3251071d4279SBram Moolenaar 		buf == curbuf ? '%' :
3252071d4279SBram Moolenaar 			(curwin->w_alt_fnum == buf->b_fnum ? '#' : ' '),
3253071d4279SBram Moolenaar 		buf->b_ml.ml_mfp == NULL ? ' ' :
3254071d4279SBram Moolenaar 			(buf->b_nwindows == 0 ? 'h' : 'a'),
3255304b64c9SBram Moolenaar 		ro_char,
3256304b64c9SBram Moolenaar 		changed_char,
325751485f06SBram Moolenaar 		NameBuff);
3258507edf63SBram Moolenaar 	if (len > IOSIZE - 20)
3259507edf63SBram Moolenaar 	    len = IOSIZE - 20;
3260071d4279SBram Moolenaar 
3261c667da51SBram Moolenaar 	// put "line 999" in column 40 or after the file name
3262071d4279SBram Moolenaar 	i = 40 - vim_strsize(IObuff);
3263071d4279SBram Moolenaar 	do
3264071d4279SBram Moolenaar 	    IObuff[len++] = ' ';
3265abab0b0fSBram Moolenaar 	while (--i > 0 && len < IOSIZE - 18);
326652410575SBram Moolenaar #ifdef FEAT_VIMINFO
326752410575SBram Moolenaar 	if (vim_strchr(eap->arg, 't') && buf->b_last_used)
326852410575SBram Moolenaar 	    add_time(IObuff + len, (size_t)(IOSIZE - len), buf->b_last_used);
326952410575SBram Moolenaar 	else
327052410575SBram Moolenaar #endif
32710ab2a887SBram Moolenaar 	    vim_snprintf((char *)IObuff + len, (size_t)(IOSIZE - len),
32720ab2a887SBram Moolenaar 		    _("line %ld"), buf == curbuf ? curwin->w_cursor.lnum
32739c13b359SBram Moolenaar 					       : (long)buflist_findlnum(buf));
3274071d4279SBram Moolenaar 	msg_outtrans(IObuff);
3275c667da51SBram Moolenaar 	out_flush();	    // output one line at a time
3276071d4279SBram Moolenaar 	ui_breakcheck();
3277071d4279SBram Moolenaar     }
327852410575SBram Moolenaar 
327952410575SBram Moolenaar #ifdef FEAT_VIMINFO
328052410575SBram Moolenaar     if (buflist_data)
328152410575SBram Moolenaar 	ga_clear(&buflist);
328252410575SBram Moolenaar #endif
3283071d4279SBram Moolenaar }
3284071d4279SBram Moolenaar 
3285071d4279SBram Moolenaar /*
3286071d4279SBram Moolenaar  * Get file name and line number for file 'fnum'.
3287071d4279SBram Moolenaar  * Used by DoOneCmd() for translating '%' and '#'.
3288071d4279SBram Moolenaar  * Used by insert_reg() and cmdline_paste() for '#' register.
3289071d4279SBram Moolenaar  * Return FAIL if not found, OK for success.
3290071d4279SBram Moolenaar  */
3291071d4279SBram Moolenaar     int
buflist_name_nr(int fnum,char_u ** fname,linenr_T * lnum)32927454a06eSBram Moolenaar buflist_name_nr(
32937454a06eSBram Moolenaar     int		fnum,
32947454a06eSBram Moolenaar     char_u	**fname,
32957454a06eSBram Moolenaar     linenr_T	*lnum)
3296071d4279SBram Moolenaar {
3297071d4279SBram Moolenaar     buf_T	*buf;
3298071d4279SBram Moolenaar 
3299071d4279SBram Moolenaar     buf = buflist_findnr(fnum);
3300071d4279SBram Moolenaar     if (buf == NULL || buf->b_fname == NULL)
3301071d4279SBram Moolenaar 	return FAIL;
3302071d4279SBram Moolenaar 
3303071d4279SBram Moolenaar     *fname = buf->b_fname;
3304071d4279SBram Moolenaar     *lnum = buflist_findlnum(buf);
3305071d4279SBram Moolenaar 
3306071d4279SBram Moolenaar     return OK;
3307071d4279SBram Moolenaar }
3308071d4279SBram Moolenaar 
3309071d4279SBram Moolenaar /*
33103d6014f0SBram Moolenaar  * Set the file name for "buf"' to "ffname_arg", short file name to
33113d6014f0SBram Moolenaar  * "sfname_arg".
3312071d4279SBram Moolenaar  * The file name with the full path is also remembered, for when :cd is used.
3313071d4279SBram Moolenaar  * Returns FAIL for failure (file name already in use by other buffer)
3314071d4279SBram Moolenaar  *	OK otherwise.
3315071d4279SBram Moolenaar  */
3316071d4279SBram Moolenaar     int
setfname(buf_T * buf,char_u * ffname_arg,char_u * sfname_arg,int message)33177454a06eSBram Moolenaar setfname(
33187454a06eSBram Moolenaar     buf_T	*buf,
33193d6014f0SBram Moolenaar     char_u	*ffname_arg,
33203d6014f0SBram Moolenaar     char_u	*sfname_arg,
3321c667da51SBram Moolenaar     int		message)	// give message when buffer already exists
3322071d4279SBram Moolenaar {
33233d6014f0SBram Moolenaar     char_u	*ffname = ffname_arg;
33243d6014f0SBram Moolenaar     char_u	*sfname = sfname_arg;
332581695250SBram Moolenaar     buf_T	*obuf = NULL;
3326071d4279SBram Moolenaar #ifdef UNIX
33278767f52fSBram Moolenaar     stat_T	st;
3328071d4279SBram Moolenaar #endif
3329071d4279SBram Moolenaar 
3330071d4279SBram Moolenaar     if (ffname == NULL || *ffname == NUL)
3331071d4279SBram Moolenaar     {
3332c667da51SBram Moolenaar 	// Removing the name.
33333d6014f0SBram Moolenaar 	if (buf->b_sfname != buf->b_ffname)
3334d23a8236SBram Moolenaar 	    VIM_CLEAR(buf->b_sfname);
33353d6014f0SBram Moolenaar 	else
33363d6014f0SBram Moolenaar 	    buf->b_sfname = NULL;
33373d6014f0SBram Moolenaar 	VIM_CLEAR(buf->b_ffname);
3338071d4279SBram Moolenaar #ifdef UNIX
3339071d4279SBram Moolenaar 	st.st_dev = (dev_T)-1;
3340071d4279SBram Moolenaar #endif
3341071d4279SBram Moolenaar     }
3342071d4279SBram Moolenaar     else
3343071d4279SBram Moolenaar     {
3344c667da51SBram Moolenaar 	fname_expand(buf, &ffname, &sfname); // will allocate ffname
3345c667da51SBram Moolenaar 	if (ffname == NULL)		    // out of memory
3346071d4279SBram Moolenaar 	    return FAIL;
3347071d4279SBram Moolenaar 
3348071d4279SBram Moolenaar 	/*
3349c5935a85SBram Moolenaar 	 * If the file name is already used in another buffer:
3350071d4279SBram Moolenaar 	 * - if the buffer is loaded, fail
3351071d4279SBram Moolenaar 	 * - if the buffer is not loaded, delete it from the list
3352071d4279SBram Moolenaar 	 */
3353071d4279SBram Moolenaar #ifdef UNIX
3354071d4279SBram Moolenaar 	if (mch_stat((char *)ffname, &st) < 0)
3355071d4279SBram Moolenaar 	    st.st_dev = (dev_T)-1;
335681695250SBram Moolenaar #endif
335781695250SBram Moolenaar 	if (!(buf->b_flags & BF_DUMMY))
335881695250SBram Moolenaar #ifdef UNIX
3359071d4279SBram Moolenaar 	    obuf = buflist_findname_stat(ffname, &st);
3360071d4279SBram Moolenaar #else
3361071d4279SBram Moolenaar 	    obuf = buflist_findname(ffname);
3362071d4279SBram Moolenaar #endif
3363071d4279SBram Moolenaar 	if (obuf != NULL && obuf != buf)
3364071d4279SBram Moolenaar 	{
3365d3710cf0SBram Moolenaar 	    win_T	*win;
3366d3710cf0SBram Moolenaar 	    tabpage_T   *tab;
3367d3710cf0SBram Moolenaar 	    int		in_use = FALSE;
3368d3710cf0SBram Moolenaar 
3369d3710cf0SBram Moolenaar 	    // during startup a window may use a buffer that is not loaded yet
3370d3710cf0SBram Moolenaar 	    FOR_ALL_TAB_WINDOWS(tab, win)
3371d3710cf0SBram Moolenaar 		if (win->w_buffer == obuf)
3372d3710cf0SBram Moolenaar 		    in_use = TRUE;
3373d3710cf0SBram Moolenaar 
3374d3710cf0SBram Moolenaar 	    // it's loaded or used in a window, fail
3375d3710cf0SBram Moolenaar 	    if (obuf->b_ml.ml_mfp != NULL || in_use)
3376071d4279SBram Moolenaar 	    {
3377071d4279SBram Moolenaar 		if (message)
3378f9e3e09fSBram Moolenaar 		    emsg(_("E95: Buffer with this name already exists"));
3379071d4279SBram Moolenaar 		vim_free(ffname);
3380071d4279SBram Moolenaar 		return FAIL;
3381071d4279SBram Moolenaar 	    }
3382c667da51SBram Moolenaar 	    // delete from the list
3383a6e8f888SBram Moolenaar 	    close_buffer(NULL, obuf, DOBUF_WIPE, FALSE, FALSE);
3384071d4279SBram Moolenaar 	}
3385071d4279SBram Moolenaar 	sfname = vim_strsave(sfname);
3386071d4279SBram Moolenaar 	if (ffname == NULL || sfname == NULL)
3387071d4279SBram Moolenaar 	{
3388071d4279SBram Moolenaar 	    vim_free(sfname);
3389071d4279SBram Moolenaar 	    vim_free(ffname);
3390071d4279SBram Moolenaar 	    return FAIL;
3391071d4279SBram Moolenaar 	}
3392071d4279SBram Moolenaar #ifdef USE_FNAME_CASE
3393c667da51SBram Moolenaar 	fname_case(sfname, 0);    // set correct case for short file name
3394071d4279SBram Moolenaar #endif
33953d6014f0SBram Moolenaar 	if (buf->b_sfname != buf->b_ffname)
3396071d4279SBram Moolenaar 	    vim_free(buf->b_sfname);
33973d6014f0SBram Moolenaar 	vim_free(buf->b_ffname);
3398071d4279SBram Moolenaar 	buf->b_ffname = ffname;
3399071d4279SBram Moolenaar 	buf->b_sfname = sfname;
3400071d4279SBram Moolenaar     }
3401071d4279SBram Moolenaar     buf->b_fname = buf->b_sfname;
3402071d4279SBram Moolenaar #ifdef UNIX
3403071d4279SBram Moolenaar     if (st.st_dev == (dev_T)-1)
3404f1726cc8SBram Moolenaar 	buf->b_dev_valid = FALSE;
3405071d4279SBram Moolenaar     else
3406071d4279SBram Moolenaar     {
3407f1726cc8SBram Moolenaar 	buf->b_dev_valid = TRUE;
3408071d4279SBram Moolenaar 	buf->b_dev = st.st_dev;
3409071d4279SBram Moolenaar 	buf->b_ino = st.st_ino;
3410071d4279SBram Moolenaar     }
3411071d4279SBram Moolenaar #endif
3412071d4279SBram Moolenaar 
3413071d4279SBram Moolenaar     buf->b_shortname = FALSE;
3414071d4279SBram Moolenaar 
3415071d4279SBram Moolenaar     buf_name_changed(buf);
3416071d4279SBram Moolenaar     return OK;
3417071d4279SBram Moolenaar }
3418071d4279SBram Moolenaar 
3419071d4279SBram Moolenaar /*
342086b68359SBram Moolenaar  * Crude way of changing the name of a buffer.  Use with care!
342186b68359SBram Moolenaar  * The name should be relative to the current directory.
342286b68359SBram Moolenaar  */
342386b68359SBram Moolenaar     void
buf_set_name(int fnum,char_u * name)34247454a06eSBram Moolenaar buf_set_name(int fnum, char_u *name)
342586b68359SBram Moolenaar {
342686b68359SBram Moolenaar     buf_T	*buf;
342786b68359SBram Moolenaar 
342886b68359SBram Moolenaar     buf = buflist_findnr(fnum);
342986b68359SBram Moolenaar     if (buf != NULL)
343086b68359SBram Moolenaar     {
34313d6014f0SBram Moolenaar 	if (buf->b_sfname != buf->b_ffname)
343286b68359SBram Moolenaar 	    vim_free(buf->b_sfname);
343386b68359SBram Moolenaar 	vim_free(buf->b_ffname);
3434f193fffdSBram Moolenaar 	buf->b_ffname = vim_strsave(name);
3435f193fffdSBram Moolenaar 	buf->b_sfname = NULL;
3436c667da51SBram Moolenaar 	// Allocate ffname and expand into full path.  Also resolves .lnk
3437c667da51SBram Moolenaar 	// files on Win32.
3438f193fffdSBram Moolenaar 	fname_expand(buf, &buf->b_ffname, &buf->b_sfname);
343986b68359SBram Moolenaar 	buf->b_fname = buf->b_sfname;
344086b68359SBram Moolenaar     }
344186b68359SBram Moolenaar }
344286b68359SBram Moolenaar 
344386b68359SBram Moolenaar /*
3444071d4279SBram Moolenaar  * Take care of what needs to be done when the name of buffer "buf" has
3445071d4279SBram Moolenaar  * changed.
3446071d4279SBram Moolenaar  */
3447071d4279SBram Moolenaar     void
buf_name_changed(buf_T * buf)34487454a06eSBram Moolenaar buf_name_changed(buf_T *buf)
3449071d4279SBram Moolenaar {
3450071d4279SBram Moolenaar     /*
3451071d4279SBram Moolenaar      * If the file name changed, also change the name of the swapfile
3452071d4279SBram Moolenaar      */
3453071d4279SBram Moolenaar     if (buf->b_ml.ml_mfp != NULL)
3454071d4279SBram Moolenaar 	ml_setname(buf);
3455071d4279SBram Moolenaar 
3456*3ad69532SBram Moolenaar #ifdef FEAT_TERMINAL
3457*3ad69532SBram Moolenaar     if (buf->b_term != NULL)
3458*3ad69532SBram Moolenaar 	term_clear_status_text(buf->b_term);
3459*3ad69532SBram Moolenaar #endif
3460*3ad69532SBram Moolenaar 
3461071d4279SBram Moolenaar     if (curwin->w_buffer == buf)
3462c667da51SBram Moolenaar 	check_arg_idx(curwin);	// check file name for arg list
3463071d4279SBram Moolenaar #ifdef FEAT_TITLE
3464c667da51SBram Moolenaar     maketitle();		// set window title
3465071d4279SBram Moolenaar #endif
3466c667da51SBram Moolenaar     status_redraw_all();	// status lines need to be redrawn
3467c667da51SBram Moolenaar     fmarks_check_names(buf);	// check named file marks
3468c667da51SBram Moolenaar     ml_timestamp(buf);		// reset timestamp
3469071d4279SBram Moolenaar }
3470071d4279SBram Moolenaar 
3471071d4279SBram Moolenaar /*
3472071d4279SBram Moolenaar  * set alternate file name for current window
3473071d4279SBram Moolenaar  *
3474071d4279SBram Moolenaar  * Used by do_one_cmd(), do_write() and do_ecmd().
3475071d4279SBram Moolenaar  * Return the buffer.
3476071d4279SBram Moolenaar  */
3477071d4279SBram Moolenaar     buf_T *
setaltfname(char_u * ffname,char_u * sfname,linenr_T lnum)34787454a06eSBram Moolenaar setaltfname(
34797454a06eSBram Moolenaar     char_u	*ffname,
34807454a06eSBram Moolenaar     char_u	*sfname,
34817454a06eSBram Moolenaar     linenr_T	lnum)
3482071d4279SBram Moolenaar {
3483071d4279SBram Moolenaar     buf_T	*buf;
3484071d4279SBram Moolenaar 
3485c667da51SBram Moolenaar     // Create a buffer.  'buflisted' is not set if it's a new buffer
3486071d4279SBram Moolenaar     buf = buflist_new(ffname, sfname, lnum, 0);
3487e1004401SBram Moolenaar     if (buf != NULL && (cmdmod.cmod_flags & CMOD_KEEPALT) == 0)
3488071d4279SBram Moolenaar 	curwin->w_alt_fnum = buf->b_fnum;
3489071d4279SBram Moolenaar     return buf;
3490071d4279SBram Moolenaar }
3491071d4279SBram Moolenaar 
3492071d4279SBram Moolenaar /*
3493071d4279SBram Moolenaar  * Get alternate file name for current window.
3494071d4279SBram Moolenaar  * Return NULL if there isn't any, and give error message if requested.
3495071d4279SBram Moolenaar  */
3496071d4279SBram Moolenaar     char_u  *
getaltfname(int errmsg)34977454a06eSBram Moolenaar getaltfname(
3498c667da51SBram Moolenaar     int		errmsg)		// give error message
3499071d4279SBram Moolenaar {
3500071d4279SBram Moolenaar     char_u	*fname;
3501071d4279SBram Moolenaar     linenr_T	dummy;
3502071d4279SBram Moolenaar 
3503071d4279SBram Moolenaar     if (buflist_name_nr(0, &fname, &dummy) == FAIL)
3504071d4279SBram Moolenaar     {
3505071d4279SBram Moolenaar 	if (errmsg)
3506108010aaSBram Moolenaar 	    emsg(_(e_no_alternate_file));
3507071d4279SBram Moolenaar 	return NULL;
3508071d4279SBram Moolenaar     }
3509071d4279SBram Moolenaar     return fname;
3510071d4279SBram Moolenaar }
3511071d4279SBram Moolenaar 
3512071d4279SBram Moolenaar /*
3513071d4279SBram Moolenaar  * Add a file name to the buflist and return its number.
3514071d4279SBram Moolenaar  * Uses same flags as buflist_new(), except BLN_DUMMY.
3515071d4279SBram Moolenaar  *
3516071d4279SBram Moolenaar  * used by qf_init(), main() and doarglist()
3517071d4279SBram Moolenaar  */
3518071d4279SBram Moolenaar     int
buflist_add(char_u * fname,int flags)35197454a06eSBram Moolenaar buflist_add(char_u *fname, int flags)
3520071d4279SBram Moolenaar {
3521071d4279SBram Moolenaar     buf_T	*buf;
3522071d4279SBram Moolenaar 
3523071d4279SBram Moolenaar     buf = buflist_new(fname, NULL, (linenr_T)0, flags);
3524071d4279SBram Moolenaar     if (buf != NULL)
3525071d4279SBram Moolenaar 	return buf->b_fnum;
3526071d4279SBram Moolenaar     return 0;
3527071d4279SBram Moolenaar }
3528071d4279SBram Moolenaar 
3529071d4279SBram Moolenaar #if defined(BACKSLASH_IN_FILENAME) || defined(PROTO)
3530071d4279SBram Moolenaar /*
3531071d4279SBram Moolenaar  * Adjust slashes in file names.  Called after 'shellslash' was set.
3532071d4279SBram Moolenaar  */
3533071d4279SBram Moolenaar     void
buflist_slash_adjust(void)35347454a06eSBram Moolenaar buflist_slash_adjust(void)
3535071d4279SBram Moolenaar {
3536071d4279SBram Moolenaar     buf_T	*bp;
3537071d4279SBram Moolenaar 
353829323590SBram Moolenaar     FOR_ALL_BUFFERS(bp)
3539071d4279SBram Moolenaar     {
3540071d4279SBram Moolenaar 	if (bp->b_ffname != NULL)
3541071d4279SBram Moolenaar 	    slash_adjust(bp->b_ffname);
3542071d4279SBram Moolenaar 	if (bp->b_sfname != NULL)
3543071d4279SBram Moolenaar 	    slash_adjust(bp->b_sfname);
3544071d4279SBram Moolenaar     }
3545071d4279SBram Moolenaar }
3546071d4279SBram Moolenaar #endif
3547071d4279SBram Moolenaar 
3548071d4279SBram Moolenaar /*
3549701f7afcSBram Moolenaar  * Set alternate cursor position for the current buffer and window "win".
3550071d4279SBram Moolenaar  * Also save the local window option values.
3551071d4279SBram Moolenaar  */
3552071d4279SBram Moolenaar     void
buflist_altfpos(win_T * win)35537454a06eSBram Moolenaar buflist_altfpos(win_T *win)
3554071d4279SBram Moolenaar {
3555701f7afcSBram Moolenaar     buflist_setfpos(curbuf, win, win->w_cursor.lnum, win->w_cursor.col, TRUE);
3556071d4279SBram Moolenaar }
3557071d4279SBram Moolenaar 
3558071d4279SBram Moolenaar /*
3559071d4279SBram Moolenaar  * Return TRUE if 'ffname' is not the same file as current file.
3560071d4279SBram Moolenaar  * Fname must have a full path (expanded by mch_FullName()).
3561071d4279SBram Moolenaar  */
3562071d4279SBram Moolenaar     int
otherfile(char_u * ffname)35637454a06eSBram Moolenaar otherfile(char_u *ffname)
3564071d4279SBram Moolenaar {
3565071d4279SBram Moolenaar     return otherfile_buf(curbuf, ffname
3566071d4279SBram Moolenaar #ifdef UNIX
3567071d4279SBram Moolenaar 	    , NULL
3568071d4279SBram Moolenaar #endif
3569071d4279SBram Moolenaar 	    );
3570071d4279SBram Moolenaar }
3571071d4279SBram Moolenaar 
3572071d4279SBram Moolenaar     static int
otherfile_buf(buf_T * buf,char_u * ffname,stat_T * stp)35737454a06eSBram Moolenaar otherfile_buf(
35747454a06eSBram Moolenaar     buf_T		*buf,
35757454a06eSBram Moolenaar     char_u		*ffname
3576071d4279SBram Moolenaar #ifdef UNIX
35778767f52fSBram Moolenaar     , stat_T		*stp
3578071d4279SBram Moolenaar #endif
3579071d4279SBram Moolenaar     )
3580071d4279SBram Moolenaar {
3581c667da51SBram Moolenaar     // no name is different
3582071d4279SBram Moolenaar     if (ffname == NULL || *ffname == NUL || buf->b_ffname == NULL)
3583071d4279SBram Moolenaar 	return TRUE;
3584071d4279SBram Moolenaar     if (fnamecmp(ffname, buf->b_ffname) == 0)
3585071d4279SBram Moolenaar 	return FALSE;
3586071d4279SBram Moolenaar #ifdef UNIX
3587071d4279SBram Moolenaar     {
35888767f52fSBram Moolenaar 	stat_T	    st;
3589071d4279SBram Moolenaar 
3590c667da51SBram Moolenaar 	// If no stat_T given, get it now
3591071d4279SBram Moolenaar 	if (stp == NULL)
3592071d4279SBram Moolenaar 	{
3593f1726cc8SBram Moolenaar 	    if (!buf->b_dev_valid || mch_stat((char *)ffname, &st) < 0)
3594071d4279SBram Moolenaar 		st.st_dev = (dev_T)-1;
3595071d4279SBram Moolenaar 	    stp = &st;
3596071d4279SBram Moolenaar 	}
3597c667da51SBram Moolenaar 	// Use dev/ino to check if the files are the same, even when the names
3598c667da51SBram Moolenaar 	// are different (possible with links).  Still need to compare the
3599c667da51SBram Moolenaar 	// name above, for when the file doesn't exist yet.
3600c667da51SBram Moolenaar 	// Problem: The dev/ino changes when a file is deleted (and created
3601c667da51SBram Moolenaar 	// again) and remains the same when renamed/moved.  We don't want to
3602c667da51SBram Moolenaar 	// mch_stat() each buffer each time, that would be too slow.  Get the
3603c667da51SBram Moolenaar 	// dev/ino again when they appear to match, but not when they appear
3604c667da51SBram Moolenaar 	// to be different: Could skip a buffer when it's actually the same
3605c667da51SBram Moolenaar 	// file.
3606071d4279SBram Moolenaar 	if (buf_same_ino(buf, stp))
3607071d4279SBram Moolenaar 	{
3608071d4279SBram Moolenaar 	    buf_setino(buf);
3609071d4279SBram Moolenaar 	    if (buf_same_ino(buf, stp))
3610071d4279SBram Moolenaar 		return FALSE;
3611071d4279SBram Moolenaar 	}
3612071d4279SBram Moolenaar     }
3613071d4279SBram Moolenaar #endif
3614071d4279SBram Moolenaar     return TRUE;
3615071d4279SBram Moolenaar }
3616071d4279SBram Moolenaar 
3617071d4279SBram Moolenaar #if defined(UNIX) || defined(PROTO)
3618071d4279SBram Moolenaar /*
3619071d4279SBram Moolenaar  * Set inode and device number for a buffer.
3620071d4279SBram Moolenaar  * Must always be called when b_fname is changed!.
3621071d4279SBram Moolenaar  */
3622071d4279SBram Moolenaar     void
buf_setino(buf_T * buf)36237454a06eSBram Moolenaar buf_setino(buf_T *buf)
3624071d4279SBram Moolenaar {
36258767f52fSBram Moolenaar     stat_T	st;
3626071d4279SBram Moolenaar 
3627071d4279SBram Moolenaar     if (buf->b_fname != NULL && mch_stat((char *)buf->b_fname, &st) >= 0)
3628071d4279SBram Moolenaar     {
3629f1726cc8SBram Moolenaar 	buf->b_dev_valid = TRUE;
3630071d4279SBram Moolenaar 	buf->b_dev = st.st_dev;
3631071d4279SBram Moolenaar 	buf->b_ino = st.st_ino;
3632071d4279SBram Moolenaar     }
3633071d4279SBram Moolenaar     else
3634f1726cc8SBram Moolenaar 	buf->b_dev_valid = FALSE;
3635071d4279SBram Moolenaar }
3636071d4279SBram Moolenaar 
3637071d4279SBram Moolenaar /*
3638071d4279SBram Moolenaar  * Return TRUE if dev/ino in buffer "buf" matches with "stp".
3639071d4279SBram Moolenaar  */
3640071d4279SBram Moolenaar     static int
buf_same_ino(buf_T * buf,stat_T * stp)36417454a06eSBram Moolenaar buf_same_ino(
36427454a06eSBram Moolenaar     buf_T	*buf,
36438767f52fSBram Moolenaar     stat_T	*stp)
3644071d4279SBram Moolenaar {
3645f1726cc8SBram Moolenaar     return (buf->b_dev_valid
3646071d4279SBram Moolenaar 	    && stp->st_dev == buf->b_dev
3647071d4279SBram Moolenaar 	    && stp->st_ino == buf->b_ino);
3648071d4279SBram Moolenaar }
3649071d4279SBram Moolenaar #endif
3650071d4279SBram Moolenaar 
36511d2ba7faSBram Moolenaar /*
36521d2ba7faSBram Moolenaar  * Print info about the current buffer.
36531d2ba7faSBram Moolenaar  */
3654071d4279SBram Moolenaar     void
fileinfo(int fullname,int shorthelp,int dont_truncate)36557454a06eSBram Moolenaar fileinfo(
3656c667da51SBram Moolenaar     int fullname,	    // when non-zero print full path
36577454a06eSBram Moolenaar     int shorthelp,
36587454a06eSBram Moolenaar     int	dont_truncate)
3659071d4279SBram Moolenaar {
3660071d4279SBram Moolenaar     char_u	*name;
3661071d4279SBram Moolenaar     int		n;
366232526b3cSBram Moolenaar     char	*p;
366332526b3cSBram Moolenaar     char	*buffer;
36649c13b359SBram Moolenaar     size_t	len;
3665071d4279SBram Moolenaar 
3666c799fe20SBram Moolenaar     buffer = alloc(IOSIZE);
3667071d4279SBram Moolenaar     if (buffer == NULL)
3668071d4279SBram Moolenaar 	return;
3669071d4279SBram Moolenaar 
3670c667da51SBram Moolenaar     if (fullname > 1)	    // 2 CTRL-G: include buffer number
3671071d4279SBram Moolenaar     {
367232526b3cSBram Moolenaar 	vim_snprintf(buffer, IOSIZE, "buf %d: ", curbuf->b_fnum);
3673071d4279SBram Moolenaar 	p = buffer + STRLEN(buffer);
3674071d4279SBram Moolenaar     }
3675071d4279SBram Moolenaar     else
3676071d4279SBram Moolenaar 	p = buffer;
3677071d4279SBram Moolenaar 
3678071d4279SBram Moolenaar     *p++ = '"';
3679071d4279SBram Moolenaar     if (buf_spname(curbuf) != NULL)
368032526b3cSBram Moolenaar 	vim_strncpy((char_u *)p, buf_spname(curbuf), IOSIZE - (p - buffer) - 1);
3681071d4279SBram Moolenaar     else
3682071d4279SBram Moolenaar     {
3683071d4279SBram Moolenaar 	if (!fullname && curbuf->b_fname != NULL)
3684071d4279SBram Moolenaar 	    name = curbuf->b_fname;
3685071d4279SBram Moolenaar 	else
3686071d4279SBram Moolenaar 	    name = curbuf->b_ffname;
368732526b3cSBram Moolenaar 	home_replace(shorthelp ? curbuf : NULL, name, (char_u *)p,
3688071d4279SBram Moolenaar 					  (int)(IOSIZE - (p - buffer)), TRUE);
3689071d4279SBram Moolenaar     }
3690071d4279SBram Moolenaar 
369132526b3cSBram Moolenaar     vim_snprintf_add(buffer, IOSIZE, "\"%s%s%s%s%s%s",
3692071d4279SBram Moolenaar 	    curbufIsChanged() ? (shortmess(SHM_MOD)
3693071d4279SBram Moolenaar 					  ?  " [+]" : _(" [Modified]")) : " ",
3694071d4279SBram Moolenaar 	    (curbuf->b_flags & BF_NOTEDITED)
3695071d4279SBram Moolenaar #ifdef FEAT_QUICKFIX
3696071d4279SBram Moolenaar 		    && !bt_dontwrite(curbuf)
3697071d4279SBram Moolenaar #endif
3698071d4279SBram Moolenaar 					? _("[Not edited]") : "",
3699071d4279SBram Moolenaar 	    (curbuf->b_flags & BF_NEW)
3700071d4279SBram Moolenaar #ifdef FEAT_QUICKFIX
3701071d4279SBram Moolenaar 		    && !bt_dontwrite(curbuf)
3702071d4279SBram Moolenaar #endif
3703722e505dSBram Moolenaar 					   ? new_file_message() : "",
3704071d4279SBram Moolenaar 	    (curbuf->b_flags & BF_READERR) ? _("[Read errors]") : "",
370523584033SBram Moolenaar 	    curbuf->b_p_ro ? (shortmess(SHM_RO) ? _("[RO]")
3706071d4279SBram Moolenaar 						      : _("[readonly]")) : "",
3707071d4279SBram Moolenaar 	    (curbufIsChanged() || (curbuf->b_flags & BF_WRITE_MASK)
3708071d4279SBram Moolenaar 							  || curbuf->b_p_ro) ?
3709071d4279SBram Moolenaar 								    " " : "");
3710c667da51SBram Moolenaar     // With 32 bit longs and more than 21,474,836 lines multiplying by 100
3711c667da51SBram Moolenaar     // causes an overflow, thus for large numbers divide instead.
3712071d4279SBram Moolenaar     if (curwin->w_cursor.lnum > 1000000L)
3713071d4279SBram Moolenaar 	n = (int)(((long)curwin->w_cursor.lnum) /
3714071d4279SBram Moolenaar 				   ((long)curbuf->b_ml.ml_line_count / 100L));
3715071d4279SBram Moolenaar     else
3716071d4279SBram Moolenaar 	n = (int)(((long)curwin->w_cursor.lnum * 100L) /
3717071d4279SBram Moolenaar 					    (long)curbuf->b_ml.ml_line_count);
3718071d4279SBram Moolenaar     if (curbuf->b_ml.ml_flags & ML_EMPTY)
371932526b3cSBram Moolenaar 	vim_snprintf_add(buffer, IOSIZE, "%s", _(no_lines_msg));
3720071d4279SBram Moolenaar #ifdef FEAT_CMDL_INFO
3721071d4279SBram Moolenaar     else if (p_ru)
3722c667da51SBram Moolenaar 	// Current line and column are already on the screen -- webb
372332526b3cSBram Moolenaar 	vim_snprintf_add(buffer, IOSIZE,
3724da6e8919SBram Moolenaar 		NGETTEXT("%ld line --%d%%--", "%ld lines --%d%%--",
3725da6e8919SBram Moolenaar 						   curbuf->b_ml.ml_line_count),
3726071d4279SBram Moolenaar 		(long)curbuf->b_ml.ml_line_count, n);
3727071d4279SBram Moolenaar #endif
3728071d4279SBram Moolenaar     else
3729071d4279SBram Moolenaar     {
373032526b3cSBram Moolenaar 	vim_snprintf_add(buffer, IOSIZE,
3731071d4279SBram Moolenaar 		_("line %ld of %ld --%d%%-- col "),
3732071d4279SBram Moolenaar 		(long)curwin->w_cursor.lnum,
3733071d4279SBram Moolenaar 		(long)curbuf->b_ml.ml_line_count,
3734071d4279SBram Moolenaar 		n);
3735071d4279SBram Moolenaar 	validate_virtcol();
37360ab2a887SBram Moolenaar 	len = STRLEN(buffer);
373732526b3cSBram Moolenaar 	col_print((char_u *)buffer + len, IOSIZE - len,
3738071d4279SBram Moolenaar 		   (int)curwin->w_cursor.col + 1, (int)curwin->w_virtcol + 1);
3739071d4279SBram Moolenaar     }
3740071d4279SBram Moolenaar 
374132526b3cSBram Moolenaar     (void)append_arg_number(curwin, (char_u *)buffer, IOSIZE,
374232526b3cSBram Moolenaar 							 !shortmess(SHM_FILE));
3743071d4279SBram Moolenaar 
3744071d4279SBram Moolenaar     if (dont_truncate)
3745071d4279SBram Moolenaar     {
3746c667da51SBram Moolenaar 	// Temporarily set msg_scroll to avoid the message being truncated.
3747c667da51SBram Moolenaar 	// First call msg_start() to get the message in the right place.
3748071d4279SBram Moolenaar 	msg_start();
3749071d4279SBram Moolenaar 	n = msg_scroll;
3750071d4279SBram Moolenaar 	msg_scroll = TRUE;
3751071d4279SBram Moolenaar 	msg(buffer);
3752071d4279SBram Moolenaar 	msg_scroll = n;
3753071d4279SBram Moolenaar     }
3754071d4279SBram Moolenaar     else
3755071d4279SBram Moolenaar     {
375632526b3cSBram Moolenaar 	p = (char *)msg_trunc_attr(buffer, FALSE, 0);
3757071d4279SBram Moolenaar 	if (restart_edit != 0 || (msg_scrolled && !need_wait_return))
3758c667da51SBram Moolenaar 	    // Need to repeat the message after redrawing when:
3759c667da51SBram Moolenaar 	    // - When restart_edit is set (otherwise there will be a delay
3760c667da51SBram Moolenaar 	    //   before redrawing).
3761c667da51SBram Moolenaar 	    // - When the screen was scrolled but there is no wait-return
3762c667da51SBram Moolenaar 	    //   prompt.
376332526b3cSBram Moolenaar 	    set_keep_msg((char_u *)p, 0);
3764071d4279SBram Moolenaar     }
3765071d4279SBram Moolenaar 
3766071d4279SBram Moolenaar     vim_free(buffer);
3767071d4279SBram Moolenaar }
3768071d4279SBram Moolenaar 
3769071d4279SBram Moolenaar     void
col_print(char_u * buf,size_t buflen,int col,int vcol)37707454a06eSBram Moolenaar col_print(
37717454a06eSBram Moolenaar     char_u  *buf,
37727454a06eSBram Moolenaar     size_t  buflen,
37737454a06eSBram Moolenaar     int	    col,
37747454a06eSBram Moolenaar     int	    vcol)
3775071d4279SBram Moolenaar {
3776071d4279SBram Moolenaar     if (col == vcol)
37770ab2a887SBram Moolenaar 	vim_snprintf((char *)buf, buflen, "%d", col);
3778071d4279SBram Moolenaar     else
37790ab2a887SBram Moolenaar 	vim_snprintf((char *)buf, buflen, "%d-%d", col, vcol);
3780071d4279SBram Moolenaar }
3781071d4279SBram Moolenaar 
3782071d4279SBram Moolenaar #if defined(FEAT_TITLE) || defined(PROTO)
3783071d4279SBram Moolenaar static char_u *lasttitle = NULL;
3784071d4279SBram Moolenaar static char_u *lasticon = NULL;
3785071d4279SBram Moolenaar 
378684a93085SBram Moolenaar /*
378784a93085SBram Moolenaar  * Put the file name in the title bar and icon of the window.
378884a93085SBram Moolenaar  */
3789071d4279SBram Moolenaar     void
maketitle(void)37907454a06eSBram Moolenaar maketitle(void)
3791071d4279SBram Moolenaar {
3792071d4279SBram Moolenaar     char_u	*p;
379384a93085SBram Moolenaar     char_u	*title_str = NULL;
379484a93085SBram Moolenaar     char_u	*icon_str = NULL;
3795071d4279SBram Moolenaar     int		maxlen = 0;
3796071d4279SBram Moolenaar     int		len;
3797071d4279SBram Moolenaar     int		mustset;
3798071d4279SBram Moolenaar     char_u	buf[IOSIZE];
3799071d4279SBram Moolenaar     int		off;
3800071d4279SBram Moolenaar 
3801071d4279SBram Moolenaar     if (!redrawing())
3802071d4279SBram Moolenaar     {
3803c667da51SBram Moolenaar 	// Postpone updating the title when 'lazyredraw' is set.
3804071d4279SBram Moolenaar 	need_maketitle = TRUE;
3805071d4279SBram Moolenaar 	return;
3806071d4279SBram Moolenaar     }
3807071d4279SBram Moolenaar 
3808071d4279SBram Moolenaar     need_maketitle = FALSE;
3809bed7becaSBram Moolenaar     if (!p_title && !p_icon && lasttitle == NULL && lasticon == NULL)
381084a93085SBram Moolenaar 	return;  // nothing to do
3811071d4279SBram Moolenaar 
3812071d4279SBram Moolenaar     if (p_title)
3813071d4279SBram Moolenaar     {
3814071d4279SBram Moolenaar 	if (p_titlelen > 0)
3815071d4279SBram Moolenaar 	{
3816071d4279SBram Moolenaar 	    maxlen = p_titlelen * Columns / 100;
3817071d4279SBram Moolenaar 	    if (maxlen < 10)
3818071d4279SBram Moolenaar 		maxlen = 10;
3819071d4279SBram Moolenaar 	}
3820071d4279SBram Moolenaar 
382184a93085SBram Moolenaar 	title_str = buf;
3822071d4279SBram Moolenaar 	if (*p_titlestring != NUL)
3823071d4279SBram Moolenaar 	{
3824071d4279SBram Moolenaar #ifdef FEAT_STL_OPT
3825d3667a2eSBram Moolenaar 	    if (stl_syntax & STL_IN_TITLE)
3826d3667a2eSBram Moolenaar 	    {
38272a0449d1SBram Moolenaar 		int	use_sandbox = FALSE;
382853989554SBram Moolenaar 		int	called_emsg_before = called_emsg;
38292a0449d1SBram Moolenaar 
38302a0449d1SBram Moolenaar # ifdef FEAT_EVAL
3831d1f56e68SBram Moolenaar 		use_sandbox = was_set_insecurely((char_u *)"titlestring", 0);
38322a0449d1SBram Moolenaar # endif
383384a93085SBram Moolenaar 		build_stl_str_hl(curwin, title_str, sizeof(buf),
38342a0449d1SBram Moolenaar 					      p_titlestring, use_sandbox,
3835d1f56e68SBram Moolenaar 					      0, maxlen, NULL, NULL);
383653989554SBram Moolenaar 		if (called_emsg > called_emsg_before)
3837d3667a2eSBram Moolenaar 		    set_string_option_direct((char_u *)"titlestring", -1,
3838d3667a2eSBram Moolenaar 					   (char_u *)"", OPT_FREE, SID_ERROR);
3839d3667a2eSBram Moolenaar 	    }
3840071d4279SBram Moolenaar 	    else
3841071d4279SBram Moolenaar #endif
384284a93085SBram Moolenaar 		title_str = p_titlestring;
3843071d4279SBram Moolenaar 	}
3844071d4279SBram Moolenaar 	else
3845071d4279SBram Moolenaar 	{
3846c667da51SBram Moolenaar 	    // format: "fname + (path) (1 of 2) - VIM"
3847071d4279SBram Moolenaar 
38482c66669cSBram Moolenaar #define SPACE_FOR_FNAME (IOSIZE - 100)
38492c66669cSBram Moolenaar #define SPACE_FOR_DIR   (IOSIZE - 20)
3850c667da51SBram Moolenaar #define SPACE_FOR_ARGNR (IOSIZE - 10)  // at least room for " - VIM"
3851071d4279SBram Moolenaar 	    if (curbuf->b_fname == NULL)
38522c66669cSBram Moolenaar 		vim_strncpy(buf, (char_u *)_("[No Name]"), SPACE_FOR_FNAME);
385321554414SBram Moolenaar #ifdef FEAT_TERMINAL
385421554414SBram Moolenaar 	    else if (curbuf->b_term != NULL)
385521554414SBram Moolenaar 	    {
385621554414SBram Moolenaar 		vim_strncpy(buf, term_get_status_text(curbuf->b_term),
385721554414SBram Moolenaar 							      SPACE_FOR_FNAME);
385821554414SBram Moolenaar 	    }
385921554414SBram Moolenaar #endif
3860071d4279SBram Moolenaar 	    else
3861071d4279SBram Moolenaar 	    {
3862071d4279SBram Moolenaar 		p = transstr(gettail(curbuf->b_fname));
38632c66669cSBram Moolenaar 		vim_strncpy(buf, p, SPACE_FOR_FNAME);
3864071d4279SBram Moolenaar 		vim_free(p);
3865071d4279SBram Moolenaar 	    }
3866071d4279SBram Moolenaar 
386721554414SBram Moolenaar #ifdef FEAT_TERMINAL
386821554414SBram Moolenaar 	    if (curbuf->b_term == NULL)
386921554414SBram Moolenaar #endif
3870071d4279SBram Moolenaar 		switch (bufIsChanged(curbuf)
3871071d4279SBram Moolenaar 			+ (curbuf->b_p_ro * 2)
3872071d4279SBram Moolenaar 			+ (!curbuf->b_p_ma * 4))
3873071d4279SBram Moolenaar 		{
3874071d4279SBram Moolenaar 		    case 1: STRCAT(buf, " +"); break;
3875071d4279SBram Moolenaar 		    case 2: STRCAT(buf, " ="); break;
3876071d4279SBram Moolenaar 		    case 3: STRCAT(buf, " =+"); break;
3877071d4279SBram Moolenaar 		    case 4:
3878071d4279SBram Moolenaar 		    case 6: STRCAT(buf, " -"); break;
3879071d4279SBram Moolenaar 		    case 5:
3880071d4279SBram Moolenaar 		    case 7: STRCAT(buf, " -+"); break;
3881071d4279SBram Moolenaar 		}
3882071d4279SBram Moolenaar 
388321554414SBram Moolenaar 	    if (curbuf->b_fname != NULL
388421554414SBram Moolenaar #ifdef FEAT_TERMINAL
388521554414SBram Moolenaar 		    && curbuf->b_term == NULL
388621554414SBram Moolenaar #endif
388721554414SBram Moolenaar 		    )
3888071d4279SBram Moolenaar 	    {
3889c667da51SBram Moolenaar 		// Get path of file, replace home dir with ~
3890071d4279SBram Moolenaar 		off = (int)STRLEN(buf);
3891071d4279SBram Moolenaar 		buf[off++] = ' ';
3892071d4279SBram Moolenaar 		buf[off++] = '(';
3893071d4279SBram Moolenaar 		home_replace(curbuf, curbuf->b_ffname,
38942c66669cSBram Moolenaar 					buf + off, SPACE_FOR_DIR - off, TRUE);
3895071d4279SBram Moolenaar #ifdef BACKSLASH_IN_FILENAME
3896c667da51SBram Moolenaar 		// avoid "c:/name" to be reduced to "c"
3897071d4279SBram Moolenaar 		if (isalpha(buf[off]) && buf[off + 1] == ':')
3898071d4279SBram Moolenaar 		    off += 2;
3899071d4279SBram Moolenaar #endif
3900c667da51SBram Moolenaar 		// remove the file name
39011cd871b5SBram Moolenaar 		p = gettail_sep(buf + off);
3902071d4279SBram Moolenaar 		if (p == buf + off)
39031f2903c4SBram Moolenaar 		{
3904c667da51SBram Moolenaar 		    // must be a help buffer
390521554414SBram Moolenaar 		    vim_strncpy(buf + off, (char_u *)_("help"),
39062c66669cSBram Moolenaar 					   (size_t)(SPACE_FOR_DIR - off - 1));
39071f2903c4SBram Moolenaar 		}
3908071d4279SBram Moolenaar 		else
3909071d4279SBram Moolenaar 		    *p = NUL;
39101cd871b5SBram Moolenaar 
3911c667da51SBram Moolenaar 		// Translate unprintable chars and concatenate.  Keep some
3912c667da51SBram Moolenaar 		// room for the server name.  When there is no room (very long
3913c667da51SBram Moolenaar 		// file name) use (...).
39142c66669cSBram Moolenaar 		if (off < SPACE_FOR_DIR)
39152c66669cSBram Moolenaar 		{
3916071d4279SBram Moolenaar 		    p = transstr(buf + off);
39172c66669cSBram Moolenaar 		    vim_strncpy(buf + off, p, (size_t)(SPACE_FOR_DIR - off));
3918071d4279SBram Moolenaar 		    vim_free(p);
39192c66669cSBram Moolenaar 		}
39202c66669cSBram Moolenaar 		else
39212c66669cSBram Moolenaar 		{
39222c66669cSBram Moolenaar 		    vim_strncpy(buf + off, (char_u *)"...",
39232c66669cSBram Moolenaar 					     (size_t)(SPACE_FOR_ARGNR - off));
39242c66669cSBram Moolenaar 		}
3925071d4279SBram Moolenaar 		STRCAT(buf, ")");
3926071d4279SBram Moolenaar 	    }
3927071d4279SBram Moolenaar 
39282c66669cSBram Moolenaar 	    append_arg_number(curwin, buf, SPACE_FOR_ARGNR, FALSE);
3929071d4279SBram Moolenaar 
3930071d4279SBram Moolenaar #if defined(FEAT_CLIENTSERVER)
3931071d4279SBram Moolenaar 	    if (serverName != NULL)
3932071d4279SBram Moolenaar 	    {
3933071d4279SBram Moolenaar 		STRCAT(buf, " - ");
3934ef9d6aa7SBram Moolenaar 		vim_strcat(buf, serverName, IOSIZE);
3935071d4279SBram Moolenaar 	    }
3936071d4279SBram Moolenaar 	    else
3937071d4279SBram Moolenaar #endif
3938071d4279SBram Moolenaar 		STRCAT(buf, " - VIM");
3939071d4279SBram Moolenaar 
3940071d4279SBram Moolenaar 	    if (maxlen > 0)
3941071d4279SBram Moolenaar 	    {
3942c667da51SBram Moolenaar 		// make it shorter by removing a bit in the middle
3943f31b764cSBram Moolenaar 		if (vim_strsize(buf) > maxlen)
3944f31b764cSBram Moolenaar 		    trunc_string(buf, buf, maxlen, IOSIZE);
3945071d4279SBram Moolenaar 	    }
3946071d4279SBram Moolenaar 	}
3947071d4279SBram Moolenaar     }
394884a93085SBram Moolenaar     mustset = value_changed(title_str, &lasttitle);
3949071d4279SBram Moolenaar 
3950071d4279SBram Moolenaar     if (p_icon)
3951071d4279SBram Moolenaar     {
395284a93085SBram Moolenaar 	icon_str = buf;
3953071d4279SBram Moolenaar 	if (*p_iconstring != NUL)
3954071d4279SBram Moolenaar 	{
3955071d4279SBram Moolenaar #ifdef FEAT_STL_OPT
3956d3667a2eSBram Moolenaar 	    if (stl_syntax & STL_IN_ICON)
3957d3667a2eSBram Moolenaar 	    {
39582a0449d1SBram Moolenaar 		int	use_sandbox = FALSE;
395953989554SBram Moolenaar 		int	called_emsg_before = called_emsg;
39602a0449d1SBram Moolenaar 
39612a0449d1SBram Moolenaar # ifdef FEAT_EVAL
3962d1f56e68SBram Moolenaar 		use_sandbox = was_set_insecurely((char_u *)"iconstring", 0);
39632a0449d1SBram Moolenaar # endif
396484a93085SBram Moolenaar 		build_stl_str_hl(curwin, icon_str, sizeof(buf),
39652a0449d1SBram Moolenaar 						    p_iconstring, use_sandbox,
3966d1f56e68SBram Moolenaar 						    0, 0, NULL, NULL);
396753989554SBram Moolenaar 		if (called_emsg > called_emsg_before)
3968d3667a2eSBram Moolenaar 		    set_string_option_direct((char_u *)"iconstring", -1,
3969d3667a2eSBram Moolenaar 					   (char_u *)"", OPT_FREE, SID_ERROR);
3970d3667a2eSBram Moolenaar 	    }
3971071d4279SBram Moolenaar 	    else
3972071d4279SBram Moolenaar #endif
397384a93085SBram Moolenaar 		icon_str = p_iconstring;
3974071d4279SBram Moolenaar 	}
3975071d4279SBram Moolenaar 	else
3976071d4279SBram Moolenaar 	{
3977071d4279SBram Moolenaar 	    if (buf_spname(curbuf) != NULL)
397884a93085SBram Moolenaar 		p = buf_spname(curbuf);
3979c667da51SBram Moolenaar 	    else		    // use file name only in icon
398084a93085SBram Moolenaar 		p = gettail(curbuf->b_ffname);
398184a93085SBram Moolenaar 	    *icon_str = NUL;
3982c667da51SBram Moolenaar 	    // Truncate name at 100 bytes.
398384a93085SBram Moolenaar 	    len = (int)STRLEN(p);
3984071d4279SBram Moolenaar 	    if (len > 100)
3985071d4279SBram Moolenaar 	    {
3986071d4279SBram Moolenaar 		len -= 100;
3987071d4279SBram Moolenaar 		if (has_mbyte)
398884a93085SBram Moolenaar 		    len += (*mb_tail_off)(p, p + len) + 1;
398984a93085SBram Moolenaar 		p += len;
3990071d4279SBram Moolenaar 	    }
399184a93085SBram Moolenaar 	    STRCPY(icon_str, p);
399284a93085SBram Moolenaar 	    trans_characters(icon_str, IOSIZE);
3993071d4279SBram Moolenaar 	}
3994071d4279SBram Moolenaar     }
3995071d4279SBram Moolenaar 
399684a93085SBram Moolenaar     mustset |= value_changed(icon_str, &lasticon);
3997071d4279SBram Moolenaar 
3998071d4279SBram Moolenaar     if (mustset)
3999071d4279SBram Moolenaar 	resettitle();
4000071d4279SBram Moolenaar }
4001071d4279SBram Moolenaar 
4002071d4279SBram Moolenaar /*
4003071d4279SBram Moolenaar  * Used for title and icon: Check if "str" differs from "*last".  Set "*last"
4004071d4279SBram Moolenaar  * from "str" if it does.
400584a93085SBram Moolenaar  * Return TRUE if resettitle() is to be called.
4006071d4279SBram Moolenaar  */
4007071d4279SBram Moolenaar     static int
value_changed(char_u * str,char_u ** last)400884a93085SBram Moolenaar value_changed(char_u *str, char_u **last)
4009071d4279SBram Moolenaar {
4010071d4279SBram Moolenaar     if ((str == NULL) != (*last == NULL)
4011071d4279SBram Moolenaar 	    || (str != NULL && *last != NULL && STRCMP(str, *last) != 0))
4012071d4279SBram Moolenaar     {
4013071d4279SBram Moolenaar 	vim_free(*last);
4014071d4279SBram Moolenaar 	if (str == NULL)
401584a93085SBram Moolenaar 	{
4016071d4279SBram Moolenaar 	    *last = NULL;
401740385dbcSBram Moolenaar 	    mch_restore_title(
401840385dbcSBram Moolenaar 		  last == &lasttitle ? SAVE_RESTORE_TITLE : SAVE_RESTORE_ICON);
401984a93085SBram Moolenaar 	}
4020071d4279SBram Moolenaar 	else
402184a93085SBram Moolenaar 	{
4022071d4279SBram Moolenaar 	    *last = vim_strsave(str);
4023071d4279SBram Moolenaar 	    return TRUE;
4024071d4279SBram Moolenaar 	}
402584a93085SBram Moolenaar     }
4026071d4279SBram Moolenaar     return FALSE;
4027071d4279SBram Moolenaar }
4028071d4279SBram Moolenaar 
4029071d4279SBram Moolenaar /*
4030071d4279SBram Moolenaar  * Put current window title back (used after calling a shell)
4031071d4279SBram Moolenaar  */
4032071d4279SBram Moolenaar     void
resettitle(void)40337454a06eSBram Moolenaar resettitle(void)
4034071d4279SBram Moolenaar {
4035071d4279SBram Moolenaar     mch_settitle(lasttitle, lasticon);
4036071d4279SBram Moolenaar }
4037ea408854SBram Moolenaar 
4038ea408854SBram Moolenaar # if defined(EXITFREE) || defined(PROTO)
4039ea408854SBram Moolenaar     void
free_titles(void)40407454a06eSBram Moolenaar free_titles(void)
4041ea408854SBram Moolenaar {
4042ea408854SBram Moolenaar     vim_free(lasttitle);
4043ea408854SBram Moolenaar     vim_free(lasticon);
4044ea408854SBram Moolenaar }
4045ea408854SBram Moolenaar # endif
4046ea408854SBram Moolenaar 
4047c667da51SBram Moolenaar #endif // FEAT_TITLE
4048071d4279SBram Moolenaar 
4049ba6c0524SBram Moolenaar #if defined(FEAT_STL_OPT) || defined(FEAT_GUI_TABLINE) || defined(PROTO)
40508133cc6bSBram Moolenaar 
40518133cc6bSBram Moolenaar /*
40528133cc6bSBram Moolenaar  * Used for building in the status line.
40538133cc6bSBram Moolenaar  */
40548133cc6bSBram Moolenaar typedef struct
40558133cc6bSBram Moolenaar {
40568133cc6bSBram Moolenaar     char_u	*stl_start;
40578133cc6bSBram Moolenaar     int		stl_minwid;
40588133cc6bSBram Moolenaar     int		stl_maxwid;
40598133cc6bSBram Moolenaar     enum {
40608133cc6bSBram Moolenaar 	Normal,
40618133cc6bSBram Moolenaar 	Empty,
40628133cc6bSBram Moolenaar 	Group,
40638133cc6bSBram Moolenaar 	Middle,
40648133cc6bSBram Moolenaar 	Highlight,
40658133cc6bSBram Moolenaar 	TabPage,
40668133cc6bSBram Moolenaar 	Trunc
40678133cc6bSBram Moolenaar     }		stl_type;
40688133cc6bSBram Moolenaar } stl_item_T;
40698133cc6bSBram Moolenaar 
40708133cc6bSBram Moolenaar static size_t		stl_items_len = 20; // Initial value, grows as needed.
40718133cc6bSBram Moolenaar static stl_item_T      *stl_items = NULL;
40728133cc6bSBram Moolenaar static int	       *stl_groupitem = NULL;
40738133cc6bSBram Moolenaar static stl_hlrec_T     *stl_hltab = NULL;
40748133cc6bSBram Moolenaar static stl_hlrec_T     *stl_tabtab = NULL;
40758133cc6bSBram Moolenaar 
4076071d4279SBram Moolenaar /*
40772a0449d1SBram Moolenaar  * Build a string from the status line items in "fmt".
4078071d4279SBram Moolenaar  * Return length of string in screen cells.
4079071d4279SBram Moolenaar  *
40802a0449d1SBram Moolenaar  * Normally works for window "wp", except when working for 'tabline' then it
40812a0449d1SBram Moolenaar  * is "curwin".
40822a0449d1SBram Moolenaar  *
4083071d4279SBram Moolenaar  * Items are drawn interspersed with the text that surrounds it
4084071d4279SBram Moolenaar  * Specials: %-<wid>(xxx%) => group, %= => middle marker, %< => truncation
4085071d4279SBram Moolenaar  * Item: %-<minwid>.<maxwid><itemch> All but <itemch> are optional
4086071d4279SBram Moolenaar  *
4087071d4279SBram Moolenaar  * If maxwidth is not zero, the string will be filled at any middle marker
4088071d4279SBram Moolenaar  * or truncated if too long, fillchar is used for all whitespace.
4089071d4279SBram Moolenaar  */
4090071d4279SBram Moolenaar     int
build_stl_str_hl(win_T * wp,char_u * out,size_t outlen,char_u * fmt,int use_sandbox UNUSED,int fillchar,int maxwidth,stl_hlrec_T ** hltab,stl_hlrec_T ** tabtab)40917454a06eSBram Moolenaar build_stl_str_hl(
40927454a06eSBram Moolenaar     win_T	*wp,
4093c667da51SBram Moolenaar     char_u	*out,		// buffer to write into != NameBuff
4094c667da51SBram Moolenaar     size_t	outlen,		// length of out[]
40957454a06eSBram Moolenaar     char_u	*fmt,
4096c667da51SBram Moolenaar     int		use_sandbox UNUSED, // "fmt" was set insecurely, use sandbox
40977454a06eSBram Moolenaar     int		fillchar,
40987454a06eSBram Moolenaar     int		maxwidth,
40998133cc6bSBram Moolenaar     stl_hlrec_T **hltab,	// return: HL attributes (can be NULL)
41008133cc6bSBram Moolenaar     stl_hlrec_T **tabtab)	// return: tab page nrs (can be NULL)
4101071d4279SBram Moolenaar {
410210772307SBram Moolenaar     linenr_T	lnum;
410310772307SBram Moolenaar     size_t	len;
4104071d4279SBram Moolenaar     char_u	*p;
4105071d4279SBram Moolenaar     char_u	*s;
4106071d4279SBram Moolenaar     char_u	*t;
4107567199b6SBram Moolenaar     int		byteval;
4108071d4279SBram Moolenaar #ifdef FEAT_EVAL
4109ba2929b6SBram Moolenaar     win_T	*save_curwin;
4110ba2929b6SBram Moolenaar     buf_T	*save_curbuf;
4111dee50a51SBram Moolenaar     int		save_VIsual_active;
4112071d4279SBram Moolenaar #endif
4113071d4279SBram Moolenaar     int		empty_line;
4114071d4279SBram Moolenaar     colnr_T	virtcol;
4115071d4279SBram Moolenaar     long	l;
4116071d4279SBram Moolenaar     long	n;
4117071d4279SBram Moolenaar     int		prevchar_isflag;
4118071d4279SBram Moolenaar     int		prevchar_isitem;
4119071d4279SBram Moolenaar     int		itemisflag;
4120071d4279SBram Moolenaar     int		fillable;
4121071d4279SBram Moolenaar     char_u	*str;
4122071d4279SBram Moolenaar     long	num;
4123071d4279SBram Moolenaar     int		width;
4124071d4279SBram Moolenaar     int		itemcnt;
4125071d4279SBram Moolenaar     int		curitem;
41266b89dbb5SBram Moolenaar     int		group_end_userhl;
41276b89dbb5SBram Moolenaar     int		group_start_userhl;
4128071d4279SBram Moolenaar     int		groupdepth;
412930e3de21Sshadmansaleh #ifdef FEAT_EVAL
413030e3de21Sshadmansaleh     int		evaldepth;
413130e3de21Sshadmansaleh #endif
4132071d4279SBram Moolenaar     int		minwid;
4133071d4279SBram Moolenaar     int		maxwid;
4134071d4279SBram Moolenaar     int		zeropad;
4135071d4279SBram Moolenaar     char_u	base;
4136071d4279SBram Moolenaar     char_u	opt;
4137071d4279SBram Moolenaar #define TMPLEN 70
41381c6fd1e1SBram Moolenaar     char_u	buf_tmp[TMPLEN];
41391c6fd1e1SBram Moolenaar     char_u	win_tmp[TMPLEN];
4140030f0dfaSBram Moolenaar     char_u	*usefmt = fmt;
41418133cc6bSBram Moolenaar     stl_hlrec_T *sp;
4142ba2929b6SBram Moolenaar     int		save_must_redraw = must_redraw;
4143ba2929b6SBram Moolenaar     int		save_redr_type = curwin->w_redr_type;
4144030f0dfaSBram Moolenaar 
41458133cc6bSBram Moolenaar     if (stl_items == NULL)
41468133cc6bSBram Moolenaar     {
41478133cc6bSBram Moolenaar 	stl_items = ALLOC_MULT(stl_item_T, stl_items_len);
41488133cc6bSBram Moolenaar 	stl_groupitem = ALLOC_MULT(int, stl_items_len);
41498133cc6bSBram Moolenaar 	stl_hltab  = ALLOC_MULT(stl_hlrec_T, stl_items_len);
41508133cc6bSBram Moolenaar 	stl_tabtab = ALLOC_MULT(stl_hlrec_T, stl_items_len);
41518133cc6bSBram Moolenaar     }
41528133cc6bSBram Moolenaar 
4153030f0dfaSBram Moolenaar #ifdef FEAT_EVAL
4154030f0dfaSBram Moolenaar     /*
4155030f0dfaSBram Moolenaar      * When the format starts with "%!" then evaluate it as an expression and
4156030f0dfaSBram Moolenaar      * use the result as the actual format string.
4157030f0dfaSBram Moolenaar      */
4158030f0dfaSBram Moolenaar     if (fmt[0] == '%' && fmt[1] == '!')
4159030f0dfaSBram Moolenaar     {
41601c6fd1e1SBram Moolenaar 	typval_T	tv;
41611c6fd1e1SBram Moolenaar 
41621c6fd1e1SBram Moolenaar 	tv.v_type = VAR_NUMBER;
41631c6fd1e1SBram Moolenaar 	tv.vval.v_number = wp->w_id;
41641c6fd1e1SBram Moolenaar 	set_var((char_u *)"g:statusline_winid", &tv, FALSE);
41651c6fd1e1SBram Moolenaar 
4166b171fb17SBram Moolenaar 	usefmt = eval_to_string_safe(fmt + 2, use_sandbox);
4167030f0dfaSBram Moolenaar 	if (usefmt == NULL)
41684100af78SBram Moolenaar 	    usefmt = fmt;
41691c6fd1e1SBram Moolenaar 
41701c6fd1e1SBram Moolenaar 	do_unlet((char_u *)"g:statusline_winid", TRUE);
4171030f0dfaSBram Moolenaar     }
4172030f0dfaSBram Moolenaar #endif
4173071d4279SBram Moolenaar 
4174071d4279SBram Moolenaar     if (fillchar == 0)
4175071d4279SBram Moolenaar 	fillchar = ' ';
4176910f66f9SBram Moolenaar 
417710772307SBram Moolenaar     // The cursor in windows other than the current one isn't always
417810772307SBram Moolenaar     // up-to-date, esp. because of autocommands and timers.
417910772307SBram Moolenaar     lnum = wp->w_cursor.lnum;
418010772307SBram Moolenaar     if (lnum > wp->w_buffer->b_ml.ml_line_count)
418110772307SBram Moolenaar     {
418210772307SBram Moolenaar 	lnum = wp->w_buffer->b_ml.ml_line_count;
418310772307SBram Moolenaar 	wp->w_cursor.lnum = lnum;
418410772307SBram Moolenaar     }
418510772307SBram Moolenaar 
418610772307SBram Moolenaar     // Get line & check if empty (cursorpos will show "0-1").  Note that
418710772307SBram Moolenaar     // p will become invalid when getting another buffer line.
418810772307SBram Moolenaar     p = ml_get_buf(wp->w_buffer, lnum, FALSE);
4189567199b6SBram Moolenaar     empty_line = (*p == NUL);
4190567199b6SBram Moolenaar 
419110772307SBram Moolenaar     // Get the byte value now, in case we need it below. This is more efficient
419210772307SBram Moolenaar     // than making a copy of the line.
419310772307SBram Moolenaar     len = STRLEN(p);
419410772307SBram Moolenaar     if (wp->w_cursor.col > (colnr_T)len)
419510772307SBram Moolenaar     {
419610772307SBram Moolenaar 	// Line may have changed since checking the cursor column, or the lnum
419710772307SBram Moolenaar 	// was adjusted above.
419810772307SBram Moolenaar 	wp->w_cursor.col = (colnr_T)len;
419910772307SBram Moolenaar 	wp->w_cursor.coladd = 0;
4200567199b6SBram Moolenaar 	byteval = 0;
420110772307SBram Moolenaar     }
4202567199b6SBram Moolenaar     else
4203567199b6SBram Moolenaar 	byteval = (*mb_ptr2char)(p + wp->w_cursor.col);
4204071d4279SBram Moolenaar 
4205071d4279SBram Moolenaar     groupdepth = 0;
420630e3de21Sshadmansaleh #ifdef FEAT_EVAL
420730e3de21Sshadmansaleh     evaldepth = 0;
420830e3de21Sshadmansaleh #endif
4209071d4279SBram Moolenaar     p = out;
4210071d4279SBram Moolenaar     curitem = 0;
4211071d4279SBram Moolenaar     prevchar_isflag = TRUE;
4212071d4279SBram Moolenaar     prevchar_isitem = FALSE;
4213030f0dfaSBram Moolenaar     for (s = usefmt; *s; )
4214071d4279SBram Moolenaar     {
42158133cc6bSBram Moolenaar 	if (curitem == (int)stl_items_len)
4216b75d09d4SBram Moolenaar 	{
42178133cc6bSBram Moolenaar 	    size_t	new_len = stl_items_len * 3 / 2;
42188133cc6bSBram Moolenaar 	    stl_item_T	*new_items;
42198133cc6bSBram Moolenaar 	    int		*new_groupitem;
42208133cc6bSBram Moolenaar 	    stl_hlrec_T	*new_hlrec;
42218133cc6bSBram Moolenaar 
42228133cc6bSBram Moolenaar 	    new_items = vim_realloc(stl_items, sizeof(stl_item_T) * new_len);
42238133cc6bSBram Moolenaar 	    if (new_items == NULL)
4224b75d09d4SBram Moolenaar 		break;
42258133cc6bSBram Moolenaar 	    stl_items = new_items;
42268133cc6bSBram Moolenaar 	    new_groupitem = vim_realloc(stl_groupitem, sizeof(int) * new_len);
42278133cc6bSBram Moolenaar 	    if (new_groupitem == NULL)
42288133cc6bSBram Moolenaar 		break;
42298133cc6bSBram Moolenaar 	    stl_groupitem = new_groupitem;
42308133cc6bSBram Moolenaar 	    new_hlrec = vim_realloc(stl_hltab, sizeof(stl_hlrec_T) * new_len);
42318133cc6bSBram Moolenaar 	    if (new_hlrec == NULL)
42328133cc6bSBram Moolenaar 		break;
42338133cc6bSBram Moolenaar 	    stl_hltab = new_hlrec;
42348133cc6bSBram Moolenaar 	    new_hlrec = vim_realloc(stl_tabtab, sizeof(stl_hlrec_T) * new_len);
42358133cc6bSBram Moolenaar 	    if (new_hlrec == NULL)
42368133cc6bSBram Moolenaar 		break;
42378133cc6bSBram Moolenaar 	    stl_tabtab = new_hlrec;
42388133cc6bSBram Moolenaar 	    stl_items_len = new_len;
4239b75d09d4SBram Moolenaar 	}
4240b75d09d4SBram Moolenaar 
4241071d4279SBram Moolenaar 	if (*s != NUL && *s != '%')
4242071d4279SBram Moolenaar 	    prevchar_isflag = prevchar_isitem = FALSE;
4243071d4279SBram Moolenaar 
4244071d4279SBram Moolenaar 	/*
4245071d4279SBram Moolenaar 	 * Handle up to the next '%' or the end.
4246071d4279SBram Moolenaar 	 */
4247071d4279SBram Moolenaar 	while (*s != NUL && *s != '%' && p + 1 < out + outlen)
4248071d4279SBram Moolenaar 	    *p++ = *s++;
4249071d4279SBram Moolenaar 	if (*s == NUL || p + 1 >= out + outlen)
4250071d4279SBram Moolenaar 	    break;
4251071d4279SBram Moolenaar 
4252071d4279SBram Moolenaar 	/*
4253071d4279SBram Moolenaar 	 * Handle one '%' item.
4254071d4279SBram Moolenaar 	 */
4255071d4279SBram Moolenaar 	s++;
4256c667da51SBram Moolenaar 	if (*s == NUL)  // ignore trailing %
42571d87f516SBram Moolenaar 	    break;
4258071d4279SBram Moolenaar 	if (*s == '%')
4259071d4279SBram Moolenaar 	{
4260071d4279SBram Moolenaar 	    if (p + 1 >= out + outlen)
4261071d4279SBram Moolenaar 		break;
4262071d4279SBram Moolenaar 	    *p++ = *s++;
4263071d4279SBram Moolenaar 	    prevchar_isflag = prevchar_isitem = FALSE;
4264071d4279SBram Moolenaar 	    continue;
4265071d4279SBram Moolenaar 	}
4266071d4279SBram Moolenaar 	if (*s == STL_MIDDLEMARK)
4267071d4279SBram Moolenaar 	{
4268071d4279SBram Moolenaar 	    s++;
4269071d4279SBram Moolenaar 	    if (groupdepth > 0)
4270071d4279SBram Moolenaar 		continue;
42718133cc6bSBram Moolenaar 	    stl_items[curitem].stl_type = Middle;
42728133cc6bSBram Moolenaar 	    stl_items[curitem++].stl_start = p;
4273071d4279SBram Moolenaar 	    continue;
4274071d4279SBram Moolenaar 	}
4275071d4279SBram Moolenaar 	if (*s == STL_TRUNCMARK)
4276071d4279SBram Moolenaar 	{
4277071d4279SBram Moolenaar 	    s++;
42788133cc6bSBram Moolenaar 	    stl_items[curitem].stl_type = Trunc;
42798133cc6bSBram Moolenaar 	    stl_items[curitem++].stl_start = p;
4280071d4279SBram Moolenaar 	    continue;
4281071d4279SBram Moolenaar 	}
4282071d4279SBram Moolenaar 	if (*s == ')')
4283071d4279SBram Moolenaar 	{
4284071d4279SBram Moolenaar 	    s++;
4285071d4279SBram Moolenaar 	    if (groupdepth < 1)
4286071d4279SBram Moolenaar 		continue;
4287071d4279SBram Moolenaar 	    groupdepth--;
4288071d4279SBram Moolenaar 
42898133cc6bSBram Moolenaar 	    t = stl_items[stl_groupitem[groupdepth]].stl_start;
4290071d4279SBram Moolenaar 	    *p = NUL;
4291071d4279SBram Moolenaar 	    l = vim_strsize(t);
42928133cc6bSBram Moolenaar 	    if (curitem > stl_groupitem[groupdepth] + 1
42938133cc6bSBram Moolenaar 		    && stl_items[stl_groupitem[groupdepth]].stl_minwid == 0)
4294071d4279SBram Moolenaar 	    {
4295c667da51SBram Moolenaar 		// remove group if all items are empty and highlight group
4296c667da51SBram Moolenaar 		// doesn't change
42976b89dbb5SBram Moolenaar 		group_start_userhl = group_end_userhl = 0;
42988133cc6bSBram Moolenaar 		for (n = stl_groupitem[groupdepth] - 1; n >= 0; n--)
4299235dddf1SBram Moolenaar 		{
43008133cc6bSBram Moolenaar 		    if (stl_items[n].stl_type == Highlight)
4301235dddf1SBram Moolenaar 		    {
43028133cc6bSBram Moolenaar 			group_start_userhl = group_end_userhl =
43038133cc6bSBram Moolenaar 						       stl_items[n].stl_minwid;
4304235dddf1SBram Moolenaar 			break;
4305235dddf1SBram Moolenaar 		    }
4306235dddf1SBram Moolenaar 		}
43078133cc6bSBram Moolenaar 		for (n = stl_groupitem[groupdepth] + 1; n < curitem; n++)
43086b89dbb5SBram Moolenaar 		{
43098133cc6bSBram Moolenaar 		    if (stl_items[n].stl_type == Normal)
4310071d4279SBram Moolenaar 			break;
43118133cc6bSBram Moolenaar 		    if (stl_items[n].stl_type == Highlight)
43128133cc6bSBram Moolenaar 			group_end_userhl = stl_items[n].stl_minwid;
43136b89dbb5SBram Moolenaar 		}
43146b89dbb5SBram Moolenaar 		if (n == curitem && group_start_userhl == group_end_userhl)
4315071d4279SBram Moolenaar 		{
4316f56c95fdSBram Moolenaar 		    // empty group
4317071d4279SBram Moolenaar 		    p = t;
4318071d4279SBram Moolenaar 		    l = 0;
43198133cc6bSBram Moolenaar 		    for (n = stl_groupitem[groupdepth] + 1; n < curitem; n++)
4320f56c95fdSBram Moolenaar 		    {
4321f56c95fdSBram Moolenaar 			// do not use the highlighting from the removed group
43228133cc6bSBram Moolenaar 			if (stl_items[n].stl_type == Highlight)
43238133cc6bSBram Moolenaar 			    stl_items[n].stl_type = Empty;
4324f56c95fdSBram Moolenaar 			// adjust the start position of TabPage to the next
4325f56c95fdSBram Moolenaar 			// item position
43268133cc6bSBram Moolenaar 			if (stl_items[n].stl_type == TabPage)
43278133cc6bSBram Moolenaar 			    stl_items[n].stl_start = p;
4328f56c95fdSBram Moolenaar 		    }
4329071d4279SBram Moolenaar 		}
4330071d4279SBram Moolenaar 	    }
43318133cc6bSBram Moolenaar 	    if (l > stl_items[stl_groupitem[groupdepth]].stl_maxwid)
4332071d4279SBram Moolenaar 	    {
4333c667da51SBram Moolenaar 		// truncate, remove n bytes of text at the start
4334071d4279SBram Moolenaar 		if (has_mbyte)
4335071d4279SBram Moolenaar 		{
4336c667da51SBram Moolenaar 		    // Find the first character that should be included.
4337071d4279SBram Moolenaar 		    n = 0;
43388133cc6bSBram Moolenaar 		    while (l >= stl_items[stl_groupitem[groupdepth]].stl_maxwid)
4339071d4279SBram Moolenaar 		    {
4340071d4279SBram Moolenaar 			l -= ptr2cells(t + n);
43410fa313a7SBram Moolenaar 			n += (*mb_ptr2len)(t + n);
4342071d4279SBram Moolenaar 		    }
4343071d4279SBram Moolenaar 		}
4344071d4279SBram Moolenaar 		else
43458133cc6bSBram Moolenaar 		    n = (long)(p - t) - stl_items[stl_groupitem[groupdepth]]
43468133cc6bSBram Moolenaar 							       .stl_maxwid + 1;
4347071d4279SBram Moolenaar 
4348071d4279SBram Moolenaar 		*t = '<';
43490ab2a887SBram Moolenaar 		mch_memmove(t + 1, t + n, (size_t)(p - (t + n)));
4350071d4279SBram Moolenaar 		p = p - n + 1;
435113505972SBram Moolenaar 
435213505972SBram Moolenaar 		// Fill up space left over by half a double-wide char.
43538133cc6bSBram Moolenaar 		while (++l < stl_items[stl_groupitem[groupdepth]].stl_minwid)
4354008bff96SBram Moolenaar 		    MB_CHAR2BYTES(fillchar, p);
4355071d4279SBram Moolenaar 
4356c667da51SBram Moolenaar 		// correct the start of the items for the truncation
43578133cc6bSBram Moolenaar 		for (l = stl_groupitem[groupdepth] + 1; l < curitem; l++)
4358071d4279SBram Moolenaar 		{
43598133cc6bSBram Moolenaar 		    stl_items[l].stl_start -= n;
43608133cc6bSBram Moolenaar 		    if (stl_items[l].stl_start < t)
43618133cc6bSBram Moolenaar 			stl_items[l].stl_start = t;
4362071d4279SBram Moolenaar 		}
4363071d4279SBram Moolenaar 	    }
43648133cc6bSBram Moolenaar 	    else if (abs(stl_items[stl_groupitem[groupdepth]].stl_minwid) > l)
4365071d4279SBram Moolenaar 	    {
4366c667da51SBram Moolenaar 		// fill
43678133cc6bSBram Moolenaar 		n = stl_items[stl_groupitem[groupdepth]].stl_minwid;
4368071d4279SBram Moolenaar 		if (n < 0)
4369071d4279SBram Moolenaar 		{
4370c667da51SBram Moolenaar 		    // fill by appending characters
4371071d4279SBram Moolenaar 		    n = 0 - n;
4372071d4279SBram Moolenaar 		    while (l++ < n && p + 1 < out + outlen)
4373008bff96SBram Moolenaar 			MB_CHAR2BYTES(fillchar, p);
4374071d4279SBram Moolenaar 		}
4375071d4279SBram Moolenaar 		else
4376071d4279SBram Moolenaar 		{
4377c667da51SBram Moolenaar 		    // fill by inserting characters
4378008bff96SBram Moolenaar 		    l = (n - l) * MB_CHAR2LEN(fillchar);
4379008bff96SBram Moolenaar 		    mch_memmove(t + l, t, (size_t)(p - t));
4380071d4279SBram Moolenaar 		    if (p + l >= out + outlen)
4381a93fa7eeSBram Moolenaar 			l = (long)((out + outlen) - p - 1);
4382071d4279SBram Moolenaar 		    p += l;
43838133cc6bSBram Moolenaar 		    for (n = stl_groupitem[groupdepth] + 1; n < curitem; n++)
43848133cc6bSBram Moolenaar 			stl_items[n].stl_start += l;
4385071d4279SBram Moolenaar 		    for ( ; l > 0; l--)
4386008bff96SBram Moolenaar 			MB_CHAR2BYTES(fillchar, t);
4387071d4279SBram Moolenaar 		}
4388071d4279SBram Moolenaar 	    }
4389071d4279SBram Moolenaar 	    continue;
4390071d4279SBram Moolenaar 	}
4391071d4279SBram Moolenaar 	minwid = 0;
4392071d4279SBram Moolenaar 	maxwid = 9999;
4393071d4279SBram Moolenaar 	zeropad = FALSE;
4394071d4279SBram Moolenaar 	l = 1;
4395071d4279SBram Moolenaar 	if (*s == '0')
4396071d4279SBram Moolenaar 	{
4397071d4279SBram Moolenaar 	    s++;
4398071d4279SBram Moolenaar 	    zeropad = TRUE;
4399071d4279SBram Moolenaar 	}
4400071d4279SBram Moolenaar 	if (*s == '-')
4401071d4279SBram Moolenaar 	{
4402071d4279SBram Moolenaar 	    s++;
4403071d4279SBram Moolenaar 	    l = -1;
4404071d4279SBram Moolenaar 	}
4405071d4279SBram Moolenaar 	if (VIM_ISDIGIT(*s))
4406071d4279SBram Moolenaar 	{
4407071d4279SBram Moolenaar 	    minwid = (int)getdigits(&s);
4408c667da51SBram Moolenaar 	    if (minwid < 0)	// overflow
4409071d4279SBram Moolenaar 		minwid = 0;
4410071d4279SBram Moolenaar 	}
4411030f0dfaSBram Moolenaar 	if (*s == STL_USER_HL)
4412071d4279SBram Moolenaar 	{
44138133cc6bSBram Moolenaar 	    stl_items[curitem].stl_type = Highlight;
44148133cc6bSBram Moolenaar 	    stl_items[curitem].stl_start = p;
44158133cc6bSBram Moolenaar 	    stl_items[curitem].stl_minwid = minwid > 9 ? 1 : minwid;
4416071d4279SBram Moolenaar 	    s++;
4417071d4279SBram Moolenaar 	    curitem++;
4418071d4279SBram Moolenaar 	    continue;
4419071d4279SBram Moolenaar 	}
4420d1f56e68SBram Moolenaar 	if (*s == STL_TABPAGENR || *s == STL_TABCLOSENR)
4421d1f56e68SBram Moolenaar 	{
4422d1f56e68SBram Moolenaar 	    if (*s == STL_TABCLOSENR)
4423d1f56e68SBram Moolenaar 	    {
4424d1f56e68SBram Moolenaar 		if (minwid == 0)
4425d1f56e68SBram Moolenaar 		{
4426c667da51SBram Moolenaar 		    // %X ends the close label, go back to the previously
4427c667da51SBram Moolenaar 		    // define tab label nr.
4428d1f56e68SBram Moolenaar 		    for (n = curitem - 1; n >= 0; --n)
44298133cc6bSBram Moolenaar 			if (stl_items[n].stl_type == TabPage
44308133cc6bSBram Moolenaar 					       && stl_items[n].stl_minwid >= 0)
4431d1f56e68SBram Moolenaar 			{
44328133cc6bSBram Moolenaar 			    minwid = stl_items[n].stl_minwid;
4433d1f56e68SBram Moolenaar 			    break;
4434d1f56e68SBram Moolenaar 			}
4435d1f56e68SBram Moolenaar 		}
4436d1f56e68SBram Moolenaar 		else
4437c667da51SBram Moolenaar 		    // close nrs are stored as negative values
4438d1f56e68SBram Moolenaar 		    minwid = - minwid;
4439d1f56e68SBram Moolenaar 	    }
44408133cc6bSBram Moolenaar 	    stl_items[curitem].stl_type = TabPage;
44418133cc6bSBram Moolenaar 	    stl_items[curitem].stl_start = p;
44428133cc6bSBram Moolenaar 	    stl_items[curitem].stl_minwid = minwid;
4443d1f56e68SBram Moolenaar 	    s++;
4444d1f56e68SBram Moolenaar 	    curitem++;
4445d1f56e68SBram Moolenaar 	    continue;
4446d1f56e68SBram Moolenaar 	}
4447071d4279SBram Moolenaar 	if (*s == '.')
4448071d4279SBram Moolenaar 	{
4449071d4279SBram Moolenaar 	    s++;
4450071d4279SBram Moolenaar 	    if (VIM_ISDIGIT(*s))
4451071d4279SBram Moolenaar 	    {
4452071d4279SBram Moolenaar 		maxwid = (int)getdigits(&s);
4453c667da51SBram Moolenaar 		if (maxwid <= 0)	// overflow
4454071d4279SBram Moolenaar 		    maxwid = 50;
4455071d4279SBram Moolenaar 	    }
4456071d4279SBram Moolenaar 	}
4457071d4279SBram Moolenaar 	minwid = (minwid > 50 ? 50 : minwid) * l;
4458071d4279SBram Moolenaar 	if (*s == '(')
4459071d4279SBram Moolenaar 	{
44608133cc6bSBram Moolenaar 	    stl_groupitem[groupdepth++] = curitem;
44618133cc6bSBram Moolenaar 	    stl_items[curitem].stl_type = Group;
44628133cc6bSBram Moolenaar 	    stl_items[curitem].stl_start = p;
44638133cc6bSBram Moolenaar 	    stl_items[curitem].stl_minwid = minwid;
44648133cc6bSBram Moolenaar 	    stl_items[curitem].stl_maxwid = maxwid;
4465071d4279SBram Moolenaar 	    s++;
4466071d4279SBram Moolenaar 	    curitem++;
4467071d4279SBram Moolenaar 	    continue;
4468071d4279SBram Moolenaar 	}
446930e3de21Sshadmansaleh #ifdef FEAT_EVAL
447030e3de21Sshadmansaleh 	// Denotes end of expanded %{} block
447130e3de21Sshadmansaleh 	if (*s == '}' && evaldepth > 0)
447230e3de21Sshadmansaleh 	{
447330e3de21Sshadmansaleh 	    s++;
447430e3de21Sshadmansaleh 	    evaldepth--;
447530e3de21Sshadmansaleh 	    continue;
447630e3de21Sshadmansaleh 	}
447730e3de21Sshadmansaleh #endif
4478071d4279SBram Moolenaar 	if (vim_strchr(STL_ALL, *s) == NULL)
4479071d4279SBram Moolenaar 	{
4480071d4279SBram Moolenaar 	    s++;
4481071d4279SBram Moolenaar 	    continue;
4482071d4279SBram Moolenaar 	}
4483071d4279SBram Moolenaar 	opt = *s++;
4484071d4279SBram Moolenaar 
4485c667da51SBram Moolenaar 	// OK - now for the real work
4486071d4279SBram Moolenaar 	base = 'D';
4487071d4279SBram Moolenaar 	itemisflag = FALSE;
4488071d4279SBram Moolenaar 	fillable = TRUE;
4489071d4279SBram Moolenaar 	num = -1;
4490071d4279SBram Moolenaar 	str = NULL;
4491071d4279SBram Moolenaar 	switch (opt)
4492071d4279SBram Moolenaar 	{
4493071d4279SBram Moolenaar 	case STL_FILEPATH:
4494071d4279SBram Moolenaar 	case STL_FULLPATH:
4495071d4279SBram Moolenaar 	case STL_FILENAME:
4496c667da51SBram Moolenaar 	    fillable = FALSE;	// don't change ' ' to fillchar
4497071d4279SBram Moolenaar 	    if (buf_spname(wp->w_buffer) != NULL)
4498e1704badSBram Moolenaar 		vim_strncpy(NameBuff, buf_spname(wp->w_buffer), MAXPATHL - 1);
4499071d4279SBram Moolenaar 	    else
4500071d4279SBram Moolenaar 	    {
4501071d4279SBram Moolenaar 		t = (opt == STL_FULLPATH) ? wp->w_buffer->b_ffname
4502071d4279SBram Moolenaar 					  : wp->w_buffer->b_fname;
4503071d4279SBram Moolenaar 		home_replace(wp->w_buffer, t, NameBuff, MAXPATHL, TRUE);
4504071d4279SBram Moolenaar 	    }
4505071d4279SBram Moolenaar 	    trans_characters(NameBuff, MAXPATHL);
4506071d4279SBram Moolenaar 	    if (opt != STL_FILENAME)
4507071d4279SBram Moolenaar 		str = NameBuff;
4508071d4279SBram Moolenaar 	    else
4509071d4279SBram Moolenaar 		str = gettail(NameBuff);
4510071d4279SBram Moolenaar 	    break;
4511071d4279SBram Moolenaar 
4512c667da51SBram Moolenaar 	case STL_VIM_EXPR: // '{'
451330e3de21Sshadmansaleh 	{
451430e3de21Sshadmansaleh #ifdef FEAT_EVAL
451530e3de21Sshadmansaleh 	    char_u *block_start = s - 1;
451630e3de21Sshadmansaleh #endif
451730e3de21Sshadmansaleh 	    int reevaluate = (*s == '%');
451830e3de21Sshadmansaleh 
451930e3de21Sshadmansaleh 	    if (reevaluate)
452030e3de21Sshadmansaleh 		s++;
4521071d4279SBram Moolenaar 	    itemisflag = TRUE;
4522071d4279SBram Moolenaar 	    t = p;
452330e3de21Sshadmansaleh 	    while ((*s != '}' || (reevaluate && s[-1] != '%'))
452430e3de21Sshadmansaleh 					  && *s != NUL && p + 1 < out + outlen)
4525071d4279SBram Moolenaar 		*p++ = *s++;
4526c667da51SBram Moolenaar 	    if (*s != '}')	// missing '}' or out of space
4527071d4279SBram Moolenaar 		break;
4528071d4279SBram Moolenaar 	    s++;
452930e3de21Sshadmansaleh 	    if (reevaluate)
453030e3de21Sshadmansaleh 		p[-1] = 0; // remove the % at the end of %{% expr %}
453130e3de21Sshadmansaleh 	    else
4532071d4279SBram Moolenaar 		*p = 0;
4533071d4279SBram Moolenaar 	    p = t;
4534071d4279SBram Moolenaar #ifdef FEAT_EVAL
45351c6fd1e1SBram Moolenaar 	    vim_snprintf((char *)buf_tmp, sizeof(buf_tmp),
45361c6fd1e1SBram Moolenaar 							 "%d", curbuf->b_fnum);
45371c6fd1e1SBram Moolenaar 	    set_internal_string_var((char_u *)"g:actual_curbuf", buf_tmp);
45381c6fd1e1SBram Moolenaar 	    vim_snprintf((char *)win_tmp, sizeof(win_tmp), "%d", curwin->w_id);
45391c6fd1e1SBram Moolenaar 	    set_internal_string_var((char_u *)"g:actual_curwin", win_tmp);
4540071d4279SBram Moolenaar 
4541ba2929b6SBram Moolenaar 	    save_curbuf = curbuf;
4542ba2929b6SBram Moolenaar 	    save_curwin = curwin;
4543dee50a51SBram Moolenaar 	    save_VIsual_active = VIsual_active;
4544071d4279SBram Moolenaar 	    curwin = wp;
4545071d4279SBram Moolenaar 	    curbuf = wp->w_buffer;
4546dee50a51SBram Moolenaar 	    // Visual mode is only valid in the current window.
4547dee50a51SBram Moolenaar 	    if (curwin != save_curwin)
4548dee50a51SBram Moolenaar 		VIsual_active = FALSE;
4549071d4279SBram Moolenaar 
4550b171fb17SBram Moolenaar 	    str = eval_to_string_safe(p, use_sandbox);
4551071d4279SBram Moolenaar 
4552ba2929b6SBram Moolenaar 	    curwin = save_curwin;
4553ba2929b6SBram Moolenaar 	    curbuf = save_curbuf;
4554dee50a51SBram Moolenaar 	    VIsual_active = save_VIsual_active;
45550182465bSBram Moolenaar 	    do_unlet((char_u *)"g:actual_curbuf", TRUE);
45561c6fd1e1SBram Moolenaar 	    do_unlet((char_u *)"g:actual_curwin", TRUE);
4557071d4279SBram Moolenaar 
4558071d4279SBram Moolenaar 	    if (str != NULL && *str != 0)
4559071d4279SBram Moolenaar 	    {
4560071d4279SBram Moolenaar 		if (*skipdigits(str) == NUL)
4561071d4279SBram Moolenaar 		{
4562071d4279SBram Moolenaar 		    num = atoi((char *)str);
4563d23a8236SBram Moolenaar 		    VIM_CLEAR(str);
4564071d4279SBram Moolenaar 		    itemisflag = FALSE;
4565071d4279SBram Moolenaar 		}
4566071d4279SBram Moolenaar 	    }
456730e3de21Sshadmansaleh 
456830e3de21Sshadmansaleh 	    // If the output of the expression needs to be evaluated
456930e3de21Sshadmansaleh 	    // replace the %{} block with the result of evaluation
457030e3de21Sshadmansaleh 	    if (reevaluate && str != NULL && *str != 0
457130e3de21Sshadmansaleh 		    && strchr((const char *)str, '%') != NULL
457230e3de21Sshadmansaleh 		    && evaldepth < MAX_STL_EVAL_DEPTH)
457330e3de21Sshadmansaleh 	    {
457430e3de21Sshadmansaleh 		size_t parsed_usefmt = (size_t)(block_start - usefmt);
457530e3de21Sshadmansaleh 		size_t str_length = strlen((const char *)str);
457630e3de21Sshadmansaleh 		size_t fmt_length = strlen((const char *)s);
457730e3de21Sshadmansaleh 		size_t new_fmt_len = parsed_usefmt
457830e3de21Sshadmansaleh 						 + str_length + fmt_length + 3;
457930e3de21Sshadmansaleh 		char_u *new_fmt = (char_u *)alloc(new_fmt_len * sizeof(char_u));
458030e3de21Sshadmansaleh 		char_u *new_fmt_p = new_fmt;
458130e3de21Sshadmansaleh 
458230e3de21Sshadmansaleh 		new_fmt_p = (char_u *)memcpy(new_fmt_p, usefmt, parsed_usefmt)
458330e3de21Sshadmansaleh 							       + parsed_usefmt;
458430e3de21Sshadmansaleh 		new_fmt_p = (char_u *)memcpy(new_fmt_p , str, str_length)
458530e3de21Sshadmansaleh 								  + str_length;
458630e3de21Sshadmansaleh 		new_fmt_p = (char_u *)memcpy(new_fmt_p, "%}", 2) + 2;
458730e3de21Sshadmansaleh 		new_fmt_p = (char_u *)memcpy(new_fmt_p , s, fmt_length)
458830e3de21Sshadmansaleh 								  + fmt_length;
458930e3de21Sshadmansaleh 		*new_fmt_p = 0;
459030e3de21Sshadmansaleh 		new_fmt_p = NULL;
459130e3de21Sshadmansaleh 
459230e3de21Sshadmansaleh 		if (usefmt != fmt)
459330e3de21Sshadmansaleh 		    vim_free(usefmt);
459430e3de21Sshadmansaleh 		VIM_CLEAR(str);
459530e3de21Sshadmansaleh 		usefmt = new_fmt;
459630e3de21Sshadmansaleh 		s = usefmt + parsed_usefmt;
459730e3de21Sshadmansaleh 		evaldepth++;
459830e3de21Sshadmansaleh 		continue;
459930e3de21Sshadmansaleh 	    }
4600071d4279SBram Moolenaar #endif
4601071d4279SBram Moolenaar 	    break;
460230e3de21Sshadmansaleh 	}
4603071d4279SBram Moolenaar 	case STL_LINE:
4604071d4279SBram Moolenaar 	    num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY)
4605071d4279SBram Moolenaar 		  ? 0L : (long)(wp->w_cursor.lnum);
4606071d4279SBram Moolenaar 	    break;
4607071d4279SBram Moolenaar 
4608071d4279SBram Moolenaar 	case STL_NUMLINES:
4609071d4279SBram Moolenaar 	    num = wp->w_buffer->b_ml.ml_line_count;
4610071d4279SBram Moolenaar 	    break;
4611071d4279SBram Moolenaar 
4612071d4279SBram Moolenaar 	case STL_COLUMN:
4613071d4279SBram Moolenaar 	    num = !(State & INSERT) && empty_line
4614071d4279SBram Moolenaar 		  ? 0 : (int)wp->w_cursor.col + 1;
4615071d4279SBram Moolenaar 	    break;
4616071d4279SBram Moolenaar 
4617071d4279SBram Moolenaar 	case STL_VIRTCOL:
4618071d4279SBram Moolenaar 	case STL_VIRTCOL_ALT:
4619c667da51SBram Moolenaar 	    // In list mode virtcol needs to be recomputed
4620071d4279SBram Moolenaar 	    virtcol = wp->w_virtcol;
4621eed9d462SBram Moolenaar 	    if (wp->w_p_list && wp->w_lcs_chars.tab1 == NUL)
4622071d4279SBram Moolenaar 	    {
4623071d4279SBram Moolenaar 		wp->w_p_list = FALSE;
4624071d4279SBram Moolenaar 		getvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL);
4625071d4279SBram Moolenaar 		wp->w_p_list = TRUE;
4626071d4279SBram Moolenaar 	    }
4627071d4279SBram Moolenaar 	    ++virtcol;
4628c667da51SBram Moolenaar 	    // Don't display %V if it's the same as %c.
4629071d4279SBram Moolenaar 	    if (opt == STL_VIRTCOL_ALT
4630071d4279SBram Moolenaar 		    && (virtcol == (colnr_T)(!(State & INSERT) && empty_line
4631071d4279SBram Moolenaar 			    ? 0 : (int)wp->w_cursor.col + 1)))
4632071d4279SBram Moolenaar 		break;
4633071d4279SBram Moolenaar 	    num = (long)virtcol;
4634071d4279SBram Moolenaar 	    break;
4635071d4279SBram Moolenaar 
4636071d4279SBram Moolenaar 	case STL_PERCENTAGE:
4637071d4279SBram Moolenaar 	    num = (int)(((long)wp->w_cursor.lnum * 100L) /
4638071d4279SBram Moolenaar 			(long)wp->w_buffer->b_ml.ml_line_count);
4639071d4279SBram Moolenaar 	    break;
4640071d4279SBram Moolenaar 
4641071d4279SBram Moolenaar 	case STL_ALTPERCENT:
46421c6fd1e1SBram Moolenaar 	    str = buf_tmp;
46430ab2a887SBram Moolenaar 	    get_rel_pos(wp, str, TMPLEN);
4644071d4279SBram Moolenaar 	    break;
4645071d4279SBram Moolenaar 
4646071d4279SBram Moolenaar 	case STL_ARGLISTSTAT:
4647071d4279SBram Moolenaar 	    fillable = FALSE;
46481c6fd1e1SBram Moolenaar 	    buf_tmp[0] = 0;
46491c6fd1e1SBram Moolenaar 	    if (append_arg_number(wp, buf_tmp, (int)sizeof(buf_tmp), FALSE))
46501c6fd1e1SBram Moolenaar 		str = buf_tmp;
4651071d4279SBram Moolenaar 	    break;
4652071d4279SBram Moolenaar 
4653071d4279SBram Moolenaar 	case STL_KEYMAP:
4654071d4279SBram Moolenaar 	    fillable = FALSE;
46551c6fd1e1SBram Moolenaar 	    if (get_keymap_str(wp, (char_u *)"<%s>", buf_tmp, TMPLEN))
46561c6fd1e1SBram Moolenaar 		str = buf_tmp;
4657071d4279SBram Moolenaar 	    break;
4658071d4279SBram Moolenaar 	case STL_PAGENUM:
4659bfb2d40bSBram Moolenaar #if defined(FEAT_PRINTER) || defined(FEAT_GUI_TABLINE)
4660ba6c0524SBram Moolenaar 	    num = printer_page_num;
4661071d4279SBram Moolenaar #else
4662071d4279SBram Moolenaar 	    num = 0;
4663071d4279SBram Moolenaar #endif
4664071d4279SBram Moolenaar 	    break;
4665071d4279SBram Moolenaar 
4666071d4279SBram Moolenaar 	case STL_BUFNO:
4667071d4279SBram Moolenaar 	    num = wp->w_buffer->b_fnum;
4668071d4279SBram Moolenaar 	    break;
4669071d4279SBram Moolenaar 
4670071d4279SBram Moolenaar 	case STL_OFFSET_X:
4671071d4279SBram Moolenaar 	    base = 'X';
4672c667da51SBram Moolenaar 	    // FALLTHROUGH
4673071d4279SBram Moolenaar 	case STL_OFFSET:
4674071d4279SBram Moolenaar #ifdef FEAT_BYTEOFF
4675071d4279SBram Moolenaar 	    l = ml_find_line_or_offset(wp->w_buffer, wp->w_cursor.lnum, NULL);
4676071d4279SBram Moolenaar 	    num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) || l < 0 ?
4677071d4279SBram Moolenaar 		  0L : l + 1 + (!(State & INSERT) && empty_line ?
4678071d4279SBram Moolenaar 				0 : (int)wp->w_cursor.col);
4679071d4279SBram Moolenaar #endif
4680071d4279SBram Moolenaar 	    break;
4681071d4279SBram Moolenaar 
4682071d4279SBram Moolenaar 	case STL_BYTEVAL_X:
4683071d4279SBram Moolenaar 	    base = 'X';
4684c667da51SBram Moolenaar 	    // FALLTHROUGH
4685071d4279SBram Moolenaar 	case STL_BYTEVAL:
4686567199b6SBram Moolenaar 	    num = byteval;
4687071d4279SBram Moolenaar 	    if (num == NL)
4688071d4279SBram Moolenaar 		num = 0;
4689071d4279SBram Moolenaar 	    else if (num == CAR && get_fileformat(wp->w_buffer) == EOL_MAC)
4690071d4279SBram Moolenaar 		num = NL;
4691071d4279SBram Moolenaar 	    break;
4692071d4279SBram Moolenaar 
4693071d4279SBram Moolenaar 	case STL_ROFLAG:
4694071d4279SBram Moolenaar 	case STL_ROFLAG_ALT:
4695071d4279SBram Moolenaar 	    itemisflag = TRUE;
4696071d4279SBram Moolenaar 	    if (wp->w_buffer->b_p_ro)
469723584033SBram Moolenaar 		str = (char_u *)((opt == STL_ROFLAG_ALT) ? ",RO" : _("[RO]"));
4698071d4279SBram Moolenaar 	    break;
4699071d4279SBram Moolenaar 
4700071d4279SBram Moolenaar 	case STL_HELPFLAG:
4701071d4279SBram Moolenaar 	case STL_HELPFLAG_ALT:
4702071d4279SBram Moolenaar 	    itemisflag = TRUE;
4703071d4279SBram Moolenaar 	    if (wp->w_buffer->b_help)
4704071d4279SBram Moolenaar 		str = (char_u *)((opt == STL_HELPFLAG_ALT) ? ",HLP"
4705899dddf8SBram Moolenaar 							       : _("[Help]"));
4706071d4279SBram Moolenaar 	    break;
4707071d4279SBram Moolenaar 
4708071d4279SBram Moolenaar 	case STL_FILETYPE:
4709071d4279SBram Moolenaar 	    if (*wp->w_buffer->b_p_ft != NUL
4710071d4279SBram Moolenaar 		    && STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 3)
4711071d4279SBram Moolenaar 	    {
47121c6fd1e1SBram Moolenaar 		vim_snprintf((char *)buf_tmp, sizeof(buf_tmp), "[%s]",
47139c13b359SBram Moolenaar 							wp->w_buffer->b_p_ft);
47141c6fd1e1SBram Moolenaar 		str = buf_tmp;
4715071d4279SBram Moolenaar 	    }
4716071d4279SBram Moolenaar 	    break;
4717071d4279SBram Moolenaar 
4718071d4279SBram Moolenaar 	case STL_FILETYPE_ALT:
4719071d4279SBram Moolenaar 	    itemisflag = TRUE;
4720071d4279SBram Moolenaar 	    if (*wp->w_buffer->b_p_ft != NUL
4721071d4279SBram Moolenaar 		    && STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 2)
4722071d4279SBram Moolenaar 	    {
47231c6fd1e1SBram Moolenaar 		vim_snprintf((char *)buf_tmp, sizeof(buf_tmp), ",%s",
47249c13b359SBram Moolenaar 							wp->w_buffer->b_p_ft);
47251c6fd1e1SBram Moolenaar 		for (t = buf_tmp; *t != 0; t++)
4726071d4279SBram Moolenaar 		    *t = TOUPPER_LOC(*t);
47271c6fd1e1SBram Moolenaar 		str = buf_tmp;
4728071d4279SBram Moolenaar 	    }
4729071d4279SBram Moolenaar 	    break;
4730071d4279SBram Moolenaar 
47314033c55eSBram Moolenaar #if defined(FEAT_QUICKFIX)
4732071d4279SBram Moolenaar 	case STL_PREVIEWFLAG:
4733071d4279SBram Moolenaar 	case STL_PREVIEWFLAG_ALT:
4734071d4279SBram Moolenaar 	    itemisflag = TRUE;
4735071d4279SBram Moolenaar 	    if (wp->w_p_pvw)
4736071d4279SBram Moolenaar 		str = (char_u *)((opt == STL_PREVIEWFLAG_ALT) ? ",PRV"
4737071d4279SBram Moolenaar 							    : _("[Preview]"));
4738071d4279SBram Moolenaar 	    break;
47397fd73200SBram Moolenaar 
47407fd73200SBram Moolenaar 	case STL_QUICKFIX:
47417fd73200SBram Moolenaar 	    if (bt_quickfix(wp->w_buffer))
47427fd73200SBram Moolenaar 		str = (char_u *)(wp->w_llist_ref
47437fd73200SBram Moolenaar 			    ? _(msg_loclist)
47447fd73200SBram Moolenaar 			    : _(msg_qflist));
47457fd73200SBram Moolenaar 	    break;
4746071d4279SBram Moolenaar #endif
4747071d4279SBram Moolenaar 
4748071d4279SBram Moolenaar 	case STL_MODIFIED:
4749071d4279SBram Moolenaar 	case STL_MODIFIED_ALT:
4750071d4279SBram Moolenaar 	    itemisflag = TRUE;
4751071d4279SBram Moolenaar 	    switch ((opt == STL_MODIFIED_ALT)
4752071d4279SBram Moolenaar 		    + bufIsChanged(wp->w_buffer) * 2
4753071d4279SBram Moolenaar 		    + (!wp->w_buffer->b_p_ma) * 4)
4754071d4279SBram Moolenaar 	    {
4755071d4279SBram Moolenaar 		case 2: str = (char_u *)"[+]"; break;
4756071d4279SBram Moolenaar 		case 3: str = (char_u *)",+"; break;
4757071d4279SBram Moolenaar 		case 4: str = (char_u *)"[-]"; break;
4758071d4279SBram Moolenaar 		case 5: str = (char_u *)",-"; break;
4759071d4279SBram Moolenaar 		case 6: str = (char_u *)"[+-]"; break;
4760071d4279SBram Moolenaar 		case 7: str = (char_u *)",+-"; break;
4761071d4279SBram Moolenaar 	    }
4762071d4279SBram Moolenaar 	    break;
4763030f0dfaSBram Moolenaar 
4764030f0dfaSBram Moolenaar 	case STL_HIGHLIGHT:
4765030f0dfaSBram Moolenaar 	    t = s;
4766030f0dfaSBram Moolenaar 	    while (*s != '#' && *s != NUL)
4767030f0dfaSBram Moolenaar 		++s;
4768030f0dfaSBram Moolenaar 	    if (*s == '#')
4769030f0dfaSBram Moolenaar 	    {
47708133cc6bSBram Moolenaar 		stl_items[curitem].stl_type = Highlight;
47718133cc6bSBram Moolenaar 		stl_items[curitem].stl_start = p;
47728133cc6bSBram Moolenaar 		stl_items[curitem].stl_minwid = -syn_namen2id(t, (int)(s - t));
4773030f0dfaSBram Moolenaar 		curitem++;
4774030f0dfaSBram Moolenaar 	    }
477511808226SBram Moolenaar 	    if (*s != NUL)
4776030f0dfaSBram Moolenaar 		++s;
4777030f0dfaSBram Moolenaar 	    continue;
4778071d4279SBram Moolenaar 	}
4779071d4279SBram Moolenaar 
47808133cc6bSBram Moolenaar 	stl_items[curitem].stl_start = p;
47818133cc6bSBram Moolenaar 	stl_items[curitem].stl_type = Normal;
4782071d4279SBram Moolenaar 	if (str != NULL && *str)
4783071d4279SBram Moolenaar 	{
4784071d4279SBram Moolenaar 	    t = str;
4785071d4279SBram Moolenaar 	    if (itemisflag)
4786071d4279SBram Moolenaar 	    {
4787071d4279SBram Moolenaar 		if ((t[0] && t[1])
4788071d4279SBram Moolenaar 			&& ((!prevchar_isitem && *t == ',')
4789071d4279SBram Moolenaar 			      || (prevchar_isflag && *t == ' ')))
4790071d4279SBram Moolenaar 		    t++;
4791071d4279SBram Moolenaar 		prevchar_isflag = TRUE;
4792071d4279SBram Moolenaar 	    }
4793071d4279SBram Moolenaar 	    l = vim_strsize(t);
4794071d4279SBram Moolenaar 	    if (l > 0)
4795071d4279SBram Moolenaar 		prevchar_isitem = TRUE;
4796071d4279SBram Moolenaar 	    if (l > maxwid)
4797071d4279SBram Moolenaar 	    {
4798071d4279SBram Moolenaar 		while (l >= maxwid)
4799071d4279SBram Moolenaar 		    if (has_mbyte)
4800071d4279SBram Moolenaar 		    {
4801071d4279SBram Moolenaar 			l -= ptr2cells(t);
48020fa313a7SBram Moolenaar 			t += (*mb_ptr2len)(t);
4803071d4279SBram Moolenaar 		    }
4804071d4279SBram Moolenaar 		    else
4805071d4279SBram Moolenaar 			l -= byte2cells(*t++);
4806071d4279SBram Moolenaar 		if (p + 1 >= out + outlen)
4807071d4279SBram Moolenaar 		    break;
4808071d4279SBram Moolenaar 		*p++ = '<';
4809071d4279SBram Moolenaar 	    }
4810071d4279SBram Moolenaar 	    if (minwid > 0)
4811071d4279SBram Moolenaar 	    {
4812071d4279SBram Moolenaar 		for (; l < minwid && p + 1 < out + outlen; l++)
4813071d4279SBram Moolenaar 		{
4814c667da51SBram Moolenaar 		    // Don't put a "-" in front of a digit.
4815071d4279SBram Moolenaar 		    if (l + 1 == minwid && fillchar == '-' && VIM_ISDIGIT(*t))
4816071d4279SBram Moolenaar 			*p++ = ' ';
4817071d4279SBram Moolenaar 		    else
4818008bff96SBram Moolenaar 			MB_CHAR2BYTES(fillchar, p);
4819071d4279SBram Moolenaar 		}
4820071d4279SBram Moolenaar 		minwid = 0;
4821071d4279SBram Moolenaar 	    }
4822071d4279SBram Moolenaar 	    else
4823071d4279SBram Moolenaar 		minwid *= -1;
4824008bff96SBram Moolenaar 	    for (; *t && p + 1 < out + outlen; t++)
4825071d4279SBram Moolenaar 	    {
4826c667da51SBram Moolenaar 		// Change a space by fillchar, unless fillchar is '-' and a
4827c667da51SBram Moolenaar 		// digit follows.
4828008bff96SBram Moolenaar 		if (fillable && *t == ' '
4829008bff96SBram Moolenaar 				&& (!VIM_ISDIGIT(*(t + 1)) || fillchar != '-'))
4830008bff96SBram Moolenaar 		    MB_CHAR2BYTES(fillchar, p);
4831008bff96SBram Moolenaar 		else
4832008bff96SBram Moolenaar 		    *p++ = *t;
4833071d4279SBram Moolenaar 	    }
4834071d4279SBram Moolenaar 	    for (; l < minwid && p + 1 < out + outlen; l++)
4835008bff96SBram Moolenaar 		MB_CHAR2BYTES(fillchar, p);
4836071d4279SBram Moolenaar 	}
4837071d4279SBram Moolenaar 	else if (num >= 0)
4838071d4279SBram Moolenaar 	{
4839071d4279SBram Moolenaar 	    int nbase = (base == 'D' ? 10 : (base == 'O' ? 8 : 16));
4840071d4279SBram Moolenaar 	    char_u nstr[20];
4841071d4279SBram Moolenaar 
4842071d4279SBram Moolenaar 	    if (p + 20 >= out + outlen)
4843c667da51SBram Moolenaar 		break;		// not sufficient space
4844071d4279SBram Moolenaar 	    prevchar_isitem = TRUE;
4845071d4279SBram Moolenaar 	    t = nstr;
4846071d4279SBram Moolenaar 	    if (opt == STL_VIRTCOL_ALT)
4847071d4279SBram Moolenaar 	    {
4848071d4279SBram Moolenaar 		*t++ = '-';
4849071d4279SBram Moolenaar 		minwid--;
4850071d4279SBram Moolenaar 	    }
4851071d4279SBram Moolenaar 	    *t++ = '%';
4852071d4279SBram Moolenaar 	    if (zeropad)
4853071d4279SBram Moolenaar 		*t++ = '0';
4854071d4279SBram Moolenaar 	    *t++ = '*';
48550ab2a887SBram Moolenaar 	    *t++ = nbase == 16 ? base : (char_u)(nbase == 8 ? 'o' : 'd');
4856071d4279SBram Moolenaar 	    *t = 0;
4857071d4279SBram Moolenaar 
4858071d4279SBram Moolenaar 	    for (n = num, l = 1; n >= nbase; n /= nbase)
4859071d4279SBram Moolenaar 		l++;
4860071d4279SBram Moolenaar 	    if (opt == STL_VIRTCOL_ALT)
4861071d4279SBram Moolenaar 		l++;
4862071d4279SBram Moolenaar 	    if (l > maxwid)
4863071d4279SBram Moolenaar 	    {
4864071d4279SBram Moolenaar 		l += 2;
4865071d4279SBram Moolenaar 		n = l - maxwid;
4866071d4279SBram Moolenaar 		while (l-- > maxwid)
4867071d4279SBram Moolenaar 		    num /= nbase;
4868071d4279SBram Moolenaar 		*t++ = '>';
4869071d4279SBram Moolenaar 		*t++ = '%';
4870071d4279SBram Moolenaar 		*t = t[-3];
4871071d4279SBram Moolenaar 		*++t = 0;
48729c13b359SBram Moolenaar 		vim_snprintf((char *)p, outlen - (p - out), (char *)nstr,
48739c13b359SBram Moolenaar 								   0, num, n);
4874071d4279SBram Moolenaar 	    }
4875071d4279SBram Moolenaar 	    else
48769c13b359SBram Moolenaar 		vim_snprintf((char *)p, outlen - (p - out), (char *)nstr,
48779c13b359SBram Moolenaar 								 minwid, num);
4878071d4279SBram Moolenaar 	    p += STRLEN(p);
4879071d4279SBram Moolenaar 	}
4880071d4279SBram Moolenaar 	else
48818133cc6bSBram Moolenaar 	    stl_items[curitem].stl_type = Empty;
4882071d4279SBram Moolenaar 
4883071d4279SBram Moolenaar 	if (opt == STL_VIM_EXPR)
4884071d4279SBram Moolenaar 	    vim_free(str);
4885071d4279SBram Moolenaar 
4886071d4279SBram Moolenaar 	if (num >= 0 || (!itemisflag && str && *str))
4887c667da51SBram Moolenaar 	    prevchar_isflag = FALSE;	    // Item not NULL, but not a flag
4888071d4279SBram Moolenaar 	curitem++;
4889071d4279SBram Moolenaar     }
4890071d4279SBram Moolenaar     *p = NUL;
4891071d4279SBram Moolenaar     itemcnt = curitem;
4892071d4279SBram Moolenaar 
4893030f0dfaSBram Moolenaar #ifdef FEAT_EVAL
4894030f0dfaSBram Moolenaar     if (usefmt != fmt)
4895030f0dfaSBram Moolenaar 	vim_free(usefmt);
4896030f0dfaSBram Moolenaar #endif
4897030f0dfaSBram Moolenaar 
4898071d4279SBram Moolenaar     width = vim_strsize(out);
4899071d4279SBram Moolenaar     if (maxwidth > 0 && width > maxwidth)
4900071d4279SBram Moolenaar     {
4901c667da51SBram Moolenaar 	// Result is too long, must truncate somewhere.
4902071d4279SBram Moolenaar 	l = 0;
4903071d4279SBram Moolenaar 	if (itemcnt == 0)
4904071d4279SBram Moolenaar 	    s = out;
4905071d4279SBram Moolenaar 	else
4906071d4279SBram Moolenaar 	{
4907071d4279SBram Moolenaar 	    for ( ; l < itemcnt; l++)
49088133cc6bSBram Moolenaar 		if (stl_items[l].stl_type == Trunc)
4909071d4279SBram Moolenaar 		{
4910c667da51SBram Moolenaar 		    // Truncate at %< item.
49118133cc6bSBram Moolenaar 		    s = stl_items[l].stl_start;
4912071d4279SBram Moolenaar 		    break;
4913071d4279SBram Moolenaar 		}
4914071d4279SBram Moolenaar 	    if (l == itemcnt)
4915071d4279SBram Moolenaar 	    {
4916c667da51SBram Moolenaar 		// No %< item, truncate first item.
49178133cc6bSBram Moolenaar 		s = stl_items[0].stl_start;
4918071d4279SBram Moolenaar 		l = 0;
4919071d4279SBram Moolenaar 	    }
4920071d4279SBram Moolenaar 	}
4921071d4279SBram Moolenaar 
4922071d4279SBram Moolenaar 	if (width - vim_strsize(s) >= maxwidth)
4923071d4279SBram Moolenaar 	{
4924c667da51SBram Moolenaar 	    // Truncation mark is beyond max length
4925071d4279SBram Moolenaar 	    if (has_mbyte)
4926071d4279SBram Moolenaar 	    {
4927071d4279SBram Moolenaar 		s = out;
4928071d4279SBram Moolenaar 		width = 0;
4929071d4279SBram Moolenaar 		for (;;)
4930071d4279SBram Moolenaar 		{
4931071d4279SBram Moolenaar 		    width += ptr2cells(s);
4932071d4279SBram Moolenaar 		    if (width >= maxwidth)
4933071d4279SBram Moolenaar 			break;
49340fa313a7SBram Moolenaar 		    s += (*mb_ptr2len)(s);
4935071d4279SBram Moolenaar 		}
4936c667da51SBram Moolenaar 		// Fill up for half a double-wide character.
4937071d4279SBram Moolenaar 		while (++width < maxwidth)
4938008bff96SBram Moolenaar 		    MB_CHAR2BYTES(fillchar, s);
4939071d4279SBram Moolenaar 	    }
4940071d4279SBram Moolenaar 	    else
4941071d4279SBram Moolenaar 		s = out + maxwidth - 1;
4942071d4279SBram Moolenaar 	    for (l = 0; l < itemcnt; l++)
49438133cc6bSBram Moolenaar 		if (stl_items[l].stl_start > s)
4944071d4279SBram Moolenaar 		    break;
4945071d4279SBram Moolenaar 	    itemcnt = l;
4946071d4279SBram Moolenaar 	    *s++ = '>';
4947071d4279SBram Moolenaar 	    *s = 0;
4948071d4279SBram Moolenaar 	}
4949071d4279SBram Moolenaar 	else
4950071d4279SBram Moolenaar 	{
4951071d4279SBram Moolenaar 	    if (has_mbyte)
4952071d4279SBram Moolenaar 	    {
4953071d4279SBram Moolenaar 		n = 0;
4954071d4279SBram Moolenaar 		while (width >= maxwidth)
4955071d4279SBram Moolenaar 		{
4956071d4279SBram Moolenaar 		    width -= ptr2cells(s + n);
49570fa313a7SBram Moolenaar 		    n += (*mb_ptr2len)(s + n);
4958071d4279SBram Moolenaar 		}
4959071d4279SBram Moolenaar 	    }
4960071d4279SBram Moolenaar 	    else
4961071d4279SBram Moolenaar 		n = width - maxwidth + 1;
4962071d4279SBram Moolenaar 	    p = s + n;
4963f233048aSBram Moolenaar 	    STRMOVE(s + 1, p);
4964071d4279SBram Moolenaar 	    *s = '<';
4965071d4279SBram Moolenaar 
4966c667da51SBram Moolenaar 	    // Fill up for half a double-wide character.
4967071d4279SBram Moolenaar 	    while (++width < maxwidth)
4968071d4279SBram Moolenaar 	    {
4969071d4279SBram Moolenaar 		s = s + STRLEN(s);
4970008bff96SBram Moolenaar 		MB_CHAR2BYTES(fillchar, s);
4971071d4279SBram Moolenaar 		*s = NUL;
4972071d4279SBram Moolenaar 	    }
4973071d4279SBram Moolenaar 
4974c667da51SBram Moolenaar 	    --n;	// count the '<'
4975071d4279SBram Moolenaar 	    for (; l < itemcnt; l++)
4976071d4279SBram Moolenaar 	    {
49778133cc6bSBram Moolenaar 		if (stl_items[l].stl_start - n >= s)
49788133cc6bSBram Moolenaar 		    stl_items[l].stl_start -= n;
4979071d4279SBram Moolenaar 		else
49808133cc6bSBram Moolenaar 		    stl_items[l].stl_start = s;
4981071d4279SBram Moolenaar 	    }
4982071d4279SBram Moolenaar 	}
4983071d4279SBram Moolenaar 	width = maxwidth;
4984071d4279SBram Moolenaar     }
4985071d4279SBram Moolenaar     else if (width < maxwidth && STRLEN(out) + maxwidth - width + 1 < outlen)
4986071d4279SBram Moolenaar     {
4987c667da51SBram Moolenaar 	// Apply STL_MIDDLE if any
4988071d4279SBram Moolenaar 	for (l = 0; l < itemcnt; l++)
49898133cc6bSBram Moolenaar 	    if (stl_items[l].stl_type == Middle)
4990071d4279SBram Moolenaar 		break;
4991071d4279SBram Moolenaar 	if (l < itemcnt)
4992071d4279SBram Moolenaar 	{
4993008bff96SBram Moolenaar 	    int middlelength = (maxwidth - width) * MB_CHAR2LEN(fillchar);
4994008bff96SBram Moolenaar 	    p = stl_items[l].stl_start + middlelength;
49958133cc6bSBram Moolenaar 	    STRMOVE(p, stl_items[l].stl_start);
4996008bff96SBram Moolenaar 	    for (s = stl_items[l].stl_start; s < p;)
4997008bff96SBram Moolenaar 		MB_CHAR2BYTES(fillchar, s);
4998071d4279SBram Moolenaar 	    for (l++; l < itemcnt; l++)
4999008bff96SBram Moolenaar 		stl_items[l].stl_start += middlelength;
5000071d4279SBram Moolenaar 	    width = maxwidth;
5001071d4279SBram Moolenaar 	}
5002071d4279SBram Moolenaar     }
5003071d4279SBram Moolenaar 
5004c667da51SBram Moolenaar     // Store the info about highlighting.
5005d1f56e68SBram Moolenaar     if (hltab != NULL)
5006071d4279SBram Moolenaar     {
50078133cc6bSBram Moolenaar 	*hltab = stl_hltab;
50088133cc6bSBram Moolenaar 	sp = stl_hltab;
5009071d4279SBram Moolenaar 	for (l = 0; l < itemcnt; l++)
5010071d4279SBram Moolenaar 	{
50118133cc6bSBram Moolenaar 	    if (stl_items[l].stl_type == Highlight)
5012071d4279SBram Moolenaar 	    {
50138133cc6bSBram Moolenaar 		sp->start = stl_items[l].stl_start;
50148133cc6bSBram Moolenaar 		sp->userhl = stl_items[l].stl_minwid;
5015d1f56e68SBram Moolenaar 		sp++;
5016071d4279SBram Moolenaar 	    }
5017071d4279SBram Moolenaar 	}
5018d1f56e68SBram Moolenaar 	sp->start = NULL;
5019d1f56e68SBram Moolenaar 	sp->userhl = 0;
5020d1f56e68SBram Moolenaar     }
5021d1f56e68SBram Moolenaar 
5022c667da51SBram Moolenaar     // Store the info about tab pages labels.
5023d1f56e68SBram Moolenaar     if (tabtab != NULL)
5024d1f56e68SBram Moolenaar     {
50258133cc6bSBram Moolenaar 	*tabtab = stl_tabtab;
50268133cc6bSBram Moolenaar 	sp = stl_tabtab;
5027d1f56e68SBram Moolenaar 	for (l = 0; l < itemcnt; l++)
5028d1f56e68SBram Moolenaar 	{
50298133cc6bSBram Moolenaar 	    if (stl_items[l].stl_type == TabPage)
5030d1f56e68SBram Moolenaar 	    {
50318133cc6bSBram Moolenaar 		sp->start = stl_items[l].stl_start;
50328133cc6bSBram Moolenaar 		sp->userhl = stl_items[l].stl_minwid;
5033d1f56e68SBram Moolenaar 		sp++;
5034d1f56e68SBram Moolenaar 	    }
5035d1f56e68SBram Moolenaar 	}
5036d1f56e68SBram Moolenaar 	sp->start = NULL;
5037d1f56e68SBram Moolenaar 	sp->userhl = 0;
5038071d4279SBram Moolenaar     }
5039071d4279SBram Moolenaar 
5040c667da51SBram Moolenaar     // When inside update_screen we do not want redrawing a stausline, ruler,
5041c667da51SBram Moolenaar     // title, etc. to trigger another redraw, it may cause an endless loop.
504265ed1368SBram Moolenaar     if (updating_screen)
504365ed1368SBram Moolenaar     {
5044ba2929b6SBram Moolenaar 	must_redraw = save_must_redraw;
5045ba2929b6SBram Moolenaar 	curwin->w_redr_type = save_redr_type;
504665ed1368SBram Moolenaar     }
5047ba2929b6SBram Moolenaar 
5048071d4279SBram Moolenaar     return width;
5049071d4279SBram Moolenaar }
5050c667da51SBram Moolenaar #endif // FEAT_STL_OPT
5051071d4279SBram Moolenaar 
5052ba6c0524SBram Moolenaar #if defined(FEAT_STL_OPT) || defined(FEAT_CMDL_INFO) \
5053ba6c0524SBram Moolenaar 	    || defined(FEAT_GUI_TABLINE) || defined(PROTO)
5054071d4279SBram Moolenaar /*
50550ab2a887SBram Moolenaar  * Get relative cursor position in window into "buf[buflen]", in the form 99%,
50560ab2a887SBram Moolenaar  * using "Top", "Bot" or "All" when appropriate.
5057071d4279SBram Moolenaar  */
5058071d4279SBram Moolenaar     void
get_rel_pos(win_T * wp,char_u * buf,int buflen)50597454a06eSBram Moolenaar get_rel_pos(
50607454a06eSBram Moolenaar     win_T	*wp,
50617454a06eSBram Moolenaar     char_u	*buf,
50627454a06eSBram Moolenaar     int		buflen)
5063071d4279SBram Moolenaar {
5064c667da51SBram Moolenaar     long	above; // number of lines above window
5065c667da51SBram Moolenaar     long	below; // number of lines below window
5066071d4279SBram Moolenaar 
5067c667da51SBram Moolenaar     if (buflen < 3) // need at least 3 chars for writing
50680027c218SBram Moolenaar 	return;
5069071d4279SBram Moolenaar     above = wp->w_topline - 1;
5070071d4279SBram Moolenaar #ifdef FEAT_DIFF
5071071d4279SBram Moolenaar     above += diff_check_fill(wp, wp->w_topline) - wp->w_topfill;
507229bc9db3SBram Moolenaar     if (wp->w_topline == 1 && wp->w_topfill >= 1)
5073c667da51SBram Moolenaar 	above = 0;  // All buffer lines are displayed and there is an
5074c667da51SBram Moolenaar 		    // indication of filler lines, that can be considered
5075c667da51SBram Moolenaar 		    // seeing all lines.
5076071d4279SBram Moolenaar #endif
5077071d4279SBram Moolenaar     below = wp->w_buffer->b_ml.ml_line_count - wp->w_botline + 1;
5078071d4279SBram Moolenaar     if (below <= 0)
50790ab2a887SBram Moolenaar 	vim_strncpy(buf, (char_u *)(above == 0 ? _("All") : _("Bot")),
50800ab2a887SBram Moolenaar 							(size_t)(buflen - 1));
5081071d4279SBram Moolenaar     else if (above <= 0)
50820ab2a887SBram Moolenaar 	vim_strncpy(buf, (char_u *)_("Top"), (size_t)(buflen - 1));
5083071d4279SBram Moolenaar     else
50840ab2a887SBram Moolenaar 	vim_snprintf((char *)buf, (size_t)buflen, "%2d%%", above > 1000000L
5085071d4279SBram Moolenaar 				    ? (int)(above / ((above + below) / 100L))
5086071d4279SBram Moolenaar 				    : (int)(above * 100L / (above + below)));
5087071d4279SBram Moolenaar }
5088071d4279SBram Moolenaar #endif
5089071d4279SBram Moolenaar 
5090071d4279SBram Moolenaar /*
50910ab2a887SBram Moolenaar  * Append (file 2 of 8) to "buf[buflen]", if editing more than one file.
5092071d4279SBram Moolenaar  * Return TRUE if it was appended.
5093071d4279SBram Moolenaar  */
50940ab2a887SBram Moolenaar     static int
append_arg_number(win_T * wp,char_u * buf,int buflen,int add_file)50957454a06eSBram Moolenaar append_arg_number(
50967454a06eSBram Moolenaar     win_T	*wp,
50977454a06eSBram Moolenaar     char_u	*buf,
50987454a06eSBram Moolenaar     int		buflen,
5099c667da51SBram Moolenaar     int		add_file)	// Add "file" before the arg number
5100071d4279SBram Moolenaar {
5101071d4279SBram Moolenaar     char_u	*p;
5102071d4279SBram Moolenaar 
5103c667da51SBram Moolenaar     if (ARGCOUNT <= 1)		// nothing to do
5104071d4279SBram Moolenaar 	return FALSE;
5105071d4279SBram Moolenaar 
5106c667da51SBram Moolenaar     p = buf + STRLEN(buf);	// go to the end of the buffer
5107c667da51SBram Moolenaar     if (p - buf + 35 >= buflen)	// getting too long
5108071d4279SBram Moolenaar 	return FALSE;
5109071d4279SBram Moolenaar     *p++ = ' ';
5110071d4279SBram Moolenaar     *p++ = '(';
5111071d4279SBram Moolenaar     if (add_file)
5112071d4279SBram Moolenaar     {
5113071d4279SBram Moolenaar 	STRCPY(p, "file ");
5114071d4279SBram Moolenaar 	p += 5;
5115071d4279SBram Moolenaar     }
51160ab2a887SBram Moolenaar     vim_snprintf((char *)p, (size_t)(buflen - (p - buf)),
51170ab2a887SBram Moolenaar 		wp->w_arg_idx_invalid ? "(%d) of %d)"
5118071d4279SBram Moolenaar 				  : "%d of %d)", wp->w_arg_idx + 1, ARGCOUNT);
5119071d4279SBram Moolenaar     return TRUE;
5120071d4279SBram Moolenaar }
5121071d4279SBram Moolenaar 
5122071d4279SBram Moolenaar /*
5123071d4279SBram Moolenaar  * If fname is not a full path, make it a full path.
5124071d4279SBram Moolenaar  * Returns pointer to allocated memory (NULL for failure).
5125071d4279SBram Moolenaar  */
5126071d4279SBram Moolenaar     char_u  *
fix_fname(char_u * fname)51277454a06eSBram Moolenaar fix_fname(char_u  *fname)
5128071d4279SBram Moolenaar {
5129071d4279SBram Moolenaar     /*
5130071d4279SBram Moolenaar      * Force expanding the path always for Unix, because symbolic links may
5131071d4279SBram Moolenaar      * mess up the full path name, even though it starts with a '/'.
5132071d4279SBram Moolenaar      * Also expand when there is ".." in the file name, try to remove it,
5133071d4279SBram Moolenaar      * because "c:/src/../README" is equal to "c:/README".
51349b942209SBram Moolenaar      * Similarly "c:/src//file" is equal to "c:/src/file".
5135071d4279SBram Moolenaar      * For MS-Windows also expand names like "longna~1" to "longname".
5136071d4279SBram Moolenaar      */
513738323e4fSBram Moolenaar #ifdef UNIX
5138071d4279SBram Moolenaar     return FullName_save(fname, TRUE);
5139071d4279SBram Moolenaar #else
51409b942209SBram Moolenaar     if (!vim_isAbsName(fname)
51419b942209SBram Moolenaar 	    || strstr((char *)fname, "..") != NULL
51429b942209SBram Moolenaar 	    || strstr((char *)fname, "//") != NULL
51439b942209SBram Moolenaar # ifdef BACKSLASH_IN_FILENAME
51449b942209SBram Moolenaar 	    || strstr((char *)fname, "\\\\") != NULL
51459b942209SBram Moolenaar # endif
514648e330afSBram Moolenaar # if defined(MSWIN)
5147071d4279SBram Moolenaar 	    || vim_strchr(fname, '~') != NULL
5148071d4279SBram Moolenaar # endif
5149071d4279SBram Moolenaar 	    )
5150071d4279SBram Moolenaar 	return FullName_save(fname, FALSE);
5151071d4279SBram Moolenaar 
5152071d4279SBram Moolenaar     fname = vim_strsave(fname);
5153071d4279SBram Moolenaar 
5154071d4279SBram Moolenaar # ifdef USE_FNAME_CASE
5155071d4279SBram Moolenaar     if (fname != NULL)
5156c667da51SBram Moolenaar 	fname_case(fname, 0);	// set correct case for file name
5157071d4279SBram Moolenaar # endif
5158071d4279SBram Moolenaar 
5159071d4279SBram Moolenaar     return fname;
5160071d4279SBram Moolenaar #endif
5161071d4279SBram Moolenaar }
5162071d4279SBram Moolenaar 
5163071d4279SBram Moolenaar /*
51643d6014f0SBram Moolenaar  * Make "*ffname" a full file name, set "*sfname" to "*ffname" if not NULL.
51653d6014f0SBram Moolenaar  * "*ffname" becomes a pointer to allocated memory (or NULL).
51663d6014f0SBram Moolenaar  * When resolving a link both "*sfname" and "*ffname" will point to the same
51673d6014f0SBram Moolenaar  * allocated memory.
51683d6014f0SBram Moolenaar  * The "*ffname" and "*sfname" pointer values on call will not be freed.
516932aa1020SBram Moolenaar  * Note that the resulting "*ffname" pointer should be considered not allocated.
5170071d4279SBram Moolenaar  */
5171071d4279SBram Moolenaar     void
fname_expand(buf_T * buf UNUSED,char_u ** ffname,char_u ** sfname)51727454a06eSBram Moolenaar fname_expand(
51737454a06eSBram Moolenaar     buf_T	*buf UNUSED,
51747454a06eSBram Moolenaar     char_u	**ffname,
51757454a06eSBram Moolenaar     char_u	**sfname)
5176071d4279SBram Moolenaar {
51773d6014f0SBram Moolenaar     if (*ffname == NULL)	    // no file name given, nothing to do
5178071d4279SBram Moolenaar 	return;
51793d6014f0SBram Moolenaar     if (*sfname == NULL)	    // no short file name given, use ffname
5180071d4279SBram Moolenaar 	*sfname = *ffname;
51813d6014f0SBram Moolenaar     *ffname = fix_fname(*ffname);   // expand to full path
5182071d4279SBram Moolenaar 
5183071d4279SBram Moolenaar #ifdef FEAT_SHORTCUT
5184071d4279SBram Moolenaar     if (!buf->b_p_bin)
5185071d4279SBram Moolenaar     {
5186f193fffdSBram Moolenaar 	char_u  *rfname;
5187071d4279SBram Moolenaar 
51883d6014f0SBram Moolenaar 	// If the file name is a shortcut file, use the file it links to.
5189dce1e89bSBram Moolenaar 	rfname = mch_resolve_path(*ffname, FALSE);
5190f193fffdSBram Moolenaar 	if (rfname != NULL)
5191071d4279SBram Moolenaar 	{
5192071d4279SBram Moolenaar 	    vim_free(*ffname);
5193071d4279SBram Moolenaar 	    *ffname = rfname;
5194071d4279SBram Moolenaar 	    *sfname = rfname;
5195071d4279SBram Moolenaar 	}
5196071d4279SBram Moolenaar     }
5197071d4279SBram Moolenaar #endif
5198071d4279SBram Moolenaar }
5199071d4279SBram Moolenaar 
5200071d4279SBram Moolenaar /*
5201071d4279SBram Moolenaar  * Open a window for a number of buffers.
5202071d4279SBram Moolenaar  */
5203071d4279SBram Moolenaar     void
ex_buffer_all(exarg_T * eap)52047454a06eSBram Moolenaar ex_buffer_all(exarg_T *eap)
5205071d4279SBram Moolenaar {
5206071d4279SBram Moolenaar     buf_T	*buf;
5207071d4279SBram Moolenaar     win_T	*wp, *wpnext;
5208071d4279SBram Moolenaar     int		split_ret = OK;
5209071d4279SBram Moolenaar     int		p_ea_save;
5210071d4279SBram Moolenaar     int		open_wins = 0;
5211071d4279SBram Moolenaar     int		r;
5212c667da51SBram Moolenaar     int		count;		// Maximum number of windows to open.
5213c667da51SBram Moolenaar     int		all;		// When TRUE also load inactive buffers.
5214e1004401SBram Moolenaar     int		had_tab = cmdmod.cmod_tab;
5215e1438bb8SBram Moolenaar     tabpage_T	*tpnext;
5216071d4279SBram Moolenaar 
5217c667da51SBram Moolenaar     if (eap->addr_count == 0)	// make as many windows as possible
5218071d4279SBram Moolenaar 	count = 9999;
5219071d4279SBram Moolenaar     else
5220c667da51SBram Moolenaar 	count = eap->line2;	// make as many windows as specified
5221071d4279SBram Moolenaar     if (eap->cmdidx == CMD_unhide || eap->cmdidx == CMD_sunhide)
5222071d4279SBram Moolenaar 	all = FALSE;
5223071d4279SBram Moolenaar     else
5224071d4279SBram Moolenaar 	all = TRUE;
5225071d4279SBram Moolenaar 
5226071d4279SBram Moolenaar     setpcmark();
5227071d4279SBram Moolenaar 
5228071d4279SBram Moolenaar #ifdef FEAT_GUI
5229071d4279SBram Moolenaar     need_mouse_correct = TRUE;
5230071d4279SBram Moolenaar #endif
5231071d4279SBram Moolenaar 
5232071d4279SBram Moolenaar     /*
5233071d4279SBram Moolenaar      * Close superfluous windows (two windows for the same buffer).
5234071d4279SBram Moolenaar      * Also close windows that are not full-width.
5235071d4279SBram Moolenaar      */
5236e1438bb8SBram Moolenaar     if (had_tab > 0)
523749e649fcSBram Moolenaar 	goto_tabpage_tp(first_tabpage, TRUE, TRUE);
5238e1438bb8SBram Moolenaar     for (;;)
5239e1438bb8SBram Moolenaar     {
5240e1438bb8SBram Moolenaar 	tpnext = curtab->tp_next;
5241071d4279SBram Moolenaar 	for (wp = firstwin; wp != NULL; wp = wpnext)
5242071d4279SBram Moolenaar 	{
5243071d4279SBram Moolenaar 	    wpnext = wp->w_next;
5244bfb2d40bSBram Moolenaar 	    if ((wp->w_buffer->b_nwindows > 1
5245e1004401SBram Moolenaar 		    || ((cmdmod.cmod_split & WSP_VERT)
5246071d4279SBram Moolenaar 			? wp->w_height + wp->w_status_height < Rows - p_ch
5247bfb2d40bSBram Moolenaar 							    - tabline_height()
5248071d4279SBram Moolenaar 			: wp->w_width != Columns)
52494033c55eSBram Moolenaar 		    || (had_tab > 0 && wp != firstwin)) && !ONE_WINDOW
5250f2bd8ef2SBram Moolenaar 			     && !(wp->w_closing || wp->w_buffer->b_locked > 0))
5251071d4279SBram Moolenaar 	    {
5252071d4279SBram Moolenaar 		win_close(wp, FALSE);
5253c667da51SBram Moolenaar 		wpnext = firstwin;	// just in case an autocommand does
5254c667da51SBram Moolenaar 					// something strange with windows
5255c667da51SBram Moolenaar 		tpnext = first_tabpage;	// start all over...
5256071d4279SBram Moolenaar 		open_wins = 0;
5257071d4279SBram Moolenaar 	    }
5258071d4279SBram Moolenaar 	    else
5259071d4279SBram Moolenaar 		++open_wins;
5260071d4279SBram Moolenaar 	}
5261071d4279SBram Moolenaar 
5262c667da51SBram Moolenaar 	// Without the ":tab" modifier only do the current tab page.
5263e1438bb8SBram Moolenaar 	if (had_tab == 0 || tpnext == NULL)
5264e1438bb8SBram Moolenaar 	    break;
526549e649fcSBram Moolenaar 	goto_tabpage_tp(tpnext, TRUE, TRUE);
5266e1438bb8SBram Moolenaar     }
5267e1438bb8SBram Moolenaar 
5268071d4279SBram Moolenaar     /*
5269071d4279SBram Moolenaar      * Go through the buffer list.  When a buffer doesn't have a window yet,
5270071d4279SBram Moolenaar      * open one.  Otherwise move the window to the right position.
5271071d4279SBram Moolenaar      * Watch out for autocommands that delete buffers or windows!
5272071d4279SBram Moolenaar      */
5273c667da51SBram Moolenaar     // Don't execute Win/Buf Enter/Leave autocommands here.
5274071d4279SBram Moolenaar     ++autocmd_no_enter;
5275071d4279SBram Moolenaar     win_enter(lastwin, FALSE);
5276071d4279SBram Moolenaar     ++autocmd_no_leave;
5277071d4279SBram Moolenaar     for (buf = firstbuf; buf != NULL && open_wins < count; buf = buf->b_next)
5278071d4279SBram Moolenaar     {
5279c667da51SBram Moolenaar 	// Check if this buffer needs a window
5280071d4279SBram Moolenaar 	if ((!all && buf->b_ml.ml_mfp == NULL) || !buf->b_p_bl)
5281071d4279SBram Moolenaar 	    continue;
5282071d4279SBram Moolenaar 
5283b475fb91SBram Moolenaar 	if (had_tab != 0)
5284b475fb91SBram Moolenaar 	{
5285c667da51SBram Moolenaar 	    // With the ":tab" modifier don't move the window.
5286b475fb91SBram Moolenaar 	    if (buf->b_nwindows > 0)
5287c667da51SBram Moolenaar 		wp = lastwin;	    // buffer has a window, skip it
5288b475fb91SBram Moolenaar 	    else
5289b475fb91SBram Moolenaar 		wp = NULL;
5290b475fb91SBram Moolenaar 	}
5291b475fb91SBram Moolenaar 	else
5292b475fb91SBram Moolenaar 	{
5293c667da51SBram Moolenaar 	    // Check if this buffer already has a window
529429323590SBram Moolenaar 	    FOR_ALL_WINDOWS(wp)
5295071d4279SBram Moolenaar 		if (wp->w_buffer == buf)
5296071d4279SBram Moolenaar 		    break;
5297c667da51SBram Moolenaar 	    // If the buffer already has a window, move it
5298071d4279SBram Moolenaar 	    if (wp != NULL)
5299071d4279SBram Moolenaar 		win_move_after(wp, curwin);
5300b475fb91SBram Moolenaar 	}
5301b475fb91SBram Moolenaar 
5302b475fb91SBram Moolenaar 	if (wp == NULL && split_ret == OK)
5303071d4279SBram Moolenaar 	{
5304b25f9a97SBram Moolenaar 	    bufref_T	bufref;
5305b25f9a97SBram Moolenaar 
5306b25f9a97SBram Moolenaar 	    set_bufref(&bufref, buf);
5307f2bd8ef2SBram Moolenaar 
5308c667da51SBram Moolenaar 	    // Split the window and put the buffer in it
5309071d4279SBram Moolenaar 	    p_ea_save = p_ea;
5310c667da51SBram Moolenaar 	    p_ea = TRUE;		// use space from all windows
5311071d4279SBram Moolenaar 	    split_ret = win_split(0, WSP_ROOM | WSP_BELOW);
5312071d4279SBram Moolenaar 	    ++open_wins;
5313071d4279SBram Moolenaar 	    p_ea = p_ea_save;
5314071d4279SBram Moolenaar 	    if (split_ret == FAIL)
5315071d4279SBram Moolenaar 		continue;
5316071d4279SBram Moolenaar 
5317c667da51SBram Moolenaar 	    // Open the buffer in this window.
5318071d4279SBram Moolenaar 	    swap_exists_action = SEA_DIALOG;
5319071d4279SBram Moolenaar 	    set_curbuf(buf, DOBUF_GOTO);
5320b25f9a97SBram Moolenaar 	    if (!bufref_valid(&bufref))
5321071d4279SBram Moolenaar 	    {
5322c667da51SBram Moolenaar 		// autocommands deleted the buffer!!!
5323071d4279SBram Moolenaar 		swap_exists_action = SEA_NONE;
5324071d4279SBram Moolenaar 		break;
5325071d4279SBram Moolenaar 	    }
5326071d4279SBram Moolenaar 	    if (swap_exists_action == SEA_QUIT)
5327071d4279SBram Moolenaar 	    {
5328f2bd8ef2SBram Moolenaar #if defined(FEAT_EVAL)
5329c0197e28SBram Moolenaar 		cleanup_T   cs;
53305eb86f91SBram Moolenaar 
5331c667da51SBram Moolenaar 		// Reset the error/interrupt/exception state here so that
5332c667da51SBram Moolenaar 		// aborting() returns FALSE when closing a window.
5333c0197e28SBram Moolenaar 		enter_cleanup(&cs);
5334c0197e28SBram Moolenaar #endif
53355eb86f91SBram Moolenaar 
5336c667da51SBram Moolenaar 		// User selected Quit at ATTENTION prompt; close this window.
5337071d4279SBram Moolenaar 		win_close(curwin, TRUE);
5338071d4279SBram Moolenaar 		--open_wins;
5339071d4279SBram Moolenaar 		swap_exists_action = SEA_NONE;
534012033fb4SBram Moolenaar 		swap_exists_did_quit = TRUE;
5341c0197e28SBram Moolenaar 
5342f2bd8ef2SBram Moolenaar #if defined(FEAT_EVAL)
5343c667da51SBram Moolenaar 		// Restore the error/interrupt/exception state if not
5344c667da51SBram Moolenaar 		// discarded by a new aborting error, interrupt, or uncaught
5345c667da51SBram Moolenaar 		// exception.
5346c0197e28SBram Moolenaar 		leave_cleanup(&cs);
5347c0197e28SBram Moolenaar #endif
5348071d4279SBram Moolenaar 	    }
5349071d4279SBram Moolenaar 	    else
5350071d4279SBram Moolenaar 		handle_swap_exists(NULL);
5351071d4279SBram Moolenaar 	}
5352071d4279SBram Moolenaar 
5353071d4279SBram Moolenaar 	ui_breakcheck();
5354071d4279SBram Moolenaar 	if (got_int)
5355071d4279SBram Moolenaar 	{
5356c667da51SBram Moolenaar 	    (void)vgetc();	// only break the file loading, not the rest
5357071d4279SBram Moolenaar 	    break;
5358071d4279SBram Moolenaar 	}
53595eb86f91SBram Moolenaar #ifdef FEAT_EVAL
5360c667da51SBram Moolenaar 	// Autocommands deleted the buffer or aborted script processing!!!
53615eb86f91SBram Moolenaar 	if (aborting())
53625eb86f91SBram Moolenaar 	    break;
53635eb86f91SBram Moolenaar #endif
5364c667da51SBram Moolenaar 	// When ":tab" was used open a new tab for a new window repeatedly.
5365e1438bb8SBram Moolenaar 	if (had_tab > 0 && tabpage_index(NULL) <= p_tpm)
5366e1004401SBram Moolenaar 	    cmdmod.cmod_tab = 9999;
5367071d4279SBram Moolenaar     }
5368071d4279SBram Moolenaar     --autocmd_no_enter;
5369c667da51SBram Moolenaar     win_enter(firstwin, FALSE);		// back to first window
5370071d4279SBram Moolenaar     --autocmd_no_leave;
5371071d4279SBram Moolenaar 
5372071d4279SBram Moolenaar     /*
5373071d4279SBram Moolenaar      * Close superfluous windows.
5374071d4279SBram Moolenaar      */
5375071d4279SBram Moolenaar     for (wp = lastwin; open_wins > count; )
5376071d4279SBram Moolenaar     {
5377eb44a68bSBram Moolenaar 	r = (buf_hide(wp->w_buffer) || !bufIsChanged(wp->w_buffer)
5378071d4279SBram Moolenaar 				     || autowrite(wp->w_buffer, FALSE) == OK);
5379071d4279SBram Moolenaar 	if (!win_valid(wp))
5380071d4279SBram Moolenaar 	{
5381c667da51SBram Moolenaar 	    // BufWrite Autocommands made the window invalid, start over
5382071d4279SBram Moolenaar 	    wp = lastwin;
5383071d4279SBram Moolenaar 	}
5384f2bd8ef2SBram Moolenaar 	else if (r)
5385071d4279SBram Moolenaar 	{
5386eb44a68bSBram Moolenaar 	    win_close(wp, !buf_hide(wp->w_buffer));
5387071d4279SBram Moolenaar 	    --open_wins;
5388071d4279SBram Moolenaar 	    wp = lastwin;
5389071d4279SBram Moolenaar 	}
5390071d4279SBram Moolenaar 	else
5391071d4279SBram Moolenaar 	{
5392071d4279SBram Moolenaar 	    wp = wp->w_prev;
5393071d4279SBram Moolenaar 	    if (wp == NULL)
5394071d4279SBram Moolenaar 		break;
5395071d4279SBram Moolenaar 	}
5396071d4279SBram Moolenaar     }
5397071d4279SBram Moolenaar }
5398071d4279SBram Moolenaar 
5399071d4279SBram Moolenaar 
5400f28dbceaSBram Moolenaar static int  chk_modeline(linenr_T, int);
5401a3227e2bSBram Moolenaar 
5402071d4279SBram Moolenaar /*
5403071d4279SBram Moolenaar  * do_modelines() - process mode lines for the current file
5404071d4279SBram Moolenaar  *
5405a3227e2bSBram Moolenaar  * "flags" can be:
5406a3227e2bSBram Moolenaar  * OPT_WINONLY	    only set options local to window
5407a3227e2bSBram Moolenaar  * OPT_NOWIN	    don't set options local to window
5408a3227e2bSBram Moolenaar  *
5409071d4279SBram Moolenaar  * Returns immediately if the "ml" option isn't set.
5410071d4279SBram Moolenaar  */
5411071d4279SBram Moolenaar     void
do_modelines(int flags)54127454a06eSBram Moolenaar do_modelines(int flags)
5413071d4279SBram Moolenaar {
5414071d4279SBram Moolenaar     linenr_T	lnum;
5415071d4279SBram Moolenaar     int		nmlines;
5416071d4279SBram Moolenaar     static int	entered = 0;
5417071d4279SBram Moolenaar 
5418071d4279SBram Moolenaar     if (!curbuf->b_p_ml || (nmlines = (int)p_mls) == 0)
5419071d4279SBram Moolenaar 	return;
5420071d4279SBram Moolenaar 
5421c667da51SBram Moolenaar     // Disallow recursive entry here.  Can happen when executing a modeline
5422c667da51SBram Moolenaar     // triggers an autocommand, which reloads modelines with a ":do".
5423071d4279SBram Moolenaar     if (entered)
5424071d4279SBram Moolenaar 	return;
5425071d4279SBram Moolenaar 
5426071d4279SBram Moolenaar     ++entered;
54279dcd349cSHu Jialun     for (lnum = 1; curbuf->b_p_ml && lnum <= curbuf->b_ml.ml_line_count && lnum <= nmlines;
5428071d4279SBram Moolenaar 								       ++lnum)
5429a3227e2bSBram Moolenaar 	if (chk_modeline(lnum, flags) == FAIL)
5430071d4279SBram Moolenaar 	    nmlines = 0;
5431071d4279SBram Moolenaar 
54329dcd349cSHu Jialun     for (lnum = curbuf->b_ml.ml_line_count; curbuf->b_p_ml && lnum > 0 && lnum > nmlines
5433071d4279SBram Moolenaar 		       && lnum > curbuf->b_ml.ml_line_count - nmlines; --lnum)
5434a3227e2bSBram Moolenaar 	if (chk_modeline(lnum, flags) == FAIL)
5435071d4279SBram Moolenaar 	    nmlines = 0;
5436071d4279SBram Moolenaar     --entered;
5437071d4279SBram Moolenaar }
5438071d4279SBram Moolenaar 
5439c667da51SBram Moolenaar #include "version.h"		// for version number
5440071d4279SBram Moolenaar 
5441071d4279SBram Moolenaar /*
5442071d4279SBram Moolenaar  * chk_modeline() - check a single line for a mode string
5443071d4279SBram Moolenaar  * Return FAIL if an error encountered.
5444071d4279SBram Moolenaar  */
5445071d4279SBram Moolenaar     static int
chk_modeline(linenr_T lnum,int flags)54467454a06eSBram Moolenaar chk_modeline(
54477454a06eSBram Moolenaar     linenr_T	lnum,
5448c667da51SBram Moolenaar     int		flags)		// Same as for do_modelines().
5449071d4279SBram Moolenaar {
5450071d4279SBram Moolenaar     char_u	*s;
5451071d4279SBram Moolenaar     char_u	*e;
5452c667da51SBram Moolenaar     char_u	*linecopy;		// local copy of any modeline found
5453071d4279SBram Moolenaar     int		prev;
5454071d4279SBram Moolenaar     int		vers;
5455071d4279SBram Moolenaar     int		end;
5456071d4279SBram Moolenaar     int		retval = OK;
5457f29c1c6aSBram Moolenaar     sctx_T	save_current_sctx;
54589b8d6226SBram Moolenaar 
5459e31ee868SBram Moolenaar     ESTACK_CHECK_DECLARATION
5460071d4279SBram Moolenaar 
5461071d4279SBram Moolenaar     prev = -1;
5462071d4279SBram Moolenaar     for (s = ml_get(lnum); *s != NUL; ++s)
5463071d4279SBram Moolenaar     {
5464071d4279SBram Moolenaar 	if (prev == -1 || vim_isspace(prev))
5465071d4279SBram Moolenaar 	{
5466071d4279SBram Moolenaar 	    if ((prev != -1 && STRNCMP(s, "ex:", (size_t)3) == 0)
5467071d4279SBram Moolenaar 		    || STRNCMP(s, "vi:", (size_t)3) == 0)
5468071d4279SBram Moolenaar 		break;
5469c667da51SBram Moolenaar 	    // Accept both "vim" and "Vim".
5470c14621eaSBram Moolenaar 	    if ((s[0] == 'v' || s[0] == 'V') && s[1] == 'i' && s[2] == 'm')
5471071d4279SBram Moolenaar 	    {
5472071d4279SBram Moolenaar 		if (s[3] == '<' || s[3] == '=' || s[3] == '>')
5473071d4279SBram Moolenaar 		    e = s + 4;
5474071d4279SBram Moolenaar 		else
5475071d4279SBram Moolenaar 		    e = s + 3;
5476071d4279SBram Moolenaar 		vers = getdigits(&e);
5477071d4279SBram Moolenaar 		if (*e == ':'
5478630a730fSBram Moolenaar 			&& (s[0] != 'V'
5479630a730fSBram Moolenaar 				  || STRNCMP(skipwhite(e + 1), "set", 3) == 0)
5480071d4279SBram Moolenaar 			&& (s[3] == ':'
5481071d4279SBram Moolenaar 			    || (VIM_VERSION_100 >= vers && isdigit(s[3]))
5482071d4279SBram Moolenaar 			    || (VIM_VERSION_100 < vers && s[3] == '<')
5483071d4279SBram Moolenaar 			    || (VIM_VERSION_100 > vers && s[3] == '>')
5484071d4279SBram Moolenaar 			    || (VIM_VERSION_100 == vers && s[3] == '=')))
5485071d4279SBram Moolenaar 		    break;
5486071d4279SBram Moolenaar 	    }
5487071d4279SBram Moolenaar 	}
5488071d4279SBram Moolenaar 	prev = *s;
5489071d4279SBram Moolenaar     }
5490071d4279SBram Moolenaar 
5491071d4279SBram Moolenaar     if (*s)
5492071d4279SBram Moolenaar     {
5493c667da51SBram Moolenaar 	do				// skip over "ex:", "vi:" or "vim:"
5494071d4279SBram Moolenaar 	    ++s;
5495071d4279SBram Moolenaar 	while (s[-1] != ':');
5496071d4279SBram Moolenaar 
5497c667da51SBram Moolenaar 	s = linecopy = vim_strsave(s);	// copy the line, it will change
5498071d4279SBram Moolenaar 	if (linecopy == NULL)
5499071d4279SBram Moolenaar 	    return FAIL;
5500071d4279SBram Moolenaar 
55011a47ae32SBram Moolenaar 	// prepare for emsg()
55021a47ae32SBram Moolenaar 	estack_push(ETYPE_MODELINE, (char_u *)"modelines", lnum);
5503e31ee868SBram Moolenaar 	ESTACK_CHECK_SETUP
5504071d4279SBram Moolenaar 
5505071d4279SBram Moolenaar 	end = FALSE;
5506071d4279SBram Moolenaar 	while (end == FALSE)
5507071d4279SBram Moolenaar 	{
5508071d4279SBram Moolenaar 	    s = skipwhite(s);
5509071d4279SBram Moolenaar 	    if (*s == NUL)
5510071d4279SBram Moolenaar 		break;
5511071d4279SBram Moolenaar 
5512071d4279SBram Moolenaar 	    /*
5513071d4279SBram Moolenaar 	     * Find end of set command: ':' or end of line.
5514071d4279SBram Moolenaar 	     * Skip over "\:", replacing it with ":".
5515071d4279SBram Moolenaar 	     */
5516071d4279SBram Moolenaar 	    for (e = s; *e != ':' && *e != NUL; ++e)
5517071d4279SBram Moolenaar 		if (e[0] == '\\' && e[1] == ':')
5518f233048aSBram Moolenaar 		    STRMOVE(e, e + 1);
5519071d4279SBram Moolenaar 	    if (*e == NUL)
5520071d4279SBram Moolenaar 		end = TRUE;
5521071d4279SBram Moolenaar 
5522071d4279SBram Moolenaar 	    /*
5523071d4279SBram Moolenaar 	     * If there is a "set" command, require a terminating ':' and
5524071d4279SBram Moolenaar 	     * ignore the stuff after the ':'.
5525071d4279SBram Moolenaar 	     * "vi:set opt opt opt: foo" -- foo not interpreted
5526071d4279SBram Moolenaar 	     * "vi:opt opt opt: foo" -- foo interpreted
5527071d4279SBram Moolenaar 	     * Accept "se" for compatibility with Elvis.
5528071d4279SBram Moolenaar 	     */
5529071d4279SBram Moolenaar 	    if (STRNCMP(s, "set ", (size_t)4) == 0
5530071d4279SBram Moolenaar 		    || STRNCMP(s, "se ", (size_t)3) == 0)
5531071d4279SBram Moolenaar 	    {
5532c667da51SBram Moolenaar 		if (*e != ':')		// no terminating ':'?
5533071d4279SBram Moolenaar 		    break;
5534071d4279SBram Moolenaar 		end = TRUE;
5535071d4279SBram Moolenaar 		s = vim_strchr(s, ' ') + 1;
5536071d4279SBram Moolenaar 	    }
5537c667da51SBram Moolenaar 	    *e = NUL;			// truncate the set command
5538071d4279SBram Moolenaar 
5539c667da51SBram Moolenaar 	    if (*s != NUL)		// skip over an empty "::"
5540071d4279SBram Moolenaar 	    {
554148f377a4SBram Moolenaar 		int secure_save = secure;
55429b8d6226SBram Moolenaar 
5543f29c1c6aSBram Moolenaar 		save_current_sctx = current_sctx;
55449b8d6226SBram Moolenaar 		current_sctx.sc_version = 1;
55459b8d6226SBram Moolenaar #ifdef FEAT_EVAL
5546f29c1c6aSBram Moolenaar 		current_sctx.sc_sid = SID_MODELINE;
5547ded5f1beSBram Moolenaar 		current_sctx.sc_seq = 0;
55481a47ae32SBram Moolenaar 		current_sctx.sc_lnum = lnum;
5549071d4279SBram Moolenaar #endif
55509b8d6226SBram Moolenaar 
55515958f95aSBram Moolenaar 		// Make sure no risky things are executed as a side effect.
555282b033efSBram Moolenaar 		secure = 1;
55535958f95aSBram Moolenaar 
5554a3227e2bSBram Moolenaar 		retval = do_set(s, OPT_MODELINE | OPT_LOCAL | flags);
55555958f95aSBram Moolenaar 
555648f377a4SBram Moolenaar 		secure = secure_save;
5557f29c1c6aSBram Moolenaar 		current_sctx = save_current_sctx;
5558c667da51SBram Moolenaar 		if (retval == FAIL)		// stop if error found
5559071d4279SBram Moolenaar 		    break;
5560071d4279SBram Moolenaar 	    }
5561c667da51SBram Moolenaar 	    s = e + 1;			// advance to next part
5562071d4279SBram Moolenaar 	}
5563071d4279SBram Moolenaar 
5564e31ee868SBram Moolenaar 	ESTACK_CHECK_NOW
55651a47ae32SBram Moolenaar 	estack_pop();
5566071d4279SBram Moolenaar 	vim_free(linecopy);
5567071d4279SBram Moolenaar     }
5568071d4279SBram Moolenaar     return retval;
5569071d4279SBram Moolenaar }
5570071d4279SBram Moolenaar 
5571f0a521f4SBram Moolenaar /*
557291335e5aSBram Moolenaar  * Return TRUE if "buf" is a normal buffer, 'buftype' is empty.
557391335e5aSBram Moolenaar  */
557491335e5aSBram Moolenaar     int
bt_normal(buf_T * buf)557591335e5aSBram Moolenaar bt_normal(buf_T *buf)
557691335e5aSBram Moolenaar {
557791335e5aSBram Moolenaar     return buf != NULL && buf->b_p_bt[0] == NUL;
557891335e5aSBram Moolenaar }
557991335e5aSBram Moolenaar 
5580113e1072SBram Moolenaar #if defined(FEAT_QUICKFIX) || defined(PROTO)
558191335e5aSBram Moolenaar /*
5582f0a521f4SBram Moolenaar  * Return TRUE if "buf" is the quickfix buffer.
5583f0a521f4SBram Moolenaar  */
5584f0a521f4SBram Moolenaar     int
bt_quickfix(buf_T * buf)5585f0a521f4SBram Moolenaar bt_quickfix(buf_T *buf)
5586f0a521f4SBram Moolenaar {
5587f0a521f4SBram Moolenaar     return buf != NULL && buf->b_p_bt[0] == 'q';
5588f0a521f4SBram Moolenaar }
5589113e1072SBram Moolenaar #endif
5590f0a521f4SBram Moolenaar 
5591113e1072SBram Moolenaar #if defined(FEAT_TERMINAL) || defined(PROTO)
5592f0a521f4SBram Moolenaar /*
5593f0a521f4SBram Moolenaar  * Return TRUE if "buf" is a terminal buffer.
5594f0a521f4SBram Moolenaar  */
5595f0a521f4SBram Moolenaar     int
bt_terminal(buf_T * buf)5596f0a521f4SBram Moolenaar bt_terminal(buf_T *buf)
5597f0a521f4SBram Moolenaar {
5598f0a521f4SBram Moolenaar     return buf != NULL && buf->b_p_bt[0] == 't';
5599f0a521f4SBram Moolenaar }
5600113e1072SBram Moolenaar #endif
5601f0a521f4SBram Moolenaar 
5602f0a521f4SBram Moolenaar /*
5603d28cc3f5SBram Moolenaar  * Return TRUE if "buf" is a help buffer.
5604d28cc3f5SBram Moolenaar  */
5605d28cc3f5SBram Moolenaar     int
bt_help(buf_T * buf)5606d28cc3f5SBram Moolenaar bt_help(buf_T *buf)
5607d28cc3f5SBram Moolenaar {
5608d28cc3f5SBram Moolenaar     return buf != NULL && buf->b_help;
5609d28cc3f5SBram Moolenaar }
5610d28cc3f5SBram Moolenaar 
5611d28cc3f5SBram Moolenaar /*
5612f273245fSBram Moolenaar  * Return TRUE if "buf" is a prompt buffer.
5613f273245fSBram Moolenaar  */
5614f273245fSBram Moolenaar     int
bt_prompt(buf_T * buf)5615f273245fSBram Moolenaar bt_prompt(buf_T *buf)
5616f273245fSBram Moolenaar {
56174d784b21SBram Moolenaar     return buf != NULL && buf->b_p_bt[0] == 'p' && buf->b_p_bt[1] == 'r';
56184d784b21SBram Moolenaar }
56194d784b21SBram Moolenaar 
56204d784b21SBram Moolenaar /*
56214d784b21SBram Moolenaar  * Return TRUE if "buf" is a buffer for a popup window.
56224d784b21SBram Moolenaar  */
56234d784b21SBram Moolenaar     int
bt_popup(buf_T * buf)56244d784b21SBram Moolenaar bt_popup(buf_T *buf)
56254d784b21SBram Moolenaar {
56264d784b21SBram Moolenaar     return buf != NULL && buf->b_p_bt != NULL
56274d784b21SBram Moolenaar 	&& buf->b_p_bt[0] == 'p' && buf->b_p_bt[1] == 'o';
5628f273245fSBram Moolenaar }
5629f273245fSBram Moolenaar 
5630f273245fSBram Moolenaar /*
563104958cbaSBram Moolenaar  * Return TRUE if "buf" is a "nofile", "acwrite", "terminal" or "prompt"
563204958cbaSBram Moolenaar  * buffer.  This means the buffer name is not a file name.
5633f0a521f4SBram Moolenaar  */
5634f0a521f4SBram Moolenaar     int
bt_nofilename(buf_T * buf)563526910de8SBram Moolenaar bt_nofilename(buf_T *buf)
5636f0a521f4SBram Moolenaar {
5637f0a521f4SBram Moolenaar     return buf != NULL && ((buf->b_p_bt[0] == 'n' && buf->b_p_bt[2] == 'f')
5638f0a521f4SBram Moolenaar 	    || buf->b_p_bt[0] == 'a'
5639f273245fSBram Moolenaar 	    || buf->b_p_bt[0] == 't'
5640f273245fSBram Moolenaar 	    || buf->b_p_bt[0] == 'p');
5641f0a521f4SBram Moolenaar }
5642f0a521f4SBram Moolenaar 
5643f0a521f4SBram Moolenaar /*
564426910de8SBram Moolenaar  * Return TRUE if "buf" has 'buftype' set to "nofile".
564526910de8SBram Moolenaar  */
564626910de8SBram Moolenaar     int
bt_nofile(buf_T * buf)564726910de8SBram Moolenaar bt_nofile(buf_T *buf)
564826910de8SBram Moolenaar {
564926910de8SBram Moolenaar     return buf != NULL && buf->b_p_bt[0] == 'n' && buf->b_p_bt[2] == 'f';
565026910de8SBram Moolenaar }
565126910de8SBram Moolenaar 
565226910de8SBram Moolenaar /*
565304958cbaSBram Moolenaar  * Return TRUE if "buf" is a "nowrite", "nofile", "terminal" or "prompt"
565404958cbaSBram Moolenaar  * buffer.
5655f0a521f4SBram Moolenaar  */
5656f0a521f4SBram Moolenaar     int
bt_dontwrite(buf_T * buf)5657f0a521f4SBram Moolenaar bt_dontwrite(buf_T *buf)
5658f0a521f4SBram Moolenaar {
5659f273245fSBram Moolenaar     return buf != NULL && (buf->b_p_bt[0] == 'n'
5660f273245fSBram Moolenaar 		 || buf->b_p_bt[0] == 't'
5661f273245fSBram Moolenaar 		 || buf->b_p_bt[0] == 'p');
5662f0a521f4SBram Moolenaar }
5663f0a521f4SBram Moolenaar 
5664113e1072SBram Moolenaar #if defined(FEAT_QUICKFIX) || defined(PROTO)
5665f0a521f4SBram Moolenaar     int
bt_dontwrite_msg(buf_T * buf)5666f0a521f4SBram Moolenaar bt_dontwrite_msg(buf_T *buf)
5667f0a521f4SBram Moolenaar {
5668f0a521f4SBram Moolenaar     if (bt_dontwrite(buf))
5669f0a521f4SBram Moolenaar     {
5670f9e3e09fSBram Moolenaar 	emsg(_("E382: Cannot write, 'buftype' option is set"));
5671f0a521f4SBram Moolenaar 	return TRUE;
5672f0a521f4SBram Moolenaar     }
5673f0a521f4SBram Moolenaar     return FALSE;
5674f0a521f4SBram Moolenaar }
5675113e1072SBram Moolenaar #endif
5676f0a521f4SBram Moolenaar 
5677f0a521f4SBram Moolenaar /*
5678f0a521f4SBram Moolenaar  * Return TRUE if the buffer should be hidden, according to 'hidden', ":hide"
5679f0a521f4SBram Moolenaar  * and 'bufhidden'.
5680f0a521f4SBram Moolenaar  */
5681f0a521f4SBram Moolenaar     int
buf_hide(buf_T * buf)5682f0a521f4SBram Moolenaar buf_hide(buf_T *buf)
5683f0a521f4SBram Moolenaar {
5684c667da51SBram Moolenaar     // 'bufhidden' overrules 'hidden' and ":hide", check it first
5685f0a521f4SBram Moolenaar     switch (buf->b_p_bh[0])
5686f0a521f4SBram Moolenaar     {
5687c667da51SBram Moolenaar 	case 'u':		    // "unload"
5688c667da51SBram Moolenaar 	case 'w':		    // "wipe"
5689c667da51SBram Moolenaar 	case 'd': return FALSE;	    // "delete"
5690c667da51SBram Moolenaar 	case 'h': return TRUE;	    // "hide"
5691f0a521f4SBram Moolenaar     }
5692e1004401SBram Moolenaar     return (p_hid || (cmdmod.cmod_flags & CMOD_HIDE));
5693f0a521f4SBram Moolenaar }
5694071d4279SBram Moolenaar 
5695071d4279SBram Moolenaar /*
5696071d4279SBram Moolenaar  * Return special buffer name.
5697071d4279SBram Moolenaar  * Returns NULL when the buffer has a normal file name.
5698071d4279SBram Moolenaar  */
5699e1704badSBram Moolenaar     char_u *
buf_spname(buf_T * buf)57007454a06eSBram Moolenaar buf_spname(buf_T *buf)
5701071d4279SBram Moolenaar {
57024033c55eSBram Moolenaar #if defined(FEAT_QUICKFIX)
5703071d4279SBram Moolenaar     if (bt_quickfix(buf))
570428c258fdSBram Moolenaar     {
570528c258fdSBram Moolenaar 	/*
5706ee8188fcSBram Moolenaar 	 * Differentiate between the quickfix and location list buffers using
5707ee8188fcSBram Moolenaar 	 * the buffer number stored in the global quickfix stack.
570828c258fdSBram Moolenaar 	 */
5709ee8188fcSBram Moolenaar 	if (buf->b_fnum == qf_stack_get_bufnr())
5710e1704badSBram Moolenaar 	    return (char_u *)_(msg_qflist);
5711ee8188fcSBram Moolenaar 	else
5712ee8188fcSBram Moolenaar 	    return (char_u *)_(msg_loclist);
571328c258fdSBram Moolenaar     }
5714071d4279SBram Moolenaar #endif
571521554414SBram Moolenaar 
5716c667da51SBram Moolenaar     // There is no _file_ when 'buftype' is "nofile", b_sfname
5717c667da51SBram Moolenaar     // contains the name as specified by the user.
571826910de8SBram Moolenaar     if (bt_nofilename(buf))
5719071d4279SBram Moolenaar     {
572021554414SBram Moolenaar #ifdef FEAT_TERMINAL
572121554414SBram Moolenaar 	if (buf->b_term != NULL)
572221554414SBram Moolenaar 	    return term_get_status_text(buf->b_term);
572321554414SBram Moolenaar #endif
5724e561a7e2SBram Moolenaar 	if (buf->b_fname != NULL)
5725e561a7e2SBram Moolenaar 	    return buf->b_fname;
5726891e1fd8SBram Moolenaar #ifdef FEAT_JOB_CHANNEL
5727891e1fd8SBram Moolenaar 	if (bt_prompt(buf))
5728891e1fd8SBram Moolenaar 	    return (char_u *)_("[Prompt]");
5729891e1fd8SBram Moolenaar #endif
573005ad5ff0SBram Moolenaar #ifdef FEAT_PROP_POPUP
5731c6896e20SBram Moolenaar 	if (bt_popup(buf))
5732c6896e20SBram Moolenaar 	    return (char_u *)_("[Popup]");
5733c6896e20SBram Moolenaar #endif
5734e1704badSBram Moolenaar 	return (char_u *)_("[Scratch]");
5735071d4279SBram Moolenaar     }
573621554414SBram Moolenaar 
5737071d4279SBram Moolenaar     if (buf->b_fname == NULL)
573800806bceSBram Moolenaar 	return buf_get_fname(buf);
5739071d4279SBram Moolenaar     return NULL;
5740071d4279SBram Moolenaar }
5741071d4279SBram Moolenaar 
5742071d4279SBram Moolenaar /*
574300806bceSBram Moolenaar  * Get "buf->b_fname", use "[No Name]" if it is NULL.
574400806bceSBram Moolenaar  */
574500806bceSBram Moolenaar     char_u *
buf_get_fname(buf_T * buf)574600806bceSBram Moolenaar buf_get_fname(buf_T *buf)
574700806bceSBram Moolenaar {
574800806bceSBram Moolenaar     if (buf->b_fname == NULL)
574900806bceSBram Moolenaar 	return (char_u *)_("[No Name]");
575000806bceSBram Moolenaar     return buf->b_fname;
575100806bceSBram Moolenaar }
575200806bceSBram Moolenaar 
575300806bceSBram Moolenaar /*
5754071d4279SBram Moolenaar  * Set 'buflisted' for curbuf to "on" and trigger autocommands if it changed.
5755071d4279SBram Moolenaar  */
5756071d4279SBram Moolenaar     void
set_buflisted(int on)57577454a06eSBram Moolenaar set_buflisted(int on)
5758071d4279SBram Moolenaar {
5759071d4279SBram Moolenaar     if (on != curbuf->b_p_bl)
5760071d4279SBram Moolenaar     {
5761071d4279SBram Moolenaar 	curbuf->b_p_bl = on;
5762071d4279SBram Moolenaar 	if (on)
5763071d4279SBram Moolenaar 	    apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, curbuf);
5764071d4279SBram Moolenaar 	else
5765071d4279SBram Moolenaar 	    apply_autocmds(EVENT_BUFDELETE, NULL, NULL, FALSE, curbuf);
5766071d4279SBram Moolenaar     }
5767071d4279SBram Moolenaar }
5768071d4279SBram Moolenaar 
5769071d4279SBram Moolenaar /*
5770071d4279SBram Moolenaar  * Read the file for "buf" again and check if the contents changed.
5771071d4279SBram Moolenaar  * Return TRUE if it changed or this could not be checked.
5772071d4279SBram Moolenaar  */
5773071d4279SBram Moolenaar     int
buf_contents_changed(buf_T * buf)57747454a06eSBram Moolenaar buf_contents_changed(buf_T *buf)
5775071d4279SBram Moolenaar {
5776071d4279SBram Moolenaar     buf_T	*newbuf;
5777071d4279SBram Moolenaar     int		differ = TRUE;
5778071d4279SBram Moolenaar     linenr_T	lnum;
5779071d4279SBram Moolenaar     aco_save_T	aco;
5780071d4279SBram Moolenaar     exarg_T	ea;
5781071d4279SBram Moolenaar 
5782c667da51SBram Moolenaar     // Allocate a buffer without putting it in the buffer list.
5783071d4279SBram Moolenaar     newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY);
5784071d4279SBram Moolenaar     if (newbuf == NULL)
5785071d4279SBram Moolenaar 	return TRUE;
5786071d4279SBram Moolenaar 
5787c667da51SBram Moolenaar     // Force the 'fileencoding' and 'fileformat' to be equal.
5788071d4279SBram Moolenaar     if (prep_exarg(&ea, buf) == FAIL)
5789071d4279SBram Moolenaar     {
5790071d4279SBram Moolenaar 	wipe_buffer(newbuf, FALSE);
5791071d4279SBram Moolenaar 	return TRUE;
5792071d4279SBram Moolenaar     }
5793071d4279SBram Moolenaar 
5794c667da51SBram Moolenaar     // set curwin/curbuf to buf and save a few things
5795071d4279SBram Moolenaar     aucmd_prepbuf(&aco, newbuf);
5796071d4279SBram Moolenaar 
57974770d09aSBram Moolenaar     if (ml_open(curbuf) == OK
5798071d4279SBram Moolenaar 	    && readfile(buf->b_ffname, buf->b_fname,
5799071d4279SBram Moolenaar 				  (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM,
5800071d4279SBram Moolenaar 					    &ea, READ_NEW | READ_DUMMY) == OK)
5801071d4279SBram Moolenaar     {
5802c667da51SBram Moolenaar 	// compare the two files line by line
5803071d4279SBram Moolenaar 	if (buf->b_ml.ml_line_count == curbuf->b_ml.ml_line_count)
5804071d4279SBram Moolenaar 	{
5805071d4279SBram Moolenaar 	    differ = FALSE;
5806071d4279SBram Moolenaar 	    for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum)
5807071d4279SBram Moolenaar 		if (STRCMP(ml_get_buf(buf, lnum, FALSE), ml_get(lnum)) != 0)
5808071d4279SBram Moolenaar 		{
5809071d4279SBram Moolenaar 		    differ = TRUE;
5810071d4279SBram Moolenaar 		    break;
5811071d4279SBram Moolenaar 		}
5812071d4279SBram Moolenaar 	}
5813071d4279SBram Moolenaar     }
5814071d4279SBram Moolenaar     vim_free(ea.cmd);
5815071d4279SBram Moolenaar 
5816c667da51SBram Moolenaar     // restore curwin/curbuf and a few other things
5817071d4279SBram Moolenaar     aucmd_restbuf(&aco);
5818071d4279SBram Moolenaar 
5819c667da51SBram Moolenaar     if (curbuf != newbuf)	// safety check
5820071d4279SBram Moolenaar 	wipe_buffer(newbuf, FALSE);
5821071d4279SBram Moolenaar 
5822071d4279SBram Moolenaar     return differ;
5823071d4279SBram Moolenaar }
5824071d4279SBram Moolenaar 
5825071d4279SBram Moolenaar /*
5826071d4279SBram Moolenaar  * Wipe out a buffer and decrement the last buffer number if it was used for
5827071d4279SBram Moolenaar  * this buffer.  Call this to wipe out a temp buffer that does not contain any
5828071d4279SBram Moolenaar  * marks.
5829071d4279SBram Moolenaar  */
5830071d4279SBram Moolenaar     void
wipe_buffer(buf_T * buf,int aucmd)58317454a06eSBram Moolenaar wipe_buffer(
58327454a06eSBram Moolenaar     buf_T	*buf,
5833a6e8f888SBram Moolenaar     int		aucmd)	    // When TRUE trigger autocommands.
5834071d4279SBram Moolenaar {
5835071d4279SBram Moolenaar     if (buf->b_fnum == top_file_num - 1)
5836071d4279SBram Moolenaar 	--top_file_num;
5837071d4279SBram Moolenaar 
5838c667da51SBram Moolenaar     if (!aucmd)		    // Don't trigger BufDelete autocommands here.
583978ab331eSBram Moolenaar 	block_autocmds();
5840f2bd8ef2SBram Moolenaar 
5841a6e8f888SBram Moolenaar     close_buffer(NULL, buf, DOBUF_WIPE, FALSE, TRUE);
5842f2bd8ef2SBram Moolenaar 
5843071d4279SBram Moolenaar     if (!aucmd)
584478ab331eSBram Moolenaar 	unblock_autocmds();
5845071d4279SBram Moolenaar }
5846