mark.c revision 1.1.1.3 1 1.1 tron /*
2 1.1.1.3 simonb * Copyright (C) 1984-2023 Mark Nudelman
3 1.1 tron *
4 1.1 tron * You may distribute under the terms of either the GNU General Public
5 1.1 tron * License or the Less License, as specified in the README file.
6 1.1 tron *
7 1.1.1.2 tron * For more information, see the README file.
8 1.1 tron */
9 1.1 tron
10 1.1 tron
11 1.1 tron #include "less.h"
12 1.1.1.3 simonb #include "position.h"
13 1.1 tron
14 1.1 tron extern IFILE curr_ifile;
15 1.1 tron extern int sc_height;
16 1.1 tron extern int jump_sline;
17 1.1.1.3 simonb extern int perma_marks;
18 1.1 tron
19 1.1 tron /*
20 1.1 tron * A mark is an ifile (input file) plus a position within the file.
21 1.1 tron */
22 1.1.1.3 simonb struct mark
23 1.1.1.3 simonb {
24 1.1.1.3 simonb /*
25 1.1.1.3 simonb * Normally m_ifile != IFILE_NULL and m_filename == NULL.
26 1.1.1.3 simonb * For restored marks we set m_filename instead of m_ifile
27 1.1.1.3 simonb * because we don't want to create an ifile until the
28 1.1.1.3 simonb * user explicitly requests the file (by name or mark).
29 1.1.1.3 simonb */
30 1.1.1.3 simonb char m_letter; /* Associated character */
31 1.1.1.3 simonb IFILE m_ifile; /* Input file being marked */
32 1.1.1.3 simonb char *m_filename; /* Name of the input file */
33 1.1.1.3 simonb struct scrpos m_scrpos; /* Position of the mark */
34 1.1 tron };
35 1.1 tron
36 1.1 tron /*
37 1.1 tron * The table of marks.
38 1.1 tron * Each mark is identified by a lowercase or uppercase letter.
39 1.1 tron * The final one is lmark, for the "last mark"; addressed by the apostrophe.
40 1.1 tron */
41 1.1.1.3 simonb #define NMARKS ((2*26)+2) /* a-z, A-Z, mousemark, lastmark */
42 1.1.1.3 simonb #define NUMARKS ((2*26)+1) /* user marks (not lastmark) */
43 1.1.1.3 simonb #define MOUSEMARK (NMARKS-2)
44 1.1.1.3 simonb #define LASTMARK (NMARKS-1)
45 1.1 tron static struct mark marks[NMARKS];
46 1.1.1.3 simonb public int marks_modified = 0;
47 1.1.1.3 simonb
48 1.1.1.3 simonb
49 1.1.1.3 simonb /*
50 1.1.1.3 simonb * Initialize a mark struct.
51 1.1.1.3 simonb */
52 1.1.1.3 simonb static void cmark(struct mark *m, IFILE ifile, POSITION pos, int ln)
53 1.1.1.3 simonb {
54 1.1.1.3 simonb m->m_ifile = ifile;
55 1.1.1.3 simonb m->m_scrpos.pos = pos;
56 1.1.1.3 simonb m->m_scrpos.ln = ln;
57 1.1.1.3 simonb if (m->m_filename != NULL)
58 1.1.1.3 simonb /* Normally should not happen but a corrupt lesshst file can do it. */
59 1.1.1.3 simonb free(m->m_filename);
60 1.1.1.3 simonb m->m_filename = NULL;
61 1.1.1.3 simonb }
62 1.1 tron
63 1.1 tron /*
64 1.1 tron * Initialize the mark table to show no marks are set.
65 1.1 tron */
66 1.1.1.3 simonb public void init_mark(void)
67 1.1 tron {
68 1.1 tron int i;
69 1.1 tron
70 1.1 tron for (i = 0; i < NMARKS; i++)
71 1.1.1.3 simonb {
72 1.1.1.3 simonb char letter;
73 1.1.1.3 simonb switch (i) {
74 1.1.1.3 simonb case MOUSEMARK: letter = '#'; break;
75 1.1.1.3 simonb case LASTMARK: letter = '\''; break;
76 1.1.1.3 simonb default: letter = (i < 26) ? 'a'+i : 'A'+i-26; break;
77 1.1.1.3 simonb }
78 1.1.1.3 simonb marks[i].m_letter = letter;
79 1.1.1.3 simonb cmark(&marks[i], NULL_IFILE, NULL_POSITION, -1);
80 1.1.1.3 simonb }
81 1.1.1.3 simonb }
82 1.1.1.3 simonb
83 1.1.1.3 simonb /*
84 1.1.1.3 simonb * Set m_ifile and clear m_filename.
85 1.1.1.3 simonb */
86 1.1.1.3 simonb static void mark_set_ifile(struct mark *m, IFILE ifile)
87 1.1.1.3 simonb {
88 1.1.1.3 simonb m->m_ifile = ifile;
89 1.1.1.3 simonb /* With m_ifile set, m_filename is no longer needed. */
90 1.1.1.3 simonb free(m->m_filename);
91 1.1.1.3 simonb m->m_filename = NULL;
92 1.1 tron }
93 1.1 tron
94 1.1 tron /*
95 1.1.1.3 simonb * Populate the m_ifile member of a mark struct from m_filename.
96 1.1 tron */
97 1.1.1.3 simonb static void mark_get_ifile(struct mark *m)
98 1.1 tron {
99 1.1.1.3 simonb if (m->m_ifile != NULL_IFILE)
100 1.1.1.3 simonb return; /* m_ifile is already set */
101 1.1.1.3 simonb mark_set_ifile(m, get_ifile(m->m_filename, prev_ifile(NULL_IFILE)));
102 1.1.1.3 simonb }
103 1.1.1.3 simonb
104 1.1.1.3 simonb /*
105 1.1.1.3 simonb * Return the user mark struct identified by a character.
106 1.1.1.3 simonb */
107 1.1.1.3 simonb static struct mark * getumark(LWCHAR c)
108 1.1.1.3 simonb {
109 1.1.1.3 simonb PARG parg;
110 1.1 tron if (c >= 'a' && c <= 'z')
111 1.1 tron return (&marks[c-'a']);
112 1.1 tron if (c >= 'A' && c <= 'Z')
113 1.1 tron return (&marks[c-'A'+26]);
114 1.1.1.3 simonb if (c == '\'')
115 1.1.1.3 simonb return (&marks[LASTMARK]);
116 1.1.1.3 simonb if (c == '#')
117 1.1.1.3 simonb return (&marks[MOUSEMARK]);
118 1.1.1.3 simonb parg.p_char = (char) c;
119 1.1.1.3 simonb error("Invalid mark letter %c", &parg);
120 1.1 tron return (NULL);
121 1.1 tron }
122 1.1 tron
123 1.1 tron /*
124 1.1 tron * Get the mark structure identified by a character.
125 1.1.1.3 simonb * The mark struct may either be in the mark table (user mark)
126 1.1 tron * or may be constructed on the fly for certain characters like ^, $.
127 1.1 tron */
128 1.1.1.3 simonb static struct mark * getmark(LWCHAR c)
129 1.1 tron {
130 1.1.1.3 simonb struct mark *m;
131 1.1 tron static struct mark sm;
132 1.1 tron
133 1.1 tron switch (c)
134 1.1 tron {
135 1.1 tron case '^':
136 1.1 tron /*
137 1.1 tron * Beginning of the current file.
138 1.1 tron */
139 1.1 tron m = &sm;
140 1.1.1.3 simonb cmark(m, curr_ifile, ch_zero(), 0);
141 1.1 tron break;
142 1.1 tron case '$':
143 1.1 tron /*
144 1.1 tron * End of the current file.
145 1.1 tron */
146 1.1 tron if (ch_end_seek())
147 1.1 tron {
148 1.1 tron error("Cannot seek to end of file", NULL_PARG);
149 1.1 tron return (NULL);
150 1.1 tron }
151 1.1 tron m = &sm;
152 1.1.1.3 simonb cmark(m, curr_ifile, ch_tell(), sc_height);
153 1.1 tron break;
154 1.1 tron case '.':
155 1.1 tron /*
156 1.1 tron * Current position in the current file.
157 1.1 tron */
158 1.1 tron m = &sm;
159 1.1.1.3 simonb get_scrpos(&m->m_scrpos, TOP);
160 1.1.1.3 simonb cmark(m, curr_ifile, m->m_scrpos.pos, m->m_scrpos.ln);
161 1.1 tron break;
162 1.1 tron case '\'':
163 1.1 tron /*
164 1.1 tron * The "last mark".
165 1.1 tron */
166 1.1 tron m = &marks[LASTMARK];
167 1.1 tron break;
168 1.1 tron default:
169 1.1 tron /*
170 1.1 tron * Must be a user-defined mark.
171 1.1 tron */
172 1.1 tron m = getumark(c);
173 1.1 tron if (m == NULL)
174 1.1 tron break;
175 1.1 tron if (m->m_scrpos.pos == NULL_POSITION)
176 1.1 tron {
177 1.1 tron error("Mark not set", NULL_PARG);
178 1.1 tron return (NULL);
179 1.1 tron }
180 1.1 tron break;
181 1.1 tron }
182 1.1 tron return (m);
183 1.1 tron }
184 1.1 tron
185 1.1 tron /*
186 1.1.1.3 simonb * Is a mark letter invalid?
187 1.1 tron */
188 1.1.1.3 simonb public int badmark(LWCHAR c)
189 1.1 tron {
190 1.1 tron return (getmark(c) == NULL);
191 1.1 tron }
192 1.1 tron
193 1.1 tron /*
194 1.1 tron * Set a user-defined mark.
195 1.1 tron */
196 1.1.1.3 simonb public void setmark(LWCHAR c, int where)
197 1.1 tron {
198 1.1.1.3 simonb struct mark *m;
199 1.1 tron struct scrpos scrpos;
200 1.1 tron
201 1.1 tron m = getumark(c);
202 1.1 tron if (m == NULL)
203 1.1 tron return;
204 1.1.1.3 simonb get_scrpos(&scrpos, where);
205 1.1.1.3 simonb if (scrpos.pos == NULL_POSITION)
206 1.1.1.3 simonb {
207 1.1.1.3 simonb bell();
208 1.1.1.3 simonb return;
209 1.1.1.3 simonb }
210 1.1.1.3 simonb cmark(m, curr_ifile, scrpos.pos, scrpos.ln);
211 1.1.1.3 simonb marks_modified = 1;
212 1.1.1.3 simonb }
213 1.1.1.3 simonb
214 1.1.1.3 simonb /*
215 1.1.1.3 simonb * Clear a user-defined mark.
216 1.1.1.3 simonb */
217 1.1.1.3 simonb public void clrmark(LWCHAR c)
218 1.1.1.3 simonb {
219 1.1.1.3 simonb struct mark *m;
220 1.1.1.3 simonb
221 1.1.1.3 simonb m = getumark(c);
222 1.1.1.3 simonb if (m == NULL)
223 1.1.1.3 simonb return;
224 1.1.1.3 simonb if (m->m_scrpos.pos == NULL_POSITION)
225 1.1.1.3 simonb {
226 1.1.1.3 simonb bell();
227 1.1.1.3 simonb return;
228 1.1.1.3 simonb }
229 1.1.1.3 simonb m->m_scrpos.pos = NULL_POSITION;
230 1.1.1.3 simonb marks_modified = 1;
231 1.1 tron }
232 1.1 tron
233 1.1 tron /*
234 1.1 tron * Set lmark (the mark named by the apostrophe).
235 1.1 tron */
236 1.1.1.3 simonb public void lastmark(void)
237 1.1 tron {
238 1.1 tron struct scrpos scrpos;
239 1.1 tron
240 1.1 tron if (ch_getflags() & CH_HELPFILE)
241 1.1 tron return;
242 1.1.1.3 simonb get_scrpos(&scrpos, TOP);
243 1.1 tron if (scrpos.pos == NULL_POSITION)
244 1.1 tron return;
245 1.1.1.3 simonb cmark(&marks[LASTMARK], curr_ifile, scrpos.pos, scrpos.ln);
246 1.1.1.3 simonb marks_modified = 1;
247 1.1 tron }
248 1.1 tron
249 1.1 tron /*
250 1.1 tron * Go to a mark.
251 1.1 tron */
252 1.1.1.3 simonb public void gomark(LWCHAR c)
253 1.1 tron {
254 1.1.1.3 simonb struct mark *m;
255 1.1 tron struct scrpos scrpos;
256 1.1 tron
257 1.1 tron m = getmark(c);
258 1.1 tron if (m == NULL)
259 1.1 tron return;
260 1.1 tron
261 1.1 tron /*
262 1.1 tron * If we're trying to go to the lastmark and
263 1.1 tron * it has not been set to anything yet,
264 1.1 tron * set it to the beginning of the current file.
265 1.1.1.3 simonb * {{ Couldn't we instead set marks[LASTMARK] in edit()? }}
266 1.1 tron */
267 1.1 tron if (m == &marks[LASTMARK] && m->m_scrpos.pos == NULL_POSITION)
268 1.1.1.3 simonb cmark(m, curr_ifile, ch_zero(), jump_sline);
269 1.1 tron
270 1.1.1.3 simonb mark_get_ifile(m);
271 1.1.1.3 simonb
272 1.1.1.3 simonb /* Save scrpos; if it's LASTMARK it could change in edit_ifile. */
273 1.1 tron scrpos = m->m_scrpos;
274 1.1 tron if (m->m_ifile != curr_ifile)
275 1.1 tron {
276 1.1 tron /*
277 1.1 tron * Not in the current file; edit the correct file.
278 1.1 tron */
279 1.1 tron if (edit_ifile(m->m_ifile))
280 1.1 tron return;
281 1.1 tron }
282 1.1 tron
283 1.1 tron jump_loc(scrpos.pos, scrpos.ln);
284 1.1 tron }
285 1.1 tron
286 1.1 tron /*
287 1.1 tron * Return the position associated with a given mark letter.
288 1.1 tron *
289 1.1 tron * We don't return which screen line the position
290 1.1 tron * is associated with, but this doesn't matter much,
291 1.1 tron * because it's always the first non-blank line on the screen.
292 1.1 tron */
293 1.1.1.3 simonb public POSITION markpos(LWCHAR c)
294 1.1 tron {
295 1.1.1.3 simonb struct mark *m;
296 1.1 tron
297 1.1 tron m = getmark(c);
298 1.1 tron if (m == NULL)
299 1.1 tron return (NULL_POSITION);
300 1.1 tron
301 1.1 tron if (m->m_ifile != curr_ifile)
302 1.1 tron {
303 1.1 tron error("Mark not in current file", NULL_PARG);
304 1.1 tron return (NULL_POSITION);
305 1.1 tron }
306 1.1 tron return (m->m_scrpos.pos);
307 1.1 tron }
308 1.1 tron
309 1.1 tron /*
310 1.1.1.3 simonb * Return the mark associated with a given position, if any.
311 1.1.1.3 simonb */
312 1.1.1.3 simonb public char posmark(POSITION pos)
313 1.1.1.3 simonb {
314 1.1.1.3 simonb int i;
315 1.1.1.3 simonb
316 1.1.1.3 simonb /* Only user marks */
317 1.1.1.3 simonb for (i = 0; i < NUMARKS; i++)
318 1.1.1.3 simonb {
319 1.1.1.3 simonb if (marks[i].m_ifile == curr_ifile && marks[i].m_scrpos.pos == pos)
320 1.1.1.3 simonb {
321 1.1.1.3 simonb if (i < 26) return 'a' + i;
322 1.1.1.3 simonb if (i < 26*2) return 'A' + (i - 26);
323 1.1.1.3 simonb return '#';
324 1.1.1.3 simonb }
325 1.1.1.3 simonb }
326 1.1.1.3 simonb return 0;
327 1.1.1.3 simonb }
328 1.1.1.3 simonb
329 1.1.1.3 simonb /*
330 1.1 tron * Clear the marks associated with a specified ifile.
331 1.1 tron */
332 1.1.1.3 simonb public void unmark(IFILE ifile)
333 1.1 tron {
334 1.1 tron int i;
335 1.1 tron
336 1.1 tron for (i = 0; i < NMARKS; i++)
337 1.1 tron if (marks[i].m_ifile == ifile)
338 1.1 tron marks[i].m_scrpos.pos = NULL_POSITION;
339 1.1 tron }
340 1.1.1.3 simonb
341 1.1.1.3 simonb /*
342 1.1.1.3 simonb * Check if any marks refer to a specified ifile vi m_filename
343 1.1.1.3 simonb * rather than m_ifile.
344 1.1.1.3 simonb */
345 1.1.1.3 simonb public void mark_check_ifile(IFILE ifile)
346 1.1.1.3 simonb {
347 1.1.1.3 simonb int i;
348 1.1.1.3 simonb char *filename = get_real_filename(ifile);
349 1.1.1.3 simonb
350 1.1.1.3 simonb for (i = 0; i < NMARKS; i++)
351 1.1.1.3 simonb {
352 1.1.1.3 simonb struct mark *m = &marks[i];
353 1.1.1.3 simonb char *mark_filename = m->m_filename;
354 1.1.1.3 simonb if (mark_filename != NULL)
355 1.1.1.3 simonb {
356 1.1.1.3 simonb mark_filename = lrealpath(mark_filename);
357 1.1.1.3 simonb if (strcmp(filename, mark_filename) == 0)
358 1.1.1.3 simonb mark_set_ifile(m, ifile);
359 1.1.1.3 simonb free(mark_filename);
360 1.1.1.3 simonb }
361 1.1.1.3 simonb }
362 1.1.1.3 simonb }
363 1.1.1.3 simonb
364 1.1.1.3 simonb #if CMD_HISTORY
365 1.1.1.3 simonb
366 1.1.1.3 simonb /*
367 1.1.1.3 simonb * Save marks to history file.
368 1.1.1.3 simonb */
369 1.1.1.3 simonb public void save_marks(FILE *fout, char *hdr)
370 1.1.1.3 simonb {
371 1.1.1.3 simonb int i;
372 1.1.1.3 simonb
373 1.1.1.3 simonb if (!perma_marks)
374 1.1.1.3 simonb return;
375 1.1.1.3 simonb
376 1.1.1.3 simonb fprintf(fout, "%s\n", hdr);
377 1.1.1.3 simonb for (i = 0; i < NMARKS; i++)
378 1.1.1.3 simonb {
379 1.1.1.3 simonb char *filename;
380 1.1.1.3 simonb struct mark *m = &marks[i];
381 1.1.1.3 simonb char pos_str[INT_STRLEN_BOUND(m->m_scrpos.pos) + 2];
382 1.1.1.3 simonb if (m->m_scrpos.pos == NULL_POSITION)
383 1.1.1.3 simonb continue;
384 1.1.1.3 simonb postoa(m->m_scrpos.pos, pos_str, 10);
385 1.1.1.3 simonb filename = m->m_filename;
386 1.1.1.3 simonb if (filename == NULL)
387 1.1.1.3 simonb filename = get_real_filename(m->m_ifile);
388 1.1.1.3 simonb if (strcmp(filename, "-") != 0)
389 1.1.1.3 simonb fprintf(fout, "m %c %d %s %s\n",
390 1.1.1.3 simonb m->m_letter, m->m_scrpos.ln, pos_str, filename);
391 1.1.1.3 simonb }
392 1.1.1.3 simonb }
393 1.1.1.3 simonb
394 1.1.1.3 simonb /*
395 1.1.1.3 simonb * Restore one mark from the history file.
396 1.1.1.3 simonb */
397 1.1.1.3 simonb public void restore_mark(char *line)
398 1.1.1.3 simonb {
399 1.1.1.3 simonb struct mark *m;
400 1.1.1.3 simonb int ln;
401 1.1.1.3 simonb POSITION pos;
402 1.1.1.3 simonb
403 1.1.1.3 simonb #define skip_whitespace while (*line == ' ') line++
404 1.1.1.3 simonb if (*line++ != 'm')
405 1.1.1.3 simonb return;
406 1.1.1.3 simonb skip_whitespace;
407 1.1.1.3 simonb m = getumark(*line++);
408 1.1.1.3 simonb if (m == NULL)
409 1.1.1.3 simonb return;
410 1.1.1.3 simonb skip_whitespace;
411 1.1.1.3 simonb ln = lstrtoi(line, &line, 10);
412 1.1.1.3 simonb if (ln < 0)
413 1.1.1.3 simonb return;
414 1.1.1.3 simonb if (ln < 1)
415 1.1.1.3 simonb ln = 1;
416 1.1.1.3 simonb if (ln > sc_height)
417 1.1.1.3 simonb ln = sc_height;
418 1.1.1.3 simonb skip_whitespace;
419 1.1.1.3 simonb pos = lstrtopos(line, &line, 10);
420 1.1.1.3 simonb if (pos < 0)
421 1.1.1.3 simonb return;
422 1.1.1.3 simonb skip_whitespace;
423 1.1.1.3 simonb cmark(m, NULL_IFILE, pos, ln);
424 1.1.1.3 simonb m->m_filename = save(line);
425 1.1.1.3 simonb }
426 1.1.1.3 simonb
427 1.1.1.3 simonb #endif /* CMD_HISTORY */
428