aboutsummaryrefslogtreecommitdiff
path: root/forwback.c
blob: 6f0b118c4b0c9465d34c1180d735feef2834df89 (plain) (blame)
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
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
/*
 * Copyright (C) 1984-2022  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.
 */


/*
 * Primitives for displaying the file on the screen,
 * scrolling either forward or backward.
 */

#include "less.h"
#include "position.h"

public int screen_trashed;
public int squished;
public int no_back_scroll = 0;
public int forw_prompt;
public int first_time = 1;

extern int sigs;
extern int top_scroll;
extern int quiet;
extern int sc_width, sc_height;
extern int hshift;
extern int auto_wrap;
extern int plusoption;
extern int forw_scroll;
extern int back_scroll;
extern int ignore_eoi;
extern int clear_bg;
extern int final_attr;
extern int header_lines;
extern int header_cols;
#if HILITE_SEARCH
extern int size_linebuf;
extern int hilite_search;
extern int status_col;
#endif
#if TAGS
extern char *tagoption;
#endif

/*
 * Sound the bell to indicate user is trying to move past end of file.
 */
	static void
eof_bell(VOID_PARAM)
{
#if HAVE_TIME
	static time_type last_eof_bell = 0;
	time_type now = get_time();
	if (now == last_eof_bell) /* max once per second */
		return;
	last_eof_bell = now;
#endif
	if (quiet == NOT_QUIET)
		bell();
	else
		vbell();
}

/*
 * Check to see if the end of file is currently displayed.
 */
	public int
eof_displayed(VOID_PARAM)
{
	POSITION pos;

	if (ignore_eoi)
		return (0);

	if (ch_length() == NULL_POSITION)
		/*
		 * If the file length is not known,
		 * we can't possibly be displaying EOF.
		 */
		return (0);

	/*
	 * If the bottom line is empty, we are at EOF.
	 * If the bottom line ends at the file length,
	 * we must be just at EOF.
	 */
	pos = position(BOTTOM_PLUS_ONE);
	return (pos == NULL_POSITION || pos == ch_length());
}

/*
 * Check to see if the entire file is currently displayed.
 */
	public int
entire_file_displayed(VOID_PARAM)
{
	POSITION pos;

	/* Make sure last line of file is displayed. */
	if (!eof_displayed())
		return (0);

	/* Make sure first line of file is displayed. */
	pos = position(0);
	return (pos == NULL_POSITION || pos == 0);
}

/*
 * If the screen is "squished", repaint it.
 * "Squished" means the first displayed line is not at the top
 * of the screen; this can happen when we display a short file
 * for the first time.
 */
	public void
squish_check(VOID_PARAM)
{
	if (!squished)
		return;
	squished = 0;
	repaint();
}

/*
 * Read the first pfx columns of the next line.
 * If skipeol==0 stop there, otherwise read and discard chars to end of line.
 */
	static POSITION
forw_line_pfx(pos, pfx, skipeol)
	POSITION pos;
	int pfx;
	int skipeol;
{
	int save_sc_width = sc_width;
	int save_auto_wrap = auto_wrap;
	int save_hshift = hshift;
	/* Set fake sc_width to force only pfx chars to be read. */
	sc_width = pfx + line_pfx_width();
	auto_wrap = 0;
	hshift = 0;
	pos = forw_line_seg(pos, skipeol, FALSE, FALSE);
	sc_width = save_sc_width;
	auto_wrap = save_auto_wrap;
	hshift = save_hshift;
	return pos;
}

/*
 * Set header text color.
 * Underline last line of headers, but not at beginning of file
 * (where there is no gap between the last header line and the next line).
 */
	static void
set_attr_header(ln)
	int ln;
{
	set_attr_line(AT_COLOR_HEADER);
	if (ln+1 == header_lines && position(0) != ch_zero())
		set_attr_line(AT_UNDERLINE);
}

/*
 * Display file headers, overlaying text already drawn
 * at top and left of screen.
 */
	public int
overlay_header(VOID_PARAM)
{
	POSITION pos = ch_zero(); /* header lines are at beginning of file */
	int ln;
	int moved = FALSE;

	if (header_lines > 0)
	{
		/* Draw header_lines lines from start of file at top of screen. */
		home();
		for (ln = 0; ln < header_lines; ++ln)
		{
			pos = forw_line(pos);
			set_attr_header(ln);
			clear_eol();
			put_line();
		}
		moved = TRUE;
	}
	if (header_cols > 0)
	{
		/* Draw header_cols columns at left of each line. */
		home();
		pos = ch_zero();
		for (ln = 0; ln < sc_height-1; ++ln)
		{
			if (ln >= header_lines) /* switch from header lines to normal lines */
				pos = position(ln);
			if (pos == NULL_POSITION)
				putchr('\n');
			else 
			{
				/* Need skipeol for all header lines except the last one. */
				pos = forw_line_pfx(pos, header_cols, ln+1 < header_lines);
				set_attr_header(ln);
				put_line();
			}
		}
		moved = TRUE;
	}
	if (moved)
		lower_left();
	return moved;
}

/*
 * Display n lines, scrolling forward, 
 * starting at position pos in the input file.
 * "force" means display the n lines even if we hit end of file.
 * "only_last" means display only the last screenful if n > screen size.
 * "nblank" is the number of blank lines to draw before the first
 *   real line.  If nblank > 0, the pos must be NULL_POSITION.
 *   The first real line after the blanks will start at ch_zero().
 */
	public void
forw(n, pos, force, only_last, nblank)
	int n;
	POSITION pos;
	int force;
	int only_last;
	int nblank;
{
	int nlines = 0;
	int do_repaint;

	squish_check();

	/*
	 * do_repaint tells us not to display anything till the end, 
	 * then just repaint the entire screen.
	 * We repaint if we are supposed to display only the last 
	 * screenful and the request is for more than a screenful.
	 * Also if the request exceeds the forward scroll limit
	 * (but not if the request is for exactly a screenful, since
	 * repainting itself involves scrolling forward a screenful).
	 */
	do_repaint = (only_last && n > sc_height-1) || 
		(forw_scroll >= 0 && n > forw_scroll && n != sc_height-1);

#if HILITE_SEARCH
	if (hilite_search == OPT_ONPLUS || is_filtering() || status_col) {
		prep_hilite(pos, pos + 4*size_linebuf, ignore_eoi ? 1 : -1);
		pos = next_unfiltered(pos);
	}
#endif

	if (!do_repaint)
	{
		if (top_scroll && n >= sc_height - 1 && pos != ch_length())
		{
			/*
			 * Start a new screen.
			 * {{ This is not really desirable if we happen
			 *    to hit eof in the middle of this screen,
			 *    but we don't yet know if that will happen. }}
			 */
			pos_clear();
			add_forw_pos(pos);
			force = 1;
			clear();
			home();
		}

		if (pos != position(BOTTOM_PLUS_ONE) || empty_screen())
		{
			/*
			 * This is not contiguous with what is
			 * currently displayed.  Clear the screen image 
			 * (position table) and start a new screen.
			 */
			pos_clear();
			add_forw_pos(pos);
			force = 1;
			if (top_scroll)
			{
				clear();
				home();
			} else if (!first_time && !is_filtering())
			{
				putstr("...skipping...\n");
			}
		}
	}

	while (--n >= 0)
	{
		/*
		 * Read the next line of input.
		 */
		if (nblank > 0)
		{
			/*
			 * Still drawing blanks; don't get a line 
			 * from the file yet.
			 * If this is the last blank line, get ready to
			 * read a line starting at ch_zero() next time.
			 */
			if (--nblank == 0)
				pos = ch_zero();
		} else
		{
			/* 
			 * Get the next line from the file.
			 */
			pos = forw_line(pos);
#if HILITE_SEARCH
			pos = next_unfiltered(pos);
#endif
			if (pos == NULL_POSITION)
			{
				/*
				 * End of file: stop here unless the top line 
				 * is still empty, or "force" is true.
				 * Even if force is true, stop when the last
				 * line in the file reaches the top of screen.
				 */
				if (!force && position(TOP) != NULL_POSITION)
					break;
				if (!empty_lines(0, 0) && 
				    !empty_lines(1, 1) &&
				     empty_lines(2, sc_height-1))
					break;
			}
		}
		/*
		 * Add the position of the next line to the position table.
		 * Display the current line on the screen.
		 */
		add_forw_pos(pos);
		nlines++;
		if (do_repaint)
			continue;
		/*
		 * If this is the first screen displayed and
		 * we hit an early EOF (i.e. before the requested
		 * number of lines), we "squish" the display down
		 * at the bottom of the screen.
		 * But don't do this if a + option or a -t option
		 * was given.  These options can cause us to
		 * start the display after the beginning of the file,
		 * and it is not appropriate to squish in that case.
		 */
		if (first_time && pos == NULL_POSITION && !top_scroll && 
#if TAGS
		    tagoption == NULL &&
#endif
		    !plusoption)
		{
			squished = 1;
			continue;
		}
		put_line();
#if 0
		/* {{ 
		 * Can't call clear_eol here.  The cursor might be at end of line
		 * on an ignaw terminal, so clear_eol would clear the last char
		 * of the current line instead of all of the next line.
		 * If we really need to do this on clear_bg terminals, we need
		 * to find a better way.
		 * }}
		 */
		if (clear_bg && apply_at_specials(final_attr) != AT_NORMAL)
		{
			/*
			 * Writing the last character on the last line
			 * of the display may have scrolled the screen.
			 * If we were in standout mode, clear_bg terminals 
			 * will fill the new line with the standout color.
			 * Now we're in normal mode again, so clear the line.
			 */
			clear_eol();
		}
#endif
		forw_prompt = 1;
	}

	if (header_lines > 0)
	{
		/*
		 * Don't allow ch_zero to appear on screen except at top of screen.
		 * Otherwise duplicate header lines may be displayed.
		 */
		if (onscreen(ch_zero()) > 0)
		{
			jump_loc(ch_zero(), 0); /* {{ yuck }} */
			return;
		}
	}
	if (nlines == 0 && !ignore_eoi)
		eof_bell();
	else if (do_repaint)
		repaint();
	else
	{
		overlay_header();
		/* lower_left(); {{ considered harmful? }} */
	}
	first_time = 0;
	(void) currline(BOTTOM);
}

/*
 * Display n lines, scrolling backward.
 */
	public void
back(n, pos, force, only_last)
	int n;
	POSITION pos;
	int force;
	int only_last;
{
	int nlines = 0;
	int do_repaint;

	squish_check();
	do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1) || header_lines > 0);
#if HILITE_SEARCH
	if (hilite_search == OPT_ONPLUS || is_filtering() || status_col) {
		prep_hilite((pos < 3*size_linebuf) ?  0 : pos - 3*size_linebuf, pos, -1);
	}
#endif
	while (--n >= 0)
	{
		/*
		 * Get the previous line of input.
		 */
#if HILITE_SEARCH
		pos = prev_unfiltered(pos);
#endif

		pos = back_line(pos);
		if (pos == NULL_POSITION)
		{
			/*
			 * Beginning of file: stop here unless "force" is true.
			 */
			if (!force)
				break;
		}
		/*
		 * Add the position of the previous line to the position table.
		 * Display the line on the screen.
		 */
		add_back_pos(pos);
		nlines++;
		if (!do_repaint)
		{
			home();
			add_line();
			put_line();
		}
	}
	if (nlines == 0)
		eof_bell();
	else if (do_repaint)
		repaint();
	else
	{
		overlay_header();
		lower_left();
	}
	(void) currline(BOTTOM);
}

/*
 * Display n more lines, forward.
 * Start just after the line currently displayed at the bottom of the screen.
 */
	public void
forward(n, force, only_last)
	int n;
	int force;
	int only_last;
{
	POSITION pos;

	if (get_quit_at_eof() && eof_displayed() && !(ch_getflags() & CH_HELPFILE))
	{
		/*
		 * If the -e flag is set and we're trying to go
		 * forward from end-of-file, go on to the next file.
		 */
		if (edit_next(1))
			quit(QUIT_OK);
		return;
	}

	pos = position(BOTTOM_PLUS_ONE);
	if (pos == NULL_POSITION && (!force || empty_lines(2, sc_height-1)))
	{
		if (ignore_eoi)
		{
			/*
			 * ignore_eoi is to support A_F_FOREVER.
			 * Back up until there is a line at the bottom
			 * of the screen.
			 */
			if (empty_screen())
				pos = ch_zero();
			else
			{
				do
				{
					back(1, position(TOP), 1, 0);
					pos = position(BOTTOM_PLUS_ONE);
				} while (pos == NULL_POSITION);
			}
		} else
		{
			eof_bell();
			return;
		}
	}
	forw(n, pos, force, only_last, 0);
}

/*
 * Display n more lines, backward.
 * Start just before the line currently displayed at the top of the screen.
 */
	public void
backward(n, force, only_last)
	int n;
	int force;
	int only_last;
{
	POSITION pos;

	pos = position(TOP);
	if (pos == NULL_POSITION && (!force || position(BOTTOM) == 0))
	{
		eof_bell();
		return;   
	}
	back(n, pos, force, only_last);
}

/*
 * Get the backwards scroll limit.
 * Must call this function instead of just using the value of
 * back_scroll, because the default case depends on sc_height and
 * top_scroll, as well as back_scroll.
 */
	public int
get_back_scroll(VOID_PARAM)
{
	if (no_back_scroll)
		return (0);
	if (back_scroll >= 0)
		return (back_scroll);
	if (top_scroll)
		return (sc_height - 2);
	return (10000); /* infinity */
}

/*
 * Will the entire file fit on one screen?
 */
	public int
get_one_screen(VOID_PARAM)
{
	int nlines;
	POSITION pos = ch_zero();

	for (nlines = 0;  nlines < sc_height;  nlines++)
	{
		pos = forw_line(pos);
		if (pos == NULL_POSITION) break;
	}
	return (nlines < sc_height);
}