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