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
init_mark()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 *
getumark(c)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 *
getmark(c)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
badmark(c)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
setmark(c,where)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
clrmark(c)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
lastmark()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
gomark(c)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
markpos(c)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
posmark(pos)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
unmark(ifile)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