Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/less/mark.c
39476 views
1
/*
2
* Copyright (C) 1984-2025 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 = (char) ((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(char 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(char 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(char c)
189
{
190
return (getmark(c) == NULL);
191
}
192
193
/*
194
* Set a user-defined mark.
195
*/
196
public void setmark(char 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(char 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(char 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(char 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
unsigned char 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 (char) ('a' + i);
322
if (i < 26*2) return (char) ('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
constant 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, constant 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
constant 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(constant 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 = lstrtoic(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 = lstrtoposc(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 */
428
429