xref: /freebsd/contrib/less/position.c (revision c77c488926555ca344ae3a417544cf7a720e1de1)
1a5f0fb15SPaul Saab /*
2*c77c4889SXin LI  * Copyright (C) 1984-2024  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 /*
12a5f0fb15SPaul Saab  * Routines dealing with the "position" table.
13a5f0fb15SPaul Saab  * This is a table which tells the position (in the input file) of the
14a5f0fb15SPaul Saab  * first char on each currently displayed line.
15a5f0fb15SPaul Saab  *
16a5f0fb15SPaul Saab  * {{ The position table is scrolled by moving all the entries.
17a5f0fb15SPaul Saab  *    Would be better to have a circular table
18a5f0fb15SPaul Saab  *    and just change a couple of pointers. }}
19a5f0fb15SPaul Saab  */
20a5f0fb15SPaul Saab 
21a5f0fb15SPaul Saab #include "less.h"
22a5f0fb15SPaul Saab #include "position.h"
23a5f0fb15SPaul Saab 
24a5f0fb15SPaul Saab static POSITION *table = NULL;  /* The position table */
25b7780dbeSXin LI static int table_size = 0;
26a5f0fb15SPaul Saab 
27a5f0fb15SPaul Saab extern int sc_width, sc_height;
28*c77c4889SXin LI extern int hshift;
29a5f0fb15SPaul Saab 
30a5f0fb15SPaul Saab /*
31a5f0fb15SPaul Saab  * Return the starting file position of a line displayed on the screen.
32a5f0fb15SPaul Saab  * The line may be specified as a line number relative to the top
33a5f0fb15SPaul Saab  * of the screen, but is usually one of these special cases:
34a5f0fb15SPaul Saab  *      the top (first) line on the screen
35a5f0fb15SPaul Saab  *      the second line on the screen
36a5f0fb15SPaul Saab  *      the bottom line on the screen
37a5f0fb15SPaul Saab  *      the line after the bottom line on the screen
38a5f0fb15SPaul Saab  */
39d713e089SXin LI public POSITION position(int sindex)
40a5f0fb15SPaul Saab {
41b2ea2440SXin LI 	switch (sindex)
42a5f0fb15SPaul Saab 	{
43a5f0fb15SPaul Saab 	case BOTTOM:
44b2ea2440SXin LI 		sindex = sc_height - 2;
45a5f0fb15SPaul Saab 		break;
46a5f0fb15SPaul Saab 	case BOTTOM_PLUS_ONE:
47b2ea2440SXin LI 		sindex = sc_height - 1;
48a5f0fb15SPaul Saab 		break;
49a5f0fb15SPaul Saab 	case MIDDLE:
50b2ea2440SXin LI 		sindex = (sc_height - 1) / 2;
51b2ea2440SXin LI 		break;
52a5f0fb15SPaul Saab 	}
53b2ea2440SXin LI 	return (table[sindex]);
54a5f0fb15SPaul Saab }
55a5f0fb15SPaul Saab 
56a5f0fb15SPaul Saab /*
57a5f0fb15SPaul Saab  * Add a new file position to the bottom of the position table.
58a5f0fb15SPaul Saab  */
59d713e089SXin LI public void add_forw_pos(POSITION pos)
60a5f0fb15SPaul Saab {
611ea31627SRobert Watson 	int i;
62a5f0fb15SPaul Saab 
63a5f0fb15SPaul Saab 	/*
64a5f0fb15SPaul Saab 	 * Scroll the position table up.
65a5f0fb15SPaul Saab 	 */
66a5f0fb15SPaul Saab 	for (i = 1;  i < sc_height;  i++)
67a5f0fb15SPaul Saab 		table[i-1] = table[i];
68a5f0fb15SPaul Saab 	table[sc_height - 1] = pos;
69a5f0fb15SPaul Saab }
70a5f0fb15SPaul Saab 
71a5f0fb15SPaul Saab /*
72a5f0fb15SPaul Saab  * Add a new file position to the top of the position table.
73a5f0fb15SPaul Saab  */
74d713e089SXin LI public void add_back_pos(POSITION pos)
75a5f0fb15SPaul Saab {
761ea31627SRobert Watson 	int i;
77a5f0fb15SPaul Saab 
78a5f0fb15SPaul Saab 	/*
79a5f0fb15SPaul Saab 	 * Scroll the position table down.
80a5f0fb15SPaul Saab 	 */
81a5f0fb15SPaul Saab 	for (i = sc_height - 1;  i > 0;  i--)
82a5f0fb15SPaul Saab 		table[i] = table[i-1];
83a5f0fb15SPaul Saab 	table[0] = pos;
84a5f0fb15SPaul Saab }
85a5f0fb15SPaul Saab 
86a5f0fb15SPaul Saab /*
87a5f0fb15SPaul Saab  * Initialize the position table, done whenever we clear the screen.
88a5f0fb15SPaul Saab  */
89d713e089SXin LI public void pos_clear(void)
90a5f0fb15SPaul Saab {
911ea31627SRobert Watson 	int i;
92a5f0fb15SPaul Saab 
93a5f0fb15SPaul Saab 	for (i = 0;  i < sc_height;  i++)
94a5f0fb15SPaul Saab 		table[i] = NULL_POSITION;
95a5f0fb15SPaul Saab }
96a5f0fb15SPaul Saab 
97a5f0fb15SPaul Saab /*
98a5f0fb15SPaul Saab  * Allocate or reallocate the position table.
99a5f0fb15SPaul Saab  */
100d713e089SXin LI public void pos_init(void)
101a5f0fb15SPaul Saab {
102a5f0fb15SPaul Saab 	struct scrpos scrpos;
103a5f0fb15SPaul Saab 
104a5f0fb15SPaul Saab 	if (sc_height <= table_size)
105a5f0fb15SPaul Saab 		return;
106a5f0fb15SPaul Saab 	/*
107a5f0fb15SPaul Saab 	 * If we already have a table, remember the first line in it
108a5f0fb15SPaul Saab 	 * before we free it, so we can copy that line to the new table.
109a5f0fb15SPaul Saab 	 */
110a5f0fb15SPaul Saab 	if (table != NULL)
111a5f0fb15SPaul Saab 	{
112b2ea2440SXin LI 		get_scrpos(&scrpos, TOP);
113a5f0fb15SPaul Saab 		free((char*)table);
114a5f0fb15SPaul Saab 	} else
115a5f0fb15SPaul Saab 		scrpos.pos = NULL_POSITION;
116*c77c4889SXin LI 	table = (POSITION *) ecalloc((size_t) sc_height, sizeof(POSITION)); /*{{type-issue}}*/
117a5f0fb15SPaul Saab 	table_size = sc_height;
118a5f0fb15SPaul Saab 	pos_clear();
119a5f0fb15SPaul Saab 	if (scrpos.pos != NULL_POSITION)
120a5f0fb15SPaul Saab 		table[scrpos.ln-1] = scrpos.pos;
121a5f0fb15SPaul Saab }
122a5f0fb15SPaul Saab 
123a5f0fb15SPaul Saab /*
124a5f0fb15SPaul Saab  * See if the byte at a specified position is currently on the screen.
125a5f0fb15SPaul Saab  * Check the position table to see if the position falls within its range.
126a5f0fb15SPaul Saab  * Return the position table entry if found, -1 if not.
127a5f0fb15SPaul Saab  */
128d713e089SXin LI public int onscreen(POSITION pos)
129a5f0fb15SPaul Saab {
1301ea31627SRobert Watson 	int i;
131a5f0fb15SPaul Saab 
132a5f0fb15SPaul Saab 	if (pos < table[0])
133a5f0fb15SPaul Saab 		return (-1);
134a5f0fb15SPaul Saab 	for (i = 1;  i < sc_height;  i++)
135a5f0fb15SPaul Saab 		if (pos < table[i])
136a5f0fb15SPaul Saab 			return (i-1);
137a5f0fb15SPaul Saab 	return (-1);
138a5f0fb15SPaul Saab }
139a5f0fb15SPaul Saab 
140a5f0fb15SPaul Saab /*
141a5f0fb15SPaul Saab  * See if the entire screen is empty.
142a5f0fb15SPaul Saab  */
143d713e089SXin LI public int empty_screen(void)
144a5f0fb15SPaul Saab {
145a5f0fb15SPaul Saab 	return (empty_lines(0, sc_height-1));
146a5f0fb15SPaul Saab }
147a5f0fb15SPaul Saab 
148d713e089SXin LI public int empty_lines(int s, int e)
149a5f0fb15SPaul Saab {
1501ea31627SRobert Watson 	int i;
151a5f0fb15SPaul Saab 
152a5f0fb15SPaul Saab 	for (i = s;  i <= e;  i++)
1534cc5fc9aSXin LI 		if (table[i] != NULL_POSITION && table[i] != 0)
154a5f0fb15SPaul Saab 			return (0);
155a5f0fb15SPaul Saab 	return (1);
156a5f0fb15SPaul Saab }
157a5f0fb15SPaul Saab 
158a5f0fb15SPaul Saab /*
159a5f0fb15SPaul Saab  * Get the current screen position.
160a5f0fb15SPaul Saab  * The screen position consists of both a file position and
161a5f0fb15SPaul Saab  * a screen line number where the file position is placed on the screen.
162a5f0fb15SPaul Saab  * Normally the screen line number is 0, but if we are positioned
163a5f0fb15SPaul Saab  * such that the top few lines are empty, we may have to set
164a5f0fb15SPaul Saab  * the screen line to a number > 0.
165a5f0fb15SPaul Saab  */
166d713e089SXin LI public void get_scrpos(struct scrpos *scrpos, int where)
167a5f0fb15SPaul Saab {
1681ea31627SRobert Watson 	int i;
169b2ea2440SXin LI 	int dir;
170b2ea2440SXin LI 	int last;
171b2ea2440SXin LI 
172b2ea2440SXin LI 	switch (where)
173b2ea2440SXin LI 	{
174b7780dbeSXin LI 	case TOP:
175b7780dbeSXin LI 		i = 0; dir = +1; last = sc_height-2;
176b7780dbeSXin LI 		break;
177b7780dbeSXin LI 	case BOTTOM: case BOTTOM_PLUS_ONE:
178b7780dbeSXin LI 		i = sc_height-2; dir = -1; last = 0;
179b7780dbeSXin LI 		break;
180b7780dbeSXin LI 	default:
181b7780dbeSXin LI 		i = where;
182b7780dbeSXin LI 		if (table[i] == NULL_POSITION) {
183b7780dbeSXin LI 			scrpos->pos = NULL_POSITION;
184b7780dbeSXin LI 			return;
185b7780dbeSXin LI 		}
186b7780dbeSXin LI 		/* Values of dir and last don't matter after this. */
187b7780dbeSXin LI 		break;
188b2ea2440SXin LI 	}
189a5f0fb15SPaul Saab 
190a5f0fb15SPaul Saab 	/*
191a5f0fb15SPaul Saab 	 * Find the first line on the screen which has something on it,
192a5f0fb15SPaul Saab 	 * and return the screen line number and the file position.
193a5f0fb15SPaul Saab 	 */
194b2ea2440SXin LI 	for (;; i += dir)
195b2ea2440SXin LI 	{
196a5f0fb15SPaul Saab 		if (table[i] != NULL_POSITION)
197a5f0fb15SPaul Saab 		{
198a5f0fb15SPaul Saab 			scrpos->ln = i+1;
199a5f0fb15SPaul Saab 			scrpos->pos = table[i];
200a5f0fb15SPaul Saab 			return;
201a5f0fb15SPaul Saab 		}
202b2ea2440SXin LI 		if (i == last) break;
203b2ea2440SXin LI 	}
204a5f0fb15SPaul Saab 	/*
205a5f0fb15SPaul Saab 	 * The screen is empty.
206a5f0fb15SPaul Saab 	 */
207a5f0fb15SPaul Saab 	scrpos->pos = NULL_POSITION;
208a5f0fb15SPaul Saab }
209a5f0fb15SPaul Saab 
210a5f0fb15SPaul Saab /*
211a5f0fb15SPaul Saab  * Adjust a screen line number to be a simple positive integer
212a5f0fb15SPaul Saab  * in the range { 0 .. sc_height-2 }.
213a5f0fb15SPaul Saab  * (The bottom line, sc_height-1, is reserved for prompts, etc.)
214a5f0fb15SPaul Saab  * The given "sline" may be in the range { 1 .. sc_height-1 }
215a5f0fb15SPaul Saab  * to refer to lines relative to the top of the screen (starting from 1),
216a5f0fb15SPaul Saab  * or it may be in { -1 .. -(sc_height-1) } to refer to lines
217a5f0fb15SPaul Saab  * relative to the bottom of the screen.
218a5f0fb15SPaul Saab  */
219d713e089SXin LI public int sindex_from_sline(int sline)
220a5f0fb15SPaul Saab {
221a5f0fb15SPaul Saab 	/*
222a5f0fb15SPaul Saab 	 * Negative screen line number means
223a5f0fb15SPaul Saab 	 * relative to the bottom of the screen.
224a5f0fb15SPaul Saab 	 */
225a5f0fb15SPaul Saab 	if (sline < 0)
226a5f0fb15SPaul Saab 		sline += sc_height;
227a5f0fb15SPaul Saab 	/*
228b2ea2440SXin LI 	 * Can't be less than 1 or greater than sc_height.
229a5f0fb15SPaul Saab 	 */
230a5f0fb15SPaul Saab 	if (sline <= 0)
231a5f0fb15SPaul Saab 		sline = 1;
232b2ea2440SXin LI 	if (sline > sc_height)
233b2ea2440SXin LI 		sline = sc_height;
234a5f0fb15SPaul Saab 	/*
235a5f0fb15SPaul Saab 	 * Return zero-based line number, not one-based.
236a5f0fb15SPaul Saab 	 */
237a5f0fb15SPaul Saab 	return (sline-1);
238a5f0fb15SPaul Saab }
239*c77c4889SXin LI 
240*c77c4889SXin LI /*
241*c77c4889SXin LI  * Given a line that starts at linepos,
242*c77c4889SXin LI  * and the character at byte offset choff into that line,
243*c77c4889SXin LI  * return the number of characters (not bytes) between the
244*c77c4889SXin LI  * beginning of the line and the first byte of the choff character.
245*c77c4889SXin LI  */
246*c77c4889SXin LI static int pos_shift(POSITION linepos, size_t choff)
247*c77c4889SXin LI {
248*c77c4889SXin LI 	constant char *line;
249*c77c4889SXin LI 	size_t line_len;
250*c77c4889SXin LI 	POSITION pos;
251*c77c4889SXin LI 	int cvt_ops;
252*c77c4889SXin LI 	char *cline;
253*c77c4889SXin LI 
254*c77c4889SXin LI 	pos = forw_raw_line_len(linepos, choff, &line, &line_len);
255*c77c4889SXin LI 	if (pos == NULL_POSITION || line_len != choff)
256*c77c4889SXin LI 		return -1;
257*c77c4889SXin LI 	cvt_ops = get_cvt_ops(0); /* {{ Passing 0 ignores SRCH_NO_REGEX; does it matter? }} */
258*c77c4889SXin LI 	/* {{ It would be nice to be able to call cvt_text with dst=NULL, to avoid need to alloc a useless cline. }} */
259*c77c4889SXin LI 	cline = (char *) ecalloc(1, cvt_length(line_len, cvt_ops));
260*c77c4889SXin LI 	cvt_text(cline, line, NULL, &line_len, cvt_ops);
261*c77c4889SXin LI 	free(cline);
262*c77c4889SXin LI 	return (int) line_len;  /*{{type-issue}}*/
263*c77c4889SXin LI }
264*c77c4889SXin LI 
265*c77c4889SXin LI /*
266*c77c4889SXin LI  * Return the position of the first char of the line containing tpos.
267*c77c4889SXin LI  * Thus if tpos is the first char of its line, just return tpos.
268*c77c4889SXin LI  */
269*c77c4889SXin LI static POSITION beginning_of_line(POSITION tpos)
270*c77c4889SXin LI {
271*c77c4889SXin LI 	ch_seek(tpos);
272*c77c4889SXin LI 	while (ch_tell() != ch_zero())
273*c77c4889SXin LI 	{
274*c77c4889SXin LI 		int ch = ch_back_get();
275*c77c4889SXin LI 		if (ch == '\n')
276*c77c4889SXin LI 		{
277*c77c4889SXin LI 			(void) ch_forw_get();
278*c77c4889SXin LI 			break;
279*c77c4889SXin LI 		}
280*c77c4889SXin LI 	}
281*c77c4889SXin LI 	return ch_tell();
282*c77c4889SXin LI }
283*c77c4889SXin LI 
284*c77c4889SXin LI /*
285*c77c4889SXin LI  * When viewing long lines, it may be that the first char in the top screen
286*c77c4889SXin LI  * line is not the first char in its (file) line (the table is "beheaded").
287*c77c4889SXin LI  * This function sets that entry to the position of the first char in the line,
288*c77c4889SXin LI  * and sets hshift so that the first char in the first line is unchanged.
289*c77c4889SXin LI  */
290*c77c4889SXin LI public void pos_rehead(void)
291*c77c4889SXin LI {
292*c77c4889SXin LI 	POSITION linepos;
293*c77c4889SXin LI 	POSITION tpos = table[TOP];
294*c77c4889SXin LI 	if (tpos == NULL_POSITION)
295*c77c4889SXin LI 		return;
296*c77c4889SXin LI 	linepos = beginning_of_line(tpos);
297*c77c4889SXin LI 	if (linepos == tpos)
298*c77c4889SXin LI 		return;
299*c77c4889SXin LI 	table[TOP] = linepos;
300*c77c4889SXin LI 	hshift = pos_shift(linepos, (size_t) (tpos - linepos));
301*c77c4889SXin LI 	screen_trashed();
302*c77c4889SXin LI }
303