From f9ab90d9d6d02989a075d0f0074496d5b1045e4b Mon Sep 17 00:00:00 2001 From: rgrimes Date: Fri, 27 May 1994 12:33:43 +0000 Subject: BSD 4.4 Lite Usr.bin Sources --- usr.bin/more/prim.c | 834 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 834 insertions(+) create mode 100644 usr.bin/more/prim.c (limited to 'usr.bin/more/prim.c') diff --git a/usr.bin/more/prim.c b/usr.bin/more/prim.c new file mode 100644 index 0000000..d5af8f3 --- /dev/null +++ b/usr.bin/more/prim.c @@ -0,0 +1,834 @@ +/* + * Copyright (c) 1988 Mark Nudleman + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)prim.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +/* + * Primitives for displaying the file on the screen. + */ + +#include +#include +#include +#include + +int back_scroll = -1; +int hit_eof; /* keeps track of how many times we hit end of file */ +int screen_trashed; + +static int squished; + +extern int sigs; +extern int top_scroll; +extern int sc_width, sc_height; +extern int caseless; +extern int linenums; +extern int tagoption; +extern char *line; +extern int retain_below; + +off_t position(), forw_line(), back_line(), forw_raw_line(), back_raw_line(); +off_t ch_length(), ch_tell(); + +/* + * Check to see if the end of file is currently "displayed". + */ +eof_check() +{ + off_t pos; + + if (sigs) + return; + /* + * 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); + if (pos == NULL_POSITION || pos == ch_length()) + hit_eof++; +} + +/* + * 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. + */ +squish_check() +{ + if (squished) { + squished = 0; + repaint(); + } +} + +/* + * Display n lines, scrolling forward, starting at position pos in the + * input file. "only_last" means display only the last screenful if + * n > screen size. + */ +forw(n, pos, only_last) + register int n; + off_t pos; + int only_last; +{ + extern int short_file; + static int first_time = 1; + int eof = 0, do_repaint; + + squish_check(); + + /* + * do_repaint tells us not to display anything till the end, + * then just repaint the entire screen. + */ + do_repaint = (only_last && n > sc_height-1); + + if (!do_repaint) { + if (top_scroll && n >= sc_height - 1) { + /* + * 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. }} + */ + clear(); + home(); + } else { + lower_left(); + clear_eol(); + } + + /* + * This is not contiguous with what is currently displayed. + * Clear the screen image (position table) and start a new + * screen. + */ + if (pos != position(BOTTOM_PLUS_ONE)) { + pos_clear(); + add_forw_pos(pos); + if (top_scroll) { + clear(); + home(); + } else if (!first_time) + putstr("...skipping...\n"); + } + } + + for (short_file = 0; --n >= 0;) { + /* + * Read the next line of input. + */ + pos = forw_line(pos); + if (pos == NULL_POSITION) { + /* + * end of file; copy the table if the file was + * too small for an entire screen. + */ + eof = 1; + if (position(TOP) == NULL_POSITION) { + copytable(); + if (!position(TOP)) + short_file = 1; + } + break; + } + /* + * Add the position of the next line to the position table. + * Display the current line on the screen. + */ + add_forw_pos(pos); + 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 -t option was given; it 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 && line == NULL && !top_scroll && !tagoption) { + squished = 1; + continue; + } + put_line(); + } + + if (eof && !sigs) + hit_eof++; + else + eof_check(); + if (do_repaint) + repaint(); + first_time = 0; + (void) currline(BOTTOM); +} + +/* + * Display n lines, scrolling backward. + */ +back(n, pos, only_last) + register int n; + off_t pos; + int only_last; +{ + int do_repaint; + + squish_check(); + do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1)); + hit_eof = 0; + while (--n >= 0) + { + /* + * Get the previous line of input. + */ + pos = back_line(pos); + if (pos == NULL_POSITION) + break; + /* + * Add the position of the previous line to the position table. + * Display the line on the screen. + */ + add_back_pos(pos); + if (!do_repaint) + { + if (retain_below) + { + lower_left(); + clear_eol(); + } + home(); + add_line(); + put_line(); + } + } + + eof_check(); + if (do_repaint) + repaint(); + (void) currline(BOTTOM); +} + +/* + * Display n more lines, forward. + * Start just after the line currently displayed at the bottom of the screen. + */ +forward(n, only_last) + int n; + int only_last; +{ + off_t pos; + + if (hit_eof) { + /* + * If we're trying to go forward from end-of-file, + * go on to the next file. + */ + next_file(1); + return; + } + + pos = position(BOTTOM_PLUS_ONE); + if (pos == NULL_POSITION) + { + hit_eof++; + return; + } + forw(n, pos, only_last); +} + +/* + * Display n more lines, backward. + * Start just before the line currently displayed at the top of the screen. + */ +backward(n, only_last) + int n; + int only_last; +{ + off_t pos; + + pos = position(TOP); + /* + * This will almost never happen, because the top line is almost + * never empty. + */ + if (pos == NULL_POSITION) + return; + back(n, pos, only_last); +} + +/* + * Repaint the screen, starting from a specified position. + */ +prepaint(pos) + off_t pos; +{ + hit_eof = 0; + forw(sc_height-1, pos, 0); + screen_trashed = 0; +} + +/* + * Repaint the screen. + */ +repaint() +{ + /* + * Start at the line currently at the top of the screen + * and redisplay the screen. + */ + prepaint(position(TOP)); +} + +/* + * Jump to the end of the file. + * It is more convenient to paint the screen backward, + * from the end of the file toward the beginning. + */ +jump_forw() +{ + off_t pos; + + if (ch_end_seek()) + { + error("Cannot seek to end of file"); + return; + } + lastmark(); + pos = ch_tell(); + clear(); + pos_clear(); + add_back_pos(pos); + back(sc_height - 1, pos, 0); +} + +/* + * Jump to line n in the file. + */ +jump_back(n) + register int n; +{ + register int c, nlines; + + /* + * This is done the slow way, by starting at the beginning + * of the file and counting newlines. + * + * {{ Now that we have line numbering (in linenum.c), + * we could improve on this by starting at the + * nearest known line rather than at the beginning. }} + */ + if (ch_seek((off_t)0)) { + /* + * Probably a pipe with beginning of file no longer buffered. + * If he wants to go to line 1, we do the best we can, + * by going to the first line which is still buffered. + */ + if (n <= 1 && ch_beg_seek() == 0) + jump_loc(ch_tell()); + error("Cannot get to beginning of file"); + return; + } + + /* + * Start counting lines. + */ + for (nlines = 1; nlines < n; nlines++) + while ((c = ch_forw_get()) != '\n') + if (c == EOI) { + char message[40]; + (void)sprintf(message, "File has only %d lines", + nlines - 1); + error(message); + return; + } + jump_loc(ch_tell()); +} + +/* + * Jump to a specified percentage into the file. + * This is a poor compensation for not being able to + * quickly jump to a specific line number. + */ +jump_percent(percent) + int percent; +{ + off_t pos, len, ch_length(); + register int c; + + /* + * Determine the position in the file + * (the specified percentage of the file's length). + */ + if ((len = ch_length()) == NULL_POSITION) + { + error("Don't know length of file"); + return; + } + pos = (percent * len) / 100; + + /* + * Back up to the beginning of the line. + */ + if (ch_seek(pos) == 0) + { + while ((c = ch_back_get()) != '\n' && c != EOI) + ; + if (c == '\n') + (void) ch_forw_get(); + pos = ch_tell(); + } + jump_loc(pos); +} + +/* + * Jump to a specified position in the file. + */ +jump_loc(pos) + off_t pos; +{ + register int nline; + off_t tpos; + + if ((nline = onscreen(pos)) >= 0) { + /* + * The line is currently displayed. + * Just scroll there. + */ + forw(nline, position(BOTTOM_PLUS_ONE), 0); + return; + } + + /* + * Line is not on screen. + * Seek to the desired location. + */ + if (ch_seek(pos)) { + error("Cannot seek to that position"); + return; + } + + /* + * See if the desired line is BEFORE the currently displayed screen. + * If so, then move forward far enough so the line we're on will be + * at the bottom of the screen, in order to be able to call back() + * to make the screen scroll backwards & put the line at the top of + * the screen. + * {{ This seems inefficient, but it's not so bad, + * since we can never move forward more than a + * screenful before we stop to redraw the screen. }} + */ + tpos = position(TOP); + if (tpos != NULL_POSITION && pos < tpos) { + off_t npos = pos; + /* + * Note that we can't forw_line() past tpos here, + * so there should be no EOI at this stage. + */ + for (nline = 0; npos < tpos && nline < sc_height - 1; nline++) + npos = forw_line(npos); + + if (npos < tpos) { + /* + * More than a screenful back. + */ + lastmark(); + clear(); + pos_clear(); + add_back_pos(npos); + } + + /* + * Note that back() will repaint() if nline > back_scroll. + */ + back(nline, npos, 0); + return; + } + /* + * Remember where we were; clear and paint the screen. + */ + lastmark(); + prepaint(pos); +} + +/* + * The table of marks. + * A mark is simply a position in the file. + */ +#define NMARKS (27) /* 26 for a-z plus one for quote */ +#define LASTMARK (NMARKS-1) /* For quote */ +static off_t marks[NMARKS]; + +/* + * Initialize the mark table to show no marks are set. + */ +init_mark() +{ + int i; + + for (i = 0; i < NMARKS; i++) + marks[i] = NULL_POSITION; +} + +/* + * See if a mark letter is valid (between a and z). + */ + static int +badmark(c) + int c; +{ + if (c < 'a' || c > 'z') + { + error("Choose a letter between 'a' and 'z'"); + return (1); + } + return (0); +} + +/* + * Set a mark. + */ +setmark(c) + int c; +{ + if (badmark(c)) + return; + marks[c-'a'] = position(TOP); +} + +lastmark() +{ + marks[LASTMARK] = position(TOP); +} + +/* + * Go to a previously set mark. + */ +gomark(c) + int c; +{ + off_t pos; + + if (c == '\'') { + pos = marks[LASTMARK]; + if (pos == NULL_POSITION) + pos = 0; + } + else { + if (badmark(c)) + return; + pos = marks[c-'a']; + if (pos == NULL_POSITION) { + error("mark not set"); + return; + } + } + jump_loc(pos); +} + +/* + * 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. + */ +get_back_scroll() +{ + if (back_scroll >= 0) + return (back_scroll); + if (top_scroll) + return (sc_height - 2); + return (sc_height - 1); +} + +/* + * Search for the n-th occurence of a specified pattern, + * either forward or backward. + */ +search(search_forward, pattern, n, wantmatch) + register int search_forward; + register char *pattern; + register int n; + int wantmatch; +{ + off_t pos, linepos; + register char *p; + register char *q; + int linenum; + int linematch; +#ifdef RECOMP + char *re_comp(); + char *errmsg; +#else +#ifdef REGCMP + char *regcmp(); + static char *cpattern = NULL; +#else + static char lpbuf[100]; + static char *last_pattern = NULL; + char *strcpy(); +#endif +#endif + + /* + * For a caseless search, convert any uppercase in the pattern to + * lowercase. + */ + if (caseless && pattern != NULL) + for (p = pattern; *p; p++) + if (isupper(*p)) + *p = tolower(*p); +#ifdef RECOMP + + /* + * (re_comp handles a null pattern internally, + * so there is no need to check for a null pattern here.) + */ + if ((errmsg = re_comp(pattern)) != NULL) + { + error(errmsg); + return(0); + } +#else +#ifdef REGCMP + if (pattern == NULL || *pattern == '\0') + { + /* + * A null pattern means use the previous pattern. + * The compiled previous pattern is in cpattern, so just use it. + */ + if (cpattern == NULL) + { + error("No previous regular expression"); + return(0); + } + } else + { + /* + * Otherwise compile the given pattern. + */ + char *s; + if ((s = regcmp(pattern, 0)) == NULL) + { + error("Invalid pattern"); + return(0); + } + if (cpattern != NULL) + free(cpattern); + cpattern = s; + } +#else + if (pattern == NULL || *pattern == '\0') + { + /* + * Null pattern means use the previous pattern. + */ + if (last_pattern == NULL) + { + error("No previous regular expression"); + return(0); + } + pattern = last_pattern; + } else + { + (void)strcpy(lpbuf, pattern); + last_pattern = lpbuf; + } +#endif +#endif + + /* + * Figure out where to start the search. + */ + + if (position(TOP) == NULL_POSITION) { + /* + * Nothing is currently displayed. Start at the beginning + * of the file. (This case is mainly for searches from the + * command line. + */ + pos = (off_t)0; + } else if (!search_forward) { + /* + * Backward search: start just before the top line + * displayed on the screen. + */ + pos = position(TOP); + } else { + /* + * Start at the second screen line displayed on the screen. + */ + pos = position(TOP_PLUS_ONE); + } + + if (pos == NULL_POSITION) + { + /* + * Can't find anyplace to start searching from. + */ + error("Nothing to search"); + return(0); + } + + linenum = find_linenum(pos); + for (;;) + { + /* + * Get lines until we find a matching one or + * until we hit end-of-file (or beginning-of-file + * if we're going backwards). + */ + if (sigs) + /* + * A signal aborts the search. + */ + return(0); + + if (search_forward) + { + /* + * Read the next line, and save the + * starting position of that line in linepos. + */ + linepos = pos; + pos = forw_raw_line(pos); + if (linenum != 0) + linenum++; + } else + { + /* + * Read the previous line and save the + * starting position of that line in linepos. + */ + pos = back_raw_line(pos); + linepos = pos; + if (linenum != 0) + linenum--; + } + + if (pos == NULL_POSITION) + { + /* + * We hit EOF/BOF without a match. + */ + error("Pattern not found"); + return(0); + } + + /* + * If we're using line numbers, we might as well + * remember the information we have now (the position + * and line number of the current line). + */ + if (linenums) + add_lnum(linenum, pos); + + /* + * If this is a caseless search, convert uppercase in the + * input line to lowercase. + */ + if (caseless) + for (p = q = line; *p; p++, q++) + *q = isupper(*p) ? tolower(*p) : *p; + + /* + * Remove any backspaces along with the preceeding char. + * This allows us to match text which is underlined or + * overstruck. + */ + for (p = q = line; *p; p++, q++) + if (q > line && *p == '\b') + /* Delete BS and preceeding char. */ + q -= 2; + else + /* Otherwise, just copy. */ + *q = *p; + + /* + * Test the next line to see if we have a match. + * This is done in a variety of ways, depending + * on what pattern matching functions are available. + */ +#ifdef REGCMP + linematch = (regex(cpattern, line) != NULL); +#else +#ifdef RECOMP + linematch = (re_exec(line) == 1); +#else + linematch = match(pattern, line); +#endif +#endif + /* + * We are successful if wantmatch and linematch are + * both true (want a match and got it), + * or both false (want a non-match and got it). + */ + if (((wantmatch && linematch) || (!wantmatch && !linematch)) && + --n <= 0) + /* + * Found the line. + */ + break; + } + jump_loc(linepos); + return(1); +} + +#if !defined(REGCMP) && !defined(RECOMP) +/* + * We have neither regcmp() nor re_comp(). + * We use this function to do simple pattern matching. + * It supports no metacharacters like *, etc. + */ +static +match(pattern, buf) + char *pattern, *buf; +{ + register char *pp, *lp; + + for ( ; *buf != '\0'; buf++) + { + for (pp = pattern, lp = buf; *pp == *lp; pp++, lp++) + if (*pp == '\0' || *lp == '\0') + break; + if (*pp == '\0') + return (1); + } + return (0); +} +#endif -- cgit v1.1