[alt.sources] less

mark@unix386.Convergent.COM (Mark Nudelman) (03/06/91)

For some reason, I have been recieving a lot requests lately for
the latest version of "less".  So I decided to post it here rather
than sending umpteen copies thru mail.  If you haven't seen less
before, its a paginator program, similar to more or pg.

If you find any bugs or have any suggestions for improvements,
PLEASE PLEASE PLEASE send them to me rather than posting patches
to the net.  I will combine them and post "official" patches as
seems appropriate.  I will attempt to address any issues in a
timely manner, although I'm very busy right now and can't promise
quick turnaround at all times.

Enjoy!

Mark Nudelman
{uunet,sun,decwrl,hplabs}!pyramid!ctnews!unix386!mark

===================================================================

#! /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 \"README\"
sed "s/^X//" >'README' <<'END_OF_FILE'
X=======================================================================
X===     NOTE: THIS IS A BETA DISTRIBUTION OF less (version 170)     ===
X===            PLEASE REPORT ANY PROBLEMS TO THE AUTHOR.            ===
X=======================================================================
X
XThis is the distribution of "less", a paginator similar to "more" or "pg".
XThe manual page is in less.man (nroff source in less.nro).
XMajor changes made since the last posted version are in CHANGES.
X
XINSTALLATION (Unix systems only):
X
X1. Move the distributed source to its own directory and 
X   unpack it by running "sh" or "unshar" on the distribution 
X   files, if you have not already done so.
X
X2. Type "sh linstall" and answer the questions it asks.
X   This will generate a makefile and a defines.h.
X
X   If you choose not to include some features in your version,
X   you may wish to edit the manual page "less.nro" and the help
X   page "less.help" to remove the references to the appropriate 
X   commands or options.
X
X3. It is a good idea to look over the generated makefile 
X   and make sure it looks ok.
X
X4. Type "make" and watch the fun.
X
X5. If the make succeeds, it will generate a program "less"
X   in your current directory.  Test the generated program.
X
X6. When satisfied that it works, if you wish to install it
X   in a public place, type "make install".
X
XIf you have any problems building or running "less", 
Xsuggestions, complaints, etc., you may mail to the 
Xauthor via USENET at:
X	sun	 \
X	uunet 	  } !pyramid!ctnews!unix386!mark
X	hplabs   /
X
X
XNote to hackers: comments noting possible improvements are enclosed
Xin double curly brackets {{ like this }}.
END_OF_FILE
echo shar: Extracting \"CHANGES\"
sed "s/^X//" >'CHANGES' <<'END_OF_FILE'
X	Major changes between "less" versions 123 and 170
X
X* New option -j allows target lines to be positioned anywhere on screen.
X
X* New option -S truncates displayed line at the screen width,
X  rather than wrapping onto the next line.
X
X* New option -y limits amount of forward scroll.
X
X* New option -T specifies a "tags" file.
X
X* Non-printable, non-control characters are displayed in octal.
X  Such characters, as well as control characters, are displayed 
X  in blinking mode.
X
X* New command -+ sets an option to its default.
X* New command -- sets an option to the opposite of its default.
X
X* Lesskey file may have a string appended to a key's action,
X  which acts as though typed in after the command.
X
X* New commands ESC-^F and ESC-^B match arbitrary types of brackets.
X
X* New command F monitors a growing file (like "tail -f").
X
X* New command | pipes a section of the input file into a shell command.
X
X* New command :x directly jumps to a file in the command line list.
X
X* Search commands have been enhanced and reorganized:
X	n	Repeat search, same direction.
X	N	Repeat search, opposite direction.
X	ESC-/	Search forward thru file boundaries
X	ESC-?	Search backward thru file boundaries
X	ESC-n	Repeat search thru file boundaries, same direction.
X	ESC-N	Repeat search thru file boundaries, opposite direction.
X  Special character * causes search to search thru file boundaries.
X  Special character @ causes search to begin at start/end of file list.
X
X* Examining a new file adds it to the command line list.
X  A list of files, or an expression which matches more than one file,
X  may be examined; all of them are added to the command line list.
X
X* Environment variables LESSCHARSET and LESSCHARDEF can define
X  a non-ASCII character set.
X
X* Partial support for MSDOS, including options -R for repainting screen
X  on quit, -v/-V to select video mode, and -W to change window size.
X
X
X======================================================================
X
X
X	Major changes between "less" versions 97 and 123
X
X* New option (-N) causes line numbers to be displayed in the
X  text of the file (like vi "set nu").
X
X* New option (-?) prints help message immediately.
X
X* New option (-r) displays "raw" control characters, without
X  mapping them to ^X notation.
X
X* New option (-f) forces less to open non-regular files
X  (directories, etc).
X
X* New option (-k) can be used to specify lesskey files by name.
X
X* New option (-y) can be used to set a forward scroll limit
X  (like -h sets a backward scroll limit).
X
X* File marks (set by the m command) are now preserved when a new
X  file is edited.  The ' command can thus be used to switch files.
X
X* New command ESC-/ searches all files (on the command line) 
X  for a pattern.
X
X* New command ESC-n repeats previous search, spanning files.
X
X* The N command has been changed to repeat the previous search
X  in the reverse direction.  The old N command is still available 
X  via :n.
X
X* New command ESC-N repeats previous search in the reverse
X  direction and spanning files.
X
X* 8 bit characters are now supported.  A new option (-g) can be 
X  used to strip off the eighth bit (the previous behavior).
X
X* Options which take a following string (like -t) may now
X  optionally have a space between the option letter and the string.
X
X* Six new commands { } ( ) [ and ] can be used to match
X  brackets of specific types, similar to vi % command.
X
X* New commands z and w move forward/backward one window and
X  simultaneously set the window size.
X
X* Prompt string expansion now has %L for line number of the last
X  line in the file, and %E for the name of the editor.
X  Also, % escapes which refer to a line (b=bottom, t=top, etc.)
X  can use j for the jump target line.
X
X* New environment variable LESSEDIT can be used to tailor the
X  command string passed to the editor by the v command.
X
X* Examining a file which was previously examined will return
X  to the same position in the file.
X
X* A "%" is expanded to the current filename and a "#" to the 
X  previous filename, in both shell commands and the E command.
X  (Previously % worked only in shell commands and # worked 
X  only in the E command.)
X
X* New command ":ta" is equivalent to "-t".
X
X* New command "s" is equivalent to "-l".
X
X* The - command may be followed by "+X" to revert to the default
X  for option X, or "-X" to get the opposite of the default.
X
X* Lesskey files may now include characters after the action as
X  extra input to be parsed after the action; for example:
X  "toggle-option X" to toggle a specific option X.
END_OF_FILE
echo shar: Extracting \"linstall\"
sed "s/^X//" >'linstall' <<'END_OF_FILE'
X#! /bin/sh
X# Installation script for less.
X# This script prompts the operator for various information
X# and constructs a makefile.
X
Xecho "This script will build a makefile for less."
Xecho "If you already have a file called \"makefile\" it will be overwritten,"
Xecho "as will the file \"defines.h\"."
Xecho "Press RETURN to continue."
Xread ans
X
Xecho "I will ask you some questions about your system."
Xecho "If you do not know the answer to any question,"
Xecho "just press RETURN and I will choose a default for you."
Xecho "Press RETURN now."
Xread ans
X
XECHO=./vecho
Xif [ ! -f $ECHO ]
Xthen
X	echo "One moment..."
X	cc -o $ECHO vecho.c
X	echo ""
Xfi
X
X$ECHO "Most Unix systems are derived from either System V"
X$ECHO "or Berkeley BSD 4.1, 4.2, 4.3, etc."
X$ECHO ""
X$ECHO "Is your system closest to:"
X$ECHO "  1. System V"
X$ECHO "  2. BSD 4.1"
X$ECHO "  3. BSD 4.2 or later"
X$ECHO "  4. Xenix"
X$ECHO "Enter a number, or just RETURN if you don't know: \c"
Xread ans
Xxenix=0
Xcase "X$ans" in
XX1) sys=sys5; sysname="System V" ;;
XX2) sys=bsd; bsd41=1; sysname="BSD 4.1" ;;
XX3) sys=bsd; bsd41=0; sysname="BSD 4.2" ;;
XX4) sys=sys5; xenix=1; sysname="Xenix" ;;
X*) sys=unknown ;;
Xesac
X$ECHO ""
X
XDATE=`date`
Xcat >makefile <<EOF
X# Makefile for "less"
X# Generated $DATE by $0.
XEOF
X
Xcat >>makefile <<"EOF"
X#
X# Invoked as:
X#	make all
X#   or	make install
X# Plain "make" is equivalent to "make all".
X#
X# If you add or delete functions, remake funcs.h by doing:
X#	make newfuncs
X# This depends on the coding convention of function headers looking like:
X#	" \t public <function-type> \n <function-name> ( ... ) "
X#
X# Also provided:
X#	make lint	# Runs "lint" on all the sources.
X#	make clean	# Removes "less" and the .o files.
X#	make clobber	# Pretty much the same as make "clean".
X
XSHELL = /bin/sh
X
XEOF
X
Xcat >defines.h <<EOF
X/* Definition file for less */
X/* Generated $DATE by $0. */
X
XEOF
X
Xcat >>defines.h <<EOF
X/*
X * Define XENIX if running under XENIX 3.0.
X */
X#define	XENIX		$xenix
X
XEOF
X$ECHO ""
X
X
X
Xif [ "X$sys" = "Xunknown" ]
Xthen
X	alldefault=0
Xelse
X	def=yes
X	alldefault=1
X	$ECHO "Do you want to use ALL the defaults for $sysname?"
X	$ECHO "  Enter \"yes\" if you have a STANDARD $sysname."
X	$ECHO "  Enter \"no\" if you want to change any of the defaults. [$def] \c"
X	read ans
X	case "X$ans" in
X	X[yY]*) alldefault=1 ;;
X	X[nN]*) alldefault=0 ;;
X	esac
X	$ECHO ""
Xfi
X
Xif [ $alldefault = 0 ]
Xthen
X	alloptional=0
Xelse
X	def=yes
X	alloptional=1
X	$ECHO "Do you want to use all the optional features of less?"
X	$ECHO "  Less has several features which you may or may not"
X	$ECHO "  wish to include, such as shell escapes."
X	$ECHO "  Enter \"yes\" if you want to include ALL the optional features."
X	$ECHO "  Enter \"no\" if you want to select individual features. [$def] \c"
X	read ans
X	case "X$ans" in
X	X[yY]*) alloptional=1 ;;
X	X[nN]*) alloptional=0 ;;
X	esac
X	$ECHO ""
Xfi
X
X
X
Xdef=yes
Xx=1
Xif [ $alldefault = 0 ]
Xthen
X	$ECHO "Does your C compiler support the \"void\" type? [$def] \c"
X	read ans
X	case "X$ans" in
X	X[yY]*) x=1 ;;
X	X[nN]*) x=0 ;;
X	esac
X	$ECHO ""
Xfi
Xcat >>defines.h <<EOF
X/*
X * VOID is 1 if your C compiler supports the "void" type,
X * 0 if it does not.
X */
X#define	VOID		$x
X#if VOID
X#define	VOID_POINTER	void *
X#else
X#define	VOID_POINTER	char *
X#endif
X
XEOF
X
X
X
Xdef=long
Xif [ $alldefault = 0 ]
Xthen
X	$ECHO "What type is the \"offset\" argument to lseek? [$def] \c"
X	read ans
X	if [ "X$ans" != "X" ]
X	then
X		def=$ans
X	fi
X	$ECHO ""
Xfi
Xcat >>defines.h <<EOF
X/*
X * offset_t is the type which lseek() returns.
X * It is also the type of lseek()'s second argument.
X */
X#define	offset_t	$def
X
XEOF
X
X
X
X
Xdef=yes; x=1
Xif [ $alldefault = 0 ]
Xthen
X	$ECHO "Most Unix systems provide the stat() function."
X	$ECHO "Does your system have stat()? [$def] \c"
X	read ans
X	case "X$ans" in
X	X[yY]*) x=1 ;;
X	X[nN]*) x=0 ;;
X	esac
X	$ECHO ""
Xfi
Xcat >>defines.h <<EOF
X/*
X * STAT is 1 if your system has the stat() call.
X */
X#define	STAT		$x
X
XEOF
X
X
X
X
Xdef=yes; x=1
Xif [ $alldefault = 0 ]
Xthen
X	$ECHO "Most Unix systems provide the perror() function."
X	$ECHO "Does your system have perror()? [$def] \c"
X	read ans
X	case "X$ans" in
X	X[yY]*) x=1 ;;
X	X[nN]*) x=0 ;;
X	esac
X	$ECHO ""
Xfi
Xcat >>defines.h <<EOF
X/*
X * PERROR is 1 if your system has the perror() call.
X * (Actually, if it has sys_errlist, sys_nerr and errno.)
X */
X#define	PERROR		$x
X
XEOF
X
X
X
X
Xdef=yes; x=1
Xif [ $alldefault = 0 ]
Xthen
X	$ECHO "Most Unix systems provide the time() function."
X	$ECHO "Does your system have time()? [$def] \c"
X	read ans
X	case "X$ans" in
X	X[yY]*) x=1 ;;
X	X[nN]*) x=0 ;;
X	esac
X	$ECHO ""
Xfi
Xcat >>defines.h <<EOF
X/*
X * GET_TIME is 1 if your system has the time() call.
X */
X#define	GET_TIME	$x
X
XEOF
X
Xif [ $x = 0 ]
Xthen
X	$ECHO "What is the APPROXIMATE performance of your"
X	$ECHO "machine, as a percentage of a Vax 11/750?"
X	$ECHO "(Enter 100 if your machine is as fast as a Vax,"
X	$ECHO " 50 if it is half as fast, 200 if it is twice as fast, etc.)"
X	$ECHO "The accuracy of this information is not critical."
X	while :
X	do
X		$ECHO "Percent of Vax 11/750 [100]: \c"
X		read ans
X		if [ "X$ans" = "X" ]
X		then
X			ans=100
X		fi
X		longloop=`expr "$ans" "*" 3`
X		if [ $? = 0 ]
X		then
X			break
X		fi
X		$ECHO "Enter a number please!"
X	done
X	$ECHO ""
X
X	cat >>defines.h <<EOF
X/*
X * LONGLOOP is the number of lines we should process in the line number
X * scan before displaying a warning that it will take a while.
X */
X#define	LONGLOOP	($longloop)
XEOF
Xfi
X
X
X
X
Xif [ "$sys" = "bsd" ]
Xthen
X	def=no; x=0
Xelse
X	def=yes; x=1
Xfi
Xif [ $alldefault = 0 ]
Xthen
X	$ECHO "Most System V systems have termio.h, while most"
X	$ECHO "Berkeley-derived systems have sgtty.h."
X	$ECHO "Does your system have termio.h? [$def] \c"
X	read ans
X	case "X$ans" in
X	X[yY]*) x=1 ;;
X	X[nN]*) x=0 ;;
X	esac
X	$ECHO ""
Xfi
Xcat >>defines.h <<EOF
X/*
X * TERMIO is 1 if your system has /usr/include/termio.h.
X * This is normally the case for System 5.
X * If TERMIO is 0 your system must have /usr/include/sgtty.h.
X * This is normally the case for BSD.
X */
X#define	TERMIO		$x
X
XEOF
X
X
X
X
Xif [ "$sys" = "bsd" -a "$bsd41" = "0" ]
Xthen
X	def=yes; x=1
Xelse
X	def=no; x=0
Xfi
Xif [ $alldefault = 0 ]
Xthen
X	$ECHO "Most BSD 4.2 and 4.3 systems have both _setjmp() and setjmp()."
X	$ECHO "Most System V and BSD 4.1 systems have only setjmp()."
X	$ECHO "Does your system have both _setjmp() and setjmp()? [$def] \c"
X	read ans
X	case "X$ans" in
X	X[yY]*) x=1 ;;
X	X[nN]*) x=0 ;;
X	esac
X	$ECHO ""
Xfi
Xcat >>defines.h <<EOF
X/*
X * HAS__SETJMP is 1 if your system has the _setjmp() call.
X * This is normally the case only for BSD 4.2 and up,
X * not for BSD 4.1 or System 5.
X */
X#define	HAS__SETJMP	$x
X
XEOF
X
X
X
X
Xif [ "$sys" = "bsd" -a "$bsd41" = "0" ]
Xthen
X	def=yes; x=1
Xelse
X	def=no; x=0
Xfi
Xif [ $alldefault = 0 ]
Xthen
X	$ECHO "Most BSD 4.2 and 4.3 systems have the sigsetmask() call."
X	$ECHO "Most System V and BSD 4.1 systems do not."
X	$ECHO "Does your system have sigsetmask()? [$def] \c"
X	read ans
X	case "X$ans" in
X	X[yY]*) x=1 ;;
X	X[nN]*) x=0 ;;
X	esac
X	$ECHO ""
Xfi
Xcat >>defines.h <<EOF
X/*
X * SIGSETMASK is 1 if your system has the sigsetmask() call.
X * This is normally the case only for BSD 4.2,
X * not for BSD 4.1 or System 5.
X */
X#define	SIGSETMASK	$x
X
XEOF
X
X
X
Xif [ "$sys" = "bsd" ]
Xthen
X	def=2; REGCMP=0;RECOMP=1
Xelse
X	def=1; REGCMP=1;RECOMP=0
Xfi
Xif [ $alldefault = 0 ]
Xthen
X	$ECHO "Most System V systems have the regcmp() function."
X	$ECHO "Most Berkeley-derived systems have the re_comp() function."
X	$ECHO "Does your system have:"
X	$ECHO "  1. regcmp"
X	$ECHO "  2. re_comp"
X	$ECHO "  3. neither   [$def] \c"
X	read ans
X	case "X$ans" in
X	X1) REGCMP=1;RECOMP=0 ;;
X	X2) REGCMP=0;RECOMP=1 ;;
X	X3) REGCMP=0;RECOMP=0 ;;
X	esac
X	$ECHO ""
Xfi
Xcat >>defines.h <<EOF
X/*
X * REGCMP is 1 if your system has the regcmp() function.
X * This is normally the case for System 5.
X * RECOMP is 1 if your system has the re_comp() function.
X * This is normally the case for BSD.
X * If neither is 1, pattern matching is supported, but without metacharacters.
X */
X#define	REGCMP		$REGCMP
X#define	RECOMP		$RECOMP
X
XEOF
X
X
X
X
Xdef=yes
Xx=1
Xif [ $alloptional = 0 ]
Xthen
X	$ECHO "Do you wish to allow shell escapes? [$def] \c"
X	read ans
X	case "X$ans" in
X	X[yY]*) x=1 ;;
X	X[nN]*) x=0 ;;
X	esac
X	$ECHO ""
Xfi
Xcat >>defines.h <<EOF
X/*
X * SHELL_ESCAPE is 1 if you wish to allow shell escapes.
X * (This is possible only if your system supplies the system() function.)
X */
X#define	SHELL_ESCAPE	$x
X
XEOF
X
X
X
Xdef=yes
Xx=1
Xedname="vi"
Xif [ $alloptional = 0 ]
Xthen
X	$ECHO "Do you wish to allow editor escapes? [$def] \c"
X	read ans
X	case "X$ans" in
X	X[nN]*) x=0; edname="" ;;
X	X[yY]*) x=1
X		$ECHO "What is the pathname of the default editor? [$edname] \c"
X		read ans 
X		if [ "x$ans" != "x" ]
X		then
X			edname=$ans
X		fi
X		;;
X	esac
X	$ECHO ""
Xfi
Xcat >>defines.h <<EOF
X/*
X * EDITOR is 1 if you wish to allow editor invocation (the "v" command).
X * (This is possible only if your system supplies the system() function.)
X * EDIT_PGM is the name of the (default) editor to be invoked.
X */
X#define	EDITOR		$x
X#define	EDIT_PGM	"$edname"
X
XEOF
X
X
X
X
Xdef=yes
Xx=1
Xif [ $alloptional = 0 ]
Xthen
X	$ECHO "Do you wish to support \"tag\" files? [$def] \c"
X	read ans
X	case "X$ans" in
X	X[yY]*) x=1 ;;
X	X[nN]*) x=0 ;;
X	esac
X	$ECHO ""
Xfi
Xcat >>defines.h <<EOF
X/*
X * TAGS is 1 if you wish to support tag files.
X */
X#define	TAGS		$x
X
XEOF
X
X
X
Xdef=yes
Xx=1
Xif [ $alloptional = 0 ]
Xthen
X	$ECHO "Do you wish to allow user-defined key definitions? [$def] \c"
X	read ans
X	case "X$ans" in
X	X[yY]*) x=1 ;;
X	X[nN]*) x=0 ;;
X	esac
X	$ECHO ""
Xfi
XUSERFILE=$x
Xcat >>defines.h <<EOF
X/*
X * USERFILE is 1 if you wish to allow a .less file to specify 
X * user-defined key bindings.
X */
X#define	USERFILE	$x
X
XEOF
X
X
X
Xdef=yes
Xx=1
Xif [ $alldefault = 0 ]
Xthen
X	$ECHO "If your system provides the popen() function and"
X	$ECHO "the \"echo\" shell command, you may allow shell metacharacters" 
X	$ECHO "to be expanded in filenames."
X	$ECHO "Do you wish to allow shell metacharacters in filenames? [$def] \c"
X	read ans
X	case "X$ans" in
X	X[yY]*) x=1 ;;
X	X[nN]*) x=0 ;;
X	esac
X	$ECHO ""
Xfi
Xcat >>defines.h <<EOF
X/*
X * GLOB is 1 if you wish to have shell metacharacters expanded in filenames.
X * This will generally work if your system provides the "popen" function
X * and the "echo" shell command.
X */
X#define	GLOB		$x
X
X/*
X * PIPEC is 1 if you wish to have the "|" command
X * which allows the user to pipe data into a shell command.
X */
X#define	PIPEC		$x
X
XEOF
X
X
X
Xdef=yes
Xx=1
Xif [ $alloptional = 0 ]
Xthen
X	$ECHO "Do you wish to allow log files (-l option)? [$def] \c"
X	read ans
X	case "X$ans" in
X	X[yY]*) x=1 ;;
X	X[nN]*) x=0 ;;
X	esac
X	$ECHO ""
Xfi
Xcat >>defines.h <<EOF
X/*
X * LOGFILE is 1 if you wish to allow the -l option (to create log files).
X */
X#define	LOGFILE		$x
X
XEOF
X
Xcat >>defines.h <<EOF
X/*
X * ONLY_RETURN is 1 if you want RETURN to be the only input which
X * will continue past an error message.
X * Otherwise, any key will continue past an error message.
X */
X#define	ONLY_RETURN	0
X
XEOF
X
Xcat >>makefile <<EOF
X
X##########################################################################
X# Compilation environment.
X##########################################################################
X
XEOF
X
X
X
Xif [ "$xenix" = "1" ]
Xthen
X	LIBS="-ltermlib"
Xelif [ "$sys" = "bsd" ]
Xthen
X	LIBS="-ltermcap"
Xelse
X	LIBS="-lcurses -ltermcap -lPW"
Xfi
Xif [ $alldefault = 0 ]
Xthen
X	$ECHO "To build \"less\", you must link with libraries supplied by your system."
X	$ECHO "(If this needs to be changed later, edit the makefile"
X	$ECHO "and change the definition of LIBS.)"
X	$ECHO "What libraries should be used [$LIBS] \c"
X	read ans
X	if [ "X$ans" != "X" ]
X	then
X		LIBS="$ans"
X	fi
X	$ECHO ""
Xfi
Xcat >>makefile <<EOF
X# LIBS is the list of libraries needed.
XLIBS = $LIBS
X
XEOF
X
X
X
XINSTALL_LESS="/usr/local/bin/less"
XINSTALL_KEY="/usr/local/bin/lesskey"
XINSTALL_HELP="/usr/local/bin/less.hlp"
XINSTALL_LESSMAN="/usr/man/man1/less.1"
XINSTALL_KEYMAN="/usr/man/man1/lesskey.1"
XLESS_MANUAL="less.nro"
XKEY_MANUAL="lesskey.nro"
Xif [ $alldefault = 0 ]
Xthen
X	$ECHO "What is the name of the \"public\" (installed) version of less?"
X	$ECHO " [$INSTALL_LESS] \c"
X	read ans
X	if [ "X$ans" != "X" ]
X	then
X		INSTALL_LESS="$ans"
X	fi
X	$ECHO "What is the name of the \"public\" (installed) version of lesskey?"
X	$ECHO " [$INSTALL_KEY] \c"
X	read ans
X	if [ "X$ans" != "X" ]
X	then
X		INSTALL_KEY="$ans"
X	fi
X	$ECHO "What is the name of the \"public\" (installed) version of the help file?"
X	$ECHO " [$INSTALL_HELP] \c"
X	read ans
X	if [ "X$ans" != "X" ]
X	then
X		INSTALL_HELP="$ans"
X	fi
X	$ECHO "What is the name of the \"public\" (installed) version of the less manual page?"
X	$ECHO " [$INSTALL_LESSMAN] \c"
X	read ans
X	if [ "X$ans" != "X" ]
X	then
X		INSTALL_LESSMAN="$ans"
X	fi
X	$ECHO "What is the name of the \"public\" (installed) version of the lesskey manual page?"
X	$ECHO " [$INSTALL_KEYMAN] \c"
X	read ans
X	if [ "X$ans" != "X" ]
X	then
X		INSTALL_KEYMAN="$ans"
X	fi
X	$ECHO ""
Xfi
X
Xcat >>defines.h <<EOF
X/*
X * HELPFILE is the full pathname of the help file.
X */
X#define	HELPFILE	"$INSTALL_HELP"
X
XEOF
X
Xcat >>makefile <<EOF
X# INSTALL_LESS is a list of the public versions of less.
X# INSTALL_KEY is a list of the public versions of lesskey.
X# INSTALL_HELP is a list of the public version of the help file.
X# INSTALL_LESSMAN is a list of the public versions of the less manual page.
X# INSTALL_KEYMAN is a list of the public versions of the lesskey manual page.
XINSTALL_LESS =		\$(ROOT)$INSTALL_LESS
XINSTALL_KEY =		\$(ROOT)$INSTALL_KEY
XINSTALL_HELP =		\$(ROOT)$INSTALL_HELP
XINSTALL_LESSMAN =	\$(ROOT)$INSTALL_LESSMAN
XINSTALL_KEYMAN =	\$(ROOT)$INSTALL_KEYMAN
XLESS_MANUAL =		$LESS_MANUAL
XKEY_MANUAL =		$KEY_MANUAL
XHELPFILE =		$INSTALL_HELP
X
X
XEOF
X
X
X
Xcat >>makefile <<"EOF"
X# OPTIM is passed to the compiler and the loader.
X# It is normally "-O" but may be, for example, "-g".
XOPTIM = -O
X
XCFLAGS = $(OPTIM)
X
X
X
X##########################################################################
X# Files
X##########################################################################
X
XSRC1 =	ch.c cmdbuf.c command.c decode.c help.c input.c 
XSRC2 =	line.c linenum.c main.c edit.c option.c optfunc.c \
X	opttbl.c os.c 
XSRC3 =	charset.c filename.c lsystem.c output.c position.c ifile.c \
X	brac.c forwback.c jump.c search.c 
XSRC4 =	mark.c prompt.c screen.c signal.c tags.c ttyin.c version.c
X
XSRC =	$(SRC1) $(SRC2) $(SRC3) $(SRC4)
X
XOBJ =	brac.o ch.o charset.o cmdbuf.o command.o decode.o edit.o filename.o \
X	forwback.o help.o input.o jump.o line.o linenum.o \
X	lsystem.o main.o option.o optfunc.o opttbl.o os.o \
X	output.o position.o mark.o ifile.o prompt.o screen.o \
X	search.o signal.o tags.o ttyin.o version.o
X
X
X##########################################################################
X# Rules for building stuff
X##########################################################################
X
XEOF
X
Xif [ "$USERFILE" = "1" ]
Xthen
X	cat >>makefile <<"EOF"
Xall: less lesskey
Xinstall: install_less install_help install_key install_lman install_kman
XEOF
Xelse
X	cat >>makefile <<"EOF"
Xall: less
Xinstall: install_less install_help install_lman
XEOF
Xfi
X
Xcat >>makefile <<"EOF"
X
Xless: $(OBJ)
X	$(CC) $(LDFLAGS) $(OPTIM) -o less $(OBJ) $(LIBS) $(LDLIBS)
X
Xlesskey: lesskey.o
X	$(CC) $(LDFLAGS) $(OPTIM) -o lesskey lesskey.o $(LDLIBS)
X
Xinstall_less: less
X	for f in $(INSTALL_LESS); do  rm -f $$f; cp less $$f;  done
X	touch install_less
X
Xinstall_key: lesskey
X	for f in $(INSTALL_KEY); do  rm -f $$f; cp lesskey $$f;  done
X	touch install_key
X
Xinstall_help: less.hlp
X	for f in $(INSTALL_HELP); do  rm -f $$f; cp less.hlp $$f;  done
X	touch install_help
X
Xinstall_lman: $(LESS_MANUAL) 
X	for f in $(INSTALL_LESSMAN); do  rm -f $$f; cp $(LESS_MANUAL) $$f;  done
X	touch install_lman
X
Xinstall_kman: $(KEY_MANUAL)
X	for f in $(INSTALL_KEYMAN); do  rm -f $$f; cp $(KEY_MANUAL) $$f;  done
X	touch install_kman
X
X##########################################################################
X# Maintenance
X##########################################################################
X
Xlint:
X	lint -hp $(SRC)
X
Xnewfuncs funcs.h:
X	if [ -f funcs.h ]; then mv funcs.h funcs.h.OLD; fi
X	awk -f mkfuncs.awk $(SRC) >funcs.h
X
Xclean:
X	rm -f $(OBJ) lesskey.o less lesskey vecho
X
Xclobber:
X	rm -f *.o less lesskey vecho install_less install_key \
X		install_help install_lman install_kman
X
Xshar:
X	shar -v README CHANGES linstall \
X		less.nro lesskey.nro \
X		vecho.c mkfuncs.awk > less1.shr
X	shar -v less.man lesskey.man \
X		less.h position.h cmd.h option.h > less2.shr 
X	shar -v lesskey.c $(SRC1) > less3.shr
X	shar -v $(SRC2) > less4.shr
X	shar -v $(SRC3) less.hlp > less5.shr
X	shar -v $(SRC4) funcs.h > less6.shr
X
X
X##########################################################################
X# Dependencies
X##########################################################################
X
X$(OBJ): less.h funcs.h defines.h position.h
Xcommand.o decode.o: cmd.h
Xoption.o opttbl.o optfunc.o: option.h
X
Xlesskey.o: less.h funcs.h defines.h cmd.h
X
XEOF
X$ECHO ""
X
X$ECHO "The makefile and defines.h have been built."
X$ECHO "You should check them to make sure everything is as you want it to be."
X$ECHO "When you are satisfied, just type \"make\", and \"less\" will be built."
END_OF_FILE
echo shar: Extracting \"less.nro\"
sed "s/^X//" >'less.nro' <<'END_OF_FILE'
X.TH LESS 1
X.SH NAME
Xless \- opposite of more
X.SH SYNOPSIS
X.B "less -?"
X.br
X.B "less [-[+]aABcCdeEfimMnNqQrsSuUw] [-b\fIN\fP] [-x\fIN\fP] [-[z]\fIN\fP]"
X.br
X.B "     [-h\fIN\fP] [-y\fIN\fP] [-P[mM=]\fIstring\fP] [-[oO]\fIlogfile\fP] [-k\fIkeyfile\fP]"
X.br
X.B "     [-t\fItag\fP] [-T\fItagsfile\fP] [+\fIcmd\fP] [\fIfilename\fP]..."
X
X.SH DESCRIPTION
X.I Less
Xis a program similar to 
X.I more
X(1), but which allows backward movement
Xin the file as well as forward movement.
XAlso,
X.I less
Xdoes not have to read the entire input file before starting,
Xso with large input files it starts up faster than text editors like
X.I vi
X(1).
X.I Less
Xuses termcap (or terminfo on some systems),
Xso it can run on a variety of terminals.
XThere is even limited support for hardcopy terminals.
X(On a hardcopy terminal, lines which should be printed at the top
Xof the screen are prefixed with an up-arrow.)
X.PP
XCommands are based on both
X.I more
Xand
X.I vi.
XCommands may be preceded by a decimal number, 
Xcalled N in the descriptions below.
XThe number is used by some commands, as indicated.
X
X.SH COMMANDS
XIn the following descriptions, ^X means control-X.
XESC stands for the ESCAPE key; for example ESC-v means the
Xtwo character sequence "ESCAPE", then "v".
X.IP "h or H"
XHelp: display a summary of these commands.
XIf you forget all the other commands, remember this one.
X.PP
X.IP "SPACE or ^V or f or ^F"
XScroll forward N lines, default one window (see option -z below).
XIf N is more than the screen size, only the final screenful is displayed.
XWarning: some systems use ^V as a special literalization character.
X.PP
X.IP "z"
XLike SPACE, but if N is specified, it becomes the new window size.
X.PP
X.IP "RETURN or ^N or e or ^E or j or ^J"
XScroll forward N lines, default 1.
XThe entire N lines are displayed, even if N is more than the screen size.
X.PP
X.IP "d or ^D"
XScroll forward N lines, default one half of the screen size.
XIf N is specified, it becomes the new default for 
Xsubsequent d and u commands.
X.PP
X.IP "b or ^B or ESC-v"
XScroll backward N lines, default one window (see option -z below).
XIf N is more than the screen size, only the final screenful is displayed.
X.PP
X.IP "w"
XLike ESC-v, but if N is specified, it becomes the new window size.
X.PP
X.IP "y or ^Y or ^P or k or ^K"
XScroll backward N lines, default 1.
XThe entire N lines are displayed, even if N is more than the screen size.
XWarning: some systems use ^Y as a special job control character.
X.PP
X.IP "u or ^U"
XScroll backward N lines, default one half of the screen size.
XIf N is specified, it becomes the new default for 
Xsubsequent d and u commands.
X.PP
X.IP "r or ^R or ^L"
XRepaint the screen.
X.PP
X.IP R
XRepaint the screen, discarding any buffered input.
XUseful if the file is changing while it is being viewed.
X.PP
X.IP "F"
XScroll forward, and keep trying to read when the
Xend of file is reached.
XNormally this command would be used when already at the end of the file.
XIt is a way to monitor the tail of a file which is growing
Xwhile it is being viewed.
X(The behavior is similar to the "tail -f" command.)
X.PP
X.IP "g or < or ESC-<"
XGo to line N in the file, default 1 (beginning of file).
X(Warning: this may be slow if N is large.)
X.PP
X.IP "G or > or ESC->"
XGo to line N in the file, default the end of the file.
X(Warning: this may be slow if N is large,
Xor if N is not specified and
Xstandard input, rather than a file, is being read.)
X.PP
X.IP "p or %"
XGo to a position N percent into the file.
XN should be between 0 and 100.
X(This works if standard input is being read, but only if
X.I less
Xhas already read to the end of the file.
XIt is always fast, but not always useful.)
X.PP
X.IP "{"
XIf a left curly bracket appears in the top line displayed
Xon the screen,
Xthe { command will go to the matching right curly bracket.
XThe matching right curly bracket is positioned on the bottom
Xline of the screen.
XIf there is more than one left curly bracket on the top line,
Xa number N may be used to specify the N-th bracket on the line.
X.PP
X.IP "}"
XIf a right curly bracket appears in the bottom line displayed
Xon the screen,
Xthe } command will go to the matching left curly bracket.
XThe matching left curly bracket is positioned on the top
Xline of the screen.
XIf there is more than one right curly bracket on the top line,
Xa number N may be used to specify the N-th bracket on the line.
X.PP
X.IP "("
XLike {, but applies to parentheses rather than curly brackets.
X.PP
X.IP ")"
XLike }, but applies to parentheses rather than curly brackets.
X.PP
X.IP "["
XLike {, but applies to square brackets rather than curly brackets.
X.PP
X.IP "]"
XLike }, but applies to square brackets rather than curly brackets.
X.PP
X.IP "ESC-^F"
XFollowed by two characters,
Xacts like {, but uses the two characters as open and close brackets,
Xrespectively.
XFor example, "ESC ^F < >" could be used to 
Xgo forward to the > which matches the < in the top displayed line.
X.IP "ESC-^B"
XFollowed by two characters,
Xacts like }, but uses the two characters as open and close brackets,
Xrespectively.
XFor example, "ESC ^B < >" could be used to 
Xgo backward to the < which matches the > in the bottom displayed line.
X.IP m
XFollowed by any lowercase letter, 
Xmarks the current position with that letter.
X.PP
X.IP "'"
X(Single quote.)
XFollowed by any lowercase letter, returns to the position which
Xwas previously marked with that letter.
XFollowed by another single quote, returns to the position at
Xwhich the last "large" movement command was executed.
XFollowed by a ^ or $, jumps to the beginning or end of the
Xfile respectively.
XMarks are preserved when a new file is examined,
Xso the ' command can be used to switch between input files.
X.PP
X.IP "^X^X"
XSame as single quote.
X.PP
X.IP /pattern
XSearch forward in the file for the N-th line containing the pattern.
XN defaults to 1.
XThe pattern is a regular expression, as recognized by
X.I ed.
XThe search starts at the second line displayed
X(but see the -a and -j options, which change this).
X.sp
XCertain characters are special
Xif entered at the beginning of the pattern;
Xthey modify the type of search rather than become part of the pattern:
X.RS
X.IP !
XSearch for lines which do NOT match the pattern.
X.IP *
XSearch multiple files.
XThat is, if the search reaches the end of the current file 
Xwithout finding a match,
Xthe search continues in the next file in the command line list.
X.IP @
XBegin the search at the first line of the first file
Xin the command line list,
Xregardless of what is currently displayed on the screen
Xor the settings of the -a or -j options.
X.RE
X.PP
X.IP ?pattern
XSearch backward in the file for the N-th line containing the pattern.
XThe search starts at the line immediately before the top line displayed.
X.sp
XCertain characters are special as in the / command:
X.RS
X.IP !
XSearch for lines which do NOT match the pattern.
X.IP *
XSearch multiple files.
XThat is, if the search reaches the beginning of the current file 
Xwithout finding a match,
Xthe search continues in the previous file in the command line list.
X.IP @
XBegin the search at the last line of the last file
Xin the command line list,
Xregardless of what is currently displayed on the screen
Xor the settings of the -a or -j options.
X.RE
X.PP
X.IP "ESC-/pattern"
XSame as "/*".
X.PP
X.IP "ESC-?pattern"
XSame as "?*".
X.PP
X.IP n
XRepeat previous search, for N-th line containing the last pattern.
XIf the previous search was modified by !, the search is made for the
XN-th line NOT containing the pattern.
XIf the previous search was modified by *, the search continues
Xin the next (or previous) file if not satisfied in the current file.
XThere is no effect if the previous search was modified by @.
X.PP
X.IP N
XRepeat previous search, but in the reverse direction.
X.PP
X.IP "ESC-n"
XRepeat previous search, but crossing file boundaries.
XThe effect is as if the previous search were modified by *.
X.PP
X.IP "ESC-N"
XRepeat previous search, but in the reverse direction
Xand crossing file boundaries.
X.PP
X.IP ":e [filename]"
XExamine a new file.
XIf the filename is missing, the "current" file (see the :n and :p commands
Xbelow) from the list of files in the command line is re-examined.
XA percent sign (%) in the filename is replaced by the name of the
Xcurrent file.  
XA pound sign (#) is replaced by the name of the previously examined file.
XThe filename is inserted into the command line list of files
Xso that it can be seen by subsequent :n and :p commands.
XIf the filename consists of several files, they are all inserted into
Xthe list of files and the first one is examined.
X.PP
X.IP "^X^V or E"
XSame as :e.
XWarning: some systems use ^V as a special literalization character.
X.PP
X.IP ":n"
XExamine the next file (from the list of files given in the command line).
XIf a number N is specified, the N-th next file is examined.
X.PP
X.IP ":p"
XExamine the previous file in the command line list.
XIf a number N is specified, the N-th previous file is examined.
X.PP
X.IP ":x"
XExamine the first file in the command line list.
XIf a number N is specified, the N-th file in the list is examined.
X.PP
X.IP "= or ^G or :f"
XPrints some information about the file being viewed,
Xincluding its name
Xand the line number and byte offset of the bottom line being displayed.
XIf possible, it also prints the length of the file,
Xthe number of lines in the file
Xand the percent of the file above the last displayed line.
X.PP
X.IP \-
XFollowed by one of the command line option letters (see below),
Xthis will change the setting of that option
Xand print a message describing the new setting.
XIf the option letter has a numeric value (such as -b or -h),
Xor a string value (such as -P or -t),
Xa new value may be entered after the option letter.
XIf no new value is entered, a message describing
Xthe current setting is printed and nothing is changed.
X.PP
X.IP \-+
XFollowed by one of the command line option letters (see below),
Xthis will reset the option to its default setting
Xand print a message describing the new setting.
X(The "\-+\fIX\fP" command does the same thing
Xas "\-+\fIX\fP" on the command line.)
XThis does not work for string-valued options.
X.PP
X.IP \-\-
XFollowed by one of the command line option letters (see below),
Xthis will reset the option to the "opposite" of its default setting
Xand print a message describing the new setting.
X(The "\-\-\fIX\fP" command does the same thing
Xas "\-\fIX\fP" on the command line.)
XThis does not work for numeric or string-valued options.
X.PP
X.IP _
X(Underscore.)
XFollowed by one of the command line option letters (see below),
Xthis will print a message describing the current setting of that option.
XThe setting of the option is not changed.
X.PP
X.IP +cmd
XCauses the specified cmd to be executed each time a new file is examined.
XFor example, +G causes 
X.I less
Xto initially display each file starting at the end 
Xrather than the beginning.
X.PP
X.IP V
XPrints the version number of 
X.I less 
Xbeing run.
X.PP
X.IP "q or :q or :Q or ZZ or ESC ESC"
XExits
X.I less.
X.PP
XThe following 
Xthree
Xcommands may or may not be valid, depending on your particular installation.
X.PP
X.IP v
XInvokes an editor to edit the current file being viewed.
XThe editor is taken from the environment variable EDITOR,
Xor defaults to "vi".
XSee also the discussion of LESSEDIT under the section on PROMPTS below.
X.PP
X.IP "! shell-command"
XInvokes a shell to run the shell-command given.
XA percent sign (%) in the command is replaced by the name of the
Xcurrent file.  
XA pound sign (#) is replaced by the name of the previously examined file.
X"!!" repeats the last shell command.
X"!" with no shell command simply invokes a shell.
XIn all cases, the shell is taken from the environment variable SHELL,
Xor defaults to "sh".
X.PP
X.IP "| <m> shell-command"
X<m> represents any mark letter.
XPipes a section of the input file to the given shell command.
XThe section of the file to be piped is between the current position and 
Xthe position marked by the letter.
X<m> may also be ^ or $ to indicate beginning or end of file respectively.
XIf <m> is . or newline, the current screen is piped.
XThe current screen is the minimum amount piped in any case.
X.PP
X.SH OPTIONS
XCommand line options are described below.
XMost options may be changed while
X.I less 
Xis running, via the "\-" command.
X.PP
XOptions are also taken from the environment variable "LESS".
XFor example, 
Xto avoid typing "less -options ..." each time 
X.I less 
Xis invoked, you might tell 
X.I csh:
X.sp
Xsetenv LESS "-options"
X.sp
Xor if you use 
X.I sh:
X.sp
XLESS="-options"; export LESS
X.sp
XThe environment variable is parsed before the command line,
Xso command line options override the LESS environment variable.
XIf an option appears in the LESS variable, it can be reset
Xto its default on the command line by beginning the command
Xline option with "-+".
X.sp
XA dollar sign ($) may be used to signal the end of an option string.
XThis is important only for options like -P which take a
Xfollowing string.
X.IP -?
XThis option displays a summary of the commands accepted by
X.I less
X(the same as the h command).
XIf this option is given, all other options are ignored, and
X.I less
Xexits after the help screen is viewed.
X(Depending on how your shell interprets the question mark,
Xit may be necessary to quote the question mark, thus: "-\\?".)
X.IP -a
XCauses searches to start after the last line
Xdisplayed on the screen, 
Xthus skipping all lines displayed on the screen.
XBy default, searches start at the second line on the screen
X(or after the last found line; see the -j option).
X.IP -b\fIn\fP
XCauses
X.I less
Xto use a non-standard number of buffers.
XBuffers are 1K, and by default 10 buffers are used
X(except if data in coming from standard input; see the -B option).
XThe number \fIn\fP specifies a different number of buffers to use.
X.IP -B
XDisables automatic allocation of buffers,
Xso that only the default number of buffers are used.
XIf more data is read than will fit in the buffers, the oldest
Xdata is discarded.
XBy default, when data is coming from standard input,
Xbuffers are allocated automatically as needed
Xto avoid loss of data.
X.IP -c
XCauses full screen repaints to be painted from the top line down.
XBy default,
Xfull screen repaints are done by scrolling from the bottom of the screen.
X.IP -C
XThe -C option is like -c, but the screen is cleared before it is repainted.
X.IP -d
XThe -d option suppresses the error message
Xnormally displayed if the terminal is dumb;
Xthat is, lacks some important capability,
Xsuch as the ability to clear the screen or scroll backward.
XThe -d option does not otherwise change the behavior of
X.I less
Xon a dumb terminal).
X.IP -e
XCauses 
X.I less 
Xto automatically exit
Xthe second time it reaches end-of-file.
XBy default, the only way to exit 
X.I less
Xis via the "q" command.
X.IP -E
XCauses 
X.I less
Xto automatically exit the first time it reaches end-of-file.
X.IP -f
XForces non-regular files to be opened.
X(A non-regular file is a directory or a device special file.)
XAlso suppresses the warning message when a binary file is opened.
XBy default,
X.I less
Xwill refuse to open non-regular files.
X.IP -h\fIn\fP
XSpecifies a maximum number of lines to scroll backward.
XIf it is necessary to scroll backward more than \fIn\fP lines,
Xthe screen is repainted in a forward direction instead.
X(If the terminal does not have the ability to scroll
Xbackward, -h0 is implied.)
X.IP -i
XCauses searches to ignore case; that is,
Xuppercase and lowercase are considered identical.
XAlso, text which is overstruck or underlined can be searched for.
XThis option is ignored if any uppercase letters
Xappear in the search pattern.
X.IP -j\fIn\fP
XSpecifies a line on the screen where "target" lines
Xare to be positioned.
XTarget lines are the object of text searches, 
Xtag searches, jumps to a line number,
Xjumps to a file percentage, and jumps to a marked position.
XThe screen line is specified by a number: the top line on the screen
Xis 1, the next is 2, and so on.
XThe number may be negative to specify a line relative to the bottom
Xof the screen: the bottom line on the screen is -1, the second
Xto the bottom is -2, and so on.
XIf the -j option is used, searches begin at the line immediately
Xafter the target line.
XFor example, if "-j4" is used, the target line is the
Xfourth line on the screen, so searches begin at the fifth line
Xon the screen.
X.IP -k\fIfilename\fP
XCauses
X.I less
Xto open and interpret the named file as a
X.I lesskey
X(1) file.
XMultiple -k options may be specified.
XIf a file called .less exists in the user's home directory, this
Xfile is also used as a
X.I lesskey
Xfile.
X.IP -m
XCauses 
X.I less
Xto prompt verbosely (like \fImore\fP),
Xwith the percent into the file.
XBy default,
X.I less
Xprompts with a colon.
X.IP -M
XCauses 
X.I less
Xto prompt even more verbosely than 
X.I more.
X.IP -n
XSuppresses line numbers.
XThe default (to use line numbers) may cause
X.I less
Xto run more slowly in some cases, especially with a very large input file.
XSuppressing line numbers with the -n flag will avoid this problem.
XUsing line numbers means: the line number will be displayed in the verbose
Xprompt and in the = command,
Xand the v command will pass the current line number to the editor
X(see also the discussion of LESSEDIT in PROMPTS below).
X.IP -N
XCauses a line number to be displayed at the beginning of
Xeach line in the display.
X.IP -o\fIfilename\fP
XCauses
X.I less
Xto copy its input to the named file as it is being viewed.
XThis applies only when the input file is a pipe,
Xnot an ordinary file.
XIf the file already exists, 
X.I less
Xwill ask for confirmation before overwriting it.
X.IP -O\fIfilename\fP
XThe -O option is like -o, but it will overwrite an existing
Xfile without asking for confirmation.
X.sp
XIf no log file has been specified,
Xthe -o and -O options can be used from within 
X.I less
Xto specify a log file.
XWithout a file name, they will simply report the name of the log file.
XThe "s" command is equivalent to specifying -o from within
X.I less.
X.IP -p\fIpattern\fP
XThe -p option on the command line is equivalent to 
Xspecifying +/\fIpattern\fP;
Xthat is, it tells
X.I less
Xto start at the first occurence of \fIpattern\fP in the file.
X.IP -P\fIprompt\fP
XProvides a way to tailor the three prompt
Xstyles to your own preference.
XThis option would normally be put in the LESS environment
Xvariable, rather than being typed in with each 
X.I less
Xcommand.
XSuch an option must either be the last option in the LESS variable,
Xor be terminated by a dollar sign.
X-P followed by a string changes the default (short) prompt to that string.
X-Pm changes the medium (-m) prompt to the string, and
X-PM changes the long (-M) prompt.
XAlso, -P= changes the message printed by the = command to the given string.
XAll prompt strings consist of a sequence of 
Xletters and special escape sequences.
XSee the section on PROMPTS for more details.
X.IP -q
XCauses moderately "quiet" operation:
Xthe terminal bell is not rung 
Xif an attempt is made to scroll past the end of the file
Xor before the beginning of the file.
XIf the terminal has a "visual bell", it is used instead.
XThe bell will be rung on certain other errors,
Xsuch as typing an invalid character.
XThe default is to ring the terminal bell in all such cases.
X.IP -Q
XCauses totally "quiet" operation:
Xthe terminal bell is never rung.
X.IP -r
XCauses "raw" control characters to be displayed.
XThe default is to display control characters using the caret notation;
Xfor example, a control-A (octal 001) is displayed as "^A".
XWarning: when the -r flag is used,
X.I less
Xcannot keep track of the actual appearance of the screen
X(since this depends on how the screen responds to
Xeach type of control character).
XThus, various display problems may result,
Xsuch as long lines being split in the wrong place.
X.IP -s
XCauses consecutive blank lines to be squeezed into a single blank line.
XThis is useful when viewing
X.I nroff
Xoutput.
X.IP -S
XCauses lines longer than the screen width to be
Xchopped rather than folded.
XThat is, the remainder of a long line is simply discarded.
XThe default is to fold long lines; that is, display the remainder
Xon the next line.
X.IP -t\fItag\fP
XThe -t option, followed immediately by a TAG,
Xwill edit the file containing that tag.
XFor this to work, there must be a file called "tags" in the
Xcurrent directory, which was previously built by the 
X.I ctags
X(1) command.
XThis option may also be specified from within 
X.I less 
X(using the \- command) as a way of examining a new file.
XThe command ":t" is equivalent to specifying -t from within
X.I less.
X.IP -T\fItagsfile\fP
XSpecifies a tags file to be used instead of "tags".
X.IP -u
XCauses backspaces and carriage returns to be treated as printable characters;
Xthat is, they are sent to the terminal when they appear in the input.
X.IP -U
XCauses backspaces and carriage returns to be treated as control characters;
Xthat is, they are handled as specified by the -r option.
X.sp
XBy default, if neither -u nor -U is given,
Xbackspaces which appear adjacent to an underscore character
Xare treated specially:
Xthe underlined text is displayed 
Xusing the terminal's hardware underlining capability.
XAlso, backspaces which appear between two identical characters
Xare treated specially: 
Xthe overstruck text is printed 
Xusing the terminal's hardware boldface capability.
XOther backspaces are deleted, along with the preceding character.
XCarriage returns immediately followed by a newline are deleted.
XOther carriage returns are handled as specified by the -r option.
X.IP -w
XCauses blank lines to be used to represent lines
Xpast the end of the file.
XBy default,
Xa tilde character is used.
X.IP -x\fIn\fP
XSets tab stops every \fIn\fP positions.
XThe default for \fIn\fP is 8.
X.IP -y\fIn\fP
XSpecifies a maximum number of lines to scroll forward.
XIf it is necessary to scroll forward more than \fIn\fP lines,
Xthe screen is repainted instead.
XThe -c or -C option may be used to repaint from the top of
Xthe screen if desired.
XBy default, any forward movement causes scrolling.
X.IP -[z]\fIn\fP
XChanges the default scrolling window size to \fIn\fP lines.
XThe default is one screenful.
XThe z and w commands can also be used to change the window size.
XThe "z" may be omitted, as in "-\fIn\fP" for compatibility with
X.I more.
X.IP +
XIf a command line option begins with \fB+\fP,
Xthe remainder of that option is taken to be an initial command to
X.I less.
XFor example, +G tells
X.I less
Xto start at the end of the file rather than the beginning,
Xand +/xyz tells it to start at the first occurrence of "xyz" in the file.
XAs a special case, +<number> acts like +<number>g; 
Xthat is, it starts the display at the specified line number
X(however, see the caveat under the "g" command above).
XIf the option starts with ++, the initial command applies to
Xevery file being viewed, not just the first one.
XThe + command described previously
Xmay also be used to set (or change) an initial command for every file.
X
X.SH "KEY BINDINGS"
XYou may define your own 
X.I less
Xcommands by using the program 
X.I lesskey
X(1)
Xto create a file called ".less" in your home directory.
XThis file specifies a set of command keys and an action
Xassociated with each key.
XSee the
X.I lesskey
Xmanual page for more details.
X
X.SH "NATIONAL CHARACTER SETS"
XThere are three types of characters in the input file:
X.IP "normal characters"
Xcan be displayed directly to the screen.
X.IP "control characters"
Xshould not be displayed directly, but are expected to be found
Xin ordinary text files (such as backspace and tab).
X.IP "binary characters"
Xcannot be displayed directly and are not expected to be found
Xin text files.
X.PP
XBy default, 
X.I less
Xuses the ASCII character set.
XIn the ASCII character set, characters
Xwith values between 128 and 255 are treated as binary.
XThe LESSCHARSET environment variable may be used to select
Xanother character set.
XIf it is set to the value "latin1",
Xthe ISO 8859/1 character set is assumed.
XLatin-1 is the same as ASCII, except characters between 128 and 255 are
Xtreated as normal characters.
XThe only valid values for LESSCHARSET currently are "ascii" and "latin1".
X.PP
XIn special cases, it may be desired to tailor
X.I less
Xto use a character set other than the ones definable by LESSCHARSET.
XIn this case, the environment variable LESSCHARDEF can be used
Xto define a character set.
XIt should be set to a string where each character in the string represents
Xone character in the character set.
XThe character "." is used for a normal character, "c" for control,
Xand "b" for binary.
XA decimal number may be used for repetition.
XFor example, "bccc4b." would mean character 0 is binary,
X1, 2 and 3 are control, 4, 5, 6 and 7 are binary, and 8 is normal.
XAll characters after the last are taken to be the same as the last,
Xso characters 9 through 255 would be normal.
X(This is an example, and does not necessarily 
Xrepresent any real character set.)
X.PP
XSetting LESSCHARDEF to "8bcccbcc18b95.b" is the same as setting
XLESSCHARSET to "ascii".
XSetting LESSCHARDEF to "8bcccbcc18b95.33b." is the same as setting
XLESSCHARSET to "latin1".
X.PP
XControl and binary characters are displayed in blinking mode.
XEach such character is displayed in caret notation if possible
X(e.g. ^A for control-A).  Caret notation is used only if 
Xinverting the 0100 bit results in a normal printable character.
XOtherwise, the character is displayed as an octal number preceded
Xby a backslash.
XThis octal format can be changed by 
Xsetting the LESSBINFMT environment variable
Xto a printf-style format string; the default is '\\%o'.
X
X.SH "PROMPTS"
XThe -P option allows you to tailor the prompt to your preference.
XThe string given to the -P option replaces the specified prompt string.
XCertain characters in the string are interpreted specially.
XThe prompt mechanism is rather complicated to provide flexibility,
Xbut the ordinary user need not understand the details of constructing
Xpersonalized prompt strings.
X.sp
XA percent sign followed by a single character is expanded
Xaccording to what the following character is:
X.IP "%b\fIX\fP"
XReplaced by the byte offset into the current input file.
XThe b is followed by a single character (shown as \fIX\fP above)
Xwhich specifies the line whose byte offset is to be used.
XIf the character is a "t", the byte offset of the top line in the
Xdisplay is used,
Xan "m" means use the middle line,
Xa "b" means use the bottom line,
Xa "B" means use the line just after the bottom line,
Xand a "j" means use the "target" line, as specified by the -j option.
X.IP "%B"
XReplaced by the size of the current input file.
X.IP "%E"
XReplaced by the name of the editor (from the EDITOR environment variable).
XSee the discussion of the LESSEDIT feature below.
X.IP "%f"
XReplaced by the name of the current input file.
X.IP "%i"
XReplaced by the index of the current file in the list of
Xinput files.
X.IP "%l\fIX\fP"
XReplaced by the line number of a line in the input file.
XThe line to be used is determined by the \fIX\fP, as with the %b option.
X.IP "%L"
XReplaced by the line number of the last line in the input file.
X.IP "%m"
XReplaced by the total number of input files.
X.IP "%p\fIX\fP"
XReplaced by the percent into the current input file.
XThe line used is determined by the \fIX\fP as with the %b option.
X.IP "%s"
XSame as %B.
X.IP "%t"
XCauses any trailing spaces to be removed.
XUsually used at the end of the string, but may appear anywhere.
X.IP "%x"
XReplaced by the name of the next input file in the list.
X.PP
XIf any item is unknown (for example, the file size if input
Xis a pipe), a question mark is printed instead.
X.PP
XThe format of the prompt string can be changed
Xdepending on certain conditions.
XA question mark followed by a single character acts like an "IF":
Xdepending on the following character, a condition is evaluated.
XIf the condition is true, any characters following the question mark
Xand condition character, up to a period, are included in the prompt.
XIf the condition is false, such characters are not included.
XA colon appearing between the question mark and the
Xperiod can be used to establish an "ELSE": any characters between
Xthe colon and the period are included in the string if and only if
Xthe IF condition is false.
XCondition characters (which follow a question mark) may be:
X.IP "?a"
XTrue if any characters have been included in the prompt so far.
X.IP "?b\fIX\fP"
XTrue if the byte offset of the specified line is known.
X.IP "?B"
XTrue if the size of current input file is known.
X.IP "?e"
XTrue if at end-of-file.
X.IP "?f"
XTrue if there is an input filename
X(that is, if input is not a pipe).
X.IP "?l\fIX\fP"
XTrue if the line number of the specified line is known.
X.IP "?L"
XTrue if the line number of the last line in the file is known.
X.IP "?m"
XTrue if there is more than one input file.
X.IP "?n"
XTrue if this is the first prompt in a new input file.
X.IP "?p\fIX\fP"
XTrue if the percent into the current input file
Xof the specified line is known.
X.IP "?s"
XSame as "?B".
X.IP "?x"
XTrue if there is a next input file
X(that is, if the current input file is not the last one).
X.PP
XAny characters other than the special ones
X(question mark, colon, period, percent, and backslash)
Xbecome literally part of the prompt.
XAny of the special characters may be included in the prompt literally
Xby preceding it with a backslash.
X.PP
XSome examples:
X.sp
X?f%f:Standard input.
X.sp
XThis prompt prints the filename, if known;
Xotherwise the string "Standard input".
X.sp
X?f%f .?ltLine %lt:?pt%pt\\%:?btByte %bt:-...
X.sp
XThis prompt would print the filename, if known.
XThe filename is followed by the line number, if known,
Xotherwise the percent if known, otherwise the byte offset if known.
XOtherwise, a dash is printed.
XNotice how each question mark has a matching period,
Xand how the % after the %pt
Xis included literally by escaping it with a backslash.
X.sp
X?n?f%f\ .?m(file\ %i\ of\ %m)\ ..?e(END)\ ?x-\ Next\\:\ %x..%t
X.sp
XThis prints the filename if this is the first prompt in a file,
Xfollowed by the "file N of N" message if there is more
Xthan one input file.
XThen, if we are at end-of-file, the string "(END)" is printed
Xfollowed by the name of the next file, if there is one.
XFinally, any trailing spaces are truncated.
XThis is the default prompt.
XFor reference, here are the defaults for
Xthe other two prompts (-m and -M respectively).
XEach is broken into two lines here for readability only.
X.nf
X.sp
X?n?f%f\ .?m(file\ %i\ of\ %m)\ ..?e(END)\ ?x-\ Next\\:\ %x.:
X	?pB%pB\\%:byte\ %bB?s/%s...%t
X.sp
X?f%f\ .?n?m(file\ %i\ of\ %m)\ ..?ltline\ %lt?L/%L.\ :byte\ %bB?s/%s.\ .
X	?e(END)\ ?x-\ Next\\:\ %x.:?pB%pB\\%..%t
X.sp
X.fi
XAnd here is the default message produced by the = command:
X.nf
X.sp
X?f%f\ .?m(file\ %i\ of\ %m)\ .?ltline\ %lt?L/%L.\ .
X	byte\ %bB?s/%s.\ ?e(END)\ :?pB%pB\\%..%t
X.fi
X.PP
XThe prompt expansion features are also used for another purpose:
Xif an environment variable LESSEDIT is defined, it is used
Xas the command to be executed when the v command is invoked.
XThe LESSEDIT string is expanded in the same way as the prompt strings.
XThe default value for LESSEDIT is:
X.nf
X.sp
X	%E\ ?lm+%lm.\ %f
X.sp
X.fi
XNote that this expands to the editor name, followed by a + and the
Xline number, followed by the file name.
XIf your editor does not accept the "+linenumber" syntax, or has other
Xdifferences in invocation syntax, the LESSEDIT variable can be 
Xchanged to modify this default.
X
X.SH "ENVIRONMENT VARIABLES"
X.IP COLUMNS
XSets the number of columns on the screen.
XTakes precedence over the number of columns specified by the TERM variable.
X.IP EDITOR
XThe name of the editor (used for the v command).
X.IP HOME
XName of the user's home directory (used to find a .less file).
X.IP LESS
XFlags which are passed to 
X.I less
Xautomatically.
X.IP LESSBINFMT
XFormat for displaying non-printable, non-control characters.
X.IP LESSCHARDEF
XDefines a character set.
X.IP LESSCHARSET
XSelects a predefined character set.
X.IP LESSEDIT
XEditor prototype string (used for the v command).
XSee discussion under PROMPTS.
X.IP LINES
XSets the number of lines on the screen.
XTakes precedence over the number of lines specified by the TERM variable.
X.IP SHELL
XThe shell used to execute the ! command, as well as to expand filenames.
X.IP TERM
XThe type of terminal on which
X.I less
Xis being run.
X
X.SH "SEE ALSO"
Xlesskey(1)
X
X.SH WARNINGS
XThe = command and prompts (unless changed by -P)
Xreport the line number of the line at the top of the screen,
Xbut the byte and percent of the line at the bottom of the screen.
X.PP
XIf the :e command is used to name more than one file,
Xand one of the named files has been viewed previously,
Xthe new files may be entered into the list in an unexpected order.
X.PP
XThe handling of national character sets is nonstandard as well as
Xinsufficient for multibyte characters.
XIt will probably change in a later release.
END_OF_FILE
echo shar: Extracting \"lesskey.nro\"
sed "s/^X//" >'lesskey.nro' <<'END_OF_FILE'
X.TH LESSKEY 1
X.SH NAME
Xlesskey \- specify key bindings for less
X.SH SYNOPSIS
X.B "lesskey [-o output] [input]"
X.SH DESCRIPTION
X.I Lesskey
Xis used to specify a set of key bindings to be used by 
X.I less.
XThe input file is a text file which describes the key bindings,
Xand the output file is a binary file which is used by 
X.I less.
XIf no input file is specified, standard input is used.
XIf no output file is specified, $HOME/.less is used.
X.PP
XThe input file consists of lines of the form:
X.sp
X	string <whitespace> action [extra-string] <newline>
X.sp
XWhitespace is any sequence of one or more spaces and/or tabs.
XThe "string" is the command key(s) which invoke the action.
XThe string may be a single command key, or a sequence of up to 15 keys.
XThe "action" is the name of the less action, from the list below.
XThe characters in the "string" may appear literally, or be
Xprefixed by a carat to indicate a control key.
XA backslash may be used to cause the following character
Xto be taken literally.
XCharacters which must be preceded by backslash include
Xcarat, space, tab and the backslash itself.
XA backslash followed by one to three octal digits may be used to
Xspecify a character by its octal value.
XBlank lines and lines which start with a pound sign (#) are ignored.
X.PP
XAn action may be followed by an extra string.
XThis string is parsed after the command is entered,
Xjust as if it were entered on the command line.
XThis feature can be used in certain cases to extend
Xthe functionality of a command.
XFor example, these entries would create a pair of commands
Xto turn on/off line numbers using
X.I vi
Xsyntax:
X.sp
X.nf
X	:set\\ nu		toggle-option -N
X	:set\\ nonu	toggle-option +N
X.fi
X.sp
XSee also the ":ta" command in the example below.
X
X.SH EXAMPLE
XThe following input file describes the set of
Xdefault command keys used by less:
X.sp
X.nf
X	r		forw-line 
X	n		forw-line 
X	e		forw-line 
X	j		forw-line 
X	^E		forw-line 
X	^N		forw-line 
X	k		back-line 
X	y		back-line 
X	^Y		back-line 
X	^K		back-line 
X	^P		back-line 
X	J		forw-line-force 
X	K		back-line-force 
X	Y		back-line-force 
X	d		forw-scroll 
X	^D		forw-scroll 
X	u		back-scroll 
X	^U		back-scroll 
X	'		back-scroll 
X	\e40		forw-screen 
X	f		forw-screen 
X	^F		forw-screen 
X	^V		forw-screen 
X	b		back-screen 
X	^B		back-screen 
X	\e33v		back-screen 
X	z		forw-window 
X	w		back-window 
X	F		forw-forever 
X	R		repaint-flush 
X	r		repaint 
X	^R		repaint 
X	^L		repaint 
X	g		goto-line 
X	<		goto-line 
X	\e33<		goto-line 
X	p		percent 
X	%		percent 
X	{		forw-bracket {}
X	}		back-bracket {}
X	(		forw-bracket ()
X	)		back-bracket ()
X	[		forw-bracket []
X	]		back-bracket []
X	\e33^F	forw-bracket 
X	\e33^B	back-bracket 
X	G		goto-end 
X	\e33>		goto-end 
X	>		goto-end 
X	P		goto-end 
X	=		status 
X	^G		status 
X	:f		status 
X	/		forw-search 
X	?		back-search 
X	\e33/		forw-search *
X	\e33?		back-search *
X	n		repeat-search 
X	\e33n		repeat-search-all 
X	N		reverse-search 
X	\e33N		reverse-search-all 
X	m		set-mark 
X	'		goto-mark 
X	^X^X		goto-mark 
X	E		examine 
X	:e		examine 
X	^X^V		examine 
X	:n		next-file 
X	:p		prev-file 
X	:x		index-file 
X	-		toggle-option 
X	:t		toggle-option t
X	s		toggle-option o
X	_		display-option 
X	|		pipe 
X	v		visual 
X	!		shell 
X	+		firstcmd 
X	H		help 
X	h		help 
X	V		version 
X	q		quit 
X	:q		quit 
X	:Q		quit 
X	ZZ		quit 
X	\e33\e33	quit 
X.fi
X.sp
XCommands specified by
X.I lesskey
Xtake precedence over the default commands.
XA default command key may be disabled by including it in the
Xkey file with the action "invalid".
X
X.SH "SEE ALSO"
Xless(1)
END_OF_FILE
echo shar: Extracting \"vecho.c\"
sed "s/^X//" >'vecho.c' <<'END_OF_FILE'
X/*
X * This dumb little program emulates the System V "echo" command,
X * to accommodate BSD systems which don't understand the \c escape,
X * meaning don't echo a newline.  BSD uses "echo -n".
X */
X
X#include <stdio.h>
X
Xint putnl;
X
Xmain(argc, argv)
X	int argc;
X	char *argv[];
X{
X	putnl = 1;
X	while (--argc > 0)
X	{
X		vecho(*++argv);
X		if (argc > 1)
X			putchar(' ');
X	}
X	if (putnl)
X		putchar('\n');
X	exit(0);
X}
X
Xvecho(str)
X	char *str;
X{
X	register char *s;
X
X	for (s = str;  *s != '\0';  s++)
X	{
X		if (*s == '\\' && s[1] == 'c')
X		{
X			putnl = 0;
X			return;
X		}
X		putchar(*s);
X	}
X}
END_OF_FILE
echo shar: Extracting \"mkfuncs.awk\"
sed "s/^X//" >'mkfuncs.awk' <<'END_OF_FILE'
XBEGIN { FS="("; state = 0 }
X
X/^	public/ { ftype = $0; state = 1 }
X
X{ if (state == 1)
X	state = 2
X  else if (state == 2)
X	{ print ftype,$1,"();"; state = 0 }
X}
END_OF_FILE

mark@unix386.Convergent.COM (Mark Nudelman) (03/06/91)

#! /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 \"less.man\"
sed "s/^X//" >'less.man' <<'END_OF_FILE'
X
X
X
X     LLLLEEEESSSSSSSS((((1111))))                     UUUUNNNNIIIIXXXX 5555....0000                      LLLLEEEESSSSSSSS((((1111))))
X
X
X
X     NNNNAAAAMMMMEEEE
X          less - opposite of more
X
X     SSSSYYYYNNNNOOOOPPPPSSSSIIIISSSS
X          lllleeeessssssss ----????
X          lllleeeessssssss [[[[----[[[[++++]]]]aaaaAAAABBBBccccCCCCddddeeeeEEEEffffiiiimmmmMMMMnnnnNNNNqqqqQQQQrrrrssssSSSSuuuuUUUUwwww]]]] [[[[----bbbb_N]]]] [[[[----xxxx_N]]]] [[[[----[[[[zzzz]]]]_N]]]]
X               [[[[----hhhh_N]]]] [[[[----yyyy_N]]]] [[[[----PPPP[[[[mmmmMMMM====]]]]_s_t_r_i_n_g]]]] [[[[----[[[[ooooOOOO]]]]_l_o_g_f_i_l_e]]]] [[[[----kkkk_k_e_y_f_i_l_e]]]]
X               [[[[----tttt_t_a_g]]]] [[[[----TTTT_t_a_g_s_f_i_l_e]]]] [[[[++++_c_m_d]]]] [[[[_f_i_l_e_n_a_m_e]]]]............
X
X
X     DDDDEEEESSSSCCCCRRRRIIIIPPPPTTTTIIIIOOOONNNN
X          _L_e_s_s is a program similar to _m_o_r_e (1), but which allows
X          backward movement in the file as well as forward movement.
X          Also, _l_e_s_s does not have to read the entire input file
X          before starting, so with large input files it starts up
X          faster than text editors like _v_i (1).  _L_e_s_s uses termcap (or
X          terminfo on some systems), so it can run on a variety of
X          terminals.  There is even limited support for hardcopy
X          terminals.  (On a hardcopy terminal, lines which should be
X          printed at the top of the screen are prefixed with an up-
X          arrow.)
X
X          Commands are based on both _m_o_r_e and _v_i. Commands may be
X          preceded by a decimal number, called N in the descriptions
X          below.  The number is used by some commands, as indicated.
X
X
X     CCCCOOOOMMMMMMMMAAAANNNNDDDDSSSS
X          In the following descriptions, ^X means control-X.  ESC
X          stands for the ESCAPE key; for example ESC-v means the two
X          character sequence "ESCAPE", then "v".
X
X          h or H
X               Help: display a summary of these commands.  If you
X               forget all the other commands, remember this one.
X
X          SPACE or ^V or f or ^F
X               Scroll forward N lines, default one window (see option
X               -z below).  If N is more than the screen size, only the
X               final screenful is displayed.  Warning: some systems
X               use ^V as a special literalization character.
X
X          z    Like SPACE, but if N is specified, it becomes the new
X               window size.
X
X          RETURN or ^N or e or ^E or j or ^J
X               Scroll forward N lines, default 1.  The entire N lines
X               are displayed, even if N is more than the screen size.
X
X          d or ^D
X               Scroll forward N lines, default one half of the screen
X               size.  If N is specified, it becomes the new default
X
X
X
X     Page 1                                           (printed 3/2/91)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSS((((1111))))                     UUUUNNNNIIIIXXXX 5555....0000                      LLLLEEEESSSSSSSS((((1111))))
X
X
X
X               for subsequent d and u commands.
X
X          b or ^B or ESC-v
X               Scroll backward N lines, default one window (see option
X               -z below).  If N is more than the screen size, only the
X               final screenful is displayed.
X
X          w    Like ESC-v, but if N is specified, it becomes the new
X               window size.
X
X          y or ^Y or ^P or k or ^K
X               Scroll backward N lines, default 1.  The entire N lines
X               are displayed, even if N is more than the screen size.
X               Warning: some systems use ^Y as a special job control
X               character.
X
X          u or ^U
X               Scroll backward N lines, default one half of the screen
X               size.  If N is specified, it becomes the new default
X               for subsequent d and u commands.
X
X          r or ^R or ^L
X               Repaint the screen.
X
X          R    Repaint the screen, discarding any buffered input.
X               Useful if the file is changing while it is being
X               viewed.
X
X          F    Scroll forward, and keep trying to read when the end of
X               file is reached.  Normally this command would be used
X               when already at the end of the file.  It is a way to
X               monitor the tail of a file which is growing while it is
X               being viewed.  (The behavior is similar to the "tail
X               -f" command.)
X
X          g or < or ESC-<
X               Go to line N in the file, default 1 (beginning of
X               file).  (Warning: this may be slow if N is large.)
X
X          G or > or ESC->
X               Go to line N in the file, default the end of the file.
X               (Warning: this may be slow if N is large, or if N is
X               not specified and standard input, rather than a file,
X               is being read.)
X
X          p or %
X               Go to a position N percent into the file.  N should be
X               between 0 and 100.  (This works if standard input is
X               being read, but only if _l_e_s_s has already read to the
X               end of the file.  It is always fast, but not always
X               useful.)
X
X
X
X
X     Page 2                                           (printed 3/2/91)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSS((((1111))))                     UUUUNNNNIIIIXXXX 5555....0000                      LLLLEEEESSSSSSSS((((1111))))
X
X
X
X          {    If a left curly bracket appears in the top line
X               displayed on the screen, the { command will go to the
X               matching right curly bracket.  The matching right curly
X               bracket is positioned on the bottom line of the screen.
X               If there is more than one left curly bracket on the top
X               line, a number N may be used to specify the N-th
X               bracket on the line.
X
X          }    If a right curly bracket appears in the bottom line
X               displayed on the screen, the } command will go to the
X               matching left curly bracket.  The matching left curly
X               bracket is positioned on the top line of the screen.
X               If there is more than one right curly bracket on the
X               top line, a number N may be used to specify the N-th
X               bracket on the line.
X
X          (    Like {, but applies to parentheses rather than curly
X               brackets.
X
X          )    Like }, but applies to parentheses rather than curly
X               brackets.
X
X          [    Like {, but applies to square brackets rather than
X               curly brackets.
X
X          ]    Like }, but applies to square brackets rather than
X               curly brackets.
X
X          ESC-^F
X               Followed by two characters, acts like {, but uses the
X               two characters as open and close brackets,
X               respectively.  For example, "ESC ^F < >" could be used
X               to go forward to the > which matches the < in the top
X               displayed line.
X
X          ESC-^B
X               Followed by two characters, acts like }, but uses the
X               two characters as open and close brackets,
X               respectively.  For example, "ESC ^B < >" could be used
X               to go backward to the < which matches the > in the
X               bottom displayed line.
X
X          m    Followed by any lowercase letter, marks the current
X               position with that letter.
X
X          '    (Single quote.) Followed by any lowercase letter,
X               returns to the position which was previously marked
X               with that letter.  Followed by another single quote,
X               returns to the position at which the last "large"
X               movement command was executed.  Followed by a ^ or $,
X               jumps to the beginning or end of the file respectively.
X               Marks are preserved when a new file is examined, so the
X
X
X
X     Page 3                                           (printed 3/2/91)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSS((((1111))))                     UUUUNNNNIIIIXXXX 5555....0000                      LLLLEEEESSSSSSSS((((1111))))
X
X
X
X               ' command can be used to switch between input files.
X
X          ^X^X Same as single quote.
X
X          /pattern
X               Search forward in the file for the N-th line containing
X               the pattern.  N defaults to 1.  The pattern is a
X               regular expression, as recognized by _e_d. The search
X               starts at the second line displayed (but see the -a and
X               -j options, which change this).
X
X               Certain characters are special if entered at the
X               beginning of the pattern; they modify the type of
X               search rather than become part of the pattern:
X
X               !    Search for lines which do NOT match the pattern.
X
X               *    Search multiple files.  That is, if the search
X                    reaches the end of the current file without
X                    finding a match, the search continues in the next
X                    file in the command line list.
X
X               @    Begin the search at the first line of the first
X                    file in the command line list, regardless of what
X                    is currently displayed on the screen or the
X                    settings of the -a or -j options.
X
X          ?pattern
X               Search backward in the file for the N-th line
X               containing the pattern.  The search starts at the line
X               immediately before the top line displayed.
X
X               Certain characters are special as in the / command:
X
X               !    Search for lines which do NOT match the pattern.
X
X               *    Search multiple files.  That is, if the search
X                    reaches the beginning of the current file without
X                    finding a match, the search continues in the
X                    previous file in the command line list.
X
X               @    Begin the search at the last line of the last file
X                    in the command line list, regardless of what is
X                    currently displayed on the screen or the settings
X                    of the -a or -j options.
X
X          ESC-/pattern
X               Same as "/*".
X
X          ESC-?pattern
X               Same as "?*".
X
X
X
X
X     Page 4                                           (printed 3/2/91)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSS((((1111))))                     UUUUNNNNIIIIXXXX 5555....0000                      LLLLEEEESSSSSSSS((((1111))))
X
X
X
X          n    Repeat previous search, for N-th line containing the
X               last pattern.  If the previous search was modified by
X               !, the search is made for the N-th line NOT containing
X               the pattern.  If the previous search was modified by *,
X               the search continues in the next (or previous) file if
X               not satisfied in the current file.  There is no effect
X               if the previous search was modified by @.
X
X          N    Repeat previous search, but in the reverse direction.
X
X          ESC-n
X               Repeat previous search, but crossing file boundaries.
X               The effect is as if the previous search were modified
X               by *.
X
X          ESC-N
X               Repeat previous search, but in the reverse direction
X               and crossing file boundaries.
X
X          :e [filename]
X               Examine a new file.  If the filename is missing, the
X               "current" file (see the :n and :p commands below) from
X               the list of files in the command line is re-examined.
X               A percent sign (%) in the filename is replaced by the
X               name of the current file. A pound sign (#) is replaced
X               by the name of the previously examined file.  The
X               filename is inserted into the command line list of
X               files so that it can be seen by subsequent :n and :p
X               commands.  If the filename consists of several files,
X               they are all inserted into the list of files and the
X               first one is examined.
X
X          ^X^V or E
X               Same as :e.  Warning: some systems use ^V as a special
X               literalization character.
X
X          :n   Examine the next file (from the list of files given in
X               the command line).  If a number N is specified, the N-
X               th next file is examined.
X
X          :p   Examine the previous file in the command line list.  If
X               a number N is specified, the N-th previous file is
X               examined.
X
X          :x   Examine the first file in the command line list.  If a
X               number N is specified, the N-th file in the list is
X               examined.
X
X          = or ^G or :f
X               Prints some information about the file being viewed,
X               including its name and the line number and byte offset
X               of the bottom line being displayed.  If possible, it
X
X
X
X     Page 5                                           (printed 3/2/91)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSS((((1111))))                     UUUUNNNNIIIIXXXX 5555....0000                      LLLLEEEESSSSSSSS((((1111))))
X
X
X
X               also prints the length of the file, the number of lines
X               in the file and the percent of the file above the last
X               displayed line.
X
X          -    Followed by one of the command line option letters (see
X               below), this will change the setting of that option and
X               print a message describing the new setting.  If the
X               option letter has a numeric value (such as -b or -h),
X               or a string value (such as -P or -t), a new value may
X               be entered after the option letter.  If no new value is
X               entered, a message describing the current setting is
X               printed and nothing is changed.
X
X          -+   Followed by one of the command line option letters (see
X               below), this will reset the option to its default
X               setting and print a message describing the new setting.
X               (The "-+_X" command does the same thing as "-+_X" on the
X               command line.) This does not work for string-valued
X               options.
X
X          --   Followed by one of the command line option letters (see
X               below), this will reset the option to the "opposite" of
X               its default setting and print a message describing the
X               new setting.  (The "--_X" command does the same thing as
X               "-_X" on the command line.) This does not work for
X               numeric or string-valued options.
X
X          _    (Underscore.) Followed by one of the command line
X               option letters (see below), this will print a message
X               describing the current setting of that option.  The
X               setting of the option is not changed.
X
X          +cmd Causes the specified cmd to be executed each time a new
X               file is examined.  For example, +G causes _l_e_s_s to
X               initially display each file starting at the end rather
X               than the beginning.
X
X          V    Prints the version number of _l_e_s_s being run.
X
X          q or :q or :Q or ZZ or ESC ESC
X               Exits _l_e_s_s.
X
X          The following three commands may or may not be valid,
X          depending on your particular installation.
X
X          v    Invokes an editor to edit the current file being
X               viewed.  The editor is taken from the environment
X               variable EDITOR, or defaults to "vi".  See also the
X               discussion of LESSEDIT under the section on PROMPTS
X               below.
X
X          ! shell-command
X
X
X
X     Page 6                                           (printed 3/2/91)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSS((((1111))))                     UUUUNNNNIIIIXXXX 5555....0000                      LLLLEEEESSSSSSSS((((1111))))
X
X
X
X               Invokes a shell to run the shell-command given.  A
X               percent sign (%) in the command is replaced by the name
X               of the current file. A pound sign (#) is replaced by
X               the name of the previously examined file.  "!!" repeats
X               the last shell command.  "!" with no shell command
X               simply invokes a shell.  In all cases, the shell is
X               taken from the environment variable SHELL, or defaults
X               to "sh".
X
X          | <m> shell-command
X               <m> represents any mark letter.  Pipes a section of the
X               input file to the given shell command.  The section of
X               the file to be piped is between the current position
X               and the position marked by the letter.  <m> may also be
X               ^ or $ to indicate beginning or end of file
X               respectively.  If <m> is . or newline, the current
X               screen is piped.  The current screen is the minimum
X               amount piped in any case.
X
X     OOOOPPPPTTTTIIIIOOOONNNNSSSS
X          Command line options are described below.  Most options may
X          be changed while _l_e_s_s is running, via the "-" command.
X
X          Options are also taken from the environment variable "LESS".
X          For example, to avoid typing "less -options ..." each time
X          _l_e_s_s is invoked, you might tell _c_s_h:
X
X          setenv LESS "-options"
X
X          or if you use _s_h:
X
X          LESS="-options"; export LESS
X
X          The environment variable is parsed before the command line,
X          so command line options override the LESS environment
X          variable.  If an option appears in the LESS variable, it can
X          be reset to its default on the command line by beginning the
X          command line option with "-+".
X
X          A dollar sign ($) may be used to signal the end of an option
X          string.  This is important only for options like -P which
X          take a following string.
X
X          -?   This option displays a summary of the commands accepted
X               by _l_e_s_s (the same as the h command).  If this option is
X               given, all other options are ignored, and _l_e_s_s exits
X               after the help screen is viewed.  (Depending on how
X               your shell interprets the question mark, it may be
X               necessary to quote the question mark, thus: "-\?".)
X
X          -a   Causes searches to start after the last line displayed
X               on the screen, thus skipping all lines displayed on the
X
X
X
X     Page 7                                           (printed 3/2/91)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSS((((1111))))                     UUUUNNNNIIIIXXXX 5555....0000                      LLLLEEEESSSSSSSS((((1111))))
X
X
X
X               screen.  By default, searches start at the second line
X               on the screen (or after the last found line; see the -j
X               option).
X
X          -b_n  Causes _l_e_s_s to use a non-standard number of buffers.
X               Buffers are 1K, and by default 10 buffers are used
X               (except if data in coming from standard input; see the
X               -B option).  The number _n specifies a different number
X               of buffers to use.
X
X          -B   Disables automatic allocation of buffers, so that only
X               the default number of buffers are used.  If more data
X               is read than will fit in the buffers, the oldest data
X               is discarded.  By default, when data is coming from
X               standard input, buffers are allocated automatically as
X               needed to avoid loss of data.
X
X          -c   Causes full screen repaints to be painted from the top
X               line down.  By default, full screen repaints are done
X               by scrolling from the bottom of the screen.
X
X          -C   The -C option is like -c, but the screen is cleared
X               before it is repainted.
X
X          -d   The -d option suppresses the error message normally
X               displayed if the terminal is dumb; that is, lacks some
X               important capability, such as the ability to clear the
X               screen or scroll backward.  The -d option does not
X               otherwise change the behavior of _l_e_s_s on a dumb
X               terminal).
X
X          -e   Causes _l_e_s_s to automatically exit the second time it
X               reaches end-of-file.  By default, the only way to exit
X               _l_e_s_s is via the "q" command.
X
X          -E   Causes _l_e_s_s to automatically exit the first time it
X               reaches end-of-file.
X
X          -f   Forces non-regular files to be opened.  (A non-regular
X               file is a directory or a device special file.) Also
X               suppresses the warning message when a binary file is
X               opened.  By default, _l_e_s_s will refuse to open non-
X               regular files.
X
X          -h_n  Specifies a maximum number of lines to scroll backward.
X               If it is necessary to scroll backward more than _n
X               lines, the screen is repainted in a forward direction
X               instead.  (If the terminal does not have the ability to
X               scroll backward, -h0 is implied.)
X
X          -i   Causes searches to ignore case; that is, uppercase and
X               lowercase are considered identical.  Also, text which
X
X
X
X     Page 8                                           (printed 3/2/91)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSS((((1111))))                     UUUUNNNNIIIIXXXX 5555....0000                      LLLLEEEESSSSSSSS((((1111))))
X
X
X
X               is overstruck or underlined can be searched for.  This
X               option is ignored if any uppercase letters appear in
X               the search pattern.
X
X          -j_n  Specifies a line on the screen where "target" lines are
X               to be positioned.  Target lines are the object of text
X               searches, tag searches, jumps to a line number, jumps
X               to a file percentage, and jumps to a marked position.
X               The screen line is specified by a number: the top line
X               on the screen is 1, the next is 2, and so on.  The
X               number may be negative to specify a line relative to
X               the bottom of the screen: the bottom line on the screen
X               is -1, the second to the bottom is -2, and so on.  If
X               the -j option is used, searches begin at the line
X               immediately after the target line.  For example, if "-
X               j4" is used, the target line is the fourth line on the
X               screen, so searches begin at the fifth line on the
X               screen.
X
X          -k_f_i_l_e_n_a_m_e
X               Causes _l_e_s_s to open and interpret the named file as a
X               _l_e_s_s_k_e_y (1) file.  Multiple -k options may be
X               specified.  If a file called .less exists in the user's
X               home directory, this file is also used as a _l_e_s_s_k_e_y
X               file.
X
X          -m   Causes _l_e_s_s to prompt verbosely (like _m_o_r_e), with the
X               percent into the file.  By default, _l_e_s_s prompts with a
X               colon.
X
X          -M   Causes _l_e_s_s to prompt even more verbosely than _m_o_r_e.
X
X          -n   Suppresses line numbers.  The default (to use line
X               numbers) may cause _l_e_s_s to run more slowly in some
X               cases, especially with a very large input file.
X               Suppressing line numbers with the -n flag will avoid
X               this problem.  Using line numbers means: the line
X               number will be displayed in the verbose prompt and in
X               the = command, and the v command will pass the current
X               line number to the editor (see also the discussion of
X               LESSEDIT in PROMPTS below).
X
X          -N   Causes a line number to be displayed at the beginning
X               of each line in the display.
X
X          -o_f_i_l_e_n_a_m_e
X               Causes _l_e_s_s to copy its input to the named file as it
X               is being viewed.  This applies only when the input file
X               is a pipe, not an ordinary file.  If the file already
X               exists, _l_e_s_s will ask for confirmation before
X               overwriting it.
X
X
X
X
X     Page 9                                           (printed 3/2/91)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSS((((1111))))                     UUUUNNNNIIIIXXXX 5555....0000                      LLLLEEEESSSSSSSS((((1111))))
X
X
X
X          -O_f_i_l_e_n_a_m_e
X               The -O option is like -o, but it will overwrite an
X               existing file without asking for confirmation.
X
X               If no log file has been specified, the -o and -O
X               options can be used from within _l_e_s_s to specify a log
X               file.  Without a file name, they will simply report the
X               name of the log file.  The "s" command is equivalent to
X               specifying -o from within _l_e_s_s.
X
X          -p_p_a_t_t_e_r_n
X               The -p option on the command line is equivalent to
X               specifying +/_p_a_t_t_e_r_n; that is, it tells _l_e_s_s to start
X               at the first occurence of _p_a_t_t_e_r_n in the file.
X
X          -P_p_r_o_m_p_t
X               Provides a way to tailor the three prompt styles to
X               your own preference.  This option would normally be put
X               in the LESS environment variable, rather than being
X               typed in with each _l_e_s_s command.  Such an option must
X               either be the last option in the LESS variable, or be
X               terminated by a dollar sign.  -P followed by a string
X               changes the default (short) prompt to that string.  -Pm
X               changes the medium (-m) prompt to the string, and -PM
X               changes the long (-M) prompt.  Also, -P= changes the
X               message printed by the = command to the given string.
X               All prompt strings consist of a sequence of letters and
X               special escape sequences.  See the section on PROMPTS
X               for more details.
X
X          -q   Causes moderately "quiet" operation: the terminal bell
X               is not rung if an attempt is made to scroll past the
X               end of the file or before the beginning of the file.
X               If the terminal has a "visual bell", it is used
X               instead.  The bell will be rung on certain other
X               errors, such as typing an invalid character.  The
X               default is to ring the terminal bell in all such cases.
X
X          -Q   Causes totally "quiet" operation: the terminal bell is
X               never rung.
X
X          -r   Causes "raw" control characters to be displayed.  The
X               default is to display control characters using the
X               caret notation; for example, a control-A (octal 001) is
X               displayed as "^A".  Warning: when the -r flag is used,
X               _l_e_s_s cannot keep track of the actual appearance of the
X               screen (since this depends on how the screen responds
X               to each type of control character).  Thus, various
X               display problems may result, such as long lines being
X               split in the wrong place.
X
X          -s   Causes consecutive blank lines to be squeezed into a
X
X
X
X     Page 10                                          (printed 3/2/91)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSS((((1111))))                     UUUUNNNNIIIIXXXX 5555....0000                      LLLLEEEESSSSSSSS((((1111))))
X
X
X
X               single blank line.  This is useful when viewing _n_r_o_f_f
X               output.
X
X          -S   Causes lines longer than the screen width to be chopped
X               rather than folded.  That is, the remainder of a long
X               line is simply discarded.  The default is to fold long
X               lines; that is, display the remainder on the next line.
X
X          -t_t_a_g
X               The -t option, followed immediately by a TAG, will edit
X               the file containing that tag.  For this to work, there
X               must be a file called "tags" in the current directory,
X               which was previously built by the _c_t_a_g_s (1) command.
X               This option may also be specified from within _l_e_s_s
X               (using the - command) as a way of examining a new file.
X               The command ":t" is equivalent to specifying -t from
X               within _l_e_s_s.
X
X          -T_t_a_g_s_f_i_l_e
X               Specifies a tags file to be used instead of "tags".
X
X          -u   Causes backspaces and carriage returns to be treated as
X               printable characters; that is, they are sent to the
X               terminal when they appear in the input.
X
X          -U   Causes backspaces and carriage returns to be treated as
X               control characters; that is, they are handled as
X               specified by the -r option.
X
X               By default, if neither -u nor -U is given, backspaces
X               which appear adjacent to an underscore character are
X               treated specially: the underlined text is displayed
X               using the terminal's hardware underlining capability.
X               Also, backspaces which appear between two identical
X               characters are treated specially: the overstruck text
X               is printed using the terminal's hardware boldface
X               capability.  Other backspaces are deleted, along with
X               the preceding character.  Carriage returns immediately
X               followed by a newline are deleted.  Other carriage
X               returns are handled as specified by the -r option.
X
X          -w   Causes blank lines to be used to represent lines past
X               the end of the file.  By default, a tilde character is
X               used.
X
X          -x_n  Sets tab stops every _n positions.  The default for _n is
X               8.
X
X          -y_n  Specifies a maximum number of lines to scroll forward.
X               If it is necessary to scroll forward more than _n lines,
X               the screen is repainted instead.  The -c or -C option
X               may be used to repaint from the top of the screen if
X
X
X
X     Page 11                                          (printed 3/2/91)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSS((((1111))))                     UUUUNNNNIIIIXXXX 5555....0000                      LLLLEEEESSSSSSSS((((1111))))
X
X
X
X               desired.  By default, any forward movement causes
X               scrolling.
X
X          -[z]_n
X               Changes the default scrolling window size to _n lines.
X               The default is one screenful.  The z and w commands can
X               also be used to change the window size.  The "z" may be
X               omitted, as in "-_n" for compatibility with _m_o_r_e.
X
X          +    If a command line option begins with ++++, the remainder
X               of that option is taken to be an initial command to
X               _l_e_s_s. For example, +G tells _l_e_s_s to start at the end of
X               the file rather than the beginning, and +/xyz tells it
X               to start at the first occurrence of "xyz" in the file.
X               As a special case, +<number> acts like +<number>g; that
X               is, it starts the display at the specified line number
X               (however, see the caveat under the "g" command above).
X               If the option starts with ++, the initial command
X               applies to every file being viewed, not just the first
X               one.  The + command described previously may also be
X               used to set (or change) an initial command for every
X               file.
X
X
X     KKKKEEEEYYYY BBBBIIIINNNNDDDDIIIINNNNGGGGSSSS
X          You may define your own _l_e_s_s commands by using the program
X          _l_e_s_s_k_e_y (1) to create a file called ".less" in your home
X          directory.  This file specifies a set of command keys and an
X          action associated with each key.  See the _l_e_s_s_k_e_y manual
X          page for more details.
X
X
X     NNNNAAAATTTTIIIIOOOONNNNAAAALLLL CCCCHHHHAAAARRRRAAAACCCCTTTTEEEERRRR SSSSEEEETTTTSSSS
X          There are three types of characters in the input file:
X
X          normal characters
X               can be displayed directly to the screen.
X
X          control characters
X               should not be displayed directly, but are expected to
X               be found in ordinary text files (such as backspace and
X               tab).
X
X          binary characters
X               cannot be displayed directly and are not expected to be
X               found in text files.
X
X          By default, _l_e_s_s uses the ASCII character set.  In the ASCII
X          character set, characters with values between 128 and 255
X          are treated as binary.  The LESSCHARSET environment variable
X          may be used to select another character set.  If it is set
X          to the value "latin1", the ISO 8859/1 character set is
X
X
X
X     Page 12                                          (printed 3/2/91)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSS((((1111))))                     UUUUNNNNIIIIXXXX 5555....0000                      LLLLEEEESSSSSSSS((((1111))))
X
X
X
X          assumed.  Latin-1 is the same as ASCII, except characters
X          between 128 and 255 are treated as normal characters.  The
X          only valid values for LESSCHARSET currently are "ascii" and
X          "latin1".
X
X          In special cases, it may be desired to tailor _l_e_s_s to use a
X          character set other than the ones definable by LESSCHARSET.
X          In this case, the environment variable LESSCHARDEF can be
X          used to define a character set.  It should be set to a
X          string where each character in the string represents one
X          character in the character set.  The character "." is used
X          for a normal character, "c" for control, and "b" for binary.
X          A decimal number may be used for repetition.  For example,
X          "bccc4b." would mean character 0 is binary, 1, 2 and 3 are
X          control, 4, 5, 6 and 7 are binary, and 8 is normal.  All
X          characters after the last are taken to be the same as the
X          last, so characters 9 through 255 would be normal.  (This is
X          an example, and does not necessarily represent any real
X          character set.)
X
X          Setting LESSCHARDEF to "8bcccbcc18b95.b" is the same as
X          setting LESSCHARSET to "ascii".  Setting LESSCHARDEF to
X          "8bcccbcc18b95.33b." is the same as setting LESSCHARSET to
X          "latin1".
X
X          Control and binary characters are displayed in blinking
X          mode.  Each such character is displayed in caret notation if
X          possible (e.g. ^A for control-A).  Caret notation is used
X          only if inverting the 0100 bit results in a normal printable
X          character.  Otherwise, the character is displayed as an
X          octal number preceded by a backslash.  This octal format can
X          be changed by setting the LESSBINFMT environment variable to
X          a printf-style format string; the default is '\%o'.
X
X
X     PPPPRRRROOOOMMMMPPPPTTTTSSSS
X          The -P option allows you to tailor the prompt to your
X          preference.  The string given to the -P option replaces the
X          specified prompt string.  Certain characters in the string
X          are interpreted specially.  The prompt mechanism is rather
X          complicated to provide flexibility, but the ordinary user
X          need not understand the details of constructing personalized
X          prompt strings.
X
X          A percent sign followed by a single character is expanded
X          according to what the following character is:
X
X          %b_X  Replaced by the byte offset into the current input
X               file.  The b is followed by a single character (shown
X               as _X above) which specifies the line whose byte offset
X               is to be used.  If the character is a "t", the byte
X               offset of the top line in the display is used, an "m"
X
X
X
X     Page 13                                          (printed 3/2/91)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSS((((1111))))                     UUUUNNNNIIIIXXXX 5555....0000                      LLLLEEEESSSSSSSS((((1111))))
X
X
X
X               means use the middle line, a "b" means use the bottom
X               line, a "B" means use the line just after the bottom
X               line, and a "j" means use the "target" line, as
X               specified by the -j option.
X
X          %B   Replaced by the size of the current input file.
X
X          %E   Replaced by the name of the editor (from the EDITOR
X               environment variable).  See the discussion of the
X               LESSEDIT feature below.
X
X          %f   Replaced by the name of the current input file.
X
X          %i   Replaced by the index of the current file in the list
X               of input files.
X
X          %l_X  Replaced by the line number of a line in the input
X               file.  The line to be used is determined by the _X, as
X               with the %b option.
X
X          %L   Replaced by the line number of the last line in the
X               input file.
X
X          %m   Replaced by the total number of input files.
X
X          %p_X  Replaced by the percent into the current input file.
X               The line used is determined by the _X as with the %b
X               option.
X
X          %s   Same as %B.
X
X          %t   Causes any trailing spaces to be removed.  Usually used
X               at the end of the string, but may appear anywhere.
X
X          %x   Replaced by the name of the next input file in the
X               list.
X
X          If any item is unknown (for example, the file size if input
X          is a pipe), a question mark is printed instead.
X
X          The format of the prompt string can be changed depending on
X          certain conditions.  A question mark followed by a single
X          character acts like an "IF": depending on the following
X          character, a condition is evaluated.  If the condition is
X          true, any characters following the question mark and
X          condition character, up to a period, are included in the
X          prompt.  If the condition is false, such characters are not
X          included.  A colon appearing between the question mark and
X          the period can be used to establish an "ELSE": any
X          characters between the colon and the period are included in
X          the string if and only if the IF condition is false.
X          Condition characters (which follow a question mark) may be:
X
X
X
X     Page 14                                          (printed 3/2/91)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSS((((1111))))                     UUUUNNNNIIIIXXXX 5555....0000                      LLLLEEEESSSSSSSS((((1111))))
X
X
X
X          ?a   True if any characters have been included in the prompt
X               so far.
X
X          ?b_X  True if the byte offset of the specified line is known.
X
X          ?B   True if the size of current input file is known.
X
X          ?e   True if at end-of-file.
X
X          ?f   True if there is an input filename (that is, if input
X               is not a pipe).
X
X          ?l_X  True if the line number of the specified line is known.
X
X          ?L   True if the line number of the last line in the file is
X               known.
X
X          ?m   True if there is more than one input file.
X
X          ?n   True if this is the first prompt in a new input file.
X
X          ?p_X  True if the percent into the current input file of the
X               specified line is known.
X
X          ?s   Same as "?B".
X
X          ?x   True if there is a next input file (that is, if the
X               current input file is not the last one).
X
X          Any characters other than the special ones (question mark,
X          colon, period, percent, and backslash) become literally part
X          of the prompt.  Any of the special characters may be
X          included in the prompt literally by preceding it with a
X          backslash.
X
X          Some examples:
X
X          ?f%f:Standard input.
X
X          This prompt prints the filename, if known; otherwise the
X          string "Standard input".
X
X          ?f%f .?ltLine %lt:?pt%pt\%:?btByte %bt:-...
X
X          This prompt would print the filename, if known.  The
X          filename is followed by the line number, if known, otherwise
X          the percent if known, otherwise the byte offset if known.
X          Otherwise, a dash is printed.  Notice how each question mark
X          has a matching period, and how the % after the %pt is
X          included literally by escaping it with a backslash.
X
X          ?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\: %x..%t
X
X
X
X     Page 15                                          (printed 3/2/91)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSS((((1111))))                     UUUUNNNNIIIIXXXX 5555....0000                      LLLLEEEESSSSSSSS((((1111))))
X
X
X
X          This prints the filename if this is the first prompt in a
X          file, followed by the "file N of N" message if there is more
X          than one input file.  Then, if we are at end-of-file, the
X          string "(END)" is printed followed by the name of the next
X          file, if there is one.  Finally, any trailing spaces are
X          truncated.  This is the default prompt.  For reference, here
X          are the defaults for the other two prompts (-m and -M
X          respectively).  Each is broken into two lines here for
X          readability only.
X
X          ?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\: %x.:
X               ?pB%pB\%:byte %bB?s/%s...%t
X
X          ?f%f .?n?m(file %i of %m) ..?ltline %lt?L/%L. :byte %bB?s/%s. .
X               ?e(END) ?x- Next\: %x.:?pB%pB\%..%t
X
X          And here is the default message produced by the = command:
X
X          ?f%f .?m(file %i of %m) .?ltline %lt?L/%L. .
X               byte %bB?s/%s. ?e(END) :?pB%pB\%..%t
X
X          The prompt expansion features are also used for another
X          purpose: if an environment variable LESSEDIT is defined, it
X          is used as the command to be executed when the v command is
X          invoked.  The LESSEDIT string is expanded in the same way as
X          the prompt strings.  The default value for LESSEDIT is:
X
X               %E ?lm+%lm. %f
X
X          Note that this expands to the editor name, followed by a +
X          and the line number, followed by the file name.  If your
X          editor does not accept the "+linenumber" syntax, or has
X          other differences in invocation syntax, the LESSEDIT
X          variable can be changed to modify this default.
X
X
X     EEEENNNNVVVVIIIIRRRROOOONNNNMMMMEEEENNNNTTTT VVVVAAAARRRRIIIIAAAABBBBLLLLEEEESSSS
X          COLUMNS
X               Sets the number of columns on the screen.  Takes
X               precedence over the number of columns specified by the
X               TERM variable.
X
X          EDITOR
X               The name of the editor (used for the v command).
X
X          HOME Name of the user's home directory (used to find a .less
X               file).
X
X          LESS Flags which are passed to _l_e_s_s automatically.
X
X          LESSBINFMT
X               Format for displaying non-printable, non-control
X
X
X
X     Page 16                                          (printed 3/2/91)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSS((((1111))))                     UUUUNNNNIIIIXXXX 5555....0000                      LLLLEEEESSSSSSSS((((1111))))
X
X
X
X               characters.
X
X          LESSCHARDEF
X               Defines a character set.
X
X          LESSCHARSET
X               Selects a predefined character set.
X
X          LESSEDIT
X               Editor prototype string (used for the v command).  See
X               discussion under PROMPTS.
X
X          LINES
X               Sets the number of lines on the screen.  Takes
X               precedence over the number of lines specified by the
X               TERM variable.
X
X          SHELL
X               The shell used to execute the ! command, as well as to
X               expand filenames.
X
X          TERM The type of terminal on which _l_e_s_s is being run.
X
X
X     SSSSEEEEEEEE AAAALLLLSSSSOOOO
X          lesskey(1)
X
X
X     WWWWAAAARRRRNNNNIIIINNNNGGGGSSSS
X          The = command and prompts (unless changed by -P) report the
X          line number of the line at the top of the screen, but the
X          byte and percent of the line at the bottom of the screen.
X
X          If the :e command is used to name more than one file, and
X          one of the named files has been viewed previously, the new
X          files may be entered into the list in an unexpected order.
X
X          The handling of national character sets is nonstandard as
X          well as insufficient for multibyte characters.  It will
X          probably change in a later release.
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X     Page 17                                          (printed 3/2/91)
X
X
X
END_OF_FILE
echo shar: Extracting \"lesskey.man\"
sed "s/^X//" >'lesskey.man' <<'END_OF_FILE'
X
X
X
X     LLLLEEEESSSSSSSSKKKKEEEEYYYY((((1111))))                  UUUUNNNNIIIIXXXX 5555....0000                   LLLLEEEESSSSSSSSKKKKEEEEYYYY((((1111))))
X
X
X
X     NNNNAAAAMMMMEEEE
X          lesskey - specify key bindings for less
X
X     SSSSYYYYNNNNOOOOPPPPSSSSIIIISSSS
X          lllleeeesssssssskkkkeeeeyyyy [[[[----oooo oooouuuuttttppppuuuutttt]]]] [[[[iiiinnnnppppuuuutttt]]]]
X
X     DDDDEEEESSSSCCCCRRRRIIIIPPPPTTTTIIIIOOOONNNN
X          _L_e_s_s_k_e_y is used to specify a set of key bindings to be used
X          by _l_e_s_s. The input file is a text file which describes the
X          key bindings, and the output file is a binary file which is
X          used by _l_e_s_s. If no input file is specified, standard input
X          is used.  If no output file is specified, $HOME/.less is
X          used.
X
X          The input file consists of lines of the form:
X
X               string <whitespace> action [extra-string] <newline>
X
X          Whitespace is any sequence of one or more spaces and/or
X          tabs.  The "string" is the command key(s) which invoke the
X          action.  The string may be a single command key, or a
X          sequence of up to 15 keys.  The "action" is the name of the
X          less action, from the list below.  The characters in the
X          "string" may appear literally, or be prefixed by a carat to
X          indicate a control key.  A backslash may be used to cause
X          the following character to be taken literally.  Characters
X          which must be preceded by backslash include carat, space,
X          tab and the backslash itself.  A backslash followed by one
X          to three octal digits may be used to specify a character by
X          its octal value.  Blank lines and lines which start with a
X          pound sign (#) are ignored.
X
X          An action may be followed by an extra string.  This string
X          is parsed after the command is entered, just as if it were
X          entered on the command line.  This feature can be used in
X          certain cases to extend the functionality of a command.  For
X          example, these entries would create a pair of commands to
X          turn on/off line numbers using _v_i syntax:
X
X               :set\ nu       toggle-option -N
X               :set\ nonu     toggle-option +N
X
X          See also the ":ta" command in the example below.
X
X
X     EEEEXXXXAAAAMMMMPPPPLLLLEEEE
X          The following input file describes the set of default
X          command keys used by less:
X
X               r         forw-line
X               n         forw-line
X               e         forw-line
X
X
X
X     Page 1                                           (printed 3/2/91)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSSKKKKEEEEYYYY((((1111))))                  UUUUNNNNIIIIXXXX 5555....0000                   LLLLEEEESSSSSSSSKKKKEEEEYYYY((((1111))))
X
X
X
X               j         forw-line
X               ^E        forw-line
X               ^N        forw-line
X               k         back-line
X               y         back-line
X               ^Y        back-line
X               ^K        back-line
X               ^P        back-line
X               J         forw-line-force
X               K         back-line-force
X               Y         back-line-force
X               d         forw-scroll
X               ^D        forw-scroll
X               u         back-scroll
X               ^U        back-scroll
X               '         back-scroll
X               \40       forw-screen
X               f         forw-screen
X               ^F        forw-screen
X               ^V        forw-screen
X               b         back-screen
X               ^B        back-screen
X               \33v      back-screen
X               z         forw-window
X               w         back-window
X               F         forw-forever
X               R         repaint-flush
X               r         repaint
X               ^R        repaint
X               ^L        repaint
X               g         goto-line
X               <         goto-line
X               \33<      goto-line
X               p         percent
X               %         percent
X               {         forw-bracket {}
X               }         back-bracket {}
X               (         forw-bracket ()
X               )         back-bracket ()
X               [         forw-bracket []
X               ]         back-bracket []
X               \33^F     forw-bracket
X               \33^B     back-bracket
X               G         goto-end
X               \33>      goto-end
X               >         goto-end
X               P         goto-end
X               =         status
X               ^G        status
X               :f        status
X               /         forw-search
X               ?         back-search
X
X
X
X     Page 2                                           (printed 3/2/91)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSSKKKKEEEEYYYY((((1111))))                  UUUUNNNNIIIIXXXX 5555....0000                   LLLLEEEESSSSSSSSKKKKEEEEYYYY((((1111))))
X
X
X
X               \33/      forw-search *
X               \33?      back-search *
X               n         repeat-search
X               \33n      repeat-search-all
X               N         reverse-search
X               \33N      reverse-search-all
X               m         set-mark
X               '         goto-mark
X               ^X^X      goto-mark
X               E         examine
X               :e        examine
X               ^X^V      examine
X               :n        next-file
X               :p        prev-file
X               :x        index-file
X               -         toggle-option
X               :t        toggle-option t
X               s         toggle-option o
X               _         display-option
X               |         pipe
X               v         visual
X               !         shell
X               +         firstcmd
X               H         help
X               h         help
X               V         version
X               q         quit
X               :q        quit
X               :Q        quit
X               ZZ        quit
X               \33\33    quit
X
X          Commands specified by _l_e_s_s_k_e_y take precedence over the
X          default commands.  A default command key may be disabled by
X          including it in the key file with the action "invalid".
X
X
X     SSSSEEEEEEEE AAAALLLLSSSSOOOO
X          less(1)
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X     Page 3                                           (printed 3/2/91)
X
X
X
END_OF_FILE
echo shar: Extracting \"less.h\"
sed "s/^X//" >'less.h' <<'END_OF_FILE'
X/*
X * Standard include file for "less".
X */
X
X/*
X * Include the file of compile-time options.
X */
X#include "defines.h"
X
X/*
X * Language details.
X */
X#if !VOID
X#define	void  int
X#endif
X#define	public		/* PUBLIC FUNCTION */
X
X/*
X * Special types and constants.
X */
Xtypedef long		POSITION;
X/*
X * {{ Warning: if POSITION is changed to other than "long",
X *    you may have to change some of the printfs which use "%ld"
X *    to print a variable of type POSITION. }}
X */
X
X#define	NULL_POSITION	((POSITION)(-1))
X
X/*
X * The type of an interrupt handler.
X */
X#define	HANDLER		void
X
X/*
X * An IFILE represents an input file.
X */
X#define	IFILE		VOID_POINTER
X#define	NULL_IFILE	((IFILE)NULL)
X
X/*
X * The structure used to represent a "screen position".
X * This consists of a file position, and a screen line number.
X * The meaning is that the line starting at the given file
X * position is displayed on the ln-th line of the screen.
X * (Screen lines before ln are empty.)
X */
Xstruct scrpos
X{
X	POSITION pos;
X	int ln;
X};
X
Xtypedef union parg
X{
X	char *p_string;
X	int p_int;
X} PARG;
X
X#define	NULL_PARG	((PARG *)NULL)
X
X#define	EOI		(-1)
X
X#ifndef NULL
X#define	NULL		(0)
X#endif
X
X#define	READ_INTR	(-2)
X
X/* How quiet should we be? */
X#define	NOT_QUIET	0	/* Ring bell at eof and for errors */
X#define	LITTLE_QUIET	1	/* Ring bell only for errors */
X#define	VERY_QUIET	2	/* Never ring bell */
X
X/* How should we prompt? */
X#define	PR_SHORT	0	/* Prompt with colon */
X#define	PR_MEDIUM	1	/* Prompt with message */
X#define	PR_LONG		2	/* Prompt with longer message */
X
X/* How should we handle backspaces? */
X#define	BS_SPECIAL	0	/* Do special things for underlining and bold */
X#define	BS_NORMAL	1	/* \b treated as normal char; actually output */
X#define	BS_CONTROL	2	/* \b treated as control char; prints as ^H */
X
X/* How should we search? */
X#define	SRCH_FORW	0	/* Search forward from current position */
X#define	SRCH_BACK	1	/* Search backward from current position */
X#define	SRCH_NOMATCH	0100	/* Search for non-matching lines */
X#define	SRCH_PAST_EOF	0200	/* Search past end-of-file, into next file */
X#define	SRCH_FIRST_FILE	0400	/* Search starting at the first file */
X
X#define	SRCH_DIR(t)	((t) & 077)
X#define	SRCH_FLAG(t)	((t) & 07700)
X
X/* Special chars used to tell put_line() to do something special */
X#define	NORMAL		(0)
X#define	UNDERLINE	(1)
X#define	BOLD		(2)
X#define	BLINK		(3)
X#define	INVIS		(4)
X
X#define	CONTROL(c)		((c)&037)
X#define	ESC			CONTROL('[')
X
X#define	SIGNAL(sig,func)	signal(sig,func)
X
X/* Library function declarations */
Xoffset_t lseek();
X#define	BAD_LSEEK	((offset_t)-1)
XVOID_POINTER calloc();
X
X#define	ch_zero()	((POSITION)0)
X#include "funcs.h"
END_OF_FILE
echo shar: Extracting \"position.h\"
sed "s/^X//" >'position.h' <<'END_OF_FILE'
X/*
X * Include file for interfacing to position.c modules.
X */
X#define	TOP		(0)
X#define	TOP_PLUS_ONE	(1)
X#define	BOTTOM		(-1)
X#define	BOTTOM_PLUS_ONE	(-2)
X#define	MIDDLE		(-3)
END_OF_FILE
echo shar: Extracting \"cmd.h\"
sed "s/^X//" >'cmd.h' <<'END_OF_FILE'
X#define	MAX_USERCMD		500
X#define	MAX_CMDLEN		16
X
X#define	A_B_LINE		2
X#define	A_B_SCREEN		3
X#define	A_B_SCROLL		4
X#define	A_B_SEARCH		5
X#define	A_DIGIT			6
X#define	A_DISP_OPTION		7
X#define	A_DEBUG			8
X#define	A_EXAMINE		9
X#define	A_FIRSTCMD		10
X#define	A_FREPAINT		11
X#define	A_F_LINE		12
X#define	A_F_SCREEN		13
X#define	A_F_SCROLL		14
X#define	A_F_SEARCH		15
X#define	A_GOEND			16
X#define	A_GOLINE		17
X#define	A_GOMARK		18
X#define	A_HELP			19
X#define	A_NEXT_FILE		20
X#define	A_PERCENT		21
X#define	A_PREFIX		22
X#define	A_PREV_FILE		23
X#define	A_QUIT			24
X#define	A_REPAINT		25
X#define	A_SETMARK		26
X#define	A_SHELL			27
X#define	A_STAT			28
X#define	A_FF_LINE		29
X#define	A_BF_LINE		30
X#define	A_VERSION		31
X#define	A_VISUAL		32
X#define	A_F_WINDOW		33
X#define	A_B_WINDOW		34
X#define	A_F_BRACKET		35
X#define	A_B_BRACKET		36
X#define	A_PIPE			37
X#define	A_INDEX_FILE		38
X
X
X
X
X#define	A_AGAIN_SEARCH		43
X#define	A_T_AGAIN_SEARCH	44
X#define	A_REVERSE_SEARCH	45
X#define	A_T_REVERSE_SEARCH	46
X#define	A_OPT_TOGGLE		47
X#define	A_OPT_SET		48
X#define	A_OPT_UNSET		49
X#define	A_F_FOREVER		50
X#define	A_GOPOS			51
X
X#define	A_INVALID		100
X#define	A_NOACTION		101
X#define	A_UINVALID		102
X
X#define	A_EXTRA			0200
END_OF_FILE
echo shar: Extracting \"option.h\"
sed "s/^X//" >'option.h' <<'END_OF_FILE'
X#define	END_OPTION_STRING	('$')
X
X/*
X * Types of options.
X */
X#define	BOOL		01	/* Boolean option: 0 or 1 */
X#define	TRIPLE		02	/* Triple-valued option: 0, 1 or 2 */
X#define	NUMBER		04	/* Numeric option */
X#define	STRING		010	/* String-valued option */
X#define	NOVAR		020	/* No associated variable */
X#define	REPAINT		040	/* Repaint screen after toggling option */
X#define	NO_TOGGLE	0100	/* Option cannot be toggled with "-" cmd */
X
X#define	OTYPE		(BOOL|TRIPLE|NUMBER|STRING|NOVAR)
X
X/*
X * Argument to a handling function tells what type of activity:
X */
X#define	INIT	0	/* Initialization (from command line) */
X#define	QUERY	1	/* Query (from _ or - command) */
X#define	TOGGLE	2	/* Change value (from - command) */
X
X/* Flag to toggle_option to specify how to "toggle" */
X#define	OPT_NO_TOGGLE	0
X#define	OPT_TOGGLE	1
X#define	OPT_UNSET	2
X#define	OPT_SET		3
X
Xstruct option
X{
X	char oletter;		/* The controlling letter (a-z) */
X	char otype;		/* Type of the option */
X	int odefault;		/* Default value */
X	int *ovar;		/* Pointer to the associated variable */
X	void (*ofunc)();	/* Pointer to special handling function */
X	char *odesc[3];		/* Description of each value */
X};
X
END_OF_FILE

mark@unix386.Convergent.COM (Mark Nudelman) (03/06/91)

#! /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 \"lesskey.c\"
sed "s/^X//" >'lesskey.c' <<'END_OF_FILE'
X/*
X *	lesskey [-o output] [input]
X *
X *	Make a .less file.
X *	If no input file is specified, standard input is used.
X *	If no output file is specified, $HOME/.less is used.
X *
X *	The .less file is used to specify (to "less") user-defined
X *	key bindings.  Basically any sequence of 1 to MAX_CMDLEN
X *	keystrokes may be bound to an existing less function.
X *
X *	The input file is an ascii file consisting of a 
X *	sequence of lines of the form:
X *		string <whitespace> action [chars] <newline>
X *
X *	"string" is a sequence of command characters which form
X *		the new user-defined command.  The command
X *		characters may be:
X *		1. The actual character itself.
X *		2. A character preceded by ^ to specify a
X *		   control character (e.g. ^X means control-X).
X *		3. Any character (other than an octal digit) preceded by
X *		   a \ to specify the character itself (characters which
X *		   must be preceded by \ include ^, \, and whitespace.
X *		4. A backslash followed by one to three octal digits
X *		   to specify a character by its octal value.
X *	"action" is the name of a "less" action, from the table below.
X *	"chars" is an optional sequence of characters which is treated
X *		as keyboard input after the command is executed.
X *
X *	Blank lines and lines which start with # are ignored.
X *
X *
X *	The output file is a non-ascii file, consisting of
X *	zero or more byte sequences of the form:
X *		string <0> <action>
X *	or
X *		string <0> <action|A_EXTRA> chars <0>
X *
X *	"string" is the command string.
X *	"<0>" is one null byte.
X *	"<action>" is one byte containing the action code (the A_xxx value).
X *	If action is ORed with A_EXTRA, the action byte is followed
X *		by the null-terminated "chars" string.
X */
X
X#include <stdio.h>
X#include "less.h"
X#include "cmd.h"
X
Xchar usertable[MAX_USERCMD];
X
Xstruct cmdname
X{
X	char *cn_name;
X	int cn_action;
X} cmdnames[] = 
X{
X	"back-bracket",		A_B_BRACKET,
X	"back-line",		A_B_LINE,
X	"back-line-force",	A_BF_LINE,
X	"back-screen",		A_B_SCREEN,
X	"back-scroll",		A_B_SCROLL,
X	"back-search",		A_B_SEARCH,
X	"back-window",		A_B_WINDOW,
X	"debug",		A_DEBUG,
X	"display-flag",		A_DISP_OPTION,
X	"display-option",	A_DISP_OPTION,
X	"end",			A_GOEND,
X	"examine",		A_EXAMINE,
X	"first-cmd",		A_FIRSTCMD,
X	"firstcmd",		A_FIRSTCMD,
X	"flush-repaint",	A_FREPAINT,
X	"forw-bracket",		A_F_BRACKET,
X	"forw-forever",		A_F_FOREVER,
X	"forw-line",		A_F_LINE,
X	"forw-line-force",	A_FF_LINE,
X	"forw-screen",		A_F_SCREEN,
X	"forw-scroll",		A_F_SCROLL,
X	"forw-search",		A_F_SEARCH,
X	"forw-window",		A_F_WINDOW,
X	"goto-end",		A_GOEND,
X	"goto-line",		A_GOLINE,
X	"goto-mark",		A_GOMARK,
X	"help",			A_HELP,
X	"index-file",		A_INDEX_FILE,
X	"invalid",		A_UINVALID,
X	"next-file",		A_NEXT_FILE,
X	"noaction",		A_NOACTION,
X	"percent",		A_PERCENT,
X	"pipe",			A_PIPE,
X	"prev-file",		A_PREV_FILE,
X	"quit",			A_QUIT,
X	"repaint",		A_REPAINT,
X	"repaint-flush",	A_FREPAINT,
X	"repeat-search",	A_AGAIN_SEARCH,
X	"repeat-search-all",	A_T_AGAIN_SEARCH,
X	"reverse-search",	A_REVERSE_SEARCH,
X	"reverse-search-all",	A_T_REVERSE_SEARCH,
X	"set-mark",		A_SETMARK,
X	"shell",		A_SHELL,
X	"status",		A_STAT,
X	"toggle-flag",		A_OPT_TOGGLE,
X	"toggle-option",	A_OPT_TOGGLE,
X	"version",		A_VERSION,
X	"visual",		A_VISUAL,
X	NULL,			0
X};
X
Xmain(argc, argv)
X	int argc;
X	char *argv[];
X{
X	char *p;		/* {{ Can't be register since we use &p }} */
X	register char *up;	/* Pointer into usertable */
X	FILE *desc;		/* Description file (input) */
X	FILE *out;		/* Output file */
X	int linenum;		/* Line number in input file */
X	char *currcmd;		/* Start of current command string */
X	int errors;
X	int i, j;
X	char line[200];
X	char *outfile;
X
X	extern char *getenv();
X
X	/*
X	 * Process command line arguments.
X	 */
X	outfile = NULL;
X	while (--argc > 0 && **(++argv) == '-')
X	{
X		switch (argv[0][1])
X		{
X		case 'o':
X			outfile = &argv[0][2];
X			if (*outfile == '\0')
X			{
X				if (--argc <= 0)
X					usage();
X				outfile = *(++argv);
X			}
X			break;
X		default:
X			usage();
X		}
X	}
X	if (argc > 1)
X		usage();
X
X
X	/*
X	 * Open the input file, or use standard input if none specified.
X	 */
X	if (argc > 0)
X	{
X		if ((desc = fopen(*argv, "r")) == NULL)
X		{
X			perror(*argv);
X			exit(1);
X		}
X	} else
X		desc = stdin;
X
X	/*
X	 * Read the input file, one line at a time.
X	 * Each line consists of a command string,
X	 * followed by white space, followed by an action name.
X	 */
X	linenum = 0;
X	errors = 0;
X	up = usertable;
X	while (fgets(line, sizeof(line), desc) != NULL)
X	{
X		++linenum;
X
X		/*
X		 * Skip leading white space.
X		 * Replace the final newline with a null byte.
X		 * Ignore blank lines and comment lines.
X		 */
X		p = line;
X		while (*p == ' ' || *p == '\t')
X			++p;
X		for (i = 0;  p[i] != '\n' && p[i] != '\0';  i++)
X			;
X		p[i] = '\0';
X		if (*p == '#' || *p == '\0')
X			continue;
X
X		/*
X		 * Parse the command string and store it in the usertable.
X		 */
X		currcmd = up;
X		do
X		{
X			if (up >= usertable + MAX_USERCMD)
X			{
X				fprintf(stderr, "too many commands, line %d\n",
X					linenum);
X				exit(1);
X			}
X			if (up >= currcmd + MAX_CMDLEN)
X			{
X				fprintf(stderr, "command too long on line %d\n",
X					linenum);
X				errors++;
X				break;
X			}
X
X			*up++ = tchar(&p);
X
X		} while (*p != ' ' && *p != '\t' && *p != '\0');
X
X		/*
X		 * Terminate the command string with a null byte.
X		 */
X		*up++ = '\0';
X
X		/*
X		 * Skip white space between the command string
X		 * and the action name.
X		 * Terminate the action name with a null byte if it 
X		 * is followed by whitespace or a # comment.
X		 */
X		if (*p == '\0')
X		{
X			fprintf(stderr, "missing whitespace on line %d\n",
X				linenum);
X			errors++;
X			continue;
X		}
X		while (*p == ' ' || *p == '\t')
X			++p;
X		for (j = 0;  p[j] != ' ' && p[j] != '\t' && 
X			     p[j] != '#' && p[j] != '\0';  j++)
X			;
X		p[j] = '\0';
X
X		/*
X		 * Parse the action name and store it in the usertable.
X		 */
X		for (i = 0;  cmdnames[i].cn_name != NULL;  i++)
X			if (strcmp(cmdnames[i].cn_name, p) == 0)
X				break;
X		if (cmdnames[i].cn_name == NULL)
X		{
X			fprintf(stderr, "unknown action <%s> on line %d\n",
X				p, linenum);
X			errors++;
X			continue;
X		}
X		*up++ = cmdnames[i].cn_action;
X
X		/*
X		 * See if an extra string follows the action name.
X		 */
X		for (j = j+1;  p[j] == ' ' || p[j] == '\t';  j++)
X			;
X		p += j;
X		if (*p != '\0')
X		{
X			/*
X			 * OR the special value A_EXTRA into the action byte.
X			 * Put the extra string after the action byte.
X			 */
X			up[-1] |= A_EXTRA;
X			while (*p != '\0')
X				*up++ = tchar(&p);
X			*up++ = '\0';
X		}
X	}
X
X	if (errors > 0)
X	{
X		fprintf(stderr, "%d errors; no output produced\n", errors);
X		exit(1);
X	}
X
X	/*
X	 * Write the output file.
X	 * If no output file was specified, use "$HOME/.less"
X	 */
X	if (outfile == NULL)
X	{
X		p = getenv("HOME");
X		if (p == NULL || *p == '\0')
X		{
X			fprintf(stderr, "cannot find $HOME - using current directory\n");
X#if __MSDOS__
X			strcpy(line, "_less");
X#else
X			strcpy(line, ".less");
X#endif
X		} else
X		{
X			strcpy(line, p);
X#if __MSDOS__
X			strcat(line, "\\_less");
X#else
X			strcat(line, "/.less");
X#endif
X		}
X		outfile = line;
X	}
X	if ((out = fopen(outfile, "w")) == NULL)
X		perror(outfile);
X	else
X		fwrite((char *)usertable, 1, up-usertable, out);
X}
X
X/*
X * Parse one character of a string.
X */
Xtchar(pp)
X	char **pp;
X{
X	register char *p;
X	register char ch;
X	register int i;
X
X	p = *pp;
X	switch (*p)
X	{
X	case '\\':
X		if (*++p >= '0' && *p <= '7')
X		{
X			/*
X			 * Parse an octal number.
X			 */
X			ch = 0;
X			i = 0;
X			do
X				ch = 8*ch + (*p - '0');
X			while (*++p >= '0' && *p <= '7' && ++i < 3);
X			*pp = p;
X			return (ch);
X		}
X		/*
X		 * Backslash followed by a char just means that char.
X		 */
X		*pp = p+1;
X		return (*p);
X	case '^':
X		/*
X		 * Carat means CONTROL.
X		 */
X		*pp = p+2;
X		return (CONTROL(p[1]));
X	}
X	*pp = p+1;
X	return (*p);
X}
X
Xusage()
X{
X	fprintf(stderr, "usage: lesskey [-o output] [input]\n");
X	exit(1);
X}
END_OF_FILE
echo shar: Extracting \"ch.c\"
sed "s/^X//" >'ch.c' <<'END_OF_FILE'
X/*
X * Low level character input from the input file.
X * We use these special purpose routines which optimize moving
X * both forward and backward from the current read pointer.
X */
X
X#include "less.h"
X
Xpublic int file = -1;		/* File descriptor of the input file */
Xpublic int ignore_eoi;
X
X/*
X * Pool of buffers holding the most recently used blocks of the input file.
X */
X#define BUFSIZ	1024
Xstruct buf {
X	struct buf *next, *prev;  /* Must be first to match struct filestate */
X	long block;
X	unsigned int datasize;
X	unsigned char data[BUFSIZ];
X};
X
X/*
X * The buffer pool is kept as a doubly-linked circular list,
X * in order from most- to least-recently used.
X * The circular list is anchored by the file state "thisfile".
X *
X * The file state is maintained in a filestate structure.
X * There are two such structures, one used when input is a pipe
X * and the other when input is an ordinary file.
X * This is so that we can leave a pipe, look and other files,
X * and return to the pipe without losing buffered data.
X * Buffered data can be reconstructed for a non-pipe file by
X * simply re-reading the file, but a pipe cannot be re-read.
X */
X
Xstatic struct filestate {
X	struct buf *next, *prev;   /* Must be first to match struct buf */
X	POSITION fpos;
X	int nbufs;
X	long block;
X	int offset;
X	POSITION fsize;
X};
X
X#define	END_OF_CHAIN	((struct buf *)thisfile)
X#define	buf_head	thisfile->next
X#define	buf_tail	thisfile->prev
X#define	ch_nbufs	thisfile->nbufs
X#define	ch_block	thisfile->block
X#define	ch_offset	thisfile->offset
X#define	ch_fpos		thisfile->fpos
X#define	ch_fsize	thisfile->fsize
X
Xstatic struct filestate pipefile =
X	{ (struct buf *)&pipefile, (struct buf *)&pipefile };
X
Xstatic struct filestate nonpipefile = 
X	{ (struct buf *)&nonpipefile, (struct buf *)&nonpipefile };
X
Xstatic struct filestate *thisfile;
X
Xextern int ispipe;
Xextern int autobuf;
Xextern int sigs;
X#if LOGFILE
Xextern int logfile;
X#endif
X
Xstatic int ch_addbuf();
X
X
X/*
X * Get the character pointed to by the read pointer.
X * ch_get() is a macro which is more efficient to call
X * than fch_get (the function), in the usual case 
X * that the block desired is at the head of the chain.
X */
X#define	ch_get()   ((ch_block == buf_head->block && \
X		     ch_offset < buf_head->datasize) ? \
X			buf_head->data[ch_offset] : fch_get())
X	static int
Xfch_get()
X{
X	register struct buf *bp;
X	register int n;
X	register int slept;
X	POSITION pos;
X	POSITION len;
X
X	slept = 0;
X
X	/*
X	 * Look for a buffer holding the desired block.
X	 */
X	for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
X		if (bp->block == ch_block)
X		{
X			if (ch_offset >= bp->datasize)
X				/*
X				 * Need more data in this buffer.
X				 */
X				goto read_more;
X			goto found;
X		}
X	/*
X	 * Block is not in a buffer.  
X	 * Take the least recently used buffer 
X	 * and read the desired block into it.
X	 * If the LRU buffer has data in it, 
X	 * and autobuf is true, and input is a pipe, 
X	 * then try to allocate a new buffer first.
X	 */
X	if (autobuf && ispipe && buf_tail->block != (long)(-1))
X		if (ch_addbuf(1))
X			/*
X			 * Allocation failed: turn off autobuf.
X			 */
X			autobuf = 0;
X	bp = buf_tail;
X	bp->block = ch_block;
X	bp->datasize = 0;
X
X    read_more:
X	pos = (ch_block * BUFSIZ) + bp->datasize;
X	if ((len = ch_length()) != NULL_POSITION && pos >= len)
X		/*
X		 * At end of file.
X		 */
X		return (EOI);
X
X	if (pos != ch_fpos)
X	{
X		/*
X		 * Not at the correct position: must seek.
X		 * If input is a pipe, we're in trouble (can't seek on a pipe).
X		 * Some data has been lost: just return "?".
X		 */
X		if (ispipe)
X			return ('?');
X		if (lseek(file, (offset_t)pos, 0) == BAD_LSEEK)
X		{
X 			error("seek error", NULL_PARG);
X 			quit(1);
X 		}
X 		ch_fpos = pos;
X 	}
X
X	/*
X	 * Read the block.
X	 * If we read less than a full block, that's ok.
X	 * We use partial block and pick up the rest next time.
X	 */
X	n = iread(file, &bp->data[bp->datasize], 
X		(unsigned int)(BUFSIZ - bp->datasize));
X	if (n == READ_INTR)
X		return (EOI);
X	if (n < 0)
X	{
X		error("read error", NULL_PARG);
X		quit(1);
X	}
X	ch_fpos += n;
X
X#if LOGFILE
X	/*
X	 * If we have a log file, write the new data to it.
X	 */
X	if (logfile >= 0 && n > 0)
X		write(logfile, &bp->data[bp->datasize], n);
X#endif
X
X	bp->datasize += n;
X
X	/*
X	 * If we have read to end of file, set ch_fsize to indicate
X	 * the position of the end of file.
X	 */
X	if (n == 0)
X	{
X		ch_fsize = pos;
X		if (ignore_eoi)
X		{
X			/*
X			 * We are ignoring EOF.
X			 * Wait a while, then try again.
X			 */
X			if (!slept)
X				ierror("Waiting for data", NULL_PARG);
X			sleep(1);
X			slept = 1;
X		}
X		if (sigs)
X			return (EOI);
X	}
X
X    found:
X	if (buf_head != bp)
X	{
X		/*
X		 * Move the buffer to the head of the buffer chain.
X		 * This orders the buffer chain, most- to least-recently used.
X		 */
X		bp->next->prev = bp->prev;
X		bp->prev->next = bp->next;
X
X		bp->next = buf_head;
X		bp->prev = END_OF_CHAIN;
X		buf_head->prev = bp;
X		buf_head = bp;
X	}
X
X	if (ch_offset >= bp->datasize)
X		/*
X		 * After all that, we still don't have enough data.
X		 * Go back and try again.
X		 */
X		goto read_more;
X
X	return (bp->data[ch_offset]);
X}
X
X#if LOGFILE
X/*
X * Close the logfile.
X * If we haven't read all of standard input into it, do that now.
X */
X	public void
Xend_logfile()
X{
X	static int tried = 0;
X
X	if (logfile < 0)
X		return;
X	if (!tried && ch_fsize == NULL_POSITION)
X	{
X		tried = 1;
X		ierror("Finishing logfile", NULL_PARG);
X		while (ch_forw_get() != EOI)
X			if (sigs)
X				break;
X	}
X	close(logfile);
X	logfile = -1;
X}
X
X/*
X * Start a log file AFTER less has already been running.
X * Invoked from the - command; see toggle_option().
X * Write all the existing buffered data to the log file.
X */
X	public void
Xsync_logfile()
X{
X	register struct buf *bp;
X	long block;
X	long last_block;
X
X	last_block = (ch_fpos + BUFSIZ - 1) / BUFSIZ;
X	for (block = 0;  block <= last_block;  block++)
X		for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
X			if (bp->block == block)
X			{
X				write(logfile, bp->data, bp->datasize);
X				break;
X			}
X}
X
X#endif
X
X/*
X * Determine if a specific block is currently in one of the buffers.
X */
X	static int
Xbuffered(block)
X	long block;
X{
X	register struct buf *bp;
X
X	for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
X		if (bp->block == block)
X			return (1);
X	return (0);
X}
X
X/*
X * Seek to a specified position in the file.
X * Return 0 if successful, non-zero if can't seek there.
X */
X	public int
Xch_seek(pos)
X	register POSITION pos;
X{
X	long new_block;
X	POSITION len;
X
X	len = ch_length();
X	if (pos < ch_zero() || (len != NULL_POSITION && pos > len))
X		return (1);
X
X	new_block = pos / BUFSIZ;
X	if (ispipe && pos != ch_fpos && !buffered(new_block))
X		return (1);
X	/*
X	 * Set read pointer.
X	 */
X	ch_block = new_block;
X	ch_offset = pos % BUFSIZ;
X	return (0);
X}
X
X/*
X * Seek to the end of the file.
X */
X	public int
Xch_end_seek()
X{
X	POSITION len;
X
X	if (!ispipe)
X		ch_fsize = filesize(file);
X
X	len = ch_length();
X	if (len != NULL_POSITION)
X		return (ch_seek(len));
X
X	/*
X	 * Do it the slow way: read till end of data.
X	 */
X	while (ch_forw_get() != EOI)
X		if (sigs)
X			return (1);
X	return (0);
X}
X
X/*
X * Seek to the beginning of the file, or as close to it as we can get.
X * We may not be able to seek there if input is a pipe and the
X * beginning of the pipe is no longer buffered.
X */
X	public int
Xch_beg_seek()
X{
X	register struct buf *bp, *firstbp;
X
X	/*
X	 * Try a plain ch_seek first.
X	 */
X	if (ch_seek(ch_zero()) == 0)
X		return (0);
X
X	/*
X	 * Can't get to position 0.
X	 * Look thru the buffers for the one closest to position 0.
X	 */
X	firstbp = bp = buf_head;
X	if (bp == END_OF_CHAIN)
X		return (1);
X	while ((bp = bp->next) != END_OF_CHAIN)
X		if (bp->block < firstbp->block)
X			firstbp = bp;
X	ch_block = firstbp->block;
X	ch_offset = 0;
X	return (0);
X}
X
X/*
X * Return the length of the file, if known.
X */
X	public POSITION
Xch_length()
X{
X	if (ignore_eoi)
X		return (NULL_POSITION);
X	return (ch_fsize);
X}
X
X/*
X * Return the current position in the file.
X */
X#define	tellpos(blk,off)   ((POSITION)((((long)(blk)) * BUFSIZ) + (off)))
X
X	public POSITION
Xch_tell()
X{
X	return (tellpos(ch_block, ch_offset));
X}
X
X/*
X * Get the current char and post-increment the read pointer.
X */
X	public int
Xch_forw_get()
X{
X	register int c;
X
X	c = ch_get();
X	if (c == EOI)
X		return (EOI);
X	if (ch_offset < BUFSIZ-1)
X		ch_offset++;
X	else
X	{
X#if __ZOFFSET /* NOT WORKING */
X		if (ch_fsize != NULL_POSITION && 
X		    tellpos(ch_block+1, 0) >= ch_fsize)
X			return (EOI);
X#endif
X		ch_block ++;
X		ch_offset = 0;
X	}
X	return (c);
X}
X
X/*
X * Pre-decrement the read pointer and get the new current char.
X */
X	public int
Xch_back_get()
X{
X	if (ch_offset > 0)
X		ch_offset --;
X	else
X	{
X#if __ZOFFSET /* NOT WORKING */
X		if (tellpos(ch_block-1, BUFSIZ-1) < ch_zero())
X			return (EOI);
X#else
X		if (ch_block <= 0)
X			return (EOI);
X#endif
X		if (ispipe && !buffered(ch_block-1))
X			return (EOI);
X		ch_block--;
X		ch_offset = BUFSIZ-1;
X	}
X	return (ch_get());
X}
X
X/*
X * Allocate buffers.
X * Caller wants us to have a total of at least want_nbufs buffers.
X */
X	public int
Xch_nbuf(want_nbufs)
X	int want_nbufs;
X{
X	PARG parg;
X
X	if (ch_nbufs < want_nbufs && ch_addbuf(want_nbufs - ch_nbufs))
X	{
X		/*
X		 * Cannot allocate enough buffers.
X		 * If we don't have ANY, then quit.
X		 * Otherwise, just report the error and return.
X		 */
X		parg.p_int = want_nbufs - ch_nbufs;
X		error("Cannot allocate %d buffers", &parg);
X		if (ch_nbufs == 0)
X			quit(1);
X	}
X	return (ch_nbufs);
X}
X
X/*
X * Flush any saved file state, including buffer contents.
X */
X	public void
Xch_flush()
X{
X	register struct buf *bp;
X
X	if (ispipe)
X	{
X		/*
X		 * If input is a pipe, we don't flush buffer contents,
X		 * since the contents can't be recovered.
X		 */
X		ch_fsize = NULL_POSITION;
X		return;
X	}
X
X	/*
X	 * Initialize all the buffers.
X	 */
X	for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
X		bp->block = (long)(-1);
X
X	/*
X	 * Figure out the size of the file, if we can.
X	 */
X	ch_fsize = filesize(file);
X
X	/*
X	 * Seek to a known position: the beginning of the file.
X	 */
X	ch_fpos = 0;
X	ch_block = ch_fpos / BUFSIZ;
X	ch_offset = ch_fpos % BUFSIZ;
X
X	if (lseek(file, (offset_t)0, 0) == BAD_LSEEK)
X	{
X		/*
X		 * Warning only; even if the seek fails for some reason,
X		 * there's a good chance we're at the beginning anyway.
X		 * {{ I think this is bogus reasoning. }}
X		 */
X		error("seek error to 0", NULL_PARG);
X	}
X}
X
X/*
X * Allocate some new buffers.
X * The buffers are added to the tail of the buffer chain.
X */
X	static int
Xch_addbuf(nnew)
X	int nnew;
X{
X	register struct buf *bp;
X	register struct buf *newbufs;
X
X	/*
X	 * We don't have enough buffers.  
X	 * Allocate some new ones.
X	 */
X	newbufs = (struct buf *) calloc(nnew, sizeof(struct buf));
X	if (newbufs == NULL)
X		return (1);
X
X	/*
X	 * Initialize the new buffers and link them together.
X	 * Link them all onto the tail of the buffer list.
X	 */
X	ch_nbufs += nnew;
X	for (bp = &newbufs[0];  bp < &newbufs[nnew];  bp++)
X	{
X		bp->next = bp + 1;
X		bp->prev = bp - 1;
X		bp->block = (long)(-1);
X	}
X	newbufs[nnew-1].next = END_OF_CHAIN;
X	newbufs[0].prev = buf_tail;
X	buf_tail->next = &newbufs[0];
X	buf_tail = &newbufs[nnew-1];
X	return (0);
X}
X
X/*
X * Use the pipe file state.
X */
X	public void
Xch_pipe()
X{
X	thisfile = &pipefile;
X}
X
X/*
X * Use the non-pipe file state.
X */
X	public void
Xch_nonpipe()
X{
X	thisfile = &nonpipefile;
X}
END_OF_FILE
echo shar: Extracting \"cmdbuf.c\"
sed "s/^X//" >'cmdbuf.c' <<'END_OF_FILE'
X/*
X * Functions which manipulate the command buffer.
X * Used only by command() and related functions.
X */
X
X#include "less.h"
X
Xextern int erase_char, kill_char;
Xextern int sc_width;
X
Xstatic char cmdbuf[120];	/* Buffer for holding a multi-char command */
Xstatic int cmd_col;		/* Current column of the multi-char command */
Xstatic char *cp;		/* Pointer into cmdbuf */
X
X/*
X * Reset command buffer (to empty).
X */
X	public void
Xcmd_reset()
X{
X	cp = cmdbuf;
X	*cp = '\0';
X	cmd_col = 0;
X}
X
X/*
X * How many characters are in the command buffer?
X */
X	public int
Xlen_cmdbuf()
X{
X	return (cp - cmdbuf);
X}
X
X/*
X * Backspace in the command buffer.
X */
X	public int
Xcmd_erase()
X{
X	register char *s;
X
X	if (cp == cmdbuf)
X		/*
X		 * Backspace past beginning of the string:
X		 * this usually means abort the command.
X		 */
X		return (1);
X
X	--cp;
X	if (*cp == ESC)
X		s = "ESC";
X	else
X		s = prchar(*cp);
X	while (*s++ != '\0')
X	{
X		backspace();
X		cmd_col--;
X	}
X	*cp = '\0';
X	return (0);
X}
X
X/*
X * Process a single character of a multi-character command, such as
X * a number, or the pattern of a search command.
X */
X	public int
Xcmd_char(c)
X	int c;
X{
X	char *s;
X
X	if (c == erase_char)
X	{
X		if (cmd_erase())
X			return (1);
X	} else if (c == kill_char)
X	{
X		/* {{ Could do this faster, but who cares? }} */
X		while (cmd_erase() == 0)
X			;
X	} else if (cp >= &cmdbuf[sizeof(cmdbuf)-1])
X	{
X		/*
X		 * No room in the command buffer.
X		 */
X		bell();
X	} else if (cmd_col >= sc_width-4)
X	{
X		/*
X		 * No room on the screen.
X		 * {{ Could get fancy here; maybe shift the displayed
X		 *    line and make room for more chars, like ksh. }}
X		 */
X		bell();
X	} else
X	{
X		/*
X		 * Append the character to the string.
X		 */
X		*cp++ = c;
X		*cp = '\0';
X		if (c == ESC)
X			s = "ESC";
X		else
X			s = prchar(c);
X		putstr(s);
X		cmd_col += strlen(s);
X	}
X	return (0);
X}
X
X/*
X * Return the number currently in the command buffer.
X */
X	public int
Xcmd_int()
X{
X	return (atoi(cmdbuf));
X}
X
X/*
X * Display a string, usually as a prompt for input into the command buffer.
X */
X	public void
Xcmd_putstr(s)
X	char *s;
X{
X	putstr(s);
X	cmd_col += strlen(s);
X}
X
X/*
X * Return a pointer to the command buffer.
X */
X	public char *
Xget_cmdbuf()
X{
X	return (cmdbuf);
X}
END_OF_FILE
echo shar: Extracting \"command.c\"
sed "s/^X//" >'command.c' <<'END_OF_FILE'
X/*
X * User-level command processor.
X */
X
X#include "less.h"
X#include "position.h"
X#include "option.h"
X#include "cmd.h"
X
X#define	NO_MCA		0
X#define	MCA_DONE	1
X#define	MCA_MORE	2
X
Xextern int erase_char, kill_char;
Xextern int ispipe;
Xextern int sigs;
Xextern int quit_at_eof;
Xextern int hit_eof;
Xextern int sc_width;
Xextern int sc_height;
Xextern int swindow;
Xextern int jump_sline;
Xextern int quitting;
Xextern int scroll;
Xextern int nohelp;
Xextern int ignore_eoi;
Xextern char *every_first_cmd;
Xextern char version[];
Xextern struct scrpos initial_scrpos;
Xextern IFILE curr_ifile;
X#if EDITOR
Xextern char *editor;
Xextern char *editproto;
X#endif
Xextern int screen_trashed;	/* The screen has been overwritten */
X
Xstatic char ungot[100];
Xstatic char *ungotp = NULL;
X#if SHELL_ESCAPE
Xstatic char *shellcmd = NULL;	/* For holding last shell command for "!!" */
X#endif
Xstatic int mca;			/* The multicharacter command (action) */
Xstatic int search_type;		/* The previous type of search */
Xstatic int number;		/* The number typed by the user */
Xstatic char optchar;
Xstatic int optflag;
X#if PIPEC
Xstatic char pipec;
X#endif
X
Xstatic void multi_search();
X
X/*
X * Move the cursor to lower left before executing a command.
X * This looks nicer if the command takes a long time before
X * updating the screen.
X */
X	static void
Xcmd_exec()
X{
X	lower_left();
X	flush();
X}
X
X/*
X * Set up the display to start a new multi-character command.
X */
X	static void
Xstart_mca(action, prompt)
X	int action;
X	char *prompt;
X{
X	mca = action;
X	lower_left();
X	clear_eol();
X	cmd_putstr(prompt);
X}
X
X/*
X * Set up the display to start a new search command.
X */
X	static void
Xsearch_mca()
X{
X	switch (SRCH_DIR(search_type))
X	{
X	case SRCH_FORW:
X		mca = A_F_SEARCH;
X		break;
X	case SRCH_BACK:
X		mca = A_B_SEARCH;
X		break;
X	}
X
X	lower_left();
X	clear_eol();
X
X	if (search_type & SRCH_FIRST_FILE)
X		cmd_putstr("@");
X	else
X		cmd_putstr(" ");
X
X	if (search_type & SRCH_PAST_EOF)
X		cmd_putstr("*");
X	else
X		cmd_putstr(" ");
X
X	cmd_putstr(" ");
X
X	if (search_type & SRCH_NOMATCH)
X		cmd_putstr("!");
X	else
X		cmd_putstr(" ");
X
X	switch (SRCH_DIR(search_type))
X	{
X	case SRCH_FORW:
X		cmd_putstr("/");
X		break;
X	case SRCH_BACK:
X		cmd_putstr("?");
X		break;
X	}
X}
X
X/*
X * Execute a multicharacter command.
X */
X	static void
Xexec_mca()
X{
X	register char *cbuf;
X	register char *s;
X
X	cmd_exec();
X	cbuf = get_cmdbuf();
X
X	switch (mca)
X	{
X	case A_F_SEARCH:
X	case A_B_SEARCH:
X		multi_search(cbuf, number);
X		break;
X	case A_FIRSTCMD:
X		/*
X		 * Skip leading spaces or + signs in the string.
X		 */
X		while (*cbuf == '+' || *cbuf == ' ')
X			cbuf++;
X		if (every_first_cmd != NULL)
X			free(every_first_cmd);
X		if (*cbuf == '\0')
X			every_first_cmd = NULL;
X		else
X			every_first_cmd = save(cbuf);
X		break;
X	case A_OPT_TOGGLE:
X		toggle_option(optchar, cbuf, optflag);
X		optchar = '\0';
X		break;
X	case A_F_BRACKET:
X		match_brac(cbuf[0], cbuf[1], 1, number);
X		break;
X	case A_B_BRACKET:
X		match_brac(cbuf[1], cbuf[0], 0, number);
X		break;
X	case A_EXAMINE:
X		/*
X		 * Ignore leading spaces and glob the filename.
X		 */
X		cbuf = skipsp(cbuf);
X		s = glob(cbuf);
X		if (s != NULL)
X		{
X			edit_list(s);
X			free(s);
X		} else
X			edit_list(cbuf);
X		break;
X#if SHELL_ESCAPE
X	case A_SHELL:
X		/*
X		 * !! just uses whatever is in shellcmd.
X		 * Otherwise, copy cmdbuf to shellcmd,
X		 * expanding any special characters ("%" or "#").
X		 */
X		if (*cbuf != '!')
X		{
X			if (shellcmd != NULL)
X				free(shellcmd);
X			shellcmd = fexpand(cbuf);
X			if (shellcmd == NULL)
X				break;
X		}
X
X		if (shellcmd == NULL)
X			lsystem("");
X		else
X			lsystem(shellcmd);
X		error("!done", NULL_PARG);
X		break;
X#endif
X#if PIPEC
X	case A_PIPE:
X		(void) pipe_mark(pipec, cbuf);
X		error("|done", NULL_PARG);
X		break;
X#endif
X	}
X}
X
X/*
X * Add a character to a multi-character command.
X */
X	static int
Xmca_char(c)
X	int c;
X{
X	char *p;
X	int flag;
X	char buf[3];
X
X	switch (mca)
X	{
X	case 0:
X		/*
X		 * Not in a multicharacter command.
X		 */
X		return (NO_MCA);
X
X	case A_PREFIX:
X		/*
X		 * In the prefix of a command.
X		 * This not considered a multichar command
X		 * (even tho it uses cmdbuf, etc.).
X		 * It is handled in the commands() switch.
X		 */
X		return (NO_MCA);
X
X	case A_DIGIT:
X		/*
X		 * Entering digits of a number.
X		 * Terminated by a non-digit.
X		 */
X		if ((c < '0' || c > '9') &&
X			c != erase_char && c != kill_char)
X		{
X			/*
X			 * Not part of the number.
X			 * Treat as a normal command character.
X			 */
X			number = cmd_int();
X			mca = 0;
X			return (NO_MCA);
X		}
X		break;
X
X	case A_OPT_TOGGLE:
X		/*
X		 * Special case for the TOGGLE_OPTION command.
X		 * If the option letter which was entered is a
X		 * single-char option, execute the command immediately,
X		 * so user doesn't have to hit RETURN.
X		 * If the first char is + or -, this indicates
X		 * OPT_UNSET or OPT_SET respectively, instead of OPT_TOGGLE.
X		 */
X		if (c == erase_char || c == kill_char)
X			break;
X		if (optchar != '\0' && optchar != '+' && optchar != '-')
X			/*
X			 * We already have the option letter.
X			 */
X			break;
X		switch (c)
X		{
X		case '+':
X			optflag = OPT_UNSET;
X			break;
X		case '-':
X			optflag = OPT_SET;
X			break;
X		default:
X			optchar = c;
X			if (optflag != OPT_TOGGLE || single_char_option(c))
X			{
X				toggle_option(c, "", optflag);
X				return (MCA_DONE);
X			}
X			break;
X		}
X		if (optchar == '+' || optchar == '-')
X		{
X			optchar = c;
X			break;
X		}
X		/*
X		 * Display a prompt appropriate for the option letter.
X		 */
X		if ((p = opt_prompt(c)) == NULL)
X		{
X			buf[0] = '-';
X			buf[1] = c;
X			buf[2] = '\0';
X			p = buf;
X		}
X		start_mca(A_OPT_TOGGLE, p);
X		return (MCA_MORE);
X
X	case A_F_SEARCH:
X	case A_B_SEARCH:
X		/*
X		 * Special case for search commands.
X		 * Certain characters as the first char of 
X		 * the pattern have special meaning:
X		 *	!  Toggle the NOMATCH flag
X		 *	*  Toggle the PAST_EOF flag
X		 *	@  Toggle the FIRST_FILE flag
X		 */
X		if (len_cmdbuf() > 0)
X			/*
X			 * Only works for the first char of the pattern.
X			 */
X			break;
X
X		flag = 0;
X		switch (c)
X		{
X		case '!':
X			flag = SRCH_NOMATCH;
X			break;
X		case '@':
X			flag = SRCH_FIRST_FILE;
X			break;
X		case '*':
X			flag = SRCH_PAST_EOF;
X			break;
X		}
X		if (flag != 0)
X		{
X			search_type ^= flag;
X			search_mca();
X			return (MCA_MORE);
X		}
X		break;
X	}
X
X	/*
X	 * Any other multicharacter command
X	 * is terminated by a newline.
X	 */
X	if (c == '\n' || c == '\r')
X	{
X		/*
X		 * Execute the command.
X		 */
X		exec_mca();
X		return (MCA_DONE);
X	}
X	/*
X	 * Append the char to the command buffer.
X	 */
X	if (cmd_char(c))
X		/*
X		 * Abort the multi-char command.
X		 */
X		return (MCA_DONE);
X
X	if ((mca == A_F_BRACKET || mca == A_B_BRACKET) && len_cmdbuf() >= 2)
X	{
X		/*
X		 * Special case for the bracket-matching commands.
X		 * Execute the command after getting exactly two
X		 * characters from the user.
X		 */
X		exec_mca();
X		return (MCA_DONE);
X	}
X
X	/*
X	 * Need another character.
X	 */
X	return (MCA_MORE);
X}
X
X/*
X * Display the appropriate prompt.
X */
X	static void
Xprompt()
X{
X	register char *p;
X
X	if (ungotp != NULL && ungotp > ungot)
X	{
X		/*
X		 * No prompt necessary if commands are from 
X		 * ungotten chars rather than from the user.
X		 */
X		return;
X	}
X
X	/*
X	 * If nothing is displayed yet, display starting from initial_scrpos.
X	 */
X	if (empty_screen())
X	{
X		if (initial_scrpos.pos == NULL_POSITION)
X			/*
X			 * {{ Maybe this should be:
X			 *    jump_loc(ch_zero(), jump_sline);
X			 *    but this behavior seems rather unexpected 
X			 *    on the first screen. }}
X			 */
X			jump_loc(ch_zero(), 1);
X		else
X			jump_loc(initial_scrpos.pos, initial_scrpos.ln);
X	} else if (screen_trashed)
X		repaint();
X
X	/*
X	 * If the -E flag is set and we've hit EOF on the last file, quit.
X	 */
X	if (quit_at_eof == 2 && hit_eof && 
X	    next_ifile(curr_ifile) == NULL_IFILE)
X		quit(0);
X
X	/*
X	 * Select the proper prompt and display it.
X	 */
X	lower_left();
X	clear_eol();
X	p = pr_string();
X	if (p == NULL)
X		putchr(':');
X	else
X	{
X		so_enter();
X		putstr(p);
X		so_exit();
X	}
X#if __MSDOS__
X	scroll_bar();
X#endif
X}
X
X/*
X * Get command character.
X * The character normally comes from the keyboard,
X * but may come from ungotten characters
X * (characters previously given to ungetcc or ungetsc).
X */
X	static int
Xgetcc()
X{
X	if (ungotp == NULL)
X		/*
X		 * Normal case: no ungotten chars, so get one from the user.
X		 */
X		return (getchr());
X
X	if (ungotp > ungot)
X		/*
X		 * Return the next ungotten char.
X		 */
X		return (*--ungotp);
X
X	/*
X	 * We have just run out of ungotten chars.
X	 */
X	ungotp = NULL;
X	if (len_cmdbuf() == 0 || !empty_screen())
X		return (getchr());
X	/*
X	 * Command is incomplete, so try to complete it.
X	 */
X	switch (mca)
X	{
X	case A_DIGIT:
X		/*
X		 * We have a number but no command.  Treat as #g.
X		 */
X		return ('g');
X
X	case A_F_SEARCH:
X	case A_B_SEARCH:
X		/*
X		 * We have "/string" but no newline.  Add the \n.
X		 */
X		return ('\n'); 
X
X	default:
X		/*
X		 * Some other incomplete command.  Let user complete it.
X		 */
X		return (getchr());
X	}
X}
X
X/*
X * "Unget" a command character.
X * The next getcc() will return this character.
X */
X	public void
Xungetcc(c)
X	int c;
X{
X	if (ungotp == NULL)
X		ungotp = ungot;
X	if (ungotp >= ungot + sizeof(ungot))
X	{
X		error("ungetcc overflow", NULL_PARG);
X		quit(1);
X	}
X	*ungotp++ = c;
X}
X
X/*
X * Unget a whole string of command characters.
X * The next sequence of getcc()'s will return this string.
X */
X	public void
Xungetsc(s)
X	char *s;
X{
X	register char *p;
X
X	for (p = s + strlen(s) - 1;  p >= s;  p--)
X		ungetcc(*p);
X}
X
X/*
X * Search for a pattern, possibly in multiple files.
X * If SRCH_FIRST_FILE is set, begin searching at the first file.
X * If SRCH_PAST_EOF is set, continue the search thru multiple files.
X */
X	static void
Xmulti_search(pattern, n)
X	char *pattern;
X	int n;
X{
X	register int nomore;
X	char *curr_filename;
X	int changed_file;
X	struct scrpos scrpos;
X
X	changed_file = 0;
X	curr_filename = get_filename(curr_ifile);
X
X	if (search_type & SRCH_FIRST_FILE)
X	{
X		/*
X		 * Start at the first (or last) file 
X		 * in the command line list.
X		 */
X		if (SRCH_DIR(search_type) == SRCH_FORW)
X			nomore = edit_first();
X		else
X			nomore = edit_last();
X		if (nomore)
X			return;
X		changed_file = 1;
X		search_type &= ~SRCH_FIRST_FILE;
X	}
X
X	for (;;)
X	{
X		if ((n = search(search_type, pattern, n)) == 0)
X			/*
X			 * Found it.
X			 */
X			return;
X
X		if (n < 0)
X			/*
X			 * Some kind of error in the search.
X			 * Error message has been printed by search().
X			 */
X			break;
X
X		if ((search_type & SRCH_PAST_EOF) == 0)
X			/*
X			 * We didn't find a match, but we're
X			 * supposed to search only one file.
X			 */
X			break;
X		/*
X		 * Move on to the next file.
X		 */
X		if (SRCH_DIR(search_type) == SRCH_BACK)
X			nomore = edit_prev(1);
X		else
X			nomore = edit_next(1);
X		if (nomore)
X			break;
X		changed_file = 1;
X	}
X
X	/*
X	 * Didn't find it.
X	 * Print an error message if we haven't already.
X	 */
X	if (n > 0)
X		error("Pattern not found", NULL_PARG);
X
X	if (changed_file)
X		/*
X		 * Restore the file we were originally viewing.
X		 */
X		(void) edit(curr_filename, 0);
X}
X
X/*
X * Main command processor.
X * Accept and execute commands until a quit command.
X */
X	public void
Xcommands()
X{
X	register int c;
X	register int action;
X	register char *cbuf;
X	char *s;
X	char tbuf[2];
X	PARG parg;
X
X	search_type = SRCH_FORW;
X	scroll = (sc_height + 1) / 2;
X
X	for (;;)
X	{
X		mca = 0;
X		number = 0;
X		optchar = '\0';
X
X		/*
X		 * See if any signals need processing.
X		 */
X		if (sigs)
X		{
X			psignals();
X			if (quitting)
X				quit(-1);
X		}
X			
X		/*
X		 * Display prompt and accept a character.
X		 */
X		cmd_reset();
X		prompt();
X		if (sigs)
X			continue;
X		c = getcc();
X
X	again:
X		if (sigs)
X			continue;
X
X		/*
X		 * If we are in a multicharacter command, call mca_char.
X		 * Otherwise we call cmd_decode to determine the
X		 * action to be performed.
X		 */
X		if (mca)
X			switch (mca_char(c))
X			{
X			case MCA_MORE:
X				/*
X				 * Need another character.
X				 */
X				c = getcc();
X				goto again;
X			case MCA_DONE:
X				/*
X				 * Command has been handled by mca_char.
X				 * Start clean with a prompt.
X				 */
X				continue;
X			case NO_MCA:
X				/*
X				 * Not a multi-char command
X				 * (at least, not anymore).
X				 */
X				break;
X			}
X
X		/*
X		 * Decode the command character and decide what to do.
X		 */
X		if (mca)
X		{
X			/*
X			 * We're in a multichar command.
X			 * Add the character to the command buffer
X			 * and display it on the screen.
X			 * If the user backspaces past the start 
X			 * of the line, abort the command.
X			 */
X			if (cmd_char(c) || len_cmdbuf() == 0)
X				continue;
X			cbuf = get_cmdbuf();
X		} else
X		{
X			/*
X			 * Don't use cmd_char if we're starting fresh
X			 * at the beginning of a command, because we
X			 * don't want to echo the command until we know
X			 * it is a multichar command.  We also don't
X			 * want erase_char/kill_char to be treated
X			 * as line editing characters.
X			 */
X			tbuf[0] = c;
X			tbuf[1] = '\0';
X			cbuf = tbuf;
X		}
X		s = NULL;
X		action = cmd_decode(cbuf, &s);
X		/*
X		 * If an "extra" string was returned,
X		 * process it as a string of command characters.
X		 */
X		if (s != NULL)
X			ungetsc(s);
X		/*
X		 * Clear the cmdbuf string.
X		 * (But not if we're in the prefix of a command,
X		 * because the partial command string is kept there.)
X		 */
X		if (action != A_PREFIX)
X			cmd_reset();
X
X		switch (action)
X		{
X		case A_DIGIT:
X			/*
X			 * First digit of a number.
X			 */
X			start_mca(A_DIGIT, ":");
X			goto again;
X
X		case A_F_WINDOW:
X			/*
X			 * Forward one window (and set the window size).
X			 */
X			if (number > 0)
X				swindow = number;
X			/* FALLTHRU */
X		case A_F_SCREEN:
X			/*
X			 * Forward one screen.
X			 */
X			if (number <= 0)
X				number = swindow;
X			cmd_exec();
X			forward(number, 0, 1);
X			break;
X
X		case A_B_WINDOW:
X			/*
X			 * Backward one window (and set the window size).
X			 */
X			if (number > 0)
X				swindow = number;
X			/* FALLTHRU */
X		case A_B_SCREEN:
X			/*
X			 * Backward one screen.
X			 */
X			if (number <= 0)
X				number = swindow;
X			cmd_exec();
X			backward(number, 0, 1);
X			break;
X
X		case A_F_LINE:
X			/*
X			 * Forward N (default 1) line.
X			 */
X			if (number <= 0)
X				number = 1;
X			cmd_exec();
X			forward(number, 0, 0);
X			break;
X
X		case A_B_LINE:
X			/*
X			 * Backward N (default 1) line.
X			 */
X			if (number <= 0)
X				number = 1;
X			cmd_exec();
X			backward(number, 0, 0);
X			break;
X
X		case A_FF_LINE:
X			/*
X			 * Force forward N (default 1) line.
X			 */
X			if (number <= 0)
X				number = 1;
X			cmd_exec();
X			forward(number, 1, 0);
X			break;
X
X		case A_BF_LINE:
X			/*
X			 * Force backward N (default 1) line.
X			 */
X			if (number <= 0)
X				number = 1;
X			cmd_exec();
X			backward(number, 1, 0);
X			break;
X		
X		case A_F_FOREVER:
X			/*
X			 * Forward forever, ignoring EOF.
X			 */
X			cmd_exec();
X			ignore_eoi = 1;
X			while (sigs == 0)
X				forward(1, 0, 0);
X			ignore_eoi = 0;
X			break;
X
X		case A_F_SCROLL:
X			/*
X			 * Forward N lines 
X			 * (default same as last 'd' or 'u' command).
X			 */
X			if (number > 0)
X				scroll = number;
X			cmd_exec();
X			forward(scroll, 0, 0);
X			break;
X
X		case A_B_SCROLL:
X			/*
X			 * Forward N lines 
X			 * (default same as last 'd' or 'u' command).
X			 */
X			if (number > 0)
X				scroll = number;
X			cmd_exec();
X			backward(scroll, 0, 0);
X			break;
X
X		case A_FREPAINT:
X			/*
X			 * Flush buffers, then repaint screen.
X			 * Don't flush the buffers on a pipe!
X			 */
X			ch_flush();
X			if (!ispipe)
X				clr_linenum();
X			/* FALLTHRU */
X		case A_REPAINT:
X			/*
X			 * Repaint screen.
X			 */
X			cmd_exec();
X			repaint();
X			break;
X
X		case A_GOLINE:
X			/*
X			 * Go to line N, default beginning of file.
X			 */
X			if (number <= 0)
X				number = 1;
X			cmd_exec();
X			jump_back(number);
X			break;
X
X		case A_PERCENT:
X			/*
X			 * Go to a specified percentage into the file.
X			 */
X			if (number < 0)
X				number = 0;
X			if (number > 100)
X				number = 100;
X			cmd_exec();
X			jump_percent(number);
X			break;
X
X		case A_GOEND:
X			/*
X			 * Go to line N, default end of file.
X			 */
X			cmd_exec();
X			if (number <= 0)
X				jump_forw();
X			else
X				jump_back(number);
X			break;
X
X		case A_GOPOS:
X			/*
X			 * Go to a specified byte position in the file.
X			 */
X			cmd_exec();
X			if (number < 0)
X				number = 0;
X			jump_line_loc((POSITION)number, jump_sline);
X			break;
X
X		case A_STAT:
X			/*
X			 * Print file name, etc.
X			 */
X			cmd_exec();
X			parg.p_string = eq_message();
X			error("%s", &parg);
X			break;
X			
X		case A_VERSION:
X			/*
X			 * Print version number, without the "@(#)".
X			 */
X			cmd_exec();
X			parg.p_string = version+4;
X			error("%s", &parg);
X			break;
X
X		case A_QUIT:
X			/*
X			 * Exit.
X			 */
X			quit(0);
X
X		case A_B_SEARCH:
X			search_type = SRCH_BACK;
X			goto do_search;
X		case A_F_SEARCH:
X			search_type = SRCH_FORW;
X		do_search:
X			/*
X			 * Search for a pattern.
X			 * Get the first char of the pattern.
X			 */
X			if (number <= 0)
X				number = 1;
X			search_mca();
X			c = getcc();
X			goto again;
X
X		case A_T_REVERSE_SEARCH:
X			search_type |= SRCH_PAST_EOF;
X			/* FALLTHRU */
X
X		case A_REVERSE_SEARCH:
X			/*
X			 * Repeat previous search, in reverse direction.
X			 */
X			c = SRCH_FLAG(search_type);
X			if (SRCH_DIR(search_type) == SRCH_BACK)
X				search_type = SRCH_FORW;
X			else
X				search_type = SRCH_BACK;
X			search_type |= c;
X			goto do_again_search;
X
X		case A_T_AGAIN_SEARCH:
X			search_type |= SRCH_PAST_EOF;
X			goto do_again_search;
X
X		case A_AGAIN_SEARCH:
X			/*
X			 * Repeat previous search.
X			 */
X		do_again_search:
X			if (number <= 0)
X				number = 1;
X			search_mca();
X			cmd_exec();
X			multi_search((char *)NULL, number);
X			break;
X		
X		case A_HELP:
X			/*
X			 * Help.
X			 */
X			if (nohelp)
X			{
X				bell();
X				break;
X			}
X			lower_left();
X			clear_eol();
X			putstr("help");
X			cmd_exec();
X			help();
X			break;
X
X		case A_EXAMINE:
X			/*
X			 * Edit a new file.  Get the filename.
X			 */
X			start_mca(A_EXAMINE, "Examine: ");
X			c = getcc();
X			goto again;
X			
X		case A_VISUAL:
X			/*
X			 * Invoke an editor on the input file.
X			 */
X#if EDITOR
X			if (strcmp(get_filename(curr_ifile), "-") == 0)
X			{
X				error("Cannot edit standard input", NULL_PARG);
X				break;
X			}
X			/*
X			 * Expand the editor prototype string
X			 * and pass it to the system to execute.
X			 */
X			cmd_exec();
X			lsystem(pr_expand(editproto, 0));
X			/*
X			 * Re-edit the file, since data may have changed.
X			 * Some editors even recreate the file, so flushing
X			 * buffers is not sufficient.
X			 */
X			(void) edit(get_filename(curr_ifile), 0);
X			break;
X#else
X			error("Command not available", NULL_PARG);
X			break;
X#endif
X
X		case A_NEXT_FILE:
X			/*
X			 * Examine next file.
X			 */
X			if (number <= 0)
X				number = 1;
X			if (edit_next(number))
X			{
X				if (quit_at_eof && hit_eof)
X					quit(0);
X				parg.p_string = (number > 1) ? "(N-th) " : "";
X				error("No %snext file", &parg);
X			}
X			break;
X
X		case A_PREV_FILE:
X			/*
X			 * Examine previous file.
X			 */
X			if (number <= 0)
X				number = 1;
X			if (edit_prev(number))
X			{
X				parg.p_string = (number > 1) ? "(N-th) " : "";
X				error("No %sprevious file", &parg);
X			}
X			break;
X
X		case A_INDEX_FILE:
X			/*
X			 * Examine a particular file.
X			 */
X			if (number <= 0)
X				number = 1;
X			if (edit_index(number))
X				error("No such file", NULL_PARG);
X			break;
X
X		case A_OPT_TOGGLE:
X			start_mca(A_OPT_TOGGLE, "-");
X			optflag = OPT_TOGGLE;
X			c = getcc();
X			goto again;
X
X		case A_DISP_OPTION:
X			/*
X			 * Report a flag setting.
X			 */
X			start_mca(A_DISP_OPTION, "_");
X			c = getcc();
X			if (c == erase_char || c == kill_char)
X				break;
X			toggle_option(c, "", OPT_NO_TOGGLE);
X			break;
X
X		case A_FIRSTCMD:
X			/*
X			 * Set an initial command for new files.
X			 */
X			start_mca(A_FIRSTCMD, "+");
X			c = getcc();
X			goto again;
X
X		case A_SHELL:
X			/*
X			 * Shell escape.
X			 */
X#if SHELL_ESCAPE
X			start_mca(A_SHELL, "!");
X			c = getcc();
X			goto again;
X#else
X			error("Command not available", NULL_PARG);
X			break;
X#endif
X
X		case A_SETMARK:
X			/*
X			 * Set a mark.
X			 */
X			start_mca(A_SETMARK, "mark: ");
X			c = getcc();
X			if (c == erase_char || c == kill_char ||
X			    c == '\n' || c == '\r')
X				break;
X			setmark(c);
X			break;
X
X		case A_GOMARK:
X			/*
X			 * Go to a mark.
X			 */
X			start_mca(A_GOMARK, "goto mark: ");
X			c = getcc();
X			if (c == erase_char || c == kill_char || 
X			    c == '\n' || c == '\r')
X				break;
X			gomark(c);
X			break;
X
X#if PIPEC
X		case A_PIPE:
X			start_mca(A_PIPE, "|mark: ");
X			c = getcc();
X			if (c == erase_char || c == kill_char)
X				break;
X			if (c == '\n' || c == '\r')
X				c = '.';
X			if (badmark(c))
X				break;
X			pipec = c;
X			start_mca(A_PIPE, "!");
X			c = getcc();
X			goto again;
X#endif
X
X		case A_B_BRACKET:
X		case A_F_BRACKET:
X			start_mca(action, "Brackets: ");
X			c = getcc();
X			goto again;
X
X		case A_PREFIX:
X			/*
X			 * The command is incomplete (more chars are needed).
X			 * Display the current char, so the user knows
X			 * what's going on, and get another character.
X			 */
X			if (mca != A_PREFIX)
X			{
X				start_mca(A_PREFIX, " ");
X				cmd_reset();
X				(void) cmd_char(c);
X			}
X			c = getcc();
X			goto again;
X
X		case A_NOACTION:
X			break;
X
X		default:
X			bell();
X			break;
X		}
X	}
X}
END_OF_FILE
echo shar: Extracting \"decode.c\"
sed "s/^X//" >'decode.c' <<'END_OF_FILE'
X/*
X * Routines to decode user commands.
X *
X * This is all table driven.
X * A command table is a sequence of command descriptors.
X * Each command descriptor is a sequence of bytes with the following format:
X *	<c1><c2>...<cN><0><action>
X * The characters c1,c2,...,cN are the command string; that is,
X * the characters which the user must type.
X * It is terminated by a null <0> byte.
X * The byte after the null byte is the action code associated
X * with the command string.
X * If an action byte is OR-ed with A_EXTRA, this indicates
X * that the option byte is followed by an extra string.
X *
X * There may be many command tables.
X * The first (default) table is built-in.
X * Other tables are read in from "lesskey" files.
X * All the tables are linked together and are searched in order.
X */
X
X#include "less.h"
X#include "cmd.h"
X#if __MSDOS__
X#include <io.h>
X#include <stdlib.h>
X#endif
X
X/*
X * Command table is ordered roughly according to expected
X * frequency of use, so the common commands are near the beginning.
X */
Xstatic char cmdtable[] =
X{
X#if __MSDOS__
X	/*
X	 * PC function keys.
X	 * Note that '\0' is converted to '\200' on input.
X	 */
X	'\200','\120',0,		A_F_LINE,		/* down arrow */
X	'\200','\121',0,		A_F_SCREEN,		/* page down */
X	'\200','\110',0,		A_B_LINE,		/* up arrow */
X	'\200','\111',0,		A_B_SCREEN,		/* page up */
X	'\200','\107',0,		A_GOLINE,		/* home */
X	'\200','\117',0,		A_GOEND,		/* end */
X	'\200','\073',0,		A_HELP,			/* F1 */
X	'\200','\104',0,		A_MODIFY_WINDOW,	/* F10 */
X	'\200','\103',0,		A_MODIFY_COLOURS,	/* F9 */
X#endif
X	'\r',0,				A_F_LINE,
X	'\n',0,				A_F_LINE,
X	'e',0,				A_F_LINE,
X	'j',0,				A_F_LINE,
X	CONTROL('E'),0,			A_F_LINE,
X	CONTROL('N'),0,			A_F_LINE,
X	'k',0,				A_B_LINE,
X	'y',0,				A_B_LINE,
X	CONTROL('Y'),0,			A_B_LINE,
X	CONTROL('K'),0,			A_B_LINE,
X	CONTROL('P'),0,			A_B_LINE,
X	'J',0,				A_FF_LINE,
X	'K',0,				A_BF_LINE,
X	'Y',0,				A_BF_LINE,
X	'd',0,				A_F_SCROLL,
X	CONTROL('D'),0,			A_F_SCROLL,
X	'u',0,				A_B_SCROLL,
X	CONTROL('U'),0,			A_B_SCROLL,
X	' ',0,				A_F_SCREEN,
X	'f',0,				A_F_SCREEN,
X	CONTROL('F'),0,			A_F_SCREEN,
X	CONTROL('V'),0,			A_F_SCREEN,
X	'b',0,				A_B_SCREEN,
X	CONTROL('B'),0,			A_B_SCREEN,
X	ESC,'v',0,			A_B_SCREEN,
X	'z',0,				A_F_WINDOW,
X	'w',0,				A_B_WINDOW,
X	'F',0,				A_F_FOREVER,
X	'R',0,				A_FREPAINT,
X	'r',0,				A_REPAINT,
X	CONTROL('R'),0,			A_REPAINT,
X	CONTROL('L'),0,			A_REPAINT,
X	'g',0,				A_GOLINE,
X	'<',0,				A_GOLINE,
X	ESC,'<',0,			A_GOLINE,
X	'p',0,				A_PERCENT,
X	'%',0,				A_PERCENT,
X	'{',0,				A_F_BRACKET|A_EXTRA,	'{','}',0,
X	'}',0,				A_B_BRACKET|A_EXTRA,	'{','}',0,
X	'(',0,				A_F_BRACKET|A_EXTRA,	'(',')',0,
X	')',0,				A_B_BRACKET|A_EXTRA,	'(',')',0,
X	'[',0,				A_F_BRACKET|A_EXTRA,	'[',']',0,
X	']',0,				A_B_BRACKET|A_EXTRA,	'[',']',0,
X	ESC,CONTROL('F'),0,		A_F_BRACKET,
X	ESC,CONTROL('B'),0,		A_B_BRACKET,
X	'G',0,				A_GOEND,
X	ESC,'>',0,			A_GOEND,
X	'>',0,				A_GOEND,
X	'P',0,				A_GOPOS,
X
X	'0',0,				A_DIGIT,
X	'1',0,				A_DIGIT,
X	'2',0,				A_DIGIT,
X	'3',0,				A_DIGIT,
X	'4',0,				A_DIGIT,
X	'5',0,				A_DIGIT,
X	'6',0,				A_DIGIT,
X	'7',0,				A_DIGIT,
X	'8',0,				A_DIGIT,
X	'9',0,				A_DIGIT,
X
X	'=',0,				A_STAT,
X	CONTROL('G'),0,			A_STAT,
X	':','f',0,			A_STAT,
X	'/',0,				A_F_SEARCH,
X	'?',0,				A_B_SEARCH,
X	ESC,'/',0,			A_F_SEARCH|A_EXTRA,	'*',0,
X	ESC,'?',0,			A_B_SEARCH|A_EXTRA,	'*',0,
X	'n',0,				A_AGAIN_SEARCH,
X	ESC,'n',0,			A_T_AGAIN_SEARCH,
X	'N',0,				A_REVERSE_SEARCH,
X	ESC,'N',0,			A_T_REVERSE_SEARCH,
X	'm',0,				A_SETMARK,
X	'\'',0,				A_GOMARK,
X	CONTROL('X'),CONTROL('X'),0,	A_GOMARK,
X	'E',0,				A_EXAMINE,
X	':','e',0,			A_EXAMINE,
X	CONTROL('X'),CONTROL('V'),0,	A_EXAMINE,
X	':','n',0,			A_NEXT_FILE,
X	':','p',0,			A_PREV_FILE,
X	':','x',0,			A_INDEX_FILE,
X	'-',0,				A_OPT_TOGGLE,
X	':','t',0,			A_OPT_TOGGLE|A_EXTRA,	't',0,
X	's',0,				A_OPT_TOGGLE|A_EXTRA,	'o',0,
X	'_',0,				A_DISP_OPTION,
X	'|',0,				A_PIPE,
X	'v',0,				A_VISUAL,
X	'!',0,				A_SHELL,
X	'+',0,				A_FIRSTCMD,
X
X	'H',0,				A_HELP,
X	'h',0,				A_HELP,
X	'V',0,				A_VERSION,
X	'q',0,				A_QUIT,
X	':','q',0,			A_QUIT,
X	':','Q',0,			A_QUIT,
X	'Z','Z',0,			A_QUIT,
X	ESC,ESC,0,			A_QUIT,
X};
X
X/*
X * Structure to support a list of command tables.
X */
Xstruct tablelist
X{
X	struct tablelist *t_next;
X	char *t_start;
X	char *t_end;
X};
X
X/*
X * Structure for the default command table.
X */
Xstatic struct tablelist deftable = 
X	{ NULL, cmdtable, cmdtable+sizeof(cmdtable) };
X
X/*
X * List of tables; initially contains only the default table.
X */
Xstatic struct tablelist *tables = &deftable;
X
Xstatic int cmd_search();
X
Xextern int erase_char, kill_char;
X
X/*
X * Decode a command character and return the associated action.
X * The "extra" string, if any, is returned in sp.
X */
X	public int
Xcmd_decode(cmd, sp)
X	char *cmd;
X	char **sp;
X{
X	register struct tablelist *t;
X	register int action;
X
X	/*
X	 * Search thru all the command tables.
X	 * Stop when we find an action which is not A_INVALID.
X	 */
X	for (t = tables;  t != NULL;  t = t->t_next)
X	{
X		action = cmd_search(cmd, t->t_start, t->t_end, sp);
X		if (action != A_INVALID)
X			break;
X	}
X	return (action);
X}
X
X/*
X * Search a command table for the current command string (in cmd).
X */
X	static int
Xcmd_search(cmd, table, endtable, sp)
X	char *cmd;
X	char *table;
X	char *endtable;
X	char **sp;
X{
X	register char *p;
X	register char *q;
X	register int a;
X
X	for (p = table, q = cmd;  p < endtable;  p++, q++)
X	{
X		if (*p == *q)
X		{
X			/*
X			 * Current characters match.
X			 * If we're at the end of the string, we've found it.
X			 * Return the action code, which is the character
X			 * after the null at the end of the string
X			 * in the command table.
X			 */
X			if (*p == '\0')
X			{
X				a = *++p & 0377;
X				/*
X				 * Check for an "extra" string.
X				 */
X				if (a & A_EXTRA)
X				{
X					*sp = ++p;
X					a &= ~A_EXTRA;
X				} else
X					*sp = NULL;
X				return (a);
X			}
X		} else if (*q == '\0')
X		{
X			/*
X			 * Hit the end of the user's command,
X			 * but not the end of the string in the command table.
X			 * The user's command is incomplete.
X			 */
X			return (A_PREFIX);
X		} else
X		{
X			/*
X			 * Not a match.
X			 * Skip ahead to the next command in the
X			 * command table, and reset the pointer
X			 * to the beginning of the user's command.
X			 */
X			while (*p++ != '\0') ;
X			if (*p & A_EXTRA)
X				while (*++p != '\0') ;
X			q = cmd-1;
X		}
X	}
X	/*
X	 * No match found in the entire command table.
X	 */
X	return (A_INVALID);
X}
X
X#if USERFILE
X/*
X * Set up a user command table, based on a "lesskey" file.
X */
X	public int
Xadd_cmdtable(filename)
X	char *filename;
X{
X	register struct tablelist *t;
X	register POSITION len;
X	register long n;
X	register int f;
X
X	/*
X	 * Try to open the lesskey file.
X	 * If we can't, return an error.
X	 */
X	f = open(filename, 0);
X	if (f < 0)
X		return (-1);
X
X	/*
X	 * Read the file into the user table.
X	 * We first figure out the size of the file and allocate space for it.
X	 * {{ Minimal error checking is done here.
X	 *    A garbage .less file will produce strange results.
X	 *    To avoid a large amount of error checking code here, we
X	 *    rely on the lesskey program to generate a good .less file. }}
X	 */
X	len = filesize(f);
X	if (len == NULL_POSITION || len < 3)
X	{
X		/*
X		 * Bad file (valid file must have at least 3 chars).
X		 */
X		close(f);
X		return (-1);
X	}
X	if ((t = (struct tablelist *) 
X			calloc(1, sizeof(struct tablelist))) == NULL)
X	{
X		close(f);
X		return (-1);
X	}
X	if ((t->t_start = (char *) calloc(len, sizeof(char))) == NULL)
X	{
X		free((char *)t);
X		close(f);
X		return (-1);
X	}
X	if (lseek(f, (offset_t)0, 0) == BAD_LSEEK)
X	{
X		free(t->t_start);
X		free((char *)t);
X		close(f);
X		return (-1);
X	}
X	n = read(f, t->t_start, (unsigned int) len);
X	close(f);
X
X	/*
X	 * In a valid lesskey file, the last byte or 
X	 * the second to the last byte must be zero.
X	 */
X	if (n != len || (t->t_start[n-1] != '\0' && t->t_start[n-2] != '\0'))
X	{
X		free(t->t_start);
X		free((char *)t);
X		return (-1);
X	}
X	t->t_end = t->t_start + n;
X
X	/*
X	 * Link it into the list of tables.
X	 */
X	t->t_next = tables;
X	tables = t;
X	return (0);
X}
X
X/*
X * Try to add the lesskey file "$HOME/.less"
X */
X	public void
Xadd_hometable()
X{
X	char *filename;
X
X#if __MSDOS__
X	filename = homefile("_less");
X#else
X	filename = homefile(".less");
X#endif
X	if (filename == NULL)
X		return;
X	/*
X	 * Ignore errors.
X	 */
X	(void) add_cmdtable(filename);
X	free(filename);
X}
X#endif
END_OF_FILE
echo shar: Extracting \"help.c\"
sed "s/^X//" >'help.c' <<'END_OF_FILE'
X/*
X * Display some help.
X * Just invoke another "less" to display the help file.
X *
X * {{ This makes this function very simple, and makes changing the
X *    help file very easy, but it may present difficulties on
X *    (non-Unix) systems which do not supply the "system()" function. }}
X */
X
X#include  "less.h"
X
X#if __MSDOS__
X#include <io.h>
X#include <dir.h>
X#include <string.h>
X#include <stdlib.h>
Xextern int output_mode;
X#endif
X
Xextern char *progname;
X
X	public void
Xhelp()
X{
X	char *helpfile;
X	char *cmd;
X
X	helpfile = find_helpfile();
X	if (helpfile == NULL)
X	{
X		error("Cannot find help file", NULL_PARG);
X		return;
X	}
X#if __MSDOS__
X	putenv("LESS=-+v -+E -+s -mHPmHELP -- ?eEND -- Press g to see "
X		"it again:Press RETURN for more., or q when done ");
X	cmd = (char *) ecalloc(strlen(helpfile) + strlen(progname) + 50,
X				sizeof(char));
X	if (output_mode == 0)
X		sprintf(cmd, "-%s %s", progname, helpfile);
X	else
X		sprintf(cmd, "-%s -qVW4,4,76,23,Help %s", progname, helpfile);
X#else
X	cmd = (char *) ecalloc(strlen(helpfile) + strlen(progname) + 150,
X				sizeof(char));
X	sprintf(cmd, 
X	 "-%s -m -H -+E -+s '-PmHELP -- ?eEND -- Press g to see it again:Press RETURN for more., or q when done ' %s",
X		progname, helpfile);
X#endif
X	free(helpfile);
X	lsystem(cmd);
X	error("End of help", NULL_PARG);
X	free(cmd);
X}
END_OF_FILE
echo shar: Extracting \"input.c\"
sed "s/^X//" >'input.c' <<'END_OF_FILE'
X/*
X * High level routines dealing with getting lines of input 
X * from the file being viewed.
X *
X * When we speak of "lines" here, we mean PRINTABLE lines;
X * lines processed with respect to the screen width.
X * We use the term "raw line" to refer to lines simply
X * delimited by newlines; not processed with respect to screen width.
X */
X
X#include "less.h"
X
Xextern int squeeze;
Xextern int chopline;
Xextern int sigs;
X
X/*
X * Get the next line.
X * A "current" position is passed and a "new" position is returned.
X * The current position is the position of the first character of
X * a line.  The new position is the position of the first character
X * of the NEXT line.  The line obtained is the line starting at curr_pos.
X */
X	public POSITION
Xforw_line(curr_pos)
X	POSITION curr_pos;
X{
X	POSITION new_pos;
X	register int c;
X	int blankline;
X	int endline;
X
X	if (curr_pos == NULL_POSITION || ch_seek(curr_pos))
X	{
X		null_line();
X		return (NULL_POSITION);
X	}
X
X	prewind();
X	plinenum(curr_pos);
X	(void) ch_seek(curr_pos);
X
X	c = ch_forw_get();
X	if (c == EOI)
X	{
X		null_line();
X		return (NULL_POSITION);
X	}
X	blankline = (c == '\n' || c == '\r');
X
X	for (;;)
X	{
X		if (sigs)
X		{
X			null_line();
X			return (NULL_POSITION);
X		}
X		if (c == '\n' || c == EOI)
X		{
X			/*
X			 * End of the line.
X			 */
X			new_pos = ch_tell();
X			endline = 1;
X			break;
X		}
X
X		/*
X		 * Append the char to the line and get the next char.
X		 */
X		if (pappend(c))
X		{
X			/*
X			 * The char won't fit in the line; the line
X			 * is too long to print in the screen width.
X			 * End the line here.
X			 */
X			if (chopline)
X			{
X				do
X				{
X					c = ch_forw_get();
X				} while (c != '\n' && c != EOI);
X				new_pos = ch_tell();
X				endline = 1;
X			} else
X			{
X				new_pos = ch_tell() - 1;
X				endline = 0;
X			}
X			break;
X		}
X		c = ch_forw_get();
X	}
X	pdone(endline);
X
X	if (squeeze && blankline)
X	{
X		/*
X		 * This line is blank.
X		 * Skip down to the last contiguous blank line
X		 * and pretend it is the one which we are returning.
X		 */
X		while ((c = ch_forw_get()) == '\n' || c == '\r')
X			if (sigs)
X			{
X				null_line();
X				return (NULL_POSITION);
X			}
X		if (c != EOI)
X			(void) ch_back_get();
X		new_pos = ch_tell();
X	}
X
X	return (new_pos);
X}
X
X/*
X * Get the previous line.
X * A "current" position is passed and a "new" position is returned.
X * The current position is the position of the first character of
X * a line.  The new position is the position of the first character
X * of the PREVIOUS line.  The line obtained is the one starting at new_pos.
X */
X	public POSITION
Xback_line(curr_pos)
X	POSITION curr_pos;
X{
X	POSITION new_pos, begin_new_pos;
X	int c;
X	int endline;
X
X	if (curr_pos == NULL_POSITION || curr_pos <= ch_zero() ||
X		ch_seek(curr_pos-1))
X	{
X		null_line();
X		return (NULL_POSITION);
X	}
X
X	if (squeeze)
X	{
X		/*
X		 * Find out if the "current" line was blank.
X		 */
X		(void) ch_forw_get();	/* Skip the newline */
X		c = ch_forw_get();	/* First char of "current" line */
X		(void) ch_back_get();	/* Restore our position */
X		(void) ch_back_get();
X
X		if (c == '\n')
X		{
X			/*
X			 * The "current" line was blank.
X			 * Skip over any preceding blank lines,
X			 * since we skipped them in forw_line().
X			 */
X			while ((c = ch_back_get()) == '\n' || c == '\r')
X				if (sigs)
X				{
X					null_line();
X					return (NULL_POSITION);
X				}
X			if (c == EOI)
X			{
X				null_line();
X				return (NULL_POSITION);
X			}
X			(void) ch_forw_get();
X		}
X	}
X
X	/*
X	 * Scan backwards until we hit the beginning of the line.
X	 */
X	for (;;)
X	{
X		if (sigs)
X		{
X			null_line();
X			return (NULL_POSITION);
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_tell();
X			break;
X		}
X	}
X
X	/*
X	 * Now scan forwards from the beginning of this line.
X	 * We keep discarding "printable lines" (based on screen width)
X	 * until we reach the curr_pos.
X	 *
X	 * {{ This algorithm is pretty inefficient if the lines
X	 *    are much longer than the screen width, 
X	 *    but I don't know of any better way. }}
X	 */
X	if (ch_seek(new_pos))
X	{
X		null_line();
X		return (NULL_POSITION);
X	}
X	endline = 0;
X    loop:
X	begin_new_pos = new_pos;
X	prewind();
X	plinenum(new_pos);
X	(void) ch_seek(new_pos);
X
X	do
X	{
X		c = ch_forw_get();
X		if (c == EOI || sigs)
X		{
X			null_line();
X			return (NULL_POSITION);
X		}
X		new_pos++;
X		if (c == '\n')
X		{
X			endline = 1;
X			break;
X		}
X		if (pappend(c))
X		{
X			/*
X			 * Got a full printable line, but we haven't
X			 * reached our curr_pos yet.  Discard the line
X			 * and start a new one.
X			 */
X			if (chopline)
X			{
X				endline = 1;
X				break;
X			}
X			pdone(0);
X			(void) ch_back_get();
X			new_pos--;
X			goto loop;
X		}
X	} while (new_pos < curr_pos);
X
X	pdone(endline);
X
X	return (begin_new_pos);
X}
END_OF_FILE

mark@unix386.Convergent.COM (Mark Nudelman) (03/06/91)

#! /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

mark@unix386.Convergent.COM (Mark Nudelman) (03/06/91)

#! /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 \"charset.c\"
sed "s/^X//" >'charset.c' <<'END_OF_FILE'
X/*
X * Functions to define the character set
X * and do things specific to the character set.
X */
X
X#include "less.h"
X
X/*
X * Predefined character sets,
X * selected by the LESSCHARSET environment variable.
X */
Xstruct charset {
X	char *name;
X	char *desc;
X} charsets[] = {
X	{ "ascii",	"8bcccbcc18b95.b"	},
X	{ "latin1",	"8bcccbcc18b95.33b."	},
X	{ NULL }
X};
X
X#define	IS_BINARY_CHAR	01
X#define	IS_CONTROL_CHAR	02
X
Xstatic char chardef[256];
Xstatic char *binfmt = "\\%o";
X
Xextern char *getenv();
X
X/*
X * Define a charset, given a description string.
X * The string consists of 256 letters,
X * one for each character in the charset.
X * If the string is shorter than 256 letters, missing letters
X * are taken to be identical to the last one.
X * A decimal number followed by a letter is taken to be a 
X * repetition of the letter.
X *
X * Each letter is one of:
X *	. normal character
X *	b binary character
X *	c control character
X */
X	static void
Xichardef(s)
X	char *s;
X{
X	register char *cp;
X	register int n;
X	register char v;
X
X	n = 0;
X	cp = chardef;
X	while (*s != '\0')
X	{
X		switch (*s++)
X		{
X		case '.':
X			v = 0;
X			break;
X		case 'c':
X			v = IS_CONTROL_CHAR;
X			break;
X		case 'b':
X			v = IS_BINARY_CHAR|IS_CONTROL_CHAR;
X			break;
X
X		case '0': case '1': case '2': case '3': case '4':
X		case '5': case '6': case '7': case '8': case '9':
X			n = (10 * n) + (s[-1] - '0');
X			continue;
X
X		default:
X			error("invalid chardef", NULL_PARG);
X			quit(1);
X			/*NOTREACHED*/
X		}
X
X		do
X		{
X			if (cp >= chardef + sizeof(chardef))
X			{
X				error("chardef longer than 256", NULL_PARG);
X				quit(1);
X				/*NOTREACHED*/
X			}
X			*cp++ = v;
X		} while (--n > 0);
X		n = 0;
X	}
X
X	while (cp < chardef + sizeof(chardef))
X		*cp++ = v;
X}
X
X/*
X * Define a charset, given a charset name.
X * The valid charset names are listed in the "charsets" array.
X */
X	static int
Xicharset(name)
X	register char *name;
X{
X	register struct charset *p;
X
X	if (name == NULL || *name == '\0')
X		return (0);
X
X	for (p = charsets;  p->name != NULL;  p++)
X	{
X		if (strcmp(name, p->name) == 0)
X		{
X			ichardef(p->desc);
X			return (1);
X		}
X	}
X
X	error("invalid charset name", NULL_PARG);
X	quit(1);
X	/*NOTREACHED*/
X}
X
X/*
X * Initialize charset data structures.
X */
X	public void
Xinit_charset()
X{
X	register char *s;
X
X	/*
X	 * Try environment variable LESSCHARSET.
X	 * If LESSCHARSET is not set, try LESSCHARDEF.
X	 * If LESSCHARDEF is not set, default to "ascii" charset.
X	 */
X	s = getenv("LESSCHARSET");
X	if (icharset(s))
X		return;
X
X	s = getenv("LESSCHARDEF");
X	if (s != NULL && *s != '\0')
X	{
X		ichardef(s);
X		return;
X	}
X
X	(void) icharset("ascii");
X
X	s = getenv("LESSBINFMT");
X	if (s != NULL && *s != '\0')
X		binfmt = s;
X}
X
X/*
X * Is a given character a "binary" character?
X */
X	public int
Xbinary_char(c)
X	int c;
X{
X	return (chardef[c] & IS_BINARY_CHAR);
X}
X
X/*
X * Is a given character a "control" character?
X */
X	public int
Xcontrol_char(c)
X	int c;
X{
X	return (chardef[c] & IS_CONTROL_CHAR);
X}
X
X/*
X * Return the printable form of a character.
X * For example, in the "ascii" charset '\3' is printed as "^C".
X */
X	public char *
Xprchar(c)
X	int c;
X{
X	static char buf[8];
X
X	if (!control_char(c))
X		sprintf(buf, "%c", c);
X	else if (!control_char(c ^ 0100))
X		sprintf(buf, "^%c", c ^ 0100);
X	else
X		sprintf(buf, binfmt, c);
X	return (buf);
X}
END_OF_FILE
echo shar: Extracting \"filename.c\"
sed "s/^X//" >'filename.c' <<'END_OF_FILE'
X/*
X * Routines to mess around with filenames (and files).
X * Much of this is very OS dependent.
X */
X
X#include <stdio.h>
X#include "less.h"
X
Xextern char *getenv();
X
Xextern int force_open;
Xextern IFILE curr_ifile;
Xextern IFILE old_ifile;
X
X/*
X * Return the full pathname of the given file in the "home directory".
X */
X	public char *
Xhomefile(filename)
X	char *filename;
X{
X	register char *pathname;
X	register char *homedir;
X
X	homedir = getenv("HOME");
X#if __MSDOS__
X	/*
X	 * Most MSDOS users do not have $HOME defined,
X	 * so if no $HOME then look for "_less" anywhere 
X	 * on search path (always begins at current directory).
X	 */
X	if (homedir == NULL)
X	{
X		extern char *searchpath();
X		pathname = searchpath(filename);
X		if (pathname == NULL)
X			return (NULL);
X		pathname = save(pathname);
X	} else
X	{
X		pathname = (char *) calloc(strlen(homedir)+strlen(filename)+2, 
X					sizeof(char));
X		if (pathname == NULL)
X			return (NULL);
X		sprintf(pathname, "%s\\%s", homedir, filename);
X	}
X#else
X	if (homedir == NULL)
X		return (NULL);
X	pathname = (char *) calloc(strlen(homedir)+strlen(filename)+2,
X				sizeof(char));
X	if (pathname == NULL)
X		return (NULL);
X	sprintf(pathname, "%s/%s", homedir, filename);
X#endif
X	return (pathname);
X}
X
X/*
X * Find out where the help file is.
X */
X	public char *
Xfind_helpfile()
X{
X#if __MSDOS__
X	extern char *searchpath();
X
X	/*
X	 * Look in current directory.
X	 */
X	if (access(HELPFILE,0) == 0)
X		return (HELPFILE);
X	/*
X	 * Find the basename of HELPFILE,
X	 * and look for it in each directory in the search path.
X	 */
X	if ((helpfile = strrchr(HELPFILE, '\\')) == NULL)
X		helpfile = HELPFILE;
X	else
X		helpfile++;
X	return (searchpath(helpfile));
X#else
X	return (save(HELPFILE));
X#endif
X}
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		switch (*fr)
X		{
X		case '%':
X			n += strlen(get_filename(curr_ifile));
X			break;
X		case '#':
X			if (old_ifile == NULL_IFILE)
X			{
X				error("No previous file", NULL_PARG);
X				return (NULL);
X			}
X			n += strlen(get_filename(old_ifile));
X			break;
X		default:
X			n++;
X			break;
X		}
X	}
X
X	e = (char *) 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		switch (*fr)
X		{
X		case '%':
X			strcpy(to, get_filename(curr_ifile));
X			to += strlen(to);
X			break;
X		case '#':
X			strcpy(to, get_filename(old_ifile));
X			to += strlen(to);
X			break;
X		default:
X			*to++ = *fr;
X			break;
X		}
X	}
X	*to = '\0';
X	return (e);
X}
X
X/*
X * Try to determine if a file is "binary".
X * This is just a guess, and we need not try too hard to make it accurate.
X */
X	int
Xbinary_file(f)
X	int f;
X{
X	int i;
X	int n;
X	char data[64];
X
X	n = read(f, data, sizeof(data));
X	for (i = 0;  i < n;  i++)
X		if (binary_char(data[i]))
X			return (1);
X	return (0);
X}
X
X/*
X * Try to determine the size of a file by seeking to the end.
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 */
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	int len;
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 = (char *) 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 = (char *) 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	len = 100;
X	gfilename = (char *) ecalloc(len, sizeof(char));
X	for (p = gfilename;  ;  p++)
X	{
X		if ((ch = getc(f)) == '\n' || ch == EOF)
X			break;
X		if (p - gfilename >= len-1)
X		{
X			len *= 2;
X			*p = '\0';
X			p = (char *) ecalloc(len, sizeof(char));
X			strcpy(p, gfilename);
X			free(gfilename);
X			gfilename = p;
X			p = gfilename + strlen(gfilename);
X		}
X		*p = ch;
X	}
X	*p = '\0';
X	pclose(f);
X	if (*gfilename == '\0')
X		return (NULL);
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#if STAT
X
X#include <sys/types.h>
X#include <sys/stat.h>
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	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 = (char *) ecalloc(strlen(filename) + sizeof(is_dir), 
X			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 = (char *) ecalloc(strlen(filename) + sizeof(not_reg), 
X			sizeof(char));
X		strcpy(m, filename);
X		strcat(m, not_reg);
X		return (m);
X	}
X
X	return (NULL);
X}
X
X/*
X * Return the size of a file, as cheaply as possible.
X * In Unix, we can stat the file.
X */
X	public POSITION
Xfilesize(f)
X	int f;
X{
X	struct stat statbuf;
X
X	if (fstat(f, &statbuf) < 0)
X		/*
X		 * Can't stat; try seeking to the end.
X		 */
X		return (seek_filesize(f));
X
X	return ((POSITION) statbuf.st_size);
X}
X
X#else
X
X/*
X * If we have no way to find out, just say the file is good.
X */
X	public char *
Xbad_file(filename)
X	char *filename;
X{
X	return (NULL);
X}
X
X/*
X * We can find the file size by seeking.
X */
X	public POSITION
Xfilesize(f)
X	int f;
X{
X	return (seek_filesize(f));
X}
X
X#endif
END_OF_FILE
echo shar: Extracting \"lsystem.c\"
sed "s/^X//" >'lsystem.c' <<'END_OF_FILE'
X/*
X * Routines to execute other programs.
X * Necessarily very OS dependent.
X */
X
X#include <stdio.h>
X#include <signal.h>
X
X#include "less.h"
X#include "position.h"
X
X#if __MSDOS__
X#include <process.h>
X#include <dos.h>
X#include <fcntl.h>
X#include <io.h>
X#include <errno.h>
X#include <dir.h>
X#include <string.h>
X#include <stdlib.h>
X#include <time.h>
X#include <ctype.h>
Xchar get_swchar();
Xvoid swchar_to_dos();
Xvoid swchar_to_unix();
X#endif
X
Xextern char *getenv();
X
Xextern int screen_trashed;
Xextern IFILE curr_ifile;
X
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	register char *curr_filename;
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	 * Close the current input file.
X	 */
X	curr_filename = get_filename(curr_ifile);
X	(void) edit(NULL, 0);
X
X	/*
X	 * De-initialize the terminal and take out of raw mode.
X	 */
X	deinit();
X	flush();	/* Make sure the deinit chars get out */
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 user's terminal
X	 * (the normal standard input), even if less's standard input 
X	 * is coming from a pipe.
X	 */
X#if __MSDOS__
X{
X	register int inp2;
X
X	inp = dup(0);
X	inp2 = open("CON", O_TEXT|O_RDONLY);
X	dup2(0,inp2);
X}
X#else
X	inp = dup(0);
X	close(0);
X	if (open("/dev/tty", 0) < 0)
X		dup(inp);
X#endif
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#if __MSDOS__
X{
X	int result;
X	char sw_char;
X
X	sw_char = get_swchar();
X	swchar_to_dos();
X	result = system(cmd);
X	if (result != 0)
X		perror("less");
X	if (sw_char == '-')
X		swchar_to_unix();
X}
X#else
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 = (char *) ecalloc(strlen(shell) + strlen(cmd) + 7, 
X					sizeof(char));
X			sprintf(p, "%s -c \"%s\"", shell, cmd);
X		}
X	}
X	if (p == NULL)
X	{
X		if (*cmd == '\0')
X			p = save("sh");
X		else
X			p = save(cmd);
X	}
X
X	system(p);
X	free(p);
X#endif
X
X	/*
X	 * Restore standard input, reset signals, raw mode, etc.
X	 */
X#if __MSDOS__
X	close(inp2);
X	dup2(0,inp);
X	close(inp);
X#else
X	close(0);
X	dup(inp);
X	close(inp);
X#endif
X
X	init_signals(1);
X	raw_mode(1);
X	init();
X	screen_trashed = 1;
X
X	/*
X	 * Reopen the current input file.
X	 */
X	(void) edit(curr_filename, 0);
X
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#if PIPEC
X
X/*
X * Pipe a section of the input file into the given shell command.
X * The section to be piped is the section "between" the current
X * position and the position marked by the given letter.
X *
X * The "current" position means the top line displayed if the mark
X * is after the current screen, or the bottom line displayed if
X * the mark is before the current screen.
X * If the mark is on the current screen, the whole screen is displayed.
X */
X	public int
Xpipe_mark(c, cmd)
X	int c;
X	char *cmd;
X{
X	POSITION mpos, tpos, bpos;
X
X	/*
X	 * mpos = the marked position.
X	 * tpos = top of screen.
X	 * bpos = bottom of screen.
X	 */
X	mpos = markpos(c);
X	if (mpos == NULL_POSITION)
X		return (-1);
X	tpos = position(TOP);
X	if (tpos == NULL_POSITION)
X		tpos = ch_zero();
X	bpos = position(BOTTOM_PLUS_ONE);
X
X	if (mpos <= tpos)
X		return (pipe_data(cmd, mpos, bpos));
X	else if (bpos == NULL_POSITION || mpos <= bpos)
X		return (pipe_data(cmd, tpos, bpos));
X	else
X		return (pipe_data(cmd, tpos, mpos));
X}
X
X/*
X * Create a pipe to the given shell command.
X * Feed it the file contents between the positions spos and epos.
X */
X	public int
Xpipe_data(cmd, spos, epos)
X	char *cmd;
X	POSITION spos;
X	POSITION epos;
X{
X	register FILE *f;
X	register int c;
X	int inp;
X	extern FILE *popen();
X
X	/*
X	 * This is structured much like lsystem().
X	 * Since we're running a shell program, we must be careful
X	 * to perform the necessary deinitialization before running
X	 * the command, and reinitialization after it.
X	 */
X	if (ch_seek(spos) != 0)
X	{
X		error("Cannot seek to start position", NULL_PARG);
X		return (-1);
X	}
X
X	if ((f = popen(cmd, "w")) == NULL)
X	{
X		error("Cannot create pipe", NULL_PARG);
X		return (-1);
X	}
X	lower_left();
X	clear_eol();
X	putstr("!");
X	putstr(cmd);
X	putstr("\n");
X
X	deinit();
X	flush();
X	raw_mode(0);
X	init_signals(0);
X
X	while (epos == NULL_POSITION || spos++ < epos)
X	{
X		/*
X		 * Read a character from the file and give it to the pipe.
X		 */
X		c = ch_forw_get();
X		if (c == EOI)
X			break;
X		putc(c, f);
X	}
X	pclose(f);
X
X	init_signals(1);
X	raw_mode(1);
X	init();
X	screen_trashed = 1;
X#if defined(SIGWINCH) || defined(SIGWIND)
X	/* {{ Probably don't need this here. }} */
X	winch();
X#endif
X	return (0);
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() */
Xpublic int need_clr;
X
Xextern int sigs;
Xextern int sc_width;
Xextern int so_s_width, so_e_width;
Xextern int screen_trashed;
Xextern int any_display;
X#if __MSDOS__
Xextern int output_mode;
X#endif
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	int a;
X	int curr_attr;
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	curr_attr = NORMAL;
X
X	for (i = 0;  (c = gline(i, &a)) != '\0';  i++)
X	{
X		if (a != curr_attr)
X		{
X			/*
X			 * Changing attributes.
X			 * Display the exit sequence for the old attribute
X			 * and the enter sequence for the new one.
X			 */
X			switch (curr_attr)
X			{
X			case UNDERLINE:	ul_exit();	break;
X			case BOLD:	bo_exit();	break;
X			case BLINK:	bl_exit();	break;
X			}
X			switch (a)
X			{
X			case UNDERLINE:	ul_enter();	break;
X			case BOLD:	bo_enter();	break;
X			case BLINK:	bl_enter();	break;
X			}
X			curr_attr = a;
X		}
X		if (curr_attr == INVIS)
X			continue;
X		if (c == '\b')
X			putbs();
X		else
X			putchr(c);
X	}
X}
X
Xstatic char obuf[1024];
Xstatic char *ob = obuf;
X
X/*
X * Flush buffered output.
X *
X * If we haven't displayed any file data yet,
X * output messages on error output (file descriptor 2),
X * otherwise output on standard output (file descriptor 1).
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	public void
Xflush()
X{
X	register int n;
X	register int fd;
X
X#if __MSDOS__
X	if (output_mode == 0)
X	{
X		*ob = '\0';
X		cputs(obuf);
X		ob = obuf;
X		return;
X	}
X#endif
X	n = ob - obuf;
X	if (n == 0)
X		return;
X	fd = (any_display) ? 1 : 2;
X	if (write(fd, obuf, n) != n)
X		screen_trashed = 1;
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#if __MSDOS__
X	if (c == '\n')
X		*ob++ = '\r';
X#endif
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/*
X * Output an integer in a given radix.
X */
X	static int
Xiprintnum(num, radix)
X	int num;
X	int radix;
X{
X	register char *s;
X	int r;
X	int neg;
X	char buf[10];
X
X	if (neg = (num < 0))
X		num = -num;
X
X	s = buf;
X	do
X	{
X		*s++ = (num % radix) + '0';
X	} while ((num /= radix) != 0);
X
X	if (neg)
X		*s++ = '-';
X	r = s - buf;
X
X	while (s > buf)
X		putchr(*--s);
X	return (r);
X}
X
X/*
X * This function implements printf-like functionality
X * using a more portable argument list mechanism than printf's.
X */
X	static int
Xiprintf(fmt, parg)
X	register char *fmt;
X	PARG *parg;
X{
X	register char *s;
X	register int n;
X	register int col;
X
X	col = 0;
X	while (*fmt != '\0')
X	{
X		if (*fmt != '%')
X		{
X			putchr(*fmt++);
X			col++;
X		} else
X		{
X			++fmt;
X			switch (*fmt++) {
X			case 's':
X				s = parg->p_string;
X				parg++;
X				while (*s != '\0')
X				{
X					putchr(*s++);
X					col++;
X				}
X				break;
X			case 'd':
X				n = parg->p_int;
X				parg++;
X				col += iprintnum(n, 10);
X				break;
X			}
X		}
X	}
X	return (col);
X}
X
X/*
X * Output a message in the lower left corner of the screen
X * and wait for carriage return.
X */
X	public void
Xerror(fmt, parg)
X	char *fmt;
X	PARG *parg;
X{
X	int c;
X	int col = 0;
X	static char return_to_continue[] = "  (press RETURN)";
X
X	errmsgs++;
X
X	if (any_display)
X	{
X		lower_left();
X		clear_eol();
X		so_enter();
X		col += so_s_width;
X	}
X
X	col += iprintf(fmt, parg);
X
X	if (!any_display)
X	{
X		putchr('\n');
X		return;
X	}
X
X	putstr(return_to_continue);
X	so_exit();
X	col += sizeof(return_to_continue) + so_e_width;
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		ungetcc(c);
X#endif
X	lower_left();
X
X	if (col >= 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/*
X * Output a message in the lower left corner of the screen
X * and don't wait for carriage return.
X * Usually used to warn that we are beginning a potentially
X * time-consuming operation.
X */
X	public void
Xierror(fmt, parg)
X	char *fmt;
X	PARG *parg;
X{
X	lower_left();
X	clear_eol();
X	so_enter();
X	(void) iprintf(fmt, parg);
X	putstr(intr_to_abort);
X	so_exit();
X	flush();
X	need_clr = 1;
X}
X
X/*
X * Output a message in the lower left corner of the screen
X * and return a single-character response.
X */
X	public int
Xquery(fmt, parg)
X	char *fmt;
X	PARG *parg;
X{
X	register int c;
X	int col = 0;
X
X	if (any_display)
X	{
X		lower_left();
X		clear_eol();
X	}
X
X	(void) iprintf(fmt, parg);
X	c = getchr();
X
X	if (!any_display)
X	{
X		putchr('\n');
X		return (c);
X	}
X
X	lower_left();
X	if (col >= sc_width)
X		screen_trashed = 1;
X	flush();
X
X	return (c);
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}
X
X/*
X * See if the entire screen is empty.
X */
X	public int
Xempty_screen()
X{
X	return (empty_lines(0, sc_height-1));
X}
X
X	public int
Xempty_lines(s, e)
X	int s;
X	int e;
X{
X	register int i;
X
X	for (i = s;  i <= e;  i++)
X		if (table[i] != NULL_POSITION)
X			return (0);
X	return (1);
X}
X
X/*
X * Get the current screen position.
X * The screen position consists of both a file position and
X * a screen line number where the file position is placed on the screen.
X * Normally the screen line number is 0, but if we are positioned
X * such that the top few lines are empty, we may have to set
X * the screen line to a number > 0.
X */
X	public void
Xget_scrpos(scrpos)
X	struct scrpos *scrpos;
X{
X	register int i;
X
X	/*
X	 * Find the first line on the screen which has something on it,
X	 * and return the screen line number and the file position.
X	 */
X	for (i = 0; i < sc_height;  i++)
X		if (table[i] != NULL_POSITION)
X		{
X			scrpos->ln = i+1;
X			scrpos->pos = table[i];
X			return;
X		}
X	/*
X	 * The screen is empty.
X	 */
X	scrpos->pos = NULL_POSITION;
X}
X
X/*
X * Adjust a screen line number to be a simple positive integer
X * in the range { 0 .. sc_height-2 }.
X * (The bottom line, sc_height-1, is reserved for prompts, etc.)
X * The given "sline" may be in the range { 1 .. sc_height-1 }
X * to refer to lines relative to the top of the screen (starting from 1),
X * or it may be in { -1 .. -(sc_height-1) } to refer to lines
X * relative to the bottom of the screen.
X */
X	public int
Xadjsline(sline)
X	int sline;
X{
X	/*
X	 * Negative screen line number means
X	 * relative to the bottom of the screen.
X	 */
X	if (sline < 0)
X		sline += sc_height;
X	/*
X	 * Can't be less than 1 or greater than sc_height-1.
X	 */
X	if (sline <= 0)
X		sline = 1;
X	if (sline >= sc_height)
X		sline = sc_height - 1;
X	/*
X	 * Return zero-based line number, not one-based.
X	 */
X	return (sline-1);
X}
END_OF_FILE
echo shar: Extracting \"ifile.c\"
sed "s/^X//" >'ifile.c' <<'END_OF_FILE'
X/*
X * An IFILE represents an input file.
X *
X * It is actually a pointer to an ifile structure,
X * but is opaque outside this module.
X * Ifile structures are kept in a linked list in the order they 
X * appear on the command line.
X * Any new file which does not already appear in the list is
X * inserted after the current file.
X */
X
X#include "less.h"
X
Xstruct ifile {
X	struct ifile *h_next;		/* Links for command line list */
X	struct ifile *h_prev;
X	int h_index;			/* Index within command line list */
X	char *h_filename;		/* Name of the file */
X	struct scrpos h_scrpos;		/* Saved position within the file */
X};
X
X/*
X * Convert an IFILE (external representation)
X * to a struct file (internal representation), and vice versa.
X */
X#define int_ifile(h)	((struct ifile *)(h))
X#define ext_ifile(h)	((IFILE)(h))
X
X/*
X * Anchor for linked list.
X */
Xstatic struct ifile anchor = { &anchor, &anchor, 0 };
Xstatic int ifiles = 0;
X
X/*
X * Allocate a new ifile structure and stick a filename in it.
X * It should go after "prev" in the list
X * (or at the beginning of the list if "prev" is NULL).
X * Return a pointer to the new ifile structure.
X */
X	static struct ifile *
Xnew_ifile(filename, prev)
X	char *filename;
X	struct ifile *prev;
X{
X	register struct ifile *p;
X	register struct ifile *np;
X
X	/*
X	 * Allocate and initialize structure.
X	 */
X	p = (struct ifile *) ecalloc(1, sizeof(struct ifile));
X	p->h_filename = filename;
X	p->h_scrpos.pos = NULL_POSITION;
X
X	/*
X	 * Link into list.
X	 */
X	if (prev == NULL)
X		prev = &anchor;
X	p->h_next = prev->h_next;
X	p->h_prev = prev;
X	prev->h_next->h_prev = p;
X	prev->h_next = p;
X
X	/*
X	 * Calculate index for the new one,
X	 * and adjust the indexes for subsequent ifiles in the list.
X	 */
X	p->h_index = prev->h_index + 1;
X	for (np = p->h_next;  np != &anchor;  np = np->h_next)
X		np->h_index++;
X
X	ifiles++;
X	return (p);
X}
X
X/*
X * Get the ifile after a given one in the list.
X */
X	public IFILE
Xnext_ifile(h)
X	IFILE h;
X{
X	register struct ifile *p;
X
X	p = (h == NULL_IFILE) ? &anchor : int_ifile(h);
X	if (p->h_next == &anchor)
X		return (NULL_IFILE);
X	return (ext_ifile(p->h_next));
X}
X
X/*
X * Get the ifile before a given one in the list.
X */
X	public IFILE
Xprev_ifile(h)
X	IFILE h;
X{
X	register struct ifile *p;
X
X	p = (h == NULL_IFILE) ? &anchor : int_ifile(h);
X	if (p->h_prev == &anchor)
X		return (NULL_IFILE);
X	return (ext_ifile(p->h_prev));
X}
X
X/*
X * Return the number of ifiles.
X */
X	public int
Xnifile()
X{
X	return (ifiles);
X}
X
X/*
X * Find an ifile structure, given a filename.
X */
X	static struct ifile *
Xfind_ifile(filename)
X	char *filename;
X{
X	register struct ifile *p;
X
X	for (p = anchor.h_next;  p != &anchor;  p = p->h_next)
X		if (strcmp(filename, p->h_filename) == 0)
X			return (p);
X	return (NULL);
X}
X
X/*
X * Get the ifile associated with a filename.
X * If the filename has not been seen before,
X * insert the new ifile after "prev" in the list.
X */
X	public IFILE
Xget_ifile(filename, prev)
X	char *filename;
X	IFILE prev;
X{
X	register struct ifile *p;
X
X	if ((p = find_ifile(filename)) == NULL)
X		p = new_ifile(save(filename), int_ifile(prev));
X	return (ext_ifile(p));
X}
X
X/*
X * Get the filename associated with a ifile.
X */
X	public char *
Xget_filename(ifile)
X	IFILE ifile;
X{
X	return (int_ifile(ifile)->h_filename);
X}
X
X/*
X * Get the index of the file associated with a ifile.
X */
X	public int
Xget_index(ifile)
X	IFILE ifile;
X{
X	return (int_ifile(ifile)->h_index); 
X}
X
X/*
X * Save the file position to be associated with a given file.
X */
X	public void
Xstore_pos(ifile, scrpos)
X	IFILE ifile;
X	struct scrpos *scrpos;
X{
X	int_ifile(ifile)->h_scrpos = *scrpos;
X}
X
X/*
X * Recall the file position associated with a file.
X * If no position has been associated with the file, return NULL_POSITION.
X */
X	public void
Xget_pos(ifile, scrpos)
X	IFILE ifile;
X	struct scrpos *scrpos;
X{
X	*scrpos = int_ifile(ifile)->h_scrpos;
X}
END_OF_FILE
echo shar: Extracting \"brac.c\"
sed "s/^X//" >'brac.c' <<'END_OF_FILE'
X/*
X * Routines to perform bracket matching functions.
X */
X
X#include "less.h"
X#include "position.h"
X
X/*
X * Try to match the n-th open bracket 
X *  which appears in the top displayed line (forwdir),
X * or the n-th close bracket 
X *  which appears in the bottom displayed line (!forwdir).
X * The characters which serve as "open bracket" and 
X * "close bracket" are given.
X */
X	public void
Xmatch_brac(obrac, cbrac, forwdir, n)
X	register int obrac;
X	register int cbrac;
X	int forwdir;
X	int n;
X{
X	register int c;
X	register int nest;
X	POSITION pos;
X	int (*chget)();
X
X	extern int ch_forw_get(), ch_back_get();
X
X	/*
X	 * Seek to the line containing the open bracket.
X	 * This is either the top or bottom line on the screen,
X	 * depending on the type of bracket.
X	 */
X	pos = position((forwdir) ? TOP : BOTTOM);
X	if (pos == NULL_POSITION || ch_seek(pos))
X	{
X		if (forwdir)
X			error("Nothing in top line", NULL_PARG);
X		else
X			error("Nothing in bottom line", NULL_PARG);
X		return;
X	}
X
X	/*
X	 * Look thru the line to find the open bracket to match.
X	 */
X	do
X	{
X		if ((c = ch_forw_get()) == '\n' || c == EOI)
X		{
X			if (forwdir)
X				error("No bracket in top line", NULL_PARG);
X			else
X				error("No bracket in bottom line", NULL_PARG);
X			return;
X		}
X	} while (c != obrac || --n > 0);
X
X	/*
X	 * Position the file just "after" the open bracket
X	 * (in the direction in which we will be searching).
X	 * If searching forward, we are already after the bracket.
X	 * If searching backward, skip back over the open bracket.
X	 */
X	if (!forwdir)
X		(void) ch_back_get();
X
X	/*
X	 * Search the file for the matching bracket.
X	 */
X	chget = (forwdir) ? ch_forw_get : ch_back_get;
X	nest = 0;
X	while ((c = (*chget)()) != EOI)
X	{
X		if (c == obrac)
X			nest++;
X		else if (c == cbrac && --nest < 0)
X		{
X			/*
X			 * Found the matching bracket.
X			 * If searching backward, put it on the top line.
X			 * If searching forward, put it on the bottom line.
X			 */
X			jump_line_loc(ch_tell(), forwdir ? -1 : 1);
X			return;
X		}
X	}
X	error("No matching bracket", NULL_PARG);
X}
END_OF_FILE
echo shar: Extracting \"forwback.c\"
sed "s/^X//" >'forwback.c' <<'END_OF_FILE'
X/*
X * Primitives for displaying the file on the screen,
X * scrolling either forward or backward.
X */
X
X#include "less.h"
X#include "position.h"
X
Xpublic int hit_eof;	/* Keeps track of how many times we hit end of file */
Xpublic int screen_trashed;
Xpublic int squished;
X
Xextern int sigs;
Xextern int top_scroll;
Xextern int quiet;
Xextern int sc_width, sc_height;
Xextern int quit_at_eof;
Xextern int plusoption;
Xextern int forw_scroll;
Xextern int back_scroll;
Xextern int need_clr;
Xextern int ignore_eoi;
X#if TAGS
Xextern int tagoption;
X#endif
X
X/*
X * Sound the bell to indicate user is trying to move past end of file.
X */
X	static void
Xeof_bell()
X{
X	if (quiet == NOT_QUIET)
X		bell();
X	else
X		vbell();
X}
X
X/*
X * Check to see if the end of file is currently "displayed".
X */
X	static void
Xeof_check()
X{
X	POSITION pos;
X
X	if (sigs)
X		return;
X	/*
X	 * If the bottom line is empty, we are at EOF.
X	 * If the bottom line ends at the file length,
X	 * we must be just at EOF.
X	 */
X	pos = position(BOTTOM_PLUS_ONE);
X	if (pos == NULL_POSITION || pos == ch_length())
X		hit_eof++;
X}
X
X/*
X * If the screen is "squished", repaint it.
X * "Squished" means the first displayed line is not at the top
X * of the screen; this can happen when we display a short file
X * for the first time.
X */
X	static void
Xsquish_check()
X{
X	if (!squished)
X		return;
X	squished = 0;
X	repaint();
X}
X
X/*
X * Display n lines, scrolling forward, 
X * starting at position pos in the input file.
X * "force" means display the n lines even if we hit end of file.
X * "only_last" means display only the last screenful if n > screen size.
X * "nblank" is the number of blank lines to draw before the first
X *   real line.  If nblank > 0, the pos must be NULL_POSITION.
X *   The first real line after the blanks will start at ch_zero().
X */
X	public void
Xforw(n, pos, force, only_last, nblank)
X	register int n;
X	POSITION pos;
X	int force;
X	int only_last;
X	int nblank;
X{
X	int eof = 0;
X	int nlines = 0;
X	int do_repaint;
X	static int first_time = 1;
X
X	squish_check();
X
X	/*
X	 * do_repaint tells us not to display anything till the end, 
X	 * then just repaint the entire screen.
X	 * We repaint if we are supposed to display only the last 
X	 * screenful and the request is for more than a screenful.
X	 * Also if the request exceeds the forward scroll limit
X	 * (but not if the request is for exactly a screenful, since
X	 * repainting itself involves scrolling forward a screenful).
X	 */
X	do_repaint = (only_last && n > sc_height-1) || 
X		(forw_scroll >= 0 && n > forw_scroll && n != sc_height-1);
X
X	if (!do_repaint)
X	{
X		if (top_scroll && n >= sc_height - 1 && pos != ch_length())
X		{
X			/*
X			 * Start a new screen.
X			 * {{ This is not really desirable if we happen
X			 *    to hit eof in the middle of this screen,
X			 *    but we don't yet know if that will happen. }}
X			 */
X			if (top_scroll == 2 || first_time)
X				clear();
X			home();
X			force = 1;
X		} else
X		{
X			lower_left();
X			clear_eol();
X		}
X
X		if (pos != position(BOTTOM_PLUS_ONE) || empty_screen())
X		{
X			/*
X			 * This is not contiguous with what is
X			 * currently displayed.  Clear the screen image 
X			 * (position table) and start a new screen.
X			 */
X			pos_clear();
X			add_forw_pos(pos);
X			force = 1;
X			if (top_scroll)
X			{
X				if (top_scroll == 2)
X					clear();
X				home();
X			} else if (!first_time)
X			{
X				putstr("...skipping...\n");
X			}
X		}
X	}
X
X	while (--n >= 0)
X	{
X		/*
X		 * Read the next line of input.
X		 */
X		if (nblank > 0)
X		{
X			/*
X			 * Still drawing blanks; don't get a line 
X			 * from the file yet.
X			 * If this is the last blank line, get ready to
X			 * read a line starting at ch_zero() next time.
X			 */
X			if (--nblank == 0)
X				pos = ch_zero();
X		} else
X		{
X			/* 
X			 * Get the next line from the file.
X			 */
X			pos = forw_line(pos);
X			if (pos == NULL_POSITION)
X			{
X				/*
X				 * End of file: stop here unless the top line 
X				 * is still empty, or "force" is true.
X				 */
X				eof = 1;
X				if (!force && position(TOP) != NULL_POSITION)
X					break;
X			}
X		}
X		/*
X		 * Add the position of the next line to the position table.
X		 * Display the current line on the screen.
X		 */
X		add_forw_pos(pos);
X		nlines++;
X		if (do_repaint)
X			continue;
X		/*
X		 * If this is the first screen displayed and
X		 * we hit an early EOF (i.e. before the requested
X		 * number of lines), we "squish" the display down
X		 * at the bottom of the screen.
X		 * But don't do this if a + option or a -t option
X		 * was given.  These options can cause us to
X		 * start the display after the beginning of the file,
X		 * and it is not appropriate to squish in that case.
X		 */
X		if (first_time && pos == NULL_POSITION && !top_scroll && 
X#if TAGS
X		    !tagoption &&
X#endif
X		    !plusoption)
X		{
X			squished = 1;
X			continue;
X		}
X		if (top_scroll == 1)
X			clear_eol();
X		put_line();
X	}
X
X	if (eof && !sigs)
X		hit_eof++;
X	else
X		eof_check();
X	if (nlines == 0)
X		eof_bell();
X	else if (do_repaint)
X		repaint();
X	first_time = 0;
X	(void) currline(BOTTOM);
X}
X
X/*
X * Display n lines, scrolling backward.
X */
X	public void
Xback(n, pos, force, only_last)
X	register int n;
X	POSITION pos;
X	int force;
X	int only_last;
X{
X	int nlines = 0;
X	int do_repaint;
X
X	squish_check();
X	do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1));
X	hit_eof = 0;
X	while (--n >= 0)
X	{
X		/*
X		 * Get the previous line of input.
X		 */
X		pos = back_line(pos);
X		if (pos == NULL_POSITION)
X		{
X			/*
X			 * Beginning of file: stop here unless "force" is true.
X			 */
X			if (!force)
X				break;
X		}
X		/*
X		 * Add the position of the previous line to the position table.
X		 * Display the line on the screen.
X		 */
X		add_back_pos(pos);
X		nlines++;
X		if (!do_repaint)
X		{
X			home();
X			add_line();
X			put_line();
X		}
X	}
X
X	eof_check();
X	if (nlines == 0)
X		eof_bell();
X	else if (do_repaint)
X		repaint();
X	(void) currline(BOTTOM);
X}
X
X/*
X * Display n more lines, forward.
X * Start just after the line currently displayed at the bottom of the screen.
X */
X	public void
Xforward(n, force, only_last)
X	int n;
X	int force;
X	int only_last;
X{
X	POSITION pos;
X
X	if (quit_at_eof && hit_eof)
X	{
X		/*
X		 * If the -e flag is set and we're trying to go
X		 * forward from end-of-file, go on to the next file.
X		 */
X		if (edit_next(1))
X			quit(0);
X		return;
X	}
X
X	pos = position(BOTTOM_PLUS_ONE);
X	if (pos == NULL_POSITION && (!force || empty_lines(2, sc_height-1)))
X	{
X		if (ignore_eoi)
X		{
X			/*
X			 * ignore_eoi is to support A_F_FOREVER.
X			 * Back up until there is a line at the bottom
X			 * of the screen.
X			 */
X			if (empty_screen())
X				pos = ch_zero();
X			else
X			{
X				do
X				{
X					back(1, position(TOP), 1, 0);
X					pos = position(BOTTOM_PLUS_ONE);
X				} while (pos == NULL_POSITION);
X			}
X		} else
X		{
X			eof_bell();
X			hit_eof++;
X			return;
X		}
X	}
X	forw(n, pos, force, only_last, 0);
X}
X
X/*
X * Display n more lines, backward.
X * Start just before the line currently displayed at the top of the screen.
X */
X	public void
Xbackward(n, force, only_last)
X	int n;
X	int force;
X	int only_last;
X{
X	POSITION pos;
X
X	pos = position(TOP);
X	if (pos == NULL_POSITION && (!force || position(BOTTOM) == 0))
X	{
X		eof_bell();
X		return;   
X	}
X	back(n, pos, force, only_last);
X}
X
X/*
X * Get the backwards scroll limit.
X * Must call this function instead of just using the value of
X * back_scroll, because the default case depends on sc_height and
X * top_scroll, as well as back_scroll.
X */
X	public int
Xget_back_scroll()
X{
X	if (back_scroll >= 0)
X		return (back_scroll);
X	if (top_scroll)
X		return (sc_height - 2);
X	return (10000); /* infinity */
X}
END_OF_FILE
echo shar: Extracting \"jump.c\"
sed "s/^X//" >'jump.c' <<'END_OF_FILE'
X/*
X * Routines which jump to a new location in the file.
X */
X
X#include "less.h"
X#include "position.h"
X
Xextern int hit_eof;
Xextern int jump_sline;
Xextern int squished;
Xextern int screen_trashed;
Xextern int sc_width, sc_height;
X
X/*
X * Jump to the end of the file.
X */
X	public void
Xjump_forw()
X{
X	if (ch_end_seek())
X	{
X		error("Cannot seek to end of file", NULL_PARG);
X		return;
X	}
X	/*
X	 * Position the last line in the file at the last screen line.
X	 */
X	jump_loc(back_line(ch_tell()), sc_height-1);
X}
X
X/*
X * Jump to line n in the file.
X */
X	public void
Xjump_back(n)
X	int n;
X{
X	POSITION pos;
X	PARG parg;
X
X	/*
X	 * Find the position of the specified line.
X	 * If we can seek there, just jump to it.
X	 * If we can't seek, but we're trying to go to line number 1,
X	 * use ch_beg_seek() to get as close as we can.
X	 */
X	pos = find_pos(n);
X	if (pos != NULL_POSITION && ch_seek(pos) == 0)
X	{
X		jump_loc(pos, jump_sline);
X	} else if (n <= 1 && ch_beg_seek() == 0)
X	{
X		jump_loc(ch_tell(), jump_sline);
X		error("Cannot seek to beginning of file", NULL_PARG);
X	} else
X	{
X		parg.p_int = n;
X		error("Cannot seek to line number %d", &parg);
X	}
X}
X
X/*
X * Repaint the screen.
X */
X	public void
Xrepaint()
X{
X	struct scrpos scrpos;
X	/*
X	 * Start at the line currently at the top of the screen
X	 * and redisplay the screen.
X	 */
X	get_scrpos(&scrpos);
X	pos_clear();
X	jump_loc(scrpos.pos, scrpos.ln);
X}
X
X/*
X * Jump to a specified percentage into the file.
X */
X	public void
Xjump_percent(percent)
X	int percent;
X{
X	POSITION pos, len;
X
X	/*
X	 * Determine the position in the file
X	 * (the specified percentage of the file's length).
X	 */
X	if ((len = ch_length()) == NULL_POSITION)
X	{
X		error("Don't know length of file", NULL_PARG);
X		return;
X	}
X	/*
X	 * {{ This calculation may overflow! }}
X	 */
X	pos = (percent * len) / 100;
X	if (pos >= len)
X		pos = len-1;
X
X	jump_line_loc(pos, jump_sline);
X}
X
X/*
X * Jump to a specified position in the file.
X * Like jump_loc, but the position need not be 
X * the first character in a line.
X */
X	public void
Xjump_line_loc(pos, sline)
X	POSITION pos;
X	int sline;
X{
X	int c;
X
X	if (ch_seek(pos) == 0)
X	{
X		/*
X		 * Back up to the beginning of the line.
X		 */
X		while ((c = ch_back_get()) != '\n' && c != EOI)
X			;
X		if (c == '\n')
X			(void) ch_forw_get();
X		pos = ch_tell();
X	}
X	jump_loc(pos, sline);
X}
X
X/*
X * Jump to a specified position in the file.
X * The position must be the first character in a line.
X * Place the target line on a specified line on the screen.
X */
X	public void
Xjump_loc(pos, sline)
X	POSITION pos;
X	int sline;
X{
X	register int nline;
X	POSITION tpos;
X	POSITION bpos;
X
X	/*
X	 * Normalize sline.
X	 */
X	sline = adjsline(sline);
X
X	if ((nline = onscreen(pos)) >= 0)
X	{
X		/*
X		 * The line is currently displayed.  
X		 * Just scroll there.
X		 */
X		nline -= sline;
X		if (nline > 0)
X			forw(nline, position(BOTTOM_PLUS_ONE), 1, 0, 0);
X		else
X			back(-nline, position(TOP), 1, 0);
X		return;
X	}
X
X	/*
X	 * Line is not on screen.
X	 * Seek to the desired location.
X	 */
X	if (ch_seek(pos))
X	{
X		error("Cannot seek to that file position", NULL_PARG);
X		return;
X	}
X
X	/*
X	 * See if the desired line is before or after 
X	 * the currently displayed screen.
X	 */
X	tpos = position(TOP);
X	bpos = position(BOTTOM_PLUS_ONE);
X	if (tpos == NULL_POSITION || pos >= tpos)
X	{
X		/*
X		 * The desired line is after the current screen.
X		 * Move back in the file far enough so that we can
X		 * call forw() and put the desired line at the 
X		 * sline-th line on the screen.
X		 */
X		for (nline = 0;  nline < sline;  nline++)
X		{
X			if (bpos != NULL_POSITION && pos <= bpos)
X			{
X				/*
X				 * Surprise!  The desired line is
X				 * close enough to the current screen
X				 * that we can just scroll there after all.
X				 */
X				forw(sc_height-sline+nline-1, bpos, 1, 0, 0);
X				return;
X			}
X			pos = back_line(pos);
X			if (pos == NULL_POSITION)
X			{
X				/*
X				 * Oops.  Ran into the beginning of the file.
X				 * Exit the loop here and rely on forw()
X				 * below to draw the required number of
X				 * blank lines at the top of the screen.
X				 */
X				break;
X			}
X		}
X		lastmark();
X		hit_eof = 0;
X		squished = 0;
X		screen_trashed = 0;
X		forw(sc_height-1, pos, 1, 0, sline-nline);
X	} else
X	{
X		/*
X		 * The desired line is before the current screen.
X		 * Move forward in the file far enough so that we
X		 * can call back() and put the desired line at the 
X		 * sline-th line on the screen.
X		 */
X		for (nline = sline;  nline < sc_height - 1;  nline++)
X		{
X			pos = forw_line(pos);
X			if (pos == NULL_POSITION)
X			{
X				/* Cannot happen! */
X				error("Program error: EOI in jump_loc (forw)",
X					NULL_PARG);
X				quit(1);
X			}
X			if (pos >= tpos)
X			{
X				/* 
X				 * Surprise!  The desired line is
X				 * close enough to the current screen
X				 * that we can just scroll there after all.
X				 */
X				back(nline+1, tpos, 1, 0);
X				return;
X			}
X		}
X		lastmark();
X		clear();
X		screen_trashed = 0;
X		add_back_pos(pos);
X		back(sc_height-1, pos, 1, 0);
X	}
X}
END_OF_FILE
echo shar: Extracting \"search.c\"
sed "s/^X//" >'search.c' <<'END_OF_FILE'
X/*
X * Routines to search a file for a pattern.
X */
X
X#include "less.h"
X#include "position.h"
X#if REGCOMP
X#include "regexp.h"
X#endif
X
Xextern int sigs;
Xextern int how_search;
Xextern int top_scroll;
Xextern int back_scroll;
Xextern int caseless;
Xextern int linenums;
Xextern int sc_height;
Xextern int jump_sline;
X
X/*
X * Search for the n-th occurrence of a specified pattern, 
X * either forward or backward.
X * Return the number of matches not yet found in this file
X * (that is, n minus the number of matches found).
X * Return -1 if the search should be aborted.
X * Caller may continue the search in another file 
X * if less than n matches are found in this file.
X */
X	public int
Xsearch(search_type, pattern, n)
X	int search_type;
X	char *pattern;
X	int n;
X{
X	POSITION pos, linepos;
X	register char *p;
X	register char *q;
X	register int goforw;
X	register int want_match;
X	char *line;
X	int linenum;
X	int line_match;
X	static int is_caseless;
X#if RECOMP
X	char *re_comp();
X	PARG parg;
X#else
X#if REGCMP
X	char *regcmp();
X	static char *cpattern = NULL;
X#else
X#if REGCOMP
X	static struct regexp *regpattern = NULL;
X#else
X	static char lpbuf[100];
X	static char *last_pattern = NULL;
X#endif
X#endif
X#endif
X
X	/*
X	 * Extract flags and type of search.
X	 */
X	goforw = (SRCH_DIR(search_type) == SRCH_FORW);
X	want_match = !(search_type & SRCH_NOMATCH);
X
X	if (pattern != NULL && (is_caseless = caseless))
X	{
X		/*
X		 * Search will ignore case, unless
X		 * there are any uppercase letters in the pattern.
X		 */
X		for (p = pattern;  *p != '\0';  p++)
X			if (*p >= 'A' && *p <= 'Z')
X			{
X				is_caseless = 0;
X				break;
X			}
X	}
X#if RECOMP
X
X	/*
X	 * (re_comp handles a null pattern internally, 
X	 *  so there is no need to check for a null pattern here.)
X	 */
X	if ((parg.p_string = re_comp(pattern)) != NULL)
X	{
X		error("%s", &parg);
X		return (-1);
X	}
X#else
X#if REGCMP
X	if (pattern == NULL || *pattern == '\0')
X	{
X		/*
X		 * A null pattern means use the previous pattern.
X		 * The compiled previous pattern is in cpattern, so just use it.
X		 */
X		if (cpattern == NULL)
X		{
X			error("No previous regular expression", NULL_PARG);
X			return (-1);
X		}
X	} else
X	{
X		/*
X		 * Otherwise compile the given pattern.
X		 */
X		char *s;
X		if ((s = regcmp(pattern, 0)) == NULL)
X		{
X			error("Invalid pattern", NULL_PARG);
X			return (-1);
X		}
X		if (cpattern != NULL)
X			free(cpattern);
X		cpattern = s;
X	}
X#else
X#if REGCOMP
X	if (pattern == NULL || *pattern == '\0')
X	{
X		/*
X		 * A null pattern means use the previous pattern.
X		 * The compiled previous pattern is in regpattern, 
X		 * so just use it.
X		 */
X		if (regpattern == NULL)
X		{
X			error("No previous regular expression", NULL_PARG);
X			return (-1);
X		}
X	} else
X	{
X		/*
X		 * Otherwise compile the given pattern.
X		 */
X		struct regexp *s;
X		if ((s = regcomp(pattern)) == NULL)
X		{
X			error("Invalid pattern", NULL_PARG);
X			return (-1);
X		}
X		if (regpattern != NULL)
X			free(regpattern);
X		regpattern = s;
X	}
X#else
X	if (pattern == NULL || *pattern == '\0')
X	{
X		/*
X		 * Null pattern means use the previous pattern.
X		 */
X		if (last_pattern == NULL)
X		{
X			error("No previous regular expression", NULL_PARG);
X			return (-1);
X		}
X		pattern = last_pattern;
X	} else
X	{
X		strcpy(lpbuf, pattern);
X		last_pattern = lpbuf;
X	}
X#endif
X#endif
X#endif
X
X	/*
X	 * Figure out where to start the search.
X	 */
X	if (empty_screen())
X	{
X		/*
X		 * Start at the beginning (or end) of the file.
X		 * (The empty_screen() case is mainly for 
X		 * command line initiated searches;
X		 * for example, "+/xyz" on the command line.)
X		 */
X		if (goforw)
X			pos = ch_zero();
X		else 
X		{
X			pos = ch_length();
X			if (pos == NULL_POSITION)
X				pos = ch_zero();
X		}
X	} else 
X	{
X		if (how_search)
X		{
X			if (goforw)
X				linenum = BOTTOM_PLUS_ONE;
X			else
X				linenum = TOP;
X			pos = position(linenum);
X		} else
X		{
X			linenum = adjsline(jump_sline);
X			pos = position(linenum);
X			if (goforw)
X				pos = forw_raw_line(pos, (char **)NULL);
X		}
X	}
X
X	if (pos == NULL_POSITION)
X	{
X		/*
X		 * Can't find anyplace to start searching from.
X		 */
X		error("Nothing to search", NULL_PARG);
X		return (-1);
X	}
X
X	linenum = find_linenum(pos);
X	for (;;)
X	{
X		/*
X		 * Get lines until we find a matching one or 
X		 * until we hit end-of-file (or beginning-of-file 
X		 * if we're going backwards).
X		 */
X		if (sigs)
X			/*
X			 * A signal aborts the search.
X			 */
X			return (-1);
X
X		if (goforw)
X		{
X			/*
X			 * Read the next line, and save the 
X			 * starting position of that line in linepos.
X			 */
X			linepos = pos;
X			pos = forw_raw_line(pos, &line);
X			if (linenum != 0)
X				linenum++;
X		} else
X		{
X			/*
X			 * Read the previous line and save the
X			 * starting position of that line in linepos.
X			 */
X			pos = back_raw_line(pos, &line);
X			linepos = pos;
X			if (linenum != 0)
X				linenum--;
X		}
X
X		if (pos == NULL_POSITION)
X		{
X			/*
X			 * We hit EOF/BOF without a match.
X			 */
X			return (n);
X		}
X
X		/*
X		 * If we're using line numbers, we might as well
X		 * remember the information we have now (the position
X		 * and line number of the current line).
X		 */
X		if (linenums)
X			add_lnum(linenum, pos);
X
X		if (is_caseless)
X		{
X			/*
X			 * If this is a caseless search, convert 
X			 * uppercase in the input line to lowercase.
X			 * While we're at it, remove any backspaces
X			 * along with the preceding char.
X			 * This allows us to match text which is 
X			 * underlined or overstruck.
X			 */
X			for (p = q = line;  *p != '\0';  p++, q++)
X			{
X				if (*p >= 'A' && *p <= 'Z')
X					/* Convert uppercase to lowercase. */
X					*q = *p + 'a' - 'A';
X				else if (q > line && *p == '\b')
X					/* Delete BS and preceding char. */
X					q -= 2;
X				else
X					/* Otherwise, just copy. */
X					*q = *p;
X			}
X		}
X
X		/*
X		 * Test the next line to see if we have a match.
X		 * This is done in a variety of ways, depending
X		 * on what pattern matching functions are available.
X		 */
X#if REGCMP
X		line_match = (regex(cpattern, line) != NULL);
X#else
X#if RECOMP
X		line_match = (re_exec(line) == 1);
X#else
X#if REGCOMP
X		linematch = regexec(regpattern, line);
X#else
X		line_match = match(pattern, line);
X#endif
X#endif
X#endif
X		/*
X		 * We are successful if want_match and line_match are
X		 * both true (want a match and got it),
X		 * or both false (want a non-match and got it).
X		 */
X		if (((want_match && line_match) || (!want_match && !line_match)) &&
X		      --n <= 0)
X			/*
X			 * Found the line.
X			 */
X			break;
X	}
X
X	jump_loc(linepos, jump_sline);
X	return (0);
X}
X
X#if (!REGCMP) && (!RECOMP) && (!REGCOMP)
X/*
X * We have neither regcmp() nor re_comp().
X * We use this function to do simple pattern matching.
X * It supports no metacharacters like *, etc.
X */
X	static int
Xmatch(pattern, buf)
X	char *pattern, *buf;
X{
X	register char *pp, *lp;
X
X	for ( ;  *buf != '\0';  buf++)
X	{
X		for (pp = pattern, lp = buf;  *pp == *lp;  pp++, lp++)
X			if (*pp == '\0' || *lp == '\0')
X				break;
X		if (*pp == '\0')
X			return (1);
X	}
X	return (0);
X}
X#endif
END_OF_FILE
echo shar: Extracting \"less.hlp\"
sed "s/^X//" >'less.hlp' <<'END_OF_FILE'
X
X                     SUMMARY OF COMMANDS
X
X      Commands marked with * may be preceded by a number, N.
X      Notes in parentheses indicate the behavior if N is given.
X
X  h  H                 Display this help.
X  q  :q  :Q  ZZ        Exit.
X
X  e  ^E  j  ^N  CR  *  Forward  one line (or N lines).
X  y  ^Y  k  ^K  ^P  *  Backward one line (or N lines).
X  f  ^F  ^V  SPACE  *  Forward  one window (or N lines).
X  b  ^B  ESC-v      *  Backward one window (or N lines).
X  z                 *  Forward  one window (and set window to N).
X  w                 *  Backward one window (and set window to N).
X  d  ^D             *  Forward  one half-window (and set half-window to N).
X  u  ^U             *  Backward one half-window (and set half-window to N).
X  F                    Forward forever; like "tail -f".
X  r  ^R  ^L            Repaint screen.
X  R                    Repaint screen, discarding buffered input.
X
X  NOTE: default "window" is the screen height.
X        default "half-window" is half of the screen height.
X
X  /pattern          *  Search forward for (N-th) matching line.
X  ?pattern          *  Search backward for (N-th) matching line.
X  ESC-/pattern      *  Search all files for (N-th) matching line.
X
X  /!pattern         *  Search forward for (N-th) NON-matching line.
X  ?!pattern         *  Search backward for (N-th) NON-matching line.
X  ESC-/!pattern     *  Search from all files for (N-th) NON-matching line.
X
X  n                 *  Repeat previous search (for N-th occurrence).
X  N                 *  Repeat previous search in reverse direction.
X  ESC-n             *  Repeat previous search, spanning files.
X  ESC-N             *  Repeat previous search, reverse dir. & spanning files.
X
X  g  <  ESC-<       *  Go to first line in file (or line N).
X  G  >  ESC->       *  Go to last line in file (or line N).
X  p  %              *  Go to beginning of file (or N percent into file).
X  {                 *  Go to the } which matches the (N-th) { in the top line.
X  }                 *  Go to the { which matches the (N-th) } in the top line.
X  (                 *  Go to the ) which matches the (N-th) ( in the top line.
X  )                 *  Go to the ( which matches the (N-th) ) in the top line.
X  [                 *  Go to the ] which matches the (N-th) [ in the top line.
X  ]                 *  Go to the [ which matches the (N-th) ] in the top line.
X  m<letter>            Mark the current position with <letter>.
X  '<letter>            Go to a previously marked position.
X  ''                   Go to the previous position.
X  ^X^X                 Same as '.
X
X  E [file]             Examine a new file.
X  :e  ^X^V             Same as E.
X  :n                *  Examine the (N-th) next file from the command line.
X  :p                *  Examine the (N-th) previous file from the command line.
X  =  ^G  :f            Print current file name.
X  V                    Print version number of "less".
X
X  -<flag>              Toggle a command line flag [see FLAGS below].
X  _<flag>              Display the setting of a command line flag.
X  +cmd                 Execute the less cmd each time a new file is examined.
X
X  !command             Passes the command to $SHELL to be executed.
X  |Xcommand            Pipe file between current pos & mark X to shell command.
X  v                    Edit the current file with $EDITOR.
X
X
X                         FLAGS
X
X        Most flags may be changed either on the command line,
X        or from within less by using the - command.
X
X  -a            Set forward search starting location.
X  -b [N]        Number of buffers.
X  -B            Automatically allocate buffers.
X  -c  -C        Repaint by scrolling/clearing.
X  -d            Dumb terminal.
X  -e  -E        Quit at end of file.
X  -f            Force open non-regular files.
X  -h [N]        Backward scroll limit.
X  -i            Ignore case in searches.
X  -j [N]        Screen position of target lines.
X  -k [file]     Use a lesskey file.
X  -l [file]     Log file.
X  -L [file]     Log file (unconditionally overwrite).
X  -m  -M        Set prompt style.
X  -n  -N        Use line numbers.
X  -P [prompt]   Define new prompt.
X  -q  -Q        Quiet the terminal bell.
X  -r  -R        Translate control characters.
X  -s            Squeeze multiple blank lines.
X  -t [tag]      Find a tag.
X  -T [tagsfile] Use an alternate tags file.
X  -u  -U        Change handling of backspaces.
X  -w            Display ~ for lines after end-of-file.
X  -x [N]        Set tab stops.
X  -y [N]        Forward scroll limit.
X  -z [N]        Set size of window.
X
END_OF_FILE

mark@unix386.Convergent.COM (Mark Nudelman) (03/06/91)

#! /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 \"mark.c\"
sed "s/^X//" >'mark.c' <<'END_OF_FILE'
X#include "less.h"
X#include "position.h"
X
Xextern IFILE curr_ifile;
Xextern int sc_height;
Xextern int jump_sline;
X
X/*
X * A mark is an ifile (input file) plus a position within the file.
X */
Xstruct mark {
X	IFILE m_ifile;
X	struct scrpos m_scrpos;
X};
X
X/*
X * The table of marks.
X * Each mark is identified by a lowercase or uppercase letter.
X */
X#define	NMARKS		(2*26)		/* a-z, A-Z */
Xstatic struct mark marks[NMARKS];
X
X/*
X * Special mark for the "last mark"; addressed by the apostrophe.
X */
Xstatic struct mark lmark;
X
X/*
X * Initialize the mark table to show no marks are set.
X */
X	public void
Xinit_mark()
X{
X	int i;
X
X	for (i = 0;  i < NMARKS;  i++)
X		marks[i].m_scrpos.pos = NULL_POSITION;
X	lmark.m_scrpos.pos = NULL_POSITION;
X}
X
X/*
X * See if a mark letter is valid (between a and z).
X */
X	static struct mark *
Xgetumark(c)
X	int c;
X{
X	if (c >= 'a' && c <= 'z')
X		return (&marks[c-'a']);
X
X	if (c >= 'A' && c <= 'Z')
X		return (&marks[c-'A'+26]);
X
X	error("Invalid mark letter", NULL_PARG);
X	return (NULL);
X}
X
X/*
X * Get the mark structure identified by a character.
X * The mark struct may come either from the mark table
X * or may be constructed on the fly for certain characters like ^, $.
X */
X	static struct mark *
Xgetmark(c)
X	int c;
X{
X	register struct mark *m;
X	static struct mark sm;
X
X	switch (c)
X	{
X	case '^':
X		/*
X		 * Beginning of the current file.
X		 */
X		m = &sm;
X		m->m_scrpos.pos = ch_zero();
X		m->m_scrpos.ln = 0;
X		m->m_ifile = curr_ifile;
X		break;
X	case '$':
X		/*
X		 * End of the current file.
X		 */
X		if (ch_end_seek())
X		{
X			error("Cannot seek to end of file", NULL_PARG);
X			return (NULL);
X		}
X		m = &sm;
X		m->m_scrpos.pos = ch_tell();
X		m->m_scrpos.ln = sc_height-1;
X		m->m_ifile = curr_ifile;
X		break;
X	case '.':
X		/*
X		 * Current position in the current file.
X		 */
X		m = &sm;
X		m->m_scrpos.pos = ch_tell();
X		m->m_scrpos.ln = 0;
X		m->m_ifile = curr_ifile;
X		break;
X	case '\'':
X		/*
X		 * The "last mark".
X		 */
X		m = &lmark;
X		break;
X	default:
X		/*
X		 * Must be a user-defined mark.
X		 */
X		m = getumark(c);
X		if (m == NULL)
X			break;
X		if (m->m_scrpos.pos == NULL_POSITION)
X		{
X			error("Mark not set", NULL_PARG);
X			return (NULL);
X		}
X		break;
X	}
X	return (m);
X}
X
X/*
X * Is a mark letter is invalid?
X */
X	public int
Xbadmark(c)
X	int c;
X{
X	return (getmark(c) == NULL);
X}
X
X/*
X * Set a user-defined mark.
X */
X	public void
Xsetmark(c)
X	int c;
X{
X	register struct mark *m;
X	struct scrpos scrpos;
X
X	m = getumark(c);
X	if (m == NULL)
X		return;
X	get_scrpos(&scrpos);
X	m->m_scrpos = scrpos;
X	m->m_ifile = curr_ifile;
X}
X
X/*
X * Set lmark (the mark named by the apostrophe).
X */
X	public void
Xlastmark()
X{
X	struct scrpos scrpos;
X
X	get_scrpos(&scrpos);
X	if (scrpos.pos == NULL_POSITION)
X		return;
X	lmark.m_scrpos = scrpos;
X	lmark.m_ifile = curr_ifile;
X}
X
X/*
X * Go to a mark.
X */
X	public void
Xgomark(c)
X	int c;
X{
X	register struct mark *m;
X	struct scrpos scrpos;
X
X	m = getmark(c);
X	if (m == NULL)
X		return;
X
X	/*
X	 * If we're trying to go to the lastmark and 
X	 * it has not been set to anything yet,
X	 * set it to the beginning of the current file.
X	 */
X	if (m == &lmark && m->m_scrpos.pos == NULL_POSITION)
X	{
X		m->m_ifile = curr_ifile;
X		m->m_scrpos.pos = ch_zero();
X		m->m_scrpos.ln = jump_sline;
X	}
X
X	/*
X	 * If we're using lmark, we must save the screen position now,
X	 * because if we call edit() below, lmark will change.
X	 * (We save the screen position even if we're not using lmark.)
X	 */
X	scrpos = m->m_scrpos;
X	if (m->m_ifile != curr_ifile)
X	{
X		/*
X		 * Not in the current file; edit the correct file.
X		 */
X		if (edit(get_filename(m->m_ifile), 0))
X			return;
X	}
X
X	jump_loc(scrpos.pos, scrpos.ln);
X}
X
X/*
X * Return the position associated with a given mark letter.
X *
X * We don't return which screen line the position 
X * is associated with, but this doesn't matter much,
X * because it's always the first non-blank line on the screen.
X */
X	public POSITION
Xmarkpos(c)
X	int c;
X{
X	register struct mark *m;
X
X	m = getmark(c);
X	if (m == NULL)
X		return (NULL_POSITION);
X
X	if (m->m_ifile != curr_ifile)
X	{
X		error("Mark not in current file", NULL_PARG);
X		return (NULL_POSITION);
X	}
X	return (m->m_scrpos.pos);
X}
END_OF_FILE
echo shar: Extracting \"prompt.c\"
sed "s/^X//" >'prompt.c' <<'END_OF_FILE'
X/*
X * Prompting and other messages.
X * There are three flavors of prompts, SHORT, MEDIUM and LONG,
X * selected by the -m/-M options.
X * There is also the "equals message", printed by the = command.
X * A prompt is a message composed of various pieces, such as the 
X * name of the file being viewed, the percentage into the file, etc.
X */
X
X#include "less.h"
X#include "position.h"
X
Xextern int pr_type;
Xextern int hit_eof;
Xextern int new_file;
Xextern int sc_width;
Xextern int so_s_width, so_e_width;
Xextern int linenums;
Xextern int sc_height;
Xextern int jump_sline;
Xextern IFILE curr_ifile;
X#if EDITOR
Xextern char *editor;
X#endif
X
X/*
X * Prototypes for the three flavors of prompts.
X * These strings are expanded by pr_expand().
X */
Xstatic char s_proto[] =
X  "?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\\: %x..%t";
Xstatic char m_proto[] =
X  "?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t";
Xstatic char M_proto[] =
X  "?f%f .?n?m(file %i of %m) ..?ltline %lt?L/%L. :byte %bB?s/%s. .?e(END) ?x- Next\\: %x.:?pB%pB\\%..%t";
Xstatic char e_proto[] =
X  "?f%f .?m(file %i of %m) .?ltline %lt?L/%L. .byte %bB?s/%s. ?e(END) :?pB%pB\\%..%t";
X
Xpublic char *prproto[3];
Xpublic char *eqproto = e_proto;
X
Xstatic char message[250];
Xstatic char *mp;
X
X/*
X * Initialize the prompt prototype strings.
X */
X	public void
Xinit_prompt()
X{
X	prproto[0] = save(s_proto);
X	prproto[1] = save(m_proto);
X	prproto[2] = save(M_proto);
X	eqproto = save(e_proto);
X}
X
X/*
X * Set the message pointer to the end of the message string.
X */
X	static void
Xsetmp()
X{
X	while (*mp != '\0')
X		mp++;
X}
X
X/*
X * Append a POSITION (as a decimal integer) to the end of the message.
X */
X	static void
Xap_pos(pos)
X	POSITION pos;
X{
X	sprintf(mp, "%ld", (long)pos);
X	setmp();
X}
X
X/*
X * Append an integer to the end of the message.
X */
X	static void
Xap_int(n)
X	int n;
X{
X	sprintf(mp, "%d", n);
X	setmp();
X}
X
X/*
X * Append a string to the end of the message.
X */
X	static void
Xap_str(s)
X	char *s;
X{
X	strtcpy(mp, s, (unsigned int)(&message[sizeof(message)] - mp));
X	setmp();
X}
X
X/*
X * Append a question mark to the end of the message.
X */
X	static void
Xap_quest()
X{
X	*mp++ = '?';
X}
X
X/*
X * Return the "current" byte offset in the file.
X */
X	static POSITION
Xcurr_byte(where)
X	int where;
X{
X	POSITION pos;
X
X	pos = position(where);
X	while (pos == NULL_POSITION && where >= 0 && where < sc_height)
X		pos = position(++where);
X	if (pos == NULL_POSITION)
X		pos = ch_length();
X	return (pos);
X}
X
X/*
X * Return the value of a prototype conditional.
X * A prototype string may include conditionals which consist of a 
X * question mark followed by a single letter.
X * Here we decode that letter and return the appropriate boolean value.
X */
X	static int
Xcond(c, where)
X	char c;
X	int where;
X{
X	switch (c)
X	{
X	case 'a':	/* Anything in the message yet? */
X		return (mp > message);
X	case 'b':	/* Current byte offset known? */
X		return (curr_byte(where) != NULL_POSITION);
X	case 'e':	/* At end of file? */
X		return (hit_eof);
X	case 'f':	/* Filename known? */
X		return (strcmp(get_filename(curr_ifile), "-") != 0);
X	case 'l':	/* Line number known? */
X		return (linenums);
X	case 'L':	/* Final line number known? */
X		return (linenums && ch_length() != NULL_POSITION);
X	case 'm':	/* More than one file? */
X		return (nifile() > 1);
X	case 'n':	/* First prompt in a new file? */
X		return (new_file);
X	case 'p':	/* Percent into file known? */
X		return (curr_byte(where) != NULL_POSITION && 
X				ch_length() > 0);
X	case 's':	/* Size of file known? */
X	case 'B':
X		return (ch_length() != NULL_POSITION);
X	case 'x':	/* Is there a "next" file? */
X		return (next_ifile(curr_ifile) != NULL_IFILE);
X	}
X	return (0);
X}
X
X/*
X * Decode a "percent" prototype character.
X * A prototype string may include various "percent" escapes;
X * that is, a percent sign followed by a single letter.
X * Here we decode that letter and take the appropriate action,
X * usually by appending something to the message being built.
X */
X	static void
Xprotochar(c, where)
X	int c;
X	int where;
X{
X	POSITION pos;
X	POSITION len;
X	int n;
X	IFILE h;
X
X	switch (c)
X	{
X	case 'b':	/* Current byte offset */
X		pos = curr_byte(where);
X		if (pos != NULL_POSITION)
X			ap_pos(pos);
X		else
X			ap_quest();
X		break;
X#if EDITOR
X	case 'E':	/* Editor name */
X		ap_str(editor);
X		break;
X#endif
X	case 'f':	/* File name */
X		ap_str(get_filename(curr_ifile));
X		break;
X	case 'i':	/* Index into list of files */
X		ap_int(get_index(curr_ifile));
X		break;
X	case 'l':	/* Current line number */
X		n = currline(where);
X		if (n != 0)
X			ap_int(n);
X		else
X			ap_quest();
X		break;
X	case 'L':	/* Final line number */
X		len = ch_length();
X		if (len == NULL_POSITION || len == ch_zero() ||
X		    (n = find_linenum(len)) <= 0)
X			ap_quest();
X		else
X			ap_int(n-1);
X		break;
X	case 'm':	/* Number of files */
X		ap_int(nifile());
X		break;
X	case 'p':	/* Percent into file */
X		pos = curr_byte(where);
X		len = ch_length();
X		if (pos != NULL_POSITION && len > 0)
X			/*
X			 * {{ This calculation may overflow! }}
X			 */
X			ap_int((int)(100*pos / len));
X		else
X			ap_quest();
X		break;
X	case 's':	/* Size of file */
X	case 'B':
X		len = ch_length();
X		if (len != NULL_POSITION)
X			ap_pos(len);
X		else
X			ap_quest();
X		break;
X	case 't':	/* Truncate trailing spaces in the message */
X		while (mp > message && mp[-1] == ' ')
X			mp--;
X		break;
X	case 'x':	/* Name of next file */
X		h = next_ifile(curr_ifile);
X		if (h != NULL_IFILE)
X			ap_str(get_filename(h));
X		else
X			ap_quest();
X		break;
X	}
X}
X
X/*
X * Skip a false conditional.
X * When a false condition is found (either a false IF or the ELSE part 
X * of a true IF), this routine scans the prototype string to decide
X * where to resume parsing the string.
X * We must keep track of nested IFs and skip them properly.
X */
X	static char *
Xskipcond(p)
X	register char *p;
X{
X	register int iflevel;
X
X	/*
X	 * We came in here after processing a ? or :,
X	 * so we start nested one level deep.
X	 */
X	iflevel = 1;
X
X	for (;;) switch (*++p)
X	{
X	case '?':
X		/*
X		 * Start of a nested IF.
X		 */
X		iflevel++;
X		break;
X	case ':':
X		/*
X		 * Else.
X		 * If this matches the IF we came in here with,
X		 * then we're done.
X		 */
X		if (iflevel == 1)
X			return (p);
X		break;
X	case '.':
X		/*
X		 * Endif.
X		 * If this matches the IF we came in here with,
X		 * then we're done.
X		 */
X		if (--iflevel == 0)
X			return (p);
X		break;
X	case '\\':
X		/*
X		 * Backslash escapes the next character.
X		 */
X		++p;
X		break;
X	case '\0':
X		/*
X		 * Whoops.  Hit end of string.
X		 * This is a malformed conditional, but just treat it
X		 * as if all active conditionals ends here.
X		 */
X		return (p-1);
X	}
X	/*NOTREACHED*/
X}
X
X	static char *
Xwherechar(p, wp)
X	char *p;
X	int *wp;
X{
X	switch (*p)
X	{
X	case 'b': case 'l': case 'p':
X		switch (*++p)
X		{
X		case 't':   *wp = TOP;			break;
X		case 'm':   *wp = MIDDLE;		break;
X		case 'b':   *wp = BOTTOM;		break;
X		case 'B':   *wp = BOTTOM_PLUS_ONE;	break;
X		case 'j':   *wp = adjsline(jump_sline);	break;
X		default:    *wp = TOP;  p--;		break;
X		}
X	}
X	return (p);
X}
X
X/*
X * Construct a message based on a prototype string.
X */
X	public char *
Xpr_expand(proto, maxwidth)
X	char *proto;
X	int maxwidth;
X{
X	register char *p;
X	register int c;
X	int where;
X
X	mp = message;
X
X	if (*proto == '\0')
X		return ("");
X
X	for (p = proto;  *p != '\0';  p++)
X	{
X		switch (*p)
X		{
X		default:	/* Just put the character in the message */
X			*mp++ = *p;
X			break;
X		case '\\':	/* Backslash escapes the next character */
X			p++;
X			*mp++ = *p;
X			break;
X		case '?':	/* Conditional (IF) */
X			if ((c = *++p) == '\0')
X				--p;
X			else
X			{
X				p = wherechar(p, &where);
X				if (!cond(c, where))
X					p = skipcond(p);
X			}
X			break;
X		case ':':	/* ELSE */
X			p = skipcond(p);
X			break;
X		case '.':	/* ENDIF */
X			break;
X		case '%':	/* Percent escape */
X			if ((c = *++p) == '\0')
X				--p;
X			else
X			{
X				p = wherechar(p, &where);
X				protochar(c, where);
X			}
X			break;
X		}
X	}
X
X	new_file = 0;
X	if (mp == message)
X		return (NULL);
X	*mp = '\0';
X	if (maxwidth > 0 && mp >= message + maxwidth)
X	{
X		/*
X		 * Message is too long.
X		 * Return just the final portion of it.
X		 */
X		return (mp - maxwidth);
X	}
X	return (message);
X}
X
X/*
X * Return a message suitable for printing by the "=" command.
X */
X	public char *
Xeq_message()
X{
X	return (pr_expand(eqproto, 0));
X}
X
X/*
X * Return a prompt.
X * This depends on the prompt type (SHORT, MEDIUM, LONG), etc.
X * If we can't come up with an appropriate prompt, return NULL
X * and the caller will prompt with a colon.
X */
X	public char *
Xpr_string()
X{
X	return (pr_expand(prproto[pr_type], sc_width-so_s_width-so_e_width-2));
X}
END_OF_FILE
echo shar: Extracting \"screen.c\"
sed "s/^X//" >'screen.c' <<'END_OF_FILE'
X/*
X * Routines which deal with the characteristics of the terminal.
X * Uses termcap to be as terminal-independent as possible.
X *
X * {{ Someday this should be rewritten to use curses. }}
X */
X
X#include "less.h"
X#if XENIX
X#include <sys/types.h>
X#include <sys/ioctl.h>
X#endif
X
X#if TERMIO
X#include <termio.h>
X#else
X#include <sgtty.h>
X#endif
X
X#if !TERMIO && defined(TIOCGWINSZ)
X#include <sys/ioctl.h>
X#else
X/*
X * For the Unix PC (ATT 7300 & 3B1):
X * Since WIOCGETD is defined in sys/window.h, we can't use that to decide
X * whether to include sys/window.h.  Use SIGPHONE from signal.h instead.
X */
X#include <signal.h>
X#ifdef SIGPHONE
X#include <sys/window.h>
X#endif
X#endif
X
X/*
X * Strings passed to tputs() to do various terminal functions.
X */
Xstatic char
X	*sc_pad,		/* Pad string */
X	*sc_home,		/* Cursor home */
X	*sc_addline,		/* Add line, scroll down following lines */
X	*sc_lower_left,		/* Cursor to last line, first column */
X	*sc_move,		/* General cursor positioning */
X	*sc_clear,		/* Clear screen */
X	*sc_eol_clear,		/* Clear to end of line */
X	*sc_s_in,		/* Enter standout (highlighted) mode */
X	*sc_s_out,		/* Exit standout mode */
X	*sc_u_in,		/* Enter underline mode */
X	*sc_u_out,		/* Exit underline mode */
X	*sc_b_in,		/* Enter bold mode */
X	*sc_b_out,		/* Exit bold mode */
X	*sc_bl_in,		/* Enter blink mode */
X	*sc_bl_out,		/* Exit blink mode */
X	*sc_visual_bell,	/* Visual bell (flash screen) sequence */
X	*sc_backspace,		/* Backspace cursor */
X	*sc_init,		/* Startup terminal initialization */
X	*sc_deinit;		/* Exit terminal de-initialization */
X
Xstatic int init_done = 0;
X
Xpublic int auto_wrap;		/* Terminal does \r\n when write past margin */
Xpublic int ignaw;		/* Terminal ignores \n immediately after wrap */
Xpublic int erase_char, kill_char; /* The user's erase and line-kill chars */
Xpublic int sc_width, sc_height;	/* Height & width of screen */
Xpublic int bo_s_width, bo_e_width;	/* Printing width of boldface seq */
Xpublic int ul_s_width, ul_e_width;	/* Printing width of underline seq */
Xpublic int so_s_width, so_e_width;	/* Printing width of standout seq */
Xpublic int bl_s_width, bl_e_width;	/* Printing width of blink seq */
X
Xstatic char *cheaper();
X
X/*
X * These two variables are sometimes defined in,
X * and needed by, the termcap library.
X * It may be necessary on some systems to declare them extern here.
X */
X/*extern*/ short ospeed;	/* Terminal output baud rate */
X/*extern*/ char PC;		/* Pad character */
X
Xextern int quiet;		/* If VERY_QUIET, use visual bell for bell */
Xextern int know_dumb;		/* Don't complain about a dumb terminal */
Xextern int back_scroll;
Xextern int swindow;
Xextern char *tgetstr();
Xextern char *tgoto();
Xextern char *getenv();
X
X
X/*
X * Change terminal to "raw mode", or restore to "normal" mode.
X * "Raw mode" means 
X *	1. An outstanding read will complete on receipt of a single keystroke.
X *	2. Input is not echoed.  
X *	3. On output, \n is mapped to \r\n.
X *	4. \t is NOT expanded into spaces.
X *	5. Signal-causing characters such as ctrl-C (interrupt),
X *	   etc. are NOT disabled.
X * It doesn't matter whether an input \n is mapped to \r, or vice versa.
X */
X	public void
Xraw_mode(on)
X	int on;
X{
X	static int curr_on = 0;
X
X	if (on == curr_on)
X		return;
X#if TERMIO
X    {
X	struct termio s;
X	static struct termio save_term;
X
X	if (on)
X	{
X		/*
X		 * Get terminal modes.
X		 */
X		ioctl(2, TCGETA, &s);
X
X		/*
X		 * Save modes and set certain variables dependent on modes.
X		 */
X		save_term = s;
X		ospeed = s.c_cflag & CBAUD;
X		erase_char = s.c_cc[VERASE];
X		kill_char = s.c_cc[VKILL];
X
X		/*
X		 * Set the modes to the way we want them.
X		 */
X		s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
X		s.c_oflag |=  (OPOST|ONLCR|TAB3);
X		s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
X		s.c_cc[VMIN] = 1;
X		s.c_cc[VTIME] = 0;
X	} else
X	{
X		/*
X		 * Restore saved modes.
X		 */
X		s = save_term;
X	}
X	ioctl(2, TCSETAW, &s);
X    }
X#else
X    {
X	struct sgttyb s;
X	static struct sgttyb save_term;
X
X	if (on)
X	{
X		/*
X		 * Get terminal modes.
X		 */
X		ioctl(2, TIOCGETP, &s);
X
X		/*
X		 * Save modes and set certain variables dependent on modes.
X		 */
X		save_term = s;
X		ospeed = s.sg_ospeed;
X		erase_char = s.sg_erase;
X		kill_char = s.sg_kill;
X
X		/*
X		 * Set the modes to the way we want them.
X		 */
X		s.sg_flags |= CBREAK;
X		s.sg_flags &= ~(ECHO|XTABS);
X	} else
X	{
X		/*
X		 * Restore saved modes.
X		 */
X		s = save_term;
X	}
X	ioctl(2, TIOCSETN, &s);
X    }
X#endif
X	curr_on = on;
X}
X
X	static void
Xcannot(s)
X	char *s;
X{
X	PARG parg;
X
X	if (know_dumb)
X		/* 
X		 * User knows this is a dumb terminal, so don't tell him.
X		 */
X		return;
X
X	parg.p_string = s;
X	error("WARNING: terminal cannot %s", &parg);
X}
X
X/*
X * Get size of the output screen.
X */
X	public void
Xget_scrsize(p_height, p_width)
X	int *p_height;
X	int *p_width;
X{
X	register char *s;
X#ifdef TIOCGWINSZ
X	struct winsize w;
X#else
X#ifdef WIOCGETD
X	struct uwdata w;
X#endif
X#endif
X
X#ifdef TIOCGWINSZ
X	if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_row > 0)
X		*p_height = w.ws_row;
X	else
X#else
X#ifdef WIOCGETD
X	if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_height > 0)
X		*p_height = w.uw_height/w.uw_vs;
X	else
X#endif
X#endif
X	if ((s = getenv("LINES")) != NULL)
X		*p_height = atoi(s);
X	else
X 		*p_height = tgetnum("li");
X
X	if (*p_height <= 0)
X		*p_height = 24;
X
X#ifdef TIOCGWINSZ
X 	if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_col > 0)
X		*p_width = w.ws_col;
X	else
X#ifdef WIOCGETD
X	if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_width > 0)
X		*p_width = w.uw_width/w.uw_hs;
X	else
X#endif
X#endif
X	if ((s = getenv("COLUMNS")) != NULL)
X		*p_width = atoi(s);
X	else
X 		*p_width = tgetnum("co");
X
X 	if (*p_width <= 0)
X  		*p_width = 80;
X}
X
X/*
X * Get terminal capabilities via termcap.
X */
X	public void
Xget_term()
X{
X	char *sp;
X	register char *t1, *t2;
X	register int hard;
X	char *term;
X	char termbuf[2048];
X
X	static char sbuf[1024];
X
X	/*
X	 * Find out what kind of terminal this is.
X	 */
X 	if ((term = getenv("TERM")) == NULL)
X 		term = "unknown";
X 	if (tgetent(termbuf, term) <= 0)
X 		strcpy(termbuf, "dumb:hc:");
X
X 	hard = tgetflag("hc");
X
X	/*
X	 * Get size of the screen.
X	 */
X	get_scrsize(&sc_height, &sc_width);
X	pos_init();
X	if (swindow < 0)
X		swindow = sc_height - 1;
X
X	auto_wrap = tgetflag("am");
X	ignaw = tgetflag("xn");
X
X	/*
X	 * Assumes termcap variable "sg" is the printing width of:
X	 * the standout sequence, the end standout sequence,
X	 * the underline sequence, the end underline sequence,
X	 * the boldface sequence, and the end boldface sequence.
X	 */
X	if ((so_s_width = tgetnum("sg")) < 0)
X		so_s_width = 0;
X	so_e_width = so_s_width;
X
X	bo_s_width = bo_e_width = so_s_width;
X	ul_s_width = ul_e_width = so_s_width;
X	bl_s_width = bl_e_width = so_s_width;
X
X	/*
X	 * Get various string-valued capabilities.
X	 */
X	sp = sbuf;
X
X	sc_pad = tgetstr("pc", &sp);
X	if (sc_pad != NULL)
X		PC = *sc_pad;
X
X	sc_init = tgetstr("ti", &sp);
X	if (sc_init == NULL)
X		sc_init = "";
X
X	sc_deinit= tgetstr("te", &sp);
X	if (sc_deinit == NULL)
X		sc_deinit = "";
X
X	sc_eol_clear = tgetstr("ce", &sp);
X	if (hard || sc_eol_clear == NULL || *sc_eol_clear == '\0')
X	{
X		cannot("clear to end of line");
X		sc_eol_clear = "";
X	}
X
X	sc_clear = tgetstr("cl", &sp);
X	if (hard || sc_clear == NULL || *sc_clear == '\0')
X	{
X		cannot("clear screen");
X		sc_clear = "\n\n";
X	}
X
X	sc_move = tgetstr("cm", &sp);
X	if (hard || sc_move == NULL || *sc_move == '\0')
X	{
X		/*
X		 * This is not an error here, because we don't 
X		 * always need sc_move.
X		 * We need it only if we don't have home or lower-left.
X		 */
X		sc_move = "";
X	}
X
X	sc_s_in = tgetstr("so", &sp);
X	if (hard || sc_s_in == NULL)
X		sc_s_in = "";
X
X	sc_s_out = tgetstr("se", &sp);
X	if (hard || sc_s_out == NULL)
X		sc_s_out = "";
X
X	sc_u_in = tgetstr("us", &sp);
X	if (hard || sc_u_in == NULL)
X		sc_u_in = sc_s_in;
X
X	sc_u_out = tgetstr("ue", &sp);
X	if (hard || sc_u_out == NULL)
X		sc_u_out = sc_s_out;
X
X	sc_b_in = tgetstr("md", &sp);
X	if (hard || sc_b_in == NULL)
X	{
X		sc_b_in = sc_s_in;
X		sc_b_out = sc_s_out;
X	} else
X	{
X		sc_b_out = tgetstr("me", &sp);
X		if (hard || sc_b_out == NULL)
X			sc_b_out = "";
X	}
X
X	sc_bl_in = tgetstr("mb", &sp);
X	if (hard || sc_bl_in == NULL)
X	{
X		sc_bl_in = sc_s_in;
X		sc_bl_out = sc_s_out;
X	} else
X	{
X		sc_bl_out = sc_b_out;
X	}
X
X	sc_visual_bell = tgetstr("vb", &sp);
X	if (hard || sc_visual_bell == NULL)
X		sc_visual_bell = "";
X
X	if (tgetflag("bs"))
X		sc_backspace = "\b";
X	else
X	{
X		sc_backspace = tgetstr("bc", &sp);
X		if (sc_backspace == NULL || *sc_backspace == '\0')
X			sc_backspace = "\b";
X	}
X
X	/*
X	 * Choose between using "ho" and "cm" ("home" and "cursor move")
X	 * to move the cursor to the upper left corner of the screen.
X	 */
X	t1 = tgetstr("ho", &sp);
X	if (hard || t1 == NULL)
X		t1 = "";
X	if (*sc_move == '\0')
X		t2 = "";
X	else
X	{
X		strcpy(sp, tgoto(sc_move, 0, 0));
X		t2 = sp;
X		sp += strlen(sp) + 1;
X	}
X	sc_home = cheaper(t1, t2, "home cursor", "|\b^");
X
X	/*
X	 * Choose between using "ll" and "cm"  ("lower left" and "cursor move")
X	 * to move the cursor to the lower left corner of the screen.
X	 */
X	t1 = tgetstr("ll", &sp);
X	if (hard || t1 == NULL)
X		t1 = "";
X	if (*sc_move == '\0')
X		t2 = "";
X	else
X	{
X		strcpy(sp, tgoto(sc_move, 0, sc_height-1));
X		t2 = sp;
X		sp += strlen(sp) + 1;
X	}
X	sc_lower_left = cheaper(t1, t2,
X		"move cursor to lower left of screen", "\r");
X
X	/*
X	 * Choose between using "al" or "sr" ("add line" or "scroll reverse")
X	 * to add a line at the top of the screen.
X	 */
X	t1 = tgetstr("al", &sp);
X	if (hard || t1 == NULL)
X		t1 = "";
X	t2 = tgetstr("sr", &sp);
X	if (hard || t2 == NULL)
X		t2 = "";
X	sc_addline = cheaper(t1, t2, "scroll backwards", "");
X	if (*sc_addline == '\0')
X	{
X		/*
X		 * Force repaint on any backward movement.
X		 */
X		back_scroll = 0;
X	}
X}
X
X/*
X * Return the "best" of the two given termcap strings.
X * The best, if both exist, is the one with the lower 
X * cost (see cost() function).
X */
X	static char *
Xcheaper(t1, t2, doit, def)
X	char *t1, *t2;
X	char *doit;
X	char *def;
X{
X	if (*t1 == '\0' && *t2 == '\0')
X	{
X		cannot(doit);
X		return (def);
X	}
X	if (*t1 == '\0')
X		return (t2);
X	if (*t2 == '\0')
X		return (t1);
X	if (cost(t1) < cost(t2))
X		return (t1);
X	return (t2);
X}
X
X/*
X * Return the cost of displaying a termcap string.
X * We use the trick of calling tputs, but as a char printing function
X * we give it inc_costcount, which just increments "costcount".
X * This tells us how many chars would be printed by using this string.
X * {{ Couldn't we just use strlen? }}
X */
Xstatic int costcount;
X
X/*ARGSUSED*/
X	static void
Xinc_costcount(c)
X	int c;
X{
X	costcount++;
X}
X
X	static int
Xcost(t)
X	char *t;
X{
X	costcount = 0;
X	tputs(t, sc_height, inc_costcount);
X	return (costcount);
X}
X
X
X/*
X * Below are the functions which perform all the 
X * terminal-specific screen manipulation.
X */
X
X
X/*
X * Initialize terminal
X */
X	public void
Xinit()
X{
X	tputs(sc_init, sc_height, putchr);
X	init_done = 1;
X}
X
X/*
X * Deinitialize terminal
X */
X	public void
Xdeinit()
X{
X	if (!init_done)
X		return;
X	tputs(sc_deinit, sc_height, putchr);
X	init_done = 0;
X}
X
X/*
X * Home cursor (move to upper left corner of screen).
X */
X	public void
Xhome()
X{
X	tputs(sc_home, 1, putchr);
X}
X
X/*
X * Add a blank line (called with cursor at home).
X * Should scroll the display down.
X */
X	public void
Xadd_line()
X{
X	tputs(sc_addline, sc_height, putchr);
X}
X
X/*
X * Move cursor to lower left corner of screen.
X */
X	public void
Xlower_left()
X{
X	tputs(sc_lower_left, 1, putchr);
X}
X
X/*
X * Ring the terminal bell.
X */
X	public void
Xbell()
X{
X	if (quiet == VERY_QUIET)
X		vbell();
X	else
X		putchr('\7');
X}
X
X/*
X * Output the "visual bell", if there is one.
X */
X	public void
Xvbell()
X{
X	if (*sc_visual_bell == '\0')
X		return;
X	tputs(sc_visual_bell, sc_height, putchr);
X}
X
X/*
X * Clear the screen.
X */
X	public void
Xclear()
X{
X	tputs(sc_clear, sc_height, putchr);
X}
X
X/*
X * Clear from the cursor to the end of the cursor's line.
X * {{ This must not move the cursor. }}
X */
X	public void
Xclear_eol()
X{
X	tputs(sc_eol_clear, 1, putchr);
X}
X
X/*
X * Begin "standout" (bold, underline, or whatever).
X */
X	public void
Xso_enter()
X{
X	tputs(sc_s_in, 1, putchr);
X}
X
X/*
X * End "standout".
X */
X	public void
Xso_exit()
X{
X	tputs(sc_s_out, 1, putchr);
X}
X
X/*
X * Begin "underline" (hopefully real underlining, 
X * otherwise whatever the terminal provides).
X */
X	public void
Xul_enter()
X{
X	tputs(sc_u_in, 1, putchr);
X}
X
X/*
X * End "underline".
X */
X	public void
Xul_exit()
X{
X	tputs(sc_u_out, 1, putchr);
X}
X
X/*
X * Begin "bold"
X */
X	public void
Xbo_enter()
X{
X	tputs(sc_b_in, 1, putchr);
X}
X
X/*
X * End "bold".
X */
X	public void
Xbo_exit()
X{
X	tputs(sc_b_out, 1, putchr);
X}
X
X/*
X * Begin "blink"
X */
X	public void
Xbl_enter()
X{
X	tputs(sc_bl_in, 1, putchr);
X}
X
X/*
X * End "blink".
X */
X	public void
Xbl_exit()
X{
X	tputs(sc_bl_out, 1, putchr);
X}
X
X/*
X * Erase the character to the left of the cursor 
X * and move the cursor left.
X */
X	public void
Xbackspace()
X{
X	/* 
X	 * Try to erase the previous character by overstriking with a space.
X	 */
X	tputs(sc_backspace, 1, putchr);
X	putchr(' ');
X	tputs(sc_backspace, 1, putchr);
X}
X
X/*
X * Output a plain backspace, without erasing the previous char.
X */
X	public void
Xputbs()
X{
X	tputs(sc_backspace, 1, putchr);
X}
END_OF_FILE
echo shar: Extracting \"signal.c\"
sed "s/^X//" >'signal.c' <<'END_OF_FILE'
X/*
X * Routines dealing with signals.
X *
X * A signal usually merely causes a bit to be set in the "signals" word.
X * At some convenient time, the mainline code checks to see if any
X * signals need processing by calling psignal().
X * If we happen to be reading from a file [in iread()] at the time
X * the signal is received, we call intread to interrupt the iread.
X */
X
X#include "less.h"
X#include <signal.h>
X
X/*
X * "sigs" contains bits indicating signals which need to be processed.
X */
Xpublic int sigs;
X
X#define	S_INTERRUPT	01
X#ifdef SIGTSTP
X#define	S_STOP		02
X#endif
X#if defined(SIGWINCH) || defined(SIGWIND)
X#define S_WINCH		04
X#endif
X
Xextern int sc_width, sc_height;
Xextern int swindow;
Xextern int screen_trashed;
Xextern int lnloop;
Xextern int linenums;
Xextern int scroll;
Xextern int reading;
X
X/*
X * Interrupt signal handler.
X */
X	/* ARGSUSED*/
X	static HANDLER
Xu_interrupt(type)
X	int type;
X{
X	SIGNAL(SIGINT, u_interrupt);
X	sigs |= S_INTERRUPT;
X	if (reading)
X		intread();
X}
X
X	public void
Xfake_interrupt()
X{
X	sigs |= S_INTERRUPT;
X}
X
X#ifdef SIGTSTP
X/*
X * "Stop" (^Z) signal handler.
X */
X	/* ARGSUSED*/
X	static HANDLER
Xstop(type)
X	int type;
X{
X	SIGNAL(SIGTSTP, stop);
X	sigs |= S_STOP;
X	if (reading)
X		intread();
X}
X#endif
X
X#ifdef SIGWINCH
X/*
X * "Window" change handler
X */
X	/* ARGSUSED*/
X	public HANDLER
Xwinch(type)
X	int type;
X{
X	SIGNAL(SIGWINCH, winch);
X	sigs |= S_WINCH;
X	if (reading)
X		intread();
X}
X#else
X#ifdef SIGWIND
X/*
X * "Window" change handler
X */
X	/* ARGSUSED*/
X	public HANDLER
Xwinch(type)
X	int type;
X{
X	SIGNAL(SIGWIND, winch);
X	sigs |= S_WINCH;
X	if (reading)
X		intread();
X}
X#endif
X#endif
X
X/*
X * Set up the signal handlers.
X */
X	public void
Xinit_signals(on)
X	int on;
X{
X	if (on)
X	{
X		/*
X		 * Set signal handlers.
X		 */
X		(void) SIGNAL(SIGINT, u_interrupt);
X#ifdef SIGTSTP
X		(void) SIGNAL(SIGTSTP, stop);
X#endif
X#ifdef SIGWINCH
X		(void) SIGNAL(SIGWINCH, winch);
X#else
X#ifdef SIGWIND
X		(void) SIGNAL(SIGWIND, winch);
X#endif
X#endif
X	} else
X	{
X		/*
X		 * Restore signals to defaults.
X		 */
X		(void) SIGNAL(SIGINT, SIG_DFL);
X#ifdef SIGTSTP
X		(void) SIGNAL(SIGTSTP, SIG_DFL);
X#endif
X#ifdef SIGWINCH
X		(void) SIGNAL(SIGWINCH, SIG_IGN);
X#endif
X#ifdef SIGWIND
X		(void) SIGNAL(SIGWIND, SIG_IGN);
X#endif
X	}
X}
X
X/*
X * Process any signals we have received.
X * A received signal cause a bit to be set in "sigs".
X */
X	public void
Xpsignals()
X{
X	register int tsignals;
X
X	if ((tsignals = sigs) == 0)
X		return;
X	sigs = 0;
X
X#ifdef S_WINCH
X	if (tsignals & S_WINCH)
X	{
X		int old_width, old_height;
X		/*
X		 * Re-execute get_term() to read the new window size.
X		 */
X		old_width = sc_width;
X		old_height = sc_height;
X		swindow = -1;
X		get_term();
X		if (sc_width != old_width || sc_height != old_height)
X		{
X			scroll = (sc_height + 1) / 2;
X			screen_trashed = 1;
X		}
X	}
X#endif
X#ifdef SIGTSTP
X	if (tsignals & S_STOP)
X	{
X		/*
X		 * Clean up the terminal.
X		 */
X#ifdef SIGTTOU
X		SIGNAL(SIGTTOU, SIG_IGN);
X#endif
X		lower_left();
X		clear_eol();
X		deinit();
X		flush();
X		raw_mode(0);
X#ifdef SIGTTOU
X		SIGNAL(SIGTTOU, SIG_DFL);
X#endif
X		SIGNAL(SIGTSTP, SIG_DFL);
X		kill(getpid(), SIGTSTP);
X		/*
X		 * ... Bye bye. ...
X		 * Hopefully we'll be back later and resume here...
X		 * Reset the terminal and arrange to repaint the
X		 * screen when we get back to the main command loop.
X		 */
X		SIGNAL(SIGTSTP, stop);
X		raw_mode(1);
X		init();
X		screen_trashed = 1;
X	}
X#endif
X	if (tsignals & S_INTERRUPT)
X	{
X		bell();
X		/*
X		 * {{ You may wish to replace the bell() with 
X		 *    error("Interrupt", NULL_PARG); }}
X		 */
X
X		/*
X		 * If we were interrupted while in the "calculating 
X		 * line numbers" loop, turn off line numbers.
X		 */
X		if (lnloop)
X		{
X			lnloop = 0;
X			if (linenums == 2)
X				screen_trashed = 1;
X			linenums = 0;
X			error("Line numbers turned off", NULL_PARG);
X		}
X
X	}
X}
END_OF_FILE
echo shar: Extracting \"tags.c\"
sed "s/^X//" >'tags.c' <<'END_OF_FILE'
X#include <stdio.h>
X#include "less.h"
X
X#define	WHITESP(c)	((c)==' ' || (c)=='\t')
X
X#if TAGS
X
Xpublic char *tagfile;
Xpublic char *tagpattern;
X
Xpublic char *tags = "tags";
X
Xextern int linenums;
Xextern int sigs;
Xextern int jump_sline;
X
X/*
X * Find a tag in the "tags" file.
X * Sets "tagfile" to the name of the file containing the tag,
X * and "tagpattern" to the search pattern which should be used
X * to find the tag.
X */
X	public int
Xfindtag(tag)
X	register char *tag;
X{
X	register char *p;
X	register FILE *f;
X	register int taglen;
X	int search_char;
X	static char tline[200];
X
X	if ((f = fopen(tags, "r")) == NULL)
X	{
X		error("No tags file", NULL_PARG);
X		tagfile = NULL;
X		return;
X	}
X
X	taglen = strlen(tag);
X
X	/*
X	 * Search the tags file for the desired tag.
X	 */
X	while (fgets(tline, sizeof(tline), f) != NULL)
X	{
X		if (strncmp(tag, tline, taglen) != 0 || !WHITESP(tline[taglen]))
X			continue;
X
X		/*
X		 * Found it.
X		 * The line contains the tag, the filename and the
X		 * pattern, separated by white space.
X		 * The pattern is surrounded by a pair of identical
X		 * search characters.
X		 * Parse the line and extract these parts.
X		 */
X		tagfile = tagpattern = NULL;
X
X		/*
X		 * Skip over the whitespace after the tag name.
X		 */
X		for (p = tline;  !WHITESP(*p) && *p != '\0';  p++)
X			continue;
X		while (WHITESP(*p))
X			p++;
X		if (*p == '\0')
X			/* File name is missing! */
X			continue;
X
X		/*
X		 * Save the file name.
X		 * Skip over the whitespace after the file name.
X		 */
X		tagfile = p;
X		while (!WHITESP(*p) && *p != '\0')
X			p++;
X		*p++ = '\0';
X		while (WHITESP(*p))
X			p++;
X		if (*p == '\0')
X			/* Pattern is missing! */
X			continue;
X
X		/*
X		 * Save the pattern.
X		 * Skip to the end of the pattern.
X		 * Delete the initial "^" and the final "$" from the pattern.
X		 */
X		search_char = *p++;
X		if (*p == '^')
X			p++;
X		tagpattern = p;
X		while (*p != search_char && *p != '\0')
X			p++;
X		if (p[-1] == '$')
X			p--;
X		*p = '\0';
X
X		fclose(f);
X		return;
X	}
X	fclose(f);
X	error("No such tag in tags file", NULL_PARG);
X	tagfile = NULL;
X}
X
X/*
X * Search for a tag.
X * This is a stripped-down version of search().
X * We don't use search() for several reasons:
X *   -	We don't want to blow away any search string we may have saved.
X *   -	The various regular-expression functions (from different systems:
X *	regcmp vs. re_comp) behave differently in the presence of 
X *	parentheses (which are almost always found in a tag).
X */
X	public int
Xtagsearch()
X{
X	POSITION pos, linepos;
X	int linenum;
X	char *line;
X
X	pos = ch_zero();
X	linenum = find_linenum(pos);
X
X	for (;;)
X	{
X		/*
X		 * Get lines until we find a matching one or 
X		 * until we hit end-of-file.
X		 */
X		if (sigs)
X			return (1);
X
X		/*
X		 * Read the next line, and save the 
X		 * starting position of that line in linepos.
X		 */
X		linepos = pos;
X		pos = forw_raw_line(pos, &line);
X		if (linenum != 0)
X			linenum++;
X
X		if (pos == NULL_POSITION)
X		{
X			/*
X			 * We hit EOF without a match.
X			 */
X			error("Tag not found", NULL_PARG);
X			return (1);
X		}
X
X		/*
X		 * If we're using line numbers, we might as well
X		 * remember the information we have now (the position
X		 * and line number of the current line).
X		 */
X		if (linenums)
X			add_lnum(linenum, pos);
X
X		/*
X		 * Test the line to see if we have a match.
X		 * Use strncmp because the pattern may be
X		 * truncated (in the tags file) if it is too long.
X		 */
X		if (strncmp(tagpattern, line, strlen(tagpattern)) == 0)
X			break;
X	}
X
X	jump_loc(linepos, jump_sline);
X	return (0);
X}
X
X#endif
END_OF_FILE
echo shar: Extracting \"ttyin.c\"
sed "s/^X//" >'ttyin.c' <<'END_OF_FILE'
X/*
X * Routines dealing with getting input from the keyboard (i.e. from the user).
X */
X
X#include "less.h"
X#if __MSDOS__
X#include <io.h>
X#include <stdio.h>
X#include <fcntl.h>
X#include <signal.h>
X#endif
X
Xstatic int tty;
X
X/*
X * Open keyboard for input.
X */
X	public void
Xopen_getchr()
X{
X#if __MDDOS__
X	/*
X	 * Open a new handle to CON: in binary mode 
X	 * for unbuffered keyboard read.
X	 */
X	tty = open("CON", O_RDONLY|O_BINARY);
X#else
X	/*
X	 * Just use file descriptor 2, which in Unix
X	 * is usually attached to the screen and keyboard.
X	 */
X	tty = 2;
X#endif
X}
X
X/*
X * Get a character from the keyboard.
X */
X	public int
Xgetchr()
X{
X	char c;
X	int result;
X
X	do
X	{
X		result = iread(tty, &c, sizeof(char));
X		if (result == READ_INTR)
X			return (READ_INTR);
X		if (result < 0)
X		{
X			/*
X			 * Don't call error() here,
X			 * because error calls getchr!
X			 */
X			quit(1);
X		}
X#if __MSDOS__
X		/*
X		 * In raw read, we don't see ^C so look here for it.
X		 */
X		if (c == '\003')
X			raise(SIGINT);
X#endif
X		/*
X		 * Various parts of the program cannot handle
X		 * an input character of '\0'.
X		 * If a '\0' was actually typed, convert it to '\200' here.
X		 */
X		if (c == '\0')
X			c = '\200';
X	} while (result != 1);
X
X	return (c);
X}
END_OF_FILE
echo shar: Extracting \"version.c\"
sed "s/^X//" >'version.c' <<'END_OF_FILE'
X/*
X *		less
X *	Copyright (c) 1984,1985,1989  Mark Nudelman
X *
X *	This program may be freely used and/or modified, 
X *	with the following provisions:
X *	1. This notice and the above copyright notice must remain intact.
X *	2. Neither this program, nor any modification of it,
X *	   may be sold for profit without written consent of the author.
X *
X *	 ---------------------------------------------------------------
X *	|  Special note to the person who ported "less" to the Amiga:	|
X *	|  If you're going to be vain enough to splash your name on	|
X *	|  the screen every time someone runs less, you might at	|
X *	|  least credit the author.					|
X *	 ---------------------------------------------------------------
X *
X *	This program is a paginator similar to "more", 
X *	but allows you to move both forward and backward in the file.  
X *	Commands are based on "more" and "vi".
X *
X *	----------------------- CHANGES ---------------------------------
X *
X *	    Allowed use on standard input		1/29/84   markn
X *	    Added E, N, P commands			2/1/84    markn
X *	    Added '=' command, 'stop' signal handling	4/17/84   markn
X *	    Added line folding				4/20/84   markn
X *	v2: Fixed '=' command to use BOTTOM_PLUS_ONE, 
X *	    instead of TOP, added 'p' & 'v' commands	4/27/84   markn
X *	v3: Added -m and -t options, '-' command	5/3/84    markn
X *	v4: Added LESS environment variable		5/3/84    markn
X *	v5: New comments, fixed '-' command slightly	5/3/84    markn
X *	v6: Added -Q, visual bell			5/15/84   markn
X *	v7: Fixed jump_back(n) bug: n should count real
X *	    lines, not folded lines.  Also allow number
X *	    on G command.				5/24/84   markn
X *	v8: Re-do -q and -Q commands			5/30/84   markn
X *	v9: Added "+<cmd>" argument			9/25/84   markn
X *	v10: Fixed bug in -b<n> argument processing	10/10/84  markn
X *	v11: Made error() ring bell if \n not entered.	10/18/84  markn
X *	-----------------------------------------------------------------
X *	v12: Reorganized signal handling and made
X *	     portable to 4.2bsd.			2/13/85   mark
X *	v13: Reword error message for '-' command.	2/16/85   mark
X *	v14: Added -bf and -bp variants of -b.		2/22/85   mark
X *	v15: Miscellaneous changes.			2/25/85   mark
X *	v16: Added -u flag for backspace processing.	3/13/85   mark
X *	v17: Added j and k commands, 
X *		changed -t default.			4/13/85   mark
X *	v18: Rewrote signal handling code.		4/20/85   mark
X *	v19: Got rid of "verbose" eq_message().		5/2/85    mark
X *	     Made search() scroll in some cases.
X *	v20: Fixed screen.c ioctls for System V.	5/21/85   mark
X *	v21: Fixed some first_cmd bugs.			5/23/85   mark
X *	v22: Added support for no RECOMP nor REGCMP.	5/24/85   mark
X * 	v23: Miscellanous changes and prettying up.	5/25/85   mark
X *		Posted to USENET.
X *	-----------------------------------------------------------------
X *      v24: Added ti,te terminal init & de-init       6/3/85 Mike Kersenbrock
X *	v25: Added -U flag, standout mode underlining.	6/8/85    mark
X *	v26: Added -M flag.				6/9/85    mark
X *	     Use underline termcap (us) if it exists.
X *	v27: Renamed some variables to make unique in	6/15/85   mark
X *	     6 chars.  Minor fix to -m.
X *	v28: Fixed right margin bug.			6/28/85   mark
X *	v29: Incorporated M.Rose's changes to signal.c	6/28/85   mark
X *	v30: Fixed stupid bug in argument processing.	6/29/85   mark
X *	v31: Added -p flag, changed repaint algorithm.  7/15/85   mark
X *	     Added kludge for magic cookie terminals.
X *	v32: Added cat_file if output not a tty.	7/16/85   mark
X *	v33: Added -e flag and EDITOR.			7/23/85   mark
X *	v34: Added -s flag.				7/26/85   mark
X *	v35: Rewrote option handling; added option.c.	7/27/85   mark
X *	v36: Fixed -e flag to work if not last file.	7/29/85   mark
X *	v37: Added -x flag.				8/10/85   mark
X *	v38: Changed prompting; created prompt.c.	8/19/85   mark
X *	v39: (Not -p) does not initially clear screen.	8/24/85   mark
X *	v40: Added "skipping" indicator in forw().	8/26/85   mark
X *		Posted to USENET.
X *	-----------------------------------------------------------------
X *	v41: ONLY_RETURN, control char commands,	9/17/85   mark
X *	     faster search, other minor fixes.
X *	v42: Added ++ command line syntax;		9/25/85   mark
X *	     ch_fsize for pipes.
X *	v43: Added -h flag, changed prim.c algorithms.	10/15/85  mark
X *	v44: Made END print in all cases of eof;	10/16/85  mark
X *	     ignore SIGTTOU after receiving SIGTSTP.
X *	v45: Never print backspaces unless -u.		10/16/85  mark
X *	v46: Backwards scroll in jump_loc.		10/24/85  mark
X *	v47: Fixed bug in edit(): *first_cmd==0		10/30/85  mark
X *	v48: Use TIOCSETN instead of TIOCSETP.		11/16/85  mark
X *	     Added marks (m and ' commands).
X *		Posted to USENET.
X *	-----------------------------------------------------------------
X *	v49: Fixed bug: signal didn't clear mcc.	1/9/86    mark
X *	v50: Added ' (quote) to gomark.			1/15/86   mark
X *	v51: Added + cmd, fixed problem if first_cmd
X *	     fails, made g cmd sort of "work" on pipes
X *	     even if bof is no longer buffered.		1/16/86   mark
X *	v52: Made short files work better.		1/17/86   mark
X *	v53: Added -P option.				1/20/86   mark
X *	v54: Changed help to use HELPFILE.		1/20/86   mark
X *	v55: Messages work better if not tty output.	1/23/86   mark
X *	v56: Added -l option.				1/24/86   mark
X *	v57: Fixed -l to get confirmation before
X *	     overwriting an existing file.		1/31/86   mark
X *	v58: Added filename globbing.			8/28/86   mark
X *	v59: Fixed some bugs with very long filenames.	9/15/86   mark
X *	v60: Incorporated changes from Leith (Casey)
X *	     Leedom for boldface and -z option.		9/26/86   mark
X *	v61: Got rid of annoying repaints after ! cmd.	9/26/86   mark
X *		Posted to USENET.
X *	-----------------------------------------------------------------
X *	v62: Added is_directory(); change -z default to
X *	     -1 instead of 24; cat-and-exit if -e and
X *	     file is less than a screenful.		12/23/86  mark
X *	v63: Fixed bug in cat-and-exit if > 1 file.	1/8/87    mark
X *	v64: Changed puts/putstr, putc/putchr, 
X *	     getc/getchr to avoid name conflict with 
X *	     stdio functions.				1/12/87  mark
X *	v65: Allowed '-' command to change NUMBER
X *	     valued options (thanks to Gary Puckering)	1/26/87  mark
X *	v66: Fixed bug: prepaint should use force=1.	2/13/87  mark
X *	v67: Added !! and % expansion to ! command.	2/24/87  mark
X *	v68: Added SIGWINCH and TIOCGWINSZ support;
X *	     changed is_directory to bad_file.
X *	     (thanks to J. Robert Ward)			2/25/87  mark
X *	v69: Added SIGWIND and WIOCGETD (for Unix PC).	2/25/87  mark
X *	v70: Changed help cmd from 'h' to 'H'; better 
X *	     error msgs in bad_file, errno_message.	3/13/87  mark
X *	v71: Changed -p to -c, made triple -c/-C
X *	     for clear-eol like more's -c.		5/11/87  mark
X *	v72: Added -E, -L, use $SHELL in lsystem().	6/26/87  mark
X *	     (thanks to Steve Spearman)
X *	v73: Allow Examine "#" for previous file.	6/26/87  mark
X *		Posted to USENET 8/25/87.
X *	-----------------------------------------------------------------
X *	v74: Fix conflict in EOF symbol with stdio.h,	9/18/87  mark
X *	     Make os.c more portable to BSD.
X *	v75: Fix problems in get_term (thanks to 	9/23/87  mark
X *	     Paul Eggert); new backwards scrolling in
X *	     jump_loc (thanks to Marion Hakanson).
X *	v76: Added -i flag; allow single "!" to		9/23/87  mark
X *	     invoke a shell (thanks to Franco Barber).
X *	v77: Added -n flag and line number support.	9/24/87  mark
X *	v78: Fixed problem with prompts longer than	9/25/87  mark
X *	     the screen width.	
X *	v79: Added the _ command.			9/29/87  mark
X *	v80: Allow signal to break out of linenum scan.	10/6/87  mark
X *	v81: Allow -b to be changed from within less.	10/6/87  mark
X *	v82: Add cmd_decode to use a table for key	10/7/87  mark
X *	     binding (thanks to David Nason).
X *	v83: Allow .less file for user-defined keys.	10/9/87  mark
X *	v84: Fix -e/-E problems (thanks to Felix Lee).	10/11/87 mark
X *	v85: Search now keeps track of line numbers.	10/15/87 mark
X *	v86: Added -B option and autobuf; fixed		10/20/87 mark
X *	     "pipe error" bug.
X *	v87: Fix bug re BSD signals while reading file.	3/1/88   mark
X *	v88: Use new format for -P option (thanks to	3/12/88  mark
X *	     der Mouse), allow "+-c" without message,
X *	     fix bug re BSD hangup.
X *	v89: Turn off line numbers if linenum scan	3/18/88  mark
X *	     is interrupted.
X *	v90: Allow -P from within less.			3/30/88  mark
X *	v91: Added tags file support (new -t option)	3/30/88  mark
X *	     (thanks to Brian Campbell).
X *	v92: Added -+option syntax.			4/4/88   mark
X *	v93: Add support for slow input (thanks to	4/11/88  mark
X *	     Joe Orost & apologies for taking almost
X *	     3 years to get this in!)
X *	v94: Redo reading/signal stuff.			4/11/88  mark
X *	v95: Repaint screen better after signal.	4/20/88  mark
X *	v96: Add /! and ?! commands.			4/21/88  mark
X *	v97: Allow -l/-L from within less.		5/17/88  mark
X *	     Eliminate some static arrays (use calloc).
X *		Posted to USENET.
X *	-----------------------------------------------------------------
X *	v98: Fix incorrect calloc call; uninitialized	10/14/88 mark
X *	     var in exec_mca; core dump on unknown TERM.
X *	     Make v cmd work if past last line of file.
X *	     Fix some signal bugs.
X *	v99: Allow space between -X and string,		10/29/88 mark
X *	     when X is a string-valued option.
X *	v100: Fix globbing bug when $SHELL not set;	1/5/89   mark
X *	      allow spaces after -t command.
X *	v101: Fix problem with long (truncated) lines	1/6/89   mark
X *	      in tags file (thanks to Neil Dixon).
X *	v102: Fix bug with E# when no prev file;	1/6/89   mark
X *	      allow spaces after -l command.
X *	v103: Add -N, -f and -? options.  Add z and w	3/14/89  mark
X *	      commands.  Add %L for prompt strings.
X *	v104: Added EDITPROTO.				3/16/89  mark
X *	v105: Fix bug in find_linenum which cached	3/20/89  mark
X *	      incorrectly on long lines.
X *	v106: Added -k option and multiple lesskey      3/31/89  mark
X *	      files.
X *	v107: Add 8-bit char support and -g option.	4/27/89  mark
X *	      Split option code into 3 files.
X *	v108: Allocate position table dynamically       5/5/89   mark
X *	      (thanks to Paul Eggert); change % command
X *	      from "percent" to vi-style brace finder.
X *	v109: Added ESC-% command, split prim.c.	5/10/89  mark
X *	v110: Fixed bug in + option; fixed repaint bug	5/24/89  mark
X *	      under Sun windows (thanks to Paul Eggert).
X *	v111: Generalized # and % expansion; use 	5/25/89  mark
X *	      calloc for some error messages.
X *	v112: Get rid of ESC-%, add {}()[] commands.	5/30/89  mark
X *	v113: Optimize lseeks (thanks to Paul Eggert).	5/31/89  mark
X *	v114: Added ESC-/ and ESC-/! commands.		7/25/89  mark
X *	v115: Added ESC-n command.			7/26/89  mark
X *	v116: Added find_pos to optimize g command.	7/31/89  mark
X *	v117: Change -f option to -r.			8/1/89   mark
X *	v118: Save positions for all previous files,	8/2/89   mark
X *	      not just the immediately previous one.
X *	v119: Save marks across file boundaries.	8/7/89   mark
X *	      Add file handle stuff.
X *	v120: Add :ta command.				8/11/89  mark
X *	v121: Add -f option.				8/16/89  mark
X *	v122: Fix performance with many buffers.	8/30/89  mark
X *	v123: Verbose prompts for string options.	8/31/89  mark
X *		Posted beta to USENET.
X *	-----------------------------------------------------------------
X *	v124: Reorganize search commands,		9/18/89  mark
X *	      N = rev, ESC-n = span, add ESC-N.
X *	v125: Fix tab bug (thanks to Alex Liu).		9/18/89  mark
X *	      Fix EOF bug when both -w and -c.
X *	v126: Add -j option.				10/25/89 mark
X *	v127: Fix problems with blank lines before BOF.	10/27/89 mark
X *	v128: Add %bj, etc. to prompt strings.		10/27/89 mark
X *	v129: Add -+,-- commands; add set-option and	11/3/89  mark
X *	      unset-option to lesskey.
X *	v130: Generalize A_EXTRA to string, remove	11/6/89  mark
X *	      set-option, unset-option from lesskey.
X *	v131: Changed name of EDITPROTO to LESSEDIT.	11/7/89  mark
X *	v132: Allow editing of command prefix.		11/8/89  mark
X *	v133: Add -y option (thanks to Jeff Sullivan).	11/16/89 mark
X *	v134: Glob filenames in the -l command.		12/1/89  mark
X *	v135: Combined {}()[] commands into one, and	12/5/89  mark
X *	      added ESC-^F and ESC-^B commands.
X *	v136: Added -S, -R flags.  Added | command.	1/20/90  mark
X *	      Added warning for binary files. (thanks 
X *	      to Richard Brittain and J. Sullivan).
X *	v137: Rewrote horrible pappend code.		1/21/90  mark
X *	      Added * notation for hi-bit chars.
X *	v138: Fix magic cookie terminal handling.	1/24/90  mark
X *	      Get rid of "cleanup" loop in ch_get.
X *	v139: Added MSDOS support.  (many thanks	1/27/90  mark
X *	      to Richard Brittain).
X *	v140: Editing a new file adds it to the		2/7/90   mark
X *	      command line list.
X *	v141: Add edit_list for editing >1 file.	2/8/90   mark
X *	v142: Add :x command.				2/10/90  mark
X *	v143: Add * and @ modifies to search cmds.	2/11/90  mark
X *	      Change ESC-/ cmd from /@* to /*.
X *	v144: Messed around with ch_zero; 		3/1/90   mark
X *	      no real change.
X *	v145: Added -R and -v/-V for MSDOS;		3/2/90   mark
X *	      renamed FILENAME to avoid conflict.
X *	v146: Pull cmdbuf functions out of command.c	3/5/90   mark
X *	v147: Implement ?@; fix multi-file edit bugs.	3/7/90   mark
X *	v148: Fixed bug in :e<file> then :e#.		3/29/90  mark
X *	v149: Change error,ierror,query to use PARG.	4/3/90   mark
X *	v150: Add LESS_CHARSET, LESS_CHARDEF.		4/6/90   mark
X *	v151: Remove -g option; clean up ispipe.	4/13/90  mark
X *	v152: lsystem() closes input file, for		4/14/90  mark
X *	      editors which require exclusive open.
X *	v153: Fix bug if SHELL unset; 			4/18/90  mark
X *	      fix bug in overstrike control char.
X *	v154: Output to fd 2 via buffer.		4/25/90  mark
X *	v155: Ignore -i if uppercase in pattern		4/30/90  mark
X *	      (thanks to Michael Rendell.)
X *	v156: Remove scroll limits in forw() & back();	5/3/90   mark
X *	      causes problems with -c.
X *	v157: Forward search starts at next real line	5/4/90   mark
X *	      (not screen line) after jump target.
X *	v158: Added F command.				6/14/90  mark
X *	v159: Fix bug in exiting: output not flushed.	7/29/90  mark
X *	v160: Clear screen before initial output w/ -c.	7/29/90  mark
X *	v161: Add -T flag.				7/29/90  mark
X *	v162: Fix bug with +F on command line.		8/14/90  mark
X *	v163: Added LESSBINFMT variable.		8/21/90  mark
X *	v164: Added -p, LINES, COLUMNS and		9/5/90   mark
X *	      unset mark ' == BOF, for 1003.2 D5.
X *	v165: At EOF with -c set, don't display empty	9/6/90   mark
X *	      screen when try to page forward.
X *	v166: Fix G when final line in file wraps.	9/6/90   mark
X *	v167: Translate CR/LF -> LF for 1003.2.		9/11/90  mark
X *	v168: Return to curr file if "tag not found".	9/13/90  mark
X *	v169: G goes to EOF even if file has grown.	12/12/90 mark
X *	v170: Add optimization for BSD _setjmp;		1/17/91  mark
X *	      fix #include ioctl.h TERMIO problem.
X *	      (thanks to Paul Eggert)
X */
X
Xchar version[] = "@(#) less  version 170";
END_OF_FILE
echo shar: Extracting \"funcs.h\"
sed "s/^X//" >'funcs.h' <<'END_OF_FILE'
X	public void end_logfile ();
X	public void sync_logfile ();
X	public int ch_seek ();
X	public int ch_end_seek ();
X	public int ch_beg_seek ();
X	public POSITION ch_length ();
X	public POSITION ch_tell ();
X	public int ch_forw_get ();
X	public int ch_back_get ();
X	public int ch_nbuf ();
X	public void ch_flush ();
X	public void ch_pipe ();
X	public void ch_nonpipe ();
X	public void cmd_reset ();
X	public int len_cmdbuf ();
X	public int cmd_erase ();
X	public int cmd_char ();
X	public int cmd_int ();
X	public void cmd_putstr ();
X	public char * get_cmdbuf ();
X	public void ungetcc ();
X	public void ungetsc ();
X	public void commands ();
X	public int cmd_decode ();
X	public int add_cmdtable ();
X	public void add_hometable ();
X	public void help ();
X	public POSITION forw_line ();
X	public POSITION back_line ();
X	public void prewind ();
X	public void plinenum ();
X	public int pappend ();
X	public void pdone ();
X	public int gline ();
X	public void null_line ();
X	public POSITION forw_raw_line ();
X	public POSITION back_raw_line ();
X	public void clr_linenum ();
X	public void add_lnum ();
X	public int find_linenum ();
X	public POSITION find_pos ();
X	public int currline ();
X	public void strtcpy ();
X	public char * save ();
X	public VOID_POINTER ecalloc ();
X	public char * skipsp ();
X	public void quit ();
X	public int edit ();
X	public void edit_list ();
X	public int edit_first ();
X	public int edit_last ();
X	public int edit_next ();
X	public int edit_prev ();
X	public int edit_index ();
X	public void cat_file ();
X	public void use_logfile ();
X	public void scan_option ();
X	public void toggle_option ();
X	public int single_char_option ();
X	public char * opt_prompt ();
X	public int isoptpending ();
X	public void nopendopt ();
X	public int getnum ();
X	public void opt_o ();
X	public void opt__O ();
X	public void opt_l ();
X	public void opt__L ();
X	public void opt_k ();
X	public void opt_t ();
X	public void opt__T ();
X	public void opt_p ();
X	public void opt__P ();
X	public void opt_b ();
X	public void opt_v ();
X	public void opt_W ();
X	public void opt_query ();
X	public void init_option ();
X	public struct option * findopt ();
X	public int iread ();
X	public void intread ();
X	public long get_time ();
X	public char * errno_message ();
X	public char * errno_message ();
X	public void init_charset ();
X	public int binary_char ();
X	public int control_char ();
X	public char * prchar ();
X	public char * homefile ();
X	public char * find_helpfile ();
X	public char * fexpand ();
X	public char * glob ();
X	public char * glob ();
X	public char * bad_file ();
X	public POSITION filesize ();
X	public char * bad_file ();
X	public POSITION filesize ();
X	public void lsystem ();
X	public int pipe_mark ();
X	public int pipe_data ();
X	public void put_line ();
X	public void flush ();
X	public void putchr ();
X	public void putstr ();
X	public void error ();
X	public void ierror ();
X	public int query ();
X	public POSITION position ();
X	public void add_forw_pos ();
X	public void add_back_pos ();
X	public void pos_clear ();
X	public void pos_init ();
X	public int onscreen ();
X	public int empty_screen ();
X	public int empty_lines ();
X	public void get_scrpos ();
X	public int adjsline ();
X	public IFILE next_ifile ();
X	public IFILE prev_ifile ();
X	public int nifile ();
X	public IFILE get_ifile ();
X	public char * get_filename ();
X	public int get_index ();
X	public void store_pos ();
X	public void get_pos ();
X	public void match_brac ();
X	public void forw ();
X	public void back ();
X	public void forward ();
X	public void backward ();
X	public int get_back_scroll ();
X	public void jump_forw ();
X	public void jump_back ();
X	public void repaint ();
X	public void jump_percent ();
X	public void jump_line_loc ();
X	public void jump_loc ();
X	public int search ();
X	public void init_mark ();
X	public int badmark ();
X	public void setmark ();
X	public void lastmark ();
X	public void gomark ();
X	public POSITION markpos ();
X	public void init_prompt ();
X	public char * pr_expand ();
X	public char * eq_message ();
X	public char * pr_string ();
X	public void raw_mode ();
X	public void get_scrsize ();
X	public void get_term ();
X	public void init ();
X	public void deinit ();
X	public void home ();
X	public void add_line ();
X	public void lower_left ();
X	public void bell ();
X	public void vbell ();
X	public void clear ();
X	public void clear_eol ();
X	public void so_enter ();
X	public void so_exit ();
X	public void ul_enter ();
X	public void ul_exit ();
X	public void bo_enter ();
X	public void bo_exit ();
X	public void bl_enter ();
X	public void bl_exit ();
X	public void backspace ();
X	public void putbs ();
X	public void fake_interrupt ();
X	public HANDLER winch ();
X	public HANDLER winch ();
X	public void init_signals ();
X	public void psignals ();
X	public int findtag ();
X	public int tagsearch ();
X	public void open_getchr ();
X	public int getchr ();
END_OF_FILE