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