xref: /vim-8.2.3635/src/mark.c (revision 147e7d0c)
1 /* vi:set ts=8 sts=4 sw=4 noet:
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  * mark.c: functions for setting marks and jumping to them
12  */
13 
14 #include "vim.h"
15 
16 /*
17  * This file contains routines to maintain and manipulate marks.
18  */
19 
20 /*
21  * If a named file mark's lnum is non-zero, it is valid.
22  * If a named file mark's fnum is non-zero, it is for an existing buffer,
23  * otherwise it is from .viminfo and namedfm[n].fname is the file name.
24  * There are marks 'A - 'Z (set by user) and '0 to '9 (set when writing
25  * viminfo).
26  */
27 #define EXTRA_MARKS 10					/* marks 0-9 */
28 static xfmark_T namedfm[NMARKS + EXTRA_MARKS];		/* marks with file nr */
29 
30 static void fmarks_check_one(xfmark_T *fm, char_u *name, buf_T *buf);
31 static char_u *mark_line(pos_T *mp, int lead_len);
32 static void show_one_mark(int, char_u *, pos_T *, char_u *, int current);
33 #ifdef FEAT_VIMINFO
34 static void write_one_filemark(FILE *fp, xfmark_T *fm, int c1, int c2);
35 #endif
36 static void mark_adjust_internal(linenr_T line1, linenr_T line2, long amount,
37     long amount_after, int adjust_folds);
38 
39 /*
40  * Set named mark "c" at current cursor position.
41  * Returns OK on success, FAIL if bad name given.
42  */
43     int
44 setmark(int c)
45 {
46     return setmark_pos(c, &curwin->w_cursor, curbuf->b_fnum);
47 }
48 
49 /*
50  * Set named mark "c" to position "pos".
51  * When "c" is upper case use file "fnum".
52  * Returns OK on success, FAIL if bad name given.
53  */
54     int
55 setmark_pos(int c, pos_T *pos, int fnum)
56 {
57     int		i;
58     buf_T	*buf;
59 
60     /* Check for a special key (may cause islower() to crash). */
61     if (c < 0)
62 	return FAIL;
63 
64     if (c == '\'' || c == '`')
65     {
66 	if (pos == &curwin->w_cursor)
67 	{
68 	    setpcmark();
69 	    /* keep it even when the cursor doesn't move */
70 	    curwin->w_prev_pcmark = curwin->w_pcmark;
71 	}
72 	else
73 	    curwin->w_pcmark = *pos;
74 	return OK;
75     }
76 
77     buf = buflist_findnr(fnum);
78     if (buf == NULL)
79 	return FAIL;
80 
81     if (c == '"')
82     {
83 	buf->b_last_cursor = *pos;
84 	return OK;
85     }
86 
87     /* Allow setting '[ and '] for an autocommand that simulates reading a
88      * file. */
89     if (c == '[')
90     {
91 	buf->b_op_start = *pos;
92 	return OK;
93     }
94     if (c == ']')
95     {
96 	buf->b_op_end = *pos;
97 	return OK;
98     }
99 
100     if (c == '<' || c == '>')
101     {
102 	if (c == '<')
103 	    buf->b_visual.vi_start = *pos;
104 	else
105 	    buf->b_visual.vi_end = *pos;
106 	if (buf->b_visual.vi_mode == NUL)
107 	    /* Visual_mode has not yet been set, use a sane default. */
108 	    buf->b_visual.vi_mode = 'v';
109 	return OK;
110     }
111 
112     if (ASCII_ISLOWER(c))
113     {
114 	i = c - 'a';
115 	buf->b_namedm[i] = *pos;
116 	return OK;
117     }
118     if (ASCII_ISUPPER(c) || VIM_ISDIGIT(c))
119     {
120 	if (VIM_ISDIGIT(c))
121 	    i = c - '0' + NMARKS;
122 	else
123 	    i = c - 'A';
124 	namedfm[i].fmark.mark = *pos;
125 	namedfm[i].fmark.fnum = fnum;
126 	VIM_CLEAR(namedfm[i].fname);
127 #ifdef FEAT_VIMINFO
128 	namedfm[i].time_set = vim_time();
129 #endif
130 	return OK;
131     }
132     return FAIL;
133 }
134 
135 /*
136  * Set the previous context mark to the current position and add it to the
137  * jump list.
138  */
139     void
140 setpcmark(void)
141 {
142 #ifdef FEAT_JUMPLIST
143     int		i;
144     xfmark_T	*fm;
145 #endif
146 #ifdef JUMPLIST_ROTATE
147     xfmark_T	tempmark;
148 #endif
149 
150     /* for :global the mark is set only once */
151     if (global_busy || listcmd_busy || cmdmod.keepjumps)
152 	return;
153 
154     curwin->w_prev_pcmark = curwin->w_pcmark;
155     curwin->w_pcmark = curwin->w_cursor;
156 
157 #ifdef FEAT_JUMPLIST
158 # ifdef JUMPLIST_ROTATE
159     /*
160      * If last used entry is not at the top, put it at the top by rotating
161      * the stack until it is (the newer entries will be at the bottom).
162      * Keep one entry (the last used one) at the top.
163      */
164     if (curwin->w_jumplistidx < curwin->w_jumplistlen)
165 	++curwin->w_jumplistidx;
166     while (curwin->w_jumplistidx < curwin->w_jumplistlen)
167     {
168 	tempmark = curwin->w_jumplist[curwin->w_jumplistlen - 1];
169 	for (i = curwin->w_jumplistlen - 1; i > 0; --i)
170 	    curwin->w_jumplist[i] = curwin->w_jumplist[i - 1];
171 	curwin->w_jumplist[0] = tempmark;
172 	++curwin->w_jumplistidx;
173     }
174 # endif
175 
176     /* If jumplist is full: remove oldest entry */
177     if (++curwin->w_jumplistlen > JUMPLISTSIZE)
178     {
179 	curwin->w_jumplistlen = JUMPLISTSIZE;
180 	vim_free(curwin->w_jumplist[0].fname);
181 	for (i = 1; i < JUMPLISTSIZE; ++i)
182 	    curwin->w_jumplist[i - 1] = curwin->w_jumplist[i];
183     }
184     curwin->w_jumplistidx = curwin->w_jumplistlen;
185     fm = &curwin->w_jumplist[curwin->w_jumplistlen - 1];
186 
187     fm->fmark.mark = curwin->w_pcmark;
188     fm->fmark.fnum = curbuf->b_fnum;
189     fm->fname = NULL;
190 # ifdef FEAT_VIMINFO
191     fm->time_set = vim_time();
192 # endif
193 #endif
194 }
195 
196 /*
197  * To change context, call setpcmark(), then move the current position to
198  * where ever, then call checkpcmark().  This ensures that the previous
199  * context will only be changed if the cursor moved to a different line.
200  * If pcmark was deleted (with "dG") the previous mark is restored.
201  */
202     void
203 checkpcmark(void)
204 {
205     if (curwin->w_prev_pcmark.lnum != 0
206 	    && (EQUAL_POS(curwin->w_pcmark, curwin->w_cursor)
207 		|| curwin->w_pcmark.lnum == 0))
208     {
209 	curwin->w_pcmark = curwin->w_prev_pcmark;
210 	curwin->w_prev_pcmark.lnum = 0;		/* Show it has been checked */
211     }
212 }
213 
214 #if defined(FEAT_JUMPLIST) || defined(PROTO)
215 /*
216  * move "count" positions in the jump list (count may be negative)
217  */
218     pos_T *
219 movemark(int count)
220 {
221     pos_T	*pos;
222     xfmark_T	*jmp;
223 
224     cleanup_jumplist(curwin, TRUE);
225 
226     if (curwin->w_jumplistlen == 0)	    /* nothing to jump to */
227 	return (pos_T *)NULL;
228 
229     for (;;)
230     {
231 	if (curwin->w_jumplistidx + count < 0
232 		|| curwin->w_jumplistidx + count >= curwin->w_jumplistlen)
233 	    return (pos_T *)NULL;
234 
235 	/*
236 	 * if first CTRL-O or CTRL-I command after a jump, add cursor position
237 	 * to list.  Careful: If there are duplicates (CTRL-O immediately after
238 	 * starting Vim on a file), another entry may have been removed.
239 	 */
240 	if (curwin->w_jumplistidx == curwin->w_jumplistlen)
241 	{
242 	    setpcmark();
243 	    --curwin->w_jumplistidx;	/* skip the new entry */
244 	    if (curwin->w_jumplistidx + count < 0)
245 		return (pos_T *)NULL;
246 	}
247 
248 	curwin->w_jumplistidx += count;
249 
250 	jmp = curwin->w_jumplist + curwin->w_jumplistidx;
251 	if (jmp->fmark.fnum == 0)
252 	    fname2fnum(jmp);
253 	if (jmp->fmark.fnum != curbuf->b_fnum)
254 	{
255 	    /* jump to other file */
256 	    if (buflist_findnr(jmp->fmark.fnum) == NULL)
257 	    {					     /* Skip this one .. */
258 		count += count < 0 ? -1 : 1;
259 		continue;
260 	    }
261 	    if (buflist_getfile(jmp->fmark.fnum, jmp->fmark.mark.lnum,
262 							    0, FALSE) == FAIL)
263 		return (pos_T *)NULL;
264 	    /* Set lnum again, autocommands my have changed it */
265 	    curwin->w_cursor = jmp->fmark.mark;
266 	    pos = (pos_T *)-1;
267 	}
268 	else
269 	    pos = &(jmp->fmark.mark);
270 	return pos;
271     }
272 }
273 
274 /*
275  * Move "count" positions in the changelist (count may be negative).
276  */
277     pos_T *
278 movechangelist(int count)
279 {
280     int		n;
281 
282     if (curbuf->b_changelistlen == 0)	    /* nothing to jump to */
283 	return (pos_T *)NULL;
284 
285     n = curwin->w_changelistidx;
286     if (n + count < 0)
287     {
288 	if (n == 0)
289 	    return (pos_T *)NULL;
290 	n = 0;
291     }
292     else if (n + count >= curbuf->b_changelistlen)
293     {
294 	if (n == curbuf->b_changelistlen - 1)
295 	    return (pos_T *)NULL;
296 	n = curbuf->b_changelistlen - 1;
297     }
298     else
299 	n += count;
300     curwin->w_changelistidx = n;
301     return curbuf->b_changelist + n;
302 }
303 #endif
304 
305 /*
306  * Find mark "c" in buffer pointed to by "buf".
307  * If "changefile" is TRUE it's allowed to edit another file for '0, 'A, etc.
308  * If "fnum" is not NULL store the fnum there for '0, 'A etc., don't edit
309  * another file.
310  * Returns:
311  * - pointer to pos_T if found.  lnum is 0 when mark not set, -1 when mark is
312  *   in another file which can't be gotten. (caller needs to check lnum!)
313  * - NULL if there is no mark called 'c'.
314  * - -1 if mark is in other file and jumped there (only if changefile is TRUE)
315  */
316     pos_T *
317 getmark_buf(buf_T *buf, int c, int changefile)
318 {
319     return getmark_buf_fnum(buf, c, changefile, NULL);
320 }
321 
322     pos_T *
323 getmark(int c, int changefile)
324 {
325     return getmark_buf_fnum(curbuf, c, changefile, NULL);
326 }
327 
328     pos_T *
329 getmark_buf_fnum(
330     buf_T	*buf,
331     int		c,
332     int		changefile,
333     int		*fnum)
334 {
335     pos_T		*posp;
336     pos_T		*startp, *endp;
337     static pos_T	pos_copy;
338 
339     posp = NULL;
340 
341     /* Check for special key, can't be a mark name and might cause islower()
342      * to crash. */
343     if (c < 0)
344 	return posp;
345 #ifndef EBCDIC
346     if (c > '~')			/* check for islower()/isupper() */
347 	;
348     else
349 #endif
350 	if (c == '\'' || c == '`')	/* previous context mark */
351     {
352 	pos_copy = curwin->w_pcmark;	/* need to make a copy because */
353 	posp = &pos_copy;		/*   w_pcmark may be changed soon */
354     }
355     else if (c == '"')			/* to pos when leaving buffer */
356 	posp = &(buf->b_last_cursor);
357     else if (c == '^')			/* to where Insert mode stopped */
358 	posp = &(buf->b_last_insert);
359     else if (c == '.')			/* to where last change was made */
360 	posp = &(buf->b_last_change);
361     else if (c == '[')			/* to start of previous operator */
362 	posp = &(buf->b_op_start);
363     else if (c == ']')			/* to end of previous operator */
364 	posp = &(buf->b_op_end);
365     else if (c == '{' || c == '}')	/* to previous/next paragraph */
366     {
367 	pos_T	pos;
368 	oparg_T	oa;
369 	int	slcb = listcmd_busy;
370 
371 	pos = curwin->w_cursor;
372 	listcmd_busy = TRUE;	    /* avoid that '' is changed */
373 	if (findpar(&oa.inclusive,
374 			       c == '}' ? FORWARD : BACKWARD, 1L, NUL, FALSE))
375 	{
376 	    pos_copy = curwin->w_cursor;
377 	    posp = &pos_copy;
378 	}
379 	curwin->w_cursor = pos;
380 	listcmd_busy = slcb;
381     }
382     else if (c == '(' || c == ')')	/* to previous/next sentence */
383     {
384 	pos_T	pos;
385 	int	slcb = listcmd_busy;
386 
387 	pos = curwin->w_cursor;
388 	listcmd_busy = TRUE;	    /* avoid that '' is changed */
389 	if (findsent(c == ')' ? FORWARD : BACKWARD, 1L))
390 	{
391 	    pos_copy = curwin->w_cursor;
392 	    posp = &pos_copy;
393 	}
394 	curwin->w_cursor = pos;
395 	listcmd_busy = slcb;
396     }
397     else if (c == '<' || c == '>')	/* start/end of visual area */
398     {
399 	startp = &buf->b_visual.vi_start;
400 	endp = &buf->b_visual.vi_end;
401 	if (((c == '<') == LT_POS(*startp, *endp) || endp->lnum == 0)
402 							  && startp->lnum != 0)
403 	    posp = startp;
404 	else
405 	    posp = endp;
406 	/*
407 	 * For Visual line mode, set mark at begin or end of line
408 	 */
409 	if (buf->b_visual.vi_mode == 'V')
410 	{
411 	    pos_copy = *posp;
412 	    posp = &pos_copy;
413 	    if (c == '<')
414 		pos_copy.col = 0;
415 	    else
416 		pos_copy.col = MAXCOL;
417 #ifdef FEAT_VIRTUALEDIT
418 	    pos_copy.coladd = 0;
419 #endif
420 	}
421     }
422     else if (ASCII_ISLOWER(c))		/* normal named mark */
423     {
424 	posp = &(buf->b_namedm[c - 'a']);
425     }
426     else if (ASCII_ISUPPER(c) || VIM_ISDIGIT(c))	/* named file mark */
427     {
428 	if (VIM_ISDIGIT(c))
429 	    c = c - '0' + NMARKS;
430 	else
431 	    c -= 'A';
432 	posp = &(namedfm[c].fmark.mark);
433 
434 	if (namedfm[c].fmark.fnum == 0)
435 	    fname2fnum(&namedfm[c]);
436 
437 	if (fnum != NULL)
438 	    *fnum = namedfm[c].fmark.fnum;
439 	else if (namedfm[c].fmark.fnum != buf->b_fnum)
440 	{
441 	    /* mark is in another file */
442 	    posp = &pos_copy;
443 
444 	    if (namedfm[c].fmark.mark.lnum != 0
445 				       && changefile && namedfm[c].fmark.fnum)
446 	    {
447 		if (buflist_getfile(namedfm[c].fmark.fnum,
448 				      (linenr_T)1, GETF_SETMARK, FALSE) == OK)
449 		{
450 		    /* Set the lnum now, autocommands could have changed it */
451 		    curwin->w_cursor = namedfm[c].fmark.mark;
452 		    return (pos_T *)-1;
453 		}
454 		pos_copy.lnum = -1;	/* can't get file */
455 	    }
456 	    else
457 		pos_copy.lnum = 0;	/* mark exists, but is not valid in
458 					   current buffer */
459 	}
460     }
461 
462     return posp;
463 }
464 
465 /*
466  * Search for the next named mark in the current file.
467  *
468  * Returns pointer to pos_T of the next mark or NULL if no mark is found.
469  */
470     pos_T *
471 getnextmark(
472     pos_T	*startpos,	/* where to start */
473     int		dir,	/* direction for search */
474     int		begin_line)
475 {
476     int		i;
477     pos_T	*result = NULL;
478     pos_T	pos;
479 
480     pos = *startpos;
481 
482     /* When searching backward and leaving the cursor on the first non-blank,
483      * position must be in a previous line.
484      * When searching forward and leaving the cursor on the first non-blank,
485      * position must be in a next line. */
486     if (dir == BACKWARD && begin_line)
487 	pos.col = 0;
488     else if (dir == FORWARD && begin_line)
489 	pos.col = MAXCOL;
490 
491     for (i = 0; i < NMARKS; i++)
492     {
493 	if (curbuf->b_namedm[i].lnum > 0)
494 	{
495 	    if (dir == FORWARD)
496 	    {
497 		if ((result == NULL || LT_POS(curbuf->b_namedm[i], *result))
498 			&& LT_POS(pos, curbuf->b_namedm[i]))
499 		    result = &curbuf->b_namedm[i];
500 	    }
501 	    else
502 	    {
503 		if ((result == NULL || LT_POS(*result, curbuf->b_namedm[i]))
504 			&& LT_POS(curbuf->b_namedm[i], pos))
505 		    result = &curbuf->b_namedm[i];
506 	    }
507 	}
508     }
509 
510     return result;
511 }
512 
513 /*
514  * For an xtended filemark: set the fnum from the fname.
515  * This is used for marks obtained from the .viminfo file.  It's postponed
516  * until the mark is used to avoid a long startup delay.
517  */
518     void
519 fname2fnum(xfmark_T *fm)
520 {
521     char_u	*p;
522 
523     if (fm->fname != NULL)
524     {
525 	/*
526 	 * First expand "~/" in the file name to the home directory.
527 	 * Don't expand the whole name, it may contain other '~' chars.
528 	 */
529 	if (fm->fname[0] == '~' && (fm->fname[1] == '/'
530 #ifdef BACKSLASH_IN_FILENAME
531 		    || fm->fname[1] == '\\'
532 #endif
533 		    ))
534 	{
535 	    int len;
536 
537 	    expand_env((char_u *)"~/", NameBuff, MAXPATHL);
538 	    len = (int)STRLEN(NameBuff);
539 	    vim_strncpy(NameBuff + len, fm->fname + 2, MAXPATHL - len - 1);
540 	}
541 	else
542 	    vim_strncpy(NameBuff, fm->fname, MAXPATHL - 1);
543 
544 	/* Try to shorten the file name. */
545 	mch_dirname(IObuff, IOSIZE);
546 	p = shorten_fname(NameBuff, IObuff);
547 
548 	/* buflist_new() will call fmarks_check_names() */
549 	(void)buflist_new(NameBuff, p, (linenr_T)1, 0);
550     }
551 }
552 
553 /*
554  * Check all file marks for a name that matches the file name in buf.
555  * May replace the name with an fnum.
556  * Used for marks that come from the .viminfo file.
557  */
558     void
559 fmarks_check_names(buf_T *buf)
560 {
561     char_u	*name;
562     int		i;
563 #ifdef FEAT_JUMPLIST
564     win_T	*wp;
565 #endif
566 
567     if (buf->b_ffname == NULL)
568 	return;
569 
570     name = home_replace_save(buf, buf->b_ffname);
571     if (name == NULL)
572 	return;
573 
574     for (i = 0; i < NMARKS + EXTRA_MARKS; ++i)
575 	fmarks_check_one(&namedfm[i], name, buf);
576 
577 #ifdef FEAT_JUMPLIST
578     FOR_ALL_WINDOWS(wp)
579     {
580 	for (i = 0; i < wp->w_jumplistlen; ++i)
581 	    fmarks_check_one(&wp->w_jumplist[i], name, buf);
582     }
583 #endif
584 
585     vim_free(name);
586 }
587 
588     static void
589 fmarks_check_one(xfmark_T *fm, char_u *name, buf_T *buf)
590 {
591     if (fm->fmark.fnum == 0
592 	    && fm->fname != NULL
593 	    && fnamecmp(name, fm->fname) == 0)
594     {
595 	fm->fmark.fnum = buf->b_fnum;
596 	VIM_CLEAR(fm->fname);
597     }
598 }
599 
600 /*
601  * Check a if a position from a mark is valid.
602  * Give and error message and return FAIL if not.
603  */
604     int
605 check_mark(pos_T *pos)
606 {
607     if (pos == NULL)
608     {
609 	emsg(_(e_umark));
610 	return FAIL;
611     }
612     if (pos->lnum <= 0)
613     {
614 	/* lnum is negative if mark is in another file can can't get that
615 	 * file, error message already give then. */
616 	if (pos->lnum == 0)
617 	    emsg(_(e_marknotset));
618 	return FAIL;
619     }
620     if (pos->lnum > curbuf->b_ml.ml_line_count)
621     {
622 	emsg(_(e_markinval));
623 	return FAIL;
624     }
625     return OK;
626 }
627 
628 /*
629  * clrallmarks() - clear all marks in the buffer 'buf'
630  *
631  * Used mainly when trashing the entire buffer during ":e" type commands
632  */
633     void
634 clrallmarks(buf_T *buf)
635 {
636     static int		i = -1;
637 
638     if (i == -1)	/* first call ever: initialize */
639 	for (i = 0; i < NMARKS + 1; i++)
640 	{
641 	    namedfm[i].fmark.mark.lnum = 0;
642 	    namedfm[i].fname = NULL;
643 #ifdef FEAT_VIMINFO
644 	    namedfm[i].time_set = 0;
645 #endif
646 	}
647 
648     for (i = 0; i < NMARKS; i++)
649 	buf->b_namedm[i].lnum = 0;
650     buf->b_op_start.lnum = 0;		/* start/end op mark cleared */
651     buf->b_op_end.lnum = 0;
652     buf->b_last_cursor.lnum = 1;	/* '" mark cleared */
653     buf->b_last_cursor.col = 0;
654 #ifdef FEAT_VIRTUALEDIT
655     buf->b_last_cursor.coladd = 0;
656 #endif
657     buf->b_last_insert.lnum = 0;	/* '^ mark cleared */
658     buf->b_last_change.lnum = 0;	/* '. mark cleared */
659 #ifdef FEAT_JUMPLIST
660     buf->b_changelistlen = 0;
661 #endif
662 }
663 
664 /*
665  * Get name of file from a filemark.
666  * When it's in the current buffer, return the text at the mark.
667  * Returns an allocated string.
668  */
669     char_u *
670 fm_getname(fmark_T *fmark, int lead_len)
671 {
672     if (fmark->fnum == curbuf->b_fnum)		    /* current buffer */
673 	return mark_line(&(fmark->mark), lead_len);
674     return buflist_nr2name(fmark->fnum, FALSE, TRUE);
675 }
676 
677 /*
678  * Return the line at mark "mp".  Truncate to fit in window.
679  * The returned string has been allocated.
680  */
681     static char_u *
682 mark_line(pos_T *mp, int lead_len)
683 {
684     char_u	*s, *p;
685     int		len;
686 
687     if (mp->lnum == 0 || mp->lnum > curbuf->b_ml.ml_line_count)
688 	return vim_strsave((char_u *)"-invalid-");
689     // Allow for up to 5 bytes per character.
690     s = vim_strnsave(skipwhite(ml_get(mp->lnum)), (int)Columns * 5);
691     if (s == NULL)
692 	return NULL;
693     // Truncate the line to fit it in the window.
694     len = 0;
695     for (p = s; *p != NUL; MB_PTR_ADV(p))
696     {
697 	len += ptr2cells(p);
698 	if (len >= Columns - lead_len)
699 	    break;
700     }
701     *p = NUL;
702     return s;
703 }
704 
705 /*
706  * print the marks
707  */
708     void
709 do_marks(exarg_T *eap)
710 {
711     char_u	*arg = eap->arg;
712     int		i;
713     char_u	*name;
714 
715     if (arg != NULL && *arg == NUL)
716 	arg = NULL;
717 
718     show_one_mark('\'', arg, &curwin->w_pcmark, NULL, TRUE);
719     for (i = 0; i < NMARKS; ++i)
720 	show_one_mark(i + 'a', arg, &curbuf->b_namedm[i], NULL, TRUE);
721     for (i = 0; i < NMARKS + EXTRA_MARKS; ++i)
722     {
723 	if (namedfm[i].fmark.fnum != 0)
724 	    name = fm_getname(&namedfm[i].fmark, 15);
725 	else
726 	    name = namedfm[i].fname;
727 	if (name != NULL)
728 	{
729 	    show_one_mark(i >= NMARKS ? i - NMARKS + '0' : i + 'A',
730 		    arg, &namedfm[i].fmark.mark, name,
731 		    namedfm[i].fmark.fnum == curbuf->b_fnum);
732 	    if (namedfm[i].fmark.fnum != 0)
733 		vim_free(name);
734 	}
735     }
736     show_one_mark('"', arg, &curbuf->b_last_cursor, NULL, TRUE);
737     show_one_mark('[', arg, &curbuf->b_op_start, NULL, TRUE);
738     show_one_mark(']', arg, &curbuf->b_op_end, NULL, TRUE);
739     show_one_mark('^', arg, &curbuf->b_last_insert, NULL, TRUE);
740     show_one_mark('.', arg, &curbuf->b_last_change, NULL, TRUE);
741     show_one_mark('<', arg, &curbuf->b_visual.vi_start, NULL, TRUE);
742     show_one_mark('>', arg, &curbuf->b_visual.vi_end, NULL, TRUE);
743     show_one_mark(-1, arg, NULL, NULL, FALSE);
744 }
745 
746     static void
747 show_one_mark(
748     int		c,
749     char_u	*arg,
750     pos_T	*p,
751     char_u	*name,
752     int		current)	/* in current file */
753 {
754     static int	did_title = FALSE;
755     int		mustfree = FALSE;
756 
757     if (c == -1)			    /* finish up */
758     {
759 	if (did_title)
760 	    did_title = FALSE;
761 	else
762 	{
763 	    if (arg == NULL)
764 		MSG(_("No marks set"));
765 	    else
766 		semsg(_("E283: No marks matching \"%s\""), arg);
767 	}
768     }
769     /* don't output anything if 'q' typed at --more-- prompt */
770     else if (!got_int
771 	    && (arg == NULL || vim_strchr(arg, c) != NULL)
772 	    && p->lnum != 0)
773     {
774 	if (!did_title)
775 	{
776 	    /* Highlight title */
777 	    MSG_PUTS_TITLE(_("\nmark line  col file/text"));
778 	    did_title = TRUE;
779 	}
780 	msg_putchar('\n');
781 	if (!got_int)
782 	{
783 	    sprintf((char *)IObuff, " %c %6ld %4d ", c, p->lnum, p->col);
784 	    msg_outtrans(IObuff);
785 	    if (name == NULL && current)
786 	    {
787 		name = mark_line(p, 15);
788 		mustfree = TRUE;
789 	    }
790 	    if (name != NULL)
791 	    {
792 		msg_outtrans_attr(name, current ? HL_ATTR(HLF_D) : 0);
793 		if (mustfree)
794 		    vim_free(name);
795 	    }
796 	}
797 	out_flush();		    /* show one line at a time */
798     }
799 }
800 
801 /*
802  * ":delmarks[!] [marks]"
803  */
804     void
805 ex_delmarks(exarg_T *eap)
806 {
807     char_u	*p;
808     int		from, to;
809     int		i;
810     int		lower;
811     int		digit;
812     int		n;
813 
814     if (*eap->arg == NUL && eap->forceit)
815 	/* clear all marks */
816 	clrallmarks(curbuf);
817     else if (eap->forceit)
818 	emsg(_(e_invarg));
819     else if (*eap->arg == NUL)
820 	emsg(_(e_argreq));
821     else
822     {
823 	/* clear specified marks only */
824 	for (p = eap->arg; *p != NUL; ++p)
825 	{
826 	    lower = ASCII_ISLOWER(*p);
827 	    digit = VIM_ISDIGIT(*p);
828 	    if (lower || digit || ASCII_ISUPPER(*p))
829 	    {
830 		if (p[1] == '-')
831 		{
832 		    /* clear range of marks */
833 		    from = *p;
834 		    to = p[2];
835 		    if (!(lower ? ASCII_ISLOWER(p[2])
836 				: (digit ? VIM_ISDIGIT(p[2])
837 				    : ASCII_ISUPPER(p[2])))
838 			    || to < from)
839 		    {
840 			semsg(_(e_invarg2), p);
841 			return;
842 		    }
843 		    p += 2;
844 		}
845 		else
846 		    /* clear one lower case mark */
847 		    from = to = *p;
848 
849 		for (i = from; i <= to; ++i)
850 		{
851 		    if (lower)
852 			curbuf->b_namedm[i - 'a'].lnum = 0;
853 		    else
854 		    {
855 			if (digit)
856 			    n = i - '0' + NMARKS;
857 			else
858 			    n = i - 'A';
859 			namedfm[n].fmark.mark.lnum = 0;
860 			VIM_CLEAR(namedfm[n].fname);
861 #ifdef FEAT_VIMINFO
862 			namedfm[n].time_set = 0;
863 #endif
864 		    }
865 		}
866 	    }
867 	    else
868 		switch (*p)
869 		{
870 		    case '"': curbuf->b_last_cursor.lnum = 0; break;
871 		    case '^': curbuf->b_last_insert.lnum = 0; break;
872 		    case '.': curbuf->b_last_change.lnum = 0; break;
873 		    case '[': curbuf->b_op_start.lnum    = 0; break;
874 		    case ']': curbuf->b_op_end.lnum      = 0; break;
875 		    case '<': curbuf->b_visual.vi_start.lnum = 0; break;
876 		    case '>': curbuf->b_visual.vi_end.lnum   = 0; break;
877 		    case ' ': break;
878 		    default:  semsg(_(e_invarg2), p);
879 			      return;
880 		}
881 	}
882     }
883 }
884 
885 #if defined(FEAT_JUMPLIST) || defined(PROTO)
886 /*
887  * print the jumplist
888  */
889     void
890 ex_jumps(exarg_T *eap UNUSED)
891 {
892     int		i;
893     char_u	*name;
894 
895     cleanup_jumplist(curwin, TRUE);
896 
897     /* Highlight title */
898     MSG_PUTS_TITLE(_("\n jump line  col file/text"));
899     for (i = 0; i < curwin->w_jumplistlen && !got_int; ++i)
900     {
901 	if (curwin->w_jumplist[i].fmark.mark.lnum != 0)
902 	{
903 	    name = fm_getname(&curwin->w_jumplist[i].fmark, 16);
904 
905 	    // apply :filter /pat/ or file name not available
906 	    if (name == NULL || message_filtered(name))
907 		continue;
908 
909 	    msg_putchar('\n');
910 	    if (got_int)
911 	    {
912 		vim_free(name);
913 		break;
914 	    }
915 	    sprintf((char *)IObuff, "%c %2d %5ld %4d ",
916 		i == curwin->w_jumplistidx ? '>' : ' ',
917 		i > curwin->w_jumplistidx ? i - curwin->w_jumplistidx
918 					  : curwin->w_jumplistidx - i,
919 		curwin->w_jumplist[i].fmark.mark.lnum,
920 		curwin->w_jumplist[i].fmark.mark.col);
921 	    msg_outtrans(IObuff);
922 	    msg_outtrans_attr(name,
923 			    curwin->w_jumplist[i].fmark.fnum == curbuf->b_fnum
924 							? HL_ATTR(HLF_D) : 0);
925 	    vim_free(name);
926 	    ui_breakcheck();
927 	}
928 	out_flush();
929     }
930     if (curwin->w_jumplistidx == curwin->w_jumplistlen)
931 	MSG_PUTS("\n>");
932 }
933 
934     void
935 ex_clearjumps(exarg_T *eap UNUSED)
936 {
937     free_jumplist(curwin);
938     curwin->w_jumplistlen = 0;
939     curwin->w_jumplistidx = 0;
940 }
941 
942 /*
943  * print the changelist
944  */
945     void
946 ex_changes(exarg_T *eap UNUSED)
947 {
948     int		i;
949     char_u	*name;
950 
951     /* Highlight title */
952     MSG_PUTS_TITLE(_("\nchange line  col text"));
953 
954     for (i = 0; i < curbuf->b_changelistlen && !got_int; ++i)
955     {
956 	if (curbuf->b_changelist[i].lnum != 0)
957 	{
958 	    msg_putchar('\n');
959 	    if (got_int)
960 		break;
961 	    sprintf((char *)IObuff, "%c %3d %5ld %4d ",
962 		    i == curwin->w_changelistidx ? '>' : ' ',
963 		    i > curwin->w_changelistidx ? i - curwin->w_changelistidx
964 						: curwin->w_changelistidx - i,
965 		    (long)curbuf->b_changelist[i].lnum,
966 		    curbuf->b_changelist[i].col);
967 	    msg_outtrans(IObuff);
968 	    name = mark_line(&curbuf->b_changelist[i], 17);
969 	    if (name == NULL)
970 		break;
971 	    msg_outtrans_attr(name, HL_ATTR(HLF_D));
972 	    vim_free(name);
973 	    ui_breakcheck();
974 	}
975 	out_flush();
976     }
977     if (curwin->w_changelistidx == curbuf->b_changelistlen)
978 	MSG_PUTS("\n>");
979 }
980 #endif
981 
982 #define one_adjust(add) \
983     { \
984 	lp = add; \
985 	if (*lp >= line1 && *lp <= line2) \
986 	{ \
987 	    if (amount == MAXLNUM) \
988 		*lp = 0; \
989 	    else \
990 		*lp += amount; \
991 	} \
992 	else if (amount_after && *lp > line2) \
993 	    *lp += amount_after; \
994     }
995 
996 /* don't delete the line, just put at first deleted line */
997 #define one_adjust_nodel(add) \
998     { \
999 	lp = add; \
1000 	if (*lp >= line1 && *lp <= line2) \
1001 	{ \
1002 	    if (amount == MAXLNUM) \
1003 		*lp = line1; \
1004 	    else \
1005 		*lp += amount; \
1006 	} \
1007 	else if (amount_after && *lp > line2) \
1008 	    *lp += amount_after; \
1009     }
1010 
1011 /*
1012  * Adjust marks between line1 and line2 (inclusive) to move 'amount' lines.
1013  * Must be called before changed_*(), appended_lines() or deleted_lines().
1014  * May be called before or after changing the text.
1015  * When deleting lines line1 to line2, use an 'amount' of MAXLNUM: The marks
1016  * within this range are made invalid.
1017  * If 'amount_after' is non-zero adjust marks after line2.
1018  * Example: Delete lines 34 and 35: mark_adjust(34, 35, MAXLNUM, -2);
1019  * Example: Insert two lines below 55: mark_adjust(56, MAXLNUM, 2, 0);
1020  *				   or: mark_adjust(56, 55, MAXLNUM, 2);
1021  */
1022     void
1023 mark_adjust(
1024     linenr_T	line1,
1025     linenr_T	line2,
1026     long	amount,
1027     long	amount_after)
1028 {
1029     mark_adjust_internal(line1, line2, amount, amount_after, TRUE);
1030 }
1031 
1032     void
1033 mark_adjust_nofold(
1034     linenr_T line1,
1035     linenr_T line2,
1036     long amount,
1037     long amount_after)
1038 {
1039     mark_adjust_internal(line1, line2, amount, amount_after, FALSE);
1040 }
1041 
1042     static void
1043 mark_adjust_internal(
1044     linenr_T line1,
1045     linenr_T line2,
1046     long amount,
1047     long amount_after,
1048     int adjust_folds UNUSED)
1049 {
1050     int		i;
1051     int		fnum = curbuf->b_fnum;
1052     linenr_T	*lp;
1053     win_T	*win;
1054     tabpage_T	*tab;
1055     static pos_T initpos = INIT_POS_T(1, 0, 0);
1056 
1057     if (line2 < line1 && amount_after == 0L)	    /* nothing to do */
1058 	return;
1059 
1060     if (!cmdmod.lockmarks)
1061     {
1062 	/* named marks, lower case and upper case */
1063 	for (i = 0; i < NMARKS; i++)
1064 	{
1065 	    one_adjust(&(curbuf->b_namedm[i].lnum));
1066 	    if (namedfm[i].fmark.fnum == fnum)
1067 		one_adjust_nodel(&(namedfm[i].fmark.mark.lnum));
1068 	}
1069 	for (i = NMARKS; i < NMARKS + EXTRA_MARKS; i++)
1070 	{
1071 	    if (namedfm[i].fmark.fnum == fnum)
1072 		one_adjust_nodel(&(namedfm[i].fmark.mark.lnum));
1073 	}
1074 
1075 	/* last Insert position */
1076 	one_adjust(&(curbuf->b_last_insert.lnum));
1077 
1078 	/* last change position */
1079 	one_adjust(&(curbuf->b_last_change.lnum));
1080 
1081 	/* last cursor position, if it was set */
1082 	if (!EQUAL_POS(curbuf->b_last_cursor, initpos))
1083 	    one_adjust(&(curbuf->b_last_cursor.lnum));
1084 
1085 
1086 #ifdef FEAT_JUMPLIST
1087 	/* list of change positions */
1088 	for (i = 0; i < curbuf->b_changelistlen; ++i)
1089 	    one_adjust_nodel(&(curbuf->b_changelist[i].lnum));
1090 #endif
1091 
1092 	/* Visual area */
1093 	one_adjust_nodel(&(curbuf->b_visual.vi_start.lnum));
1094 	one_adjust_nodel(&(curbuf->b_visual.vi_end.lnum));
1095 
1096 #ifdef FEAT_QUICKFIX
1097 	/* quickfix marks */
1098 	qf_mark_adjust(NULL, line1, line2, amount, amount_after);
1099 	/* location lists */
1100 	FOR_ALL_TAB_WINDOWS(tab, win)
1101 	    qf_mark_adjust(win, line1, line2, amount, amount_after);
1102 #endif
1103 
1104 #ifdef FEAT_SIGNS
1105 	sign_mark_adjust(line1, line2, amount, amount_after);
1106 #endif
1107     }
1108 
1109     /* previous context mark */
1110     one_adjust(&(curwin->w_pcmark.lnum));
1111 
1112     /* previous pcmark */
1113     one_adjust(&(curwin->w_prev_pcmark.lnum));
1114 
1115     /* saved cursor for formatting */
1116     if (saved_cursor.lnum != 0)
1117 	one_adjust_nodel(&(saved_cursor.lnum));
1118 
1119     /*
1120      * Adjust items in all windows related to the current buffer.
1121      */
1122     FOR_ALL_TAB_WINDOWS(tab, win)
1123     {
1124 #ifdef FEAT_JUMPLIST
1125 	if (!cmdmod.lockmarks)
1126 	    /* Marks in the jumplist.  When deleting lines, this may create
1127 	     * duplicate marks in the jumplist, they will be removed later. */
1128 	    for (i = 0; i < win->w_jumplistlen; ++i)
1129 		if (win->w_jumplist[i].fmark.fnum == fnum)
1130 		    one_adjust_nodel(&(win->w_jumplist[i].fmark.mark.lnum));
1131 #endif
1132 
1133 	if (win->w_buffer == curbuf)
1134 	{
1135 	    if (!cmdmod.lockmarks)
1136 		/* marks in the tag stack */
1137 		for (i = 0; i < win->w_tagstacklen; i++)
1138 		    if (win->w_tagstack[i].fmark.fnum == fnum)
1139 			one_adjust_nodel(&(win->w_tagstack[i].fmark.mark.lnum));
1140 
1141 	    /* the displayed Visual area */
1142 	    if (win->w_old_cursor_lnum != 0)
1143 	    {
1144 		one_adjust_nodel(&(win->w_old_cursor_lnum));
1145 		one_adjust_nodel(&(win->w_old_visual_lnum));
1146 	    }
1147 
1148 	    /* topline and cursor position for windows with the same buffer
1149 	     * other than the current window */
1150 	    if (win != curwin)
1151 	    {
1152 		if (win->w_topline >= line1 && win->w_topline <= line2)
1153 		{
1154 		    if (amount == MAXLNUM)	    /* topline is deleted */
1155 		    {
1156 			if (line1 <= 1)
1157 			    win->w_topline = 1;
1158 			else
1159 			    win->w_topline = line1 - 1;
1160 		    }
1161 		    else		/* keep topline on the same line */
1162 			win->w_topline += amount;
1163 #ifdef FEAT_DIFF
1164 		    win->w_topfill = 0;
1165 #endif
1166 		}
1167 		else if (amount_after && win->w_topline > line2)
1168 		{
1169 		    win->w_topline += amount_after;
1170 #ifdef FEAT_DIFF
1171 		    win->w_topfill = 0;
1172 #endif
1173 		}
1174 		if (win->w_cursor.lnum >= line1 && win->w_cursor.lnum <= line2)
1175 		{
1176 		    if (amount == MAXLNUM) /* line with cursor is deleted */
1177 		    {
1178 			if (line1 <= 1)
1179 			    win->w_cursor.lnum = 1;
1180 			else
1181 			    win->w_cursor.lnum = line1 - 1;
1182 			win->w_cursor.col = 0;
1183 		    }
1184 		    else		/* keep cursor on the same line */
1185 			win->w_cursor.lnum += amount;
1186 		}
1187 		else if (amount_after && win->w_cursor.lnum > line2)
1188 		    win->w_cursor.lnum += amount_after;
1189 	    }
1190 
1191 #ifdef FEAT_FOLDING
1192 	    /* adjust folds */
1193 	    if (adjust_folds)
1194 		foldMarkAdjust(win, line1, line2, amount, amount_after);
1195 #endif
1196 	}
1197     }
1198 
1199 #ifdef FEAT_DIFF
1200     /* adjust diffs */
1201     diff_mark_adjust(line1, line2, amount, amount_after);
1202 #endif
1203 }
1204 
1205 /* This code is used often, needs to be fast. */
1206 #define col_adjust(pp) \
1207     { \
1208 	posp = pp; \
1209 	if (posp->lnum == lnum && posp->col >= mincol) \
1210 	{ \
1211 	    posp->lnum += lnum_amount; \
1212 	    if (col_amount < 0 && posp->col <= (colnr_T)-col_amount) \
1213 		posp->col = 0; \
1214 	    else if (posp->col < spaces_removed) \
1215 		posp->col = col_amount + spaces_removed; \
1216 	    else \
1217 		posp->col += col_amount; \
1218 	} \
1219     }
1220 
1221 /*
1222  * Adjust marks in line "lnum" at column "mincol" and further: add
1223  * "lnum_amount" to the line number and add "col_amount" to the column
1224  * position.
1225  * "spaces_removed" is the number of spaces that were removed, matters when the
1226  * cursor is inside them.
1227  */
1228     void
1229 mark_col_adjust(
1230     linenr_T	lnum,
1231     colnr_T	mincol,
1232     long	lnum_amount,
1233     long	col_amount,
1234     int		spaces_removed)
1235 {
1236     int		i;
1237     int		fnum = curbuf->b_fnum;
1238     win_T	*win;
1239     pos_T	*posp;
1240 
1241     if ((col_amount == 0L && lnum_amount == 0L) || cmdmod.lockmarks)
1242 	return; /* nothing to do */
1243 
1244     /* named marks, lower case and upper case */
1245     for (i = 0; i < NMARKS; i++)
1246     {
1247 	col_adjust(&(curbuf->b_namedm[i]));
1248 	if (namedfm[i].fmark.fnum == fnum)
1249 	    col_adjust(&(namedfm[i].fmark.mark));
1250     }
1251     for (i = NMARKS; i < NMARKS + EXTRA_MARKS; i++)
1252     {
1253 	if (namedfm[i].fmark.fnum == fnum)
1254 	    col_adjust(&(namedfm[i].fmark.mark));
1255     }
1256 
1257     /* last Insert position */
1258     col_adjust(&(curbuf->b_last_insert));
1259 
1260     /* last change position */
1261     col_adjust(&(curbuf->b_last_change));
1262 
1263 #ifdef FEAT_JUMPLIST
1264     /* list of change positions */
1265     for (i = 0; i < curbuf->b_changelistlen; ++i)
1266 	col_adjust(&(curbuf->b_changelist[i]));
1267 #endif
1268 
1269     /* Visual area */
1270     col_adjust(&(curbuf->b_visual.vi_start));
1271     col_adjust(&(curbuf->b_visual.vi_end));
1272 
1273     /* previous context mark */
1274     col_adjust(&(curwin->w_pcmark));
1275 
1276     /* previous pcmark */
1277     col_adjust(&(curwin->w_prev_pcmark));
1278 
1279     /* saved cursor for formatting */
1280     col_adjust(&saved_cursor);
1281 
1282     /*
1283      * Adjust items in all windows related to the current buffer.
1284      */
1285     FOR_ALL_WINDOWS(win)
1286     {
1287 #ifdef FEAT_JUMPLIST
1288 	/* marks in the jumplist */
1289 	for (i = 0; i < win->w_jumplistlen; ++i)
1290 	    if (win->w_jumplist[i].fmark.fnum == fnum)
1291 		col_adjust(&(win->w_jumplist[i].fmark.mark));
1292 #endif
1293 
1294 	if (win->w_buffer == curbuf)
1295 	{
1296 	    /* marks in the tag stack */
1297 	    for (i = 0; i < win->w_tagstacklen; i++)
1298 		if (win->w_tagstack[i].fmark.fnum == fnum)
1299 		    col_adjust(&(win->w_tagstack[i].fmark.mark));
1300 
1301 	    /* cursor position for other windows with the same buffer */
1302 	    if (win != curwin)
1303 		col_adjust(&win->w_cursor);
1304 	}
1305     }
1306 }
1307 
1308 #ifdef FEAT_JUMPLIST
1309 /*
1310  * When deleting lines, this may create duplicate marks in the
1311  * jumplist. They will be removed here for the specified window.
1312  * When "loadfiles" is TRUE first ensure entries have the "fnum" field set
1313  * (this may be a bit slow).
1314  */
1315     void
1316 cleanup_jumplist(win_T *wp, int loadfiles)
1317 {
1318     int	    i;
1319     int	    from, to;
1320 
1321     if (loadfiles)
1322     {
1323 	/* If specified, load all the files from the jump list. This is
1324 	 * needed to properly clean up duplicate entries, but will take some
1325 	 * time. */
1326 	for (i = 0; i < wp->w_jumplistlen; ++i)
1327 	{
1328 	    if ((wp->w_jumplist[i].fmark.fnum == 0) &&
1329 		    (wp->w_jumplist[i].fmark.mark.lnum != 0))
1330 		fname2fnum(&wp->w_jumplist[i]);
1331 	}
1332     }
1333 
1334     to = 0;
1335     for (from = 0; from < wp->w_jumplistlen; ++from)
1336     {
1337 	if (wp->w_jumplistidx == from)
1338 	    wp->w_jumplistidx = to;
1339 	for (i = from + 1; i < wp->w_jumplistlen; ++i)
1340 	    if (wp->w_jumplist[i].fmark.fnum
1341 					== wp->w_jumplist[from].fmark.fnum
1342 		    && wp->w_jumplist[from].fmark.fnum != 0
1343 		    && wp->w_jumplist[i].fmark.mark.lnum
1344 				  == wp->w_jumplist[from].fmark.mark.lnum)
1345 		break;
1346 	if (i >= wp->w_jumplistlen)	    /* no duplicate */
1347 	    wp->w_jumplist[to++] = wp->w_jumplist[from];
1348 	else
1349 	    vim_free(wp->w_jumplist[from].fname);
1350     }
1351     if (wp->w_jumplistidx == wp->w_jumplistlen)
1352 	wp->w_jumplistidx = to;
1353     wp->w_jumplistlen = to;
1354 }
1355 
1356 /*
1357  * Copy the jumplist from window "from" to window "to".
1358  */
1359     void
1360 copy_jumplist(win_T *from, win_T *to)
1361 {
1362     int		i;
1363 
1364     for (i = 0; i < from->w_jumplistlen; ++i)
1365     {
1366 	to->w_jumplist[i] = from->w_jumplist[i];
1367 	if (from->w_jumplist[i].fname != NULL)
1368 	    to->w_jumplist[i].fname = vim_strsave(from->w_jumplist[i].fname);
1369     }
1370     to->w_jumplistlen = from->w_jumplistlen;
1371     to->w_jumplistidx = from->w_jumplistidx;
1372 }
1373 
1374 /*
1375  * Free items in the jumplist of window "wp".
1376  */
1377     void
1378 free_jumplist(win_T *wp)
1379 {
1380     int		i;
1381 
1382     for (i = 0; i < wp->w_jumplistlen; ++i)
1383 	vim_free(wp->w_jumplist[i].fname);
1384 }
1385 #endif /* FEAT_JUMPLIST */
1386 
1387     void
1388 set_last_cursor(win_T *win)
1389 {
1390     if (win->w_buffer != NULL)
1391 	win->w_buffer->b_last_cursor = win->w_cursor;
1392 }
1393 
1394 #if defined(EXITFREE) || defined(PROTO)
1395     void
1396 free_all_marks(void)
1397 {
1398     int		i;
1399 
1400     for (i = 0; i < NMARKS + EXTRA_MARKS; i++)
1401 	if (namedfm[i].fmark.mark.lnum != 0)
1402 	    vim_free(namedfm[i].fname);
1403 }
1404 #endif
1405 
1406 #if defined(FEAT_VIMINFO) || defined(PROTO)
1407     int
1408 read_viminfo_filemark(vir_T *virp, int force)
1409 {
1410     char_u	*str;
1411     xfmark_T	*fm;
1412     int		i;
1413 
1414     /* We only get here if line[0] == '\'' or '-'.
1415      * Illegal mark names are ignored (for future expansion). */
1416     str = virp->vir_line + 1;
1417     if (
1418 #ifndef EBCDIC
1419 	    *str <= 127 &&
1420 #endif
1421 	    ((*virp->vir_line == '\'' && (VIM_ISDIGIT(*str) || isupper(*str)))
1422 	     || (*virp->vir_line == '-' && *str == '\'')))
1423     {
1424 	if (*str == '\'')
1425 	{
1426 #ifdef FEAT_JUMPLIST
1427 	    /* If the jumplist isn't full insert fmark as oldest entry */
1428 	    if (curwin->w_jumplistlen == JUMPLISTSIZE)
1429 		fm = NULL;
1430 	    else
1431 	    {
1432 		for (i = curwin->w_jumplistlen; i > 0; --i)
1433 		    curwin->w_jumplist[i] = curwin->w_jumplist[i - 1];
1434 		++curwin->w_jumplistidx;
1435 		++curwin->w_jumplistlen;
1436 		fm = &curwin->w_jumplist[0];
1437 		fm->fmark.mark.lnum = 0;
1438 		fm->fname = NULL;
1439 	    }
1440 #else
1441 	    fm = NULL;
1442 #endif
1443 	}
1444 	else if (VIM_ISDIGIT(*str))
1445 	    fm = &namedfm[*str - '0' + NMARKS];
1446 	else
1447 	    fm = &namedfm[*str - 'A'];
1448 	if (fm != NULL && (fm->fmark.mark.lnum == 0 || force))
1449 	{
1450 	    str = skipwhite(str + 1);
1451 	    fm->fmark.mark.lnum = getdigits(&str);
1452 	    str = skipwhite(str);
1453 	    fm->fmark.mark.col = getdigits(&str);
1454 #ifdef FEAT_VIRTUALEDIT
1455 	    fm->fmark.mark.coladd = 0;
1456 #endif
1457 	    fm->fmark.fnum = 0;
1458 	    str = skipwhite(str);
1459 	    vim_free(fm->fname);
1460 	    fm->fname = viminfo_readstring(virp, (int)(str - virp->vir_line),
1461 								       FALSE);
1462 	    fm->time_set = 0;
1463 	}
1464     }
1465     return vim_fgets(virp->vir_line, LSIZE, virp->vir_fd);
1466 }
1467 
1468 static xfmark_T *vi_namedfm = NULL;
1469 #ifdef FEAT_JUMPLIST
1470 static xfmark_T *vi_jumplist = NULL;
1471 static int vi_jumplist_len = 0;
1472 #endif
1473 
1474 /*
1475  * Prepare for reading viminfo marks when writing viminfo later.
1476  */
1477     void
1478 prepare_viminfo_marks(void)
1479 {
1480     vi_namedfm = (xfmark_T *)alloc_clear((NMARKS + EXTRA_MARKS)
1481 						     * (int)sizeof(xfmark_T));
1482 #ifdef FEAT_JUMPLIST
1483     vi_jumplist = (xfmark_T *)alloc_clear(JUMPLISTSIZE
1484 						     * (int)sizeof(xfmark_T));
1485     vi_jumplist_len = 0;
1486 #endif
1487 }
1488 
1489     void
1490 finish_viminfo_marks(void)
1491 {
1492     int		i;
1493 
1494     if (vi_namedfm != NULL)
1495     {
1496 	for (i = 0; i < NMARKS + EXTRA_MARKS; ++i)
1497 	    vim_free(vi_namedfm[i].fname);
1498 	VIM_CLEAR(vi_namedfm);
1499     }
1500 #ifdef FEAT_JUMPLIST
1501     if (vi_jumplist != NULL)
1502     {
1503 	for (i = 0; i < vi_jumplist_len; ++i)
1504 	    vim_free(vi_jumplist[i].fname);
1505 	VIM_CLEAR(vi_jumplist);
1506     }
1507 #endif
1508 }
1509 
1510 /*
1511  * Accept a new style mark line from the viminfo, store it when it's new.
1512  */
1513     void
1514 handle_viminfo_mark(garray_T *values, int force)
1515 {
1516     bval_T	*vp = (bval_T *)values->ga_data;
1517     int		name;
1518     linenr_T	lnum;
1519     colnr_T	col;
1520     time_t	timestamp;
1521     xfmark_T	*fm = NULL;
1522 
1523     /* Check the format:
1524      * |{bartype},{name},{lnum},{col},{timestamp},{filename} */
1525     if (values->ga_len < 5
1526 	    || vp[0].bv_type != BVAL_NR
1527 	    || vp[1].bv_type != BVAL_NR
1528 	    || vp[2].bv_type != BVAL_NR
1529 	    || vp[3].bv_type != BVAL_NR
1530 	    || vp[4].bv_type != BVAL_STRING)
1531 	return;
1532 
1533     name = vp[0].bv_nr;
1534     if (name != '\'' && !VIM_ISDIGIT(name) && !ASCII_ISUPPER(name))
1535 	return;
1536     lnum = vp[1].bv_nr;
1537     col = vp[2].bv_nr;
1538     if (lnum <= 0 || col < 0)
1539 	return;
1540     timestamp = (time_t)vp[3].bv_nr;
1541 
1542     if (name == '\'')
1543     {
1544 #ifdef FEAT_JUMPLIST
1545 	if (vi_jumplist != NULL)
1546 	{
1547 	    if (vi_jumplist_len < JUMPLISTSIZE)
1548 		fm = &vi_jumplist[vi_jumplist_len++];
1549 	}
1550 	else
1551 	{
1552 	    int idx;
1553 	    int i;
1554 
1555 	    /* If we have a timestamp insert it in the right place. */
1556 	    if (timestamp != 0)
1557 	    {
1558 		for (idx = curwin->w_jumplistlen - 1; idx >= 0; --idx)
1559 		    if (curwin->w_jumplist[idx].time_set < timestamp)
1560 		    {
1561 			++idx;
1562 			break;
1563 		    }
1564 		/* idx cannot be zero now */
1565 		if (idx < 0 && curwin->w_jumplistlen < JUMPLISTSIZE)
1566 		    /* insert as the oldest entry */
1567 		    idx = 0;
1568 	    }
1569 	    else if (curwin->w_jumplistlen < JUMPLISTSIZE)
1570 		/* insert as oldest entry */
1571 		idx = 0;
1572 	    else
1573 		idx = -1;
1574 
1575 	    if (idx >= 0)
1576 	    {
1577 		if (curwin->w_jumplistlen == JUMPLISTSIZE)
1578 		{
1579 		    /* Drop the oldest entry. */
1580 		    --idx;
1581 		    vim_free(curwin->w_jumplist[0].fname);
1582 		    for (i = 0; i < idx; ++i)
1583 			curwin->w_jumplist[i] = curwin->w_jumplist[i + 1];
1584 		}
1585 		else
1586 		{
1587 		    /* Move newer entries forward. */
1588 		    for (i = curwin->w_jumplistlen; i > idx; --i)
1589 			curwin->w_jumplist[i] = curwin->w_jumplist[i - 1];
1590 		    ++curwin->w_jumplistidx;
1591 		    ++curwin->w_jumplistlen;
1592 		}
1593 		fm = &curwin->w_jumplist[idx];
1594 		fm->fmark.mark.lnum = 0;
1595 		fm->fname = NULL;
1596 		fm->time_set = 0;
1597 	    }
1598 	}
1599 #endif
1600     }
1601     else
1602     {
1603 	int idx;
1604 
1605 	if (VIM_ISDIGIT(name))
1606 	{
1607 	    if (vi_namedfm != NULL)
1608 		idx = name - '0' + NMARKS;
1609 	    else
1610 	    {
1611 		int i;
1612 
1613 		/* Do not use the name from the viminfo file, insert in time
1614 		 * order. */
1615 		for (idx = NMARKS; idx < NMARKS + EXTRA_MARKS; ++idx)
1616 		    if (namedfm[idx].time_set < timestamp)
1617 			break;
1618 		if (idx == NMARKS + EXTRA_MARKS)
1619 		    /* All existing entries are newer. */
1620 		    return;
1621 		i = NMARKS + EXTRA_MARKS - 1;
1622 
1623 		vim_free(namedfm[i].fname);
1624 		for ( ; i > idx; --i)
1625 		    namedfm[i] = namedfm[i - 1];
1626 		namedfm[idx].fname = NULL;
1627 	    }
1628 	}
1629 	else
1630 	    idx = name - 'A';
1631 	if (vi_namedfm != NULL)
1632 	    fm = &vi_namedfm[idx];
1633 	else
1634 	    fm = &namedfm[idx];
1635     }
1636 
1637     if (fm != NULL)
1638     {
1639 	if (vi_namedfm != NULL || fm->fmark.mark.lnum == 0
1640 					  || fm->time_set < timestamp || force)
1641 	{
1642 	    fm->fmark.mark.lnum = lnum;
1643 	    fm->fmark.mark.col = col;
1644 #ifdef FEAT_VIRTUALEDIT
1645 	    fm->fmark.mark.coladd = 0;
1646 #endif
1647 	    fm->fmark.fnum = 0;
1648 	    vim_free(fm->fname);
1649 	    if (vp[4].bv_allocated)
1650 	    {
1651 		fm->fname = vp[4].bv_string;
1652 		vp[4].bv_string = NULL;
1653 	    }
1654 	    else
1655 		fm->fname = vim_strsave(vp[4].bv_string);
1656 	    fm->time_set = timestamp;
1657 	}
1658     }
1659 }
1660 
1661 /*
1662  * Return TRUE if marks for "buf" should not be written.
1663  */
1664     static int
1665 skip_for_viminfo(buf_T *buf)
1666 {
1667     return
1668 #ifdef FEAT_TERMINAL
1669 	    bt_terminal(buf) ||
1670 #endif
1671 	    removable(buf->b_ffname);
1672 }
1673 
1674     void
1675 write_viminfo_filemarks(FILE *fp)
1676 {
1677     int		i;
1678     char_u	*name;
1679     buf_T	*buf;
1680     xfmark_T	*fm;
1681     int		vi_idx;
1682     int		idx;
1683 
1684     if (get_viminfo_parameter('f') == 0)
1685 	return;
1686 
1687     fputs(_("\n# File marks:\n"), fp);
1688 
1689     /* Write the filemarks 'A - 'Z */
1690     for (i = 0; i < NMARKS; i++)
1691     {
1692 	if (vi_namedfm != NULL && (vi_namedfm[i].time_set > namedfm[i].time_set
1693 					  || namedfm[i].fmark.mark.lnum == 0))
1694 	    fm = &vi_namedfm[i];
1695 	else
1696 	    fm = &namedfm[i];
1697 	write_one_filemark(fp, fm, '\'', i + 'A');
1698     }
1699 
1700     /*
1701      * Find a mark that is the same file and position as the cursor.
1702      * That one, or else the last one is deleted.
1703      * Move '0 to '1, '1 to '2, etc. until the matching one or '9
1704      * Set the '0 mark to current cursor position.
1705      */
1706     if (curbuf->b_ffname != NULL && !skip_for_viminfo(curbuf))
1707     {
1708 	name = buflist_nr2name(curbuf->b_fnum, TRUE, FALSE);
1709 	for (i = NMARKS; i < NMARKS + EXTRA_MARKS - 1; ++i)
1710 	    if (namedfm[i].fmark.mark.lnum == curwin->w_cursor.lnum
1711 		    && (namedfm[i].fname == NULL
1712 			    ? namedfm[i].fmark.fnum == curbuf->b_fnum
1713 			    : (name != NULL
1714 				    && STRCMP(name, namedfm[i].fname) == 0)))
1715 		break;
1716 	vim_free(name);
1717 
1718 	vim_free(namedfm[i].fname);
1719 	for ( ; i > NMARKS; --i)
1720 	    namedfm[i] = namedfm[i - 1];
1721 	namedfm[NMARKS].fmark.mark = curwin->w_cursor;
1722 	namedfm[NMARKS].fmark.fnum = curbuf->b_fnum;
1723 	namedfm[NMARKS].fname = NULL;
1724 	namedfm[NMARKS].time_set = vim_time();
1725     }
1726 
1727     /* Write the filemarks '0 - '9.  Newest (highest timestamp) first. */
1728     vi_idx = NMARKS;
1729     idx = NMARKS;
1730     for (i = NMARKS; i < NMARKS + EXTRA_MARKS; i++)
1731     {
1732 	xfmark_T *vi_fm = vi_namedfm != NULL ? &vi_namedfm[vi_idx] : NULL;
1733 
1734 	if (vi_fm != NULL
1735 		&& vi_fm->fmark.mark.lnum != 0
1736 		&& (vi_fm->time_set > namedfm[idx].time_set
1737 		    || namedfm[idx].fmark.mark.lnum == 0))
1738 	{
1739 	    fm = vi_fm;
1740 	    ++vi_idx;
1741 	}
1742 	else
1743 	{
1744 	    fm = &namedfm[idx++];
1745 	    if (vi_fm != NULL
1746 		  && vi_fm->fmark.mark.lnum == fm->fmark.mark.lnum
1747 		  && vi_fm->time_set == fm->time_set
1748 		  && ((vi_fm->fmark.fnum != 0
1749 			  && vi_fm->fmark.fnum == fm->fmark.fnum)
1750 		      || (vi_fm->fname != NULL
1751 			  && fm->fname != NULL
1752 			  && STRCMP(vi_fm->fname, fm->fname) == 0)))
1753 		++vi_idx;  /* skip duplicate */
1754 	}
1755 	write_one_filemark(fp, fm, '\'', i - NMARKS + '0');
1756     }
1757 
1758 #ifdef FEAT_JUMPLIST
1759     /* Write the jumplist with -' */
1760     fputs(_("\n# Jumplist (newest first):\n"), fp);
1761     setpcmark();	/* add current cursor position */
1762     cleanup_jumplist(curwin, FALSE);
1763     vi_idx = 0;
1764     idx = curwin->w_jumplistlen - 1;
1765     for (i = 0; i < JUMPLISTSIZE; ++i)
1766     {
1767 	xfmark_T	*vi_fm;
1768 
1769 	fm = idx >= 0 ? &curwin->w_jumplist[idx] : NULL;
1770 	vi_fm = vi_idx < vi_jumplist_len ? &vi_jumplist[vi_idx] : NULL;
1771 	if (fm == NULL && vi_fm == NULL)
1772 	    break;
1773 	if (fm == NULL || (vi_fm != NULL && fm->time_set < vi_fm->time_set))
1774 	{
1775 	    fm = vi_fm;
1776 	    ++vi_idx;
1777 	}
1778 	else
1779 	    --idx;
1780 	if (fm->fmark.fnum == 0
1781 		|| ((buf = buflist_findnr(fm->fmark.fnum)) != NULL
1782 		    && !skip_for_viminfo(buf)))
1783 	    write_one_filemark(fp, fm, '-', '\'');
1784     }
1785 #endif
1786 }
1787 
1788     static void
1789 write_one_filemark(
1790     FILE	*fp,
1791     xfmark_T	*fm,
1792     int		c1,
1793     int		c2)
1794 {
1795     char_u	*name;
1796 
1797     if (fm->fmark.mark.lnum == 0)	/* not set */
1798 	return;
1799 
1800     if (fm->fmark.fnum != 0)		/* there is a buffer */
1801 	name = buflist_nr2name(fm->fmark.fnum, TRUE, FALSE);
1802     else
1803 	name = fm->fname;		/* use name from .viminfo */
1804     if (name != NULL && *name != NUL)
1805     {
1806 	fprintf(fp, "%c%c  %ld  %ld  ", c1, c2, (long)fm->fmark.mark.lnum,
1807 						    (long)fm->fmark.mark.col);
1808 	viminfo_writestring(fp, name);
1809 
1810 	/* Barline: |{bartype},{name},{lnum},{col},{timestamp},{filename}
1811 	 * size up to filename: 8 + 3 * 20 */
1812 	fprintf(fp, "|%d,%d,%ld,%ld,%ld,", BARTYPE_MARK, c2,
1813 		(long)fm->fmark.mark.lnum, (long)fm->fmark.mark.col,
1814 		(long)fm->time_set);
1815 	barline_writestring(fp, name, LSIZE - 70);
1816 	putc('\n', fp);
1817     }
1818 
1819     if (fm->fmark.fnum != 0)
1820 	vim_free(name);
1821 }
1822 
1823 /*
1824  * Return TRUE if "name" is on removable media (depending on 'viminfo').
1825  */
1826     int
1827 removable(char_u *name)
1828 {
1829     char_u  *p;
1830     char_u  part[51];
1831     int	    retval = FALSE;
1832     size_t  n;
1833 
1834     name = home_replace_save(NULL, name);
1835     if (name != NULL)
1836     {
1837 	for (p = p_viminfo; *p; )
1838 	{
1839 	    copy_option_part(&p, part, 51, ", ");
1840 	    if (part[0] == 'r')
1841 	    {
1842 		n = STRLEN(part + 1);
1843 		if (MB_STRNICMP(part + 1, name, n) == 0)
1844 		{
1845 		    retval = TRUE;
1846 		    break;
1847 		}
1848 	    }
1849 	}
1850 	vim_free(name);
1851     }
1852     return retval;
1853 }
1854 
1855     static void
1856 write_one_mark(FILE *fp_out, int c, pos_T *pos)
1857 {
1858     if (pos->lnum != 0)
1859 	fprintf(fp_out, "\t%c\t%ld\t%d\n", c, (long)pos->lnum, (int)pos->col);
1860 }
1861 
1862 
1863     static void
1864 write_buffer_marks(buf_T *buf, FILE *fp_out)
1865 {
1866     int		i;
1867     pos_T	pos;
1868 
1869     home_replace(NULL, buf->b_ffname, IObuff, IOSIZE, TRUE);
1870     fprintf(fp_out, "\n> ");
1871     viminfo_writestring(fp_out, IObuff);
1872 
1873     /* Write the last used timestamp as the lnum of the non-existing mark '*'.
1874      * Older Vims will ignore it and/or copy it. */
1875     pos.lnum = (linenr_T)buf->b_last_used;
1876     pos.col = 0;
1877     write_one_mark(fp_out, '*', &pos);
1878 
1879     write_one_mark(fp_out, '"', &buf->b_last_cursor);
1880     write_one_mark(fp_out, '^', &buf->b_last_insert);
1881     write_one_mark(fp_out, '.', &buf->b_last_change);
1882 #ifdef FEAT_JUMPLIST
1883     /* changelist positions are stored oldest first */
1884     for (i = 0; i < buf->b_changelistlen; ++i)
1885     {
1886 	/* skip duplicates */
1887 	if (i == 0 || !EQUAL_POS(buf->b_changelist[i - 1],
1888 							 buf->b_changelist[i]))
1889 	    write_one_mark(fp_out, '+', &buf->b_changelist[i]);
1890     }
1891 #endif
1892     for (i = 0; i < NMARKS; i++)
1893 	write_one_mark(fp_out, 'a' + i, &buf->b_namedm[i]);
1894 }
1895 
1896 /*
1897  * Write all the named marks for all buffers.
1898  * When "buflist" is not NULL fill it with the buffers for which marks are to
1899  * be written.
1900  */
1901     void
1902 write_viminfo_marks(FILE *fp_out, garray_T *buflist)
1903 {
1904     buf_T	*buf;
1905     int		is_mark_set;
1906     int		i;
1907     win_T	*win;
1908     tabpage_T	*tp;
1909 
1910     /*
1911      * Set b_last_cursor for the all buffers that have a window.
1912      */
1913     FOR_ALL_TAB_WINDOWS(tp, win)
1914 	set_last_cursor(win);
1915 
1916     fputs(_("\n# History of marks within files (newest to oldest):\n"), fp_out);
1917     FOR_ALL_BUFFERS(buf)
1918     {
1919 	/*
1920 	 * Only write something if buffer has been loaded and at least one
1921 	 * mark is set.
1922 	 */
1923 	if (buf->b_marks_read)
1924 	{
1925 	    if (buf->b_last_cursor.lnum != 0)
1926 		is_mark_set = TRUE;
1927 	    else
1928 	    {
1929 		is_mark_set = FALSE;
1930 		for (i = 0; i < NMARKS; i++)
1931 		    if (buf->b_namedm[i].lnum != 0)
1932 		    {
1933 			is_mark_set = TRUE;
1934 			break;
1935 		    }
1936 	    }
1937 	    if (is_mark_set && buf->b_ffname != NULL
1938 		      && buf->b_ffname[0] != NUL
1939 		      && !skip_for_viminfo(buf))
1940 	    {
1941 		if (buflist == NULL)
1942 		    write_buffer_marks(buf, fp_out);
1943 		else if (ga_grow(buflist, 1) == OK)
1944 		    ((buf_T **)buflist->ga_data)[buflist->ga_len++] = buf;
1945 	    }
1946 	}
1947     }
1948 }
1949 
1950 /*
1951  * Compare functions for qsort() below, that compares b_last_used.
1952  */
1953     static int
1954 #ifdef __BORLANDC__
1955 _RTLENTRYF
1956 #endif
1957 buf_compare(const void *s1, const void *s2)
1958 {
1959     buf_T *buf1 = *(buf_T **)s1;
1960     buf_T *buf2 = *(buf_T **)s2;
1961 
1962     if (buf1->b_last_used == buf2->b_last_used)
1963 	return 0;
1964     return buf1->b_last_used > buf2->b_last_used ? -1 : 1;
1965 }
1966 
1967 /*
1968  * Handle marks in the viminfo file:
1969  * fp_out != NULL: copy marks, in time order with buffers in "buflist".
1970  * fp_out == NULL && (flags & VIF_WANT_MARKS): read marks for curbuf only
1971  * fp_out == NULL && (flags & VIF_GET_OLDFILES | VIF_FORCEIT): fill v:oldfiles
1972  */
1973     void
1974 copy_viminfo_marks(
1975     vir_T	*virp,
1976     FILE	*fp_out,
1977     garray_T	*buflist,
1978     int		eof,
1979     int		flags)
1980 {
1981     char_u	*line = virp->vir_line;
1982     buf_T	*buf;
1983     int		num_marked_files;
1984     int		load_marks;
1985     int		copy_marks_out;
1986     char_u	*str;
1987     int		i;
1988     char_u	*p;
1989     char_u	*name_buf;
1990     pos_T	pos;
1991 #ifdef FEAT_EVAL
1992     list_T	*list = NULL;
1993 #endif
1994     int		count = 0;
1995     int		buflist_used = 0;
1996     buf_T	*buflist_buf = NULL;
1997 
1998     if ((name_buf = alloc(LSIZE)) == NULL)
1999 	return;
2000     *name_buf = NUL;
2001 
2002     if (fp_out != NULL && buflist->ga_len > 0)
2003     {
2004 	/* Sort the list of buffers on b_last_used. */
2005 	qsort(buflist->ga_data, (size_t)buflist->ga_len,
2006 						sizeof(buf_T *), buf_compare);
2007 	buflist_buf = ((buf_T **)buflist->ga_data)[0];
2008     }
2009 
2010 #ifdef FEAT_EVAL
2011     if (fp_out == NULL && (flags & (VIF_GET_OLDFILES | VIF_FORCEIT)))
2012     {
2013 	list = list_alloc();
2014 	if (list != NULL)
2015 	    set_vim_var_list(VV_OLDFILES, list);
2016     }
2017 #endif
2018 
2019     num_marked_files = get_viminfo_parameter('\'');
2020     while (!eof && (count < num_marked_files || fp_out == NULL))
2021     {
2022 	if (line[0] != '>')
2023 	{
2024 	    if (line[0] != '\n' && line[0] != '\r' && line[0] != '#')
2025 	    {
2026 		if (viminfo_error("E576: ", _("Missing '>'"), line))
2027 		    break;	/* too many errors, return now */
2028 	    }
2029 	    eof = vim_fgets(line, LSIZE, virp->vir_fd);
2030 	    continue;		/* Skip this dud line */
2031 	}
2032 
2033 	/*
2034 	 * Handle long line and translate escaped characters.
2035 	 * Find file name, set str to start.
2036 	 * Ignore leading and trailing white space.
2037 	 */
2038 	str = skipwhite(line + 1);
2039 	str = viminfo_readstring(virp, (int)(str - virp->vir_line), FALSE);
2040 	if (str == NULL)
2041 	    continue;
2042 	p = str + STRLEN(str);
2043 	while (p != str && (*p == NUL || vim_isspace(*p)))
2044 	    p--;
2045 	if (*p)
2046 	    p++;
2047 	*p = NUL;
2048 
2049 #ifdef FEAT_EVAL
2050 	if (list != NULL)
2051 	    list_append_string(list, str, -1);
2052 #endif
2053 
2054 	/*
2055 	 * If fp_out == NULL, load marks for current buffer.
2056 	 * If fp_out != NULL, copy marks for buffers not in buflist.
2057 	 */
2058 	load_marks = copy_marks_out = FALSE;
2059 	if (fp_out == NULL)
2060 	{
2061 	    if ((flags & VIF_WANT_MARKS) && curbuf->b_ffname != NULL)
2062 	    {
2063 		if (*name_buf == NUL)	    /* only need to do this once */
2064 		    home_replace(NULL, curbuf->b_ffname, name_buf, LSIZE, TRUE);
2065 		if (fnamecmp(str, name_buf) == 0)
2066 		    load_marks = TRUE;
2067 	    }
2068 	}
2069 	else /* fp_out != NULL */
2070 	{
2071 	    /* This is slow if there are many buffers!! */
2072 	    FOR_ALL_BUFFERS(buf)
2073 		if (buf->b_ffname != NULL)
2074 		{
2075 		    home_replace(NULL, buf->b_ffname, name_buf, LSIZE, TRUE);
2076 		    if (fnamecmp(str, name_buf) == 0)
2077 			break;
2078 		}
2079 
2080 	    /*
2081 	     * Copy marks if the buffer has not been loaded.
2082 	     */
2083 	    if (buf == NULL || !buf->b_marks_read)
2084 	    {
2085 		int	did_read_line = FALSE;
2086 
2087 		if (buflist_buf != NULL)
2088 		{
2089 		    /* Read the next line.  If it has the "*" mark compare the
2090 		     * time stamps.  Write entries from "buflist" that are
2091 		     * newer. */
2092 		    if (!(eof = viminfo_readline(virp)) && line[0] == TAB)
2093 		    {
2094 			did_read_line = TRUE;
2095 			if (line[1] == '*')
2096 			{
2097 			    long	ltime;
2098 
2099 			    sscanf((char *)line + 2, "%ld ", &ltime);
2100 			    while ((time_T)ltime < buflist_buf->b_last_used)
2101 			    {
2102 				write_buffer_marks(buflist_buf, fp_out);
2103 				if (++count >= num_marked_files)
2104 				    break;
2105 				if (++buflist_used == buflist->ga_len)
2106 				{
2107 				    buflist_buf = NULL;
2108 				    break;
2109 				}
2110 				buflist_buf =
2111 				   ((buf_T **)buflist->ga_data)[buflist_used];
2112 			    }
2113 			}
2114 			else
2115 			{
2116 			    /* No timestamp, must be written by an older Vim.
2117 			     * Assume all remaining buffers are older then
2118 			     * ours.  */
2119 			    while (count < num_marked_files
2120 					    && buflist_used < buflist->ga_len)
2121 			    {
2122 				buflist_buf = ((buf_T **)buflist->ga_data)
2123 							     [buflist_used++];
2124 				write_buffer_marks(buflist_buf, fp_out);
2125 				++count;
2126 			    }
2127 			    buflist_buf = NULL;
2128 			}
2129 
2130 			if (count >= num_marked_files)
2131 			{
2132 			    vim_free(str);
2133 			    break;
2134 			}
2135 		    }
2136 		}
2137 
2138 		fputs("\n> ", fp_out);
2139 		viminfo_writestring(fp_out, str);
2140 		if (did_read_line)
2141 		    fputs((char *)line, fp_out);
2142 
2143 		count++;
2144 		copy_marks_out = TRUE;
2145 	    }
2146 	}
2147 	vim_free(str);
2148 
2149 #ifdef FEAT_VIRTUALEDIT
2150 	pos.coladd = 0;
2151 #endif
2152 	while (!(eof = viminfo_readline(virp)) && line[0] == TAB)
2153 	{
2154 	    if (load_marks)
2155 	    {
2156 		if (line[1] != NUL)
2157 		{
2158 		    unsigned u;
2159 
2160 		    sscanf((char *)line + 2, "%ld %u", &pos.lnum, &u);
2161 		    pos.col = u;
2162 		    switch (line[1])
2163 		    {
2164 			case '"': curbuf->b_last_cursor = pos; break;
2165 			case '^': curbuf->b_last_insert = pos; break;
2166 			case '.': curbuf->b_last_change = pos; break;
2167 			case '+':
2168 #ifdef FEAT_JUMPLIST
2169 				  /* changelist positions are stored oldest
2170 				   * first */
2171 				  if (curbuf->b_changelistlen == JUMPLISTSIZE)
2172 				      /* list is full, remove oldest entry */
2173 				      mch_memmove(curbuf->b_changelist,
2174 					    curbuf->b_changelist + 1,
2175 					    sizeof(pos_T) * (JUMPLISTSIZE - 1));
2176 				  else
2177 				      ++curbuf->b_changelistlen;
2178 				  curbuf->b_changelist[
2179 					   curbuf->b_changelistlen - 1] = pos;
2180 #endif
2181 				  break;
2182 
2183 				  /* Using the line number for the last-used
2184 				   * timestamp. */
2185 			case '*': curbuf->b_last_used = pos.lnum; break;
2186 
2187 			default:  if ((i = line[1] - 'a') >= 0 && i < NMARKS)
2188 				      curbuf->b_namedm[i] = pos;
2189 		    }
2190 		}
2191 	    }
2192 	    else if (copy_marks_out)
2193 		fputs((char *)line, fp_out);
2194 	}
2195 
2196 	if (load_marks)
2197 	{
2198 #ifdef FEAT_JUMPLIST
2199 	    win_T	*wp;
2200 
2201 	    FOR_ALL_WINDOWS(wp)
2202 	    {
2203 		if (wp->w_buffer == curbuf)
2204 		    wp->w_changelistidx = curbuf->b_changelistlen;
2205 	    }
2206 #endif
2207 	    break;
2208 	}
2209     }
2210 
2211     if (fp_out != NULL)
2212 	/* Write any remaining entries from buflist. */
2213 	while (count < num_marked_files && buflist_used < buflist->ga_len)
2214 	{
2215 	    buflist_buf = ((buf_T **)buflist->ga_data)[buflist_used++];
2216 	    write_buffer_marks(buflist_buf, fp_out);
2217 	    ++count;
2218 	}
2219 
2220     vim_free(name_buf);
2221 }
2222 #endif /* FEAT_VIMINFO */
2223