less (part 4 of 6)
Mark Nudelman
mark at unix386.Convergent.COM
Wed Mar 6 10:14:05 AEST 1991
#! /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 <fcntl.h>
X#include <stdlib.h>
X#include <string.h>
X#include <io.h>
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 "-<number>"
X * instead of -z<number> 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 <stdio.h>
X#include <signal.h>
X#include <setjmp.h>
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
More information about the Alt.sources
mailing list