xref: /vim-8.2.3635/src/buffer.c (revision e37d50a5)
1 /* vi:set ts=8 sts=4 sw=4:
2  *
3  * VIM - Vi IMproved	by Bram Moolenaar
4  *
5  * Do ":help uganda"  in Vim to read copying and usage conditions.
6  * Do ":help credits" in Vim to see a list of people who contributed.
7  * See README.txt for an overview of the Vim source code.
8  */
9 
10 /*
11  * buffer.c: functions for dealing with the buffer structure
12  */
13 
14 /*
15  * The buffer list is a double linked list of all buffers.
16  * Each buffer can be in one of these states:
17  * never loaded: BF_NEVERLOADED is set, only the file name is valid
18  *   not loaded: b_ml.ml_mfp == NULL, no memfile allocated
19  *	 hidden: b_nwindows == 0, loaded but not displayed in a window
20  *	 normal: loaded and displayed in a window
21  *
22  * Instead of storing file names all over the place, each file name is
23  * stored in the buffer list. It can be referenced by a number.
24  *
25  * The current implementation remembers all file names ever used.
26  */
27 
28 #include "vim.h"
29 
30 #if defined(FEAT_CMDL_COMPL) || defined(FEAT_LISTCMDS) || defined(FEAT_EVAL) || defined(FEAT_PERL)
31 static char_u	*buflist_match __ARGS((regprog_T *prog, buf_T *buf));
32 # define HAVE_BUFLIST_MATCH
33 static char_u	*fname_match __ARGS((regprog_T *prog, char_u *name));
34 #endif
35 static void	buflist_setfpos __ARGS((buf_T *buf, win_T *win, linenr_T lnum, colnr_T col, int copy_options));
36 static wininfo_T *find_wininfo __ARGS((buf_T *buf));
37 #ifdef UNIX
38 static buf_T	*buflist_findname_stat __ARGS((char_u *ffname, struct stat *st));
39 static int	otherfile_buf __ARGS((buf_T *buf, char_u *ffname, struct stat *stp));
40 static int	buf_same_ino __ARGS((buf_T *buf, struct stat *stp));
41 #else
42 static int	otherfile_buf __ARGS((buf_T *buf, char_u *ffname));
43 #endif
44 #ifdef FEAT_TITLE
45 static int	ti_change __ARGS((char_u *str, char_u **last));
46 #endif
47 static void	free_buffer __ARGS((buf_T *));
48 static void	free_buffer_stuff __ARGS((buf_T *buf, int free_options));
49 static void	clear_wininfo __ARGS((buf_T *buf));
50 
51 #ifdef UNIX
52 # define dev_T dev_t
53 #else
54 # define dev_T unsigned
55 #endif
56 
57 #if defined(FEAT_SIGNS)
58 static void insert_sign __ARGS((buf_T *buf, signlist_T *prev, signlist_T *next, int id, linenr_T lnum, int typenr));
59 static void buf_delete_signs __ARGS((buf_T *buf));
60 #endif
61 
62 /*
63  * Open current buffer, that is: open the memfile and read the file into memory
64  * return FAIL for failure, OK otherwise
65  */
66     int
67 open_buffer(read_stdin, eap)
68     int		read_stdin;	    /* read file from stdin */
69     exarg_T	*eap;		    /* for forced 'ff' and 'fenc' or NULL */
70 {
71     int		retval = OK;
72 #ifdef FEAT_AUTOCMD
73     buf_T	*old_curbuf;
74 #endif
75 
76     /*
77      * The 'readonly' flag is only set when BF_NEVERLOADED is being reset.
78      * When re-entering the same buffer, it should not change, because the
79      * user may have reset the flag by hand.
80      */
81     if (readonlymode && curbuf->b_ffname != NULL
82 					&& (curbuf->b_flags & BF_NEVERLOADED))
83 	curbuf->b_p_ro = TRUE;
84 
85     if (ml_open(curbuf) == FAIL)
86     {
87 	/*
88 	 * There MUST be a memfile, otherwise we can't do anything
89 	 * If we can't create one for the current buffer, take another buffer
90 	 */
91 	close_buffer(NULL, curbuf, 0);
92 	for (curbuf = firstbuf; curbuf != NULL; curbuf = curbuf->b_next)
93 	    if (curbuf->b_ml.ml_mfp != NULL)
94 		break;
95 	/*
96 	 * if there is no memfile at all, exit
97 	 * This is OK, since there are no changes to lose.
98 	 */
99 	if (curbuf == NULL)
100 	{
101 	    EMSG(_("E82: Cannot allocate any buffer, exiting..."));
102 	    getout(2);
103 	}
104 	EMSG(_("E83: Cannot allocate buffer, using other one..."));
105 	enter_buffer(curbuf);
106 	return FAIL;
107     }
108 
109 #ifdef FEAT_AUTOCMD
110     /* The autocommands in readfile() may change the buffer, but only AFTER
111      * reading the file. */
112     old_curbuf = curbuf;
113     modified_was_set = FALSE;
114 #endif
115 
116     /* mark cursor position as being invalid */
117     changed_line_abv_curs();
118 
119     if (curbuf->b_ffname != NULL
120 #ifdef FEAT_NETBEANS_INTG
121 	    && netbeansReadFile
122 #endif
123        )
124     {
125 #ifdef FEAT_NETBEANS_INTG
126 	int oldFire = netbeansFireChanges;
127 
128 	netbeansFireChanges = 0;
129 #endif
130 	retval = readfile(curbuf->b_ffname, curbuf->b_fname,
131 		  (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, eap, READ_NEW);
132 #ifdef FEAT_NETBEANS_INTG
133 	netbeansFireChanges = oldFire;
134 #endif
135 	/* Help buffer is filtered. */
136 	if (curbuf->b_help)
137 	    fix_help_buffer();
138     }
139     else if (read_stdin)
140     {
141 	int		save_bin = curbuf->b_p_bin;
142 	linenr_T	line_count;
143 
144 	/*
145 	 * First read the text in binary mode into the buffer.
146 	 * Then read from that same buffer and append at the end.  This makes
147 	 * it possible to retry when 'fileformat' or 'fileencoding' was
148 	 * guessed wrong.
149 	 */
150 	curbuf->b_p_bin = TRUE;
151 	retval = readfile(NULL, NULL, (linenr_T)0,
152 		  (linenr_T)0, (linenr_T)MAXLNUM, NULL, READ_NEW + READ_STDIN);
153 	curbuf->b_p_bin = save_bin;
154 	if (retval == OK)
155 	{
156 	    line_count = curbuf->b_ml.ml_line_count;
157 	    retval = readfile(NULL, NULL, (linenr_T)line_count,
158 			    (linenr_T)0, (linenr_T)MAXLNUM, eap, READ_BUFFER);
159 	    if (retval == OK)
160 	    {
161 		/* Delete the binary lines. */
162 		while (--line_count >= 0)
163 		    ml_delete((linenr_T)1, FALSE);
164 	    }
165 	    else
166 	    {
167 		/* Delete the converted lines. */
168 		while (curbuf->b_ml.ml_line_count > line_count)
169 		    ml_delete(line_count, FALSE);
170 	    }
171 	    /* Put the cursor on the first line. */
172 	    curwin->w_cursor.lnum = 1;
173 	    curwin->w_cursor.col = 0;
174 
175 	    /* Set or reset 'modified' before executing autocommands, so that
176 	     * it can be changed there. */
177 	    if (!readonlymode && !bufempty())
178 		changed();
179 	    else if (retval != FAIL)
180 		unchanged(curbuf, FALSE);
181 #ifdef FEAT_AUTOCMD
182 # ifdef FEAT_EVAL
183 	    apply_autocmds_retval(EVENT_STDINREADPOST, NULL, NULL, FALSE,
184 							curbuf, &retval);
185 # else
186 	    apply_autocmds(EVENT_STDINREADPOST, NULL, NULL, FALSE, curbuf);
187 # endif
188 #endif
189 	}
190     }
191 
192     /* if first time loading this buffer, init b_chartab[] */
193     if (curbuf->b_flags & BF_NEVERLOADED)
194 	(void)buf_init_chartab(curbuf, FALSE);
195 
196     /*
197      * Set/reset the Changed flag first, autocmds may change the buffer.
198      * Apply the automatic commands, before processing the modelines.
199      * So the modelines have priority over auto commands.
200      */
201     /* When reading stdin, the buffer contents always needs writing, so set
202      * the changed flag.  Unless in readonly mode: "ls | gview -".
203      * When interrupted and 'cpoptions' contains 'i' set changed flag. */
204     if ((got_int && vim_strchr(p_cpo, CPO_INTMOD) != NULL)
205 #ifdef FEAT_AUTOCMD
206 		|| modified_was_set	/* ":set modified" used in autocmd */
207 # ifdef FEAT_EVAL
208 		|| (aborting() && vim_strchr(p_cpo, CPO_INTMOD) != NULL)
209 # endif
210 #endif
211        )
212 	changed();
213     else if (retval != FAIL && !read_stdin)
214 	unchanged(curbuf, FALSE);
215     save_file_ff(curbuf);		/* keep this fileformat */
216 
217     /* require "!" to overwrite the file, because it wasn't read completely */
218 #ifdef FEAT_EVAL
219     if (aborting())
220 #else
221     if (got_int)
222 #endif
223 	curbuf->b_flags |= BF_READERR;
224 
225 #ifdef FEAT_FOLDING
226     /* Need to update automatic folding.  Do this before the autocommands,
227      * they may use the fold info. */
228     foldUpdateAll(curwin);
229 #endif
230 
231 #ifdef FEAT_AUTOCMD
232     /* need to set w_topline, unless some autocommand already did that. */
233     if (!(curwin->w_valid & VALID_TOPLINE))
234     {
235 	curwin->w_topline = 1;
236 # ifdef FEAT_DIFF
237 	curwin->w_topfill = 0;
238 # endif
239     }
240 # ifdef FEAT_EVAL
241     apply_autocmds_retval(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf, &retval);
242 # else
243     apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
244 # endif
245 #endif
246 
247     if (retval != FAIL)
248     {
249 #ifdef FEAT_AUTOCMD
250 	/*
251 	 * The autocommands may have changed the current buffer.  Apply the
252 	 * modelines to the correct buffer, if it still exists and is loaded.
253 	 */
254 	if (buf_valid(old_curbuf) && old_curbuf->b_ml.ml_mfp != NULL)
255 	{
256 	    aco_save_T	aco;
257 
258 	    /* Go to the buffer that was opened. */
259 	    aucmd_prepbuf(&aco, old_curbuf);
260 #endif
261 	    do_modelines(0);
262 	    curbuf->b_flags &= ~(BF_CHECK_RO | BF_NEVERLOADED);
263 
264 #ifdef FEAT_AUTOCMD
265 # ifdef FEAT_EVAL
266 	    apply_autocmds_retval(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf,
267 								    &retval);
268 # else
269 	    apply_autocmds(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf);
270 # endif
271 
272 	    /* restore curwin/curbuf and a few other things */
273 	    aucmd_restbuf(&aco);
274 	}
275 #endif
276     }
277 
278     return retval;
279 }
280 
281 /*
282  * Return TRUE if "buf" points to a valid buffer (in the buffer list).
283  */
284     int
285 buf_valid(buf)
286     buf_T	*buf;
287 {
288     buf_T	*bp;
289 
290     for (bp = firstbuf; bp != NULL; bp = bp->b_next)
291 	if (bp == buf)
292 	    return TRUE;
293     return FALSE;
294 }
295 
296 /*
297  * Close the link to a buffer.
298  * "action" is used when there is no longer a window for the buffer.
299  * It can be:
300  * 0			buffer becomes hidden
301  * DOBUF_UNLOAD		buffer is unloaded
302  * DOBUF_DELETE		buffer is unloaded and removed from buffer list
303  * DOBUF_WIPE		buffer is unloaded and really deleted
304  * When doing all but the first one on the current buffer, the caller should
305  * get a new buffer very soon!
306  *
307  * The 'bufhidden' option can force freeing and deleting.
308  */
309     void
310 close_buffer(win, buf, action)
311     win_T	*win;		/* if not NULL, set b_last_cursor */
312     buf_T	*buf;
313     int		action;
314 {
315 #ifdef FEAT_AUTOCMD
316     int		is_curbuf;
317     int		nwindows = buf->b_nwindows;
318 #endif
319     int		unload_buf = (action != 0);
320     int		del_buf = (action == DOBUF_DEL || action == DOBUF_WIPE);
321     int		wipe_buf = (action == DOBUF_WIPE);
322 
323 #ifdef FEAT_QUICKFIX
324     /*
325      * Force unloading or deleting when 'bufhidden' says so.
326      * The caller must take care of NOT deleting/freeing when 'bufhidden' is
327      * "hide" (otherwise we could never free or delete a buffer).
328      */
329     if (buf->b_p_bh[0] == 'd')		/* 'bufhidden' == "delete" */
330     {
331 	del_buf = TRUE;
332 	unload_buf = TRUE;
333     }
334     else if (buf->b_p_bh[0] == 'w')	/* 'bufhidden' == "wipe" */
335     {
336 	del_buf = TRUE;
337 	unload_buf = TRUE;
338 	wipe_buf = TRUE;
339     }
340     else if (buf->b_p_bh[0] == 'u')	/* 'bufhidden' == "unload" */
341 	unload_buf = TRUE;
342 #endif
343 
344     if (win != NULL)
345     {
346 	/* Set b_last_cursor when closing the last window for the buffer.
347 	 * Remember the last cursor position and window options of the buffer.
348 	 * This used to be only for the current window, but then options like
349 	 * 'foldmethod' may be lost with a ":only" command. */
350 	if (buf->b_nwindows == 1)
351 	    set_last_cursor(win);
352 	buflist_setfpos(buf, win,
353 		    win->w_cursor.lnum == 1 ? 0 : win->w_cursor.lnum,
354 		    win->w_cursor.col, TRUE);
355     }
356 
357 #ifdef FEAT_AUTOCMD
358     /* When the buffer is no longer in a window, trigger BufWinLeave */
359     if (buf->b_nwindows == 1)
360     {
361 	apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname,
362 								  FALSE, buf);
363 	if (!buf_valid(buf))	    /* autocommands may delete the buffer */
364 	    return;
365 
366 	/* When the buffer becomes hidden, but is not unloaded, trigger
367 	 * BufHidden */
368 	if (!unload_buf)
369 	{
370 	    apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname,
371 								  FALSE, buf);
372 	    if (!buf_valid(buf))	/* autocmds may delete the buffer */
373 		return;
374 	}
375 # ifdef FEAT_EVAL
376 	if (aborting())	    /* autocmds may abort script processing */
377 	    return;
378 # endif
379     }
380     nwindows = buf->b_nwindows;
381 #endif
382 
383     /* decrease the link count from windows (unless not in any window) */
384     if (buf->b_nwindows > 0)
385 	--buf->b_nwindows;
386 
387     /* Return when a window is displaying the buffer or when it's not
388      * unloaded. */
389     if (buf->b_nwindows > 0 || !unload_buf)
390     {
391 #if 0	    /* why was this here? */
392 	if (buf == curbuf)
393 	    u_sync();	    /* sync undo before going to another buffer */
394 #endif
395 	return;
396     }
397 
398     /* Always remove the buffer when there is no file name. */
399     if (buf->b_ffname == NULL)
400 	del_buf = TRUE;
401 
402     /*
403      * Free all things allocated for this buffer.
404      * Also calls the "BufDelete" autocommands when del_buf is TRUE.
405      */
406 #ifdef FEAT_AUTOCMD
407     /* Remember if we are closing the current buffer.  Restore the number of
408      * windows, so that autocommands in buf_freeall() don't get confused. */
409     is_curbuf = (buf == curbuf);
410     buf->b_nwindows = nwindows;
411 #endif
412 
413     buf_freeall(buf, del_buf, wipe_buf);
414 
415 #ifdef FEAT_AUTOCMD
416     /* Autocommands may have deleted the buffer. */
417     if (!buf_valid(buf))
418 	return;
419 # ifdef FEAT_EVAL
420     if (aborting())	    /* autocmds may abort script processing */
421 	return;
422 # endif
423 
424     /* Autocommands may have opened or closed windows for this buffer.
425      * Decrement the count for the close we do here. */
426     if (buf->b_nwindows > 0)
427 	--buf->b_nwindows;
428 
429     /*
430      * It's possible that autocommands change curbuf to the one being deleted.
431      * This might cause the previous curbuf to be deleted unexpectedly.  But
432      * in some cases it's OK to delete the curbuf, because a new one is
433      * obtained anyway.  Therefore only return if curbuf changed to the
434      * deleted buffer.
435      */
436     if (buf == curbuf && !is_curbuf)
437 	return;
438 #endif
439 
440 #ifdef FEAT_NETBEANS_INTG
441     if (usingNetbeans)
442 	netbeans_file_closed(buf);
443 #endif
444     /* Change directories when the 'acd' option is set. */
445     DO_AUTOCHDIR
446 
447     /*
448      * Remove the buffer from the list.
449      */
450     if (wipe_buf)
451     {
452 #ifdef FEAT_SUN_WORKSHOP
453 	if (usingSunWorkShop)
454 	    workshop_file_closed_lineno((char *)buf->b_ffname,
455 			(int)buf->b_last_cursor.lnum);
456 #endif
457 	vim_free(buf->b_ffname);
458 	vim_free(buf->b_sfname);
459 	if (buf->b_prev == NULL)
460 	    firstbuf = buf->b_next;
461 	else
462 	    buf->b_prev->b_next = buf->b_next;
463 	if (buf->b_next == NULL)
464 	    lastbuf = buf->b_prev;
465 	else
466 	    buf->b_next->b_prev = buf->b_prev;
467 	free_buffer(buf);
468     }
469     else
470     {
471 	if (del_buf)
472 	{
473 	    /* Free all internal variables and reset option values, to make
474 	     * ":bdel" compatible with Vim 5.7. */
475 	    free_buffer_stuff(buf, TRUE);
476 
477 	    /* Make it look like a new buffer. */
478 	    buf->b_flags = BF_CHECK_RO | BF_NEVERLOADED;
479 
480 	    /* Init the options when loaded again. */
481 	    buf->b_p_initialized = FALSE;
482 	}
483 	buf_clear_file(buf);
484 	if (del_buf)
485 	    buf->b_p_bl = FALSE;
486     }
487 }
488 
489 /*
490  * Make buffer not contain a file.
491  */
492     void
493 buf_clear_file(buf)
494     buf_T	*buf;
495 {
496     buf->b_ml.ml_line_count = 1;
497     unchanged(buf, TRUE);
498 #ifndef SHORT_FNAME
499     buf->b_shortname = FALSE;
500 #endif
501     buf->b_p_eol = TRUE;
502     buf->b_start_eol = TRUE;
503 #ifdef FEAT_MBYTE
504     buf->b_p_bomb = FALSE;
505     buf->b_start_bomb = FALSE;
506 #endif
507     buf->b_ml.ml_mfp = NULL;
508     buf->b_ml.ml_flags = ML_EMPTY;		/* empty buffer */
509 #ifdef FEAT_NETBEANS_INTG
510     netbeans_deleted_all_lines(buf);
511 #endif
512 }
513 
514 /*
515  * buf_freeall() - free all things allocated for a buffer that are related to
516  * the file.
517  */
518 /*ARGSUSED*/
519     void
520 buf_freeall(buf, del_buf, wipe_buf)
521     buf_T	*buf;
522     int		del_buf;	/* buffer is going to be deleted */
523     int		wipe_buf;	/* buffer is going to be wiped out */
524 {
525 #ifdef FEAT_AUTOCMD
526     int		is_curbuf = (buf == curbuf);
527 
528     apply_autocmds(EVENT_BUFUNLOAD, buf->b_fname, buf->b_fname, FALSE, buf);
529     if (!buf_valid(buf))	    /* autocommands may delete the buffer */
530 	return;
531     if (del_buf && buf->b_p_bl)
532     {
533 	apply_autocmds(EVENT_BUFDELETE, buf->b_fname, buf->b_fname, FALSE, buf);
534 	if (!buf_valid(buf))	    /* autocommands may delete the buffer */
535 	    return;
536     }
537     if (wipe_buf)
538     {
539 	apply_autocmds(EVENT_BUFWIPEOUT, buf->b_fname, buf->b_fname,
540 								  FALSE, buf);
541 	if (!buf_valid(buf))	    /* autocommands may delete the buffer */
542 	    return;
543     }
544 # ifdef FEAT_EVAL
545     if (aborting())	    /* autocmds may abort script processing */
546 	return;
547 # endif
548 
549     /*
550      * It's possible that autocommands change curbuf to the one being deleted.
551      * This might cause curbuf to be deleted unexpectedly.  But in some cases
552      * it's OK to delete the curbuf, because a new one is obtained anyway.
553      * Therefore only return if curbuf changed to the deleted buffer.
554      */
555     if (buf == curbuf && !is_curbuf)
556 	return;
557 #endif
558 #ifdef FEAT_DIFF
559     diff_buf_delete(buf);	    /* Can't use 'diff' for unloaded buffer. */
560 #endif
561 
562 #ifdef FEAT_FOLDING
563     /* No folds in an empty buffer. */
564 # ifdef FEAT_WINDOWS
565     {
566 	win_T		*win;
567 	tabpage_T	*tp;
568 
569 	FOR_ALL_TAB_WINDOWS(tp, win)
570 	    if (win->w_buffer == buf)
571 		clearFolding(win);
572     }
573 # else
574     if (curwin->w_buffer == buf)
575 	clearFolding(curwin);
576 # endif
577 #endif
578 
579 #ifdef FEAT_TCL
580     tcl_buffer_free(buf);
581 #endif
582     u_blockfree(buf);		    /* free the memory allocated for undo */
583     ml_close(buf, TRUE);	    /* close and delete the memline/memfile */
584     buf->b_ml.ml_line_count = 0;    /* no lines in buffer */
585     u_clearall(buf);		    /* reset all undo information */
586 #ifdef FEAT_SYN_HL
587     syntax_clear(buf);		    /* reset syntax info */
588 #endif
589     buf->b_flags &= ~BF_READERR;    /* a read error is no longer relevant */
590 }
591 
592 /*
593  * Free a buffer structure and the things it contains related to the buffer
594  * itself (not the file, that must have been done already).
595  */
596     static void
597 free_buffer(buf)
598     buf_T	*buf;
599 {
600     free_buffer_stuff(buf, TRUE);
601 #ifdef FEAT_MZSCHEME
602     mzscheme_buffer_free(buf);
603 #endif
604 #ifdef FEAT_PERL
605     perl_buf_free(buf);
606 #endif
607 #ifdef FEAT_PYTHON
608     python_buffer_free(buf);
609 #endif
610 #ifdef FEAT_RUBY
611     ruby_buffer_free(buf);
612 #endif
613 #ifdef FEAT_AUTOCMD
614     aubuflocal_remove(buf);
615 #endif
616     vim_free(buf);
617 }
618 
619 /*
620  * Free stuff in the buffer for ":bdel" and when wiping out the buffer.
621  */
622     static void
623 free_buffer_stuff(buf, free_options)
624     buf_T	*buf;
625     int		free_options;		/* free options as well */
626 {
627     if (free_options)
628     {
629 	clear_wininfo(buf);		/* including window-local options */
630 	free_buf_options(buf, TRUE);
631     }
632 #ifdef FEAT_EVAL
633     vars_clear(&buf->b_vars.dv_hashtab); /* free all internal variables */
634     hash_init(&buf->b_vars.dv_hashtab);
635 #endif
636 #ifdef FEAT_USR_CMDS
637     uc_clear(&buf->b_ucmds);		/* clear local user commands */
638 #endif
639 #ifdef FEAT_SIGNS
640     buf_delete_signs(buf);		/* delete any signs */
641 #endif
642 #ifdef FEAT_LOCALMAP
643     map_clear_int(buf, MAP_ALL_MODES, TRUE, FALSE);  /* clear local mappings */
644     map_clear_int(buf, MAP_ALL_MODES, TRUE, TRUE);   /* clear local abbrevs */
645 #endif
646 #ifdef FEAT_MBYTE
647     vim_free(buf->b_start_fenc);
648     buf->b_start_fenc = NULL;
649 #endif
650 }
651 
652 /*
653  * Free the b_wininfo list for buffer "buf".
654  */
655     static void
656 clear_wininfo(buf)
657     buf_T	*buf;
658 {
659     wininfo_T	*wip;
660 
661     while (buf->b_wininfo != NULL)
662     {
663 	wip = buf->b_wininfo;
664 	buf->b_wininfo = wip->wi_next;
665 	if (wip->wi_optset)
666 	{
667 	    clear_winopt(&wip->wi_opt);
668 #ifdef FEAT_FOLDING
669 	    deleteFoldRecurse(&wip->wi_folds);
670 #endif
671 	}
672 	vim_free(wip);
673     }
674 }
675 
676 #if defined(FEAT_LISTCMDS) || defined(PROTO)
677 /*
678  * Go to another buffer.  Handles the result of the ATTENTION dialog.
679  */
680     void
681 goto_buffer(eap, start, dir, count)
682     exarg_T	*eap;
683     int		start;
684     int		dir;
685     int		count;
686 {
687 # if defined(FEAT_WINDOWS) && defined(HAS_SWAP_EXISTS_ACTION)
688     buf_T	*old_curbuf = curbuf;
689 
690     swap_exists_action = SEA_DIALOG;
691 # endif
692     (void)do_buffer(*eap->cmd == 's' ? DOBUF_SPLIT : DOBUF_GOTO,
693 					     start, dir, count, eap->forceit);
694 # if defined(FEAT_WINDOWS) && defined(HAS_SWAP_EXISTS_ACTION)
695     if (swap_exists_action == SEA_QUIT && *eap->cmd == 's')
696     {
697 #  if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
698 	cleanup_T   cs;
699 
700 	/* Reset the error/interrupt/exception state here so that
701 	 * aborting() returns FALSE when closing a window. */
702 	enter_cleanup(&cs);
703 #  endif
704 
705 	/* Quitting means closing the split window, nothing else. */
706 	win_close(curwin, TRUE);
707 	swap_exists_action = SEA_NONE;
708 	swap_exists_did_quit = TRUE;
709 
710 #  if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
711 	/* Restore the error/interrupt/exception state if not discarded by a
712 	 * new aborting error, interrupt, or uncaught exception. */
713 	leave_cleanup(&cs);
714 #  endif
715     }
716     else
717 	handle_swap_exists(old_curbuf);
718 # endif
719 }
720 #endif
721 
722 #if defined(HAS_SWAP_EXISTS_ACTION) || defined(PROTO)
723 /*
724  * Handle the situation of swap_exists_action being set.
725  * It is allowed for "old_curbuf" to be NULL or invalid.
726  */
727     void
728 handle_swap_exists(old_curbuf)
729     buf_T	*old_curbuf;
730 {
731 # if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
732     cleanup_T	cs;
733 # endif
734 
735     if (swap_exists_action == SEA_QUIT)
736     {
737 # if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
738 	/* Reset the error/interrupt/exception state here so that
739 	 * aborting() returns FALSE when closing a buffer. */
740 	enter_cleanup(&cs);
741 # endif
742 
743 	/* User selected Quit at ATTENTION prompt.  Go back to previous
744 	 * buffer.  If that buffer is gone or the same as the current one,
745 	 * open a new, empty buffer. */
746 	swap_exists_action = SEA_NONE;	/* don't want it again */
747 	swap_exists_did_quit = TRUE;
748 	close_buffer(curwin, curbuf, DOBUF_UNLOAD);
749 	if (!buf_valid(old_curbuf) || old_curbuf == curbuf)
750 	    old_curbuf = buflist_new(NULL, NULL, 1L, BLN_CURBUF | BLN_LISTED);
751 	if (old_curbuf != NULL)
752 	    enter_buffer(old_curbuf);
753 	/* If "old_curbuf" is NULL we are in big trouble here... */
754 
755 # if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
756 	/* Restore the error/interrupt/exception state if not discarded by a
757 	 * new aborting error, interrupt, or uncaught exception. */
758 	leave_cleanup(&cs);
759 # endif
760     }
761     else if (swap_exists_action == SEA_RECOVER)
762     {
763 # if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
764 	/* Reset the error/interrupt/exception state here so that
765 	 * aborting() returns FALSE when closing a buffer. */
766 	enter_cleanup(&cs);
767 # endif
768 
769 	/* User selected Recover at ATTENTION prompt. */
770 	msg_scroll = TRUE;
771 	ml_recover();
772 	MSG_PUTS("\n");	/* don't overwrite the last message */
773 	cmdline_row = msg_row;
774 	do_modelines(0);
775 
776 # if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
777 	/* Restore the error/interrupt/exception state if not discarded by a
778 	 * new aborting error, interrupt, or uncaught exception. */
779 	leave_cleanup(&cs);
780 # endif
781     }
782     swap_exists_action = SEA_NONE;
783 }
784 #endif
785 
786 #if defined(FEAT_LISTCMDS) || defined(PROTO)
787 /*
788  * do_bufdel() - delete or unload buffer(s)
789  *
790  * addr_count == 0: ":bdel" - delete current buffer
791  * addr_count == 1: ":N bdel" or ":bdel N [N ..]" - first delete
792  *		    buffer "end_bnr", then any other arguments.
793  * addr_count == 2: ":N,N bdel" - delete buffers in range
794  *
795  * command can be DOBUF_UNLOAD (":bunload"), DOBUF_WIPE (":bwipeout") or
796  * DOBUF_DEL (":bdel")
797  *
798  * Returns error message or NULL
799  */
800     char_u *
801 do_bufdel(command, arg, addr_count, start_bnr, end_bnr, forceit)
802     int		command;
803     char_u	*arg;		/* pointer to extra arguments */
804     int		addr_count;
805     int		start_bnr;	/* first buffer number in a range */
806     int		end_bnr;	/* buffer nr or last buffer nr in a range */
807     int		forceit;
808 {
809     int		do_current = 0;	/* delete current buffer? */
810     int		deleted = 0;	/* number of buffers deleted */
811     char_u	*errormsg = NULL; /* return value */
812     int		bnr;		/* buffer number */
813     char_u	*p;
814 
815 #ifdef FEAT_NETBEANS_INTG
816     netbeansCloseFile = 1;
817 #endif
818     if (addr_count == 0)
819     {
820 	(void)do_buffer(command, DOBUF_CURRENT, FORWARD, 0, forceit);
821     }
822     else
823     {
824 	if (addr_count == 2)
825 	{
826 	    if (*arg)		/* both range and argument is not allowed */
827 		return (char_u *)_(e_trailing);
828 	    bnr = start_bnr;
829 	}
830 	else	/* addr_count == 1 */
831 	    bnr = end_bnr;
832 
833 	for ( ;!got_int; ui_breakcheck())
834 	{
835 	    /*
836 	     * delete the current buffer last, otherwise when the
837 	     * current buffer is deleted, the next buffer becomes
838 	     * the current one and will be loaded, which may then
839 	     * also be deleted, etc.
840 	     */
841 	    if (bnr == curbuf->b_fnum)
842 		do_current = bnr;
843 	    else if (do_buffer(command, DOBUF_FIRST, FORWARD, (int)bnr,
844 							       forceit) == OK)
845 		++deleted;
846 
847 	    /*
848 	     * find next buffer number to delete/unload
849 	     */
850 	    if (addr_count == 2)
851 	    {
852 		if (++bnr > end_bnr)
853 		    break;
854 	    }
855 	    else    /* addr_count == 1 */
856 	    {
857 		arg = skipwhite(arg);
858 		if (*arg == NUL)
859 		    break;
860 		if (!VIM_ISDIGIT(*arg))
861 		{
862 		    p = skiptowhite_esc(arg);
863 		    bnr = buflist_findpat(arg, p, command == DOBUF_WIPE, FALSE);
864 		    if (bnr < 0)	    /* failed */
865 			break;
866 		    arg = p;
867 		}
868 		else
869 		    bnr = getdigits(&arg);
870 	    }
871 	}
872 	if (!got_int && do_current && do_buffer(command, DOBUF_FIRST,
873 					  FORWARD, do_current, forceit) == OK)
874 	    ++deleted;
875 
876 	if (deleted == 0)
877 	{
878 	    if (command == DOBUF_UNLOAD)
879 		STRCPY(IObuff, _("E515: No buffers were unloaded"));
880 	    else if (command == DOBUF_DEL)
881 		STRCPY(IObuff, _("E516: No buffers were deleted"));
882 	    else
883 		STRCPY(IObuff, _("E517: No buffers were wiped out"));
884 	    errormsg = IObuff;
885 	}
886 	else if (deleted >= p_report)
887 	{
888 	    if (command == DOBUF_UNLOAD)
889 	    {
890 		if (deleted == 1)
891 		    MSG(_("1 buffer unloaded"));
892 		else
893 		    smsg((char_u *)_("%d buffers unloaded"), deleted);
894 	    }
895 	    else if (command == DOBUF_DEL)
896 	    {
897 		if (deleted == 1)
898 		    MSG(_("1 buffer deleted"));
899 		else
900 		    smsg((char_u *)_("%d buffers deleted"), deleted);
901 	    }
902 	    else
903 	    {
904 		if (deleted == 1)
905 		    MSG(_("1 buffer wiped out"));
906 		else
907 		    smsg((char_u *)_("%d buffers wiped out"), deleted);
908 	    }
909 	}
910     }
911 
912 #ifdef FEAT_NETBEANS_INTG
913     netbeansCloseFile = 0;
914 #endif
915 
916     return errormsg;
917 }
918 
919 /*
920  * Implementation of the commands for the buffer list.
921  *
922  * action == DOBUF_GOTO	    go to specified buffer
923  * action == DOBUF_SPLIT    split window and go to specified buffer
924  * action == DOBUF_UNLOAD   unload specified buffer(s)
925  * action == DOBUF_DEL	    delete specified buffer(s) from buffer list
926  * action == DOBUF_WIPE	    delete specified buffer(s) really
927  *
928  * start == DOBUF_CURRENT   go to "count" buffer from current buffer
929  * start == DOBUF_FIRST	    go to "count" buffer from first buffer
930  * start == DOBUF_LAST	    go to "count" buffer from last buffer
931  * start == DOBUF_MOD	    go to "count" modified buffer from current buffer
932  *
933  * Return FAIL or OK.
934  */
935     int
936 do_buffer(action, start, dir, count, forceit)
937     int		action;
938     int		start;
939     int		dir;		/* FORWARD or BACKWARD */
940     int		count;		/* buffer number or number of buffers */
941     int		forceit;	/* TRUE for :...! */
942 {
943     buf_T	*buf;
944     buf_T	*bp;
945     int		unload = (action == DOBUF_UNLOAD || action == DOBUF_DEL
946 						     || action == DOBUF_WIPE);
947 
948     switch (start)
949     {
950 	case DOBUF_FIRST:   buf = firstbuf; break;
951 	case DOBUF_LAST:    buf = lastbuf;  break;
952 	default:	    buf = curbuf;   break;
953     }
954     if (start == DOBUF_MOD)	    /* find next modified buffer */
955     {
956 	while (count-- > 0)
957 	{
958 	    do
959 	    {
960 		buf = buf->b_next;
961 		if (buf == NULL)
962 		    buf = firstbuf;
963 	    }
964 	    while (buf != curbuf && !bufIsChanged(buf));
965 	}
966 	if (!bufIsChanged(buf))
967 	{
968 	    EMSG(_("E84: No modified buffer found"));
969 	    return FAIL;
970 	}
971     }
972     else if (start == DOBUF_FIRST && count) /* find specified buffer number */
973     {
974 	while (buf != NULL && buf->b_fnum != count)
975 	    buf = buf->b_next;
976     }
977     else
978     {
979 	bp = NULL;
980 	while (count > 0 || (!unload && !buf->b_p_bl && bp != buf))
981 	{
982 	    /* remember the buffer where we start, we come back there when all
983 	     * buffers are unlisted. */
984 	    if (bp == NULL)
985 		bp = buf;
986 	    if (dir == FORWARD)
987 	    {
988 		buf = buf->b_next;
989 		if (buf == NULL)
990 		    buf = firstbuf;
991 	    }
992 	    else
993 	    {
994 		buf = buf->b_prev;
995 		if (buf == NULL)
996 		    buf = lastbuf;
997 	    }
998 	    /* don't count unlisted buffers */
999 	    if (unload || buf->b_p_bl)
1000 	    {
1001 		 --count;
1002 		 bp = NULL;	/* use this buffer as new starting point */
1003 	    }
1004 	    if (bp == buf)
1005 	    {
1006 		/* back where we started, didn't find anything. */
1007 		EMSG(_("E85: There is no listed buffer"));
1008 		return FAIL;
1009 	    }
1010 	}
1011     }
1012 
1013     if (buf == NULL)	    /* could not find it */
1014     {
1015 	if (start == DOBUF_FIRST)
1016 	{
1017 	    /* don't warn when deleting */
1018 	    if (!unload)
1019 		EMSGN(_("E86: Buffer %ld does not exist"), count);
1020 	}
1021 	else if (dir == FORWARD)
1022 	    EMSG(_("E87: Cannot go beyond last buffer"));
1023 	else
1024 	    EMSG(_("E88: Cannot go before first buffer"));
1025 	return FAIL;
1026     }
1027 
1028 #ifdef FEAT_GUI
1029     need_mouse_correct = TRUE;
1030 #endif
1031 
1032 #ifdef FEAT_LISTCMDS
1033     /*
1034      * delete buffer buf from memory and/or the list
1035      */
1036     if (unload)
1037     {
1038 	int	forward;
1039 	int	retval;
1040 
1041 	/* When unloading or deleting a buffer that's already unloaded and
1042 	 * unlisted: fail silently. */
1043 	if (action != DOBUF_WIPE && buf->b_ml.ml_mfp == NULL && !buf->b_p_bl)
1044 	    return FAIL;
1045 
1046 	if (!forceit && bufIsChanged(buf))
1047 	{
1048 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
1049 	    if ((p_confirm || cmdmod.confirm) && p_write)
1050 	    {
1051 		dialog_changed(buf, FALSE);
1052 # ifdef FEAT_AUTOCMD
1053 		if (!buf_valid(buf))
1054 		    /* Autocommand deleted buffer, oops!  It's not changed
1055 		     * now. */
1056 		    return FAIL;
1057 # endif
1058 		/* If it's still changed fail silently, the dialog already
1059 		 * mentioned why it fails. */
1060 		if (bufIsChanged(buf))
1061 		    return FAIL;
1062 	    }
1063 	    else
1064 #endif
1065 	    {
1066 		EMSGN(_("E89: No write since last change for buffer %ld (add ! to override)"),
1067 								 buf->b_fnum);
1068 		return FAIL;
1069 	    }
1070 	}
1071 
1072 	/*
1073 	 * If deleting the last (listed) buffer, make it empty.
1074 	 * The last (listed) buffer cannot be unloaded.
1075 	 */
1076 	for (bp = firstbuf; bp != NULL; bp = bp->b_next)
1077 	    if (bp->b_p_bl && bp != buf)
1078 		break;
1079 	if (bp == NULL && buf == curbuf)
1080 	{
1081 	    if (action == DOBUF_UNLOAD)
1082 	    {
1083 		EMSG(_("E90: Cannot unload last buffer"));
1084 		return FAIL;
1085 	    }
1086 
1087 	    /* Close any other windows on this buffer, then make it empty. */
1088 #ifdef FEAT_WINDOWS
1089 	    close_windows(buf, TRUE);
1090 #endif
1091 	    setpcmark();
1092 	    retval = do_ecmd(0, NULL, NULL, NULL, ECMD_ONE,
1093 						  forceit ? ECMD_FORCEIT : 0);
1094 
1095 	    /*
1096 	     * do_ecmd() may create a new buffer, then we have to delete
1097 	     * the old one.  But do_ecmd() may have done that already, check
1098 	     * if the buffer still exists.
1099 	     */
1100 	    if (buf != curbuf && buf_valid(buf) && buf->b_nwindows == 0)
1101 		close_buffer(NULL, buf, action);
1102 	    return retval;
1103 	}
1104 
1105 #ifdef FEAT_WINDOWS
1106 	/*
1107 	 * If the deleted buffer is the current one, close the current window
1108 	 * (unless it's the only window).  Repeat this so long as we end up in
1109 	 * a window with this buffer.
1110 	 */
1111 	while (buf == curbuf
1112 		   && (firstwin != lastwin || first_tabpage->tp_next != NULL))
1113 	    win_close(curwin, FALSE);
1114 #endif
1115 
1116 	/*
1117 	 * If the buffer to be deleted is not the current one, delete it here.
1118 	 */
1119 	if (buf != curbuf)
1120 	{
1121 #ifdef FEAT_WINDOWS
1122 	    close_windows(buf, FALSE);
1123 #endif
1124 	    if (buf != curbuf && buf_valid(buf) && buf->b_nwindows <= 0)
1125 		close_buffer(NULL, buf, action);
1126 	    return OK;
1127 	}
1128 
1129 	/*
1130 	 * Deleting the current buffer: Need to find another buffer to go to.
1131 	 * There must be another, otherwise it would have been handled above.
1132 	 * First use au_new_curbuf, if it is valid.
1133 	 * Then prefer the buffer we most recently visited.
1134 	 * Else try to find one that is loaded, after the current buffer,
1135 	 * then before the current buffer.
1136 	 * Finally use any buffer.
1137 	 */
1138 	buf = NULL;	/* selected buffer */
1139 	bp = NULL;	/* used when no loaded buffer found */
1140 #ifdef FEAT_AUTOCMD
1141 	if (au_new_curbuf != NULL && buf_valid(au_new_curbuf))
1142 	    buf = au_new_curbuf;
1143 # ifdef FEAT_JUMPLIST
1144 	else
1145 # endif
1146 #endif
1147 #ifdef FEAT_JUMPLIST
1148 	    if (curwin->w_jumplistlen > 0)
1149 	{
1150 	    int     jumpidx;
1151 
1152 	    jumpidx = curwin->w_jumplistidx - 1;
1153 	    if (jumpidx < 0)
1154 		jumpidx = curwin->w_jumplistlen - 1;
1155 
1156 	    forward = jumpidx;
1157 	    while (jumpidx != curwin->w_jumplistidx)
1158 	    {
1159 		buf = buflist_findnr(curwin->w_jumplist[jumpidx].fmark.fnum);
1160 		if (buf != NULL)
1161 		{
1162 		    if (buf == curbuf || !buf->b_p_bl)
1163 			buf = NULL;	/* skip current and unlisted bufs */
1164 		    else if (buf->b_ml.ml_mfp == NULL)
1165 		    {
1166 			/* skip unloaded buf, but may keep it for later */
1167 			if (bp == NULL)
1168 			    bp = buf;
1169 			buf = NULL;
1170 		    }
1171 		}
1172 		if (buf != NULL)   /* found a valid buffer: stop searching */
1173 		    break;
1174 		/* advance to older entry in jump list */
1175 		if (!jumpidx && curwin->w_jumplistidx == curwin->w_jumplistlen)
1176 		    break;
1177 		if (--jumpidx < 0)
1178 		    jumpidx = curwin->w_jumplistlen - 1;
1179 		if (jumpidx == forward)		/* List exhausted for sure */
1180 		    break;
1181 	    }
1182 	}
1183 #endif
1184 
1185 	if (buf == NULL)	/* No previous buffer, Try 2'nd approach */
1186 	{
1187 	    forward = TRUE;
1188 	    buf = curbuf->b_next;
1189 	    for (;;)
1190 	    {
1191 		if (buf == NULL)
1192 		{
1193 		    if (!forward)	/* tried both directions */
1194 			break;
1195 		    buf = curbuf->b_prev;
1196 		    forward = FALSE;
1197 		    continue;
1198 		}
1199 		/* in non-help buffer, try to skip help buffers, and vv */
1200 		if (buf->b_help == curbuf->b_help && buf->b_p_bl)
1201 		{
1202 		    if (buf->b_ml.ml_mfp != NULL)   /* found loaded buffer */
1203 			break;
1204 		    if (bp == NULL)	/* remember unloaded buf for later */
1205 			bp = buf;
1206 		}
1207 		if (forward)
1208 		    buf = buf->b_next;
1209 		else
1210 		    buf = buf->b_prev;
1211 	    }
1212 	}
1213 	if (buf == NULL)	/* No loaded buffer, use unloaded one */
1214 	    buf = bp;
1215 	if (buf == NULL)	/* No loaded buffer, find listed one */
1216 	{
1217 	    for (buf = firstbuf; buf != NULL; buf = buf->b_next)
1218 		if (buf->b_p_bl && buf != curbuf)
1219 		    break;
1220 	}
1221 	if (buf == NULL)	/* Still no buffer, just take one */
1222 	{
1223 	    if (curbuf->b_next != NULL)
1224 		buf = curbuf->b_next;
1225 	    else
1226 		buf = curbuf->b_prev;
1227 	}
1228     }
1229 
1230     /*
1231      * make buf current buffer
1232      */
1233     if (action == DOBUF_SPLIT)	    /* split window first */
1234     {
1235 # ifdef FEAT_WINDOWS
1236 	/* If 'switchbuf' contains "useopen": jump to first window containing
1237 	 * "buf" if one exists */
1238 	if ((swb_flags & SWB_USEOPEN) && buf_jump_open_win(buf))
1239 	    return OK;
1240 	/* If 'switchbuf' contians "usetab": jump to first window in any tab
1241 	 * page containing "buf" if one exists */
1242 	if ((swb_flags & SWB_USETAB) && buf_jump_open_tab(buf))
1243 	    return OK;
1244 	if (win_split(0, 0) == FAIL)
1245 # endif
1246 	    return FAIL;
1247     }
1248 #endif
1249 
1250     /* go to current buffer - nothing to do */
1251     if (buf == curbuf)
1252 	return OK;
1253 
1254     /*
1255      * Check if the current buffer may be abandoned.
1256      */
1257     if (action == DOBUF_GOTO && !can_abandon(curbuf, forceit))
1258     {
1259 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
1260 	if ((p_confirm || cmdmod.confirm) && p_write)
1261 	{
1262 	    dialog_changed(curbuf, FALSE);
1263 # ifdef FEAT_AUTOCMD
1264 	    if (!buf_valid(buf))
1265 		/* Autocommand deleted buffer, oops! */
1266 		return FAIL;
1267 # endif
1268 	}
1269 	if (bufIsChanged(curbuf))
1270 #endif
1271 	{
1272 	    EMSG(_(e_nowrtmsg));
1273 	    return FAIL;
1274 	}
1275     }
1276 
1277     /* Go to the other buffer. */
1278     set_curbuf(buf, action);
1279 
1280 #if defined(FEAT_LISTCMDS) && defined(FEAT_SCROLLBIND)
1281     if (action == DOBUF_SPLIT)
1282 	curwin->w_p_scb = FALSE;	/* reset 'scrollbind' */
1283 #endif
1284 
1285 #if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
1286     if (aborting())	    /* autocmds may abort script processing */
1287 	return FAIL;
1288 #endif
1289 
1290     return OK;
1291 }
1292 
1293 #endif /* FEAT_LISTCMDS */
1294 
1295 /*
1296  * Set current buffer to "buf".  Executes autocommands and closes current
1297  * buffer.  "action" tells how to close the current buffer:
1298  * DOBUF_GOTO	    free or hide it
1299  * DOBUF_SPLIT	    nothing
1300  * DOBUF_UNLOAD	    unload it
1301  * DOBUF_DEL	    delete it
1302  * DOBUF_WIPE	    wipe it out
1303  */
1304     void
1305 set_curbuf(buf, action)
1306     buf_T	*buf;
1307     int		action;
1308 {
1309     buf_T	*prevbuf;
1310     int		unload = (action == DOBUF_UNLOAD || action == DOBUF_DEL
1311 						     || action == DOBUF_WIPE);
1312 
1313     setpcmark();
1314     if (!cmdmod.keepalt)
1315 	curwin->w_alt_fnum = curbuf->b_fnum; /* remember alternate file */
1316     buflist_altfpos();			 /* remember curpos */
1317 
1318 #ifdef FEAT_VISUAL
1319     /* Don't restart Select mode after switching to another buffer. */
1320     VIsual_reselect = FALSE;
1321 #endif
1322 
1323     /* close_windows() or apply_autocmds() may change curbuf */
1324     prevbuf = curbuf;
1325 
1326 #ifdef FEAT_AUTOCMD
1327     apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf);
1328 # ifdef FEAT_EVAL
1329     if (buf_valid(prevbuf) && !aborting())
1330 # else
1331     if (buf_valid(prevbuf))
1332 # endif
1333 #endif
1334     {
1335 #ifdef FEAT_WINDOWS
1336 	if (unload)
1337 	    close_windows(prevbuf, FALSE);
1338 #endif
1339 #if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
1340 	if (buf_valid(prevbuf) && !aborting())
1341 #else
1342 	if (buf_valid(prevbuf))
1343 #endif
1344 	{
1345 	    if (prevbuf == curbuf)
1346 		u_sync(FALSE);
1347 	    close_buffer(prevbuf == curwin->w_buffer ? curwin : NULL, prevbuf,
1348 		    unload ? action : (action == DOBUF_GOTO
1349 			&& !P_HID(prevbuf)
1350 			&& !bufIsChanged(prevbuf)) ? DOBUF_UNLOAD : 0);
1351 	}
1352     }
1353 #ifdef FEAT_AUTOCMD
1354 # ifdef FEAT_EVAL
1355     /* An autocommand may have deleted buf or aborted the script processing! */
1356     if (buf_valid(buf) && !aborting())
1357 # else
1358     if (buf_valid(buf))	    /* an autocommand may have deleted buf! */
1359 # endif
1360 #endif
1361 	enter_buffer(buf);
1362 }
1363 
1364 /*
1365  * Enter a new current buffer.
1366  * Old curbuf must have been abandoned already!
1367  */
1368     void
1369 enter_buffer(buf)
1370     buf_T	*buf;
1371 {
1372     /* Copy buffer and window local option values.  Not for a help buffer. */
1373     buf_copy_options(buf, BCO_ENTER | BCO_NOHELP);
1374     if (!buf->b_help)
1375 	get_winopts(buf);
1376 #ifdef FEAT_FOLDING
1377     else
1378 	/* Remove all folds in the window. */
1379 	clearFolding(curwin);
1380     foldUpdateAll(curwin);	/* update folds (later). */
1381 #endif
1382 
1383     /* Get the buffer in the current window. */
1384     curwin->w_buffer = buf;
1385     curbuf = buf;
1386     ++curbuf->b_nwindows;
1387 
1388 #ifdef FEAT_DIFF
1389     if (curwin->w_p_diff)
1390 	diff_buf_add(curbuf);
1391 #endif
1392 
1393     /* Cursor on first line by default. */
1394     curwin->w_cursor.lnum = 1;
1395     curwin->w_cursor.col = 0;
1396 #ifdef FEAT_VIRTUALEDIT
1397     curwin->w_cursor.coladd = 0;
1398 #endif
1399     curwin->w_set_curswant = TRUE;
1400 
1401     /* Make sure the buffer is loaded. */
1402     if (curbuf->b_ml.ml_mfp == NULL)	/* need to load the file */
1403     {
1404 #ifdef FEAT_AUTOCMD
1405 	/* If there is no filetype, allow for detecting one.  Esp. useful for
1406 	 * ":ball" used in a autocommand.  If there already is a filetype we
1407 	 * might prefer to keep it. */
1408 	if (*curbuf->b_p_ft == NUL)
1409 	    did_filetype = FALSE;
1410 #endif
1411 
1412 	open_buffer(FALSE, NULL);
1413     }
1414     else
1415     {
1416 	if (!msg_silent)
1417 	    need_fileinfo = TRUE;	/* display file info after redraw */
1418 	(void)buf_check_timestamp(curbuf, FALSE); /* check if file changed */
1419 #ifdef FEAT_AUTOCMD
1420 	curwin->w_topline = 1;
1421 # ifdef FEAT_DIFF
1422 	curwin->w_topfill = 0;
1423 # endif
1424 	apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
1425 	apply_autocmds(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf);
1426 #endif
1427     }
1428 
1429     /* If autocommands did not change the cursor position, restore cursor lnum
1430      * and possibly cursor col. */
1431     if (curwin->w_cursor.lnum == 1 && inindent(0))
1432 	buflist_getfpos();
1433 
1434     check_arg_idx(curwin);		/* check for valid arg_idx */
1435 #ifdef FEAT_TITLE
1436     maketitle();
1437 #endif
1438 #ifdef FEAT_AUTOCMD
1439     if (curwin->w_topline == 1)		/* when autocmds didn't change it */
1440 #endif
1441 	scroll_cursor_halfway(FALSE);	/* redisplay at correct position */
1442 
1443 #ifdef FEAT_NETBEANS_INTG
1444     /* Send fileOpened event because we've changed buffers. */
1445     if (usingNetbeans && isNetbeansBuffer(curbuf))
1446 	netbeans_file_activated(curbuf);
1447 #endif
1448 
1449     /* Change directories when the 'acd' option is set. */
1450     DO_AUTOCHDIR
1451 
1452 #ifdef FEAT_KEYMAP
1453     if (curbuf->b_kmap_state & KEYMAP_INIT)
1454 	keymap_init();
1455 #endif
1456 #ifdef FEAT_SPELL
1457     /* May need to set the spell language.  Can only do this after the buffer
1458      * has been properly setup. */
1459     if (!curbuf->b_help && curwin->w_p_spell && *curbuf->b_p_spl != NUL)
1460 	did_set_spelllang(curbuf);
1461 #endif
1462 
1463     redraw_later(NOT_VALID);
1464 }
1465 
1466 #if defined(FEAT_AUTOCHDIR) || defined(PROTO)
1467 /*
1468  * Change to the directory of the current buffer.
1469  */
1470     void
1471 do_autochdir()
1472 {
1473     if (curbuf->b_ffname != NULL && vim_chdirfile(curbuf->b_ffname) == OK)
1474 	shorten_fnames(TRUE);
1475 }
1476 #endif
1477 
1478 /*
1479  * functions for dealing with the buffer list
1480  */
1481 
1482 /*
1483  * Add a file name to the buffer list.  Return a pointer to the buffer.
1484  * If the same file name already exists return a pointer to that buffer.
1485  * If it does not exist, or if fname == NULL, a new entry is created.
1486  * If (flags & BLN_CURBUF) is TRUE, may use current buffer.
1487  * If (flags & BLN_LISTED) is TRUE, add new buffer to buffer list.
1488  * If (flags & BLN_DUMMY) is TRUE, don't count it as a real buffer.
1489  * This is the ONLY way to create a new buffer.
1490  */
1491 static int  top_file_num = 1;		/* highest file number */
1492 
1493     buf_T *
1494 buflist_new(ffname, sfname, lnum, flags)
1495     char_u	*ffname;	/* full path of fname or relative */
1496     char_u	*sfname;	/* short fname or NULL */
1497     linenr_T	lnum;		/* preferred cursor line */
1498     int		flags;		/* BLN_ defines */
1499 {
1500     buf_T	*buf;
1501 #ifdef UNIX
1502     struct stat	st;
1503 #endif
1504 
1505     fname_expand(curbuf, &ffname, &sfname);	/* will allocate ffname */
1506 
1507     /*
1508      * If file name already exists in the list, update the entry.
1509      */
1510 #ifdef UNIX
1511     /* On Unix we can use inode numbers when the file exists.  Works better
1512      * for hard links. */
1513     if (sfname == NULL || mch_stat((char *)sfname, &st) < 0)
1514 	st.st_dev = (dev_T)-1;
1515 #endif
1516     if (ffname != NULL && !(flags & BLN_DUMMY) && (buf =
1517 #ifdef UNIX
1518 		buflist_findname_stat(ffname, &st)
1519 #else
1520 		buflist_findname(ffname)
1521 #endif
1522 		) != NULL)
1523     {
1524 	vim_free(ffname);
1525 	if (lnum != 0)
1526 	    buflist_setfpos(buf, curwin, lnum, (colnr_T)0, FALSE);
1527 	/* copy the options now, if 'cpo' doesn't have 's' and not done
1528 	 * already */
1529 	buf_copy_options(buf, 0);
1530 	if ((flags & BLN_LISTED) && !buf->b_p_bl)
1531 	{
1532 	    buf->b_p_bl = TRUE;
1533 #ifdef FEAT_AUTOCMD
1534 	    if (!(flags & BLN_DUMMY))
1535 		apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, buf);
1536 #endif
1537 	}
1538 	return buf;
1539     }
1540 
1541     /*
1542      * If the current buffer has no name and no contents, use the current
1543      * buffer.	Otherwise: Need to allocate a new buffer structure.
1544      *
1545      * This is the ONLY place where a new buffer structure is allocated!
1546      * (A spell file buffer is allocated in spell.c, but that's not a normal
1547      * buffer.)
1548      */
1549     buf = NULL;
1550     if ((flags & BLN_CURBUF)
1551 	    && curbuf != NULL
1552 	    && curbuf->b_ffname == NULL
1553 	    && curbuf->b_nwindows <= 1
1554 	    && (curbuf->b_ml.ml_mfp == NULL || bufempty()))
1555     {
1556 	buf = curbuf;
1557 #ifdef FEAT_AUTOCMD
1558 	/* It's like this buffer is deleted.  Watch out for autocommands that
1559 	 * change curbuf!  If that happens, allocate a new buffer anyway. */
1560 	if (curbuf->b_p_bl)
1561 	    apply_autocmds(EVENT_BUFDELETE, NULL, NULL, FALSE, curbuf);
1562 	if (buf == curbuf)
1563 	    apply_autocmds(EVENT_BUFWIPEOUT, NULL, NULL, FALSE, curbuf);
1564 # ifdef FEAT_EVAL
1565 	if (aborting())		/* autocmds may abort script processing */
1566 	    return NULL;
1567 # endif
1568 #endif
1569 #ifdef FEAT_QUICKFIX
1570 # ifdef FEAT_AUTOCMD
1571 	if (buf == curbuf)
1572 # endif
1573 	{
1574 	    /* Make sure 'bufhidden' and 'buftype' are empty */
1575 	    clear_string_option(&buf->b_p_bh);
1576 	    clear_string_option(&buf->b_p_bt);
1577 	}
1578 #endif
1579     }
1580     if (buf != curbuf || curbuf == NULL)
1581     {
1582 	buf = (buf_T *)alloc_clear((unsigned)sizeof(buf_T));
1583 	if (buf == NULL)
1584 	{
1585 	    vim_free(ffname);
1586 	    return NULL;
1587 	}
1588     }
1589 
1590     if (ffname != NULL)
1591     {
1592 	buf->b_ffname = ffname;
1593 	buf->b_sfname = vim_strsave(sfname);
1594     }
1595 
1596     clear_wininfo(buf);
1597     buf->b_wininfo = (wininfo_T *)alloc_clear((unsigned)sizeof(wininfo_T));
1598 
1599     if ((ffname != NULL && (buf->b_ffname == NULL || buf->b_sfname == NULL))
1600 	    || buf->b_wininfo == NULL)
1601     {
1602 	vim_free(buf->b_ffname);
1603 	buf->b_ffname = NULL;
1604 	vim_free(buf->b_sfname);
1605 	buf->b_sfname = NULL;
1606 	if (buf != curbuf)
1607 	    free_buffer(buf);
1608 	return NULL;
1609     }
1610 
1611     if (buf == curbuf)
1612     {
1613 	/* free all things allocated for this buffer */
1614 	buf_freeall(buf, FALSE, FALSE);
1615 	if (buf != curbuf)	 /* autocommands deleted the buffer! */
1616 	    return NULL;
1617 #if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
1618 	if (aborting())		/* autocmds may abort script processing */
1619 	    return NULL;
1620 #endif
1621 	/* buf->b_nwindows = 0; why was this here? */
1622 	free_buffer_stuff(buf, FALSE);	/* delete local variables et al. */
1623 #ifdef FEAT_KEYMAP
1624 	/* need to reload lmaps and set b:keymap_name */
1625 	curbuf->b_kmap_state |= KEYMAP_INIT;
1626 #endif
1627     }
1628     else
1629     {
1630 	/*
1631 	 * put new buffer at the end of the buffer list
1632 	 */
1633 	buf->b_next = NULL;
1634 	if (firstbuf == NULL)		/* buffer list is empty */
1635 	{
1636 	    buf->b_prev = NULL;
1637 	    firstbuf = buf;
1638 	}
1639 	else				/* append new buffer at end of list */
1640 	{
1641 	    lastbuf->b_next = buf;
1642 	    buf->b_prev = lastbuf;
1643 	}
1644 	lastbuf = buf;
1645 
1646 	buf->b_fnum = top_file_num++;
1647 	if (top_file_num < 0)		/* wrap around (may cause duplicates) */
1648 	{
1649 	    EMSG(_("W14: Warning: List of file names overflow"));
1650 	    if (emsg_silent == 0)
1651 	    {
1652 		out_flush();
1653 		ui_delay(3000L, TRUE);	/* make sure it is noticed */
1654 	    }
1655 	    top_file_num = 1;
1656 	}
1657 
1658 	/*
1659 	 * Always copy the options from the current buffer.
1660 	 */
1661 	buf_copy_options(buf, BCO_ALWAYS);
1662     }
1663 
1664     buf->b_wininfo->wi_fpos.lnum = lnum;
1665     buf->b_wininfo->wi_win = curwin;
1666 
1667 #ifdef FEAT_EVAL
1668     init_var_dict(&buf->b_vars, &buf->b_bufvar);    /* init b: variables */
1669 #endif
1670 #ifdef FEAT_SYN_HL
1671     hash_init(&buf->b_keywtab);
1672     hash_init(&buf->b_keywtab_ic);
1673 #endif
1674 
1675     buf->b_fname = buf->b_sfname;
1676 #ifdef UNIX
1677     if (st.st_dev == (dev_T)-1)
1678 	buf->b_dev = -1;
1679     else
1680     {
1681 	buf->b_dev = st.st_dev;
1682 	buf->b_ino = st.st_ino;
1683     }
1684 #endif
1685     buf->b_u_synced = TRUE;
1686     buf->b_flags = BF_CHECK_RO | BF_NEVERLOADED;
1687     if (flags & BLN_DUMMY)
1688 	buf->b_flags |= BF_DUMMY;
1689     buf_clear_file(buf);
1690     clrallmarks(buf);			/* clear marks */
1691     fmarks_check_names(buf);		/* check file marks for this file */
1692     buf->b_p_bl = (flags & BLN_LISTED) ? TRUE : FALSE;	/* init 'buflisted' */
1693 #ifdef FEAT_AUTOCMD
1694     if (!(flags & BLN_DUMMY))
1695     {
1696 	apply_autocmds(EVENT_BUFNEW, NULL, NULL, FALSE, buf);
1697 	if (flags & BLN_LISTED)
1698 	    apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, buf);
1699 # ifdef FEAT_EVAL
1700 	if (aborting())		/* autocmds may abort script processing */
1701 	    return NULL;
1702 # endif
1703     }
1704 #endif
1705 
1706     return buf;
1707 }
1708 
1709 /*
1710  * Free the memory for the options of a buffer.
1711  * If "free_p_ff" is TRUE also free 'fileformat', 'buftype' and
1712  * 'fileencoding'.
1713  */
1714     void
1715 free_buf_options(buf, free_p_ff)
1716     buf_T	*buf;
1717     int		free_p_ff;
1718 {
1719     if (free_p_ff)
1720     {
1721 #ifdef FEAT_MBYTE
1722 	clear_string_option(&buf->b_p_fenc);
1723 #endif
1724 	clear_string_option(&buf->b_p_ff);
1725 #ifdef FEAT_QUICKFIX
1726 	clear_string_option(&buf->b_p_bh);
1727 	clear_string_option(&buf->b_p_bt);
1728 #endif
1729     }
1730 #ifdef FEAT_FIND_ID
1731     clear_string_option(&buf->b_p_def);
1732     clear_string_option(&buf->b_p_inc);
1733 # ifdef FEAT_EVAL
1734     clear_string_option(&buf->b_p_inex);
1735 # endif
1736 #endif
1737 #if defined(FEAT_CINDENT) && defined(FEAT_EVAL)
1738     clear_string_option(&buf->b_p_inde);
1739     clear_string_option(&buf->b_p_indk);
1740 #endif
1741 #if defined(FEAT_BEVAL) && defined(FEAT_EVAL)
1742     clear_string_option(&buf->b_p_bexpr);
1743 #endif
1744 #if defined(FEAT_EVAL)
1745     clear_string_option(&buf->b_p_fex);
1746 #endif
1747 #ifdef FEAT_CRYPT
1748     clear_string_option(&buf->b_p_key);
1749 #endif
1750     clear_string_option(&buf->b_p_kp);
1751     clear_string_option(&buf->b_p_mps);
1752     clear_string_option(&buf->b_p_fo);
1753     clear_string_option(&buf->b_p_flp);
1754     clear_string_option(&buf->b_p_isk);
1755 #ifdef FEAT_KEYMAP
1756     clear_string_option(&buf->b_p_keymap);
1757     ga_clear(&buf->b_kmap_ga);
1758 #endif
1759 #ifdef FEAT_COMMENTS
1760     clear_string_option(&buf->b_p_com);
1761 #endif
1762 #ifdef FEAT_FOLDING
1763     clear_string_option(&buf->b_p_cms);
1764 #endif
1765     clear_string_option(&buf->b_p_nf);
1766 #ifdef FEAT_SYN_HL
1767     clear_string_option(&buf->b_p_syn);
1768 #endif
1769 #ifdef FEAT_SPELL
1770     clear_string_option(&buf->b_p_spc);
1771     clear_string_option(&buf->b_p_spf);
1772     vim_free(buf->b_cap_prog);
1773     buf->b_cap_prog = NULL;
1774     clear_string_option(&buf->b_p_spl);
1775 #endif
1776 #ifdef FEAT_SEARCHPATH
1777     clear_string_option(&buf->b_p_sua);
1778 #endif
1779 #ifdef FEAT_AUTOCMD
1780     clear_string_option(&buf->b_p_ft);
1781 #endif
1782 #ifdef FEAT_OSFILETYPE
1783     clear_string_option(&buf->b_p_oft);
1784 #endif
1785 #ifdef FEAT_CINDENT
1786     clear_string_option(&buf->b_p_cink);
1787     clear_string_option(&buf->b_p_cino);
1788 #endif
1789 #if defined(FEAT_CINDENT) || defined(FEAT_SMARTINDENT)
1790     clear_string_option(&buf->b_p_cinw);
1791 #endif
1792 #ifdef FEAT_INS_EXPAND
1793     clear_string_option(&buf->b_p_cpt);
1794 #endif
1795 #ifdef FEAT_COMPL_FUNC
1796     clear_string_option(&buf->b_p_cfu);
1797     clear_string_option(&buf->b_p_ofu);
1798 #endif
1799 #ifdef FEAT_QUICKFIX
1800     clear_string_option(&buf->b_p_gp);
1801     clear_string_option(&buf->b_p_mp);
1802     clear_string_option(&buf->b_p_efm);
1803 #endif
1804     clear_string_option(&buf->b_p_ep);
1805     clear_string_option(&buf->b_p_path);
1806     clear_string_option(&buf->b_p_tags);
1807 #ifdef FEAT_INS_EXPAND
1808     clear_string_option(&buf->b_p_dict);
1809     clear_string_option(&buf->b_p_tsr);
1810 #endif
1811 #ifdef FEAT_TEXTOBJ
1812     clear_string_option(&buf->b_p_qe);
1813 #endif
1814     buf->b_p_ar = -1;
1815 }
1816 
1817 /*
1818  * get alternate file n
1819  * set linenr to lnum or altfpos.lnum if lnum == 0
1820  *	also set cursor column to altfpos.col if 'startofline' is not set.
1821  * if (options & GETF_SETMARK) call setpcmark()
1822  * if (options & GETF_ALT) we are jumping to an alternate file.
1823  * if (options & GETF_SWITCH) respect 'switchbuf' settings when jumping
1824  *
1825  * return FAIL for failure, OK for success
1826  */
1827     int
1828 buflist_getfile(n, lnum, options, forceit)
1829     int		n;
1830     linenr_T	lnum;
1831     int		options;
1832     int		forceit;
1833 {
1834     buf_T	*buf;
1835 #ifdef FEAT_WINDOWS
1836     win_T	*wp = NULL;
1837 #endif
1838     pos_T	*fpos;
1839     colnr_T	col;
1840 
1841     buf = buflist_findnr(n);
1842     if (buf == NULL)
1843     {
1844 	if ((options & GETF_ALT) && n == 0)
1845 	    EMSG(_(e_noalt));
1846 	else
1847 	    EMSGN(_("E92: Buffer %ld not found"), n);
1848 	return FAIL;
1849     }
1850 
1851     /* if alternate file is the current buffer, nothing to do */
1852     if (buf == curbuf)
1853 	return OK;
1854 
1855     if (text_locked())
1856     {
1857 	text_locked_msg();
1858 	return FAIL;
1859     }
1860 #ifdef FEAT_AUTOCMD
1861     if (curbuf_locked())
1862 	return FAIL;
1863 #endif
1864 
1865     /* altfpos may be changed by getfile(), get it now */
1866     if (lnum == 0)
1867     {
1868 	fpos = buflist_findfpos(buf);
1869 	lnum = fpos->lnum;
1870 	col = fpos->col;
1871     }
1872     else
1873 	col = 0;
1874 
1875 #ifdef FEAT_WINDOWS
1876     if (options & GETF_SWITCH)
1877     {
1878 	/* If 'switchbuf' contains "useopen": jump to first window containing
1879 	 * "buf" if one exists */
1880 	if (swb_flags & SWB_USEOPEN)
1881 	    wp = buf_jump_open_win(buf);
1882 	/* If 'switchbuf' contians "usetab": jump to first window in any tab
1883 	 * page containing "buf" if one exists */
1884 	if (wp == NULL && (swb_flags & SWB_USETAB))
1885 	    wp = buf_jump_open_tab(buf);
1886 	/* If 'switchbuf' contains "split" or "newtab" and the current buffer
1887 	 * isn't empty: open new window */
1888 	if (wp == NULL && (swb_flags & (SWB_SPLIT | SWB_NEWTAB)) && !bufempty())
1889 	{
1890 	    if (swb_flags & SWB_NEWTAB)		/* Open in a new tab */
1891 		tabpage_new();
1892 	    else if (win_split(0, 0) == FAIL)	/* Open in a new window */
1893 		return FAIL;
1894 # ifdef FEAT_SCROLLBIND
1895 	    curwin->w_p_scb = FALSE;
1896 # endif
1897 	}
1898     }
1899 #endif
1900 
1901     ++RedrawingDisabled;
1902     if (getfile(buf->b_fnum, NULL, NULL, (options & GETF_SETMARK),
1903 							  lnum, forceit) <= 0)
1904     {
1905 	--RedrawingDisabled;
1906 
1907 	/* cursor is at to BOL and w_cursor.lnum is checked due to getfile() */
1908 	if (!p_sol && col != 0)
1909 	{
1910 	    curwin->w_cursor.col = col;
1911 	    check_cursor_col();
1912 #ifdef FEAT_VIRTUALEDIT
1913 	    curwin->w_cursor.coladd = 0;
1914 #endif
1915 	    curwin->w_set_curswant = TRUE;
1916 	}
1917 	return OK;
1918     }
1919     --RedrawingDisabled;
1920     return FAIL;
1921 }
1922 
1923 /*
1924  * go to the last know line number for the current buffer
1925  */
1926     void
1927 buflist_getfpos()
1928 {
1929     pos_T	*fpos;
1930 
1931     fpos = buflist_findfpos(curbuf);
1932 
1933     curwin->w_cursor.lnum = fpos->lnum;
1934     check_cursor_lnum();
1935 
1936     if (p_sol)
1937 	curwin->w_cursor.col = 0;
1938     else
1939     {
1940 	curwin->w_cursor.col = fpos->col;
1941 	check_cursor_col();
1942 #ifdef FEAT_VIRTUALEDIT
1943 	curwin->w_cursor.coladd = 0;
1944 #endif
1945 	curwin->w_set_curswant = TRUE;
1946     }
1947 }
1948 
1949 #if defined(FEAT_QUICKFIX) || defined(FEAT_EVAL) || defined(PROTO)
1950 /*
1951  * Find file in buffer list by name (it has to be for the current window).
1952  * Returns NULL if not found.
1953  */
1954     buf_T *
1955 buflist_findname_exp(fname)
1956     char_u *fname;
1957 {
1958     char_u	*ffname;
1959     buf_T	*buf = NULL;
1960 
1961     /* First make the name into a full path name */
1962     ffname = FullName_save(fname,
1963 #ifdef UNIX
1964 	    TRUE	    /* force expansion, get rid of symbolic links */
1965 #else
1966 	    FALSE
1967 #endif
1968 	    );
1969     if (ffname != NULL)
1970     {
1971 	buf = buflist_findname(ffname);
1972 	vim_free(ffname);
1973     }
1974     return buf;
1975 }
1976 #endif
1977 
1978 /*
1979  * Find file in buffer list by name (it has to be for the current window).
1980  * "ffname" must have a full path.
1981  * Skips dummy buffers.
1982  * Returns NULL if not found.
1983  */
1984     buf_T *
1985 buflist_findname(ffname)
1986     char_u	*ffname;
1987 {
1988 #ifdef UNIX
1989     struct stat st;
1990 
1991     if (mch_stat((char *)ffname, &st) < 0)
1992 	st.st_dev = (dev_T)-1;
1993     return buflist_findname_stat(ffname, &st);
1994 }
1995 
1996 /*
1997  * Same as buflist_findname(), but pass the stat structure to avoid getting it
1998  * twice for the same file.
1999  * Returns NULL if not found.
2000  */
2001     static buf_T *
2002 buflist_findname_stat(ffname, stp)
2003     char_u	*ffname;
2004     struct stat	*stp;
2005 {
2006 #endif
2007     buf_T	*buf;
2008 
2009     for (buf = firstbuf; buf != NULL; buf = buf->b_next)
2010 	if ((buf->b_flags & BF_DUMMY) == 0 && !otherfile_buf(buf, ffname
2011 #ifdef UNIX
2012 		    , stp
2013 #endif
2014 		    ))
2015 	    return buf;
2016     return NULL;
2017 }
2018 
2019 #if defined(FEAT_LISTCMDS) || defined(FEAT_EVAL) || defined(FEAT_PERL) || defined(PROTO)
2020 /*
2021  * Find file in buffer list by a regexp pattern.
2022  * Return fnum of the found buffer.
2023  * Return < 0 for error.
2024  */
2025 /*ARGSUSED*/
2026     int
2027 buflist_findpat(pattern, pattern_end, unlisted, diffmode)
2028     char_u	*pattern;
2029     char_u	*pattern_end;	/* pointer to first char after pattern */
2030     int		unlisted;	/* find unlisted buffers */
2031     int		diffmode;	/* find diff-mode buffers only */
2032 {
2033     buf_T	*buf;
2034     regprog_T	*prog;
2035     int		match = -1;
2036     int		find_listed;
2037     char_u	*pat;
2038     char_u	*patend;
2039     int		attempt;
2040     char_u	*p;
2041     int		toggledollar;
2042 
2043     if (pattern_end == pattern + 1 && (*pattern == '%' || *pattern == '#'))
2044     {
2045 	if (*pattern == '%')
2046 	    match = curbuf->b_fnum;
2047 	else
2048 	    match = curwin->w_alt_fnum;
2049 #ifdef FEAT_DIFF
2050 	if (diffmode && !diff_mode_buf(buflist_findnr(match)))
2051 	    match = -1;
2052 #endif
2053     }
2054 
2055     /*
2056      * Try four ways of matching a listed buffer:
2057      * attempt == 0: without '^' or '$' (at any position)
2058      * attempt == 1: with '^' at start (only at position 0)
2059      * attempt == 2: with '$' at end (only match at end)
2060      * attempt == 3: with '^' at start and '$' at end (only full match)
2061      * Repeat this for finding an unlisted buffer if there was no matching
2062      * listed buffer.
2063      */
2064     else
2065     {
2066 	pat = file_pat_to_reg_pat(pattern, pattern_end, NULL, FALSE);
2067 	if (pat == NULL)
2068 	    return -1;
2069 	patend = pat + STRLEN(pat) - 1;
2070 	toggledollar = (patend > pat && *patend == '$');
2071 
2072 	/* First try finding a listed buffer.  If not found and "unlisted"
2073 	 * is TRUE, try finding an unlisted buffer. */
2074 	find_listed = TRUE;
2075 	for (;;)
2076 	{
2077 	    for (attempt = 0; attempt <= 3; ++attempt)
2078 	    {
2079 		/* may add '^' and '$' */
2080 		if (toggledollar)
2081 		    *patend = (attempt < 2) ? NUL : '$'; /* add/remove '$' */
2082 		p = pat;
2083 		if (*p == '^' && !(attempt & 1))	 /* add/remove '^' */
2084 		    ++p;
2085 		prog = vim_regcomp(p, p_magic ? RE_MAGIC : 0);
2086 		if (prog == NULL)
2087 		{
2088 		    vim_free(pat);
2089 		    return -1;
2090 		}
2091 
2092 		for (buf = firstbuf; buf != NULL; buf = buf->b_next)
2093 		    if (buf->b_p_bl == find_listed
2094 #ifdef FEAT_DIFF
2095 			    && (!diffmode || diff_mode_buf(buf))
2096 #endif
2097 			    && buflist_match(prog, buf) != NULL)
2098 		    {
2099 			if (match >= 0)		/* already found a match */
2100 			{
2101 			    match = -2;
2102 			    break;
2103 			}
2104 			match = buf->b_fnum;	/* remember first match */
2105 		    }
2106 
2107 		vim_free(prog);
2108 		if (match >= 0)			/* found one match */
2109 		    break;
2110 	    }
2111 
2112 	    /* Only search for unlisted buffers if there was no match with
2113 	     * a listed buffer. */
2114 	    if (!unlisted || !find_listed || match != -1)
2115 		break;
2116 	    find_listed = FALSE;
2117 	}
2118 
2119 	vim_free(pat);
2120     }
2121 
2122     if (match == -2)
2123 	EMSG2(_("E93: More than one match for %s"), pattern);
2124     else if (match < 0)
2125 	EMSG2(_("E94: No matching buffer for %s"), pattern);
2126     return match;
2127 }
2128 #endif
2129 
2130 #if defined(FEAT_CMDL_COMPL) || defined(PROTO)
2131 
2132 /*
2133  * Find all buffer names that match.
2134  * For command line expansion of ":buf" and ":sbuf".
2135  * Return OK if matches found, FAIL otherwise.
2136  */
2137     int
2138 ExpandBufnames(pat, num_file, file, options)
2139     char_u	*pat;
2140     int		*num_file;
2141     char_u	***file;
2142     int		options;
2143 {
2144     int		count = 0;
2145     buf_T	*buf;
2146     int		round;
2147     char_u	*p;
2148     int		attempt;
2149     regprog_T	*prog;
2150     char_u	*patc;
2151 
2152     *num_file = 0;		    /* return values in case of FAIL */
2153     *file = NULL;
2154 
2155     /* Make a copy of "pat" and change "^" to "\(^\|[\/]\)". */
2156     if (*pat == '^')
2157     {
2158 	patc = alloc((unsigned)STRLEN(pat) + 11);
2159 	if (patc == NULL)
2160 	    return FAIL;
2161 	STRCPY(patc, "\\(^\\|[\\/]\\)");
2162 	STRCPY(patc + 11, pat + 1);
2163     }
2164     else
2165 	patc = pat;
2166 
2167     /*
2168      * attempt == 0: try match with    '\<', match at start of word
2169      * attempt == 1: try match without '\<', match anywhere
2170      */
2171     for (attempt = 0; attempt <= 1; ++attempt)
2172     {
2173 	if (attempt > 0 && patc == pat)
2174 	    break;	/* there was no anchor, no need to try again */
2175 	prog = vim_regcomp(patc + attempt * 11, RE_MAGIC);
2176 	if (prog == NULL)
2177 	{
2178 	    if (patc != pat)
2179 		vim_free(patc);
2180 	    return FAIL;
2181 	}
2182 
2183 	/*
2184 	 * round == 1: Count the matches.
2185 	 * round == 2: Build the array to keep the matches.
2186 	 */
2187 	for (round = 1; round <= 2; ++round)
2188 	{
2189 	    count = 0;
2190 	    for (buf = firstbuf; buf != NULL; buf = buf->b_next)
2191 	    {
2192 		if (!buf->b_p_bl)	/* skip unlisted buffers */
2193 		    continue;
2194 		p = buflist_match(prog, buf);
2195 		if (p != NULL)
2196 		{
2197 		    if (round == 1)
2198 			++count;
2199 		    else
2200 		    {
2201 			if (options & WILD_HOME_REPLACE)
2202 			    p = home_replace_save(buf, p);
2203 			else
2204 			    p = vim_strsave(p);
2205 			(*file)[count++] = p;
2206 		    }
2207 		}
2208 	    }
2209 	    if (count == 0)	/* no match found, break here */
2210 		break;
2211 	    if (round == 1)
2212 	    {
2213 		*file = (char_u **)alloc((unsigned)(count * sizeof(char_u *)));
2214 		if (*file == NULL)
2215 		{
2216 		    vim_free(prog);
2217 		    if (patc != pat)
2218 			vim_free(patc);
2219 		    return FAIL;
2220 		}
2221 	    }
2222 	}
2223 	vim_free(prog);
2224 	if (count)		/* match(es) found, break here */
2225 	    break;
2226     }
2227 
2228     if (patc != pat)
2229 	vim_free(patc);
2230 
2231     *num_file = count;
2232     return (count == 0 ? FAIL : OK);
2233 }
2234 
2235 #endif /* FEAT_CMDL_COMPL */
2236 
2237 #ifdef HAVE_BUFLIST_MATCH
2238 /*
2239  * Check for a match on the file name for buffer "buf" with regprog "prog".
2240  */
2241     static char_u *
2242 buflist_match(prog, buf)
2243     regprog_T	*prog;
2244     buf_T	*buf;
2245 {
2246     char_u	*match;
2247 
2248     /* First try the short file name, then the long file name. */
2249     match = fname_match(prog, buf->b_sfname);
2250     if (match == NULL)
2251 	match = fname_match(prog, buf->b_ffname);
2252 
2253     return match;
2254 }
2255 
2256 /*
2257  * Try matching the regexp in "prog" with file name "name".
2258  * Return "name" when there is a match, NULL when not.
2259  */
2260     static char_u *
2261 fname_match(prog, name)
2262     regprog_T	*prog;
2263     char_u	*name;
2264 {
2265     char_u	*match = NULL;
2266     char_u	*p;
2267     regmatch_T	regmatch;
2268 
2269     if (name != NULL)
2270     {
2271 	regmatch.regprog = prog;
2272 #ifdef CASE_INSENSITIVE_FILENAME
2273 	regmatch.rm_ic = TRUE;		/* Always ignore case */
2274 #else
2275 	regmatch.rm_ic = FALSE;		/* Never ignore case */
2276 #endif
2277 
2278 	if (vim_regexec(&regmatch, name, (colnr_T)0))
2279 	    match = name;
2280 	else
2281 	{
2282 	    /* Replace $(HOME) with '~' and try matching again. */
2283 	    p = home_replace_save(NULL, name);
2284 	    if (p != NULL && vim_regexec(&regmatch, p, (colnr_T)0))
2285 		match = name;
2286 	    vim_free(p);
2287 	}
2288     }
2289 
2290     return match;
2291 }
2292 #endif
2293 
2294 /*
2295  * find file in buffer list by number
2296  */
2297     buf_T *
2298 buflist_findnr(nr)
2299     int		nr;
2300 {
2301     buf_T	*buf;
2302 
2303     if (nr == 0)
2304 	nr = curwin->w_alt_fnum;
2305     for (buf = firstbuf; buf != NULL; buf = buf->b_next)
2306 	if (buf->b_fnum == nr)
2307 	    return (buf);
2308     return NULL;
2309 }
2310 
2311 /*
2312  * Get name of file 'n' in the buffer list.
2313  * When the file has no name an empty string is returned.
2314  * home_replace() is used to shorten the file name (used for marks).
2315  * Returns a pointer to allocated memory, of NULL when failed.
2316  */
2317     char_u *
2318 buflist_nr2name(n, fullname, helptail)
2319     int		n;
2320     int		fullname;
2321     int		helptail;	/* for help buffers return tail only */
2322 {
2323     buf_T	*buf;
2324 
2325     buf = buflist_findnr(n);
2326     if (buf == NULL)
2327 	return NULL;
2328     return home_replace_save(helptail ? buf : NULL,
2329 				     fullname ? buf->b_ffname : buf->b_fname);
2330 }
2331 
2332 /*
2333  * Set the "lnum" and "col" for the buffer "buf" and the current window.
2334  * When "copy_options" is TRUE save the local window option values.
2335  * When "lnum" is 0 only do the options.
2336  */
2337     static void
2338 buflist_setfpos(buf, win, lnum, col, copy_options)
2339     buf_T	*buf;
2340     win_T	*win;
2341     linenr_T	lnum;
2342     colnr_T	col;
2343     int		copy_options;
2344 {
2345     wininfo_T	*wip;
2346 
2347     for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next)
2348 	if (wip->wi_win == win)
2349 	    break;
2350     if (wip == NULL)
2351     {
2352 	/* allocate a new entry */
2353 	wip = (wininfo_T *)alloc_clear((unsigned)sizeof(wininfo_T));
2354 	if (wip == NULL)
2355 	    return;
2356 	wip->wi_win = win;
2357 	if (lnum == 0)		/* set lnum even when it's 0 */
2358 	    lnum = 1;
2359     }
2360     else
2361     {
2362 	/* remove the entry from the list */
2363 	if (wip->wi_prev)
2364 	    wip->wi_prev->wi_next = wip->wi_next;
2365 	else
2366 	    buf->b_wininfo = wip->wi_next;
2367 	if (wip->wi_next)
2368 	    wip->wi_next->wi_prev = wip->wi_prev;
2369 	if (copy_options && wip->wi_optset)
2370 	{
2371 	    clear_winopt(&wip->wi_opt);
2372 #ifdef FEAT_FOLDING
2373 	    deleteFoldRecurse(&wip->wi_folds);
2374 #endif
2375 	}
2376     }
2377     if (lnum != 0)
2378     {
2379 	wip->wi_fpos.lnum = lnum;
2380 	wip->wi_fpos.col = col;
2381     }
2382     if (copy_options)
2383     {
2384 	/* Save the window-specific option values. */
2385 	copy_winopt(&win->w_onebuf_opt, &wip->wi_opt);
2386 #ifdef FEAT_FOLDING
2387 	wip->wi_fold_manual = win->w_fold_manual;
2388 	cloneFoldGrowArray(&win->w_folds, &wip->wi_folds);
2389 #endif
2390 	wip->wi_optset = TRUE;
2391     }
2392 
2393     /* insert the entry in front of the list */
2394     wip->wi_next = buf->b_wininfo;
2395     buf->b_wininfo = wip;
2396     wip->wi_prev = NULL;
2397     if (wip->wi_next)
2398 	wip->wi_next->wi_prev = wip;
2399 
2400     return;
2401 }
2402 
2403 /*
2404  * Find info for the current window in buffer "buf".
2405  * If not found, return the info for the most recently used window.
2406  * Returns NULL when there isn't any info.
2407  */
2408     static wininfo_T *
2409 find_wininfo(buf)
2410     buf_T	*buf;
2411 {
2412     wininfo_T	*wip;
2413 
2414     for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next)
2415 	if (wip->wi_win == curwin)
2416 	    break;
2417     if (wip == NULL)	/* if no fpos for curwin, use the first in the list */
2418 	wip = buf->b_wininfo;
2419     return wip;
2420 }
2421 
2422 /*
2423  * Reset the local window options to the values last used in this window.
2424  * If the buffer wasn't used in this window before, use the values from
2425  * the most recently used window.  If the values were never set, use the
2426  * global values for the window.
2427  */
2428     void
2429 get_winopts(buf)
2430     buf_T	*buf;
2431 {
2432     wininfo_T	*wip;
2433 
2434     clear_winopt(&curwin->w_onebuf_opt);
2435 #ifdef FEAT_FOLDING
2436     clearFolding(curwin);
2437 #endif
2438 
2439     wip = find_wininfo(buf);
2440     if (wip != NULL && wip->wi_optset)
2441     {
2442 	copy_winopt(&wip->wi_opt, &curwin->w_onebuf_opt);
2443 #ifdef FEAT_FOLDING
2444 	curwin->w_fold_manual = wip->wi_fold_manual;
2445 	curwin->w_foldinvalid = TRUE;
2446 	cloneFoldGrowArray(&wip->wi_folds, &curwin->w_folds);
2447 #endif
2448     }
2449     else
2450 	copy_winopt(&curwin->w_allbuf_opt, &curwin->w_onebuf_opt);
2451 
2452 #ifdef FEAT_FOLDING
2453     /* Set 'foldlevel' to 'foldlevelstart' if it's not negative. */
2454     if (p_fdls >= 0)
2455 	curwin->w_p_fdl = p_fdls;
2456 #endif
2457 }
2458 
2459 /*
2460  * Find the position (lnum and col) for the buffer 'buf' for the current
2461  * window.
2462  * Returns a pointer to no_position if no position is found.
2463  */
2464     pos_T *
2465 buflist_findfpos(buf)
2466     buf_T	*buf;
2467 {
2468     wininfo_T	*wip;
2469     static pos_T no_position = {1, 0};
2470 
2471     wip = find_wininfo(buf);
2472     if (wip != NULL)
2473 	return &(wip->wi_fpos);
2474     else
2475 	return &no_position;
2476 }
2477 
2478 /*
2479  * Find the lnum for the buffer 'buf' for the current window.
2480  */
2481     linenr_T
2482 buflist_findlnum(buf)
2483     buf_T	*buf;
2484 {
2485     return buflist_findfpos(buf)->lnum;
2486 }
2487 
2488 #if defined(FEAT_LISTCMDS) || defined(PROTO)
2489 /*
2490  * List all know file names (for :files and :buffers command).
2491  */
2492 /*ARGSUSED*/
2493     void
2494 buflist_list(eap)
2495     exarg_T	*eap;
2496 {
2497     buf_T	*buf;
2498     int		len;
2499     int		i;
2500 
2501     for (buf = firstbuf; buf != NULL && !got_int; buf = buf->b_next)
2502     {
2503 	/* skip unlisted buffers, unless ! was used */
2504 	if (!buf->b_p_bl && !eap->forceit)
2505 	    continue;
2506 	msg_putchar('\n');
2507 	if (buf_spname(buf) != NULL)
2508 	    STRCPY(NameBuff, buf_spname(buf));
2509 	else
2510 	    home_replace(buf, buf->b_fname, NameBuff, MAXPATHL, TRUE);
2511 
2512 	len = vim_snprintf((char *)IObuff, IOSIZE - 20, "%3d%c%c%c%c%c \"%s\"",
2513 		buf->b_fnum,
2514 		buf->b_p_bl ? ' ' : 'u',
2515 		buf == curbuf ? '%' :
2516 			(curwin->w_alt_fnum == buf->b_fnum ? '#' : ' '),
2517 		buf->b_ml.ml_mfp == NULL ? ' ' :
2518 			(buf->b_nwindows == 0 ? 'h' : 'a'),
2519 		!buf->b_p_ma ? '-' : (buf->b_p_ro ? '=' : ' '),
2520 		(buf->b_flags & BF_READERR) ? 'x'
2521 					    : (bufIsChanged(buf) ? '+' : ' '),
2522 		NameBuff);
2523 
2524 	/* put "line 999" in column 40 or after the file name */
2525 	i = 40 - vim_strsize(IObuff);
2526 	do
2527 	{
2528 	    IObuff[len++] = ' ';
2529 	} while (--i > 0 && len < IOSIZE - 18);
2530 	vim_snprintf((char *)IObuff + len, IOSIZE - len, _("line %ld"),
2531 		buf == curbuf ? curwin->w_cursor.lnum
2532 					       : (long)buflist_findlnum(buf));
2533 	msg_outtrans(IObuff);
2534 	out_flush();	    /* output one line at a time */
2535 	ui_breakcheck();
2536     }
2537 }
2538 #endif
2539 
2540 /*
2541  * Get file name and line number for file 'fnum'.
2542  * Used by DoOneCmd() for translating '%' and '#'.
2543  * Used by insert_reg() and cmdline_paste() for '#' register.
2544  * Return FAIL if not found, OK for success.
2545  */
2546     int
2547 buflist_name_nr(fnum, fname, lnum)
2548     int		fnum;
2549     char_u	**fname;
2550     linenr_T	*lnum;
2551 {
2552     buf_T	*buf;
2553 
2554     buf = buflist_findnr(fnum);
2555     if (buf == NULL || buf->b_fname == NULL)
2556 	return FAIL;
2557 
2558     *fname = buf->b_fname;
2559     *lnum = buflist_findlnum(buf);
2560 
2561     return OK;
2562 }
2563 
2564 /*
2565  * Set the file name for "buf"' to 'ffname', short file name to 'sfname'.
2566  * The file name with the full path is also remembered, for when :cd is used.
2567  * Returns FAIL for failure (file name already in use by other buffer)
2568  *	OK otherwise.
2569  */
2570     int
2571 setfname(buf, ffname, sfname, message)
2572     buf_T	*buf;
2573     char_u	*ffname, *sfname;
2574     int		message;	/* give message when buffer already exists */
2575 {
2576     buf_T	*obuf = NULL;
2577 #ifdef UNIX
2578     struct stat st;
2579 #endif
2580 
2581     if (ffname == NULL || *ffname == NUL)
2582     {
2583 	/* Removing the name. */
2584 	vim_free(buf->b_ffname);
2585 	vim_free(buf->b_sfname);
2586 	buf->b_ffname = NULL;
2587 	buf->b_sfname = NULL;
2588 #ifdef UNIX
2589 	st.st_dev = (dev_T)-1;
2590 #endif
2591     }
2592     else
2593     {
2594 	fname_expand(buf, &ffname, &sfname); /* will allocate ffname */
2595 	if (ffname == NULL)		    /* out of memory */
2596 	    return FAIL;
2597 
2598 	/*
2599 	 * if the file name is already used in another buffer:
2600 	 * - if the buffer is loaded, fail
2601 	 * - if the buffer is not loaded, delete it from the list
2602 	 */
2603 #ifdef UNIX
2604 	if (mch_stat((char *)ffname, &st) < 0)
2605 	    st.st_dev = (dev_T)-1;
2606 #endif
2607 	if (!(buf->b_flags & BF_DUMMY))
2608 #ifdef UNIX
2609 	    obuf = buflist_findname_stat(ffname, &st);
2610 #else
2611 	    obuf = buflist_findname(ffname);
2612 #endif
2613 	if (obuf != NULL && obuf != buf)
2614 	{
2615 	    if (obuf->b_ml.ml_mfp != NULL)	/* it's loaded, fail */
2616 	    {
2617 		if (message)
2618 		    EMSG(_("E95: Buffer with this name already exists"));
2619 		vim_free(ffname);
2620 		return FAIL;
2621 	    }
2622 	    close_buffer(NULL, obuf, DOBUF_WIPE); /* delete from the list */
2623 	}
2624 	sfname = vim_strsave(sfname);
2625 	if (ffname == NULL || sfname == NULL)
2626 	{
2627 	    vim_free(sfname);
2628 	    vim_free(ffname);
2629 	    return FAIL;
2630 	}
2631 #ifdef USE_FNAME_CASE
2632 # ifdef USE_LONG_FNAME
2633 	if (USE_LONG_FNAME)
2634 # endif
2635 	    fname_case(sfname, 0);    /* set correct case for short file name */
2636 #endif
2637 	vim_free(buf->b_ffname);
2638 	vim_free(buf->b_sfname);
2639 	buf->b_ffname = ffname;
2640 	buf->b_sfname = sfname;
2641     }
2642     buf->b_fname = buf->b_sfname;
2643 #ifdef UNIX
2644     if (st.st_dev == (dev_T)-1)
2645 	buf->b_dev = -1;
2646     else
2647     {
2648 	buf->b_dev = st.st_dev;
2649 	buf->b_ino = st.st_ino;
2650     }
2651 #endif
2652 
2653 #ifndef SHORT_FNAME
2654     buf->b_shortname = FALSE;
2655 #endif
2656 
2657     buf_name_changed(buf);
2658     return OK;
2659 }
2660 
2661 /*
2662  * Crude way of changing the name of a buffer.  Use with care!
2663  * The name should be relative to the current directory.
2664  */
2665     void
2666 buf_set_name(fnum, name)
2667     int		fnum;
2668     char_u	*name;
2669 {
2670     buf_T	*buf;
2671 
2672     buf = buflist_findnr(fnum);
2673     if (buf != NULL)
2674     {
2675 	vim_free(buf->b_sfname);
2676 	vim_free(buf->b_ffname);
2677 	buf->b_ffname = vim_strsave(name);
2678 	buf->b_sfname = NULL;
2679 	/* Allocate ffname and expand into full path.  Also resolves .lnk
2680 	 * files on Win32. */
2681 	fname_expand(buf, &buf->b_ffname, &buf->b_sfname);
2682 	buf->b_fname = buf->b_sfname;
2683     }
2684 }
2685 
2686 /*
2687  * Take care of what needs to be done when the name of buffer "buf" has
2688  * changed.
2689  */
2690     void
2691 buf_name_changed(buf)
2692     buf_T	*buf;
2693 {
2694     /*
2695      * If the file name changed, also change the name of the swapfile
2696      */
2697     if (buf->b_ml.ml_mfp != NULL)
2698 	ml_setname(buf);
2699 
2700     if (curwin->w_buffer == buf)
2701 	check_arg_idx(curwin);	/* check file name for arg list */
2702 #ifdef FEAT_TITLE
2703     maketitle();		/* set window title */
2704 #endif
2705 #ifdef FEAT_WINDOWS
2706     status_redraw_all();	/* status lines need to be redrawn */
2707 #endif
2708     fmarks_check_names(buf);	/* check named file marks */
2709     ml_timestamp(buf);		/* reset timestamp */
2710 }
2711 
2712 /*
2713  * set alternate file name for current window
2714  *
2715  * Used by do_one_cmd(), do_write() and do_ecmd().
2716  * Return the buffer.
2717  */
2718     buf_T *
2719 setaltfname(ffname, sfname, lnum)
2720     char_u	*ffname;
2721     char_u	*sfname;
2722     linenr_T	lnum;
2723 {
2724     buf_T	*buf;
2725 
2726     /* Create a buffer.  'buflisted' is not set if it's a new buffer */
2727     buf = buflist_new(ffname, sfname, lnum, 0);
2728     if (buf != NULL && !cmdmod.keepalt)
2729 	curwin->w_alt_fnum = buf->b_fnum;
2730     return buf;
2731 }
2732 
2733 /*
2734  * Get alternate file name for current window.
2735  * Return NULL if there isn't any, and give error message if requested.
2736  */
2737     char_u  *
2738 getaltfname(errmsg)
2739     int		errmsg;		/* give error message */
2740 {
2741     char_u	*fname;
2742     linenr_T	dummy;
2743 
2744     if (buflist_name_nr(0, &fname, &dummy) == FAIL)
2745     {
2746 	if (errmsg)
2747 	    EMSG(_(e_noalt));
2748 	return NULL;
2749     }
2750     return fname;
2751 }
2752 
2753 /*
2754  * Add a file name to the buflist and return its number.
2755  * Uses same flags as buflist_new(), except BLN_DUMMY.
2756  *
2757  * used by qf_init(), main() and doarglist()
2758  */
2759     int
2760 buflist_add(fname, flags)
2761     char_u	*fname;
2762     int		flags;
2763 {
2764     buf_T	*buf;
2765 
2766     buf = buflist_new(fname, NULL, (linenr_T)0, flags);
2767     if (buf != NULL)
2768 	return buf->b_fnum;
2769     return 0;
2770 }
2771 
2772 #if defined(BACKSLASH_IN_FILENAME) || defined(PROTO)
2773 /*
2774  * Adjust slashes in file names.  Called after 'shellslash' was set.
2775  */
2776     void
2777 buflist_slash_adjust()
2778 {
2779     buf_T	*bp;
2780 
2781     for (bp = firstbuf; bp != NULL; bp = bp->b_next)
2782     {
2783 	if (bp->b_ffname != NULL)
2784 	    slash_adjust(bp->b_ffname);
2785 	if (bp->b_sfname != NULL)
2786 	    slash_adjust(bp->b_sfname);
2787     }
2788 }
2789 #endif
2790 
2791 /*
2792  * Set alternate cursor position for current window.
2793  * Also save the local window option values.
2794  */
2795     void
2796 buflist_altfpos()
2797 {
2798     buflist_setfpos(curbuf, curwin, curwin->w_cursor.lnum,
2799 						  curwin->w_cursor.col, TRUE);
2800 }
2801 
2802 /*
2803  * Return TRUE if 'ffname' is not the same file as current file.
2804  * Fname must have a full path (expanded by mch_FullName()).
2805  */
2806     int
2807 otherfile(ffname)
2808     char_u	*ffname;
2809 {
2810     return otherfile_buf(curbuf, ffname
2811 #ifdef UNIX
2812 	    , NULL
2813 #endif
2814 	    );
2815 }
2816 
2817     static int
2818 otherfile_buf(buf, ffname
2819 #ifdef UNIX
2820 	, stp
2821 #endif
2822 	)
2823     buf_T	*buf;
2824     char_u	*ffname;
2825 #ifdef UNIX
2826     struct stat	*stp;
2827 #endif
2828 {
2829     /* no name is different */
2830     if (ffname == NULL || *ffname == NUL || buf->b_ffname == NULL)
2831 	return TRUE;
2832     if (fnamecmp(ffname, buf->b_ffname) == 0)
2833 	return FALSE;
2834 #ifdef UNIX
2835     {
2836 	struct stat	st;
2837 
2838 	/* If no struct stat given, get it now */
2839 	if (stp == NULL)
2840 	{
2841 	    if (buf->b_dev < 0 || mch_stat((char *)ffname, &st) < 0)
2842 		st.st_dev = (dev_T)-1;
2843 	    stp = &st;
2844 	}
2845 	/* Use dev/ino to check if the files are the same, even when the names
2846 	 * are different (possible with links).  Still need to compare the
2847 	 * name above, for when the file doesn't exist yet.
2848 	 * Problem: The dev/ino changes when a file is deleted (and created
2849 	 * again) and remains the same when renamed/moved.  We don't want to
2850 	 * mch_stat() each buffer each time, that would be too slow.  Get the
2851 	 * dev/ino again when they appear to match, but not when they appear
2852 	 * to be different: Could skip a buffer when it's actually the same
2853 	 * file. */
2854 	if (buf_same_ino(buf, stp))
2855 	{
2856 	    buf_setino(buf);
2857 	    if (buf_same_ino(buf, stp))
2858 		return FALSE;
2859 	}
2860     }
2861 #endif
2862     return TRUE;
2863 }
2864 
2865 #if defined(UNIX) || defined(PROTO)
2866 /*
2867  * Set inode and device number for a buffer.
2868  * Must always be called when b_fname is changed!.
2869  */
2870     void
2871 buf_setino(buf)
2872     buf_T	*buf;
2873 {
2874     struct stat	st;
2875 
2876     if (buf->b_fname != NULL && mch_stat((char *)buf->b_fname, &st) >= 0)
2877     {
2878 	buf->b_dev = st.st_dev;
2879 	buf->b_ino = st.st_ino;
2880     }
2881     else
2882 	buf->b_dev = -1;
2883 }
2884 
2885 /*
2886  * Return TRUE if dev/ino in buffer "buf" matches with "stp".
2887  */
2888     static int
2889 buf_same_ino(buf, stp)
2890     buf_T	*buf;
2891     struct stat *stp;
2892 {
2893     return (buf->b_dev >= 0
2894 	    && stp->st_dev == buf->b_dev
2895 	    && stp->st_ino == buf->b_ino);
2896 }
2897 #endif
2898 
2899 /*
2900  * Print info about the current buffer.
2901  */
2902     void
2903 fileinfo(fullname, shorthelp, dont_truncate)
2904     int fullname;	    /* when non-zero print full path */
2905     int shorthelp;
2906     int	dont_truncate;
2907 {
2908     char_u	*name;
2909     int		n;
2910     char_u	*p;
2911     char_u	*buffer;
2912     size_t	len;
2913 
2914     buffer = alloc(IOSIZE);
2915     if (buffer == NULL)
2916 	return;
2917 
2918     if (fullname > 1)	    /* 2 CTRL-G: include buffer number */
2919     {
2920 	sprintf((char *)buffer, "buf %d: ", curbuf->b_fnum);
2921 	p = buffer + STRLEN(buffer);
2922     }
2923     else
2924 	p = buffer;
2925 
2926     *p++ = '"';
2927     if (buf_spname(curbuf) != NULL)
2928 	STRCPY(p, buf_spname(curbuf));
2929     else
2930     {
2931 	if (!fullname && curbuf->b_fname != NULL)
2932 	    name = curbuf->b_fname;
2933 	else
2934 	    name = curbuf->b_ffname;
2935 	home_replace(shorthelp ? curbuf : NULL, name, p,
2936 					  (int)(IOSIZE - (p - buffer)), TRUE);
2937     }
2938 
2939     len = STRLEN(buffer);
2940     vim_snprintf((char *)buffer + len, IOSIZE - len,
2941 	    "\"%s%s%s%s%s%s",
2942 	    curbufIsChanged() ? (shortmess(SHM_MOD)
2943 					  ?  " [+]" : _(" [Modified]")) : " ",
2944 	    (curbuf->b_flags & BF_NOTEDITED)
2945 #ifdef FEAT_QUICKFIX
2946 		    && !bt_dontwrite(curbuf)
2947 #endif
2948 					? _("[Not edited]") : "",
2949 	    (curbuf->b_flags & BF_NEW)
2950 #ifdef FEAT_QUICKFIX
2951 		    && !bt_dontwrite(curbuf)
2952 #endif
2953 					? _("[New file]") : "",
2954 	    (curbuf->b_flags & BF_READERR) ? _("[Read errors]") : "",
2955 	    curbuf->b_p_ro ? (shortmess(SHM_RO) ? "[RO]"
2956 						      : _("[readonly]")) : "",
2957 	    (curbufIsChanged() || (curbuf->b_flags & BF_WRITE_MASK)
2958 							  || curbuf->b_p_ro) ?
2959 								    " " : "");
2960     /* With 32 bit longs and more than 21,474,836 lines multiplying by 100
2961      * causes an overflow, thus for large numbers divide instead. */
2962     if (curwin->w_cursor.lnum > 1000000L)
2963 	n = (int)(((long)curwin->w_cursor.lnum) /
2964 				   ((long)curbuf->b_ml.ml_line_count / 100L));
2965     else
2966 	n = (int)(((long)curwin->w_cursor.lnum * 100L) /
2967 					    (long)curbuf->b_ml.ml_line_count);
2968     len = STRLEN(buffer);
2969     if (curbuf->b_ml.ml_flags & ML_EMPTY)
2970     {
2971 	vim_snprintf((char *)buffer + len, IOSIZE - len, "%s", _(no_lines_msg));
2972     }
2973 #ifdef FEAT_CMDL_INFO
2974     else if (p_ru)
2975     {
2976 	/* Current line and column are already on the screen -- webb */
2977 	if (curbuf->b_ml.ml_line_count == 1)
2978 	    vim_snprintf((char *)buffer + len, IOSIZE - len,
2979 		    _("1 line --%d%%--"), n);
2980 	else
2981 	    vim_snprintf((char *)buffer + len, IOSIZE - len,
2982 		    _("%ld lines --%d%%--"),
2983 					 (long)curbuf->b_ml.ml_line_count, n);
2984     }
2985 #endif
2986     else
2987     {
2988 	vim_snprintf((char *)buffer + len, IOSIZE - len,
2989 		_("line %ld of %ld --%d%%-- col "),
2990 		(long)curwin->w_cursor.lnum,
2991 		(long)curbuf->b_ml.ml_line_count,
2992 		n);
2993 	validate_virtcol();
2994 	col_print(buffer + STRLEN(buffer),
2995 		   (int)curwin->w_cursor.col + 1, (int)curwin->w_virtcol + 1);
2996     }
2997 
2998     (void)append_arg_number(curwin, buffer, !shortmess(SHM_FILE), IOSIZE);
2999 
3000     if (dont_truncate)
3001     {
3002 	/* Temporarily set msg_scroll to avoid the message being truncated.
3003 	 * First call msg_start() to get the message in the right place. */
3004 	msg_start();
3005 	n = msg_scroll;
3006 	msg_scroll = TRUE;
3007 	msg(buffer);
3008 	msg_scroll = n;
3009     }
3010     else
3011     {
3012 	p = msg_trunc_attr(buffer, FALSE, 0);
3013 	if (restart_edit != 0 || (msg_scrolled && !need_wait_return))
3014 	    /* Need to repeat the message after redrawing when:
3015 	     * - When restart_edit is set (otherwise there will be a delay
3016 	     *   before redrawing).
3017 	     * - When the screen was scrolled but there is no wait-return
3018 	     *   prompt. */
3019 	    set_keep_msg(p, 0);
3020     }
3021 
3022     vim_free(buffer);
3023 }
3024 
3025     void
3026 col_print(buf, col, vcol)
3027     char_u  *buf;
3028     int	    col;
3029     int	    vcol;
3030 {
3031     if (col == vcol)
3032 	sprintf((char *)buf, "%d", col);
3033     else
3034 	sprintf((char *)buf, "%d-%d", col, vcol);
3035 }
3036 
3037 #if defined(FEAT_TITLE) || defined(PROTO)
3038 /*
3039  * put file name in title bar of window and in icon title
3040  */
3041 
3042 static char_u *lasttitle = NULL;
3043 static char_u *lasticon = NULL;
3044 
3045     void
3046 maketitle()
3047 {
3048     char_u	*p;
3049     char_u	*t_str = NULL;
3050     char_u	*i_name;
3051     char_u	*i_str = NULL;
3052     int		maxlen = 0;
3053     int		len;
3054     int		mustset;
3055     char_u	buf[IOSIZE];
3056     int		off;
3057 
3058     if (!redrawing())
3059     {
3060 	/* Postpone updating the title when 'lazyredraw' is set. */
3061 	need_maketitle = TRUE;
3062 	return;
3063     }
3064 
3065     need_maketitle = FALSE;
3066     if (!p_title && !p_icon)
3067 	return;
3068 
3069     if (p_title)
3070     {
3071 	if (p_titlelen > 0)
3072 	{
3073 	    maxlen = p_titlelen * Columns / 100;
3074 	    if (maxlen < 10)
3075 		maxlen = 10;
3076 	}
3077 
3078 	t_str = buf;
3079 	if (*p_titlestring != NUL)
3080 	{
3081 #ifdef FEAT_STL_OPT
3082 	    if (stl_syntax & STL_IN_TITLE)
3083 	    {
3084 		int	use_sandbox = FALSE;
3085 		int	save_called_emsg = called_emsg;
3086 
3087 # ifdef FEAT_EVAL
3088 		use_sandbox = was_set_insecurely((char_u *)"titlestring", 0);
3089 # endif
3090 		called_emsg = FALSE;
3091 		build_stl_str_hl(curwin, t_str, sizeof(buf),
3092 					      p_titlestring, use_sandbox,
3093 					      0, maxlen, NULL, NULL);
3094 		if (called_emsg)
3095 		    set_string_option_direct((char_u *)"titlestring", -1,
3096 					   (char_u *)"", OPT_FREE, SID_ERROR);
3097 		called_emsg |= save_called_emsg;
3098 	    }
3099 	    else
3100 #endif
3101 		t_str = p_titlestring;
3102 	}
3103 	else
3104 	{
3105 	    /* format: "fname + (path) (1 of 2) - VIM" */
3106 
3107 	    if (curbuf->b_fname == NULL)
3108 		STRCPY(buf, _("[No Name]"));
3109 	    else
3110 	    {
3111 		p = transstr(gettail(curbuf->b_fname));
3112 		vim_strncpy(buf, p, IOSIZE - 100);
3113 		vim_free(p);
3114 	    }
3115 
3116 	    switch (bufIsChanged(curbuf)
3117 		    + (curbuf->b_p_ro * 2)
3118 		    + (!curbuf->b_p_ma * 4))
3119 	    {
3120 		case 1: STRCAT(buf, " +"); break;
3121 		case 2: STRCAT(buf, " ="); break;
3122 		case 3: STRCAT(buf, " =+"); break;
3123 		case 4:
3124 		case 6: STRCAT(buf, " -"); break;
3125 		case 5:
3126 		case 7: STRCAT(buf, " -+"); break;
3127 	    }
3128 
3129 	    if (curbuf->b_fname != NULL)
3130 	    {
3131 		/* Get path of file, replace home dir with ~ */
3132 		off = (int)STRLEN(buf);
3133 		buf[off++] = ' ';
3134 		buf[off++] = '(';
3135 		home_replace(curbuf, curbuf->b_ffname,
3136 					       buf + off, IOSIZE - off, TRUE);
3137 #ifdef BACKSLASH_IN_FILENAME
3138 		/* avoid "c:/name" to be reduced to "c" */
3139 		if (isalpha(buf[off]) && buf[off + 1] == ':')
3140 		    off += 2;
3141 #endif
3142 		/* remove the file name */
3143 		p = gettail_sep(buf + off);
3144 		if (p == buf + off)
3145 		    /* must be a help buffer */
3146 		    vim_strncpy(buf + off, (char_u *)_("help"),
3147 							    IOSIZE - off - 1);
3148 		else
3149 		    *p = NUL;
3150 
3151 		/* translate unprintable chars */
3152 		p = transstr(buf + off);
3153 		vim_strncpy(buf + off, p, IOSIZE - off - 1);
3154 		vim_free(p);
3155 		STRCAT(buf, ")");
3156 	    }
3157 
3158 	    append_arg_number(curwin, buf, FALSE, IOSIZE);
3159 
3160 #if defined(FEAT_CLIENTSERVER)
3161 	    if (serverName != NULL)
3162 	    {
3163 		STRCAT(buf, " - ");
3164 		STRCAT(buf, serverName);
3165 	    }
3166 	    else
3167 #endif
3168 		STRCAT(buf, " - VIM");
3169 
3170 	    if (maxlen > 0)
3171 	    {
3172 		/* make it shorter by removing a bit in the middle */
3173 		len = vim_strsize(buf);
3174 		if (len > maxlen)
3175 		    trunc_string(buf, buf, maxlen);
3176 	    }
3177 	}
3178     }
3179     mustset = ti_change(t_str, &lasttitle);
3180 
3181     if (p_icon)
3182     {
3183 	i_str = buf;
3184 	if (*p_iconstring != NUL)
3185 	{
3186 #ifdef FEAT_STL_OPT
3187 	    if (stl_syntax & STL_IN_ICON)
3188 	    {
3189 		int	use_sandbox = FALSE;
3190 		int	save_called_emsg = called_emsg;
3191 
3192 # ifdef FEAT_EVAL
3193 		use_sandbox = was_set_insecurely((char_u *)"iconstring", 0);
3194 # endif
3195 		called_emsg = FALSE;
3196 		build_stl_str_hl(curwin, i_str, sizeof(buf),
3197 						    p_iconstring, use_sandbox,
3198 						    0, 0, NULL, NULL);
3199 		if (called_emsg)
3200 		    set_string_option_direct((char_u *)"iconstring", -1,
3201 					   (char_u *)"", OPT_FREE, SID_ERROR);
3202 		called_emsg |= save_called_emsg;
3203 	    }
3204 	    else
3205 #endif
3206 		i_str = p_iconstring;
3207 	}
3208 	else
3209 	{
3210 	    if (buf_spname(curbuf) != NULL)
3211 		i_name = (char_u *)buf_spname(curbuf);
3212 	    else		    /* use file name only in icon */
3213 		i_name = gettail(curbuf->b_ffname);
3214 	    *i_str = NUL;
3215 	    /* Truncate name at 100 bytes. */
3216 	    len = (int)STRLEN(i_name);
3217 	    if (len > 100)
3218 	    {
3219 		len -= 100;
3220 #ifdef FEAT_MBYTE
3221 		if (has_mbyte)
3222 		    len += (*mb_tail_off)(i_name, i_name + len) + 1;
3223 #endif
3224 		i_name += len;
3225 	    }
3226 	    STRCPY(i_str, i_name);
3227 	    trans_characters(i_str, IOSIZE);
3228 	}
3229     }
3230 
3231     mustset |= ti_change(i_str, &lasticon);
3232 
3233     if (mustset)
3234 	resettitle();
3235 }
3236 
3237 /*
3238  * Used for title and icon: Check if "str" differs from "*last".  Set "*last"
3239  * from "str" if it does.
3240  * Return TRUE when "*last" changed.
3241  */
3242     static int
3243 ti_change(str, last)
3244     char_u	*str;
3245     char_u	**last;
3246 {
3247     if ((str == NULL) != (*last == NULL)
3248 	    || (str != NULL && *last != NULL && STRCMP(str, *last) != 0))
3249     {
3250 	vim_free(*last);
3251 	if (str == NULL)
3252 	    *last = NULL;
3253 	else
3254 	    *last = vim_strsave(str);
3255 	return TRUE;
3256     }
3257     return FALSE;
3258 }
3259 
3260 /*
3261  * Put current window title back (used after calling a shell)
3262  */
3263     void
3264 resettitle()
3265 {
3266     mch_settitle(lasttitle, lasticon);
3267 }
3268 
3269 # if defined(EXITFREE) || defined(PROTO)
3270     void
3271 free_titles()
3272 {
3273     vim_free(lasttitle);
3274     vim_free(lasticon);
3275 }
3276 # endif
3277 
3278 #endif /* FEAT_TITLE */
3279 
3280 #if defined(FEAT_STL_OPT) || defined(FEAT_GUI_TABLINE) || defined(PROTO)
3281 /*
3282  * Build a string from the status line items in "fmt".
3283  * Return length of string in screen cells.
3284  *
3285  * Normally works for window "wp", except when working for 'tabline' then it
3286  * is "curwin".
3287  *
3288  * Items are drawn interspersed with the text that surrounds it
3289  * Specials: %-<wid>(xxx%) => group, %= => middle marker, %< => truncation
3290  * Item: %-<minwid>.<maxwid><itemch> All but <itemch> are optional
3291  *
3292  * If maxwidth is not zero, the string will be filled at any middle marker
3293  * or truncated if too long, fillchar is used for all whitespace.
3294  */
3295 /*ARGSUSED*/
3296     int
3297 build_stl_str_hl(wp, out, outlen, fmt, use_sandbox, fillchar, maxwidth, hltab, tabtab)
3298     win_T	*wp;
3299     char_u	*out;		/* buffer to write into != NameBuff */
3300     size_t	outlen;		/* length of out[] */
3301     char_u	*fmt;
3302     int		use_sandbox;	/* "fmt" was set insecurely, use sandbox */
3303     int		fillchar;
3304     int		maxwidth;
3305     struct stl_hlrec *hltab;	/* return: HL attributes (can be NULL) */
3306     struct stl_hlrec *tabtab;	/* return: tab page nrs (can be NULL) */
3307 {
3308     char_u	*p;
3309     char_u	*s;
3310     char_u	*t;
3311     char_u	*linecont;
3312 #ifdef FEAT_EVAL
3313     win_T	*o_curwin;
3314     buf_T	*o_curbuf;
3315 #endif
3316     int		empty_line;
3317     colnr_T	virtcol;
3318     long	l;
3319     long	n;
3320     int		prevchar_isflag;
3321     int		prevchar_isitem;
3322     int		itemisflag;
3323     int		fillable;
3324     char_u	*str;
3325     long	num;
3326     int		width;
3327     int		itemcnt;
3328     int		curitem;
3329     int		groupitem[STL_MAX_ITEM];
3330     int		groupdepth;
3331     struct stl_item
3332     {
3333 	char_u		*start;
3334 	int		minwid;
3335 	int		maxwid;
3336 	enum
3337 	{
3338 	    Normal,
3339 	    Empty,
3340 	    Group,
3341 	    Middle,
3342 	    Highlight,
3343 	    TabPage,
3344 	    Trunc
3345 	}		type;
3346     }		item[STL_MAX_ITEM];
3347     int		minwid;
3348     int		maxwid;
3349     int		zeropad;
3350     char_u	base;
3351     char_u	opt;
3352 #define TMPLEN 70
3353     char_u	tmp[TMPLEN];
3354     char_u	*usefmt = fmt;
3355     struct stl_hlrec *sp;
3356 
3357 #ifdef FEAT_EVAL
3358     /*
3359      * When the format starts with "%!" then evaluate it as an expression and
3360      * use the result as the actual format string.
3361      */
3362     if (fmt[0] == '%' && fmt[1] == '!')
3363     {
3364 	usefmt = eval_to_string_safe(fmt + 2, NULL, use_sandbox);
3365 	if (usefmt == NULL)
3366 	    usefmt = fmt;
3367     }
3368 #endif
3369 
3370     if (fillchar == 0)
3371 	fillchar = ' ';
3372 #ifdef FEAT_MBYTE
3373     /* Can't handle a multi-byte fill character yet. */
3374     else if (mb_char2len(fillchar) > 1)
3375 	fillchar = '-';
3376 #endif
3377 
3378     /*
3379      * Get line & check if empty (cursorpos will show "0-1").
3380      * If inversion is possible we use it. Else '=' characters are used.
3381      */
3382     linecont = ml_get_buf(wp->w_buffer, wp->w_cursor.lnum, FALSE);
3383     empty_line = (*linecont == NUL);
3384 
3385     groupdepth = 0;
3386     p = out;
3387     curitem = 0;
3388     prevchar_isflag = TRUE;
3389     prevchar_isitem = FALSE;
3390     for (s = usefmt; *s; )
3391     {
3392 	if (*s != NUL && *s != '%')
3393 	    prevchar_isflag = prevchar_isitem = FALSE;
3394 
3395 	/*
3396 	 * Handle up to the next '%' or the end.
3397 	 */
3398 	while (*s != NUL && *s != '%' && p + 1 < out + outlen)
3399 	    *p++ = *s++;
3400 	if (*s == NUL || p + 1 >= out + outlen)
3401 	    break;
3402 
3403 	/*
3404 	 * Handle one '%' item.
3405 	 */
3406 	s++;
3407 	if (*s == '%')
3408 	{
3409 	    if (p + 1 >= out + outlen)
3410 		break;
3411 	    *p++ = *s++;
3412 	    prevchar_isflag = prevchar_isitem = FALSE;
3413 	    continue;
3414 	}
3415 	if (*s == STL_MIDDLEMARK)
3416 	{
3417 	    s++;
3418 	    if (groupdepth > 0)
3419 		continue;
3420 	    item[curitem].type = Middle;
3421 	    item[curitem++].start = p;
3422 	    continue;
3423 	}
3424 	if (*s == STL_TRUNCMARK)
3425 	{
3426 	    s++;
3427 	    item[curitem].type = Trunc;
3428 	    item[curitem++].start = p;
3429 	    continue;
3430 	}
3431 	if (*s == ')')
3432 	{
3433 	    s++;
3434 	    if (groupdepth < 1)
3435 		continue;
3436 	    groupdepth--;
3437 
3438 	    t = item[groupitem[groupdepth]].start;
3439 	    *p = NUL;
3440 	    l = vim_strsize(t);
3441 	    if (curitem > groupitem[groupdepth] + 1
3442 		    && item[groupitem[groupdepth]].minwid == 0)
3443 	    {
3444 		/* remove group if all items are empty */
3445 		for (n = groupitem[groupdepth] + 1; n < curitem; n++)
3446 		    if (item[n].type == Normal)
3447 			break;
3448 		if (n == curitem)
3449 		{
3450 		    p = t;
3451 		    l = 0;
3452 		}
3453 	    }
3454 	    if (l > item[groupitem[groupdepth]].maxwid)
3455 	    {
3456 		/* truncate, remove n bytes of text at the start */
3457 #ifdef FEAT_MBYTE
3458 		if (has_mbyte)
3459 		{
3460 		    /* Find the first character that should be included. */
3461 		    n = 0;
3462 		    while (l >= item[groupitem[groupdepth]].maxwid)
3463 		    {
3464 			l -= ptr2cells(t + n);
3465 			n += (*mb_ptr2len)(t + n);
3466 		    }
3467 		}
3468 		else
3469 #endif
3470 		    n = (long)(p - t) - item[groupitem[groupdepth]].maxwid + 1;
3471 
3472 		*t = '<';
3473 		mch_memmove(t + 1, t + n, p - (t + n));
3474 		p = p - n + 1;
3475 #ifdef FEAT_MBYTE
3476 		/* Fill up space left over by half a double-wide char. */
3477 		while (++l < item[groupitem[groupdepth]].minwid)
3478 		    *p++ = fillchar;
3479 #endif
3480 
3481 		/* correct the start of the items for the truncation */
3482 		for (l = groupitem[groupdepth] + 1; l < curitem; l++)
3483 		{
3484 		    item[l].start -= n;
3485 		    if (item[l].start < t)
3486 			item[l].start = t;
3487 		}
3488 	    }
3489 	    else if (abs(item[groupitem[groupdepth]].minwid) > l)
3490 	    {
3491 		/* fill */
3492 		n = item[groupitem[groupdepth]].minwid;
3493 		if (n < 0)
3494 		{
3495 		    /* fill by appending characters */
3496 		    n = 0 - n;
3497 		    while (l++ < n && p + 1 < out + outlen)
3498 			*p++ = fillchar;
3499 		}
3500 		else
3501 		{
3502 		    /* fill by inserting characters */
3503 		    mch_memmove(t + n - l, t, p - t);
3504 		    l = n - l;
3505 		    if (p + l >= out + outlen)
3506 			l = (long)((out + outlen) - p - 1);
3507 		    p += l;
3508 		    for (n = groupitem[groupdepth] + 1; n < curitem; n++)
3509 			item[n].start += l;
3510 		    for ( ; l > 0; l--)
3511 			*t++ = fillchar;
3512 		}
3513 	    }
3514 	    continue;
3515 	}
3516 	minwid = 0;
3517 	maxwid = 9999;
3518 	zeropad = FALSE;
3519 	l = 1;
3520 	if (*s == '0')
3521 	{
3522 	    s++;
3523 	    zeropad = TRUE;
3524 	}
3525 	if (*s == '-')
3526 	{
3527 	    s++;
3528 	    l = -1;
3529 	}
3530 	if (VIM_ISDIGIT(*s))
3531 	{
3532 	    minwid = (int)getdigits(&s);
3533 	    if (minwid < 0)	/* overflow */
3534 		minwid = 0;
3535 	}
3536 	if (*s == STL_USER_HL)
3537 	{
3538 	    item[curitem].type = Highlight;
3539 	    item[curitem].start = p;
3540 	    item[curitem].minwid = minwid > 9 ? 1 : minwid;
3541 	    s++;
3542 	    curitem++;
3543 	    continue;
3544 	}
3545 	if (*s == STL_TABPAGENR || *s == STL_TABCLOSENR)
3546 	{
3547 	    if (*s == STL_TABCLOSENR)
3548 	    {
3549 		if (minwid == 0)
3550 		{
3551 		    /* %X ends the close label, go back to the previously
3552 		     * define tab label nr. */
3553 		    for (n = curitem - 1; n >= 0; --n)
3554 			if (item[n].type == TabPage && item[n].minwid >= 0)
3555 			{
3556 			    minwid = item[n].minwid;
3557 			    break;
3558 			}
3559 		}
3560 		else
3561 		    /* close nrs are stored as negative values */
3562 		    minwid = - minwid;
3563 	    }
3564 	    item[curitem].type = TabPage;
3565 	    item[curitem].start = p;
3566 	    item[curitem].minwid = minwid;
3567 	    s++;
3568 	    curitem++;
3569 	    continue;
3570 	}
3571 	if (*s == '.')
3572 	{
3573 	    s++;
3574 	    if (VIM_ISDIGIT(*s))
3575 	    {
3576 		maxwid = (int)getdigits(&s);
3577 		if (maxwid <= 0)	/* overflow */
3578 		    maxwid = 50;
3579 	    }
3580 	}
3581 	minwid = (minwid > 50 ? 50 : minwid) * l;
3582 	if (*s == '(')
3583 	{
3584 	    groupitem[groupdepth++] = curitem;
3585 	    item[curitem].type = Group;
3586 	    item[curitem].start = p;
3587 	    item[curitem].minwid = minwid;
3588 	    item[curitem].maxwid = maxwid;
3589 	    s++;
3590 	    curitem++;
3591 	    continue;
3592 	}
3593 	if (vim_strchr(STL_ALL, *s) == NULL)
3594 	{
3595 	    s++;
3596 	    continue;
3597 	}
3598 	opt = *s++;
3599 
3600 	/* OK - now for the real work */
3601 	base = 'D';
3602 	itemisflag = FALSE;
3603 	fillable = TRUE;
3604 	num = -1;
3605 	str = NULL;
3606 	switch (opt)
3607 	{
3608 	case STL_FILEPATH:
3609 	case STL_FULLPATH:
3610 	case STL_FILENAME:
3611 	    fillable = FALSE;	/* don't change ' ' to fillchar */
3612 	    if (buf_spname(wp->w_buffer) != NULL)
3613 		STRCPY(NameBuff, buf_spname(wp->w_buffer));
3614 	    else
3615 	    {
3616 		t = (opt == STL_FULLPATH) ? wp->w_buffer->b_ffname
3617 					  : wp->w_buffer->b_fname;
3618 		home_replace(wp->w_buffer, t, NameBuff, MAXPATHL, TRUE);
3619 	    }
3620 	    trans_characters(NameBuff, MAXPATHL);
3621 	    if (opt != STL_FILENAME)
3622 		str = NameBuff;
3623 	    else
3624 		str = gettail(NameBuff);
3625 	    break;
3626 
3627 	case STL_VIM_EXPR: /* '{' */
3628 	    itemisflag = TRUE;
3629 	    t = p;
3630 	    while (*s != '}' && *s != NUL && p + 1 < out + outlen)
3631 		*p++ = *s++;
3632 	    if (*s != '}')	/* missing '}' or out of space */
3633 		break;
3634 	    s++;
3635 	    *p = 0;
3636 	    p = t;
3637 
3638 #ifdef FEAT_EVAL
3639 	    sprintf((char *)tmp, "%d", curbuf->b_fnum);
3640 	    set_internal_string_var((char_u *)"actual_curbuf", tmp);
3641 
3642 	    o_curbuf = curbuf;
3643 	    o_curwin = curwin;
3644 	    curwin = wp;
3645 	    curbuf = wp->w_buffer;
3646 
3647 	    str = eval_to_string_safe(p, &t, use_sandbox);
3648 
3649 	    curwin = o_curwin;
3650 	    curbuf = o_curbuf;
3651 	    do_unlet((char_u *)"g:actual_curbuf", TRUE);
3652 
3653 	    if (str != NULL && *str != 0)
3654 	    {
3655 		if (*skipdigits(str) == NUL)
3656 		{
3657 		    num = atoi((char *)str);
3658 		    vim_free(str);
3659 		    str = NULL;
3660 		    itemisflag = FALSE;
3661 		}
3662 	    }
3663 #endif
3664 	    break;
3665 
3666 	case STL_LINE:
3667 	    num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY)
3668 		  ? 0L : (long)(wp->w_cursor.lnum);
3669 	    break;
3670 
3671 	case STL_NUMLINES:
3672 	    num = wp->w_buffer->b_ml.ml_line_count;
3673 	    break;
3674 
3675 	case STL_COLUMN:
3676 	    num = !(State & INSERT) && empty_line
3677 		  ? 0 : (int)wp->w_cursor.col + 1;
3678 	    break;
3679 
3680 	case STL_VIRTCOL:
3681 	case STL_VIRTCOL_ALT:
3682 	    /* In list mode virtcol needs to be recomputed */
3683 	    virtcol = wp->w_virtcol;
3684 	    if (wp->w_p_list && lcs_tab1 == NUL)
3685 	    {
3686 		wp->w_p_list = FALSE;
3687 		getvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL);
3688 		wp->w_p_list = TRUE;
3689 	    }
3690 	    ++virtcol;
3691 	    /* Don't display %V if it's the same as %c. */
3692 	    if (opt == STL_VIRTCOL_ALT
3693 		    && (virtcol == (colnr_T)(!(State & INSERT) && empty_line
3694 			    ? 0 : (int)wp->w_cursor.col + 1)))
3695 		break;
3696 	    num = (long)virtcol;
3697 	    break;
3698 
3699 	case STL_PERCENTAGE:
3700 	    num = (int)(((long)wp->w_cursor.lnum * 100L) /
3701 			(long)wp->w_buffer->b_ml.ml_line_count);
3702 	    break;
3703 
3704 	case STL_ALTPERCENT:
3705 	    str = tmp;
3706 	    get_rel_pos(wp, str);
3707 	    break;
3708 
3709 	case STL_ARGLISTSTAT:
3710 	    fillable = FALSE;
3711 	    tmp[0] = 0;
3712 	    if (append_arg_number(wp, tmp, FALSE, (int)sizeof(tmp)))
3713 		str = tmp;
3714 	    break;
3715 
3716 	case STL_KEYMAP:
3717 	    fillable = FALSE;
3718 	    if (get_keymap_str(wp, tmp, TMPLEN))
3719 		str = tmp;
3720 	    break;
3721 	case STL_PAGENUM:
3722 #if defined(FEAT_PRINTER) || defined(FEAT_GUI_TABLINE)
3723 	    num = printer_page_num;
3724 #else
3725 	    num = 0;
3726 #endif
3727 	    break;
3728 
3729 	case STL_BUFNO:
3730 	    num = wp->w_buffer->b_fnum;
3731 	    break;
3732 
3733 	case STL_OFFSET_X:
3734 	    base = 'X';
3735 	case STL_OFFSET:
3736 #ifdef FEAT_BYTEOFF
3737 	    l = ml_find_line_or_offset(wp->w_buffer, wp->w_cursor.lnum, NULL);
3738 	    num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) || l < 0 ?
3739 		  0L : l + 1 + (!(State & INSERT) && empty_line ?
3740 				0 : (int)wp->w_cursor.col);
3741 #endif
3742 	    break;
3743 
3744 	case STL_BYTEVAL_X:
3745 	    base = 'X';
3746 	case STL_BYTEVAL:
3747 	    if (wp->w_cursor.col > STRLEN(linecont))
3748 		num = 0;
3749 	    else
3750 	    {
3751 #ifdef FEAT_MBYTE
3752 		num = (*mb_ptr2char)(linecont + wp->w_cursor.col);
3753 #else
3754 		num = linecont[wp->w_cursor.col];
3755 #endif
3756 	    }
3757 	    if (num == NL)
3758 		num = 0;
3759 	    else if (num == CAR && get_fileformat(wp->w_buffer) == EOL_MAC)
3760 		num = NL;
3761 	    break;
3762 
3763 	case STL_ROFLAG:
3764 	case STL_ROFLAG_ALT:
3765 	    itemisflag = TRUE;
3766 	    if (wp->w_buffer->b_p_ro)
3767 		str = (char_u *)((opt == STL_ROFLAG_ALT) ? ",RO" : "[RO]");
3768 	    break;
3769 
3770 	case STL_HELPFLAG:
3771 	case STL_HELPFLAG_ALT:
3772 	    itemisflag = TRUE;
3773 	    if (wp->w_buffer->b_help)
3774 		str = (char_u *)((opt == STL_HELPFLAG_ALT) ? ",HLP"
3775 							       : _("[Help]"));
3776 	    break;
3777 
3778 #ifdef FEAT_AUTOCMD
3779 	case STL_FILETYPE:
3780 	    if (*wp->w_buffer->b_p_ft != NUL
3781 		    && STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 3)
3782 	    {
3783 		vim_snprintf((char *)tmp, sizeof(tmp), "[%s]",
3784 							wp->w_buffer->b_p_ft);
3785 		str = tmp;
3786 	    }
3787 	    break;
3788 
3789 	case STL_FILETYPE_ALT:
3790 	    itemisflag = TRUE;
3791 	    if (*wp->w_buffer->b_p_ft != NUL
3792 		    && STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 2)
3793 	    {
3794 		vim_snprintf((char *)tmp, sizeof(tmp), ",%s",
3795 							wp->w_buffer->b_p_ft);
3796 		for (t = tmp; *t != 0; t++)
3797 		    *t = TOUPPER_LOC(*t);
3798 		str = tmp;
3799 	    }
3800 	    break;
3801 #endif
3802 
3803 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)
3804 	case STL_PREVIEWFLAG:
3805 	case STL_PREVIEWFLAG_ALT:
3806 	    itemisflag = TRUE;
3807 	    if (wp->w_p_pvw)
3808 		str = (char_u *)((opt == STL_PREVIEWFLAG_ALT) ? ",PRV"
3809 							    : _("[Preview]"));
3810 	    break;
3811 #endif
3812 
3813 	case STL_MODIFIED:
3814 	case STL_MODIFIED_ALT:
3815 	    itemisflag = TRUE;
3816 	    switch ((opt == STL_MODIFIED_ALT)
3817 		    + bufIsChanged(wp->w_buffer) * 2
3818 		    + (!wp->w_buffer->b_p_ma) * 4)
3819 	    {
3820 		case 2: str = (char_u *)"[+]"; break;
3821 		case 3: str = (char_u *)",+"; break;
3822 		case 4: str = (char_u *)"[-]"; break;
3823 		case 5: str = (char_u *)",-"; break;
3824 		case 6: str = (char_u *)"[+-]"; break;
3825 		case 7: str = (char_u *)",+-"; break;
3826 	    }
3827 	    break;
3828 
3829 	case STL_HIGHLIGHT:
3830 	    t = s;
3831 	    while (*s != '#' && *s != NUL)
3832 		++s;
3833 	    if (*s == '#')
3834 	    {
3835 		item[curitem].type = Highlight;
3836 		item[curitem].start = p;
3837 		item[curitem].minwid = -syn_namen2id(t, (int)(s - t));
3838 		curitem++;
3839 	    }
3840 	    ++s;
3841 	    continue;
3842 	}
3843 
3844 	item[curitem].start = p;
3845 	item[curitem].type = Normal;
3846 	if (str != NULL && *str)
3847 	{
3848 	    t = str;
3849 	    if (itemisflag)
3850 	    {
3851 		if ((t[0] && t[1])
3852 			&& ((!prevchar_isitem && *t == ',')
3853 			      || (prevchar_isflag && *t == ' ')))
3854 		    t++;
3855 		prevchar_isflag = TRUE;
3856 	    }
3857 	    l = vim_strsize(t);
3858 	    if (l > 0)
3859 		prevchar_isitem = TRUE;
3860 	    if (l > maxwid)
3861 	    {
3862 		while (l >= maxwid)
3863 #ifdef FEAT_MBYTE
3864 		    if (has_mbyte)
3865 		    {
3866 			l -= ptr2cells(t);
3867 			t += (*mb_ptr2len)(t);
3868 		    }
3869 		    else
3870 #endif
3871 			l -= byte2cells(*t++);
3872 		if (p + 1 >= out + outlen)
3873 		    break;
3874 		*p++ = '<';
3875 	    }
3876 	    if (minwid > 0)
3877 	    {
3878 		for (; l < minwid && p + 1 < out + outlen; l++)
3879 		{
3880 		    /* Don't put a "-" in front of a digit. */
3881 		    if (l + 1 == minwid && fillchar == '-' && VIM_ISDIGIT(*t))
3882 			*p++ = ' ';
3883 		    else
3884 			*p++ = fillchar;
3885 		}
3886 		minwid = 0;
3887 	    }
3888 	    else
3889 		minwid *= -1;
3890 	    while (*t && p + 1 < out + outlen)
3891 	    {
3892 		*p++ = *t++;
3893 		/* Change a space by fillchar, unless fillchar is '-' and a
3894 		 * digit follows. */
3895 		if (fillable && p[-1] == ' '
3896 				     && (!VIM_ISDIGIT(*t) || fillchar != '-'))
3897 		    p[-1] = fillchar;
3898 	    }
3899 	    for (; l < minwid && p + 1 < out + outlen; l++)
3900 		*p++ = fillchar;
3901 	}
3902 	else if (num >= 0)
3903 	{
3904 	    int nbase = (base == 'D' ? 10 : (base == 'O' ? 8 : 16));
3905 	    char_u nstr[20];
3906 
3907 	    if (p + 20 >= out + outlen)
3908 		break;		/* not sufficient space */
3909 	    prevchar_isitem = TRUE;
3910 	    t = nstr;
3911 	    if (opt == STL_VIRTCOL_ALT)
3912 	    {
3913 		*t++ = '-';
3914 		minwid--;
3915 	    }
3916 	    *t++ = '%';
3917 	    if (zeropad)
3918 		*t++ = '0';
3919 	    *t++ = '*';
3920 	    *t++ = nbase == 16 ? base : (nbase == 8 ? 'o' : 'd');
3921 	    *t = 0;
3922 
3923 	    for (n = num, l = 1; n >= nbase; n /= nbase)
3924 		l++;
3925 	    if (opt == STL_VIRTCOL_ALT)
3926 		l++;
3927 	    if (l > maxwid)
3928 	    {
3929 		l += 2;
3930 		n = l - maxwid;
3931 		while (l-- > maxwid)
3932 		    num /= nbase;
3933 		*t++ = '>';
3934 		*t++ = '%';
3935 		*t = t[-3];
3936 		*++t = 0;
3937 		vim_snprintf((char *)p, outlen - (p - out), (char *)nstr,
3938 								   0, num, n);
3939 	    }
3940 	    else
3941 		vim_snprintf((char *)p, outlen - (p - out), (char *)nstr,
3942 								 minwid, num);
3943 	    p += STRLEN(p);
3944 	}
3945 	else
3946 	    item[curitem].type = Empty;
3947 
3948 	if (opt == STL_VIM_EXPR)
3949 	    vim_free(str);
3950 
3951 	if (num >= 0 || (!itemisflag && str && *str))
3952 	    prevchar_isflag = FALSE;	    /* Item not NULL, but not a flag */
3953 	curitem++;
3954     }
3955     *p = NUL;
3956     itemcnt = curitem;
3957 
3958 #ifdef FEAT_EVAL
3959     if (usefmt != fmt)
3960 	vim_free(usefmt);
3961 #endif
3962 
3963     width = vim_strsize(out);
3964     if (maxwidth > 0 && width > maxwidth)
3965     {
3966 	/* Result is too long, must trunctate somewhere. */
3967 	l = 0;
3968 	if (itemcnt == 0)
3969 	    s = out;
3970 	else
3971 	{
3972 	    for ( ; l < itemcnt; l++)
3973 		if (item[l].type == Trunc)
3974 		{
3975 		    /* Truncate at %< item. */
3976 		    s = item[l].start;
3977 		    break;
3978 		}
3979 	    if (l == itemcnt)
3980 	    {
3981 		/* No %< item, truncate first item. */
3982 		s = item[0].start;
3983 		l = 0;
3984 	    }
3985 	}
3986 
3987 	if (width - vim_strsize(s) >= maxwidth)
3988 	{
3989 	    /* Truncation mark is beyond max length */
3990 #ifdef FEAT_MBYTE
3991 	    if (has_mbyte)
3992 	    {
3993 		s = out;
3994 		width = 0;
3995 		for (;;)
3996 		{
3997 		    width += ptr2cells(s);
3998 		    if (width >= maxwidth)
3999 			break;
4000 		    s += (*mb_ptr2len)(s);
4001 		}
4002 		/* Fill up for half a double-wide character. */
4003 		while (++width < maxwidth)
4004 		    *s++ = fillchar;
4005 	    }
4006 	    else
4007 #endif
4008 		s = out + maxwidth - 1;
4009 	    for (l = 0; l < itemcnt; l++)
4010 		if (item[l].start > s)
4011 		    break;
4012 	    itemcnt = l;
4013 	    *s++ = '>';
4014 	    *s = 0;
4015 	}
4016 	else
4017 	{
4018 #ifdef FEAT_MBYTE
4019 	    if (has_mbyte)
4020 	    {
4021 		n = 0;
4022 		while (width >= maxwidth)
4023 		{
4024 		    width -= ptr2cells(s + n);
4025 		    n += (*mb_ptr2len)(s + n);
4026 		}
4027 	    }
4028 	    else
4029 #endif
4030 		n = width - maxwidth + 1;
4031 	    p = s + n;
4032 	    STRMOVE(s + 1, p);
4033 	    *s = '<';
4034 
4035 	    /* Fill up for half a double-wide character. */
4036 	    while (++width < maxwidth)
4037 	    {
4038 		s = s + STRLEN(s);
4039 		*s++ = fillchar;
4040 		*s = NUL;
4041 	    }
4042 
4043 	    --n;	/* count the '<' */
4044 	    for (; l < itemcnt; l++)
4045 	    {
4046 		if (item[l].start - n >= s)
4047 		    item[l].start -= n;
4048 		else
4049 		    item[l].start = s;
4050 	    }
4051 	}
4052 	width = maxwidth;
4053     }
4054     else if (width < maxwidth && STRLEN(out) + maxwidth - width + 1 < outlen)
4055     {
4056 	/* Apply STL_MIDDLE if any */
4057 	for (l = 0; l < itemcnt; l++)
4058 	    if (item[l].type == Middle)
4059 		break;
4060 	if (l < itemcnt)
4061 	{
4062 	    p = item[l].start + maxwidth - width;
4063 	    STRMOVE(p, item[l].start);
4064 	    for (s = item[l].start; s < p; s++)
4065 		*s = fillchar;
4066 	    for (l++; l < itemcnt; l++)
4067 		item[l].start += maxwidth - width;
4068 	    width = maxwidth;
4069 	}
4070     }
4071 
4072     /* Store the info about highlighting. */
4073     if (hltab != NULL)
4074     {
4075 	sp = hltab;
4076 	for (l = 0; l < itemcnt; l++)
4077 	{
4078 	    if (item[l].type == Highlight)
4079 	    {
4080 		sp->start = item[l].start;
4081 		sp->userhl = item[l].minwid;
4082 		sp++;
4083 	    }
4084 	}
4085 	sp->start = NULL;
4086 	sp->userhl = 0;
4087     }
4088 
4089     /* Store the info about tab pages labels. */
4090     if (tabtab != NULL)
4091     {
4092 	sp = tabtab;
4093 	for (l = 0; l < itemcnt; l++)
4094 	{
4095 	    if (item[l].type == TabPage)
4096 	    {
4097 		sp->start = item[l].start;
4098 		sp->userhl = item[l].minwid;
4099 		sp++;
4100 	    }
4101 	}
4102 	sp->start = NULL;
4103 	sp->userhl = 0;
4104     }
4105 
4106     return width;
4107 }
4108 #endif /* FEAT_STL_OPT */
4109 
4110 #if defined(FEAT_STL_OPT) || defined(FEAT_CMDL_INFO) \
4111 	    || defined(FEAT_GUI_TABLINE) || defined(PROTO)
4112 /*
4113  * Get relative cursor position in window into "str[]", in the form 99%, using
4114  * "Top", "Bot" or "All" when appropriate.
4115  */
4116     void
4117 get_rel_pos(wp, str)
4118     win_T	*wp;
4119     char_u	*str;
4120 {
4121     long	above; /* number of lines above window */
4122     long	below; /* number of lines below window */
4123 
4124     above = wp->w_topline - 1;
4125 #ifdef FEAT_DIFF
4126     above += diff_check_fill(wp, wp->w_topline) - wp->w_topfill;
4127 #endif
4128     below = wp->w_buffer->b_ml.ml_line_count - wp->w_botline + 1;
4129     if (below <= 0)
4130 	STRCPY(str, above == 0 ? _("All") : _("Bot"));
4131     else if (above <= 0)
4132 	STRCPY(str, _("Top"));
4133     else
4134 	sprintf((char *)str, "%2d%%", above > 1000000L
4135 				    ? (int)(above / ((above + below) / 100L))
4136 				    : (int)(above * 100L / (above + below)));
4137 }
4138 #endif
4139 
4140 /*
4141  * Append (file 2 of 8) to 'buf', if editing more than one file.
4142  * Return TRUE if it was appended.
4143  */
4144     int
4145 append_arg_number(wp, buf, add_file, maxlen)
4146     win_T	*wp;
4147     char_u	*buf;
4148     int		add_file;	/* Add "file" before the arg number */
4149     int		maxlen;		/* maximum nr of chars in buf or zero*/
4150 {
4151     char_u	*p;
4152 
4153     if (ARGCOUNT <= 1)		/* nothing to do */
4154 	return FALSE;
4155 
4156     p = buf + STRLEN(buf);		/* go to the end of the buffer */
4157     if (maxlen && p - buf + 35 >= maxlen) /* getting too long */
4158 	return FALSE;
4159     *p++ = ' ';
4160     *p++ = '(';
4161     if (add_file)
4162     {
4163 	STRCPY(p, "file ");
4164 	p += 5;
4165     }
4166     sprintf((char *)p, wp->w_arg_idx_invalid ? "(%d) of %d)"
4167 				  : "%d of %d)", wp->w_arg_idx + 1, ARGCOUNT);
4168     return TRUE;
4169 }
4170 
4171 /*
4172  * If fname is not a full path, make it a full path.
4173  * Returns pointer to allocated memory (NULL for failure).
4174  */
4175     char_u  *
4176 fix_fname(fname)
4177     char_u  *fname;
4178 {
4179     /*
4180      * Force expanding the path always for Unix, because symbolic links may
4181      * mess up the full path name, even though it starts with a '/'.
4182      * Also expand when there is ".." in the file name, try to remove it,
4183      * because "c:/src/../README" is equal to "c:/README".
4184      * Similarly "c:/src//file" is equal to "c:/src/file".
4185      * For MS-Windows also expand names like "longna~1" to "longname".
4186      */
4187 #ifdef UNIX
4188     return FullName_save(fname, TRUE);
4189 #else
4190     if (!vim_isAbsName(fname)
4191 	    || strstr((char *)fname, "..") != NULL
4192 	    || strstr((char *)fname, "//") != NULL
4193 # ifdef BACKSLASH_IN_FILENAME
4194 	    || strstr((char *)fname, "\\\\") != NULL
4195 # endif
4196 # if defined(MSWIN) || defined(DJGPP)
4197 	    || vim_strchr(fname, '~') != NULL
4198 # endif
4199 	    )
4200 	return FullName_save(fname, FALSE);
4201 
4202     fname = vim_strsave(fname);
4203 
4204 # ifdef USE_FNAME_CASE
4205 #  ifdef USE_LONG_FNAME
4206     if (USE_LONG_FNAME)
4207 #  endif
4208     {
4209 	if (fname != NULL)
4210 	    fname_case(fname, 0);	/* set correct case for file name */
4211     }
4212 # endif
4213 
4214     return fname;
4215 #endif
4216 }
4217 
4218 /*
4219  * Make "ffname" a full file name, set "sfname" to "ffname" if not NULL.
4220  * "ffname" becomes a pointer to allocated memory (or NULL).
4221  */
4222 /*ARGSUSED*/
4223     void
4224 fname_expand(buf, ffname, sfname)
4225     buf_T	*buf;
4226     char_u	**ffname;
4227     char_u	**sfname;
4228 {
4229     if (*ffname == NULL)	/* if no file name given, nothing to do */
4230 	return;
4231     if (*sfname == NULL)	/* if no short file name given, use ffname */
4232 	*sfname = *ffname;
4233     *ffname = fix_fname(*ffname);   /* expand to full path */
4234 
4235 #ifdef FEAT_SHORTCUT
4236     if (!buf->b_p_bin)
4237     {
4238 	char_u  *rfname;
4239 
4240 	/* If the file name is a shortcut file, use the file it links to. */
4241 	rfname = mch_resolve_shortcut(*ffname);
4242 	if (rfname != NULL)
4243 	{
4244 	    vim_free(*ffname);
4245 	    *ffname = rfname;
4246 	    *sfname = rfname;
4247 	}
4248     }
4249 #endif
4250 }
4251 
4252 /*
4253  * Get the file name for an argument list entry.
4254  */
4255     char_u *
4256 alist_name(aep)
4257     aentry_T	*aep;
4258 {
4259     buf_T	*bp;
4260 
4261     /* Use the name from the associated buffer if it exists. */
4262     bp = buflist_findnr(aep->ae_fnum);
4263     if (bp == NULL || bp->b_fname == NULL)
4264 	return aep->ae_fname;
4265     return bp->b_fname;
4266 }
4267 
4268 #if defined(FEAT_WINDOWS) || defined(PROTO)
4269 /*
4270  * do_arg_all(): Open up to 'count' windows, one for each argument.
4271  */
4272     void
4273 do_arg_all(count, forceit, keep_tabs)
4274     int	count;
4275     int	forceit;		/* hide buffers in current windows */
4276     int keep_tabs;		/* keep current tabs, for ":tab drop file" */
4277 {
4278     int		i;
4279     win_T	*wp, *wpnext;
4280     char_u	*opened;	/* array of flags for which args are open */
4281     int		opened_len;	/* length of opened[] */
4282     int		use_firstwin = FALSE;	/* use first window for arglist */
4283     int		split_ret = OK;
4284     int		p_ea_save;
4285     alist_T	*alist;		/* argument list to be used */
4286     buf_T	*buf;
4287     tabpage_T	*tpnext;
4288     int		had_tab = cmdmod.tab;
4289     win_T	*new_curwin = NULL;
4290     tabpage_T	*new_curtab = NULL;
4291 
4292     if (ARGCOUNT <= 0)
4293     {
4294 	/* Don't give an error message.  We don't want it when the ":all"
4295 	 * command is in the .vimrc. */
4296 	return;
4297     }
4298     setpcmark();
4299 
4300     opened_len = ARGCOUNT;
4301     opened = alloc_clear((unsigned)opened_len);
4302     if (opened == NULL)
4303 	return;
4304 
4305 #ifdef FEAT_GUI
4306     need_mouse_correct = TRUE;
4307 #endif
4308 
4309     /*
4310      * Try closing all windows that are not in the argument list.
4311      * Also close windows that are not full width;
4312      * When 'hidden' or "forceit" set the buffer becomes hidden.
4313      * Windows that have a changed buffer and can't be hidden won't be closed.
4314      * When the ":tab" modifier was used do this for all tab pages.
4315      */
4316     if (had_tab > 0)
4317 	goto_tabpage_tp(first_tabpage);
4318     for (;;)
4319     {
4320 	tpnext = curtab->tp_next;
4321 	for (wp = firstwin; wp != NULL; wp = wpnext)
4322 	{
4323 	    wpnext = wp->w_next;
4324 	    buf = wp->w_buffer;
4325 	    if (buf->b_ffname == NULL
4326 		    || buf->b_nwindows > 1
4327 #ifdef FEAT_VERTSPLIT
4328 		    || wp->w_width != Columns
4329 #endif
4330 		    )
4331 		i = ARGCOUNT;
4332 	    else
4333 	    {
4334 		/* check if the buffer in this window is in the arglist */
4335 		for (i = 0; i < ARGCOUNT; ++i)
4336 		{
4337 		    if (ARGLIST[i].ae_fnum == buf->b_fnum
4338 			    || fullpathcmp(alist_name(&ARGLIST[i]),
4339 					      buf->b_ffname, TRUE) & FPC_SAME)
4340 		    {
4341 			if (i < opened_len)
4342 			{
4343 			    opened[i] = TRUE;
4344 			    if (i == 0)
4345 			    {
4346 				new_curwin = wp;
4347 				new_curtab = curtab;
4348 			    }
4349 			}
4350 			if (wp->w_alist != curwin->w_alist)
4351 			{
4352 			    /* Use the current argument list for all windows
4353 			     * containing a file from it. */
4354 			    alist_unlink(wp->w_alist);
4355 			    wp->w_alist = curwin->w_alist;
4356 			    ++wp->w_alist->al_refcount;
4357 			}
4358 			break;
4359 		    }
4360 		}
4361 	    }
4362 	    wp->w_arg_idx = i;
4363 
4364 	    if (i == ARGCOUNT && !keep_tabs)	/* close this window */
4365 	    {
4366 		if (P_HID(buf) || forceit || buf->b_nwindows > 1
4367 							|| !bufIsChanged(buf))
4368 		{
4369 		    /* If the buffer was changed, and we would like to hide it,
4370 		     * try autowriting. */
4371 		    if (!P_HID(buf) && buf->b_nwindows <= 1
4372 							 && bufIsChanged(buf))
4373 		    {
4374 			(void)autowrite(buf, FALSE);
4375 #ifdef FEAT_AUTOCMD
4376 			/* check if autocommands removed the window */
4377 			if (!win_valid(wp) || !buf_valid(buf))
4378 			{
4379 			    wpnext = firstwin;	/* start all over... */
4380 			    continue;
4381 			}
4382 #endif
4383 		    }
4384 #ifdef FEAT_WINDOWS
4385 		    /* don't close last window */
4386 		    if (firstwin == lastwin && first_tabpage->tp_next == NULL)
4387 #endif
4388 			use_firstwin = TRUE;
4389 #ifdef FEAT_WINDOWS
4390 		    else
4391 		    {
4392 			win_close(wp, !P_HID(buf) && !bufIsChanged(buf));
4393 # ifdef FEAT_AUTOCMD
4394 			/* check if autocommands removed the next window */
4395 			if (!win_valid(wpnext))
4396 			    wpnext = firstwin;	/* start all over... */
4397 # endif
4398 		    }
4399 #endif
4400 		}
4401 	    }
4402 	}
4403 
4404 	/* Without the ":tab" modifier only do the current tab page. */
4405 	if (had_tab == 0 || tpnext == NULL)
4406 	    break;
4407 
4408 # ifdef FEAT_AUTOCMD
4409 	/* check if autocommands removed the next tab page */
4410 	if (!valid_tabpage(tpnext))
4411 	    tpnext = first_tabpage;	/* start all over...*/
4412 # endif
4413 	goto_tabpage_tp(tpnext);
4414     }
4415 
4416     /*
4417      * Open a window for files in the argument list that don't have one.
4418      * ARGCOUNT may change while doing this, because of autocommands.
4419      */
4420     if (count > ARGCOUNT || count <= 0)
4421 	count = ARGCOUNT;
4422 
4423     /* Autocommands may do anything to the argument list.  Make sure it's not
4424      * freed while we are working here by "locking" it.  We still have to
4425      * watch out for its size to be changed. */
4426     alist = curwin->w_alist;
4427     ++alist->al_refcount;
4428 
4429 #ifdef FEAT_AUTOCMD
4430     /* Don't execute Win/Buf Enter/Leave autocommands here. */
4431     ++autocmd_no_enter;
4432     ++autocmd_no_leave;
4433 #endif
4434     win_enter(lastwin, FALSE);
4435 #ifdef FEAT_WINDOWS
4436     /* ":drop all" should re-use an empty window to avoid "--remote-tab"
4437      * leaving an empty tab page when executed locally. */
4438     if (keep_tabs && bufempty() && curbuf->b_nwindows == 1
4439 			    && curbuf->b_ffname == NULL && !curbuf->b_changed)
4440 	use_firstwin = TRUE;
4441 #endif
4442 
4443     for (i = 0; i < count && i < alist->al_ga.ga_len && !got_int; ++i)
4444     {
4445 	if (alist == &global_alist && i == global_alist.al_ga.ga_len - 1)
4446 	    arg_had_last = TRUE;
4447 	if (i < opened_len && opened[i])
4448 	{
4449 	    /* Move the already present window to below the current window */
4450 	    if (curwin->w_arg_idx != i)
4451 	    {
4452 		for (wpnext = firstwin; wpnext != NULL; wpnext = wpnext->w_next)
4453 		{
4454 		    if (wpnext->w_arg_idx == i)
4455 		    {
4456 			win_move_after(wpnext, curwin);
4457 			break;
4458 		    }
4459 		}
4460 	    }
4461 	}
4462 	else if (split_ret == OK)
4463 	{
4464 	    if (!use_firstwin)		/* split current window */
4465 	    {
4466 		p_ea_save = p_ea;
4467 		p_ea = TRUE;		/* use space from all windows */
4468 		split_ret = win_split(0, WSP_ROOM | WSP_BELOW);
4469 		p_ea = p_ea_save;
4470 		if (split_ret == FAIL)
4471 		    continue;
4472 	    }
4473 #ifdef FEAT_AUTOCMD
4474 	    else    /* first window: do autocmd for leaving this buffer */
4475 		--autocmd_no_leave;
4476 #endif
4477 
4478 	    /*
4479 	     * edit file "i"
4480 	     */
4481 	    curwin->w_arg_idx = i;
4482 	    if (i == 0)
4483 	    {
4484 		new_curwin = curwin;
4485 		new_curtab = curtab;
4486 	    }
4487 	    (void)do_ecmd(0, alist_name(&AARGLIST(alist)[i]), NULL, NULL,
4488 		      ECMD_ONE,
4489 		      ((P_HID(curwin->w_buffer)
4490 			   || bufIsChanged(curwin->w_buffer)) ? ECMD_HIDE : 0)
4491 							       + ECMD_OLDBUF);
4492 #ifdef FEAT_AUTOCMD
4493 	    if (use_firstwin)
4494 		++autocmd_no_leave;
4495 #endif
4496 	    use_firstwin = FALSE;
4497 	}
4498 	ui_breakcheck();
4499 
4500 	/* When ":tab" was used open a new tab for a new window repeatedly. */
4501 	if (had_tab > 0 && tabpage_index(NULL) <= p_tpm)
4502 	    cmdmod.tab = 9999;
4503     }
4504 
4505     /* Remove the "lock" on the argument list. */
4506     alist_unlink(alist);
4507 
4508 #ifdef FEAT_AUTOCMD
4509     --autocmd_no_enter;
4510 #endif
4511     /* to window with first arg */
4512     if (valid_tabpage(new_curtab))
4513 	goto_tabpage_tp(new_curtab);
4514     if (win_valid(new_curwin))
4515 	win_enter(new_curwin, FALSE);
4516 
4517 #ifdef FEAT_AUTOCMD
4518     --autocmd_no_leave;
4519 #endif
4520     vim_free(opened);
4521 }
4522 
4523 # if defined(FEAT_LISTCMDS) || defined(PROTO)
4524 /*
4525  * Open a window for a number of buffers.
4526  */
4527     void
4528 ex_buffer_all(eap)
4529     exarg_T	*eap;
4530 {
4531     buf_T	*buf;
4532     win_T	*wp, *wpnext;
4533     int		split_ret = OK;
4534     int		p_ea_save;
4535     int		open_wins = 0;
4536     int		r;
4537     int		count;		/* Maximum number of windows to open. */
4538     int		all;		/* When TRUE also load inactive buffers. */
4539 #ifdef FEAT_WINDOWS
4540     int		had_tab = cmdmod.tab;
4541     tabpage_T	*tpnext;
4542 #endif
4543 
4544     if (eap->addr_count == 0)	/* make as many windows as possible */
4545 	count = 9999;
4546     else
4547 	count = eap->line2;	/* make as many windows as specified */
4548     if (eap->cmdidx == CMD_unhide || eap->cmdidx == CMD_sunhide)
4549 	all = FALSE;
4550     else
4551 	all = TRUE;
4552 
4553     setpcmark();
4554 
4555 #ifdef FEAT_GUI
4556     need_mouse_correct = TRUE;
4557 #endif
4558 
4559     /*
4560      * Close superfluous windows (two windows for the same buffer).
4561      * Also close windows that are not full-width.
4562      */
4563 #ifdef FEAT_WINDOWS
4564     if (had_tab > 0)
4565 	goto_tabpage_tp(first_tabpage);
4566     for (;;)
4567     {
4568 #endif
4569 	tpnext = curtab->tp_next;
4570 	for (wp = firstwin; wp != NULL; wp = wpnext)
4571 	{
4572 	    wpnext = wp->w_next;
4573 	    if ((wp->w_buffer->b_nwindows > 1
4574 #ifdef FEAT_VERTSPLIT
4575 		    || ((cmdmod.split & WSP_VERT)
4576 			? wp->w_height + wp->w_status_height < Rows - p_ch
4577 							    - tabline_height()
4578 			: wp->w_width != Columns)
4579 #endif
4580 #ifdef FEAT_WINDOWS
4581 		    || (had_tab > 0 && wp != firstwin)
4582 #endif
4583 		    ) && firstwin != lastwin)
4584 	    {
4585 		win_close(wp, FALSE);
4586 #ifdef FEAT_AUTOCMD
4587 		wpnext = firstwin;	/* just in case an autocommand does
4588 					   something strange with windows */
4589 		tpnext = first_tabpage;	/* start all over...*/
4590 		open_wins = 0;
4591 #endif
4592 	    }
4593 	    else
4594 		++open_wins;
4595 	}
4596 
4597 #ifdef FEAT_WINDOWS
4598 	/* Without the ":tab" modifier only do the current tab page. */
4599 	if (had_tab == 0 || tpnext == NULL)
4600 	    break;
4601 	goto_tabpage_tp(tpnext);
4602     }
4603 #endif
4604 
4605     /*
4606      * Go through the buffer list.  When a buffer doesn't have a window yet,
4607      * open one.  Otherwise move the window to the right position.
4608      * Watch out for autocommands that delete buffers or windows!
4609      */
4610 #ifdef FEAT_AUTOCMD
4611     /* Don't execute Win/Buf Enter/Leave autocommands here. */
4612     ++autocmd_no_enter;
4613 #endif
4614     win_enter(lastwin, FALSE);
4615 #ifdef FEAT_AUTOCMD
4616     ++autocmd_no_leave;
4617 #endif
4618     for (buf = firstbuf; buf != NULL && open_wins < count; buf = buf->b_next)
4619     {
4620 	/* Check if this buffer needs a window */
4621 	if ((!all && buf->b_ml.ml_mfp == NULL) || !buf->b_p_bl)
4622 	    continue;
4623 
4624 #ifdef FEAT_WINDOWS
4625 	if (had_tab != 0)
4626 	{
4627 	    /* With the ":tab" modifier don't move the window. */
4628 	    if (buf->b_nwindows > 0)
4629 		wp = lastwin;	    /* buffer has a window, skip it */
4630 	    else
4631 		wp = NULL;
4632 	}
4633 	else
4634 #endif
4635 	{
4636 	    /* Check if this buffer already has a window */
4637 	    for (wp = firstwin; wp != NULL; wp = wp->w_next)
4638 		if (wp->w_buffer == buf)
4639 		    break;
4640 	    /* If the buffer already has a window, move it */
4641 	    if (wp != NULL)
4642 		win_move_after(wp, curwin);
4643 	}
4644 
4645 	if (wp == NULL && split_ret == OK)
4646 	{
4647 	    /* Split the window and put the buffer in it */
4648 	    p_ea_save = p_ea;
4649 	    p_ea = TRUE;		/* use space from all windows */
4650 	    split_ret = win_split(0, WSP_ROOM | WSP_BELOW);
4651 	    ++open_wins;
4652 	    p_ea = p_ea_save;
4653 	    if (split_ret == FAIL)
4654 		continue;
4655 
4656 	    /* Open the buffer in this window. */
4657 #if defined(HAS_SWAP_EXISTS_ACTION)
4658 	    swap_exists_action = SEA_DIALOG;
4659 #endif
4660 	    set_curbuf(buf, DOBUF_GOTO);
4661 #ifdef FEAT_AUTOCMD
4662 	    if (!buf_valid(buf))	/* autocommands deleted the buffer!!! */
4663 	    {
4664 #if defined(HAS_SWAP_EXISTS_ACTION)
4665 		swap_exists_action = SEA_NONE;
4666 # endif
4667 		break;
4668 	    }
4669 #endif
4670 #if defined(HAS_SWAP_EXISTS_ACTION)
4671 	    if (swap_exists_action == SEA_QUIT)
4672 	    {
4673 # if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
4674 		cleanup_T   cs;
4675 
4676 		/* Reset the error/interrupt/exception state here so that
4677 		 * aborting() returns FALSE when closing a window. */
4678 		enter_cleanup(&cs);
4679 # endif
4680 
4681 		/* User selected Quit at ATTENTION prompt; close this window. */
4682 		win_close(curwin, TRUE);
4683 		--open_wins;
4684 		swap_exists_action = SEA_NONE;
4685 		swap_exists_did_quit = TRUE;
4686 
4687 # if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
4688 		/* Restore the error/interrupt/exception state if not
4689 		 * discarded by a new aborting error, interrupt, or uncaught
4690 		 * exception. */
4691 		leave_cleanup(&cs);
4692 # endif
4693 	    }
4694 	    else
4695 		handle_swap_exists(NULL);
4696 #endif
4697 	}
4698 
4699 	ui_breakcheck();
4700 	if (got_int)
4701 	{
4702 	    (void)vgetc();	/* only break the file loading, not the rest */
4703 	    break;
4704 	}
4705 #ifdef FEAT_EVAL
4706 	/* Autocommands deleted the buffer or aborted script processing!!! */
4707 	if (aborting())
4708 	    break;
4709 #endif
4710 #ifdef FEAT_WINDOWS
4711 	/* When ":tab" was used open a new tab for a new window repeatedly. */
4712 	if (had_tab > 0 && tabpage_index(NULL) <= p_tpm)
4713 	    cmdmod.tab = 9999;
4714 #endif
4715     }
4716 #ifdef FEAT_AUTOCMD
4717     --autocmd_no_enter;
4718 #endif
4719     win_enter(firstwin, FALSE);		/* back to first window */
4720 #ifdef FEAT_AUTOCMD
4721     --autocmd_no_leave;
4722 #endif
4723 
4724     /*
4725      * Close superfluous windows.
4726      */
4727     for (wp = lastwin; open_wins > count; )
4728     {
4729 	r = (P_HID(wp->w_buffer) || !bufIsChanged(wp->w_buffer)
4730 				     || autowrite(wp->w_buffer, FALSE) == OK);
4731 #ifdef FEAT_AUTOCMD
4732 	if (!win_valid(wp))
4733 	{
4734 	    /* BufWrite Autocommands made the window invalid, start over */
4735 	    wp = lastwin;
4736 	}
4737 	else
4738 #endif
4739 	    if (r)
4740 	{
4741 	    win_close(wp, !P_HID(wp->w_buffer));
4742 	    --open_wins;
4743 	    wp = lastwin;
4744 	}
4745 	else
4746 	{
4747 	    wp = wp->w_prev;
4748 	    if (wp == NULL)
4749 		break;
4750 	}
4751     }
4752 }
4753 # endif /* FEAT_LISTCMDS */
4754 
4755 #endif /* FEAT_WINDOWS */
4756 
4757 static int  chk_modeline __ARGS((linenr_T, int));
4758 
4759 /*
4760  * do_modelines() - process mode lines for the current file
4761  *
4762  * "flags" can be:
4763  * OPT_WINONLY	    only set options local to window
4764  * OPT_NOWIN	    don't set options local to window
4765  *
4766  * Returns immediately if the "ml" option isn't set.
4767  */
4768     void
4769 do_modelines(flags)
4770     int		flags;
4771 {
4772     linenr_T	lnum;
4773     int		nmlines;
4774     static int	entered = 0;
4775 
4776     if (!curbuf->b_p_ml || (nmlines = (int)p_mls) == 0)
4777 	return;
4778 
4779     /* Disallow recursive entry here.  Can happen when executing a modeline
4780      * triggers an autocommand, which reloads modelines with a ":do". */
4781     if (entered)
4782 	return;
4783 
4784     ++entered;
4785     for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count && lnum <= nmlines;
4786 								       ++lnum)
4787 	if (chk_modeline(lnum, flags) == FAIL)
4788 	    nmlines = 0;
4789 
4790     for (lnum = curbuf->b_ml.ml_line_count; lnum > 0 && lnum > nmlines
4791 		       && lnum > curbuf->b_ml.ml_line_count - nmlines; --lnum)
4792 	if (chk_modeline(lnum, flags) == FAIL)
4793 	    nmlines = 0;
4794     --entered;
4795 }
4796 
4797 #include "version.h"		/* for version number */
4798 
4799 /*
4800  * chk_modeline() - check a single line for a mode string
4801  * Return FAIL if an error encountered.
4802  */
4803     static int
4804 chk_modeline(lnum, flags)
4805     linenr_T	lnum;
4806     int		flags;		/* Same as for do_modelines(). */
4807 {
4808     char_u	*s;
4809     char_u	*e;
4810     char_u	*linecopy;		/* local copy of any modeline found */
4811     int		prev;
4812     int		vers;
4813     int		end;
4814     int		retval = OK;
4815     char_u	*save_sourcing_name;
4816     linenr_T	save_sourcing_lnum;
4817 #ifdef FEAT_EVAL
4818     scid_T	save_SID;
4819 #endif
4820 
4821     prev = -1;
4822     for (s = ml_get(lnum); *s != NUL; ++s)
4823     {
4824 	if (prev == -1 || vim_isspace(prev))
4825 	{
4826 	    if ((prev != -1 && STRNCMP(s, "ex:", (size_t)3) == 0)
4827 		    || STRNCMP(s, "vi:", (size_t)3) == 0)
4828 		break;
4829 	    if (STRNCMP(s, "vim", 3) == 0)
4830 	    {
4831 		if (s[3] == '<' || s[3] == '=' || s[3] == '>')
4832 		    e = s + 4;
4833 		else
4834 		    e = s + 3;
4835 		vers = getdigits(&e);
4836 		if (*e == ':'
4837 			&& (s[3] == ':'
4838 			    || (VIM_VERSION_100 >= vers && isdigit(s[3]))
4839 			    || (VIM_VERSION_100 < vers && s[3] == '<')
4840 			    || (VIM_VERSION_100 > vers && s[3] == '>')
4841 			    || (VIM_VERSION_100 == vers && s[3] == '=')))
4842 		    break;
4843 	    }
4844 	}
4845 	prev = *s;
4846     }
4847 
4848     if (*s)
4849     {
4850 	do				/* skip over "ex:", "vi:" or "vim:" */
4851 	    ++s;
4852 	while (s[-1] != ':');
4853 
4854 	s = linecopy = vim_strsave(s);	/* copy the line, it will change */
4855 	if (linecopy == NULL)
4856 	    return FAIL;
4857 
4858 	save_sourcing_lnum = sourcing_lnum;
4859 	save_sourcing_name = sourcing_name;
4860 	sourcing_lnum = lnum;		/* prepare for emsg() */
4861 	sourcing_name = (char_u *)"modelines";
4862 
4863 	end = FALSE;
4864 	while (end == FALSE)
4865 	{
4866 	    s = skipwhite(s);
4867 	    if (*s == NUL)
4868 		break;
4869 
4870 	    /*
4871 	     * Find end of set command: ':' or end of line.
4872 	     * Skip over "\:", replacing it with ":".
4873 	     */
4874 	    for (e = s; *e != ':' && *e != NUL; ++e)
4875 		if (e[0] == '\\' && e[1] == ':')
4876 		    STRMOVE(e, e + 1);
4877 	    if (*e == NUL)
4878 		end = TRUE;
4879 
4880 	    /*
4881 	     * If there is a "set" command, require a terminating ':' and
4882 	     * ignore the stuff after the ':'.
4883 	     * "vi:set opt opt opt: foo" -- foo not interpreted
4884 	     * "vi:opt opt opt: foo" -- foo interpreted
4885 	     * Accept "se" for compatibility with Elvis.
4886 	     */
4887 	    if (STRNCMP(s, "set ", (size_t)4) == 0
4888 		    || STRNCMP(s, "se ", (size_t)3) == 0)
4889 	    {
4890 		if (*e != ':')		/* no terminating ':'? */
4891 		    break;
4892 		end = TRUE;
4893 		s = vim_strchr(s, ' ') + 1;
4894 	    }
4895 	    *e = NUL;			/* truncate the set command */
4896 
4897 	    if (*s != NUL)		/* skip over an empty "::" */
4898 	    {
4899 #ifdef FEAT_EVAL
4900 		save_SID = current_SID;
4901 		current_SID = SID_MODELINE;
4902 #endif
4903 		retval = do_set(s, OPT_MODELINE | OPT_LOCAL | flags);
4904 #ifdef FEAT_EVAL
4905 		current_SID = save_SID;
4906 #endif
4907 		if (retval == FAIL)		/* stop if error found */
4908 		    break;
4909 	    }
4910 	    s = e + 1;			/* advance to next part */
4911 	}
4912 
4913 	sourcing_lnum = save_sourcing_lnum;
4914 	sourcing_name = save_sourcing_name;
4915 
4916 	vim_free(linecopy);
4917     }
4918     return retval;
4919 }
4920 
4921 #if defined(FEAT_VIMINFO) || defined(PROTO)
4922     int
4923 read_viminfo_bufferlist(virp, writing)
4924     vir_T	*virp;
4925     int		writing;
4926 {
4927     char_u	*tab;
4928     linenr_T	lnum;
4929     colnr_T	col;
4930     buf_T	*buf;
4931     char_u	*sfname;
4932     char_u	*xline;
4933 
4934     /* Handle long line and escaped characters. */
4935     xline = viminfo_readstring(virp, 1, FALSE);
4936 
4937     /* don't read in if there are files on the command-line or if writing: */
4938     if (xline != NULL && !writing && ARGCOUNT == 0
4939 				       && find_viminfo_parameter('%') != NULL)
4940     {
4941 	/* Format is: <fname> Tab <lnum> Tab <col>.
4942 	 * Watch out for a Tab in the file name, work from the end. */
4943 	lnum = 0;
4944 	col = 0;
4945 	tab = vim_strrchr(xline, '\t');
4946 	if (tab != NULL)
4947 	{
4948 	    *tab++ = '\0';
4949 	    col = atoi((char *)tab);
4950 	    tab = vim_strrchr(xline, '\t');
4951 	    if (tab != NULL)
4952 	    {
4953 		*tab++ = '\0';
4954 		lnum = atol((char *)tab);
4955 	    }
4956 	}
4957 
4958 	/* Expand "~/" in the file name at "line + 1" to a full path.
4959 	 * Then try shortening it by comparing with the current directory */
4960 	expand_env(xline, NameBuff, MAXPATHL);
4961 	sfname = shorten_fname1(NameBuff);
4962 
4963 	buf = buflist_new(NameBuff, sfname, (linenr_T)0, BLN_LISTED);
4964 	if (buf != NULL)	/* just in case... */
4965 	{
4966 	    buf->b_last_cursor.lnum = lnum;
4967 	    buf->b_last_cursor.col = col;
4968 	    buflist_setfpos(buf, curwin, lnum, col, FALSE);
4969 	}
4970     }
4971     vim_free(xline);
4972 
4973     return viminfo_readline(virp);
4974 }
4975 
4976     void
4977 write_viminfo_bufferlist(fp)
4978     FILE    *fp;
4979 {
4980     buf_T	*buf;
4981 #ifdef FEAT_WINDOWS
4982     win_T	*win;
4983     tabpage_T	*tp;
4984 #endif
4985     char_u	*line;
4986     int		max_buffers;
4987 
4988     if (find_viminfo_parameter('%') == NULL)
4989 	return;
4990 
4991     /* Without a number -1 is returned: do all buffers. */
4992     max_buffers = get_viminfo_parameter('%');
4993 
4994     /* Allocate room for the file name, lnum and col. */
4995     line = alloc(MAXPATHL + 40);
4996     if (line == NULL)
4997 	return;
4998 
4999 #ifdef FEAT_WINDOWS
5000     FOR_ALL_TAB_WINDOWS(tp, win)
5001 	set_last_cursor(win);
5002 #else
5003     set_last_cursor(curwin);
5004 #endif
5005 
5006     fprintf(fp, _("\n# Buffer list:\n"));
5007     for (buf = firstbuf; buf != NULL ; buf = buf->b_next)
5008     {
5009 	if (buf->b_fname == NULL
5010 		|| !buf->b_p_bl
5011 #ifdef FEAT_QUICKFIX
5012 		|| bt_quickfix(buf)
5013 #endif
5014 		|| removable(buf->b_ffname))
5015 	    continue;
5016 
5017 	if (max_buffers-- == 0)
5018 	    break;
5019 	putc('%', fp);
5020 	home_replace(NULL, buf->b_ffname, line, MAXPATHL, TRUE);
5021 	sprintf((char *)line + STRLEN(line), "\t%ld\t%d",
5022 			(long)buf->b_last_cursor.lnum,
5023 			buf->b_last_cursor.col);
5024 	viminfo_writestring(fp, line);
5025     }
5026     vim_free(line);
5027 }
5028 #endif
5029 
5030 
5031 /*
5032  * Return special buffer name.
5033  * Returns NULL when the buffer has a normal file name.
5034  */
5035     char *
5036 buf_spname(buf)
5037     buf_T	*buf;
5038 {
5039 #if defined(FEAT_QUICKFIX) && defined(FEAT_WINDOWS)
5040     if (bt_quickfix(buf))
5041     {
5042 	win_T	    *win = NULL;
5043 	tabpage_T   *tp;
5044 
5045 	/*
5046 	 * For location list window, w_llist_ref points to the location list.
5047 	 * For quickfix window, w_llist_ref is NULL.
5048 	 */
5049 	FOR_ALL_TAB_WINDOWS(tp, win)
5050 	    if (win->w_buffer == buf)
5051 		break;
5052 	if (win != NULL && win->w_llist_ref != NULL)
5053 	    return _("[Location List]");
5054 	else
5055 	    return _("[Quickfix List]");
5056     }
5057 #endif
5058 #ifdef FEAT_QUICKFIX
5059     /* There is no _file_ when 'buftype' is "nofile", b_sfname
5060      * contains the name as specified by the user */
5061     if (bt_nofile(buf))
5062     {
5063 	if (buf->b_sfname != NULL)
5064 	    return (char *)buf->b_sfname;
5065 	return "[Scratch]";
5066     }
5067 #endif
5068     if (buf->b_fname == NULL)
5069 	return _("[No Name]");
5070     return NULL;
5071 }
5072 
5073 
5074 #if defined(FEAT_SIGNS) || defined(PROTO)
5075 /*
5076  * Insert the sign into the signlist.
5077  */
5078     static void
5079 insert_sign(buf, prev, next, id, lnum, typenr)
5080     buf_T	*buf;		/* buffer to store sign in */
5081     signlist_T	*prev;		/* previous sign entry */
5082     signlist_T	*next;		/* next sign entry */
5083     int		id;		/* sign ID */
5084     linenr_T	lnum;		/* line number which gets the mark */
5085     int		typenr;		/* typenr of sign we are adding */
5086 {
5087     signlist_T	*newsign;
5088 
5089     newsign = (signlist_T *)lalloc((long_u)sizeof(signlist_T), FALSE);
5090     if (newsign != NULL)
5091     {
5092 	newsign->id = id;
5093 	newsign->lnum = lnum;
5094 	newsign->typenr = typenr;
5095 	newsign->next = next;
5096 #ifdef FEAT_NETBEANS_INTG
5097 	newsign->prev = prev;
5098 	if (next != NULL)
5099 	    next->prev = newsign;
5100 #endif
5101 
5102 	if (prev == NULL)
5103 	{
5104 	    /* When adding first sign need to redraw the windows to create the
5105 	     * column for signs. */
5106 	    if (buf->b_signlist == NULL)
5107 	    {
5108 		redraw_buf_later(buf, NOT_VALID);
5109 		changed_cline_bef_curs();
5110 	    }
5111 
5112 	    /* first sign in signlist */
5113 	    buf->b_signlist = newsign;
5114 	}
5115 	else
5116 	    prev->next = newsign;
5117     }
5118 }
5119 
5120 /*
5121  * Add the sign into the signlist. Find the right spot to do it though.
5122  */
5123     void
5124 buf_addsign(buf, id, lnum, typenr)
5125     buf_T	*buf;		/* buffer to store sign in */
5126     int		id;		/* sign ID */
5127     linenr_T	lnum;		/* line number which gets the mark */
5128     int		typenr;		/* typenr of sign we are adding */
5129 {
5130     signlist_T	*sign;		/* a sign in the signlist */
5131     signlist_T	*prev;		/* the previous sign */
5132 
5133     prev = NULL;
5134     for (sign = buf->b_signlist; sign != NULL; sign = sign->next)
5135     {
5136 	if (lnum == sign->lnum && id == sign->id)
5137 	{
5138 	    sign->typenr = typenr;
5139 	    return;
5140 	}
5141 	else if (
5142 #ifndef FEAT_NETBEANS_INTG  /* keep signs sorted by lnum */
5143 		   id < 0 &&
5144 #endif
5145 			     lnum < sign->lnum)
5146 	{
5147 #ifdef FEAT_NETBEANS_INTG /* insert new sign at head of list for this lnum */
5148 	    /* XXX - GRP: Is this because of sign slide problem? Or is it
5149 	     * really needed? Or is it because we allow multiple signs per
5150 	     * line? If so, should I add that feature to FEAT_SIGNS?
5151 	     */
5152 	    while (prev != NULL && prev->lnum == lnum)
5153 		prev = prev->prev;
5154 	    if (prev == NULL)
5155 		sign = buf->b_signlist;
5156 	    else
5157 		sign = prev->next;
5158 #endif
5159 	    insert_sign(buf, prev, sign, id, lnum, typenr);
5160 	    return;
5161 	}
5162 	prev = sign;
5163     }
5164 #ifdef FEAT_NETBEANS_INTG /* insert new sign at head of list for this lnum */
5165     /* XXX - GRP: See previous comment */
5166     while (prev != NULL && prev->lnum == lnum)
5167 	prev = prev->prev;
5168     if (prev == NULL)
5169 	sign = buf->b_signlist;
5170     else
5171 	sign = prev->next;
5172 #endif
5173     insert_sign(buf, prev, sign, id, lnum, typenr);
5174 
5175     return;
5176 }
5177 
5178     int
5179 buf_change_sign_type(buf, markId, typenr)
5180     buf_T	*buf;		/* buffer to store sign in */
5181     int		markId;		/* sign ID */
5182     int		typenr;		/* typenr of sign we are adding */
5183 {
5184     signlist_T	*sign;		/* a sign in the signlist */
5185 
5186     for (sign = buf->b_signlist; sign != NULL; sign = sign->next)
5187     {
5188 	if (sign->id == markId)
5189 	{
5190 	    sign->typenr = typenr;
5191 	    return sign->lnum;
5192 	}
5193     }
5194 
5195     return 0;
5196 }
5197 
5198     int_u
5199 buf_getsigntype(buf, lnum, type)
5200     buf_T	*buf;
5201     linenr_T	lnum;
5202     int		type;	/* SIGN_ICON, SIGN_TEXT, SIGN_ANY, SIGN_LINEHL */
5203 {
5204     signlist_T	*sign;		/* a sign in a b_signlist */
5205 
5206     for (sign = buf->b_signlist; sign != NULL; sign = sign->next)
5207 	if (sign->lnum == lnum
5208 		&& (type == SIGN_ANY
5209 # ifdef FEAT_SIGN_ICONS
5210 		    || (type == SIGN_ICON
5211 			&& sign_get_image(sign->typenr) != NULL)
5212 # endif
5213 		    || (type == SIGN_TEXT
5214 			&& sign_get_text(sign->typenr) != NULL)
5215 		    || (type == SIGN_LINEHL
5216 			&& sign_get_attr(sign->typenr, TRUE) != 0)))
5217 	    return sign->typenr;
5218     return 0;
5219 }
5220 
5221 
5222     linenr_T
5223 buf_delsign(buf, id)
5224     buf_T	*buf;		/* buffer sign is stored in */
5225     int		id;		/* sign id */
5226 {
5227     signlist_T	**lastp;	/* pointer to pointer to current sign */
5228     signlist_T	*sign;		/* a sign in a b_signlist */
5229     signlist_T	*next;		/* the next sign in a b_signlist */
5230     linenr_T	lnum;		/* line number whose sign was deleted */
5231 
5232     lastp = &buf->b_signlist;
5233     lnum = 0;
5234     for (sign = buf->b_signlist; sign != NULL; sign = next)
5235     {
5236 	next = sign->next;
5237 	if (sign->id == id)
5238 	{
5239 	    *lastp = next;
5240 #ifdef FEAT_NETBEANS_INTG
5241 	    if (next != NULL)
5242 		next->prev = sign->prev;
5243 #endif
5244 	    lnum = sign->lnum;
5245 	    vim_free(sign);
5246 	    break;
5247 	}
5248 	else
5249 	    lastp = &sign->next;
5250     }
5251 
5252     /* When deleted the last sign need to redraw the windows to remove the
5253      * sign column. */
5254     if (buf->b_signlist == NULL)
5255     {
5256 	redraw_buf_later(buf, NOT_VALID);
5257 	changed_cline_bef_curs();
5258     }
5259 
5260     return lnum;
5261 }
5262 
5263 
5264 /*
5265  * Find the line number of the sign with the requested id. If the sign does
5266  * not exist, return 0 as the line number. This will still let the correct file
5267  * get loaded.
5268  */
5269     int
5270 buf_findsign(buf, id)
5271     buf_T	*buf;		/* buffer to store sign in */
5272     int		id;		/* sign ID */
5273 {
5274     signlist_T	*sign;		/* a sign in the signlist */
5275 
5276     for (sign = buf->b_signlist; sign != NULL; sign = sign->next)
5277 	if (sign->id == id)
5278 	    return sign->lnum;
5279 
5280     return 0;
5281 }
5282 
5283     int
5284 buf_findsign_id(buf, lnum)
5285     buf_T	*buf;		/* buffer whose sign we are searching for */
5286     linenr_T	lnum;		/* line number of sign */
5287 {
5288     signlist_T	*sign;		/* a sign in the signlist */
5289 
5290     for (sign = buf->b_signlist; sign != NULL; sign = sign->next)
5291 	if (sign->lnum == lnum)
5292 	    return sign->id;
5293 
5294     return 0;
5295 }
5296 
5297 
5298 # if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
5299 /* see if a given type of sign exists on a specific line */
5300     int
5301 buf_findsigntype_id(buf, lnum, typenr)
5302     buf_T	*buf;		/* buffer whose sign we are searching for */
5303     linenr_T	lnum;		/* line number of sign */
5304     int		typenr;		/* sign type number */
5305 {
5306     signlist_T	*sign;		/* a sign in the signlist */
5307 
5308     for (sign = buf->b_signlist; sign != NULL; sign = sign->next)
5309 	if (sign->lnum == lnum && sign->typenr == typenr)
5310 	    return sign->id;
5311 
5312     return 0;
5313 }
5314 
5315 
5316 #  if defined(FEAT_SIGN_ICONS) || defined(PROTO)
5317 /* return the number of icons on the given line */
5318     int
5319 buf_signcount(buf, lnum)
5320     buf_T	*buf;
5321     linenr_T	lnum;
5322 {
5323     signlist_T	*sign;		/* a sign in the signlist */
5324     int		count = 0;
5325 
5326     for (sign = buf->b_signlist; sign != NULL; sign = sign->next)
5327 	if (sign->lnum == lnum)
5328 	    if (sign_get_image(sign->typenr) != NULL)
5329 		count++;
5330 
5331     return count;
5332 }
5333 #  endif /* FEAT_SIGN_ICONS */
5334 # endif /* FEAT_NETBEANS_INTG */
5335 
5336 
5337 /*
5338  * Delete signs in buffer "buf".
5339  */
5340     static void
5341 buf_delete_signs(buf)
5342     buf_T	*buf;
5343 {
5344     signlist_T	*next;
5345 
5346     while (buf->b_signlist != NULL)
5347     {
5348 	next = buf->b_signlist->next;
5349 	vim_free(buf->b_signlist);
5350 	buf->b_signlist = next;
5351     }
5352 }
5353 
5354 /*
5355  * Delete all signs in all buffers.
5356  */
5357     void
5358 buf_delete_all_signs()
5359 {
5360     buf_T	*buf;		/* buffer we are checking for signs */
5361 
5362     for (buf = firstbuf; buf != NULL; buf = buf->b_next)
5363 	if (buf->b_signlist != NULL)
5364 	{
5365 	    /* Need to redraw the windows to remove the sign column. */
5366 	    redraw_buf_later(buf, NOT_VALID);
5367 	    buf_delete_signs(buf);
5368 	}
5369 }
5370 
5371 /*
5372  * List placed signs for "rbuf".  If "rbuf" is NULL do it for all buffers.
5373  */
5374     void
5375 sign_list_placed(rbuf)
5376     buf_T	*rbuf;
5377 {
5378     buf_T	*buf;
5379     signlist_T	*p;
5380     char	lbuf[BUFSIZ];
5381 
5382     MSG_PUTS_TITLE(_("\n--- Signs ---"));
5383     msg_putchar('\n');
5384     if (rbuf == NULL)
5385 	buf = firstbuf;
5386     else
5387 	buf = rbuf;
5388     while (buf != NULL)
5389     {
5390 	if (buf->b_signlist != NULL)
5391 	{
5392 	    vim_snprintf(lbuf, BUFSIZ, _("Signs for %s:"), buf->b_fname);
5393 	    MSG_PUTS_ATTR(lbuf, hl_attr(HLF_D));
5394 	    msg_putchar('\n');
5395 	}
5396 	for (p = buf->b_signlist; p != NULL; p = p->next)
5397 	{
5398 	    vim_snprintf(lbuf, BUFSIZ, _("    line=%ld  id=%d  name=%s"),
5399 			   (long)p->lnum, p->id, sign_typenr2name(p->typenr));
5400 	    MSG_PUTS(lbuf);
5401 	    msg_putchar('\n');
5402 	}
5403 	if (rbuf != NULL)
5404 	    break;
5405 	buf = buf->b_next;
5406     }
5407 }
5408 
5409 /*
5410  * Adjust a placed sign for inserted/deleted lines.
5411  */
5412     void
5413 sign_mark_adjust(line1, line2, amount, amount_after)
5414     linenr_T	line1;
5415     linenr_T	line2;
5416     long	amount;
5417     long	amount_after;
5418 {
5419     signlist_T	*sign;		/* a sign in a b_signlist */
5420 
5421     for (sign = curbuf->b_signlist; sign != NULL; sign = sign->next)
5422     {
5423 	if (sign->lnum >= line1 && sign->lnum <= line2)
5424 	{
5425 	    if (amount == MAXLNUM)
5426 		sign->lnum = line1;
5427 	    else
5428 		sign->lnum += amount;
5429 	}
5430 	else if (sign->lnum > line2)
5431 	    sign->lnum += amount_after;
5432     }
5433 }
5434 #endif /* FEAT_SIGNS */
5435 
5436 /*
5437  * Set 'buflisted' for curbuf to "on" and trigger autocommands if it changed.
5438  */
5439     void
5440 set_buflisted(on)
5441     int		on;
5442 {
5443     if (on != curbuf->b_p_bl)
5444     {
5445 	curbuf->b_p_bl = on;
5446 #ifdef FEAT_AUTOCMD
5447 	if (on)
5448 	    apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, curbuf);
5449 	else
5450 	    apply_autocmds(EVENT_BUFDELETE, NULL, NULL, FALSE, curbuf);
5451 #endif
5452     }
5453 }
5454 
5455 /*
5456  * Read the file for "buf" again and check if the contents changed.
5457  * Return TRUE if it changed or this could not be checked.
5458  */
5459     int
5460 buf_contents_changed(buf)
5461     buf_T	*buf;
5462 {
5463     buf_T	*newbuf;
5464     int		differ = TRUE;
5465     linenr_T	lnum;
5466     aco_save_T	aco;
5467     exarg_T	ea;
5468 
5469     /* Allocate a buffer without putting it in the buffer list. */
5470     newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY);
5471     if (newbuf == NULL)
5472 	return TRUE;
5473 
5474     /* Force the 'fileencoding' and 'fileformat' to be equal. */
5475     if (prep_exarg(&ea, buf) == FAIL)
5476     {
5477 	wipe_buffer(newbuf, FALSE);
5478 	return TRUE;
5479     }
5480 
5481     /* set curwin/curbuf to buf and save a few things */
5482     aucmd_prepbuf(&aco, newbuf);
5483 
5484     if (ml_open(curbuf) == OK
5485 	    && readfile(buf->b_ffname, buf->b_fname,
5486 				  (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM,
5487 					    &ea, READ_NEW | READ_DUMMY) == OK)
5488     {
5489 	/* compare the two files line by line */
5490 	if (buf->b_ml.ml_line_count == curbuf->b_ml.ml_line_count)
5491 	{
5492 	    differ = FALSE;
5493 	    for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum)
5494 		if (STRCMP(ml_get_buf(buf, lnum, FALSE), ml_get(lnum)) != 0)
5495 		{
5496 		    differ = TRUE;
5497 		    break;
5498 		}
5499 	}
5500     }
5501     vim_free(ea.cmd);
5502 
5503     /* restore curwin/curbuf and a few other things */
5504     aucmd_restbuf(&aco);
5505 
5506     if (curbuf != newbuf)	/* safety check */
5507 	wipe_buffer(newbuf, FALSE);
5508 
5509     return differ;
5510 }
5511 
5512 /*
5513  * Wipe out a buffer and decrement the last buffer number if it was used for
5514  * this buffer.  Call this to wipe out a temp buffer that does not contain any
5515  * marks.
5516  */
5517 /*ARGSUSED*/
5518     void
5519 wipe_buffer(buf, aucmd)
5520     buf_T	*buf;
5521     int		aucmd;	    /* When TRUE trigger autocommands. */
5522 {
5523     if (buf->b_fnum == top_file_num - 1)
5524 	--top_file_num;
5525 
5526 #ifdef FEAT_AUTOCMD
5527     if (!aucmd)		    /* Don't trigger BufDelete autocommands here. */
5528 	block_autocmds();
5529 #endif
5530     close_buffer(NULL, buf, DOBUF_WIPE);
5531 #ifdef FEAT_AUTOCMD
5532     if (!aucmd)
5533 	unblock_autocmds();
5534 #endif
5535 }
5536