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