xref: /netbsd-src/external/bsd/less/dist/mark.c (revision 20006a0bde522c99e03e2f0935b37976c345030a)
1 /*	$NetBSD	*/
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 
16 extern IFILE curr_ifile;
17 extern int sc_height;
18 extern int jump_sline;
19 
20 /*
21  * A mark is an ifile (input file) plus a position within the file.
22  */
23 struct 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)
35 static struct mark marks[NMARKS];
36 
37 /*
38  * Initialize the mark table to show no marks are set.
39  */
40 	public void
41 init_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 *
53 getumark(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 *
72 getmark(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
138 badmark(c)
139 	int c;
140 {
141 	return (getmark(c) == NULL);
142 }
143 
144 /*
145  * Set a user-defined mark.
146  */
147 	public void
148 setmark(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
166 lastmark()
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
183 gomark(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
231 markpos(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
252 unmark(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