1238730Sdelphij/*
2330571Sdelphij * Copyright (C) 1984-2017  Mark Nudelman
3238730Sdelphij *
4238730Sdelphij * You may distribute under the terms of either the GNU General Public
5238730Sdelphij * License or the Less License, as specified in the README file.
6238730Sdelphij *
7238730Sdelphij * For more information, see the README file.
8238730Sdelphij */
960786Sps
1060786Sps
1160786Sps#include "less.h"
12330571Sdelphij#include "position.h"
1360786Sps
1460786Spsextern IFILE curr_ifile;
1560786Spsextern int sc_height;
1660786Spsextern int jump_sline;
1760786Sps
1860786Sps/*
1960786Sps * The table of marks.
2060786Sps * Each mark is identified by a lowercase or uppercase letter.
2160786Sps * The final one is lmark, for the "last mark"; addressed by the apostrophe.
2260786Sps */
2360786Sps#define	NMARKS		((2*26)+1)	/* a-z, A-Z, lastmark */
2460786Sps#define	LASTMARK	(NMARKS-1)
2560786Spsstatic struct mark marks[NMARKS];
2660786Sps
2760786Sps/*
2860786Sps * Initialize the mark table to show no marks are set.
2960786Sps */
3060786Sps	public void
3160786Spsinit_mark()
3260786Sps{
3360786Sps	int i;
3460786Sps
3560786Sps	for (i = 0;  i < NMARKS;  i++)
3660786Sps		marks[i].m_scrpos.pos = NULL_POSITION;
3760786Sps}
3860786Sps
3960786Sps/*
4060786Sps * See if a mark letter is valid (between a and z).
4160786Sps */
4260786Sps	static struct mark *
4360786Spsgetumark(c)
4460786Sps	int c;
4560786Sps{
4660786Sps	if (c >= 'a' && c <= 'z')
4760786Sps		return (&marks[c-'a']);
4860786Sps
4960786Sps	if (c >= 'A' && c <= 'Z')
5060786Sps		return (&marks[c-'A'+26]);
5160786Sps
5260786Sps	error("Invalid mark letter", NULL_PARG);
5360786Sps	return (NULL);
5460786Sps}
5560786Sps
5660786Sps/*
5760786Sps * Get the mark structure identified by a character.
5860786Sps * The mark struct may come either from the mark table
5960786Sps * or may be constructed on the fly for certain characters like ^, $.
6060786Sps */
6160786Sps	static struct mark *
6260786Spsgetmark(c)
6360786Sps	int c;
6460786Sps{
65330571Sdelphij	struct mark *m;
6660786Sps	static struct mark sm;
6760786Sps
6860786Sps	switch (c)
6960786Sps	{
7060786Sps	case '^':
7160786Sps		/*
7260786Sps		 * Beginning of the current file.
7360786Sps		 */
7460786Sps		m = &sm;
7560786Sps		m->m_scrpos.pos = ch_zero();
7660786Sps		m->m_scrpos.ln = 0;
7760786Sps		m->m_ifile = curr_ifile;
7860786Sps		break;
7960786Sps	case '$':
8060786Sps		/*
8160786Sps		 * End of the current file.
8260786Sps		 */
8360786Sps		if (ch_end_seek())
8460786Sps		{
8560786Sps			error("Cannot seek to end of file", NULL_PARG);
8660786Sps			return (NULL);
8760786Sps		}
8860786Sps		m = &sm;
8960786Sps		m->m_scrpos.pos = ch_tell();
90330571Sdelphij		m->m_scrpos.ln = sc_height;
9160786Sps		m->m_ifile = curr_ifile;
9260786Sps		break;
9360786Sps	case '.':
9460786Sps		/*
9560786Sps		 * Current position in the current file.
9660786Sps		 */
9760786Sps		m = &sm;
98330571Sdelphij		get_scrpos(&m->m_scrpos, TOP);
9960786Sps		m->m_ifile = curr_ifile;
10060786Sps		break;
10160786Sps	case '\'':
10260786Sps		/*
10360786Sps		 * The "last mark".
10460786Sps		 */
10560786Sps		m = &marks[LASTMARK];
10660786Sps		break;
10760786Sps	default:
10860786Sps		/*
10960786Sps		 * Must be a user-defined mark.
11060786Sps		 */
11160786Sps		m = getumark(c);
11260786Sps		if (m == NULL)
11360786Sps			break;
11460786Sps		if (m->m_scrpos.pos == NULL_POSITION)
11560786Sps		{
11660786Sps			error("Mark not set", NULL_PARG);
11760786Sps			return (NULL);
11860786Sps		}
11960786Sps		break;
12060786Sps	}
12160786Sps	return (m);
12260786Sps}
12360786Sps
12460786Sps/*
12560786Sps * Is a mark letter is invalid?
12660786Sps */
12760786Sps	public int
12860786Spsbadmark(c)
12960786Sps	int c;
13060786Sps{
13160786Sps	return (getmark(c) == NULL);
13260786Sps}
13360786Sps
13460786Sps/*
13560786Sps * Set a user-defined mark.
13660786Sps */
13760786Sps	public void
138330571Sdelphijsetmark(c, where)
13960786Sps	int c;
140330571Sdelphij	int where;
14160786Sps{
142330571Sdelphij	struct mark *m;
14360786Sps	struct scrpos scrpos;
14460786Sps
14560786Sps	m = getumark(c);
14660786Sps	if (m == NULL)
14760786Sps		return;
148330571Sdelphij	get_scrpos(&scrpos, where);
14960786Sps	m->m_scrpos = scrpos;
15060786Sps	m->m_ifile = curr_ifile;
15160786Sps}
15260786Sps
15360786Sps/*
154330571Sdelphij * Clear a user-defined mark.
155330571Sdelphij */
156330571Sdelphij	public void
157330571Sdelphijclrmark(c)
158330571Sdelphij	int c;
159330571Sdelphij{
160330571Sdelphij	struct mark *m;
161330571Sdelphij
162330571Sdelphij	m = getumark(c);
163330571Sdelphij	if (m == NULL)
164330571Sdelphij		return;
165330571Sdelphij	m->m_scrpos.pos = NULL_POSITION;
166330571Sdelphij}
167330571Sdelphij
168330571Sdelphij/*
16960786Sps * Set lmark (the mark named by the apostrophe).
17060786Sps */
17160786Sps	public void
17260786Spslastmark()
17360786Sps{
17460786Sps	struct scrpos scrpos;
17560786Sps
17660786Sps	if (ch_getflags() & CH_HELPFILE)
17760786Sps		return;
178330571Sdelphij	get_scrpos(&scrpos, TOP);
17960786Sps	if (scrpos.pos == NULL_POSITION)
18060786Sps		return;
18160786Sps	marks[LASTMARK].m_scrpos = scrpos;
18260786Sps	marks[LASTMARK].m_ifile = curr_ifile;
18360786Sps}
18460786Sps
18560786Sps/*
18660786Sps * Go to a mark.
18760786Sps */
18860786Sps	public void
18960786Spsgomark(c)
19060786Sps	int c;
19160786Sps{
192330571Sdelphij	struct mark *m;
19360786Sps	struct scrpos scrpos;
19460786Sps
19560786Sps	m = getmark(c);
19660786Sps	if (m == NULL)
19760786Sps		return;
19860786Sps
19960786Sps	/*
20060786Sps	 * If we're trying to go to the lastmark and
20160786Sps	 * it has not been set to anything yet,
20260786Sps	 * set it to the beginning of the current file.
20360786Sps	 */
20460786Sps	if (m == &marks[LASTMARK] && m->m_scrpos.pos == NULL_POSITION)
20560786Sps	{
20660786Sps		m->m_ifile = curr_ifile;
20760786Sps		m->m_scrpos.pos = ch_zero();
20860786Sps		m->m_scrpos.ln = jump_sline;
20960786Sps	}
21060786Sps
21160786Sps	/*
21260786Sps	 * If we're using lmark, we must save the screen position now,
21360786Sps	 * because if we call edit_ifile() below, lmark will change.
21460786Sps	 * (We save the screen position even if we're not using lmark.)
21560786Sps	 */
21660786Sps	scrpos = m->m_scrpos;
21760786Sps	if (m->m_ifile != curr_ifile)
21860786Sps	{
21960786Sps		/*
22060786Sps		 * Not in the current file; edit the correct file.
22160786Sps		 */
22260786Sps		if (edit_ifile(m->m_ifile))
22360786Sps			return;
22460786Sps	}
22560786Sps
22660786Sps	jump_loc(scrpos.pos, scrpos.ln);
22760786Sps}
22860786Sps
22960786Sps/*
23060786Sps * Return the position associated with a given mark letter.
23160786Sps *
23260786Sps * We don't return which screen line the position
23360786Sps * is associated with, but this doesn't matter much,
23460786Sps * because it's always the first non-blank line on the screen.
23560786Sps */
23660786Sps	public POSITION
23760786Spsmarkpos(c)
23860786Sps	int c;
23960786Sps{
240330571Sdelphij	struct mark *m;
24160786Sps
24260786Sps	m = getmark(c);
24360786Sps	if (m == NULL)
24460786Sps		return (NULL_POSITION);
24560786Sps
24660786Sps	if (m->m_ifile != curr_ifile)
24760786Sps	{
24860786Sps		error("Mark not in current file", NULL_PARG);
24960786Sps		return (NULL_POSITION);
25060786Sps	}
25160786Sps	return (m->m_scrpos.pos);
25260786Sps}
25360786Sps
25460786Sps/*
255330571Sdelphij * Return the mark associated with a given position, if any.
256330571Sdelphij */
257330571Sdelphij	public char
258330571Sdelphijposmark(pos)
259330571Sdelphij	POSITION pos;
260330571Sdelphij{
261330571Sdelphij	int i;
262330571Sdelphij
263330571Sdelphij	/* Only lower case and upper case letters */
264330571Sdelphij	for (i = 0;  i < 26*2;  i++)
265330571Sdelphij	{
266330571Sdelphij		if (marks[i].m_ifile == curr_ifile && marks[i].m_scrpos.pos == pos)
267330571Sdelphij		{
268330571Sdelphij			if (i < 26) return 'a' + i;
269330571Sdelphij			return 'A' + i - 26;
270330571Sdelphij		}
271330571Sdelphij	}
272330571Sdelphij	return 0;
273330571Sdelphij}
274330571Sdelphij
275330571Sdelphij/*
27660786Sps * Clear the marks associated with a specified ifile.
27760786Sps */
27860786Sps	public void
27960786Spsunmark(ifile)
28060786Sps	IFILE ifile;
28160786Sps{
28260786Sps	int i;
28360786Sps
28460786Sps	for (i = 0;  i < NMARKS;  i++)
28560786Sps		if (marks[i].m_ifile == ifile)
28660786Sps			marks[i].m_scrpos.pos = NULL_POSITION;
28760786Sps}
288