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