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