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