1/*	$NetBSD: position.c,v 1.6 2023/10/06 07:33:49 simonb Exp $	*/
2
3/*
4 * Copyright (C) 1984-2023  Mark Nudelman
5 *
6 * You may distribute under the terms of either the GNU General Public
7 * License or the Less License, as specified in the README file.
8 *
9 * For more information, see the README file.
10 */
11
12
13/*
14 * Routines dealing with the "position" table.
15 * This is a table which tells the position (in the input file) of the
16 * first char on each currently displayed line.
17 *
18 * {{ The position table is scrolled by moving all the entries.
19 *    Would be better to have a circular table
20 *    and just change a couple of pointers. }}
21 */
22
23#include "less.h"
24#include "position.h"
25
26static POSITION *table = NULL;  /* The position table */
27static int table_size = 0;
28
29extern int sc_width, sc_height;
30extern int header_lines;
31
32/*
33 * Return the starting file position of a line displayed on the screen.
34 * The line may be specified as a line number relative to the top
35 * of the screen, but is usually one of these special cases:
36 *      the top (first) line on the screen
37 *      the second line on the screen
38 *      the bottom line on the screen
39 *      the line after the bottom line on the screen
40 */
41public POSITION position(int sindex)
42{
43	switch (sindex)
44	{
45	case BOTTOM:
46		sindex = sc_height - 2;
47		break;
48	case BOTTOM_PLUS_ONE:
49		sindex = sc_height - 1;
50		break;
51	case MIDDLE:
52		sindex = (sc_height - 1) / 2;
53		break;
54	}
55	return (table[sindex]);
56}
57
58/*
59 * Add a new file position to the bottom of the position table.
60 */
61public void add_forw_pos(POSITION pos)
62{
63	int i;
64
65	/*
66	 * Scroll the position table up.
67	 */
68	for (i = 1;  i < sc_height;  i++)
69		table[i-1] = table[i];
70	table[sc_height - 1] = pos;
71}
72
73/*
74 * Add a new file position to the top of the position table.
75 */
76public void add_back_pos(POSITION pos)
77{
78	int i;
79
80	/*
81	 * Scroll the position table down.
82	 */
83	for (i = sc_height - 1;  i > 0;  i--)
84		table[i] = table[i-1];
85	table[0] = pos;
86}
87
88/*
89 * Initialize the position table, done whenever we clear the screen.
90 */
91public void pos_clear(void)
92{
93	int i;
94
95	for (i = 0;  i < sc_height;  i++)
96		table[i] = NULL_POSITION;
97}
98
99/*
100 * Allocate or reallocate the position table.
101 */
102public void pos_init(void)
103{
104	struct scrpos scrpos;
105
106	if (sc_height <= table_size)
107		return;
108	/*
109	 * If we already have a table, remember the first line in it
110	 * before we free it, so we can copy that line to the new table.
111	 */
112	if (table != NULL)
113	{
114		get_scrpos(&scrpos, TOP);
115		free((char*)table);
116	} else
117		scrpos.pos = NULL_POSITION;
118	table = (POSITION *) ecalloc(sc_height, sizeof(POSITION));
119	table_size = sc_height;
120	pos_clear();
121	if (scrpos.pos != NULL_POSITION)
122		table[scrpos.ln-1] = scrpos.pos;
123}
124
125/*
126 * See if the byte at a specified position is currently on the screen.
127 * Check the position table to see if the position falls within its range.
128 * Return the position table entry if found, -1 if not.
129 */
130public int onscreen(POSITION pos)
131{
132	int i;
133
134	if (pos < table[0])
135		return (-1);
136	for (i = 1;  i < sc_height;  i++)
137		if (pos < table[i])
138			return (i-1);
139	return (-1);
140}
141
142/*
143 * See if the entire screen is empty.
144 */
145public int empty_screen(void)
146{
147	return (empty_lines(0, sc_height-1));
148}
149
150public int empty_lines(int s, int e)
151{
152	int i;
153
154	for (i = s;  i <= e;  i++)
155		if (table[i] != NULL_POSITION && table[i] != 0)
156			return (0);
157	return (1);
158}
159
160/*
161 * Get the current screen position.
162 * The screen position consists of both a file position and
163 * a screen line number where the file position is placed on the screen.
164 * Normally the screen line number is 0, but if we are positioned
165 * such that the top few lines are empty, we may have to set
166 * the screen line to a number > 0.
167 */
168public void get_scrpos(struct scrpos *scrpos, int where)
169{
170	int i;
171	int dir;
172	int last;
173
174	switch (where)
175	{
176	case TOP:
177		i = 0; dir = +1; last = sc_height-2;
178		break;
179	case BOTTOM: case BOTTOM_PLUS_ONE:
180		i = sc_height-2; dir = -1; last = 0;
181		break;
182	default:
183		i = where;
184		if (table[i] == NULL_POSITION) {
185			scrpos->pos = NULL_POSITION;
186			return;
187		}
188		/* Values of dir and last don't matter after this. */
189		break;
190	}
191
192	/*
193	 * Find the first line on the screen which has something on it,
194	 * and return the screen line number and the file position.
195	 */
196	for (;; i += dir)
197	{
198		if (table[i] != NULL_POSITION)
199		{
200			scrpos->ln = i+1;
201			scrpos->pos = table[i];
202			return;
203		}
204		if (i == last) break;
205	}
206	/*
207	 * The screen is empty.
208	 */
209	scrpos->pos = NULL_POSITION;
210}
211
212/*
213 * Adjust a screen line number to be a simple positive integer
214 * in the range { 0 .. sc_height-2 }.
215 * (The bottom line, sc_height-1, is reserved for prompts, etc.)
216 * The given "sline" may be in the range { 1 .. sc_height-1 }
217 * to refer to lines relative to the top of the screen (starting from 1),
218 * or it may be in { -1 .. -(sc_height-1) } to refer to lines
219 * relative to the bottom of the screen.
220 */
221public int sindex_from_sline(int sline)
222{
223	/*
224	 * Negative screen line number means
225	 * relative to the bottom of the screen.
226	 */
227	if (sline < 0)
228		sline += sc_height;
229	/*
230	 * Can't be less than 1 or greater than sc_height.
231	 */
232	if (sline <= 0)
233		sline = 1;
234	if (sline > sc_height)
235		sline = sc_height;
236	/*
237	 * Return zero-based line number, not one-based.
238	 */
239	return (sline-1);
240}
241