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