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