Home | History | Annotate | Line # | Download | only in dist
      1 /*	$NetBSD: jump.c,v 1.5 2023/10/06 05:49: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 which jump to a new location in the file.
     15  */
     16 
     17 #include "less.h"
     18 #include "position.h"
     19 
     20 extern int jump_sline;
     21 extern int squished;
     22 extern int screen_trashed;
     23 extern int sc_width, sc_height;
     24 extern int show_attn;
     25 extern int top_scroll;
     26 
     27 /*
     28  * Jump to the end of the file.
     29  */
     30 public void jump_forw(void)
     31 {
     32 	POSITION pos;
     33 	POSITION end_pos;
     34 
     35 	if (ch_end_seek())
     36 	{
     37 		error("Cannot seek to end of file", NULL_PARG);
     38 		return;
     39 	}
     40 	end_pos = ch_tell();
     41 	if (position(sc_height-1) == end_pos)
     42 	{
     43 		eof_bell();
     44 		return;
     45 	}
     46 	/*
     47 	 * Note; lastmark will be called later by jump_loc, but it fails
     48 	 * because the position table has been cleared by pos_clear below.
     49 	 * So call it here before calling pos_clear.
     50 	 */
     51 	lastmark();
     52 	/*
     53 	 * Position the last line in the file at the last screen line.
     54 	 * Go back one line from the end of the file
     55 	 * to get to the beginning of the last line.
     56 	 */
     57 	pos_clear();
     58 	pos = back_line(end_pos);
     59 	if (pos == NULL_POSITION)
     60 		jump_loc(ch_zero(), sc_height-1);
     61 	else
     62 	{
     63 		jump_loc(pos, sc_height-1);
     64 		if (position(sc_height-1) != end_pos)
     65 			repaint();
     66 	}
     67 }
     68 
     69 /*
     70  * Jump to the last buffered line in the file.
     71  */
     72 public void jump_forw_buffered(void)
     73 {
     74 	POSITION end;
     75 
     76 	if (ch_end_buffer_seek())
     77 	{
     78 		error("Cannot seek to end of buffers", NULL_PARG);
     79 		return;
     80 	}
     81 	end = ch_tell();
     82 	if (end != NULL_POSITION && end > 0)
     83 		jump_line_loc(end-1, sc_height-1);
     84 }
     85 
     86 /*
     87  * Jump to line n in the file.
     88  */
     89 public void jump_back(LINENUM linenum)
     90 {
     91 	POSITION pos;
     92 	PARG parg;
     93 
     94 	/*
     95 	 * Find the position of the specified line.
     96 	 * If we can seek there, just jump to it.
     97 	 * If we can't seek, but we're trying to go to line number 1,
     98 	 * use ch_beg_seek() to get as close as we can.
     99 	 */
    100 	pos = find_pos(linenum);
    101 	if (pos != NULL_POSITION && ch_seek(pos) == 0)
    102 	{
    103 		if (show_attn)
    104 			set_attnpos(pos);
    105 		jump_loc(pos, jump_sline);
    106 	} else if (linenum <= 1 && ch_beg_seek() == 0)
    107 	{
    108 		jump_loc(ch_tell(), jump_sline);
    109 		error("Cannot seek to beginning of file", NULL_PARG);
    110 	} else
    111 	{
    112 		parg.p_linenum = linenum;
    113 		error("Cannot seek to line number %n", &parg);
    114 	}
    115 }
    116 
    117 /*
    118  * Repaint the screen.
    119  */
    120 public void repaint(void)
    121 {
    122 	struct scrpos scrpos;
    123 	/*
    124 	 * Start at the line currently at the top of the screen
    125 	 * and redisplay the screen.
    126 	 */
    127 	get_scrpos(&scrpos, TOP);
    128 	pos_clear();
    129 	if (scrpos.pos == NULL_POSITION)
    130 		/* Screen hasn't been drawn yet. */
    131 		jump_loc(ch_zero(), 1);
    132 	else
    133 		jump_loc(scrpos.pos, scrpos.ln);
    134 }
    135 
    136 /*
    137  * Jump to a specified percentage into the file.
    138  */
    139 public void jump_percent(int percent, long fraction)
    140 {
    141 	POSITION pos, len;
    142 
    143 	/*
    144 	 * Determine the position in the file
    145 	 * (the specified percentage of the file's length).
    146 	 */
    147 	if ((len = ch_length()) == NULL_POSITION)
    148 	{
    149 		ierror("Determining length of file", NULL_PARG);
    150 		ch_end_seek();
    151 	}
    152 	if ((len = ch_length()) == NULL_POSITION)
    153 	{
    154 		error("Don't know length of file", NULL_PARG);
    155 		return;
    156 	}
    157 	pos = percent_pos(len, percent, fraction);
    158 	if (pos >= len)
    159 		pos = len-1;
    160 
    161 	jump_line_loc(pos, jump_sline);
    162 }
    163 
    164 /*
    165  * Jump to a specified position in the file.
    166  * Like jump_loc, but the position need not be
    167  * the first character in a line.
    168  */
    169 public void jump_line_loc(POSITION pos, int sline)
    170 {
    171 	int c;
    172 
    173 	if (ch_seek(pos) == 0)
    174 	{
    175 		/*
    176 		 * Back up to the beginning of the line.
    177 		 */
    178 		while ((c = ch_back_get()) != '\n' && c != EOI)
    179 			;
    180 		if (c == '\n')
    181 			(void) ch_forw_get();
    182 		pos = ch_tell();
    183 	}
    184 	if (show_attn)
    185 		set_attnpos(pos);
    186 	jump_loc(pos, sline);
    187 }
    188 
    189 /*
    190  * Jump to a specified position in the file.
    191  * The position must be the first character in a line.
    192  * Place the target line on a specified line on the screen.
    193  */
    194 public void jump_loc(POSITION pos, int sline)
    195 {
    196 	int nline;
    197 	int sindex;
    198 	POSITION tpos;
    199 	POSITION bpos;
    200 
    201 	/*
    202 	 * Normalize sline.
    203 	 */
    204 	sindex = sindex_from_sline(sline);
    205 
    206 	if ((nline = onscreen(pos)) >= 0)
    207 	{
    208 		/*
    209 		 * The line is currently displayed.
    210 		 * Just scroll there.
    211 		 */
    212 		nline -= sindex;
    213 		if (nline > 0)
    214 			forw(nline, position(BOTTOM_PLUS_ONE), 1, 0, 0);
    215 		else if (nline < 0)
    216 			back(-nline, position(TOP), 1, 0);
    217 #if HILITE_SEARCH
    218 		if (show_attn)
    219 			repaint_hilite(1);
    220 #endif
    221 		return;
    222 	}
    223 
    224 	/*
    225 	 * Line is not on screen.
    226 	 * Seek to the desired location.
    227 	 */
    228 	if (ch_seek(pos))
    229 	{
    230 		error("Cannot seek to that file position", NULL_PARG);
    231 		return;
    232 	}
    233 
    234 	/*
    235 	 * See if the desired line is before or after
    236 	 * the currently displayed screen.
    237 	 */
    238 	tpos = position(TOP);
    239 	bpos = position(BOTTOM_PLUS_ONE);
    240 	if (tpos == NULL_POSITION || pos >= tpos)
    241 	{
    242 		/*
    243 		 * The desired line is after the current screen.
    244 		 * Move back in the file far enough so that we can
    245 		 * call forw() and put the desired line at the
    246 		 * sline-th line on the screen.
    247 		 */
    248 		for (nline = 0;  nline < sindex;  nline++)
    249 		{
    250 			if (bpos != NULL_POSITION && pos <= bpos)
    251 			{
    252 				/*
    253 				 * Surprise!  The desired line is
    254 				 * close enough to the current screen
    255 				 * that we can just scroll there after all.
    256 				 */
    257 				forw(sc_height-sindex+nline-1, bpos, 1, 0, 0);
    258 #if HILITE_SEARCH
    259 				if (show_attn)
    260 					repaint_hilite(1);
    261 #endif
    262 				return;
    263 			}
    264 			pos = back_line(pos);
    265 			if (pos == NULL_POSITION)
    266 			{
    267 				/*
    268 				 * Oops.  Ran into the beginning of the file.
    269 				 * Exit the loop here and rely on forw()
    270 				 * below to draw the required number of
    271 				 * blank lines at the top of the screen.
    272 				 */
    273 				break;
    274 			}
    275 		}
    276 		lastmark();
    277 		squished = 0;
    278 		screen_trashed = 0;
    279 		forw(sc_height-1, pos, 1, 0, sindex-nline);
    280 	} else
    281 	{
    282 		/*
    283 		 * The desired line is before the current screen.
    284 		 * Move forward in the file far enough so that we
    285 		 * can call back() and put the desired line at the
    286 		 * sindex-th line on the screen.
    287 		 */
    288 		for (nline = sindex;  nline < sc_height - 1;  nline++)
    289 		{
    290 			pos = forw_line(pos);
    291 			if (pos == NULL_POSITION)
    292 			{
    293 				/*
    294 				 * Ran into end of file.
    295 				 * This shouldn't normally happen,
    296 				 * but may if there is some kind of read error.
    297 				 */
    298 				break;
    299 			}
    300 #if HILITE_SEARCH
    301 			pos = next_unfiltered(pos);
    302 #endif
    303 			if (pos >= tpos)
    304 			{
    305 				/*
    306 				 * Surprise!  The desired line is
    307 				 * close enough to the current screen
    308 				 * that we can just scroll there after all.
    309 				 */
    310 				back(nline+1, tpos, 1, 0);
    311 #if HILITE_SEARCH
    312 				if (show_attn)
    313 					repaint_hilite(1);
    314 #endif
    315 				return;
    316 			}
    317 		}
    318 		lastmark();
    319 		if (!top_scroll)
    320 			clear();
    321 		else
    322 			home();
    323 		screen_trashed = 0;
    324 		add_back_pos(pos);
    325 		back(sc_height-1, pos, 1, 0);
    326 	}
    327 }
    328