Home | History | Annotate | Line # | Download | only in more
prim.c revision 1.8
      1  1.8       agc /*	$NetBSD: prim.c,v 1.8 2003/08/07 09:28:01 agc Exp $	*/
      2  1.3     perry 
      3  1.1       cjs /*
      4  1.1       cjs  * Copyright (c) 1988, 1993
      5  1.1       cjs  *	The Regents of the University of California.  All rights reserved.
      6  1.1       cjs  *
      7  1.1       cjs  * Redistribution and use in source and binary forms, with or without
      8  1.1       cjs  * modification, are permitted provided that the following conditions
      9  1.1       cjs  * are met:
     10  1.1       cjs  * 1. Redistributions of source code must retain the above copyright
     11  1.1       cjs  *    notice, this list of conditions and the following disclaimer.
     12  1.1       cjs  * 2. Redistributions in binary form must reproduce the above copyright
     13  1.1       cjs  *    notice, this list of conditions and the following disclaimer in the
     14  1.1       cjs  *    documentation and/or other materials provided with the distribution.
     15  1.8       agc  * 3. Neither the name of the University nor the names of its contributors
     16  1.8       agc  *    may be used to endorse or promote products derived from this software
     17  1.8       agc  *    without specific prior written permission.
     18  1.8       agc  *
     19  1.8       agc  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     20  1.8       agc  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21  1.8       agc  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22  1.8       agc  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     23  1.8       agc  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24  1.8       agc  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     25  1.8       agc  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     26  1.8       agc  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     27  1.8       agc  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28  1.8       agc  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29  1.8       agc  * SUCH DAMAGE.
     30  1.8       agc  */
     31  1.8       agc 
     32  1.8       agc /*
     33  1.8       agc  * Copyright (c) 1988 Mark Nudleman
     34  1.8       agc  *
     35  1.8       agc  * Redistribution and use in source and binary forms, with or without
     36  1.8       agc  * modification, are permitted provided that the following conditions
     37  1.8       agc  * are met:
     38  1.8       agc  * 1. Redistributions of source code must retain the above copyright
     39  1.8       agc  *    notice, this list of conditions and the following disclaimer.
     40  1.8       agc  * 2. Redistributions in binary form must reproduce the above copyright
     41  1.8       agc  *    notice, this list of conditions and the following disclaimer in the
     42  1.8       agc  *    documentation and/or other materials provided with the distribution.
     43  1.1       cjs  * 3. All advertising materials mentioning features or use of this software
     44  1.1       cjs  *    must display the following acknowledgement:
     45  1.1       cjs  *	This product includes software developed by the University of
     46  1.1       cjs  *	California, Berkeley and its contributors.
     47  1.1       cjs  * 4. Neither the name of the University nor the names of its contributors
     48  1.1       cjs  *    may be used to endorse or promote products derived from this software
     49  1.1       cjs  *    without specific prior written permission.
     50  1.1       cjs  *
     51  1.1       cjs  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     52  1.1       cjs  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     53  1.1       cjs  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     54  1.1       cjs  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     55  1.1       cjs  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     56  1.1       cjs  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     57  1.1       cjs  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     58  1.1       cjs  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     59  1.1       cjs  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     60  1.1       cjs  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     61  1.1       cjs  * SUCH DAMAGE.
     62  1.1       cjs  */
     63  1.1       cjs 
     64  1.4  christos #include <sys/cdefs.h>
     65  1.1       cjs #ifndef lint
     66  1.4  christos #if 0
     67  1.1       cjs static char sccsid[] = "@(#)prim.c	8.1 (Berkeley) 6/6/93";
     68  1.4  christos #else
     69  1.8       agc __RCSID("$NetBSD: prim.c,v 1.8 2003/08/07 09:28:01 agc Exp $");
     70  1.4  christos #endif
     71  1.1       cjs #endif /* not lint */
     72  1.1       cjs 
     73  1.1       cjs /*
     74  1.1       cjs  * Primitives for displaying the file on the screen.
     75  1.1       cjs  */
     76  1.1       cjs 
     77  1.1       cjs #include <sys/types.h>
     78  1.1       cjs #include <stdio.h>
     79  1.1       cjs #include <ctype.h>
     80  1.5   thorpej #include <string.h>
     81  1.4  christos 
     82  1.4  christos #include "less.h"
     83  1.4  christos #include "extern.h"
     84  1.1       cjs 
     85  1.1       cjs int back_scroll = -1;
     86  1.1       cjs int hit_eof;		/* keeps track of how many times we hit end of file */
     87  1.1       cjs int screen_trashed;
     88  1.1       cjs 
     89  1.1       cjs static int squished;
     90  1.1       cjs 
     91  1.1       cjs 
     92  1.4  christos static int match(char *, char *);
     93  1.4  christos static int badmark __P((int));
     94  1.2       cjs 
     95  1.1       cjs /*
     96  1.1       cjs  * Check to see if the end of file is currently "displayed".
     97  1.1       cjs  */
     98  1.4  christos void
     99  1.1       cjs eof_check()
    100  1.4  christos /*###72 [cc] conflicting types for `eof_check'%%%*/
    101  1.1       cjs {
    102  1.1       cjs 	off_t pos;
    103  1.1       cjs 
    104  1.1       cjs 	if (sigs)
    105  1.1       cjs 		return;
    106  1.1       cjs 	/*
    107  1.1       cjs 	 * If the bottom line is empty, we are at EOF.
    108  1.1       cjs 	 * If the bottom line ends at the file length,
    109  1.1       cjs 	 * we must be just at EOF.
    110  1.1       cjs 	 */
    111  1.1       cjs 	pos = position(BOTTOM_PLUS_ONE);
    112  1.1       cjs 	if (pos == NULL_POSITION || pos == ch_length())
    113  1.1       cjs 		hit_eof++;
    114  1.1       cjs }
    115  1.1       cjs 
    116  1.1       cjs /*
    117  1.1       cjs  * If the screen is "squished", repaint it.
    118  1.1       cjs  * "Squished" means the first displayed line is not at the top
    119  1.1       cjs  * of the screen; this can happen when we display a short file
    120  1.1       cjs  * for the first time.
    121  1.1       cjs  */
    122  1.4  christos void
    123  1.1       cjs squish_check()
    124  1.4  christos /*###95 [cc] conflicting types for `squish_check'%%%*/
    125  1.1       cjs {
    126  1.1       cjs 	if (squished) {
    127  1.1       cjs 		squished = 0;
    128  1.1       cjs 		repaint();
    129  1.1       cjs 	}
    130  1.1       cjs }
    131  1.1       cjs 
    132  1.1       cjs /*
    133  1.1       cjs  * Display n lines, scrolling forward, starting at position pos in the
    134  1.1       cjs  * input file.  "only_last" means display only the last screenful if
    135  1.1       cjs  * n > screen size.
    136  1.1       cjs  */
    137  1.4  christos void
    138  1.1       cjs forw(n, pos, only_last)
    139  1.4  christos /*###109 [cc] conflicting types for `forw'%%%*/
    140  1.4  christos 	int n;
    141  1.1       cjs 	off_t pos;
    142  1.1       cjs 	int only_last;
    143  1.1       cjs {
    144  1.1       cjs 	static int first_time = 1;
    145  1.1       cjs 	int eof = 0, do_repaint;
    146  1.1       cjs 
    147  1.1       cjs 	squish_check();
    148  1.1       cjs 
    149  1.1       cjs 	/*
    150  1.1       cjs 	 * do_repaint tells us not to display anything till the end,
    151  1.1       cjs 	 * then just repaint the entire screen.
    152  1.1       cjs 	 */
    153  1.1       cjs 	do_repaint = (only_last && n > sc_height-1);
    154  1.1       cjs 
    155  1.1       cjs 	if (!do_repaint) {
    156  1.1       cjs 		if (top_scroll && n >= sc_height - 1) {
    157  1.1       cjs 			/*
    158  1.1       cjs 			 * Start a new screen.
    159  1.1       cjs 			 * {{ This is not really desirable if we happen
    160  1.1       cjs 			 *    to hit eof in the middle of this screen,
    161  1.1       cjs 			 *    but we don't yet know if that will happen. }}
    162  1.1       cjs 			 */
    163  1.1       cjs 			clear();
    164  1.1       cjs 			home();
    165  1.1       cjs 		} else {
    166  1.1       cjs 			lower_left();
    167  1.1       cjs 			clear_eol();
    168  1.1       cjs 		}
    169  1.1       cjs 
    170  1.1       cjs 		/*
    171  1.1       cjs 		 * This is not contiguous with what is currently displayed.
    172  1.1       cjs 		 * Clear the screen image (position table) and start a new
    173  1.1       cjs 		 * screen.
    174  1.1       cjs 		 */
    175  1.1       cjs 		if (pos != position(BOTTOM_PLUS_ONE)) {
    176  1.1       cjs 			pos_clear();
    177  1.1       cjs 			add_forw_pos(pos);
    178  1.1       cjs 			if (top_scroll) {
    179  1.1       cjs 				clear();
    180  1.1       cjs 				home();
    181  1.1       cjs 			} else if (!first_time)
    182  1.1       cjs 				putstr("...skipping...\n");
    183  1.1       cjs 		}
    184  1.1       cjs 	}
    185  1.1       cjs 
    186  1.1       cjs 	for (short_file = 0; --n >= 0;) {
    187  1.1       cjs 		/*
    188  1.1       cjs 		 * Read the next line of input.
    189  1.1       cjs 		 */
    190  1.1       cjs 		pos = forw_line(pos);
    191  1.1       cjs 		if (pos == NULL_POSITION) {
    192  1.1       cjs 			/*
    193  1.1       cjs 			 * end of file; copy the table if the file was
    194  1.1       cjs 			 * too small for an entire screen.
    195  1.1       cjs 			 */
    196  1.1       cjs 			eof = 1;
    197  1.1       cjs 			if (position(TOP) == NULL_POSITION) {
    198  1.1       cjs 				copytable();
    199  1.1       cjs 				if (!position(TOP))
    200  1.1       cjs 					short_file = 1;
    201  1.1       cjs 			}
    202  1.1       cjs 			break;
    203  1.1       cjs 		}
    204  1.1       cjs 		/*
    205  1.1       cjs 		 * Add the position of the next line to the position table.
    206  1.1       cjs 		 * Display the current line on the screen.
    207  1.1       cjs 		 */
    208  1.1       cjs 		add_forw_pos(pos);
    209  1.1       cjs 		if (do_repaint)
    210  1.1       cjs 			continue;
    211  1.1       cjs 		/*
    212  1.1       cjs 		 * If this is the first screen displayed and we hit an early
    213  1.1       cjs 		 * EOF (i.e. before the requested number of lines), we
    214  1.1       cjs 		 * "squish" the display down at the bottom of the screen.
    215  1.1       cjs 		 */
    216  1.2       cjs 		if (first_time && line == NULL && !top_scroll) {
    217  1.1       cjs 			squished = 1;
    218  1.1       cjs 			continue;
    219  1.1       cjs 		}
    220  1.1       cjs 		put_line();
    221  1.1       cjs 	}
    222  1.1       cjs 
    223  1.1       cjs 	if (eof && !sigs)
    224  1.1       cjs 		hit_eof++;
    225  1.1       cjs 	else
    226  1.1       cjs 		eof_check();
    227  1.1       cjs 	if (do_repaint)
    228  1.1       cjs 		repaint();
    229  1.1       cjs 	first_time = 0;
    230  1.1       cjs 	(void) currline(BOTTOM);
    231  1.1       cjs }
    232  1.1       cjs 
    233  1.1       cjs /*
    234  1.1       cjs  * Display n lines, scrolling backward.
    235  1.1       cjs  */
    236  1.4  christos void
    237  1.1       cjs back(n, pos, only_last)
    238  1.4  christos /*###207 [cc] conflicting types for `back'%%%*/
    239  1.4  christos 	int n;
    240  1.1       cjs 	off_t pos;
    241  1.1       cjs 	int only_last;
    242  1.1       cjs {
    243  1.1       cjs 	int do_repaint;
    244  1.1       cjs 
    245  1.1       cjs 	squish_check();
    246  1.1       cjs 	do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1));
    247  1.1       cjs 	hit_eof = 0;
    248  1.1       cjs 	while (--n >= 0)
    249  1.1       cjs 	{
    250  1.1       cjs 		/*
    251  1.1       cjs 		 * Get the previous line of input.
    252  1.1       cjs 		 */
    253  1.1       cjs 		pos = back_line(pos);
    254  1.1       cjs 		if (pos == NULL_POSITION)
    255  1.1       cjs 			break;
    256  1.1       cjs 		/*
    257  1.1       cjs 		 * Add the position of the previous line to the position table.
    258  1.1       cjs 		 * Display the line on the screen.
    259  1.1       cjs 		 */
    260  1.1       cjs 		add_back_pos(pos);
    261  1.1       cjs 		if (!do_repaint)
    262  1.1       cjs 		{
    263  1.1       cjs 			if (retain_below)
    264  1.1       cjs 			{
    265  1.1       cjs 				lower_left();
    266  1.1       cjs 				clear_eol();
    267  1.1       cjs 			}
    268  1.1       cjs 			home();
    269  1.1       cjs 			add_line();
    270  1.1       cjs 			put_line();
    271  1.1       cjs 		}
    272  1.1       cjs 	}
    273  1.1       cjs 
    274  1.1       cjs 	eof_check();
    275  1.1       cjs 	if (do_repaint)
    276  1.1       cjs 		repaint();
    277  1.1       cjs 	(void) currline(BOTTOM);
    278  1.1       cjs }
    279  1.1       cjs 
    280  1.1       cjs /*
    281  1.1       cjs  * Display n more lines, forward.
    282  1.1       cjs  * Start just after the line currently displayed at the bottom of the screen.
    283  1.1       cjs  */
    284  1.4  christos void
    285  1.1       cjs forward(n, only_last)
    286  1.4  christos /*###254 [cc] conflicting types for `forward'%%%*/
    287  1.1       cjs 	int n;
    288  1.1       cjs 	int only_last;
    289  1.1       cjs {
    290  1.1       cjs 	off_t pos;
    291  1.1       cjs 
    292  1.1       cjs 	if (hit_eof) {
    293  1.1       cjs 		/*
    294  1.1       cjs 		 * If we're trying to go forward from end-of-file,
    295  1.1       cjs 		 * go on to the next file.
    296  1.1       cjs 		 */
    297  1.1       cjs 		next_file(1);
    298  1.1       cjs 		return;
    299  1.1       cjs 	}
    300  1.1       cjs 
    301  1.1       cjs 	pos = position(BOTTOM_PLUS_ONE);
    302  1.1       cjs 	if (pos == NULL_POSITION)
    303  1.1       cjs 	{
    304  1.1       cjs 		hit_eof++;
    305  1.1       cjs 		return;
    306  1.1       cjs 	}
    307  1.1       cjs 	forw(n, pos, only_last);
    308  1.1       cjs }
    309  1.1       cjs 
    310  1.1       cjs /*
    311  1.1       cjs  * Display n more lines, backward.
    312  1.1       cjs  * Start just before the line currently displayed at the top of the screen.
    313  1.1       cjs  */
    314  1.4  christos void
    315  1.1       cjs backward(n, only_last)
    316  1.4  christos /*###283 [cc] conflicting types for `backward'%%%*/
    317  1.1       cjs 	int n;
    318  1.1       cjs 	int only_last;
    319  1.1       cjs {
    320  1.1       cjs 	off_t pos;
    321  1.1       cjs 
    322  1.1       cjs 	pos = position(TOP);
    323  1.1       cjs 	/*
    324  1.1       cjs 	 * This will almost never happen, because the top line is almost
    325  1.1       cjs 	 * never empty.
    326  1.1       cjs 	 */
    327  1.1       cjs 	if (pos == NULL_POSITION)
    328  1.1       cjs 		return;
    329  1.1       cjs 	back(n, pos, only_last);
    330  1.1       cjs }
    331  1.1       cjs 
    332  1.1       cjs /*
    333  1.1       cjs  * Repaint the screen, starting from a specified position.
    334  1.1       cjs  */
    335  1.4  christos void
    336  1.1       cjs prepaint(pos)
    337  1.4  christos /*###303 [cc] conflicting types for `prepaint'%%%*/
    338  1.1       cjs 	off_t pos;
    339  1.1       cjs {
    340  1.1       cjs 	hit_eof = 0;
    341  1.1       cjs 	forw(sc_height-1, pos, 0);
    342  1.1       cjs 	screen_trashed = 0;
    343  1.1       cjs }
    344  1.1       cjs 
    345  1.1       cjs /*
    346  1.1       cjs  * Repaint the screen.
    347  1.1       cjs  */
    348  1.4  christos void
    349  1.1       cjs repaint()
    350  1.4  christos /*###315 [cc] conflicting types for `repaint'%%%*/
    351  1.1       cjs {
    352  1.1       cjs 	/*
    353  1.1       cjs 	 * Start at the line currently at the top of the screen
    354  1.1       cjs 	 * and redisplay the screen.
    355  1.1       cjs 	 */
    356  1.1       cjs 	prepaint(position(TOP));
    357  1.1       cjs }
    358  1.1       cjs 
    359  1.1       cjs /*
    360  1.1       cjs  * Jump to the end of the file.
    361  1.1       cjs  * It is more convenient to paint the screen backward,
    362  1.1       cjs  * from the end of the file toward the beginning.
    363  1.1       cjs  */
    364  1.4  christos void
    365  1.1       cjs jump_forw()
    366  1.4  christos /*###330 [cc] conflicting types for `jump_forw'%%%*/
    367  1.1       cjs {
    368  1.1       cjs 	off_t pos;
    369  1.1       cjs 
    370  1.1       cjs 	if (ch_end_seek())
    371  1.1       cjs 	{
    372  1.1       cjs 		error("Cannot seek to end of file");
    373  1.1       cjs 		return;
    374  1.1       cjs 	}
    375  1.1       cjs 	lastmark();
    376  1.1       cjs 	pos = ch_tell();
    377  1.1       cjs 	clear();
    378  1.1       cjs 	pos_clear();
    379  1.1       cjs 	add_back_pos(pos);
    380  1.1       cjs 	back(sc_height - 1, pos, 0);
    381  1.1       cjs }
    382  1.1       cjs 
    383  1.1       cjs /*
    384  1.1       cjs  * Jump to line n in the file.
    385  1.1       cjs  */
    386  1.4  christos void
    387  1.1       cjs jump_back(n)
    388  1.4  christos /*###351 [cc] conflicting types for `jump_back'%%%*/
    389  1.4  christos 	int n;
    390  1.1       cjs {
    391  1.4  christos 	int c, nlines;
    392  1.1       cjs 
    393  1.1       cjs 	/*
    394  1.1       cjs 	 * This is done the slow way, by starting at the beginning
    395  1.1       cjs 	 * of the file and counting newlines.
    396  1.1       cjs 	 *
    397  1.1       cjs 	 * {{ Now that we have line numbering (in linenum.c),
    398  1.1       cjs 	 *    we could improve on this by starting at the
    399  1.1       cjs 	 *    nearest known line rather than at the beginning. }}
    400  1.1       cjs 	 */
    401  1.1       cjs 	if (ch_seek((off_t)0)) {
    402  1.1       cjs 		/*
    403  1.1       cjs 		 * Probably a pipe with beginning of file no longer buffered.
    404  1.1       cjs 		 * If he wants to go to line 1, we do the best we can,
    405  1.1       cjs 		 * by going to the first line which is still buffered.
    406  1.1       cjs 		 */
    407  1.1       cjs 		if (n <= 1 && ch_beg_seek() == 0)
    408  1.1       cjs 			jump_loc(ch_tell());
    409  1.1       cjs 		error("Cannot get to beginning of file");
    410  1.1       cjs 		return;
    411  1.1       cjs 	}
    412  1.1       cjs 
    413  1.1       cjs 	/*
    414  1.1       cjs 	 * Start counting lines.
    415  1.1       cjs 	 */
    416  1.1       cjs 	for (nlines = 1;  nlines < n;  nlines++)
    417  1.1       cjs 		while ((c = ch_forw_get()) != '\n')
    418  1.1       cjs 			if (c == EOI) {
    419  1.1       cjs 				char message[40];
    420  1.1       cjs 				(void)sprintf(message, "File has only %d lines",
    421  1.1       cjs 				    nlines - 1);
    422  1.1       cjs 				error(message);
    423  1.1       cjs 				return;
    424  1.1       cjs 			}
    425  1.1       cjs 	jump_loc(ch_tell());
    426  1.1       cjs }
    427  1.1       cjs 
    428  1.1       cjs /*
    429  1.1       cjs  * Jump to a specified percentage into the file.
    430  1.1       cjs  * This is a poor compensation for not being able to
    431  1.1       cjs  * quickly jump to a specific line number.
    432  1.1       cjs  */
    433  1.4  christos void
    434  1.1       cjs jump_percent(percent)
    435  1.4  christos /*###397 [cc] conflicting types for `jump_percent'%%%*/
    436  1.1       cjs 	int percent;
    437  1.1       cjs {
    438  1.4  christos 	off_t pos, len;
    439  1.4  christos 	int c;
    440  1.1       cjs 
    441  1.1       cjs 	/*
    442  1.1       cjs 	 * Determine the position in the file
    443  1.1       cjs 	 * (the specified percentage of the file's length).
    444  1.1       cjs 	 */
    445  1.1       cjs 	if ((len = ch_length()) == NULL_POSITION)
    446  1.1       cjs 	{
    447  1.1       cjs 		error("Don't know length of file");
    448  1.1       cjs 		return;
    449  1.1       cjs 	}
    450  1.1       cjs 	pos = (percent * len) / 100;
    451  1.1       cjs 
    452  1.1       cjs 	/*
    453  1.1       cjs 	 * Back up to the beginning of the line.
    454  1.1       cjs 	 */
    455  1.1       cjs 	if (ch_seek(pos) == 0)
    456  1.1       cjs 	{
    457  1.1       cjs 		while ((c = ch_back_get()) != '\n' && c != EOI)
    458  1.1       cjs 			;
    459  1.1       cjs 		if (c == '\n')
    460  1.1       cjs 			(void) ch_forw_get();
    461  1.1       cjs 		pos = ch_tell();
    462  1.1       cjs 	}
    463  1.1       cjs 	jump_loc(pos);
    464  1.1       cjs }
    465  1.1       cjs 
    466  1.1       cjs /*
    467  1.1       cjs  * Jump to a specified position in the file.
    468  1.1       cjs  */
    469  1.4  christos void
    470  1.1       cjs jump_loc(pos)
    471  1.4  christos /*###432 [cc] conflicting types for `jump_loc'%%%*/
    472  1.1       cjs 	off_t pos;
    473  1.1       cjs {
    474  1.4  christos 	int nline;
    475  1.1       cjs 	off_t tpos;
    476  1.1       cjs 
    477  1.1       cjs 	if ((nline = onscreen(pos)) >= 0) {
    478  1.1       cjs 		/*
    479  1.1       cjs 		 * The line is currently displayed.
    480  1.1       cjs 		 * Just scroll there.
    481  1.1       cjs 		 */
    482  1.1       cjs 		forw(nline, position(BOTTOM_PLUS_ONE), 0);
    483  1.1       cjs 		return;
    484  1.1       cjs 	}
    485  1.1       cjs 
    486  1.1       cjs 	/*
    487  1.1       cjs 	 * Line is not on screen.
    488  1.1       cjs 	 * Seek to the desired location.
    489  1.1       cjs 	 */
    490  1.1       cjs 	if (ch_seek(pos)) {
    491  1.1       cjs 		error("Cannot seek to that position");
    492  1.1       cjs 		return;
    493  1.1       cjs 	}
    494  1.1       cjs 
    495  1.1       cjs 	/*
    496  1.1       cjs 	 * See if the desired line is BEFORE the currently displayed screen.
    497  1.1       cjs 	 * If so, then move forward far enough so the line we're on will be
    498  1.1       cjs 	 * at the bottom of the screen, in order to be able to call back()
    499  1.1       cjs 	 * to make the screen scroll backwards & put the line at the top of
    500  1.1       cjs 	 * the screen.
    501  1.1       cjs 	 * {{ This seems inefficient, but it's not so bad,
    502  1.1       cjs 	 *    since we can never move forward more than a
    503  1.1       cjs 	 *    screenful before we stop to redraw the screen. }}
    504  1.1       cjs 	 */
    505  1.1       cjs 	tpos = position(TOP);
    506  1.1       cjs 	if (tpos != NULL_POSITION && pos < tpos) {
    507  1.1       cjs 		off_t npos = pos;
    508  1.1       cjs 		/*
    509  1.1       cjs 		 * Note that we can't forw_line() past tpos here,
    510  1.1       cjs 		 * so there should be no EOI at this stage.
    511  1.1       cjs 		 */
    512  1.1       cjs 		for (nline = 0;  npos < tpos && nline < sc_height - 1;  nline++)
    513  1.1       cjs 			npos = forw_line(npos);
    514  1.1       cjs 
    515  1.1       cjs 		if (npos < tpos) {
    516  1.1       cjs 			/*
    517  1.1       cjs 			 * More than a screenful back.
    518  1.1       cjs 			 */
    519  1.1       cjs 			lastmark();
    520  1.1       cjs 			clear();
    521  1.1       cjs 			pos_clear();
    522  1.1       cjs 			add_back_pos(npos);
    523  1.1       cjs 		}
    524  1.1       cjs 
    525  1.1       cjs 		/*
    526  1.1       cjs 		 * Note that back() will repaint() if nline > back_scroll.
    527  1.1       cjs 		 */
    528  1.1       cjs 		back(nline, npos, 0);
    529  1.1       cjs 		return;
    530  1.1       cjs 	}
    531  1.1       cjs 	/*
    532  1.1       cjs 	 * Remember where we were; clear and paint the screen.
    533  1.1       cjs 	 */
    534  1.1       cjs 	lastmark();
    535  1.1       cjs 	prepaint(pos);
    536  1.1       cjs }
    537  1.1       cjs 
    538  1.1       cjs /*
    539  1.1       cjs  * The table of marks.
    540  1.1       cjs  * A mark is simply a position in the file.
    541  1.1       cjs  */
    542  1.1       cjs #define	NMARKS		(27)		/* 26 for a-z plus one for quote */
    543  1.1       cjs #define	LASTMARK	(NMARKS-1)	/* For quote */
    544  1.1       cjs static off_t marks[NMARKS];
    545  1.1       cjs 
    546  1.1       cjs /*
    547  1.1       cjs  * Initialize the mark table to show no marks are set.
    548  1.1       cjs  */
    549  1.4  christos void
    550  1.1       cjs init_mark()
    551  1.4  christos /*###511 [cc] conflicting types for `init_mark'%%%*/
    552  1.1       cjs {
    553  1.1       cjs 	int i;
    554  1.1       cjs 
    555  1.1       cjs 	for (i = 0;  i < NMARKS;  i++)
    556  1.1       cjs 		marks[i] = NULL_POSITION;
    557  1.1       cjs }
    558  1.1       cjs 
    559  1.1       cjs /*
    560  1.1       cjs  * See if a mark letter is valid (between a and z).
    561  1.1       cjs  */
    562  1.4  christos static int
    563  1.1       cjs badmark(c)
    564  1.1       cjs 	int c;
    565  1.1       cjs {
    566  1.1       cjs 	if (c < 'a' || c > 'z')
    567  1.1       cjs 	{
    568  1.1       cjs 		error("Choose a letter between 'a' and 'z'");
    569  1.1       cjs 		return (1);
    570  1.1       cjs 	}
    571  1.1       cjs 	return (0);
    572  1.1       cjs }
    573  1.1       cjs 
    574  1.1       cjs /*
    575  1.1       cjs  * Set a mark.
    576  1.1       cjs  */
    577  1.4  christos void
    578  1.1       cjs setmark(c)
    579  1.4  christos /*###538 [cc] conflicting types for `setmark'%%%*/
    580  1.1       cjs 	int c;
    581  1.1       cjs {
    582  1.1       cjs 	if (badmark(c))
    583  1.1       cjs 		return;
    584  1.1       cjs 	marks[c-'a'] = position(TOP);
    585  1.1       cjs }
    586  1.1       cjs 
    587  1.4  christos void
    588  1.1       cjs lastmark()
    589  1.4  christos /*###547 [cc] conflicting types for `lastmark'%%%*/
    590  1.1       cjs {
    591  1.1       cjs 	marks[LASTMARK] = position(TOP);
    592  1.1       cjs }
    593  1.1       cjs 
    594  1.1       cjs /*
    595  1.1       cjs  * Go to a previously set mark.
    596  1.1       cjs  */
    597  1.4  christos void
    598  1.1       cjs gomark(c)
    599  1.4  christos /*###556 [cc] conflicting types for `gomark'%%%*/
    600  1.1       cjs 	int c;
    601  1.1       cjs {
    602  1.1       cjs 	off_t pos;
    603  1.1       cjs 
    604  1.1       cjs 	if (c == '\'') {
    605  1.1       cjs 		pos = marks[LASTMARK];
    606  1.1       cjs 		if (pos == NULL_POSITION)
    607  1.1       cjs 			pos = 0;
    608  1.1       cjs 	}
    609  1.1       cjs 	else {
    610  1.1       cjs 		if (badmark(c))
    611  1.1       cjs 			return;
    612  1.1       cjs 		pos = marks[c-'a'];
    613  1.1       cjs 		if (pos == NULL_POSITION) {
    614  1.1       cjs 			error("mark not set");
    615  1.1       cjs 			return;
    616  1.1       cjs 		}
    617  1.1       cjs 	}
    618  1.1       cjs 	jump_loc(pos);
    619  1.1       cjs }
    620  1.1       cjs 
    621  1.1       cjs /*
    622  1.1       cjs  * Get the backwards scroll limit.
    623  1.1       cjs  * Must call this function instead of just using the value of
    624  1.1       cjs  * back_scroll, because the default case depends on sc_height and
    625  1.1       cjs  * top_scroll, as well as back_scroll.
    626  1.1       cjs  */
    627  1.4  christos int
    628  1.1       cjs get_back_scroll()
    629  1.1       cjs {
    630  1.1       cjs 	if (back_scroll >= 0)
    631  1.1       cjs 		return (back_scroll);
    632  1.1       cjs 	if (top_scroll)
    633  1.1       cjs 		return (sc_height - 2);
    634  1.1       cjs 	return (sc_height - 1);
    635  1.1       cjs }
    636  1.1       cjs 
    637  1.1       cjs /*
    638  1.1       cjs  * Search for the n-th occurence of a specified pattern,
    639  1.1       cjs  * either forward or backward.
    640  1.1       cjs  */
    641  1.4  christos int
    642  1.1       cjs search(search_forward, pattern, n, wantmatch)
    643  1.4  christos 	int search_forward;
    644  1.4  christos 	char *pattern;
    645  1.4  christos 	int n;
    646  1.1       cjs 	int wantmatch;
    647  1.1       cjs {
    648  1.1       cjs 	off_t pos, linepos;
    649  1.4  christos 	char *p;
    650  1.4  christos 	char *q;
    651  1.1       cjs 	int linenum;
    652  1.1       cjs 	int linematch;
    653  1.1       cjs #ifdef RECOMP
    654  1.1       cjs 	char *re_comp();
    655  1.1       cjs 	char *errmsg;
    656  1.1       cjs #else
    657  1.1       cjs #ifdef REGCMP
    658  1.1       cjs 	char *regcmp();
    659  1.1       cjs 	static char *cpattern = NULL;
    660  1.1       cjs #else
    661  1.1       cjs 	static char lpbuf[100];
    662  1.1       cjs 	static char *last_pattern = NULL;
    663  1.1       cjs #endif
    664  1.1       cjs #endif
    665  1.1       cjs 
    666  1.1       cjs 	/*
    667  1.1       cjs 	 * For a caseless search, convert any uppercase in the pattern to
    668  1.1       cjs 	 * lowercase.
    669  1.1       cjs 	 */
    670  1.1       cjs 	if (caseless && pattern != NULL)
    671  1.1       cjs 		for (p = pattern;  *p;  p++)
    672  1.1       cjs 			if (isupper(*p))
    673  1.1       cjs 				*p = tolower(*p);
    674  1.1       cjs #ifdef RECOMP
    675  1.1       cjs 
    676  1.1       cjs 	/*
    677  1.1       cjs 	 * (re_comp handles a null pattern internally,
    678  1.1       cjs 	 *  so there is no need to check for a null pattern here.)
    679  1.1       cjs 	 */
    680  1.1       cjs 	if ((errmsg = re_comp(pattern)) != NULL)
    681  1.1       cjs 	{
    682  1.1       cjs 		error(errmsg);
    683  1.1       cjs 		return(0);
    684  1.1       cjs 	}
    685  1.1       cjs #else
    686  1.1       cjs #ifdef REGCMP
    687  1.1       cjs 	if (pattern == NULL || *pattern == '\0')
    688  1.1       cjs 	{
    689  1.1       cjs 		/*
    690  1.1       cjs 		 * A null pattern means use the previous pattern.
    691  1.1       cjs 		 * The compiled previous pattern is in cpattern, so just use it.
    692  1.1       cjs 		 */
    693  1.1       cjs 		if (cpattern == NULL)
    694  1.1       cjs 		{
    695  1.1       cjs 			error("No previous regular expression");
    696  1.1       cjs 			return(0);
    697  1.1       cjs 		}
    698  1.1       cjs 	} else
    699  1.1       cjs 	{
    700  1.1       cjs 		/*
    701  1.1       cjs 		 * Otherwise compile the given pattern.
    702  1.1       cjs 		 */
    703  1.1       cjs 		char *s;
    704  1.1       cjs 		if ((s = regcmp(pattern, 0)) == NULL)
    705  1.1       cjs 		{
    706  1.1       cjs 			error("Invalid pattern");
    707  1.1       cjs 			return(0);
    708  1.1       cjs 		}
    709  1.1       cjs 		if (cpattern != NULL)
    710  1.1       cjs 			free(cpattern);
    711  1.1       cjs 		cpattern = s;
    712  1.1       cjs 	}
    713  1.1       cjs #else
    714  1.1       cjs 	if (pattern == NULL || *pattern == '\0')
    715  1.1       cjs 	{
    716  1.1       cjs 		/*
    717  1.1       cjs 		 * Null pattern means use the previous pattern.
    718  1.1       cjs 		 */
    719  1.1       cjs 		if (last_pattern == NULL)
    720  1.1       cjs 		{
    721  1.1       cjs 			error("No previous regular expression");
    722  1.1       cjs 			return(0);
    723  1.1       cjs 		}
    724  1.1       cjs 		pattern = last_pattern;
    725  1.1       cjs 	} else
    726  1.1       cjs 	{
    727  1.7    itojun 		(void)strlcpy(lpbuf, pattern, sizeof(lpbuf));
    728  1.1       cjs 		last_pattern = lpbuf;
    729  1.1       cjs 	}
    730  1.1       cjs #endif
    731  1.1       cjs #endif
    732  1.1       cjs 
    733  1.1       cjs 	/*
    734  1.1       cjs 	 * Figure out where to start the search.
    735  1.1       cjs 	 */
    736  1.1       cjs 
    737  1.1       cjs 	if (position(TOP) == NULL_POSITION) {
    738  1.1       cjs 		/*
    739  1.1       cjs 		 * Nothing is currently displayed.  Start at the beginning
    740  1.1       cjs 		 * of the file.  (This case is mainly for searches from the
    741  1.1       cjs 		 * command line.
    742  1.1       cjs 		 */
    743  1.1       cjs 		pos = (off_t)0;
    744  1.1       cjs 	} else if (!search_forward) {
    745  1.1       cjs 		/*
    746  1.1       cjs 		 * Backward search: start just before the top line
    747  1.1       cjs 		 * displayed on the screen.
    748  1.1       cjs 		 */
    749  1.1       cjs 		pos = position(TOP);
    750  1.1       cjs 	} else {
    751  1.1       cjs 		/*
    752  1.1       cjs 		 * Start at the second screen line displayed on the screen.
    753  1.1       cjs 		 */
    754  1.1       cjs 		pos = position(TOP_PLUS_ONE);
    755  1.1       cjs 	}
    756  1.1       cjs 
    757  1.1       cjs 	if (pos == NULL_POSITION)
    758  1.1       cjs 	{
    759  1.1       cjs 		/*
    760  1.1       cjs 		 * Can't find anyplace to start searching from.
    761  1.1       cjs 		 */
    762  1.1       cjs 		error("Nothing to search");
    763  1.1       cjs 		return(0);
    764  1.1       cjs 	}
    765  1.1       cjs 
    766  1.1       cjs 	linenum = find_linenum(pos);
    767  1.1       cjs 	for (;;)
    768  1.1       cjs 	{
    769  1.1       cjs 		/*
    770  1.1       cjs 		 * Get lines until we find a matching one or
    771  1.1       cjs 		 * until we hit end-of-file (or beginning-of-file
    772  1.1       cjs 		 * if we're going backwards).
    773  1.1       cjs 		 */
    774  1.1       cjs 		if (sigs)
    775  1.1       cjs 			/*
    776  1.1       cjs 			 * A signal aborts the search.
    777  1.1       cjs 			 */
    778  1.1       cjs 			return(0);
    779  1.1       cjs 
    780  1.1       cjs 		if (search_forward)
    781  1.1       cjs 		{
    782  1.1       cjs 			/*
    783  1.1       cjs 			 * Read the next line, and save the
    784  1.1       cjs 			 * starting position of that line in linepos.
    785  1.1       cjs 			 */
    786  1.1       cjs 			linepos = pos;
    787  1.1       cjs 			pos = forw_raw_line(pos);
    788  1.1       cjs 			if (linenum != 0)
    789  1.1       cjs 				linenum++;
    790  1.1       cjs 		} else
    791  1.1       cjs 		{
    792  1.1       cjs 			/*
    793  1.1       cjs 			 * Read the previous line and save the
    794  1.1       cjs 			 * starting position of that line in linepos.
    795  1.1       cjs 			 */
    796  1.1       cjs 			pos = back_raw_line(pos);
    797  1.1       cjs 			linepos = pos;
    798  1.1       cjs 			if (linenum != 0)
    799  1.1       cjs 				linenum--;
    800  1.1       cjs 		}
    801  1.1       cjs 
    802  1.1       cjs 		if (pos == NULL_POSITION)
    803  1.1       cjs 		{
    804  1.1       cjs 			/*
    805  1.1       cjs 			 * We hit EOF/BOF without a match.
    806  1.1       cjs 			 */
    807  1.1       cjs 			error("Pattern not found");
    808  1.1       cjs 			return(0);
    809  1.1       cjs 		}
    810  1.1       cjs 
    811  1.1       cjs 		/*
    812  1.1       cjs 		 * If we're using line numbers, we might as well
    813  1.1       cjs 		 * remember the information we have now (the position
    814  1.1       cjs 		 * and line number of the current line).
    815  1.1       cjs 		 */
    816  1.1       cjs 		if (linenums)
    817  1.1       cjs 			add_lnum(linenum, pos);
    818  1.1       cjs 
    819  1.1       cjs 		/*
    820  1.1       cjs 		 * If this is a caseless search, convert uppercase in the
    821  1.1       cjs 		 * input line to lowercase.
    822  1.1       cjs 		 */
    823  1.1       cjs 		if (caseless)
    824  1.1       cjs 			for (p = q = line;  *p;  p++, q++)
    825  1.1       cjs 				*q = isupper(*p) ? tolower(*p) : *p;
    826  1.1       cjs 
    827  1.1       cjs 		/*
    828  1.6       wiz 		 * Remove any backspaces along with the preceding char.
    829  1.1       cjs 		 * This allows us to match text which is underlined or
    830  1.1       cjs 		 * overstruck.
    831  1.1       cjs 		 */
    832  1.1       cjs 		for (p = q = line;  *p;  p++, q++)
    833  1.1       cjs 			if (q > line && *p == '\b')
    834  1.6       wiz 				/* Delete BS and preceding char. */
    835  1.1       cjs 				q -= 2;
    836  1.1       cjs 			else
    837  1.1       cjs 				/* Otherwise, just copy. */
    838  1.1       cjs 				*q = *p;
    839  1.1       cjs 
    840  1.1       cjs 		/*
    841  1.1       cjs 		 * Test the next line to see if we have a match.
    842  1.1       cjs 		 * This is done in a variety of ways, depending
    843  1.1       cjs 		 * on what pattern matching functions are available.
    844  1.1       cjs 		 */
    845  1.1       cjs #ifdef REGCMP
    846  1.1       cjs 		linematch = (regex(cpattern, line) != NULL);
    847  1.1       cjs #else
    848  1.1       cjs #ifdef RECOMP
    849  1.1       cjs 		linematch = (re_exec(line) == 1);
    850  1.1       cjs #else
    851  1.1       cjs 		linematch = match(pattern, line);
    852  1.1       cjs #endif
    853  1.1       cjs #endif
    854  1.1       cjs 		/*
    855  1.1       cjs 		 * We are successful if wantmatch and linematch are
    856  1.1       cjs 		 * both true (want a match and got it),
    857  1.1       cjs 		 * or both false (want a non-match and got it).
    858  1.1       cjs 		 */
    859  1.1       cjs 		if (((wantmatch && linematch) || (!wantmatch && !linematch)) &&
    860  1.1       cjs 		      --n <= 0)
    861  1.1       cjs 			/*
    862  1.1       cjs 			 * Found the line.
    863  1.1       cjs 			 */
    864  1.1       cjs 			break;
    865  1.1       cjs 	}
    866  1.1       cjs 	jump_loc(linepos);
    867  1.1       cjs 	return(1);
    868  1.1       cjs }
    869  1.1       cjs 
    870  1.1       cjs #if !defined(REGCMP) && !defined(RECOMP)
    871  1.1       cjs /*
    872  1.1       cjs  * We have neither regcmp() nor re_comp().
    873  1.1       cjs  * We use this function to do simple pattern matching.
    874  1.1       cjs  * It supports no metacharacters like *, etc.
    875  1.1       cjs  */
    876  1.4  christos static int
    877  1.1       cjs match(pattern, buf)
    878  1.1       cjs 	char *pattern, *buf;
    879  1.1       cjs {
    880  1.4  christos 	char *pp, *lp;
    881  1.1       cjs 
    882  1.1       cjs 	for ( ;  *buf != '\0';  buf++)
    883  1.1       cjs 	{
    884  1.1       cjs 		for (pp = pattern, lp = buf;  *pp == *lp;  pp++, lp++)
    885  1.1       cjs 			if (*pp == '\0' || *lp == '\0')
    886  1.1       cjs 				break;
    887  1.1       cjs 		if (*pp == '\0')
    888  1.1       cjs 			return (1);
    889  1.1       cjs 	}
    890  1.1       cjs 	return (0);
    891  1.1       cjs }
    892  1.1       cjs #endif
    893