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