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