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