Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!ames!amdahl!amdcad!pyramid!ctnews!unix386!mark From: mark@unix386.Convergent.COM (Mark Nudelman) Newsgroups: alt.sources Subject: less beta release (part 5 of 5) Message-ID: <533@unix386.Convergent.COM> Date: 14 Sep 89 22:25:28 GMT Distribution: usa Organization: Convergent Technologies, San Jose, CA Lines: 2759 #! /bin/sh # This is a shell archive. # Remove anything before this line, then unpack it # by saving it into a file and typing "sh file". echo shar: Extracting \"prim1.c\" sed "s/^X//" >'prim1.c' <<'END_OF_FILE' X/* X * Primitives for displaying the file on the screen. X */ X X#include "less.h" X#include "position.h" X Xpublic int hit_eof; /* Keeps track of how many times we hit end of file */ Xpublic int screen_trashed; X Xstatic int squished; X Xextern int sigs; Xextern int top_scroll; Xextern int quiet; Xextern int sc_width, sc_height; Xextern int quit_at_eof; Xextern int plusoption; Xextern char *first_cmd; X#if TAGS Xextern int tagoption; X#endif X X/* X * Sound the bell to indicate user is trying to move past end of file. X */ X static void Xeof_bell() X{ X if (quiet == NOT_QUIET) X bell(); X else X vbell(); X} X X/* X * Check to see if the end of file is currently "displayed". X */ X static void Xeof_check() X{ X POSITION pos; X X if (sigs) X return; X /* X * If the bottom line is empty, we are at EOF. X * If the bottom line ends at the file length, X * we must be just at EOF. X */ X pos = position(BOTTOM_PLUS_ONE); X if (pos == NULL_POSITION || pos == ch_length()) X hit_eof++; X} X X/* X * If the screen is "squished", repaint it. X * "Squished" means the first displayed line is not at the top X * of the screen; this can happen when we display a short file X * for the first time. X */ X static void Xsquish_check() X{ X if (!squished) X return; X squished = 0; X repaint(); X} X X/* X * Display n lines, scrolling forward, X * starting at position pos in the input file. X * "force" means display the n lines even if we hit end of file. X * "only_last" means display only the last screenful if n > screen size. X */ X static void Xforw(n, pos, force, only_last) X register int n; X POSITION pos; X int force; X int only_last; X{ X int eof = 0; X int nlines = 0; X int do_repaint; X static int first_time = 1; X X squish_check(); X X /* X * do_repaint tells us not to display anything till the end, X * then just repaint the entire screen. X */ X do_repaint = (only_last && n > sc_height-1); X X if (!do_repaint) X { X if (top_scroll && n >= sc_height - 1) X { X /* X * Start a new screen. X * {{ This is not really desirable if we happen X * to hit eof in the middle of this screen, X * but we don't yet know if that will happen. }} X */ X if (top_scroll == 2) X clear(); X home(); X force = 1; X } else X { X lower_left(); X clear_eol(); X } X X if (pos != position(BOTTOM_PLUS_ONE)) X { X /* X * This is not contiguous with what is X * currently displayed. Clear the screen image X * (position table) and start a new screen. X */ X pos_clear(); X add_forw_pos(pos); X force = 1; X if (top_scroll) X { X if (top_scroll == 2) X clear(); X home(); X } else if (!first_time) X { X putstr("...skipping...\n"); X } X } X } X X while (--n >= 0) X { X /* X * Read the next line of input. X */ X pos = forw_line(pos); X if (pos == NULL_POSITION) X { X /* X * End of file: stop here unless the top line X * is still empty, or "force" is true. X */ X eof = 1; X if (!force && position(TOP) != NULL_POSITION) X break; X } X /* X * Add the position of the next line to the position table. X * Display the current line on the screen. X */ X add_forw_pos(pos); X nlines++; X if (do_repaint) X continue; X /* X * If this is the first screen displayed and X * we hit an early EOF (i.e. before the requested X * number of lines), we "squish" the display down X * at the bottom of the screen. X * But don't do this if a + option or a -t option X * was given. These options can cause us to X * start the display after the beginning of the file, X * and it is not appropriate to squish in that case. X */ X if (first_time && pos == NULL_POSITION && !top_scroll && X#if TAGS X !tagoption && X#endif X !plusoption) X { X squished = 1; X continue; X } X if (top_scroll == 1) X clear_eol(); X put_line(); X } X X if (eof && !sigs) X hit_eof++; X else X eof_check(); X if (nlines == 0) X eof_bell(); X else if (do_repaint) X repaint(); X first_time = 0; X (void) currline(BOTTOM); X} X X/* X * Display n lines, scrolling backward. X */ X static void Xback(n, pos, force, only_last) X register int n; X POSITION pos; X int force; X int only_last; X{ X int nlines = 0; X int do_repaint; X X squish_check(); X do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1)); X hit_eof = 0; X while (--n >= 0) X { X /* X * Get the previous line of input. X */ X pos = back_line(pos); X if (pos == NULL_POSITION) X { X /* X * Beginning of file: stop here unless "force" is true. X */ X if (!force) X break; X } X /* X * Add the position of the previous line to the position table. X * Display the line on the screen. X */ X add_back_pos(pos); X nlines++; X if (!do_repaint) X { X home(); X add_line(); X put_line(); X } X } X X eof_check(); X if (nlines == 0) X eof_bell(); X else if (do_repaint) X repaint(); X (void) currline(BOTTOM); X} X X/* X * Display n more lines, forward. X * Start just after the line currently displayed at the bottom of the screen. X */ X public void Xforward(n, only_last) X int n; X int only_last; X{ X POSITION pos; X X if (quit_at_eof && hit_eof) X { X /* X * If the -e flag is set and we're trying to go X * forward from end-of-file, go on to the next file. X */ X next_file(1); X return; X } X X pos = position(BOTTOM_PLUS_ONE); X if (pos == NULL_POSITION) X { X eof_bell(); X hit_eof++; X return; X } X forw(n, pos, 0, only_last); X} X X/* X * Display n more lines, backward. X * Start just before the line currently displayed at the top of the screen. X */ X public void Xbackward(n, only_last) X int n; X int only_last; X{ X POSITION pos; X X pos = position(TOP); X if (pos == NULL_POSITION) X { X /* X * This will almost never happen, X * because the top line is almost never empty. X */ X eof_bell(); X return; X } X back(n, pos, 0, only_last); X} X X/* X * Repaint the screen, starting from a specified position. X */ X static void Xprepaint(pos) X POSITION pos; X{ X hit_eof = 0; X squished = 0; X screen_trashed = 0; X forw(sc_height-1, pos, 1, 0); X} X X/* X * Repaint the screen. X */ X public void Xrepaint() X{ X /* X * Start at the line currently at the top of the screen X * and redisplay the screen. X */ X prepaint(position(TOP)); X} X X/* X * Jump to the end of the file. X * It is more convenient to paint the screen backward, X * from the end of the file toward the beginning. X */ X public void Xjump_forw() X{ X POSITION pos; X X if (ch_end_seek()) X { X error("Cannot seek to end of file"); X return; X } X lastmark(); X pos = ch_tell(); X clear(); X pos_clear(); X add_back_pos(pos); X screen_trashed = 0; X back(sc_height - 1, pos, 0, 0); X} X X/* X * Jump to line n in the file. X */ X public void Xjump_back(n) X int n; X{ X POSITION pos; X char m[50]; X X pos = find_pos(n); X if (pos != NULL_POSITION && ch_seek(pos) == 0) X { X jump_loc(pos); X } else if (n <= 1 && ch_beg_seek() == 0) X { X jump_loc(ch_tell()); X error("Cannot seek to beginning of file"); X } else X { X sprintf(m, "Cannot seek to line number %d", n); X error(m); X } X} X X/* X * Jump to a specified position in the file. X * The position must be the first character in a line. X */ X public void Xjump_loc(pos) X POSITION pos; X{ X register int nline; X POSITION tpos; X X if ((nline = onscreen(pos)) >= 0) X { X /* X * The line is currently displayed. X * Just scroll there. X */ X forw(nline, position(BOTTOM_PLUS_ONE), 1, 0); X return; X } X X /* X * Line is not on screen. X * Seek to the desired location. X */ X if (ch_seek(pos)) X { X error("Cannot seek to that file position"); X return; X } X X /* X * See if the desired line is BEFORE the currently X * displayed screen. If so, then move forward far X * enough so the line we're on will be at the bottom X * of the screen, in order to be able to call back() X * to make the screen scroll backwards & put the line X * at the top of the screen. X * {{ This seems inefficient, but it's not so bad, X * since we can never move forward more than a X * screenful before we stop to redraw the screen. }} X */ X tpos = position(TOP); X if (tpos != NULL_POSITION && pos < tpos) X { X POSITION npos = pos; X /* X * Note that we can't forw_line() past tpos here, X * so there should be no EOI at this stage. X */ X for (nline = 0; npos < tpos && nline < sc_height - 1; nline++) X npos = forw_line(npos); X X if (npos < tpos) X { X /* X * More than a screenful back. X */ X lastmark(); X clear(); X pos_clear(); X add_back_pos(npos); X } X X /* X * Note that back() will repaint() if nline > back_scroll. X */ X back(nline, npos, 1, 0); X return; X } X /* X * Remember where we were; clear and paint the screen. X */ X lastmark(); X prepaint(pos); X} END_OF_FILE echo shar: Extracting \"prim2.c\" sed "s/^X//" >'prim2.c' <<'END_OF_FILE' X/* X * More primitives for displaying the file on the screen. X */ X X#include "less.h" X#include "position.h" X Xextern int sigs; Xextern int how_search; Xextern int top_scroll; Xextern int back_scroll; Xextern int caseless; Xextern int linenums; Xextern int sc_height; Xextern HANDLE curr_handle; X X/* X * Jump to a specified position in the file. X * The position need not be the first character in a line. X */ X static void Xjump_line_loc(pos) X POSITION pos; X{ X int c; X X if (ch_seek(pos) == 0) X { X /* X * Back up to the beginning of the line. X */ X while ((c = ch_back_get()) != '\n' && c != EOI) X ; X if (c == '\n') X (void) ch_forw_get(); X pos = ch_tell(); X } X jump_loc(pos); X} X X/* X * Jump to a specified percentage into the file. X * This is a poor compensation for not being able to X * quickly jump to a specific line number. X */ X public void Xjump_percent(percent) X int percent; X{ X POSITION pos, len; X X /* X * Determine the position in the file X * (the specified percentage of the file's length). X */ X if ((len = ch_length()) == NULL_POSITION) X { X error("Don't know length of file"); X return; X } X pos = (percent * len) / 100; X X jump_line_loc(pos); X} X X/* X * The table of marks. X * A mark is simply a position in a file and the handle of the file. X */ X#define NMARKS (27) /* 26 for a-z plus one for quote */ X#define LASTMARK (NMARKS-1) /* For quote */ X Xstatic struct mark { X HANDLE m_handle; X POSITION m_pos; X} marks[NMARKS]; X X/* X * Initialize the mark table to show no marks are set. X */ X public void Xinit_mark() X{ X int i; X X for (i = 0; i < NMARKS; i++) X marks[i].m_pos = NULL_POSITION; X} X X/* X * See if a mark letter is valid (between a and z). X */ X static int Xbadmark(c) X int c; X{ X if (c < 'a' || c > 'z') X { X error("Choose a letter between 'a' and 'z'"); X return (1); X } X return (0); X} X X/* X * Set a mark. X */ X public void Xsetmark(c) X int c; X{ X if (badmark(c)) X return; X c -= 'a'; X marks[c].m_pos = position(TOP); X marks[c].m_handle = curr_handle; X} X X/* X * Set the LASTMARK (the mark named by the apostrophe). X */ X public void Xlastmark() X{ X POSITION pos; X X pos = position(TOP); X if (pos == NULL_POSITION) X return; X marks[LASTMARK].m_pos = pos; X marks[LASTMARK].m_handle = curr_handle; X} X X/* X * Go to a previously set mark. X */ X public void Xgomark(c) X int c; X{ X POSITION pos; X HANDLE handle; X X if (c == '\'') X c = LASTMARK; X else if (badmark(c)) X return; X else X c -= 'a'; X X pos = marks[c].m_pos; X handle = marks[c].m_handle; X if (pos == NULL_POSITION) X { X error("Mark not set"); X return; X } X X if (handle != curr_handle) X /* X * Not in the current file; edit the correct file. X */ X edit(get_filename(handle)); X X jump_loc(pos); X} X X/* X * Get the backwards scroll limit. X * Must call this function instead of just using the value of X * back_scroll, because the default case depends on sc_height and X * top_scroll, as well as back_scroll. X */ X public int Xget_back_scroll() X{ X if (back_scroll >= 0) X return (back_scroll); X if (top_scroll) X return (sc_height - 2); X return (10000); /* infinity */ X} X X/* X * Try to match the n-th bracket of the specified type X * which appears in the top displayed line. X * brac may be '\0' to mean look for any bracket. X * "Bracket" refers to any of the pairs: { }, [ ], or ( ). X */ X public void Xmatch_brac(brac, n) X register int brac; X int n; X{ X register int c; X register int nest; X int obrac, cbrac; X int forwdir; X POSITION pos; X int (*chget)(); X X extern int ch_forw_get(), ch_back_get(); X X pos = position(TOP); X if (pos == NULL_POSITION || ch_seek(pos)) X { X error("Nothing in top line"); X return; X } X X /* X * Look thru the first line to find the type of bracket to match. X */ X for (;;) X { X if ((c = ch_forw_get()) == '\n' || c == EOI) X { X error("No bracket in top line"); X return; X } X if (brac != '\0' && brac != c) X /* X * This is not the specified bracket character. X */ X continue; X X switch (c) X { X default: continue; X case '{': obrac = '{'; cbrac = '}'; forwdir = 1; break; X case '}': obrac = '}'; cbrac = '{'; forwdir = 0; break; X case '[': obrac = '['; cbrac = ']'; forwdir = 1; break; X case ']': obrac = ']'; cbrac = '['; forwdir = 0; break; X case '(': obrac = '('; cbrac = ')'; forwdir = 1; break; X case ')': obrac = ')'; cbrac = '('; forwdir = 0; break; X } X /* X * See if we have the n-th bracket in the line. X */ X if (--n <= 0) X break; X } X X if (!forwdir) X { X /* X * Position the file just "after" the open bracket. X * That is, if searching backwards, skip back over X * the open bracket now. X */ X (void) ch_back_get(); X } X X /* X * Search the file for the matching bracket. X */ X chget = (forwdir) ? ch_forw_get : ch_back_get; X nest = 0; X while ((c = (*chget)()) != EOI) X { X if (c == obrac) X nest++; X else if (c == cbrac && --nest < 0) X { X jump_line_loc(ch_tell()); X return; X } X } X error("No matching bracket"); X} X X/* X * Search for the n-th occurrence of a specified pattern, X * either forward or backward. X */ X public void Xsearch(search_type, pattern, n) X int search_type; X char *pattern; X register int n; X{ X POSITION pos, linepos; X register char *p; X register char *q; X register int goforw; X register int wantmatch; X char *line; X int linenum; X int linematch; X#if RECOMP X char *re_comp(); X char *errmsg; X#else X#if REGCMP X char *regcmp(); X static char *cpattern = NULL; X#else X static char lpbuf[100]; X static char *last_pattern = NULL; X#endif X#endif X X /* X * Extract flags and type of search. X */ X wantmatch = !(search_type & SRCH_NOMATCH); X search_type = SRCH_TYPE(search_type); X X if (caseless && pattern != NULL) X { X /* X * For a caseless search, convert any uppercase X * in the pattern to lowercase. X */ X for (p = pattern; *p != '\0'; p++) X if (*p >= 'A' && *p <= 'Z') X *p += 'a' - 'A'; X } X#if RECOMP X X /* X * (re_comp handles a null pattern internally, X * so there is no need to check for a null pattern here.) X */ X if ((errmsg = re_comp(pattern)) != NULL) X { X error(errmsg); X return; X } X#else X#if REGCMP X if (pattern == NULL || *pattern == '\0') X { X /* X * A null pattern means use the previous pattern. X * The compiled previous pattern is in cpattern, so just use it. X */ X if (cpattern == NULL) X { X error("No previous regular expression"); X return; X } X } else X { X /* X * Otherwise compile the given pattern. X */ X char *s; X if ((s = regcmp(pattern, 0)) == NULL) X { X error("Invalid pattern"); X return; X } X if (cpattern != NULL) X free(cpattern); X cpattern = s; X } X#else X if (pattern == NULL || *pattern == '\0') X { X /* X * Null pattern means use the previous pattern. X */ X if (last_pattern == NULL) X { X error("No previous regular expression"); X return; X } X pattern = last_pattern; X } else X { X strcpy(lpbuf, pattern); X last_pattern = lpbuf; X } X#endif X#endif X X /* X * Figure out where to start the search. X */ X X if (search_type == SRCH_FILE) X { X /* X * User wants to start searching at beginning of file. X * {{ Use ch_beg_seek() in case we can't seek to 0? }} X */ X pos = (POSITION)0; X goforw = 1; X } else if (position(TOP) == NULL_POSITION) X { X /* X * Nothing is currently displayed. X * Start at the beginning of the file. X * (This case is mainly for first_cmd searches, X * for example, "+/xyz" on the command line.) X */ X pos = (POSITION)0; X goforw = 1; X } else if (search_type == SRCH_BACK) X { X /* X * Backward search: start just before the top line X * displayed on the screen. X */ X pos = position(TOP); X goforw = 0; X } else if (how_search == 0) X { X /* X * Start at the second real line displayed on the screen. X */ X pos = position(TOP); X do X pos = forw_raw_line(pos, (char **)NULL); X while (pos < position(TOP_PLUS_ONE)); X goforw = 1; X } else if (how_search == 1) X { X /* X * Start just after the bottom line displayed on the screen. X */ X pos = position(BOTTOM_PLUS_ONE); X goforw = 1; X } else X { X /* X * Start at the second screen line displayed on the screen. X */ X pos = position(TOP_PLUS_ONE); X goforw = 1; X } X X if (pos == NULL_POSITION) X { X /* X * Can't find anyplace to start searching from. X */ X error("Nothing to search"); X return; X } X X linenum = find_linenum(pos); X for (;;) X { X /* X * Get lines until we find a matching one or X * until we hit end-of-file (or beginning-of-file X * if we're going backwards). X */ X if (sigs) X /* X * A signal aborts the search. X */ X return; X X if (goforw) X { X /* X * Read the next line, and save the X * starting position of that line in linepos. X */ X linepos = pos; X pos = forw_raw_line(pos, &line); X if (linenum != 0) X linenum++; X } else X { X /* X * Read the previous line and save the X * starting position of that line in linepos. X */ X pos = back_raw_line(pos, &line); X linepos = pos; X if (linenum != 0) X linenum--; X } X X if (pos == NULL_POSITION) X { X /* X * We hit EOF/BOF without a match. X */ X error("Pattern not found"); X return; X } X X /* X * If we're using line numbers, we might as well X * remember the information we have now (the position X * and line number of the current line). X */ X if (linenums) X add_lnum(linenum, pos); X X if (caseless) X { X /* X * If this is a caseless search, convert X * uppercase in the input line to lowercase. X * While we're at it, remove any backspaces X * along with the preceding char. X * This allows us to match text which is X * underlined or overstruck. X */ X for (p = q = line; *p != '\0'; p++, q++) X { X if (*p >= 'A' && *p <= 'Z') X /* Convert uppercase to lowercase. */ X *q = *p + 'a' - 'A'; X else if (q > line && *p == '\b') X /* Delete BS and preceding char. */ X q -= 2; X else X /* Otherwise, just copy. */ X *q = *p; X } X } X X /* X * Test the next line to see if we have a match. X * This is done in a variety of ways, depending X * on what pattern matching functions are available. X */ X#if REGCMP X linematch = (regex(cpattern, line) != NULL); X#else X#if RECOMP X linematch = (re_exec(line) == 1); X#else X linematch = match(pattern, line); X#endif X#endif X /* X * We are successful if wantmatch and linematch are X * both true (want a match and got it), X * or both false (want a non-match and got it). X */ X if (((wantmatch && linematch) || (!wantmatch && !linematch)) && X --n <= 0) X /* X * Found the line. X */ X break; X } X X jump_loc(linepos); X} X X#if (!REGCMP) && (!RECOMP) X/* X * We have neither regcmp() nor re_comp(). X * We use this function to do simple pattern matching. X * It supports no metacharacters like *, etc. X */ X static int Xmatch(pattern, buf) X char *pattern, *buf; X{ X register char *pp, *lp; X X for ( ; *buf != '\0'; buf++) X { X for (pp = pattern, lp = buf; *pp == *lp; pp++, lp++) X if (*pp == '\0' || *lp == '\0') X break; X if (*pp == '\0') X return (1); X } X return (0); X} X#endif END_OF_FILE echo shar: Extracting \"prompt.c\" sed "s/^X//" >'prompt.c' <<'END_OF_FILE' X/* X * Prompting and other messages. X * There are three flavors of prompts, SHORT, MEDIUM and LONG, X * selected by the -m/-M options. X * There is also the "equals message", printed by the = command. X * A prompt is a message composed of various pieces, such as the X * name of the file being viewed, the percentage into the file, etc. X */ X X#include "less.h" X#include "position.h" X Xextern int pr_type; Xextern int ispipe; Xextern int hit_eof; Xextern int new_file; Xextern int sc_width; Xextern int so_width, se_width; Xextern char *current_file; Xextern int ac; Xextern char **av; Xextern int curr_ac; Xextern int linenums; X#if EDITOR Xextern char *editor; X#endif X X/* X * Prototypes for the three flavors of prompts. X * These strings are expanded by pr_expand(). X */ Xstatic char s_proto[] = X "?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\\: %x..%t"; Xstatic char m_proto[] = X "?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t"; Xstatic char M_proto[] = X "?f%f .?n?m(file %i of %m) ..?ltline %lt?L/%L. :byte %bB?s/%s. .?e(END) ?x- Next\\: %x.:?pB%pB\\%..%t"; Xstatic char e_proto[] = X "?f%f .?m(file %i of %m) .?ltline %lt?L/%L. .byte %bB?s/%s. ?e(END) :?pB%pB\\%..%t"; X Xpublic char *prproto[3]; Xpublic char *eqproto = e_proto; X Xstatic char message[250]; Xstatic char *mp; X X/* X * Initialize the prompt prototype strings. X */ X public void Xinit_prompt() X{ X prproto[0] = save(s_proto); X prproto[1] = save(m_proto); X prproto[2] = save(M_proto); X eqproto = save(e_proto); X} X X/* X * Set the message pointer to the end of the message string. X */ X static void Xsetmp() X{ X while (*mp != '\0') X mp++; X} X X/* X * Append a POSITION (as a decimal integer) to the end of the message. X */ X static void Xap_pos(pos) X POSITION pos; X{ X sprintf(mp, "%ld", (long)pos); X setmp(); X} X X/* X * Append an integer to the end of the message. X */ X static void Xap_int(n) X int n; X{ X sprintf(mp, "%d", n); X setmp(); X} X X/* X * Append a string to the end of the message. X */ X static void Xap_str(s) X char *s; X{ X strtcpy(mp, s, (unsigned int)(&message[sizeof(message)] - mp)); X setmp(); X} X X/* X * Append a question mark to the end of the message. X */ X static void Xap_quest() X{ X *mp++ = '?'; X} X X/* X * Return the "current" byte offset in the file. X */ X static POSITION Xcurr_byte(where) X int where; X{ X POSITION pos; X X pos = position(where); X if (pos == NULL_POSITION) X pos = ch_length(); X return (pos); X} X X/* X * Return the value of a prototype conditional. X * A prototype string may include conditionals which consist of a X * question mark followed by a single letter. X * Here we decode that letter and return the appropriate boolean value. X */ X static int Xcond(c, where) X char c; X int where; X{ X switch (c) X { X case 'a': /* Anything in the message yet? */ X return (mp > message); X case 'b': /* Current byte offset known? */ X return (curr_byte(where) != NULL_POSITION); X case 'e': /* At end of file? */ X return (hit_eof); X case 'f': /* Filename known? */ X return (!ispipe); X case 'l': /* Line number known? */ X return (linenums); X case 'L': /* Final line number known? */ X return (linenums && ch_length() != NULL_POSITION); X case 'm': /* More than one file? */ X return (ac > 1); X case 'n': /* First prompt in a new file? */ X return (new_file); X case 'p': /* Percent into file known? */ X return (curr_byte(where) != NULL_POSITION && X ch_length() > 0); X case 's': /* Size of file known? */ X return (ch_length() != NULL_POSITION); X case 'x': /* Is there a "next" file? */ X return (curr_ac + 1 < ac); X } X return (0); X} X X/* X * Decode a "percent" prototype character. X * A prototype string may include various "percent" escapes; X * that is, a percent sign followed by a single letter. X * Here we decode that letter and take the appropriate action, X * usually by appending something to the message being built. X */ X static void Xprotochar(c, where) X int c; X int where; X{ X POSITION pos; X POSITION len; X int n; X X switch (c) X { X case 'b': /* Current byte offset */ X pos = curr_byte(where); X if (pos != NULL_POSITION) X ap_pos(pos); X else X ap_quest(); X break; X#if EDITOR X case 'E': /* Editor name */ X ap_str(editor); X break; X#endif X case 'f': /* File name */ X ap_str(current_file); X break; X case 'i': /* Index into list of files */ X ap_int(curr_ac + 1); X break; X case 'l': /* Current line number */ X n = currline(where); X if (n != 0) X ap_int(n); X else X ap_quest(); X break; X case 'L': /* Final line number */ X len = ch_length(); X if (len == NULL_POSITION || len == (POSITION)0 || X (n = find_linenum(len)) <= 0) X ap_quest(); X else X ap_int(n-1); X break; X case 'm': /* Number of files */ X ap_int(ac); X break; X case 'p': /* Percent into file */ X pos = curr_byte(where); X len = ch_length(); X if (pos != NULL_POSITION && len > 0) X ap_int((int)(100*pos / len)); X else X ap_quest(); X break; X case 's': /* Size of file */ X len = ch_length(); X if (len != NULL_POSITION) X ap_pos(len); X else X ap_quest(); X break; X case 't': /* Truncate trailing spaces in the message */ X while (mp > message && mp[-1] == ' ') X mp--; X break; X case 'x': /* Name of next file */ X if (curr_ac + 1 < ac) X ap_str(av[curr_ac+1]); X else X ap_quest(); X break; X } X} X X/* X * Skip a false conditional. X * When a false condition is found (either a false IF or the ELSE part X * of a true IF), this routine scans the prototype string to decide X * where to resume parsing the string. X * We must keep track of nested IFs and skip them properly. X */ X static char * Xskipcond(p) X register char *p; X{ X register int iflevel; X X /* X * We came in here after processing a ? or :, X * so we start nested one level deep. X */ X iflevel = 1; X X for (;;) switch (*++p) X { X case '?': X /* X * Start of a nested IF. X */ X iflevel++; X break; X case ':': X /* X * Else. X * If this matches the IF we came in here with, X * then we're done. X */ X if (iflevel == 1) X return (p); X break; X case '.': X /* X * Endif. X * If this matches the IF we came in here with, X * then we're done. X */ X if (--iflevel == 0) X return (p); X break; X case '\\': X /* X * Backslash escapes the next character. X */ X ++p; X break; X case '\0': X /* X * Whoops. Hit end of string. X * This is a malformed conditional, but just treat it X * as if all active conditionals ends here. X */ X return (p-1); X } X /*NOTREACHED*/ X} X X static char * Xwherechar(p, wp) X char *p; X int *wp; X{ X switch (*p) X { X case 'b': case 'l': case 'p': X switch (*++p) X { X case 't': *wp = TOP; break; X case 'm': *wp = MIDDLE; break; X case 'b': *wp = BOTTOM; break; X case 'B': *wp = BOTTOM_PLUS_ONE; break; X default: *wp = TOP; break; X } X } X return (p); X} X X/* X * Construct a message based on a prototype string. X */ X public char * Xpr_expand(proto, maxwidth) X char *proto; X int maxwidth; X{ X register char *p; X register int c; X int where; X X mp = message; X X if (*proto == '\0') X return (""); X X for (p = proto; *p != '\0'; p++) X { X switch (*p) X { X default: /* Just put the character in the message */ X *mp++ = *p; X break; X case '\\': /* Backslash escapes the next character */ X p++; X *mp++ = *p; X break; X case '?': /* Conditional (IF) */ X if ((c = *++p) == '\0') X --p; X else X { X p = wherechar(p, &where); X if (!cond(c, where)) X p = skipcond(p); X } X break; X case ':': /* ELSE */ X p = skipcond(p); X break; X case '.': /* ENDIF */ X break; X case '%': /* Percent escape */ X if ((c = *++p) == '\0') X --p; X else X { X p = wherechar(p, &where); X protochar(c, where); X } X break; X } X } X X new_file = 0; X if (mp == message) X return (NULL); X *mp = '\0'; X if (maxwidth > 0 && mp >= message + maxwidth) X { X /* X * Message is too long. X * Return just the final portion of it. X */ X return (mp - maxwidth); X } X return (message); X} X X/* X * Return a message suitable for printing by the "=" command. X */ X public char * Xeq_message() X{ X return (pr_expand(eqproto, 0)); X} X X/* X * Return a prompt. X * This depends on the prompt type (SHORT, MEDIUM, LONG), etc. X * If we can't come up with an appropriate prompt, return NULL X * and the caller will prompt with a colon. X */ X public char * Xpr_string() X{ X return (pr_expand(prproto[pr_type], sc_width-so_width-se_width-2)); X} END_OF_FILE echo shar: Extracting \"screen.c\" sed "s/^X//" >'screen.c' <<'END_OF_FILE' X/* X * Routines which deal with the characteristics of the terminal. X * Uses termcap to be as terminal-independent as possible. X * X * {{ Someday this should be rewritten to use curses. }} X */ X X#include "less.h" X#if XENIX X#include X#include X#endif X X#if TERMIO X#include X#else X#include X#endif X X#ifdef TIOCGWINSZ X#include X#else X/* X * For the Unix PC (ATT 7300 & 3B1): X * Since WIOCGETD is defined in sys/window.h, we can't use that to decide X * whether to include sys/window.h. Use SIGPHONE from sys/signal.h instead. X */ X#include X#ifdef SIGPHONE X#include X#endif X#endif X X/* X * Strings passed to tputs() to do various terminal functions. X */ Xstatic char X *sc_pad, /* Pad string */ X *sc_home, /* Cursor home */ X *sc_addline, /* Add line, scroll down following lines */ X *sc_lower_left, /* Cursor to last line, first column */ X *sc_move, /* General cursor positioning */ X *sc_clear, /* Clear screen */ X *sc_eol_clear, /* Clear to end of line */ X *sc_s_in, /* Enter standout (highlighted) mode */ X *sc_s_out, /* Exit standout mode */ X *sc_u_in, /* Enter underline mode */ X *sc_u_out, /* Exit underline mode */ X *sc_b_in, /* Enter bold mode */ X *sc_b_out, /* Exit bold mode */ X *sc_visual_bell, /* Visual bell (flash screen) sequence */ X *sc_backspace, /* Backspace cursor */ X *sc_init, /* Startup terminal initialization */ X *sc_deinit; /* Exit terminal de-initialization */ X Xpublic int auto_wrap; /* Terminal does \r\n when write past margin */ Xpublic int ignaw; /* Terminal ignores \n immediately after wrap */ Xpublic int erase_char, kill_char; /* The user's erase and line-kill chars */ Xpublic int sc_width, sc_height; /* Height & width of screen */ Xpublic int bo_width, be_width; /* Printing width of boldface sequences */ Xpublic int ul_width, ue_width; /* Printing width of underline sequences */ Xpublic int so_width, se_width; /* Printing width of standout sequences */ X Xstatic char *cheaper(); X X/* X * These two variables are sometimes defined in, X * and needed by, the termcap library. X * It may be necessary on some systems to declare them extern here. X */ X/*extern*/ short ospeed; /* Terminal output baud rate */ X/*extern*/ char PC; /* Pad character */ X Xextern int quiet; /* If VERY_QUIET, use visual bell for bell */ Xextern int know_dumb; /* Don't complain about a dumb terminal */ Xextern int back_scroll; Xextern int swindow; Xextern char *tgetstr(); Xextern char *tgoto(); X X/* X * Change terminal to "raw mode", or restore to "normal" mode. X * "Raw mode" means X * 1. An outstanding read will complete on receipt of a single keystroke. X * 2. Input is not echoed. X * 3. On output, \n is mapped to \r\n. X * 4. \t is NOT expanded into spaces. X * 5. Signal-causing characters such as ctrl-C (interrupt), X * etc. are NOT disabled. X * It doesn't matter whether an input \n is mapped to \r, or vice versa. X */ X public void Xraw_mode(on) X int on; X{ X#if TERMIO X struct termio s; X static struct termio save_term; X X if (on) X { X /* X * Get terminal modes. X */ X ioctl(2, TCGETA, &s); X X /* X * Save modes and set certain variables dependent on modes. X */ X save_term = s; X ospeed = s.c_cflag & CBAUD; X erase_char = s.c_cc[VERASE]; X kill_char = s.c_cc[VKILL]; X X /* X * Set the modes to the way we want them. X */ X s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL); X s.c_oflag |= (OPOST|ONLCR|TAB3); X s.c_oflag &= ~(OCRNL|ONOCR|ONLRET); X s.c_cc[VMIN] = 1; X s.c_cc[VTIME] = 0; X } else X { X /* X * Restore saved modes. X */ X s = save_term; X } X ioctl(2, TCSETAW, &s); X#else X struct sgttyb s; X static struct sgttyb save_term; X X if (on) X { X /* X * Get terminal modes. X */ X ioctl(2, TIOCGETP, &s); X X /* X * Save modes and set certain variables dependent on modes. X */ X save_term = s; X ospeed = s.sg_ospeed; X erase_char = s.sg_erase; X kill_char = s.sg_kill; X X /* X * Set the modes to the way we want them. X */ X s.sg_flags |= CBREAK; X s.sg_flags &= ~(ECHO|XTABS); X } else X { X /* X * Restore saved modes. X */ X s = save_term; X } X ioctl(2, TIOCSETN, &s); X#endif X} X X static void Xcannot(s) X char *s; X{ X char message[100]; X X if (know_dumb) X /* X * User knows this is a dumb terminal, so don't tell him. X */ X return; X X sprintf(message, "WARNING: terminal cannot %s", s); X error(message); X} X X/* X * Get terminal capabilities via termcap. X */ X public void Xget_term() X{ X char *sp; X register char *t1, *t2; X register int hard; X char *term; X#ifdef TIOCGWINSZ X struct winsize w; X#else X#ifdef WIOCGETD X struct uwdata w; X#endif X#endif X char termbuf[2048]; X X static char sbuf[1024]; X X extern char *getenv(); X X /* X * Find out what kind of terminal this is. X */ X if ((term = getenv("TERM")) == NULL) X term = "unknown"; X if (tgetent(termbuf, term) <= 0) X strcpy(termbuf, "dumb:co#80:hc:"); X X /* X * Get size of the screen. X */ X#ifdef TIOCGWINSZ X if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_row > 0) X sc_height = w.ws_row; X else X#else X#ifdef WIOCGETD X if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_height > 0) X sc_height = w.uw_height/w.uw_vs; X else X#endif X#endif X sc_height = tgetnum("li"); X X hard = (sc_height <= 0 || tgetflag("hc")); X if (hard) X { X /* Oh no, this is a hardcopy terminal. */ X sc_height = 24; X } X X pos_init(); X if (swindow < 0) X swindow = sc_height - 1; X X#ifdef TIOCGWINSZ X if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_col > 0) X sc_width = w.ws_col; X else X#ifdef WIOCGETD X if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_width > 0) X sc_width = w.uw_width/w.uw_hs; X else X#endif X#endif X sc_width = tgetnum("co"); X X if (sc_width <= 0) X sc_width = 80; X X auto_wrap = tgetflag("am"); X ignaw = tgetflag("xn"); X X /* X * Assumes termcap variable "sg" is the printing width of: X * the standout sequence, the end standout sequence, X * the underline sequence, the end underline sequence, X * the boldface sequence, and the end boldface sequence. X */ X if ((so_width = tgetnum("sg")) < 0) X so_width = 0; X be_width = bo_width = ue_width = ul_width = se_width = so_width; X X /* X * Get various string-valued capabilities. X */ X sp = sbuf; X X sc_pad = tgetstr("pc", &sp); X if (sc_pad != NULL) X PC = *sc_pad; X X sc_init = tgetstr("ti", &sp); X if (sc_init == NULL) X sc_init = ""; X X sc_deinit= tgetstr("te", &sp); X if (sc_deinit == NULL) X sc_deinit = ""; X X sc_eol_clear = tgetstr("ce", &sp); X if (hard || sc_eol_clear == NULL || *sc_eol_clear == '\0') X { X cannot("clear to end of line"); X sc_eol_clear = ""; X } X X sc_clear = tgetstr("cl", &sp); X if (hard || sc_clear == NULL || *sc_clear == '\0') X { X cannot("clear screen"); X sc_clear = "\n\n"; X } X X sc_move = tgetstr("cm", &sp); X if (hard || sc_move == NULL || *sc_move == '\0') X { X /* X * This is not an error here, because we don't X * always need sc_move. X * We need it only if we don't have home or lower-left. X */ X sc_move = ""; X } X X sc_s_in = tgetstr("so", &sp); X if (hard || sc_s_in == NULL) X sc_s_in = ""; X X sc_s_out = tgetstr("se", &sp); X if (hard || sc_s_out == NULL) X sc_s_out = ""; X X sc_u_in = tgetstr("us", &sp); X if (hard || sc_u_in == NULL) X sc_u_in = sc_s_in; X X sc_u_out = tgetstr("ue", &sp); X if (hard || sc_u_out == NULL) X sc_u_out = sc_s_out; X X sc_b_in = tgetstr("md", &sp); X if (hard || sc_b_in == NULL) X { X sc_b_in = sc_s_in; X sc_b_out = sc_s_out; X } else X { X sc_b_out = tgetstr("me", &sp); X if (hard || sc_b_out == NULL) X sc_b_out = ""; X } X X sc_visual_bell = tgetstr("vb", &sp); X if (hard || sc_visual_bell == NULL) X sc_visual_bell = ""; X X if (tgetflag("bs")) X sc_backspace = "\b"; X else X { X sc_backspace = tgetstr("bc", &sp); X if (sc_backspace == NULL || *sc_backspace == '\0') X sc_backspace = "\b"; X } X X /* X * Choose between using "ho" and "cm" ("home" and "cursor move") X * to move the cursor to the upper left corner of the screen. X */ X t1 = tgetstr("ho", &sp); X if (hard || t1 == NULL) X t1 = ""; X if (*sc_move == '\0') X t2 = ""; X else X { X strcpy(sp, tgoto(sc_move, 0, 0)); X t2 = sp; X sp += strlen(sp) + 1; X } X sc_home = cheaper(t1, t2, "home cursor", "|\b^"); X X /* X * Choose between using "ll" and "cm" ("lower left" and "cursor move") X * to move the cursor to the lower left corner of the screen. X */ X t1 = tgetstr("ll", &sp); X if (hard || t1 == NULL) X t1 = ""; X if (*sc_move == '\0') X t2 = ""; X else X { X strcpy(sp, tgoto(sc_move, 0, sc_height-1)); X t2 = sp; X sp += strlen(sp) + 1; X } X sc_lower_left = cheaper(t1, t2, X "move cursor to lower left of screen", "\r"); X X /* X * Choose between using "al" or "sr" ("add line" or "scroll reverse") X * to add a line at the top of the screen. X */ X t1 = tgetstr("al", &sp); X if (hard || t1 == NULL) X t1 = ""; X t2 = tgetstr("sr", &sp); X if (hard || t2 == NULL) X t2 = ""; X sc_addline = cheaper(t1, t2, "scroll backwards", ""); X if (*sc_addline == '\0') X { X /* X * Force repaint on any backward movement. X */ X back_scroll = 0; X } X} X X/* X * Return the "best" of the two given termcap strings. X * The best, if both exist, is the one with the lower X * cost (see cost() function). X */ X static char * Xcheaper(t1, t2, doit, def) X char *t1, *t2; X char *doit; X char *def; X{ X if (*t1 == '\0' && *t2 == '\0') X { X cannot(doit); X return (def); X } X if (*t1 == '\0') X return (t2); X if (*t2 == '\0') X return (t1); X if (cost(t1) < cost(t2)) X return (t1); X return (t2); X} X X/* X * Return the cost of displaying a termcap string. X * We use the trick of calling tputs, but as a char printing function X * we give it inc_costcount, which just increments "costcount". X * This tells us how many chars would be printed by using this string. X * {{ Couldn't we just use strlen? }} X */ Xstatic int costcount; X X/*ARGSUSED*/ X static void Xinc_costcount(c) X int c; X{ X costcount++; X} X X static int Xcost(t) X char *t; X{ X costcount = 0; X tputs(t, sc_height, inc_costcount); X return (costcount); X} X X X/* X * Below are the functions which perform all the X * terminal-specific screen manipulation. X */ X X X/* X * Initialize terminal X */ X public void Xinit() X{ X tputs(sc_init, sc_height, putchr); X} X X/* X * Deinitialize terminal X */ X public void Xdeinit() X{ X tputs(sc_deinit, sc_height, putchr); X} X X/* X * Home cursor (move to upper left corner of screen). X */ X public void Xhome() X{ X tputs(sc_home, 1, putchr); X} X X/* X * Add a blank line (called with cursor at home). X * Should scroll the display down. X */ X public void Xadd_line() X{ X tputs(sc_addline, sc_height, putchr); X} X X/* X * Move cursor to lower left corner of screen. X */ X public void Xlower_left() X{ X tputs(sc_lower_left, 1, putchr); X} X X/* X * Ring the terminal bell. X */ X public void Xbell() X{ X if (quiet == VERY_QUIET) X vbell(); X else X putchr('\7'); X} X X/* X * Output the "visual bell", if there is one. X */ X public void Xvbell() X{ X if (*sc_visual_bell == '\0') X return; X tputs(sc_visual_bell, sc_height, putchr); X} X X/* X * Clear the screen. X */ X public void Xclear() X{ X tputs(sc_clear, sc_height, putchr); X} X X/* X * Clear from the cursor to the end of the cursor's line. X * {{ This must not move the cursor. }} X */ X public void Xclear_eol() X{ X tputs(sc_eol_clear, 1, putchr); X} X X/* X * Begin "standout" (bold, underline, or whatever). X */ X public void Xso_enter() X{ X tputs(sc_s_in, 1, putchr); X} X X/* X * End "standout". X */ X public void Xso_exit() X{ X tputs(sc_s_out, 1, putchr); X} X X/* X * Begin "underline" (hopefully real underlining, X * otherwise whatever the terminal provides). X */ X public void Xul_enter() X{ X tputs(sc_u_in, 1, putchr); X} X X/* X * End "underline". X */ X public void Xul_exit() X{ X tputs(sc_u_out, 1, putchr); X} X X/* X * Begin "bold" X */ X public void Xbo_enter() X{ X tputs(sc_b_in, 1, putchr); X} X X/* X * End "bold". X */ X public void Xbo_exit() X{ X tputs(sc_b_out, 1, putchr); X} X X/* X * Erase the character to the left of the cursor X * and move the cursor left. X */ X public void Xbackspace() X{ X /* X * Try to erase the previous character by overstriking with a space. X */ X tputs(sc_backspace, 1, putchr); X putchr(' '); X tputs(sc_backspace, 1, putchr); X} X X/* X * Output a plain backspace, without erasing the previous char. X */ X public void Xputbs() X{ X tputs(sc_backspace, 1, putchr); X} END_OF_FILE echo shar: Extracting \"signal.c\" sed "s/^X//" >'signal.c' <<'END_OF_FILE' X/* X * Routines dealing with signals. X * X * A signal usually merely causes a bit to be set in the "signals" word. X * At some convenient time, the mainline code checks to see if any X * signals need processing by calling psignal(). X * If we happen to be reading from a file [in iread()] at the time X * the signal is received, we call intread to interrupt the iread. X */ X X#include "less.h" X#include X X/* X * "sigs" contains bits indicating signals which need to be processed. X */ Xpublic int sigs; X X#define S_INTERRUPT 01 X#ifdef SIGTSTP X#define S_STOP 02 X#endif X#if defined(SIGWINCH) || defined(SIGWIND) X#define S_WINCH 04 X#endif X Xextern int sc_width, sc_height; Xextern int swindow; Xextern int screen_trashed; Xextern int lnloop; Xextern int linenums; Xextern int scroll; Xextern int reading; X X/* X * Interrupt signal handler. X */ X static HANDLER Xinterrupt() X{ X SIGNAL(SIGINT, interrupt); X sigs |= S_INTERRUPT; X if (reading) X intread(); X} X X#ifdef SIGTSTP X/* X * "Stop" (^Z) signal handler. X */ X static HANDLER Xstop() X{ X SIGNAL(SIGTSTP, stop); X sigs |= S_STOP; X if (reading) X intread(); X} X#endif X X#ifdef SIGWINCH X/* X * "Window" change handler X */ X public HANDLER Xwinch() X{ X SIGNAL(SIGWINCH, winch); X sigs |= S_WINCH; X if (reading) X intread(); X} X#else X#ifdef SIGWIND X/* X * "Window" change handler X */ X public HANDLER Xwinch() X{ X SIGNAL(SIGWIND, winch); X sigs |= S_WINCH; X if (reading) X intread(); X} X#endif X#endif X X/* X * Set up the signal handlers. X */ X public void Xinit_signals(on) X int on; X{ X if (on) X { X /* X * Set signal handlers. X */ X (void) SIGNAL(SIGINT, interrupt); X#ifdef SIGTSTP X (void) SIGNAL(SIGTSTP, stop); X#endif X#ifdef SIGWINCH X (void) SIGNAL(SIGWINCH, winch); X#else X#ifdef SIGWIND X (void) SIGNAL(SIGWIND, winch); X#endif X#endif X } else X { X /* X * Restore signals to defaults. X */ X (void) SIGNAL(SIGINT, SIG_DFL); X#ifdef SIGTSTP X (void) SIGNAL(SIGTSTP, SIG_DFL); X#endif X#ifdef SIGWINCH X (void) SIGNAL(SIGWINCH, SIG_IGN); X#endif X#ifdef SIGWIND X (void) SIGNAL(SIGWIND, SIG_IGN); X#endif X } X} X X/* X * Process any signals we have received. X * A received signal cause a bit to be set in "sigs". X */ X public void Xpsignals() X{ X register int tsignals; X X if ((tsignals = sigs) == 0) X return; X sigs = 0; X X#ifdef S_WINCH X if (tsignals & S_WINCH) X { X int old_width, old_height; X /* X * Re-execute get_term() to read the new window size. X */ X old_width = sc_width; X old_height = sc_height; X swindow = -1; X get_term(); X if (sc_width != old_width || sc_height != old_height) X { X scroll = (sc_height + 1) / 2; X screen_trashed = 1; X } X } X#endif X#ifdef SIGTSTP X if (tsignals & S_STOP) X { X /* X * Clean up the terminal. X */ X#ifdef SIGTTOU X SIGNAL(SIGTTOU, SIG_IGN); X#endif X lower_left(); X clear_eol(); X deinit(); X flush(); X raw_mode(0); X#ifdef SIGTTOU X SIGNAL(SIGTTOU, SIG_DFL); X#endif X SIGNAL(SIGTSTP, SIG_DFL); X kill(getpid(), SIGTSTP); X /* X * ... Bye bye. ... X * Hopefully we'll be back later and resume here... X * Reset the terminal and arrange to repaint the X * screen when we get back to the main command loop. X */ X SIGNAL(SIGTSTP, stop); X raw_mode(1); X init(); X screen_trashed = 1; X } X#endif X if (tsignals & S_INTERRUPT) X { X bell(); X /* X * {{ You may wish to replace the bell() with X * error("Interrupt"); }} X */ X X /* X * If we were interrupted while in the "calculating X * line numbers" loop, turn off line numbers. X */ X if (lnloop) X { X lnloop = 0; X if (linenums == 2) X screen_trashed = 1; X linenums = 0; X error("Line numbers turned off"); X } X X } X} END_OF_FILE echo shar: Extracting \"tags.c\" sed "s/^X//" >'tags.c' <<'END_OF_FILE' X#include X#include "less.h" X X#define WHITESP(c) ((c)==' ' || (c)=='\t') X X#if TAGS X Xpublic char *tagfile; Xpublic char *tagpattern; X Xstatic char *tags = "tags"; X Xextern int linenums; Xextern int sigs; X X/* X * Find a tag in the "tags" file. X * Sets "tagfile" to the name of the file containing the tag, X * and "tagpattern" to the search pattern which should be used X * to find the tag. X */ X public int Xfindtag(tag) X register char *tag; X{ X register char *p; X register FILE *f; X register int taglen; X int search_char; X static char tline[200]; X X if ((f = fopen(tags, "r")) == NULL) X { X error("No tags file"); X tagfile = NULL; X return; X } X X taglen = strlen(tag); X X /* X * Search the tags file for the desired tag. X */ X while (fgets(tline, sizeof(tline), f) != NULL) X { X if (strncmp(tag, tline, taglen) != 0 || !WHITESP(tline[taglen])) X continue; X X /* X * Found it. X * The line contains the tag, the filename and the X * pattern, separated by white space. X * The pattern is surrounded by a pair of identical X * search characters. X * Parse the line and extract these parts. X */ X tagfile = tagpattern = NULL; X X /* X * Skip over the whitespace after the tag name. X */ X for (p = tline; !WHITESP(*p) && *p != '\0'; p++) X continue; X while (WHITESP(*p)) X p++; X if (*p == '\0') X /* File name is missing! */ X continue; X X /* X * Save the file name. X * Skip over the whitespace after the file name. X */ X tagfile = p; X while (!WHITESP(*p) && *p != '\0') X p++; X *p++ = '\0'; X while (WHITESP(*p)) X p++; X if (*p == '\0') X /* Pattern is missing! */ X continue; X X /* X * Save the pattern. X * Skip to the end of the pattern. X * Delete the initial "^" and the final "$" from the pattern. X */ X search_char = *p++; X if (*p == '^') X p++; X tagpattern = p; X while (*p != search_char && *p != '\0') X p++; X if (p[-1] == '$') X p--; X *p = '\0'; X X fclose(f); X return; X } X fclose(f); X error("No such tag in tags file"); X tagfile = NULL; X} X X/* X * Search for a tag. X * This is a stripped-down version of search(). X * We don't use search() for several reasons: X * - We don't want to blow away any search string we may have saved. X * - The various regular-expression functions (from different systems: X * regcmp vs. re_comp) behave differently in the presence of X * parentheses (which are almost always found in a tag). X */ X public int Xtagsearch() X{ X POSITION pos, linepos; X int linenum; X char *line; X X pos = (POSITION)0; X linenum = find_linenum(pos); X X for (;;) X { X /* X * Get lines until we find a matching one or X * until we hit end-of-file. X */ X if (sigs) X return (1); X X /* X * Read the next line, and save the X * starting position of that line in linepos. X */ X linepos = pos; X pos = forw_raw_line(pos, &line); X if (linenum != 0) X linenum++; X X if (pos == NULL_POSITION) X { X /* X * We hit EOF without a match. X */ X error("Tag not found"); X return (1); X } X X /* X * If we're using line numbers, we might as well X * remember the information we have now (the position X * and line number of the current line). X */ X if (linenums) X add_lnum(linenum, pos); X X /* X * Test the line to see if we have a match. X * Use strncmp because the pattern may be X * truncated (in the tags file) if it is too long. X */ X if (strncmp(tagpattern, line, strlen(tagpattern)) == 0) X break; X } X X jump_loc(linepos); X return (0); X} X X#endif END_OF_FILE echo shar: Extracting \"ttyin.c\" sed "s/^X//" >'ttyin.c' <<'END_OF_FILE' X/* X * Routines dealing with getting input from the keyboard (i.e. from the user). X */ X X#include "less.h" X Xstatic int tty; X X/* X * Open keyboard for input. X * (Just use file descriptor 2.) X */ X public void Xopen_getchr() X{ X tty = 2; X} X X/* X * Get a character from the keyboard. X */ X public int Xgetchr() X{ X char c; X int result; X X do X { X result = iread(tty, &c, sizeof(char)); X if (result == READ_INTR) X return (READ_INTR); X if (result < 0) X { X /* X * Don't call error() here, X * because error calls getchr! X */ X quit(); X } X } while (result != 1 || c == '\0'); X return (c); X} END_OF_FILE echo shar: Extracting \"version.c\" sed "s/^X//" >'version.c' <<'END_OF_FILE' X/* X * less X * Copyright (c) 1984,1985,1989 Mark Nudelman X * X * This program may be freely used and/or modified, X * with the following provisions: X * 1. This notice and the above copyright notice must remain intact. X * 2. Neither this program, nor any modification of it, X * may be sold for profit without written consent of the author. X * X * ----------------------------------------------------------------- X * Special note to whoever ported "less" to the Amiga: X * If you're going to be vain enough to splash your name on X * the screen every time someone runs less, you might at X * least credit the author. X * ----------------------------------------------------------------- X * X * This program is a paginator similar to "more", X * but allows you to move both forward and backward in the file. X * Commands are based on "more" and "vi". X * X * ----------------------- CHANGES --------------------------------- X * X * Allowed use on standard input 1/29/84 markn X * Added E, N, P commands 2/1/84 markn X * Added '=' command, 'stop' signal handling 4/17/84 markn X * Added line folding 4/20/84 markn X * v2: Fixed '=' command to use BOTTOM_PLUS_ONE, X * instead of TOP, added 'p' & 'v' commands 4/27/84 markn X * v3: Added -m and -t options, '-' command 5/3/84 markn X * v4: Added LESS environment variable 5/3/84 markn X * v5: New comments, fixed '-' command slightly 5/3/84 markn X * v6: Added -Q, visual bell 5/15/84 markn X * v7: Fixed jump_back(n) bug: n should count real X * lines, not folded lines. Also allow number X * on G command. 5/24/84 markn X * v8: Re-do -q and -Q commands 5/30/84 markn X * v9: Added "+" argument 9/25/84 markn X * v10: Fixed bug in -b argument processing 10/10/84 markn X * v11: Made error() ring bell if \n not entered. 10/18/84 markn X * ----------------------------------------------------------------- X * v12: Reorganized signal handling and made X * portable to 4.2bsd. 2/13/85 mark X * v13: Reword error message for '-' command. 2/16/85 mark X * v14: Added -bf and -bp variants of -b. 2/22/85 mark X * v15: Miscellaneous changes. 2/25/85 mark X * v16: Added -u flag for backspace processing. 3/13/85 mark X * v17: Added j and k commands, X * changed -t default. 4/13/85 mark X * v18: Rewrote signal handling code. 4/20/85 mark X * v19: Got rid of "verbose" eq_message(). 5/2/85 mark X * Made search() scroll in some cases. X * v20: Fixed screen.c ioctls for System V. 5/21/85 mark X * v21: Fixed some first_cmd bugs. 5/23/85 mark X * v22: Added support for no RECOMP nor REGCMP. 5/24/85 mark X * v23: Miscellanous changes and prettying up. 5/25/85 mark X * Posted to USENET. X * ----------------------------------------------------------------- X * v24: Added ti,te terminal init & de-init 6/3/85 Mike Kersenbrock X * v25: Added -U flag, standout mode underlining. 6/8/85 mark X * v26: Added -M flag. 6/9/85 mark X * Use underline termcap (us) if it exists. X * v27: Renamed some variables to make unique in 6/15/85 mark X * 6 chars. Minor fix to -m. X * v28: Fixed right margin bug. 6/28/85 mark X * v29: Incorporated M.Rose's changes to signal.c 6/28/85 mark X * v30: Fixed stupid bug in argument processing. 6/29/85 mark X * v31: Added -p flag, changed repaint algorithm. 7/15/85 mark X * Added kludge for magic cookie terminals. X * v32: Added cat_file if output not a tty. 7/16/85 mark X * v33: Added -e flag and EDITOR. 7/23/85 mark X * v34: Added -s flag. 7/26/85 mark X * v35: Rewrote option handling; added option.c. 7/27/85 mark X * v36: Fixed -e flag to work if not last file. 7/29/85 mark X * v37: Added -x flag. 8/10/85 mark X * v38: Changed prompting; created prompt.c. 8/19/85 mark X * v39: (Not -p) does not initially clear screen. 8/24/85 mark X * v40: Added "skipping" indicator in forw(). 8/26/85 mark X * Posted to USENET. X * ----------------------------------------------------------------- X * v41: ONLY_RETURN, control char commands, 9/17/85 mark X * faster search, other minor fixes. X * v42: Added ++ command line syntax; 9/25/85 mark X * ch_fsize for pipes. X * v43: Added -h flag, changed prim.c algorithms. 10/15/85 mark X * v44: Made END print in all cases of eof; 10/16/85 mark X * ignore SIGTTOU after receiving SIGTSTP. X * v45: Never print backspaces unless -u. 10/16/85 mark X * v46: Backwards scroll in jump_loc. 10/24/85 mark X * v47: Fixed bug in edit(): *first_cmd==0 10/30/85 mark X * v48: Use TIOCSETN instead of TIOCSETP. 11/16/85 mark X * Added marks (m and ' commands). X * Posted to USENET. X * ----------------------------------------------------------------- X * v49: Fixed bug: signal didn't clear mcc. 1/9/86 mark X * v50: Added ' (quote) to gomark. 1/15/86 mark X * v51: Added + cmd, fixed problem if first_cmd X * fails, made g cmd sort of "work" on pipes X * even if bof is no longer buffered. 1/16/86 mark X * v52: Made short files work better. 1/17/86 mark X * v53: Added -P option. 1/20/86 mark X * v54: Changed help to use HELPFILE. 1/20/86 mark X * v55: Messages work better if not tty output. 1/23/86 mark X * v56: Added -l option. 1/24/86 mark X * v57: Fixed -l to get confirmation before X * overwriting an existing file. 1/31/86 mark X * v58: Added filename globbing. 8/28/86 mark X * v59: Fixed some bugs with very long filenames. 9/15/86 mark X * v60: Incorporated changes from Leith (Casey) X * Leedom for boldface and -z option. 9/26/86 mark X * v61: Got rid of annoying repaints after ! cmd. 9/26/86 mark X * Posted to USENET. X * ----------------------------------------------------------------- X * v62: Added is_directory(); change -z default to X * -1 instead of 24; cat-and-exit if -e and X * file is less than a screenful. 12/23/86 mark X * v63: Fixed bug in cat-and-exit if > 1 file. 1/8/87 mark X * v64: Changed puts/putstr, putc/putchr, X * getc/getchr to avoid name conflict with X * stdio functions. 1/12/87 mark X * v65: Allowed '-' command to change NUMBER X * valued options (thanks to Gary Puckering) 1/26/87 mark X * v66: Fixed bug: prepaint should use force=1. 2/13/87 mark X * v67: Added !! and % expansion to ! command. 2/24/87 mark X * v68: Added SIGWINCH and TIOCGWINSZ support; X * changed is_directory to bad_file. X * (thanks to J. Robert Ward) 2/25/87 mark X * v69: Added SIGWIND and WIOCGETD (for Unix PC). 2/25/87 mark X * v70: Changed help cmd from 'h' to 'H'; better X * error msgs in bad_file, errno_message. 3/13/87 mark X * v71: Changed -p to -c, made triple -c/-C X * for clear-eol like more's -c. 5/11/87 mark X * v72: Added -E, -L, use $SHELL in lsystem(). 6/26/87 mark X * (thanks to Steve Spearman) X * v73: Allow Examine "#" for previous file. 6/26/87 mark X * Posted to USENET 8/25/87. X * ----------------------------------------------------------------- X * v74: Fix conflict in EOF symbol with stdio.h, 9/18/87 mark X * Make os.c more portable to BSD. X * v75: Fix problems in get_term (thanks to 9/23/87 mark X * Paul Eggert); new backwards scrolling in X * jump_loc (thanks to Marion Hakanson). X * v76: Added -i flag; allow single "!" to 9/23/87 mark X * invoke a shell (thanks to Franco Barber). X * v77: Added -n flag and line number support. 9/24/87 mark X * v78: Fixed problem with prompts longer than 9/25/87 mark X * the screen width. X * v79: Added the _ command. 9/29/87 mark X * v80: Allow signal to break out of linenum scan. 10/6/87 mark X * v81: Allow -b to be changed from within less. 10/6/87 mark X * v82: Add cmd_decode to use a table for key 10/7/87 mark X * binding (thanks to David Nason). X * v83: Allow .less file for user-defined keys. 10/9/87 mark X * v84: Fix -e/-E problems (thanks to Felix Lee). 10/11/87 mark X * v85: Search now keeps track of line numbers. 10/15/87 mark X * v86: Added -B option and autobuf; fixed 10/20/87 mark X * "pipe error" bug. X * v87: Fix bug re BSD signals while reading file. 3/1/88 mark X * v88: Use new format for -P option (thanks to 3/12/88 mark X * der Mouse), allow "+-c" without message, X * fix bug re BSD hangup. X * v89: Turn off line numbers if linenum scan 3/18/88 mark X * is interrupted. X * v90: Allow -P from within less. 3/30/88 mark X * v91: Added tags file support (new -t option) 3/30/88 mark X * (thanks to Brian Campbell). X * v92: Added -+option syntax. 4/4/88 mark X * v93: Add support for slow input (thanks to 4/11/88 mark X * Joe Orost & apologies for taking almost X * 3 years to get this in!) X * v94: Redo reading/signal stuff. 4/11/88 mark X * v95: Repaint screen better after signal. 4/20/88 mark X * v96: Add /! and ?! commands. 4/21/88 mark X * v97: Allow -l/-L from within less. 5/17/88 mark X * Eliminate some static arrays (use calloc). X * Posted to USENET. X * ----------------------------------------------------------------- X * v98: Fix incorrect calloc call; uninitialized 10/14/88 mark X * var in exec_mca; core dump on unknown TERM. X * Make v cmd work if past last line of file. X * Fix some signal bugs. X * v99: Allow space between -X and string, 10/29/88 mark X * when X is a string-valued option. X * v100: Fix globbing bug when $SHELL not set; 1/5/89 mark X * allow spaces after -t command. X * v101: Fix problem with long (truncated) lines 1/6/89 mark X * in tags file (thanks to Neil Dixon). X * v102: Fix bug with E# when no prev file; 1/6/89 mark X * allow spaces after -l command. X * v103: Add -N, -f and -? options. Add z and w 3/14/89 mark X * commands. Add %L for prompt strings. X * v104: Added EDITPROTO. 3/16/89 mark X * v105: Fix bug in find_linenum which cached 3/20/89 mark X * incorrectly on long lines. X * v106: Added -k option and multiple lesskey 3/31/89 mark X * files. X * v107: Add 8-bit char support and -g option. 4/27/89 mark X * Split option code into 3 files. X * v108: Allocate position table dynamically 5/5/89 mark X * (thanks to Paul Eggert); change % command X * from "percent" to vi-style brace finder. X * v109: Added ESC-% command, split prim.c. 5/10/89 mark X * v110: Fixed bug in + option; fixed repaint bug 5/24/89 mark X * under Sun windows (thanks to Paul Eggert). X * v111: Generalized # and % expansion; use 5/25/89 mark X * calloc for some error messages. X * v112: Get rid of ESC-%, add {}()[] commands. 5/30/89 mark X * v113: Optimize lseeks (thanks to Paul Eggert). 5/31/89 mark X * v114: Added ESC-/ and ESC-/! commands. 7/25/89 mark X * v115: Added ESC-n command. 7/26/89 mark X * v116: Added find_pos to optimize g command. 7/31/89 mark X * v117: Change -f option to -r. 8/1/89 mark X * v118: Save positions for all previous files, 8/2/89 mark X * not just the immediately previous one. X * v119: Save marks across file boundaries. 8/7/89 mark X * Add file handle stuff. X * v120: Add :ta command. 8/11/89 mark X * v121: Add -f option. 8/16/89 mark X * v122: Fix performance with many buffers. 8/30/89 mark X * v123: Verbose prompts for string options. 8/31/89 mark X */ X Xchar version[] = "@(#) less version 123"; END_OF_FILE