mark.c revision 330571
1/*
2 * Copyright (C) 1984-2017  Mark Nudelman
3 *
4 * You may distribute under the terms of either the GNU General Public
5 * License or the Less License, as specified in the README file.
6 *
7 * For more information, see the README file.
8 */
9
10
11#include "less.h"
12#include "position.h"
13
14extern IFILE curr_ifile;
15extern int sc_height;
16extern int jump_sline;
17
18/*
19 * The table of marks.
20 * Each mark is identified by a lowercase or uppercase letter.
21 * The final one is lmark, for the "last mark"; addressed by the apostrophe.
22 */
23#define	NMARKS		((2*26)+1)	/* a-z, A-Z, lastmark */
24#define	LASTMARK	(NMARKS-1)
25static struct mark marks[NMARKS];
26
27/*
28 * Initialize the mark table to show no marks are set.
29 */
30	public void
31init_mark()
32{
33	int i;
34
35	for (i = 0;  i < NMARKS;  i++)
36		marks[i].m_scrpos.pos = NULL_POSITION;
37}
38
39/*
40 * See if a mark letter is valid (between a and z).
41 */
42	static struct mark *
43getumark(c)
44	int c;
45{
46	if (c >= 'a' && c <= 'z')
47		return (&marks[c-'a']);
48
49	if (c >= 'A' && c <= 'Z')
50		return (&marks[c-'A'+26]);
51
52	error("Invalid mark letter", NULL_PARG);
53	return (NULL);
54}
55
56/*
57 * Get the mark structure identified by a character.
58 * The mark struct may come either from the mark table
59 * or may be constructed on the fly for certain characters like ^, $.
60 */
61	static struct mark *
62getmark(c)
63	int c;
64{
65	struct mark *m;
66	static struct mark sm;
67
68	switch (c)
69	{
70	case '^':
71		/*
72		 * Beginning of the current file.
73		 */
74		m = &sm;
75		m->m_scrpos.pos = ch_zero();
76		m->m_scrpos.ln = 0;
77		m->m_ifile = curr_ifile;
78		break;
79	case '$':
80		/*
81		 * End of the current file.
82		 */
83		if (ch_end_seek())
84		{
85			error("Cannot seek to end of file", NULL_PARG);
86			return (NULL);
87		}
88		m = &sm;
89		m->m_scrpos.pos = ch_tell();
90		m->m_scrpos.ln = sc_height;
91		m->m_ifile = curr_ifile;
92		break;
93	case '.':
94		/*
95		 * Current position in the current file.
96		 */
97		m = &sm;
98		get_scrpos(&m->m_scrpos, TOP);
99		m->m_ifile = curr_ifile;
100		break;
101	case '\'':
102		/*
103		 * The "last mark".
104		 */
105		m = &marks[LASTMARK];
106		break;
107	default:
108		/*
109		 * Must be a user-defined mark.
110		 */
111		m = getumark(c);
112		if (m == NULL)
113			break;
114		if (m->m_scrpos.pos == NULL_POSITION)
115		{
116			error("Mark not set", NULL_PARG);
117			return (NULL);
118		}
119		break;
120	}
121	return (m);
122}
123
124/*
125 * Is a mark letter is invalid?
126 */
127	public int
128badmark(c)
129	int c;
130{
131	return (getmark(c) == NULL);
132}
133
134/*
135 * Set a user-defined mark.
136 */
137	public void
138setmark(c, where)
139	int c;
140	int where;
141{
142	struct mark *m;
143	struct scrpos scrpos;
144
145	m = getumark(c);
146	if (m == NULL)
147		return;
148	get_scrpos(&scrpos, where);
149	m->m_scrpos = scrpos;
150	m->m_ifile = curr_ifile;
151}
152
153/*
154 * Clear a user-defined mark.
155 */
156	public void
157clrmark(c)
158	int c;
159{
160	struct mark *m;
161
162	m = getumark(c);
163	if (m == NULL)
164		return;
165	m->m_scrpos.pos = NULL_POSITION;
166}
167
168/*
169 * Set lmark (the mark named by the apostrophe).
170 */
171	public void
172lastmark()
173{
174	struct scrpos scrpos;
175
176	if (ch_getflags() & CH_HELPFILE)
177		return;
178	get_scrpos(&scrpos, TOP);
179	if (scrpos.pos == NULL_POSITION)
180		return;
181	marks[LASTMARK].m_scrpos = scrpos;
182	marks[LASTMARK].m_ifile = curr_ifile;
183}
184
185/*
186 * Go to a mark.
187 */
188	public void
189gomark(c)
190	int c;
191{
192	struct mark *m;
193	struct scrpos scrpos;
194
195	m = getmark(c);
196	if (m == NULL)
197		return;
198
199	/*
200	 * If we're trying to go to the lastmark and
201	 * it has not been set to anything yet,
202	 * set it to the beginning of the current file.
203	 */
204	if (m == &marks[LASTMARK] && m->m_scrpos.pos == NULL_POSITION)
205	{
206		m->m_ifile = curr_ifile;
207		m->m_scrpos.pos = ch_zero();
208		m->m_scrpos.ln = jump_sline;
209	}
210
211	/*
212	 * If we're using lmark, we must save the screen position now,
213	 * because if we call edit_ifile() below, lmark will change.
214	 * (We save the screen position even if we're not using lmark.)
215	 */
216	scrpos = m->m_scrpos;
217	if (m->m_ifile != curr_ifile)
218	{
219		/*
220		 * Not in the current file; edit the correct file.
221		 */
222		if (edit_ifile(m->m_ifile))
223			return;
224	}
225
226	jump_loc(scrpos.pos, scrpos.ln);
227}
228
229/*
230 * Return the position associated with a given mark letter.
231 *
232 * We don't return which screen line the position
233 * is associated with, but this doesn't matter much,
234 * because it's always the first non-blank line on the screen.
235 */
236	public POSITION
237markpos(c)
238	int c;
239{
240	struct mark *m;
241
242	m = getmark(c);
243	if (m == NULL)
244		return (NULL_POSITION);
245
246	if (m->m_ifile != curr_ifile)
247	{
248		error("Mark not in current file", NULL_PARG);
249		return (NULL_POSITION);
250	}
251	return (m->m_scrpos.pos);
252}
253
254/*
255 * Return the mark associated with a given position, if any.
256 */
257	public char
258posmark(pos)
259	POSITION pos;
260{
261	int i;
262
263	/* Only lower case and upper case letters */
264	for (i = 0;  i < 26*2;  i++)
265	{
266		if (marks[i].m_ifile == curr_ifile && marks[i].m_scrpos.pos == pos)
267		{
268			if (i < 26) return 'a' + i;
269			return 'A' + i - 26;
270		}
271	}
272	return 0;
273}
274
275/*
276 * Clear the marks associated with a specified ifile.
277 */
278	public void
279unmark(ifile)
280	IFILE ifile;
281{
282	int i;
283
284	for (i = 0;  i < NMARKS;  i++)
285		if (marks[i].m_ifile == ifile)
286			marks[i].m_scrpos.pos = NULL_POSITION;
287}
288