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