xref: /vim-8.2.3635/src/mark.c (revision 5be4ceec)
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 static xfmark_T namedfm[NMARKS + EXTRA_MARKS];		/* marks with file nr */
28 
29 static void fname2fnum(xfmark_T *fm);
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 static void mark_adjust_internal(linenr_T line1, linenr_T line2, long amount,
34     long amount_after, int adjust_folds);
35 
36 /*
37  * Set named mark "c" at current cursor position.
38  * Returns OK on success, FAIL if bad name given.
39  */
40     int
41 setmark(int c)
42 {
43     return setmark_pos(c, &curwin->w_cursor, curbuf->b_fnum);
44 }
45 
46 /*
47  * Set named mark "c" to position "pos".
48  * When "c" is upper case use file "fnum".
49  * Returns OK on success, FAIL if bad name given.
50  */
51     int
52 setmark_pos(int c, pos_T *pos, int fnum)
53 {
54     int		i;
55     buf_T	*buf;
56 
57     /* Check for a special key (may cause islower() to crash). */
58     if (c < 0)
59 	return FAIL;
60 
61     if (c == '\'' || c == '`')
62     {
63 	if (pos == &curwin->w_cursor)
64 	{
65 	    setpcmark();
66 	    /* keep it even when the cursor doesn't move */
67 	    curwin->w_prev_pcmark = curwin->w_pcmark;
68 	}
69 	else
70 	    curwin->w_pcmark = *pos;
71 	return OK;
72     }
73 
74     buf = buflist_findnr(fnum);
75     if (buf == NULL)
76 	return FAIL;
77 
78     if (c == '"')
79     {
80 	buf->b_last_cursor = *pos;
81 	return OK;
82     }
83 
84     /* Allow setting '[ and '] for an autocommand that simulates reading a
85      * file. */
86     if (c == '[')
87     {
88 	buf->b_op_start = *pos;
89 	return OK;
90     }
91     if (c == ']')
92     {
93 	buf->b_op_end = *pos;
94 	return OK;
95     }
96 
97     if (c == '<' || c == '>')
98     {
99 	if (c == '<')
100 	    buf->b_visual.vi_start = *pos;
101 	else
102 	    buf->b_visual.vi_end = *pos;
103 	if (buf->b_visual.vi_mode == NUL)
104 	    /* Visual_mode has not yet been set, use a sane default. */
105 	    buf->b_visual.vi_mode = 'v';
106 	return OK;
107     }
108 
109     if (ASCII_ISLOWER(c))
110     {
111 	i = c - 'a';
112 	buf->b_namedm[i] = *pos;
113 	return OK;
114     }
115     if (ASCII_ISUPPER(c) || VIM_ISDIGIT(c))
116     {
117 	if (VIM_ISDIGIT(c))
118 	    i = c - '0' + NMARKS;
119 	else
120 	    i = c - 'A';
121 	namedfm[i].fmark.mark = *pos;
122 	namedfm[i].fmark.fnum = fnum;
123 	VIM_CLEAR(namedfm[i].fname);
124 #ifdef FEAT_VIMINFO
125 	namedfm[i].time_set = vim_time();
126 #endif
127 	return OK;
128     }
129     return FAIL;
130 }
131 
132 /*
133  * Set the previous context mark to the current position and add it to the
134  * jump list.
135  */
136     void
137 setpcmark(void)
138 {
139 #ifdef FEAT_JUMPLIST
140     int		i;
141     xfmark_T	*fm;
142 #endif
143 #ifdef JUMPLIST_ROTATE
144     xfmark_T	tempmark;
145 #endif
146 
147     /* for :global the mark is set only once */
148     if (global_busy || listcmd_busy || cmdmod.keepjumps)
149 	return;
150 
151     curwin->w_prev_pcmark = curwin->w_pcmark;
152     curwin->w_pcmark = curwin->w_cursor;
153 
154 #ifdef FEAT_JUMPLIST
155 # ifdef JUMPLIST_ROTATE
156     /*
157      * If last used entry is not at the top, put it at the top by rotating
158      * the stack until it is (the newer entries will be at the bottom).
159      * Keep one entry (the last used one) at the top.
160      */
161     if (curwin->w_jumplistidx < curwin->w_jumplistlen)
162 	++curwin->w_jumplistidx;
163     while (curwin->w_jumplistidx < curwin->w_jumplistlen)
164     {
165 	tempmark = curwin->w_jumplist[curwin->w_jumplistlen - 1];
166 	for (i = curwin->w_jumplistlen - 1; i > 0; --i)
167 	    curwin->w_jumplist[i] = curwin->w_jumplist[i - 1];
168 	curwin->w_jumplist[0] = tempmark;
169 	++curwin->w_jumplistidx;
170     }
171 # endif
172 
173     /* If jumplist is full: remove oldest entry */
174     if (++curwin->w_jumplistlen > JUMPLISTSIZE)
175     {
176 	curwin->w_jumplistlen = JUMPLISTSIZE;
177 	vim_free(curwin->w_jumplist[0].fname);
178 	for (i = 1; i < JUMPLISTSIZE; ++i)
179 	    curwin->w_jumplist[i - 1] = curwin->w_jumplist[i];
180     }
181     curwin->w_jumplistidx = curwin->w_jumplistlen;
182     fm = &curwin->w_jumplist[curwin->w_jumplistlen - 1];
183 
184     fm->fmark.mark = curwin->w_pcmark;
185     fm->fmark.fnum = curbuf->b_fnum;
186     fm->fname = NULL;
187 # ifdef FEAT_VIMINFO
188     fm->time_set = vim_time();
189 # endif
190 #endif
191 }
192 
193 /*
194  * To change context, call setpcmark(), then move the current position to
195  * where ever, then call checkpcmark().  This ensures that the previous
196  * context will only be changed if the cursor moved to a different line.
197  * If pcmark was deleted (with "dG") the previous mark is restored.
198  */
199     void
200 checkpcmark(void)
201 {
202     if (curwin->w_prev_pcmark.lnum != 0
203 	    && (EQUAL_POS(curwin->w_pcmark, curwin->w_cursor)
204 		|| curwin->w_pcmark.lnum == 0))
205     {
206 	curwin->w_pcmark = curwin->w_prev_pcmark;
207 	curwin->w_prev_pcmark.lnum = 0;		/* Show it has been checked */
208     }
209 }
210 
211 #if defined(FEAT_JUMPLIST) || defined(PROTO)
212 /*
213  * move "count" positions in the jump list (count may be negative)
214  */
215     pos_T *
216 movemark(int count)
217 {
218     pos_T	*pos;
219     xfmark_T	*jmp;
220 
221     cleanup_jumplist(curwin, TRUE);
222 
223     if (curwin->w_jumplistlen == 0)	    /* nothing to jump to */
224 	return (pos_T *)NULL;
225 
226     for (;;)
227     {
228 	if (curwin->w_jumplistidx + count < 0
229 		|| curwin->w_jumplistidx + count >= curwin->w_jumplistlen)
230 	    return (pos_T *)NULL;
231 
232 	/*
233 	 * if first CTRL-O or CTRL-I command after a jump, add cursor position
234 	 * to list.  Careful: If there are duplicates (CTRL-O immediately after
235 	 * starting Vim on a file), another entry may have been removed.
236 	 */
237 	if (curwin->w_jumplistidx == curwin->w_jumplistlen)
238 	{
239 	    setpcmark();
240 	    --curwin->w_jumplistidx;	/* skip the new entry */
241 	    if (curwin->w_jumplistidx + count < 0)
242 		return (pos_T *)NULL;
243 	}
244 
245 	curwin->w_jumplistidx += count;
246 
247 	jmp = curwin->w_jumplist + curwin->w_jumplistidx;
248 	if (jmp->fmark.fnum == 0)
249 	    fname2fnum(jmp);
250 	if (jmp->fmark.fnum != curbuf->b_fnum)
251 	{
252 	    /* jump to other file */
253 	    if (buflist_findnr(jmp->fmark.fnum) == NULL)
254 	    {					     /* Skip this one .. */
255 		count += count < 0 ? -1 : 1;
256 		continue;
257 	    }
258 	    if (buflist_getfile(jmp->fmark.fnum, jmp->fmark.mark.lnum,
259 							    0, FALSE) == FAIL)
260 		return (pos_T *)NULL;
261 	    /* Set lnum again, autocommands my have changed it */
262 	    curwin->w_cursor = jmp->fmark.mark;
263 	    pos = (pos_T *)-1;
264 	}
265 	else
266 	    pos = &(jmp->fmark.mark);
267 	return pos;
268     }
269 }
270 
271 /*
272  * Move "count" positions in the changelist (count may be negative).
273  */
274     pos_T *
275 movechangelist(int count)
276 {
277     int		n;
278 
279     if (curbuf->b_changelistlen == 0)	    /* nothing to jump to */
280 	return (pos_T *)NULL;
281 
282     n = curwin->w_changelistidx;
283     if (n + count < 0)
284     {
285 	if (n == 0)
286 	    return (pos_T *)NULL;
287 	n = 0;
288     }
289     else if (n + count >= curbuf->b_changelistlen)
290     {
291 	if (n == curbuf->b_changelistlen - 1)
292 	    return (pos_T *)NULL;
293 	n = curbuf->b_changelistlen - 1;
294     }
295     else
296 	n += count;
297     curwin->w_changelistidx = n;
298     return curbuf->b_changelist + n;
299 }
300 #endif
301 
302 /*
303  * Find mark "c" in buffer pointed to by "buf".
304  * If "changefile" is TRUE it's allowed to edit another file for '0, 'A, etc.
305  * If "fnum" is not NULL store the fnum there for '0, 'A etc., don't edit
306  * another file.
307  * Returns:
308  * - pointer to pos_T if found.  lnum is 0 when mark not set, -1 when mark is
309  *   in another file which can't be gotten. (caller needs to check lnum!)
310  * - NULL if there is no mark called 'c'.
311  * - -1 if mark is in other file and jumped there (only if changefile is TRUE)
312  */
313     pos_T *
314 getmark_buf(buf_T *buf, int c, int changefile)
315 {
316     return getmark_buf_fnum(buf, c, changefile, NULL);
317 }
318 
319     pos_T *
320 getmark(int c, int changefile)
321 {
322     return getmark_buf_fnum(curbuf, c, changefile, NULL);
323 }
324 
325     pos_T *
326 getmark_buf_fnum(
327     buf_T	*buf,
328     int		c,
329     int		changefile,
330     int		*fnum)
331 {
332     pos_T		*posp;
333     pos_T		*startp, *endp;
334     static pos_T	pos_copy;
335 
336     posp = NULL;
337 
338     /* Check for special key, can't be a mark name and might cause islower()
339      * to crash. */
340     if (c < 0)
341 	return posp;
342 #ifndef EBCDIC
343     if (c > '~')			/* check for islower()/isupper() */
344 	;
345     else
346 #endif
347 	if (c == '\'' || c == '`')	/* previous context mark */
348     {
349 	pos_copy = curwin->w_pcmark;	/* need to make a copy because */
350 	posp = &pos_copy;		/*   w_pcmark may be changed soon */
351     }
352     else if (c == '"')			/* to pos when leaving buffer */
353 	posp = &(buf->b_last_cursor);
354     else if (c == '^')			/* to where Insert mode stopped */
355 	posp = &(buf->b_last_insert);
356     else if (c == '.')			/* to where last change was made */
357 	posp = &(buf->b_last_change);
358     else if (c == '[')			/* to start of previous operator */
359 	posp = &(buf->b_op_start);
360     else if (c == ']')			/* to end of previous operator */
361 	posp = &(buf->b_op_end);
362     else if (c == '{' || c == '}')	/* to previous/next paragraph */
363     {
364 	pos_T	pos;
365 	oparg_T	oa;
366 	int	slcb = listcmd_busy;
367 
368 	pos = curwin->w_cursor;
369 	listcmd_busy = TRUE;	    /* avoid that '' is changed */
370 	if (findpar(&oa.inclusive,
371 			       c == '}' ? FORWARD : BACKWARD, 1L, NUL, FALSE))
372 	{
373 	    pos_copy = curwin->w_cursor;
374 	    posp = &pos_copy;
375 	}
376 	curwin->w_cursor = pos;
377 	listcmd_busy = slcb;
378     }
379     else if (c == '(' || c == ')')	/* to previous/next sentence */
380     {
381 	pos_T	pos;
382 	int	slcb = listcmd_busy;
383 
384 	pos = curwin->w_cursor;
385 	listcmd_busy = TRUE;	    /* avoid that '' is changed */
386 	if (findsent(c == ')' ? FORWARD : BACKWARD, 1L))
387 	{
388 	    pos_copy = curwin->w_cursor;
389 	    posp = &pos_copy;
390 	}
391 	curwin->w_cursor = pos;
392 	listcmd_busy = slcb;
393     }
394     else if (c == '<' || c == '>')	/* start/end of visual area */
395     {
396 	startp = &buf->b_visual.vi_start;
397 	endp = &buf->b_visual.vi_end;
398 	if (((c == '<') == LT_POS(*startp, *endp) || endp->lnum == 0)
399 							  && startp->lnum != 0)
400 	    posp = startp;
401 	else
402 	    posp = endp;
403 	/*
404 	 * For Visual line mode, set mark at begin or end of line
405 	 */
406 	if (buf->b_visual.vi_mode == 'V')
407 	{
408 	    pos_copy = *posp;
409 	    posp = &pos_copy;
410 	    if (c == '<')
411 		pos_copy.col = 0;
412 	    else
413 		pos_copy.col = MAXCOL;
414 	    pos_copy.coladd = 0;
415 	}
416     }
417     else if (ASCII_ISLOWER(c))		/* normal named mark */
418     {
419 	posp = &(buf->b_namedm[c - 'a']);
420     }
421     else if (ASCII_ISUPPER(c) || VIM_ISDIGIT(c))	/* named file mark */
422     {
423 	if (VIM_ISDIGIT(c))
424 	    c = c - '0' + NMARKS;
425 	else
426 	    c -= 'A';
427 	posp = &(namedfm[c].fmark.mark);
428 
429 	if (namedfm[c].fmark.fnum == 0)
430 	    fname2fnum(&namedfm[c]);
431 
432 	if (fnum != NULL)
433 	    *fnum = namedfm[c].fmark.fnum;
434 	else if (namedfm[c].fmark.fnum != buf->b_fnum)
435 	{
436 	    /* mark is in another file */
437 	    posp = &pos_copy;
438 
439 	    if (namedfm[c].fmark.mark.lnum != 0
440 				       && changefile && namedfm[c].fmark.fnum)
441 	    {
442 		if (buflist_getfile(namedfm[c].fmark.fnum,
443 				      (linenr_T)1, GETF_SETMARK, FALSE) == OK)
444 		{
445 		    /* Set the lnum now, autocommands could have changed it */
446 		    curwin->w_cursor = namedfm[c].fmark.mark;
447 		    return (pos_T *)-1;
448 		}
449 		pos_copy.lnum = -1;	/* can't get file */
450 	    }
451 	    else
452 		pos_copy.lnum = 0;	/* mark exists, but is not valid in
453 					   current buffer */
454 	}
455     }
456 
457     return posp;
458 }
459 
460 /*
461  * Search for the next named mark in the current file.
462  *
463  * Returns pointer to pos_T of the next mark or NULL if no mark is found.
464  */
465     pos_T *
466 getnextmark(
467     pos_T	*startpos,	/* where to start */
468     int		dir,	/* direction for search */
469     int		begin_line)
470 {
471     int		i;
472     pos_T	*result = NULL;
473     pos_T	pos;
474 
475     pos = *startpos;
476 
477     /* When searching backward and leaving the cursor on the first non-blank,
478      * position must be in a previous line.
479      * When searching forward and leaving the cursor on the first non-blank,
480      * position must be in a next line. */
481     if (dir == BACKWARD && begin_line)
482 	pos.col = 0;
483     else if (dir == FORWARD && begin_line)
484 	pos.col = MAXCOL;
485 
486     for (i = 0; i < NMARKS; i++)
487     {
488 	if (curbuf->b_namedm[i].lnum > 0)
489 	{
490 	    if (dir == FORWARD)
491 	    {
492 		if ((result == NULL || LT_POS(curbuf->b_namedm[i], *result))
493 			&& LT_POS(pos, curbuf->b_namedm[i]))
494 		    result = &curbuf->b_namedm[i];
495 	    }
496 	    else
497 	    {
498 		if ((result == NULL || LT_POS(*result, curbuf->b_namedm[i]))
499 			&& LT_POS(curbuf->b_namedm[i], pos))
500 		    result = &curbuf->b_namedm[i];
501 	    }
502 	}
503     }
504 
505     return result;
506 }
507 
508 /*
509  * For an xtended filemark: set the fnum from the fname.
510  * This is used for marks obtained from the .viminfo file.  It's postponed
511  * until the mark is used to avoid a long startup delay.
512  */
513     static void
514 fname2fnum(xfmark_T *fm)
515 {
516     char_u	*p;
517 
518     if (fm->fname != NULL)
519     {
520 	/*
521 	 * First expand "~/" in the file name to the home directory.
522 	 * Don't expand the whole name, it may contain other '~' chars.
523 	 */
524 	if (fm->fname[0] == '~' && (fm->fname[1] == '/'
525 #ifdef BACKSLASH_IN_FILENAME
526 		    || fm->fname[1] == '\\'
527 #endif
528 		    ))
529 	{
530 	    int len;
531 
532 	    expand_env((char_u *)"~/", NameBuff, MAXPATHL);
533 	    len = (int)STRLEN(NameBuff);
534 	    vim_strncpy(NameBuff + len, fm->fname + 2, MAXPATHL - len - 1);
535 	}
536 	else
537 	    vim_strncpy(NameBuff, fm->fname, MAXPATHL - 1);
538 
539 	/* Try to shorten the file name. */
540 	mch_dirname(IObuff, IOSIZE);
541 	p = shorten_fname(NameBuff, IObuff);
542 
543 	/* buflist_new() will call fmarks_check_names() */
544 	(void)buflist_new(NameBuff, p, (linenr_T)1, 0);
545     }
546 }
547 
548 /*
549  * Check all file marks for a name that matches the file name in buf.
550  * May replace the name with an fnum.
551  * Used for marks that come from the .viminfo file.
552  */
553     void
554 fmarks_check_names(buf_T *buf)
555 {
556     char_u	*name;
557     int		i;
558 #ifdef FEAT_JUMPLIST
559     win_T	*wp;
560 #endif
561 
562     if (buf->b_ffname == NULL)
563 	return;
564 
565     name = home_replace_save(buf, buf->b_ffname);
566     if (name == NULL)
567 	return;
568 
569     for (i = 0; i < NMARKS + EXTRA_MARKS; ++i)
570 	fmarks_check_one(&namedfm[i], name, buf);
571 
572 #ifdef FEAT_JUMPLIST
573     FOR_ALL_WINDOWS(wp)
574     {
575 	for (i = 0; i < wp->w_jumplistlen; ++i)
576 	    fmarks_check_one(&wp->w_jumplist[i], name, buf);
577     }
578 #endif
579 
580     vim_free(name);
581 }
582 
583     static void
584 fmarks_check_one(xfmark_T *fm, char_u *name, buf_T *buf)
585 {
586     if (fm->fmark.fnum == 0
587 	    && fm->fname != NULL
588 	    && fnamecmp(name, fm->fname) == 0)
589     {
590 	fm->fmark.fnum = buf->b_fnum;
591 	VIM_CLEAR(fm->fname);
592     }
593 }
594 
595 /*
596  * Check a if a position from a mark is valid.
597  * Give and error message and return FAIL if not.
598  */
599     int
600 check_mark(pos_T *pos)
601 {
602     if (pos == NULL)
603     {
604 	emsg(_(e_umark));
605 	return FAIL;
606     }
607     if (pos->lnum <= 0)
608     {
609 	/* lnum is negative if mark is in another file can can't get that
610 	 * file, error message already give then. */
611 	if (pos->lnum == 0)
612 	    emsg(_(e_marknotset));
613 	return FAIL;
614     }
615     if (pos->lnum > curbuf->b_ml.ml_line_count)
616     {
617 	emsg(_(e_markinval));
618 	return FAIL;
619     }
620     return OK;
621 }
622 
623 /*
624  * clrallmarks() - clear all marks in the buffer 'buf'
625  *
626  * Used mainly when trashing the entire buffer during ":e" type commands
627  */
628     void
629 clrallmarks(buf_T *buf)
630 {
631     static int		i = -1;
632 
633     if (i == -1)	/* first call ever: initialize */
634 	for (i = 0; i < NMARKS + 1; i++)
635 	{
636 	    namedfm[i].fmark.mark.lnum = 0;
637 	    namedfm[i].fname = NULL;
638 #ifdef FEAT_VIMINFO
639 	    namedfm[i].time_set = 0;
640 #endif
641 	}
642 
643     for (i = 0; i < NMARKS; i++)
644 	buf->b_namedm[i].lnum = 0;
645     buf->b_op_start.lnum = 0;		/* start/end op mark cleared */
646     buf->b_op_end.lnum = 0;
647     buf->b_last_cursor.lnum = 1;	/* '" mark cleared */
648     buf->b_last_cursor.col = 0;
649     buf->b_last_cursor.coladd = 0;
650     buf->b_last_insert.lnum = 0;	/* '^ mark cleared */
651     buf->b_last_change.lnum = 0;	/* '. mark cleared */
652 #ifdef FEAT_JUMPLIST
653     buf->b_changelistlen = 0;
654 #endif
655 }
656 
657 /*
658  * Get name of file from a filemark.
659  * When it's in the current buffer, return the text at the mark.
660  * Returns an allocated string.
661  */
662     char_u *
663 fm_getname(fmark_T *fmark, int lead_len)
664 {
665     if (fmark->fnum == curbuf->b_fnum)		    /* current buffer */
666 	return mark_line(&(fmark->mark), lead_len);
667     return buflist_nr2name(fmark->fnum, FALSE, TRUE);
668 }
669 
670 /*
671  * Return the line at mark "mp".  Truncate to fit in window.
672  * The returned string has been allocated.
673  */
674     static char_u *
675 mark_line(pos_T *mp, int lead_len)
676 {
677     char_u	*s, *p;
678     int		len;
679 
680     if (mp->lnum == 0 || mp->lnum > curbuf->b_ml.ml_line_count)
681 	return vim_strsave((char_u *)"-invalid-");
682     // Allow for up to 5 bytes per character.
683     s = vim_strnsave(skipwhite(ml_get(mp->lnum)), (int)Columns * 5);
684     if (s == NULL)
685 	return NULL;
686     // Truncate the line to fit it in the window.
687     len = 0;
688     for (p = s; *p != NUL; MB_PTR_ADV(p))
689     {
690 	len += ptr2cells(p);
691 	if (len >= Columns - lead_len)
692 	    break;
693     }
694     *p = NUL;
695     return s;
696 }
697 
698 /*
699  * print the marks
700  */
701     void
702 ex_marks(exarg_T *eap)
703 {
704     char_u	*arg = eap->arg;
705     int		i;
706     char_u	*name;
707 
708     if (arg != NULL && *arg == NUL)
709 	arg = NULL;
710 
711     show_one_mark('\'', arg, &curwin->w_pcmark, NULL, TRUE);
712     for (i = 0; i < NMARKS; ++i)
713 	show_one_mark(i + 'a', arg, &curbuf->b_namedm[i], NULL, TRUE);
714     for (i = 0; i < NMARKS + EXTRA_MARKS; ++i)
715     {
716 	if (namedfm[i].fmark.fnum != 0)
717 	    name = fm_getname(&namedfm[i].fmark, 15);
718 	else
719 	    name = namedfm[i].fname;
720 	if (name != NULL)
721 	{
722 	    show_one_mark(i >= NMARKS ? i - NMARKS + '0' : i + 'A',
723 		    arg, &namedfm[i].fmark.mark, name,
724 		    namedfm[i].fmark.fnum == curbuf->b_fnum);
725 	    if (namedfm[i].fmark.fnum != 0)
726 		vim_free(name);
727 	}
728     }
729     show_one_mark('"', arg, &curbuf->b_last_cursor, NULL, TRUE);
730     show_one_mark('[', arg, &curbuf->b_op_start, NULL, TRUE);
731     show_one_mark(']', arg, &curbuf->b_op_end, NULL, TRUE);
732     show_one_mark('^', arg, &curbuf->b_last_insert, NULL, TRUE);
733     show_one_mark('.', arg, &curbuf->b_last_change, NULL, TRUE);
734     show_one_mark('<', arg, &curbuf->b_visual.vi_start, NULL, TRUE);
735     show_one_mark('>', arg, &curbuf->b_visual.vi_end, NULL, TRUE);
736     show_one_mark(-1, arg, NULL, NULL, FALSE);
737 }
738 
739     static void
740 show_one_mark(
741     int		c,
742     char_u	*arg,
743     pos_T	*p,
744     char_u	*name_arg,
745     int		current)	/* in current file */
746 {
747     static int	did_title = FALSE;
748     int		mustfree = FALSE;
749     char_u	*name = name_arg;
750 
751     if (c == -1)			    /* finish up */
752     {
753 	if (did_title)
754 	    did_title = FALSE;
755 	else
756 	{
757 	    if (arg == NULL)
758 		msg(_("No marks set"));
759 	    else
760 		semsg(_("E283: No marks matching \"%s\""), arg);
761 	}
762     }
763     // don't output anything if 'q' typed at --more-- prompt
764     else if (!got_int
765 	    && (arg == NULL || vim_strchr(arg, c) != NULL)
766 	    && p->lnum != 0)
767     {
768 	if (name == NULL && current)
769 	{
770 	    name = mark_line(p, 15);
771 	    mustfree = TRUE;
772 	}
773 	if (!message_filtered(name))
774 	{
775 	    if (!did_title)
776 	    {
777 		// Highlight title
778 		msg_puts_title(_("\nmark line  col file/text"));
779 		did_title = TRUE;
780 	    }
781 	    msg_putchar('\n');
782 	    if (!got_int)
783 	    {
784 		sprintf((char *)IObuff, " %c %6ld %4d ", c, p->lnum, p->col);
785 		msg_outtrans(IObuff);
786 		if (name != NULL)
787 		{
788 		    msg_outtrans_attr(name, current ? HL_ATTR(HLF_D) : 0);
789 		}
790 	    }
791 	    out_flush();		    // show one line at a time
792 	}
793 	if (mustfree)
794 	    vim_free(name);
795     }
796 }
797 
798 /*
799  * ":delmarks[!] [marks]"
800  */
801     void
802 ex_delmarks(exarg_T *eap)
803 {
804     char_u	*p;
805     int		from, to;
806     int		i;
807     int		lower;
808     int		digit;
809     int		n;
810 
811     if (*eap->arg == NUL && eap->forceit)
812 	/* clear all marks */
813 	clrallmarks(curbuf);
814     else if (eap->forceit)
815 	emsg(_(e_invarg));
816     else if (*eap->arg == NUL)
817 	emsg(_(e_argreq));
818     else
819     {
820 	/* clear specified marks only */
821 	for (p = eap->arg; *p != NUL; ++p)
822 	{
823 	    lower = ASCII_ISLOWER(*p);
824 	    digit = VIM_ISDIGIT(*p);
825 	    if (lower || digit || ASCII_ISUPPER(*p))
826 	    {
827 		if (p[1] == '-')
828 		{
829 		    /* clear range of marks */
830 		    from = *p;
831 		    to = p[2];
832 		    if (!(lower ? ASCII_ISLOWER(p[2])
833 				: (digit ? VIM_ISDIGIT(p[2])
834 				    : ASCII_ISUPPER(p[2])))
835 			    || to < from)
836 		    {
837 			semsg(_(e_invarg2), p);
838 			return;
839 		    }
840 		    p += 2;
841 		}
842 		else
843 		    /* clear one lower case mark */
844 		    from = to = *p;
845 
846 		for (i = from; i <= to; ++i)
847 		{
848 		    if (lower)
849 			curbuf->b_namedm[i - 'a'].lnum = 0;
850 		    else
851 		    {
852 			if (digit)
853 			    n = i - '0' + NMARKS;
854 			else
855 			    n = i - 'A';
856 			namedfm[n].fmark.mark.lnum = 0;
857 			VIM_CLEAR(namedfm[n].fname);
858 #ifdef FEAT_VIMINFO
859 			namedfm[n].time_set = 0;
860 #endif
861 		    }
862 		}
863 	    }
864 	    else
865 		switch (*p)
866 		{
867 		    case '"': curbuf->b_last_cursor.lnum = 0; break;
868 		    case '^': curbuf->b_last_insert.lnum = 0; break;
869 		    case '.': curbuf->b_last_change.lnum = 0; break;
870 		    case '[': curbuf->b_op_start.lnum    = 0; break;
871 		    case ']': curbuf->b_op_end.lnum      = 0; break;
872 		    case '<': curbuf->b_visual.vi_start.lnum = 0; break;
873 		    case '>': curbuf->b_visual.vi_end.lnum   = 0; break;
874 		    case ' ': break;
875 		    default:  semsg(_(e_invarg2), p);
876 			      return;
877 		}
878 	}
879     }
880 }
881 
882 #if defined(FEAT_JUMPLIST) || defined(PROTO)
883 /*
884  * print the jumplist
885  */
886     void
887 ex_jumps(exarg_T *eap UNUSED)
888 {
889     int		i;
890     char_u	*name;
891 
892     cleanup_jumplist(curwin, TRUE);
893 
894     /* Highlight title */
895     msg_puts_title(_("\n jump line  col file/text"));
896     for (i = 0; i < curwin->w_jumplistlen && !got_int; ++i)
897     {
898 	if (curwin->w_jumplist[i].fmark.mark.lnum != 0)
899 	{
900 	    name = fm_getname(&curwin->w_jumplist[i].fmark, 16);
901 
902 	    // apply :filter /pat/ or file name not available
903 	    if (name == NULL || message_filtered(name))
904 	    {
905 		vim_free(name);
906 		continue;
907 	    }
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 = {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 /*
1407  * Return a pointer to the named file marks.
1408  */
1409     xfmark_T *
1410 get_namedfm(void)
1411 {
1412     return namedfm;
1413 }
1414