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