2 * Copyright (C) 1984-2024 Mark Nudelman
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.
7 * For more information, see the README file.
14 extern IFILE curr_ifile
;
16 extern int jump_sline
;
17 extern int perma_marks
;
20 * A mark is an ifile (input file) plus a position within the file.
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).
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 */
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.
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;
50 * Initialize a mark struct.
52 static void cmark(struct mark
*m
, IFILE ifile
, POSITION pos
, int ln
)
55 m
->m_scrpos
.pos
= pos
;
57 if (m
->m_filename
!= NULL
)
58 /* Normally should not happen but a corrupt lesshst file can do it. */
64 * Initialize the mark table to show no marks are set.
66 public void init_mark(void)
70 for (i
= 0; i
< NMARKS
; i
++)
74 case MOUSEMARK
: letter
= '#'; break;
75 case LASTMARK
: letter
= '\''; break;
76 default: letter
= (char) ((i
< 26) ? 'a'+i
: 'A'+i
-26); break;
78 marks
[i
].m_letter
= letter
;
79 cmark(&marks
[i
], NULL_IFILE
, NULL_POSITION
, -1);
84 * Set m_ifile and clear m_filename.
86 static void mark_set_ifile(struct mark
*m
, IFILE ifile
)
89 /* With m_ifile set, m_filename is no longer needed. */
95 * Populate the m_ifile member of a mark struct from m_filename.
97 static void mark_get_ifile(struct mark
*m
)
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
)));
105 * Return the user mark struct identified by a character.
107 static struct mark
* getumark(char c
)
110 if (c
>= 'a' && c
<= 'z')
111 return (&marks
[c
-'a']);
112 if (c
>= 'A' && c
<= 'Z')
113 return (&marks
[c
-'A'+26]);
115 return (&marks
[LASTMARK
]);
117 return (&marks
[MOUSEMARK
]);
118 parg
.p_char
= (char) c
;
119 error("Invalid mark letter %c", &parg
);
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 ^, $.
128 static struct mark
* getmark(char c
)
131 static struct mark sm
;
137 * Beginning of the current file.
140 cmark(m
, curr_ifile
, ch_zero(), 0);
144 * End of the current file.
148 error("Cannot seek to end of file", NULL_PARG
);
152 cmark(m
, curr_ifile
, ch_tell(), sc_height
);
156 * Current position in the current file.
159 get_scrpos(&m
->m_scrpos
, TOP
);
160 cmark(m
, curr_ifile
, m
->m_scrpos
.pos
, m
->m_scrpos
.ln
);
166 m
= &marks
[LASTMARK
];
170 * Must be a user-defined mark.
175 if (m
->m_scrpos
.pos
== NULL_POSITION
)
177 error("Mark not set", NULL_PARG
);
186 * Is a mark letter invalid?
188 public int badmark(char c
)
190 return (getmark(c
) == NULL
);
194 * Set a user-defined mark.
196 public void setmark(char c
, int where
)
199 struct scrpos scrpos
;
204 get_scrpos(&scrpos
, where
);
205 if (scrpos
.pos
== NULL_POSITION
)
210 cmark(m
, curr_ifile
, scrpos
.pos
, scrpos
.ln
);
215 * Clear a user-defined mark.
217 public void clrmark(char c
)
224 if (m
->m_scrpos
.pos
== NULL_POSITION
)
229 m
->m_scrpos
.pos
= NULL_POSITION
;
234 * Set lmark (the mark named by the apostrophe).
236 public void lastmark(void)
238 struct scrpos scrpos
;
240 if (ch_getflags() & CH_HELPFILE
)
242 get_scrpos(&scrpos
, TOP
);
243 if (scrpos
.pos
== NULL_POSITION
)
245 cmark(&marks
[LASTMARK
], curr_ifile
, scrpos
.pos
, scrpos
.ln
);
252 public void gomark(char c
)
255 struct scrpos scrpos
;
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()? }}
267 if (m
== &marks
[LASTMARK
] && m
->m_scrpos
.pos
== NULL_POSITION
)
268 cmark(m
, curr_ifile
, ch_zero(), jump_sline
);
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
)
277 * Not in the current file; edit the correct file.
279 if (edit_ifile(m
->m_ifile
))
283 jump_loc(scrpos
.pos
, scrpos
.ln
);
287 * Return the position associated with a given mark letter.
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.
293 public POSITION
markpos(char c
)
299 return (NULL_POSITION
);
301 if (m
->m_ifile
!= curr_ifile
)
303 error("Mark not in current file", NULL_PARG
);
304 return (NULL_POSITION
);
306 return (m
->m_scrpos
.pos
);
310 * Return the mark associated with a given position, if any.
312 public char posmark(POSITION pos
)
316 /* Only user marks */
317 for (i
= 0; i
< NUMARKS
; i
++)
319 if (marks
[i
].m_ifile
== curr_ifile
&& marks
[i
].m_scrpos
.pos
== pos
)
321 if (i
< 26) return (char) ('a' + i
);
322 if (i
< 26*2) return (char) ('A' + (i
- 26));
330 * Clear the marks associated with a specified ifile.
332 public void unmark(IFILE ifile
)
336 for (i
= 0; i
< NMARKS
; i
++)
337 if (marks
[i
].m_ifile
== ifile
)
338 marks
[i
].m_scrpos
.pos
= NULL_POSITION
;
342 * Check if any marks refer to a specified ifile vi m_filename
343 * rather than m_ifile.
345 public void mark_check_ifile(IFILE ifile
)
348 constant
char *filename
= get_real_filename(ifile
);
350 for (i
= 0; i
< NMARKS
; i
++)
352 struct mark
*m
= &marks
[i
];
353 char *mark_filename
= m
->m_filename
;
354 if (mark_filename
!= NULL
)
356 mark_filename
= lrealpath(mark_filename
);
357 if (strcmp(filename
, mark_filename
) == 0)
358 mark_set_ifile(m
, ifile
);
367 * Save marks to history file.
369 public void save_marks(FILE *fout
, constant
char *hdr
)
376 fprintf(fout
, "%s\n", hdr
);
377 for (i
= 0; i
< NMARKS
; i
++)
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
)
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
);
395 * Restore one mark from the history file.
397 public void restore_mark(constant
char *line
)
403 #define skip_whitespace while (*line == ' ') line++
407 m
= getumark(*line
++);
411 ln
= lstrtoic(line
, &line
, 10);
419 pos
= lstrtoposc(line
, &line
, 10);
423 cmark(m
, NULL_IFILE
, pos
, ln
);
424 m
->m_filename
= save(line
);
427 #endif /* CMD_HISTORY */