Path: utzoo!utgpu!cs.utexas.edu!usc!apple!amdcad!pyramid!ctnews!unix386!mark From: mark@unix386.Convergent.COM (Mark Nudelman) Newsgroups: alt.sources Subject: less (part 4 of 6) Message-ID: <6423@unix386.Convergent.COM> Date: 6 Mar 91 00:14:05 GMT Organization: Unisys/Convergent, San Jose, CA Lines: 2994 #! /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 \"line.c\" sed "s/^X//" >'line.c' <<'END_OF_FILE' X/* X * Routines to manipulate the "line buffer". X * The line buffer holds a line of output as it is being built X * in preparation for output to the screen. X */ X X#include "less.h" X Xstatic char linebuf[1024]; /* Buffer which holds the current output line */ Xstatic char attr[1024]; /* Extension of linebuf to hold attributes */ Xstatic int curr; /* Index into linebuf */ Xstatic int column; /* Printable length, accounting for X backspaces, etc. */ Xstatic int overstrike; /* Next char should overstrike previous char */ Xstatic int is_null_line; /* There is no current line */ Xstatic char pendc; X Xextern int bs_mode; Xextern int tabstop; Xextern int linenums; Xextern int ctldisp; Xextern int twiddle; Xextern int auto_wrap, ignaw; Xextern int bo_s_width, bo_e_width; Xextern int ul_s_width, ul_e_width; Xextern int bl_s_width, bl_e_width; Xextern int sc_width, sc_height; X X/* X * Rewind the line buffer. X */ X public void Xprewind() X{ X curr = 0; X column = 0; X overstrike = 0; X is_null_line = 0; X pendc = '\0'; X} X X/* X * Insert the line number (of the given position) into the line buffer. X */ X public void Xplinenum(pos) X POSITION pos; X{ X register int lno; X register int i; X register int n; X X /* X * We display the line number at the start of each line X * only if the -N option is set. X */ X if (linenums != 2) X return; X X /* X * Get the line number and put it in the current line. X * {{ Note: since find_linenum calls forw_raw_line, X * it may seek in the input file, requiring the caller X * of plinenum to re-seek if necessary. }} X */ X lno = find_linenum(pos); X X sprintf(&linebuf[curr], "%6d", lno); X n = strlen(&linebuf[curr]); X column += n; X for (i = 0; i < n; i++) X attr[curr++] = 0; X X /* X * Append enough spaces to bring us to the next tab stop. X * {{ We could avoid this at the cost of adding some X * complication to the tab stop logic in pappend(). }} X */ X do X { X linebuf[curr] = ' '; X attr[curr++] = 0; X column++; X } while ((column % tabstop) != 0); X} X X/* X * Return the printing width of the start (enter) sequence X * for a given character attribute. X */ X int Xattr_swidth(a) X int a; X{ X switch (a) X { X case BOLD: return (bo_s_width); X case UNDERLINE: return (ul_s_width); X case BLINK: return (bl_s_width); X } X return (0); X} X X/* X * Return the printing width of the end (exit) sequence X * for a given character attribute. X */ X int Xattr_ewidth(a) X int a; X{ X switch (a) X { X case BOLD: return (bo_e_width); X case UNDERLINE: return (ul_e_width); X case BLINK: return (bl_e_width); X } X return (0); X} X X/* X * Return the printing width of a given character and attribute, X * if the character were added to the current position in the line buffer. X * Adding a character with a given attribute may cause an enter or exit X * attribute sequence to be inserted, so this must be taken into account. X */ X static int Xpwidth(c, a) X int c; X int a; X{ X register int w; X X if (c == '\b') X /* X * Backspace moves backwards one position. X */ X return (-1); X X if (control_char(c)) X /* X * Control characters do unpredicatable things, X * so we don't even try to guess; say it doesn't move. X * This can only happen if the -r flag is in effect. X */ X return (0); X X /* X * Other characters take one space, X * plus the width of any attribute enter/exit sequence. X */ X w = 1; X if (curr > 0 && attr[curr-1] != a) X w += attr_ewidth(attr[curr-1]); X if (a && (curr == 0 || attr[curr-1] != a)) X w += attr_swidth(a); X return (w); X} X X/* X * Delete the previous character in the line buffer. X */ X static void Xbackc() X{ X curr--; X column -= pwidth(linebuf[curr], attr[curr]); X} X X/* X * Append a character and attribute to the line buffer. X */ X static int Xstorec(c, a) X int c; X int a; X{ X register int w; X X w = pwidth(c, a); X if (ctldisp > 0 && column + w + attr_ewidth(a) > sc_width) X /* X * Won't fit on screen. X */ X return (1); X X if (curr >= sizeof(linebuf)-2) X /* X * Won't fit in line buffer. X */ X return (1); X X /* X * Special handling for "magic cookie" terminals. X * If an attribute enter/exit sequence has a printing width > 0, X * and the sequence is adjacent to a space, delete the space. X * We just mark the space as invisible, to avoid having too X * many spaces deleted. X * {{ Note that even if the attribute width is > 1, we X * delete only one space. It's not worth trying to do more. X * It's hardly worth doing this much. }} X */ X if (curr > 0 && a != NORMAL && X linebuf[curr-1] == ' ' && attr[curr-1] == NORMAL && X attr_swidth(a) > 0) X { X /* X * We are about to append an enter-attribute sequence X * just after a space. Delete the space. X */ X attr[curr-1] = INVIS; X column--; X } else if (curr > 0 && attr[curr-1] != NORMAL && X attr[curr-1] != INVIS && c == ' ' && a == NORMAL && X attr_ewidth(attr[curr-1]) > 0) X { X /* X * We are about to append a space just after an X * exit-attribute sequence. Delete the space. X */ X a = INVIS; X column--; X } X /* End of magic cookie handling. */ X X linebuf[curr] = c; X attr[curr] = a; X column += w; X return (0); X} X X/* X * Append a character to the line buffer. X * Expand tabs into spaces, handle underlining, boldfacing, etc. X * Returns 0 if ok, 1 if couldn't fit in buffer. X */ X public int Xpappend(c) X register int c; X{ X if (pendc) X { X if (do_append(pendc)) X /* X * Oops. We've probably lost the char which X * was in pendc, since caller won't back up. X */ X return (1); X pendc = '\0'; X } X X if (c == '\r' && bs_mode == BS_SPECIAL) X { X /* X * Don't put the CR into the buffer until we see X * the next char. If the next char is a newline, X * discard the CR. X */ X pendc = c; X return (0); X } X X return (do_append(c)); X} X X static int Xdo_append(c) X int c; X{ X register char *s; X register int a; X X#define STOREC(c,a) if (storec((c),(a))) return (1); else curr++ X X if (overstrike) X { X /* X * Overstrike the character at the current position X * in the line buffer. This will cause either X * underline (if a "_" is overstruck), X * bold (if an identical character is overstruck), X * or just deletion of the character in the buffer. X */ X overstrike = 0; X if (c == linebuf[curr]) X STOREC(linebuf[curr], BOLD); X else if (c == '_') X STOREC(linebuf[curr], UNDERLINE); X else if (linebuf[curr] == '_') X STOREC(c, UNDERLINE); X else if (control_char(c)) X goto do_control_char; X else X STOREC(c, NORMAL); X } else if (c == '\b') X { X switch (bs_mode) X { X case BS_NORMAL: X STOREC(c, NORMAL); X break; X case BS_CONTROL: X goto do_control_char; X case BS_SPECIAL: X if (curr == 0) X break; X backc(); X overstrike = 1; X break; X } X } else if (c == '\t') X { X /* X * Expand a tab into spaces. X */ X do X { X STOREC(' ', NORMAL); X } while ((column % tabstop) != 0); X } else if (control_char(c)) X { X do_control_char: X if (ctldisp == 0) X { X /* X * Output as a normal character. X */ X STOREC(c, NORMAL); X } else X { X /* X * Output in the (blinking) ^X format. X */ X s = prchar(c); X a = BLINK; X X /* X * Make sure we can get the entire representation X * the character on this line. X */ X if (column + strlen(s) + X attr_swidth(a) + attr_ewidth(a) > sc_width) X return (1); X X for ( ; *s != 0; s++) X STOREC(*s, a); X } X } else X { X STOREC(c, NORMAL); X } X X return (0); X} X X/* X * Terminate the line in the line buffer. X */ X public void Xpdone(endline) X int endline; X{ X register char c; X X if (pendc && (pendc != '\r' || !endline)) X /* X * If we had a pending character, put it in the buffer. X * But discard a pending CR if we are at end of line X * (that is, discard the CR in a CR/LF sequence). X */ X (void) do_append(pendc); X X /* X * Add a newline if necessary, X * and append a '\0' to the end of the line. X */ X if (column < sc_width || !auto_wrap || ignaw) X { X linebuf[curr] = '\n'; X attr[curr] = NORMAL; X curr++; X } X linebuf[curr] = '\0'; X attr[curr] = NORMAL; X} X X/* X * Get a character from the current line. X * Return the character as the function return value, X * and the character attribute in *ap. X */ X public int Xgline(i, ap) X register int i; X register int *ap; X{ X if (is_null_line) X { X /* X * If there is no current line, we pretend the line is X * either "~" or "", depending on the "twiddle" flag. X */ X *ap = NORMAL; X if (twiddle) X return ("~\n"[i]); X return ("\n"[i]); X } X X *ap = attr[i]; X return (linebuf[i] & 0377); X} X X/* X * Indicate that there is no current line. X */ X public void Xnull_line() X{ X is_null_line = 1; X} X X/* X * Analogous to forw_line(), but deals with "raw lines": X * lines which are not split for screen width. X * {{ This is supposed to be more efficient than forw_line(). }} X */ X public POSITION Xforw_raw_line(curr_pos, linep) X POSITION curr_pos; X char **linep; X{ X register char *p; X register int c; X POSITION new_pos; X X if (curr_pos == NULL_POSITION || ch_seek(curr_pos) || X (c = ch_forw_get()) == EOI) X return (NULL_POSITION); X X p = linebuf; X X for (;;) X { X if (c == '\n' || c == EOI) X { X new_pos = ch_tell(); X break; X } X if (p >= &linebuf[sizeof(linebuf)-1]) X { X /* X * Overflowed the input buffer. X * Pretend the line ended here. X * {{ The line buffer is supposed to be big X * enough that this never happens. }} X */ X new_pos = ch_tell() - 1; X break; X } X *p++ = c; X c = ch_forw_get(); X } X *p = '\0'; X if (linep != NULL) X *linep = linebuf; X return (new_pos); X} X X/* X * Analogous to back_line(), but deals with "raw lines". X * {{ This is supposed to be more efficient than back_line(). }} X */ X public POSITION Xback_raw_line(curr_pos, linep) X POSITION curr_pos; X char **linep; X{ X register char *p; X register int c; X POSITION new_pos; X X if (curr_pos == NULL_POSITION || curr_pos <= ch_zero() || X ch_seek(curr_pos-1)) X return (NULL_POSITION); X X p = &linebuf[sizeof(linebuf)]; X *--p = '\0'; X X for (;;) X { X c = ch_back_get(); X if (c == '\n') X { X /* X * This is the newline ending the previous line. X * We have hit the beginning of the line. X */ X new_pos = ch_tell() + 1; X break; X } X if (c == EOI) X { X /* X * We have hit the beginning of the file. X * This must be the first line in the file. X * This must, of course, be the beginning of the line. X */ X new_pos = ch_zero(); X break; X } X if (p <= linebuf) X { X /* X * Overflowed the input buffer. X * Pretend the line ended here. X */ X new_pos = ch_tell() + 1; X break; X } X *--p = c; X } X if (linep != NULL) X *linep = p; X return (new_pos); X} END_OF_FILE echo shar: Extracting \"linenum.c\" sed "s/^X//" >'linenum.c' <<'END_OF_FILE' X/* X * Code to handle displaying line numbers. X * X * Finding the line number of a given file position is rather tricky. X * We don't want to just start at the beginning of the file and X * count newlines, because that is slow for large files (and also X * wouldn't work if we couldn't get to the start of the file; e.g. X * if input is a long pipe). X * X * So we use the function add_lnum to cache line numbers. X * We try to be very clever and keep only the more interesting X * line numbers when we run out of space in our table. A line X * number is more interesting than another when it is far from X * other line numbers. For example, we'd rather keep lines X * 100,200,300 than 100,101,300. 200 is more interesting than X * 101 because 101 can be derived very cheaply from 100, while X * 200 is more expensive to derive from 100. X * X * The function currline() returns the line number of a given X * position in the file. As a side effect, it calls add_lnum X * to cache the line number. Therefore currline is occasionally X * called to make sure we cache line numbers often enough. X */ X X#include "less.h" X#include "position.h" X X/* X * Structure to keep track of a line number and the associated file position. X * A doubly-linked circular list of line numbers is kept ordered by line number. X */ Xstruct linenum X{ X struct linenum *next; /* Link to next in the list */ X struct linenum *prev; /* Line to previous in the list */ X POSITION pos; /* File position */ X POSITION gap; /* Gap between prev and next */ X int line; /* Line number */ X}; X/* X * "gap" needs some explanation: the gap of any particular line number X * is the distance between the previous one and the next one in the list. X * ("Distance" means difference in file position.) In other words, the X * gap of a line number is the gap which would be introduced if this X * line number were deleted. It is used to decide which one to replace X * when we have a new one to insert and the table is full. X */ X X#define NPOOL 50 /* Size of line number pool */ X X#define LONGTIME (2) /* In seconds */ X Xpublic int lnloop = 0; /* Are we in the line num loop? */ X Xstatic struct linenum anchor; /* Anchor of the list */ Xstatic struct linenum *freelist; /* Anchor of the unused entries */ Xstatic struct linenum pool[NPOOL]; /* The pool itself */ Xstatic struct linenum *spare; /* We always keep one spare entry */ X Xextern int linenums; Xextern int sigs; Xextern int sc_height; X X/* X * Initialize the line number structures. X */ X public void Xclr_linenum() X{ X register struct linenum *p; X X /* X * Put all the entries on the free list. X * Leave one for the "spare". X */ X for (p = pool; p < &pool[NPOOL-2]; p++) X p->next = p+1; X pool[NPOOL-2].next = NULL; X freelist = pool; X X spare = &pool[NPOOL-1]; X X /* X * Initialize the anchor. X */ X anchor.next = anchor.prev = &anchor; X anchor.gap = 0; X anchor.pos = (POSITION)0; X anchor.line = 1; X} X X/* X * Calculate the gap for an entry. X */ X static void Xcalcgap(p) X register struct linenum *p; X{ X /* X * Don't bother to compute a gap for the anchor. X * Also don't compute a gap for the last one in the list. X * The gap for that last one should be considered infinite, X * but we never look at it anyway. X */ X if (p == &anchor || p->next == &anchor) X return; X p->gap = p->next->pos - p->prev->pos; X} X X/* X * Add a new line number to the cache. X * The specified position (pos) should be the file position of the X * FIRST character in the specified line. X */ X public void Xadd_lnum(lno, pos) X int lno; X POSITION pos; X{ X register struct linenum *p; X register struct linenum *new; X register struct linenum *nextp; X register struct linenum *prevp; X register POSITION mingap; X X /* X * Find the proper place in the list for the new one. X * The entries are sorted by position. X */ X for (p = anchor.next; p != &anchor && p->pos < pos; p = p->next) X if (p->line == lno) X /* We already have this one. */ X return; X nextp = p; X prevp = p->prev; X X if (freelist != NULL) X { X /* X * We still have free (unused) entries. X * Use one of them. X */ X new = freelist; X freelist = freelist->next; X } else X { X /* X * No free entries. X * Use the "spare" entry. X */ X new = spare; X spare = NULL; X } X X /* X * Fill in the fields of the new entry, X * and insert it into the proper place in the list. X */ X new->next = nextp; X new->prev = prevp; X new->pos = pos; X new->line = lno; X X nextp->prev = new; X prevp->next = new; X X /* X * Recalculate gaps for the new entry and the neighboring entries. X */ X calcgap(new); X calcgap(nextp); X calcgap(prevp); X X if (spare == NULL) X { X /* X * We have used the spare entry. X * Scan the list to find the one with the smallest X * gap, take it out and make it the spare. X * We should never remove the last one, so stop when X * we get to p->next == &anchor. This also avoids X * looking at the gap of the last one, which is X * not computed by calcgap. X */ X mingap = anchor.next->gap; X for (p = anchor.next; p->next != &anchor; p = p->next) X { X if (p->gap <= mingap) X { X spare = p; X mingap = p->gap; X } X } X spare->next->prev = spare->prev; X spare->prev->next = spare->next; X } X} X X/* X * If we get stuck in a long loop trying to figure out the X * line number, print a message to tell the user what we're doing. X */ X static void Xlongloopmessage() X{ X ierror("Calculating line numbers", NULL_PARG); X /* X * Set the lnloop flag here, so if the user interrupts while X * we are calculating line numbers, the signal handler will X * turn off line numbers (linenums=0). X */ X lnloop = 1; X} X Xstatic int loopcount; X#if GET_TIME Xstatic long startime; X#endif X X static void Xlongish() X{ X#if GET_TIME X if (loopcount >= 0 && ++loopcount > 100) X { X loopcount = 0; X if (get_time() >= startime + LONGTIME) X { X longloopmessage(); X loopcount = -1; X } X } X#else X if (loopcount >= 0 && ++loopcount > LONGLOOP) X { X longloopmessage(); X loopcount = -1; X } X#endif X} X X/* X * Find the line number associated with a given position. X * Return 0 if we can't figure it out. X */ X public int Xfind_linenum(pos) X POSITION pos; X{ X register struct linenum *p; X register int lno; X POSITION cpos; X X if (!linenums) X /* X * We're not using line numbers. X */ X return (0); X if (pos == NULL_POSITION) X /* X * Caller doesn't know what he's talking about. X */ X return (0); X if (pos <= ch_zero()) X /* X * Beginning of file is always line number 1. X */ X return (1); X X /* X * Find the entry nearest to the position we want. X */ X for (p = anchor.next; p != &anchor && p->pos < pos; p = p->next) X continue; X if (p->pos == pos) X /* Found it exactly. */ X return (p->line); X X /* X * This is the (possibly) time-consuming part. X * We start at the line we just found and start X * reading the file forward or backward till we X * get to the place we want. X * X * First decide whether we should go forward from the X * previous one or backwards from the next one. X * The decision is based on which way involves X * traversing fewer bytes in the file. X */ X flush(); X#if GET_TIME X startime = get_time(); X#endif X if (p == &anchor || pos - p->prev->pos < p->pos - pos) X { X /* X * Go forward. X */ X p = p->prev; X if (ch_seek(p->pos)) X return (0); X loopcount = 0; X for (lno = p->line, cpos = p->pos; cpos < pos; lno++) X { X /* X * Allow a signal to abort this loop. X */ X cpos = forw_raw_line(cpos, (char **)NULL); X if (sigs || cpos == NULL_POSITION) X return (0); X longish(); X } X lnloop = 0; X /* X * We might as well cache it. X */ X add_lnum(lno, cpos); X /* X * If the given position is not at the start of a line, X * make sure we return the correct line number. X */ X if (cpos > pos) X lno--; X } else X { X /* X * Go backward. X */ X if (ch_seek(p->pos)) X return (0); X loopcount = 0; X for (lno = p->line, cpos = p->pos; cpos > pos; lno--) X { X /* X * Allow a signal to abort this loop. X */ X cpos = back_raw_line(cpos, (char **)NULL); X if (sigs || cpos == NULL_POSITION) X return (0); X longish(); X } X lnloop = 0; X /* X * We might as well cache it. X */ X add_lnum(lno, cpos); X } X X return (lno); X} X X/* X * Find the position of a given line number. X * Return NULL_POSITION if we can't figure it out. X */ X public POSITION Xfind_pos(lno) X int lno; X{ X register struct linenum *p; X POSITION cpos; X int clno; X X if (lno <= 1) X /* X * Line number 1 is beginning of file. X */ X return (ch_zero()); X X /* X * Find the entry nearest to the line number we want. X */ X for (p = anchor.next; p != &anchor && p->line < lno; p = p->next) X continue; X if (p->line == lno) X /* Found it exactly. */ X return (p->pos); X X flush(); X if (p == &anchor || lno - p->prev->line < p->line - lno) X { X /* X * Go forward. X */ X p = p->prev; X if (ch_seek(p->pos)) X return (NULL_POSITION); X for (clno = p->line, cpos = p->pos; clno < lno; clno++) X { X /* X * Allow a signal to abort this loop. X */ X cpos = forw_raw_line(cpos, (char **)NULL); X if (sigs || cpos == NULL_POSITION) X return (NULL_POSITION); X } X } else X { X /* X * Go backward. X */ X if (ch_seek(p->pos)) X return (NULL_POSITION); X for (clno = p->line, cpos = p->pos; clno > lno; clno--) X { X /* X * Allow a signal to abort this loop. X */ X cpos = back_raw_line(cpos, (char **)NULL); X if (sigs || cpos == NULL_POSITION) X return (NULL_POSITION); X } X } X /* X * We might as well cache it. X */ X add_lnum(clno, cpos); X return (cpos); X} X X/* X * Return the line number of the "current" line. X * The argument "where" tells which line is to be considered X * the "current" line (e.g. TOP, BOTTOM, MIDDLE, etc). X */ X public int Xcurrline(where) X int where; X{ X POSITION pos; X POSITION len; X int lnum; X X pos = position(where); X len = ch_length(); X while (pos == NULL_POSITION && where >= 0 && where < sc_height) X pos = position(++where); X if (pos == NULL_POSITION) X pos = len; X lnum = find_linenum(pos); X if (pos == len) X lnum--; X return (lnum); X} END_OF_FILE echo shar: Extracting \"main.c\" sed "s/^X//" >'main.c' <<'END_OF_FILE' X/* X * Entry point, initialization, miscellaneous routines. X */ X X#include "less.h" X#include "position.h" X Xpublic int ispipe; Xpublic char * every_first_cmd = NULL; Xpublic int new_file; Xpublic int is_tty; Xpublic IFILE curr_ifile = NULL_IFILE; Xpublic IFILE old_ifile = NULL_IFILE; Xpublic struct scrpos initial_scrpos; Xpublic int any_display = 0; Xpublic int scroll; Xpublic char * progname; Xpublic int quitting; X Xextern int file; Xextern int quit_at_eof; Xextern int hit_eof; Xextern int cbufs; Xextern int errmsgs; Xextern int screen_trashed; Xextern int force_open; X X#if LOGFILE Xpublic int logfile = -1; Xpublic int force_logfile = 0; Xpublic char * namelogfile = NULL; X#endif X X#if EDITOR Xpublic char * editor; Xpublic char * editproto; X#endif X X#if TAGS Xextern char * tagfile; Xextern char * tagpattern; Xextern int tagoption; X#endif X X X X/* X * Entry point. X */ Xmain(argc, argv) X int argc; X char *argv[]; X{ X IFILE h; X int nofiles; X extern char *getenv(); X X progname = *argv++; X X /* X * Process command line arguments and LESS environment arguments. X * Command line arguments override environment arguments. X */ X init_prompt(); X init_charset(); X init_option(); X scan_option(getenv("LESS")); X X#define isoptstring(s) (((s)[0] == '-' || (s)[0] == '+') && (s)[1] != '\0') X while (--argc > 0 && (isoptstring(argv[0]) || isoptpending())) X scan_option(*argv++); X#undef isoptstring X X if (isoptpending()) X { X /* X * Last command line option was a flag requiring a X * following string, but there was no following string. X */ X nopendopt(); X quit(0); X } X X#if USERFILE X /* X * Try to use the lesskey file "$HOME/.less". X */ X add_hometable(); X#endif X#if EDITOR X editor = getenv("EDITOR"); X if (editor == NULL || *editor == '\0') X editor = EDIT_PGM; X editproto = getenv("LESSEDIT"); X if (editproto == NULL || *editproto == '\0') X editproto = "%E ?lm+%lm. %f"; X#endif X X /* X * Set up terminal, etc. X */ X is_tty = isatty(1); X if (!is_tty) X { X /* X * Output is not a tty. X * Just copy the input file(s) to output. X */ X if (argc <= 0) X { X if (edit("-", 0) == 0) X cat_file(); X } else X { X while (--argc >= 0) X { X if (edit(*argv++, 0) == 0) X cat_file(); X } X } X quit(0); X } X X /* X * Call get_ifile with all the command line filenames X * to "register" them with the ifile system. X */ X h = NULL_IFILE; X while (--argc >= 0) X h = get_ifile(*argv++, h); X X init_mark(); X raw_mode(1); X get_term(); X open_getchr(); X X init_signals(1); X X /* X * Select the first file to examine. X */ X#if TAGS X if (tagoption) X { X /* X * A -t option was given. X * Verify that no filenames were also given. X * Edit the file selected by the "tags" search, X * and search for the proper line in the file. X */ X if (nifile() > 0) X { X error("No filenames allowed with -t option", NULL_PARG); X quit(1); X } X if (tagfile == NULL) X quit(1); X if (edit(tagfile, 0) || tagsearch()) X quit(1); X } else X#endif X if (nifile() == 0) X nofiles = edit("-", 0); /* Standard input */ X else X nofiles = edit_first(); X X if (nofiles) X { X quit(1); X /*NOTREACHED*/ X } X X init(); X commands(); X quit(0); X /*NOTREACHED*/ X} X X/* X * Copy a string, truncating to the specified length if necessary. X * Unlike strncpy(), the resulting string is guaranteed to be null-terminated. X */ X public void Xstrtcpy(to, from, len) X char *to; X char *from; X unsigned int len; X{ X strncpy(to, from, len); X to[len-1] = '\0'; X} X X/* X * Copy a string to a "safe" place X * (that is, to a buffer allocated by calloc). X */ X public char * Xsave(s) X char *s; X{ X register char *p; X X p = (char *) ecalloc(strlen(s)+1, sizeof(char)); X strcpy(p, s); X return (p); X} X X public VOID_POINTER Xecalloc(count, size) X int count; X unsigned int size; X{ X register VOID_POINTER p; X X p = calloc(count, size); X if (p != NULL) X return (p); X error("Cannot allocate memory", NULL_PARG); X quit(1); X /*NOTREACHED*/ X} X X/* X * Skip leading spaces in a string. X */ X public char * Xskipsp(s) X register char *s; X{ X while (*s == ' ' || *s == '\t') X s++; X return (s); X} X X/* X * Exit the program. X */ X public void Xquit(status) X int status; X{ X static int save_status; X X /* X * Put cursor at bottom left corner, clear the line, X * reset the terminal modes, and exit. X */ X if (status < 0) X status = save_status; X else X save_status = status; X quitting = 1; X#if LOGFILE X end_logfile(); X#endif X if (any_display) X { X lower_left(); X clear_eol(); X } X deinit(); X flush(); X raw_mode(0); X#if __MSDOS__ X restore_screen(); X /* X * If we don't close 2, we get some garbage from X * 2's buffer when it flushes automatically. X * I cannot track this one down RB X * The same bug shows up if we use ^C^C to abort. X */ X close(2); X#endif X exit(status); X} END_OF_FILE echo shar: Extracting \"edit.c\" sed "s/^X//" >'edit.c' <<'END_OF_FILE' X#include "less.h" X X#if __MSDOS__ X#include X#include X#include X#include X#endif X X#define ISPIPE(fd) ((fd)==0) Xextern int ispipe; Xextern int new_file; Xextern int errmsgs; Xextern int quit_at_eof; Xextern int hit_eof; Xextern int file; Xextern int cbufs; Xextern char *every_first_cmd; Xextern int any_display; Xextern int force_open; Xextern int is_tty; Xextern IFILE curr_ifile; Xextern IFILE old_ifile; Xextern struct scrpos initial_scrpos; X X#if LOGFILE Xextern int logfile; Xextern int force_logfile; Xextern char *namelogfile; X#endif X X X/* X * Edit a new file. X * Filename == "-" means standard input. X * Filename == NULL means just close the current file. X */ X public int Xedit(filename, just_looking) X register char *filename; X int just_looking; X{ X register int f; X register char *m; X int answer; X int no_display; X struct scrpos scrpos; X PARG parg; X X if (filename == NULL) X { X /* X * Close the current file, but don't open a new one. X */ X f = -1; X } else if (strcmp(filename, "-") == 0) X { X /* X * Use standard input. X */ X f = 0; X } else if ((parg.p_string = bad_file(filename)) != NULL) X { X error("%s", &parg); X free(parg.p_string); X return (1); X#if __MSDOS__ X } else if ((f = open(filename, O_RDONLY|O_BINARY)) < 0) X#else X } else if ((f = open(filename, 0)) < 0) X#endif X { X parg.p_string = errno_message(filename); X error("%s", &parg); X free(parg.p_string); X return (1); X } else if (!force_open && !just_looking && binary_file(f)) X { X parg.p_string = filename; X answer = query("\"%s\" may be a binary file. Continue? ", X &parg); X if (answer != 'y' && answer != 'Y') X { X close(f); X return (1); X } X } X X if (f >= 0 && isatty(f)) X { X /* X * Not really necessary to call this an error, X * but if the control terminal (for commands) X * and the input file (for data) are the same, X * we get weird results at best. X */ X#if __MSDOS__ X parg.p_string = "less -?"; X#else X parg.p_string = "less -\\?"; X#endif X error("Cannot take input from a terminal (\"%s\" for help)", X &parg); X if (!ISPIPE(f)) X close(f); X return (1); X } X X#if LOGFILE X if (f >= 0 && ISPIPE(f) && namelogfile != NULL && is_tty) X use_logfile(); X#endif X X /* X * We are now committed to using the new file. X * Close the current input file and set up to use the new one. X */ X if (curr_ifile != NULL_IFILE) X { X /* X * Save the current position so that we can return to X * the same position if we edit this file again. X */ X get_scrpos(&scrpos); X if (scrpos.pos != NULL_POSITION) X { X store_pos(curr_ifile, &scrpos); X lastmark(); X } X } X X /* X * Close the current file, unless it is a pipe. X */ X if (!ISPIPE(file)) X close(file); X file = f; X X if (f < 0) X return (1); X X /* X * Get the new ifile. X * Get the saved position for that file. X */ X old_ifile = curr_ifile; X curr_ifile = get_ifile(filename, curr_ifile); X get_pos(curr_ifile, &initial_scrpos); X X ispipe = ISPIPE(f); X if (ispipe) X ch_pipe(); X else X ch_nonpipe(); X (void) ch_nbuf(cbufs); X ch_flush(); X X new_file = 1; X X#if __MSDOS__ X top_filename(); X#endif X X if (every_first_cmd != NULL) X ungetsc(every_first_cmd); X X no_display = !any_display; X flush(); X any_display = 1; X X if (is_tty) X { X /* X * Output is to a real tty. X */ X X /* X * Indicate there is nothing displayed yet. X */ X pos_clear(); X clr_linenum(); X if (no_display && errmsgs > 0) X { X /* X * We displayed some messages on error output X * (file descriptor 2; see error() function). X * Before erasing the screen contents, X * display the file name and wait for a keystroke. X */ X parg.p_string = filename; X error("%s", &parg); X } X } X return (0); X} X X/* X * Edit a space-separated list of files. X * For each filename in the list, enter it into the ifile list. X * Then edit the first one. X */ X public void Xedit_list(list) X char *list; X{ X register char *s; X register char *es; X register char *filename; X char *good_filename; X IFILE save_curr_ifile; X X /* X * good_filename keeps track of the first valid filename. X */ X good_filename = NULL; X s = list; X es = s + strlen(s); X save_curr_ifile = curr_ifile; X while ((s = skipsp(s)) < es) X { X /* X * Get the next filename and null terminate it. X */ X filename = s; X while (*s != ' ' && *s != '\0') X s++; X if (*s != '\0') X *s++ = '\0'; X /* X * Try to edit the file. X * This enters it into the command line list (if it is good). X * If it is the first good file we've seen, remember it. X * {{ A little weirdness here: if any of the filenames X * are already in the list, subsequent ones get X * entered after the position where that one already X * was, instead of at the end. }} X */ X if (edit(filename, 1) == 0 && good_filename == NULL) X good_filename = filename; X } X X /* X * Edit the first valid filename in the list. X */ X if (good_filename != NULL) X { X curr_ifile = save_curr_ifile; X (void) edit(good_filename, 0); X } X} X X/* X * Edit the first file in the command line (ifile) list. X */ X public int Xedit_first() X{ X curr_ifile = NULL_IFILE; X return (edit_next(1)); X} X X/* X * Edit the last file in the command line (ifile) list. X */ X public int Xedit_last() X{ X curr_ifile = NULL_IFILE; X return (edit_prev(1)); X} X X X/* X * Edit the next file in the command line (ifile) list. X */ X public int Xedit_next(n) X int n; X{ X IFILE h; X X h = curr_ifile; X while (--n >= 0 || edit(get_filename(h), 0)) X { X if ((h = next_ifile(h)) == NULL_IFILE) X /* X * Reached end of the ifile list. X */ X return (1); X } X /* X * Found a file that we can edit. X */ X return (0); X} X X/* X * Edit the previous file in the command line list. X */ X public int Xedit_prev(n) X int n; X{ X IFILE h; X X h = curr_ifile; X while (--n >= 0 || edit(get_filename(h), 0)) X { X if ((h = prev_ifile(h)) == NULL_IFILE) X /* X * Reached beginning of the ifile list. X */ X return (1); X } X /* X * Found a file that we can edit. X */ X return (0); X} X X/* X * Edit a specific file in the command line (ifile) list. X */ X public int Xedit_index(n) X int n; X{ X IFILE h; X X h = NULL_IFILE; X do X { X if ((h = next_ifile(h)) == NULL_IFILE) X { X /* X * Reached end of the list without finding it. X */ X return (1); X } X } while (get_index(h) != n); X X return (edit(get_filename(h), 0)); X} X X/* X * Copy a file directly to standard output. X * Used if standard output is not a tty. X */ X public void Xcat_file() X{ X register int c; X X while ((c = ch_forw_get()) != EOI) X putchr(c); X flush(); X} X X#if LOGFILE X X/* X * If the user asked for a log file and our input file X * is standard input, create the log file. X * We take care not to blindly overwrite an existing file. X */ X public void Xuse_logfile() X{ X register int exists; X register int answer; X PARG parg; X X end_logfile(); X X /* X * {{ We could use access() here. }} X */ X exists = open(namelogfile, 0); X close(exists); X exists = (exists >= 0); X X /* X * Decide whether to overwrite the log file or append to it. X * (If it doesn't exist we "overwrite" it. X */ X if (!exists || force_logfile) X { X /* X * Overwrite (or create) the log file. X */ X answer = 'O'; X } else X { X /* X * Ask user what to do. X */ X parg.p_string = namelogfile; X answer = query("Warning: \"%s\" exists; Overwrite, Append or Don't log? ", &parg); X } X Xloop: X switch (answer) X { X case 'O': case 'o': X /* X * Overwrite: create the file. X */ X logfile = creat(namelogfile, 0644); X break; X case 'A': case 'a': X /* X * Append: open the file and seek to the end. X */ X#if __MSDOS__ X logfile = open(namelogfile, O_APPEND|O_WRONLY); X#else X logfile = open(namelogfile, 1); X#endif X if (lseek(logfile, (offset_t)0, 2) == BAD_LSEEK) X { X close(logfile); X logfile = -1; X } X break; X case 'D': case 'd': X /* X * Don't do anything. X */ X return; X case 'q': X quit(0); X /*NOTREACHED*/ X default: X /* X * Eh? X */ X answer = query("Overwrite, Append, or Don't log? ", NULL_PARG); X goto loop; X } X X if (logfile < 0) X { X /* X * Error in opening logfile. X */ X parg.p_string = namelogfile; X error("Cannot write to \"%s\"", &parg); X } X} X X#endif END_OF_FILE echo shar: Extracting \"option.c\" sed "s/^X//" >'option.c' <<'END_OF_FILE' X/* X * Process command line options. X * X * Each option is a single letter which controls a program variable. X * The options have defaults which may be changed via X * the command line option, toggled via the "-" command, X * or queried via the "_" command. X */ X X#include "less.h" X#include "option.h" X Xstatic struct option *pendopt; Xpublic int plusoption; X Xstatic char *propt(); Xstatic char *optstring(); X Xextern int screen_trashed; Xextern char *every_first_cmd; X X/* X * Scan an argument (either from the command line or from the X * LESS environment variable) and process it. X */ X public void Xscan_option(s) X char *s; X{ X register struct option *o; X register int c; X char *str; X int set_default; X PARG parg; X X if (s == NULL) X return; X X /* X * If we have a pending string-valued option, handle it now. X * This happens if the previous option was, for example, "-P" X * without a following string. In that case, the current X * option is simply the string for the previous option. X */ X if (pendopt != NULL) X { X (*pendopt->ofunc)(INIT, s); X pendopt = NULL; X return; X } X X set_default = 0; X X while (*s != '\0') X { X /* X * Check some special cases first. X */ X switch (c = *s++) X { X case ' ': X case '\t': X case END_OPTION_STRING: X continue; X case '-': X /* X * "-+" means set these options back to their defaults. X * (They may have been set otherwise by previous X * options.) X */ X if (set_default = (*s == '+')) X s++; X continue; X case '+': X /* X * An option prefixed by a "+" is ungotten, so X * that it is interpreted as less commands X * processed at the start of the first input file. X * "++" means process the commands at the start of X * EVERY input file. X */ X plusoption = 1; X if (*s == '+') X every_first_cmd = save(++s); X ungetsc(s); X s = optstring(s, c); X continue; X case '0': case '1': case '2': case '3': case '4': X case '5': case '6': case '7': case '8': case '9': X /* X * Special "more" compatibility form "-" X * instead of -z to set the scrolling X * window size. X */ X s--; X c = 'z'; X break; X } X X /* X * Not a special case. X * Look up the option letter in the option table. X */ X o = findopt(c); X if (o == NULL) X { X parg.p_string = propt(c); X error("There is no %s flag (\"less -\\?\" for help)", X &parg); X quit(1); X } X X switch (o->otype & OTYPE) X { X case BOOL: X if (set_default) X *(o->ovar) = o->odefault; X else X *(o->ovar) = ! o->odefault; X break; X case TRIPLE: X if (set_default) X *(o->ovar) = o->odefault; X else X *(o->ovar) = toggle_triple(o->odefault, X (o->oletter == c)); X break; X case STRING: X if (*s == '\0') X { X /* X * Set pendopt and return. X * We will get the string next time X * scan_option is called. X */ X pendopt = o; X return; X } X /* X * Don't do anything here. X * All processing of STRING options is done by X * the handling function. X */ X str = s; X s = optstring(s, c); X break; X case NUMBER: X *(o->ovar) = getnum(&s, c, (int*)NULL); X break; X } X /* X * If the option has a handling function, call it. X */ X if (o->ofunc != NULL) X (*o->ofunc)(INIT, str); X } X} X X/* X * Toggle command line flags from within the program. X * Used by the "-" and "_" commands. X * how_toggle may be: X * OPT_NO_TOGGLE just report the current setting, without changing it. X * OPT_TOGGLE invert the current setting X * OPT_UNSET set to the default value X * OPT_SET set to the inverse of the default value X */ X public void Xtoggle_option(c, s, how_toggle) X int c; X char *s; X int how_toggle; X{ X register struct option *o; X register int num; X int err; X PARG parg; X X /* X * Look up the option letter in the option table. X */ X o = findopt(c); X if (o == NULL) X { X parg.p_string = propt(c); X error("There is no %s flag", &parg); X return; X } X X if (how_toggle == OPT_TOGGLE && (o->otype & NO_TOGGLE)) X { X parg.p_string = propt(c); X error("Cannot change the %s flag", &parg); X return; X } X X /* X * Check for something which appears to be a do_toggle X * (because the "-" command was used), but really is not. X * This could be a string option with no string, or X * a number option with no number. X */ X switch (o->otype & OTYPE) X { X case STRING: X case NUMBER: X if (how_toggle == OPT_TOGGLE && *s == '\0') X how_toggle = OPT_NO_TOGGLE; X break; X } X X /* X * Now actually toggle (change) the variable. X */ X if (how_toggle != OPT_NO_TOGGLE) X { X switch (o->otype & OTYPE) X { X case BOOL: X /* X * Boolean. X */ X switch (how_toggle) X { X case OPT_TOGGLE: X *(o->ovar) = ! *(o->ovar); X break; X case OPT_UNSET: X *(o->ovar) = o->odefault; X break; X case OPT_SET: X *(o->ovar) = ! o->odefault; X break; X } X break; X case TRIPLE: X /* X * Triple: X * If user gave the lower case letter, then switch X * to 1 unless already 1, in which case make it 0. X * If user gave the upper case letter, then switch X * to 2 unless already 2, in which case make it 0. X */ X switch (how_toggle) X { X case OPT_TOGGLE: X *(o->ovar) = toggle_triple(*(o->ovar), X o->oletter == c); X break; X case OPT_UNSET: X *(o->ovar) = o->odefault; X break; X case OPT_SET: X *(o->ovar) = toggle_triple(o->odefault, X o->oletter == c); X break; X } X break; X case STRING: X /* X * String: don't do anything here. X * The handling function will do everything. X */ X switch (how_toggle) X { X case OPT_SET: X case OPT_UNSET: X error("Can't use \"-+\" or \"--\" for a string flag", X NULL_PARG); X return; X } X break; X case NUMBER: X /* X * Number: set the variable to the given number. X */ X switch (how_toggle) X { X case OPT_TOGGLE: X num = getnum(&s, '\0', &err); X if (!err) X *(o->ovar) = num; X break; X case OPT_UNSET: X *(o->ovar) = o->odefault; X break; X case OPT_SET: X error("Can't use \"--\" for a numeric flag", X NULL_PARG); X return; X } X break; X } X } X X /* X * Call the handling function for any special action X * specific to this option. X */ X if (o->ofunc != NULL) X (*o->ofunc)((how_toggle==OPT_NO_TOGGLE) ? QUERY : TOGGLE, s); X X /* X * Print a message describing the new setting. X */ X switch (o->otype & OTYPE) X { X case BOOL: X case TRIPLE: X /* X * Print the odesc message. X */ X error(o->odesc[*(o->ovar)], NULL_PARG); X break; X case NUMBER: X /* X * The message is in odesc[1] and has a %d for X * the value of the variable. X */ X parg.p_int = *(o->ovar); X error(o->odesc[1], &parg); X break; X case STRING: X /* X * Message was already printed by the handling function. X */ X break; X } X X if (how_toggle != OPT_NO_TOGGLE && (o->otype & REPAINT)) X screen_trashed = 1; X} X X/* X * "Toggle" a triple-valued option. X */ X static int Xtoggle_triple(val, lc) X int val; X int lc; X{ X if (lc) X return ((val == 1) ? 0 : 1); X else X return ((val == 2) ? 0 : 2); X} X X/* X * Return a string suitable for printing as the "name" of an option. X * For example, if the option letter is 'x', just return "-x". X */ X static char * Xpropt(c) X int c; X{ X static char buf[8]; X X sprintf(buf, "-%s", prchar(c)); X return (buf); X} X X/* X * Determine if an option is a single character option (BOOL or TRIPLE), X * or if it a multi-character option (NUMBER). X */ X public int Xsingle_char_option(c) X int c; X{ X register struct option *o; X X o = findopt(c); X if (o == NULL) X return (1); X return (o->otype & (BOOL|TRIPLE|NOVAR|NO_TOGGLE)); X} X X/* X * Return the prompt to be used for a given option letter. X * Only string and number valued options have prompts. X */ X public char * Xopt_prompt(c) X int c; X{ X register struct option *o; X X o = findopt(c); X if (o == NULL || (o->otype & (STRING|NUMBER)) == 0) X return (NULL); X return (o->odesc[0]); X} X X/* X * Return whether or not there is a string option pending; X * that is, if the previous option was a string-valued option letter X * (like -P) without a following string. X * In that case, the current option is taken to be the string for X * the previous option. X */ X public int Xisoptpending() X{ X return (pendopt != NULL); X} X X/* X * Print error message about missing string. X */ X static void Xnostring(c) X int c; X{ X PARG parg; X parg.p_string = propt(c); X error("String is required after %s", &parg); X} X X/* X * Print error message if a STRING type option is not followed by a string. X */ X public void Xnopendopt() X{ X nostring(pendopt->oletter); X} X X/* X * Scan to end of string or to an END_OPTION_STRING character. X * In the latter case, replace the char with a null char. X * Return a pointer to the remainder of the string, if any. X */ X static char * Xoptstring(s, c) X char *s; X int c; X{ X register char *p; X X if (*s == '\0') X { X nostring(c); X quit(1); X } X for (p = s; *p != '\0'; p++) X if (*p == END_OPTION_STRING) X { X *p = '\0'; X return (p+1); X } X return (p); X} X X/* X * Translate a string into a number. X * Like atoi(), but takes a pointer to a char *, and updates X * the char * to point after the translated number. X */ X public int Xgetnum(sp, c, errp) X char **sp; X int c; X int *errp; X{ X register char *s; X register int n; X register int neg; X PARG parg; X X s = skipsp(*sp); X neg = 0; X if (*s == '-') X { X neg = 1; X s++; X } X if (*s < '0' || *s > '9') X { X if (errp != NULL) X { X *errp = 1; X return (-1); X } X parg.p_string = propt(c); X error("Number is required after %s", &parg); X quit(1); X } X X n = 0; X while (*s >= '0' && *s <= '9') X n = 10 * n + *s++ - '0'; X *sp = s; X if (errp != NULL) X *errp = 0; X if (neg) X n = -n; X return (n); X} END_OF_FILE echo shar: Extracting \"optfunc.c\" sed "s/^X//" >'optfunc.c' <<'END_OF_FILE' X/* X * Handling functions for command line options. X * X * Most options are handled by the generic code in option.c. X * But all string options, and a few non-string options, require X * special handling specific to the particular option. X * This special processing is done by the "handling functions" in this file. X * X * Each handling function is passed a "type" and, if it is a string X * option, the string which should be "assigned" to the option. X * The type may be one of: X * INIT The option is being initialized from the command line. X * TOGGLE The option is being changed from within the program. X * QUERY The setting of the option is merely being queried. X */ X X#include "less.h" X#include "option.h" X Xextern int nbufs; Xextern int ispipe; Xextern int cbufs; Xextern int pr_type; Xextern int nohelp; Xextern int plusoption; Xextern char *prproto[]; Xextern char *eqproto; Xextern IFILE curr_ifile; X#if LOGFILE Xextern char *namelogfile; Xextern int force_logfile; Xextern int logfile; Xextern char *glob(); X#endif X#if TAGS Xpublic int tagoption = 0; Xextern char *tagfile; Xextern char *tagpattern; Xextern char *tags; X#endif X#if __MSDOS__ Xpublic char *window_box = NULL; Xextern int directvideo; Xextern int output_mode; X#endif X X X#if LOGFILE X/* X * Handler for -o option. X */ X public void Xopt_o(type, s) X int type; X char *s; X{ X PARG parg; X X switch (type) X { X case INIT: X namelogfile = s; X break; X case TOGGLE: X if (!ispipe) X { X error("Input is not a pipe", NULL_PARG); X return; X } X if (logfile >= 0) X { X error("Log file is already in use", NULL_PARG); X return; X } X s = skipsp(s); X namelogfile = glob(s); X if (namelogfile == NULL) X namelogfile = save(s); X use_logfile(); X sync_logfile(); X break; X case QUERY: X if (logfile < 0) X error("No log file", NULL_PARG); X else X { X parg.p_string = namelogfile; X error("Log file \"%s\"", &parg); X } X break; X } X} X X/* X * Handler for -O option. X */ X public void Xopt__O(type, s) X int type; X char *s; X{ X force_logfile = 1; X opt_o(type, s); X} X X/* X * Handlers for obsolete -l and -L options. X */ X public void Xopt_l(type, s) X int type; X char *s; X{ X error("The -l option is obsolete. Use -o", NULL_PARG); X} X X public void Xopt__L(type, s) X int type; X char *s; X{ X error("The -L option is obsolete. Use -O", NULL_PARG); X} X#endif X X#if USERFILE X public void Xopt_k(type, s) X int type; X char *s; X{ X PARG parg; X X switch (type) X { X case INIT: X if (add_cmdtable(s)) X { X parg.p_string = s; X error("Cannot use lesskey file \"%s\"", &parg); X } X break; X case QUERY: X case TOGGLE: X error("Cannot query the -k flag", NULL_PARG); X break; X } X} X#endif X X#if TAGS X/* X * Handler for -t option. X */ X public void Xopt_t(type, s) X int type; X char *s; X{ X char *curr_filename; X X switch (type) X { X case INIT: X tagoption = 1; X findtag(s); X break; X case TOGGLE: X findtag(skipsp(s)); X if (tagfile != NULL) X { X curr_filename = get_filename(curr_ifile); X if (edit(tagfile, 0) == 0) X if (tagsearch()) X (void) edit(curr_filename, 0); X } X break; X case QUERY: X error("Tag is required after -t", NULL_PARG); X break; X } X} X X/* X * Handler for -T option. X */ X public void Xopt__T(type, s) X int type; X char *s; X{ X PARG parg; X X switch (type) X { X case INIT: X tags = s; X break; X case TOGGLE: X s = skipsp(s); X tags = glob(s); X if (tags == NULL) X tags = save(s); X break; X case QUERY: X parg.p_string = tags; X error("Tags file \"%s\"", &parg); X break; X } X} X#endif X X/* X * Handler for -p option. X */ X public void Xopt_p(type, s) X int type; X register char *s; X{ X switch (type) X { X case INIT: X /* X * Unget a search command for the specified string. X * {{ This won't work if the "/" command is X * changed or invalidated by a .lesskey file. }} X */ X plusoption = 1; X ungetsc(s); X ungetsc("/"); X break; X case QUERY: X error("Pattern is required after -p", NULL_PARG); X break; X } X} X X/* X * Handler for -P option. X */ X public void Xopt__P(type, s) X int type; X register char *s; X{ X register char **proto; X PARG parg; X X switch (type) X { X case INIT: X case TOGGLE: X /* X * Figure out which prototype string should be changed. X */ X switch (*s) X { X case 'm': proto = &prproto[PR_MEDIUM]; s++; break; X case 'M': proto = &prproto[PR_LONG]; s++; break; X case '=': proto = &eqproto; s++; break; X default: proto = &prproto[pr_type]; break; X } X free(*proto); X *proto = save(s); X break; X case QUERY: X parg.p_string = prproto[pr_type]; X error("%s", &parg); X break; X } X} X X/* X * Handler for the -b option. X */ X /*ARGSUSED*/ X public void Xopt_b(type, s) X int type; X char *s; X{ X switch (type) X { X case TOGGLE: X case QUERY: X /* X * Allocate the new number of buffers. X */ X cbufs = ch_nbuf(cbufs); X break; X case INIT: X break; X } X} X X#if __MSDOS__ X/* X * Handler for -v option. (use BIOS or direct video) X */ X public void Xopt_v(type, s) X int type; X register char *s; X{ X switch (type) X { X case INIT: X case TOGGLE: X if (output_mode == 2) X directvideo = 1; X else X directvideo = 0; X break; X case QUERY: X break; X } X} X X/* X * Handler for -W option. (set/modify window boundaries) X */ X public void Xopt_W(type, s) X int type; X register char *s; X{ X PARG parg; X X switch (type) X { X case INIT: X window_box = save(s); X break; /* get_term will take care of actually setting window */ X#ifdef MOVE_WINDOW X case TOGGLE: X if (window_box != NULL) X free(window_box); X window_box = save(s); X reset_window(); X break; X#endif X case QUERY: X parg.p_string = window_box; X error("%s", &parg); X break; X } X} X#endif X X/* X * "-?" means display a help message. X * If from the command line, exit immediately. X */ X /*ARGSUSED*/ X public void Xopt_query(type, s) X int type; X char *s; X{ X if (nohelp) X return; X switch (type) X { X case QUERY: X case TOGGLE: X error("Use \"h\" for help", NULL_PARG); X break; X case INIT: X raw_mode(1); X init(); X help(); X quit(0); X /*NOTREACHED*/ X } X} END_OF_FILE echo shar: Extracting \"opttbl.c\" sed "s/^X//" >'opttbl.c' <<'END_OF_FILE' X/* X * The option table. X */ X X#include "less.h" X#include "option.h" X X#define toupper(c) ((c)-'a'+'A') X X/* X * Variables controlled by command line options. X */ Xpublic int quiet; /* Should we suppress the audible bell? */ Xpublic int how_search; /* Where should forward searches start? */ Xpublic int top_scroll; /* Repaint screen from top? X (alternative is scroll from bottom) */ Xpublic int pr_type; /* Type of prompt (short, medium, long) */ Xpublic int bs_mode; /* How to process backspaces */ Xpublic int know_dumb; /* Don't complain about dumb terminals */ Xpublic int quit_at_eof; /* Quit after hitting end of file twice */ Xpublic int squeeze; /* Squeeze multiple blank lines into one */ Xpublic int tabstop; /* Tab settings */ Xpublic int back_scroll; /* Repaint screen on backwards movement */ Xpublic int forw_scroll; /* Repaint screen on forward movement */ Xpublic int twiddle; /* Display "~" for lines after EOF */ Xpublic int caseless; /* Do "caseless" searches */ Xpublic int linenums; /* Use line numbers */ Xpublic int cbufs; /* Current number of buffers */ Xpublic int autobuf; /* Automatically allocate buffers as needed */ Xpublic int nohelp; /* Disable the HELP command */ Xpublic int ctldisp; /* Send control chars to screen untranslated */ Xpublic int force_open; /* Open the file even if not regular file */ Xpublic int swindow; /* Size of scrolling window */ Xpublic int jump_sline; /* Screen line of "jump target" */ Xpublic int chopline; /* Truncate displayed lines at screen width */ X#if __MSDOS__ Xpublic int output_mode; /* Which screen output method */ Xpublic int refresh_on_quit; /* Repaint screen on quit, if possible */ X#endif X X/* X * Table of all options and their semantics. X */ Xstatic struct option option[] = X{ X { 'a', BOOL, 0, &how_search, NULL, X "Search includes displayed screen", X "Search skips displayed screen", X NULL X }, X { 'b', NUMBER, 10, &cbufs, opt_b, X "Buffers: ", X "%d buffers", X NULL X }, X { 'B', BOOL, 1, &autobuf, NULL, X "Don't automatically allocate buffers", X "Automatically allocate buffers when needed", X NULL X }, X { 'c', TRIPLE, 0, &top_scroll, NULL, X "Repaint by scrolling from bottom of screen", X "Repaint by clearing each line", X "Repaint by painting from top of screen" X }, X { 'd', BOOL|NO_TOGGLE, 0, &know_dumb, NULL, X "Assume intelligent terminal", X "Assume dumb terminal", X NULL X }, X { 'e', TRIPLE, 0, &quit_at_eof, NULL, X "Don't quit at end-of-file", X "Quit at end-of-file", X "Quit immediately at end-of-file" X }, X { 'f', BOOL, 0, &force_open, NULL, X "Open only regular files", X "Open even non-regular files", X NULL X }, X { 'h', NUMBER, -1, &back_scroll, NULL, X "Backwards scroll limit: ", X "Backwards scroll limit is %d lines", X NULL X }, X { 'H', BOOL|NO_TOGGLE, 0, &nohelp, NULL, X "Allow help command", X "Don't allow help command", X NULL X }, X { 'i', BOOL, 0, &caseless, NULL, X "Case is significant in searches", X "Ignore case in searches", X NULL X }, X { 'j', NUMBER, 1, &jump_sline, NULL, X "Target line: ", X "Position target at screen line %d", X NULL X }, X#if USERFILE X { 'k', STRING|NO_TOGGLE, 0, NULL, opt_k, X NULL, NULL, NULL X }, X#endif X#if LOGFILE X { 'l', STRING, 0, NULL, opt_l, X NULL, NULL, NULL X }, X { 'L', STRING, 0, NULL, opt__L, X NULL, NULL, NULL X }, X#endif X { 'm', TRIPLE, 0, &pr_type, NULL, X "Short prompt", X "Medium prompt", X "Long prompt" X }, X { 'n', TRIPLE|REPAINT, 1, &linenums, NULL, X "Don't use line numbers", X "Use line numbers", X "Constantly display line numbers" X }, X#if LOGFILE X { 'o', STRING, 0, NULL, opt_o, X "log file: ", NULL, NULL X }, X { 'O', STRING, 0, NULL, opt__O, X "Log file: ", NULL, NULL X }, X#endif X { 'p', STRING|NO_TOGGLE, 0, NULL, opt_p, X NULL, NULL, NULL X }, X { 'P', STRING, 0, NULL, opt__P, X "prompt: ", NULL, NULL X }, X { 'q', TRIPLE, 0, &quiet, NULL, X "Ring the bell for errors AND at eof/bof", X "Ring the bell for errors but not at eof/bof", X "Never ring the bell" X }, X { 'r', BOOL|REPAINT, 1, &ctldisp, NULL, X "Display control characters directly", X "Display control characters as ^X", X NULL X }, X#if __MSDOS__ X { 'R', BOOL|REPAINT, 0, &refresh_on_quit, NULL, X "Don't repaint screen on quit", X "Repaint screen on quit", X NULL X }, X#endif X { 's', BOOL|REPAINT, 0, &squeeze, NULL, X "Display all blank lines", X "Squeeze multiple blank lines", X NULL X }, X { 'S', BOOL|REPAINT, 0, &chopline, NULL, X "Fold long lines", X "Chop long lines", X NULL X }, X#if TAGS X { 't', STRING, 0, NULL, opt_t, X "tag: ", NULL, NULL X }, X { 'T', STRING, 0, NULL, opt__T, X "tags file: ", NULL, NULL X }, X#endif X { 'u', TRIPLE|REPAINT, 0, &bs_mode, NULL, X "Display underlined text in underline mode", X "Backspaces cause overstrike", X "Print backspace as ^H" X }, X#if __MSDOS__ X { 'v', TRIPLE|NO_TOGGLE, 0, &output_mode, opt_v, X "Output is to standard output, using ansi screen control", X "Output is to video BIOS", X "Output is directly to memory mapped video" X }, X#endif X { 'w', BOOL|REPAINT, 1, &twiddle, NULL, X "Display nothing for lines after end-of-file", X "Display ~ for lines after end-of-file", X NULL X }, X#if __MSDOS__ X#if MOVE_WINDOW X#define W_FLAGS STRING X#else X#define W_FLAGS STRING|NO_TOGGLE X#endif X { 'W', W_FLAGS, 0, NULL, opt_W, X "window boundaries: ", NULL, NULL X }, X#undef W_FLAGS X#endif X { 'x', NUMBER|REPAINT, 8, &tabstop, NULL, X "Tab stops: ", X "Tab stops every %d spaces", X NULL X }, X { 'y', NUMBER, -1, &forw_scroll, NULL, X "Forward scroll limit: ", X "Forward scroll limit is %d lines", X NULL X }, X { 'z', NUMBER, -1, &swindow, NULL, X "Scroll window size: ", X "Scroll window size is %d lines", X NULL X }, X { '?', NOVAR, 0, NULL, opt_query, X NULL, NULL, NULL X }, X { '\0' } X}; X X X/* X * Initialize each option to its default value. X */ X public void Xinit_option() X{ X register struct option *o; X X for (o = option; o->oletter != '\0'; o++) X { X /* X * Set each variable to its default. X */ X if (o->ovar != NULL) X *(o->ovar) = o->odefault; X } X} X X/* X * Find an option in the option table. X */ X public struct option * Xfindopt(c) X int c; X{ X register struct option *o; X X for (o = option; o->oletter != '\0'; o++) X { X if (o->oletter == c) X return (o); X if ((o->otype & TRIPLE) && toupper(o->oletter) == c) X return (o); X } X return (NULL); X} END_OF_FILE echo shar: Extracting \"os.c\" sed "s/^X//" >'os.c' <<'END_OF_FILE' X/* X * Operating system dependent routines. X * X * Most of the stuff in here is based on Unix, but an attempt X * has been made to make things work on other operating systems. X * This will sometimes result in a loss of functionality, unless X * someone rewrites code specifically for the new operating system. X * X * The makefile provides defines to decide whether various X * Unix features are present. X */ X X#include X#include X#include X#include "less.h" X X/* X * BSD setjmp() saves (and longjmp() restores) the signal mask. X * This costs a system call or two per setjmp(), so if possible we clear the X * signal mask with sigsetmask(), and use _setjmp()/_longjmp() instead. X * On other systems, setjmp() doesn't affect the signal mask and so X * _setjmp() does not exist; we just use setjmp(). X */ X#if HAS__SETJMP && SIGSETMASK X#define SET_JUMP _setjmp X#define LONG_JUMP _longjmp X#else X#define SET_JUMP setjmp X#define LONG_JUMP longjmp X#endif X Xextern char *getenv(); X Xpublic int reading; X Xstatic jmp_buf read_label; X X/* X * Like read() system call, but is deliberately interruptible. X * A call to intread() from a signal handler will interrupt X * any pending iread(). X */ X public int Xiread(fd, buf, len) X int fd; X char *buf; X unsigned int len; X{ X register int n; X X if (SET_JUMP(read_label)) X { X /* X * We jumped here from intread. X */ X reading = 0; X#if SIGSETMASK X sigsetmask(0); X#endif X return (READ_INTR); X } X X flush(); X reading = 1; X n = read(fd, buf, len); X reading = 0; X if (n < 0) X return (-1); X return (n); X} X X/* X * Interrupt a pending iread(). X */ X public void Xintread() X{ X LONG_JUMP(read_label, 1); X} X X#if GET_TIME X public long Xget_time() X{ X long t; X X time(&t); X return (t); X} X#endif X X/* X * errno_message: Return an error message based on the value of "errno". X */ X X#if PERROR X Xextern char *sys_errlist[]; Xextern int sys_nerr; Xextern int errno; X X public char * Xerrno_message(filename) X char *filename; X{ X register char *p; X register char *m; X char msg[16]; X X if (errno < sys_nerr) X p = sys_errlist[errno]; X else X { X sprintf(msg, "Error %d", errno); X p = msg; X } X m = (char *) ecalloc(strlen(filename) + strlen(p) + 3, sizeof(char)); X sprintf(m, "%s: %s", filename, p); X return (m); X} X X#else X X public char * Xerrno_message(filename) X char *filename; X{ X register char *m; X static char msg[] = ": cannot open"; X X m = (char *) ecalloc(strlen(filename) + sizeof(msg), sizeof(char)); X strcpy(m, filename); X strcat(m, msg); X return (m); X} X X#endif END_OF_FILE