xref: /freebsd/contrib/less/mark.c (revision 6829dae12bb055451fa467da4589c43bd03b1e64)
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 
14 extern IFILE curr_ifile;
15 extern int sc_height;
16 extern 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)
25 static struct mark marks[NMARKS];
26 
27 /*
28  * Initialize the mark table to show no marks are set.
29  */
30 	public void
31 init_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 *
43 getumark(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 *
62 getmark(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
128 badmark(c)
129 	int c;
130 {
131 	return (getmark(c) == NULL);
132 }
133 
134 /*
135  * Set a user-defined mark.
136  */
137 	public void
138 setmark(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
157 clrmark(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
172 lastmark()
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
189 gomark(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
237 markpos(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
258 posmark(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
279 unmark(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