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 * Code to handle displaying line numbers. 13a5f0fb15SPaul Saab * 14a5f0fb15SPaul Saab * Finding the line number of a given file position is rather tricky. 15a5f0fb15SPaul Saab * We don't want to just start at the beginning of the file and 16a5f0fb15SPaul Saab * count newlines, because that is slow for large files (and also 17a5f0fb15SPaul Saab * wouldn't work if we couldn't get to the start of the file; e.g. 18a5f0fb15SPaul Saab * if input is a long pipe). 19a5f0fb15SPaul Saab * 20a5f0fb15SPaul Saab * So we use the function add_lnum to cache line numbers. 21a5f0fb15SPaul Saab * We try to be very clever and keep only the more interesting 22a5f0fb15SPaul Saab * line numbers when we run out of space in our table. A line 23a5f0fb15SPaul Saab * number is more interesting than another when it is far from 24a5f0fb15SPaul Saab * other line numbers. For example, we'd rather keep lines 25a5f0fb15SPaul Saab * 100,200,300 than 100,101,300. 200 is more interesting than 26a5f0fb15SPaul Saab * 101 because 101 can be derived very cheaply from 100, while 27a5f0fb15SPaul Saab * 200 is more expensive to derive from 100. 28a5f0fb15SPaul Saab * 29a5f0fb15SPaul Saab * The function currline() returns the line number of a given 30a5f0fb15SPaul Saab * position in the file. As a side effect, it calls add_lnum 31a5f0fb15SPaul Saab * to cache the line number. Therefore currline is occasionally 32a5f0fb15SPaul Saab * called to make sure we cache line numbers often enough. 33a5f0fb15SPaul Saab */ 34a5f0fb15SPaul Saab 35a5f0fb15SPaul Saab #include "less.h" 36a5f0fb15SPaul Saab 37a5f0fb15SPaul Saab /* 38a5f0fb15SPaul Saab * Structure to keep track of a line number and the associated file position. 39a5f0fb15SPaul Saab * A doubly-linked circular list of line numbers is kept ordered by line number. 40a5f0fb15SPaul Saab */ 41000ba3e8STim J. Robbins struct linenum_info 42a5f0fb15SPaul Saab { 43000ba3e8STim J. Robbins struct linenum_info *next; /* Link to next in the list */ 44000ba3e8STim J. Robbins struct linenum_info *prev; /* Line to previous in the list */ 45a5f0fb15SPaul Saab POSITION pos; /* File position */ 46a5f0fb15SPaul Saab POSITION gap; /* Gap between prev and next */ 47000ba3e8STim J. Robbins LINENUM line; /* Line number */ 48a5f0fb15SPaul Saab }; 49a5f0fb15SPaul Saab /* 50a5f0fb15SPaul Saab * "gap" needs some explanation: the gap of any particular line number 51a5f0fb15SPaul Saab * is the distance between the previous one and the next one in the list. 52a5f0fb15SPaul Saab * ("Distance" means difference in file position.) In other words, the 53a5f0fb15SPaul Saab * gap of a line number is the gap which would be introduced if this 54a5f0fb15SPaul Saab * line number were deleted. It is used to decide which one to replace 55a5f0fb15SPaul Saab * when we have a new one to insert and the table is full. 56a5f0fb15SPaul Saab */ 57a5f0fb15SPaul Saab 587374caaaSXin LI #define NPOOL 200 /* Size of line number pool */ 59a5f0fb15SPaul Saab 60a5f0fb15SPaul Saab #define LONGTIME (2) /* In seconds */ 61a5f0fb15SPaul Saab 62000ba3e8STim J. Robbins static struct linenum_info anchor; /* Anchor of the list */ 63000ba3e8STim J. Robbins static struct linenum_info *freelist; /* Anchor of the unused entries */ 64000ba3e8STim J. Robbins static struct linenum_info pool[NPOOL]; /* The pool itself */ 65000ba3e8STim J. Robbins static struct linenum_info *spare; /* We always keep one spare entry */ 66*c77c4889SXin LI public lbool scanning_eof = FALSE; 67a5f0fb15SPaul Saab 68a5f0fb15SPaul Saab extern int linenums; 69a5f0fb15SPaul Saab extern int sigs; 70a5f0fb15SPaul Saab extern int sc_height; 7195270f73SXin LI extern int header_lines; 7295270f73SXin LI extern int nonum_headers; 73a5f0fb15SPaul Saab 74a5f0fb15SPaul Saab /* 75a5f0fb15SPaul Saab * Initialize the line number structures. 76a5f0fb15SPaul Saab */ 77d713e089SXin LI public void clr_linenum(void) 78a5f0fb15SPaul Saab { 791ea31627SRobert Watson struct linenum_info *p; 80a5f0fb15SPaul Saab 81a5f0fb15SPaul Saab /* 82a5f0fb15SPaul Saab * Put all the entries on the free list. 83a5f0fb15SPaul Saab * Leave one for the "spare". 84a5f0fb15SPaul Saab */ 85a5f0fb15SPaul Saab for (p = pool; p < &pool[NPOOL-2]; p++) 86a5f0fb15SPaul Saab p->next = p+1; 87a5f0fb15SPaul Saab pool[NPOOL-2].next = NULL; 88a5f0fb15SPaul Saab freelist = pool; 89a5f0fb15SPaul Saab 90a5f0fb15SPaul Saab spare = &pool[NPOOL-1]; 91a5f0fb15SPaul Saab 92a5f0fb15SPaul Saab /* 93a5f0fb15SPaul Saab * Initialize the anchor. 94a5f0fb15SPaul Saab */ 95a5f0fb15SPaul Saab anchor.next = anchor.prev = &anchor; 96a5f0fb15SPaul Saab anchor.gap = 0; 97a5f0fb15SPaul Saab anchor.pos = (POSITION)0; 98a5f0fb15SPaul Saab anchor.line = 1; 99a5f0fb15SPaul Saab } 100a5f0fb15SPaul Saab 101a5f0fb15SPaul Saab /* 102a5f0fb15SPaul Saab * Calculate the gap for an entry. 103a5f0fb15SPaul Saab */ 104d713e089SXin LI static void calcgap(struct linenum_info *p) 105a5f0fb15SPaul Saab { 106a5f0fb15SPaul Saab /* 107a5f0fb15SPaul Saab * Don't bother to compute a gap for the anchor. 108a5f0fb15SPaul Saab * Also don't compute a gap for the last one in the list. 109a5f0fb15SPaul Saab * The gap for that last one should be considered infinite, 110a5f0fb15SPaul Saab * but we never look at it anyway. 111a5f0fb15SPaul Saab */ 112a5f0fb15SPaul Saab if (p == &anchor || p->next == &anchor) 113a5f0fb15SPaul Saab return; 114a5f0fb15SPaul Saab p->gap = p->next->pos - p->prev->pos; 115a5f0fb15SPaul Saab } 116a5f0fb15SPaul Saab 117a5f0fb15SPaul Saab /* 118a5f0fb15SPaul Saab * Add a new line number to the cache. 119a5f0fb15SPaul Saab * The specified position (pos) should be the file position of the 120a5f0fb15SPaul Saab * FIRST character in the specified line. 121a5f0fb15SPaul Saab */ 122d713e089SXin LI public void add_lnum(LINENUM linenum, POSITION pos) 123a5f0fb15SPaul Saab { 1241ea31627SRobert Watson struct linenum_info *p; 1251ea31627SRobert Watson struct linenum_info *new; 1261ea31627SRobert Watson struct linenum_info *nextp; 1271ea31627SRobert Watson struct linenum_info *prevp; 1281ea31627SRobert Watson POSITION mingap; 129a5f0fb15SPaul Saab 130a5f0fb15SPaul Saab /* 131a5f0fb15SPaul Saab * Find the proper place in the list for the new one. 132a5f0fb15SPaul Saab * The entries are sorted by position. 133a5f0fb15SPaul Saab */ 134a5f0fb15SPaul Saab for (p = anchor.next; p != &anchor && p->pos < pos; p = p->next) 135000ba3e8STim J. Robbins if (p->line == linenum) 136a5f0fb15SPaul Saab /* We already have this one. */ 137a5f0fb15SPaul Saab return; 138a5f0fb15SPaul Saab nextp = p; 139a5f0fb15SPaul Saab prevp = p->prev; 140a5f0fb15SPaul Saab 141a5f0fb15SPaul Saab if (freelist != NULL) 142a5f0fb15SPaul Saab { 143a5f0fb15SPaul Saab /* 144a5f0fb15SPaul Saab * We still have free (unused) entries. 145a5f0fb15SPaul Saab * Use one of them. 146a5f0fb15SPaul Saab */ 147a5f0fb15SPaul Saab new = freelist; 148a5f0fb15SPaul Saab freelist = freelist->next; 149a5f0fb15SPaul Saab } else 150a5f0fb15SPaul Saab { 151a5f0fb15SPaul Saab /* 152a5f0fb15SPaul Saab * No free entries. 153a5f0fb15SPaul Saab * Use the "spare" entry. 154a5f0fb15SPaul Saab */ 155a5f0fb15SPaul Saab new = spare; 156a5f0fb15SPaul Saab spare = NULL; 157a5f0fb15SPaul Saab } 158a5f0fb15SPaul Saab 159a5f0fb15SPaul Saab /* 160a5f0fb15SPaul Saab * Fill in the fields of the new entry, 161a5f0fb15SPaul Saab * and insert it into the proper place in the list. 162a5f0fb15SPaul Saab */ 163a5f0fb15SPaul Saab new->next = nextp; 164a5f0fb15SPaul Saab new->prev = prevp; 165a5f0fb15SPaul Saab new->pos = pos; 166000ba3e8STim J. Robbins new->line = linenum; 167a5f0fb15SPaul Saab 168a5f0fb15SPaul Saab nextp->prev = new; 169a5f0fb15SPaul Saab prevp->next = new; 170a5f0fb15SPaul Saab 171a5f0fb15SPaul Saab /* 172a5f0fb15SPaul Saab * Recalculate gaps for the new entry and the neighboring entries. 173a5f0fb15SPaul Saab */ 174a5f0fb15SPaul Saab calcgap(new); 175a5f0fb15SPaul Saab calcgap(nextp); 176a5f0fb15SPaul Saab calcgap(prevp); 177a5f0fb15SPaul Saab 178a5f0fb15SPaul Saab if (spare == NULL) 179a5f0fb15SPaul Saab { 180a5f0fb15SPaul Saab /* 181a5f0fb15SPaul Saab * We have used the spare entry. 182a5f0fb15SPaul Saab * Scan the list to find the one with the smallest 183a5f0fb15SPaul Saab * gap, take it out and make it the spare. 184a5f0fb15SPaul Saab * We should never remove the last one, so stop when 185a5f0fb15SPaul Saab * we get to p->next == &anchor. This also avoids 186a5f0fb15SPaul Saab * looking at the gap of the last one, which is 187a5f0fb15SPaul Saab * not computed by calcgap. 188a5f0fb15SPaul Saab */ 189a5f0fb15SPaul Saab mingap = anchor.next->gap; 190a5f0fb15SPaul Saab for (p = anchor.next; p->next != &anchor; p = p->next) 191a5f0fb15SPaul Saab { 192a5f0fb15SPaul Saab if (p->gap <= mingap) 193a5f0fb15SPaul Saab { 194a5f0fb15SPaul Saab spare = p; 195a5f0fb15SPaul Saab mingap = p->gap; 196a5f0fb15SPaul Saab } 197a5f0fb15SPaul Saab } 198a5f0fb15SPaul Saab spare->next->prev = spare->prev; 199a5f0fb15SPaul Saab spare->prev->next = spare->next; 200a5f0fb15SPaul Saab } 201a5f0fb15SPaul Saab } 202a5f0fb15SPaul Saab 203a5f0fb15SPaul Saab /* 204a5f0fb15SPaul Saab * If we get stuck in a long loop trying to figure out the 205a5f0fb15SPaul Saab * line number, print a message to tell the user what we're doing. 206a5f0fb15SPaul Saab */ 207d713e089SXin LI static void longloopmessage(void) 208a5f0fb15SPaul Saab { 209a5f0fb15SPaul Saab ierror("Calculating line numbers", NULL_PARG); 210a5f0fb15SPaul Saab } 211a5f0fb15SPaul Saab 212*c77c4889SXin LI struct delayed_msg 213*c77c4889SXin LI { 214*c77c4889SXin LI void (*message)(void); 215*c77c4889SXin LI int loopcount; 216a5f0fb15SPaul Saab #if HAVE_TIME 217*c77c4889SXin LI time_type startime; 218a5f0fb15SPaul Saab #endif 219*c77c4889SXin LI }; 220a5f0fb15SPaul Saab 221*c77c4889SXin LI static void start_delayed_msg(struct delayed_msg *dmsg, void (*message)(void)) 222*c77c4889SXin LI { 223*c77c4889SXin LI dmsg->loopcount = 0; 224*c77c4889SXin LI dmsg->message = message; 225*c77c4889SXin LI #if HAVE_TIME 226*c77c4889SXin LI dmsg->startime = get_time(); 227*c77c4889SXin LI #endif 228*c77c4889SXin LI } 229*c77c4889SXin LI 230*c77c4889SXin LI static void delayed_msg(struct delayed_msg *dmsg) 231a5f0fb15SPaul Saab { 232a5f0fb15SPaul Saab #if HAVE_TIME 233*c77c4889SXin LI if (dmsg->loopcount >= 0 && ++(dmsg->loopcount) > 100) 234a5f0fb15SPaul Saab { 235*c77c4889SXin LI dmsg->loopcount = 0; 236*c77c4889SXin LI if (get_time() >= dmsg->startime + LONGTIME) 237a5f0fb15SPaul Saab { 238*c77c4889SXin LI dmsg->message(); 239*c77c4889SXin LI dmsg->loopcount = -1; 240a5f0fb15SPaul Saab } 241a5f0fb15SPaul Saab } 242a5f0fb15SPaul Saab #else 243*c77c4889SXin LI if (dmsg->loopcount >= 0 && ++(dmsg->loopcount) > LONGLOOP) 244a5f0fb15SPaul Saab { 245*c77c4889SXin LI dmsg->message(); 246*c77c4889SXin LI dmsg->loopcount = -1; 247a5f0fb15SPaul Saab } 248a5f0fb15SPaul Saab #endif 249a5f0fb15SPaul Saab } 250a5f0fb15SPaul Saab 251a5f0fb15SPaul Saab /* 2527374caaaSXin LI * Turn off line numbers because the user has interrupted 2537374caaaSXin LI * a lengthy line number calculation. 2547374caaaSXin LI */ 255*c77c4889SXin LI static void abort_delayed_msg(struct delayed_msg *dmsg) 2567374caaaSXin LI { 257*c77c4889SXin LI if (dmsg->loopcount >= 0) 2582235c7feSXin LI return; 2597374caaaSXin LI if (linenums == OPT_ONPLUS) 2607374caaaSXin LI /* 2617374caaaSXin LI * We were displaying line numbers, so need to repaint. 2627374caaaSXin LI */ 263*c77c4889SXin LI screen_trashed(); 2647374caaaSXin LI linenums = 0; 2657374caaaSXin LI error("Line numbers turned off", NULL_PARG); 2667374caaaSXin LI } 2677374caaaSXin LI 2687374caaaSXin LI /* 269a5f0fb15SPaul Saab * Find the line number associated with a given position. 270a5f0fb15SPaul Saab * Return 0 if we can't figure it out. 271a5f0fb15SPaul Saab */ 272d713e089SXin LI public LINENUM find_linenum(POSITION pos) 273a5f0fb15SPaul Saab { 2741ea31627SRobert Watson struct linenum_info *p; 2751ea31627SRobert Watson LINENUM linenum; 276a5f0fb15SPaul Saab POSITION cpos; 277*c77c4889SXin LI struct delayed_msg dmsg; 278a5f0fb15SPaul Saab 279a5f0fb15SPaul Saab if (!linenums) 280a5f0fb15SPaul Saab /* 281a5f0fb15SPaul Saab * We're not using line numbers. 282a5f0fb15SPaul Saab */ 283a5f0fb15SPaul Saab return (0); 284a5f0fb15SPaul Saab if (pos == NULL_POSITION) 285a5f0fb15SPaul Saab /* 286a5f0fb15SPaul Saab * Caller doesn't know what he's talking about. 287a5f0fb15SPaul Saab */ 288a5f0fb15SPaul Saab return (0); 289a5f0fb15SPaul Saab if (pos <= ch_zero()) 290a5f0fb15SPaul Saab /* 291a5f0fb15SPaul Saab * Beginning of file is always line number 1. 292a5f0fb15SPaul Saab */ 293a5f0fb15SPaul Saab return (1); 294a5f0fb15SPaul Saab 295a5f0fb15SPaul Saab /* 296a5f0fb15SPaul Saab * Find the entry nearest to the position we want. 297a5f0fb15SPaul Saab */ 298a5f0fb15SPaul Saab for (p = anchor.next; p != &anchor && p->pos < pos; p = p->next) 299a5f0fb15SPaul Saab continue; 300a5f0fb15SPaul Saab if (p->pos == pos) 301a5f0fb15SPaul Saab /* Found it exactly. */ 302a5f0fb15SPaul Saab return (p->line); 303a5f0fb15SPaul Saab 304a5f0fb15SPaul Saab /* 305a5f0fb15SPaul Saab * This is the (possibly) time-consuming part. 306a5f0fb15SPaul Saab * We start at the line we just found and start 307a5f0fb15SPaul Saab * reading the file forward or backward till we 308a5f0fb15SPaul Saab * get to the place we want. 309a5f0fb15SPaul Saab * 310a5f0fb15SPaul Saab * First decide whether we should go forward from the 311a5f0fb15SPaul Saab * previous one or backwards from the next one. 312a5f0fb15SPaul Saab * The decision is based on which way involves 313a5f0fb15SPaul Saab * traversing fewer bytes in the file. 314a5f0fb15SPaul Saab */ 315*c77c4889SXin LI start_delayed_msg(&dmsg, longloopmessage); 316a5f0fb15SPaul Saab if (p == &anchor || pos - p->prev->pos < p->pos - pos) 317a5f0fb15SPaul Saab { 318a5f0fb15SPaul Saab /* 319a5f0fb15SPaul Saab * Go forward. 320a5f0fb15SPaul Saab */ 321a5f0fb15SPaul Saab p = p->prev; 322a5f0fb15SPaul Saab if (ch_seek(p->pos)) 323a5f0fb15SPaul Saab return (0); 324000ba3e8STim J. Robbins for (linenum = p->line, cpos = p->pos; cpos < pos; linenum++) 325a5f0fb15SPaul Saab { 326a5f0fb15SPaul Saab /* 327a5f0fb15SPaul Saab * Allow a signal to abort this loop. 328a5f0fb15SPaul Saab */ 329*c77c4889SXin LI cpos = forw_raw_line(cpos, NULL, NULL); 3307374caaaSXin LI if (ABORT_SIGS()) { 331*c77c4889SXin LI abort_delayed_msg(&dmsg); 3327374caaaSXin LI return (0); 3337374caaaSXin LI } 3347374caaaSXin LI if (cpos == NULL_POSITION) 335a5f0fb15SPaul Saab return (0); 336*c77c4889SXin LI delayed_msg(&dmsg); 337a5f0fb15SPaul Saab } 338a5f0fb15SPaul Saab /* 339a5f0fb15SPaul Saab * We might as well cache it. 340a5f0fb15SPaul Saab */ 341000ba3e8STim J. Robbins add_lnum(linenum, cpos); 342a5f0fb15SPaul Saab /* 343a5f0fb15SPaul Saab * If the given position is not at the start of a line, 344a5f0fb15SPaul Saab * make sure we return the correct line number. 345a5f0fb15SPaul Saab */ 346a5f0fb15SPaul Saab if (cpos > pos) 347000ba3e8STim J. Robbins linenum--; 348a5f0fb15SPaul Saab } else 349a5f0fb15SPaul Saab { 350a5f0fb15SPaul Saab /* 351a5f0fb15SPaul Saab * Go backward. 352a5f0fb15SPaul Saab */ 353a5f0fb15SPaul Saab if (ch_seek(p->pos)) 354a5f0fb15SPaul Saab return (0); 355000ba3e8STim J. Robbins for (linenum = p->line, cpos = p->pos; cpos > pos; linenum--) 356a5f0fb15SPaul Saab { 357a5f0fb15SPaul Saab /* 358a5f0fb15SPaul Saab * Allow a signal to abort this loop. 359a5f0fb15SPaul Saab */ 360*c77c4889SXin LI cpos = back_raw_line(cpos, NULL, NULL); 3617374caaaSXin LI if (ABORT_SIGS()) { 362*c77c4889SXin LI abort_delayed_msg(&dmsg); 3637374caaaSXin LI return (0); 3647374caaaSXin LI } 3657374caaaSXin LI if (cpos == NULL_POSITION) 366a5f0fb15SPaul Saab return (0); 367*c77c4889SXin LI delayed_msg(&dmsg); 368a5f0fb15SPaul Saab } 369a5f0fb15SPaul Saab /* 370a5f0fb15SPaul Saab * We might as well cache it. 371a5f0fb15SPaul Saab */ 372000ba3e8STim J. Robbins add_lnum(linenum, cpos); 373a5f0fb15SPaul Saab } 374000ba3e8STim J. Robbins return (linenum); 375a5f0fb15SPaul Saab } 376a5f0fb15SPaul Saab 377a5f0fb15SPaul Saab /* 378a5f0fb15SPaul Saab * Find the position of a given line number. 379a5f0fb15SPaul Saab * Return NULL_POSITION if we can't figure it out. 380a5f0fb15SPaul Saab */ 381d713e089SXin LI public POSITION find_pos(LINENUM linenum) 382a5f0fb15SPaul Saab { 3831ea31627SRobert Watson struct linenum_info *p; 384a5f0fb15SPaul Saab POSITION cpos; 385000ba3e8STim J. Robbins LINENUM clinenum; 386a5f0fb15SPaul Saab 387000ba3e8STim J. Robbins if (linenum <= 1) 388a5f0fb15SPaul Saab /* 389a5f0fb15SPaul Saab * Line number 1 is beginning of file. 390a5f0fb15SPaul Saab */ 391a5f0fb15SPaul Saab return (ch_zero()); 392a5f0fb15SPaul Saab 393a5f0fb15SPaul Saab /* 394a5f0fb15SPaul Saab * Find the entry nearest to the line number we want. 395a5f0fb15SPaul Saab */ 396000ba3e8STim J. Robbins for (p = anchor.next; p != &anchor && p->line < linenum; p = p->next) 397a5f0fb15SPaul Saab continue; 398000ba3e8STim J. Robbins if (p->line == linenum) 399a5f0fb15SPaul Saab /* Found it exactly. */ 400a5f0fb15SPaul Saab return (p->pos); 401a5f0fb15SPaul Saab 402000ba3e8STim J. Robbins if (p == &anchor || linenum - p->prev->line < p->line - linenum) 403a5f0fb15SPaul Saab { 404a5f0fb15SPaul Saab /* 405a5f0fb15SPaul Saab * Go forward. 406a5f0fb15SPaul Saab */ 407a5f0fb15SPaul Saab p = p->prev; 408a5f0fb15SPaul Saab if (ch_seek(p->pos)) 409a5f0fb15SPaul Saab return (NULL_POSITION); 410000ba3e8STim J. Robbins for (clinenum = p->line, cpos = p->pos; clinenum < linenum; clinenum++) 411a5f0fb15SPaul Saab { 412a5f0fb15SPaul Saab /* 413a5f0fb15SPaul Saab * Allow a signal to abort this loop. 414a5f0fb15SPaul Saab */ 415*c77c4889SXin LI cpos = forw_raw_line(cpos, NULL, NULL); 4167374caaaSXin LI if (ABORT_SIGS()) 4177374caaaSXin LI return (NULL_POSITION); 4187374caaaSXin LI if (cpos == NULL_POSITION) 419a5f0fb15SPaul Saab return (NULL_POSITION); 420a5f0fb15SPaul Saab } 421a5f0fb15SPaul Saab } else 422a5f0fb15SPaul Saab { 423a5f0fb15SPaul Saab /* 424a5f0fb15SPaul Saab * Go backward. 425a5f0fb15SPaul Saab */ 426a5f0fb15SPaul Saab if (ch_seek(p->pos)) 427a5f0fb15SPaul Saab return (NULL_POSITION); 428000ba3e8STim J. Robbins for (clinenum = p->line, cpos = p->pos; clinenum > linenum; clinenum--) 429a5f0fb15SPaul Saab { 430a5f0fb15SPaul Saab /* 431a5f0fb15SPaul Saab * Allow a signal to abort this loop. 432a5f0fb15SPaul Saab */ 433*c77c4889SXin LI cpos = back_raw_line(cpos, NULL, NULL); 4347374caaaSXin LI if (ABORT_SIGS()) 4357374caaaSXin LI return (NULL_POSITION); 4367374caaaSXin LI if (cpos == NULL_POSITION) 437a5f0fb15SPaul Saab return (NULL_POSITION); 438a5f0fb15SPaul Saab } 439a5f0fb15SPaul Saab } 440a5f0fb15SPaul Saab /* 441a5f0fb15SPaul Saab * We might as well cache it. 442a5f0fb15SPaul Saab */ 443000ba3e8STim J. Robbins add_lnum(clinenum, cpos); 444a5f0fb15SPaul Saab return (cpos); 445a5f0fb15SPaul Saab } 446a5f0fb15SPaul Saab 447a5f0fb15SPaul Saab /* 448a5f0fb15SPaul Saab * Return the line number of the "current" line. 449a5f0fb15SPaul Saab * The argument "where" tells which line is to be considered 450a5f0fb15SPaul Saab * the "current" line (e.g. TOP, BOTTOM, MIDDLE, etc). 451a5f0fb15SPaul Saab */ 452d713e089SXin LI public LINENUM currline(int where) 453a5f0fb15SPaul Saab { 454a5f0fb15SPaul Saab POSITION pos; 455a5f0fb15SPaul Saab POSITION len; 456000ba3e8STim J. Robbins LINENUM linenum; 457a5f0fb15SPaul Saab 458a5f0fb15SPaul Saab pos = position(where); 459a5f0fb15SPaul Saab len = ch_length(); 460a5f0fb15SPaul Saab while (pos == NULL_POSITION && where >= 0 && where < sc_height) 461a5f0fb15SPaul Saab pos = position(++where); 462a5f0fb15SPaul Saab if (pos == NULL_POSITION) 463a5f0fb15SPaul Saab pos = len; 464000ba3e8STim J. Robbins linenum = find_linenum(pos); 465a5f0fb15SPaul Saab if (pos == len) 466000ba3e8STim J. Robbins linenum--; 467000ba3e8STim J. Robbins return (linenum); 468a5f0fb15SPaul Saab } 46930a1828cSXin LI 470*c77c4889SXin LI static void detlenmessage(void) 471*c77c4889SXin LI { 472*c77c4889SXin LI ierror("Determining length of file", NULL_PARG); 473*c77c4889SXin LI } 474*c77c4889SXin LI 47530a1828cSXin LI /* 47630a1828cSXin LI * Scan entire file, counting line numbers. 47730a1828cSXin LI */ 478d713e089SXin LI public void scan_eof(void) 47930a1828cSXin LI { 480d713e089SXin LI POSITION pos = ch_zero(); 48130a1828cSXin LI LINENUM linenum = 0; 482*c77c4889SXin LI struct delayed_msg dmsg; 48330a1828cSXin LI 48430a1828cSXin LI if (ch_seek(0)) 48530a1828cSXin LI return; 486d713e089SXin LI /* 487d713e089SXin LI * scanning_eof prevents the "Waiting for data" message from 488d713e089SXin LI * overwriting "Determining length of file". 489d713e089SXin LI */ 490*c77c4889SXin LI start_delayed_msg(&dmsg, detlenmessage); 491d713e089SXin LI scanning_eof = TRUE; 49230a1828cSXin LI while (pos != NULL_POSITION) 49330a1828cSXin LI { 49430a1828cSXin LI /* For efficiency, only add one every 256 line numbers. */ 49530a1828cSXin LI if ((linenum++ % 256) == 0) 49630a1828cSXin LI add_lnum(linenum, pos); 497*c77c4889SXin LI pos = forw_raw_line(pos, NULL, NULL); 49830a1828cSXin LI if (ABORT_SIGS()) 499*c77c4889SXin LI { 500*c77c4889SXin LI abort_delayed_msg(&dmsg); 50130a1828cSXin LI break; 50230a1828cSXin LI } 503*c77c4889SXin LI delayed_msg(&dmsg); 504*c77c4889SXin LI } 505d713e089SXin LI scanning_eof = FALSE; 50630a1828cSXin LI } 50795270f73SXin LI 50895270f73SXin LI /* 50995270f73SXin LI * Return a line number adjusted for display 51095270f73SXin LI * (handles the --no-number-headers option). 51195270f73SXin LI */ 512d713e089SXin LI public LINENUM vlinenum(LINENUM linenum) 51395270f73SXin LI { 51495270f73SXin LI if (nonum_headers) 51595270f73SXin LI linenum = (linenum < header_lines) ? 0 : linenum - header_lines; 51695270f73SXin LI return linenum; 51795270f73SXin LI } 518