less beta release (part 4 of 5)

Mark Nudelman mark at unix386.Convergent.COM
Fri Sep 15 08:24:53 AEST 1989


#! /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 \"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;
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");
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 == (POSITION)0)
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 ((POSITION)0);
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	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 *	first_cmd = NULL;
Xpublic char *	every_first_cmd = NULL;
Xpublic int	new_file;
Xpublic int	is_tty;
Xpublic char *	current_file = NULL;
Xpublic char *	previous_file = NULL;
Xpublic HANDLE	curr_handle;
Xpublic POSITION	initial_pos;
Xpublic int	any_display;
Xpublic int	scroll;
Xpublic int	ac;
Xpublic char **	av;
Xpublic int 	curr_ac;
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;
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 * Edit a new file.
X * Filename "-" means standard input.
X * No filename means the "current" file, from the command line.
X */
X	public void
Xedit(filename)
X	register char *filename;
X{
X	register int f;
X	register char *m;
X	static int didpipe;
X
X	if (filename == NULL || *filename == '\0')
X	{
X		if (curr_ac >= ac)
X		{
X			error("No current file");
X			return;
X		}
X		filename = av[curr_ac];
X	}
X
X	if (strcmp(filename, "-") == 0)
X	{
X		/* 
X		 * Use standard input.
X		 */
X		if (didpipe)
X		{
X			error("Can view standard input only once");
X			return;
X		}
X		f = 0;
X	} else if ((m = bad_file(filename)) != NULL)
X	{
X		error(m);
X		free(m);
X		return;
X	} else if ((f = open(filename, 0)) < 0)
X	{
X		m = errno_message(filename);
X		error(m);
X		free(m);
X		return;
X	}
X
X	if (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		error("Can't take input from a terminal (\"less -\\?\" for help)");
X		if (f > 0)
X			close(f);
X		return;
X	}
X
X#if LOGFILE
X	if (f == 0 && 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 (current_file != NULL)
X	{
X		store_pos(curr_handle, position(TOP));
X		lastmark();
X	}
X
X	curr_handle = get_handle(filename);
X	initial_pos = recall_pos(curr_handle);
X
X	previous_file = current_file;
X	current_file = get_filename(curr_handle);
X
X	if (file > 0)
X		close(file);
X	new_file = 1;
X	ispipe = (f == 0);
X	/*
X	 * {{ Would this make more sense? }}
X	 * ispipe = (lseek(f, (offset_t)0, 0) == BAD_LSEEK);
X	 */
X	if (ispipe)
X		didpipe = 1;
X	file = f;
X	ch_init(cbufs, 0);
X
X	if (every_first_cmd != NULL)
X		first_cmd = every_first_cmd;
X
X	if (is_tty)
X	{
X		int no_display = !any_display;
X		any_display = 1;
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			error(filename);
X		}
X	}
X}
X
X/*
X * Edit the next file in the command line list.
X */
X	public void
Xnext_file(n)
X	int n;
X{
X	if (curr_ac + n >= ac)
X	{
X		if (quit_at_eof && hit_eof)
X			quit();
X		error("No (N-th) next file");
X	} else
X		edit(av[curr_ac += n]);
X}
X
X/*
X * Edit the previous file in the command line list.
X */
X	public void
Xprev_file(n)
X	int n;
X{
X	if (curr_ac - n < 0)
X		error("No (N-th) previous file");
X	else
X		edit(av[curr_ac -= n]);
X}
X
X/*
X * Copy a file directly to standard output.
X * Used if standard output is not a tty.
X */
X	static 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 */
Xuse_logfile()
X{
X	register int exists;
X	register int answer;
X	register char *m;
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	if (exists && !force_logfile)
X	{
X		static char w[] = "WARNING: log file exists: ";
X		m = ecalloc(sizeof(w) + strlen(namelogfile), sizeof(char));
X		strcpy(m, w);
X		strcat(m, namelogfile);
X		error(m);
X		free(m);
X		answer = 'X';	/* Ask the user what to do */
X	} else
X		answer = 'O';	/* Create the log file */
X
Xloop:
X	switch (answer)
X	{
X	case 'O': case 'o':
X		logfile = creat(namelogfile, 0644);
X		break;
X	case 'A': case 'a':
X		logfile = open(namelogfile, 1);
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		answer = 0;	/* Don't print an error message */
X		break;
X	case 'q':
X		quit();
X	default:
X		putstr("\n  Overwrite, Append, or Don't log? ");
X		screen_trashed = 1;
X		answer = getchr();
X		putstr("\n");
X		flush();
X		goto loop;
X	}
X
X	if (logfile < 0 && answer != 0)
X	{
X		m = ecalloc(strlen(namelogfile) + 20, sizeof(char));
X		sprintf(m, "Cannot write to \"%s\"", namelogfile);
X		error(m);
X		free(m);
X	}
X}
X
X#endif
X
X/*
X * Entry point.
X */
Xmain(argc, argv)
X	int argc;
X	char *argv[];
X{
X	register int i;
X	extern char *getenv();
X
X	/*
X	 * Process command line arguments and LESS environment arguments.
X	 * Command line arguments override environment arguments.
X	 */
X	init_prompt();
X	init_option();
X	scan_option(getenv("LESS"));
X
X#define	isoptstring(s)	(((s)[0] == '-' || (s)[0] == '+') && (s)[1] != '\0')
X	argv++;
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		exit(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("EDITPROTO");
X	if (editproto == NULL || *editproto == '\0')
X		editproto = "%E ?lm+%lm. %f";
X#endif
X
X	/*
X	 * Set up list of files to be examined.
X	 */
X	ac = argc;
X	av = argv;
X	curr_ac = 0;
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 (ac < 1)
X		{
X			edit("-");
X			cat_file();
X		} else
X		{
X			do
X			{
X				edit((char *)NULL);
X				if (file >= 0)
X					cat_file();
X			} while (++curr_ac < ac);
X		}
X		exit(0);
X	}
X
X	/*
X	 * Call save_handle with all the command line filenames,
X	 * just to avoid copying them to calloc'ed space later
X	 * when we call get_handle() from edit().
X	 */
X	for (i = 0;  i < ac;  i++)
X		save_handle(av[i]);
X
X	init_mark();
X	raw_mode(1);
X	get_term();
X	open_getchr();
X	init();
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 (ac > 0)
X		{
X			error("No filenames allowed with -t option");
X			quit();
X		}
X		if (tagfile == NULL)
X			quit();
X		edit(tagfile);
X		if (file < 0)
X			quit();
X		if (tagsearch())
X			quit();
X	} else
X#endif
X	if (ac < 1)
X		edit("-");	/* Standard input */
X	else 
X	{
X		/*
X		 * Try all the files named as command arguments.
X		 * We are simply looking for one which can be
X		 * opened without error.
X		 */
X		do
X		{
X			edit((char *)NULL);
X		} while (file < 0 && ++curr_ac < ac);
X	}
X
X	if (file >= 0)
X		commands();
X	quit();
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 char *
Xecalloc(count, size)
X	int count;
X	unsigned int size;
X{
X	register char *p;
X
X	p = calloc(count, size);
X	if (p != NULL)
X		return (p);
X	error("Cannot allocate memory");
X	quit();
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()
X{
X	/*
X	 * Put cursor at bottom left corner, clear the line,
X	 * reset the terminal modes, and exit.
X	 */
X	quitting = 1;
X#if LOGFILE
X	end_logfile();
X#endif
X	lower_left();
X	clear_eol();
X	deinit();
X	flush();
X	raw_mode(0);
X	exit(0);
X}
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 *first_cmd;
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	char message[80];
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 "+" becomes the "first_cmd"
X			 * string, which 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			first_cmd = 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			sprintf(message, 
X				"There is no %s flag (\"less -\\?\" for help)",
X				propt(c));
X			error(message);
X			exit(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 if (o->oletter == c)
X				*(o->ovar) = (o->odefault == 1) ? 0 : 1;
X			else
X				*(o->ovar) = (o->odefault == 2) ? 0 : 2;
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);
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 * If do_toggle is zero, just report the current setting, without changing it.
X */
X	public void
Xtoggle_option(c, s, do_toggle)
X	int c;
X	char *s;
X	int do_toggle;
X{
X	register struct option *o;
X	int num;
X	char message[100];
X
X	/*
X	 * Look up the option letter in the option table.
X	 */
X	o = findopt(c);
X	if (o == NULL)
X	{
X		sprintf(message, "There is no %s flag", propt(c));
X		error(message);
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		if (*s == '\0')
X			do_toggle = 0;
X		break;
X	case NUMBER:
X		num = getnum(&s, '\0');
X		if (num < 0)
X			do_toggle = 0;
X		break;
X	}
X
X	/*
X	 * Now actually toggle (change) the variable.
X	 */
X	if (do_toggle)
X	{
X		if (o->otype & NO_TOGGLE)
X		{
X			sprintf(message, "Cannot change the %s flag", propt(c));
X			error(message);
X			return;
X		} 
X
X		switch (o->otype & OTYPE)
X		{
X		case BOOL:
X			/*
X			 * Boolean: just negate.
X			 */
X			*(o->ovar) = ! *(o->ovar);
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			if (o->oletter == c)
X				*(o->ovar) = (*(o->ovar) == 1) ? 0 : 1;
X			else
X				*(o->ovar) = (*(o->ovar) == 2) ? 0 : 2;
X			break;
X		case STRING:
X			/*
X			 * String: don't do anything here.
X			 *	The handling function will do everything.
X			 */
X			break;
X		case NUMBER:
X			/*
X			 * Number: set the variable to the given number.
X			 */
X			*(o->ovar) = num;
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)(do_toggle ? TOGGLE : QUERY, 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)]);
X		break;
X	case NUMBER:
X		/*
X		 * The odesc message has a %d for the value of the variable.
X		 */
X		sprintf(message, o->odesc[0], *(o->ovar));
X		error(message);
X		break;
X	case STRING:
X		/*
X		 * Message was already printed by the handling function.
X		 */
X		break;
X	}
X
X	if (do_toggle && (o->otype & REPAINT))
X		screen_trashed = 1;
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[4];
X
X	if (control_char(c))
X		sprintf(buf, "-^%c", carat_char(c));
X	else
X		sprintf(buf, "-%c", 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));
X}
X
X/*
X * Return the prompt to be used for a given option letter.
X * Only string 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) == 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	char message[80];
X
X	sprintf(message, "String is required after %s", propt(c));
X	error(message);
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		exit(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)
X	char **sp;
X	int c;
X{
X	register char *s;
X	register int n;
X	char message[80];
X
X	s = skipsp(*sp);
X	if (*s < '0' || *s > '9')
X	{
X		if (c == '\0')
X			return (-1);
X		sprintf(message, "Number is required after %s", propt(c));
X		error(message);
X		exit(1);
X	}
X
X	n = 0;
X	while (*s >= '0' && *s <= '9')
X		n = 10 * n + *s++ - '0';
X	*sp = s;
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 seven_bit;
Xextern int nohelp;
Xextern char *prproto[];
Xextern char *eqproto;
X#if LOGFILE
Xextern char *namelogfile;
Xextern int force_logfile;
Xextern int logfile;
X#endif
X#if TAGS
Xpublic int tagoption = 0;
Xextern char *tagfile;
Xextern char *tagpattern;
X#endif
X
X
X#if LOGFILE
X/*
X * Handler for -l option.
X */
X	public void
Xopt_l(type, s)
X	int type;
X	char *s;
X{
X	char *m;
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");
X			return;
X		}
X		if (logfile >= 0)
X		{
X			error("Log file is already in use");
X			return;
X		}
X		namelogfile = save(skipsp(s));
X		use_logfile();
X		sync_logfile();
X		break;
X	case QUERY:
X		if (logfile < 0)
X			error("No log file");
X		else
X		{
X			static char lf[] = "log file \"";
X			m = ecalloc(sizeof(lf) + strlen(namelogfile) + 2, sizeof(char));
X			strcpy(m, lf);
X			strcat(m, namelogfile);
X			strcat(m, "\"");
X			error(m);
X			free(m);
X		}
X		break;
X	}
X}
X
X/*
X * Handler for -L option.
X */
X	public void
Xopt__L(type, s)
X	int type;
X	char *s;
X{
X	force_logfile = 1;
X	opt_l(type, s);
X}
X#endif
X
X#if USERFILE
X	public void
Xopt_k(type, s)
X	int type;
X	char *s;
X{
X	char *message;
X	static char MSG[] = "Cannot use lesskey file: ";
X
X	switch (type)
X	{
X	case INIT:
X		if (add_cmdtable(s) == 0)
X			return;
X		message = (char *) ecalloc(strlen(s) + sizeof(MSG) + 1, 
X						sizeof(char));
X		strcpy(message, MSG);
X		strcat(message, s);
X		error(message);
X		free(message);
X		break;
X	case QUERY:
X	case TOGGLE:
X		error("Cannot query -k option");
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	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			edit(tagfile);
X			(void) tagsearch();
X		}
X		break;
X	case QUERY:
X		error("Tag is required after -t");
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	register char **proto;
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		error(prproto[pr_type]);
X		break;
X	}
X}
X
X/*
X * Handler for the -b option.
X */
X	public void
Xopt_b(type)
X	int type;
X{
X	switch (type)
X	{
X	case TOGGLE:
X		/*
X		 * Allocate the new number of buffers.
X		 */
X		ch_init(cbufs, 1);
X		break;
X	case QUERY:
X	case INIT:
X		break;
X	}
X}
X
X/*
X * Handler for the -g option.
X */
X	public void
Xopt_g(type)
X	int type;
X{
X	switch (type)
X	{
X	case TOGGLE:
X		/*
X		 * Flush the buffers, since the buffered data may
X		 * be bad (if we are switching from 7 bits to 8 bits,
X		 * the eighth bit has been stripped from the buffered data).
X		 */
X		if (ispipe)
X		{
X			error("Can't change -g on a pipe");
X			seven_bit = !seven_bit;
X			break;
X		}
X		ch_init(0, 0);
X		break;
X	case INIT:
X	case QUERY:
X		break;
X	}
X}
X
X/*
X * "-?" means display a help message.
X * If from the command line, exit immediately.
X */
X	public void
Xopt_query(type)
X	int type;
X{
X	if (nohelp)
X		return;
X	switch (type)
X	{
X	case QUERY:
X	case TOGGLE:
X		error("Use \"h\" for help");
X		break;
X	case INIT:
X		raw_mode(1);
X		init();
X		help();
X		quit();
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 seven_bit;		/* Force seven-bit characters? */
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 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 sendctl;		/* Send control chars to screen untranslated */
Xpublic int force_open;		/* Open the file even if not regular file */
Xpublic int swindow;
X
X/*
X * Table of all options and their semantics.
X */
Xstatic struct option option[] =
X{
X	{ 'a', TRIPLE, 0, &how_search, NULL,
X		"Forward search starts at second REAL line displayed",
X		"Forward search starts at bottom of screen",
X		"Forward search starts at second SCREEN line displayed"
X	},
X	{ 'b', NUMBER, 10, &cbufs, opt_b, 
X		"%d buffers",
X		NULL, 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	{ 'g', BOOL, 0, &seven_bit, opt_g,
X		"Use eight bit characters",
X		"Use seven bit characters",
X		NULL
X	},
X	{ 'h', NUMBER, -1, &back_scroll, NULL,
X		"Backwards scroll limit is %d lines",
X		NULL, 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#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		"log file: ", NULL, NULL
X	},
X	{ 'L', STRING, 0, NULL, opt__L,
X		"Log file: ", 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	{ '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, 0, &sendctl, NULL,
X		"Control characters are translated",
X		"Control characters displayed directly",
X		NULL
X	},
X	{ 's', BOOL|REPAINT, 0, &squeeze, NULL,
X		"Don't squeeze multiple blank lines",
X		"Squeeze multiple blank lines",
X		NULL
X	},
X#if TAGS
X	{ 't', STRING, 0, NULL, opt_t,
X		"tag: ", NULL, NULL
X	},
X#endif
X	{ 'u', TRIPLE|REPAINT, 0, &bs_mode, NULL,
X		"Underlined text displayed in underline mode",
X		"Backspaces cause overstrike",
X		"Backspaces print as ^H"
X	},
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	{ 'x', NUMBER|REPAINT, 8, &tabstop, NULL,
X		"Tab stops every %d spaces", 
X		NULL, NULL
X		
X	},
X	{ 'z', NUMBER, -1, &swindow, NULL,
X		"Scroll window size is %d lines",
X		NULL, 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
Xextern char *getenv();
X
Xpublic int reading;
X
Xextern int screen_trashed;
Xextern int force_open;
Xextern char *current_file;
Xextern char *previous_file;
X
Xstatic jmp_buf read_label;
X
X/*
X * Pass the specified command to a shell to be executed.
X * Like plain "system()", but handles resetting terminal modes, etc.
X */
X	public void
Xlsystem(cmd)
X	char *cmd;
X{
X	register int inp;
X	register char *shell;
X	register char *p;
X
X	/*
X	 * Print the command which is to be executed,
X	 * unless the command starts with a "-".
X	 */
X	if (cmd[0] == '-')
X		cmd++;
X	else
X	{
X		lower_left();
X		clear_eol();
X		putstr("!");
X		putstr(cmd);
X		putstr("\n");
X	}
X
X	/*
X	 * De-initialize the terminal and take out of raw mode.
X	 */
X	deinit();
X	flush();
X	raw_mode(0);
X
X	/*
X	 * Restore signals to their defaults.
X	 */
X	init_signals(0);
X
X	/*
X	 * Force standard input to be the terminal, "/dev/tty",
X	 * even if less's standard input is coming from a pipe.
X	 */
X	inp = dup(0);
X	close(0);
X	if (open("/dev/tty", 0) < 0)
X		dup(inp);
X
X	/*
X	 * Pass the command to the system to be executed.
X	 * If we have a SHELL environment variable, use
X	 * <$SHELL -c "command"> instead of just <command>.
X	 * If the command is empty, just invoke a shell.
X	 */
X	p = NULL;
X	if ((shell = getenv("SHELL")) != NULL && *shell != '\0')
X	{
X		if (*cmd == '\0')
X			p = save(shell);
X		else
X		{
X			p = ecalloc(strlen(shell) + strlen(cmd) + 7, sizeof(char));
X			sprintf(p, "%s -c \"%s\"", shell, cmd);
X		}
X	}
X	if (p == NULL)
X		p = save("sh");
X
X	system(p);
X	free(p);
X
X	/*
X	 * Restore standard input, reset signals, raw mode, etc.
X	 */
X	close(0);
X	dup(inp);
X	close(inp);
X
X	init_signals(1);
X	raw_mode(1);
X	init();
X	screen_trashed = 1;
X#if defined(SIGWINCH) || defined(SIGWIND)
X	/*
X	 * Since we were ignoring window change signals while we executed
X	 * the system command, we must assume the window changed.
X	 * Warning: this leaves a signal pending (in "sigs"),
X	 * so psignals() should be called soon after lsystem().
X	 */
X	winch();
X#endif
X}
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	int len;
X{
X	register int n;
X
X	if (setjmp(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	public void
Xintread()
X{
X	longjmp(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 * Expand a string, substituting any "%" with the current filename,
X * and any "#" with the previous filename.
X */
X	public char *
Xfexpand(s)
X	char *s;
X{
X	register char *fr, *to;
X	register int n;
X	register char *e;
X
X	/*
X	 * Make one pass to see how big a buffer we 
X	 * need to allocate for the expanded string.
X	 */
X	n = 0;
X	for (fr = s;  *fr != '\0';  fr++)
X	{
X		if (*fr == '%')
X			n += strlen(current_file);
X		else if (*fr == '#')
X		{
X			if (previous_file == NULL)
X			{
X				error("No previous file");
X				return (NULL);
X			}
X			n += strlen(previous_file);
X		} else
X			n++;
X	}
X
X	e = ecalloc(n+1, sizeof(char));
X
X	/*
X	 * Now copy the string, expanding any "%" or "#".
X	 */
X	to = e;
X	for (fr = s;  *fr != '\0';  fr++)
X	{
X		if (*fr == '%')
X		{
X			strcpy(to, current_file);
X			to += strlen(to);
X		} else if (*fr == '#')
X		{
X			strcpy(to, previous_file);
X			to += strlen(to);
X		} else
X			*to++ = *fr;
X	}
X	*to = '\0';
X	return (e);
X}
X
X	static POSITION
Xseek_filesize(f)
X	int f;
X{
X	offset_t spos;
X
X	spos = lseek(f, (offset_t)0, 2);
X	if (spos == BAD_LSEEK)
X		return (NULL_POSITION);
X	return ((POSITION) spos);
X}
X
X/*
X * Expand a filename, substituting any environment variables, etc.
X * The implementation of this is necessarily very operating system
X * dependent.  This implementation is unabashedly only for Unix systems.
X */
X#if GLOB
X
XFILE *popen();
X
X	public char *
Xglob(filename)
X	char *filename;
X{
X	FILE *f;
X	char *p;
X	int ch;
X	char *cmd;
X	char *gfilename;
X
X	filename = fexpand(filename);
X	if (filename == NULL)
X		return (NULL);
X
X	/*
X	 * We get the shell to expand the filename for us by passing
X	 * an "echo" command to the shell and reading its output.
X	 */
X	p = getenv("SHELL");
X	if (p == NULL || *p == '\0')
X	{
X		/*
X		 * Read the output of <echo filename>.
X		 */
X		cmd = ecalloc(strlen(filename)+6, sizeof(char));
X		sprintf(cmd, "echo %s", filename);
X	} else
X	{
X		/*
X		 * Read the output of <$SHELL -c "echo filename">.
X		 */
X		cmd = ecalloc(strlen(p)+strlen(filename)+12, sizeof(char));
X		sprintf(cmd, "%s -c \"echo %s\"", p, filename);
X	}
X
X	f = popen(cmd, "r");
X	free(cmd);
X	if (f == NULL)
X		return (filename);
X	free(filename);
X
X	gfilename = ecalloc(FILENAME, sizeof(char));
X	for (p = gfilename;  p < &gfilename[FILENAME-1];  p++)
X	{
X		if ((ch = getc(f)) == '\n' || ch == EOF)
X			break;
X		*p = ch;
X	}
X	*p = '\0';
X	pclose(f);
X	return (gfilename);
X}
X
X#else
X
X	public char *
Xglob(filename)
X	char *filename;
X{
X	return (fexpand(filename));
X}
X
X#endif
X
X
X/*
X * Returns NULL if the file can be opened and
X * is an ordinary file, otherwise an error message
X * (if it cannot be opened or is a directory, etc.)
X */
X
X#if STAT
X
X#include <sys/types.h>
X#include <sys/stat.h>
X
X	public char *
Xbad_file(filename)
X	char *filename;
X{
X	register char *m;
X	struct stat statbuf;
X
X	if (stat(filename, &statbuf) < 0)
X		return (errno_message(filename));
X
X	if (force_open)
X		return (NULL);
X
X	if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
X	{
X		static char is_dir[] = " is a directory";
X		m = ecalloc(strlen(filename) + sizeof(is_dir), sizeof(char));
X		strcpy(m, filename);
X		strcat(m, is_dir);
X		return (m);
X	}
X	if ((statbuf.st_mode & S_IFMT) != S_IFREG)
X	{
X		static char not_reg[] = " is not a regular file";
X		m = ecalloc(strlen(filename) + sizeof(not_reg), sizeof(char));
X		strcpy(m, filename);
X		strcat(m, not_reg);
X		return (m);
X	}
X	return (NULL);
X}
X
X	public POSITION
Xfilesize(f)
X	int f;
X{
X	struct stat statbuf;
X
X	if (fstat(f, &statbuf) < 0)
X		return (seek_filesize(f));
X	return ((POSITION) statbuf.st_size);
X}
X
X#else
X
X	public char *
Xbad_file(filename)
X	char *filename;
X{
X	return (NULL);
X}
X
X	public POSITION
Xfilesize(f)
X	int f;
X{
X	return (seek_filesize(f));
X}
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 = 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 = 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
echo shar: Extracting \"output.c\"
sed "s/^X//" >'output.c' <<'END_OF_FILE'
X/*
X * High level routines dealing with the output to the screen.
X */
X
X#include "less.h"
X
Xpublic int errmsgs;	/* Count of messages displayed by error() */
Xstatic int need_clr;
X
Xextern int sigs;
Xextern int sc_width;
Xextern int so_width, se_width;
Xextern int screen_trashed;
Xextern int any_display;
Xextern char *first_cmd;
X
X
X/*
X * Display the line which is in the line buffer.
X */
X	public void
Xput_line()
X{
X	register int c;
X	register int i;
X
X	if (sigs)
X	{
X		/*
X		 * Don't output if a signal is pending.
X		 */
X		screen_trashed = 1;
X		return;
X	}
X
X	for (i = 0;  (c = gline(i)) != '\0';  i++)
X	{
X		switch (c)
X		{
X		case UL_CHAR:
X			ul_enter();
X			break;
X		case UE_CHAR:
X			ul_exit();
X			break;
X		case BO_CHAR:
X			bo_enter();
X			break;
X		case BE_CHAR:
X			bo_exit();
X			break;
X		case '\b':
X			putbs();
X			break;
X		default:
X			if (c & CARATBIT)
X			{
X				/*
X				 * Control characters arrive here as the
X				 * normal character [carat_char(c)] with
X				 * the CARATBIT bit set.  See pappend().
X				 */
X				putchr('^');
X				putchr(c &~ CARATBIT);
X			} else
X			{
X				putchr(c);
X			}
X		}
X	}
X}
X
X/*
X * Is a given character a "control" character?
X * {{ ASCII DEPENDENT }}
X */
X	public int
Xcontrol_char(c)
X	int c;
X{
X	return (c < ' ' || c == '\177');
X}
X
X/*
X * Return the printable character used to identify a control character
X * (printed after a carat; e.g. '\3' => "^C").
X * {{ ASCII DEPENDENT }}
X */
X	public int
Xcarat_char(c)
X	int c;
X{
X	return ((c == '\177') ? '?' : (c | 0100));
X}
X
X
Xstatic char obuf[1024];
Xstatic char *ob = obuf;
X
X/*
X * Flush buffered output.
X */
X	public void
Xflush()
X{
X	register int n;
X
X	n = ob - obuf;
X	if (n == 0)
X		return;
X	if (write(1, obuf, n) != n)
X		screen_trashed = 1;
X	ob = obuf;
X}
X
X/*
X * Discard buffered output.
X */
X	public void
Xdropout()
X{
X	ob = obuf;
X}
X
X/*
X * Output a character.
X */
X	public void
Xputchr(c)
X	int c;
X{
X	if (ob >= &obuf[sizeof(obuf)])
X		flush();
X	if (need_clr)
X	{
X		need_clr = 0;
X		lower_left();
X		clear_eol();
X	}
X	*ob++ = c;
X}
X
X/*
X * Output a string.
X */
X	public void
Xputstr(s)
X	register char *s;
X{
X	while (*s != '\0')
X		putchr(*s++);
X}
X
X/*
X * Output a message in the lower left corner of the screen
X * and wait for carriage return.
X */
X
Xstatic char return_to_continue[] = "  (press RETURN)";
X
X	public void
Xerror(s)
X	char *s;
X{
X	register int c;
X	static char buf[2] = { '\0', '\0' };
X
X	errmsgs++;
X	if (!any_display)
X	{
X		/*
X		 * Nothing has been displayed yet.
X		 * Output this message on error output (file
X		 * descriptor 2) and don't wait for a keystroke
X		 * to continue.
X		 *
X		 * This has the desirable effect of producing all
X		 * error messages on error output if standard output
X		 * is directed to a file.  It also does the same if
X		 * we never produce any real output; for example, if
X		 * the input file(s) cannot be opened.  If we do
X		 * eventually produce output, code in edit() makes
X		 * sure these messages can be seen before they are
X		 * overwritten or scrolled away.
X		 */
X		write(2, s, strlen(s));
X		write(2, "\n", 1);
X		return;
X	}
X
X	lower_left();
X	clear_eol();
X	so_enter();
X	putstr(s);
X	putstr(return_to_continue);
X	so_exit();
X
X#if ONLY_RETURN
X	while ((c = getchr()) != '\n' && c != '\r')
X		bell();
X#else
X	c = getchr();
X	if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR)
X	{
X		buf[0] = c;
X		first_cmd = buf;
X	}
X#endif
X	lower_left();
X
X	if (strlen(s) + sizeof(return_to_continue) + 
X	    so_width + se_width + 1 > sc_width)
X		/*
X		 * Printing the message has probably scrolled the screen.
X		 * {{ Unless the terminal doesn't have auto margins,
X		 *    in which case we just hammered on the right margin. }}
X		 */
X		screen_trashed = 1;
X
X	flush();
X}
X
Xstatic char intr_to_abort[] = "... (interrupt to abort)";
X
X	public void
Xierror(s)
X	char *s;
X{
X
X	lower_left();
X	clear_eol();
X	so_enter();
X	putstr(s);
X	putstr(intr_to_abort);
X	so_exit();
X	flush();
X	need_clr = 1;
X}
END_OF_FILE
echo shar: Extracting \"position.c\"
sed "s/^X//" >'position.c' <<'END_OF_FILE'
X/*
X * Routines dealing with the "position" table.
X * This is a table which tells the position (in the input file) of the
X * first char on each currently displayed line.
X *
X * {{ The position table is scrolled by moving all the entries.
X *    Would be better to have a circular table 
X *    and just change a couple of pointers. }}
X */
X
X#include "less.h"
X#include "position.h"
X
Xstatic POSITION *table = NULL;	/* The position table */
Xstatic int table_size;
X
Xextern int sc_width, sc_height;
X
X/*
X * Return the starting file position of a line displayed on the screen.
X * The line may be specified as a line number relative to the top
X * of the screen, but is usually one of these special cases:
X *	the top (first) line on the screen
X *	the second line on the screen
X *	the bottom line on the screen
X *	the line after the bottom line on the screen
X */
X	public POSITION
Xposition(where)
X	int where;
X{
X	switch (where)
X	{
X	case BOTTOM:
X		where = sc_height - 2;
X		break;
X	case BOTTOM_PLUS_ONE:
X		where = sc_height - 1;
X		break;
X	case MIDDLE:
X		where = sc_height / 2;
X	}
X	return (table[where]);
X}
X
X/*
X * Add a new file position to the bottom of the position table.
X */
X	public void
Xadd_forw_pos(pos)
X	POSITION pos;
X{
X	register int i;
X
X	/*
X	 * Scroll the position table up.
X	 */
X	for (i = 1;  i < sc_height;  i++)
X		table[i-1] = table[i];
X	table[sc_height - 1] = pos;
X}
X
X/*
X * Add a new file position to the top of the position table.
X */
X	public void
Xadd_back_pos(pos)
X	POSITION pos;
X{
X	register int i;
X
X	/*
X	 * Scroll the position table down.
X	 */
X	for (i = sc_height - 1;  i > 0;  i--)
X		table[i] = table[i-1];
X	table[0] = pos;
X}
X
X/*
X * Initialize the position table, done whenever we clear the screen.
X */
X	public void
Xpos_clear()
X{
X	register int i;
X
X	for (i = 0;  i < sc_height;  i++)
X		table[i] = NULL_POSITION;
X}
X
X/*
X * Allocate the position table.
X */
X	public void
Xpos_init()
X{
X	if (sc_height <= table_size)
X		return;
X	if (table != NULL)
X		free((char*)table);
X	table = (POSITION *) ecalloc(sc_height, sizeof(POSITION));
X	table_size = sc_height;
X}
X
X/*
X * See if the byte at a specified position is currently on the screen.
X * Check the position table to see if the position falls within its range.
X * Return the position table entry if found, -1 if not.
X */
X	public int
Xonscreen(pos)
X	POSITION pos;
X{
X	register int i;
X
X	if (pos < table[0])
X		return (-1);
X	for (i = 1;  i < sc_height;  i++)
X		if (pos < table[i])
X			return (i-1);
X	return (-1);
X}
END_OF_FILE
echo shar: Extracting \"handle.c\"
sed "s/^X//" >'handle.c' <<'END_OF_FILE'
X/*
X * Stuff to manipulate file handles.
X * A file handle is a small integer that refers to a file name.
X * The point of all this is to avoid keeping big file names around
X * in many different places; instead we keep file HANDLES around
X * and all the names are stored away here in this module.
X * There are routines here to convert a handle to a name and vice versa.
X *
X * Also done in this module is keeping track of a file position
X * for every filename.  This is used to restore the last position
X * when we re-examine a previously examined file.
X */
X
X#include "less.h"
X
Xstruct fhandle {
X	struct fhandle *h_next;
X	HANDLE h_handle;
X	char *h_filename;
X	POSITION h_pos;
X};
X
Xstatic HANDLE xhandle = (HANDLE)0;
Xstatic struct fhandle *anchor = NULL;
X
X/*
X * Allocate a new handle structure and stick a filename in it.
X */
X	static struct fhandle *
Xnew_handle(filename)
X	char *filename;
X{
X	register struct fhandle *p;
X
X	p = (struct fhandle *) ecalloc(1, sizeof(struct fhandle));
X	p->h_handle = ++xhandle;
X	p->h_filename = filename;
X	p->h_next = anchor;
X	p->h_pos = (POSITION)0;
X	anchor = p;
X	return (p);
X}
X
X/*
X * Find a handle structure, given a handle.
X */
X	static struct fhandle *
Xh_to_fh(handle)
X	HANDLE handle;
X{
X	register struct fhandle *p;
X
X	for (p = anchor;  p != NULL;  p = p->h_next)
X		if (handle == p->h_handle)
X			return (p);
X
X	error("ERROR: h_to_fh");
X	quit();
X	/*NOTREACHED*/
X}
X
X/*
X * Find a handle structure, given a filename.
X */
X	static struct fhandle *
Xfn_to_fh(filename)
X	char *filename;
X{
X	register struct fhandle *p;
X
X	for (p = anchor;  p != NULL;  p = p->h_next)
X		if (strcmp(filename, p->h_filename) == 0)
X			return (p);
X	return (NULL);
X}
X
X/*
X * Get the handle associated with a filename.
X */
X	public HANDLE
Xget_handle(filename)
X	char *filename;
X{
X	register struct fhandle *p;
X
X	if ((p = fn_to_fh(filename)) == NULL)
X		p = new_handle(save(filename));
X	return (p->h_handle);
X}
X
X/*
X * Stash away a filename.
X * The filename must be known to be STATIC;
X * this function, unlike get_handle(), 
X * does not make a copy of the filename.
X */
X	public void
Xsave_handle(filename)
X	char *filename;
X{
X	if (fn_to_fh(filename) == NULL)
X		(void) new_handle(filename);
X}
X
X/*
X * Get the filename associated with a handle.
X */
X	public char *
Xget_filename(handle)
X	HANDLE handle;
X{
X	register struct fhandle *p;
X
X	p = h_to_fh(handle);
X	return (p->h_filename);
X}
X
X/*
X * Save the current position in a given file.
X */
X	public void
Xstore_pos(handle, pos)
X	HANDLE handle;
X	POSITION pos;
X{
X	register struct fhandle *p;
X
X	p = h_to_fh(handle);
X	p->h_pos = pos;
X}
X
X/*
X * Recall the current position in a file which may have been seen before.
X * If it hasn't been seen before, return NULL_POSITION.
X */
X	public POSITION
Xrecall_pos(handle)
X	HANDLE handle;
X{
X	register struct fhandle *p;
X
X	p = h_to_fh(handle);
X	return (p->h_pos);
X}
END_OF_FILE



More information about the Alt.sources mailing list