160786Sps/*
2240121Sdelphij * Copyright (C) 1984-2012  Mark Nudelman
360786Sps *
460786Sps * You may distribute under the terms of either the GNU General Public
560786Sps * License or the Less License, as specified in the README file.
660786Sps *
7240121Sdelphij * For more information, see the README file.
860786Sps */
960786Sps
1060786Sps
1160786Sps#include "less.h"
1260786Sps
1360786Spsextern IFILE curr_ifile;
1460786Spsextern int sc_height;
1560786Spsextern int jump_sline;
1660786Sps
1760786Sps/*
1860786Sps * A mark is an ifile (input file) plus a position within the file.
1960786Sps */
2060786Spsstruct mark {
2160786Sps	IFILE m_ifile;
2260786Sps	struct scrpos m_scrpos;
2360786Sps};
2460786Sps
2560786Sps/*
2660786Sps * The table of marks.
2760786Sps * Each mark is identified by a lowercase or uppercase letter.
2860786Sps * The final one is lmark, for the "last mark"; addressed by the apostrophe.
2960786Sps */
3060786Sps#define	NMARKS		((2*26)+1)	/* a-z, A-Z, lastmark */
3160786Sps#define	LASTMARK	(NMARKS-1)
3260786Spsstatic struct mark marks[NMARKS];
3360786Sps
3460786Sps/*
3560786Sps * Initialize the mark table to show no marks are set.
3660786Sps */
3760786Sps	public void
3860786Spsinit_mark()
3960786Sps{
4060786Sps	int i;
4160786Sps
4260786Sps	for (i = 0;  i < NMARKS;  i++)
4360786Sps		marks[i].m_scrpos.pos = NULL_POSITION;
4460786Sps}
4560786Sps
4660786Sps/*
4760786Sps * See if a mark letter is valid (between a and z).
4860786Sps */
4960786Sps	static struct mark *
5060786Spsgetumark(c)
5160786Sps	int c;
5260786Sps{
5360786Sps	if (c >= 'a' && c <= 'z')
5460786Sps		return (&marks[c-'a']);
5560786Sps
5660786Sps	if (c >= 'A' && c <= 'Z')
5760786Sps		return (&marks[c-'A'+26]);
5860786Sps
5960786Sps	error("Invalid mark letter", NULL_PARG);
6060786Sps	return (NULL);
6160786Sps}
6260786Sps
6360786Sps/*
6460786Sps * Get the mark structure identified by a character.
6560786Sps * The mark struct may come either from the mark table
6660786Sps * or may be constructed on the fly for certain characters like ^, $.
6760786Sps */
6860786Sps	static struct mark *
6960786Spsgetmark(c)
7060786Sps	int c;
7160786Sps{
7260786Sps	register struct mark *m;
7360786Sps	static struct mark sm;
7460786Sps
7560786Sps	switch (c)
7660786Sps	{
7760786Sps	case '^':
7860786Sps		/*
7960786Sps		 * Beginning of the current file.
8060786Sps		 */
8160786Sps		m = &sm;
8260786Sps		m->m_scrpos.pos = ch_zero();
8360786Sps		m->m_scrpos.ln = 0;
8460786Sps		m->m_ifile = curr_ifile;
8560786Sps		break;
8660786Sps	case '$':
8760786Sps		/*
8860786Sps		 * End of the current file.
8960786Sps		 */
9060786Sps		if (ch_end_seek())
9160786Sps		{
9260786Sps			error("Cannot seek to end of file", NULL_PARG);
9360786Sps			return (NULL);
9460786Sps		}
9560786Sps		m = &sm;
9660786Sps		m->m_scrpos.pos = ch_tell();
9760786Sps		m->m_scrpos.ln = sc_height-1;
9860786Sps		m->m_ifile = curr_ifile;
9960786Sps		break;
10060786Sps	case '.':
10160786Sps		/*
10260786Sps		 * Current position in the current file.
10360786Sps		 */
10460786Sps		m = &sm;
10560786Sps		get_scrpos(&m->m_scrpos);
10660786Sps		m->m_ifile = curr_ifile;
10760786Sps		break;
10860786Sps	case '\'':
10960786Sps		/*
11060786Sps		 * The "last mark".
11160786Sps		 */
11260786Sps		m = &marks[LASTMARK];
11360786Sps		break;
11460786Sps	default:
11560786Sps		/*
11660786Sps		 * Must be a user-defined mark.
11760786Sps		 */
11860786Sps		m = getumark(c);
11960786Sps		if (m == NULL)
12060786Sps			break;
12160786Sps		if (m->m_scrpos.pos == NULL_POSITION)
12260786Sps		{
12360786Sps			error("Mark not set", NULL_PARG);
12460786Sps			return (NULL);
12560786Sps		}
12660786Sps		break;
12760786Sps	}
12860786Sps	return (m);
12960786Sps}
13060786Sps
13160786Sps/*
13260786Sps * Is a mark letter is invalid?
13360786Sps */
13460786Sps	public int
13560786Spsbadmark(c)
13660786Sps	int c;
13760786Sps{
13860786Sps	return (getmark(c) == NULL);
13960786Sps}
14060786Sps
14160786Sps/*
14260786Sps * Set a user-defined mark.
14360786Sps */
14460786Sps	public void
14560786Spssetmark(c)
14660786Sps	int c;
14760786Sps{
14860786Sps	register struct mark *m;
14960786Sps	struct scrpos scrpos;
15060786Sps
15160786Sps	m = getumark(c);
15260786Sps	if (m == NULL)
15360786Sps		return;
15460786Sps	get_scrpos(&scrpos);
15560786Sps	m->m_scrpos = scrpos;
15660786Sps	m->m_ifile = curr_ifile;
15760786Sps}
15860786Sps
15960786Sps/*
16060786Sps * Set lmark (the mark named by the apostrophe).
16160786Sps */
16260786Sps	public void
16360786Spslastmark()
16460786Sps{
16560786Sps	struct scrpos scrpos;
16660786Sps
16760786Sps	if (ch_getflags() & CH_HELPFILE)
16860786Sps		return;
16960786Sps	get_scrpos(&scrpos);
17060786Sps	if (scrpos.pos == NULL_POSITION)
17160786Sps		return;
17260786Sps	marks[LASTMARK].m_scrpos = scrpos;
17360786Sps	marks[LASTMARK].m_ifile = curr_ifile;
17460786Sps}
17560786Sps
17660786Sps/*
17760786Sps * Go to a mark.
17860786Sps */
17960786Sps	public void
18060786Spsgomark(c)
18160786Sps	int c;
18260786Sps{
18360786Sps	register struct mark *m;
18460786Sps	struct scrpos scrpos;
18560786Sps
18660786Sps	m = getmark(c);
18760786Sps	if (m == NULL)
18860786Sps		return;
18960786Sps
19060786Sps	/*
19160786Sps	 * If we're trying to go to the lastmark and
19260786Sps	 * it has not been set to anything yet,
19360786Sps	 * set it to the beginning of the current file.
19460786Sps	 */
19560786Sps	if (m == &marks[LASTMARK] && m->m_scrpos.pos == NULL_POSITION)
19660786Sps	{
19760786Sps		m->m_ifile = curr_ifile;
19860786Sps		m->m_scrpos.pos = ch_zero();
19960786Sps		m->m_scrpos.ln = jump_sline;
20060786Sps	}
20160786Sps
20260786Sps	/*
20360786Sps	 * If we're using lmark, we must save the screen position now,
20460786Sps	 * because if we call edit_ifile() below, lmark will change.
20560786Sps	 * (We save the screen position even if we're not using lmark.)
20660786Sps	 */
20760786Sps	scrpos = m->m_scrpos;
20860786Sps	if (m->m_ifile != curr_ifile)
20960786Sps	{
21060786Sps		/*
21160786Sps		 * Not in the current file; edit the correct file.
21260786Sps		 */
21360786Sps		if (edit_ifile(m->m_ifile))
21460786Sps			return;
21560786Sps	}
21660786Sps
21760786Sps	jump_loc(scrpos.pos, scrpos.ln);
21860786Sps}
21960786Sps
22060786Sps/*
22160786Sps * Return the position associated with a given mark letter.
22260786Sps *
22360786Sps * We don't return which screen line the position
22460786Sps * is associated with, but this doesn't matter much,
22560786Sps * because it's always the first non-blank line on the screen.
22660786Sps */
22760786Sps	public POSITION
22860786Spsmarkpos(c)
22960786Sps	int c;
23060786Sps{
23160786Sps	register struct mark *m;
23260786Sps
23360786Sps	m = getmark(c);
23460786Sps	if (m == NULL)
23560786Sps		return (NULL_POSITION);
23660786Sps
23760786Sps	if (m->m_ifile != curr_ifile)
23860786Sps	{
23960786Sps		error("Mark not in current file", NULL_PARG);
24060786Sps		return (NULL_POSITION);
24160786Sps	}
24260786Sps	return (m->m_scrpos.pos);
24360786Sps}
24460786Sps
24560786Sps/*
24660786Sps * Clear the marks associated with a specified ifile.
24760786Sps */
24860786Sps	public void
24960786Spsunmark(ifile)
25060786Sps	IFILE ifile;
25160786Sps{
25260786Sps	int i;
25360786Sps
25460786Sps	for (i = 0;  i < NMARKS;  i++)
25560786Sps		if (marks[i].m_ifile == ifile)
25660786Sps			marks[i].m_scrpos.pos = NULL_POSITION;
25760786Sps}
258