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