summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoretheisen <etheisen@cvs.openbsd.org>1996-09-21 05:39:47 +0000
committeretheisen <etheisen@cvs.openbsd.org>1996-09-21 05:39:47 +0000
commit47d3895c0565680e9e7eba725be0896bfd06ce11 (patch)
tree921c411da39dc3da0c9f04e1732558983f372819
parenta2e06afb07d9a651364258a3d25e37f99d1dae46 (diff)
Import of unmolested less-290.
-rw-r--r--usr.bin/less/INSTALL146
-rw-r--r--usr.bin/less/Makefile.dos54
-rw-r--r--usr.bin/less/Makefile.in148
-rw-r--r--usr.bin/less/Makefile.os239
-rw-r--r--usr.bin/less/NEWS239
-rw-r--r--usr.bin/less/README127
-rw-r--r--usr.bin/less/acconfig.h74
-rw-r--r--usr.bin/less/brac.c117
-rw-r--r--usr.bin/less/ch.c763
-rw-r--r--usr.bin/less/charset.c287
-rw-r--r--usr.bin/less/cmd.h117
-rw-r--r--usr.bin/less/cmdbuf.c939
-rw-r--r--usr.bin/less/command.c1253
-rw-r--r--usr.bin/less/configure1934
-rw-r--r--usr.bin/less/configure.in161
-rw-r--r--usr.bin/less/decode.c654
-rw-r--r--usr.bin/less/defines.dos279
-rw-r--r--usr.bin/less/defines.h.in267
-rw-r--r--usr.bin/less/defines.h.top143
-rw-r--r--usr.bin/less/defines.os2275
-rw-r--r--usr.bin/less/doscreen.c571
-rw-r--r--usr.bin/less/edit.c656
-rw-r--r--usr.bin/less/filename.c715
-rw-r--r--usr.bin/less/forwback.c398
-rw-r--r--usr.bin/less/funcs.h224
-rw-r--r--usr.bin/less/help.c88
-rw-r--r--usr.bin/less/ifile.c336
-rw-r--r--usr.bin/less/input.c310
-rw-r--r--usr.bin/less/install.sh119
-rw-r--r--usr.bin/less/jump.c295
-rw-r--r--usr.bin/less/less.h253
-rw-r--r--usr.bin/less/less.hlp109
-rw-r--r--usr.bin/less/less.man1518
-rw-r--r--usr.bin/less/less.nro1235
-rw-r--r--usr.bin/less/lesskey.c687
-rw-r--r--usr.bin/less/lesskey.h28
-rw-r--r--usr.bin/less/lesskey.man264
-rw-r--r--usr.bin/less/lesskey.nro216
-rw-r--r--usr.bin/less/line.c582
-rw-r--r--usr.bin/less/linenum.c471
-rw-r--r--usr.bin/less/lsystem.c300
-rw-r--r--usr.bin/less/main.c337
-rw-r--r--usr.bin/less/mark.c264
-rw-r--r--usr.bin/less/mkinstalldirs32
-rw-r--r--usr.bin/less/optfunc.c496
-rw-r--r--usr.bin/less/option.c549
-rw-r--r--usr.bin/less/option.h67
-rw-r--r--usr.bin/less/opttbl.c273
-rw-r--r--usr.bin/less/os.c235
-rw-r--r--usr.bin/less/output.c363
-rw-r--r--usr.bin/less/position.c248
-rw-r--r--usr.bin/less/position.h35
-rw-r--r--usr.bin/less/prompt.c451
-rw-r--r--usr.bin/less/regexp.c1228
-rw-r--r--usr.bin/less/regexp.h33
-rw-r--r--usr.bin/less/screen.c1196
-rw-r--r--usr.bin/less/search.c1197
-rw-r--r--usr.bin/less/signal.c242
-rw-r--r--usr.bin/less/tags.c220
-rw-r--r--usr.bin/less/ttyin.c116
-rw-r--r--usr.bin/less/version.c513
61 files changed, 25486 insertions, 0 deletions
diff --git a/usr.bin/less/INSTALL b/usr.bin/less/INSTALL
new file mode 100644
index 00000000000..8a7d026f700
--- /dev/null
+++ b/usr.bin/less/INSTALL
@@ -0,0 +1,146 @@
+ This is a generic INSTALL file for utilities distributions.
+If this package does not come with, e.g., installable documentation or
+data files, please ignore the references to them below.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation, and
+creates the Makefile(s) (one in each subdirectory of the source
+directory). In some packages it creates a C header file containing
+system-dependent definitions. It also creates a file `config.status'
+that you can run in the future to recreate the current configuration.
+
+To compile this package:
+
+1. Configure the package for your system.
+
+ Normally, you just `cd' to the directory containing the package's
+source code and type `./configure'. If you're using `csh' on an old
+version of System V, you might need to type `sh configure' instead to
+prevent `csh' from trying to execute `configure' itself.
+
+ Running `configure' takes awhile. While it is running, it
+prints some messages that tell what it is doing. If you don't want to
+see any messages, run `configure' with its standard output redirected
+to `/dev/null'; for example, `./configure >/dev/null'.
+
+ To compile the package in a different directory from the one
+containing the source code, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'. If
+for some reason `configure' is not in the source code directory that
+you are configuring, then it will report that it can't find the source
+code. In that case, run `configure' with the option `--srcdir=DIR',
+where DIR is the directory that contains the source code.
+
+ By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc. You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PATH'. Alternately, you can do so by consistently
+giving a value for the `prefix' variable when you run `make', e.g.,
+ make prefix=/usr/gnu
+ make prefix=/usr/gnu install
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+give `configure' the option `--exec-prefix=PATH' or set the `make'
+variable `exec_prefix' to PATH, the package will use PATH as the prefix
+for installing programs and libraries. Data files and documentation
+will still use the regular prefix. Normally, all files are installed
+using the same prefix.
+
+ Some packages pay attention to `--with-PACKAGE' options to
+`configure', where PACKAGE is something like `gnu-as' or `x' (for the
+X Window System). They may also pay attention to `--enable-FEATURE'
+options, where FEATURE indicates an optional part of the package. The
+README should mention any `--with-' and `--enable-' options that the
+package recognizes.
+
+ `configure' also recognizes the following options:
+
+`--help'
+ Print a summary of the options to `configure', and exit.
+
+`--quiet'
+`--silent'
+ Do not print messages saying which checks are being made.
+
+`--verbose'
+ Print the results of the checks.
+
+`--version'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`--x-includes=DIR'
+ X include files are in DIR.
+
+`--x-libraries=DIR'
+ X library files are in DIR.
+
+ `configure' also accepts and ignores some other options.
+
+ On systems that require unusual options for compilation or linking
+that the package's `configure' script does not know about, you can give
+`configure' initial values for variables by setting them in the
+environment. In Bourne-compatible shells, you can do that on the
+command line like this:
+
+ CC='gcc -traditional' LIBS=-lposix ./configure
+
+On systems that have the `env' program, you can do it like this:
+
+ env CC='gcc -traditional' LIBS=-lposix ./configure
+
+ Here are the `make' variables that you might want to override with
+environment variables when running `configure'.
+
+ For these variables, any value given in the environment overrides the
+value that `configure' would choose:
+
+ - Variable: CC
+ C compiler program. The default is `cc'.
+
+ - Variable: INSTALL
+ Program to use to install files. The default is `install' if you
+ have it, `cp' otherwise.
+
+ For these variables, any value given in the environment is added to
+the value that `configure' chooses:
+
+ - Variable: DEFS
+ Configuration options, in the form `-Dfoo -Dbar...'. Do not use
+ this variable in packages that create a configuration header file.
+
+ - Variable: LIBS
+ Libraries to link with, in the form `-lfoo -lbar...'.
+
+ If you need to do unusual things to compile the package, we encourage
+you to figure out how `configure' could check whether to do them, and
+mail diffs or instructions to the address given in the README so we
+can include them in the next release.
+
+2. Type `make' to compile the package. If you want, you can override
+the `make' variables CFLAGS and LDFLAGS like this:
+
+ make CFLAGS=-O2 LDFLAGS=-s
+
+3. If the package comes with self-tests and you want to run them,
+type `make check'. If you're not sure whether there are any, try it;
+if `make' responds with something like
+ make: *** No way to make target `check'. Stop.
+then the package does not come with self-tests.
+
+4. Type `make install' to install programs, data files, and
+documentation.
+
+5. You can remove the program binaries and object files from the
+source directory by typing `make clean'. To also remove the
+Makefile(s), the header file containing system-dependent definitions
+(if the package uses one), and `config.status' (all the files that
+`configure' created), type `make distclean'.
+
+ The file `configure.in' is used to create `configure' by a program
+called `autoconf'. You only need it if you want to regenerate
+`configure' using a newer version of `autoconf'.
diff --git a/usr.bin/less/Makefile.dos b/usr.bin/less/Makefile.dos
new file mode 100644
index 00000000000..492e3e1c3cd
--- /dev/null
+++ b/usr.bin/less/Makefile.dos
@@ -0,0 +1,54 @@
+# Makefile for less.
+# MS-DOS version
+
+#### Start of system configuration section. ####
+
+CC = cl
+LIBDIR = \msc6.0\lib
+
+CFLAGS = -O /Alfw
+LDFLAGS = -O
+LIBS = ${LIBDIR}\llibce.lib ${LIBDIR}\graphics.lib
+
+#### End of system configuration section. ####
+
+# This rule allows us to supply the necessary -D options
+# in addition to whatever the user asks for.
+.c.obj:
+ ${CC} -c -I. ${CPPFLAGS} ${CFLAGS} $<
+
+SRC = main.c doscreen.c brac.c ch.c charset.c cmdbuf.c command.c \
+ decode.c edit.c filename.c forwback.c help.c ifile.c \
+ input.c jump.c line.c linenum.c lsystem.c \
+ mark.c optfunc.c option.c opttbl.c os.c output.c \
+ position.c prompt.c search.c signal.c tags.c \
+ ttyin.c version.c
+OBJ = main.obj doscreen.obj brac.obj ch.obj charset.obj cmdbuf.obj command.obj \
+ decode.obj edit.obj filename.obj forwback.obj help.obj ifile.obj \
+ input.obj jump.obj line.obj linenum.obj lsystem.obj \
+ mark.obj optfunc.obj option.obj opttbl.obj os.obj output.obj \
+ position.obj prompt.obj search.obj signal.obj tags.obj \
+ ttyin.obj version.obj
+
+all: less lesskey
+
+# This is really horrible, but the command line is too long for
+# MS-DOS if we try to link $(OBJ).
+less: $(OBJ)
+ -del lesskey.obj
+ $(CC) $(LDFLAGS) -o $@ *.obj $(LIBS)
+
+lesskey: lesskey.obj version.obj
+ $(CC) $(LDFLAGS) -o $@ lesskey.obj version.obj $(LIBS)
+
+defines.h: defines.dos
+ -del defines.h
+ -copy defines.dos defines.h
+
+$(OBJ): less.h defines.h
+
+clean:
+ -del *.obj
+ -del less.exe
+ -del lesskey.exe
+
diff --git a/usr.bin/less/Makefile.in b/usr.bin/less/Makefile.in
new file mode 100644
index 00000000000..9c15e7858dc
--- /dev/null
+++ b/usr.bin/less/Makefile.in
@@ -0,0 +1,148 @@
+# Makefile for less.
+
+#### Start of system configuration section. ####
+
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+CC = @CC@
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+
+CFLAGS = @CFLAGS@
+CFLAGS_COMPILE_ONLY = -c
+LDFLAGS = @LDFLAGS@
+O=o
+
+LIBS = @LIBS@
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+# Where the installed binary goes.
+bindir = ${exec_prefix}/bin
+binprefix =
+
+# Where the help file goes.
+datadir = ${prefix}/share
+
+mandir = ${prefix}/man/man${manext}
+manext = 1
+manprefix =
+
+#### End of system configuration section. ####
+
+SHELL = /bin/sh
+
+# This rule allows us to supply the necessary -D options
+# in addition to whatever the user asks for.
+.c.o:
+ ${CC} -I. ${CFLAGS_COMPILE_ONLY} ${CPPFLAGS} ${CFLAGS} $<
+
+SRC = main.c screen.c brac.c ch.c charset.c cmdbuf.c \
+ command.c decode.c edit.c filename.c forwback.c \
+ help.c ifile.c input.c jump.c line.c linenum.c \
+ lsystem.c mark.c optfunc.c option.c opttbl.c os.c \
+ output.c position.c prompt.c search.c signal.c \
+ tags.c ttyin.c version.c
+OBJ = main.${O} screen.${O} brac.${O} ch.${O} charset.${O} cmdbuf.${O} \
+ command.${O} decode.${O} edit.${O} filename.${O} forwback.${O} \
+ help.${O} ifile.${O} input.${O} jump.${O} line.${O} linenum.${O} \
+ lsystem.${O} mark.${O} optfunc.${O} option.${O} opttbl.${O} os.${O} \
+ output.${O} position.${O} prompt.${O} search.${O} signal.${O} \
+ tags.${O} ttyin.${O} version.${O} @REGEX_O@
+DISTFILES = ${SRC} regexp.c regexp.h \
+ INSTALL Makefile.in README NEWS \
+ configure configure.in acconfig.h lesskey.c \
+ cmd.h funcs.h less.h lesskey.h option.h position.h \
+ install.sh defines.h.in defines.h.top mkinstalldirs \
+ less.nro lesskey.nro less.man lesskey.man less.hlp \
+ Makefile.dos defines.dos doscreen.c \
+ Makefile.os2 defines.os2
+
+all: less lesskey
+
+less: ${OBJ}
+ ${CC} ${LDFLAGS} -o $@ ${OBJ} ${LIBS}
+
+lesskey: lesskey.${O} version.${O}
+ ${CC} ${LDFLAGS} -o $@ lesskey.${O} version.${O}
+
+${OBJ}: less.h defines.h funcs.h
+
+filename.${O}: filename.c
+ ${CC} -c -DHELPFILE=\"${datadir}/less.hlp\" -I. ${CPPFLAGS} ${CFLAGS} ${srcdir}/filename.c
+
+install: all less.hlp less.nro installdirs
+ ${INSTALL_PROGRAM} less ${bindir}/${binprefix}less
+ ${INSTALL_PROGRAM} lesskey ${bindir}/${binprefix}lesskey
+ ${INSTALL_DATA} ${srcdir}/less.hlp ${datadir}/less.hlp
+ ${INSTALL_DATA} ${srcdir}/less.nro ${mandir}/${manprefix}less.${manext}
+ ${INSTALL_DATA} ${srcdir}/lesskey.nro ${mandir}/${manprefix}lesskey.${manext}
+
+installdirs: mkinstalldirs
+ ${srcdir}/mkinstalldirs ${bindir} ${datadir} ${mandir}
+
+uninstall:
+ rm -f ${bindir}/${binprefix}less ${bindir}/${binprefix}lesskey
+ rm -f ${datadir}/less.hlp
+ rm -f ${mandir}/less.${manext} ${mandir}/lesskey.${manext}
+
+info:
+install-info:
+dvi:
+check:
+installcheck:
+
+TAGS:
+ cd ${srcdir} && etags *.c *.h
+
+# config.status might not change defines.h
+# Don't rerun config.status if we just configured (so there's no stamp-h).
+defines.h: stamp-h
+stamp-h: defines.h.in config.status
+ test ! -f stamp-h || CONFIG_FILES= CONFIG_HEADERS=defines.h ./config.status
+ touch stamp-h
+Makefile: Makefile.in config.status
+ CONFIG_FILES=Makefile CONFIG_HEADERS= ./config.status
+config.status: ${srcdir}/configure
+ ./config.status --recheck
+
+configure: configure.in
+ autoheader
+ autoconf
+
+lint:
+ lint -I. ${CPPFLAGS} ${SRC}
+newfuncs:
+ mv -f funcs.h funcs.h.old
+ awk -f mkfuncs.awk ${SRC} >funcs.h
+clean:
+ rm -f *.${O} core less lesskey
+
+mostlyclean: clean
+
+distclean: clean
+ rm -f Makefile config.status config.log config.cache defines.h stamp-h
+
+realclean: distclean
+ rm -f TAGS
+
+dist: ${DISTFILES}
+ if [ ! -d release ]; then mkdir release; fi
+ @REL=`sed -e '/char version/!d' -e 's/[^0-9.]*\([0-9.]*\).*/less-\1/' -e q version.c`; \
+ rm -rf release/$$REL; mkdir release/$$REL; \
+ echo "Creating release/$$REL/$$REL.tar.gz"; \
+ rm -rf $$REL; mkdir $$REL; \
+ for file in ${DISTFILES}; do \
+ ln $$file $$REL || \
+ { echo "cannot link, copying $$file"; cp -p $$file $$REL; }; \
+ done; \
+ tar -chf - $$REL | gzip -c >release/$$REL/$$REL.tar.gz; \
+ rm -rf $$REL
+
+tagall:
+ @REL=`sed -e '/char version/!d' -e 's/[^0-9.]*\([0-9.]*\).*/v\1/' -e q version.c`; \
+ echo "tagging $$REL"; \
+ $(RCS) -N$$REL: RCS/*,v
diff --git a/usr.bin/less/Makefile.os2 b/usr.bin/less/Makefile.os2
new file mode 100644
index 00000000000..e890f405eda
--- /dev/null
+++ b/usr.bin/less/Makefile.os2
@@ -0,0 +1,39 @@
+# Makefile for less.
+# OS/2 version, for emx+gcc compiler
+
+#### Start of system configuration section. ####
+
+CC = gcc -Zomf
+CFLAGS = -I. -O
+LDFLAGS = -s -Zcrtdll -Zstack 512
+LIBS = -ltermcap
+O = obj
+
+#### End of system configuration section. ####
+
+.SUFFIXES: .c .$(O)
+
+# This rule allows us to supply the necessary -D options
+# in addition to whatever the user asks for.
+.c.$(O):
+ ${CC} -c ${CPPFLAGS} ${CFLAGS} $<
+
+OBJ = main.$(O) screen.$(O) brac.$(O) ch.$(O) charset.$(O) cmdbuf.$(O) \
+ command.$(O) decode.$(O) edit.$(O) filename.$(O) forwback.$(O) \
+ help.$(O) ifile.$(O) input.$(O) jump.$(O) line.$(O) linenum.$(O) \
+ lsystem.$(O) mark.$(O) optfunc.$(O) option.$(O) opttbl.$(O) os.$(O) \
+ output.$(O) position.$(O) prompt.$(O) search.$(O) signal.$(O) \
+ tags.$(O) ttyin.$(O) version.$(O) regexp.$(O)
+
+all: less.exe lesskey.exe
+
+less.exe: $(OBJ)
+ $(CC) $(OBJ) -o $@ $(LDFLAGS) $(LIBS)
+
+lesskey.exe: lesskey.$(O) version.$(O)
+ $(CC) $(CFLAGS) lesskey.$(O) version.$(O) -o $@ $(LDFLAGS)
+
+$(OBJ): defines.h less.h
+
+defines.h: defines.os2
+ copy defines.os2 defines.h
diff --git a/usr.bin/less/NEWS b/usr.bin/less/NEWS
new file mode 100644
index 00000000000..f928d95a5e8
--- /dev/null
+++ b/usr.bin/less/NEWS
@@ -0,0 +1,239 @@
+ Major changes between "less" versions 278 and 290
+
+* Accepts GNU-style options "--help" and "--version".
+
+* OS/2 version looks for less.ini in $HOME before $INIT and $PATH.
+
+* Bug fixes
+
+======================================================================
+
+
+ Major changes between "less" versions 252 and 278
+
+* A LESSOPEN preprocessor may now pipe the converted file data to less,
+ rather than writing it to a temporary file.
+
+* Search pattern highlighting has been fixed. It now highlights
+ reliably, even if a string is split across two screen lines,
+ contains TABs, etc.
+
+* The -F flag (which suppress search highlighting) has been changed
+ to -G. A new flag, -g, changes search highlighting to highlight
+ only the string found by the last search command, instead of all
+ strings which match the last search command.
+
+* New flag -I acts like -i, but ignores case even if the search
+ pattern contains uppercase letters.
+
+* Less now checks for the environment variable VISUAL before EDITOR.
+
+* Ported to OS/2.
+
+======================================================================
+
+
+ Major changes between "less" versions 237 and 252
+
+* Changes in line-editing keys:
+ The literal key is now ^V or ^A rather than \ (backslash).
+ Filename completion commands (TAB and ^L) are disabled
+ when typing a search pattern.
+
+* Line-editing command keys can be redefined using lesskey.
+
+* Lesskey with no input file defaults to $HOME/.lesskey
+ rather than standard input.
+
+* New option -V displays version number of less.
+
+* New option -V displays version number of lesskey.
+
+* Help file less.hlp is now installed by default in /usr/local/share
+ rather than /usr/local/lib.
+
+
+======================================================================
+
+
+ Major changes between "less" versions 170 and 237
+
+* By popular demand, text which matches the current search pattern
+ is highlighted. New -F flag disables this feature.
+
+* Henry Spencer's regexp.c is now included, for systems which do not
+ have a regular expression library.
+ regexp.c is Copyright (c) 1986 by University of Toronto.
+
+* New line-editing keys, including command history (arrow keys) and
+ filename completion (TAB).
+
+* Input preprocessor allows modification of input files (e.g. uncompress)
+ via LESSOPEN/LESSCLOSE environment variables.
+
+* New -X flag disables sending termcap "ti" and "te" (initialize and
+ deinitialize) strings to the terminal.
+
+* Changing -i from within less now correctly affects a subsequent
+ repeated search.
+
+* Searching for underlined or overstruck text now works when the -u
+ flag is in effect, rather than the -i flag.
+
+* Use setlocale (LANG and LC_CTYPE environment variables) to determine
+ the character set if LESSCHARSET/LESSCHARDEF are not set.
+
+* The default format for displaying binary characters is now standout
+ (reverse video) rather than blinking. This can still be changed by
+ setting the LESSBINFMT environment variable.
+
+* Use autoconf installation technology.
+
+* Ported to MS-DOS.
+
+ ********************************
+ Things that may surprise you
+ ********************************
+
+* When you enter text at the bottom of the screen (search string,
+ filename, etc.), some keys act different than previously.
+ Specifically, \ (backslash), ESC, TAB, BACKTAB, and control-L
+ now have line editing functions.
+
+* Some previous unofficial versions of less were able to display
+ compressed files. The new LESSOPEN/LESSCLOSE feature now provides
+ this functionality in a different way.
+
+* Some previous unofficial versions of less provided a -Z flag to
+ set the number of lines of text to retain between full screen scrolls.
+ The -z-n flag (that is, -z with a negative number) provides this
+ functionality.
+
+
+======================================================================
+
+
+ Major changes between "less" versions 123 and 170
+
+* New option -j allows target lines to be positioned anywhere on screen.
+
+* New option -S truncates displayed line at the screen width,
+ rather than wrapping onto the next line.
+
+* New option -y limits amount of forward scroll.
+
+* New option -T specifies a "tags" file.
+
+* Non-printable, non-control characters are displayed in octal.
+ Such characters, as well as control characters, are displayed
+ in blinking mode.
+
+* New command -+ sets an option to its default.
+* New command -- sets an option to the opposite of its default.
+
+* Lesskey file may have a string appended to a key's action,
+ which acts as though typed in after the command.
+
+* New commands ESC-^F and ESC-^B match arbitrary types of brackets.
+
+* New command F monitors a growing file (like "tail -f").
+
+* New command | pipes a section of the input file into a shell command.
+
+* New command :x directly jumps to a file in the command line list.
+
+* Search commands have been enhanced and reorganized:
+ n Repeat search, same direction.
+ N Repeat search, opposite direction.
+ ESC-/ Search forward thru file boundaries
+ ESC-? Search backward thru file boundaries
+ ESC-n Repeat search thru file boundaries, same direction.
+ ESC-N Repeat search thru file boundaries, opposite direction.
+ Special character * causes search to search thru file boundaries.
+ Special character @ causes search to begin at start/end of file list.
+
+* Examining a new file adds it to the command line list.
+ A list of files, or an expression which matches more than one file,
+ may be examined; all of them are added to the command line list.
+
+* Environment variables LESSCHARSET and LESSCHARDEF can define
+ a non-ASCII character set.
+
+* Partial support for MSDOS, including options -R for repainting screen
+ on quit, -v/-V to select video mode, and -W to change window size.
+
+
+======================================================================
+
+
+ Major changes between "less" versions 97 and 123
+
+* New option (-N) causes line numbers to be displayed in the
+ text of the file (like vi "set nu").
+
+* New option (-?) prints help message immediately.
+
+* New option (-r) displays "raw" control characters, without
+ mapping them to ^X notation.
+
+* New option (-f) forces less to open non-regular files
+ (directories, etc).
+
+* New option (-k) can be used to specify lesskey files by name.
+
+* New option (-y) can be used to set a forward scroll limit
+ (like -h sets a backward scroll limit).
+
+* File marks (set by the m command) are now preserved when a new
+ file is edited. The ' command can thus be used to switch files.
+
+* New command ESC-/ searches all files (on the command line)
+ for a pattern.
+
+* New command ESC-n repeats previous search, spanning files.
+
+* The N command has been changed to repeat the previous search
+ in the reverse direction. The old N command is still available
+ via :n.
+
+* New command ESC-N repeats previous search in the reverse
+ direction and spanning files.
+
+* 8 bit characters are now supported. A new option (-g) can be
+ used to strip off the eighth bit (the previous behavior).
+
+* Options which take a following string (like -t) may now
+ optionally have a space between the option letter and the string.
+
+* Six new commands { } ( ) [ and ] can be used to match
+ brackets of specific types, similar to vi % command.
+
+* New commands z and w move forward/backward one window and
+ simultaneously set the window size.
+
+* Prompt string expansion now has %L for line number of the last
+ line in the file, and %E for the name of the editor.
+ Also, % escapes which refer to a line (b=bottom, t=top, etc.)
+ can use j for the jump target line.
+
+* New environment variable LESSEDIT can be used to tailor the
+ command string passed to the editor by the v command.
+
+* Examining a file which was previously examined will return
+ to the same position in the file.
+
+* A "%" is expanded to the current filename and a "#" to the
+ previous filename, in both shell commands and the E command.
+ (Previously % worked only in shell commands and # worked
+ only in the E command.)
+
+* New command ":ta" is equivalent to "-t".
+
+* New command "s" is equivalent to "-l".
+
+* The - command may be followed by "+X" to revert to the default
+ for option X, or "-X" to get the opposite of the default.
+
+* Lesskey files may now include characters after the action as
+ extra input to be parsed after the action; for example:
+ "toggle-option X" to toggle a specific option X.
diff --git a/usr.bin/less/README b/usr.bin/less/README
new file mode 100644
index 00000000000..0419a52f6e7
--- /dev/null
+++ b/usr.bin/less/README
@@ -0,0 +1,127 @@
+=======================================================================
+=== NOTE: THIS IS A DISTRIBUTION OF less (version 290) ===
+=== PLEASE REPORT ANY PROBLEMS TO THE AUTHOR AT markn@3do.com. ===
+=======================================================================
+
+This is the distribution of "less", a paginator similar to "more" or "pg".
+The formatted manual page is in less.man.
+The manual page nroff source is in less.nro.
+Major changes made since the last posted version are in NEWS.
+
+=======================================================================
+INSTALLATION (Unix systems only):
+
+1. Move the distributed source to its own directory and
+ unpack it by running "sh" or "unshar" on the distribution
+ files, if you have not already done so.
+
+2. Type "sh configure".
+ This will generate a Makefile and a defines.h.
+ Warning: if you have a GNU sed, make sure it is version 2.05 or later.
+
+3. It is a good idea to look over the generated Makefile and defines.h
+ and make sure they look ok. If you know of any peculiarities of
+ your system that configure might not have detected, you may fix the
+ Makefile now.
+
+ If you wish, you may edit defines.h to remove some optional features.
+ If you wish to build a "secure" version of less (which disables all
+ features which might allow a user to do unintended things to the system
+ on which less is running), edit defines.h and define SECURE to 1.
+ If you choose not to include some features in your version, you may
+ wish to edit the manual page "less.nro" and the help page "less.hlp"
+ to remove the descriptions of the features which you are removing.
+
+4. Type "make" and watch the fun.
+
+ If you want less to find the help file in a directory other than
+ the default (/usr/local/share), define "datadir" to that directory.
+ For example: "make datadir=/usr/local/helpfiles"
+
+5. If the make succeeds, it will generate a program "less"
+ in your current directory. Test the generated program.
+
+6. When satisfied that it works, if you wish to install it
+ in a public place, type "make install".
+
+ The default install destinations are:
+ Executables (less, lesskey) in /usr/local/bin
+ Data files (less.hlp) in /usr/local/share
+ Documentation (less.nro, lesskey.nro) in /usr/local/man/man1
+ If you want to install any of these files elsewhere, define
+ bindir, datadir, and/or mandir to the appropriate directories.
+ datadir must be defined the same as in step 4.
+
+If you have any problems building or running "less", suggestions,
+complaints, etc., you may mail to the author at markn@3do.com
+
+Note to hackers: comments noting possible improvements are enclosed
+in double curly brackets {{ like this }}.
+
+
+
+=======================================================================
+INSTALLATION (MS-DOS systems only)
+
+1. Move the distributed source to its own directory.
+ Make sure the source has been converted to have CR-LF rather than
+ LF as line terminators.
+
+2. Rename MAKEFILE.DOS to MAKEFILE.
+
+3. Look at MAKEFILE to make sure that the definitions for CC and LIBDIR
+ are correct. CC should be the name of your Microsoft C compiler and
+ LIBDIR should be the directory where the Microsoft C libraries
+ reside. If these definitions need to be changed, you can either
+ modify the definitions directly in MAKEFILE, or set your environment
+ variables CC and/or LIBDIR to override the definitions in MAKEFILE.
+
+4. If you wish, you may edit DEFINES.DOS to remove some optional features.
+ If you choose not to include some features in your version, you may
+ wish to edit the manual page LESS.MAN and the help page LESS.HLP
+ to remove the descriptions of the features which you are removing.
+
+5. Type "make" and watch the fun.
+ You may need to invoke "make -e", if your "make" requires the -e
+ flag to import environment variables.
+ If your compiler runs out of memory, try running "make -n >cmds.bat"
+ and then run cmds.bat.
+
+6. If the make succeeds, it will generate a program "LESS.EXE"
+ in your current directory. Test the generated program.
+
+7. When satisfied that it works, you may wish to install LESS.EXE,
+ LESSKEY.EXE and LESS.HLP in a directory which is included in
+ your PATH.
+
+
+
+=======================================================================
+INSTALLATION (OS/2 systems only)
+
+1. Move the distributed source to its own directory.
+
+2. Rename Makefile.os2 to Makefile.
+
+3. Check the Makefile to make sure the definitions look ok.
+
+4. If you wish, you may edit defines.os2 to remove some optional features.
+ If you choose not to include some features in your version, you may
+ wish to edit the manual page less.man and the help page less.hlp
+ to remove the descriptions of the features which you are removing.
+
+5. Type "make" and watch the fun.
+
+6. If the make succeeds, it will generate a program "less.exe"
+ in your current directory. Test the generated program.
+
+7. Make sure you have the emx runtime installed. You need the emx DLLs
+ emx.dll and emxlibcs.dll and also the termcap database, termcap.dat.
+ Make sure you have termcap.dat either in the default location or
+ somewhere in a directory listed in the PATH or INIT environment
+ variables.
+
+8. When satisfied that it works, you may wish to install less.exe,
+ lesskey.exe and less.hlp in a directory which is included in
+ your PATH.
+
diff --git a/usr.bin/less/acconfig.h b/usr.bin/less/acconfig.h
new file mode 100644
index 00000000000..9b3721d7280
--- /dev/null
+++ b/usr.bin/less/acconfig.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * Regular expression library.
+ * Define exactly one of the following to be 1:
+ * HAVE_POSIX_REGCOMP: POSIX regcomp() and regex.h
+ * HAVE_RE_COMP: BSD re_comp()
+ * HAVE_REGCMP: System V regcmp()
+ * HAVE_V8_REGCOMP: Henry Spencer V8 regcomp() and regexp.h
+ * NO_REGEX: pattern matching is supported, but without metacharacters.
+ */
+#undef HAVE_POSIX_REGCOMP
+#undef HAVE_RE_COMP
+#undef HAVE_REGCMP
+#undef HAVE_V8_REGCOMP
+#undef NO_REGEX
+
+/* Define HAVE_VOID if your compiler supports the "void" type. */
+#undef HAVE_VOID
+
+/* Define HAVE_TIME_T if your system supports the "time_t" type. */
+#undef HAVE_TIME_T
+
+/* Define HAVE_STRERROR if you have the strerror() function. */
+#undef HAVE_STRERROR
+
+/* Define HAVE_FILENO if you have the fileno() macro. */
+#undef HAVE_FILENO
+
+/* Define HAVE_ERRNO if you have the errno variable */
+#undef HAVE_ERRNO
+
+/* Define HAVE_SYS_ERRLIST if you have the sys_errlist[] variable */
+#undef HAVE_SYS_ERRLIST
+
+/* Define HAVE_OSPEED if your termcap library has the ospeed variable */
+#undef HAVE_OSPEED
+/* Define MUST_DEFINE_OSPEED if you have ospeed but it is not defined
+ * in termcap.h. */
+#undef MUST_DEFINE_OSPEED
+
+/* Define HAVE_LOCALE if you have locale.h and setlocale. */
+#undef HAVE_LOCALE
+
+/* Define HAVE_TERMIOS_FUNCS if you have tcgetattr/tcsetattr */
+#undef HAVE_TERMIOS_FUNCS
+
+/* Define HAVE_UPPER_LOWER if you have isupper, islower, toupper, tolower */
+#undef HAVE_UPPER_LOWER
diff --git a/usr.bin/less/brac.c b/usr.bin/less/brac.c
new file mode 100644
index 00000000000..1d484b83bce
--- /dev/null
+++ b/usr.bin/less/brac.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * Routines to perform bracket matching functions.
+ */
+
+#include "less.h"
+#include "position.h"
+
+/*
+ * Try to match the n-th open bracket
+ * which appears in the top displayed line (forwdir),
+ * or the n-th close bracket
+ * which appears in the bottom displayed line (!forwdir).
+ * The characters which serve as "open bracket" and
+ * "close bracket" are given.
+ */
+ public void
+match_brac(obrac, cbrac, forwdir, n)
+ register int obrac;
+ register int cbrac;
+ int forwdir;
+ int n;
+{
+ register int c;
+ register int nest;
+ POSITION pos;
+ int (*chget)();
+
+ extern int ch_forw_get(), ch_back_get();
+
+ /*
+ * Seek to the line containing the open bracket.
+ * This is either the top or bottom line on the screen,
+ * depending on the type of bracket.
+ */
+ pos = position((forwdir) ? TOP : BOTTOM);
+ if (pos == NULL_POSITION || ch_seek(pos))
+ {
+ if (forwdir)
+ error("Nothing in top line", NULL_PARG);
+ else
+ error("Nothing in bottom line", NULL_PARG);
+ return;
+ }
+
+ /*
+ * Look thru the line to find the open bracket to match.
+ */
+ do
+ {
+ if ((c = ch_forw_get()) == '\n' || c == EOI)
+ {
+ if (forwdir)
+ error("No bracket in top line", NULL_PARG);
+ else
+ error("No bracket in bottom line", NULL_PARG);
+ return;
+ }
+ } while (c != obrac || --n > 0);
+
+ /*
+ * Position the file just "after" the open bracket
+ * (in the direction in which we will be searching).
+ * If searching forward, we are already after the bracket.
+ * If searching backward, skip back over the open bracket.
+ */
+ if (!forwdir)
+ (void) ch_back_get();
+
+ /*
+ * Search the file for the matching bracket.
+ */
+ chget = (forwdir) ? ch_forw_get : ch_back_get;
+ nest = 0;
+ while ((c = (*chget)()) != EOI)
+ {
+ if (c == obrac)
+ nest++;
+ else if (c == cbrac && --nest < 0)
+ {
+ /*
+ * Found the matching bracket.
+ * If searching backward, put it on the top line.
+ * If searching forward, put it on the bottom line.
+ */
+ jump_line_loc(ch_tell(), forwdir ? -1 : 1);
+ return;
+ }
+ }
+ error("No matching bracket", NULL_PARG);
+}
diff --git a/usr.bin/less/ch.c b/usr.bin/less/ch.c
new file mode 100644
index 00000000000..b1d35aa9fea
--- /dev/null
+++ b/usr.bin/less/ch.c
@@ -0,0 +1,763 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * Low level character input from the input file.
+ * We use these special purpose routines which optimize moving
+ * both forward and backward from the current read pointer.
+ */
+
+#include "less.h"
+
+public int ignore_eoi;
+
+/*
+ * Pool of buffers holding the most recently used blocks of the input file.
+ * The buffer pool is kept as a doubly-linked circular list,
+ * in order from most- to least-recently used.
+ * The circular list is anchored by the file state "thisfile".
+ */
+#define LBUFSIZE 1024
+struct buf {
+ struct buf *next, *prev; /* Must be first to match struct filestate */
+ long block;
+ unsigned int datasize;
+ unsigned char data[LBUFSIZE];
+};
+
+/*
+ * The file state is maintained in a filestate structure.
+ * A pointer to the filestate is kept in the ifile structure.
+ */
+struct filestate {
+ /* -- Following members must match struct buf */
+ struct buf *buf_next, *buf_prev;
+ long buf_block;
+ /* -- End of struct buf copy */
+ int file;
+ int flags;
+ POSITION fpos;
+ int nbufs;
+ long block;
+ int offset;
+ POSITION fsize;
+};
+
+
+#define END_OF_CHAIN ((struct buf *)thisfile)
+#define ch_bufhead thisfile->buf_next
+#define ch_buftail thisfile->buf_prev
+#define ch_nbufs thisfile->nbufs
+#define ch_block thisfile->block
+#define ch_offset thisfile->offset
+#define ch_fpos thisfile->fpos
+#define ch_fsize thisfile->fsize
+#define ch_flags thisfile->flags
+#define ch_file thisfile->file
+
+static struct filestate *thisfile;
+static int ch_ungotchar = -1;
+
+extern int autobuf;
+extern int sigs;
+extern int cbufs;
+extern IFILE curr_ifile;
+#if LOGFILE
+extern int logfile;
+extern char *namelogfile;
+#endif
+
+static int ch_addbuf();
+
+
+/*
+ * Get the character pointed to by the read pointer.
+ * ch_get() is a macro which is more efficient to call
+ * than fch_get (the function), in the usual case
+ * that the block desired is at the head of the chain.
+ */
+#define ch_get() ((ch_block == ch_bufhead->block && \
+ ch_offset < ch_bufhead->datasize) ? \
+ ch_bufhead->data[ch_offset] : fch_get())
+ int
+fch_get()
+{
+ register struct buf *bp;
+ register int n;
+ register int slept;
+ POSITION pos;
+ POSITION len;
+
+ slept = FALSE;
+
+ /*
+ * Look for a buffer holding the desired block.
+ */
+ for (bp = ch_bufhead; bp != END_OF_CHAIN; bp = bp->next)
+ if (bp->block == ch_block)
+ {
+ if (ch_offset >= bp->datasize)
+ /*
+ * Need more data in this buffer.
+ */
+ goto read_more;
+ goto found;
+ }
+ /*
+ * Block is not in a buffer.
+ * Take the least recently used buffer
+ * and read the desired block into it.
+ * If the LRU buffer has data in it,
+ * then maybe allocate a new buffer.
+ */
+ if (ch_buftail == END_OF_CHAIN || ch_buftail->block != (long)(-1))
+ {
+ /*
+ * There is no empty buffer to use.
+ * Allocate a new buffer if:
+ * 1. We can't seek on this file and -b is not in effect; or
+ * 2. We haven't allocated the max buffers for this file yet.
+ */
+ if ((autobuf && !(ch_flags & CH_CANSEEK)) ||
+ (cbufs == -1 || ch_nbufs < cbufs))
+ if (ch_addbuf())
+ /*
+ * Allocation failed: turn off autobuf.
+ */
+ autobuf = OPT_OFF;
+ }
+ bp = ch_buftail;
+ bp->block = ch_block;
+ bp->datasize = 0;
+
+ read_more:
+ pos = (ch_block * LBUFSIZE) + bp->datasize;
+ if ((len = ch_length()) != NULL_POSITION && pos >= len)
+ /*
+ * At end of file.
+ */
+ return (EOI);
+
+ if (pos != ch_fpos)
+ {
+ /*
+ * Not at the correct position: must seek.
+ * If input is a pipe, we're in trouble (can't seek on a pipe).
+ * Some data has been lost: just return "?".
+ */
+ if (!(ch_flags & CH_CANSEEK))
+ return ('?');
+ if (lseek(ch_file, (off_t)pos, 0) == BAD_LSEEK)
+ {
+ error("seek error", NULL_PARG);
+ clear_eol();
+ return (EOI);
+ }
+ ch_fpos = pos;
+ }
+
+ /*
+ * Read the block.
+ * If we read less than a full block, that's ok.
+ * We use partial block and pick up the rest next time.
+ */
+ if (ch_ungotchar == -1)
+ {
+ n = iread(ch_file, &bp->data[bp->datasize],
+ (unsigned int)(LBUFSIZE - bp->datasize));
+ } else
+ {
+ bp->data[bp->datasize] = ch_ungotchar;
+ n = 1;
+ ch_ungotchar = -1;
+ }
+
+ if (n == READ_INTR)
+ return (EOI);
+ if (n < 0)
+ {
+ error("read error", NULL_PARG);
+ clear_eol();
+ n = 0;
+ }
+
+#if LOGFILE
+ /*
+ * If we have a log file, write the new data to it.
+ */
+ if (logfile >= 0 && n > 0)
+ write(logfile, (char *) &bp->data[bp->datasize], n);
+#endif
+
+ ch_fpos += n;
+ bp->datasize += n;
+
+ /*
+ * If we have read to end of file, set ch_fsize to indicate
+ * the position of the end of file.
+ */
+ if (n == 0)
+ {
+ ch_fsize = pos;
+ if (ignore_eoi)
+ {
+ /*
+ * We are ignoring EOF.
+ * Wait a while, then try again.
+ */
+ if (!slept)
+ ierror("Waiting for data", NULL_PARG);
+#if !MSOFTC
+ sleep(1);
+#endif
+ slept = TRUE;
+ }
+ if (ABORT_SIGS())
+ return (EOI);
+ }
+
+ found:
+ if (ch_bufhead != bp)
+ {
+ /*
+ * Move the buffer to the head of the buffer chain.
+ * This orders the buffer chain, most- to least-recently used.
+ */
+ bp->next->prev = bp->prev;
+ bp->prev->next = bp->next;
+
+ bp->next = ch_bufhead;
+ bp->prev = END_OF_CHAIN;
+ ch_bufhead->prev = bp;
+ ch_bufhead = bp;
+ }
+
+ if (ch_offset >= bp->datasize)
+ /*
+ * After all that, we still don't have enough data.
+ * Go back and try again.
+ */
+ goto read_more;
+
+ return (bp->data[ch_offset]);
+}
+
+/*
+ * ch_ungetchar is a rather kludgy and limited way to push
+ * a single char onto an input file descriptor.
+ */
+ public void
+ch_ungetchar(c)
+ int c;
+{
+ if (c != -1 && ch_ungotchar != -1)
+ error("ch_ungetchar overrun", NULL_PARG);
+ ch_ungotchar = c;
+}
+
+#if LOGFILE
+/*
+ * Close the logfile.
+ * If we haven't read all of standard input into it, do that now.
+ */
+ public void
+end_logfile()
+{
+ static int tried = FALSE;
+
+ if (logfile < 0)
+ return;
+ if (!tried && ch_fsize == NULL_POSITION)
+ {
+ tried = TRUE;
+ ierror("Finishing logfile", NULL_PARG);
+ while (ch_forw_get() != EOI)
+ if (ABORT_SIGS())
+ break;
+ }
+ close(logfile);
+ logfile = -1;
+ namelogfile = NULL;
+}
+
+/*
+ * Start a log file AFTER less has already been running.
+ * Invoked from the - command; see toggle_option().
+ * Write all the existing buffered data to the log file.
+ */
+ public void
+sync_logfile()
+{
+ register struct buf *bp;
+ int warned = FALSE;
+ long block;
+ long nblocks;
+
+ nblocks = (ch_fpos + LBUFSIZE - 1) / LBUFSIZE;
+ for (block = 0; block < nblocks; block++)
+ {
+ for (bp = ch_bufhead; ; bp = bp->next)
+ {
+ if (bp == END_OF_CHAIN)
+ {
+ if (!warned)
+ {
+ error("Warning: log file is incomplete",
+ NULL_PARG);
+ warned = TRUE;
+ }
+ break;
+ }
+ if (bp->block == block)
+ {
+ write(logfile, (char *) bp->data, bp->datasize);
+ break;
+ }
+ }
+ }
+}
+
+#endif
+
+/*
+ * Determine if a specific block is currently in one of the buffers.
+ */
+ static int
+buffered(block)
+ long block;
+{
+ register struct buf *bp;
+
+ for (bp = ch_bufhead; bp != END_OF_CHAIN; bp = bp->next)
+ if (bp->block == block)
+ return (TRUE);
+ return (FALSE);
+}
+
+/*
+ * Seek to a specified position in the file.
+ * Return 0 if successful, non-zero if can't seek there.
+ */
+ public int
+ch_seek(pos)
+ register POSITION pos;
+{
+ long new_block;
+ POSITION len;
+
+ len = ch_length();
+ if (pos < ch_zero() || (len != NULL_POSITION && pos > len))
+ return (1);
+
+ new_block = pos / LBUFSIZE;
+ if (!(ch_flags & CH_CANSEEK) && pos != ch_fpos && !buffered(new_block))
+ {
+ if (ch_fpos > pos)
+ return (1);
+ while (ch_fpos < pos)
+ {
+ if (ch_forw_get() == EOI)
+ return (1);
+ if (ABORT_SIGS())
+ return (1);
+ }
+ return (0);
+ }
+ /*
+ * Set read pointer.
+ */
+ ch_block = new_block;
+ ch_offset = pos % LBUFSIZE;
+ return (0);
+}
+
+/*
+ * Seek to the end of the file.
+ */
+ public int
+ch_end_seek()
+{
+ POSITION len;
+
+ if (ch_flags & CH_CANSEEK)
+ ch_fsize = filesize(ch_file);
+
+ len = ch_length();
+ if (len != NULL_POSITION)
+ return (ch_seek(len));
+
+ /*
+ * Do it the slow way: read till end of data.
+ */
+ while (ch_forw_get() != EOI)
+ if (ABORT_SIGS())
+ return (1);
+ return (0);
+}
+
+/*
+ * Seek to the beginning of the file, or as close to it as we can get.
+ * We may not be able to seek there if input is a pipe and the
+ * beginning of the pipe is no longer buffered.
+ */
+ public int
+ch_beg_seek()
+{
+ register struct buf *bp, *firstbp;
+
+ /*
+ * Try a plain ch_seek first.
+ */
+ if (ch_seek(ch_zero()) == 0)
+ return (0);
+
+ /*
+ * Can't get to position 0.
+ * Look thru the buffers for the one closest to position 0.
+ */
+ firstbp = bp = ch_bufhead;
+ if (bp == END_OF_CHAIN)
+ return (1);
+ while ((bp = bp->next) != END_OF_CHAIN)
+ if (bp->block < firstbp->block)
+ firstbp = bp;
+ ch_block = firstbp->block;
+ ch_offset = 0;
+ return (0);
+}
+
+/*
+ * Return the length of the file, if known.
+ */
+ public POSITION
+ch_length()
+{
+ if (ignore_eoi)
+ return (NULL_POSITION);
+ return (ch_fsize);
+}
+
+/*
+ * Return the current position in the file.
+ */
+#define tellpos(blk,off) ((POSITION)((((long)(blk)) * LBUFSIZE) + (off)))
+
+ public POSITION
+ch_tell()
+{
+ return (tellpos(ch_block, ch_offset));
+}
+
+/*
+ * Get the current char and post-increment the read pointer.
+ */
+ public int
+ch_forw_get()
+{
+ register int c;
+
+ c = ch_get();
+ if (c == EOI)
+ return (EOI);
+ if (ch_offset < LBUFSIZE-1)
+ ch_offset++;
+ else
+ {
+ ch_block ++;
+ ch_offset = 0;
+ }
+ return (c);
+}
+
+/*
+ * Pre-decrement the read pointer and get the new current char.
+ */
+ public int
+ch_back_get()
+{
+ if (ch_offset > 0)
+ ch_offset --;
+ else
+ {
+ if (ch_block <= 0)
+ return (EOI);
+ if (!(ch_flags & CH_CANSEEK) && !buffered(ch_block-1))
+ return (EOI);
+ ch_block--;
+ ch_offset = LBUFSIZE-1;
+ }
+ return (ch_get());
+}
+
+/*
+ * Allocate buffers.
+ * Caller wants us to have a total of at least want_nbufs buffers.
+ */
+ public int
+ch_nbuf(want_nbufs)
+ int want_nbufs;
+{
+ PARG parg;
+
+ while (ch_nbufs < want_nbufs)
+ {
+ if (ch_addbuf())
+ {
+ /*
+ * Cannot allocate enough buffers.
+ * If we don't have ANY, then quit.
+ * Otherwise, just report the error and return.
+ */
+ parg.p_int = want_nbufs - ch_nbufs;
+ error("Cannot allocate %d buffers", &parg);
+ if (ch_nbufs == 0)
+ quit(QUIT_ERROR);
+ break;
+ }
+ }
+ return (ch_nbufs);
+}
+
+/*
+ * Flush (discard) any saved file state, including buffer contents.
+ */
+ public void
+ch_flush()
+{
+ register struct buf *bp;
+
+ if (!(ch_flags & CH_CANSEEK))
+ {
+ /*
+ * If input is a pipe, we don't flush buffer contents,
+ * since the contents can't be recovered.
+ */
+ ch_fsize = NULL_POSITION;
+ return;
+ }
+
+ /*
+ * Initialize all the buffers.
+ */
+ for (bp = ch_bufhead; bp != END_OF_CHAIN; bp = bp->next)
+ bp->block = (long)(-1);
+
+ /*
+ * Figure out the size of the file, if we can.
+ */
+ ch_fsize = filesize(ch_file);
+
+ /*
+ * Seek to a known position: the beginning of the file.
+ */
+ ch_fpos = 0;
+ ch_block = 0; /* ch_fpos / LBUFSIZE; */
+ ch_offset = 0; /* ch_fpos % LBUFSIZE; */
+
+ if (lseek(ch_file, (off_t)0, 0) == BAD_LSEEK)
+ {
+ /*
+ * Warning only; even if the seek fails for some reason,
+ * there's a good chance we're at the beginning anyway.
+ * {{ I think this is bogus reasoning. }}
+ */
+ error("seek error to 0", NULL_PARG);
+ }
+}
+
+/*
+ * Allocate a new buffer.
+ * The buffer is added to the tail of the buffer chain.
+ */
+ static int
+ch_addbuf()
+{
+ register struct buf *bp;
+
+ /*
+ * Allocate and initialize a new buffer and link it
+ * onto the tail of the buffer list.
+ */
+ bp = (struct buf *) calloc(1, sizeof(struct buf));
+ if (bp == NULL)
+ return (1);
+ ch_nbufs++;
+ bp->block = (long)(-1);
+ bp->next = END_OF_CHAIN;
+ bp->prev = ch_buftail;
+ ch_buftail->next = bp;
+ ch_buftail = bp;
+ return (0);
+}
+
+/*
+ * Delete all buffers for this file.
+ */
+ static void
+ch_delbufs()
+{
+ register struct buf *bp;
+
+ while (ch_bufhead != END_OF_CHAIN)
+ {
+ bp = ch_bufhead;
+ bp->next->prev = bp->prev;;
+ bp->prev->next = bp->next;
+ free(bp);
+ }
+ ch_nbufs = 0;
+}
+
+/*
+ * Is it possible to seek on a file descriptor?
+ */
+ public int
+seekable(f)
+ int f;
+{
+ return (lseek(f, (off_t)1, 0) != BAD_LSEEK);
+}
+
+/*
+ * Initialize file state for a new file.
+ */
+ public void
+ch_init(f, flags)
+ int f;
+ int flags;
+{
+ /*
+ * See if we already have a filestate for this file.
+ */
+ thisfile = (struct filestate *) get_filestate(curr_ifile);
+ if (thisfile == NULL)
+ {
+ /*
+ * Allocate and initialize a new filestate.
+ */
+ thisfile = (struct filestate *)
+ calloc(1, sizeof(struct filestate));
+ thisfile->buf_next = thisfile->buf_prev = END_OF_CHAIN;
+ thisfile->buf_block = (long)(-1);
+ thisfile->nbufs = 0;
+ thisfile->flags = 0;
+ thisfile->fpos = 0;
+ thisfile->block = 0;
+ thisfile->offset = 0;
+ thisfile->file = -1;
+ thisfile->fsize = NULL_POSITION;
+ ch_flags = flags;
+ /*
+ * Try to seek; set CH_CANSEEK if it works.
+ */
+ if (seekable(f))
+ ch_flags |= CH_CANSEEK;
+ set_filestate(curr_ifile, (void *) thisfile);
+ }
+ if (thisfile->file == -1)
+ thisfile->file = f;
+ ch_flush();
+}
+
+/*
+ * Close a filestate.
+ */
+ public void
+ch_close()
+{
+ int keepstate = FALSE;
+
+ if (ch_flags & (CH_CANSEEK|CH_POPENED))
+ {
+ /*
+ * We can seek or re-open, so we don't need to keep buffers.
+ */
+ ch_delbufs();
+ } else
+ keepstate = TRUE;
+ if (!(ch_flags & CH_KEEPOPEN))
+ {
+ /*
+ * We don't need to keep the file descriptor open
+ * (because we can re-open it.)
+ * But don't really close it if it was opened via popen(),
+ * because pclose() wants to close it.
+ */
+ if (!(ch_flags & CH_POPENED))
+ close(ch_file);
+ ch_file = -1;
+ } else
+ keepstate = TRUE;
+ if (!keepstate)
+ {
+ /*
+ * We don't even need to keep the filestate structure.
+ */
+ free(thisfile);
+ thisfile = NULL;
+ set_filestate(curr_ifile, (void *) NULL);
+ }
+}
+
+/*
+ * Return ch_flags for the current file.
+ */
+ public int
+ch_getflags()
+{
+ return (ch_flags);
+}
+
+#if 0
+ public void
+ch_dump(struct filestate *fs)
+{
+ struct buf *bp;
+ unsigned char *s;
+
+ if (fs == NULL)
+ {
+ printf(" --no filestate\n");
+ return;
+ }
+ printf(" file %d, flags %x, fpos %x, fsize %x, blk/off %x/%x\n",
+ fs->file, fs->flags, fs->fpos,
+ fs->fsize, fs->block, fs->offset);
+ printf(" %d bufs:\n", fs->nbufs);
+ for (bp = fs->buf_next; bp != (struct buf *)fs; bp = bp->next)
+ {
+ printf("%x: blk %x, size %x \"",
+ bp, bp->block, bp->datasize);
+ for (s = bp->data; s < bp->data + 30; s++)
+ if (*s >= ' ' && *s < 0x7F)
+ printf("%c", *s);
+ else
+ printf(".");
+ printf("\"\n");
+ }
+}
+#endif
diff --git a/usr.bin/less/charset.c b/usr.bin/less/charset.c
new file mode 100644
index 00000000000..42f3fff158a
--- /dev/null
+++ b/usr.bin/less/charset.c
@@ -0,0 +1,287 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * Functions to define the character set
+ * and do things specific to the character set.
+ */
+
+#include "less.h"
+#if HAVE_LOCALE
+#include <locale.h>
+#include <ctype.h>
+#endif
+
+/*
+ * Predefined character sets,
+ * selected by the LESSCHARSET environment variable.
+ */
+struct charset {
+ char *name;
+ char *desc;
+} charsets[] = {
+ { "ascii", "8bcccbcc18b95.b" },
+ { "latin1", "8bcccbcc18b95.33b." },
+ { "dos", "8bcccbcc12bc5b95.b." },
+ { "koi8-r", "8bcccbcc18b95.b128." },
+ { "next", "8bcccbcc18b95.bb125.bb" },
+ { NULL }
+};
+
+#define IS_BINARY_CHAR 01
+#define IS_CONTROL_CHAR 02
+
+static char chardef[256];
+static char *binfmt = NULL;
+public int binattr = AT_STANDOUT;
+
+
+/*
+ * Define a charset, given a description string.
+ * The string consists of 256 letters,
+ * one for each character in the charset.
+ * If the string is shorter than 256 letters, missing letters
+ * are taken to be identical to the last one.
+ * A decimal number followed by a letter is taken to be a
+ * repetition of the letter.
+ *
+ * Each letter is one of:
+ * . normal character
+ * b binary character
+ * c control character
+ */
+ static void
+ichardef(s)
+ char *s;
+{
+ register char *cp;
+ register int n;
+ register char v;
+
+ n = 0;
+ v = 0;
+ cp = chardef;
+ while (*s != '\0')
+ {
+ switch (*s++)
+ {
+ case '.':
+ v = 0;
+ break;
+ case 'c':
+ v = IS_CONTROL_CHAR;
+ break;
+ case 'b':
+ v = IS_BINARY_CHAR|IS_CONTROL_CHAR;
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ n = (10 * n) + (s[-1] - '0');
+ continue;
+
+ default:
+ error("invalid chardef", NULL_PARG);
+ quit(QUIT_ERROR);
+ /*NOTREACHED*/
+ }
+
+ do
+ {
+ if (cp >= chardef + sizeof(chardef))
+ {
+ error("chardef longer than 256", NULL_PARG);
+ quit(QUIT_ERROR);
+ /*NOTREACHED*/
+ }
+ *cp++ = v;
+ } while (--n > 0);
+ n = 0;
+ }
+
+ while (cp < chardef + sizeof(chardef))
+ *cp++ = v;
+}
+
+/*
+ * Define a charset, given a charset name.
+ * The valid charset names are listed in the "charsets" array.
+ */
+ static int
+icharset(name)
+ register char *name;
+{
+ register struct charset *p;
+
+ if (name == NULL || *name == '\0')
+ return (0);
+
+ for (p = charsets; p->name != NULL; p++)
+ {
+ if (strcmp(name, p->name) == 0)
+ {
+ ichardef(p->desc);
+ return (1);
+ }
+ }
+
+ error("invalid charset name", NULL_PARG);
+ quit(QUIT_ERROR);
+ /*NOTREACHED*/
+}
+
+#if HAVE_LOCALE
+/*
+ * Define a charset, given a locale name.
+ */
+ static void
+ilocale()
+{
+ register int c;
+
+ setlocale(LC_CTYPE, "");
+ for (c = 0; c < sizeof(chardef); c++)
+ {
+ if (isprint(c))
+ chardef[c] = 0;
+ else if (iscntrl(c))
+ chardef[c] = IS_CONTROL_CHAR;
+ else
+ chardef[c] = IS_BINARY_CHAR|IS_CONTROL_CHAR;
+ }
+}
+#endif
+
+/*
+ * Define the printing format for control chars.
+ */
+ public void
+setbinfmt(s)
+ char *s;
+{
+ if (s == NULL || *s == '\0')
+ s = "*s<%X>";
+ /*
+ * Select the attributes if it starts with "*".
+ */
+ if (*s == '*')
+ {
+ switch (s[1])
+ {
+ case 'd': binattr = AT_BOLD; break;
+ case 'k': binattr = AT_BLINK; break;
+ case 's': binattr = AT_STANDOUT; break;
+ case 'u': binattr = AT_UNDERLINE; break;
+ default: binattr = AT_NORMAL; break;
+ }
+ s += 2;
+ }
+ binfmt = s;
+}
+
+/*
+ * Initialize charset data structures.
+ */
+ public void
+init_charset()
+{
+ register char *s;
+
+ s = getenv("LESSBINFMT");
+ setbinfmt(s);
+
+ /*
+ * See if environment variable LESSCHARSET is defined.
+ */
+ s = getenv("LESSCHARSET");
+ if (icharset(s))
+ return;
+ /*
+ * LESSCHARSET is not defined: try LESSCHARDEF.
+ */
+ s = getenv("LESSCHARDEF");
+ if (s != NULL && *s != '\0')
+ {
+ ichardef(s);
+ return;
+ }
+#if HAVE_LOCALE
+ /*
+ * Use setlocale.
+ */
+ ilocale();
+#else
+ /*
+ * Default to "ascii".
+ */
+ (void) icharset("ascii");
+#endif
+}
+
+/*
+ * Is a given character a "binary" character?
+ */
+ public int
+binary_char(c)
+ int c;
+{
+ c &= 0377;
+ return (chardef[c] & IS_BINARY_CHAR);
+}
+
+/*
+ * Is a given character a "control" character?
+ */
+ public int
+control_char(c)
+ int c;
+{
+ c &= 0377;
+ return (chardef[c] & IS_CONTROL_CHAR);
+}
+
+/*
+ * Return the printable form of a character.
+ * For example, in the "ascii" charset '\3' is printed as "^C".
+ */
+ public char *
+prchar(c)
+ int c;
+{
+ static char buf[8];
+
+ c &= 0377;
+ if (!control_char(c))
+ sprintf(buf, "%c", c);
+ else if (c == ESC)
+ sprintf(buf, "ESC");
+ else if (c < 128 && !control_char(c ^ 0100))
+ sprintf(buf, "^%c", c ^ 0100);
+ else
+ sprintf(buf, binfmt, c);
+ return (buf);
+}
diff --git a/usr.bin/less/cmd.h b/usr.bin/less/cmd.h
new file mode 100644
index 00000000000..081b1aa8af6
--- /dev/null
+++ b/usr.bin/less/cmd.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#define MAX_USERCMD 500
+#define MAX_CMDLEN 16
+
+#define A_B_LINE 2
+#define A_B_SCREEN 3
+#define A_B_SCROLL 4
+#define A_B_SEARCH 5
+#define A_DIGIT 6
+#define A_DISP_OPTION 7
+#define A_DEBUG 8
+#define A_EXAMINE 9
+#define A_FIRSTCMD 10
+#define A_FREPAINT 11
+#define A_F_LINE 12
+#define A_F_SCREEN 13
+#define A_F_SCROLL 14
+#define A_F_SEARCH 15
+#define A_GOEND 16
+#define A_GOLINE 17
+#define A_GOMARK 18
+#define A_HELP 19
+#define A_NEXT_FILE 20
+#define A_PERCENT 21
+#define A_PREFIX 22
+#define A_PREV_FILE 23
+#define A_QUIT 24
+#define A_REPAINT 25
+#define A_SETMARK 26
+#define A_SHELL 27
+#define A_STAT 28
+#define A_FF_LINE 29
+#define A_BF_LINE 30
+#define A_VERSION 31
+#define A_VISUAL 32
+#define A_F_WINDOW 33
+#define A_B_WINDOW 34
+#define A_F_BRACKET 35
+#define A_B_BRACKET 36
+#define A_PIPE 37
+#define A_INDEX_FILE 38
+#define A_UNDO_SEARCH 39
+
+
+
+#define A_AGAIN_SEARCH 43
+#define A_T_AGAIN_SEARCH 44
+#define A_REVERSE_SEARCH 45
+#define A_T_REVERSE_SEARCH 46
+#define A_OPT_TOGGLE 47
+#define A_OPT_SET 48
+#define A_OPT_UNSET 49
+#define A_F_FOREVER 50
+#define A_GOPOS 51
+
+#define A_INVALID 100
+#define A_NOACTION 101
+#define A_UINVALID 102
+#define A_END_LIST 103
+
+#define A_EXTRA 0200
+
+
+/* Line editting characters */
+
+#define EC_BACKSPACE 1
+#define EC_LINEKILL 2
+#define EC_RIGHT 3
+#define EC_LEFT 4
+#define EC_W_LEFT 5
+#define EC_W_RIGHT 6
+#define EC_INSERT 7
+#define EC_DELETE 8
+#define EC_HOME 9
+#define EC_END 10
+#define EC_W_BACKSPACE 11
+#define EC_W_DELETE 12
+#define EC_UP 13
+#define EC_DOWN 14
+#define EC_EXPAND 15
+#define EC_F_COMPLETE 17
+#define EC_B_COMPLETE 18
+#define EC_LITERAL 19
+
+#define EC_UINVALID 102
+
+/* Flags for editchar() */
+#define EC_PEEK 01
+#define EC_NOHISTORY 02
+#define EC_NOCOMPLETE 04
+
diff --git a/usr.bin/less/cmdbuf.c b/usr.bin/less/cmdbuf.c
new file mode 100644
index 00000000000..a2410427270
--- /dev/null
+++ b/usr.bin/less/cmdbuf.c
@@ -0,0 +1,939 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * Functions which manipulate the command buffer.
+ * Used only by command() and related functions.
+ */
+
+#include "less.h"
+#include "cmd.h"
+
+extern int sc_width;
+
+static char cmdbuf[120]; /* Buffer for holding a multi-char command */
+static int cmd_col; /* Current column of the multi-char command */
+static char *cp; /* Pointer into cmdbuf */
+static int literal;
+
+#if TAB_COMPLETE_FILENAME
+static int cmd_complete();
+/*
+ * These variables are statics used by cmd_complete.
+ */
+static int in_completion = 0;
+static char *tk_text;
+static char *tk_original;
+static char *tk_ipoint;
+static char *tk_trial;
+static struct textlist tk_tlist;
+#endif
+
+#if CMD_HISTORY
+/*
+ * A mlist structure represents a command history.
+ */
+struct mlist
+{
+ struct mlist *next;
+ struct mlist *prev;
+ struct mlist *curr_mp;
+ char *string;
+};
+
+/*
+ * These are the various command histories that exist.
+ */
+struct mlist mlist_search =
+ { &mlist_search, &mlist_search, &mlist_search, NULL };
+public void *ml_search = (void *) &mlist_search;
+struct mlist mlist_examine =
+ { &mlist_examine, &mlist_examine, &mlist_examine, NULL };
+public void *ml_examine = (void *) &mlist_examine;
+#if SHELL_ESCAPE || PIPEC
+struct mlist mlist_shell =
+ { &mlist_shell, &mlist_shell, &mlist_shell, NULL };
+public void *ml_shell = (void *) &mlist_shell;
+#endif /* SHELL_ESCAPE || PIPEC */
+
+/*
+ * History for the current command.
+ */
+static struct mlist *curr_mlist = NULL;
+
+#endif /* CMD_HISTORY */
+
+/*
+ * Reset command buffer (to empty).
+ */
+ public void
+cmd_reset()
+{
+ cp = cmdbuf;
+ *cp = '\0';
+ cmd_col = 0;
+ literal = 0;
+}
+
+/*
+ * How many characters are in the command buffer?
+ */
+ public int
+len_cmdbuf()
+{
+ return (strlen(cmdbuf));
+}
+
+/*
+ * Backspace in the command buffer.
+ * Delete the char to the left of the cursor.
+ */
+ static int
+cmd_erase()
+{
+ register char *s;
+ char *p;
+ int col;
+
+ if (cp == cmdbuf)
+ {
+ /*
+ * Backspace past beginning of the buffer:
+ * this usually means abort the command.
+ */
+ return (CC_QUIT);
+ }
+ /*
+ * Back up the pointer.
+ */
+ --cp;
+ /*
+ * Remember the current cursor column and
+ * set it back the width of the char being erased.
+ */
+ col = cmd_col;
+ p = prchar(*cp);
+ cmd_col -= strlen(p);
+ /*
+ * Shift left the buffer after the erased char.
+ */
+ for (s = cp; *s != '\0'; s++)
+ s[0] = s[1];
+ /*
+ * Back up the cursor to the position of the erased char,
+ * clear the tail of the line,
+ * and reprint the line after the erased char.
+ */
+ while (col > cmd_col)
+ {
+ putbs();
+ col--;
+ }
+ clear_eol();
+ for (s = cp; *s != '\0'; s++)
+ {
+ p = prchar(*s);
+ putstr(p);
+ col += strlen(p);
+ }
+ /*
+ * Back up the cursor again.
+ */
+ while (col > cmd_col)
+ {
+ putbs();
+ col--;
+ }
+
+ /*
+ * This is rather weird.
+ * We say that erasing the entire command string causes us
+ * to abort the current command, BUT ONLY IF there is no history
+ * for this type of command. This causes commands like search (/)
+ * and edit (:e) to stay active even if we erase the entire string,
+ * but commands like <digit> and - go away when we erase the string.
+ * (See same thing in cmd_kill.)
+ */
+ if (curr_mlist == NULL && cp == cmdbuf && *cp == '\0')
+ return (CC_QUIT);
+ return (CC_OK);
+}
+
+/*
+ * Delete the char under the cursor.
+ */
+ static int
+cmd_delete()
+{
+ char *p;
+
+ if (*cp == '\0')
+ {
+ /*
+ * At end of string; there is no char under the cursor.
+ */
+ return (CC_OK);
+ }
+ /*
+ * Move right, then use cmd_erase.
+ */
+ p = prchar(*cp);
+ cp++;
+ putstr(p);
+ cmd_col += strlen(p);
+ cmd_erase();
+ return (CC_OK);
+}
+
+/*
+ * Delete the "word" to the left of the cursor.
+ */
+ static int
+cmd_werase()
+{
+ if (cp > cmdbuf && cp[-1] == ' ')
+ {
+ /*
+ * If the char left of cursor is a space,
+ * erase all the spaces left of cursor (to the first non-space).
+ */
+ while (cp > cmdbuf && cp[-1] == ' ')
+ (void) cmd_erase();
+ } else
+ {
+ /*
+ * If the char left of cursor is not a space,
+ * erase all the nonspaces left of cursor (the whole "word").
+ */
+ while (cp > cmdbuf && cp[-1] != ' ')
+ (void) cmd_erase();
+ }
+ return (CC_OK);
+}
+
+/*
+ * Delete the "word" under the cursor.
+ */
+ static int
+cmd_wdelete()
+{
+ if (*cp == ' ')
+ {
+ /*
+ * If the char under the cursor is a space,
+ * delete it and all the spaces right of cursor.
+ */
+ while (*cp == ' ')
+ (void) cmd_delete();
+ } else
+ {
+ /*
+ * If the char under the cursor is not a space,
+ * delete it and all nonspaces right of cursor (the whole word).
+ */
+ while (*cp != ' ' && *cp != '\0')
+ (void) cmd_delete();
+ }
+ return (CC_OK);
+}
+
+/*
+ * Move cursor to start of command buffer.
+ */
+ static int
+cmd_home()
+{
+ char *p;
+
+ /*
+ * Back up until we hit start of buffer.
+ */
+ while (cp > cmdbuf)
+ {
+ cp--;
+ p = prchar(*cp);
+ cmd_col -= strlen(p);
+ while (*p++ != '\0')
+ putbs();
+ }
+ return (CC_OK);
+}
+
+/*
+ * Delete all chars in the command buffer.
+ */
+ static int
+cmd_kill()
+{
+ if (cmdbuf[0] == '\0')
+ {
+ /*
+ * Buffer is already empty; abort the current command.
+ */
+ return (CC_QUIT);
+ }
+ (void) cmd_home();
+ *cp = '\0';
+ clear_eol();
+ /*
+ * Same weirdness as in cmd_erase.
+ * If the current command has no history, abort the current command.
+ */
+ if (curr_mlist == NULL)
+ return (CC_QUIT);
+ return (CC_OK);
+}
+
+/*
+ * Move cursor right one character.
+ */
+ static int
+cmd_right()
+{
+ char *p;
+
+ if (*cp == '\0')
+ {
+ /*
+ * Already at the end of the line.
+ */
+ return (CC_OK);
+ }
+ p = prchar(*cp);
+ cp++;
+ putstr(p);
+ cmd_col += strlen(p);
+ return (CC_OK);
+}
+
+/*
+ * Move cursor left one character.
+ */
+ static int
+cmd_left()
+{
+ char *p;
+
+ if (cp <= cmdbuf)
+ {
+ /* Already at the beginning of the line */
+ return (CC_OK);
+ }
+ cp--;
+ p = prchar(*cp);
+ cmd_col -= strlen(p);
+ while (*p++ != '\0')
+ putbs();
+ return (CC_OK);
+}
+
+#if CMD_HISTORY
+/*
+ * Select an mlist structure to be the current command history.
+ */
+ public void
+set_mlist(mlist)
+ void *mlist;
+{
+ curr_mlist = (struct mlist *) mlist;
+}
+
+/*
+ * Move up or down in the currently selected command history list.
+ */
+ static int
+cmd_updown(action)
+ int action;
+{
+ char *p;
+ char *s;
+
+ if (curr_mlist == NULL)
+ {
+ /*
+ * The current command has no history list.
+ */
+ bell();
+ return (CC_OK);
+ }
+ cmd_home();
+ clear_eol();
+ /*
+ * Move curr_mp to the next/prev entry.
+ */
+ if (action == EC_UP)
+ curr_mlist->curr_mp = curr_mlist->curr_mp->prev;
+ else
+ curr_mlist->curr_mp = curr_mlist->curr_mp->next;
+ /*
+ * Copy the entry into cmdbuf and echo it on the screen.
+ */
+ s = curr_mlist->curr_mp->string;
+ if (s == NULL)
+ s = "";
+ for (cp = cmdbuf; *s != '\0'; s++, cp++)
+ {
+ *cp = *s;
+ p = prchar(*cp);
+ cmd_col += strlen(p);
+ putstr(p);
+ }
+ *cp = '\0';
+ return (CC_OK);
+}
+
+/*
+ * Accept the command in the command buffer.
+ * Add it to the currently selected history list.
+ */
+ public void
+cmd_accept()
+{
+ struct mlist *ml;
+
+ /*
+ * Nothing to do if there is no currently selected history list.
+ */
+ if (curr_mlist == NULL)
+ return;
+ /*
+ * Don't save a trivial command.
+ */
+ if (strlen(cmdbuf) == 0)
+ return;
+ /*
+ * Don't save if a duplicate of a command which is already in the history.
+ * But select the one already in the history to be current.
+ */
+ for (ml = curr_mlist->next; ml != curr_mlist; ml = ml->next)
+ {
+ if (strcmp(ml->string, cmdbuf) == 0)
+ break;
+ }
+ if (ml == curr_mlist)
+ {
+ /*
+ * Did not find command in history.
+ * Save the command and put it at the end of the history list.
+ */
+ ml = (struct mlist *) ecalloc(1, sizeof(struct mlist));
+ ml->string = save(cmdbuf);
+ ml->next = curr_mlist;
+ ml->prev = curr_mlist->prev;
+ curr_mlist->prev->next = ml;
+ curr_mlist->prev = ml;
+ }
+ /*
+ * Point to the cmd just after the just-accepted command.
+ * Thus, an UPARROW will always retrieve the previous command.
+ */
+ curr_mlist->curr_mp = ml->next;
+}
+#endif
+
+/*
+ * Try to perform a line-edit function on the command buffer,
+ * using a specified char as a line-editing command.
+ * Returns:
+ * CC_PASS The char does not invoke a line edit function.
+ * CC_OK Line edit function done.
+ * CC_QUIT The char requests the current command to be aborted.
+ */
+ static int
+cmd_edit(c)
+ int c;
+{
+ int action;
+ int flags;
+
+#if TAB_COMPLETE_FILENAME
+#define not_in_completion() in_completion = 0
+#else
+#define not_in_completion()
+#endif
+
+ /*
+ * See if the char is indeed a line-editing command.
+ */
+ flags = 0;
+ if (curr_mlist == NULL)
+ /*
+ * No current history; don't accept history manipulation cmds.
+ */
+ flags |= EC_NOHISTORY;
+ if (curr_mlist == &mlist_search)
+ /*
+ * In a search command; don't accept file-completion cmds.
+ */
+ flags |= EC_NOCOMPLETE;
+
+ action = editchar(c, flags);
+
+ switch (action)
+ {
+ case EC_RIGHT:
+ not_in_completion();
+ return (cmd_right());
+ case EC_LEFT:
+ not_in_completion();
+ return (cmd_left());
+ case EC_W_RIGHT:
+ not_in_completion();
+ while (*cp != '\0' && *cp != ' ')
+ cmd_right();
+ while (*cp == ' ')
+ cmd_right();
+ return (CC_OK);
+ case EC_W_LEFT:
+ not_in_completion();
+ while (cp > cmdbuf && cp[-1] == ' ')
+ cmd_left();
+ while (cp > cmdbuf && cp[-1] != ' ')
+ cmd_left();
+ return (CC_OK);
+ case EC_HOME:
+ not_in_completion();
+ return (cmd_home());
+ case EC_END:
+ not_in_completion();
+ while (*cp != '\0')
+ cmd_right();
+ return (CC_OK);
+ case EC_INSERT:
+ not_in_completion();
+ return (CC_OK);
+ case EC_BACKSPACE:
+ not_in_completion();
+ return (cmd_erase());
+ case EC_LINEKILL:
+ not_in_completion();
+ return (cmd_kill());
+ case EC_W_BACKSPACE:
+ not_in_completion();
+ return (cmd_werase());
+ case EC_DELETE:
+ not_in_completion();
+ return (cmd_delete());
+ case EC_W_DELETE:
+ not_in_completion();
+ return (cmd_wdelete());
+ case EC_LITERAL:
+ literal = 1;
+ return (CC_OK);
+#if CMD_HISTORY
+ case EC_UP:
+ case EC_DOWN:
+ not_in_completion();
+ return (cmd_updown(action));
+#endif
+#if TAB_COMPLETE_FILENAME
+ case EC_F_COMPLETE:
+ case EC_B_COMPLETE:
+ case EC_EXPAND:
+ return (cmd_complete(action));
+#endif
+ default:
+ not_in_completion();
+ return (CC_PASS);
+ }
+}
+
+/*
+ * Insert a char into the command buffer, at the current position.
+ */
+ static int
+cmd_ichar(c)
+ int c;
+{
+ int col;
+ char *p;
+ char *s;
+
+ if (strlen(cmdbuf) >= sizeof(cmdbuf)-2)
+ {
+ /*
+ * No room in the command buffer for another char.
+ */
+ bell();
+ return (CC_ERROR);
+ }
+
+ /*
+ * Remember the current cursor column and
+ * move it forward the width of the char being inserted.
+ */
+ col = cmd_col;
+ p = prchar(c);
+ cmd_col += strlen(p);
+ if (cmd_col >= sc_width-1)
+ {
+ cmd_col -= strlen(p);
+ bell();
+ return (CC_ERROR);
+ }
+ /*
+ * Insert the character in the string.
+ * First, make room for the new char.
+ */
+ for (s = &cmdbuf[strlen(cmdbuf)]; s >= cp; s--)
+ s[1] = s[0];
+ *cp++ = c;
+ /*
+ * Reprint the tail of the line after the inserted char.
+ */
+ clear_eol();
+ for (s = cp-1; *s != '\0'; s++)
+ {
+ p = prchar(*s);
+ col += strlen(p);
+ if (col >= sc_width-1)
+ {
+ /*
+ * Oops. There is no room on the screen
+ * for the new char. Back up the cursor to
+ * just after the inserted char and erase it.
+ */
+ col -= strlen(p);
+ while (col > cmd_col)
+ {
+ putbs();
+ col--;
+ }
+ (void) cmd_erase();
+ bell();
+ return (CC_ERROR);
+ }
+ putstr(p);
+ }
+ /*
+ * Back up the cursor to just after the inserted char.
+ */
+ while (col > cmd_col)
+ {
+ putbs();
+ col--;
+ }
+ return (CC_OK);
+}
+
+#if TAB_COMPLETE_FILENAME
+/*
+ * Insert a string into the command buffer, at the current position.
+ */
+ static int
+cmd_istr(str)
+ char *str;
+{
+ char *s;
+ int action;
+
+ for (s = str; *s != '\0'; s++)
+ {
+ action = cmd_ichar(*s);
+ if (action != CC_OK)
+ {
+ bell();
+ return (action);
+ }
+ }
+ return (CC_OK);
+}
+
+/*
+ * Find the beginning and end of the "current" word.
+ * This is the word which the cursor (cp) is inside or at the end of.
+ * Return pointer to the beginning of the word and put the
+ * cursor at the end of the word.
+ */
+ static char *
+delimit_word()
+{
+ char *word;
+
+ /*
+ * Move cursor to end of word.
+ */
+ if (*cp != ' ' && *cp != '\0')
+ {
+ /*
+ * Cursor is on a nonspace.
+ * Move cursor right to the next space.
+ */
+ while (*cp != ' ' && *cp != '\0')
+ cmd_right();
+ } else if (cp > cmdbuf && cp[-1] != ' ')
+ {
+ /*
+ * Cursor is on a space, and char to the left is a nonspace.
+ * We're already at the end of the word.
+ */
+ ;
+ } else
+ {
+ /*
+ * Cursor is on a space and char to the left is a space.
+ * Huh? There's no word here.
+ */
+ return (NULL);
+ }
+ /*
+ * Search backwards for beginning of the word.
+ */
+ if (cp == cmdbuf)
+ return (NULL);
+ for (word = cp-1; word > cmdbuf; word--)
+ if (word[-1] == ' ')
+ break;
+ return (word);
+}
+
+/*
+ * Set things up to enter completion mode.
+ * Expand the word under the cursor into a list of filenames
+ * which start with that word, and set tk_text to that list.
+ */
+ static void
+init_compl()
+{
+ char *word;
+ char c;
+
+ /*
+ * Get rid of any previous tk_text.
+ */
+ if (tk_text != NULL)
+ {
+ free(tk_text);
+ tk_text = NULL;
+ }
+ /*
+ * Find the original (uncompleted) word in the command buffer.
+ */
+ word = delimit_word();
+ if (word == NULL)
+ return;
+ /*
+ * Set the insertion point to the point in the command buffer
+ * where the original (uncompleted) word now sits.
+ */
+ tk_ipoint = word;
+ /*
+ * Save the original (uncompleted) word
+ */
+ if (tk_original != NULL)
+ free(tk_original);
+ tk_original = (char *) ecalloc(cp-word+1, sizeof(char));
+ strncpy(tk_original, word, cp-word);
+ /*
+ * Get the expanded filename.
+ * This may result in a single filename, or
+ * a blank-separated list of filenames.
+ */
+ c = *cp;
+ *cp = '\0';
+ tk_text = fcomplete(word);
+ *cp = c;
+}
+
+/*
+ * Return the next word in the current completion list.
+ */
+ static char *
+next_compl(action, prev)
+ int action;
+ char *prev;
+{
+ switch (action)
+ {
+ case EC_F_COMPLETE:
+ return (forw_textlist(&tk_tlist, prev));
+ case EC_B_COMPLETE:
+ return (back_textlist(&tk_tlist, prev));
+ default:
+ /* Cannot happen */
+ return ("?");
+ }
+}
+
+/*
+ * Complete the filename before (or under) the cursor.
+ * cmd_complete may be called multiple times. The global in_completion
+ * remembers whether this call is the first time (create the list),
+ * or a subsequent time (step thru the list).
+ */
+ static int
+cmd_complete(action)
+ int action;
+{
+
+ if (!in_completion || action == EC_EXPAND)
+ {
+ /*
+ * Expand the word under the cursor and
+ * use the first word in the expansion
+ * (or the entire expansion if we're doing EC_EXPAND).
+ */
+ init_compl();
+ if (tk_text == NULL)
+ {
+ bell();
+ return (CC_OK);
+ }
+ if (action == EC_EXPAND)
+ {
+ /*
+ * Use the whole list.
+ */
+ tk_trial = tk_text;
+ } else
+ {
+ /*
+ * Use the first filename in the list.
+ */
+ in_completion = 1;
+ init_textlist(&tk_tlist, tk_text);
+ tk_trial = next_compl(action, (char*)NULL);
+ }
+ } else
+ {
+ /*
+ * We already have a completion list.
+ * Use the next/previous filename from the list.
+ */
+ tk_trial = next_compl(action, tk_trial);
+ }
+
+ /*
+ * Remove the original word, or the previous trial completion.
+ */
+ while (cp > tk_ipoint)
+ (void) cmd_erase();
+
+ if (tk_trial == NULL)
+ {
+ /*
+ * There are no more trial completions.
+ * Insert the original (uncompleted) filename.
+ */
+ in_completion = 0;
+ if (cmd_istr(tk_original) != CC_OK)
+ goto fail;
+ } else
+ {
+ /*
+ * Insert trial completion.
+ */
+ if (cmd_istr(tk_trial) != CC_OK)
+ goto fail;
+ }
+
+ return (CC_OK);
+
+fail:
+ in_completion = 0;
+ bell();
+ return (CC_OK);
+}
+
+#endif /* TAB_COMPLETE_FILENAME */
+
+/*
+ * Process a single character of a multi-character command, such as
+ * a number, or the pattern of a search command.
+ * Returns:
+ * CC_OK The char was accepted.
+ * CC_QUIT The char requests the command to be aborted.
+ * CC_ERROR The char could not be accepted due to an error.
+ */
+ public int
+cmd_char(c)
+ int c;
+{
+ int action;
+
+ if (literal)
+ {
+ /*
+ * Insert the char, even if it is a line-editing char.
+ */
+ literal = 0;
+ return (cmd_ichar(c));
+ }
+
+ /*
+ * See if it is a special line-editing character.
+ */
+ if (in_mca())
+ {
+ action = cmd_edit(c);
+ switch (action)
+ {
+ case CC_OK:
+ case CC_QUIT:
+ return (action);
+ case CC_PASS:
+ break;
+ }
+ }
+
+ /*
+ * Insert the char into the command buffer.
+ */
+ action = cmd_ichar(c);
+ if (action != CC_OK)
+ return (action);
+ return (CC_OK);
+}
+
+/*
+ * Return the number currently in the command buffer.
+ */
+ public int
+cmd_int()
+{
+ return (atoi(cmdbuf));
+}
+
+/*
+ * Display a string, usually as a prompt for input into the command buffer.
+ */
+ public void
+cmd_putstr(s)
+ char *s;
+{
+ putstr(s);
+ cmd_col += strlen(s);
+}
+
+/*
+ * Return a pointer to the command buffer.
+ */
+ public char *
+get_cmdbuf()
+{
+ return (cmdbuf);
+}
diff --git a/usr.bin/less/command.c b/usr.bin/less/command.c
new file mode 100644
index 00000000000..d1ff9946237
--- /dev/null
+++ b/usr.bin/less/command.c
@@ -0,0 +1,1253 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * User-level command processor.
+ */
+
+#include "less.h"
+#include "position.h"
+#include "option.h"
+#include "cmd.h"
+
+extern int erase_char, kill_char;
+extern int sigs;
+extern int quit_at_eof;
+extern int hit_eof;
+extern int sc_width;
+extern int sc_height;
+extern int swindow;
+extern int jump_sline;
+extern int quitting;
+extern int wscroll;
+extern int nohelp;
+extern int top_scroll;
+extern int ignore_eoi;
+extern char *every_first_cmd;
+extern char *curr_altfilename;
+extern char version[];
+extern struct scrpos initial_scrpos;
+extern IFILE curr_ifile;
+#if CMD_HISTORY
+extern void *ml_search;
+extern void *ml_examine;
+#if SHELL_ESCAPE || PIPEC
+extern void *ml_shell;
+#endif
+#else
+/* No CMD_HISTORY */
+#define ml_search NULL
+#define ml_examine NULL
+#define ml_shell NULL
+#endif
+#if EDITOR
+extern char *editor;
+extern char *editproto;
+#endif
+extern int screen_trashed; /* The screen has been overwritten */
+
+static char ungot[100];
+static char *ungotp = NULL;
+#if SHELL_ESCAPE
+static char *shellcmd = NULL; /* For holding last shell command for "!!" */
+#endif
+static int mca; /* The multicharacter command (action) */
+static int search_type; /* The previous type of search */
+static int number; /* The number typed by the user */
+static char optchar;
+static int optflag;
+#if PIPEC
+static char pipec;
+#endif
+
+static void multi_search();
+
+/*
+ * Move the cursor to lower left before executing a command.
+ * This looks nicer if the command takes a long time before
+ * updating the screen.
+ */
+ static void
+cmd_exec()
+{
+ lower_left();
+ flush();
+}
+
+/*
+ * Set up the display to start a new multi-character command.
+ */
+ static void
+start_mca(action, prompt, mlist)
+ int action;
+ char *prompt;
+ void *mlist;
+{
+ mca = action;
+ clear_bot();
+ cmd_putstr(prompt);
+#if CMD_HISTORY
+ set_mlist(mlist);
+#endif
+}
+
+ public int
+in_mca()
+{
+ return (mca != 0 && mca != A_PREFIX);
+}
+
+/*
+ * Set up the display to start a new search command.
+ */
+ static void
+mca_search()
+{
+ if (search_type & SRCH_FORW)
+ mca = A_F_SEARCH;
+ else
+ mca = A_B_SEARCH;
+
+ clear_bot();
+
+ if (search_type & SRCH_FIRST_FILE)
+ cmd_putstr("@");
+
+ if (search_type & SRCH_PAST_EOF)
+ cmd_putstr("*");
+
+ if (search_type & SRCH_NOMATCH)
+ cmd_putstr("!");
+
+ if (search_type & SRCH_FORW)
+ cmd_putstr("/");
+ else
+ cmd_putstr("?");
+#if CMD_HISTORY
+ set_mlist(ml_search);
+#endif
+}
+
+/*
+ * Execute a multicharacter command.
+ */
+ static void
+exec_mca()
+{
+ register char *cbuf;
+
+ cmd_exec();
+ cbuf = get_cmdbuf();
+
+ switch (mca)
+ {
+ case A_F_SEARCH:
+ case A_B_SEARCH:
+ multi_search(cbuf, number);
+ break;
+ case A_FIRSTCMD:
+ /*
+ * Skip leading spaces or + signs in the string.
+ */
+ while (*cbuf == '+' || *cbuf == ' ')
+ cbuf++;
+ if (every_first_cmd != NULL)
+ free(every_first_cmd);
+ if (*cbuf == '\0')
+ every_first_cmd = NULL;
+ else
+ every_first_cmd = save(cbuf);
+ break;
+ case A_OPT_TOGGLE:
+ toggle_option(optchar, cbuf, optflag);
+ optchar = '\0';
+ break;
+ case A_F_BRACKET:
+ match_brac(cbuf[0], cbuf[1], 1, number);
+ break;
+ case A_B_BRACKET:
+ match_brac(cbuf[1], cbuf[0], 0, number);
+ break;
+#if EXAMINE
+ case A_EXAMINE:
+ edit_list(cbuf);
+ break;
+#endif
+#if SHELL_ESCAPE
+ case A_SHELL:
+ /*
+ * !! just uses whatever is in shellcmd.
+ * Otherwise, copy cmdbuf to shellcmd,
+ * expanding any special characters ("%" or "#").
+ */
+ if (*cbuf != '!')
+ {
+ if (shellcmd != NULL)
+ free(shellcmd);
+ shellcmd = fexpand(cbuf);
+ }
+
+ if (shellcmd == NULL)
+ lsystem("");
+ else
+ lsystem(shellcmd);
+ error("!done", NULL_PARG);
+ break;
+#endif
+#if PIPEC
+ case A_PIPE:
+ (void) pipe_mark(pipec, cbuf);
+ error("|done", NULL_PARG);
+ break;
+#endif
+ }
+}
+
+/*
+ * Add a character to a multi-character command.
+ */
+ static int
+mca_char(c)
+ int c;
+{
+ char *p;
+ int flag;
+ char buf[3];
+
+ switch (mca)
+ {
+ case 0:
+ /*
+ * Not in a multicharacter command.
+ */
+ return (NO_MCA);
+
+ case A_PREFIX:
+ /*
+ * In the prefix of a command.
+ * This not considered a multichar command
+ * (even tho it uses cmdbuf, etc.).
+ * It is handled in the commands() switch.
+ */
+ return (NO_MCA);
+
+ case A_DIGIT:
+ /*
+ * Entering digits of a number.
+ * Terminated by a non-digit.
+ */
+ if ((c < '0' || c > '9') &&
+ editchar(c, EC_PEEK|EC_NOHISTORY|EC_NOCOMPLETE) == A_INVALID)
+ {
+ /*
+ * Not part of the number.
+ * Treat as a normal command character.
+ */
+ number = cmd_int();
+ mca = 0;
+ cmd_accept();
+ return (NO_MCA);
+ }
+ break;
+
+ case A_OPT_TOGGLE:
+ /*
+ * Special case for the TOGGLE_OPTION command.
+ * If the option letter which was entered is a
+ * single-char option, execute the command immediately,
+ * so user doesn't have to hit RETURN.
+ * If the first char is + or -, this indicates
+ * OPT_UNSET or OPT_SET respectively, instead of OPT_TOGGLE.
+ */
+ if (c == erase_char || c == kill_char)
+ break;
+ if (optchar != '\0' && optchar != '+' && optchar != '-')
+ /*
+ * We already have the option letter.
+ */
+ break;
+ switch (c)
+ {
+ case '+':
+ optflag = OPT_UNSET;
+ break;
+ case '-':
+ optflag = OPT_SET;
+ break;
+ default:
+ optchar = c;
+ if (optflag != OPT_TOGGLE || single_char_option(c))
+ {
+ toggle_option(c, "", optflag);
+ return (MCA_DONE);
+ }
+ break;
+ }
+ if (optchar == '+' || optchar == '-')
+ {
+ optchar = c;
+ break;
+ }
+ /*
+ * Display a prompt appropriate for the option letter.
+ */
+ if ((p = opt_prompt(c)) == NULL)
+ {
+ buf[0] = '-';
+ buf[1] = c;
+ buf[2] = '\0';
+ p = buf;
+ }
+ start_mca(A_OPT_TOGGLE, p, (void*)NULL);
+ return (MCA_MORE);
+
+ case A_F_SEARCH:
+ case A_B_SEARCH:
+ /*
+ * Special case for search commands.
+ * Certain characters as the first char of
+ * the pattern have special meaning:
+ * ! Toggle the NOMATCH flag
+ * * Toggle the PAST_EOF flag
+ * @ Toggle the FIRST_FILE flag
+ */
+ if (len_cmdbuf() > 0)
+ /*
+ * Only works for the first char of the pattern.
+ */
+ break;
+
+ flag = 0;
+ switch (c)
+ {
+ case '!':
+ flag = SRCH_NOMATCH;
+ break;
+ case '@':
+ flag = SRCH_FIRST_FILE;
+ break;
+ case '*':
+ flag = SRCH_PAST_EOF;
+ break;
+ }
+ if (flag != 0)
+ {
+ search_type ^= flag;
+ mca_search();
+ return (MCA_MORE);
+ }
+ break;
+ }
+
+ /*
+ * Any other multicharacter command
+ * is terminated by a newline.
+ */
+ if (c == '\n' || c == '\r')
+ {
+ /*
+ * Execute the command.
+ */
+ exec_mca();
+ return (MCA_DONE);
+ }
+ /*
+ * Append the char to the command buffer.
+ */
+ if (cmd_char(c) == CC_QUIT)
+ /*
+ * Abort the multi-char command.
+ */
+ return (MCA_DONE);
+
+ if ((mca == A_F_BRACKET || mca == A_B_BRACKET) && len_cmdbuf() >= 2)
+ {
+ /*
+ * Special case for the bracket-matching commands.
+ * Execute the command after getting exactly two
+ * characters from the user.
+ */
+ exec_mca();
+ return (MCA_DONE);
+ }
+
+ /*
+ * Need another character.
+ */
+ return (MCA_MORE);
+}
+
+/*
+ * Display the appropriate prompt.
+ */
+ static void
+prompt()
+{
+ register char *p;
+
+ if (ungotp != NULL && ungotp > ungot)
+ {
+ /*
+ * No prompt necessary if commands are from
+ * ungotten chars rather than from the user.
+ */
+ return;
+ }
+
+ /*
+ * If nothing is displayed yet, display starting from initial_scrpos.
+ */
+ if (empty_screen())
+ {
+ if (initial_scrpos.pos == NULL_POSITION)
+ /*
+ * {{ Maybe this should be:
+ * jump_loc(ch_zero(), jump_sline);
+ * but this behavior seems rather unexpected
+ * on the first screen. }}
+ */
+ jump_loc(ch_zero(), 1);
+ else
+ jump_loc(initial_scrpos.pos, initial_scrpos.ln);
+ } else if (screen_trashed)
+ {
+ int save_top_scroll;
+ save_top_scroll = top_scroll;
+ top_scroll = 1;
+ repaint();
+ top_scroll = save_top_scroll;
+ }
+
+ /*
+ * If the -E flag is set and we've hit EOF on the last file, quit.
+ */
+ if (quit_at_eof == OPT_ONPLUS && hit_eof &&
+ next_ifile(curr_ifile) == NULL_IFILE)
+ quit(QUIT_OK);
+
+ /*
+ * Select the proper prompt and display it.
+ */
+ clear_bot();
+ p = pr_string();
+ if (p == NULL)
+ putchr(':');
+ else
+ {
+ so_enter();
+ putstr(p);
+ so_exit();
+ }
+}
+
+ public void
+dispversion()
+{
+ PARG parg;
+
+ parg.p_string = version;
+ error("less version %s", &parg);
+}
+
+/*
+ * Get command character.
+ * The character normally comes from the keyboard,
+ * but may come from ungotten characters
+ * (characters previously given to ungetcc or ungetsc).
+ */
+ public int
+getcc()
+{
+ if (ungotp == NULL)
+ /*
+ * Normal case: no ungotten chars, so get one from the user.
+ */
+ return (getchr());
+
+ if (ungotp > ungot)
+ /*
+ * Return the next ungotten char.
+ */
+ return (*--ungotp);
+
+ /*
+ * We have just run out of ungotten chars.
+ */
+ ungotp = NULL;
+ if (len_cmdbuf() == 0 || !empty_screen())
+ return (getchr());
+ /*
+ * Command is incomplete, so try to complete it.
+ */
+ switch (mca)
+ {
+ case A_DIGIT:
+ /*
+ * We have a number but no command. Treat as #g.
+ */
+ return ('g');
+
+ case A_F_SEARCH:
+ case A_B_SEARCH:
+ /*
+ * We have "/string" but no newline. Add the \n.
+ */
+ return ('\n');
+
+ default:
+ /*
+ * Some other incomplete command. Let user complete it.
+ */
+ return (getchr());
+ }
+}
+
+/*
+ * "Unget" a command character.
+ * The next getcc() will return this character.
+ */
+ public void
+ungetcc(c)
+ int c;
+{
+ if (ungotp == NULL)
+ ungotp = ungot;
+ if (ungotp >= ungot + sizeof(ungot))
+ {
+ error("ungetcc overflow", NULL_PARG);
+ quit(QUIT_ERROR);
+ }
+ *ungotp++ = c;
+}
+
+/*
+ * Unget a whole string of command characters.
+ * The next sequence of getcc()'s will return this string.
+ */
+ public void
+ungetsc(s)
+ char *s;
+{
+ register char *p;
+
+ for (p = s + strlen(s) - 1; p >= s; p--)
+ ungetcc(*p);
+}
+
+/*
+ * Search for a pattern, possibly in multiple files.
+ * If SRCH_FIRST_FILE is set, begin searching at the first file.
+ * If SRCH_PAST_EOF is set, continue the search thru multiple files.
+ */
+ static void
+multi_search(pattern, n)
+ char *pattern;
+ int n;
+{
+ register int nomore;
+ IFILE save_ifile;
+ int changed_file;
+
+ changed_file = 0;
+ save_ifile = curr_ifile;
+
+ if (search_type & SRCH_FIRST_FILE)
+ {
+ /*
+ * Start at the first (or last) file
+ * in the command line list.
+ */
+ if (search_type & SRCH_FORW)
+ nomore = edit_first();
+ else
+ nomore = edit_last();
+ if (nomore)
+ return;
+ changed_file = 1;
+ search_type &= ~SRCH_FIRST_FILE;
+ }
+
+ for (;;)
+ {
+ if ((n = search(search_type, pattern, n)) == 0)
+ /*
+ * Found it.
+ */
+ return;
+
+ if (n < 0)
+ /*
+ * Some kind of error in the search.
+ * Error message has been printed by search().
+ */
+ break;
+
+ if ((search_type & SRCH_PAST_EOF) == 0)
+ /*
+ * We didn't find a match, but we're
+ * supposed to search only one file.
+ */
+ break;
+ /*
+ * Move on to the next file.
+ */
+ if (search_type & SRCH_FORW)
+ nomore = edit_next(1);
+ else
+ nomore = edit_prev(1);
+ if (nomore)
+ break;
+ changed_file = 1;
+ }
+
+ /*
+ * Didn't find it.
+ * Print an error message if we haven't already.
+ */
+ if (n > 0)
+ error("Pattern not found", NULL_PARG);
+
+ if (changed_file)
+ {
+ /*
+ * Restore the file we were originally viewing.
+ */
+ if (edit_ifile(save_ifile))
+ quit(QUIT_ERROR);
+ }
+}
+
+/*
+ * Main command processor.
+ * Accept and execute commands until a quit command.
+ */
+ public void
+commands()
+{
+ register int c;
+ register int action;
+ register char *cbuf;
+ int save_search_type;
+ char *s;
+ char tbuf[2];
+ PARG parg;
+
+ search_type = SRCH_FORW;
+ wscroll = (sc_height + 1) / 2;
+
+ for (;;)
+ {
+ mca = 0;
+ cmd_accept();
+ number = 0;
+ optchar = '\0';
+
+ /*
+ * See if any signals need processing.
+ */
+ if (sigs)
+ {
+ psignals();
+ if (quitting)
+ quit(QUIT_SAVED_STATUS);
+ }
+
+ /*
+ * Display prompt and accept a character.
+ */
+ cmd_reset();
+ prompt();
+ if (sigs)
+ continue;
+ c = getcc();
+
+ again:
+ if (sigs)
+ continue;
+
+ /*
+ * If we are in a multicharacter command, call mca_char.
+ * Otherwise we call fcmd_decode to determine the
+ * action to be performed.
+ */
+ if (mca)
+ switch (mca_char(c))
+ {
+ case MCA_MORE:
+ /*
+ * Need another character.
+ */
+ c = getcc();
+ goto again;
+ case MCA_DONE:
+ /*
+ * Command has been handled by mca_char.
+ * Start clean with a prompt.
+ */
+ continue;
+ case NO_MCA:
+ /*
+ * Not a multi-char command
+ * (at least, not anymore).
+ */
+ break;
+ }
+
+ /*
+ * Decode the command character and decide what to do.
+ */
+ if (mca)
+ {
+ /*
+ * We're in a multichar command.
+ * Add the character to the command buffer
+ * and display it on the screen.
+ * If the user backspaces past the start
+ * of the line, abort the command.
+ */
+ if (cmd_char(c) == CC_QUIT || len_cmdbuf() == 0)
+ continue;
+ cbuf = get_cmdbuf();
+ } else
+ {
+ /*
+ * Don't use cmd_char if we're starting fresh
+ * at the beginning of a command, because we
+ * don't want to echo the command until we know
+ * it is a multichar command. We also don't
+ * want erase_char/kill_char to be treated
+ * as line editing characters.
+ */
+ tbuf[0] = c;
+ tbuf[1] = '\0';
+ cbuf = tbuf;
+ }
+ s = NULL;
+ action = fcmd_decode(cbuf, &s);
+ /*
+ * If an "extra" string was returned,
+ * process it as a string of command characters.
+ */
+ if (s != NULL)
+ ungetsc(s);
+ /*
+ * Clear the cmdbuf string.
+ * (But not if we're in the prefix of a command,
+ * because the partial command string is kept there.)
+ */
+ if (action != A_PREFIX)
+ cmd_reset();
+
+ switch (action)
+ {
+ case A_DIGIT:
+ /*
+ * First digit of a number.
+ */
+ start_mca(A_DIGIT, ":", (void*)NULL);
+ goto again;
+
+ case A_F_WINDOW:
+ /*
+ * Forward one window (and set the window size).
+ */
+ if (number > 0)
+ swindow = number;
+ /* FALLTHRU */
+ case A_F_SCREEN:
+ /*
+ * Forward one screen.
+ */
+ if (number <= 0)
+ number = get_swindow();
+ cmd_exec();
+ forward(number, 0, 1);
+ break;
+
+ case A_B_WINDOW:
+ /*
+ * Backward one window (and set the window size).
+ */
+ if (number > 0)
+ swindow = number;
+ /* FALLTHRU */
+ case A_B_SCREEN:
+ /*
+ * Backward one screen.
+ */
+ if (number <= 0)
+ number = get_swindow();
+ cmd_exec();
+ backward(number, 0, 1);
+ break;
+
+ case A_F_LINE:
+ /*
+ * Forward N (default 1) line.
+ */
+ if (number <= 0)
+ number = 1;
+ cmd_exec();
+ forward(number, 0, 0);
+ break;
+
+ case A_B_LINE:
+ /*
+ * Backward N (default 1) line.
+ */
+ if (number <= 0)
+ number = 1;
+ cmd_exec();
+ backward(number, 0, 0);
+ break;
+
+ case A_FF_LINE:
+ /*
+ * Force forward N (default 1) line.
+ */
+ if (number <= 0)
+ number = 1;
+ cmd_exec();
+ forward(number, 1, 0);
+ break;
+
+ case A_BF_LINE:
+ /*
+ * Force backward N (default 1) line.
+ */
+ if (number <= 0)
+ number = 1;
+ cmd_exec();
+ backward(number, 1, 0);
+ break;
+
+ case A_F_FOREVER:
+ /*
+ * Forward forever, ignoring EOF.
+ */
+ cmd_exec();
+ jump_forw();
+ ignore_eoi = 1;
+ hit_eof = 0;
+ while (!ABORT_SIGS())
+ forward(1, 0, 0);
+ ignore_eoi = 0;
+ break;
+
+ case A_F_SCROLL:
+ /*
+ * Forward N lines
+ * (default same as last 'd' or 'u' command).
+ */
+ if (number > 0)
+ wscroll = number;
+ cmd_exec();
+ forward(wscroll, 0, 0);
+ break;
+
+ case A_B_SCROLL:
+ /*
+ * Forward N lines
+ * (default same as last 'd' or 'u' command).
+ */
+ if (number > 0)
+ wscroll = number;
+ cmd_exec();
+ backward(wscroll, 0, 0);
+ break;
+
+ case A_FREPAINT:
+ /*
+ * Flush buffers, then repaint screen.
+ * Don't flush the buffers on a pipe!
+ */
+ if (ch_getflags() & CH_CANSEEK)
+ {
+ ch_flush();
+ clr_linenum();
+ }
+ /* FALLTHRU */
+ case A_REPAINT:
+ /*
+ * Repaint screen.
+ */
+ cmd_exec();
+ repaint();
+ break;
+
+ case A_GOLINE:
+ /*
+ * Go to line N, default beginning of file.
+ */
+ if (number <= 0)
+ number = 1;
+ cmd_exec();
+ jump_back(number);
+ break;
+
+ case A_PERCENT:
+ /*
+ * Go to a specified percentage into the file.
+ */
+ if (number < 0)
+ number = 0;
+ if (number > 100)
+ number = 100;
+ cmd_exec();
+ jump_percent(number);
+ break;
+
+ case A_GOEND:
+ /*
+ * Go to line N, default end of file.
+ */
+ cmd_exec();
+ if (number <= 0)
+ jump_forw();
+ else
+ jump_back(number);
+ break;
+
+ case A_GOPOS:
+ /*
+ * Go to a specified byte position in the file.
+ */
+ cmd_exec();
+ if (number < 0)
+ number = 0;
+ jump_line_loc((POSITION)number, jump_sline);
+ break;
+
+ case A_STAT:
+ /*
+ * Print file name, etc.
+ */
+ cmd_exec();
+ parg.p_string = eq_message();
+ error("%s", &parg);
+ break;
+
+ case A_VERSION:
+ /*
+ * Print version number, without the "@(#)".
+ */
+ cmd_exec();
+ dispversion();
+ break;
+
+ case A_QUIT:
+ /*
+ * Exit.
+ */
+ quit(QUIT_OK);
+
+/*
+ * Define abbreviation for a commonly used sequence below.
+ */
+#define DO_SEARCH() if (number <= 0) number = 1; \
+ mca_search(); \
+ cmd_exec(); \
+ multi_search((char *)NULL, number);
+
+
+ case A_F_SEARCH:
+ /*
+ * Search forward for a pattern.
+ * Get the first char of the pattern.
+ */
+ search_type = SRCH_FORW;
+ if (number <= 0)
+ number = 1;
+ mca_search();
+ c = getcc();
+ goto again;
+
+ case A_B_SEARCH:
+ /*
+ * Search backward for a pattern.
+ * Get the first char of the pattern.
+ */
+ search_type = SRCH_BACK;
+ if (number <= 0)
+ number = 1;
+ mca_search();
+ c = getcc();
+ goto again;
+
+ case A_AGAIN_SEARCH:
+ /*
+ * Repeat previous search.
+ */
+ DO_SEARCH();
+ break;
+
+ case A_T_AGAIN_SEARCH:
+ /*
+ * Repeat previous search, multiple files.
+ */
+ search_type |= SRCH_PAST_EOF;
+ DO_SEARCH();
+ break;
+
+ case A_REVERSE_SEARCH:
+ /*
+ * Repeat previous search, in reverse direction.
+ */
+ save_search_type = search_type;
+ search_type = SRCH_REVERSE(search_type);
+ DO_SEARCH();
+ search_type = save_search_type;
+ break;
+
+ case A_T_REVERSE_SEARCH:
+ /*
+ * Repeat previous search,
+ * multiple files in reverse direction.
+ */
+ save_search_type = search_type;
+ search_type = SRCH_REVERSE(search_type);
+ search_type |= SRCH_PAST_EOF;
+ DO_SEARCH();
+ search_type = save_search_type;
+ break;
+
+ case A_UNDO_SEARCH:
+ undo_search();
+ break;
+
+ case A_HELP:
+ /*
+ * Help.
+ */
+ if (nohelp)
+ {
+ bell();
+ break;
+ }
+ clear_bot();
+ putstr(" help");
+ cmd_exec();
+ help(0);
+ break;
+
+ case A_EXAMINE:
+#if EXAMINE
+ /*
+ * Edit a new file. Get the filename.
+ */
+ start_mca(A_EXAMINE, "Examine: ", ml_examine);
+ c = getcc();
+ goto again;
+#else
+ error("Command not available", NULL_PARG);
+ break;
+#endif
+
+ case A_VISUAL:
+ /*
+ * Invoke an editor on the input file.
+ */
+#if EDITOR
+ if (strcmp(get_filename(curr_ifile), "-") == 0)
+ {
+ error("Cannot edit standard input", NULL_PARG);
+ break;
+ }
+ if (curr_altfilename != NULL)
+ {
+ error("Cannot edit file processed with LESSOPEN",
+ NULL_PARG);
+ break;
+ }
+ /*
+ * Expand the editor prototype string
+ * and pass it to the system to execute.
+ */
+ cmd_exec();
+ lsystem(pr_expand(editproto, 0));
+ /*
+ * Re-edit the file, since data may have changed.
+ * Some editors even recreate the file, so flushing
+ * buffers is not sufficient.
+ */
+ if (edit_ifile(curr_ifile))
+ quit(QUIT_ERROR);
+ break;
+#else
+ error("Command not available", NULL_PARG);
+ break;
+#endif
+
+ case A_NEXT_FILE:
+ /*
+ * Examine next file.
+ */
+ if (number <= 0)
+ number = 1;
+ if (edit_next(number))
+ {
+ if (quit_at_eof && hit_eof)
+ quit(QUIT_OK);
+ parg.p_string = (number > 1) ? "(N-th) " : "";
+ error("No %snext file", &parg);
+ }
+ break;
+
+ case A_PREV_FILE:
+ /*
+ * Examine previous file.
+ */
+ if (number <= 0)
+ number = 1;
+ if (edit_prev(number))
+ {
+ parg.p_string = (number > 1) ? "(N-th) " : "";
+ error("No %sprevious file", &parg);
+ }
+ break;
+
+ case A_INDEX_FILE:
+ /*
+ * Examine a particular file.
+ */
+ if (number <= 0)
+ number = 1;
+ if (edit_index(number))
+ error("No such file", NULL_PARG);
+ break;
+
+ case A_OPT_TOGGLE:
+ start_mca(A_OPT_TOGGLE, "-", (void*)NULL);
+ optflag = OPT_TOGGLE;
+ c = getcc();
+ goto again;
+
+ case A_DISP_OPTION:
+ /*
+ * Report a flag setting.
+ */
+ start_mca(A_DISP_OPTION, "_", (void*)NULL);
+ c = getcc();
+ if (c == erase_char || c == kill_char)
+ break;
+ toggle_option(c, "", OPT_NO_TOGGLE);
+ break;
+
+ case A_FIRSTCMD:
+ /*
+ * Set an initial command for new files.
+ */
+ start_mca(A_FIRSTCMD, "+", (void*)NULL);
+ c = getcc();
+ goto again;
+
+ case A_SHELL:
+ /*
+ * Shell escape.
+ */
+#if SHELL_ESCAPE
+ start_mca(A_SHELL, "!", ml_shell);
+ c = getcc();
+ goto again;
+#else
+ error("Command not available", NULL_PARG);
+ break;
+#endif
+
+ case A_SETMARK:
+ /*
+ * Set a mark.
+ */
+ start_mca(A_SETMARK, "mark: ", (void*)NULL);
+ c = getcc();
+ if (c == erase_char || c == kill_char ||
+ c == '\n' || c == '\r')
+ break;
+ setmark(c);
+ break;
+
+ case A_GOMARK:
+ /*
+ * Go to a mark.
+ */
+ start_mca(A_GOMARK, "goto mark: ", (void*)NULL);
+ c = getcc();
+ if (c == erase_char || c == kill_char ||
+ c == '\n' || c == '\r')
+ break;
+ gomark(c);
+ break;
+
+ case A_PIPE:
+#if PIPEC
+ start_mca(A_PIPE, "|mark: ", (void*)NULL);
+ c = getcc();
+ if (c == erase_char || c == kill_char)
+ break;
+ if (c == '\n' || c == '\r')
+ c = '.';
+ if (badmark(c))
+ break;
+ pipec = c;
+ start_mca(A_PIPE, "!", ml_shell);
+ c = getcc();
+ goto again;
+#else
+ error("Command not available", NULL_PARG);
+ break;
+#endif
+
+ case A_B_BRACKET:
+ case A_F_BRACKET:
+ start_mca(action, "Brackets: ", (void*)NULL);
+ c = getcc();
+ goto again;
+
+ case A_PREFIX:
+ /*
+ * The command is incomplete (more chars are needed).
+ * Display the current char, so the user knows
+ * what's going on, and get another character.
+ */
+ if (mca != A_PREFIX)
+ {
+ start_mca(A_PREFIX, " ", (void*)NULL);
+ cmd_reset();
+ (void) cmd_char(c);
+ }
+ c = getcc();
+ goto again;
+
+ case A_NOACTION:
+ break;
+
+ default:
+ bell();
+ break;
+ }
+ }
+}
diff --git a/usr.bin/less/configure b/usr.bin/less/configure
new file mode 100644
index 00000000000..1cf49c4ae05
--- /dev/null
+++ b/usr.bin/less/configure
@@ -0,0 +1,1934 @@
+#!/bin/sh
+
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf version 2.1
+# Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc.
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+# Defaults:
+ac_help=
+ac_default_prefix=/usr/local
+# Any additions from configure.in:
+
+# Initialize some variables set by options.
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+build=NONE
+cache_file=./config.cache
+exec_prefix=NONE
+host=NONE
+no_create=
+nonopt=NONE
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+target=NONE
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Initialize some other variables.
+subdirs=
+
+ac_prev=
+for ac_option
+do
+
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval "$ac_prev=\$ac_option"
+ ac_prev=
+ continue
+ fi
+
+ case "$ac_option" in
+ -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+ *) ac_optarg= ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case "$ac_option" in
+
+ -build | --build | --buil | --bui | --bu | --b)
+ ac_prev=build ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=* | --b=*)
+ build="$ac_optarg" ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file="$ac_optarg" ;;
+
+ -disable-* | --disable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ eval "enable_${ac_feature}=no" ;;
+
+ -enable-* | --enable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "enable_${ac_feature}='$ac_optarg'" ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix="$ac_optarg" ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he)
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat << EOF
+Usage: configure [options] [host]
+Options: [defaults in brackets after descriptions]
+Configuration:
+ --cache-file=FILE cache test results in FILE
+ --help print this message
+ --no-create do not create output files
+ --quiet, --silent do not print \`checking...' messages
+ --version print the version of autoconf that created configure
+Directory and file names:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=PREFIX install architecture-dependent files in PREFIX
+ [same as prefix]
+ --srcdir=DIR find the sources in DIR [configure dir or ..]
+ --program-prefix=PREFIX prepend PREFIX to installed program names
+ --program-suffix=SUFFIX append SUFFIX to installed program names
+ --program-transform-name=PROGRAM run sed PROGRAM on installed program names
+Host type:
+ --build=BUILD configure for building on BUILD [BUILD=HOST]
+ --host=HOST configure for HOST [guessed]
+ --target=TARGET configure for TARGET [TARGET=HOST]
+Features and packages:
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --x-includes=DIR X include files are in DIR
+ --x-libraries=DIR X library files are in DIR
+--enable and --with options recognized:$ac_help
+EOF
+ exit 0 ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host="$ac_optarg" ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix="$ac_optarg" ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix="$ac_optarg" ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix="$ac_optarg" ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name="$ac_optarg" ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site="$ac_optarg" ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir="$ac_optarg" ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target="$ac_optarg" ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers)
+ echo "configure generated by autoconf version 2.1"
+ exit 0 ;;
+
+ -with-* | --with-*)
+ ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "with_${ac_package}='$ac_optarg'" ;;
+
+ -without-* | --without-*)
+ ac_package=`echo $ac_option|sed -e 's/-*without-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ eval "with_${ac_package}=no" ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes="$ac_optarg" ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries="$ac_optarg" ;;
+
+ -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; }
+ ;;
+
+ *)
+ if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
+ echo "configure: warning: $ac_option: invalid host type" 1>&2
+ fi
+ if test "x$nonopt" != xNONE; then
+ { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; }
+ fi
+ nonopt="$ac_option"
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; }
+fi
+
+trap 'rm -fr conftest* confdefs* core $ac_clean_files; exit 1' 1 2 15
+
+# File descriptor usage:
+# 0 unused; standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 unused; some systems may open it to /dev/tty
+# 4 checking for... messages and results
+# 5 compiler messages saved in config.log
+if test "$silent" = yes; then
+ exec 4>/dev/null
+else
+ exec 4>&1
+fi
+exec 5>./config.log
+
+echo "\
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+" 1>&5
+
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell metacharacters.
+ac_configure_args=
+for ac_arg
+do
+ case "$ac_arg" in
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c) ;;
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+ ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ *) ac_configure_args="$ac_configure_args $ac_arg" ;;
+ esac
+done
+
+# NLS nuisances.
+# Only set LANG and LC_ALL to C if already set.
+# These must not be set unconditionally because not all systems understand
+# e.g. LANG=C (notably SCO).
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LANG+set}" = set; then LANG=C; export LANG; fi
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo > confdefs.h
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+ac_unique_file=forwback.c
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then its parent.
+ ac_prog=$0
+ ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
+ test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
+ srcdir=$ac_confdir
+ if test ! -r $srcdir/$ac_unique_file; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+ if test "$ac_srcdir_defaulted" = yes; then
+ { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; }
+ else
+ { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; }
+ fi
+fi
+srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
+
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+ if test "x$prefix" != xNONE; then
+ CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+ else
+ CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+ fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+ if test -r "$ac_site_file"; then
+ echo "loading site script $ac_site_file"
+ . "$ac_site_file"
+ fi
+done
+
+if test -r "$cache_file"; then
+ echo "loading cache $cache_file"
+ . $cache_file
+else
+ echo "creating cache $cache_file"
+ > $cache_file
+fi
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} $CFLAGS $CPPFLAGS conftest.$ac_ext -c 1>&5 2>&5'
+ac_link='${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext -o conftest $LIBS 1>&5 2>&5'
+
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+ # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+ if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+ ac_n= ac_c='
+' ac_t=' '
+ else
+ ac_n=-n ac_c= ac_t=
+ fi
+else
+ ac_n= ac_c='\c' ac_t=
+fi
+
+
+
+
+# Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+ for ac_dir in $PATH; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CC="gcc"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_prog_CC" && ac_cv_prog_CC="cc"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&4
+else
+ echo "$ac_t""no" 1>&4
+fi
+
+
+echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_prog_gcc'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ cat > conftest.c <<EOF
+#ifdef __GNUC__
+ yes;
+#endif
+EOF
+if ${CC-cc} -E conftest.c 2>&5 | egrep yes >/dev/null 2>&1; then
+ ac_cv_prog_gcc=yes
+else
+ ac_cv_prog_gcc=no
+fi
+fi
+echo "$ac_t""$ac_cv_prog_gcc" 1>&4
+if test $ac_cv_prog_gcc = yes; then
+ GCC=yes
+ if test "${CFLAGS+set}" != set; then
+ echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_prog_gcc_g'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then
+ ac_cv_prog_gcc_g=yes
+else
+ ac_cv_prog_gcc_g=no
+fi
+rm -f conftest*
+
+fi
+ echo "$ac_t""$ac_cv_prog_gcc_g" 1>&4
+ if test $ac_cv_prog_gcc_g = yes; then
+ CFLAGS="-g -O"
+ else
+ CFLAGS="-O"
+ fi
+ fi
+else
+ GCC=
+ test "${CFLAGS+set}" = set || CFLAGS="-g"
+fi
+
+echo $ac_n "checking for POSIXized ISC""... $ac_c" 1>&4
+if test -d /etc/conf/kconfig.d &&
+ grep _POSIX_VERSION /usr/include/sys/unistd.h >/dev/null 2>&1
+then
+ echo "$ac_t""yes" 1>&4
+ ISC=yes # If later tests want to check for ISC.
+ cat >> confdefs.h <<\EOF
+#define _POSIX_SOURCE 1
+EOF
+
+ if test "$GCC" = yes; then
+ CC="$CC -posix"
+ else
+ CC="$CC -Xp"
+ fi
+else
+ echo "$ac_t""no" 1>&4
+ ISC=
+fi
+
+echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&4
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+if eval "test \"`echo '${'ac_cv_prog_CPP'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ # This must be in double quotes, not single quotes, because CPP may get
+ # substituted into the Makefile and "${CC-cc}" will confuse make.
+ CPP="${CC-cc} -E"
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp.
+ cat > conftest.$ac_ext <<EOF
+#line 511 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+eval "$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ rm -rf conftest*
+ CPP="${CC-cc} -E -traditional-cpp"
+ cat > conftest.$ac_ext <<EOF
+#line 525 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+eval "$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ rm -rf conftest*
+ CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+ ac_cv_prog_CPP="$CPP"
+fi
+fi
+CPP="$ac_cv_prog_CPP"
+echo "$ac_t""$CPP" 1>&4
+
+if test $ac_cv_prog_gcc = yes; then
+ echo $ac_n "checking whether ${CC-cc} needs -traditional""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_prog_gcc_traditional'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ ac_pattern="Autoconf.*'x'"
+ cat > conftest.$ac_ext <<EOF
+#line 555 "configure"
+#include "confdefs.h"
+#include <sgtty.h>
+Autoconf TIOCGETP
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "$ac_pattern" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_prog_gcc_traditional=yes
+else
+ rm -rf conftest*
+ ac_cv_prog_gcc_traditional=no
+fi
+rm -f conftest*
+
+
+ if test $ac_cv_prog_gcc_traditional = no; then
+ cat > conftest.$ac_ext <<EOF
+#line 573 "configure"
+#include "confdefs.h"
+#include <termio.h>
+Autoconf TCGETA
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "$ac_pattern" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_prog_gcc_traditional=yes
+fi
+rm -f conftest*
+
+ fi
+fi
+ echo "$ac_t""$ac_cv_prog_gcc_traditional" 1>&4
+ if test $ac_cv_prog_gcc_traditional = yes; then
+ CC="$CC -traditional"
+ fi
+fi
+
+ac_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+ if test -f $ac_dir/install-sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f $ac_dir/install.sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; }
+fi
+ac_config_guess=$ac_aux_dir/config.guess
+ac_config_sub=$ac_aux_dir/config.sub
+ac_configure=$ac_aux_dir/configure # This should be Cygnus configure.
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# ./install, which can be erroneously created by make from ./install.sh.
+echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&4
+if test -z "$INSTALL"; then
+if eval "test \"`echo '${'ac_cv_path_install'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+ for ac_dir in $PATH; do
+ case "$ac_dir" in
+ ''|.|/etc|/usr/sbin|/usr/etc|/sbin|/usr/afsws/bin|/usr/ucb) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ for ac_prog in ginstall installbsd scoinst install; do
+ if test -f $ac_dir/$ac_prog; then
+ if test $ac_prog = install &&
+ grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ # OSF/1 installbsd also uses dspmsg, but is usable.
+ :
+ else
+ ac_cv_path_install="$ac_dir/$ac_prog -c"
+ break 2
+ fi
+ fi
+ done
+ ;;
+ esac
+ done
+ IFS="$ac_save_ifs"
+ # As a last resort, use the slow shell script.
+ test -z "$ac_cv_path_install" && ac_cv_path_install="$ac_install_sh"
+fi
+ INSTALL="$ac_cv_path_install"
+fi
+echo "$ac_t""$INSTALL" 1>&4
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+
+TERMLIBS=
+echo $ac_n "checking for -lcurses""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_lib_curses'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ ac_save_LIBS="$LIBS"
+LIBS="$LIBS -lcurses "
+cat > conftest.$ac_ext <<EOF
+#line 672 "configure"
+#include "confdefs.h"
+
+int main() { return 0; }
+int t() {
+initscr()
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ eval "ac_cv_lib_curses=yes"
+else
+ rm -rf conftest*
+ eval "ac_cv_lib_curses=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'curses`\" = yes"; then
+ echo "$ac_t""yes" 1>&4
+ TERMLIBS="$TERMLIBS -lcurses"
+else
+ echo "$ac_t""no" 1>&4
+fi
+
+echo $ac_n "checking for -ltermcap""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_lib_termcap'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ ac_save_LIBS="$LIBS"
+LIBS="$LIBS -ltermcap "
+cat > conftest.$ac_ext <<EOF
+#line 705 "configure"
+#include "confdefs.h"
+
+int main() { return 0; }
+int t() {
+tgetent()
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ eval "ac_cv_lib_termcap=yes"
+else
+ rm -rf conftest*
+ eval "ac_cv_lib_termcap=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'termcap`\" = yes"; then
+ echo "$ac_t""yes" 1>&4
+ TERMLIBS="$TERMLIBS -ltermcap"
+else
+ echo "$ac_t""no" 1>&4
+fi
+
+echo $ac_n "checking for -ltermlib""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_lib_termlib'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ ac_save_LIBS="$LIBS"
+LIBS="$LIBS -ltermlib "
+cat > conftest.$ac_ext <<EOF
+#line 738 "configure"
+#include "confdefs.h"
+
+int main() { return 0; }
+int t() {
+tgetent()
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ eval "ac_cv_lib_termlib=yes"
+else
+ rm -rf conftest*
+ eval "ac_cv_lib_termlib=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'termlib`\" = yes"; then
+ echo "$ac_t""yes" 1>&4
+ have_termlib=yes
+else
+ echo "$ac_t""no" 1>&4
+have_termlib=no
+fi
+
+echo $ac_n "checking for -lgen""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_lib_gen'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ ac_save_LIBS="$LIBS"
+LIBS="$LIBS -lgen "
+cat > conftest.$ac_ext <<EOF
+#line 772 "configure"
+#include "confdefs.h"
+
+int main() { return 0; }
+int t() {
+regcmp()
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ eval "ac_cv_lib_gen=yes"
+else
+ rm -rf conftest*
+ eval "ac_cv_lib_gen=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'gen`\" = yes"; then
+ echo "$ac_t""yes" 1>&4
+ ac_tr_lib=HAVE_LIB`echo gen | tr '[a-z]' '[A-Z]'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="$LIBS -lgen"
+
+else
+ echo "$ac_t""no" 1>&4
+fi
+
+echo $ac_n "checking for -lintl""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_lib_intl'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ ac_save_LIBS="$LIBS"
+LIBS="$LIBS -lintl "
+cat > conftest.$ac_ext <<EOF
+#line 811 "configure"
+#include "confdefs.h"
+
+int main() { return 0; }
+int t() {
+regcmp()
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ eval "ac_cv_lib_intl=yes"
+else
+ rm -rf conftest*
+ eval "ac_cv_lib_intl=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'intl`\" = yes"; then
+ echo "$ac_t""yes" 1>&4
+ ac_tr_lib=HAVE_LIB`echo intl | tr '[a-z]' '[A-Z]'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="$LIBS -lintl"
+
+else
+ echo "$ac_t""no" 1>&4
+fi
+
+echo $ac_n "checking for -lPW""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_lib_PW'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ ac_save_LIBS="$LIBS"
+LIBS="$LIBS -lPW "
+cat > conftest.$ac_ext <<EOF
+#line 850 "configure"
+#include "confdefs.h"
+
+int main() { return 0; }
+int t() {
+regcmp()
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ eval "ac_cv_lib_PW=yes"
+else
+ rm -rf conftest*
+ eval "ac_cv_lib_PW=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'PW`\" = yes"; then
+ echo "$ac_t""yes" 1>&4
+ ac_tr_lib=HAVE_LIB`echo PW | tr '[a-z]' '[A-Z]'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="$LIBS -lPW"
+
+else
+ echo "$ac_t""no" 1>&4
+fi
+
+
+echo $ac_n "checking for working terminal libraries""... $ac_c" 1>&4
+SAVE_LIBS=$LIBS
+LIBS="$LIBS $TERMLIBS"
+cat > conftest.$ac_ext <<EOF
+#line 887 "configure"
+#include "confdefs.h"
+
+int main() { return 0; }
+int t() {
+tgetent(0); tgetflag(0); tgetnum(0); tgetstr(0);
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ termok=yes
+else
+ rm -rf conftest*
+ termok=no
+fi
+rm -f conftest*
+
+if test $termok = yes; then
+ echo "$ac_t""using $TERMLIBS" 1>&4
+else
+ LIBS="$SAVE_LIBS"
+ if test $have_termlib = yes; then
+ LIBS="$LIBS -ltermlib"
+ echo "$ac_t""using -ltermlib" 1>&4
+ else
+ echo "$ac_t""TERMINAL LIBRARY BROKEN - configure failed" 1>&4
+ exit 1
+ fi
+fi
+
+# If we cannot run a trivial program, we must be cross compiling.
+echo $ac_n "checking whether cross-compiling""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_c_cross'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_cross=yes
+else
+cat > conftest.$ac_ext <<EOF
+#line 926 "configure"
+#include "confdefs.h"
+main(){return(0);}
+EOF
+eval $ac_link
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ ac_cv_c_cross=no
+else
+ ac_cv_c_cross=yes
+fi
+fi
+rm -fr conftest*
+fi
+cross_compiling=$ac_cv_c_cross
+echo "$ac_t""$ac_cv_c_cross" 1>&4
+
+echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_header_stdc'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ cat > conftest.$ac_ext <<EOF
+#line 947 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+EOF
+eval "$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ ac_cv_header_stdc=yes
+else
+ echo "$ac_err" >&5
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 969 "configure"
+#include "confdefs.h"
+#include <string.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "memchr" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 987 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "free" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+if test "$cross_compiling" = yes; then
+ ac_cv_header_stdc=no
+else
+cat > conftest.$ac_ext <<EOF
+#line 1008 "configure"
+#include "confdefs.h"
+#include <ctype.h>
+#define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int main () { int i; for (i = 0; i < 256; i++)
+if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2);
+exit (0); }
+
+EOF
+eval $ac_link
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ :
+else
+ ac_cv_header_stdc=no
+fi
+fi
+rm -fr conftest*
+fi
+fi
+echo "$ac_t""$ac_cv_header_stdc" 1>&4
+if test $ac_cv_header_stdc = yes; then
+ cat >> confdefs.h <<\EOF
+#define STDC_HEADERS 1
+EOF
+
+fi
+
+for ac_hdr in ctype.h errno.h fcntl.h stdio.h termcap.h termio.h termios.h time.h unistd.h values.h sys/ioctl.h sys/stream.h sys/ptem.h
+do
+ac_safe=`echo "$ac_hdr" | tr './\055' '___'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1045 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+eval "$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&4
+ ac_tr_hdr=HAVE_`echo $ac_hdr | tr '[a-z]./\055' '[A-Z]___'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&4
+fi
+done
+
+
+echo $ac_n "checking for off_t""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_type_off_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1079 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "off_t" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_off_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_off_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_off_t" 1>&4
+if test $ac_cv_type_off_t = no; then
+ cat >> confdefs.h <<\EOF
+#define off_t long
+EOF
+
+fi
+
+echo $ac_n "checking for void""... $ac_c" 1>&4
+cat > conftest.$ac_ext <<EOF
+#line 1107 "configure"
+#include "confdefs.h"
+
+int main() { return 0; }
+int t() {
+void *foo = 0;
+; return 0; }
+EOF
+if eval $ac_compile; then
+ rm -rf conftest*
+ echo "$ac_t""yes" 1>&4; cat >> confdefs.h <<\EOF
+#define HAVE_VOID 1
+EOF
+
+else
+ rm -rf conftest*
+ echo "$ac_t""no" 1>&4
+fi
+rm -f conftest*
+
+echo $ac_n "checking for time_t""... $ac_c" 1>&4
+cat > conftest.$ac_ext <<EOF
+#line 1129 "configure"
+#include "confdefs.h"
+#include <time.h>
+int main() { return 0; }
+int t() {
+time_t t = 0;
+; return 0; }
+EOF
+if eval $ac_compile; then
+ rm -rf conftest*
+ echo "$ac_t""yes" 1>&4; cat >> confdefs.h <<\EOF
+#define HAVE_TIME_T 1
+EOF
+
+else
+ rm -rf conftest*
+ echo "$ac_t""no" 1>&4
+fi
+rm -f conftest*
+
+
+echo $ac_n "checking return type of signal handlers""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_type_signal'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1155 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <signal.h>
+#ifdef signal
+#undef signal
+#endif
+extern void (*signal ()) ();
+int main() { return 0; }
+int t() {
+int i;
+; return 0; }
+EOF
+if eval $ac_compile; then
+ rm -rf conftest*
+ ac_cv_type_signal=void
+else
+ rm -rf conftest*
+ ac_cv_type_signal=int
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_signal" 1>&4
+cat >> confdefs.h <<EOF
+#define RETSIGTYPE $ac_cv_type_signal
+EOF
+
+
+for ac_func in _setjmp system sigsetmask memcpy strchr
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1191 "configure"
+#include "confdefs.h"
+#include <ctype.h> /* Arbitrary system header to define __stub macros. */
+/* Override any gcc2 internal prototype to avoid an error. */
+char $ac_func();
+
+int main() { return 0; }
+int t() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+
+fi
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&4
+ ac_tr_func=HAVE_`echo $ac_func | tr '[a-z]' '[A-Z]'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&4
+fi
+done
+
+echo $ac_n "checking for tcgetattr""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_func_tcgetattr'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1238 "configure"
+#include "confdefs.h"
+#include <ctype.h> /* Arbitrary system header to define __stub macros. */
+/* Override any gcc2 internal prototype to avoid an error. */
+char tcgetattr();
+
+int main() { return 0; }
+int t() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_tcgetattr) || defined (__stub___tcgetattr)
+choke me
+#else
+tcgetattr();
+#endif
+
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ eval "ac_cv_func_tcgetattr=yes"
+else
+ rm -rf conftest*
+ eval "ac_cv_func_tcgetattr=no"
+fi
+rm -f conftest*
+
+fi
+if eval "test \"`echo '$ac_cv_func_'tcgetattr`\" = yes"; then
+ echo "$ac_t""yes" 1>&4
+ cat >> confdefs.h <<\EOF
+#define HAVE_TERMIOS_FUNCS 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&4
+fi
+
+echo $ac_n "checking for fileno""... $ac_c" 1>&4
+cat > conftest.$ac_ext <<EOF
+#line 1280 "configure"
+#include "confdefs.h"
+
+#if HAVE_STDIO_H
+#include <stdio.h>
+#endif
+int main() { return 0; }
+int t() {
+static int x; x = fileno(stdin);
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ echo "$ac_t""yes" 1>&4; cat >> confdefs.h <<\EOF
+#define HAVE_FILENO 1
+EOF
+
+else
+ rm -rf conftest*
+ echo "$ac_t""no" 1>&4
+fi
+rm -f conftest*
+
+echo $ac_n "checking for strerror""... $ac_c" 1>&4
+cat > conftest.$ac_ext <<EOF
+#line 1305 "configure"
+#include "confdefs.h"
+
+#if HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+int main() { return 0; }
+int t() {
+static char *x; x = strerror(0);
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ echo "$ac_t""yes" 1>&4; cat >> confdefs.h <<\EOF
+#define HAVE_STRERROR 1
+EOF
+
+else
+ rm -rf conftest*
+ echo "$ac_t""no" 1>&4
+fi
+rm -f conftest*
+
+echo $ac_n "checking for sys_errlist""... $ac_c" 1>&4
+cat > conftest.$ac_ext <<EOF
+#line 1336 "configure"
+#include "confdefs.h"
+
+int main() { return 0; }
+int t() {
+extern char *sys_errlist[]; static char **x; x = sys_errlist;
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ echo "$ac_t""yes" 1>&4; cat >> confdefs.h <<\EOF
+#define HAVE_SYS_ERRLIST 1
+EOF
+
+else
+ rm -rf conftest*
+ echo "$ac_t""no" 1>&4
+fi
+rm -f conftest*
+
+echo $ac_n "checking for errno""... $ac_c" 1>&4
+cat > conftest.$ac_ext <<EOF
+#line 1358 "configure"
+#include "confdefs.h"
+
+int main() { return 0; }
+int t() {
+extern int errno; static int x; x = errno;
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ echo "$ac_t""yes" 1>&4; cat >> confdefs.h <<\EOF
+#define HAVE_ERRNO 1
+EOF
+
+else
+ rm -rf conftest*
+ echo "$ac_t""no" 1>&4
+fi
+rm -f conftest*
+
+echo $ac_n "checking for locale""... $ac_c" 1>&4
+cat > conftest.$ac_ext <<EOF
+#line 1380 "configure"
+#include "confdefs.h"
+#include <locale.h>
+#include <ctype.h>
+int main() { return 0; }
+int t() {
+setlocale(LC_CTYPE,""); isprint(0); iscntrl(0);
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ echo "$ac_t""yes" 1>&4; cat >> confdefs.h <<\EOF
+#define HAVE_LOCALE 1
+EOF
+
+else
+ rm -rf conftest*
+ echo "$ac_t""no" 1>&4
+fi
+rm -f conftest*
+
+echo $ac_n "checking for ctype functions""... $ac_c" 1>&4
+cat > conftest.$ac_ext <<EOF
+#line 1403 "configure"
+#include "confdefs.h"
+
+#if HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+int main() { return 0; }
+int t() {
+static int x; x = isupper(x); x = tolower(x); x = toupper(x);
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ echo "$ac_t""yes" 1>&4; cat >> confdefs.h <<\EOF
+#define HAVE_UPPER_LOWER 1
+EOF
+
+else
+ rm -rf conftest*
+ echo "$ac_t""no" 1>&4
+fi
+rm -f conftest*
+
+
+have_ospeed=no
+echo $ac_n "checking termcap for ospeed""... $ac_c" 1>&4
+cat > conftest.$ac_ext <<EOF
+#line 1430 "configure"
+#include "confdefs.h"
+
+#include <sys/types.h>
+#if HAVE_TERMIOS_H
+#include <termios.h>
+#endif
+#if HAVE_TERMCAP_H
+#include <termcap.h>
+#endif
+int main() { return 0; }
+int t() {
+ospeed = 0;
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ echo "$ac_t""yes" 1>&4; cat >> confdefs.h <<\EOF
+#define HAVE_OSPEED 1
+EOF
+ have_ospeed=yes
+fi
+rm -f conftest*
+
+if test $have_ospeed = no; then
+cat > conftest.$ac_ext <<EOF
+#line 1456 "configure"
+#include "confdefs.h"
+
+int main() { return 0; }
+int t() {
+extern short ospeed; ospeed = 0;
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ echo "$ac_t""yes" 1>&4; cat >> confdefs.h <<\EOF
+#define HAVE_OSPEED 1
+EOF
+ cat >> confdefs.h <<\EOF
+#define MUST_DEFINE_OSPEED 1
+EOF
+
+else
+ rm -rf conftest*
+ echo "$ac_t""no" 1>&4
+fi
+rm -f conftest*
+
+fi
+
+have_regex=no
+have_posix_regex=unknown
+echo "checking for regular expression library" 1>&4
+if test "$cross_compiling" = yes; then
+ have_posix_regex=unknown
+else
+cat > conftest.$ac_ext <<EOF
+#line 1488 "configure"
+#include "confdefs.h"
+
+#include <sys/types.h>
+#include <regex.h>
+main() { regex_t r; regmatch_t rm;
+if (regcomp(&r, "abc", 0)) exit(1);
+if (regexec(&r, "xabcy", 1, &rm, 0)) exit(1);
+exit(0); }
+EOF
+eval $ac_link
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ have_posix_regex=yes
+else
+ have_posix_regex=no
+fi
+fi
+rm -fr conftest*
+if test $have_posix_regex = yes; then
+ echo "$ac_t""using POSIX regcomp" 1>&4
+ cat >> confdefs.h <<\EOF
+#define HAVE_POSIX_REGCOMP 1
+EOF
+
+ have_regex=yes
+elif test $have_posix_regex = unknown; then
+ cat > conftest.$ac_ext <<EOF
+#line 1515 "configure"
+#include "confdefs.h"
+
+#include <sys/types.h>
+#include <regex.h>
+int main() { return 0; }
+int t() {
+regex_t *r; regfree(r);
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ echo "$ac_t""using POSIX regcomp" 1>&4
+ cat >> confdefs.h <<\EOF
+#define HAVE_POSIX_REGCOMP 1
+EOF
+ have_regex=yes
+fi
+rm -f conftest*
+
+fi
+if test $have_regex = no; then
+echo $ac_n "checking for regcmp""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_func_regcmp'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1542 "configure"
+#include "confdefs.h"
+#include <ctype.h> /* Arbitrary system header to define __stub macros. */
+/* Override any gcc2 internal prototype to avoid an error. */
+char regcmp();
+
+int main() { return 0; }
+int t() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_regcmp) || defined (__stub___regcmp)
+choke me
+#else
+regcmp();
+#endif
+
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ eval "ac_cv_func_regcmp=yes"
+else
+ rm -rf conftest*
+ eval "ac_cv_func_regcmp=no"
+fi
+rm -f conftest*
+
+fi
+if eval "test \"`echo '$ac_cv_func_'regcmp`\" = yes"; then
+ echo "$ac_t""yes" 1>&4
+ echo "$ac_t""using regcmp" 1>&4; cat >> confdefs.h <<\EOF
+#define HAVE_REGCMP 1
+EOF
+ have_regex=yes
+else
+ echo "$ac_t""no" 1>&4
+fi
+
+fi
+if test $have_regex = no; then
+cat > conftest.$ac_ext <<EOF
+#line 1585 "configure"
+#include "confdefs.h"
+
+#include "regexp.h"
+int main() { return 0; }
+int t() {
+regcomp("");
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ echo "$ac_t""using V8 regcomp" 1>&4; cat >> confdefs.h <<\EOF
+#define HAVE_V8_REGCOMP 1
+EOF
+ have_regex=yes
+fi
+rm -f conftest*
+
+fi
+if test $have_regex = no && test -f ${srcdir}/regex.c; then
+echo "$ac_t""using POSIX regcomp -- local source" 1>&4; cat >> confdefs.h <<\EOF
+#define HAVE_POSIX_REGCOMP 1
+EOF
+ REGEX_O='regex.$(O)' have_regex=yes
+fi
+if test $have_regex = no && test -f ${srcdir}/regexp.c; then
+echo "$ac_t""using V8 regcomp -- local source" 1>&4; cat >> confdefs.h <<\EOF
+#define HAVE_V8_REGCOMP 1
+EOF
+ REGEX_O='regexp.$(O)' have_regex=yes
+fi
+if test $have_regex = no; then
+echo "$ac_t""using re_comp" 1>&4; echo $ac_n "checking for re_comp""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_func_re_comp'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1622 "configure"
+#include "confdefs.h"
+#include <ctype.h> /* Arbitrary system header to define __stub macros. */
+/* Override any gcc2 internal prototype to avoid an error. */
+char re_comp();
+
+int main() { return 0; }
+int t() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_re_comp) || defined (__stub___re_comp)
+choke me
+#else
+re_comp();
+#endif
+
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ eval "ac_cv_func_re_comp=yes"
+else
+ rm -rf conftest*
+ eval "ac_cv_func_re_comp=no"
+fi
+rm -f conftest*
+
+fi
+if eval "test \"`echo '$ac_cv_func_'re_comp`\" = yes"; then
+ echo "$ac_t""yes" 1>&4
+ cat >> confdefs.h <<\EOF
+#define HAVE_RE_COMP 1
+EOF
+ have_regex=yes
+else
+ echo "$ac_t""no" 1>&4
+fi
+
+fi
+if test $have_regex = no; then
+echo "$ac_t""cannot find regular expression library" 1>&4; cat >> confdefs.h <<\EOF
+#define NO_REGEX 1
+EOF
+
+fi
+
+trap '' 1 2 15
+if test -w $cache_file; then
+echo "updating cache $cache_file"
+cat > $cache_file <<\EOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs. It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already. You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# Ultrix sh set writes to stderr and can't be redirected directly.
+(set) 2>&1 |
+ sed -n "s/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/: \${\1='\2'}/p" \
+ >> $cache_file
+else
+echo "not updating unwritable cache $cache_file"
+fi
+
+trap 'rm -fr conftest* confdefs* core $ac_clean_files; exit 1' 1 2 15
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Any assignment to VPATH causes Sun make to only execute
+# the first set of double-colon rules, so remove it if not needed.
+# If there is a colon in the path, we need to keep it.
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d'
+fi
+
+trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
+
+DEFS=-DHAVE_CONFIG_H
+
+# Without the "./", some shells look in PATH for config.status.
+: ${CONFIG_STATUS=./config.status}
+
+echo creating $CONFIG_STATUS
+rm -f $CONFIG_STATUS
+cat > $CONFIG_STATUS <<EOF
+#!/bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $ac_configure_args
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
+for ac_option
+do
+ case "\$ac_option" in
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
+ exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
+ -version | --version | --versio | --versi | --vers | --ver | --ve | --v)
+ echo "$CONFIG_STATUS generated by autoconf version 2.1"
+ exit 0 ;;
+ -help | --help | --hel | --he | --h)
+ echo "\$ac_cs_usage"; exit 0 ;;
+ *) echo "\$ac_cs_usage"; exit 1 ;;
+ esac
+done
+
+ac_given_srcdir=$srcdir
+ac_given_INSTALL="$INSTALL"
+
+trap 'rm -fr Makefile defines.h conftest*; exit 1' 1 2 15
+
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g$/@g/; /@g$/s/[\\\\&%]/\\\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g$/%g/' > conftest.subs <<\CEOF
+$ac_vpsub
+$extrasub
+s%@CFLAGS@%$CFLAGS%g
+s%@CPPFLAGS@%$CPPFLAGS%g
+s%@CXXFLAGS@%$CXXFLAGS%g
+s%@DEFS@%$DEFS%g
+s%@LDFLAGS@%$LDFLAGS%g
+s%@LIBS@%$LIBS%g
+s%@exec_prefix@%$exec_prefix%g
+s%@prefix@%$prefix%g
+s%@program_transform_name@%$program_transform_name%g
+s%@CC@%$CC%g
+s%@CPP@%$CPP%g
+s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
+s%@INSTALL_DATA@%$INSTALL_DATA%g
+s%@REGEX_O@%$REGEX_O%g
+
+CEOF
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+CONFIG_FILES=\${CONFIG_FILES-"Makefile"}
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+ # Support "outfile[:infile]", defaulting infile="outfile.in".
+ case "$ac_file" in
+ *:*) ac_file_in=`echo "$ac_file"|sed 's%.*:%%'`
+ ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ *) ac_file_in="${ac_file}.in" ;;
+ esac
+
+ # Adjust relative srcdir, etc. for subdirectories.
+
+ # Remove last slash and all that follows it. Not all systems have dirname.
+ ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+ if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+ # The file is in a subdirectory.
+ test ! -d "$ac_dir" && mkdir "$ac_dir"
+ ac_dir_suffix="/$ac_dir"
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+ else
+ ac_dir_suffix= ac_dots=
+ fi
+
+ case "$ac_given_srcdir" in
+ .) srcdir=.
+ if test -z "$ac_dots"; then top_srcdir=.
+ else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+ /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+ *) # Relative path.
+ srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+ top_srcdir="$ac_dots$ac_given_srcdir" ;;
+ esac
+
+ case "$ac_given_INSTALL" in
+ [/$]*) INSTALL="$ac_given_INSTALL" ;;
+ *) INSTALL="$ac_dots$ac_given_INSTALL" ;;
+ esac
+ echo creating "$ac_file"
+ rm -f "$ac_file"
+ configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+ case "$ac_file" in
+ *Makefile*) ac_comsub="1i\\
+# $configure_input" ;;
+ *) ac_comsub= ;;
+ esac
+ sed -e "$ac_comsub
+s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+s%@INSTALL@%$INSTALL%g
+" -f conftest.subs $ac_given_srcdir/$ac_file_in > $ac_file
+fi; done
+rm -f conftest.subs
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)'
+ac_dB='\([ ][ ]*\)[^ ]*%\1#\2'
+ac_dC='\3'
+ac_dD='%g'
+# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE".
+ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+ac_uB='\([ ]\)%\1#\2define\3'
+ac_uC=' '
+ac_uD='\4%g'
+# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+ac_eB='$%\1#\2define\3'
+ac_eC=' '
+ac_eD='%g'
+
+CONFIG_HEADERS=${CONFIG_HEADERS-"defines.h"}
+for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then
+ # Support "outfile[:infile]", defaulting infile="outfile.in".
+ case "$ac_file" in
+ *:*) ac_file_in=`echo "$ac_file"|sed 's%.*:%%'`
+ ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ *) ac_file_in="${ac_file}.in" ;;
+ esac
+
+ echo creating $ac_file
+
+ rm -f conftest.frag conftest.in conftest.out
+ cp $ac_given_srcdir/$ac_file_in conftest.in
+
+EOF
+
+# Transform confdefs.h into a sed script conftest.vals that substitutes
+# the proper values into config.h.in to produce config.h. And first:
+# Protect against being on the right side of a sed subst in config.status.
+# Protect against being in an unquoted here document in config.status.
+rm -f conftest.vals
+cat > conftest.hdr <<\EOF
+s/[\\&%]/\\&/g
+s%[\\$`]%\\&%g
+s%#define \([A-Za-z_][A-Za-z0-9_]*\) \(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp
+s%ac_d%ac_u%gp
+s%ac_u%ac_e%gp
+EOF
+sed -n -f conftest.hdr confdefs.h > conftest.vals
+rm -f conftest.hdr
+
+# This sed command replaces #undef with comments. This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >> conftest.vals <<\EOF
+s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */%
+EOF
+
+# Break up conftest.vals because some shells have a limit on
+# the size of here documents, and old seds have small limits too.
+# Maximum number of lines to put in a single here document.
+ac_max_here_lines=12
+
+rm -f conftest.tail
+while :
+do
+ ac_lines=`grep -c . conftest.vals`
+ # grep -c gives empty output for an empty file on some AIX systems.
+ if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi
+ # Write a limited-size here document to conftest.frag.
+ echo ' cat > conftest.frag <<CEOF' >> $CONFIG_STATUS
+ sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS
+ echo 'CEOF
+ sed -f conftest.frag conftest.in > conftest.out
+ rm -f conftest.in
+ mv conftest.out conftest.in
+' >> $CONFIG_STATUS
+ sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail
+ rm -f conftest.vals
+ mv conftest.tail conftest.vals
+done
+rm -f conftest.vals
+
+cat >> $CONFIG_STATUS <<\EOF
+ rm -f conftest.frag conftest.h
+ echo "/* $ac_file. Generated automatically by configure. */" > conftest.h
+ cat conftest.in >> conftest.h
+ rm -f conftest.in
+ if cmp -s $ac_file conftest.h 2>/dev/null; then
+ echo "$ac_file is unchanged"
+ rm -f conftest.h
+ else
+ rm -f $ac_file
+ mv conftest.h $ac_file
+ fi
+fi; done
+
+
+
+exit 0
+EOF
+chmod +x $CONFIG_STATUS
+rm -fr confdefs* $ac_clean_files
+test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS
+
diff --git a/usr.bin/less/configure.in b/usr.bin/less/configure.in
new file mode 100644
index 00000000000..9c7326450fa
--- /dev/null
+++ b/usr.bin/less/configure.in
@@ -0,0 +1,161 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT(forwback.c)
+AC_CONFIG_HEADER(defines.h)
+
+dnl Checks for programs.
+AC_PROG_CC
+AC_ISC_POSIX
+AC_PROG_GCC_TRADITIONAL
+AC_PROG_INSTALL
+
+dnl Checks for libraries.
+TERMLIBS=
+AC_CHECK_LIB(curses, initscr, [TERMLIBS="$TERMLIBS -lcurses"])
+AC_CHECK_LIB(termcap, tgetent, [TERMLIBS="$TERMLIBS -ltermcap"])
+AC_CHECK_LIB(termlib, tgetent, [have_termlib=yes], [have_termlib=no])
+dnl Regular expressions (regcmp) are in -lgen on Solaris 2,
+dnl and in -lintl on SCO Unix.
+AC_CHECK_LIB(gen, regcmp)
+AC_CHECK_LIB(intl, regcmp)
+AC_CHECK_LIB(PW, regcmp)
+
+dnl Solaris has curses & termcap, but they don't work without libucb
+dnl which is broken, so we use termlib.
+AC_MSG_CHECKING(for working terminal libraries)
+SAVE_LIBS=$LIBS
+LIBS="$LIBS $TERMLIBS"
+AC_TRY_LINK(, [tgetent(0); tgetflag(0); tgetnum(0); tgetstr(0);],
+ [termok=yes], [termok=no])
+if test $termok = yes; then
+ AC_MSG_RESULT(using $TERMLIBS)
+else
+ LIBS="$SAVE_LIBS"
+ if test $have_termlib = yes; then
+ LIBS="$LIBS -ltermlib"
+ AC_MSG_RESULT(using -ltermlib)
+ else
+ AC_MSG_RESULT(TERMINAL LIBRARY BROKEN - configure failed)
+ exit 1
+ fi
+fi
+
+dnl Checks for header files.
+AC_HEADER_STDC
+AC_CHECK_HEADERS(ctype.h errno.h fcntl.h stdio.h termcap.h termio.h termios.h time.h unistd.h values.h sys/ioctl.h sys/stream.h sys/ptem.h)
+
+dnl Checks for identifiers.
+AC_TYPE_OFF_T
+AC_MSG_CHECKING(for void)
+AC_TRY_COMPILE(, [void *foo = 0;],
+ [AC_MSG_RESULT(yes); AC_DEFINE(HAVE_VOID)], [AC_MSG_RESULT(no)])
+AC_MSG_CHECKING(for time_t)
+AC_TRY_COMPILE([#include <time.h>], [time_t t = 0;],
+ [AC_MSG_RESULT(yes); AC_DEFINE(HAVE_TIME_T)], [AC_MSG_RESULT(no)])
+
+dnl Checks for functions and external variables.
+AC_TYPE_SIGNAL
+AC_CHECK_FUNCS(_setjmp system sigsetmask memcpy strchr)
+dnl Some systems have termios.h but not the corresponding functions.
+AC_CHECK_FUNC(tcgetattr, AC_DEFINE(HAVE_TERMIOS_FUNCS))
+AC_MSG_CHECKING(for fileno)
+AC_TRY_LINK([
+#if HAVE_STDIO_H
+#include <stdio.h>
+#endif], [static int x; x = fileno(stdin);],
+ [AC_MSG_RESULT(yes); AC_DEFINE(HAVE_FILENO)], [AC_MSG_RESULT(no)])
+AC_MSG_CHECKING(for strerror)
+AC_TRY_LINK([
+#if HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif], [static char *x; x = strerror(0);],
+ [AC_MSG_RESULT(yes); AC_DEFINE(HAVE_STRERROR)], [AC_MSG_RESULT(no)])
+AC_MSG_CHECKING(for sys_errlist)
+AC_TRY_LINK(, [extern char *sys_errlist[]; static char **x; x = sys_errlist;],
+ [AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SYS_ERRLIST)], [AC_MSG_RESULT(no)])
+AC_MSG_CHECKING(for errno)
+AC_TRY_LINK(, [extern int errno; static int x; x = errno;],
+ [AC_MSG_RESULT(yes); AC_DEFINE(HAVE_ERRNO)], [AC_MSG_RESULT(no)])
+AC_MSG_CHECKING(for locale)
+AC_TRY_LINK([#include <locale.h>
+#include <ctype.h>], [setlocale(LC_CTYPE,""); isprint(0); iscntrl(0);],
+ [AC_MSG_RESULT(yes); AC_DEFINE(HAVE_LOCALE)], [AC_MSG_RESULT(no)])
+AC_MSG_CHECKING(for ctype functions)
+AC_TRY_LINK([
+#if HAVE_CTYPE_H
+#include <ctype.h>
+#endif], [static int x; x = isupper(x); x = tolower(x); x = toupper(x);],
+ [AC_MSG_RESULT(yes); AC_DEFINE(HAVE_UPPER_LOWER)], [AC_MSG_RESULT(no)])
+
+dnl Checks for external variable ospeed in the termcap library.
+have_ospeed=no
+AC_MSG_CHECKING(termcap for ospeed)
+AC_TRY_LINK([
+#include <sys/types.h>
+#if HAVE_TERMIOS_H
+#include <termios.h>
+#endif
+#if HAVE_TERMCAP_H
+#include <termcap.h>
+#endif], [ospeed = 0;],
+[AC_MSG_RESULT(yes); AC_DEFINE(HAVE_OSPEED) have_ospeed=yes])
+if test $have_ospeed = no; then
+AC_TRY_LINK(, [extern short ospeed; ospeed = 0;],
+ [AC_MSG_RESULT(yes); AC_DEFINE(HAVE_OSPEED) AC_DEFINE(MUST_DEFINE_OSPEED)],
+ [AC_MSG_RESULT(no)])
+fi
+
+dnl Checks for regular expression functions.
+have_regex=no
+have_posix_regex=unknown
+AC_CHECKING(for regular expression library)
+dnl Some versions of Solaris have a regcomp() function, but it doesn't work!
+dnl So we run a test program. If we're cross-compiling, do it the old way.
+AC_TRY_RUN([
+#include <sys/types.h>
+#include <regex.h>
+main() { regex_t r; regmatch_t rm;
+if (regcomp(&r, "abc", 0)) exit(1);
+if (regexec(&r, "xabcy", 1, &rm, 0)) exit(1);
+exit(0); }],
+ have_posix_regex=yes, have_posix_regex=no, have_posix_regex=unknown)
+if test $have_posix_regex = yes; then
+ AC_MSG_RESULT(using POSIX regcomp)
+ AC_DEFINE(HAVE_POSIX_REGCOMP)
+ have_regex=yes
+elif test $have_posix_regex = unknown; then
+ AC_TRY_LINK([
+#include <sys/types.h>
+#include <regex.h>],
+ [regex_t *r; regfree(r);],
+ AC_MSG_RESULT(using POSIX regcomp)
+ AC_DEFINE(HAVE_POSIX_REGCOMP) have_regex=yes)
+fi
+if test $have_regex = no; then
+AC_CHECK_FUNC(regcmp,
+AC_MSG_RESULT(using regcmp); AC_DEFINE(HAVE_REGCMP) have_regex=yes)
+fi
+if test $have_regex = no; then
+AC_TRY_LINK([
+#include "regexp.h"], [regcomp("");],
+AC_MSG_RESULT(using V8 regcomp); AC_DEFINE(HAVE_V8_REGCOMP) have_regex=yes)
+fi
+if test $have_regex = no && test -f ${srcdir}/regex.c; then
+AC_MSG_RESULT(using POSIX regcomp -- local source); AC_DEFINE(HAVE_POSIX_REGCOMP) REGEX_O='regex.$(O)' AC_SUBST(REGEX_O) have_regex=yes
+fi
+if test $have_regex = no && test -f ${srcdir}/regexp.c; then
+AC_MSG_RESULT(using V8 regcomp -- local source); AC_DEFINE(HAVE_V8_REGCOMP) REGEX_O='regexp.$(O)' AC_SUBST(REGEX_O) have_regex=yes
+fi
+if test $have_regex = no; then
+AC_MSG_RESULT(using re_comp); AC_CHECK_FUNC(re_comp, AC_DEFINE(HAVE_RE_COMP) have_regex=yes)
+fi
+if test $have_regex = no; then
+AC_MSG_RESULT(cannot find regular expression library); AC_DEFINE(NO_REGEX)
+fi
+
+AC_OUTPUT(Makefile)
diff --git a/usr.bin/less/decode.c b/usr.bin/less/decode.c
new file mode 100644
index 00000000000..b0c2dcd42cd
--- /dev/null
+++ b/usr.bin/less/decode.c
@@ -0,0 +1,654 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * Routines to decode user commands.
+ *
+ * This is all table driven.
+ * A command table is a sequence of command descriptors.
+ * Each command descriptor is a sequence of bytes with the following format:
+ * <c1><c2>...<cN><0><action>
+ * The characters c1,c2,...,cN are the command string; that is,
+ * the characters which the user must type.
+ * It is terminated by a null <0> byte.
+ * The byte after the null byte is the action code associated
+ * with the command string.
+ * If an action byte is OR-ed with A_EXTRA, this indicates
+ * that the option byte is followed by an extra string.
+ *
+ * There may be many command tables.
+ * The first (default) table is built-in.
+ * Other tables are read in from "lesskey" files.
+ * All the tables are linked together and are searched in order.
+ */
+
+#include "less.h"
+#include "cmd.h"
+#include "lesskey.h"
+
+extern int erase_char, kill_char;
+
+/*
+ * Command table is ordered roughly according to expected
+ * frequency of use, so the common commands are near the beginning.
+ */
+static unsigned char cmdtable[] =
+{
+ '\r',0, A_F_LINE,
+ '\n',0, A_F_LINE,
+ 'e',0, A_F_LINE,
+ 'j',0, A_F_LINE,
+ CONTROL('E'),0, A_F_LINE,
+ CONTROL('N'),0, A_F_LINE,
+ 'k',0, A_B_LINE,
+ 'y',0, A_B_LINE,
+ CONTROL('Y'),0, A_B_LINE,
+ CONTROL('K'),0, A_B_LINE,
+ CONTROL('P'),0, A_B_LINE,
+ 'J',0, A_FF_LINE,
+ 'K',0, A_BF_LINE,
+ 'Y',0, A_BF_LINE,
+ 'd',0, A_F_SCROLL,
+ CONTROL('D'),0, A_F_SCROLL,
+ 'u',0, A_B_SCROLL,
+ CONTROL('U'),0, A_B_SCROLL,
+ ' ',0, A_F_SCREEN,
+ 'f',0, A_F_SCREEN,
+ CONTROL('F'),0, A_F_SCREEN,
+ CONTROL('V'),0, A_F_SCREEN,
+ 'b',0, A_B_SCREEN,
+ CONTROL('B'),0, A_B_SCREEN,
+ ESC,'v',0, A_B_SCREEN,
+ 'z',0, A_F_WINDOW,
+ 'w',0, A_B_WINDOW,
+ 'F',0, A_F_FOREVER,
+ 'R',0, A_FREPAINT,
+ 'r',0, A_REPAINT,
+ CONTROL('R'),0, A_REPAINT,
+ CONTROL('L'),0, A_REPAINT,
+ ESC,'u',0, A_UNDO_SEARCH,
+ 'g',0, A_GOLINE,
+ '<',0, A_GOLINE,
+ ESC,'<',0, A_GOLINE,
+ 'p',0, A_PERCENT,
+ '%',0, A_PERCENT,
+ '{',0, A_F_BRACKET|A_EXTRA, '{','}',0,
+ '}',0, A_B_BRACKET|A_EXTRA, '{','}',0,
+ '(',0, A_F_BRACKET|A_EXTRA, '(',')',0,
+ ')',0, A_B_BRACKET|A_EXTRA, '(',')',0,
+ '[',0, A_F_BRACKET|A_EXTRA, '[',']',0,
+ ']',0, A_B_BRACKET|A_EXTRA, '[',']',0,
+ ESC,CONTROL('F'),0, A_F_BRACKET,
+ ESC,CONTROL('B'),0, A_B_BRACKET,
+ 'G',0, A_GOEND,
+ ESC,'>',0, A_GOEND,
+ '>',0, A_GOEND,
+ 'P',0, A_GOPOS,
+
+ '0',0, A_DIGIT,
+ '1',0, A_DIGIT,
+ '2',0, A_DIGIT,
+ '3',0, A_DIGIT,
+ '4',0, A_DIGIT,
+ '5',0, A_DIGIT,
+ '6',0, A_DIGIT,
+ '7',0, A_DIGIT,
+ '8',0, A_DIGIT,
+ '9',0, A_DIGIT,
+
+ '=',0, A_STAT,
+ CONTROL('G'),0, A_STAT,
+ ':','f',0, A_STAT,
+ '/',0, A_F_SEARCH,
+ '?',0, A_B_SEARCH,
+ ESC,'/',0, A_F_SEARCH|A_EXTRA, '*',0,
+ ESC,'?',0, A_B_SEARCH|A_EXTRA, '*',0,
+ 'n',0, A_AGAIN_SEARCH,
+ ESC,'n',0, A_T_AGAIN_SEARCH,
+ 'N',0, A_REVERSE_SEARCH,
+ ESC,'N',0, A_T_REVERSE_SEARCH,
+ 'm',0, A_SETMARK,
+ '\'',0, A_GOMARK,
+ CONTROL('X'),CONTROL('X'),0, A_GOMARK,
+ 'E',0, A_EXAMINE,
+ ':','e',0, A_EXAMINE,
+ CONTROL('X'),CONTROL('V'),0, A_EXAMINE,
+ ':','n',0, A_NEXT_FILE,
+ ':','p',0, A_PREV_FILE,
+ ':','x',0, A_INDEX_FILE,
+ '-',0, A_OPT_TOGGLE,
+ ':','t',0, A_OPT_TOGGLE|A_EXTRA, 't',0,
+ 's',0, A_OPT_TOGGLE|A_EXTRA, 'o',0,
+ '_',0, A_DISP_OPTION,
+ '|',0, A_PIPE,
+ 'v',0, A_VISUAL,
+ '!',0, A_SHELL,
+ '+',0, A_FIRSTCMD,
+
+ 'H',0, A_HELP,
+ 'h',0, A_HELP,
+ 'V',0, A_VERSION,
+ 'q',0, A_QUIT,
+ ':','q',0, A_QUIT,
+ ':','Q',0, A_QUIT,
+ 'Z','Z',0, A_QUIT
+};
+
+static unsigned char edittable[] =
+{
+ '\t',0, EC_F_COMPLETE, /* TAB */
+ '\17',0, EC_B_COMPLETE, /* BACKTAB */
+ '\14',0, EC_EXPAND, /* CTRL-L */
+ CONTROL('V'),0, EC_LITERAL, /* BACKSLASH */
+ CONTROL('A'),0, EC_LITERAL, /* BACKSLASH */
+ ESC,'l',0, EC_RIGHT, /* ESC l */
+ ESC,'h',0, EC_LEFT, /* ESC h */
+ ESC,'b',0, EC_W_LEFT, /* ESC b */
+ ESC,'w',0, EC_W_RIGHT, /* ESC w */
+ ESC,'i',0, EC_INSERT, /* ESC i */
+ ESC,'x',0, EC_DELETE, /* ESC x */
+ ESC,'X',0, EC_W_DELETE, /* ESC X */
+ ESC,'\b',0, EC_W_BACKSPACE, /* ESC BACKSPACE */
+ ESC,'0',0, EC_HOME, /* ESC 0 */
+ ESC,'$',0, EC_END, /* ESC $ */
+ ESC,'k',0, EC_UP, /* ESC k */
+ ESC,'j',0, EC_DOWN, /* ESC j */
+ ESC,'\t',0, EC_B_COMPLETE /* ESC TAB */
+};
+
+/*
+ * Structure to support a list of command tables.
+ */
+struct tablelist
+{
+ struct tablelist *t_next;
+ char *t_start;
+ char *t_end;
+};
+
+/*
+ * List of command tables and list of line-edit tables.
+ */
+static struct tablelist *list_fcmd_tables = NULL;
+static struct tablelist *list_ecmd_tables = NULL;
+
+
+/*
+ * Initialize the command lists.
+ */
+ public void
+init_cmds()
+{
+ /*
+ * Add the default command tables.
+ */
+ add_fcmd_table((char*)cmdtable, sizeof(cmdtable));
+ add_ecmd_table((char*)edittable, sizeof(edittable));
+ get_editkeys();
+#if USERFILE
+ /*
+ * Try to add the tables in the standard lesskey file "$HOME/.less".
+ */
+ add_hometable();
+#endif
+}
+
+/*
+ *
+ */
+ static int
+add_cmd_table(tlist, buf, len)
+ struct tablelist **tlist;
+ char *buf;
+ int len;
+{
+ register struct tablelist *t;
+
+ if (len == 0)
+ return (0);
+ /*
+ * Allocate a tablelist structure, initialize it,
+ * and link it into the list of tables.
+ */
+ if ((t = (struct tablelist *)
+ calloc(1, sizeof(struct tablelist))) == NULL)
+ {
+ return (-1);
+ }
+ t->t_start = buf;
+ t->t_end = buf + len;
+ t->t_next = *tlist;
+ *tlist = t;
+ return (0);
+}
+
+/*
+ * Add a command table.
+ */
+ public void
+add_fcmd_table(buf, len)
+ char *buf;
+ int len;
+{
+ if (add_cmd_table(&list_fcmd_tables, buf, len) < 0)
+ error("Warning: some commands disabled", NULL_PARG);
+}
+
+/*
+ * Add an editing command table.
+ */
+ public void
+add_ecmd_table(buf, len)
+ char *buf;
+ int len;
+{
+ if (add_cmd_table(&list_ecmd_tables, buf, len) < 0)
+ error("Warning: some edit commands disabled", NULL_PARG);
+}
+
+/*
+ * Search a single command table for the command string in cmd.
+ */
+ public int
+cmd_search(cmd, table, endtable, sp)
+ char *cmd;
+ char *table;
+ char *endtable;
+ char **sp;
+{
+ register char *p;
+ register char *q;
+ register int a;
+
+ for (p = table, q = cmd; p < endtable; p++, q++)
+ {
+ if (*p == *q)
+ {
+ /*
+ * Current characters match.
+ * If we're at the end of the string, we've found it.
+ * Return the action code, which is the character
+ * after the null at the end of the string
+ * in the command table.
+ */
+ if (*p == '\0')
+ {
+ a = *++p & 0377;
+ if (a == A_END_LIST)
+ {
+ /*
+ * We get here only if the original
+ * cmd string passed in was empty ("").
+ * I don't think that can happen,
+ * but just in case ...
+ */
+ return (A_UINVALID);
+ }
+ /*
+ * Check for an "extra" string.
+ */
+ if (a & A_EXTRA)
+ {
+ *sp = ++p;
+ a &= ~A_EXTRA;
+ } else
+ *sp = NULL;
+ return (a);
+ }
+ } else if (*q == '\0')
+ {
+ /*
+ * Hit the end of the user's command,
+ * but not the end of the string in the command table.
+ * The user's command is incomplete.
+ */
+ return (A_PREFIX);
+ } else
+ {
+ /*
+ * Not a match.
+ * Skip ahead to the next command in the
+ * command table, and reset the pointer
+ * to the beginning of the user's command.
+ */
+ if (*p == '\0' && p[1] == A_END_LIST)
+ {
+ /*
+ * A_END_LIST is a special marker that tells
+ * us to abort the cmd search.
+ */
+ return (A_UINVALID);
+ }
+ while (*p++ != '\0') ;
+ if (*p & A_EXTRA)
+ while (*++p != '\0') ;
+ q = cmd-1;
+ }
+ }
+ /*
+ * No match found in the entire command table.
+ */
+ return (A_INVALID);
+}
+
+/*
+ * Decode a command character and return the associated action.
+ * The "extra" string, if any, is returned in sp.
+ */
+ static int
+cmd_decode(tlist, cmd, sp)
+ struct tablelist *tlist;
+ char *cmd;
+ char **sp;
+{
+ register struct tablelist *t;
+ register int action = A_INVALID;
+
+ /*
+ * Search thru all the command tables.
+ * Stop when we find an action which is not A_INVALID.
+ */
+ for (t = tlist; t != NULL; t = t->t_next)
+ {
+ action = cmd_search(cmd, t->t_start, t->t_end, sp);
+ if (action != A_INVALID)
+ break;
+ }
+ return (action);
+}
+
+/*
+ * Decode a command from the cmdtables list.
+ */
+ public int
+fcmd_decode(cmd, sp)
+ char *cmd;
+ char **sp;
+{
+ return (cmd_decode(list_fcmd_tables, cmd, sp));
+}
+
+/*
+ * Decode a command from the edittables list.
+ */
+ public int
+ecmd_decode(cmd, sp)
+ char *cmd;
+ char **sp;
+{
+ return (cmd_decode(list_ecmd_tables, cmd, sp));
+}
+
+#if USERFILE
+ static int
+gint(sp)
+ char **sp;
+{
+ int n;
+
+ n = *(*sp)++;
+ n += *(*sp)++ * KRADIX;
+ return (n);
+}
+
+ static int
+old_lesskey(buf, len)
+ char *buf;
+ int len;
+{
+ /*
+ * Old-style lesskey file.
+ * The file must end with either
+ * ...,cmd,0,action
+ * or ...,cmd,0,action|A_EXTRA,string,0
+ * So the last byte or the second to last byte must be zero.
+ */
+ if (buf[len-1] != '\0' && buf[len-2] != '\0')
+ return (-1);
+ add_fcmd_table(buf, len);
+ return (0);
+}
+
+ static int
+new_lesskey(buf, len)
+ char *buf;
+ int len;
+{
+ char *p;
+ register int c;
+ register int done;
+ register int n;
+
+ /*
+ * New-style lesskey file.
+ * Extract the pieces.
+ */
+ if (buf[len-3] != C0_END_LESSKEY_MAGIC ||
+ buf[len-2] != C1_END_LESSKEY_MAGIC ||
+ buf[len-1] != C2_END_LESSKEY_MAGIC)
+ return (-1);
+ p = buf + 4;
+ done = 0;
+ while (!done)
+ {
+ c = *p++;
+ switch (c)
+ {
+ case CMD_SECTION:
+ n = gint(&p);
+ add_fcmd_table(p, n);
+ p += n;
+ break;
+ case EDIT_SECTION:
+ n = gint(&p);
+ add_ecmd_table(p, n);
+ p += n;
+ break;
+ case END_SECTION:
+ done = 1;
+ break;
+ default:
+ free(buf);
+ return (-1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Set up a user command table, based on a "lesskey" file.
+ */
+ public int
+lesskey(filename)
+ char *filename;
+{
+ register char *buf;
+ register POSITION len;
+ register long n;
+ register int f;
+
+ /*
+ * Try to open the lesskey file.
+ */
+ f = open(filename, OPEN_READ);
+ if (f < 0)
+ return (1);
+
+ /*
+ * Read the file into a buffer.
+ * We first figure out the size of the file and allocate space for it.
+ * {{ Minimal error checking is done here.
+ * A garbage .less file will produce strange results.
+ * To avoid a large amount of error checking code here, we
+ * rely on the lesskey program to generate a good .less file. }}
+ */
+ len = filesize(f);
+ if (len == NULL_POSITION || len < 3)
+ {
+ /*
+ * Bad file (valid file must have at least 3 chars).
+ */
+ close(f);
+ return (-1);
+ }
+ if ((buf = (char *) calloc((int)len, sizeof(char))) == NULL)
+ {
+ close(f);
+ return (-1);
+ }
+ if (lseek(f, (off_t)0, 0) == BAD_LSEEK)
+ {
+ free(buf);
+ close(f);
+ return (-1);
+ }
+ n = read(f, buf, (unsigned int) len);
+ close(f);
+ if (n != len)
+ {
+ free(buf);
+ return (-1);
+ }
+
+ /*
+ * Figure out if this is an old-style (before version 241)
+ * or new-style lesskey file format.
+ */
+ if (buf[0] != C0_LESSKEY_MAGIC || buf[1] != C1_LESSKEY_MAGIC ||
+ buf[2] != C2_LESSKEY_MAGIC || buf[3] != C3_LESSKEY_MAGIC)
+ return (old_lesskey(buf, (int)len));
+ return (new_lesskey(buf, (int)len));
+}
+
+/*
+ * Add the standard lesskey file "$HOME/.less"
+ */
+ public void
+add_hometable()
+{
+ char *filename;
+ PARG parg;
+
+ filename = homefile(LESSKEYFILE);
+ if (filename == NULL)
+ return;
+ if (lesskey(filename) < 0)
+ {
+ parg.p_string = filename;
+ error("Cannot use lesskey file \"%s\"", &parg);
+ }
+ free(filename);
+}
+#endif
+
+/*
+ * See if a char is a special line-editing command.
+ */
+ public int
+editchar(c, flags)
+ int c;
+ int flags;
+{
+ int action;
+ int nch;
+ char *s;
+ char usercmd[MAX_CMDLEN+1];
+
+ /*
+ * An editing character could actually be a sequence of characters;
+ * for example, an escape sequence sent by pressing the uparrow key.
+ * To match the editing string, we use the command decoder
+ * but give it the edit-commands command table
+ * This table is constructed to match the user's keyboard.
+ */
+ if (c == erase_char)
+ return (EC_BACKSPACE);
+ if (c == kill_char)
+ return (EC_LINEKILL);
+
+ /*
+ * Collect characters in a buffer.
+ * Start with the one we have, and get more if we need them.
+ */
+ nch = 0;
+ do {
+ if (nch > 0)
+ c = getcc();
+ usercmd[nch] = c;
+ usercmd[nch+1] = '\0';
+ nch++;
+ action = ecmd_decode(usercmd, &s);
+ } while (action == A_PREFIX);
+
+ if (flags & EC_NOHISTORY)
+ {
+ /*
+ * The caller says there is no history list.
+ * Reject any history-manipulation action.
+ */
+ switch (action)
+ {
+ case EC_UP:
+ case EC_DOWN:
+ action = A_INVALID;
+ break;
+ }
+ }
+ if (flags & EC_NOCOMPLETE)
+ {
+ /*
+ * The caller says we don't want any filename completion cmds.
+ * Reject them.
+ */
+ switch (action)
+ {
+ case EC_F_COMPLETE:
+ case EC_B_COMPLETE:
+ case EC_EXPAND:
+ action = A_INVALID;
+ break;
+ }
+ }
+ if ((flags & EC_PEEK) || action == A_INVALID)
+ {
+ /*
+ * We're just peeking, or we didn't understand the command.
+ * Unget all the characters we read in the loop above.
+ * This does NOT include the original character that was
+ * passed in as a parameter.
+ */
+ while (nch > 1) {
+ ungetcc(usercmd[--nch]);
+ }
+ } else
+ {
+ if (s != NULL)
+ ungetsc(s);
+ }
+ return action;
+}
+
diff --git a/usr.bin/less/defines.dos b/usr.bin/less/defines.dos
new file mode 100644
index 00000000000..02ec2e0cea7
--- /dev/null
+++ b/usr.bin/less/defines.dos
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/* DOS definition file for less. */
+/*
+ * This file has 2 sections:
+ * User preferences.
+ * Settings always true for the Microsoft C compiler for MS-DOS systems.
+ */
+
+/* User preferences. */
+
+/*
+ * SECURE is 1 if you wish to disable a bunch of features in order to
+ * be safe to run by unprivileged users.
+ */
+#define SECURE 0
+
+/*
+ * SHELL_ESCAPE is 1 if you wish to allow shell escapes.
+ * (This is possible only if your system supplies the system() function.)
+ */
+#define SHELL_ESCAPE (!SECURE)
+
+/*
+ * EXAMINE is 1 if you wish to allow examining files by name from within less.
+ */
+#define EXAMINE (!SECURE)
+
+/*
+ * TAB_COMPLETE_FILENAME is 1 if you wish to allow the TAB key
+ * to complete filenames at prompts.
+ */
+#define TAB_COMPLETE_FILENAME (!SECURE)
+
+/*
+ * CMD_HISTORY is 1 if you wish to allow keys to cycle through
+ * previous commands at prompts.
+ */
+#define CMD_HISTORY 1
+
+/*
+ * HILITE_SEARCH is 1 if you wish to have search targets to be
+ * displayed in standout mode.
+ */
+#define HILITE_SEARCH 1
+
+/*
+ * EDITOR is 1 if you wish to allow editor invocation (the "v" command).
+ * (This is possible only if your system supplies the system() function.)
+ * EDIT_PGM is the name of the (default) editor to be invoked.
+ */
+#define EDITOR (!SECURE)
+#define EDIT_PGM "vi"
+
+/*
+ * TAGS is 1 if you wish to support tag files.
+ */
+#define TAGS (!SECURE)
+
+/*
+ * USERFILE is 1 if you wish to allow a .less file to specify
+ * user-defined key bindings.
+ */
+#define USERFILE (!SECURE)
+
+/*
+ * GLOB is 1 if you wish to have shell metacharacters expanded in filenames.
+ * This will generally work if your system provides the "popen" function
+ * and the "echo" shell command.
+ */
+#define GLOB 0
+
+/*
+ * PIPEC is 1 if you wish to have the "|" command
+ * which allows the user to pipe data into a shell command.
+ */
+#define PIPEC 0
+
+/*
+ * LOGFILE is 1 if you wish to allow the -l option (to create log files).
+ */
+#define LOGFILE (!SECURE)
+
+/*
+ * GNU_OPTIONS is 1 if you wish to support the GNU-style command
+ * line options --help and --version.
+ */
+#define GNU_OPTIONS 1
+
+/*
+ * ONLY_RETURN is 1 if you want RETURN to be the only input which
+ * will continue past an error message.
+ * Otherwise, any key will continue past an error message.
+ */
+#define ONLY_RETURN 0
+
+/*
+ * LESSKEYFILE is the filename of the default lesskey output file
+ * (in the HOME directory).
+ * DEF_LESSKEYINFILE is the filename of the default lesskey input
+ * (in the HOME directory).
+ */
+#define LESSKEYFILE "_less"
+#define DEF_LESSKEYINFILE "_lesskey"
+
+/*
+ * HELPFILE is the full pathname of the help file.
+ */
+#define HELPFILE "less.hlp"
+
+/* Settings always true for the Microsoft C compiler for MS-DOS systems. */
+
+/*
+ * Define MSOFTC if compiling under Microsoft C.
+ */
+#define MSOFTC 1
+
+/*
+ * HAVE_SYS_TYPES_H is 1 if your system has <sys/types.h>.
+ */
+#define HAVE_SYS_TYPES_H 1
+
+/*
+ * HAVE_STAT is 1 if your system has the stat() call.
+ */
+#define HAVE_STAT 1
+
+/*
+ * HAVE_PERROR is 1 if your system has the perror() call.
+ * (Actually, if it has sys_errlist, sys_nerr and errno.)
+ */
+#define HAVE_PERROR 1
+
+/*
+ * HAVE_TIME is 1 if your system has the time() call.
+ */
+#define HAVE_TIME 1
+
+/*
+ * HAVE_SHELL is 1 if your system supports a SHELL command interpreter.
+ */
+#define HAVE_SHELL 0
+
+/* Define to `long' if <sys/types.h> doesn't define. */
+/* #define off_t long */
+
+/* Define as the return type of signal handlers (int or void). */
+#define RETSIGTYPE void
+
+/* Define if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+#define __STDC__ 1
+
+/*
+ * Regular expression library.
+ * Define exactly one of the following to be 1:
+ * HAVE_POSIX_REGCOMP: POSIX regcomp() and regex.h
+ * HAVE_RE_COMP: BSD re_comp()
+ * HAVE_REGCMP: System V regcmp()
+ * HAVE_V8_REGCOMP: Henry Spencer V8 regcomp() and regexp.h
+ * NO_REGEX: pattern matching is supported, but without metacharacters.
+ */
+/* #undef HAVE_POSIX_REGCOMP */
+/* #undef HAVE_RE_COMP */
+/* #undef HAVE_REGCMP */
+/* #undef HAVE_V8_REGCOMP */
+#define NO_REGEX 1
+
+/* Define HAVE_VOID if your compiler supports the "void" type. */
+#define HAVE_VOID 1
+
+/* Define HAVE_TIME_T if your system supports the "time_t" type. */
+#define HAVE_TIME_T 0
+
+/* Define HAVE_STRERROR if you have the strerror() function. */
+#define HAVE_STRERROR 0
+
+/* Define HAVE_FILENO if you have the fileno() macro. */
+#define HAVE_FILENO 0
+
+/* Define HAVE_ERRNO if you have the errno variable */
+#define HAVE_ERRNO 0
+
+/* Define HAVE_SYS_ERRLIST if you have the sys_errlist[] variable */
+#define HAVE_SYS_ERRLIST 0
+
+/* Define HAVE_OSPEED if your termcap library has the ospeed variable */
+#define HAVE_OSPEED 0
+/* Define MUST_DEFINE_OSPEED if you have ospeed but it is not defined
+ * in termcap.h. */
+#define MUST_DEFINE_OSPEED 0
+
+/* Define HAVE_LOCALE if you have locale.h and setlocale. */
+#define HAVE_LOCALE 0
+
+/* Define HAVE_TERMIOS_FUNCS if you have tcgetattr/tcsetattr */
+#define HAVE_TERMIOS_FUNCS 0
+
+/* Define HAVE_UPPER_LOWER if you have isupper, islower, toupper, tolower */
+#define HAVE_UPPER_LOWER 1
+
+/* Define if you have the _setjmp function. */
+#define HAVE__SETJMP 0
+
+/* Define if you have the memcpy function. */
+#define HAVE_MEMCPY 1
+
+/* Define if you have the sigsetmask function. */
+#define HAVE_SIGSETMASK 0
+
+/* Define if you have the strchr function. */
+#define HAVE_STRCHR 1
+
+/* Define if you have the system function. */
+#define HAVE_SYSTEM 1
+
+/* Define if you have the <ctype.h> header file. */
+#define HAVE_CTYPE_H 1
+
+/* Define if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 0
+
+/* Define if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define if you have the <stdio.h> header file. */
+#define HAVE_STDIO_H 1
+
+/* Define if you have the <sys/ioctl.h> header file. */
+#define HAVE_SYS_IOCTL_H 0
+
+/* Define if you have the <sys/ptem.h> header file. */
+#define HAVE_SYS_PTEM_H 0
+
+/* Define if you have the <sys/stream.h> header file. */
+#define HAVE_SYS_STREAM_H 0
+
+/* Define if you have the <termcap.h> header file. */
+/* #undef HAVE_TERMCAP_H */
+
+/* Define if you have the <termio.h> header file. */
+#define HAVE_TERMIO_H 0
+
+/* Define if you have the <termios.h> header file. */
+#define HAVE_TERMIOS_H 1
+
+/* Define if you have the <time.h> header file. */
+#define HAVE_TIME_H 1
+
+/* Define if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define if you have the <values.h> header file. */
+#define HAVE_VALUES_H 0
diff --git a/usr.bin/less/defines.h.in b/usr.bin/less/defines.h.in
new file mode 100644
index 00000000000..113ecee2b35
--- /dev/null
+++ b/usr.bin/less/defines.h.in
@@ -0,0 +1,267 @@
+/* defines.h.in. Generated automatically from configure.in by autoheader. */
+/* Unix definition file for less. -*- C -*-
+ *
+ * This file has 3 sections:
+ * User preferences.
+ * Settings always true on Unix.
+ * Settings automatically determined by configure.
+ *
+ * * * * * * WARNING * * * * * *
+ * If you edit defines.h by hand, do "touch stamp-h" before you run make
+ * so config.status doesn't overwrite your changes.
+ */
+
+/* User preferences. */
+
+/*
+ * SECURE is 1 if you wish to disable a bunch of features in order to
+ * be safe to run by unprivileged users.
+ */
+#define SECURE 0
+
+/*
+ * SHELL_ESCAPE is 1 if you wish to allow shell escapes.
+ * (This is possible only if your system supplies the system() function.)
+ */
+#define SHELL_ESCAPE (!SECURE)
+
+/*
+ * EXAMINE is 1 if you wish to allow examining files by name from within less.
+ */
+#define EXAMINE (!SECURE)
+
+/*
+ * TAB_COMPLETE_FILENAME is 1 if you wish to allow the TAB key
+ * to complete filenames at prompts.
+ */
+#define TAB_COMPLETE_FILENAME (!SECURE)
+
+/*
+ * CMD_HISTORY is 1 if you wish to allow keys to cycle through
+ * previous commands at prompts.
+ */
+#define CMD_HISTORY 1
+
+/*
+ * HILITE_SEARCH is 1 if you wish to have search targets to be
+ * displayed in standout mode.
+ */
+#define HILITE_SEARCH 1
+
+/*
+ * EDITOR is 1 if you wish to allow editor invocation (the "v" command).
+ * (This is possible only if your system supplies the system() function.)
+ * EDIT_PGM is the name of the (default) editor to be invoked.
+ */
+#define EDITOR (!SECURE)
+#define EDIT_PGM "vi"
+
+/*
+ * TAGS is 1 if you wish to support tag files.
+ */
+#define TAGS (!SECURE)
+
+/*
+ * USERFILE is 1 if you wish to allow a .less file to specify
+ * user-defined key bindings.
+ */
+#define USERFILE (!SECURE)
+
+/*
+ * GLOB is 1 if you wish to have shell metacharacters expanded in filenames.
+ * This will generally work if your system provides the "popen" function
+ * and the "echo" shell command.
+ */
+#define GLOB (!SECURE)
+
+/*
+ * PIPEC is 1 if you wish to have the "|" command
+ * which allows the user to pipe data into a shell command.
+ */
+#define PIPEC (!SECURE)
+
+/*
+ * LOGFILE is 1 if you wish to allow the -l option (to create log files).
+ */
+#define LOGFILE (!SECURE)
+
+/*
+ * GNU_OPTIONS is 1 if you wish to support the GNU-style command
+ * line options --help and --version.
+ */
+#define GNU_OPTIONS 1
+
+/*
+ * ONLY_RETURN is 1 if you want RETURN to be the only input which
+ * will continue past an error message.
+ * Otherwise, any key will continue past an error message.
+ */
+#define ONLY_RETURN 0
+
+/*
+ * LESSKEYFILE is the filename of the default lesskey output file
+ * (in the HOME directory).
+ * DEF_LESSKEYINFILE is the filename of the default lesskey input
+ * (in the HOME directory).
+ */
+#define LESSKEYFILE ".less"
+#define DEF_LESSKEYINFILE ".lesskey"
+
+
+/* Settings always true on Unix. */
+
+/*
+ * Define MSOFTC if compiling under Microsoft C.
+ */
+#define MSOFTC 0
+
+/*
+ * HAVE_SYS_TYPES_H is 1 if your system has <sys/types.h>.
+ */
+#define HAVE_SYS_TYPES_H 1
+
+/*
+ * HAVE_STAT is 1 if your system has the stat() call.
+ */
+#define HAVE_STAT 1
+
+/*
+ * HAVE_PERROR is 1 if your system has the perror() call.
+ * (Actually, if it has sys_errlist, sys_nerr and errno.)
+ */
+#define HAVE_PERROR 1
+
+/*
+ * HAVE_TIME is 1 if your system has the time() call.
+ */
+#define HAVE_TIME 1
+
+/*
+ * HAVE_SHELL is 1 if your system supports a SHELL command interpreter.
+ */
+#define HAVE_SHELL 1
+
+/* Settings automatically determined by configure. */
+
+/* Define to `long' if <sys/types.h> doesn't define. */
+#undef off_t
+
+/* Define if you need to in order for stat and other things to work. */
+#undef _POSIX_SOURCE
+
+/* Define as the return type of signal handlers (int or void). */
+#undef RETSIGTYPE
+
+/* Define if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/*
+ * Regular expression library.
+ * Define exactly one of the following to be 1:
+ * HAVE_POSIX_REGCOMP: POSIX regcomp() and regex.h
+ * HAVE_RE_COMP: BSD re_comp()
+ * HAVE_REGCMP: System V regcmp()
+ * HAVE_V8_REGCOMP: Henry Spencer V8 regcomp() and regexp.h
+ * NO_REGEX: pattern matching is supported, but without metacharacters.
+ */
+#undef HAVE_POSIX_REGCOMP
+#undef HAVE_RE_COMP
+#undef HAVE_REGCMP
+#undef HAVE_V8_REGCOMP
+#undef NO_REGEX
+
+/* Define HAVE_VOID if your compiler supports the "void" type. */
+#undef HAVE_VOID
+
+/* Define HAVE_TIME_T if your system supports the "time_t" type. */
+#undef HAVE_TIME_T
+
+/* Define HAVE_STRERROR if you have the strerror() function. */
+#undef HAVE_STRERROR
+
+/* Define HAVE_FILENO if you have the fileno() macro. */
+#undef HAVE_FILENO
+
+/* Define HAVE_ERRNO if you have the errno variable */
+#undef HAVE_ERRNO
+
+/* Define HAVE_SYS_ERRLIST if you have the sys_errlist[] variable */
+#undef HAVE_SYS_ERRLIST
+
+/* Define HAVE_OSPEED if your termcap library has the ospeed variable */
+#undef HAVE_OSPEED
+/* Define MUST_DEFINE_OSPEED if you have ospeed but it is not defined
+ * in termcap.h. */
+#undef MUST_DEFINE_OSPEED
+
+/* Define HAVE_LOCALE if you have locale.h and setlocale. */
+#undef HAVE_LOCALE
+
+/* Define HAVE_TERMIOS_FUNCS if you have tcgetattr/tcsetattr */
+#undef HAVE_TERMIOS_FUNCS
+
+/* Define HAVE_UPPER_LOWER if you have isupper, islower, toupper, tolower */
+#undef HAVE_UPPER_LOWER
+
+/* Define if you have the _setjmp function. */
+#undef HAVE__SETJMP
+
+/* Define if you have the memcpy function. */
+#undef HAVE_MEMCPY
+
+/* Define if you have the sigsetmask function. */
+#undef HAVE_SIGSETMASK
+
+/* Define if you have the strchr function. */
+#undef HAVE_STRCHR
+
+/* Define if you have the system function. */
+#undef HAVE_SYSTEM
+
+/* Define if you have the <ctype.h> header file. */
+#undef HAVE_CTYPE_H
+
+/* Define if you have the <errno.h> header file. */
+#undef HAVE_ERRNO_H
+
+/* Define if you have the <fcntl.h> header file. */
+#undef HAVE_FCNTL_H
+
+/* Define if you have the <stdio.h> header file. */
+#undef HAVE_STDIO_H
+
+/* Define if you have the <sys/ioctl.h> header file. */
+#undef HAVE_SYS_IOCTL_H
+
+/* Define if you have the <sys/ptem.h> header file. */
+#undef HAVE_SYS_PTEM_H
+
+/* Define if you have the <sys/stream.h> header file. */
+#undef HAVE_SYS_STREAM_H
+
+/* Define if you have the <termcap.h> header file. */
+#undef HAVE_TERMCAP_H
+
+/* Define if you have the <termio.h> header file. */
+#undef HAVE_TERMIO_H
+
+/* Define if you have the <termios.h> header file. */
+#undef HAVE_TERMIOS_H
+
+/* Define if you have the <time.h> header file. */
+#undef HAVE_TIME_H
+
+/* Define if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define if you have the <values.h> header file. */
+#undef HAVE_VALUES_H
+
+/* Define if you have the PW library (-lPW). */
+#undef HAVE_LIBPW
+
+/* Define if you have the gen library (-lgen). */
+#undef HAVE_LIBGEN
+
+/* Define if you have the intl library (-lintl). */
+#undef HAVE_LIBINTL
diff --git a/usr.bin/less/defines.h.top b/usr.bin/less/defines.h.top
new file mode 100644
index 00000000000..5d1f8c5bf70
--- /dev/null
+++ b/usr.bin/less/defines.h.top
@@ -0,0 +1,143 @@
+/* Unix definition file for less. -*- C -*-
+ *
+ * This file has 3 sections:
+ * User preferences.
+ * Settings always true on Unix.
+ * Settings automatically determined by configure.
+ *
+ * * * * * * WARNING * * * * * *
+ * If you edit defines.h by hand, do "touch stamp-h" before you run make
+ * so config.status doesn't overwrite your changes.
+ */
+
+/* User preferences. */
+
+/*
+ * SECURE is 1 if you wish to disable a bunch of features in order to
+ * be safe to run by unprivileged users.
+ */
+#define SECURE 0
+
+/*
+ * SHELL_ESCAPE is 1 if you wish to allow shell escapes.
+ * (This is possible only if your system supplies the system() function.)
+ */
+#define SHELL_ESCAPE (!SECURE)
+
+/*
+ * EXAMINE is 1 if you wish to allow examining files by name from within less.
+ */
+#define EXAMINE (!SECURE)
+
+/*
+ * TAB_COMPLETE_FILENAME is 1 if you wish to allow the TAB key
+ * to complete filenames at prompts.
+ */
+#define TAB_COMPLETE_FILENAME (!SECURE)
+
+/*
+ * CMD_HISTORY is 1 if you wish to allow keys to cycle through
+ * previous commands at prompts.
+ */
+#define CMD_HISTORY 1
+
+/*
+ * HILITE_SEARCH is 1 if you wish to have search targets to be
+ * displayed in standout mode.
+ */
+#define HILITE_SEARCH 1
+
+/*
+ * EDITOR is 1 if you wish to allow editor invocation (the "v" command).
+ * (This is possible only if your system supplies the system() function.)
+ * EDIT_PGM is the name of the (default) editor to be invoked.
+ */
+#define EDITOR (!SECURE)
+#define EDIT_PGM "vi"
+
+/*
+ * TAGS is 1 if you wish to support tag files.
+ */
+#define TAGS (!SECURE)
+
+/*
+ * USERFILE is 1 if you wish to allow a .less file to specify
+ * user-defined key bindings.
+ */
+#define USERFILE (!SECURE)
+
+/*
+ * GLOB is 1 if you wish to have shell metacharacters expanded in filenames.
+ * This will generally work if your system provides the "popen" function
+ * and the "echo" shell command.
+ */
+#define GLOB (!SECURE)
+
+/*
+ * PIPEC is 1 if you wish to have the "|" command
+ * which allows the user to pipe data into a shell command.
+ */
+#define PIPEC (!SECURE)
+
+/*
+ * LOGFILE is 1 if you wish to allow the -l option (to create log files).
+ */
+#define LOGFILE (!SECURE)
+
+/*
+ * GNU_OPTIONS is 1 if you wish to support the GNU-style command
+ * line options --help and --version.
+ */
+#define GNU_OPTIONS 1
+
+/*
+ * ONLY_RETURN is 1 if you want RETURN to be the only input which
+ * will continue past an error message.
+ * Otherwise, any key will continue past an error message.
+ */
+#define ONLY_RETURN 0
+
+/*
+ * LESSKEYFILE is the filename of the default lesskey output file
+ * (in the HOME directory).
+ * DEF_LESSKEYINFILE is the filename of the default lesskey input
+ * (in the HOME directory).
+ */
+#define LESSKEYFILE ".less"
+#define DEF_LESSKEYINFILE ".lesskey"
+
+
+/* Settings always true on Unix. */
+
+/*
+ * Define MSOFTC if compiling under Microsoft C.
+ */
+#define MSOFTC 0
+
+/*
+ * HAVE_SYS_TYPES_H is 1 if your system has <sys/types.h>.
+ */
+#define HAVE_SYS_TYPES_H 1
+
+/*
+ * HAVE_STAT is 1 if your system has the stat() call.
+ */
+#define HAVE_STAT 1
+
+/*
+ * HAVE_PERROR is 1 if your system has the perror() call.
+ * (Actually, if it has sys_errlist, sys_nerr and errno.)
+ */
+#define HAVE_PERROR 1
+
+/*
+ * HAVE_TIME is 1 if your system has the time() call.
+ */
+#define HAVE_TIME 1
+
+/*
+ * HAVE_SHELL is 1 if your system supports a SHELL command interpreter.
+ */
+#define HAVE_SHELL 1
+
+/* Settings automatically determined by configure. */
diff --git a/usr.bin/less/defines.os2 b/usr.bin/less/defines.os2
new file mode 100644
index 00000000000..71a7b91827e
--- /dev/null
+++ b/usr.bin/less/defines.os2
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* OS/2 definition file for less. */
+/*
+ * This file has 2 sections:
+ * User preferences.
+ * Settings always true for the emx compiler for OS/2 systems.
+ */
+
+
+/* User preferences. */
+
+/*
+ * SECURE is 1 if you wish to disable a bunch of features in order to
+ * be safe to run by unprivileged users.
+ */
+#define SECURE 0
+
+/*
+ * SHELL_ESCAPE is 1 if you wish to allow shell escapes.
+ * (This is possible only if your system supplies the system() function.)
+ */
+#define SHELL_ESCAPE (!SECURE)
+
+/*
+ * EXAMINE is 1 if you wish to allow examining files by name from within less.
+ */
+#define EXAMINE (!SECURE)
+
+/*
+ * TAB_COMPLETE_FILENAME is 1 if you wish to allow the TAB key
+ * to complete filenames at prompts.
+ */
+#define TAB_COMPLETE_FILENAME (!SECURE)
+
+/*
+ * CMD_HISTORY is 1 if you wish to allow keys to cycle through
+ * previous commands at prompts.
+ */
+#define CMD_HISTORY 1
+
+/*
+ * HILITE_SEARCH is 1 if you wish to have search targets to be
+ * displayed in standout mode.
+ */
+#define HILITE_SEARCH 1
+
+/*
+ * EDITOR is 1 if you wish to allow editor invocation (the "v" command).
+ * (This is possible only if your system supplies the system() function.)
+ * EDIT_PGM is the name of the (default) editor to be invoked.
+ */
+#define EDITOR (!SECURE)
+#define EDIT_PGM "me"
+
+/*
+ * TAGS is 1 if you wish to support tag files.
+ */
+#define TAGS (!SECURE)
+
+/*
+ * USERFILE is 1 if you wish to allow a .less file to specify
+ * user-defined key bindings.
+ */
+#define USERFILE (!SECURE)
+
+/*
+ * GLOB is 1 if you wish to have shell metacharacters expanded in filenames.
+ * This will generally work if your system provides the "popen" function
+ * and the "echo" shell command.
+ */
+#define GLOB (!SECURE)
+
+/*
+ * PIPEC is 1 if you wish to have the "|" command
+ * which allows the user to pipe data into a shell command.
+ */
+#define PIPEC (!SECURE)
+
+/*
+ * LOGFILE is 1 if you wish to allow the -l option (to create log files).
+ */
+#define LOGFILE (!SECURE)
+
+/*
+ * GNU_OPTIONS is 1 if you wish to support the GNU-style command
+ * line options --help and --version.
+ */
+#define GNU_OPTIONS 1
+
+/*
+ * ONLY_RETURN is 1 if you want RETURN to be the only input which
+ * will continue past an error message.
+ * Otherwise, any key will continue past an error message.
+ */
+#define ONLY_RETURN 0
+
+/*
+ * LESSKEYFILE is the filename of the default lesskey output file
+ * (in the HOME directory).
+ * DEF_LESSKEYINFILE is the filename of the default lesskey input
+ * (in the HOME directory).
+ */
+#define LESSKEYFILE "less.ini"
+#define DEF_LESSKEYINFILE "lesskey.ini"
+
+
+/*
+ * HELPFILE is the full pathname of the help file.
+ */
+#define HELPFILE "less.hlp"
+
+/* Settings always true for the emx compiler for OS/2 systems. */
+#define OS2 1
+
+/*
+ * HAVE_SYS_TYPES_H is 1 if your system has <sys/types.h>.
+ */
+#define HAVE_SYS_TYPES_H 1
+
+/*
+ * HAVE_STAT is 1 if your system has the stat() call.
+ */
+#define HAVE_STAT 1
+
+/*
+ * HAVE_PERROR is 1 if your system has the perror() call.
+ * (Actually, if it has sys_errlist, sys_nerr and errno.)
+ */
+#define HAVE_PERROR 1
+
+/*
+ * HAVE_TIME is 1 if your system has the time() call.
+ */
+#define HAVE_TIME 1
+
+/*
+ * HAVE_SHELL is 1 if your system supports a SHELL command interpreter.
+ */
+#define HAVE_SHELL 0
+
+/* Define to `long' if <sys/types.h> doesn't define. */
+/* #define off_t long */
+
+/* Define as the return type of signal handlers (int or void). */
+#define RETSIGTYPE void
+
+/* Define if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/*
+ * Regular expression library.
+ * Define exactly one of the following to be 1:
+ * HAVE_POSIX_REGCOMP: POSIX regcomp() and regex.h
+ * HAVE_RE_COMP: BSD re_comp()
+ * HAVE_REGCMP: System V regcmp()
+ * HAVE_V8_REGCOMP: Henry Spencer V8 regcomp() and regexp.h
+ * NO_REGEX: pattern matching is supported, but without metacharacters.
+ */
+/* #undef HAVE_POSIX_REGCOMP */
+/* #undef HAVE_RE_COMP */
+/* #undef HAVE_REGCMP */
+#define HAVE_V8_REGCOMP 1
+/* #undef NO_REGEX */
+
+/* Define HAVE_VOID if your compiler supports the "void" type. */
+#define HAVE_VOID 1
+
+/* Define HAVE_TIME_T if your system supports the "time_t" type. */
+#define HAVE_TIME_T 0
+
+/* Define HAVE_STRERROR if you have the strerror() function. */
+#define HAVE_STRERROR 1
+
+/* Define HAVE_FILENO if you have the fileno() macro. */
+#define HAVE_FILENO 1
+
+/* Define HAVE_ERRNO if you have the errno variable */
+#define HAVE_ERRNO 1
+
+/* Define HAVE_SYS_ERRLIST if you have the sys_errlist[] variable */
+#define HAVE_SYS_ERRLIST 1
+
+/* Define HAVE_OSPEED if your termcap library has the ospeed variable */
+#define HAVE_OSPEED 0
+/* Define MUST_DEFINE_OSPEED if you have ospeed but it is not defined
+ * in termcap.h. */
+#define MUST_DEFINE_OSPEED 0
+
+/* Define HAVE_LOCALE if you have locale.h and setlocale. */
+#define HAVE_LOCALE 0
+
+/* Define HAVE_TERMIOS_FUNCS if you have tcgetattr/tcsetattr */
+#define HAVE_TERMIOS_FUNCS 0
+
+/* Define HAVE_UPPER_LOWER if you have isupper, islower, toupper, tolower */
+#define HAVE_UPPER_LOWER 1
+
+/* Define if you have the _setjmp function. */
+#define HAVE__SETJMP 0
+
+/* Define if you have the memcpy function. */
+#define HAVE_MEMCPY 1
+
+/* Define if you have the sigsetmask function. */
+#define HAVE_SIGSETMASK 0
+
+/* Define if you have the strchr function. */
+#define HAVE_STRCHR 1
+
+/* Define if you have the system function. */
+#define HAVE_SYSTEM 1
+
+/* Define if you have the <ctype.h> header file. */
+#define HAVE_CTYPE_H 1
+
+/* Define if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 0
+
+/* Define if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define if you have the <stdio.h> header file. */
+#define HAVE_STDIO_H 1
+
+/* Define if you have the <sys/ioctl.h> header file. */
+#define HAVE_SYS_IOCTL_H 0
+
+/* Define if you have the <sys/ptem.h> header file. */
+#define HAVE_SYS_PTEM_H 0
+
+/* Define if you have the <sys/stream.h> header file. */
+#define HAVE_SYS_STREAM_H 0
+
+/* Define if you have the <termcap.h> header file. */
+#define HAVE_TERMCAP_H 0
+
+/* Define if you have the <termio.h> header file. */
+#define HAVE_TERMIO_H 0
+
+/* Define if you have the <termios.h> header file. */
+#define HAVE_TERMIOS_H 0
+
+/* Define if you have the <time.h> header file. */
+#define HAVE_TIME_H 1
+
+/* Define if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define if you have the <values.h> header file. */
+#define HAVE_VALUES_H 0
diff --git a/usr.bin/less/doscreen.c b/usr.bin/less/doscreen.c
new file mode 100644
index 00000000000..9275f722e47
--- /dev/null
+++ b/usr.bin/less/doscreen.c
@@ -0,0 +1,571 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * Routines which deal with the characteristics of the terminal.
+ *
+ * This file is specific to MS-DOS and uses Microsoft C graphics functions.
+ */
+
+#include "less.h"
+#include "cmd.h"
+
+#include <graph.h>
+#include <time.h>
+
+static int init_done = 0;
+static int videopages;
+static long msec_loops;
+
+public int auto_wrap; /* Terminal does \r\n when write past margin */
+public int ignaw; /* Terminal ignores \n immediately after wrap */
+public int erase_char, kill_char; /* The user's erase and line-kill chars */
+public int sc_width, sc_height; /* Height & width of screen */
+public int bo_s_width, bo_e_width; /* Printing width of boldface seq */
+public int ul_s_width, ul_e_width; /* Printing width of underline seq */
+public int so_s_width, so_e_width; /* Printing width of standout seq */
+public int bl_s_width, bl_e_width; /* Printing width of blink seq */
+
+public int nm_fg_color = 7; /* Color of normal text */
+public int nm_bg_color = 0;
+public int bo_fg_color = 15; /* Color of bold text */
+public int bo_bg_color = 0;
+public int ul_fg_color = 9; /* Color of underlined text */
+public int ul_bg_color = 0;
+public int so_fg_color = 0; /* Color of standout text */
+public int so_bg_color = 7;
+public int bl_fg_color = 12; /* Color of blinking text */
+public int bl_bg_color = 0;
+
+static int sy_fg_color;
+static int sy_bg_color;
+static int flash_created = 0;
+
+extern int quiet; /* If VERY_QUIET, use visual bell for bell */
+extern int know_dumb; /* Don't complain about a dumb terminal */
+extern int back_scroll;
+extern int swindow;
+extern char *getenv();
+
+/*
+ * Change terminal to "raw mode", or restore to "normal" mode.
+ * "Raw mode" means
+ * 1. An outstanding read will complete on receipt of a single keystroke.
+ * 2. Input is not echoed.
+ * 3. On output, \n is mapped to \r\n.
+ * 4. \t is NOT expanded into spaces.
+ * 5. Signal-causing characters such as ctrl-C (interrupt),
+ * etc. are NOT disabled.
+ * It doesn't matter whether an input \n is mapped to \r, or vice versa.
+ */
+ public void
+raw_mode(on)
+ int on;
+{
+ static int curr_on = 0;
+
+ if (on == curr_on)
+ return;
+ erase_char = CONTROL('h');
+ kill_char = '\33'; /* ESC */
+ curr_on = on;
+}
+
+/*
+ * Get size of the output screen.
+ */
+ public void
+scrsize(p_height, p_width)
+ int *p_height;
+ int *p_width;
+{
+ register char *s;
+ struct videoconfig w;
+
+ _getvideoconfig(&w);
+
+ if (w.numtextrows)
+ *p_height = w.numtextrows;
+ else if ((s = getenv("LINES")) != NULL && *s != '\0')
+ *p_height = atoi(s);
+ if (*p_height <= 0)
+ *p_height = 24;
+
+ if (w.numtextcols > 0)
+ *p_width = w.numtextcols;
+ else if ((s = getenv("COLUMNS")) != NULL)
+ *p_width = atoi(s);
+ if (*p_width <= 0)
+ *p_width = 80;
+}
+
+/*
+ * Figure out how many empty loops it takes to delay a millisecond.
+ */
+ static void
+get_clock()
+{
+ clock_t start;
+
+ /*
+ * Get synchronized at the start of a tick.
+ */
+ start = clock();
+ while (clock() == start)
+ ;
+ /*
+ * Now count loops till the next tick.
+ */
+ start = clock();
+ msec_loops = 0;
+ while (clock() == start)
+ msec_loops++;
+ /*
+ * Convert from (loops per clock) to (loops per millisecond).
+ */
+ msec_loops *= CLOCKS_PER_SEC;
+ msec_loops /= 1000;
+}
+
+ public void
+get_editkeys()
+{
+}
+
+/*
+ * Get terminal capabilities via termcap.
+ */
+ public void
+get_term()
+{
+ scrsize(&sc_height, &sc_width);
+ pos_init();
+ auto_wrap = 1;
+ ignaw = 0;
+ so_e_width = so_s_width = 0;
+ bo_s_width = bo_e_width = 0;
+ ul_s_width = ul_e_width = 0;
+ bl_s_width = bl_e_width = 0;
+ get_clock();
+}
+
+
+/*
+ * Below are the functions which perform all the
+ * terminal-specific screen manipulation.
+ */
+
+
+/*
+ * Initialize terminal
+ */
+ public void
+init()
+{
+ /* {{ What could we take no_init (-X) to mean? }} */
+ sy_bg_color = _getbkcolor();
+ sy_fg_color = _gettextcolor();
+ flush();
+ init_done = 1;
+}
+
+/*
+ * Create an alternate screen which is all white.
+ * This screen is used to create a "flash" effect, by displaying it
+ * briefly and then switching back to the normal screen.
+ * {{ Yuck! There must be a better way to get a visual bell. }}
+ */
+ static void
+create_flash()
+{
+ struct videoconfig w;
+ char *blanks;
+ int row, col;
+
+ _getvideoconfig(&w);
+ videopages = w.numvideopages;
+ if (videopages < 2)
+ {
+ so_enter();
+ so_exit();
+ } else
+ {
+ _setactivepage(1);
+ so_enter();
+ blanks = (char *) ecalloc(w.numtextcols, sizeof(char));
+ for (col = 0; col < w.numtextcols; col++)
+ blanks[col] = ' ';
+ for (row = w.numtextrows; row > 0; row--)
+ _outmem(blanks, w.numtextcols);
+ _setactivepage(0);
+ _setvisualpage(0);
+ free(blanks);
+ so_exit();
+ }
+ flash_created = 1;
+}
+
+/*
+ * Deinitialize terminal
+ */
+ public void
+deinit()
+{
+ if (!init_done)
+ return;
+ _setbkcolor(sy_bg_color);
+ _settextcolor(sy_fg_color);
+ putstr("\n");
+ init_done = 0;
+}
+
+/*
+ * Home cursor (move to upper left corner of screen).
+ */
+ public void
+home()
+{
+ flush();
+ _settextposition(1,1);
+}
+
+/*
+ * Add a blank line (called with cursor at home).
+ * Should scroll the display down.
+ */
+ public void
+add_line()
+{
+ flush();
+ _scrolltextwindow(_GSCROLLDOWN);
+ _settextposition(1,1);
+}
+
+/*
+ * Move cursor to lower left corner of screen.
+ */
+ public void
+lower_left()
+{
+ flush();
+ _settextposition(sc_height,1);
+}
+
+/*
+ * Delay for a specified number of milliseconds.
+ */
+ static void
+dummy_func()
+{
+ static long delay_dummy = 0;
+ delay_dummy++;
+}
+
+ static void
+delay(msec)
+ int msec;
+{
+ long i;
+
+ while (msec-- > 0)
+ {
+ for (i = 0; i < msec_loops; i++)
+ {
+ /*
+ * Make it look like we're doing something here,
+ * so the optimizer doesn't remove the whole loop.
+ */
+ dummy_func();
+ }
+ }
+}
+
+/*
+ * Make a noise.
+ */
+ static void
+beep()
+{
+ write(1, "\7", 1);
+}
+
+/*
+ * Output the "visual bell", if there is one.
+ */
+ public void
+vbell()
+{
+ if (!flash_created)
+ /*
+ * Create a "flash" on the second video page.
+ */
+ create_flash();
+ if (videopages < 2)
+ /*
+ * There is no "second video page".
+ */
+ return;
+ _setvisualpage(1);
+ /*
+ * Leave it displayed for 100 msec.
+ */
+ delay(100);
+ _setvisualpage(0);
+}
+
+/*
+ * Ring the terminal bell.
+ */
+ public void
+bell()
+{
+ if (quiet == VERY_QUIET)
+ vbell();
+ else
+ beep();
+}
+
+/*
+ * Clear the screen.
+ */
+ public void
+clear()
+{
+ flush();
+ _clearscreen(_GCLEARSCREEN);
+}
+
+/*
+ * Clear from the cursor to the end of the cursor's line.
+ * {{ This must not move the cursor. }}
+ */
+ public void
+clear_eol()
+{
+ short top, left;
+ short bot, right;
+ struct rccoord tpos;
+
+ flush();
+ /*
+ * Save current state.
+ */
+ tpos = _gettextposition();
+ _gettextwindow(&top, &left, &bot, &right);
+ /*
+ * Set a temporary window to the current line,
+ * from the cursor's position to the right edge of the screen.
+ * Then clear that window.
+ */
+ _settextwindow(tpos.row, tpos.col, tpos.row, sc_width);
+ _clearscreen(_GWINDOW);
+ /*
+ * Restore state.
+ */
+ _settextwindow(top, left, bot, right);
+ _settextposition(tpos.row, tpos.col);
+}
+
+/*
+ * Clear the bottom line of the display.
+ * Leave the cursor at the beginning of the bottom line.
+ */
+ public void
+clear_bot()
+{
+ lower_left();
+ clear_eol();
+}
+
+/*
+ * Begin "standout" (bold, underline, or whatever).
+ */
+ public void
+so_enter()
+{
+ flush();
+ _setbkcolor(so_bg_color);
+ _settextcolor(so_fg_color);
+}
+
+/*
+ * End "standout".
+ */
+ public void
+so_exit()
+{
+ flush();
+ _setbkcolor(nm_bg_color);
+ _settextcolor(nm_fg_color);
+}
+
+/*
+ * Begin "underline" (hopefully real underlining,
+ * otherwise whatever the terminal provides).
+ */
+ public void
+ul_enter()
+{
+ flush();
+ _setbkcolor(ul_bg_color);
+ _settextcolor(ul_fg_color);
+}
+
+/*
+ * End "underline".
+ */
+ public void
+ul_exit()
+{
+ flush();
+ _setbkcolor(nm_bg_color);
+ _settextcolor(nm_fg_color);
+}
+
+/*
+ * Begin "bold"
+ */
+ public void
+bo_enter()
+{
+ flush();
+ _setbkcolor(bo_bg_color);
+ _settextcolor(bo_fg_color);
+}
+
+/*
+ * End "bold".
+ */
+ public void
+bo_exit()
+{
+ flush();
+ _setbkcolor(nm_bg_color);
+ _settextcolor(nm_fg_color);
+}
+
+/*
+ * Begin "blink"
+ */
+ public void
+bl_enter()
+{
+ flush();
+ _setbkcolor(bl_bg_color);
+ _settextcolor(bl_fg_color);
+}
+
+/*
+ * End "blink".
+ */
+ public void
+bl_exit()
+{
+ flush();
+ _setbkcolor(nm_bg_color);
+ _settextcolor(nm_fg_color);
+}
+
+/*
+ * Erase the character to the left of the cursor
+ * and move the cursor left.
+ */
+ public void
+backspace()
+{
+ struct rccoord tpos;
+
+ /*
+ * Erase the previous character by overstriking with a space.
+ */
+ flush();
+ tpos = _gettextposition();
+ if (tpos.col <= 1)
+ return;
+ _settextposition(tpos.row, tpos.col-1);
+ _outtext(" ");
+ _settextposition(tpos.row, tpos.col-1);
+}
+
+/*
+ * Output a plain backspace, without erasing the previous char.
+ */
+ public void
+putbs()
+{
+ struct rccoord tpos;
+
+ flush();
+ tpos = _gettextposition();
+ if (tpos.col <= 1)
+ return;
+ _settextposition(tpos.row, tpos.col-1);
+}
+
+/*
+ * Table of line editting characters, for editchar() in decode.c.
+ */
+char edittable[] = {
+ '\340','\115',0, EC_RIGHT, /* RIGHTARROW */
+ '\340','\113',0, EC_LEFT, /* LEFTARROW */
+ '\340','\163',0, EC_W_LEFT, /* CTRL-LEFTARROW */
+ '\340','\164',0, EC_W_RIGHT, /* CTRL-RIGHTARROW */
+ '\340','\122',0, EC_INSERT, /* INSERT */
+ '\340','\123',0, EC_DELETE, /* DELETE */
+ '\340','\223',0, EC_W_DELETE, /* CTRL-DELETE */
+ '\177',0, EC_W_BACKSPACE, /* CTRL-BACKSPACE */
+ '\340','\107',0, EC_HOME, /* HOME */
+ '\340','\117',0, EC_END, /* END */
+ '\340','\110',0, EC_UP, /* UPARROW */
+ '\340','\120',0, EC_DOWN, /* DOWNARROW */
+ '\t',0, EC_F_COMPLETE, /* TAB */
+ '\17',0, EC_B_COMPLETE, /* BACKTAB (?) */
+ '\340','\17',0, EC_B_COMPLETE, /* BACKTAB */
+ '\14',0, EC_EXPAND, /* CTRL-L */
+ 0 /* Extra byte to terminate; subtracted from size, below */
+};
+
+int sz_edittable = sizeof(edittable) -1;
+
+
+char kcmdtable[] =
+{
+ /*
+ * PC function keys.
+ * Note that '\0' is converted to '\340' on input.
+ */
+ '\340','\120',0, A_F_LINE, /* down arrow */
+ '\340','\121',0, A_F_SCREEN, /* page down */
+ '\340','\110',0, A_B_LINE, /* up arrow */
+ '\340','\111',0, A_B_SCREEN, /* page up */
+ '\340','\107',0, A_GOLINE, /* home */
+ '\340','\117',0, A_GOEND, /* end */
+ '\340','\073',0, A_HELP, /* F1 */
+ '\340','\022',0, A_EXAMINE, /* Alt-E */
+ 0
+};
+int sz_kcmdtable = sizeof(kcmdtable) - 1;
diff --git a/usr.bin/less/edit.c b/usr.bin/less/edit.c
new file mode 100644
index 00000000000..e68e394c712
--- /dev/null
+++ b/usr.bin/less/edit.c
@@ -0,0 +1,656 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include "less.h"
+
+public int fd0 = 0;
+
+extern int new_file;
+extern int errmsgs;
+extern int quit_at_eof;
+extern int cbufs;
+extern char *every_first_cmd;
+extern int any_display;
+extern int force_open;
+extern int is_tty;
+extern IFILE curr_ifile;
+extern IFILE old_ifile;
+extern struct scrpos initial_scrpos;
+
+#if LOGFILE
+extern int logfile;
+extern int force_logfile;
+extern char *namelogfile;
+#endif
+
+char *curr_altfilename = NULL;
+static void *curr_altpipe;
+
+
+/*
+ * Textlist functions deal with a list of words separated by spaces.
+ * init_textlist sets up a textlist structure.
+ * forw_textlist uses that structure to iterate thru the list of
+ * words, returning each one as a standard null-terminated string.
+ * back_textlist does the same, but runs thru the list backwards.
+ */
+ public void
+init_textlist(tlist, str)
+ struct textlist *tlist;
+ char *str;
+{
+ char *s;
+
+ tlist->string = skipsp(str);
+ tlist->endstring = tlist->string + strlen(tlist->string);
+ for (s = str; s < tlist->endstring; s++)
+ {
+ if (*s == ' ')
+ *s = '\0';
+ }
+}
+
+ public char *
+forw_textlist(tlist, prev)
+ struct textlist *tlist;
+ char *prev;
+{
+ char *s;
+
+ /*
+ * prev == NULL means return the first word in the list.
+ * Otherwise, return the word after "prev".
+ */
+ if (prev == NULL)
+ s = tlist->string;
+ else
+ s = prev + strlen(prev);
+ if (s >= tlist->endstring)
+ return (NULL);
+ while (*s == '\0')
+ s++;
+ if (s >= tlist->endstring)
+ return (NULL);
+ return (s);
+}
+
+ public char *
+back_textlist(tlist, prev)
+ struct textlist *tlist;
+ char *prev;
+{
+ char *s;
+
+ /*
+ * prev == NULL means return the last word in the list.
+ * Otherwise, return the word before "prev".
+ */
+ if (prev == NULL)
+ s = tlist->endstring;
+ else if (prev <= tlist->string)
+ return (NULL);
+ else
+ s = prev - 1;
+ while (*s == '\0')
+ s--;
+ if (s <= tlist->string)
+ return (NULL);
+ while (s[-1] != '\0' && s > tlist->string)
+ s--;
+ return (s);
+}
+
+/*
+ * Close the current input file.
+ */
+ static void
+close_file()
+{
+ struct scrpos scrpos;
+
+ if (curr_ifile == NULL_IFILE)
+ return;
+ /*
+ * Save the current position so that we can return to
+ * the same position if we edit this file again.
+ */
+ get_scrpos(&scrpos);
+ if (scrpos.pos != NULL_POSITION)
+ {
+ store_pos(curr_ifile, &scrpos);
+ lastmark();
+ }
+ /*
+ * Close the file descriptor, unless it is a pipe.
+ */
+ ch_close();
+ /*
+ * If we opened a file using an alternate name,
+ * do special stuff to close it.
+ */
+ if (curr_altfilename != NULL)
+ {
+ close_altfile(curr_altfilename, get_filename(curr_ifile),
+ curr_altpipe);
+ free(curr_altfilename);
+ curr_altfilename = NULL;
+ }
+ curr_ifile = NULL_IFILE;
+}
+
+/*
+ * Edit a new file (given its name).
+ * Filename == "-" means standard input.
+ * Filename == NULL means just close the current file.
+ */
+ public int
+edit(filename)
+ char *filename;
+{
+ if (filename == NULL)
+ return (edit_ifile(NULL_IFILE));
+ return (edit_ifile(get_ifile(filename, curr_ifile)));
+}
+
+/*
+ * Edit a new file (given its IFILE).
+ * ifile == NULL means just close the current file.
+ */
+ public int
+edit_ifile(ifile)
+ IFILE ifile;
+{
+ int f;
+ int answer;
+ int no_display;
+ int chflags;
+ char *filename;
+ char *open_filename;
+ char *alt_filename;
+ void *alt_pipe;
+ IFILE was_curr_ifile;
+ PARG parg;
+
+ if (ifile == curr_ifile)
+ {
+ /*
+ * Already have the correct file open.
+ */
+ return (0);
+ }
+
+ /*
+ * We must close the currently open file now.
+ * This is necessary to make the open_altfile/close_altfile pairs
+ * nest properly (or rather to avoid nesting at all).
+ * {{ Some stupid implementations of popen() mess up if you do:
+ * fA = popen("A"); fB = popen("B"); pclose(fA); pclose(fB); }}
+ */
+#if LOGFILE
+ end_logfile();
+#endif
+ was_curr_ifile = curr_ifile;
+ if (curr_ifile != NULL_IFILE)
+ {
+ close_file();
+ }
+
+ if (ifile == NULL_IFILE)
+ {
+ /*
+ * No new file to open.
+ * (Don't set old_ifile, because if you call edit_ifile(NULL),
+ * you're supposed to have saved curr_ifile yourself,
+ * and you'll restore it if necessary.)
+ */
+ return (0);
+ }
+
+ filename = get_filename(ifile);
+ /*
+ * See if LESSOPEN specifies an "alternate" file to open.
+ */
+ alt_pipe = NULL;
+ alt_filename = open_altfile(filename, &f, &alt_pipe);
+ open_filename = (alt_filename != NULL) ? alt_filename : filename;
+
+ chflags = 0;
+ if (alt_pipe != NULL)
+ {
+ /*
+ * The alternate "file" is actually a pipe.
+ * f has already been set to the file descriptor of the pipe
+ * in the call to open_altfile above.
+ * Keep the file descriptor open because it was opened
+ * via popen(), and pclose() wants to close it.
+ */
+ chflags |= CH_POPENED;
+ } else if (strcmp(open_filename, "-") == 0)
+ {
+ /*
+ * Use standard input.
+ * Keep the file descriptor open because we can't reopen it.
+ */
+ f = fd0;
+ chflags |= CH_KEEPOPEN;
+ } else if ((parg.p_string = bad_file(open_filename)) != NULL)
+ {
+ /*
+ * It looks like a bad file. Don't try to open it.
+ */
+ error("%s", &parg);
+ free(parg.p_string);
+ err1:
+ if (alt_filename != NULL)
+ {
+ close_altfile(alt_filename, filename, alt_pipe);
+ free(alt_filename);
+ }
+ del_ifile(ifile);
+ /*
+ * Re-open the current file.
+ */
+ (void) edit_ifile(was_curr_ifile);
+ return (1);
+ } else if ((f = open(open_filename, OPEN_READ)) < 0)
+ {
+ /*
+ * Got an error trying to open it.
+ */
+ parg.p_string = errno_message(filename);
+ error("%s", &parg);
+ free(parg.p_string);
+ goto err1;
+ } else if (!force_open && !opened(ifile) && bin_file(f))
+ {
+ /*
+ * Looks like a binary file. Ask user if we should proceed.
+ */
+ parg.p_string = filename;
+ answer = query("\"%s\" may be a binary file. See it anyway? ",
+ &parg);
+ if (answer != 'y' && answer != 'Y')
+ {
+ close(f);
+ goto err1;
+ }
+ }
+
+ /*
+ * Get the new ifile.
+ * Get the saved position for the file.
+ */
+ if (was_curr_ifile != NULL_IFILE)
+ old_ifile = was_curr_ifile;
+ curr_ifile = ifile;
+ curr_altfilename = alt_filename;
+ curr_altpipe = alt_pipe;
+ set_open(curr_ifile); /* File has been opened */
+ get_pos(curr_ifile, &initial_scrpos);
+ new_file = TRUE;
+ ch_init(f, chflags);
+#if LOGFILE
+ if (namelogfile != NULL && is_tty)
+ use_logfile(namelogfile);
+#endif
+
+ if (every_first_cmd != NULL)
+ ungetsc(every_first_cmd);
+
+ no_display = !any_display;
+ flush();
+ any_display = TRUE;
+
+ if (is_tty)
+ {
+ /*
+ * Output is to a real tty.
+ */
+
+ /*
+ * Indicate there is nothing displayed yet.
+ */
+ pos_clear();
+ clr_linenum();
+#if HILITE_SEARCH
+ clr_hilite();
+#endif
+ if (no_display && errmsgs > 0)
+ {
+ /*
+ * We displayed some messages on error output
+ * (file descriptor 2; see error() function).
+ * Before erasing the screen contents,
+ * display the file name and wait for a keystroke.
+ */
+ parg.p_string = filename;
+ error("%s", &parg);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Edit a space-separated list of files.
+ * For each filename in the list, enter it into the ifile list.
+ * Then edit the first one.
+ */
+ public int
+edit_list(filelist)
+ char *filelist;
+{
+ IFILE save_curr_ifile;
+ char *good_filename;
+ char *filename;
+ char *gfilelist;
+ char *gfilename;
+ struct textlist tl_files;
+ struct textlist tl_gfiles;
+
+ save_curr_ifile = curr_ifile;
+ good_filename = NULL;
+
+ /*
+ * Run thru each filename in the list.
+ * Try to glob the filename.
+ * If it doesn't expand, just try to open the filename.
+ * If it does expand, try to open each name in that list.
+ */
+ init_textlist(&tl_files, filelist);
+ filename = NULL;
+ while ((filename = forw_textlist(&tl_files, filename)) != NULL)
+ {
+ gfilelist = glob(filename);
+ init_textlist(&tl_gfiles, gfilelist);
+ gfilename = NULL;
+ while ((gfilename = forw_textlist(&tl_gfiles, gfilename)) != NULL)
+ {
+ if (edit(gfilename) == 0 && good_filename == NULL)
+ good_filename = get_filename(curr_ifile);
+ }
+ free(gfilelist);
+ }
+ /*
+ * Edit the first valid filename in the list.
+ */
+ if (good_filename == NULL)
+ return (1);
+ if (get_ifile(good_filename, curr_ifile) == curr_ifile)
+ /*
+ * Trying to edit the current file; don't reopen it.
+ */
+ return (0);
+ if (edit_ifile(save_curr_ifile))
+ quit(QUIT_ERROR);
+ return (edit(good_filename));
+}
+
+/*
+ * Edit the first file in the command line (ifile) list.
+ */
+ public int
+edit_first()
+{
+ curr_ifile = NULL_IFILE;
+ return (edit_next(1));
+}
+
+/*
+ * Edit the last file in the command line (ifile) list.
+ */
+ public int
+edit_last()
+{
+ curr_ifile = NULL_IFILE;
+ return (edit_prev(1));
+}
+
+
+/*
+ * Edit the next file in the command line (ifile) list.
+ */
+ public int
+edit_next(n)
+ int n;
+{
+ IFILE h;
+ IFILE next;
+
+ h = curr_ifile;
+ /*
+ * Skip n filenames, then try to edit each filename.
+ */
+ for (;;)
+ {
+ next = next_ifile(h);
+ if (--n < 0)
+ {
+ if (edit_ifile(h) == 0)
+ break;
+ }
+ if (next == NULL_IFILE)
+ {
+ /*
+ * Reached end of the ifile list.
+ */
+ return (1);
+ }
+ h = next;
+ }
+ /*
+ * Found a file that we can edit.
+ */
+ return (0);
+}
+
+/*
+ * Edit the previous file in the command line list.
+ */
+ public int
+edit_prev(n)
+ int n;
+{
+ IFILE h;
+ IFILE next;
+
+ h = curr_ifile;
+ /*
+ * Skip n filenames, then try to edit each filename.
+ */
+ for (;;)
+ {
+ next = prev_ifile(h);
+ if (--n < 0)
+ {
+ if (edit_ifile(h) == 0)
+ break;
+ }
+ if (next == NULL_IFILE)
+ {
+ /*
+ * Reached beginning of the ifile list.
+ */
+ return (1);
+ }
+ h = next;
+ }
+ /*
+ * Found a file that we can edit.
+ */
+ return (0);
+}
+
+/*
+ * Edit a specific file in the command line (ifile) list.
+ */
+ public int
+edit_index(n)
+ int n;
+{
+ IFILE h;
+
+ h = NULL_IFILE;
+ do
+ {
+ if ((h = next_ifile(h)) == NULL_IFILE)
+ {
+ /*
+ * Reached end of the list without finding it.
+ */
+ return (1);
+ }
+ } while (get_index(h) != n);
+
+ return (edit_ifile(h));
+}
+
+/*
+ * Edit standard input.
+ */
+ public int
+edit_stdin()
+{
+ if (isatty(fd0))
+ {
+#if MSOFTC || OS2
+ error("Missing filename (\"less -?\" for help)", NULL_PARG);
+#else
+ error("Missing filename (\"less -\\?\" for help)", NULL_PARG);
+#endif
+ quit(QUIT_OK);
+ }
+ return (edit("-"));
+}
+
+/*
+ * Copy a file directly to standard output.
+ * Used if standard output is not a tty.
+ */
+ public void
+cat_file()
+{
+ register int c;
+
+ while ((c = ch_forw_get()) != EOI)
+ putchr(c);
+ flush();
+}
+
+#if LOGFILE
+
+/*
+ * If the user asked for a log file and our input file
+ * is standard input, create the log file.
+ * We take care not to blindly overwrite an existing file.
+ */
+ public void
+use_logfile(filename)
+ char *filename;
+{
+ register int exists;
+ register int answer;
+ PARG parg;
+
+ if (ch_getflags() & CH_CANSEEK)
+ /*
+ * Can't currently use a log file on a file that can seek.
+ */
+ return;
+
+ /*
+ * {{ We could use access() here. }}
+ */
+ exists = open(filename, OPEN_READ);
+ close(exists);
+ exists = (exists >= 0);
+
+ /*
+ * Decide whether to overwrite the log file or append to it.
+ * If it doesn't exist we "overwrite" it.
+ */
+ if (!exists || force_logfile)
+ {
+ /*
+ * Overwrite (or create) the log file.
+ */
+ answer = 'O';
+ } else
+ {
+ /*
+ * Ask user what to do.
+ */
+ parg.p_string = filename;
+ answer = query("Warning: \"%s\" exists; Overwrite, Append or Don't log? ", &parg);
+ }
+
+loop:
+ switch (answer)
+ {
+ case 'O': case 'o':
+ /*
+ * Overwrite: create the file.
+ */
+ logfile = creat(filename, 0644);
+ break;
+ case 'A': case 'a':
+ /*
+ * Append: open the file and seek to the end.
+ */
+ logfile = open(filename, OPEN_APPEND);
+ if (lseek(logfile, (off_t)0, 2) == BAD_LSEEK)
+ {
+ close(logfile);
+ logfile = -1;
+ }
+ break;
+ case 'D': case 'd':
+ /*
+ * Don't do anything.
+ */
+ return;
+ case 'q':
+ quit(QUIT_OK);
+ /*NOTREACHED*/
+ default:
+ /*
+ * Eh?
+ */
+ answer = query("Overwrite, Append, or Don't log? (Type \"O\", \"A\", \"D\" or \"q\") ", NULL_PARG);
+ goto loop;
+ }
+
+ if (logfile < 0)
+ {
+ /*
+ * Error in opening logfile.
+ */
+ parg.p_string = filename;
+ error("Cannot write to \"%s\"", &parg);
+ }
+}
+
+#endif
diff --git a/usr.bin/less/filename.c b/usr.bin/less/filename.c
new file mode 100644
index 00000000000..82820c45060
--- /dev/null
+++ b/usr.bin/less/filename.c
@@ -0,0 +1,715 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * Routines to mess around with filenames (and files).
+ * Much of this is very OS dependent.
+ */
+
+#include "less.h"
+#if MSOFTC
+#include <dos.h>
+#endif
+
+extern int force_open;
+extern IFILE curr_ifile;
+extern IFILE old_ifile;
+
+/*
+ * Return a pathname that points to a specified file in a specified directory.
+ * Return NULL if the file does not exist in the directory.
+ */
+ static char *
+dirfile(dirname, filename)
+ char *dirname;
+ char *filename;
+{
+ char *pathname;
+ int f;
+
+ if (dirname == NULL || *dirname == '\0')
+ return (NULL);
+ /*
+ * Construct the full pathname.
+ */
+ pathname = (char *) calloc(strlen(dirname) + strlen(filename) + 2,
+ sizeof(char));
+ if (pathname == NULL)
+ return (NULL);
+#if MSOFTC || OS2
+ sprintf(pathname, "%s\\%s", dirname, filename);
+#else
+ sprintf(pathname, "%s/%s", dirname, filename);
+#endif
+ /*
+ * Make sure the file exists.
+ */
+ f = open(pathname, OPEN_READ);
+ if (f < 0)
+ {
+ free(pathname);
+ pathname = NULL;
+ } else
+ {
+ close (f);
+ }
+ return (pathname);
+}
+
+/*
+ * Return the full pathname of the given file in the "home directory".
+ */
+ public char *
+homefile(filename)
+ char *filename;
+{
+ register char *pathname;
+
+ /*
+ * Try $HOME/filename.
+ */
+ pathname = dirfile(getenv("HOME"), filename);
+ if (pathname != NULL)
+ return (pathname);
+#if OS2
+ /*
+ * Try $INIT/filename.
+ */
+ pathname = dirfile(getenv("INIT"), filename);
+ if (pathname != NULL)
+ return (pathname);
+#endif
+#if MSOFTC || OS2
+ /*
+ * Look for the file anywhere on search path.
+ */
+ pathname = (char *) calloc(_MAX_PATH, sizeof(char));
+ _searchenv(filename, "PATH", pathname);
+ if (*pathname != '\0')
+ return (pathname);
+ free(pathname);
+#endif
+ return (NULL);
+}
+
+/*
+ * Find out where the help file is.
+ */
+ public char *
+find_helpfile()
+{
+ register char *helpfile;
+
+ if ((helpfile = getenv("LESSHELP")) != NULL)
+ return (save(helpfile));
+#if MSOFTC || OS2
+ return (homefile(HELPFILE));
+#else
+ return (save(HELPFILE));
+#endif
+}
+
+/*
+ * Expand a string, substituting any "%" with the current filename,
+ * and any "#" with the previous filename.
+ * {{ This is a lot of work just to support % and #. }}
+ */
+ public char *
+fexpand(s)
+ char *s;
+{
+ register char *fr, *to;
+ register int n;
+ register char *e;
+
+ /*
+ * Make one pass to see how big a buffer we
+ * need to allocate for the expanded string.
+ */
+ n = 0;
+ for (fr = s; *fr != '\0'; fr++)
+ {
+ switch (*fr)
+ {
+ case '%':
+ if (curr_ifile == NULL_IFILE)
+ {
+ /* error("No current file", NULL_PARG); */
+ return (save(s));
+ }
+ n += strlen(get_filename(curr_ifile));
+ break;
+ case '#':
+ if (old_ifile == NULL_IFILE)
+ {
+ /* error("No previous file", NULL_PARG); */
+ return (save(s));
+ }
+ n += strlen(get_filename(old_ifile));
+ break;
+ default:
+ n++;
+ break;
+ }
+ }
+
+ e = (char *) ecalloc(n+1, sizeof(char));
+
+ /*
+ * Now copy the string, expanding any "%" or "#".
+ */
+ to = e;
+ for (fr = s; *fr != '\0'; fr++)
+ {
+ switch (*fr)
+ {
+ case '%':
+ strcpy(to, get_filename(curr_ifile));
+ to += strlen(to);
+ break;
+ case '#':
+ strcpy(to, get_filename(old_ifile));
+ to += strlen(to);
+ break;
+ default:
+ *to++ = *fr;
+ break;
+ }
+ }
+ *to = '\0';
+ return (e);
+}
+
+#if TAB_COMPLETE_FILENAME
+
+/*
+ * Return a blank-separated list of filenames which "complete"
+ * the given string.
+ */
+ public char *
+fcomplete(s)
+ char *s;
+{
+ char *fpat;
+ /*
+ * Complete the filename "s" by globbing "s*".
+ */
+#if MSOFTC
+ /*
+ * But in DOS, we have to glob "s*.*".
+ * But if the final component of the filename already has
+ * a dot in it, just do "s*".
+ * (Thus, "FILE" is globbed as "FILE*.*",
+ * but "FILE.A" is globbed as "FILE.A*").
+ */
+ char *slash;
+ for (slash = s+strlen(s)-1; slash > s; slash--)
+ if (*slash == '/' || *slash == '\\')
+ break;
+ fpat = (char *) ecalloc(strlen(s)+4, sizeof(char));
+ if (strchr(slash, '.') == NULL)
+ sprintf(fpat, "%s*.*", s);
+ else
+ sprintf(fpat, "%s*", s);
+#else
+ fpat = (char *) ecalloc(strlen(s)+2, sizeof(char));
+ sprintf(fpat, "%s*", s);
+#endif
+ s = glob(fpat);
+ if (strcmp(s,fpat) == 0)
+ {
+ /*
+ * The filename didn't expand.
+ */
+ free(s);
+ s = NULL;
+ }
+ free(fpat);
+ return (s);
+}
+#endif
+
+/*
+ * Try to determine if a file is "binary".
+ * This is just a guess, and we need not try too hard to make it accurate.
+ */
+ public int
+bin_file(f)
+ int f;
+{
+ int i;
+ int n;
+ unsigned char data[64];
+
+ if (!seekable(f))
+ return (0);
+ if (lseek(f, (off_t)0, 0) == BAD_LSEEK)
+ return (0);
+ n = read(f, data, sizeof(data));
+ for (i = 0; i < n; i++)
+ if (binary_char(data[i]))
+ return (1);
+ return (0);
+}
+
+/*
+ * Try to determine the size of a file by seeking to the end.
+ */
+ static POSITION
+seek_filesize(f)
+ int f;
+{
+ off_t spos;
+
+ spos = lseek(f, (off_t)0, 2);
+ if (spos == BAD_LSEEK)
+ return (NULL_POSITION);
+ return ((POSITION) spos);
+}
+
+#if GLOB
+
+FILE *popen();
+
+/*
+ * Read a string from a file.
+ * Return a pointer to the string in memory.
+ */
+ static char *
+readfd(fd)
+ FILE *fd;
+{
+ int len;
+ int ch;
+ char *buf;
+ char *p;
+
+ /*
+ * Make a guess about how many chars in the string
+ * and allocate a buffer to hold it.
+ */
+ len = 100;
+ buf = (char *) ecalloc(len, sizeof(char));
+ for (p = buf; ; p++)
+ {
+ if ((ch = getc(fd)) == '\n' || ch == EOF)
+ break;
+ if (p - buf >= len-1)
+ {
+ /*
+ * The string is too big to fit in the buffer we have.
+ * Allocate a new buffer, twice as big.
+ */
+ len *= 2;
+ *p = '\0';
+ p = (char *) ecalloc(len, sizeof(char));
+ strcpy(p, buf);
+ free(buf);
+ buf = p;
+ p = buf + strlen(buf);
+ }
+ *p = ch;
+ }
+ *p = '\0';
+ return (buf);
+}
+
+/*
+ * Execute a shell command.
+ * Return a pointer to a pipe connected to the shell command's standard output.
+ */
+ static FILE *
+shellcmd(cmd, s1, s2)
+ char *cmd;
+ char *s1;
+ char *s2;
+{
+ char *scmd;
+ char *scmd2;
+ char *shell;
+ FILE *fd;
+ int len;
+
+ len = strlen(cmd) +
+ (s1 == NULL ? 0 : strlen(s1)) +
+ (s2 == NULL ? 0 : strlen(s2)) + 1;
+ scmd = (char *) ecalloc(len, sizeof(char));
+ sprintf(scmd, cmd, s1, s2);
+#if HAVE_SHELL
+ shell = getenv("SHELL");
+ if (shell != NULL && *shell != '\0')
+ {
+ /*
+ * Read the output of <$SHELL -c "cmd">.
+ */
+ scmd2 = (char *) ecalloc(strlen(shell) + strlen(scmd) + 7,
+ sizeof(char));
+ sprintf(scmd2, "%s -c \"%s\"", shell, scmd);
+ free(scmd);
+ scmd = scmd2;
+ }
+#endif
+ fd = popen(scmd, "r");
+ free(scmd);
+ return (fd);
+}
+
+/*
+ * Expand a filename, doing any shell-level substitutions.
+ */
+ public char *
+glob(filename)
+ char *filename;
+{
+ char *gfilename;
+
+ filename = fexpand(filename);
+#if OS2
+{
+ char **list;
+ int cnt;
+ int length;
+
+ list = _fnexplode(filename);
+ if (list == NULL)
+ return (filename);
+ length = 0;
+ for (cnt = 0; list[cnt] != NULL; cnt++)
+ length += strlen(list[cnt]) + 1;
+ gfilename = (char *) ecalloc(length, sizeof(char));
+ for (cnt = 0; list[cnt] != NULL; cnt++)
+ {
+ strcat(gfilename, list[cnt]);
+ strcat(gfilename, " ");
+ }
+ _fnexplodefree(list);
+}
+#else
+{
+ FILE *fd;
+
+ /*
+ * We get the shell to expand the filename for us by passing
+ * an "echo" command to the shell and reading its output.
+ */
+ fd = shellcmd("echo %s", filename, (char*)NULL);
+ if (fd == NULL)
+ {
+ /*
+ * Cannot create the pipe.
+ * Just return the original (fexpanded) filename.
+ */
+ return (filename);
+ }
+ gfilename = readfd(fd);
+ pclose(fd);
+ if (*gfilename == '\0')
+ {
+ free(gfilename);
+ return (filename);
+ }
+ free(filename);
+}
+#endif
+ return (gfilename);
+}
+
+/*
+ * See if we should open a "replacement file"
+ * instead of the file we're about to open.
+ */
+ public char *
+open_altfile(filename, pf, pfd)
+ char *filename;
+ int *pf;
+ void **pfd;
+{
+ char *lessopen;
+ char *gfilename;
+ int returnfd = 0;
+ FILE *fd;
+
+ ch_ungetchar(-1);
+ if ((lessopen = getenv("LESSOPEN")) == NULL)
+ return (NULL);
+ if (strcmp(filename, "-") == 0)
+ return (NULL);
+ if (*lessopen == '|')
+ {
+ /*
+ * If LESSOPEN starts with a |, it indicates
+ * a "pipe preprocessor".
+ */
+ lessopen++;
+ returnfd = 1;
+ }
+ fd = shellcmd(lessopen, filename, (char*)NULL);
+ if (fd == NULL)
+ {
+ /*
+ * Cannot create the pipe.
+ */
+ return (NULL);
+ }
+ if (returnfd)
+ {
+#if HAVE_FILENO
+ int f;
+ char c;
+
+ /*
+ * Read one char to see if the pipe will produce any data.
+ * If it does, push the char back on the pipe.
+ */
+ f = fileno(fd);
+ if (read(f, &c, 1) != 1)
+ {
+ /*
+ * Pipe is empty. This means there is no alt file.
+ */
+ pclose(fd);
+ return (NULL);
+ }
+ ch_ungetchar(c);
+ *pfd = (void *) fd;
+ *pf = f;
+ return (save("-"));
+#else
+ error("LESSOPEN pipe is not supported", NULL_PARG);
+ return (NULL);
+#endif
+ }
+ gfilename = readfd(fd);
+ pclose(fd);
+ if (*gfilename == '\0')
+ /*
+ * Pipe is empty. This means there is no alt file.
+ */
+ return (NULL);
+ return (gfilename);
+}
+
+/*
+ * Close a replacement file.
+ */
+ public void
+close_altfile(altfilename, filename, pipefd)
+ char *altfilename;
+ char *filename;
+ void *pipefd;
+{
+ char *lessclose;
+ FILE *fd;
+
+ if (pipefd != NULL)
+ pclose((FILE*) pipefd);
+ if ((lessclose = getenv("LESSCLOSE")) == NULL)
+ return;
+ fd = shellcmd(lessclose, filename, altfilename);
+ pclose(fd);
+}
+
+#else
+#if MSOFTC
+
+ public char *
+glob(filename)
+ char *filename;
+{
+ register char *gfilename;
+ register char *p;
+ register int len;
+ register int n;
+ struct find_t fnd;
+ char drive[_MAX_DRIVE];
+ char dir[_MAX_DIR];
+ char fname[_MAX_FNAME];
+ char ext[_MAX_EXT];
+
+ filename = fexpand(filename);
+ if (_dos_findfirst(filename, ~0, &fnd) != 0)
+ return (filename);
+
+ _splitpath(filename, drive, dir, fname, ext);
+ len = 100;
+ gfilename = (char *) ecalloc(len, sizeof(char));
+ p = gfilename;
+ do {
+ n = strlen(drive) + strlen(dir) + strlen(fnd.name);
+ while (p - gfilename + n+2 >= len)
+ {
+ len *= 2;
+ *p = '\0';
+ p = (char *) ecalloc(len, sizeof(char));
+ strcpy(p, gfilename);
+ free(gfilename);
+ gfilename = p;
+ p = gfilename + strlen(gfilename);
+ }
+ sprintf(p, "%s%s%s", drive, dir, fnd.name);
+ p += n;
+ *p++ = ' ';
+ } while (_dos_findnext(&fnd) == 0);
+
+ *--p = '\0';
+ return (gfilename);
+}
+
+ public char *
+open_altfile(filename)
+ char *filename;
+{
+ return (NULL);
+}
+
+ public void
+close_altfile(altfilename, filename)
+ char *altfilename;
+ char *filename;
+{
+}
+
+#else
+
+ public char *
+glob(filename)
+ char *filename;
+{
+ return (fexpand(filename));
+}
+
+
+ public char *
+open_altfile(filename)
+ char *filename;
+{
+ return (NULL);
+}
+
+ public void
+close_altfile(altfilename, filename)
+ char *altfilename;
+ char *filename;
+{
+}
+
+#endif
+#endif
+
+
+#if HAVE_STAT
+
+#include <sys/stat.h>
+#ifndef S_ISDIR
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
+#ifndef S_ISREG
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#endif
+
+/*
+ * Returns NULL if the file can be opened and
+ * is an ordinary file, otherwise an error message
+ * (if it cannot be opened or is a directory, etc.)
+ */
+ public char *
+bad_file(filename)
+ char *filename;
+{
+ register char *m;
+ struct stat statbuf;
+
+ if (stat(filename, &statbuf) < 0)
+ return (errno_message(filename));
+
+ if (force_open)
+ return (NULL);
+
+ if (S_ISDIR(statbuf.st_mode))
+ {
+ static char is_dir[] = " is a directory";
+ m = (char *) ecalloc(strlen(filename) + sizeof(is_dir),
+ sizeof(char));
+ strcpy(m, filename);
+ strcat(m, is_dir);
+ return (m);
+ }
+ if (!S_ISREG(statbuf.st_mode))
+ {
+ static char not_reg[] = " is not a regular file";
+ m = (char *) ecalloc(strlen(filename) + sizeof(not_reg),
+ sizeof(char));
+ strcpy(m, filename);
+ strcat(m, not_reg);
+ return (m);
+ }
+
+ return (NULL);
+}
+
+/*
+ * Return the size of a file, as cheaply as possible.
+ * In Unix, we can stat the file.
+ */
+ public POSITION
+filesize(f)
+ int f;
+{
+ struct stat statbuf;
+
+ if (fstat(f, &statbuf) < 0)
+ /*
+ * Can't stat; try seeking to the end.
+ */
+ return (seek_filesize(f));
+
+ return ((POSITION) statbuf.st_size);
+}
+
+#else
+
+/*
+ * If we have no way to find out, just say the file is good.
+ */
+ public char *
+bad_file(filename)
+ char *filename;
+{
+ return (NULL);
+}
+
+/*
+ * We can find the file size by seeking.
+ */
+ public POSITION
+filesize(f)
+ int f;
+{
+ return (seek_filesize(f));
+}
+
+#endif
diff --git a/usr.bin/less/forwback.c b/usr.bin/less/forwback.c
new file mode 100644
index 00000000000..49664522f4c
--- /dev/null
+++ b/usr.bin/less/forwback.c
@@ -0,0 +1,398 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * Primitives for displaying the file on the screen,
+ * scrolling either forward or backward.
+ */
+
+#include "less.h"
+#include "position.h"
+
+public int hit_eof; /* Keeps track of how many times we hit end of file */
+public int screen_trashed;
+public int squished;
+
+extern int sigs;
+extern int top_scroll;
+extern int quiet;
+extern int sc_width, sc_height;
+extern int quit_at_eof;
+extern int plusoption;
+extern int forw_scroll;
+extern int back_scroll;
+extern int need_clr;
+extern int ignore_eoi;
+#if TAGS
+extern char *tagoption;
+#endif
+
+/*
+ * Sound the bell to indicate user is trying to move past end of file.
+ */
+ static void
+eof_bell()
+{
+ if (quiet == NOT_QUIET)
+ bell();
+ else
+ vbell();
+}
+
+/*
+ * Check to see if the end of file is currently "displayed".
+ */
+ static void
+eof_check()
+{
+ POSITION pos;
+
+ if (ignore_eoi)
+ return;
+ if (ABORT_SIGS())
+ return;
+ /*
+ * If the bottom line is empty, we are at EOF.
+ * If the bottom line ends at the file length,
+ * we must be just at EOF.
+ */
+ pos = position(BOTTOM_PLUS_ONE);
+ if (pos == NULL_POSITION || pos == ch_length())
+ hit_eof++;
+}
+
+/*
+ * If the screen is "squished", repaint it.
+ * "Squished" means the first displayed line is not at the top
+ * of the screen; this can happen when we display a short file
+ * for the first time.
+ */
+ static void
+squish_check()
+{
+ if (!squished)
+ return;
+ squished = 0;
+ repaint();
+}
+
+/*
+ * Display n lines, scrolling forward,
+ * starting at position pos in the input file.
+ * "force" means display the n lines even if we hit end of file.
+ * "only_last" means display only the last screenful if n > screen size.
+ * "nblank" is the number of blank lines to draw before the first
+ * real line. If nblank > 0, the pos must be NULL_POSITION.
+ * The first real line after the blanks will start at ch_zero().
+ */
+ public void
+forw(n, pos, force, only_last, nblank)
+ register int n;
+ POSITION pos;
+ int force;
+ int only_last;
+ int nblank;
+{
+ int eof = 0;
+ int nlines = 0;
+ int do_repaint;
+ static int first_time = 1;
+
+ squish_check();
+
+ /*
+ * do_repaint tells us not to display anything till the end,
+ * then just repaint the entire screen.
+ * We repaint if we are supposed to display only the last
+ * screenful and the request is for more than a screenful.
+ * Also if the request exceeds the forward scroll limit
+ * (but not if the request is for exactly a screenful, since
+ * repainting itself involves scrolling forward a screenful).
+ */
+ do_repaint = (only_last && n > sc_height-1) ||
+ (forw_scroll >= 0 && n > forw_scroll && n != sc_height-1);
+
+ if (!do_repaint)
+ {
+ if (top_scroll && n >= sc_height - 1 && pos != ch_length())
+ {
+ /*
+ * Start a new screen.
+ * {{ This is not really desirable if we happen
+ * to hit eof in the middle of this screen,
+ * but we don't yet know if that will happen. }}
+ */
+ if (top_scroll == OPT_ONPLUS || first_time)
+ clear();
+ home();
+ force = 1;
+ } else
+ {
+ clear_bot();
+ }
+
+ if (pos != position(BOTTOM_PLUS_ONE) || empty_screen())
+ {
+ /*
+ * This is not contiguous with what is
+ * currently displayed. Clear the screen image
+ * (position table) and start a new screen.
+ */
+ pos_clear();
+ add_forw_pos(pos);
+ force = 1;
+ if (top_scroll)
+ {
+ if (top_scroll == OPT_ONPLUS)
+ clear();
+ home();
+ } else if (!first_time)
+ {
+ putstr("...skipping...\n");
+ }
+ }
+ }
+
+ while (--n >= 0)
+ {
+ /*
+ * Read the next line of input.
+ */
+ if (nblank > 0)
+ {
+ /*
+ * Still drawing blanks; don't get a line
+ * from the file yet.
+ * If this is the last blank line, get ready to
+ * read a line starting at ch_zero() next time.
+ */
+ if (--nblank == 0)
+ pos = ch_zero();
+ } else
+ {
+ /*
+ * Get the next line from the file.
+ */
+ pos = forw_line(pos);
+ if (pos == NULL_POSITION)
+ {
+ /*
+ * End of file: stop here unless the top line
+ * is still empty, or "force" is true.
+ */
+ eof = 1;
+ if (!force && position(TOP) != NULL_POSITION)
+ break;
+ }
+ }
+ /*
+ * Add the position of the next line to the position table.
+ * Display the current line on the screen.
+ */
+ add_forw_pos(pos);
+ nlines++;
+ if (do_repaint)
+ continue;
+ /*
+ * If this is the first screen displayed and
+ * we hit an early EOF (i.e. before the requested
+ * number of lines), we "squish" the display down
+ * at the bottom of the screen.
+ * But don't do this if a + option or a -t option
+ * was given. These options can cause us to
+ * start the display after the beginning of the file,
+ * and it is not appropriate to squish in that case.
+ */
+ if (first_time && pos == NULL_POSITION && !top_scroll &&
+#if TAGS
+ tagoption == NULL &&
+#endif
+ !plusoption)
+ {
+ squished = 1;
+ continue;
+ }
+ if (top_scroll == 1)
+ clear_eol();
+ put_line();
+ }
+
+ if (ignore_eoi)
+ hit_eof = 0;
+ else if (eof && !ABORT_SIGS())
+ hit_eof++;
+ else
+ eof_check();
+ if (nlines == 0)
+ eof_bell();
+ else if (do_repaint)
+ repaint();
+ first_time = 0;
+ (void) currline(BOTTOM);
+}
+
+/*
+ * Display n lines, scrolling backward.
+ */
+ public void
+back(n, pos, force, only_last)
+ register int n;
+ POSITION pos;
+ int force;
+ int only_last;
+{
+ int nlines = 0;
+ int do_repaint;
+
+ squish_check();
+ do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1));
+ hit_eof = 0;
+ while (--n >= 0)
+ {
+ /*
+ * Get the previous line of input.
+ */
+ pos = back_line(pos);
+ if (pos == NULL_POSITION)
+ {
+ /*
+ * Beginning of file: stop here unless "force" is true.
+ */
+ if (!force)
+ break;
+ }
+ /*
+ * Add the position of the previous line to the position table.
+ * Display the line on the screen.
+ */
+ add_back_pos(pos);
+ nlines++;
+ if (!do_repaint)
+ {
+ home();
+ add_line();
+ put_line();
+ }
+ }
+
+ eof_check();
+ if (nlines == 0)
+ eof_bell();
+ else if (do_repaint)
+ repaint();
+ (void) currline(BOTTOM);
+}
+
+/*
+ * Display n more lines, forward.
+ * Start just after the line currently displayed at the bottom of the screen.
+ */
+ public void
+forward(n, force, only_last)
+ int n;
+ int force;
+ int only_last;
+{
+ POSITION pos;
+
+ if (quit_at_eof && hit_eof)
+ {
+ /*
+ * If the -e flag is set and we're trying to go
+ * forward from end-of-file, go on to the next file.
+ */
+ if (edit_next(1))
+ quit(QUIT_OK);
+ return;
+ }
+
+ pos = position(BOTTOM_PLUS_ONE);
+ if (pos == NULL_POSITION && (!force || empty_lines(2, sc_height-1)))
+ {
+ if (ignore_eoi)
+ {
+ /*
+ * ignore_eoi is to support A_F_FOREVER.
+ * Back up until there is a line at the bottom
+ * of the screen.
+ */
+ if (empty_screen())
+ pos = ch_zero();
+ else
+ {
+ do
+ {
+ back(1, position(TOP), 1, 0);
+ pos = position(BOTTOM_PLUS_ONE);
+ } while (pos == NULL_POSITION);
+ }
+ } else
+ {
+ eof_bell();
+ hit_eof++;
+ return;
+ }
+ }
+ forw(n, pos, force, only_last, 0);
+}
+
+/*
+ * Display n more lines, backward.
+ * Start just before the line currently displayed at the top of the screen.
+ */
+ public void
+backward(n, force, only_last)
+ int n;
+ int force;
+ int only_last;
+{
+ POSITION pos;
+
+ pos = position(TOP);
+ if (pos == NULL_POSITION && (!force || position(BOTTOM) == 0))
+ {
+ eof_bell();
+ return;
+ }
+ back(n, pos, force, only_last);
+}
+
+/*
+ * Get the backwards scroll limit.
+ * Must call this function instead of just using the value of
+ * back_scroll, because the default case depends on sc_height and
+ * top_scroll, as well as back_scroll.
+ */
+ public int
+get_back_scroll()
+{
+ if (back_scroll >= 0)
+ return (back_scroll);
+ if (top_scroll)
+ return (sc_height - 2);
+ return (10000); /* infinity */
+}
diff --git a/usr.bin/less/funcs.h b/usr.bin/less/funcs.h
new file mode 100644
index 00000000000..b1f600fbdd3
--- /dev/null
+++ b/usr.bin/less/funcs.h
@@ -0,0 +1,224 @@
+ public void strtcpy ();
+ public char * save ();
+ public VOID_POINTER ecalloc ();
+ public char * skipsp ();
+ public void quit ();
+ public void raw_mode ();
+ public void scrsize ();
+ public void scrsize ();
+ public void get_editkeys ();
+ public void get_term ();
+ public void init ();
+ public void deinit ();
+ public void home ();
+ public void add_line ();
+ public void lower_left ();
+ public void goto_line ();
+ public void bell ();
+ public void vbell ();
+ public void clear ();
+ public void clear_eol ();
+ public void clear_bot ();
+ public void so_enter ();
+ public void so_exit ();
+ public void ul_enter ();
+ public void ul_exit ();
+ public void bo_enter ();
+ public void bo_exit ();
+ public void bl_enter ();
+ public void bl_exit ();
+ public void backspace ();
+ public void putbs ();
+ public void match_brac ();
+ public void ch_ungetchar ();
+ public void end_logfile ();
+ public void sync_logfile ();
+ public int ch_seek ();
+ public int ch_end_seek ();
+ public int ch_beg_seek ();
+ public POSITION ch_length ();
+ public POSITION ch_tell ();
+ public int ch_forw_get ();
+ public int ch_back_get ();
+ public int ch_nbuf ();
+ public void ch_flush ();
+ public int seekable ();
+ public void ch_init ();
+ public void ch_close ();
+ public int ch_getflags ();
+ public void ch_dump ();
+ public void init_charset ();
+ public int binary_char ();
+ public int control_char ();
+ public char * prchar ();
+ public void cmd_reset ();
+ public int len_cmdbuf ();
+ public void set_mlist ();
+ public void cmd_accept ();
+ public int cmd_char ();
+ public int cmd_int ();
+ public void cmd_putstr ();
+ public char * get_cmdbuf ();
+ public int in_mca ();
+ public void dispversion ();
+ public int getcc ();
+ public void ungetcc ();
+ public void ungetsc ();
+ public void commands ();
+ public void init_cmds ();
+ public void add_fcmd_table ();
+ public void add_ecmd_table ();
+ public int cmd_search ();
+ public int fcmd_decode ();
+ public int ecmd_decode ();
+ public int lesskey ();
+ public void add_hometable ();
+ public int editchar ();
+ public void init_textlist ();
+ public char * forw_textlist ();
+ public char * back_textlist ();
+ public int edit ();
+ public int edit_ifile ();
+ public int edit_list ();
+ public int edit_first ();
+ public int edit_last ();
+ public int edit_next ();
+ public int edit_prev ();
+ public int edit_index ();
+ public int edit_stdin ();
+ public void cat_file ();
+ public void use_logfile ();
+ public char * homefile ();
+ public char * find_helpfile ();
+ public char * fexpand ();
+ public char * fcomplete ();
+ public int bin_file ();
+ public char * glob ();
+ public char * open_altfile ();
+ public void close_altfile ();
+ public char * glob ();
+ public char * open_altfile ();
+ public void close_altfile ();
+ public char * glob ();
+ public char * open_altfile ();
+ public void close_altfile ();
+ public char * bad_file ();
+ public POSITION filesize ();
+ public char * bad_file ();
+ public POSITION filesize ();
+ public void forw ();
+ public void back ();
+ public void forward ();
+ public void backward ();
+ public int get_back_scroll ();
+ public void help ();
+ public void del_ifile ();
+ public IFILE next_ifile ();
+ public IFILE prev_ifile ();
+ public IFILE getoff_ifile ();
+ public int nifile ();
+ public IFILE get_ifile ();
+ public char * get_filename ();
+ public int get_index ();
+ public void store_pos ();
+ public void get_pos ();
+ public void set_open ();
+ public int opened ();
+ public void * get_filestate ();
+ public void set_filestate ();
+ public void if_dump ();
+ public POSITION forw_line ();
+ public POSITION back_line ();
+ public void jump_forw ();
+ public void jump_back ();
+ public void repaint ();
+ public void jump_percent ();
+ public void jump_line_loc ();
+ public void jump_loc ();
+ public void prewind ();
+ public void plinenum ();
+ public int pappend ();
+ public void pdone ();
+ public int gline ();
+ public void null_line ();
+ public POSITION forw_raw_line ();
+ public POSITION back_raw_line ();
+ public void clr_linenum ();
+ public void add_lnum ();
+ public int find_linenum ();
+ public POSITION find_pos ();
+ public int currline ();
+ public void lsystem ();
+ public int pipe_mark ();
+ public int pipe_data ();
+ public void init_mark ();
+ public int badmark ();
+ public void setmark ();
+ public void lastmark ();
+ public void gomark ();
+ public POSITION markpos ();
+ public void opt_o ();
+ public void opt__O ();
+ public void opt_l ();
+ public void opt_k ();
+ public void opt_t ();
+ public void opt__T ();
+ public void opt_p ();
+ public void opt__P ();
+ public void opt_b ();
+ public void opt_i ();
+ public void opt__V ();
+ public void opt_D ();
+ public void opt_query ();
+ public int get_swindow ();
+ public void scan_option ();
+ public void toggle_option ();
+ public int single_char_option ();
+ public char * opt_prompt ();
+ public int isoptpending ();
+ public void nopendopt ();
+ public int getnum ();
+ public void init_option ();
+ public struct option * findopt ();
+ public int iread ();
+ public void intread ();
+ public long get_time ();
+ public char * errno_message ();
+ public int percentage ();
+ public void put_line ();
+ public void flush ();
+ public int putchr ();
+ public void putstr ();
+ public void error ();
+ public void ierror ();
+ public int query ();
+ public POSITION position ();
+ public void add_forw_pos ();
+ public void add_back_pos ();
+ public void pos_clear ();
+ public void pos_init ();
+ public int onscreen ();
+ public int empty_screen ();
+ public int empty_lines ();
+ public void get_scrpos ();
+ public int adjsline ();
+ public void init_prompt ();
+ public char * pr_expand ();
+ public char * eq_message ();
+ public char * pr_string ();
+ public void repaint_hilite ();
+ public void undo_search ();
+ public void clr_hilite ();
+ public int is_hilited ();
+ public void chg_caseless ();
+ public void chg_hilite ();
+ public int search ();
+ public void prep_hilite ();
+ public RETSIGTYPE winch ();
+ public RETSIGTYPE winch ();
+ public void init_signals ();
+ public void psignals ();
+ public void findtag ();
+ public POSITION tagsearch ();
+ public void open_getchr ();
+ public int getchr ();
diff --git a/usr.bin/less/help.c b/usr.bin/less/help.c
new file mode 100644
index 00000000000..27064bb0ecd
--- /dev/null
+++ b/usr.bin/less/help.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * Display some help.
+ * Just invoke another "less" to display the help file.
+ *
+ * {{ This makes this function very simple, and makes changing the
+ * help file very easy, but it may present difficulties on
+ * (non-Unix) systems which do not supply the "system()" function. }}
+ */
+
+#include "less.h"
+
+extern char *progname;
+
+ public void
+help(nomsg)
+ int nomsg;
+{
+ char *helpfile;
+ char *cmd;
+
+ helpfile = find_helpfile();
+ if (helpfile == NULL)
+ {
+ error("Cannot find help file", NULL_PARG);
+ return;
+ }
+#if !HAVE_SYSTEM
+ /*
+ * Just examine the help file.
+ */
+ (void) edit(helpfile);
+#else
+ /*
+ * Use lsystem() to invoke a new instance of less
+ * to view the help file.
+ */
+#if MSOFTC
+ putenv("LESS=-m -H -+E -+s -PmHELP -- ?eEND -- Press g to see it again:Press RETURN for more., or q when done");
+ cmd = (char *) ecalloc(strlen(helpfile) + strlen(progname) + 3,
+ sizeof(char));
+ sprintf(cmd, "-%s %s", progname, helpfile);
+#else
+ cmd = (char *) ecalloc(strlen(helpfile) + strlen(progname) + 150,
+ sizeof(char));
+#if OS2
+ sprintf(cmd,
+ "-%s -m -H -+E -+s \"-PmHELP -- ?eEND -- Press g to see it again:Press RETURN for more., or q when done \" %s",
+ progname, helpfile);
+#else
+ sprintf(cmd,
+ "-%s -m -H -+E -+s '-PmHELP -- ?eEND -- Press g to see it again:Press RETURN for more., or q when done ' %s",
+ progname, helpfile);
+#endif
+#endif
+ free(helpfile);
+ lsystem(cmd);
+ if (!nomsg)
+ error("End of help", NULL_PARG);
+ free(cmd);
+#endif
+}
diff --git a/usr.bin/less/ifile.c b/usr.bin/less/ifile.c
new file mode 100644
index 00000000000..a37f468c721
--- /dev/null
+++ b/usr.bin/less/ifile.c
@@ -0,0 +1,336 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * An IFILE represents an input file.
+ *
+ * It is actually a pointer to an ifile structure,
+ * but is opaque outside this module.
+ * Ifile structures are kept in a linked list in the order they
+ * appear on the command line.
+ * Any new file which does not already appear in the list is
+ * inserted after the current file.
+ */
+
+#include "less.h"
+
+extern IFILE curr_ifile;
+
+struct ifile {
+ struct ifile *h_next; /* Links for command line list */
+ struct ifile *h_prev;
+ char *h_filename; /* Name of the file */
+ void *h_filestate; /* File state (used in ch.c) */
+ int h_index; /* Index within command line list */
+ int h_opened; /* Only need one bit */
+ struct scrpos h_scrpos; /* Saved position within the file */
+};
+
+/*
+ * Convert an IFILE (external representation)
+ * to a struct file (internal representation), and vice versa.
+ */
+#define int_ifile(h) ((struct ifile *)(h))
+#define ext_ifile(h) ((IFILE)(h))
+
+/*
+ * Anchor for linked list.
+ */
+static struct ifile anchor = { &anchor, &anchor, 0 };
+static int ifiles = 0;
+
+ static void
+incr_index(p, incr)
+ register struct ifile *p;
+ int incr;
+{
+ for (; p != &anchor; p = p->h_next)
+ p->h_index += incr;
+}
+
+ static void
+link_ifile(p, prev)
+ struct ifile *p;
+ struct ifile *prev;
+{
+ /*
+ * Link into list.
+ */
+ if (prev == NULL)
+ prev = &anchor;
+ p->h_next = prev->h_next;
+ p->h_prev = prev;
+ prev->h_next->h_prev = p;
+ prev->h_next = p;
+ /*
+ * Calculate index for the new one,
+ * and adjust the indexes for subsequent ifiles in the list.
+ */
+ p->h_index = prev->h_index + 1;
+ incr_index(p->h_next, 1);
+ ifiles++;
+}
+
+ static void
+unlink_ifile(p)
+ struct ifile *p;
+{
+ p->h_next->h_prev = p->h_prev;
+ p->h_prev->h_next = p->h_next;
+ incr_index(p->h_next, -1);
+ ifiles--;
+}
+
+/*
+ * Allocate a new ifile structure and stick a filename in it.
+ * It should go after "prev" in the list
+ * (or at the beginning of the list if "prev" is NULL).
+ * Return a pointer to the new ifile structure.
+ */
+ static struct ifile *
+new_ifile(filename, prev)
+ char *filename;
+ struct ifile *prev;
+{
+ register struct ifile *p;
+
+ /*
+ * Allocate and initialize structure.
+ */
+ p = (struct ifile *) ecalloc(1, sizeof(struct ifile));
+ p->h_filename = save(filename);
+ p->h_scrpos.pos = NULL_POSITION;
+ p->h_opened = 0;
+ link_ifile(p, prev);
+ return (p);
+}
+
+/*
+ * Delete an existing ifile structure.
+ */
+ public void
+del_ifile(h)
+ IFILE h;
+{
+ register struct ifile *p;
+
+ if (h == NULL_IFILE)
+ return;
+ /*
+ * If the ifile we're deleting is the currently open ifile,
+ * move off it.
+ */
+ if (h == curr_ifile)
+ curr_ifile = getoff_ifile(curr_ifile);
+ p = int_ifile(h);
+ unlink_ifile(p);
+ free(p->h_filename);
+ free(p);
+}
+
+/*
+ * Get the ifile after a given one in the list.
+ */
+ public IFILE
+next_ifile(h)
+ IFILE h;
+{
+ register struct ifile *p;
+
+ p = (h == NULL_IFILE) ? &anchor : int_ifile(h);
+ if (p->h_next == &anchor)
+ return (NULL_IFILE);
+ return (ext_ifile(p->h_next));
+}
+
+/*
+ * Get the ifile before a given one in the list.
+ */
+ public IFILE
+prev_ifile(h)
+ IFILE h;
+{
+ register struct ifile *p;
+
+ p = (h == NULL_IFILE) ? &anchor : int_ifile(h);
+ if (p->h_prev == &anchor)
+ return (NULL_IFILE);
+ return (ext_ifile(p->h_prev));
+}
+
+/*
+ * Return a different ifile from the given one.
+ */
+ public IFILE
+getoff_ifile(ifile)
+ IFILE ifile;
+{
+ IFILE newifile;
+
+ if ((newifile = prev_ifile(ifile)) != NULL_IFILE)
+ return (newifile);
+ if ((newifile = next_ifile(ifile)) != NULL_IFILE)
+ return (newifile);
+ return (NULL_IFILE);
+}
+
+/*
+ * Return the number of ifiles.
+ */
+ public int
+nifile()
+{
+ return (ifiles);
+}
+
+/*
+ * Find an ifile structure, given a filename.
+ */
+ static struct ifile *
+find_ifile(filename)
+ char *filename;
+{
+ register struct ifile *p;
+
+ for (p = anchor.h_next; p != &anchor; p = p->h_next)
+ if (strcmp(filename, p->h_filename) == 0)
+ return (p);
+ return (NULL);
+}
+
+/*
+ * Get the ifile associated with a filename.
+ * If the filename has not been seen before,
+ * insert the new ifile after "prev" in the list.
+ */
+ public IFILE
+get_ifile(filename, prev)
+ char *filename;
+ IFILE prev;
+{
+ register struct ifile *p;
+
+ if ((p = find_ifile(filename)) == NULL)
+ p = new_ifile(filename, int_ifile(prev));
+ return (ext_ifile(p));
+}
+
+/*
+ * Get the filename associated with a ifile.
+ */
+ public char *
+get_filename(ifile)
+ IFILE ifile;
+{
+ if (ifile == NULL)
+ return (NULL);
+ return (int_ifile(ifile)->h_filename);
+}
+
+/*
+ * Get the index of the file associated with a ifile.
+ */
+ public int
+get_index(ifile)
+ IFILE ifile;
+{
+ return (int_ifile(ifile)->h_index);
+}
+
+/*
+ * Save the file position to be associated with a given file.
+ */
+ public void
+store_pos(ifile, scrpos)
+ IFILE ifile;
+ struct scrpos *scrpos;
+{
+ int_ifile(ifile)->h_scrpos = *scrpos;
+}
+
+/*
+ * Recall the file position associated with a file.
+ * If no position has been associated with the file, return NULL_POSITION.
+ */
+ public void
+get_pos(ifile, scrpos)
+ IFILE ifile;
+ struct scrpos *scrpos;
+{
+ *scrpos = int_ifile(ifile)->h_scrpos;
+}
+
+/*
+ * Mark the ifile as "opened".
+ */
+ public void
+set_open(ifile)
+ IFILE ifile;
+{
+ int_ifile(ifile)->h_opened = 1;
+}
+
+/*
+ * Return whether the ifile has been opened previously.
+ */
+ public int
+opened(ifile)
+ IFILE ifile;
+{
+ return (int_ifile(ifile)->h_opened);
+}
+
+ public void *
+get_filestate(ifile)
+ IFILE ifile;
+{
+ return (int_ifile(ifile)->h_filestate);
+}
+
+ public void
+set_filestate(ifile, filestate)
+ IFILE ifile;
+ void *filestate;
+{
+ int_ifile(ifile)->h_filestate = filestate;
+}
+
+#if 0
+ public void
+if_dump()
+{
+ register struct ifile *p;
+
+ for (p = anchor.h_next; p != &anchor; p = p->h_next)
+ {
+ printf("%x: %d. <%s> pos %d,%x\n",
+ p, p->h_index, p->h_filename,
+ p->h_scrpos.ln, p->h_scrpos.pos);
+ ch_dump(p->h_filestate);
+ }
+}
+#endif
diff --git a/usr.bin/less/input.c b/usr.bin/less/input.c
new file mode 100644
index 00000000000..89718d292f9
--- /dev/null
+++ b/usr.bin/less/input.c
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * High level routines dealing with getting lines of input
+ * from the file being viewed.
+ *
+ * When we speak of "lines" here, we mean PRINTABLE lines;
+ * lines processed with respect to the screen width.
+ * We use the term "raw line" to refer to lines simply
+ * delimited by newlines; not processed with respect to screen width.
+ */
+
+#include "less.h"
+
+extern int squeeze;
+extern int chopline;
+extern int sigs;
+#if HILITE_SEARCH
+extern int hilite_search;
+extern int size_linebuf;
+#endif
+
+/*
+ * Get the next line.
+ * A "current" position is passed and a "new" position is returned.
+ * The current position is the position of the first character of
+ * a line. The new position is the position of the first character
+ * of the NEXT line. The line obtained is the line starting at curr_pos.
+ */
+ public POSITION
+forw_line(curr_pos)
+ POSITION curr_pos;
+{
+ POSITION new_pos;
+ register int c;
+ int blankline;
+ int endline;
+
+ if (curr_pos == NULL_POSITION)
+ {
+ null_line();
+ return (NULL_POSITION);
+ }
+#if HILITE_SEARCH
+ if (hilite_search == OPT_ONPLUS)
+ prep_hilite(curr_pos, curr_pos + 3*size_linebuf);
+#endif
+ if (ch_seek(curr_pos))
+ {
+ null_line();
+ return (NULL_POSITION);
+ }
+
+ prewind();
+ plinenum(curr_pos);
+ (void) ch_seek(curr_pos);
+
+ c = ch_forw_get();
+ if (c == EOI)
+ {
+ null_line();
+ return (NULL_POSITION);
+ }
+ blankline = (c == '\n' || c == '\r');
+
+ for (;;)
+ {
+ if (ABORT_SIGS())
+ {
+ null_line();
+ return (NULL_POSITION);
+ }
+ if (c == '\n' || c == EOI)
+ {
+ /*
+ * End of the line.
+ */
+ new_pos = ch_tell();
+ endline = 1;
+ break;
+ }
+
+ /*
+ * Append the char to the line and get the next char.
+ */
+ if (pappend(c, ch_tell()-1))
+ {
+ /*
+ * The char won't fit in the line; the line
+ * is too long to print in the screen width.
+ * End the line here.
+ */
+ if (chopline)
+ {
+ do
+ {
+ c = ch_forw_get();
+ } while (c != '\n' && c != EOI);
+ new_pos = ch_tell();
+ endline = 1;
+ } else
+ {
+ new_pos = ch_tell() - 1;
+ endline = 0;
+ }
+ break;
+ }
+ c = ch_forw_get();
+ }
+ pdone(endline);
+
+ if (squeeze && blankline)
+ {
+ /*
+ * This line is blank.
+ * Skip down to the last contiguous blank line
+ * and pretend it is the one which we are returning.
+ */
+ while ((c = ch_forw_get()) == '\n' || c == '\r')
+ if (ABORT_SIGS())
+ {
+ null_line();
+ return (NULL_POSITION);
+ }
+ if (c != EOI)
+ (void) ch_back_get();
+ new_pos = ch_tell();
+ }
+
+ return (new_pos);
+}
+
+/*
+ * Get the previous line.
+ * A "current" position is passed and a "new" position is returned.
+ * The current position is the position of the first character of
+ * a line. The new position is the position of the first character
+ * of the PREVIOUS line. The line obtained is the one starting at new_pos.
+ */
+ public POSITION
+back_line(curr_pos)
+ POSITION curr_pos;
+{
+ POSITION new_pos, begin_new_pos;
+ int c;
+ int endline;
+
+ if (curr_pos == NULL_POSITION || curr_pos <= ch_zero())
+ {
+ null_line();
+ return (NULL_POSITION);
+ }
+#if HILITE_SEARCH
+ if (hilite_search == OPT_ONPLUS)
+ prep_hilite((curr_pos < 3*size_linebuf) ?
+ 0 : curr_pos - 3*size_linebuf, curr_pos);
+#endif
+ if (ch_seek(curr_pos-1))
+ {
+ null_line();
+ return (NULL_POSITION);
+ }
+
+ if (squeeze)
+ {
+ /*
+ * Find out if the "current" line was blank.
+ */
+ (void) ch_forw_get(); /* Skip the newline */
+ c = ch_forw_get(); /* First char of "current" line */
+ (void) ch_back_get(); /* Restore our position */
+ (void) ch_back_get();
+
+ if (c == '\n' || c == '\r')
+ {
+ /*
+ * The "current" line was blank.
+ * Skip over any preceding blank lines,
+ * since we skipped them in forw_line().
+ */
+ while ((c = ch_back_get()) == '\n' || c == '\r')
+ if (ABORT_SIGS())
+ {
+ null_line();
+ return (NULL_POSITION);
+ }
+ if (c == EOI)
+ {
+ null_line();
+ return (NULL_POSITION);
+ }
+ (void) ch_forw_get();
+ }
+ }
+
+ /*
+ * Scan backwards until we hit the beginning of the line.
+ */
+ for (;;)
+ {
+ if (ABORT_SIGS())
+ {
+ null_line();
+ return (NULL_POSITION);
+ }
+ c = ch_back_get();
+ if (c == '\n')
+ {
+ /*
+ * This is the newline ending the previous line.
+ * We have hit the beginning of the line.
+ */
+ new_pos = ch_tell() + 1;
+ break;
+ }
+ if (c == EOI)
+ {
+ /*
+ * We have hit the beginning of the file.
+ * This must be the first line in the file.
+ * This must, of course, be the beginning of the line.
+ */
+ new_pos = ch_tell();
+ break;
+ }
+ }
+
+ /*
+ * Now scan forwards from the beginning of this line.
+ * We keep discarding "printable lines" (based on screen width)
+ * until we reach the curr_pos.
+ *
+ * {{ This algorithm is pretty inefficient if the lines
+ * are much longer than the screen width,
+ * but I don't know of any better way. }}
+ */
+ if (ch_seek(new_pos))
+ {
+ null_line();
+ return (NULL_POSITION);
+ }
+ endline = 0;
+ loop:
+ begin_new_pos = new_pos;
+ prewind();
+ plinenum(new_pos);
+ (void) ch_seek(new_pos);
+
+ do
+ {
+ c = ch_forw_get();
+ if (c == EOI || ABORT_SIGS())
+ {
+ null_line();
+ return (NULL_POSITION);
+ }
+ new_pos++;
+ if (c == '\n')
+ {
+ endline = 1;
+ break;
+ }
+ if (pappend(c, ch_tell()-1))
+ {
+ /*
+ * Got a full printable line, but we haven't
+ * reached our curr_pos yet. Discard the line
+ * and start a new one.
+ */
+ if (chopline)
+ {
+ endline = 1;
+ break;
+ }
+ pdone(0);
+ (void) ch_back_get();
+ new_pos--;
+ goto loop;
+ }
+ } while (new_pos < curr_pos);
+
+ pdone(endline);
+
+ return (begin_new_pos);
+}
diff --git a/usr.bin/less/install.sh b/usr.bin/less/install.sh
new file mode 100644
index 00000000000..0ff4b6a08e8
--- /dev/null
+++ b/usr.bin/less/install.sh
@@ -0,0 +1,119 @@
+#!/bin/sh
+
+#
+# install - install a program, script, or datafile
+# This comes from X11R5; it is not part of GNU.
+#
+# $XConsortium: install.sh,v 1.2 89/12/18 14:47:22 jim Exp $
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+#
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+
+instcmd="$mvprog"
+chmodcmd=""
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+fi
+
+if [ x"$dst" = x ]
+then
+ echo "install: no destination specified"
+ exit 1
+fi
+
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+if [ -d $dst ]
+then
+ dst="$dst"/`basename $src`
+fi
+
+# Make a temp file name in the proper directory.
+
+dstdir=`dirname $dst`
+dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+$doit $instcmd $src $dsttmp
+
+# and set any options; do chmod last to preserve setuid bits
+
+if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; fi
+if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; fi
+if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; fi
+if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; fi
+
+# Now rename the file to the real destination.
+
+$doit $rmcmd $dst
+$doit $mvcmd $dsttmp $dst
+
+
+exit 0
diff --git a/usr.bin/less/jump.c b/usr.bin/less/jump.c
new file mode 100644
index 00000000000..c87eed301f4
--- /dev/null
+++ b/usr.bin/less/jump.c
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * Routines which jump to a new location in the file.
+ */
+
+#include "less.h"
+#include "position.h"
+
+extern int hit_eof;
+extern int jump_sline;
+extern int squished;
+extern int screen_trashed;
+extern int sc_width, sc_height;
+
+/*
+ * Jump to the end of the file.
+ */
+ public void
+jump_forw()
+{
+ POSITION pos;
+
+ if (ch_end_seek())
+ {
+ error("Cannot seek to end of file", NULL_PARG);
+ return;
+ }
+ /*
+ * Position the last line in the file at the last screen line.
+ * Go back one line from the end of the file
+ * to get to the beginning of the last line.
+ */
+ pos = back_line(ch_tell());
+ if (pos == NULL_POSITION)
+ jump_loc((POSITION)0, sc_height-1);
+ else
+ jump_loc(pos, sc_height-1);
+}
+
+/*
+ * Jump to line n in the file.
+ */
+ public void
+jump_back(n)
+ int n;
+{
+ POSITION pos;
+ PARG parg;
+
+ /*
+ * Find the position of the specified line.
+ * If we can seek there, just jump to it.
+ * If we can't seek, but we're trying to go to line number 1,
+ * use ch_beg_seek() to get as close as we can.
+ */
+ pos = find_pos(n);
+ if (pos != NULL_POSITION && ch_seek(pos) == 0)
+ {
+ jump_loc(pos, jump_sline);
+ } else if (n <= 1 && ch_beg_seek() == 0)
+ {
+ jump_loc(ch_tell(), jump_sline);
+ error("Cannot seek to beginning of file", NULL_PARG);
+ } else
+ {
+ parg.p_int = n;
+ error("Cannot seek to line number %d", &parg);
+ }
+}
+
+/*
+ * Repaint the screen.
+ */
+ public void
+repaint()
+{
+ struct scrpos scrpos;
+ /*
+ * Start at the line currently at the top of the screen
+ * and redisplay the screen.
+ */
+ get_scrpos(&scrpos);
+ pos_clear();
+ jump_loc(scrpos.pos, scrpos.ln);
+}
+
+/*
+ * Jump to a specified percentage into the file.
+ */
+ public void
+jump_percent(percent)
+ int percent;
+{
+ POSITION pos, len;
+
+ /*
+ * Determine the position in the file
+ * (the specified percentage of the file's length).
+ */
+ if ((len = ch_length()) == NULL_POSITION)
+ {
+ ierror("Determining length of file", NULL_PARG);
+ ch_end_seek();
+ }
+ if ((len = ch_length()) == NULL_POSITION)
+ {
+ error("Don't know length of file", NULL_PARG);
+ return;
+ }
+ /*
+ * {{ This calculation may overflow! }}
+ */
+ pos = (percent * len) / 100;
+ if (pos >= len)
+ pos = len-1;
+
+ jump_line_loc(pos, jump_sline);
+}
+
+/*
+ * Jump to a specified position in the file.
+ * Like jump_loc, but the position need not be
+ * the first character in a line.
+ */
+ public void
+jump_line_loc(pos, sline)
+ POSITION pos;
+ int sline;
+{
+ int c;
+
+ if (ch_seek(pos) == 0)
+ {
+ /*
+ * Back up to the beginning of the line.
+ */
+ while ((c = ch_back_get()) != '\n' && c != EOI)
+ ;
+ if (c == '\n')
+ (void) ch_forw_get();
+ pos = ch_tell();
+ }
+ jump_loc(pos, sline);
+}
+
+/*
+ * Jump to a specified position in the file.
+ * The position must be the first character in a line.
+ * Place the target line on a specified line on the screen.
+ */
+ public void
+jump_loc(pos, sline)
+ POSITION pos;
+ int sline;
+{
+ register int nline;
+ POSITION tpos;
+ POSITION bpos;
+
+ /*
+ * Normalize sline.
+ */
+ sline = adjsline(sline);
+
+ if ((nline = onscreen(pos)) >= 0)
+ {
+ /*
+ * The line is currently displayed.
+ * Just scroll there.
+ */
+ nline -= sline;
+ if (nline > 0)
+ forw(nline, position(BOTTOM_PLUS_ONE), 1, 0, 0);
+ else
+ back(-nline, position(TOP), 1, 0);
+ return;
+ }
+
+ /*
+ * Line is not on screen.
+ * Seek to the desired location.
+ */
+ if (ch_seek(pos))
+ {
+ error("Cannot seek to that file position", NULL_PARG);
+ return;
+ }
+
+ /*
+ * See if the desired line is before or after
+ * the currently displayed screen.
+ */
+ tpos = position(TOP);
+ bpos = position(BOTTOM_PLUS_ONE);
+ if (tpos == NULL_POSITION || pos >= tpos)
+ {
+ /*
+ * The desired line is after the current screen.
+ * Move back in the file far enough so that we can
+ * call forw() and put the desired line at the
+ * sline-th line on the screen.
+ */
+ for (nline = 0; nline < sline; nline++)
+ {
+ if (bpos != NULL_POSITION && pos <= bpos)
+ {
+ /*
+ * Surprise! The desired line is
+ * close enough to the current screen
+ * that we can just scroll there after all.
+ */
+ forw(sc_height-sline+nline-1, bpos, 1, 0, 0);
+ return;
+ }
+ pos = back_line(pos);
+ if (pos == NULL_POSITION)
+ {
+ /*
+ * Oops. Ran into the beginning of the file.
+ * Exit the loop here and rely on forw()
+ * below to draw the required number of
+ * blank lines at the top of the screen.
+ */
+ break;
+ }
+ }
+ lastmark();
+ hit_eof = 0;
+ squished = 0;
+ screen_trashed = 0;
+ forw(sc_height-1, pos, 1, 0, sline-nline);
+ } else
+ {
+ /*
+ * The desired line is before the current screen.
+ * Move forward in the file far enough so that we
+ * can call back() and put the desired line at the
+ * sline-th line on the screen.
+ */
+ for (nline = sline; nline < sc_height - 1; nline++)
+ {
+ pos = forw_line(pos);
+ if (pos == NULL_POSITION)
+ {
+ /*
+ * Ran into end of file.
+ * This shouldn't normally happen,
+ * but may if there is some kind of read error.
+ */
+ break;
+ }
+ if (pos >= tpos)
+ {
+ /*
+ * Surprise! The desired line is
+ * close enough to the current screen
+ * that we can just scroll there after all.
+ */
+ back(nline+1, tpos, 1, 0);
+ return;
+ }
+ }
+ lastmark();
+ clear();
+ screen_trashed = 0;
+ add_back_pos(pos);
+ back(sc_height-1, pos, 1, 0);
+ }
+}
diff --git a/usr.bin/less/less.h b/usr.bin/less/less.h
new file mode 100644
index 00000000000..2f6959de89c
--- /dev/null
+++ b/usr.bin/less/less.h
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * Standard include file for "less".
+ */
+
+/*
+ * Include the file of compile-time options.
+ * The <> make cc search for it in -I., not srcdir.
+ */
+#include <defines.h>
+
+#ifdef _SEQUENT_
+/*
+ * Kludge for Sequent Dynix systems that have sigsetmask, but
+ * it's not compatible with the way less calls it.
+ * {{ Do other systems need this? }}
+ */
+#undef HAVE_SIGSETMASK
+#endif
+
+/*
+ * Language details.
+ */
+#if HAVE_VOID
+#define VOID_POINTER void *
+#else
+#define VOID_POINTER char *
+#define void int
+#endif
+
+#define public /* PUBLIC FUNCTION */
+
+/* Library function declarations */
+
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <string.h>
+#endif
+
+#if !STDC_HEADERS
+char *getenv();
+off_t lseek();
+VOID_POINTER calloc();
+void free();
+#endif
+
+#if !HAVE_UPPER_LOWER
+#define isupper(c) ((c) >= 'A' && (c) <= 'Z')
+#define islower(c) ((c) >= 'a' && (c) <= 'z')
+#define toupper(c) ((c) - 'a' + 'A')
+#define tolower(c) ((c) - 'A' + 'a')
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define OPT_OFF 0
+#define OPT_ON 1
+#define OPT_ONPLUS 2
+
+#ifndef HAVE_MEMCPY
+#ifndef memcpy
+#define memcpy(to,from,len) bcopy((from),(to),(len))
+#endif
+#endif
+
+#define BAD_LSEEK ((off_t)-1)
+
+/*
+ * Special types and constants.
+ */
+typedef long POSITION;
+/*
+ * {{ Warning: if POSITION is changed to other than "long",
+ * you may have to change some of the printfs which use "%ld"
+ * to print a variable of type POSITION. }}
+ */
+
+#define NULL_POSITION ((POSITION)(-1))
+
+/*
+ * Flags for open()
+ */
+#if MSOFTC || OS2
+#define OPEN_READ (O_RDONLY|O_BINARY)
+#else
+#define OPEN_READ (0)
+#endif
+#if MSOFTC || OS2
+#define OPEN_APPEND (O_APPEND|O_WRONLY)
+#else
+#define OPEN_APPEND (1)
+#endif
+
+#if MSOFTC || OS2
+#define OPEN_TTYIN() open("CON", O_BINARY|O_RDONLY)
+#else
+#define OPEN_TTYIN() open("/dev/tty", 0)
+#endif
+
+/*
+ * An IFILE represents an input file.
+ */
+#define IFILE VOID_POINTER
+#define NULL_IFILE ((IFILE)NULL)
+
+/*
+ * The structure used to represent a "screen position".
+ * This consists of a file position, and a screen line number.
+ * The meaning is that the line starting at the given file
+ * position is displayed on the ln-th line of the screen.
+ * (Screen lines before ln are empty.)
+ */
+struct scrpos
+{
+ POSITION pos;
+ int ln;
+};
+
+typedef union parg
+{
+ char *p_string;
+ int p_int;
+} PARG;
+
+#define NULL_PARG ((PARG *)NULL)
+
+struct textlist
+{
+ char *string;
+ char *endstring;
+};
+
+#define EOI (-1)
+
+#define READ_INTR (-2)
+
+/* How quiet should we be? */
+#define NOT_QUIET 0 /* Ring bell at eof and for errors */
+#define LITTLE_QUIET 1 /* Ring bell only for errors */
+#define VERY_QUIET 2 /* Never ring bell */
+
+/* How should we prompt? */
+#define PR_SHORT 0 /* Prompt with colon */
+#define PR_MEDIUM 1 /* Prompt with message */
+#define PR_LONG 2 /* Prompt with longer message */
+
+/* How should we handle backspaces? */
+#define BS_SPECIAL 0 /* Do special things for underlining and bold */
+#define BS_NORMAL 1 /* \b treated as normal char; actually output */
+#define BS_CONTROL 2 /* \b treated as control char; prints as ^H */
+
+/* How should we search? */
+#define SRCH_FORW 0001 /* Search forward from current position */
+#define SRCH_BACK 0002 /* Search backward from current position */
+#define SRCH_FIND_ALL 0010 /* Find and highlight all matches */
+#define SRCH_NOMATCH 0100 /* Search for non-matching lines */
+#define SRCH_PAST_EOF 0200 /* Search past end-of-file, into next file */
+#define SRCH_FIRST_FILE 0400 /* Search starting at the first file */
+
+#define SRCH_REVERSE(t) (((t) & SRCH_FORW) ? \
+ (((t) & ~SRCH_FORW) | SRCH_BACK) : \
+ (((t) & ~SRCH_BACK) | SRCH_FORW))
+
+/* */
+#define NO_MCA 0
+#define MCA_DONE 1
+#define MCA_MORE 2
+
+#define CC_OK 0 /* Char was accepted & processed */
+#define CC_QUIT 1 /* Char was a request to abort current cmd */
+#define CC_ERROR 2 /* Char could not be accepted due to error */
+#define CC_PASS 3 /* Char was rejected (internal) */
+
+/* Special chars used to tell put_line() to do something special */
+#define AT_NORMAL (0)
+#define AT_UNDERLINE (1)
+#define AT_BOLD (2)
+#define AT_BLINK (3)
+#define AT_INVIS (4)
+#define AT_STANDOUT (5)
+
+#define CONTROL(c) ((c)&037)
+#define ESC CONTROL('[')
+
+#define SIGNAL(sig,func) signal(sig,func)
+
+#define S_INTERRUPT 01
+#define S_STOP 02
+#define S_WINCH 04
+#define ABORT_SIGS() (sigs & (S_INTERRUPT|S_STOP))
+
+#define QUIT_OK 0
+#define QUIT_ERROR 1
+#define QUIT_SAVED_STATUS (-1)
+
+/* filestate flags */
+#define CH_CANSEEK 001
+#define CH_KEEPOPEN 002
+#define CH_POPENED 004
+
+#define ch_zero() ((POSITION)0)
+
+#include "funcs.h"
diff --git a/usr.bin/less/less.hlp b/usr.bin/less/less.hlp
new file mode 100644
index 00000000000..d3b1647bbff
--- /dev/null
+++ b/usr.bin/less/less.hlp
@@ -0,0 +1,109 @@
+
+ SUMMARY OF COMMANDS
+
+ Commands marked with * may be preceded by a number, _N.
+ Notes in parentheses indicate the behavior if _N is given.
+
+ h H Display this help.
+ q :q :Q ZZ Exit.
+
+ e ^E j ^N CR * Forward one line (or _N lines).
+ y ^Y k ^K ^P * Backward one line (or _N lines).
+ f ^F ^V SPACE * Forward one window (or _N lines).
+ b ^B ESC-v * Backward one window (or _N lines).
+ z * Forward one window (and set window to _N).
+ w * Backward one window (and set window to _N).
+ d ^D * Forward one half-window (and set half-window to _N).
+ u ^U * Backward one half-window (and set half-window to _N).
+ F Forward forever; like "tail -f".
+ r ^R ^L Repaint screen.
+ R Repaint screen, discarding buffered input.
+ ---------------------------------------------------
+ Default "window" is the screen height.
+ Default "half-window" is half of the screen height.
+ ---------------------------------------------------
+ /_p_a_t_t_e_r_n * Search forward for (_N-th) matching line.
+ ?_p_a_t_t_e_r_n * Search backward for (_N-th) matching line.
+ n * Repeat previous search (for _N-th occurrence).
+ N * Repeat previous search in reverse direction.
+ ESC-n * Repeat previous search, spanning files.
+ ESC-N * Repeat previous search, reverse dir. & spanning files.
+ ESC-u Undo (toggle) search highlighting.
+ ---------------------------------------------------
+ Search patterns may be modified by one or more of:
+ ! search for NON-matching lines.
+ * search multiple files.
+ @ start search at first file (for /) or last file (for ?).
+ ---------------------------------------------------
+ g < ESC-< * Go to first line in file (or line _N).
+ G > ESC-> * Go to last line in file (or line _N).
+ p % * Go to beginning of file (or _N percent into file).
+ { * Go to the } matching the (_N-th) { in the top line.
+ } * Go to the { matching the (_N-th) } in the bottom line.
+ ( * Go to the ) matching the (_N-th) ( in the top line.
+ ) * Go to the ( matching the (_N-th) ) in the bottom line.
+ [ * Go to the ] matching the (_N-th) [ in the top line.
+ ] * Go to the [ matching the (_N-th) ] in the bottom line.
+ ESC-^F _<_c_1_> _<_c_2_> * Go to the _c_1 matching the (_N-th) _c_2 in the top line
+ ESC-^B _<_c_1_> _<_c_2_> * Go to the _c_2 matching the (_N-th) _c_1 in the bottom line.
+ m_<_l_e_t_t_e_r_> Mark the current position with <letter>.
+ '_<_l_e_t_t_e_r_> Go to a previously marked position.
+ '' Go to the previous position.
+ ^X^X Same as '.
+
+ :e [_f_i_l_e] Examine a new file.
+ ^X^V Same as :e.
+ :n * Examine the (_N-th) next file from the command line.
+ :p * Examine the (_N-th) previous file from the command line.
+ :x * Examine the first (or _N-th) file from the command line.
+ = ^G :f Print current file name.
+ V Print version number of "less".
+
+ -_<_f_l_a_g_> Toggle a command line flag [see FLAGS below].
+ __<_f_l_a_g_> Display the setting of a command line flag.
+ +_c_m_d Execute the less cmd each time a new file is examined.
+
+ !_c_o_m_m_a_n_d Passes the command to $SHELL to be executed.
+ |XX_c_o_m_m_a_n_d Pipe file between current pos & mark XX to shell command.
+ v Edit the current file with $VISUAL or $EDITOR.
+ ---------------------------------------------------
+ FLAGS
+
+ Most flags may be changed either on the command line,
+ or from within less by using the - command.
+
+ -? Display help (from command line).
+ -a Forward search skips current screen.
+ -b [_N] Number of buffers.
+ -B Don't automatically allocate buffers for pipes.
+ -c -C Repaint by scrolling/clearing.
+ -d Dumb terminal.
+ -e -E Quit at end of file.
+ -f Force open non-regular files.
+ -g Don't highlight matches for previous search pattern.
+ -G Highlight ALL matches for previous search pattern.
+ -h [_N] Backward scroll limit.
+ -i Ignore case in searches.
+ -I Ignore case in searches and in search patterns.
+ -j [_N] Screen position of target lines.
+ -k [_f_i_l_e] Use a lesskey file.
+ -m -M Set prompt style.
+ -n -N Use line numbers.
+ -o [_f_i_l_e] Log file.
+ -O [_f_i_l_e] Log file (unconditionally overwrite).
+ -p [_p_a_t_t_e_r_n] Start at pattern (from command line).
+ -P [_p_r_o_m_p_t] Define new prompt.
+ -q -Q Quiet the terminal bell.
+ -r Output "raw" control characters.
+ -s Squeeze multiple blank lines.
+ -S Chop long lines.
+ -t [_t_a_g] Find a tag.
+ -T [_t_a_g_s_f_i_l_e] Use an alternate tags file.
+ -u -U Change handling of backspaces.
+ -V Display the version number of "less".
+ -w Display ~ for lines after end-of-file.
+ -x [_N] Set tab stops.
+ -X Don't use termcap init/deinit strings.
+ -y [_N] Forward scroll limit.
+ -z [_N] Set size of window.
+
diff --git a/usr.bin/less/less.man b/usr.bin/less/less.man
new file mode 100644
index 00000000000..f236b2c8037
--- /dev/null
+++ b/usr.bin/less/less.man
@@ -0,0 +1,1518 @@
+
+
+
+LESS(1) USER COMMANDS LESS(1)
+
+
+
+NAME
+ less - opposite of more
+
+SYNOPSIS
+ less -?
+ less -V
+ less [-[+]aBcCdeEfgGiImMnNqQrsSuUVwX]
+ [-b _b_u_f_s] [-h _l_i_n_e_s] [-j _l_i_n_e] [-k _k_e_y_f_i_l_e]
+ [-{oO} _l_o_g_f_i_l_e] [-p _p_a_t_t_e_r_n] [-P _p_r_o_m_p_t] [-t _t_a_g]
+ [-T _t_a_g_s_f_i_l_e] [-x _t_a_b] [-y _l_i_n_e_s] [-[z] _l_i_n_e_s]
+ [+[+]_c_m_d] [_f_i_l_e_n_a_m_e]...
+
+
+DESCRIPTION
+ _L_e_s_s is a program similar to _m_o_r_e (1), but which allows
+ backward movement in the file as well as forward movement.
+ Also, _l_e_s_s does not have to read the entire input file
+ before starting, so with large input files it starts up fas-
+ ter than text editors like _v_i (1). _L_e_s_s uses termcap (or
+ terminfo on some systems), so it can run on a variety of
+ terminals. There is even limited support for hardcopy ter-
+ minals. (On a hardcopy terminal, lines which should be
+ printed at the top of the screen are prefixed with a caret.)
+
+ Commands are based on both _m_o_r_e and _v_i. Commands may be pre-
+ ceded by a decimal number, called N in the descriptions
+ below. The number is used by some commands, as indicated.
+
+
+COMMANDS
+ In the following descriptions, ^X means control-X. ESC
+ stands for the ESCAPE key; for example ESC-v means the two
+ character sequence "ESCAPE", then "v".
+
+ h or H
+ Help: display a summary of these commands. If you for-
+ get all the other commands, remember this one.
+
+ SPACE or ^V or f or ^F
+ Scroll forward N lines, default one window (see option
+ -z below). If N is more than the screen size, only the
+ final screenful is displayed. Warning: some systems
+ use ^V as a special literalization character.
+
+ z Like SPACE, but if N is specified, it becomes the new
+ window size.
+
+ RETURN or ^N or e or ^E or j or ^J
+ Scroll forward N lines, default 1. The entire N lines
+ are displayed, even if N is more than the screen size.
+
+
+
+
+
+ 1
+
+
+
+
+
+
+LESS(1) USER COMMANDS LESS(1)
+
+
+
+ d or ^D
+ Scroll forward N lines, default one half of the screen
+ size. If N is specified, it becomes the new default
+ for subsequent d and u commands.
+
+ b or ^B or ESC-v
+ Scroll backward N lines, default one window (see option
+ -z below). If N is more than the screen size, only the
+ final screenful is displayed.
+
+ w Like ESC-v, but if N is specified, it becomes the new
+ window size.
+
+ y or ^Y or ^P or k or ^K
+ Scroll backward N lines, default 1. The entire N lines
+ are displayed, even if N is more than the screen size.
+ Warning: some systems use ^Y as a special job control
+ character.
+
+ u or ^U
+ Scroll backward N lines, default one half of the screen
+ size. If N is specified, it becomes the new default
+ for subsequent d and u commands.
+
+ r or ^R or ^L
+ Repaint the screen.
+
+ R Repaint the screen, discarding any buffered input.
+ Useful if the file is changing while it is being
+ viewed.
+
+ F Scroll forward, and keep trying to read when the end of
+ file is reached. Normally this command would be used
+ when already at the end of the file. It is a way to
+ monitor the tail of a file which is growing while it is
+ being viewed. (The behavior is similar to the "tail
+ -f" command.)
+
+ g or < or ESC-<
+ Go to line N in the file, default 1 (beginning of
+ file). (Warning: this may be slow if N is large.)
+
+ G or > or ESC->
+ Go to line N in the file, default the end of the file.
+ (Warning: this may be slow if N is large, or if N is
+ not specified and standard input, rather than a file,
+ is being read.)
+
+ p or %
+ Go to a position N percent into the file. N should be
+ between 0 and 100. (This works if standard input is
+ being read, but only if _l_e_s_s has already read to the
+
+
+
+ 2
+
+
+
+
+
+
+LESS(1) USER COMMANDS LESS(1)
+
+
+
+ end of the file. It is always fast, but not always
+ useful.)
+
+ { If a left curly bracket appears in the top line
+ displayed on the screen, the { command will go to the
+ matching right curly bracket. The matching right curly
+ bracket is positioned on the bottom line of the screen.
+ If there is more than one left curly bracket on the top
+ line, a number N may be used to specify the N-th
+ bracket on the line.
+
+ } If a right curly bracket appears in the bottom line
+ displayed on the screen, the } command will go to the
+ matching left curly bracket. The matching left curly
+ bracket is positioned on the top line of the screen.
+ If there is more than one right curly bracket on the
+ top line, a number N may be used to specify the N-th
+ bracket on the line.
+
+ ( Like {, but applies to parentheses rather than curly
+ brackets.
+
+ ) Like }, but applies to parentheses rather than curly
+ brackets.
+
+ [ Like {, but applies to square brackets rather than
+ curly brackets.
+
+ ] Like }, but applies to square brackets rather than
+ curly brackets.
+
+ ESC-^F
+ Followed by two characters, acts like {, but uses the
+ two characters as open and close brackets, respec-
+ tively. For example, "ESC ^F < >" could be used to go
+ forward to the > which matches the < in the top
+ displayed line.
+
+ ESC-^B
+ Followed by two characters, acts like }, but uses the
+ two characters as open and close brackets, respec-
+ tively. For example, "ESC ^B < >" could be used to go
+ backward to the < which matches the > in the bottom
+ displayed line.
+
+ m Followed by any lowercase letter, marks the current
+ position with that letter.
+
+ ' (Single quote.) Followed by any lowercase letter,
+ returns to the position which was previously marked
+ with that letter. Followed by another single quote,
+ returns to the position at which the last "large"
+
+
+
+ 3
+
+
+
+
+
+
+LESS(1) USER COMMANDS LESS(1)
+
+
+
+ movement command was executed. Followed by a ^ or $,
+ jumps to the beginning or end of the file respectively.
+ Marks are preserved when a new file is examined, so the
+ ' command can be used to switch between input files.
+
+ ^X^X Same as single quote.
+
+ /pattern
+ Search forward in the file for the N-th line containing
+ the pattern. N defaults to 1. The pattern is a regu-
+ lar expression, as recognized by _e_d. The search starts
+ at the second line displayed (but see the -a and -j
+ options, which change this).
+
+ Certain characters are special if entered at the begin-
+ ning of the pattern; they modify the type of search
+ rather than become part of the pattern:
+
+ ! Search for lines which do NOT match the pattern.
+
+ * Search multiple files. That is, if the search
+ reaches the end of the current file without find-
+ ing a match, the search continues in the next file
+ in the command line list.
+
+ @ Begin the search at the first line of the first
+ file in the command line list, regardless of what
+ is currently displayed on the screen or the set-
+ tings of the -a or -j options.
+
+ ?pattern
+ Search backward in the file for the N-th line contain-
+ ing the pattern. The search starts at the line immedi-
+ ately before the top line displayed.
+
+ Certain characters are special as in the / command:
+
+ ! Search for lines which do NOT match the pattern.
+
+ * Search multiple files. That is, if the search
+ reaches the beginning of the current file without
+ finding a match, the search continues in the pre-
+ vious file in the command line list.
+
+ @ Begin the search at the last line of the last file
+ in the command line list, regardless of what is
+ currently displayed on the screen or the settings
+ of the -a or -j options.
+
+ ESC-/pattern
+ Same as "/*".
+
+
+
+
+ 4
+
+
+
+
+
+
+LESS(1) USER COMMANDS LESS(1)
+
+
+
+ ESC-?pattern
+ Same as "?*".
+
+ n Repeat previous search, for N-th line containing the
+ last pattern. If the previous search was modified by
+ !, the search is made for the N-th line NOT containing
+ the pattern. If the previous search was modified by *,
+ the search continues in the next (or previous) file if
+ not satisfied in the current file. There is no effect
+ if the previous search was modified by @.
+
+ N Repeat previous search, but in the reverse direction.
+
+ ESC-n
+ Repeat previous search, but crossing file boundaries.
+ The effect is as if the previous search were modified
+ by *.
+
+ ESC-N
+ Repeat previous search, but in the reverse direction
+ and crossing file boundaries.
+
+ ESC-u
+ Undo search highlighting. Turn off highlighting of
+ strings matching the current search pattern. If
+ highlighting is already off because of a previous ESC-u
+ command, turn highlighting back on. Any search command
+ will also turn highlighting back on. (Highlighting can
+ also be disabled by toggling the -G flag; in that case
+ search commands do not turn highlighting back on.)
+
+ :e [filename]
+ Examine a new file. If the filename is missing, the
+ "current" file (see the :n and :p commands below) from
+ the list of files in the command line is re-examined.
+ A percent sign (%) in the filename is replaced by the
+ name of the current file. A pound sign (#) is replaced
+ by the name of the previously examined file. The
+ filename is inserted into the command line list of
+ files so that it can be seen by subsequent :n and :p
+ commands. If the filename consists of several files,
+ they are all inserted into the list of files and the
+ first one is examined.
+
+ ^X^V or E
+ Same as :e. Warning: some systems use ^V as a special
+ literalization character.
+
+ :n Examine the next file (from the list of files given in
+ the command line). If a number N is specified, the N-
+ th next file is examined.
+
+
+
+
+ 5
+
+
+
+
+
+
+LESS(1) USER COMMANDS LESS(1)
+
+
+
+ :p Examine the previous file in the command line list. If
+ a number N is specified, the N-th previous file is
+ examined.
+
+ :x Examine the first file in the command line list. If a
+ number N is specified, the N-th file in the list is
+ examined.
+
+ = or ^G or :f
+ Prints some information about the file being viewed,
+ including its name and the line number and byte offset
+ of the bottom line being displayed. If possible, it
+ also prints the length of the file, the number of lines
+ in the file and the percent of the file above the last
+ displayed line.
+
+ - Followed by one of the command line option letters (see
+ below), this will change the setting of that option and
+ print a message describing the new setting. If the
+ option letter has a numeric value (such as -b or -h),
+ or a string value (such as -P or -t), a new value may
+ be entered after the option letter. If no new value is
+ entered, a message describing the current setting is
+ printed and nothing is changed.
+
+ -+ Followed by one of the command line option letters (see
+ below), this will reset the option to its default set-
+ ting and print a message describing the new setting.
+ (The "-+_X" command does the same thing as "-+_X" on the
+ command line.) This does not work for string-valued
+ options.
+
+ -- Followed by one of the command line option letters (see
+ below), this will reset the option to the "opposite" of
+ its default setting and print a message describing the
+ new setting. (The "--_X" command does the same thing as
+ "-_X" on the command line.) This does not work for
+ numeric or string-valued options.
+
+ _ (Underscore.) Followed by one of the command line
+ option letters (see below), this will print a message
+ describing the current setting of that option. The
+ setting of the option is not changed.
+
+ +cmd Causes the specified cmd to be executed each time a new
+ file is examined. For example, +G causes _l_e_s_s to ini-
+ tially display each file starting at the end rather
+ than the beginning.
+
+ V Prints the version number of _l_e_s_s being run.
+
+
+
+
+
+ 6
+
+
+
+
+
+
+LESS(1) USER COMMANDS LESS(1)
+
+
+
+ q or :q or :Q or ZZ
+ Exits _l_e_s_s.
+
+ The following three commands may or may not be valid,
+ depending on your particular installation.
+
+ v Invokes an editor to edit the current file being
+ viewed. The editor is taken from the environment vari-
+ able VISUAL if defined, or EDITOR if VISUAL is not
+ defined, or defaults to "vi" if neither VISUAL nor EDI-
+ TOR is defined. See also the discussion of LESSEDIT
+ under the section on PROMPTS below.
+
+ ! shell-command
+ Invokes a shell to run the shell-command given. A per-
+ cent sign (%) in the command is replaced by the name of
+ the current file. A pound sign (#) is replaced by the
+ name of the previously examined file. "!!" repeats the
+ last shell command. "!" with no shell command simply
+ invokes a shell. In all cases, the shell is taken from
+ the environment variable SHELL, or defaults to "sh".
+
+ | <m> shell-command
+ <m> represents any mark letter. Pipes a section of the
+ input file to the given shell command. The section of
+ the file to be piped is between the first line on the
+ current screen and the position marked by the letter.
+ <m> may also be ^ or $ to indicate beginning or end of
+ file respectively. If <m> is . or newline, the current
+ screen is piped.
+
+OPTIONS
+ Command line options are described below. Most options may
+ be changed while _l_e_s_s is running, via the "-" command.
+
+ Options are also taken from the environment variable "LESS".
+ For example, to avoid typing "less -options ..." each time
+ _l_e_s_s is invoked, you might tell _c_s_h:
+
+ setenv LESS "-options"
+
+ or if you use _s_h:
+
+ LESS="-options"; export LESS
+
+ The environment variable is parsed before the command line,
+ so command line options override the LESS environment vari-
+ able. If an option appears in the LESS variable, it can be
+ reset to its default on the command line by beginning the
+ command line option with "-+".
+
+ A dollar sign ($) may be used to signal the end of an option
+
+
+
+ 7
+
+
+
+
+
+
+LESS(1) USER COMMANDS LESS(1)
+
+
+
+ string. This is important only for options like -P which
+ take a following string.
+
+ -? This option displays a summary of the commands accepted
+ by _l_e_s_s (the same as the h command). If this option is
+ given, all other options are ignored, and _l_e_s_s exits
+ after the help screen is viewed. (Depending on how
+ your shell interprets the question mark, it may be
+ necessary to quote the question mark, thus: "-\?".)
+
+ -a Causes searches to start after the last line displayed
+ on the screen, thus skipping all lines displayed on the
+ screen. By default, searches start at the second line
+ on the screen (or after the last found line; see the -j
+ option).
+
+ -b_n Specifies the number of buffers _l_e_s_s will use for each
+ file. Buffers are 1K, and by default 10 buffers are
+ used for each file (except if the file is a pipe; see
+ the -B option). The number _n specifies a different
+ number of buffers to use.
+
+ -B By default, when data is read from a pipe, buffers are
+ allocated automatically as needed. If a large amount
+ of data is read from the pipe, this can cause a large
+ amount of memory to be allocated. The -B option dis-
+ ables this automatic allocation of buffers for pipes,
+ so that only the number of buffers specified by the -b
+ option are used. Warning: use of -B can result in
+ erroneous display, since only the most recently viewed
+ part of the file is kept in memory; any earlier data is
+ lost.
+
+ -c Causes full screen repaints to be painted from the top
+ line down. By default, full screen repaints are done
+ by scrolling from the bottom of the screen.
+
+ -C The -C option is like -c, but the screen is cleared
+ before it is repainted.
+
+ -d The -d option suppresses the error message normally
+ displayed if the terminal is dumb; that is, lacks some
+ important capability, such as the ability to clear the
+ screen or scroll backward. The -d option does not oth-
+ erwise change the behavior of _l_e_s_s on a dumb terminal).
+
+ -Dx_c_o_l_o_r
+ [MS-DOS only] Sets the color of the text displayed. x
+ is a single character which selects the type of text
+ whose color is being set: n=normal, s=standout, d=bold,
+ u=underlined, k=blink. _c_o_l_o_r is a pair of numbers
+ separated by a period. The first number selects the
+
+
+
+ 8
+
+
+
+
+
+
+LESS(1) USER COMMANDS LESS(1)
+
+
+
+ foreground color and the second selects the background
+ color of the text. A single number _N is the same as
+ _N._0.
+
+ -e Causes _l_e_s_s to automatically exit the second time it
+ reaches end-of-file. By default, the only way to exit
+ _l_e_s_s is via the "q" command.
+
+ -E Causes _l_e_s_s to automatically exit the first time it
+ reaches end-of-file.
+
+ -f Forces non-regular files to be opened. (A non-regular
+ file is a directory or a device special file.) Also
+ suppresses the warning message when a binary file is
+ opened. By default, _l_e_s_s will refuse to open non-
+ regular files.
+
+ -g Normally, _l_e_s_s will highlight ALL strings which match
+ the last search command. The -g flag changes this
+ behavior to highlight only the particular string which
+ was found by the last search command. This can cause
+ _l_e_s_s to run somewhat faster than the default.
+
+ -G The -G flag suppresses all highlighting of strings
+ found by search commands.
+
+ -h_n Specifies a maximum number of lines to scroll backward.
+ If it is necessary to scroll backward more than _n
+ lines, the screen is repainted in a forward direction
+ instead. (If the terminal does not have the ability to
+ scroll backward, -h0 is implied.)
+
+ -i Causes searches to ignore case; that is, uppercase and
+ lowercase are considered identical. This option is
+ ignored if any uppercase letters appear in the search
+ pattern; in other words, if a pattern contains upper-
+ case letters, then that search does not ignore case.
+
+ -I Like -i, but searches ignore case even if the pattern
+ contains uppercase letters.
+
+ -j_n Specifies a line on the screen where the "target" line
+ is to be positioned. A target line is the object of a
+ text search, tag search, jump to a line number, jump to
+ a file percentage, or jump to a marked position. The
+ screen line is specified by a number: the top line on
+ the screen is 1, the next is 2, and so on. The number
+ may be negative to specify a line relative to the bot-
+ tom of the screen: the bottom line on the screen is -1,
+ the second to the bottom is -2, and so on. If the -j
+ option is used, searches begin at the line immediately
+ after the target line. For example, if "-j4" is used,
+
+
+
+ 9
+
+
+
+
+
+
+LESS(1) USER COMMANDS LESS(1)
+
+
+
+ the target line is the fourth line on the screen, so
+ searches begin at the fifth line on the screen.
+
+ -k_f_i_l_e_n_a_m_e
+ Causes _l_e_s_s to open and interpret the named file as a
+ _l_e_s_s_k_e_y (1) file. Multiple -k options may be speci-
+ fied. If a file called .less exists in the user's home
+ directory, this file is also used as a _l_e_s_s_k_e_y file.
+
+ -m Causes _l_e_s_s to prompt verbosely (like _m_o_r_e), with the
+ percent into the file. By default, _l_e_s_s prompts with a
+ colon.
+
+ -M Causes _l_e_s_s to prompt even more verbosely than _m_o_r_e.
+
+ -n Suppresses line numbers. The default (to use line
+ numbers) may cause _l_e_s_s to run more slowly in some
+ cases, especially with a very large input file.
+ Suppressing line numbers with the -n flag will avoid
+ this problem. Using line numbers means: the line
+ number will be displayed in the verbose prompt and in
+ the = command, and the v command will pass the current
+ line number to the editor (see also the discussion of
+ LESSEDIT in PROMPTS below).
+
+ -N Causes a line number to be displayed at the beginning
+ of each line in the display.
+
+ -o_f_i_l_e_n_a_m_e
+ Causes _l_e_s_s to copy its input to the named file as it
+ is being viewed. This applies only when the input file
+ is a pipe, not an ordinary file. If the file already
+ exists, _l_e_s_s will ask for confirmation before overwrit-
+ ing it.
+
+ -O_f_i_l_e_n_a_m_e
+ The -O option is like -o, but it will overwrite an
+ existing file without asking for confirmation.
+
+ If no log file has been specified, the -o and -O
+ options can be used from within _l_e_s_s to specify a log
+ file. Without a file name, they will simply report the
+ name of the log file. The "s" command is equivalent to
+ specifying -o from within _l_e_s_s.
+
+ -p_p_a_t_t_e_r_n
+ The -p option on the command line is equivalent to
+ specifying +/_p_a_t_t_e_r_n; that is, it tells _l_e_s_s to start
+ at the first occurrence of _p_a_t_t_e_r_n in the file.
+
+ -P_p_r_o_m_p_t
+ Provides a way to tailor the three prompt styles to
+
+
+
+ 10
+
+
+
+
+
+
+LESS(1) USER COMMANDS LESS(1)
+
+
+
+ your own preference. This option would normally be put
+ in the LESS environment variable, rather than being
+ typed in with each _l_e_s_s command. Such an option must
+ either be the last option in the LESS variable, or be
+ terminated by a dollar sign. -P followed by a string
+ changes the default (short) prompt to that string. -Pm
+ changes the medium (-m) prompt to the string, and -PM
+ changes the long (-M) prompt. Also, -P= changes the
+ message printed by the = command to the given string.
+ All prompt strings consist of a sequence of letters and
+ special escape sequences. See the section on PROMPTS
+ for more details.
+
+ -q Causes moderately "quiet" operation: the terminal bell
+ is not rung if an attempt is made to scroll past the
+ end of the file or before the beginning of the file.
+ If the terminal has a "visual bell", it is used
+ instead. The bell will be rung on certain other
+ errors, such as typing an invalid character. The
+ default is to ring the terminal bell in all such cases.
+
+ -Q Causes totally "quiet" operation: the terminal bell is
+ never rung.
+
+ -r Causes "raw" control characters to be displayed. The
+ default is to display control characters using the
+ caret notation; for example, a control-A (octal 001) is
+ displayed as "^A". Warning: when the -r flag is used,
+ _l_e_s_s cannot keep track of the actual appearance of the
+ screen (since this depends on how the screen responds
+ to each type of control character). Thus, various
+ display problems may result, such as long lines being
+ split in the wrong place.
+
+ -s Causes consecutive blank lines to be squeezed into a
+ single blank line. This is useful when viewing _n_r_o_f_f
+ output.
+
+ -S Causes lines longer than the screen width to be chopped
+ rather than folded. That is, the remainder of a long
+ line is simply discarded. The default is to fold long
+ lines; that is, display the remainder on the next line.
+
+ -t_t_a_g
+ The -t option, followed immediately by a TAG, will edit
+ the file containing that tag. For this to work, there
+ must be a file called "tags" in the current directory,
+ which was previously built by the _c_t_a_g_s (1) command.
+ This option may also be specified from within _l_e_s_s
+ (using the - command) as a way of examining a new file.
+ The command ":t" is equivalent to specifying -t from
+ within _l_e_s_s.
+
+
+
+ 11
+
+
+
+
+
+
+LESS(1) USER COMMANDS LESS(1)
+
+
+
+ -T_t_a_g_s_f_i_l_e
+ Specifies a tags file to be used instead of "tags".
+
+ -u Causes backspaces and carriage returns to be treated as
+ printable characters; that is, they are sent to the
+ terminal when they appear in the input.
+
+ -U Causes backspaces and carriage returns to be treated as
+ control characters; that is, they are handled as speci-
+ fied by the -r option.
+
+ By default, if neither -u nor -U is given, backspaces
+ which appear adjacent to an underscore character are
+ treated specially: the underlined text is displayed
+ using the terminal's hardware underlining capability.
+ Also, backspaces which appear between two identical
+ characters are treated specially: the overstruck text
+ is printed using the terminal's hardware boldface capa-
+ bility. Other backspaces are deleted, along with the
+ preceding character. Carriage returns immediately fol-
+ lowed by a newline are deleted. Other carriage returns
+ are handled as specified by the -r option. Text which
+ is overstruck or underlined can be searched for if nei-
+ ther -u nor -U is in effect.
+
+ -V Displays the version number of _l_e_s_s.
+
+ -w Causes blank lines to be used to represent lines past
+ the end of the file. By default, a tilde character (~)
+ is used.
+
+ -x_n Sets tab stops every _n positions. The default for _n is
+ 8.
+
+ -X Disables sending the termcap initialization and deini-
+ tialization strings to the terminal. This is sometimes
+ desirable if the deinitialization string does something
+ unnecessary, like clearing the screen.
+
+ -y_n Specifies a maximum number of lines to scroll forward.
+ If it is necessary to scroll forward more than _n lines,
+ the screen is repainted instead. The -c or -C option
+ may be used to repaint from the top of the screen if
+ desired. By default, any forward movement causes
+ scrolling.
+
+ -[z]_n
+ Changes the default scrolling window size to _n lines.
+ The default is one screenful. The z and w commands can
+ also be used to change the window size. The "z" may be
+ omitted for compatibility with _m_o_r_e. If the number _n is
+ negative, it indicates _n lines less than the current
+
+
+
+ 12
+
+
+
+
+
+
+LESS(1) USER COMMANDS LESS(1)
+
+
+
+ screen size. For example, if the screen is 24 lines,
+ -_z-_4 sets the scrolling window to 20 lines. If the
+ screen is resized to 40 lines, the scrolling window
+ automatically changes to 36 lines.
+
+ + If a command line option begins with +, the remainder
+ of that option is taken to be an initial command to
+ _l_e_s_s. For example, +G tells _l_e_s_s to start at the end of
+ the file rather than the beginning, and +/xyz tells it
+ to start at the first occurrence of "xyz" in the file.
+ As a special case, +<number> acts like +<number>g; that
+ is, it starts the display at the specified line number
+ (however, see the caveat under the "g" command above).
+ If the option starts with ++, the initial command
+ applies to every file being viewed, not just the first
+ one. The + command described previously may also be
+ used to set (or change) an initial command for every
+ file.
+
+
+LINE EDITING
+ When entering command line at the bottom of the screen (for
+ example, a filename for the :e command, or the pattern for a
+ search command), certain keys can be used to manipulate the
+ command line. Most commands have an alternate form in [
+ brackets ] which can be used if a key does not exist on a
+ particular keyboard. (The bracketed forms do not work in the
+ MS-DOS version.) Any of these special keys may be entered
+ literally by preceding it with the "literal" character,
+ either ^V or ^A. A backslash itself may also be entered
+ literally by entering two backslashes.
+
+ LEFTARROW [ ESC-h ]
+ Move the cursor one space to the left.
+
+ RIGHTARROW [ ESC-l ]
+ Move the cursor one space to the right.
+
+ ^LEFTARROW [ ESC-b or ESC-LEFTARROW ]
+ (That is, CONTROL and LEFTARROW simultaneously.) Move
+ the cursor one word to the left.
+
+ ^RIGHTARROW [ ESC-w or ESC-RIGHTARROW ]
+ (That is, CONTROL and RIGHTARROW simultaneously.) Move
+ the cursor one word to the right.
+
+ HOME [ ESC-0 ]
+ Move the cursor to the beginning of the line.
+
+ END [ ESC-$ ]
+ Move the cursor to the end of the line.
+
+
+
+
+ 13
+
+
+
+
+
+
+LESS(1) USER COMMANDS LESS(1)
+
+
+
+ BACKSPACE
+ Delete the character to the left of the cursor, or can-
+ cel the command if the command line is empty.
+
+ DELETE or [ ESC-x ]
+ Delete the character under the cursor.
+
+ ^BACKSPACE [ ESC-BACKSPACE ]
+ (That is, CONTROL and BACKSPACE simultaneously.) Delete
+ the word to the left of the cursor.
+
+ ^DELETE [ ESC-X or ESC-DELETE ]
+ (That is, CONTROL and DELETE simultaneously.) Delete
+ the word under the cursor.
+
+ UPARROW [ ESC-k ]
+ Retrieve the previous command line.
+
+ DOWNARROW [ ESC-j ]
+ Retrieve the next command line.
+
+ TAB Complete the partial filename to the left of the cur-
+ sor. If it matches more than one filename, the first
+ match is entered into the command line. Repeated TABs
+ will cycle thru the other matching filenames.
+
+ BACKTAB [ ESC-TAB ]
+ Like, TAB, but cycles in the reverse direction thru the
+ matching filenames.
+
+ ^L Complete the partial filename to the left of the cur-
+ sor. If it matches more than one filename, all matches
+ are entered into the command line (if they fit).
+
+ ^U (Unix) or ESC (MS-DOS)
+ Delete the entire command line, or cancel the command
+ if the command line is empty. If you have changed your
+ line-kill character in Unix to something other than ^U,
+ that character is used instead of ^U.
+
+
+KEY BINDINGS
+ You may define your own _l_e_s_s commands by using the program
+ _l_e_s_s_k_e_y (1) to create a file called ".less" in your home
+ directory. This file specifies a set of command keys and an
+ action associated with each key. You may also use _l_e_s_s_k_e_y
+ to change the line-editing keys (see LINE EDITING). See the
+ _l_e_s_s_k_e_y manual page for more details.
+
+
+INPUT PREPROCESSOR
+ You may define an "input preprocessor" for _l_e_s_s. Before _l_e_s_s
+
+
+
+ 14
+
+
+
+
+
+
+LESS(1) USER COMMANDS LESS(1)
+
+
+
+ opens a file, it first gives your input preprocessor a
+ chance to modify the way the contents of the file are
+ displayed. An input preprocessor is simply an executable
+ program (or shell script), which writes the contents of the
+ file to a different file, called the replacement file. The
+ contents of the replacement file are then displayed in place
+ of the contents of the original file. However, it will
+ appear to the user as if the original file is opened; that
+ is, _l_e_s_s will display the original filename as the name of
+ the current file.
+
+ An input preprocessor receives one command line argument,
+ the original filename, as entered by the user. It should
+ create the replacement file, and when finished, print the
+ name of the replacement file to its standard output. If the
+ input preprocessor does not output a replacement filename,
+ _l_e_s_s uses the original file, as normal. The input prepro-
+ cessor is not called when viewing standard input. To set up
+ an input preprocessor, set the LESSOPEN environment variable
+ to a command line which will invoke your input preprocessor.
+ This command line should include one occurrence of the
+ string "%s", which will be replaced by the filename when the
+ input preprocessor command is invoked.
+
+ When _l_e_s_s closes a file opened in such a way, it will call
+ another program, called the input postprocessor, which may
+ perform any desired clean-up action (such as deleting the
+ replacement file created by LESSOPEN). This program
+ receives two command line arguments, the original filename
+ as entered by the user, and the name of the replacement
+ file. To set up an input postprocessor, set the LESSCLOSE
+ environment variable to a command line which will invoke
+ your input postprocessor. It may include two occurrences of
+ the string "%s"; the first is replaced with the original
+ name of the file and the second with the name of the
+ replacement file, which was output by LESSOPEN.
+
+ For example, on many Unix systems, these two scripts will
+ allow you to keep files in compressed format, but still let
+ _l_e_s_s view them directly:
+
+ lessopen.sh:
+ #! /bin/sh
+ case "$1" in
+ *.Z) uncompress -c $1 >/tmp/less.$$ 2>/dev/null
+ if [ -s /tmp/less.$$ ]; then
+ echo /tmp/less.$$
+ else
+ rm -f /tmp/less.$$
+ fi
+ ;;
+ esac
+
+
+
+ 15
+
+
+
+
+
+
+LESS(1) USER COMMANDS LESS(1)
+
+
+
+ lessclose.sh:
+ #! /bin/sh
+ rm $2
+
+ To use these scripts, put them both where they can be exe-
+ cuted and set LESSOPEN="lessopen.sh %s", and
+ LESSCLOSE="lessclose.sh %s %s". More complex LESSOPEN and
+ LESSCLOSE scripts may be written to accept other types of
+ compressed files, and so on.
+
+ It is also possible to set up an input preprocessor to pipe
+ the file data directly to _l_e_s_s, rather than putting the data
+ into a replacement file. This avoids the need to decompress
+ the entire file before starting to view it. An input
+ preprocessor that works this way is called an input pipe.
+ An input pipe, instead of writing the name of a replacement
+ file on its standard output, writes the entire contents of
+ the replacement file on its standard output. If the input
+ pipe does not write any characters on its standard output,
+ then there is no replacement file and _l_e_s_s uses the original
+ file, as normal. To use an input pipe, make the first char-
+ acter in the LESSOPEN environment variable a vertical bar
+ (|) to signify that the input preprocessor is an input pipe.
+
+ For example, on many Unix systems, this script will work
+ like the previous example scripts:
+
+ lesspipe.sh:
+ !# /bin/sh
+ case "$1" in
+ *.Z) uncompress -c $1 2>/dev/null
+ ;;
+ esac
+
+ To use this script, put it where it can be executed and set
+ LESSOPEN="|lesspipe.sh %s". When an input pipe is used, a
+ LESSCLOSE postprocessor can be used, but it is usually not
+ necessary since there is no replacement file to clean up.
+ In this case, the replacement file name passed to the LESS-
+ CLOSE postprocessor is "-".
+
+
+NATIONAL CHARACTER SETS
+ There are three types of characters in the input file:
+
+ normal characters
+ can be displayed directly to the screen.
+
+ control characters
+ should not be displayed directly, but are expected to
+ be found in ordinary text files (such as backspace and
+ tab).
+
+
+
+ 16
+
+
+
+
+
+
+LESS(1) USER COMMANDS LESS(1)
+
+
+
+ binary characters
+ should not be displayed directly and are not expected
+ to be found in text files.
+
+ A "character set" is simply a description of which charac-
+ ters are to be considered normal, control, and binary. The
+ LESSCHARSET environment variable may be used to select a
+ character set. Possible values for LESSCHARSET are:
+
+ ascii
+ The default character set. BS, TAB, NL, CR, and
+ formfeed are control characters, all chars with values
+ between 127 and 255 are binary, and all others are nor-
+ mal.
+
+ latin1
+ Selects the ISO 8859/1 character set. latin-1 is the
+ same as ASCII, except characters between 161 and 255
+ are treated as normal characters.
+
+ dos Selects a character set appropriate for MS-DOS.
+
+ koi8-r
+ Selects a Russian character set.
+
+ next Selects a character set appropriate for NeXT computers.
+
+ In special cases, it may be desired to tailor _l_e_s_s to use a
+ character set other than the ones definable by LESSCHARSET.
+ In this case, the environment variable LESSCHARDEF can be
+ used to define a character set. It should be set to a
+ string where each character in the string represents one
+ character in the character set. The character "." is used
+ for a normal character, "c" for control, and "b" for binary.
+ A decimal number may be used for repetition. For example,
+ "bccc4b." would mean character 0 is binary, 1, 2 and 3 are
+ control, 4, 5, 6 and 7 are binary, and 8 is normal. All
+ characters after the last are taken to be the same as the
+ last, so characters 9 through 255 would be normal. (This is
+ an example, and does not necessarily represent any real
+ character set.)
+
+ This table shows the value of LESSCHARDEF which is
+ equivalent to each of the possible values for LESSCHARSET:
+
+ ascii 8bcccbcc18b95.b
+ latin1 8bcccbcc18b95.33b.
+ dos 8bcccbcc12bc5b95.b.
+ koi8-r 8bcccbcc18b95.b128.
+ next 8bcccbcc18b95.bb125.bb
+
+
+
+
+
+ 17
+
+
+
+
+
+
+LESS(1) USER COMMANDS LESS(1)
+
+
+
+ If neither LESSCHARSET nor LESSCHARDEF is set, but your sys-
+ tem supports the _s_e_t_l_o_c_a_l_e interface, _l_e_s_s will use setlo-
+ cale to determine the character set. setlocale is con-
+ trolled by setting the LANG or LC_CTYPE environment vari-
+ ables.
+
+ Control and binary characters are displayed in standout
+ (reverse video). Each such character is displayed in caret
+ notation if possible (e.g. ^A for control-A). Caret nota-
+ tion is used only if inverting the 0100 bit results in a
+ normal printable character. Otherwise, the character is
+ displayed as a hex number in angle brackets. This format
+ can be changed by setting the LESSBINFMT environment vari-
+ able. LESSBINFMT may begin with a "*" and one character to
+ select the display attribute: "*k" is blinking, "*d" is
+ bold, "*u" is underlined, "*s" is standout. If LESSBINFMT
+ does not begin with a "*", normal attribute is assumed. The
+ remainder of LESSBINFMT is a string which may include one
+ printf-style escape sequence (a % followed by x, X, o, d,
+ etc.). For example, if LESSBINFMT is "*u[%x]", binary char-
+ acters are displayed in underlined hexadecimal surrounded by
+ brackets. The default if no LESSBINFMT is specified is
+ "*d<%X>".
+
+
+PROMPTS
+ The -P option allows you to tailor the prompt to your
+ preference. The string given to the -P option replaces the
+ specified prompt string. Certain characters in the string
+ are interpreted specially. The prompt mechanism is rather
+ complicated to provide flexibility, but the ordinary user
+ need not understand the details of constructing personalized
+ prompt strings.
+
+ A percent sign followed by a single character is expanded
+ according to what the following character is:
+
+ %b_X Replaced by the byte offset into the current input
+ file. The b is followed by a single character (shown
+ as _X above) which specifies the line whose byte offset
+ is to be used. If the character is a "t", the byte
+ offset of the top line in the display is used, an "m"
+ means use the middle line, a "b" means use the bottom
+ line, a "B" means use the line just after the bottom
+ line, and a "j" means use the "target" line, as speci-
+ fied by the -j option.
+
+ %B Replaced by the size of the current input file.
+
+ %E Replaced by the name of the editor (from the VISUAL
+ environment variable, or the EDITOR environment vari-
+ able if VISUAL is not defined). See the discussion of
+
+
+
+ 18
+
+
+
+
+
+
+LESS(1) USER COMMANDS LESS(1)
+
+
+
+ the LESSEDIT feature below.
+
+ %f Replaced by the name of the current input file.
+
+ %i Replaced by the index of the current file in the list
+ of input files.
+
+ %l_X Replaced by the line number of a line in the input
+ file. The line to be used is determined by the _X, as
+ with the %b option.
+
+ %L Replaced by the line number of the last line in the
+ input file.
+
+ %m Replaced by the total number of input files.
+
+ %p_X Replaced by the percent into the current input file.
+ The line used is determined by the _X as with the %b
+ option.
+
+ %s Same as %B.
+
+ %t Causes any trailing spaces to be removed. Usually used
+ at the end of the string, but may appear anywhere.
+
+ %x Replaced by the name of the next input file in the
+ list.
+
+ If any item is unknown (for example, the file size if input
+ is a pipe), a question mark is printed instead.
+
+ The format of the prompt string can be changed depending on
+ certain conditions. A question mark followed by a single
+ character acts like an "IF": depending on the following
+ character, a condition is evaluated. If the condition is
+ true, any characters following the question mark and condi-
+ tion character, up to a period, are included in the prompt.
+ If the condition is false, such characters are not included.
+ A colon appearing between the question mark and the period
+ can be used to establish an "ELSE": any characters between
+ the colon and the period are included in the string if and
+ only if the IF condition is false. Condition characters
+ (which follow a question mark) may be:
+
+ ?a True if any characters have been included in the prompt
+ so far.
+
+ ?b_X True if the byte offset of the specified line is known.
+
+ ?B True if the size of current input file is known.
+
+ ?e True if at end-of-file.
+
+
+
+ 19
+
+
+
+
+
+
+LESS(1) USER COMMANDS LESS(1)
+
+
+
+ ?f True if there is an input filename (that is, if input
+ is not a pipe).
+
+ ?l_X True if the line number of the specified line is known.
+
+ ?L True if the line number of the last line in the file is
+ known.
+
+ ?m True if there is more than one input file.
+
+ ?n True if this is the first prompt in a new input file.
+
+ ?p_X True if the percent into the current input file of the
+ specified line is known.
+
+ ?s Same as "?B".
+
+ ?x True if there is a next input file (that is, if the
+ current input file is not the last one).
+
+ Any characters other than the special ones (question mark,
+ colon, period, percent, and backslash) become literally part
+ of the prompt. Any of the special characters may be
+ included in the prompt literally by preceding it with a
+ backslash.
+
+ Some examples:
+
+ ?f%f:Standard input.
+
+ This prompt prints the filename, if known; otherwise the
+ string "Standard input".
+
+ ?f%f .?ltLine %lt:?pt%pt\%:?btByte %bt:-...
+
+ This prompt would print the filename, if known. The
+ filename is followed by the line number, if known, otherwise
+ the percent if known, otherwise the byte offset if known.
+ Otherwise, a dash is printed. Notice how each question mark
+ has a matching period, and how the % after the %pt is
+ included literally by escaping it with a backslash.
+
+ ?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\: %x..%t
+
+ This prints the filename if this is the first prompt in a
+ file, followed by the "file N of N" message if there is more
+ than one input file. Then, if we are at end-of-file, the
+ string "(END)" is printed followed by the name of the next
+ file, if there is one. Finally, any trailing spaces are
+ truncated. This is the default prompt. For reference, here
+ are the defaults for the other two prompts (-m and -M
+ respectively). Each is broken into two lines here for
+
+
+
+ 20
+
+
+
+
+
+
+LESS(1) USER COMMANDS LESS(1)
+
+
+
+ readability only.
+
+ ?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\: %x.:
+ ?pB%pB\%:byte %bB?s/%s...%t
+
+ ?f%f .?n?m(file %i of %m) ..?ltline %lt?L/%L. :byte %bB?s/%s. .
+ ?e(END) ?x- Next\: %x.:?pB%pB\%..%t
+
+ And here is the default message produced by the = command:
+
+ ?f%f .?m(file %i of %m) .?ltline %lt?L/%L. .
+ byte %bB?s/%s. ?e(END) :?pB%pB\%..%t
+
+ The prompt expansion features are also used for another pur-
+ pose: if an environment variable LESSEDIT is defined, it is
+ used as the command to be executed when the v command is
+ invoked. The LESSEDIT string is expanded in the same way as
+ the prompt strings. The default value for LESSEDIT is:
+
+ %E ?lm+%lm. %f
+
+ Note that this expands to the editor name, followed by a +
+ and the line number, followed by the file name. If your
+ editor does not accept the "+linenumber" syntax, or has
+ other differences in invocation syntax, the LESSEDIT vari-
+ able can be changed to modify this default.
+
+
+ENVIRONMENT VARIABLES
+ COLUMNS
+ Sets the number of columns on the screen. Takes pre-
+ cedence over the number of columns specified by the
+ TERM variable. (But if you have a windowing system
+ which supports TIOCGWINSZ or WIOCGETD, the window
+ system's idea of the screen size takes precedence over
+ the LINES and COLUMNS environment variables.)
+
+ EDITOR
+ The name of the editor (used for the v command).
+
+ HOME Name of the user's home directory (used to find a .less
+ file).
+
+ LANG Language for determining the character set.
+
+ LC_CTYPE
+ Language for determining the character set.
+
+ LESS Flags which are passed to _l_e_s_s automatically.
+
+ LESSBINFMT
+ Format for displaying non-printable, non-control
+
+
+
+ 21
+
+
+
+
+
+
+LESS(1) USER COMMANDS LESS(1)
+
+
+
+ characters.
+
+ LESSCHARDEF
+ Defines a character set.
+
+ LESSCHARSET
+ Selects a predefined character set.
+
+ LESSCLOSE
+ Command line to invoke the (optional) input-
+ postprocessor.
+
+ LESSEDIT
+ Editor prototype string (used for the v command). See
+ discussion under PROMPTS.
+
+ LESSHELP
+ Name of the help file.
+
+ LESSOPEN
+ Command line to invoke the (optional) input-
+ preprocessor.
+
+ LINES
+ Sets the number of lines on the screen. Takes pre-
+ cedence over the number of lines specified by the TERM
+ variable.
+
+ SHELL
+ The shell used to execute the ! command, as well as to
+ expand filenames.
+
+ TERM The type of terminal on which _l_e_s_s is being run.
+
+ VISUAL
+ The name of the editor (used for the v command).
+
+
+SEE ALSO
+ lesskey(1)
+
+
+WARNINGS
+ The = command and prompts (unless changed by -P) report the
+ line number of the line at the top of the screen, but the
+ byte and percent of the line at the bottom of the screen.
+
+ If the :e command is used to name more than one file, and
+ one of the named files has been viewed previously, the new
+ files may be entered into the list in an unexpected order.
+
+
+
+
+
+ 22
+
+
+
+
+
+
+LESS(1) USER COMMANDS LESS(1)
+
+
+
+ On certain older terminals (the so-called "magic cookie"
+ terminals), search highlighting will cause an erroneous
+ display. On such terminals, search highlighting is disabled
+ by default to avoid possible problems.
+
+ In certain cases, when search highlighting is enabled and a
+ search pattern begins with a ^, more text than the matching
+ string may be highlighted.
+
+
+
+COPYRIGHT
+ Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 23
+
+
+
diff --git a/usr.bin/less/less.nro b/usr.bin/less/less.nro
new file mode 100644
index 00000000000..e6be297fed7
--- /dev/null
+++ b/usr.bin/less/less.nro
@@ -0,0 +1,1235 @@
+.TH LESS 1
+.SH NAME
+less \- opposite of more
+.SH SYNOPSIS
+.B "less -?"
+.br
+.B "less -V"
+.br
+.B "less [-[+]aBcCdeEfgGiImMnNqQrsSuUVwX]"
+.br
+.B " [-b \fIbufs\fP] [-h \fIlines\fP] [-j \fIline\fP] [-k \fIkeyfile\fP]"
+.br
+.B " [-{oO} \fIlogfile\fP] [-p \fIpattern\fP] [-P \fIprompt\fP] [-t \fItag\fP]"
+.br
+.B " [-T \fItagsfile\fP] [-x \fItab\fP] [-y \fIlines\fP] [-[z] \fIlines\fP]"
+.br
+.B " [+[+]\fIcmd\fP] [\fIfilename\fP]..."
+
+.SH DESCRIPTION
+.I Less
+is a program similar to
+.I more
+(1), but which allows backward movement
+in the file as well as forward movement.
+Also,
+.I less
+does not have to read the entire input file before starting,
+so with large input files it starts up faster than text editors like
+.I vi
+(1).
+.I Less
+uses termcap (or terminfo on some systems),
+so it can run on a variety of terminals.
+There is even limited support for hardcopy terminals.
+(On a hardcopy terminal, lines which should be printed at the top
+of the screen are prefixed with a caret.)
+.PP
+Commands are based on both
+.I more
+and
+.I vi.
+Commands may be preceded by a decimal number,
+called N in the descriptions below.
+The number is used by some commands, as indicated.
+
+.SH COMMANDS
+In the following descriptions, ^X means control-X.
+ESC stands for the ESCAPE key; for example ESC-v means the
+two character sequence "ESCAPE", then "v".
+.IP "h or H"
+Help: display a summary of these commands.
+If you forget all the other commands, remember this one.
+.PP
+.IP "SPACE or ^V or f or ^F"
+Scroll forward N lines, default one window (see option -z below).
+If N is more than the screen size, only the final screenful is displayed.
+Warning: some systems use ^V as a special literalization character.
+.PP
+.IP "z"
+Like SPACE, but if N is specified, it becomes the new window size.
+.PP
+.IP "RETURN or ^N or e or ^E or j or ^J"
+Scroll forward N lines, default 1.
+The entire N lines are displayed, even if N is more than the screen size.
+.PP
+.IP "d or ^D"
+Scroll forward N lines, default one half of the screen size.
+If N is specified, it becomes the new default for
+subsequent d and u commands.
+.PP
+.IP "b or ^B or ESC-v"
+Scroll backward N lines, default one window (see option -z below).
+If N is more than the screen size, only the final screenful is displayed.
+.PP
+.IP "w"
+Like ESC-v, but if N is specified, it becomes the new window size.
+.PP
+.IP "y or ^Y or ^P or k or ^K"
+Scroll backward N lines, default 1.
+The entire N lines are displayed, even if N is more than the screen size.
+Warning: some systems use ^Y as a special job control character.
+.PP
+.IP "u or ^U"
+Scroll backward N lines, default one half of the screen size.
+If N is specified, it becomes the new default for
+subsequent d and u commands.
+.PP
+.IP "r or ^R or ^L"
+Repaint the screen.
+.PP
+.IP R
+Repaint the screen, discarding any buffered input.
+Useful if the file is changing while it is being viewed.
+.PP
+.IP "F"
+Scroll forward, and keep trying to read when the
+end of file is reached.
+Normally this command would be used when already at the end of the file.
+It is a way to monitor the tail of a file which is growing
+while it is being viewed.
+(The behavior is similar to the "tail -f" command.)
+.PP
+.IP "g or < or ESC-<"
+Go to line N in the file, default 1 (beginning of file).
+(Warning: this may be slow if N is large.)
+.PP
+.IP "G or > or ESC->"
+Go to line N in the file, default the end of the file.
+(Warning: this may be slow if N is large,
+or if N is not specified and
+standard input, rather than a file, is being read.)
+.PP
+.IP "p or %"
+Go to a position N percent into the file.
+N should be between 0 and 100.
+(This works if standard input is being read, but only if
+.I less
+has already read to the end of the file.
+It is always fast, but not always useful.)
+.PP
+.IP "{"
+If a left curly bracket appears in the top line displayed
+on the screen,
+the { command will go to the matching right curly bracket.
+The matching right curly bracket is positioned on the bottom
+line of the screen.
+If there is more than one left curly bracket on the top line,
+a number N may be used to specify the N-th bracket on the line.
+.PP
+.IP "}"
+If a right curly bracket appears in the bottom line displayed
+on the screen,
+the } command will go to the matching left curly bracket.
+The matching left curly bracket is positioned on the top
+line of the screen.
+If there is more than one right curly bracket on the top line,
+a number N may be used to specify the N-th bracket on the line.
+.PP
+.IP "("
+Like {, but applies to parentheses rather than curly brackets.
+.PP
+.IP ")"
+Like }, but applies to parentheses rather than curly brackets.
+.PP
+.IP "["
+Like {, but applies to square brackets rather than curly brackets.
+.PP
+.IP "]"
+Like }, but applies to square brackets rather than curly brackets.
+.PP
+.IP "ESC-^F"
+Followed by two characters,
+acts like {, but uses the two characters as open and close brackets,
+respectively.
+For example, "ESC ^F < >" could be used to
+go forward to the > which matches the < in the top displayed line.
+.IP "ESC-^B"
+Followed by two characters,
+acts like }, but uses the two characters as open and close brackets,
+respectively.
+For example, "ESC ^B < >" could be used to
+go backward to the < which matches the > in the bottom displayed line.
+.IP m
+Followed by any lowercase letter,
+marks the current position with that letter.
+.PP
+.IP "'"
+(Single quote.)
+Followed by any lowercase letter, returns to the position which
+was previously marked with that letter.
+Followed by another single quote, returns to the position at
+which the last "large" movement command was executed.
+Followed by a ^ or $, jumps to the beginning or end of the
+file respectively.
+Marks are preserved when a new file is examined,
+so the ' command can be used to switch between input files.
+.PP
+.IP "^X^X"
+Same as single quote.
+.PP
+.IP /pattern
+Search forward in the file for the N-th line containing the pattern.
+N defaults to 1.
+The pattern is a regular expression, as recognized by
+.I ed.
+The search starts at the second line displayed
+(but see the -a and -j options, which change this).
+.sp
+Certain characters are special
+if entered at the beginning of the pattern;
+they modify the type of search rather than become part of the pattern:
+.RS
+.IP !
+Search for lines which do NOT match the pattern.
+.IP *
+Search multiple files.
+That is, if the search reaches the end of the current file
+without finding a match,
+the search continues in the next file in the command line list.
+.IP @
+Begin the search at the first line of the first file
+in the command line list,
+regardless of what is currently displayed on the screen
+or the settings of the -a or -j options.
+.RE
+.PP
+.IP ?pattern
+Search backward in the file for the N-th line containing the pattern.
+The search starts at the line immediately before the top line displayed.
+.sp
+Certain characters are special as in the / command:
+.RS
+.IP !
+Search for lines which do NOT match the pattern.
+.IP *
+Search multiple files.
+That is, if the search reaches the beginning of the current file
+without finding a match,
+the search continues in the previous file in the command line list.
+.IP @
+Begin the search at the last line of the last file
+in the command line list,
+regardless of what is currently displayed on the screen
+or the settings of the -a or -j options.
+.RE
+.PP
+.IP "ESC-/pattern"
+Same as "/*".
+.PP
+.IP "ESC-?pattern"
+Same as "?*".
+.PP
+.IP n
+Repeat previous search, for N-th line containing the last pattern.
+If the previous search was modified by !, the search is made for the
+N-th line NOT containing the pattern.
+If the previous search was modified by *, the search continues
+in the next (or previous) file if not satisfied in the current file.
+There is no effect if the previous search was modified by @.
+.PP
+.IP N
+Repeat previous search, but in the reverse direction.
+.PP
+.IP "ESC-n"
+Repeat previous search, but crossing file boundaries.
+The effect is as if the previous search were modified by *.
+.PP
+.IP "ESC-N"
+Repeat previous search, but in the reverse direction
+and crossing file boundaries.
+.PP
+.IP "ESC-u"
+Undo search highlighting.
+Turn off highlighting of strings matching the current search pattern.
+If highlighting is already off because of a previous ESC-u command,
+turn highlighting back on.
+Any search command will also turn highlighting back on.
+(Highlighting can also be disabled by toggling the -G flag;
+in that case search commands do not turn highlighting back on.)
+.PP
+.IP ":e [filename]"
+Examine a new file.
+If the filename is missing, the "current" file (see the :n and :p commands
+below) from the list of files in the command line is re-examined.
+A percent sign (%) in the filename is replaced by the name of the
+current file.
+A pound sign (#) is replaced by the name of the previously examined file.
+The filename is inserted into the command line list of files
+so that it can be seen by subsequent :n and :p commands.
+If the filename consists of several files, they are all inserted into
+the list of files and the first one is examined.
+.PP
+.IP "^X^V or E"
+Same as :e.
+Warning: some systems use ^V as a special literalization character.
+.PP
+.IP ":n"
+Examine the next file (from the list of files given in the command line).
+If a number N is specified, the N-th next file is examined.
+.PP
+.IP ":p"
+Examine the previous file in the command line list.
+If a number N is specified, the N-th previous file is examined.
+.PP
+.IP ":x"
+Examine the first file in the command line list.
+If a number N is specified, the N-th file in the list is examined.
+.PP
+.IP "= or ^G or :f"
+Prints some information about the file being viewed,
+including its name
+and the line number and byte offset of the bottom line being displayed.
+If possible, it also prints the length of the file,
+the number of lines in the file
+and the percent of the file above the last displayed line.
+.PP
+.IP \-
+Followed by one of the command line option letters (see below),
+this will change the setting of that option
+and print a message describing the new setting.
+If the option letter has a numeric value (such as -b or -h),
+or a string value (such as -P or -t),
+a new value may be entered after the option letter.
+If no new value is entered, a message describing
+the current setting is printed and nothing is changed.
+.PP
+.IP \-+
+Followed by one of the command line option letters (see below),
+this will reset the option to its default setting
+and print a message describing the new setting.
+(The "\-+\fIX\fP" command does the same thing
+as "\-+\fIX\fP" on the command line.)
+This does not work for string-valued options.
+.PP
+.IP \-\-
+Followed by one of the command line option letters (see below),
+this will reset the option to the "opposite" of its default setting
+and print a message describing the new setting.
+(The "\-\-\fIX\fP" command does the same thing
+as "\-\fIX\fP" on the command line.)
+This does not work for numeric or string-valued options.
+.PP
+.IP _
+(Underscore.)
+Followed by one of the command line option letters (see below),
+this will print a message describing the current setting of that option.
+The setting of the option is not changed.
+.PP
+.IP +cmd
+Causes the specified cmd to be executed each time a new file is examined.
+For example, +G causes
+.I less
+to initially display each file starting at the end
+rather than the beginning.
+.PP
+.IP V
+Prints the version number of
+.I less
+being run.
+.PP
+.IP "q or :q or :Q or ZZ"
+Exits
+.I less.
+.PP
+The following
+three
+commands may or may not be valid, depending on your particular installation.
+.PP
+.IP v
+Invokes an editor to edit the current file being viewed.
+The editor is taken from the environment variable VISUAL if defined,
+or EDITOR if VISUAL is not defined,
+or defaults to "vi" if neither VISUAL nor EDITOR is defined.
+See also the discussion of LESSEDIT under the section on PROMPTS below.
+.PP
+.IP "! shell-command"
+Invokes a shell to run the shell-command given.
+A percent sign (%) in the command is replaced by the name of the
+current file.
+A pound sign (#) is replaced by the name of the previously examined file.
+"!!" repeats the last shell command.
+"!" with no shell command simply invokes a shell.
+In all cases, the shell is taken from the environment variable SHELL,
+or defaults to "sh".
+.PP
+.IP "| <m> shell-command"
+<m> represents any mark letter.
+Pipes a section of the input file to the given shell command.
+The section of the file to be piped is between the first line on
+the current screen and the position marked by the letter.
+<m> may also be ^ or $ to indicate beginning or end of file respectively.
+If <m> is . or newline, the current screen is piped.
+.PP
+.SH OPTIONS
+Command line options are described below.
+Most options may be changed while
+.I less
+is running, via the "\-" command.
+.PP
+Options are also taken from the environment variable "LESS".
+For example,
+to avoid typing "less -options ..." each time
+.I less
+is invoked, you might tell
+.I csh:
+.sp
+setenv LESS "-options"
+.sp
+or if you use
+.I sh:
+.sp
+LESS="-options"; export LESS
+.sp
+The environment variable is parsed before the command line,
+so command line options override the LESS environment variable.
+If an option appears in the LESS variable, it can be reset
+to its default on the command line by beginning the command
+line option with "-+".
+.sp
+A dollar sign ($) may be used to signal the end of an option string.
+This is important only for options like -P which take a
+following string.
+.IP -?
+This option displays a summary of the commands accepted by
+.I less
+(the same as the h command).
+If this option is given, all other options are ignored, and
+.I less
+exits after the help screen is viewed.
+(Depending on how your shell interprets the question mark,
+it may be necessary to quote the question mark, thus: "-\\?".)
+.IP -a
+Causes searches to start after the last line
+displayed on the screen,
+thus skipping all lines displayed on the screen.
+By default, searches start at the second line on the screen
+(or after the last found line; see the -j option).
+.IP -b\fIn\fP
+Specifies the number of buffers
+.I less
+will use for each file.
+Buffers are 1K, and by default 10 buffers are used for each file
+(except if the file is a pipe; see the -B option).
+The number \fIn\fP specifies a different number of buffers to use.
+.IP -B
+By default, when data is read from a pipe,
+buffers are allocated automatically as needed.
+If a large amount of data is read from the pipe, this can cause
+a large amount of memory to be allocated.
+The -B option disables this automatic allocation of buffers for pipes,
+so that only the number of buffers specified by the -b option are used.
+Warning: use of -B can result in erroneous display, since only the
+most recently viewed part of the file is kept in memory;
+any earlier data is lost.
+.IP -c
+Causes full screen repaints to be painted from the top line down.
+By default,
+full screen repaints are done by scrolling from the bottom of the screen.
+.IP -C
+The -C option is like -c, but the screen is cleared before it is repainted.
+.IP -d
+The -d option suppresses the error message
+normally displayed if the terminal is dumb;
+that is, lacks some important capability,
+such as the ability to clear the screen or scroll backward.
+The -d option does not otherwise change the behavior of
+.I less
+on a dumb terminal).
+.IP -D\fBx\fP\fIcolor\fP
+[MS-DOS only]
+Sets the color of the text displayed.
+\fBx\fP is a single character which selects the type of text whose color is
+being set: n=normal, s=standout, d=bold, u=underlined, k=blink.
+\fIcolor\fP is a pair of numbers separated by a period.
+The first number selects the foreground color and the second selects
+the background color of the text.
+A single number \fIN\fP is the same as \fIN.0\fP.
+.IP -e
+Causes
+.I less
+to automatically exit
+the second time it reaches end-of-file.
+By default, the only way to exit
+.I less
+is via the "q" command.
+.IP -E
+Causes
+.I less
+to automatically exit the first time it reaches end-of-file.
+.IP -f
+Forces non-regular files to be opened.
+(A non-regular file is a directory or a device special file.)
+Also suppresses the warning message when a binary file is opened.
+By default,
+.I less
+will refuse to open non-regular files.
+.IP -g
+Normally,
+.I less
+will highlight ALL strings which match the last search command.
+The -g flag changes this behavior to highlight only the particular string
+which was found by the last search command.
+This can cause
+.I less
+to run somewhat faster than the default.
+.IP -G
+The -G flag suppresses all highlighting of strings found by search commands.
+.IP -h\fIn\fP
+Specifies a maximum number of lines to scroll backward.
+If it is necessary to scroll backward more than \fIn\fP lines,
+the screen is repainted in a forward direction instead.
+(If the terminal does not have the ability to scroll
+backward, -h0 is implied.)
+.IP -i
+Causes searches to ignore case; that is,
+uppercase and lowercase are considered identical.
+This option is ignored if any uppercase letters
+appear in the search pattern;
+in other words,
+if a pattern contains uppercase letters, then that search does not ignore case.
+.IP -I
+Like -i, but searches ignore case even if
+the pattern contains uppercase letters.
+.IP -j\fIn\fP
+Specifies a line on the screen where the "target" line
+is to be positioned.
+A target line is the object of a text search,
+tag search, jump to a line number,
+jump to a file percentage, or jump to a marked position.
+The screen line is specified by a number: the top line on the screen
+is 1, the next is 2, and so on.
+The number may be negative to specify a line relative to the bottom
+of the screen: the bottom line on the screen is -1, the second
+to the bottom is -2, and so on.
+If the -j option is used, searches begin at the line immediately
+after the target line.
+For example, if "-j4" is used, the target line is the
+fourth line on the screen, so searches begin at the fifth line
+on the screen.
+.IP -k\fIfilename\fP
+Causes
+.I less
+to open and interpret the named file as a
+.I lesskey
+(1) file.
+Multiple -k options may be specified.
+If a file called .less exists in the user's home directory, this
+file is also used as a
+.I lesskey
+file.
+.IP -m
+Causes
+.I less
+to prompt verbosely (like \fImore\fP),
+with the percent into the file.
+By default,
+.I less
+prompts with a colon.
+.IP -M
+Causes
+.I less
+to prompt even more verbosely than
+.I more.
+.IP -n
+Suppresses line numbers.
+The default (to use line numbers) may cause
+.I less
+to run more slowly in some cases, especially with a very large input file.
+Suppressing line numbers with the -n flag will avoid this problem.
+Using line numbers means: the line number will be displayed in the verbose
+prompt and in the = command,
+and the v command will pass the current line number to the editor
+(see also the discussion of LESSEDIT in PROMPTS below).
+.IP -N
+Causes a line number to be displayed at the beginning of
+each line in the display.
+.IP -o\fIfilename\fP
+Causes
+.I less
+to copy its input to the named file as it is being viewed.
+This applies only when the input file is a pipe,
+not an ordinary file.
+If the file already exists,
+.I less
+will ask for confirmation before overwriting it.
+.IP -O\fIfilename\fP
+The -O option is like -o, but it will overwrite an existing
+file without asking for confirmation.
+.sp
+If no log file has been specified,
+the -o and -O options can be used from within
+.I less
+to specify a log file.
+Without a file name, they will simply report the name of the log file.
+The "s" command is equivalent to specifying -o from within
+.I less.
+.IP -p\fIpattern\fP
+The -p option on the command line is equivalent to
+specifying +/\fIpattern\fP;
+that is, it tells
+.I less
+to start at the first occurrence of \fIpattern\fP in the file.
+.IP -P\fIprompt\fP
+Provides a way to tailor the three prompt
+styles to your own preference.
+This option would normally be put in the LESS environment
+variable, rather than being typed in with each
+.I less
+command.
+Such an option must either be the last option in the LESS variable,
+or be terminated by a dollar sign.
+-P followed by a string changes the default (short) prompt to that string.
+-Pm changes the medium (-m) prompt to the string, and
+-PM changes the long (-M) prompt.
+Also, -P= changes the message printed by the = command to the given string.
+All prompt strings consist of a sequence of
+letters and special escape sequences.
+See the section on PROMPTS for more details.
+.IP -q
+Causes moderately "quiet" operation:
+the terminal bell is not rung
+if an attempt is made to scroll past the end of the file
+or before the beginning of the file.
+If the terminal has a "visual bell", it is used instead.
+The bell will be rung on certain other errors,
+such as typing an invalid character.
+The default is to ring the terminal bell in all such cases.
+.IP -Q
+Causes totally "quiet" operation:
+the terminal bell is never rung.
+.IP -r
+Causes "raw" control characters to be displayed.
+The default is to display control characters using the caret notation;
+for example, a control-A (octal 001) is displayed as "^A".
+Warning: when the -r flag is used,
+.I less
+cannot keep track of the actual appearance of the screen
+(since this depends on how the screen responds to
+each type of control character).
+Thus, various display problems may result,
+such as long lines being split in the wrong place.
+.IP -s
+Causes consecutive blank lines to be squeezed into a single blank line.
+This is useful when viewing
+.I nroff
+output.
+.IP -S
+Causes lines longer than the screen width to be
+chopped rather than folded.
+That is, the remainder of a long line is simply discarded.
+The default is to fold long lines; that is, display the remainder
+on the next line.
+.IP -t\fItag\fP
+The -t option, followed immediately by a TAG,
+will edit the file containing that tag.
+For this to work, there must be a file called "tags" in the
+current directory, which was previously built by the
+.I ctags
+(1) command.
+This option may also be specified from within
+.I less
+(using the \- command) as a way of examining a new file.
+The command ":t" is equivalent to specifying -t from within
+.I less.
+.IP -T\fItagsfile\fP
+Specifies a tags file to be used instead of "tags".
+.IP -u
+Causes backspaces and carriage returns to be treated as printable characters;
+that is, they are sent to the terminal when they appear in the input.
+.IP -U
+Causes backspaces and carriage returns to be treated as control characters;
+that is, they are handled as specified by the -r option.
+.sp
+By default, if neither -u nor -U is given,
+backspaces which appear adjacent to an underscore character
+are treated specially:
+the underlined text is displayed
+using the terminal's hardware underlining capability.
+Also, backspaces which appear between two identical characters
+are treated specially:
+the overstruck text is printed
+using the terminal's hardware boldface capability.
+Other backspaces are deleted, along with the preceding character.
+Carriage returns immediately followed by a newline are deleted.
+Other carriage returns are handled as specified by the -r option.
+Text which is overstruck or underlined can be searched for
+if neither -u nor -U is in effect.
+.IP -V
+Displays the version number of
+.I less.
+.IP -w
+Causes blank lines to be used to represent lines
+past the end of the file.
+By default,
+a tilde character (~) is used.
+.IP -x\fIn\fP
+Sets tab stops every \fIn\fP positions.
+The default for \fIn\fP is 8.
+.IP -X
+Disables sending the termcap initialization and deinitialization strings
+to the terminal.
+This is sometimes desirable if the deinitialization string does
+something unnecessary, like clearing the screen.
+.IP -y\fIn\fP
+Specifies a maximum number of lines to scroll forward.
+If it is necessary to scroll forward more than \fIn\fP lines,
+the screen is repainted instead.
+The -c or -C option may be used to repaint from the top of
+the screen if desired.
+By default, any forward movement causes scrolling.
+.IP -[z]\fIn\fP
+Changes the default scrolling window size to \fIn\fP lines.
+The default is one screenful.
+The z and w commands can also be used to change the window size.
+The "z" may be omitted for compatibility with
+.I more.
+If the number
+.I n
+is negative, it indicates
+.I n
+lines less than the current screen size.
+For example, if the screen is 24 lines, \fI-z-4\fP sets the
+scrolling window to 20 lines. If the screen is resized to 40 lines,
+the scrolling window automatically changes to 36 lines.
+.IP +
+If a command line option begins with \fB+\fP,
+the remainder of that option is taken to be an initial command to
+.I less.
+For example, +G tells
+.I less
+to start at the end of the file rather than the beginning,
+and +/xyz tells it to start at the first occurrence of "xyz" in the file.
+As a special case, +<number> acts like +<number>g;
+that is, it starts the display at the specified line number
+(however, see the caveat under the "g" command above).
+If the option starts with ++, the initial command applies to
+every file being viewed, not just the first one.
+The + command described previously
+may also be used to set (or change) an initial command for every file.
+
+.SH "LINE EDITING"
+When entering command line at the bottom of the screen
+(for example, a filename for the :e command,
+or the pattern for a search command),
+certain keys can be used to manipulate the command line.
+Most commands have an alternate form in [ brackets ] which can be used if
+a key does not exist on a particular keyboard.
+(The bracketed forms do not work in the MS-DOS version.)
+Any of these special keys may be entered literally by preceding
+it with the "literal" character, either ^V or ^A.
+A backslash itself may also be entered literally by entering two backslashes.
+.IP "LEFTARROW [ ESC-h ]"
+Move the cursor one space to the left.
+.IP "RIGHTARROW [ ESC-l ]"
+Move the cursor one space to the right.
+.IP "^LEFTARROW [ ESC-b or ESC-LEFTARROW ]"
+(That is, CONTROL and LEFTARROW simultaneously.)
+Move the cursor one word to the left.
+.IP "^RIGHTARROW [ ESC-w or ESC-RIGHTARROW ]"
+(That is, CONTROL and RIGHTARROW simultaneously.)
+Move the cursor one word to the right.
+.IP "HOME [ ESC-0 ]"
+Move the cursor to the beginning of the line.
+.IP "END [ ESC-$ ]"
+Move the cursor to the end of the line.
+.IP "BACKSPACE"
+Delete the character to the left of the cursor,
+or cancel the command if the command line is empty.
+.IP "DELETE or [ ESC-x ]"
+Delete the character under the cursor.
+.IP "^BACKSPACE [ ESC-BACKSPACE ]"
+(That is, CONTROL and BACKSPACE simultaneously.)
+Delete the word to the left of the cursor.
+.IP "^DELETE [ ESC-X or ESC-DELETE ]"
+(That is, CONTROL and DELETE simultaneously.)
+Delete the word under the cursor.
+.IP "UPARROW [ ESC-k ]"
+Retrieve the previous command line.
+.IP "DOWNARROW [ ESC-j ]"
+Retrieve the next command line.
+.IP "TAB"
+Complete the partial filename to the left of the cursor.
+If it matches more than one filename, the first match
+is entered into the command line.
+Repeated TABs will cycle thru the other matching filenames.
+.IP "BACKTAB [ ESC-TAB ]"
+Like, TAB, but cycles in the reverse direction thru the matching filenames.
+.IP "^L"
+Complete the partial filename to the left of the cursor.
+If it matches more than one filename, all matches are entered into
+the command line (if they fit).
+.IP "^U (Unix) or ESC (MS-DOS)"
+Delete the entire command line,
+or cancel the command if the command line is empty.
+If you have changed your line-kill character in Unix to something
+other than ^U, that character is used instead of ^U.
+
+.SH "KEY BINDINGS"
+You may define your own
+.I less
+commands by using the program
+.I lesskey
+(1)
+to create a file called ".less" in your home directory.
+This file specifies a set of command keys and an action
+associated with each key.
+You may also use
+.I lesskey
+to change the line-editing keys (see LINE EDITING).
+See the
+.I lesskey
+manual page for more details.
+
+.SH "INPUT PREPROCESSOR"
+You may define an "input preprocessor" for
+.I less.
+Before
+.I less
+opens a file, it first gives your input preprocessor a chance to modify the
+way the contents of the file are displayed.
+An input preprocessor is simply an executable program (or shell script),
+which writes the contents of the file to a different file,
+called the replacement file.
+The contents of the replacement file are then displayed
+in place of the contents of the original file.
+However, it will appear to the user as if the original file is opened;
+that is,
+.I less
+will display the original filename as the name of the current file.
+.PP
+An input preprocessor receives one command line argument, the original filename,
+as entered by the user.
+It should create the replacement file, and when finished,
+print the name of the replacement file to its standard output.
+If the input preprocessor does not output a replacement filename,
+.I less
+uses the original file, as normal.
+The input preprocessor is not called when viewing standard input.
+To set up an input preprocessor, set the LESSOPEN environment variable
+to a command line which will invoke your input preprocessor.
+This command line should include one occurrence of the string "%s",
+which will be replaced by the filename
+when the input preprocessor command is invoked.
+.PP
+When
+.I less
+closes a file opened in such a way, it will call another program,
+called the input postprocessor,
+which may perform any desired clean-up action (such as deleting the
+replacement file created by LESSOPEN).
+This program receives two command line arguments, the original filename
+as entered by the user, and the name of the replacement file.
+To set up an input postprocessor, set the LESSCLOSE environment variable
+to a command line which will invoke your input postprocessor.
+It may include two occurrences of the string "%s";
+the first is replaced with the original name of the file and
+the second with the name of the replacement file,
+which was output by LESSOPEN.
+.PP
+For example, on many Unix systems, these two scripts will allow you
+to keep files in compressed format, but still let
+.I less
+view them directly:
+.PP
+lessopen.sh:
+.br
+ #! /bin/sh
+.br
+ case "$1" in
+.br
+ *.Z) uncompress -c $1 >/tmp/less.$$ 2>/dev/null
+.br
+ if [ -s /tmp/less.$$ ]; then
+.br
+ echo /tmp/less.$$
+.br
+ else
+.br
+ rm -f /tmp/less.$$
+.br
+ fi
+.br
+ ;;
+.br
+ esac
+.PP
+lessclose.sh:
+.br
+ #! /bin/sh
+.br
+ rm $2
+.PP
+To use these scripts, put them both where they can be executed and
+set LESSOPEN="lessopen.sh\ %s", and
+LESSCLOSE="lessclose.sh\ %s\ %s".
+More complex LESSOPEN and LESSCLOSE scripts may be written
+to accept other types of compressed files, and so on.
+.PP
+It is also possible to set up an input preprocessor to
+pipe the file data directly to
+.I less,
+rather than putting the data into a replacement file.
+This avoids the need to decompress the entire file before
+starting to view it.
+An input preprocessor that works this way is called an input pipe.
+An input pipe, instead of writing the name of a replacement file on
+its standard output,
+writes the entire contents of the replacement file on its standard output.
+If the input pipe does not write any characters on its standard output,
+then there is no replacement file and
+.I less
+uses the original file, as normal.
+To use an input pipe,
+make the first character in the LESSOPEN environment variable a
+vertical bar (|) to signify that the input preprocessor is an input pipe.
+.PP
+For example, on many Unix systems, this script will work like the
+previous example scripts:
+.PP
+lesspipe.sh:
+.br
+ !# /bin/sh
+.br
+ case "$1" in
+.br
+ *.Z) uncompress -c $1 2>/dev/null
+.br
+ ;;
+.br
+ esac
+.br
+.PP
+To use this script, put it where it can be executed and set
+LESSOPEN="|lesspipe.sh %s".
+When an input pipe is used, a LESSCLOSE postprocessor can be used,
+but it is usually not necessary since there is no replacement file
+to clean up.
+In this case, the replacement file name passed to the LESSCLOSE
+postprocessor is "-".
+
+.SH "NATIONAL CHARACTER SETS"
+There are three types of characters in the input file:
+.IP "normal characters"
+can be displayed directly to the screen.
+.IP "control characters"
+should not be displayed directly, but are expected to be found
+in ordinary text files (such as backspace and tab).
+.IP "binary characters"
+should not be displayed directly and are not expected to be found
+in text files.
+.PP
+A "character set" is simply a description of which characters are to
+be considered normal, control, and binary.
+The LESSCHARSET environment variable may be used to select a character set.
+Possible values for LESSCHARSET are:
+.IP ascii
+The default character set.
+BS, TAB, NL, CR, and formfeed are control characters,
+all chars with values between 127 and 255 are binary,
+and all others are normal.
+.IP latin1
+Selects the ISO 8859/1 character set.
+latin-1 is the same as ASCII, except characters between 161 and 255 are
+treated as normal characters.
+.IP dos
+Selects a character set appropriate for MS-DOS.
+.IP koi8-r
+Selects a Russian character set.
+.IP next
+Selects a character set appropriate for NeXT computers.
+.PP
+In special cases, it may be desired to tailor
+.I less
+to use a character set other than the ones definable by LESSCHARSET.
+In this case, the environment variable LESSCHARDEF can be used
+to define a character set.
+It should be set to a string where each character in the string represents
+one character in the character set.
+The character "." is used for a normal character, "c" for control,
+and "b" for binary.
+A decimal number may be used for repetition.
+For example, "bccc4b." would mean character 0 is binary,
+1, 2 and 3 are control, 4, 5, 6 and 7 are binary, and 8 is normal.
+All characters after the last are taken to be the same as the last,
+so characters 9 through 255 would be normal.
+(This is an example, and does not necessarily
+represent any real character set.)
+.PP
+This table shows the value of LESSCHARDEF which is equivalent
+to each of the possible values for LESSCHARSET:
+.sp
+ ascii\ 8bcccbcc18b95.b
+.br
+ latin1 8bcccbcc18b95.33b.
+.br
+ dos\ \ \ 8bcccbcc12bc5b95.b.
+.br
+ koi8-r 8bcccbcc18b95.b128.
+.br
+ next\ \ 8bcccbcc18b95.bb125.bb
+.PP
+If neither LESSCHARSET nor LESSCHARDEF is set,
+but your system supports the
+.I setlocale
+interface,
+.I less
+will use setlocale to determine the character set.
+setlocale is controlled by setting the LANG or LC_CTYPE environment variables.
+.PP
+Control and binary characters are displayed in standout (reverse video).
+Each such character is displayed in caret notation if possible
+(e.g. ^A for control-A). Caret notation is used only if
+inverting the 0100 bit results in a normal printable character.
+Otherwise, the character is displayed as a hex number in angle brackets.
+This format can be changed by
+setting the LESSBINFMT environment variable.
+LESSBINFMT may begin with a "*" and one character to select
+the display attribute:
+"*k" is blinking, "*d" is bold, "*u" is underlined, "*s" is standout.
+If LESSBINFMT does not begin with a "*", normal attribute is assumed.
+The remainder of LESSBINFMT is a string which may include one
+printf-style escape sequence (a % followed by x, X, o, d, etc.).
+For example, if LESSBINFMT is "*u[%x]", binary characters
+are displayed in underlined hexadecimal surrounded by brackets.
+The default if no LESSBINFMT is specified is "*d<%X>".
+
+.SH "PROMPTS"
+The -P option allows you to tailor the prompt to your preference.
+The string given to the -P option replaces the specified prompt string.
+Certain characters in the string are interpreted specially.
+The prompt mechanism is rather complicated to provide flexibility,
+but the ordinary user need not understand the details of constructing
+personalized prompt strings.
+.sp
+A percent sign followed by a single character is expanded
+according to what the following character is:
+.IP "%b\fIX\fP"
+Replaced by the byte offset into the current input file.
+The b is followed by a single character (shown as \fIX\fP above)
+which specifies the line whose byte offset is to be used.
+If the character is a "t", the byte offset of the top line in the
+display is used,
+an "m" means use the middle line,
+a "b" means use the bottom line,
+a "B" means use the line just after the bottom line,
+and a "j" means use the "target" line, as specified by the -j option.
+.IP "%B"
+Replaced by the size of the current input file.
+.IP "%E"
+Replaced by the name of the editor (from the VISUAL environment variable,
+or the EDITOR environment variable if VISUAL is not defined).
+See the discussion of the LESSEDIT feature below.
+.IP "%f"
+Replaced by the name of the current input file.
+.IP "%i"
+Replaced by the index of the current file in the list of
+input files.
+.IP "%l\fIX\fP"
+Replaced by the line number of a line in the input file.
+The line to be used is determined by the \fIX\fP, as with the %b option.
+.IP "%L"
+Replaced by the line number of the last line in the input file.
+.IP "%m"
+Replaced by the total number of input files.
+.IP "%p\fIX\fP"
+Replaced by the percent into the current input file.
+The line used is determined by the \fIX\fP as with the %b option.
+.IP "%s"
+Same as %B.
+.IP "%t"
+Causes any trailing spaces to be removed.
+Usually used at the end of the string, but may appear anywhere.
+.IP "%x"
+Replaced by the name of the next input file in the list.
+.PP
+If any item is unknown (for example, the file size if input
+is a pipe), a question mark is printed instead.
+.PP
+The format of the prompt string can be changed
+depending on certain conditions.
+A question mark followed by a single character acts like an "IF":
+depending on the following character, a condition is evaluated.
+If the condition is true, any characters following the question mark
+and condition character, up to a period, are included in the prompt.
+If the condition is false, such characters are not included.
+A colon appearing between the question mark and the
+period can be used to establish an "ELSE": any characters between
+the colon and the period are included in the string if and only if
+the IF condition is false.
+Condition characters (which follow a question mark) may be:
+.IP "?a"
+True if any characters have been included in the prompt so far.
+.IP "?b\fIX\fP"
+True if the byte offset of the specified line is known.
+.IP "?B"
+True if the size of current input file is known.
+.IP "?e"
+True if at end-of-file.
+.IP "?f"
+True if there is an input filename
+(that is, if input is not a pipe).
+.IP "?l\fIX\fP"
+True if the line number of the specified line is known.
+.IP "?L"
+True if the line number of the last line in the file is known.
+.IP "?m"
+True if there is more than one input file.
+.IP "?n"
+True if this is the first prompt in a new input file.
+.IP "?p\fIX\fP"
+True if the percent into the current input file
+of the specified line is known.
+.IP "?s"
+Same as "?B".
+.IP "?x"
+True if there is a next input file
+(that is, if the current input file is not the last one).
+.PP
+Any characters other than the special ones
+(question mark, colon, period, percent, and backslash)
+become literally part of the prompt.
+Any of the special characters may be included in the prompt literally
+by preceding it with a backslash.
+.PP
+Some examples:
+.sp
+?f%f:Standard input.
+.sp
+This prompt prints the filename, if known;
+otherwise the string "Standard input".
+.sp
+?f%f .?ltLine %lt:?pt%pt\\%:?btByte %bt:-...
+.sp
+This prompt would print the filename, if known.
+The filename is followed by the line number, if known,
+otherwise the percent if known, otherwise the byte offset if known.
+Otherwise, a dash is printed.
+Notice how each question mark has a matching period,
+and how the % after the %pt
+is included literally by escaping it with a backslash.
+.sp
+?n?f%f\ .?m(file\ %i\ of\ %m)\ ..?e(END)\ ?x-\ Next\\:\ %x..%t
+.sp
+This prints the filename if this is the first prompt in a file,
+followed by the "file N of N" message if there is more
+than one input file.
+Then, if we are at end-of-file, the string "(END)" is printed
+followed by the name of the next file, if there is one.
+Finally, any trailing spaces are truncated.
+This is the default prompt.
+For reference, here are the defaults for
+the other two prompts (-m and -M respectively).
+Each is broken into two lines here for readability only.
+.nf
+.sp
+?n?f%f\ .?m(file\ %i\ of\ %m)\ ..?e(END)\ ?x-\ Next\\:\ %x.:
+ ?pB%pB\\%:byte\ %bB?s/%s...%t
+.sp
+?f%f\ .?n?m(file\ %i\ of\ %m)\ ..?ltline\ %lt?L/%L.\ :byte\ %bB?s/%s.\ .
+ ?e(END)\ ?x-\ Next\\:\ %x.:?pB%pB\\%..%t
+.sp
+.fi
+And here is the default message produced by the = command:
+.nf
+.sp
+?f%f\ .?m(file\ %i\ of\ %m)\ .?ltline\ %lt?L/%L.\ .
+ byte\ %bB?s/%s.\ ?e(END)\ :?pB%pB\\%..%t
+.fi
+.PP
+The prompt expansion features are also used for another purpose:
+if an environment variable LESSEDIT is defined, it is used
+as the command to be executed when the v command is invoked.
+The LESSEDIT string is expanded in the same way as the prompt strings.
+The default value for LESSEDIT is:
+.nf
+.sp
+ %E\ ?lm+%lm.\ %f
+.sp
+.fi
+Note that this expands to the editor name, followed by a + and the
+line number, followed by the file name.
+If your editor does not accept the "+linenumber" syntax, or has other
+differences in invocation syntax, the LESSEDIT variable can be
+changed to modify this default.
+
+.SH "ENVIRONMENT VARIABLES"
+.IP COLUMNS
+Sets the number of columns on the screen.
+Takes precedence over the number of columns specified by the TERM variable.
+(But if you have a windowing system which supports TIOCGWINSZ or WIOCGETD,
+the window system's idea of the screen size takes precedence over the
+LINES and COLUMNS environment variables.)
+.IP EDITOR
+The name of the editor (used for the v command).
+.IP HOME
+Name of the user's home directory (used to find a .less file).
+.IP LANG
+Language for determining the character set.
+.IP LC_CTYPE
+Language for determining the character set.
+.IP LESS
+Flags which are passed to
+.I less
+automatically.
+.IP LESSBINFMT
+Format for displaying non-printable, non-control characters.
+.IP LESSCHARDEF
+Defines a character set.
+.IP LESSCHARSET
+Selects a predefined character set.
+.IP LESSCLOSE
+Command line to invoke the (optional) input-postprocessor.
+.IP LESSEDIT
+Editor prototype string (used for the v command).
+See discussion under PROMPTS.
+.IP LESSHELP
+Name of the help file.
+.IP LESSOPEN
+Command line to invoke the (optional) input-preprocessor.
+.IP LINES
+Sets the number of lines on the screen.
+Takes precedence over the number of lines specified by the TERM variable.
+.IP SHELL
+The shell used to execute the ! command, as well as to expand filenames.
+.IP TERM
+The type of terminal on which
+.I less
+is being run.
+.IP VISUAL
+The name of the editor (used for the v command).
+
+.SH "SEE ALSO"
+lesskey(1)
+
+.SH WARNINGS
+The = command and prompts (unless changed by -P)
+report the line number of the line at the top of the screen,
+but the byte and percent of the line at the bottom of the screen.
+.PP
+If the :e command is used to name more than one file,
+and one of the named files has been viewed previously,
+the new files may be entered into the list in an unexpected order.
+.PP
+On certain older terminals (the so-called "magic cookie" terminals),
+search highlighting will cause an erroneous display.
+On such terminals, search highlighting is disabled by default
+to avoid possible problems.
+.PP
+In certain cases, when search highlighting is enabled and
+a search pattern begins with a ^,
+more text than the matching string may be highlighted.
+
+
+.SH COPYRIGHT
+Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
diff --git a/usr.bin/less/lesskey.c b/usr.bin/less/lesskey.c
new file mode 100644
index 00000000000..0d8754f9f4b
--- /dev/null
+++ b/usr.bin/less/lesskey.c
@@ -0,0 +1,687 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * lesskey [-o output] [input]
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * Make a .less file.
+ * If no input file is specified, standard input is used.
+ * If no output file is specified, $HOME/.less is used.
+ *
+ * The .less file is used to specify (to "less") user-defined
+ * key bindings. Basically any sequence of 1 to MAX_CMDLEN
+ * keystrokes may be bound to an existing less function.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * The input file is an ascii file consisting of a
+ * sequence of lines of the form:
+ * string <whitespace> action [chars] <newline>
+ *
+ * "string" is a sequence of command characters which form
+ * the new user-defined command. The command
+ * characters may be:
+ * 1. The actual character itself.
+ * 2. A character preceded by ^ to specify a
+ * control character (e.g. ^X means control-X).
+ * 3. A backslash followed by one to three octal digits
+ * to specify a character by its octal value.
+ * 4. A backslash followed by b, e, n, r or t
+ * to specify \b, ESC, \n, \r or \t, respectively.
+ * 5. Any character (other than those mentioned above) preceded
+ * by a \ to specify the character itself (characters which
+ * must be preceded by \ include ^, \, and whitespace.
+ * "action" is the name of a "less" action, from the table below.
+ * "chars" is an optional sequence of characters which is treated
+ * as keyboard input after the command is executed.
+ *
+ * Blank lines and lines which start with # are ignored,
+ * except for the special control lines:
+ * #line-edit Signals the beginning of the line-editing
+ * keys section.
+ * #stop Stops command parsing in less;
+ * causes all default keys to be disabled.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * The output file is a non-ascii file, consisting of a header,
+ * one or more sections, and a trailer.
+ * Each section begins with a section header, a section length word
+ * and the section data. Normally there are three sections:
+ * CMD_SECTION Definition of command keys.
+ * EDIT_SECTION Definition of editing keys.
+ * END_SECTION A special section header, with no
+ * length word or section data.
+ *
+ * Section data consists of zero or more byte sequences of the form:
+ * string <0> <action>
+ * or
+ * string <0> <action|A_EXTRA> chars <0>
+ *
+ * "string" is the command string.
+ * "<0>" is one null byte.
+ * "<action>" is one byte containing the action code (the A_xxx value).
+ * If action is ORed with A_EXTRA, the action byte is followed
+ * by the null-terminated "chars" string.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+#include "less.h"
+#include "lesskey.h"
+#include "cmd.h"
+
+struct cmdname
+{
+ char *cn_name;
+ int cn_action;
+};
+
+struct cmdname cmdnames[] =
+{
+ "back-bracket", A_B_BRACKET,
+ "back-line", A_B_LINE,
+ "back-line-force", A_BF_LINE,
+ "back-screen", A_B_SCREEN,
+ "back-scroll", A_B_SCROLL,
+ "back-search", A_B_SEARCH,
+ "back-window", A_B_WINDOW,
+ "debug", A_DEBUG,
+ "display-flag", A_DISP_OPTION,
+ "display-option", A_DISP_OPTION,
+ "end", A_GOEND,
+ "examine", A_EXAMINE,
+ "first-cmd", A_FIRSTCMD,
+ "firstcmd", A_FIRSTCMD,
+ "flush-repaint", A_FREPAINT,
+ "forw-bracket", A_F_BRACKET,
+ "forw-forever", A_F_FOREVER,
+ "forw-line", A_F_LINE,
+ "forw-line-force", A_FF_LINE,
+ "forw-screen", A_F_SCREEN,
+ "forw-scroll", A_F_SCROLL,
+ "forw-search", A_F_SEARCH,
+ "forw-window", A_F_WINDOW,
+ "goto-end", A_GOEND,
+ "goto-line", A_GOLINE,
+ "goto-mark", A_GOMARK,
+ "help", A_HELP,
+ "index-file", A_INDEX_FILE,
+ "invalid", A_UINVALID,
+ "next-file", A_NEXT_FILE,
+ "noaction", A_NOACTION,
+ "percent", A_PERCENT,
+ "pipe", A_PIPE,
+ "prev-file", A_PREV_FILE,
+ "quit", A_QUIT,
+ "repaint", A_REPAINT,
+ "repaint-flush", A_FREPAINT,
+ "repeat-search", A_AGAIN_SEARCH,
+ "repeat-search-all", A_T_AGAIN_SEARCH,
+ "reverse-search", A_REVERSE_SEARCH,
+ "reverse-search-all", A_T_REVERSE_SEARCH,
+ "set-mark", A_SETMARK,
+ "shell", A_SHELL,
+ "status", A_STAT,
+ "toggle-flag", A_OPT_TOGGLE,
+ "toggle-option", A_OPT_TOGGLE,
+ "undo-hilite", A_UNDO_SEARCH,
+ "version", A_VERSION,
+ "visual", A_VISUAL,
+ NULL, 0
+};
+
+struct cmdname editnames[] =
+{
+ "back-complete", EC_B_COMPLETE,
+ "backspace", EC_BACKSPACE,
+ "delete", EC_DELETE,
+ "down", EC_DOWN,
+ "end", EC_END,
+ "expand", EC_EXPAND,
+ "forw-complete", EC_F_COMPLETE,
+ "home", EC_HOME,
+ "insert", EC_INSERT,
+ "invalid", EC_UINVALID,
+ "kill-line", EC_LINEKILL,
+ "left", EC_LEFT,
+ "literal", EC_LITERAL,
+ "right", EC_RIGHT,
+ "up", EC_UP,
+ "word-backspace", EC_W_BACKSPACE,
+ "word-delete", EC_W_DELETE,
+ "word-left", EC_W_RIGHT,
+ "word-right", EC_W_LEFT,
+ NULL, 0
+};
+
+struct table
+{
+ struct cmdname *names;
+ char *pbuffer;
+ char buffer[MAX_USERCMD];
+};
+
+struct table cmdtable;
+struct table edittable;
+struct table *currtable = &cmdtable;
+
+char fileheader[] = {
+ C0_LESSKEY_MAGIC,
+ C1_LESSKEY_MAGIC,
+ C2_LESSKEY_MAGIC,
+ C3_LESSKEY_MAGIC
+};
+char filetrailer[] = {
+ C0_END_LESSKEY_MAGIC,
+ C1_END_LESSKEY_MAGIC,
+ C2_END_LESSKEY_MAGIC
+};
+char cmdsection[1] = { CMD_SECTION };
+char editsection[1] = { EDIT_SECTION };
+char endsection[1] = { END_SECTION };
+
+char *infile = NULL;
+char *outfile = NULL ;
+
+int linenum;
+int errors;
+
+extern char version[];
+
+ char *
+mkpathname(dirname, filename)
+ char *dirname;
+ char *filename;
+{
+ char *pathname;
+
+ pathname = calloc(strlen(dirname) + strlen(filename) + 2, sizeof(char));
+ strcpy(pathname, dirname);
+#if MSOFTC || OS2
+ strcat(pathname, "\\");
+#else
+ strcat(pathname, "/");
+#endif
+ strcat(pathname, filename);
+ return (pathname);
+}
+
+/*
+ * Figure out the name of a default file (in the user's HOME directory).
+ */
+ char *
+homefile(filename)
+ char *filename;
+{
+ char *p;
+ char *pathname;
+
+ if ((p = getenv("HOME")) != NULL && *p != '\0')
+ pathname = mkpathname(p, filename);
+#if OS2
+ else if ((p = getenv("INIT")) != NULL && *p != '\0')
+ pathname = mkpathname(p, filename);
+#endif
+ else
+ {
+ fprintf(stderr, "cannot find $HOME - using current directory\n");
+ pathname = mkpathname(".", filename);
+ }
+ return (pathname);
+}
+
+/*
+ * Parse command line arguments.
+ */
+ void
+parse_args(argc, argv)
+ int argc;
+ char **argv;
+{
+ outfile = NULL;
+ while (--argc > 0 && **(++argv) == '-' && argv[0][1] != '\0')
+ {
+ switch (argv[0][1])
+ {
+ case 'o':
+ outfile = &argv[0][2];
+ if (*outfile == '\0')
+ {
+ if (--argc <= 0)
+ usage();
+ outfile = *(++argv);
+ }
+ break;
+ case 'V':
+ printf("lesskey version %s\n", version);
+ exit(0);
+ default:
+ usage();
+ }
+ }
+ if (argc > 1)
+ usage();
+ /*
+ * Open the input file, or use DEF_LESSKEYINFILE if none specified.
+ */
+ if (argc > 0)
+ infile = *argv;
+ else
+ infile = homefile(DEF_LESSKEYINFILE);
+}
+
+/*
+ * Initialize data structures.
+ */
+ void
+init_tables()
+{
+ cmdtable.names = cmdnames;
+ cmdtable.pbuffer = cmdtable.buffer;
+
+ edittable.names = editnames;
+ edittable.pbuffer = edittable.buffer;
+}
+
+/*
+ * Parse one character of a string.
+ */
+ int
+tchar(pp)
+ char **pp;
+{
+ register char *p;
+ register char ch;
+ register int i;
+
+ p = *pp;
+ switch (*p)
+ {
+ case '\\':
+ ++p;
+ switch (*p)
+ {
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ /*
+ * Parse an octal number.
+ */
+ ch = 0;
+ i = 0;
+ do
+ ch = 8*ch + (*p - '0');
+ while (*++p >= '0' && *p <= '7' && ++i < 3);
+ *pp = p;
+ return (ch);
+ case 'b':
+ *pp = p+1;
+ return ('\r');
+ case 'e':
+ *pp = p+1;
+ return (ESC);
+ case 'n':
+ *pp = p+1;
+ return ('\n');
+ case 'r':
+ *pp = p+1;
+ return ('\r');
+ case 't':
+ *pp = p+1;
+ return ('\t');
+ default:
+ /*
+ * Backslash followed by any other char
+ * just means that char.
+ */
+ *pp = p+1;
+ return (*p);
+ }
+ case '^':
+ /*
+ * Carat means CONTROL.
+ */
+ *pp = p+2;
+ return (CONTROL(p[1]));
+ }
+ *pp = p+1;
+ return (*p);
+}
+
+/*
+ * Skip leading spaces in a string.
+ */
+ public char *
+skipsp(s)
+ register char *s;
+{
+ while (*s == ' ' || *s == '\t')
+ s++;
+ return (s);
+}
+
+/*
+ * Skip non-space characters in a string.
+ */
+ public char *
+skipnsp(s)
+ register char *s;
+{
+ while (*s != '\0' && *s != ' ' && *s != '\t')
+ s++;
+ return (s);
+}
+
+/*
+ * Clean up an input line:
+ * strip off the trailing newline & any trailing # comment.
+ */
+ char *
+clean_line(s)
+ char *s;
+{
+ register int i;
+
+ s = skipsp(s);
+ for (i = 0; s[i] != '\n' && s[i] != '\0'; i++)
+ if (s[i] == '#' && (i == 0 || s[i-1] != '\\'))
+ break;
+ s[i] = '\0';
+ return (s);
+}
+
+/*
+ * Add a byte to the output command table.
+ */
+ void
+add_cmd_char(c)
+ int c;
+{
+ if (currtable->pbuffer >= currtable->buffer + MAX_USERCMD)
+ {
+ error("too many commands");
+ exit(1);
+ }
+ *(currtable->pbuffer)++ = c;
+}
+
+/*
+ * See if we have a special "control" line.
+ */
+ int
+control_line(s)
+ char *s;
+{
+#define PREFIX(str,pat) (strncmp(str,pat,strlen(pat)-1) == 0)
+
+ if (PREFIX(s, "#line-edit"))
+ {
+ currtable = &edittable;
+ return (1);
+ }
+ if (PREFIX(s, "#command"))
+ {
+ currtable = &cmdtable;
+ return (1);
+ }
+ if (PREFIX(s, "#stop"))
+ {
+ add_cmd_char('\0');
+ add_cmd_char(A_END_LIST);
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Output some bytes.
+ */
+ void
+fputbytes(fd, buf, len)
+ FILE *fd;
+ char *buf;
+ int len;
+{
+ while (len-- > 0)
+ {
+ fwrite(buf, sizeof(char), 1, fd);
+ buf++;
+ }
+}
+
+/*
+ * Output an integer, in special KRADIX form.
+ */
+ void
+fputint(fd, val)
+ FILE *fd;
+ unsigned int val;
+{
+ char c;
+
+ if (val >= KRADIX*KRADIX)
+ {
+ fprintf(stderr, "error: integer too big (%d > %d)\n",
+ val, KRADIX*KRADIX);
+ exit(1);
+ }
+ c = val % KRADIX;
+ fwrite(&c, sizeof(char), 1, fd);
+ c = val / KRADIX;
+ fwrite(&c, sizeof(char), 1, fd);
+}
+
+/*
+ * Find an action, given the name of the action.
+ */
+ int
+findaction(actname)
+ char *actname;
+{
+ int i;
+
+ for (i = 0; currtable->names[i].cn_name != NULL; i++)
+ if (strcmp(currtable->names[i].cn_name, actname) == 0)
+ return (currtable->names[i].cn_action);
+ error("unknown action");
+ return (A_INVALID);
+}
+
+usage()
+{
+ fprintf(stderr, "usage: lesskey [-o output] [input]\n");
+ exit(1);
+}
+
+ void
+error(s)
+ char *s;
+{
+ fprintf(stderr, "line %d: %s\n", linenum, s);
+ errors++;
+}
+
+
+/*
+ * Parse a line from the lesskey file.
+ */
+ void
+parse_line(line)
+ char *line;
+{
+ char *p;
+ int cmdlen;
+ char *actname;
+ int action;
+ int c;
+
+ /*
+ * See if it is a control line.
+ */
+ if (control_line(line))
+ return;
+ /*
+ * Skip leading white space.
+ * Replace the final newline with a null byte.
+ * Ignore blank lines and comments.
+ */
+ p = clean_line(line);
+ if (*p == '\0')
+ return;
+
+ /*
+ * Parse the command string and store it in the current table.
+ */
+ cmdlen = 0;
+ do
+ {
+ c = tchar(&p);
+ if (++cmdlen > MAX_CMDLEN)
+ error("command too long");
+ else
+ add_cmd_char(c);
+ } while (*p != ' ' && *p != '\t' && *p != '\0');
+ /*
+ * Terminate the command string with a null byte.
+ */
+ add_cmd_char('\0');
+
+ /*
+ * Skip white space between the command string
+ * and the action name.
+ * Terminate the action name with a null byte.
+ */
+ p = skipsp(p);
+ if (*p == '\0')
+ {
+ error("missing action");
+ return;
+ }
+ actname = p;
+ p = skipnsp(p);
+ c = *p;
+ *p = '\0';
+
+ /*
+ * Parse the action name and store it in the current table.
+ */
+ action = findaction(actname);
+
+ /*
+ * See if an extra string follows the action name.
+ */
+ *p = c;
+ p = skipsp(p);
+ if (*p == '\0')
+ {
+ add_cmd_char(action);
+ } else
+ {
+ /*
+ * OR the special value A_EXTRA into the action byte.
+ * Put the extra string after the action byte.
+ */
+ add_cmd_char(action | A_EXTRA);
+ while (*p != '\0')
+ add_cmd_char(tchar(&p));
+ add_cmd_char('\0');
+ }
+}
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ FILE *desc;
+ FILE *out;
+ char line[200];
+
+ /*
+ * Process command line arguments.
+ */
+ parse_args(argc, argv);
+ init_tables();
+
+ /*
+ * Open the input file.
+ */
+ if (strcmp(infile, "-") == 0)
+ desc = stdin;
+ else if ((desc = fopen(infile, "r")) == NULL)
+ {
+ perror(infile);
+ exit(1);
+ }
+
+ /*
+ * Read and parse the input file, one line at a time.
+ */
+ errors = 0;
+ linenum = 0;
+ while (fgets(line, sizeof(line), desc) != NULL)
+ {
+ ++linenum;
+ parse_line(line);
+ }
+
+ /*
+ * Write the output file.
+ * If no output file was specified, use "$HOME/.less"
+ */
+ if (errors > 0)
+ {
+ fprintf(stderr, "%d errors; no output produced\n", errors);
+ exit(1);
+ }
+
+ if (outfile == NULL)
+ outfile = homefile(LESSKEYFILE);
+ if ((out = fopen(outfile, "wb")) == NULL)
+ {
+ perror(outfile);
+ exit(1);
+ }
+
+ /* File header */
+ fputbytes(out, fileheader, sizeof(fileheader));
+
+ /* Command key section */
+ fputbytes(out, cmdsection, sizeof(cmdsection));
+ fputint(out, cmdtable.pbuffer - cmdtable.buffer);
+ fputbytes(out, (char *)cmdtable.buffer, cmdtable.pbuffer-cmdtable.buffer);
+ /* Edit key section */
+ fputbytes(out, editsection, sizeof(editsection));
+ fputint(out, edittable.pbuffer - edittable.buffer);
+ fputbytes(out, (char *)edittable.buffer, edittable.pbuffer-edittable.buffer);
+
+ /* File trailer */
+ fputbytes(out, endsection, sizeof(endsection));
+ fputbytes(out, filetrailer, sizeof(filetrailer));
+ exit(0);
+}
diff --git a/usr.bin/less/lesskey.h b/usr.bin/less/lesskey.h
new file mode 100644
index 00000000000..46dd02dd49d
--- /dev/null
+++ b/usr.bin/less/lesskey.h
@@ -0,0 +1,28 @@
+/*
+ * Format of a lesskey file:
+ *
+ * LESSKEY_MAGIC (4 bytes)
+ * sections...
+ * END_LESSKEY_MAGIC (4 bytes)
+ *
+ * Each section is:
+ *
+ * section_MAGIC (1 byte)
+ * section_length (2 bytes)
+ * key table (section_length bytes)
+ */
+#define C0_LESSKEY_MAGIC '\0'
+#define C1_LESSKEY_MAGIC 'M'
+#define C2_LESSKEY_MAGIC '+'
+#define C3_LESSKEY_MAGIC 'G'
+
+#define CMD_SECTION 'c'
+#define EDIT_SECTION 'e'
+#define END_SECTION 'x'
+
+#define C0_END_LESSKEY_MAGIC 'E'
+#define C1_END_LESSKEY_MAGIC 'n'
+#define C2_END_LESSKEY_MAGIC 'd'
+
+/* */
+#define KRADIX 64
diff --git a/usr.bin/less/lesskey.man b/usr.bin/less/lesskey.man
new file mode 100644
index 00000000000..c33eb029bfa
--- /dev/null
+++ b/usr.bin/less/lesskey.man
@@ -0,0 +1,264 @@
+
+
+
+LESSKEY(1) USER COMMANDS LESSKEY(1)
+
+
+
+NAME
+ lesskey - specify key bindings for less
+
+SYNOPSIS
+ lesskey [-o output] [input]
+ lesskey -V
+
+DESCRIPTION
+ _L_e_s_s_k_e_y is used to specify a set of key bindings to be used
+ by _l_e_s_s. The input file is a text file which describes the
+ key bindings, If the input file is "-", standard input is
+ read. If no input file is specified, $HOME/.lesskey is
+ read. The output file is a binary file which is used by
+ _l_e_s_s. If no output file is specified, $HOME/.less is writ-
+ ten. If the output file already exists, _l_e_s_s_k_e_y will
+ overwrite it.
+
+ The input file consists of lines of the form:
+
+ _s_t_r_i_n_g <whitespace> _a_c_t_i_o_n [extra-string] <newline>
+
+ Whitespace is any sequence of one or more spaces and/or
+ tabs. The _s_t_r_i_n_g is the command key(s) which invoke the
+ action. The _s_t_r_i_n_g may be a single command key, or a
+ sequence of up to 15 keys. The _a_c_t_i_o_n is the name of the
+ less action, from the list below. The characters in the
+ _s_t_r_i_n_g may appear literally, or be prefixed by a caret to
+ indicate a control key. A backslash followed by one to
+ three octal digits may be used to specify a character by its
+ octal value. A backslash followed by b, e, n, r or t speci-
+ fies BACKSPACE, ESCAPE, NEWLINE, RETURN or TAB, respec-
+ tively. A backslash followed by any other character indi-
+ cates that character is to be taken literally. Characters
+ which must be preceded by backslash include caret, space,
+ tab and the backslash itself. Blank lines and lines which
+ start with a pound sign (#) are ignored.
+
+ An action may be followed by an "extra" string. When such a
+ command is entered while running _l_e_s_s, the action is per-
+ formed, and then the extra string is parsed, just as if it
+ were typed in to _l_e_s_s. This feature can be used in certain
+ cases to extend the functionality of a command. For exam-
+ ple, see the "{" and ":t" commands in the example below.
+
+ The -V flag causes _l_e_s_s_k_e_y to print its version number and
+ immediately exit. Other flags and arguments are ignored.
+
+
+EXAMPLE
+ The following input file describes the set of default com-
+ mand keys used by less:
+
+
+
+
+ 1
+
+
+
+
+
+
+LESSKEY(1) USER COMMANDS LESSKEY(1)
+
+
+
+ \r forw-line
+ \n forw-line
+ e forw-line
+ j forw-line
+ ^E forw-line
+ ^N forw-line
+ k back-line
+ y back-line
+ ^Y back-line
+ ^K back-line
+ ^P back-line
+ J forw-line-force
+ K back-line-force
+ Y back-line-force
+ d forw-scroll
+ ^D forw-scroll
+ u back-scroll
+ ^U back-scroll
+ \40 forw-screen
+ f forw-screen
+ ^F forw-screen
+ ^V forw-screen
+ b back-screen
+ ^B back-screen
+ \ev back-screen
+ z forw-window
+ w back-window
+ F forw-forever
+ R repaint-flush
+ r repaint
+ ^R repaint
+ ^L repaint
+ g goto-line
+ < goto-line
+ \e< goto-line
+ p percent
+ % percent
+ { forw-bracket {}
+ } back-bracket {}
+ ( forw-bracket ()
+ ) back-bracket ()
+ [ forw-bracket []
+ ] back-bracket []
+ \e^F forw-bracket
+ \e^B back-bracket
+ G goto-end
+ \e> goto-end
+ > goto-end
+ = status
+ ^G status
+ :f status
+ / forw-search
+
+
+
+ 2
+
+
+
+
+
+
+LESSKEY(1) USER COMMANDS LESSKEY(1)
+
+
+
+ ? back-search
+ \e/ forw-search *
+ \e? back-search *
+ n repeat-search
+ \en repeat-search-all
+ N reverse-search
+ \eN reverse-search-all
+ \u undo-hilite
+ m set-mark
+ ' goto-mark
+ ^X^X goto-mark
+ E examine
+ :e examine
+ ^X^V examine
+ :n next-file
+ :p prev-file
+ :x index-file
+ - toggle-option
+ :t toggle-option t
+ s toggle-option o
+ _ display-option
+ | pipe
+ v visual
+ ! shell
+ + firstcmd
+ H help
+ h help
+ V version
+ q quit
+ :q quit
+ :Q quit
+ ZZ quit
+
+
+PRECEDENCE
+ Commands specified by _l_e_s_s_k_e_y take precedence over the
+ default commands. A default command key may be disabled by
+ including it in the input file with the action "invalid".
+ Alternatively, a key may be defined to do nothing by using
+ the action "noaction". "noaction" is similar to "invalid",
+ but _l_e_s_s will give an error beep for an "invalid" command,
+ but not for a "noaction" command. In addition, ALL default
+ commands may be disabled by adding this control line to the
+ input file:
+
+ #stop
+
+ This will cause all default commands to be ignored. The
+ #stop line should be the last line in that section of the
+ file. (Another section, introduced by #line-edit, may fol-
+ low the #stop line.)
+
+
+
+
+ 3
+
+
+
+
+
+
+LESSKEY(1) USER COMMANDS LESSKEY(1)
+
+
+
+ Be aware that #stop can be dangerous. Since all default com-
+ mands are disabled, you must provide sufficient commands
+ before the #stop line to enable all necessary actions. For
+ example, failure to provide a "quit" command can lead to
+ frustration.
+
+
+LINE EDITING
+ New key bindings may be specified for the line editing com-
+ mands, in a manner similar to the way key bindings for ordi-
+ nary commands are specified. This control line marks the
+ beginning of a section of line-editing commands:
+
+ #line-edit
+
+ Following this line is a list of keys and actions, one per
+ line as in the example below.
+
+
+EXAMPLE
+ The following input file describes the set of default line-
+ editing keys used by less:
+
+ #line-edit
+ \t forw-complete
+ \17 back-complete
+ \e\t back-complete
+ \14 expand
+ ^V literal
+ ^A literal
+ \el right
+ \eh left
+ \eb word-left
+ \ew word-right
+ \ei insert
+ \ex delete
+ \e\b word-backspace
+ \e0 home
+ \e$ end
+ \ek up
+ \ej down
+
+
+
+SEE ALSO
+ less(1)
+
+
+COPYRIGHT
+ Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+
+
+
+
+
+ 4
+
+
+
diff --git a/usr.bin/less/lesskey.nro b/usr.bin/less/lesskey.nro
new file mode 100644
index 00000000000..8f866b51011
--- /dev/null
+++ b/usr.bin/less/lesskey.nro
@@ -0,0 +1,216 @@
+.TH LESSKEY 1
+.SH NAME
+lesskey \- specify key bindings for less
+.SH SYNOPSIS
+.B "lesskey [-o output] [input]"
+.br
+.B "lesskey -V"
+.SH DESCRIPTION
+.I Lesskey
+is used to specify a set of key bindings to be used by
+.I less.
+The input file is a text file which describes the key bindings,
+If the input file is "-", standard input is read.
+If no input file is specified, $HOME/.lesskey is read.
+The output file is a binary file which is used by
+.I less.
+If no output file is specified, $HOME/.less is written.
+If the output file already exists,
+.I lesskey
+will overwrite it.
+.PP
+The input file consists of lines of the form:
+.sp
+ \fIstring\fP <whitespace> \fIaction\fP [extra-string] <newline>
+.sp
+Whitespace is any sequence of one or more spaces and/or tabs.
+The \fIstring\fP is the command key(s) which invoke the action.
+The \fIstring\fP may be a single command key, or a sequence of up to 15 keys.
+The \fIaction\fP is the name of the less action, from the list below.
+The characters in the \fIstring\fP may appear literally, or be
+prefixed by a caret to indicate a control key.
+A backslash followed by one to three octal digits may be used to
+specify a character by its octal value.
+A backslash followed by b, e, n, r or t specifies
+BACKSPACE, ESCAPE, NEWLINE, RETURN or TAB, respectively.
+A backslash followed by any other character indicates that character is
+to be taken literally.
+Characters which must be preceded by backslash include
+caret, space, tab and the backslash itself.
+Blank lines and lines which start with a pound sign (#) are ignored.
+.PP
+An action may be followed by an "extra" string.
+When such a command is entered while running
+.I less,
+the action is performed, and then the extra
+string is parsed, just as if it were typed in to
+.I less.
+This feature can be used in certain cases to extend
+the functionality of a command.
+For example, see the "{" and ":t" commands in the example below.
+.PP
+The -V flag causes
+.I lesskey
+to print its version number and immediately exit.
+Other flags and arguments are ignored.
+
+.SH EXAMPLE
+The following input file describes the set of
+default command keys used by less:
+.sp
+.nf
+ \er forw-line
+ \en forw-line
+ e forw-line
+ j forw-line
+ ^E forw-line
+ ^N forw-line
+ k back-line
+ y back-line
+ ^Y back-line
+ ^K back-line
+ ^P back-line
+ J forw-line-force
+ K back-line-force
+ Y back-line-force
+ d forw-scroll
+ ^D forw-scroll
+ u back-scroll
+ ^U back-scroll
+ \e40 forw-screen
+ f forw-screen
+ ^F forw-screen
+ ^V forw-screen
+ b back-screen
+ ^B back-screen
+ \eev back-screen
+ z forw-window
+ w back-window
+ F forw-forever
+ R repaint-flush
+ r repaint
+ ^R repaint
+ ^L repaint
+ g goto-line
+ < goto-line
+ \ee< goto-line
+ p percent
+ % percent
+ { forw-bracket {}
+ } back-bracket {}
+ ( forw-bracket ()
+ ) back-bracket ()
+ [ forw-bracket []
+ ] back-bracket []
+ \ee^F forw-bracket
+ \ee^B back-bracket
+ G goto-end
+ \ee> goto-end
+ > goto-end
+ = status
+ ^G status
+ :f status
+ / forw-search
+ ? back-search
+ \ee/ forw-search *
+ \ee? back-search *
+ n repeat-search
+ \een repeat-search-all
+ N reverse-search
+ \eeN reverse-search-all
+ \eu undo-hilite
+ m set-mark
+ ' goto-mark
+ ^X^X goto-mark
+ E examine
+ :e examine
+ ^X^V examine
+ :n next-file
+ :p prev-file
+ :x index-file
+ - toggle-option
+ :t toggle-option t
+ s toggle-option o
+ _ display-option
+ | pipe
+ v visual
+ ! shell
+ + firstcmd
+ H help
+ h help
+ V version
+ q quit
+ :q quit
+ :Q quit
+ ZZ quit
+.fi
+.sp
+.SH PRECEDENCE
+Commands specified by
+.I lesskey
+take precedence over the default commands.
+A default command key may be disabled by including it in the
+input file with the action "invalid".
+Alternatively, a key may be defined
+to do nothing by using the action "noaction".
+"noaction" is similar to "invalid", but
+.I less
+will give an error beep for an "invalid" command,
+but not for a "noaction" command.
+In addition, ALL default commands may be disabled by
+adding this control line to the input file:
+.sp
+#stop
+.sp
+This will cause all default commands to be ignored.
+The #stop line should be the last line in that section of the file.
+(Another section, introduced by #line-edit, may follow the #stop line.)
+.PP
+Be aware that #stop can be dangerous.
+Since all default commands are disabled,
+you must provide sufficient commands before the #stop line
+to enable all necessary actions.
+For example, failure to provide a "quit" command can lead to frustration.
+
+.SH "LINE EDITING"
+New key bindings may be specified for the line editing commands,
+in a manner similar to the way key bindings for
+ordinary commands are specified.
+This control line marks the beginning of a section of line-editing commands:
+.sp
+#line-edit
+.sp
+Following this line is a list of keys and actions,
+one per line as in the example below.
+
+.SH EXAMPLE
+The following input file describes the set of
+default line-editing keys used by less:
+.sp
+.nf
+ #line-edit
+ \et forw-complete
+ \e17 back-complete
+ \ee\et back-complete
+ \e14 expand
+ ^V literal
+ ^A literal
+ \eel right
+ \eeh left
+ \eeb word-left
+ \eew word-right
+ \eei insert
+ \eex delete
+ \ee\eb word-backspace
+ \ee0 home
+ \ee$ end
+ \eek up
+ \eej down
+.fi
+.sp
+
+.SH "SEE ALSO"
+less(1)
+
+.SH COPYRIGHT
+Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
diff --git a/usr.bin/less/line.c b/usr.bin/less/line.c
new file mode 100644
index 00000000000..f7fd1124b79
--- /dev/null
+++ b/usr.bin/less/line.c
@@ -0,0 +1,582 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * Routines to manipulate the "line buffer".
+ * The line buffer holds a line of output as it is being built
+ * in preparation for output to the screen.
+ */
+
+#include "less.h"
+
+public char linebuf[1024]; /* Buffer which holds the current output line */
+public int size_linebuf = sizeof(linebuf);
+
+static char attr[1024]; /* Extension of linebuf to hold attributes */
+static int curr; /* Index into linebuf */
+static int column; /* Printable length, accounting for
+ backspaces, etc. */
+static int lno_indent; /* Number of chars used for line number */
+static int overstrike; /* Next char should overstrike previous char */
+static int is_null_line; /* There is no current line */
+static char pendc;
+static POSITION pendpos;
+
+static int do_append();
+
+extern int bs_mode;
+extern int tabstop;
+extern int linenums;
+extern int ctldisp;
+extern int twiddle;
+extern int binattr;
+extern int auto_wrap, ignaw;
+extern int bo_s_width, bo_e_width;
+extern int ul_s_width, ul_e_width;
+extern int bl_s_width, bl_e_width;
+extern int so_s_width, so_e_width;
+extern int sc_width, sc_height;
+
+/*
+ * Rewind the line buffer.
+ */
+ public void
+prewind()
+{
+ curr = 0;
+ column = 0;
+ overstrike = 0;
+ is_null_line = 0;
+ lno_indent = 0;
+ pendc = '\0';
+}
+
+/*
+ * Insert the line number (of the given position) into the line buffer.
+ */
+ public void
+plinenum(pos)
+ POSITION pos;
+{
+ register int lno;
+ register int i;
+ register int n;
+
+ /*
+ * We display the line number at the start of each line
+ * only if the -N option is set.
+ */
+ if (linenums != OPT_ONPLUS)
+ return;
+
+ /*
+ * Get the line number and put it in the current line.
+ * {{ Note: since find_linenum calls forw_raw_line,
+ * it may seek in the input file, requiring the caller
+ * of plinenum to re-seek if necessary. }}
+ */
+ lno = find_linenum(pos);
+
+ sprintf(&linebuf[curr], "%6d", lno);
+ n = strlen(&linebuf[curr]);
+ column += n;
+ for (i = 0; i < n; i++)
+ attr[curr++] = 0;
+
+ /*
+ * Append enough spaces to bring us to the next tab stop.
+ * {{ We could avoid this at the cost of adding some
+ * complication to the tab stop logic in pappend(). }}
+ */
+ if (tabstop == 0)
+ tabstop = 1;
+ do
+ {
+ linebuf[curr] = ' ';
+ attr[curr++] = AT_NORMAL;
+ column++;
+ } while ((column % tabstop) != 0);
+ lno_indent = column;
+}
+
+/*
+ * Return the printing width of the start (enter) sequence
+ * for a given character attribute.
+ */
+ int
+attr_swidth(a)
+ int a;
+{
+ switch (a)
+ {
+ case AT_BOLD: return (bo_s_width);
+ case AT_UNDERLINE: return (ul_s_width);
+ case AT_BLINK: return (bl_s_width);
+ case AT_STANDOUT: return (so_s_width);
+ }
+ return (0);
+}
+
+/*
+ * Return the printing width of the end (exit) sequence
+ * for a given character attribute.
+ */
+ int
+attr_ewidth(a)
+ int a;
+{
+ switch (a)
+ {
+ case AT_BOLD: return (bo_e_width);
+ case AT_UNDERLINE: return (ul_e_width);
+ case AT_BLINK: return (bl_e_width);
+ case AT_STANDOUT: return (so_e_width);
+ }
+ return (0);
+}
+
+/*
+ * Return the printing width of a given character and attribute,
+ * if the character were added to the current position in the line buffer.
+ * Adding a character with a given attribute may cause an enter or exit
+ * attribute sequence to be inserted, so this must be taken into account.
+ */
+ static int
+pwidth(c, a)
+ int c;
+ int a;
+{
+ register int w;
+
+ if (c == '\b')
+ /*
+ * Backspace moves backwards one position.
+ */
+ return (-1);
+
+ if (control_char(c))
+ /*
+ * Control characters do unpredicatable things,
+ * so we don't even try to guess; say it doesn't move.
+ * This can only happen if the -r flag is in effect.
+ */
+ return (0);
+
+ /*
+ * Other characters take one space,
+ * plus the width of any attribute enter/exit sequence.
+ */
+ w = 1;
+ if (curr > 0 && attr[curr-1] != a)
+ w += attr_ewidth(attr[curr-1]);
+ if (a && (curr == 0 || attr[curr-1] != a))
+ w += attr_swidth(a);
+ return (w);
+}
+
+/*
+ * Delete the previous character in the line buffer.
+ */
+ static void
+backc()
+{
+ curr--;
+ column -= pwidth(linebuf[curr], attr[curr]);
+}
+
+/*
+ * Append a character and attribute to the line buffer.
+ */
+ static int
+storec(c, a, pos)
+ int c;
+ int a;
+ POSITION pos;
+{
+ register int w;
+
+#if HILITE_SEARCH
+ if (is_hilited(pos, pos+1, 0))
+ /*
+ * This character should be highlighted.
+ * Override the attribute passed in.
+ */
+ a = AT_STANDOUT;
+#endif
+ w = pwidth(c, a);
+ if (ctldisp > 0 && column + w + attr_ewidth(a) > sc_width)
+ /*
+ * Won't fit on screen.
+ */
+ return (1);
+
+ if (curr >= sizeof(linebuf)-2)
+ /*
+ * Won't fit in line buffer.
+ */
+ return (1);
+
+ /*
+ * Special handling for "magic cookie" terminals.
+ * If an attribute enter/exit sequence has a printing width > 0,
+ * and the sequence is adjacent to a space, delete the space.
+ * We just mark the space as invisible, to avoid having too
+ * many spaces deleted.
+ * {{ Note that even if the attribute width is > 1, we
+ * delete only one space. It's not worth trying to do more.
+ * It's hardly worth doing this much. }}
+ */
+ if (curr > 0 && a != AT_NORMAL &&
+ linebuf[curr-1] == ' ' && attr[curr-1] == AT_NORMAL &&
+ attr_swidth(a) > 0)
+ {
+ /*
+ * We are about to append an enter-attribute sequence
+ * just after a space. Delete the space.
+ */
+ attr[curr-1] = AT_INVIS;
+ column--;
+ } else if (curr > 0 && attr[curr-1] != AT_NORMAL &&
+ attr[curr-1] != AT_INVIS && c == ' ' && a == AT_NORMAL &&
+ attr_ewidth(attr[curr-1]) > 0)
+ {
+ /*
+ * We are about to append a space just after an
+ * exit-attribute sequence. Delete the space.
+ */
+ a = AT_INVIS;
+ column--;
+ }
+ /* End of magic cookie handling. */
+
+ linebuf[curr] = c;
+ attr[curr] = a;
+ column += w;
+ return (0);
+}
+
+/*
+ * Append a character to the line buffer.
+ * Expand tabs into spaces, handle underlining, boldfacing, etc.
+ * Returns 0 if ok, 1 if couldn't fit in buffer.
+ */
+ public int
+pappend(c, pos)
+ register int c;
+ POSITION pos;
+{
+ if (pendc)
+ {
+ if (do_append(pendc, pendpos))
+ /*
+ * Oops. We've probably lost the char which
+ * was in pendc, since caller won't back up.
+ */
+ return (1);
+ pendc = '\0';
+ }
+
+ if (c == '\r' && bs_mode == BS_SPECIAL)
+ {
+ /*
+ * Don't put the CR into the buffer until we see
+ * the next char. If the next char is a newline,
+ * discard the CR.
+ */
+ pendc = c;
+ pendpos = pos;
+ return (0);
+ }
+
+ return (do_append(c, pos));
+}
+
+ static int
+do_append(c, pos)
+ int c;
+ POSITION pos;
+{
+ register char *s;
+ register int a;
+
+#define STOREC(c,a) \
+ if (storec((c),(a),pos)) return (1); else curr++
+
+ if (overstrike)
+ {
+ /*
+ * Overstrike the character at the current position
+ * in the line buffer. This will cause either
+ * underline (if a "_" is overstruck),
+ * bold (if an identical character is overstruck),
+ * or just deletion of the character in the buffer.
+ */
+ overstrike = 0;
+ if ((char)c == linebuf[curr])
+ STOREC(linebuf[curr], AT_BOLD);
+ else if (c == '_')
+ STOREC(linebuf[curr], AT_UNDERLINE);
+ else if (linebuf[curr] == '_')
+ STOREC(c, AT_UNDERLINE);
+ else if (control_char(c))
+ goto do_control_char;
+ else
+ STOREC(c, AT_NORMAL);
+ } else if (c == '\b')
+ {
+ switch (bs_mode)
+ {
+ case BS_NORMAL:
+ STOREC(c, AT_NORMAL);
+ break;
+ case BS_CONTROL:
+ goto do_control_char;
+ case BS_SPECIAL:
+ if (curr == 0)
+ break;
+ backc();
+ overstrike = 1;
+ break;
+ }
+ } else if (c == '\t')
+ {
+ /*
+ * Expand a tab into spaces.
+ */
+ if (tabstop == 0)
+ tabstop = 1;
+ do
+ {
+ STOREC(' ', AT_NORMAL);
+ } while ((column % tabstop) != 0);
+ } else if (control_char(c))
+ {
+ do_control_char:
+ if (ctldisp == 0)
+ {
+ /*
+ * Output as a normal character.
+ */
+ STOREC(c, AT_NORMAL);
+ } else
+ {
+ /*
+ * Convert to printable representation.
+ */
+ s = prchar(c);
+ a = binattr;
+
+ /*
+ * Make sure we can get the entire representation
+ * of the character on this line.
+ */
+ if (column + (int) strlen(s) +
+ attr_swidth(a) + attr_ewidth(a) > sc_width)
+ return (1);
+
+ for ( ; *s != 0; s++)
+ STOREC(*s, a);
+ }
+ } else
+ {
+ STOREC(c, AT_NORMAL);
+ }
+
+ return (0);
+}
+
+/*
+ * Terminate the line in the line buffer.
+ */
+ public void
+pdone(endline)
+ int endline;
+{
+ if (pendc && (pendc != '\r' || !endline))
+ /*
+ * If we had a pending character, put it in the buffer.
+ * But discard a pending CR if we are at end of line
+ * (that is, discard the CR in a CR/LF sequence).
+ */
+ (void) do_append(pendc, pendpos);
+
+ /*
+ * Add a newline if necessary,
+ * and append a '\0' to the end of the line.
+ */
+ if (column < sc_width || !auto_wrap || ignaw || ctldisp == 0)
+ {
+ linebuf[curr] = '\n';
+ attr[curr] = AT_NORMAL;
+ curr++;
+ }
+ linebuf[curr] = '\0';
+ attr[curr] = AT_NORMAL;
+}
+
+/*
+ * Get a character from the current line.
+ * Return the character as the function return value,
+ * and the character attribute in *ap.
+ */
+ public int
+gline(i, ap)
+ register int i;
+ register int *ap;
+{
+ char *s;
+
+ if (is_null_line)
+ {
+ /*
+ * If there is no current line, we pretend the line is
+ * either "~" or "", depending on the "twiddle" flag.
+ */
+ *ap = AT_NORMAL;
+ s = (twiddle) ? "~\n" : "\n";
+ return (s[i]);
+ }
+
+ *ap = attr[i];
+ return (linebuf[i] & 0377);
+}
+
+/*
+ * Indicate that there is no current line.
+ */
+ public void
+null_line()
+{
+ is_null_line = 1;
+}
+
+#if 1
+/*
+ * Analogous to forw_line(), but deals with "raw lines":
+ * lines which are not split for screen width.
+ * {{ This is supposed to be more efficient than forw_line(). }}
+ */
+ public POSITION
+forw_raw_line(curr_pos, linep)
+ POSITION curr_pos;
+ char **linep;
+{
+ register char *p;
+ register int c;
+ POSITION new_pos;
+
+ if (curr_pos == NULL_POSITION || ch_seek(curr_pos) ||
+ (c = ch_forw_get()) == EOI)
+ return (NULL_POSITION);
+
+ p = linebuf;
+
+ for (;;)
+ {
+ if (c == '\n' || c == EOI)
+ {
+ new_pos = ch_tell();
+ break;
+ }
+ if (p >= &linebuf[sizeof(linebuf)-1])
+ {
+ /*
+ * Overflowed the input buffer.
+ * Pretend the line ended here.
+ * {{ The line buffer is supposed to be big
+ * enough that this never happens. }}
+ */
+ new_pos = ch_tell() - 1;
+ break;
+ }
+ *p++ = c;
+ c = ch_forw_get();
+ }
+ *p = '\0';
+ if (linep != NULL)
+ *linep = linebuf;
+ return (new_pos);
+}
+
+/*
+ * Analogous to back_line(), but deals with "raw lines".
+ * {{ This is supposed to be more efficient than back_line(). }}
+ */
+ public POSITION
+back_raw_line(curr_pos, linep)
+ POSITION curr_pos;
+ char **linep;
+{
+ register char *p;
+ register int c;
+ POSITION new_pos;
+
+ if (curr_pos == NULL_POSITION || curr_pos <= ch_zero() ||
+ ch_seek(curr_pos-1))
+ return (NULL_POSITION);
+
+ p = &linebuf[sizeof(linebuf)];
+ *--p = '\0';
+
+ for (;;)
+ {
+ c = ch_back_get();
+ if (c == '\n')
+ {
+ /*
+ * This is the newline ending the previous line.
+ * We have hit the beginning of the line.
+ */
+ new_pos = ch_tell() + 1;
+ break;
+ }
+ if (c == EOI)
+ {
+ /*
+ * We have hit the beginning of the file.
+ * This must be the first line in the file.
+ * This must, of course, be the beginning of the line.
+ */
+ new_pos = ch_zero();
+ break;
+ }
+ if (p <= linebuf)
+ {
+ /*
+ * Overflowed the input buffer.
+ * Pretend the line ended here.
+ */
+ new_pos = ch_tell() + 1;
+ break;
+ }
+ *--p = c;
+ }
+ if (linep != NULL)
+ *linep = p;
+ return (new_pos);
+}
+#endif
diff --git a/usr.bin/less/linenum.c b/usr.bin/less/linenum.c
new file mode 100644
index 00000000000..5c4f3c72280
--- /dev/null
+++ b/usr.bin/less/linenum.c
@@ -0,0 +1,471 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * Code to handle displaying line numbers.
+ *
+ * Finding the line number of a given file position is rather tricky.
+ * We don't want to just start at the beginning of the file and
+ * count newlines, because that is slow for large files (and also
+ * wouldn't work if we couldn't get to the start of the file; e.g.
+ * if input is a long pipe).
+ *
+ * So we use the function add_lnum to cache line numbers.
+ * We try to be very clever and keep only the more interesting
+ * line numbers when we run out of space in our table. A line
+ * number is more interesting than another when it is far from
+ * other line numbers. For example, we'd rather keep lines
+ * 100,200,300 than 100,101,300. 200 is more interesting than
+ * 101 because 101 can be derived very cheaply from 100, while
+ * 200 is more expensive to derive from 100.
+ *
+ * The function currline() returns the line number of a given
+ * position in the file. As a side effect, it calls add_lnum
+ * to cache the line number. Therefore currline is occasionally
+ * called to make sure we cache line numbers often enough.
+ */
+
+#include "less.h"
+#include "position.h"
+
+/*
+ * Structure to keep track of a line number and the associated file position.
+ * A doubly-linked circular list of line numbers is kept ordered by line number.
+ */
+struct linenum
+{
+ struct linenum *next; /* Link to next in the list */
+ struct linenum *prev; /* Line to previous in the list */
+ POSITION pos; /* File position */
+ POSITION gap; /* Gap between prev and next */
+ int line; /* Line number */
+};
+/*
+ * "gap" needs some explanation: the gap of any particular line number
+ * is the distance between the previous one and the next one in the list.
+ * ("Distance" means difference in file position.) In other words, the
+ * gap of a line number is the gap which would be introduced if this
+ * line number were deleted. It is used to decide which one to replace
+ * when we have a new one to insert and the table is full.
+ */
+
+#define NPOOL 50 /* Size of line number pool */
+
+#define LONGTIME (2) /* In seconds */
+
+public int lnloop = 0; /* Are we in the line num loop? */
+
+static struct linenum anchor; /* Anchor of the list */
+static struct linenum *freelist; /* Anchor of the unused entries */
+static struct linenum pool[NPOOL]; /* The pool itself */
+static struct linenum *spare; /* We always keep one spare entry */
+
+extern int linenums;
+extern int sigs;
+extern int sc_height;
+
+/*
+ * Initialize the line number structures.
+ */
+ public void
+clr_linenum()
+{
+ register struct linenum *p;
+
+ /*
+ * Put all the entries on the free list.
+ * Leave one for the "spare".
+ */
+ for (p = pool; p < &pool[NPOOL-2]; p++)
+ p->next = p+1;
+ pool[NPOOL-2].next = NULL;
+ freelist = pool;
+
+ spare = &pool[NPOOL-1];
+
+ /*
+ * Initialize the anchor.
+ */
+ anchor.next = anchor.prev = &anchor;
+ anchor.gap = 0;
+ anchor.pos = (POSITION)0;
+ anchor.line = 1;
+}
+
+/*
+ * Calculate the gap for an entry.
+ */
+ static void
+calcgap(p)
+ register struct linenum *p;
+{
+ /*
+ * Don't bother to compute a gap for the anchor.
+ * Also don't compute a gap for the last one in the list.
+ * The gap for that last one should be considered infinite,
+ * but we never look at it anyway.
+ */
+ if (p == &anchor || p->next == &anchor)
+ return;
+ p->gap = p->next->pos - p->prev->pos;
+}
+
+/*
+ * Add a new line number to the cache.
+ * The specified position (pos) should be the file position of the
+ * FIRST character in the specified line.
+ */
+ public void
+add_lnum(lno, pos)
+ int lno;
+ POSITION pos;
+{
+ register struct linenum *p;
+ register struct linenum *new;
+ register struct linenum *nextp;
+ register struct linenum *prevp;
+ register POSITION mingap;
+
+ /*
+ * Find the proper place in the list for the new one.
+ * The entries are sorted by position.
+ */
+ for (p = anchor.next; p != &anchor && p->pos < pos; p = p->next)
+ if (p->line == lno)
+ /* We already have this one. */
+ return;
+ nextp = p;
+ prevp = p->prev;
+
+ if (freelist != NULL)
+ {
+ /*
+ * We still have free (unused) entries.
+ * Use one of them.
+ */
+ new = freelist;
+ freelist = freelist->next;
+ } else
+ {
+ /*
+ * No free entries.
+ * Use the "spare" entry.
+ */
+ new = spare;
+ spare = NULL;
+ }
+
+ /*
+ * Fill in the fields of the new entry,
+ * and insert it into the proper place in the list.
+ */
+ new->next = nextp;
+ new->prev = prevp;
+ new->pos = pos;
+ new->line = lno;
+
+ nextp->prev = new;
+ prevp->next = new;
+
+ /*
+ * Recalculate gaps for the new entry and the neighboring entries.
+ */
+ calcgap(new);
+ calcgap(nextp);
+ calcgap(prevp);
+
+ if (spare == NULL)
+ {
+ /*
+ * We have used the spare entry.
+ * Scan the list to find the one with the smallest
+ * gap, take it out and make it the spare.
+ * We should never remove the last one, so stop when
+ * we get to p->next == &anchor. This also avoids
+ * looking at the gap of the last one, which is
+ * not computed by calcgap.
+ */
+ mingap = anchor.next->gap;
+ for (p = anchor.next; p->next != &anchor; p = p->next)
+ {
+ if (p->gap <= mingap)
+ {
+ spare = p;
+ mingap = p->gap;
+ }
+ }
+ spare->next->prev = spare->prev;
+ spare->prev->next = spare->next;
+ }
+}
+
+/*
+ * If we get stuck in a long loop trying to figure out the
+ * line number, print a message to tell the user what we're doing.
+ */
+ static void
+longloopmessage()
+{
+ ierror("Calculating line numbers", NULL_PARG);
+ /*
+ * Set the lnloop flag here, so if the user interrupts while
+ * we are calculating line numbers, the signal handler will
+ * turn off line numbers (linenums=0).
+ */
+ lnloop = 1;
+}
+
+static int loopcount;
+#if HAVE_TIME
+static long startime;
+#endif
+
+ static void
+longish()
+{
+#if HAVE_TIME
+ if (loopcount >= 0 && ++loopcount > 100)
+ {
+ loopcount = 0;
+ if (get_time() >= startime + LONGTIME)
+ {
+ longloopmessage();
+ loopcount = -1;
+ }
+ }
+#else
+ if (loopcount >= 0 && ++loopcount > LONGLOOP)
+ {
+ longloopmessage();
+ loopcount = -1;
+ }
+#endif
+}
+
+/*
+ * Find the line number associated with a given position.
+ * Return 0 if we can't figure it out.
+ */
+ public int
+find_linenum(pos)
+ POSITION pos;
+{
+ register struct linenum *p;
+ register int lno;
+ POSITION cpos;
+
+ if (!linenums)
+ /*
+ * We're not using line numbers.
+ */
+ return (0);
+ if (pos == NULL_POSITION)
+ /*
+ * Caller doesn't know what he's talking about.
+ */
+ return (0);
+ if (pos <= ch_zero())
+ /*
+ * Beginning of file is always line number 1.
+ */
+ return (1);
+
+ /*
+ * Find the entry nearest to the position we want.
+ */
+ for (p = anchor.next; p != &anchor && p->pos < pos; p = p->next)
+ continue;
+ if (p->pos == pos)
+ /* Found it exactly. */
+ return (p->line);
+
+ /*
+ * This is the (possibly) time-consuming part.
+ * We start at the line we just found and start
+ * reading the file forward or backward till we
+ * get to the place we want.
+ *
+ * First decide whether we should go forward from the
+ * previous one or backwards from the next one.
+ * The decision is based on which way involves
+ * traversing fewer bytes in the file.
+ */
+ flush();
+#if HAVE_TIME
+ startime = get_time();
+#endif
+ if (p == &anchor || pos - p->prev->pos < p->pos - pos)
+ {
+ /*
+ * Go forward.
+ */
+ p = p->prev;
+ if (ch_seek(p->pos))
+ return (0);
+ loopcount = 0;
+ for (lno = p->line, cpos = p->pos; cpos < pos; lno++)
+ {
+ /*
+ * Allow a signal to abort this loop.
+ */
+ cpos = forw_raw_line(cpos, (char **)NULL);
+ if (ABORT_SIGS() || cpos == NULL_POSITION)
+ return (0);
+ longish();
+ }
+ lnloop = 0;
+ /*
+ * We might as well cache it.
+ */
+ add_lnum(lno, cpos);
+ /*
+ * If the given position is not at the start of a line,
+ * make sure we return the correct line number.
+ */
+ if (cpos > pos)
+ lno--;
+ } else
+ {
+ /*
+ * Go backward.
+ */
+ if (ch_seek(p->pos))
+ return (0);
+ loopcount = 0;
+ for (lno = p->line, cpos = p->pos; cpos > pos; lno--)
+ {
+ /*
+ * Allow a signal to abort this loop.
+ */
+ cpos = back_raw_line(cpos, (char **)NULL);
+ if (ABORT_SIGS() || cpos == NULL_POSITION)
+ return (0);
+ longish();
+ }
+ lnloop = 0;
+ /*
+ * We might as well cache it.
+ */
+ add_lnum(lno, cpos);
+ }
+
+ return (lno);
+}
+
+/*
+ * Find the position of a given line number.
+ * Return NULL_POSITION if we can't figure it out.
+ */
+ public POSITION
+find_pos(lno)
+ int lno;
+{
+ register struct linenum *p;
+ POSITION cpos;
+ int clno;
+
+ if (lno <= 1)
+ /*
+ * Line number 1 is beginning of file.
+ */
+ return (ch_zero());
+
+ /*
+ * Find the entry nearest to the line number we want.
+ */
+ for (p = anchor.next; p != &anchor && p->line < lno; p = p->next)
+ continue;
+ if (p->line == lno)
+ /* Found it exactly. */
+ return (p->pos);
+
+ flush();
+ if (p == &anchor || lno - p->prev->line < p->line - lno)
+ {
+ /*
+ * Go forward.
+ */
+ p = p->prev;
+ if (ch_seek(p->pos))
+ return (NULL_POSITION);
+ for (clno = p->line, cpos = p->pos; clno < lno; clno++)
+ {
+ /*
+ * Allow a signal to abort this loop.
+ */
+ cpos = forw_raw_line(cpos, (char **)NULL);
+ if (ABORT_SIGS() || cpos == NULL_POSITION)
+ return (NULL_POSITION);
+ }
+ } else
+ {
+ /*
+ * Go backward.
+ */
+ if (ch_seek(p->pos))
+ return (NULL_POSITION);
+ for (clno = p->line, cpos = p->pos; clno > lno; clno--)
+ {
+ /*
+ * Allow a signal to abort this loop.
+ */
+ cpos = back_raw_line(cpos, (char **)NULL);
+ if (ABORT_SIGS() || cpos == NULL_POSITION)
+ return (NULL_POSITION);
+ }
+ }
+ /*
+ * We might as well cache it.
+ */
+ add_lnum(clno, cpos);
+ return (cpos);
+}
+
+/*
+ * Return the line number of the "current" line.
+ * The argument "where" tells which line is to be considered
+ * the "current" line (e.g. TOP, BOTTOM, MIDDLE, etc).
+ */
+ public int
+currline(where)
+ int where;
+{
+ POSITION pos;
+ POSITION len;
+ int lnum;
+
+ pos = position(where);
+ len = ch_length();
+ while (pos == NULL_POSITION && where >= 0 && where < sc_height)
+ pos = position(++where);
+ if (pos == NULL_POSITION)
+ pos = len;
+ lnum = find_linenum(pos);
+ if (pos == len)
+ lnum--;
+ return (lnum);
+}
diff --git a/usr.bin/less/lsystem.c b/usr.bin/less/lsystem.c
new file mode 100644
index 00000000000..db0e4fca2fd
--- /dev/null
+++ b/usr.bin/less/lsystem.c
@@ -0,0 +1,300 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * Routines to execute other programs.
+ * Necessarily very OS dependent.
+ */
+
+#include <signal.h>
+#include "less.h"
+#include "position.h"
+
+#if MSOFTC
+#include <dos.h>
+#endif
+
+extern int screen_trashed;
+extern IFILE curr_ifile;
+
+
+#if HAVE_SYSTEM
+
+/*
+ * Pass the specified command to a shell to be executed.
+ * Like plain "system()", but handles resetting terminal modes, etc.
+ */
+ public void
+lsystem(cmd)
+ char *cmd;
+{
+ register int inp;
+#if MSOFTC || OS2
+ register int inp2;
+#endif
+ register char *shell;
+ register char *p;
+ IFILE save_ifile;
+
+ /*
+ * Print the command which is to be executed,
+ * unless the command starts with a "-".
+ */
+ if (cmd[0] == '-')
+ cmd++;
+ else
+ {
+ clear_bot();
+ putstr("!");
+ putstr(cmd);
+ putstr("\n");
+ }
+
+ /*
+ * Close the current input file.
+ */
+ save_ifile = curr_ifile;
+ (void) edit_ifile(NULL_IFILE);
+
+ /*
+ * De-initialize the terminal and take out of raw mode.
+ */
+ deinit();
+ flush(); /* Make sure the deinit chars get out */
+ raw_mode(0);
+
+ /*
+ * Restore signals to their defaults.
+ */
+ init_signals(0);
+
+ /*
+ * Force standard input to be the user's terminal
+ * (the normal standard input), even if less's standard input
+ * is coming from a pipe.
+ */
+ inp = dup(0);
+ close(0);
+ if (OPEN_TTYIN() < 0)
+ dup(inp);
+
+ /*
+ * Pass the command to the system to be executed.
+ * If we have a SHELL environment variable, use
+ * <$SHELL -c "command"> instead of just <command>.
+ * If the command is empty, just invoke a shell.
+ */
+#if HAVE_SHELL
+ p = NULL;
+ if ((shell = getenv("SHELL")) != NULL && *shell != '\0')
+ {
+ if (*cmd == '\0')
+ p = save(shell);
+ else
+ {
+ p = (char *) ecalloc(strlen(shell) + strlen(cmd) + 7,
+ sizeof(char));
+ sprintf(p, "%s -c \"%s\"", shell, cmd);
+ }
+ }
+ if (p == NULL)
+ {
+ if (*cmd == '\0')
+ p = save("sh");
+ else
+ p = save(cmd);
+ }
+
+ system(p);
+ free(p);
+#else
+#if OS2
+ if (*cmd == '\0')
+ cmd = "cmd.exe";
+#endif
+ system(cmd);
+#endif
+
+ /*
+ * Restore standard input, reset signals, raw mode, etc.
+ */
+ close(0);
+ dup(inp);
+ close(inp);
+
+ init_signals(1);
+ raw_mode(1);
+ init();
+ screen_trashed = 1;
+
+ /*
+ * Reopen the current input file.
+ */
+ if (edit_ifile(save_ifile))
+ quit(QUIT_ERROR);
+
+#if defined(SIGWINCH) || defined(SIGWIND)
+ /*
+ * Since we were ignoring window change signals while we executed
+ * the system command, we must assume the window changed.
+ * Warning: this leaves a signal pending (in "sigs"),
+ * so psignals() should be called soon after lsystem().
+ */
+ winch(0);
+#endif
+}
+
+#endif
+
+#if PIPEC
+
+/*
+ * Pipe a section of the input file into the given shell command.
+ * The section to be piped is the section "between" the current
+ * position and the position marked by the given letter.
+ *
+ * The "current" position means the top line displayed if the mark
+ * is after the current screen, or the bottom line displayed if
+ * the mark is before the current screen.
+ * If the mark is on the current screen, the whole screen is displayed.
+ */
+ public int
+pipe_mark(c, cmd)
+ int c;
+ char *cmd;
+{
+ POSITION mpos, tpos, bpos;
+
+ /*
+ * mpos = the marked position.
+ * tpos = top of screen.
+ * bpos = bottom of screen.
+ */
+ mpos = markpos(c);
+ if (mpos == NULL_POSITION)
+ return (-1);
+ tpos = position(TOP);
+ if (tpos == NULL_POSITION)
+ tpos = ch_zero();
+ bpos = position(BOTTOM);
+
+ if (c == '.')
+ return (pipe_data(cmd, tpos, bpos));
+ else if (mpos <= tpos)
+ return (pipe_data(cmd, mpos, tpos));
+ else if (bpos == NULL_POSITION)
+ return (pipe_data(cmd, tpos, bpos));
+ else
+ return (pipe_data(cmd, tpos, mpos));
+}
+
+/*
+ * Create a pipe to the given shell command.
+ * Feed it the file contents between the positions spos and epos.
+ */
+ public int
+pipe_data(cmd, spos, epos)
+ char *cmd;
+ POSITION spos;
+ POSITION epos;
+{
+ register FILE *f;
+ register int c;
+ extern FILE *popen();
+
+ /*
+ * This is structured much like lsystem().
+ * Since we're running a shell program, we must be careful
+ * to perform the necessary deinitialization before running
+ * the command, and reinitialization after it.
+ */
+ if (ch_seek(spos) != 0)
+ {
+ error("Cannot seek to start position", NULL_PARG);
+ return (-1);
+ }
+
+ if ((f = popen(cmd, "w")) == NULL)
+ {
+ error("Cannot create pipe", NULL_PARG);
+ return (-1);
+ }
+ clear_bot();
+ putstr("!");
+ putstr(cmd);
+ putstr("\n");
+
+ deinit();
+ flush();
+ raw_mode(0);
+ init_signals(0);
+#ifdef SIGPIPE
+ SIGNAL(SIGPIPE, SIG_IGN);
+#endif
+
+ c = EOI;
+ while (epos == NULL_POSITION || spos++ <= epos)
+ {
+ /*
+ * Read a character from the file and give it to the pipe.
+ */
+ c = ch_forw_get();
+ if (c == EOI)
+ break;
+ if (putc(c, f) == EOF)
+ break;
+ }
+
+ /*
+ * Finish up the last line.
+ */
+ while (c != '\n' && c != EOI )
+ {
+ c = ch_forw_get();
+ if (c == EOI)
+ break;
+ if (putc(c, f) == EOF)
+ break;
+ }
+
+ pclose(f);
+
+#ifdef SIGPIPE
+ SIGNAL(SIGPIPE, SIG_DFL);
+#endif
+ init_signals(1);
+ raw_mode(1);
+ init();
+ screen_trashed = 1;
+#if defined(SIGWINCH) || defined(SIGWIND)
+ /* {{ Probably don't need this here. }} */
+ winch(0);
+#endif
+ return (0);
+}
+
+#endif
diff --git a/usr.bin/less/main.c b/usr.bin/less/main.c
new file mode 100644
index 00000000000..ae4271056c6
--- /dev/null
+++ b/usr.bin/less/main.c
@@ -0,0 +1,337 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * Entry point, initialization, miscellaneous routines.
+ */
+
+#include "less.h"
+#include "position.h"
+
+public char * every_first_cmd = NULL;
+public int new_file;
+public int is_tty;
+public IFILE curr_ifile = NULL_IFILE;
+public IFILE old_ifile = NULL_IFILE;
+public struct scrpos initial_scrpos;
+public int any_display = FALSE;
+public int wscroll;
+public char * progname;
+public int quitting;
+
+extern int quit_at_eof;
+extern int cbufs;
+extern int errmsgs;
+extern int screen_trashed;
+extern int force_open;
+
+#if LOGFILE
+public int logfile = -1;
+public int force_logfile = FALSE;
+public char * namelogfile = NULL;
+#endif
+
+#if EDITOR
+public char * editor;
+public char * editproto;
+#endif
+
+#if TAGS
+extern char * tagfile;
+extern char * tagoption;
+extern int jump_sline;
+#endif
+
+
+
+/*
+ * Entry point.
+ */
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ IFILE ifile;
+
+#ifdef __EMX__
+ _response(&argc, &argv);
+ _wildcard(&argc, &argv);
+#endif
+
+ progname = *argv++;
+
+ /*
+ * Process command line arguments and LESS environment arguments.
+ * Command line arguments override environment arguments.
+ */
+ get_term();
+ init_cmds();
+ init_prompt();
+ init_charset();
+ init_option();
+ scan_option(getenv("LESS"));
+
+#if GNU_OPTIONS
+ /*
+ * Special case for "less --help" and "less --version".
+ */
+ if (argc == 2)
+ {
+ if (strcmp(argv[0], "--help") == 0)
+ scan_option("-?");
+ if (strcmp(argv[0], "--version") == 0)
+ scan_option("-V");
+ }
+#endif
+#define isoptstring(s) (((s)[0] == '-' || (s)[0] == '+') && (s)[1] != '\0')
+ while (--argc > 0 && (isoptstring(argv[0]) || isoptpending()))
+ scan_option(*argv++);
+#undef isoptstring
+
+ if (isoptpending())
+ {
+ /*
+ * Last command line option was a flag requiring a
+ * following string, but there was no following string.
+ */
+ nopendopt();
+ quit(QUIT_OK);
+ }
+
+#if EDITOR
+ editor = getenv("VISUAL");
+ if (editor == NULL || *editor == '\0')
+ {
+ editor = getenv("EDITOR");
+ if (editor == NULL || *editor == '\0')
+ editor = EDIT_PGM;
+ }
+ editproto = getenv("LESSEDIT");
+ if (editproto == NULL || *editproto == '\0')
+ editproto = "%E ?lm+%lm. %f";
+#endif
+
+ /*
+ * Call get_ifile with all the command line filenames
+ * to "register" them with the ifile system.
+ */
+ ifile = NULL_IFILE;
+ while (--argc >= 0)
+ {
+#if MSOFTC || OS2
+ /*
+ * Because the "shell" doesn't expand filename patterns,
+ * treat each argument as a filename pattern rather than
+ * a single filename.
+ * Expand the pattern and iterate over the expanded list.
+ */
+ struct textlist tlist;
+ char *gfilename;
+ char *filename;
+
+ gfilename = glob(*argv++);
+ init_textlist(&tlist, gfilename);
+ filename = NULL;
+ while ((filename = forw_textlist(&tlist, filename)) != NULL)
+ ifile = get_ifile(filename, ifile);
+ free(gfilename);
+#else
+ ifile = get_ifile(*argv++, ifile);
+#endif
+ }
+ /*
+ * Set up terminal, etc.
+ */
+ is_tty = isatty(1);
+ if (!is_tty)
+ {
+ /*
+ * Output is not a tty.
+ * Just copy the input file(s) to output.
+ */
+ if (nifile() == 0)
+ {
+ if (edit_stdin() == 0)
+ cat_file();
+ } else if (edit_first() == 0)
+ {
+ do {
+ cat_file();
+ } while (edit_next(1) == 0);
+ }
+ quit(QUIT_OK);
+ }
+
+ init_mark();
+ raw_mode(1);
+ open_getchr();
+ init_signals(1);
+
+ /*
+ * Select the first file to examine.
+ */
+#if TAGS
+ if (tagoption != NULL)
+ {
+ /*
+ * A -t option was given.
+ * Verify that no filenames were also given.
+ * Edit the file selected by the "tags" search,
+ * and search for the proper line in the file.
+ */
+ if (nifile() > 0)
+ {
+ error("No filenames allowed with -t option", NULL_PARG);
+ quit(QUIT_ERROR);
+ }
+ findtag(tagoption);
+ if (tagfile == NULL)
+ quit(QUIT_ERROR);
+ if (edit(tagfile)) /* Edit file which contains the tag */
+ quit(QUIT_ERROR);
+ /*
+ * Search for the line which contains the tag.
+ * Set up initial_scrpos so we display that line.
+ */
+ initial_scrpos.pos = tagsearch();
+ if (initial_scrpos.pos == NULL_POSITION)
+ quit(QUIT_ERROR);
+ initial_scrpos.ln = jump_sline;
+ } else
+#endif
+ if (nifile() == 0)
+ {
+ if (edit_stdin()) /* Edit standard input */
+ quit(QUIT_ERROR);
+ } else
+ {
+ if (edit_first()) /* Edit first valid file in cmd line */
+ quit(QUIT_ERROR);
+ }
+
+ init();
+ commands();
+ quit(QUIT_OK);
+ /*NOTREACHED*/
+}
+
+/*
+ * Copy a string, truncating to the specified length if necessary.
+ * Unlike strncpy(), the resulting string is guaranteed to be null-terminated.
+ */
+ public void
+strtcpy(to, from, len)
+ char *to;
+ char *from;
+ unsigned int len;
+{
+ strncpy(to, from, len);
+ to[len-1] = '\0';
+}
+
+/*
+ * Copy a string to a "safe" place
+ * (that is, to a buffer allocated by calloc).
+ */
+ public char *
+save(s)
+ char *s;
+{
+ register char *p;
+
+ p = (char *) ecalloc(strlen(s)+1, sizeof(char));
+ strcpy(p, s);
+ return (p);
+}
+
+/*
+ * Allocate memory.
+ * Like calloc(), but never returns an error (NULL).
+ */
+ public VOID_POINTER
+ecalloc(count, size)
+ int count;
+ unsigned int size;
+{
+ register VOID_POINTER p;
+
+ p = (VOID_POINTER) calloc(count, size);
+ if (p != NULL)
+ return (p);
+ error("Cannot allocate memory", NULL_PARG);
+ quit(QUIT_ERROR);
+ /*NOTREACHED*/
+}
+
+/*
+ * Skip leading spaces in a string.
+ */
+ public char *
+skipsp(s)
+ register char *s;
+{
+ while (*s == ' ' || *s == '\t')
+ s++;
+ return (s);
+}
+
+/*
+ * Exit the program.
+ */
+ public void
+quit(status)
+ int status;
+{
+ static int save_status;
+
+ /*
+ * Put cursor at bottom left corner, clear the line,
+ * reset the terminal modes, and exit.
+ */
+ if (status < 0)
+ status = save_status;
+ else
+ save_status = status;
+ quitting = 1;
+ edit((char*)NULL);
+ if (any_display)
+ clear_bot();
+ deinit();
+ flush();
+ raw_mode(0);
+#if MSOFTC
+ /*
+ * If we don't close 2, we get some garbage from
+ * 2's buffer when it flushes automatically.
+ * I cannot track this one down RB
+ * The same bug shows up if we use ^C^C to abort.
+ */
+ close(2);
+#endif
+ exit(status);
+}
diff --git a/usr.bin/less/mark.c b/usr.bin/less/mark.c
new file mode 100644
index 00000000000..678ab60162d
--- /dev/null
+++ b/usr.bin/less/mark.c
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include "less.h"
+#include "position.h"
+
+extern IFILE curr_ifile;
+extern int sc_height;
+extern int jump_sline;
+
+/*
+ * A mark is an ifile (input file) plus a position within the file.
+ */
+struct mark {
+ IFILE m_ifile;
+ struct scrpos m_scrpos;
+};
+
+/*
+ * The table of marks.
+ * Each mark is identified by a lowercase or uppercase letter.
+ */
+#define NMARKS (2*26) /* a-z, A-Z */
+static struct mark marks[NMARKS];
+
+/*
+ * Special mark for the "last mark"; addressed by the apostrophe.
+ */
+static struct mark lmark;
+
+/*
+ * Initialize the mark table to show no marks are set.
+ */
+ public void
+init_mark()
+{
+ int i;
+
+ for (i = 0; i < NMARKS; i++)
+ marks[i].m_scrpos.pos = NULL_POSITION;
+ lmark.m_scrpos.pos = NULL_POSITION;
+}
+
+/*
+ * See if a mark letter is valid (between a and z).
+ */
+ static struct mark *
+getumark(c)
+ int c;
+{
+ if (c >= 'a' && c <= 'z')
+ return (&marks[c-'a']);
+
+ if (c >= 'A' && c <= 'Z')
+ return (&marks[c-'A'+26]);
+
+ error("Invalid mark letter", NULL_PARG);
+ return (NULL);
+}
+
+/*
+ * Get the mark structure identified by a character.
+ * The mark struct may come either from the mark table
+ * or may be constructed on the fly for certain characters like ^, $.
+ */
+ static struct mark *
+getmark(c)
+ int c;
+{
+ register struct mark *m;
+ static struct mark sm;
+
+ switch (c)
+ {
+ case '^':
+ /*
+ * Beginning of the current file.
+ */
+ m = &sm;
+ m->m_scrpos.pos = ch_zero();
+ m->m_scrpos.ln = 0;
+ m->m_ifile = curr_ifile;
+ break;
+ case '$':
+ /*
+ * End of the current file.
+ */
+ if (ch_end_seek())
+ {
+ error("Cannot seek to end of file", NULL_PARG);
+ return (NULL);
+ }
+ m = &sm;
+ m->m_scrpos.pos = ch_tell();
+ m->m_scrpos.ln = sc_height-1;
+ m->m_ifile = curr_ifile;
+ break;
+ case '.':
+ /*
+ * Current position in the current file.
+ */
+ m = &sm;
+ m->m_scrpos.pos = ch_tell();
+ m->m_scrpos.ln = 0;
+ m->m_ifile = curr_ifile;
+ break;
+ case '\'':
+ /*
+ * The "last mark".
+ */
+ m = &lmark;
+ break;
+ default:
+ /*
+ * Must be a user-defined mark.
+ */
+ m = getumark(c);
+ if (m == NULL)
+ break;
+ if (m->m_scrpos.pos == NULL_POSITION)
+ {
+ error("Mark not set", NULL_PARG);
+ return (NULL);
+ }
+ break;
+ }
+ return (m);
+}
+
+/*
+ * Is a mark letter is invalid?
+ */
+ public int
+badmark(c)
+ int c;
+{
+ return (getmark(c) == NULL);
+}
+
+/*
+ * Set a user-defined mark.
+ */
+ public void
+setmark(c)
+ int c;
+{
+ register struct mark *m;
+ struct scrpos scrpos;
+
+ m = getumark(c);
+ if (m == NULL)
+ return;
+ get_scrpos(&scrpos);
+ m->m_scrpos = scrpos;
+ m->m_ifile = curr_ifile;
+}
+
+/*
+ * Set lmark (the mark named by the apostrophe).
+ */
+ public void
+lastmark()
+{
+ struct scrpos scrpos;
+
+ get_scrpos(&scrpos);
+ if (scrpos.pos == NULL_POSITION)
+ return;
+ lmark.m_scrpos = scrpos;
+ lmark.m_ifile = curr_ifile;
+}
+
+/*
+ * Go to a mark.
+ */
+ public void
+gomark(c)
+ int c;
+{
+ register struct mark *m;
+ struct scrpos scrpos;
+
+ m = getmark(c);
+ if (m == NULL)
+ return;
+
+ /*
+ * If we're trying to go to the lastmark and
+ * it has not been set to anything yet,
+ * set it to the beginning of the current file.
+ */
+ if (m == &lmark && m->m_scrpos.pos == NULL_POSITION)
+ {
+ m->m_ifile = curr_ifile;
+ m->m_scrpos.pos = ch_zero();
+ m->m_scrpos.ln = jump_sline;
+ }
+
+ /*
+ * If we're using lmark, we must save the screen position now,
+ * because if we call edit_ifile() below, lmark will change.
+ * (We save the screen position even if we're not using lmark.)
+ */
+ scrpos = m->m_scrpos;
+ if (m->m_ifile != curr_ifile)
+ {
+ /*
+ * Not in the current file; edit the correct file.
+ */
+ if (edit_ifile(m->m_ifile))
+ return;
+ }
+
+ jump_loc(scrpos.pos, scrpos.ln);
+}
+
+/*
+ * Return the position associated with a given mark letter.
+ *
+ * We don't return which screen line the position
+ * is associated with, but this doesn't matter much,
+ * because it's always the first non-blank line on the screen.
+ */
+ public POSITION
+markpos(c)
+ int c;
+{
+ register struct mark *m;
+
+ m = getmark(c);
+ if (m == NULL)
+ return (NULL_POSITION);
+
+ if (m->m_ifile != curr_ifile)
+ {
+ error("Mark not in current file", NULL_PARG);
+ return (NULL_POSITION);
+ }
+ return (m->m_scrpos.pos);
+}
diff --git a/usr.bin/less/mkinstalldirs b/usr.bin/less/mkinstalldirs
new file mode 100644
index 00000000000..91f6d04e17c
--- /dev/null
+++ b/usr.bin/less/mkinstalldirs
@@ -0,0 +1,32 @@
+#!/bin/sh
+# mkinstalldirs --- make directory hierarchy
+# Author: Noah Friedman <friedman@prep.ai.mit.edu>
+# Created: 1993-05-16
+# Last modified: 1994-03-25
+# Public domain
+
+errstatus=0
+
+for file in ${1+"$@"} ; do
+ set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
+ shift
+
+ pathcomp=
+ for d in ${1+"$@"} ; do
+ pathcomp="$pathcomp$d"
+ case "$pathcomp" in
+ -* ) pathcomp=./$pathcomp ;;
+ esac
+
+ if test ! -d "$pathcomp"; then
+ echo "mkdir $pathcomp" 1>&2
+ mkdir "$pathcomp" || errstatus=$?
+ fi
+
+ pathcomp="$pathcomp/"
+ done
+done
+
+exit $errstatus
+
+# mkinstalldirs ends here
diff --git a/usr.bin/less/optfunc.c b/usr.bin/less/optfunc.c
new file mode 100644
index 00000000000..c11336a0bd5
--- /dev/null
+++ b/usr.bin/less/optfunc.c
@@ -0,0 +1,496 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * Handling functions for command line options.
+ *
+ * Most options are handled by the generic code in option.c.
+ * But all string options, and a few non-string options, require
+ * special handling specific to the particular option.
+ * This special processing is done by the "handling functions" in this file.
+ *
+ * Each handling function is passed a "type" and, if it is a string
+ * option, the string which should be "assigned" to the option.
+ * The type may be one of:
+ * INIT The option is being initialized from the command line.
+ * TOGGLE The option is being changed from within the program.
+ * QUERY The setting of the option is merely being queried.
+ */
+
+#include "less.h"
+#include "option.h"
+
+extern int nbufs;
+extern int cbufs;
+extern int pr_type;
+extern int nohelp;
+extern int plusoption;
+extern int swindow;
+extern int sc_height;
+extern int any_display;
+extern char *prproto[];
+extern char *eqproto;
+extern IFILE curr_ifile;
+#if LOGFILE
+extern char *namelogfile;
+extern int force_logfile;
+extern int logfile;
+#endif
+#if TAGS
+public char *tagoption = NULL;
+extern char *tagfile;
+extern char *tags;
+extern int jump_sline;
+#endif
+#if MSOFTC
+extern int nm_fg_color, nm_bg_color;
+extern int bo_fg_color, bo_bg_color;
+extern int ul_fg_color, ul_bg_color;
+extern int so_fg_color, so_bg_color;
+extern int bl_fg_color, bl_bg_color;
+#endif
+
+
+#if LOGFILE
+/*
+ * Handler for -o option.
+ */
+ public void
+opt_o(type, s)
+ int type;
+ char *s;
+{
+ PARG parg;
+
+ switch (type)
+ {
+ case INIT:
+ namelogfile = s;
+ break;
+ case TOGGLE:
+ if (ch_getflags() & CH_CANSEEK)
+ {
+ error("Input is not a pipe", NULL_PARG);
+ return;
+ }
+ if (logfile >= 0)
+ {
+ error("Log file is already in use", NULL_PARG);
+ return;
+ }
+ s = skipsp(s);
+ namelogfile = glob(s);
+ use_logfile(namelogfile);
+ sync_logfile();
+ break;
+ case QUERY:
+ if (logfile < 0)
+ error("No log file", NULL_PARG);
+ else
+ {
+ parg.p_string = namelogfile;
+ error("Log file \"%s\"", &parg);
+ }
+ break;
+ }
+}
+
+/*
+ * Handler for -O option.
+ */
+ public void
+opt__O(type, s)
+ int type;
+ char *s;
+{
+ force_logfile = TRUE;
+ opt_o(type, s);
+}
+#endif
+
+/*
+ * Handlers for -l option.
+ */
+ public void
+opt_l(type, s)
+ int type;
+ char *s;
+{
+ int err;
+ int n;
+ char *t;
+
+ switch (type)
+ {
+ case INIT:
+ t = s;
+ n = getnum(&t, 'l', &err);
+ if (err || n <= 0)
+ {
+ error("Line number is required after -l", NULL_PARG);
+ return;
+ }
+ plusoption = TRUE;
+ ungetsc(s);
+ break;
+ }
+}
+
+#if USERFILE
+ public void
+opt_k(type, s)
+ int type;
+ char *s;
+{
+ PARG parg;
+
+ switch (type)
+ {
+ case INIT:
+ if (lesskey(s))
+ {
+ parg.p_string = s;
+ error("Cannot use lesskey file \"%s\"", &parg);
+ }
+ break;
+ }
+}
+#endif
+
+#if TAGS
+/*
+ * Handler for -t option.
+ */
+ public void
+opt_t(type, s)
+ int type;
+ char *s;
+{
+ IFILE save_ifile;
+ POSITION pos;
+
+ switch (type)
+ {
+ case INIT:
+ tagoption = s;
+ /* Do the rest in main() */
+ break;
+ case TOGGLE:
+ findtag(skipsp(s));
+ if (tagfile == NULL)
+ break;
+ save_ifile = curr_ifile;
+ if (edit(tagfile))
+ break;
+ if ((pos = tagsearch()) == NULL_POSITION)
+ {
+ if (edit_ifile(save_ifile))
+ quit(QUIT_ERROR);
+ break;
+ }
+ jump_loc(pos, jump_sline);
+ break;
+ }
+}
+
+/*
+ * Handler for -T option.
+ */
+ public void
+opt__T(type, s)
+ int type;
+ char *s;
+{
+ PARG parg;
+
+ switch (type)
+ {
+ case INIT:
+ tags = s;
+ break;
+ case TOGGLE:
+ s = skipsp(s);
+ tags = glob(s);
+ break;
+ case QUERY:
+ parg.p_string = tags;
+ error("Tags file \"%s\"", &parg);
+ break;
+ }
+}
+#endif
+
+/*
+ * Handler for -p option.
+ */
+ public void
+opt_p(type, s)
+ int type;
+ register char *s;
+{
+ switch (type)
+ {
+ case INIT:
+ /*
+ * Unget a search command for the specified string.
+ * {{ This won't work if the "/" command is
+ * changed or invalidated by a .lesskey file. }}
+ */
+ plusoption = TRUE;
+ ungetsc(s);
+ ungetsc("/");
+ break;
+ }
+}
+
+/*
+ * Handler for -P option.
+ */
+ public void
+opt__P(type, s)
+ int type;
+ register char *s;
+{
+ register char **proto;
+ PARG parg;
+
+ switch (type)
+ {
+ case INIT:
+ case TOGGLE:
+ /*
+ * Figure out which prototype string should be changed.
+ */
+ switch (*s)
+ {
+ case 'm': proto = &prproto[PR_MEDIUM]; s++; break;
+ case 'M': proto = &prproto[PR_LONG]; s++; break;
+ case '=': proto = &eqproto; s++; break;
+ default: proto = &prproto[PR_SHORT]; break;
+ }
+ free(*proto);
+ *proto = save(s);
+ break;
+ case QUERY:
+ parg.p_string = prproto[pr_type];
+ error("%s", &parg);
+ break;
+ }
+}
+
+/*
+ * Handler for the -b option.
+ */
+ /*ARGSUSED*/
+ public void
+opt_b(type, s)
+ int type;
+ char *s;
+{
+ switch (type)
+ {
+ case TOGGLE:
+ case QUERY:
+ /*
+ * Allocate the new number of buffers.
+ */
+ cbufs = ch_nbuf(cbufs);
+ break;
+ case INIT:
+ break;
+ }
+}
+
+/*
+ * Handler for the -i option.
+ */
+ /*ARGSUSED*/
+ public void
+opt_i(type, s)
+ int type;
+ char *s;
+{
+ switch (type)
+ {
+ case TOGGLE:
+ chg_caseless();
+ break;
+ case QUERY:
+ case INIT:
+ break;
+ }
+}
+
+/*
+ * Handler for the -V option.
+ */
+ /*ARGSUSED*/
+ public void
+opt__V(type, s)
+ int type;
+ char *s;
+{
+ switch (type)
+ {
+ case TOGGLE:
+ case QUERY:
+ case INIT:
+ dispversion();
+ if (type == INIT)
+ quit(QUIT_OK);
+ break;
+ }
+}
+
+#if MSOFTC
+/*
+ *
+ */
+ static void
+colordesc(s, fg_color, bg_color)
+ char *s;
+ int *fg_color;
+ int *bg_color;
+{
+ int fg, bg;
+ int err;
+
+ fg = getnum(&s, 'D', &err);
+ if (err)
+ {
+ error("Missing fg color in -D", NULL_PARG);
+ return;
+ }
+ if (*s != '.')
+ bg = 0;
+ else
+ {
+ s++;
+ bg = getnum(&s, 'D', &err);
+ if (err)
+ {
+ error("Missing fg color in -D", NULL_PARG);
+ return;
+ }
+ }
+ *fg_color = fg;
+ *bg_color = bg;
+}
+
+/*
+ * Handler for the -D option.
+ */
+ /*ARGSUSED*/
+ public void
+opt_D(type, s)
+ int type;
+ char *s;
+{
+ switch (type)
+ {
+ case INIT:
+ case TOGGLE:
+ switch (*s++)
+ {
+ case 'n':
+ colordesc(s, &nm_fg_color, &nm_bg_color);
+ break;
+ case 'd':
+ colordesc(s, &bo_fg_color, &bo_bg_color);
+ break;
+ case 'u':
+ colordesc(s, &ul_fg_color, &ul_bg_color);
+ break;
+ case 'k':
+ colordesc(s, &bl_fg_color, &bl_bg_color);
+ break;
+ case 's':
+ colordesc(s, &so_fg_color, &so_bg_color);
+ break;
+ default:
+ error("-D must be followed by n, d, u, k or s", NULL_PARG);
+ break;
+ }
+ if (type == TOGGLE)
+ {
+ so_enter();
+ so_exit();
+ }
+ break;
+ case QUERY:
+ break;
+ }
+}
+#endif
+
+/*
+ * "-?" means display a help message.
+ * If from the command line, exit immediately.
+ */
+ /*ARGSUSED*/
+ public void
+opt_query(type, s)
+ int type;
+ char *s;
+{
+ if (nohelp)
+ return;
+ switch (type)
+ {
+ case QUERY:
+ case TOGGLE:
+ error("Use \"h\" for help", NULL_PARG);
+ break;
+ case INIT:
+ /*
+ * This is "less -?".
+ * It rather ungracefully grabs control,
+ * does the initializations normally done in main,
+ * shows the help file and exits.
+ */
+ raw_mode(1);
+ get_term();
+ open_getchr();
+ init();
+ any_display = TRUE;
+ help(1);
+ quit(QUIT_OK);
+ /*NOTREACHED*/
+ }
+}
+
+/*
+ * Get the "screen window" size.
+ */
+ public int
+get_swindow()
+{
+ if (swindow > 0)
+ return (swindow);
+ return (sc_height + swindow);
+}
+
diff --git a/usr.bin/less/option.c b/usr.bin/less/option.c
new file mode 100644
index 00000000000..f445501d1e4
--- /dev/null
+++ b/usr.bin/less/option.c
@@ -0,0 +1,549 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * Process command line options.
+ *
+ * Each option is a single letter which controls a program variable.
+ * The options have defaults which may be changed via
+ * the command line option, toggled via the "-" command,
+ * or queried via the "_" command.
+ */
+
+#include "less.h"
+#include "option.h"
+
+static struct option *pendopt;
+public int plusoption = FALSE;
+
+static char *propt();
+static char *optstring();
+static int flip_triple();
+
+extern int screen_trashed;
+extern char *every_first_cmd;
+
+/*
+ * Scan an argument (either from the command line or from the
+ * LESS environment variable) and process it.
+ */
+ public void
+scan_option(s)
+ char *s;
+{
+ register struct option *o;
+ register int c;
+ char *str;
+ int set_default;
+ PARG parg;
+
+ if (s == NULL)
+ return;
+
+ /*
+ * If we have a pending string-valued option, handle it now.
+ * This happens if the previous option was, for example, "-P"
+ * without a following string. In that case, the current
+ * option is simply the string for the previous option.
+ */
+ if (pendopt != NULL)
+ {
+ (*pendopt->ofunc)(INIT, s);
+ pendopt = NULL;
+ return;
+ }
+
+ set_default = FALSE;
+
+ while (*s != '\0')
+ {
+ /*
+ * Check some special cases first.
+ */
+ switch (c = *s++)
+ {
+ case ' ':
+ case '\t':
+ case END_OPTION_STRING:
+ continue;
+ case '-':
+ /*
+ * "-+" means set these options back to their defaults.
+ * (They may have been set otherwise by previous
+ * options.)
+ */
+ if (set_default = (*s == '+'))
+ s++;
+ continue;
+ case '+':
+ /*
+ * An option prefixed by a "+" is ungotten, so
+ * that it is interpreted as less commands
+ * processed at the start of the first input file.
+ * "++" means process the commands at the start of
+ * EVERY input file.
+ */
+ plusoption = TRUE;
+ if (*s == '+')
+ every_first_cmd = save(++s);
+ else
+ ungetsc(s);
+ s = optstring(s, c);
+ continue;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ /*
+ * Special "more" compatibility form "-<number>"
+ * instead of -z<number> to set the scrolling
+ * window size.
+ */
+ s--;
+ c = 'z';
+ break;
+ }
+
+ /*
+ * Not a special case.
+ * Look up the option letter in the option table.
+ */
+ o = findopt(c);
+ if (o == NULL)
+ {
+ parg.p_string = propt(c);
+#if MSOFTC || OS2
+ error("There is no %s flag (\"less -?\" for help)",
+ &parg);
+#else
+ error("There is no %s flag (\"less -\\?\" for help)",
+ &parg);
+#endif
+ quit(QUIT_ERROR);
+ }
+
+ switch (o->otype & OTYPE)
+ {
+ case BOOL:
+ if (set_default)
+ *(o->ovar) = o->odefault;
+ else
+ *(o->ovar) = ! o->odefault;
+ break;
+ case TRIPLE:
+ if (set_default)
+ *(o->ovar) = o->odefault;
+ else
+ *(o->ovar) = flip_triple(o->odefault,
+ (o->oletter == c));
+ break;
+ case STRING:
+ if (*s == '\0')
+ {
+ /*
+ * Set pendopt and return.
+ * We will get the string next time
+ * scan_option is called.
+ */
+ pendopt = o;
+ return;
+ }
+ /*
+ * Don't do anything here.
+ * All processing of STRING options is done by
+ * the handling function.
+ */
+ str = s;
+ s = optstring(s, c);
+ break;
+ case NUMBER:
+ *(o->ovar) = getnum(&s, c, (int*)NULL);
+ break;
+ }
+ /*
+ * If the option has a handling function, call it.
+ */
+ if (o->ofunc != NULL)
+ (*o->ofunc)(INIT, str);
+ }
+}
+
+/*
+ * Toggle command line flags from within the program.
+ * Used by the "-" and "_" commands.
+ * how_toggle may be:
+ * OPT_NO_TOGGLE just report the current setting, without changing it.
+ * OPT_TOGGLE invert the current setting
+ * OPT_UNSET set to the default value
+ * OPT_SET set to the inverse of the default value
+ */
+ public void
+toggle_option(c, s, how_toggle)
+ int c;
+ char *s;
+ int how_toggle;
+{
+ register struct option *o;
+ register int num;
+ int err;
+ PARG parg;
+
+ /*
+ * Look up the option letter in the option table.
+ */
+ o = findopt(c);
+ if (o == NULL)
+ {
+ parg.p_string = propt(c);
+ error("There is no %s flag", &parg);
+ return;
+ }
+
+ if (how_toggle == OPT_TOGGLE && (o->otype & NO_TOGGLE))
+ {
+ parg.p_string = propt(c);
+ error("Cannot change the %s flag", &parg);
+ return;
+ }
+
+ if (how_toggle == OPT_NO_TOGGLE && (o->otype & NO_QUERY))
+ {
+ parg.p_string = propt(c);
+ error("Cannot query the %s flag", &parg);
+ return;
+ }
+
+ /*
+ * Check for something which appears to be a do_toggle
+ * (because the "-" command was used), but really is not.
+ * This could be a string option with no string, or
+ * a number option with no number.
+ */
+ switch (o->otype & OTYPE)
+ {
+ case STRING:
+ case NUMBER:
+ if (how_toggle == OPT_TOGGLE && *s == '\0')
+ how_toggle = OPT_NO_TOGGLE;
+ break;
+ }
+
+#if HILITE_SEARCH
+ if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
+ repaint_hilite(0);
+#endif
+
+ /*
+ * Now actually toggle (change) the variable.
+ */
+ if (how_toggle != OPT_NO_TOGGLE)
+ {
+ switch (o->otype & OTYPE)
+ {
+ case BOOL:
+ /*
+ * Boolean.
+ */
+ switch (how_toggle)
+ {
+ case OPT_TOGGLE:
+ *(o->ovar) = ! *(o->ovar);
+ break;
+ case OPT_UNSET:
+ *(o->ovar) = o->odefault;
+ break;
+ case OPT_SET:
+ *(o->ovar) = ! o->odefault;
+ break;
+ }
+ break;
+ case TRIPLE:
+ /*
+ * Triple:
+ * If user gave the lower case letter, then switch
+ * to 1 unless already 1, in which case make it 0.
+ * If user gave the upper case letter, then switch
+ * to 2 unless already 2, in which case make it 0.
+ */
+ switch (how_toggle)
+ {
+ case OPT_TOGGLE:
+ *(o->ovar) = flip_triple(*(o->ovar),
+ o->oletter == c);
+ break;
+ case OPT_UNSET:
+ *(o->ovar) = o->odefault;
+ break;
+ case OPT_SET:
+ *(o->ovar) = flip_triple(o->odefault,
+ o->oletter == c);
+ break;
+ }
+ break;
+ case STRING:
+ /*
+ * String: don't do anything here.
+ * The handling function will do everything.
+ */
+ switch (how_toggle)
+ {
+ case OPT_SET:
+ case OPT_UNSET:
+ error("Can't use \"-+\" or \"--\" for a string flag",
+ NULL_PARG);
+ return;
+ }
+ break;
+ case NUMBER:
+ /*
+ * Number: set the variable to the given number.
+ */
+ switch (how_toggle)
+ {
+ case OPT_TOGGLE:
+ num = getnum(&s, '\0', &err);
+ if (!err)
+ *(o->ovar) = num;
+ break;
+ case OPT_UNSET:
+ *(o->ovar) = o->odefault;
+ break;
+ case OPT_SET:
+ error("Can't use \"--\" for a numeric flag",
+ NULL_PARG);
+ return;
+ }
+ break;
+ }
+ }
+
+ /*
+ * Call the handling function for any special action
+ * specific to this option.
+ */
+ if (o->ofunc != NULL)
+ (*o->ofunc)((how_toggle==OPT_NO_TOGGLE) ? QUERY : TOGGLE, s);
+
+#if HILITE_SEARCH
+ if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
+ chg_hilite();
+#endif
+
+ /*
+ * Print a message describing the new setting.
+ */
+ switch (o->otype & OTYPE)
+ {
+ case BOOL:
+ case TRIPLE:
+ /*
+ * Print the odesc message.
+ */
+ error(o->odesc[*(o->ovar)], NULL_PARG);
+ break;
+ case NUMBER:
+ /*
+ * The message is in odesc[1] and has a %d for
+ * the value of the variable.
+ */
+ parg.p_int = *(o->ovar);
+ error(o->odesc[1], &parg);
+ break;
+ case STRING:
+ /*
+ * Message was already printed by the handling function.
+ */
+ break;
+ }
+
+ if (how_toggle != OPT_NO_TOGGLE && (o->otype & REPAINT))
+ screen_trashed = TRUE;
+}
+
+/*
+ * "Toggle" a triple-valued option.
+ */
+ static int
+flip_triple(val, lc)
+ int val;
+ int lc;
+{
+ if (lc)
+ return ((val == OPT_ON) ? OPT_OFF : OPT_ON);
+ else
+ return ((val == OPT_ONPLUS) ? OPT_OFF : OPT_ONPLUS);
+}
+
+/*
+ * Return a string suitable for printing as the "name" of an option.
+ * For example, if the option letter is 'x', just return "-x".
+ */
+ static char *
+propt(c)
+ int c;
+{
+ static char buf[8];
+
+ sprintf(buf, "-%s", prchar(c));
+ return (buf);
+}
+
+/*
+ * Determine if an option is a single character option (BOOL or TRIPLE),
+ * or if it a multi-character option (NUMBER).
+ */
+ public int
+single_char_option(c)
+ int c;
+{
+ register struct option *o;
+
+ o = findopt(c);
+ if (o == NULL)
+ return (TRUE);
+ return ((o->otype & (BOOL|TRIPLE|NOVAR|NO_TOGGLE)) != 0);
+}
+
+/*
+ * Return the prompt to be used for a given option letter.
+ * Only string and number valued options have prompts.
+ */
+ public char *
+opt_prompt(c)
+ int c;
+{
+ register struct option *o;
+
+ o = findopt(c);
+ if (o == NULL || (o->otype & (STRING|NUMBER)) == 0)
+ return (NULL);
+ return (o->odesc[0]);
+}
+
+/*
+ * Return whether or not there is a string option pending;
+ * that is, if the previous option was a string-valued option letter
+ * (like -P) without a following string.
+ * In that case, the current option is taken to be the string for
+ * the previous option.
+ */
+ public int
+isoptpending()
+{
+ return (pendopt != NULL);
+}
+
+/*
+ * Print error message about missing string.
+ */
+ static void
+nostring(c)
+ int c;
+{
+ PARG parg;
+ parg.p_string = propt(c);
+ error("String is required after %s", &parg);
+}
+
+/*
+ * Print error message if a STRING type option is not followed by a string.
+ */
+ public void
+nopendopt()
+{
+ nostring(pendopt->oletter);
+}
+
+/*
+ * Scan to end of string or to an END_OPTION_STRING character.
+ * In the latter case, replace the char with a null char.
+ * Return a pointer to the remainder of the string, if any.
+ */
+ static char *
+optstring(s, c)
+ char *s;
+ int c;
+{
+ register char *p;
+
+ if (*s == '\0')
+ {
+ nostring(c);
+ quit(QUIT_ERROR);
+ }
+ for (p = s; *p != '\0'; p++)
+ if (*p == END_OPTION_STRING)
+ {
+ *p = '\0';
+ return (p+1);
+ }
+ return (p);
+}
+
+/*
+ * Translate a string into a number.
+ * Like atoi(), but takes a pointer to a char *, and updates
+ * the char * to point after the translated number.
+ */
+ public int
+getnum(sp, c, errp)
+ char **sp;
+ int c;
+ int *errp;
+{
+ register char *s;
+ register int n;
+ register int neg;
+ PARG parg;
+
+ s = skipsp(*sp);
+ neg = FALSE;
+ if (*s == '-')
+ {
+ neg = TRUE;
+ s++;
+ }
+ if (*s < '0' || *s > '9')
+ {
+ if (errp != NULL)
+ {
+ *errp = TRUE;
+ return (-1);
+ }
+ parg.p_string = propt(c);
+ error("Number is required after %s", &parg);
+ quit(QUIT_ERROR);
+ }
+
+ n = 0;
+ while (*s >= '0' && *s <= '9')
+ n = 10 * n + *s++ - '0';
+ *sp = s;
+ if (errp != NULL)
+ *errp = FALSE;
+ if (neg)
+ n = -n;
+ return (n);
+}
diff --git a/usr.bin/less/option.h b/usr.bin/less/option.h
new file mode 100644
index 00000000000..59a72cf0ca0
--- /dev/null
+++ b/usr.bin/less/option.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#define END_OPTION_STRING ('$')
+
+/*
+ * Types of options.
+ */
+#define BOOL 01 /* Boolean option: 0 or 1 */
+#define TRIPLE 02 /* Triple-valued option: 0, 1 or 2 */
+#define NUMBER 04 /* Numeric option */
+#define STRING 010 /* String-valued option */
+#define NOVAR 020 /* No associated variable */
+#define REPAINT 040 /* Repaint screen after toggling option */
+#define NO_TOGGLE 0100 /* Option cannot be toggled with "-" cmd */
+#define HL_REPAINT 0200 /* Repaint hilites after toggling option */
+#define NO_QUERY 0400 /* Option cannot be queried with "_" cmd */
+
+#define OTYPE (BOOL|TRIPLE|NUMBER|STRING|NOVAR)
+
+/*
+ * Argument to a handling function tells what type of activity:
+ */
+#define INIT 0 /* Initialization (from command line) */
+#define QUERY 1 /* Query (from _ or - command) */
+#define TOGGLE 2 /* Change value (from - command) */
+
+/* Flag to toggle_option to specify how to "toggle" */
+#define OPT_NO_TOGGLE 0
+#define OPT_TOGGLE 1
+#define OPT_UNSET 2
+#define OPT_SET 3
+
+struct option
+{
+ char oletter; /* The controlling letter (a-z) */
+ int otype; /* Type of the option */
+ int odefault; /* Default value */
+ int *ovar; /* Pointer to the associated variable */
+ void (*ofunc)(); /* Pointer to special handling function */
+ char *odesc[3]; /* Description of each value */
+};
+
diff --git a/usr.bin/less/opttbl.c b/usr.bin/less/opttbl.c
new file mode 100644
index 00000000000..69e9faf2b0a
--- /dev/null
+++ b/usr.bin/less/opttbl.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * The option table.
+ */
+
+#include "less.h"
+#include "option.h"
+
+/*
+ * Variables controlled by command line options.
+ */
+public int quiet; /* Should we suppress the audible bell? */
+public int how_search; /* Where should forward searches start? */
+public int top_scroll; /* Repaint screen from top?
+ (alternative is scroll from bottom) */
+public int pr_type; /* Type of prompt (short, medium, long) */
+public int bs_mode; /* How to process backspaces */
+public int know_dumb; /* Don't complain about dumb terminals */
+public int quit_at_eof; /* Quit after hitting end of file twice */
+public int squeeze; /* Squeeze multiple blank lines into one */
+public int tabstop; /* Tab settings */
+public int back_scroll; /* Repaint screen on backwards movement */
+public int forw_scroll; /* Repaint screen on forward movement */
+public int twiddle; /* Display "~" for lines after EOF */
+public int caseless; /* Do "caseless" searches */
+public int linenums; /* Use line numbers */
+public int cbufs; /* Current number of buffers */
+public int autobuf; /* Automatically allocate buffers as needed */
+public int nohelp; /* Disable the HELP command */
+public int ctldisp; /* Send control chars to screen untranslated */
+public int force_open; /* Open the file even if not regular file */
+public int swindow; /* Size of scrolling window */
+public int jump_sline; /* Screen line of "jump target" */
+public int chopline; /* Truncate displayed lines at screen width */
+public int no_init; /* Disable sending ti/te termcap strings */
+#if HILITE_SEARCH
+public int hilite_search; /* Highlight matched search patterns? */
+#endif
+
+/*
+ * Table of all options and their semantics.
+ */
+static struct option option[] =
+{
+ { 'a', BOOL, OPT_OFF, &how_search, NULL,
+ "Search includes displayed screen",
+ "Search skips displayed screen",
+ NULL
+ },
+ { 'b', NUMBER, 10, &cbufs, opt_b,
+ "Buffers: ",
+ "%d buffers",
+ NULL
+ },
+ { 'B', BOOL, OPT_ON, &autobuf, NULL,
+ "Don't automatically allocate buffers",
+ "Automatically allocate buffers when needed",
+ NULL
+ },
+ { 'c', TRIPLE, OPT_OFF, &top_scroll, NULL,
+ "Repaint by scrolling from bottom of screen",
+ "Repaint by clearing each line",
+ "Repaint by painting from top of screen"
+ },
+ { 'd', BOOL|NO_TOGGLE, OPT_OFF, &know_dumb, NULL,
+ "Assume intelligent terminal",
+ "Assume dumb terminal",
+ NULL
+ },
+#if MSOFTC
+ { 'D', STRING|REPAINT, 0, NULL, opt_D,
+ "color desc: ", NULL, NULL
+ },
+#endif
+ { 'e', TRIPLE, OPT_OFF, &quit_at_eof, NULL,
+ "Don't quit at end-of-file",
+ "Quit at end-of-file",
+ "Quit immediately at end-of-file"
+ },
+ { 'f', BOOL, OPT_OFF, &force_open, NULL,
+ "Open only regular files",
+ "Open even non-regular files",
+ NULL
+ },
+#if HILITE_SEARCH
+ { 'g', TRIPLE|HL_REPAINT, OPT_ONPLUS, &hilite_search, NULL,
+ "Don't highlight search matches",
+ "Highlight matches for previous search only",
+ "Highlight all matches for previous search pattern",
+ },
+#endif
+ { 'h', NUMBER, -1, &back_scroll, NULL,
+ "Backwards scroll limit: ",
+ "Backwards scroll limit is %d lines",
+ NULL
+ },
+ { 'H', BOOL|NO_TOGGLE, OPT_OFF, &nohelp, NULL,
+ "Allow help command",
+ "Don't allow help command",
+ NULL
+ },
+ { 'i', TRIPLE|HL_REPAINT, OPT_OFF, &caseless, opt_i,
+ "Case is significant in searches",
+ "Ignore case in searches",
+ "Ignore case in searches and in patterns"
+ },
+ { 'j', NUMBER, 1, &jump_sline, NULL,
+ "Target line: ",
+ "Position target at screen line %d",
+ NULL
+ },
+#if USERFILE
+ { 'k', STRING|NO_TOGGLE|NO_QUERY, 0, NULL, opt_k,
+ NULL, NULL, NULL
+ },
+#endif
+ { 'l', STRING|NO_TOGGLE|NO_QUERY, 0, NULL, opt_l,
+ NULL, NULL, NULL
+ },
+ { 'm', TRIPLE, OPT_OFF, &pr_type, NULL,
+ "Short prompt",
+ "Medium prompt",
+ "Long prompt"
+ },
+ { 'n', TRIPLE|REPAINT, OPT_ON, &linenums, NULL,
+ "Don't use line numbers",
+ "Use line numbers",
+ "Constantly display line numbers"
+ },
+#if LOGFILE
+ { 'o', STRING, 0, NULL, opt_o,
+ "log file: ", NULL, NULL
+ },
+ { 'O', STRING, 0, NULL, opt__O,
+ "Log file: ", NULL, NULL
+ },
+#endif
+ { 'p', STRING|NO_TOGGLE|NO_QUERY, 0, NULL, opt_p,
+ NULL, NULL, NULL
+ },
+ { 'P', STRING, 0, NULL, opt__P,
+ "prompt: ", NULL, NULL
+ },
+ { 'q', TRIPLE, OPT_OFF, &quiet, NULL,
+ "Ring the bell for errors AND at eof/bof",
+ "Ring the bell for errors but not at eof/bof",
+ "Never ring the bell"
+ },
+ { 'r', BOOL|REPAINT, OPT_ON, &ctldisp, NULL,
+ "Display control characters directly",
+ "Display control characters as ^X",
+ NULL
+ },
+ { 's', BOOL|REPAINT, OPT_OFF, &squeeze, NULL,
+ "Display all blank lines",
+ "Squeeze multiple blank lines",
+ NULL
+ },
+ { 'S', BOOL|REPAINT, OPT_OFF, &chopline, NULL,
+ "Fold long lines",
+ "Chop long lines",
+ NULL
+ },
+#if TAGS
+ { 't', STRING|NO_QUERY, 0, NULL, opt_t,
+ "tag: ", NULL, NULL
+ },
+ { 'T', STRING, 0, NULL, opt__T,
+ "tags file: ", NULL, NULL
+ },
+#endif
+ { 'u', TRIPLE|REPAINT, OPT_OFF, &bs_mode, NULL,
+ "Display underlined text in underline mode",
+ "Backspaces cause overstrike",
+ "Print backspace as ^H"
+ },
+ { 'V', NOVAR, 0, NULL, opt__V,
+ NULL, NULL, NULL
+ },
+ { 'w', BOOL|REPAINT, OPT_ON, &twiddle, NULL,
+ "Display nothing for lines after end-of-file",
+ "Display ~ for lines after end-of-file",
+ NULL
+ },
+ { 'x', NUMBER|REPAINT, 8, &tabstop, NULL,
+ "Tab stops: ",
+ "Tab stops every %d spaces",
+ NULL
+ },
+ { 'X', BOOL|NO_TOGGLE, OPT_OFF, &no_init, NULL,
+ "Send init/deinit strings to terminal",
+ "Don't use init/deinit strings",
+ NULL
+ },
+ { 'y', NUMBER, -1, &forw_scroll, NULL,
+ "Forward scroll limit: ",
+ "Forward scroll limit is %d lines",
+ NULL
+ },
+ { 'z', NUMBER, -1, &swindow, NULL,
+ "Scroll window size: ",
+ "Scroll window size is %d lines",
+ NULL
+ },
+ { '?', NOVAR, 0, NULL, opt_query,
+ NULL, NULL, NULL
+ },
+ { '\0' }
+};
+
+
+/*
+ * Initialize each option to its default value.
+ */
+ public void
+init_option()
+{
+ register struct option *o;
+
+ for (o = option; o->oletter != '\0'; o++)
+ {
+ /*
+ * Set each variable to its default.
+ */
+ if (o->ovar != NULL)
+ *(o->ovar) = o->odefault;
+ }
+}
+
+/*
+ * Find an option in the option table.
+ */
+ public struct option *
+findopt(c)
+ int c;
+{
+ register struct option *o;
+
+ for (o = option; o->oletter != '\0'; o++)
+ {
+ if (o->oletter == c)
+ return (o);
+ if ((o->otype & TRIPLE) && toupper(o->oletter) == c)
+ return (o);
+ }
+ return (NULL);
+}
diff --git a/usr.bin/less/os.c b/usr.bin/less/os.c
new file mode 100644
index 00000000000..908d6352fda
--- /dev/null
+++ b/usr.bin/less/os.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * Operating system dependent routines.
+ *
+ * Most of the stuff in here is based on Unix, but an attempt
+ * has been made to make things work on other operating systems.
+ * This will sometimes result in a loss of functionality, unless
+ * someone rewrites code specifically for the new operating system.
+ *
+ * The makefile provides defines to decide whether various
+ * Unix features are present.
+ */
+
+#include "less.h"
+#include <signal.h>
+#include <setjmp.h>
+#if HAVE_TIME_H
+#include <time.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_VALUES_H
+#include <values.h>
+#endif
+
+#if HAVE_TIME_T
+#define time_type time_t
+#else
+#define time_type long
+#endif
+
+/*
+ * BSD setjmp() saves (and longjmp() restores) the signal mask.
+ * This costs a system call or two per setjmp(), so if possible we clear the
+ * signal mask with sigsetmask(), and use _setjmp()/_longjmp() instead.
+ * On other systems, setjmp() doesn't affect the signal mask and so
+ * _setjmp() does not exist; we just use setjmp().
+ */
+#if HAVE__SETJMP && HAVE_SIGSETMASK
+#define SET_JUMP _setjmp
+#define LONG_JUMP _longjmp
+#else
+#define SET_JUMP setjmp
+#define LONG_JUMP longjmp
+#endif
+
+public int reading;
+
+static jmp_buf read_label;
+
+/*
+ * Like read() system call, but is deliberately interruptible.
+ * A call to intread() from a signal handler will interrupt
+ * any pending iread().
+ */
+ public int
+iread(fd, buf, len)
+ int fd;
+ char *buf;
+ unsigned int len;
+{
+ register int n;
+
+#if MSOFTC
+ if (kbhit())
+ {
+ int c;
+
+ c = getch();
+ if (c == '\003')
+ return (READ_INTR);
+ ungetch(c);
+ }
+#endif
+ if (SET_JUMP(read_label))
+ {
+ /*
+ * We jumped here from intread.
+ */
+ reading = 0;
+#if HAVE_SIGSETMASK
+ sigsetmask(0);
+#endif
+ return (READ_INTR);
+ }
+
+ flush();
+ reading = 1;
+ n = read(fd, buf, len);
+ reading = 0;
+ if (n < 0)
+ return (-1);
+ return (n);
+}
+
+/*
+ * Interrupt a pending iread().
+ */
+ public void
+intread()
+{
+ LONG_JUMP(read_label, 1);
+}
+
+/*
+ * Return the current time.
+ */
+#if HAVE_TIME
+ public long
+get_time()
+{
+ time_type t;
+
+ time(&t);
+ return (t);
+}
+#endif
+
+
+#if !HAVE_STRERROR
+/*
+ * Local version of strerror, if not available from the system.
+ */
+ static char *
+strerror(err)
+ int err;
+{
+#if HAVE_SYS_ERRLIST
+ static char buf[16];
+ extern char *sys_errlist[];
+ extern int sys_nerr;
+
+ if (err < sys_nerr)
+ return sys_errlist[err];
+ sprintf(buf, "Error %d", err);
+ return buf;
+#else
+ return ("cannot open");
+#endif
+}
+#endif
+
+/*
+ * errno_message: Return an error message based on the value of "errno".
+ */
+ public char *
+errno_message(filename)
+ char *filename;
+{
+ register char *p;
+ register char *m;
+#if HAVE_ERRNO
+ extern int errno;
+ p = strerror(errno);
+#else
+ p = "cannot open";
+#endif
+ m = (char *) ecalloc(strlen(filename) + strlen(p) + 3, sizeof(char));
+ sprintf(m, "%s: %s", filename, p);
+ return (m);
+}
+
+/*
+ * Return the largest possible number that can fit in a long.
+ */
+#ifdef MAXLONG
+ static long
+get_maxlong()
+{
+ return (MAXLONG);
+}
+#else
+ static long
+get_maxlong()
+{
+ long n, n2;
+
+ /*
+ * Keep doubling n until we overflow.
+ * {{ This actually only returns the largest power of two that
+ * can fit in a long, but percentage() doesn't really need
+ * it any more accurate than that. }}
+ */
+ n2 = 128; /* Hopefully no maxlong is less than 128! */
+ do {
+ n = n2;
+ n2 *= 2;
+ } while (n2 / 2 == n);
+ return (n);
+}
+#endif
+
+/*
+ * Return the ratio of two longs, as a percentage.
+ */
+ public int
+percentage(num, den)
+ long num, den;
+{
+ static long maxlong100 = 0;
+
+ if (maxlong100 == 0)
+ maxlong100 = get_maxlong() / 100;
+ if (num > maxlong100)
+ return (num / (den/100));
+ else
+ return (100*num / den);
+}
diff --git a/usr.bin/less/output.c b/usr.bin/less/output.c
new file mode 100644
index 00000000000..dbe180073a5
--- /dev/null
+++ b/usr.bin/less/output.c
@@ -0,0 +1,363 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * High level routines dealing with the output to the screen.
+ */
+
+#include "less.h"
+
+public int errmsgs; /* Count of messages displayed by error() */
+public int need_clr;
+
+extern int sigs;
+extern int sc_width;
+extern int so_s_width, so_e_width;
+extern int screen_trashed;
+extern int any_display;
+
+/*
+ * Display the line which is in the line buffer.
+ */
+ public void
+put_line()
+{
+ register int c;
+ register int i;
+ int a;
+ int curr_attr;
+
+ if (ABORT_SIGS())
+ {
+ /*
+ * Don't output if a signal is pending.
+ */
+ screen_trashed = 1;
+ return;
+ }
+
+ curr_attr = AT_NORMAL;
+
+ for (i = 0; (c = gline(i, &a)) != '\0'; i++)
+ {
+ if (a != curr_attr)
+ {
+ /*
+ * Changing attributes.
+ * Display the exit sequence for the old attribute
+ * and the enter sequence for the new one.
+ */
+ switch (curr_attr)
+ {
+ case AT_UNDERLINE: ul_exit(); break;
+ case AT_BOLD: bo_exit(); break;
+ case AT_BLINK: bl_exit(); break;
+ case AT_STANDOUT: so_exit(); break;
+ }
+ switch (a)
+ {
+ case AT_UNDERLINE: ul_enter(); break;
+ case AT_BOLD: bo_enter(); break;
+ case AT_BLINK: bl_enter(); break;
+ case AT_STANDOUT: so_enter(); break;
+ }
+ curr_attr = a;
+ }
+ if (curr_attr == AT_INVIS)
+ continue;
+ if (c == '\b')
+ putbs();
+ else
+ putchr(c);
+ }
+
+ switch (curr_attr)
+ {
+ case AT_UNDERLINE: ul_exit(); break;
+ case AT_BOLD: bo_exit(); break;
+ case AT_BLINK: bl_exit(); break;
+ case AT_STANDOUT: so_exit(); break;
+ }
+}
+
+static char obuf[1024];
+static char *ob = obuf;
+
+/*
+ * Flush buffered output.
+ *
+ * If we haven't displayed any file data yet,
+ * output messages on error output (file descriptor 2),
+ * otherwise output on standard output (file descriptor 1).
+ *
+ * This has the desirable effect of producing all
+ * error messages on error output if standard output
+ * is directed to a file. It also does the same if
+ * we never produce any real output; for example, if
+ * the input file(s) cannot be opened. If we do
+ * eventually produce output, code in edit() makes
+ * sure these messages can be seen before they are
+ * overwritten or scrolled away.
+ */
+ public void
+flush()
+{
+ register int n;
+ register int fd;
+
+#if MSOFTC
+ *ob = '\0';
+ _outtext(obuf);
+ ob = obuf;
+#else
+ n = ob - obuf;
+ if (n == 0)
+ return;
+ fd = (any_display) ? 1 : 2;
+ if (write(fd, obuf, n) != n)
+ screen_trashed = 1;
+ ob = obuf;
+#endif
+}
+
+/*
+ * Output a character.
+ */
+ public int
+putchr(c)
+ int c;
+{
+ if (ob >= &obuf[sizeof(obuf)])
+ flush();
+ if (need_clr)
+ {
+ need_clr = 0;
+ clear_bot();
+ }
+#if MSOFTC
+ if (c == '\n')
+ putchr('\r');
+#endif
+ *ob++ = c;
+ return (c);
+}
+
+/*
+ * Output a string.
+ */
+ public void
+putstr(s)
+ register char *s;
+{
+ while (*s != '\0')
+ putchr(*s++);
+}
+
+
+/*
+ * Output an integer in a given radix.
+ */
+ static int
+iprintnum(num, radix)
+ int num;
+ int radix;
+{
+ register char *s;
+ int r;
+ int neg;
+ char buf[10];
+
+ if (neg = (num < 0))
+ num = -num;
+
+ s = buf;
+ do
+ {
+ *s++ = (num % radix) + '0';
+ } while ((num /= radix) != 0);
+
+ if (neg)
+ *s++ = '-';
+ r = s - buf;
+
+ while (s > buf)
+ putchr(*--s);
+ return (r);
+}
+
+/*
+ * This function implements printf-like functionality
+ * using a more portable argument list mechanism than printf's.
+ */
+ static int
+iprintf(fmt, parg)
+ register char *fmt;
+ PARG *parg;
+{
+ register char *s;
+ register int n;
+ register int col;
+
+ col = 0;
+ while (*fmt != '\0')
+ {
+ if (*fmt != '%')
+ {
+ putchr(*fmt++);
+ col++;
+ } else
+ {
+ ++fmt;
+ switch (*fmt++) {
+ case 's':
+ s = parg->p_string;
+ parg++;
+ while (*s != '\0')
+ {
+ putchr(*s++);
+ col++;
+ }
+ break;
+ case 'd':
+ n = parg->p_int;
+ parg++;
+ col += iprintnum(n, 10);
+ break;
+ }
+ }
+ }
+ return (col);
+}
+
+/*
+ * Output a message in the lower left corner of the screen
+ * and wait for carriage return.
+ */
+ public void
+error(fmt, parg)
+ char *fmt;
+ PARG *parg;
+{
+ int c;
+ int col = 0;
+ static char return_to_continue[] = " (press RETURN)";
+
+ errmsgs++;
+
+ if (any_display)
+ {
+ clear_bot();
+ so_enter();
+ col += so_s_width;
+ }
+
+ col += iprintf(fmt, parg);
+
+ if (!any_display)
+ {
+ putchr('\n');
+ return;
+ }
+
+ putstr(return_to_continue);
+ so_exit();
+ col += sizeof(return_to_continue) + so_e_width;
+
+#if ONLY_RETURN
+ while ((c = getchr()) != '\n' && c != '\r')
+ bell();
+#else
+ c = getchr();
+ if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR)
+ ungetcc(c);
+#endif
+ lower_left();
+
+ if (col >= sc_width)
+ /*
+ * Printing the message has probably scrolled the screen.
+ * {{ Unless the terminal doesn't have auto margins,
+ * in which case we just hammered on the right margin. }}
+ */
+ screen_trashed = 1;
+
+ flush();
+}
+
+static char intr_to_abort[] = "... (interrupt to abort)";
+
+/*
+ * Output a message in the lower left corner of the screen
+ * and don't wait for carriage return.
+ * Usually used to warn that we are beginning a potentially
+ * time-consuming operation.
+ */
+ public void
+ierror(fmt, parg)
+ char *fmt;
+ PARG *parg;
+{
+ clear_bot();
+ so_enter();
+ (void) iprintf(fmt, parg);
+ putstr(intr_to_abort);
+ so_exit();
+ flush();
+ need_clr = 1;
+}
+
+/*
+ * Output a message in the lower left corner of the screen
+ * and return a single-character response.
+ */
+ public int
+query(fmt, parg)
+ char *fmt;
+ PARG *parg;
+{
+ register int c;
+ int col = 0;
+
+ if (any_display)
+ clear_bot();
+
+ (void) iprintf(fmt, parg);
+ c = getchr();
+
+ if (!any_display)
+ {
+ putchr('\n');
+ return (c);
+ }
+
+ lower_left();
+ if (col >= sc_width)
+ screen_trashed = 1;
+ flush();
+
+ return (c);
+}
diff --git a/usr.bin/less/position.c b/usr.bin/less/position.c
new file mode 100644
index 00000000000..7778d0867a1
--- /dev/null
+++ b/usr.bin/less/position.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * Routines dealing with the "position" table.
+ * This is a table which tells the position (in the input file) of the
+ * first char on each currently displayed line.
+ *
+ * {{ The position table is scrolled by moving all the entries.
+ * Would be better to have a circular table
+ * and just change a couple of pointers. }}
+ */
+
+#include "less.h"
+#include "position.h"
+
+static POSITION *table = NULL; /* The position table */
+static int table_size;
+
+extern int sc_width, sc_height;
+
+/*
+ * Return the starting file position of a line displayed on the screen.
+ * The line may be specified as a line number relative to the top
+ * of the screen, but is usually one of these special cases:
+ * the top (first) line on the screen
+ * the second line on the screen
+ * the bottom line on the screen
+ * the line after the bottom line on the screen
+ */
+ public POSITION
+position(where)
+ int where;
+{
+ switch (where)
+ {
+ case BOTTOM:
+ where = sc_height - 2;
+ break;
+ case BOTTOM_PLUS_ONE:
+ where = sc_height - 1;
+ break;
+ case MIDDLE:
+ where = (sc_height - 1) / 2;
+ }
+ return (table[where]);
+}
+
+/*
+ * Add a new file position to the bottom of the position table.
+ */
+ public void
+add_forw_pos(pos)
+ POSITION pos;
+{
+ register int i;
+
+ /*
+ * Scroll the position table up.
+ */
+ for (i = 1; i < sc_height; i++)
+ table[i-1] = table[i];
+ table[sc_height - 1] = pos;
+}
+
+/*
+ * Add a new file position to the top of the position table.
+ */
+ public void
+add_back_pos(pos)
+ POSITION pos;
+{
+ register int i;
+
+ /*
+ * Scroll the position table down.
+ */
+ for (i = sc_height - 1; i > 0; i--)
+ table[i] = table[i-1];
+ table[0] = pos;
+}
+
+/*
+ * Initialize the position table, done whenever we clear the screen.
+ */
+ public void
+pos_clear()
+{
+ register int i;
+
+ for (i = 0; i < sc_height; i++)
+ table[i] = NULL_POSITION;
+}
+
+/*
+ * Allocate or reallocate the position table.
+ */
+ public void
+pos_init()
+{
+ struct scrpos scrpos;
+
+ if (sc_height <= table_size)
+ return;
+ /*
+ * If we already have a table, remember the first line in it
+ * before we free it, so we can copy that line to the new table.
+ */
+ if (table != NULL)
+ {
+ get_scrpos(&scrpos);
+ free((char*)table);
+ } else
+ scrpos.pos = NULL_POSITION;
+ table = (POSITION *) ecalloc(sc_height, sizeof(POSITION));
+ table_size = sc_height;
+ pos_clear();
+ if (scrpos.pos != NULL_POSITION)
+ table[scrpos.ln-1] = scrpos.pos;
+}
+
+/*
+ * See if the byte at a specified position is currently on the screen.
+ * Check the position table to see if the position falls within its range.
+ * Return the position table entry if found, -1 if not.
+ */
+ public int
+onscreen(pos)
+ POSITION pos;
+{
+ register int i;
+
+ if (pos < table[0])
+ return (-1);
+ for (i = 1; i < sc_height; i++)
+ if (pos < table[i])
+ return (i-1);
+ return (-1);
+}
+
+/*
+ * See if the entire screen is empty.
+ */
+ public int
+empty_screen()
+{
+ return (empty_lines(0, sc_height-1));
+}
+
+ public int
+empty_lines(s, e)
+ int s;
+ int e;
+{
+ register int i;
+
+ for (i = s; i <= e; i++)
+ if (table[i] != NULL_POSITION)
+ return (0);
+ return (1);
+}
+
+/*
+ * Get the current screen position.
+ * The screen position consists of both a file position and
+ * a screen line number where the file position is placed on the screen.
+ * Normally the screen line number is 0, but if we are positioned
+ * such that the top few lines are empty, we may have to set
+ * the screen line to a number > 0.
+ */
+ public void
+get_scrpos(scrpos)
+ struct scrpos *scrpos;
+{
+ register int i;
+
+ /*
+ * Find the first line on the screen which has something on it,
+ * and return the screen line number and the file position.
+ */
+ for (i = 0; i < sc_height; i++)
+ if (table[i] != NULL_POSITION)
+ {
+ scrpos->ln = i+1;
+ scrpos->pos = table[i];
+ return;
+ }
+ /*
+ * The screen is empty.
+ */
+ scrpos->pos = NULL_POSITION;
+}
+
+/*
+ * Adjust a screen line number to be a simple positive integer
+ * in the range { 0 .. sc_height-2 }.
+ * (The bottom line, sc_height-1, is reserved for prompts, etc.)
+ * The given "sline" may be in the range { 1 .. sc_height-1 }
+ * to refer to lines relative to the top of the screen (starting from 1),
+ * or it may be in { -1 .. -(sc_height-1) } to refer to lines
+ * relative to the bottom of the screen.
+ */
+ public int
+adjsline(sline)
+ int sline;
+{
+ /*
+ * Negative screen line number means
+ * relative to the bottom of the screen.
+ */
+ if (sline < 0)
+ sline += sc_height;
+ /*
+ * Can't be less than 1 or greater than sc_height-1.
+ */
+ if (sline <= 0)
+ sline = 1;
+ if (sline >= sc_height)
+ sline = sc_height - 1;
+ /*
+ * Return zero-based line number, not one-based.
+ */
+ return (sline-1);
+}
diff --git a/usr.bin/less/position.h b/usr.bin/less/position.h
new file mode 100644
index 00000000000..c227cf70e13
--- /dev/null
+++ b/usr.bin/less/position.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * Include file for interfacing to position.c modules.
+ */
+#define TOP (0)
+#define TOP_PLUS_ONE (1)
+#define BOTTOM (-1)
+#define BOTTOM_PLUS_ONE (-2)
+#define MIDDLE (-3)
diff --git a/usr.bin/less/prompt.c b/usr.bin/less/prompt.c
new file mode 100644
index 00000000000..59468f0c58b
--- /dev/null
+++ b/usr.bin/less/prompt.c
@@ -0,0 +1,451 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * Prompting and other messages.
+ * There are three flavors of prompts, SHORT, MEDIUM and LONG,
+ * selected by the -m/-M options.
+ * There is also the "equals message", printed by the = command.
+ * A prompt is a message composed of various pieces, such as the
+ * name of the file being viewed, the percentage into the file, etc.
+ */
+
+#include "less.h"
+#include "position.h"
+
+extern int pr_type;
+extern int hit_eof;
+extern int new_file;
+extern int sc_width;
+extern int so_s_width, so_e_width;
+extern int linenums;
+extern int sc_height;
+extern int jump_sline;
+extern IFILE curr_ifile;
+#if EDITOR
+extern char *editor;
+#endif
+
+/*
+ * Prototypes for the three flavors of prompts.
+ * These strings are expanded by pr_expand().
+ */
+static char s_proto[] =
+ "?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\\: %x..%t";
+static char m_proto[] =
+ "?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t";
+static char M_proto[] =
+ "?f%f .?n?m(file %i of %m) ..?ltline %lt?L/%L. :byte %bB?s/%s. .?e(END) ?x- Next\\: %x.:?pB%pB\\%..%t";
+static char e_proto[] =
+ "?f%f .?m(file %i of %m) .?ltline %lt?L/%L. .byte %bB?s/%s. ?e(END) :?pB%pB\\%..%t";
+
+public char *prproto[3];
+public char *eqproto = e_proto;
+
+static char message[250];
+static char *mp;
+
+/*
+ * Initialize the prompt prototype strings.
+ */
+ public void
+init_prompt()
+{
+ prproto[0] = save(s_proto);
+ prproto[1] = save(m_proto);
+ prproto[2] = save(M_proto);
+ eqproto = save(e_proto);
+}
+
+/*
+ * Set the message pointer to the end of the message string.
+ */
+ static void
+setmp()
+{
+ while (*mp != '\0')
+ mp++;
+}
+
+/*
+ * Append a POSITION (as a decimal integer) to the end of the message.
+ */
+ static void
+ap_pos(pos)
+ POSITION pos;
+{
+ sprintf(mp, "%ld", (long)pos);
+ setmp();
+}
+
+/*
+ * Append an integer to the end of the message.
+ */
+ static void
+ap_int(n)
+ int n;
+{
+ sprintf(mp, "%d", n);
+ setmp();
+}
+
+/*
+ * Append a string to the end of the message.
+ */
+ static void
+ap_str(s)
+ char *s;
+{
+ strtcpy(mp, s, (unsigned int)(&message[sizeof(message)] - mp));
+ setmp();
+}
+
+/*
+ * Append a question mark to the end of the message.
+ */
+ static void
+ap_quest()
+{
+ *mp++ = '?';
+}
+
+/*
+ * Return the "current" byte offset in the file.
+ */
+ static POSITION
+curr_byte(where)
+ int where;
+{
+ POSITION pos;
+
+ pos = position(where);
+ while (pos == NULL_POSITION && where >= 0 && where < sc_height)
+ pos = position(++where);
+ if (pos == NULL_POSITION)
+ pos = ch_length();
+ return (pos);
+}
+
+/*
+ * Return the value of a prototype conditional.
+ * A prototype string may include conditionals which consist of a
+ * question mark followed by a single letter.
+ * Here we decode that letter and return the appropriate boolean value.
+ */
+ static int
+cond(c, where)
+ char c;
+ int where;
+{
+ switch (c)
+ {
+ case 'a': /* Anything in the message yet? */
+ return (mp > message);
+ case 'b': /* Current byte offset known? */
+ return (curr_byte(where) != NULL_POSITION);
+ case 'e': /* At end of file? */
+ return (hit_eof);
+ case 'f': /* Filename known? */
+ return (strcmp(get_filename(curr_ifile), "-") != 0);
+ case 'l': /* Line number known? */
+ return (linenums);
+ case 'L': /* Final line number known? */
+ return (linenums && ch_length() != NULL_POSITION);
+ case 'm': /* More than one file? */
+ return (nifile() > 1);
+ case 'n': /* First prompt in a new file? */
+ return (new_file);
+ case 'p': /* Percent into file known? */
+ return (curr_byte(where) != NULL_POSITION &&
+ ch_length() > 0);
+ case 's': /* Size of file known? */
+ case 'B':
+ return (ch_length() != NULL_POSITION);
+ case 'x': /* Is there a "next" file? */
+ return (next_ifile(curr_ifile) != NULL_IFILE);
+ }
+ return (0);
+}
+
+/*
+ * Decode a "percent" prototype character.
+ * A prototype string may include various "percent" escapes;
+ * that is, a percent sign followed by a single letter.
+ * Here we decode that letter and take the appropriate action,
+ * usually by appending something to the message being built.
+ */
+ static void
+protochar(c, where)
+ int c;
+ int where;
+{
+ POSITION pos;
+ POSITION len;
+ int n;
+ IFILE h;
+
+ switch (c)
+ {
+ case 'b': /* Current byte offset */
+ pos = curr_byte(where);
+ if (pos != NULL_POSITION)
+ ap_pos(pos);
+ else
+ ap_quest();
+ break;
+#if EDITOR
+ case 'E': /* Editor name */
+ ap_str(editor);
+ break;
+#endif
+ case 'f': /* File name */
+ ap_str(get_filename(curr_ifile));
+ break;
+ case 'i': /* Index into list of files */
+ ap_int(get_index(curr_ifile));
+ break;
+ case 'l': /* Current line number */
+ n = currline(where);
+ if (n != 0)
+ ap_int(n);
+ else
+ ap_quest();
+ break;
+ case 'L': /* Final line number */
+ len = ch_length();
+ if (len == NULL_POSITION || len == ch_zero() ||
+ (n = find_linenum(len)) <= 0)
+ ap_quest();
+ else
+ ap_int(n-1);
+ break;
+ case 'm': /* Number of files */
+ ap_int(nifile());
+ break;
+ case 'p': /* Percent into file */
+ pos = curr_byte(where);
+ len = ch_length();
+ if (pos != NULL_POSITION && len > 0)
+ ap_int(percentage(pos,len));
+ else
+ ap_quest();
+ break;
+ case 's': /* Size of file */
+ case 'B':
+ len = ch_length();
+ if (len != NULL_POSITION)
+ ap_pos(len);
+ else
+ ap_quest();
+ break;
+ case 't': /* Truncate trailing spaces in the message */
+ while (mp > message && mp[-1] == ' ')
+ mp--;
+ break;
+ case 'x': /* Name of next file */
+ h = next_ifile(curr_ifile);
+ if (h != NULL_IFILE)
+ ap_str(get_filename(h));
+ else
+ ap_quest();
+ break;
+ }
+}
+
+/*
+ * Skip a false conditional.
+ * When a false condition is found (either a false IF or the ELSE part
+ * of a true IF), this routine scans the prototype string to decide
+ * where to resume parsing the string.
+ * We must keep track of nested IFs and skip them properly.
+ */
+ static char *
+skipcond(p)
+ register char *p;
+{
+ register int iflevel;
+
+ /*
+ * We came in here after processing a ? or :,
+ * so we start nested one level deep.
+ */
+ iflevel = 1;
+
+ for (;;) switch (*++p)
+ {
+ case '?':
+ /*
+ * Start of a nested IF.
+ */
+ iflevel++;
+ break;
+ case ':':
+ /*
+ * Else.
+ * If this matches the IF we came in here with,
+ * then we're done.
+ */
+ if (iflevel == 1)
+ return (p);
+ break;
+ case '.':
+ /*
+ * Endif.
+ * If this matches the IF we came in here with,
+ * then we're done.
+ */
+ if (--iflevel == 0)
+ return (p);
+ break;
+ case '\\':
+ /*
+ * Backslash escapes the next character.
+ */
+ ++p;
+ break;
+ case '\0':
+ /*
+ * Whoops. Hit end of string.
+ * This is a malformed conditional, but just treat it
+ * as if all active conditionals ends here.
+ */
+ return (p-1);
+ }
+ /*NOTREACHED*/
+}
+
+ static char *
+wherechar(p, wp)
+ char *p;
+ int *wp;
+{
+ switch (*p)
+ {
+ case 'b': case 'l': case 'p':
+ switch (*++p)
+ {
+ case 't': *wp = TOP; break;
+ case 'm': *wp = MIDDLE; break;
+ case 'b': *wp = BOTTOM; break;
+ case 'B': *wp = BOTTOM_PLUS_ONE; break;
+ case 'j': *wp = adjsline(jump_sline); break;
+ default: *wp = TOP; p--; break;
+ }
+ }
+ return (p);
+}
+
+/*
+ * Construct a message based on a prototype string.
+ */
+ public char *
+pr_expand(proto, maxwidth)
+ char *proto;
+ int maxwidth;
+{
+ register char *p;
+ register int c;
+ int where;
+
+ mp = message;
+
+ if (*proto == '\0')
+ return ("");
+
+ for (p = proto; *p != '\0'; p++)
+ {
+ switch (*p)
+ {
+ default: /* Just put the character in the message */
+ *mp++ = *p;
+ break;
+ case '\\': /* Backslash escapes the next character */
+ p++;
+ *mp++ = *p;
+ break;
+ case '?': /* Conditional (IF) */
+ if ((c = *++p) == '\0')
+ --p;
+ else
+ {
+ p = wherechar(p, &where);
+ if (!cond(c, where))
+ p = skipcond(p);
+ }
+ break;
+ case ':': /* ELSE */
+ p = skipcond(p);
+ break;
+ case '.': /* ENDIF */
+ break;
+ case '%': /* Percent escape */
+ if ((c = *++p) == '\0')
+ --p;
+ else
+ {
+ p = wherechar(p, &where);
+ protochar(c, where);
+ }
+ break;
+ }
+ }
+
+ new_file = 0;
+ if (mp == message)
+ return (NULL);
+ *mp = '\0';
+ if (maxwidth > 0 && mp >= message + maxwidth)
+ {
+ /*
+ * Message is too long.
+ * Return just the final portion of it.
+ */
+ return (mp - maxwidth);
+ }
+ return (message);
+}
+
+/*
+ * Return a message suitable for printing by the "=" command.
+ */
+ public char *
+eq_message()
+{
+ return (pr_expand(eqproto, 0));
+}
+
+/*
+ * Return a prompt.
+ * This depends on the prompt type (SHORT, MEDIUM, LONG), etc.
+ * If we can't come up with an appropriate prompt, return NULL
+ * and the caller will prompt with a colon.
+ */
+ public char *
+pr_string()
+{
+ return (pr_expand(prproto[pr_type], sc_width-so_s_width-so_e_width-2));
+}
diff --git a/usr.bin/less/regexp.c b/usr.bin/less/regexp.c
new file mode 100644
index 00000000000..43324ee21df
--- /dev/null
+++ b/usr.bin/less/regexp.c
@@ -0,0 +1,1228 @@
+/*
+ * regcomp and regexec -- regsub and regerror are elsewhere
+ *
+ * Copyright (c) 1986 by University of Toronto.
+ * Written by Henry Spencer. Not derived from licensed software.
+ *
+ * Permission is granted to anyone to use this software for any
+ * purpose on any computer system, and to redistribute it freely,
+ * subject to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of
+ * this software, no matter how awful, even if they arise
+ * from defects in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either
+ * by explicit claim or by omission.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ * Beware that some of this code is subtly aware of the way operator
+ * precedence is structured in regular expressions. Serious changes in
+ * regular-expression syntax might require a total rethink.
+ *
+ * *** NOTE: this code has been altered slightly for use in Tcl. ***
+ * Slightly modified by David MacKenzie to undo most of the changes for TCL.
+ */
+
+#include <stdio.h>
+#include "regexp.h"
+char *strchr();
+
+/*
+ * The "internal use only" fields in regexp.h are present to pass info from
+ * compile to execute that permits the execute phase to run lots faster on
+ * simple cases. They are:
+ *
+ * regstart char that must begin a match; '\0' if none obvious
+ * reganch is the match anchored (at beginning-of-line only)?
+ * regmust string (pointer into program) that match must include, or NULL
+ * regmlen length of regmust string
+ *
+ * Regstart and reganch permit very fast decisions on suitable starting points
+ * for a match, cutting down the work a lot. Regmust permits fast rejection
+ * of lines that cannot possibly match. The regmust tests are costly enough
+ * that regcomp() supplies a regmust only if the r.e. contains something
+ * potentially expensive (at present, the only such thing detected is * or +
+ * at the start of the r.e., which can involve a lot of backup). Regmlen is
+ * supplied because the test in regexec() needs it and regcomp() is
+ * computing it anyway.
+ */
+
+/*
+ * Structure for regexp "program". This is essentially a linear encoding
+ * of a nondeterministic finite-state machine (aka syntax charts or
+ * "railroad normal form" in parsing technology). Each node is an opcode
+ * plus a "next" pointer, possibly plus an operand. "Next" pointers of
+ * all nodes except BRANCH implement concatenation; a "next" pointer with
+ * a BRANCH on both ends of it is connecting two alternatives. (Here we
+ * have one of the subtle syntax dependencies: an individual BRANCH (as
+ * opposed to a collection of them) is never concatenated with anything
+ * because of operator precedence.) The operand of some types of node is
+ * a literal string; for others, it is a node leading into a sub-FSM. In
+ * particular, the operand of a BRANCH node is the first node of the branch.
+ * (NB this is *not* a tree structure: the tail of the branch connects
+ * to the thing following the set of BRANCHes.) The opcodes are:
+ */
+
+/* definition number opnd? meaning */
+#define END 0 /* no End of program. */
+#define BOL 1 /* no Match "" at beginning of line. */
+#define EOL 2 /* no Match "" at end of line. */
+#define ANY 3 /* no Match any one character. */
+#define ANYOF 4 /* str Match any character in this string. */
+#define ANYBUT 5 /* str Match any character not in this string. */
+#define BRANCH 6 /* node Match this alternative, or the next... */
+#define BACK 7 /* no Match "", "next" ptr points backward. */
+#define EXACTLY 8 /* str Match this string. */
+#define NOTHING 9 /* no Match empty string. */
+#define STAR 10 /* node Match this (simple) thing 0 or more times. */
+#define PLUS 11 /* node Match this (simple) thing 1 or more times. */
+#define OPEN 20 /* no Mark this point in input as start of #n. */
+ /* OPEN+1 is number 1, etc. */
+#define CLOSE 30 /* no Analogous to OPEN. */
+
+/*
+ * Opcode notes:
+ *
+ * BRANCH The set of branches constituting a single choice are hooked
+ * together with their "next" pointers, since precedence prevents
+ * anything being concatenated to any individual branch. The
+ * "next" pointer of the last BRANCH in a choice points to the
+ * thing following the whole choice. This is also where the
+ * final "next" pointer of each individual branch points; each
+ * branch starts with the operand node of a BRANCH node.
+ *
+ * BACK Normal "next" pointers all implicitly point forward; BACK
+ * exists to make loop structures possible.
+ *
+ * STAR,PLUS '?', and complex '*' and '+', are implemented as circular
+ * BRANCH structures using BACK. Simple cases (one character
+ * per match) are implemented with STAR and PLUS for speed
+ * and to minimize recursive plunges.
+ *
+ * OPEN,CLOSE ...are numbered at compile time.
+ */
+
+/*
+ * A node is one char of opcode followed by two chars of "next" pointer.
+ * "Next" pointers are stored as two 8-bit pieces, high order first. The
+ * value is a positive offset from the opcode of the node containing it.
+ * An operand, if any, simply follows the node. (Note that much of the
+ * code generation knows about this implicit relationship.)
+ *
+ * Using two bytes for the "next" pointer is vast overkill for most things,
+ * but allows patterns to get big without disasters.
+ */
+#define OP(p) (*(p))
+#define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377))
+#define OPERAND(p) ((p) + 3)
+
+/*
+ * See regmagic.h for one further detail of program structure.
+ */
+
+
+/*
+ * Utility definitions.
+ */
+#ifndef CHARBITS
+#define UCHARAT(p) ((int)*(unsigned char *)(p))
+#else
+#define UCHARAT(p) ((int)*(p)&CHARBITS)
+#endif
+
+#define FAIL(m) { regerror(m); return(NULL); }
+#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?')
+#define META "^$.[()|?+*\\"
+
+/*
+ * Flags to be passed up and down.
+ */
+#define HASWIDTH 01 /* Known never to match null string. */
+#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */
+#define SPSTART 04 /* Starts with * or +. */
+#define WORST 0 /* Worst case. */
+
+/*
+ * Global work variables for regcomp().
+ */
+static char *regparse; /* Input-scan pointer. */
+static int regnpar; /* () count. */
+static char regdummy;
+static char *regcode; /* Code-emit pointer; &regdummy = don't. */
+static long regsize; /* Code size. */
+
+/*
+ * The first byte of the regexp internal "program" is actually this magic
+ * number; the start node begins in the second byte.
+ */
+#define MAGIC 0234
+
+
+/*
+ * Forward declarations for regcomp()'s friends.
+ */
+#ifndef STATIC
+#define STATIC static
+#endif
+STATIC char *reg();
+STATIC char *regbranch();
+STATIC char *regpiece();
+STATIC char *regatom();
+STATIC char *regnode();
+STATIC char *regnext();
+STATIC void regc();
+STATIC void reginsert();
+STATIC void regtail();
+STATIC void regoptail();
+#ifdef STRCSPN
+STATIC int strcspn();
+#endif
+
+/*
+ - regcomp - compile a regular expression into internal code
+ *
+ * We can't allocate space until we know how big the compiled form will be,
+ * but we can't compile it (and thus know how big it is) until we've got a
+ * place to put the code. So we cheat: we compile it twice, once with code
+ * generation turned off and size counting turned on, and once "for real".
+ * This also means that we don't allocate space until we are sure that the
+ * thing really will compile successfully, and we never have to move the
+ * code and thus invalidate pointers into it. (Note that it has to be in
+ * one piece because free() must be able to free it all.)
+ *
+ * Beware that the optimization-preparation code in here knows about some
+ * of the structure of the compiled regexp.
+ */
+regexp *
+regcomp(exp)
+char *exp;
+{
+ register regexp *r;
+ register char *scan;
+ register char *longest;
+ register int len;
+ int flags;
+
+ if (exp == NULL)
+ FAIL("NULL argument");
+
+ /* First pass: determine size, legality. */
+ regparse = exp;
+ regnpar = 1;
+ regsize = 0L;
+ regcode = &regdummy;
+ regc(MAGIC);
+ if (reg(0, &flags) == NULL)
+ return(NULL);
+
+ /* Small enough for pointer-storage convention? */
+ if (regsize >= 32767L) /* Probably could be 65535L. */
+ FAIL("regexp too big");
+
+ /* Allocate space. */
+ r = (regexp *)malloc(sizeof(regexp) + (unsigned)regsize);
+ if (r == NULL)
+ FAIL("out of space");
+
+ /* Second pass: emit code. */
+ regparse = exp;
+ regnpar = 1;
+ regcode = r->program;
+ regc(MAGIC);
+ if (reg(0, &flags) == NULL)
+ return(NULL);
+
+ /* Dig out information for optimizations. */
+ r->regstart = '\0'; /* Worst-case defaults. */
+ r->reganch = 0;
+ r->regmust = NULL;
+ r->regmlen = 0;
+ scan = r->program+1; /* First BRANCH. */
+ if (OP(regnext(scan)) == END) { /* Only one top-level choice. */
+ scan = OPERAND(scan);
+
+ /* Starting-point info. */
+ if (OP(scan) == EXACTLY)
+ r->regstart = *OPERAND(scan);
+ else if (OP(scan) == BOL)
+ r->reganch++;
+
+ /*
+ * If there's something expensive in the r.e., find the
+ * longest literal string that must appear and make it the
+ * regmust. Resolve ties in favor of later strings, since
+ * the regstart check works with the beginning of the r.e.
+ * and avoiding duplication strengthens checking. Not a
+ * strong reason, but sufficient in the absence of others.
+ */
+ if (flags&SPSTART) {
+ longest = NULL;
+ len = 0;
+ for (; scan != NULL; scan = regnext(scan))
+ if (OP(scan) == EXACTLY && ((int) strlen(OPERAND(scan))) >= len) {
+ longest = OPERAND(scan);
+ len = strlen(OPERAND(scan));
+ }
+ r->regmust = longest;
+ r->regmlen = len;
+ }
+ }
+
+ return(r);
+}
+
+/*
+ - reg - regular expression, i.e. main body or parenthesized thing
+ *
+ * Caller must absorb opening parenthesis.
+ *
+ * Combining parenthesis handling with the base level of regular expression
+ * is a trifle forced, but the need to tie the tails of the branches to what
+ * follows makes it hard to avoid.
+ */
+static char *
+reg(paren, flagp)
+int paren; /* Parenthesized? */
+int *flagp;
+{
+ register char *ret;
+ register char *br;
+ register char *ender;
+ register int parno = 0;
+ int flags;
+
+ *flagp = HASWIDTH; /* Tentatively. */
+
+ /* Make an OPEN node, if parenthesized. */
+ if (paren) {
+ if (regnpar >= NSUBEXP)
+ FAIL("too many ()");
+ parno = regnpar;
+ regnpar++;
+ ret = regnode(OPEN+parno);
+ } else
+ ret = NULL;
+
+ /* Pick up the branches, linking them together. */
+ br = regbranch(&flags);
+ if (br == NULL)
+ return(NULL);
+ if (ret != NULL)
+ regtail(ret, br); /* OPEN -> first. */
+ else
+ ret = br;
+ if (!(flags&HASWIDTH))
+ *flagp &= ~HASWIDTH;
+ *flagp |= flags&SPSTART;
+ while (*regparse == '|') {
+ regparse++;
+ br = regbranch(&flags);
+ if (br == NULL)
+ return(NULL);
+ regtail(ret, br); /* BRANCH -> BRANCH. */
+ if (!(flags&HASWIDTH))
+ *flagp &= ~HASWIDTH;
+ *flagp |= flags&SPSTART;
+ }
+
+ /* Make a closing node, and hook it on the end. */
+ ender = regnode((paren) ? CLOSE+parno : END);
+ regtail(ret, ender);
+
+ /* Hook the tails of the branches to the closing node. */
+ for (br = ret; br != NULL; br = regnext(br))
+ regoptail(br, ender);
+
+ /* Check for proper termination. */
+ if (paren && *regparse++ != ')') {
+ FAIL("unmatched ()");
+ } else if (!paren && *regparse != '\0') {
+ if (*regparse == ')') {
+ FAIL("unmatched ()");
+ } else
+ FAIL("junk on end"); /* "Can't happen". */
+ /* NOTREACHED */
+ }
+
+ return(ret);
+}
+
+/*
+ - regbranch - one alternative of an | operator
+ *
+ * Implements the concatenation operator.
+ */
+static char *
+regbranch(flagp)
+int *flagp;
+{
+ register char *ret;
+ register char *chain;
+ register char *latest;
+ int flags;
+
+ *flagp = WORST; /* Tentatively. */
+
+ ret = regnode(BRANCH);
+ chain = NULL;
+ while (*regparse != '\0' && *regparse != '|' && *regparse != ')') {
+ latest = regpiece(&flags);
+ if (latest == NULL)
+ return(NULL);
+ *flagp |= flags&HASWIDTH;
+ if (chain == NULL) /* First piece. */
+ *flagp |= flags&SPSTART;
+ else
+ regtail(chain, latest);
+ chain = latest;
+ }
+ if (chain == NULL) /* Loop ran zero times. */
+ (void) regnode(NOTHING);
+
+ return(ret);
+}
+
+/*
+ - regpiece - something followed by possible [*+?]
+ *
+ * Note that the branching code sequences used for ? and the general cases
+ * of * and + are somewhat optimized: they use the same NOTHING node as
+ * both the endmarker for their branch list and the body of the last branch.
+ * It might seem that this node could be dispensed with entirely, but the
+ * endmarker role is not redundant.
+ */
+static char *
+regpiece(flagp)
+int *flagp;
+{
+ register char *ret;
+ register char op;
+ register char *next;
+ int flags;
+
+ ret = regatom(&flags);
+ if (ret == NULL)
+ return(NULL);
+
+ op = *regparse;
+ if (!ISMULT(op)) {
+ *flagp = flags;
+ return(ret);
+ }
+
+ if (!(flags&HASWIDTH) && op != '?')
+ FAIL("*+ operand could be empty");
+ *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH);
+
+ if (op == '*' && (flags&SIMPLE))
+ reginsert(STAR, ret);
+ else if (op == '*') {
+ /* Emit x* as (x&|), where & means "self". */
+ reginsert(BRANCH, ret); /* Either x */
+ regoptail(ret, regnode(BACK)); /* and loop */
+ regoptail(ret, ret); /* back */
+ regtail(ret, regnode(BRANCH)); /* or */
+ regtail(ret, regnode(NOTHING)); /* null. */
+ } else if (op == '+' && (flags&SIMPLE))
+ reginsert(PLUS, ret);
+ else if (op == '+') {
+ /* Emit x+ as x(&|), where & means "self". */
+ next = regnode(BRANCH); /* Either */
+ regtail(ret, next);
+ regtail(regnode(BACK), ret); /* loop back */
+ regtail(next, regnode(BRANCH)); /* or */
+ regtail(ret, regnode(NOTHING)); /* null. */
+ } else if (op == '?') {
+ /* Emit x? as (x|) */
+ reginsert(BRANCH, ret); /* Either x */
+ regtail(ret, regnode(BRANCH)); /* or */
+ next = regnode(NOTHING); /* null. */
+ regtail(ret, next);
+ regoptail(ret, next);
+ }
+ regparse++;
+ if (ISMULT(*regparse))
+ FAIL("nested *?+");
+
+ return(ret);
+}
+
+/*
+ - regatom - the lowest level
+ *
+ * Optimization: gobbles an entire sequence of ordinary characters so that
+ * it can turn them into a single node, which is smaller to store and
+ * faster to run. Backslashed characters are exceptions, each becoming a
+ * separate node; the code is simpler that way and it's not worth fixing.
+ */
+static char *
+regatom(flagp)
+int *flagp;
+{
+ register char *ret;
+ int flags;
+
+ *flagp = WORST; /* Tentatively. */
+
+ switch (*regparse++) {
+ case '^':
+ ret = regnode(BOL);
+ break;
+ case '$':
+ ret = regnode(EOL);
+ break;
+ case '.':
+ ret = regnode(ANY);
+ *flagp |= HASWIDTH|SIMPLE;
+ break;
+ case '[': {
+ register int clss;
+ register int classend;
+
+ if (*regparse == '^') { /* Complement of range. */
+ ret = regnode(ANYBUT);
+ regparse++;
+ } else
+ ret = regnode(ANYOF);
+ if (*regparse == ']' || *regparse == '-')
+ regc(*regparse++);
+ while (*regparse != '\0' && *regparse != ']') {
+ if (*regparse == '-') {
+ regparse++;
+ if (*regparse == ']' || *regparse == '\0')
+ regc('-');
+ else {
+ clss = UCHARAT(regparse-2)+1;
+ classend = UCHARAT(regparse);
+ if (clss > classend+1)
+ FAIL("invalid [] range");
+ for (; clss <= classend; clss++)
+ regc(clss);
+ regparse++;
+ }
+ } else
+ regc(*regparse++);
+ }
+ regc('\0');
+ if (*regparse != ']')
+ FAIL("unmatched []");
+ regparse++;
+ *flagp |= HASWIDTH|SIMPLE;
+ }
+ break;
+ case '(':
+ ret = reg(1, &flags);
+ if (ret == NULL)
+ return(NULL);
+ *flagp |= flags&(HASWIDTH|SPSTART);
+ break;
+ case '\0':
+ case '|':
+ case ')':
+ FAIL("internal urp"); /* Supposed to be caught earlier. */
+ /* NOTREACHED */
+ break;
+ case '?':
+ case '+':
+ case '*':
+ FAIL("?+* follows nothing");
+ /* NOTREACHED */
+ break;
+ case '\\':
+ if (*regparse == '\0')
+ FAIL("trailing \\");
+ ret = regnode(EXACTLY);
+ regc(*regparse++);
+ regc('\0');
+ *flagp |= HASWIDTH|SIMPLE;
+ break;
+ default: {
+ register int len;
+ register char ender;
+
+ regparse--;
+ len = strcspn(regparse, META);
+ if (len <= 0)
+ FAIL("internal disaster");
+ ender = *(regparse+len);
+ if (len > 1 && ISMULT(ender))
+ len--; /* Back off clear of ?+* operand. */
+ *flagp |= HASWIDTH;
+ if (len == 1)
+ *flagp |= SIMPLE;
+ ret = regnode(EXACTLY);
+ while (len > 0) {
+ regc(*regparse++);
+ len--;
+ }
+ regc('\0');
+ }
+ break;
+ }
+
+ return(ret);
+}
+
+/*
+ - regnode - emit a node
+ */
+static char * /* Location. */
+regnode(op)
+char op;
+{
+ register char *ret;
+ register char *ptr;
+
+ ret = regcode;
+ if (ret == &regdummy) {
+ regsize += 3;
+ return(ret);
+ }
+
+ ptr = ret;
+ *ptr++ = op;
+ *ptr++ = '\0'; /* Null "next" pointer. */
+ *ptr++ = '\0';
+ regcode = ptr;
+
+ return(ret);
+}
+
+/*
+ - regc - emit (if appropriate) a byte of code
+ */
+static void
+regc(b)
+char b;
+{
+ if (regcode != &regdummy)
+ *regcode++ = b;
+ else
+ regsize++;
+}
+
+/*
+ - reginsert - insert an operator in front of already-emitted operand
+ *
+ * Means relocating the operand.
+ */
+static void
+reginsert(op, opnd)
+char op;
+char *opnd;
+{
+ register char *src;
+ register char *dst;
+ register char *place;
+
+ if (regcode == &regdummy) {
+ regsize += 3;
+ return;
+ }
+
+ src = regcode;
+ regcode += 3;
+ dst = regcode;
+ while (src > opnd)
+ *--dst = *--src;
+
+ place = opnd; /* Op node, where operand used to be. */
+ *place++ = op;
+ *place++ = '\0';
+ *place++ = '\0';
+}
+
+/*
+ - regtail - set the next-pointer at the end of a node chain
+ */
+static void
+regtail(p, val)
+char *p;
+char *val;
+{
+ register char *scan;
+ register char *temp;
+ register int offset;
+
+ if (p == &regdummy)
+ return;
+
+ /* Find last node. */
+ scan = p;
+ for (;;) {
+ temp = regnext(scan);
+ if (temp == NULL)
+ break;
+ scan = temp;
+ }
+
+ if (OP(scan) == BACK)
+ offset = scan - val;
+ else
+ offset = val - scan;
+ *(scan+1) = (offset>>8)&0377;
+ *(scan+2) = offset&0377;
+}
+
+/*
+ - regoptail - regtail on operand of first argument; nop if operandless
+ */
+static void
+regoptail(p, val)
+char *p;
+char *val;
+{
+ /* "Operandless" and "op != BRANCH" are synonymous in practice. */
+ if (p == NULL || p == &regdummy || OP(p) != BRANCH)
+ return;
+ regtail(OPERAND(p), val);
+}
+
+/*
+ * regexec and friends
+ */
+
+/*
+ * Global work variables for regexec().
+ */
+static char *reginput; /* String-input pointer. */
+static char *regbol; /* Beginning of input, for ^ check. */
+static char **regstartp; /* Pointer to startp array. */
+static char **regendp; /* Ditto for endp. */
+
+/*
+ * Forwards.
+ */
+STATIC int regtry();
+STATIC int regmatch();
+STATIC int regrepeat();
+
+#ifdef DEBUG
+int regnarrate = 0;
+void regdump();
+STATIC char *regprop();
+#endif
+
+/*
+ - regexec - match a regexp against a string
+ */
+int
+regexec(prog, string)
+register regexp *prog;
+register char *string;
+{
+ register char *s;
+
+ /* Be paranoid... */
+ if (prog == NULL || string == NULL) {
+ regerror("NULL parameter");
+ return(0);
+ }
+
+ /* Check validity of program. */
+ if (UCHARAT(prog->program) != MAGIC) {
+ regerror("corrupted program");
+ return(0);
+ }
+
+ /* If there is a "must appear" string, look for it. */
+ if (prog->regmust != NULL) {
+ s = string;
+ while ((s = strchr(s, prog->regmust[0])) != NULL) {
+ if (strncmp(s, prog->regmust, prog->regmlen) == 0)
+ break; /* Found it. */
+ s++;
+ }
+ if (s == NULL) /* Not present. */
+ return(0);
+ }
+
+ /* Mark beginning of line for ^ . */
+ regbol = string;
+
+ /* Simplest case: anchored match need be tried only once. */
+ if (prog->reganch)
+ return(regtry(prog, string));
+
+ /* Messy cases: unanchored match. */
+ s = string;
+ if (prog->regstart != '\0')
+ /* We know what char it must start with. */
+ while ((s = strchr(s, prog->regstart)) != NULL) {
+ if (regtry(prog, s))
+ return(1);
+ s++;
+ }
+ else
+ /* We don't -- general case. */
+ do {
+ if (regtry(prog, s))
+ return(1);
+ } while (*s++ != '\0');
+
+ /* Failure. */
+ return(0);
+}
+
+/*
+ - regtry - try match at specific point
+ */
+static int /* 0 failure, 1 success */
+regtry(prog, string)
+regexp *prog;
+char *string;
+{
+ register int i;
+ register char **sp;
+ register char **ep;
+
+ reginput = string;
+ regstartp = prog->startp;
+ regendp = prog->endp;
+
+ sp = prog->startp;
+ ep = prog->endp;
+ for (i = NSUBEXP; i > 0; i--) {
+ *sp++ = NULL;
+ *ep++ = NULL;
+ }
+ if (regmatch(prog->program + 1)) {
+ prog->startp[0] = string;
+ prog->endp[0] = reginput;
+ return(1);
+ } else
+ return(0);
+}
+
+/*
+ - regmatch - main matching routine
+ *
+ * Conceptually the strategy is simple: check to see whether the current
+ * node matches, call self recursively to see whether the rest matches,
+ * and then act accordingly. In practice we make some effort to avoid
+ * recursion, in particular by going through "ordinary" nodes (that don't
+ * need to know whether the rest of the match failed) by a loop instead of
+ * by recursion.
+ */
+static int /* 0 failure, 1 success */
+regmatch(prog)
+char *prog;
+{
+ register char *scan; /* Current node. */
+ char *next; /* Next node. */
+
+ scan = prog;
+#ifdef DEBUG
+ if (scan != NULL && regnarrate)
+ fprintf(stderr, "%s(\n", regprop(scan));
+#endif
+ while (scan != NULL) {
+#ifdef DEBUG
+ if (regnarrate)
+ fprintf(stderr, "%s...\n", regprop(scan));
+#endif
+ next = regnext(scan);
+
+ switch (OP(scan)) {
+ case BOL:
+ if (reginput != regbol)
+ return(0);
+ break;
+ case EOL:
+ if (*reginput != '\0')
+ return(0);
+ break;
+ case ANY:
+ if (*reginput == '\0')
+ return(0);
+ reginput++;
+ break;
+ case EXACTLY: {
+ register int len;
+ register char *opnd;
+
+ opnd = OPERAND(scan);
+ /* Inline the first character, for speed. */
+ if (*opnd != *reginput)
+ return(0);
+ len = strlen(opnd);
+ if (len > 1 && strncmp(opnd, reginput, len) != 0)
+ return(0);
+ reginput += len;
+ }
+ break;
+ case ANYOF:
+ if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == NULL)
+ return(0);
+ reginput++;
+ break;
+ case ANYBUT:
+ if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != NULL)
+ return(0);
+ reginput++;
+ break;
+ case NOTHING:
+ break;
+ case BACK:
+ break;
+ case OPEN+1:
+ case OPEN+2:
+ case OPEN+3:
+ case OPEN+4:
+ case OPEN+5:
+ case OPEN+6:
+ case OPEN+7:
+ case OPEN+8:
+ case OPEN+9: {
+ register int no;
+ register char *save;
+
+ no = OP(scan) - OPEN;
+ save = reginput;
+
+ if (regmatch(next)) {
+ /*
+ * Don't set startp if some later
+ * invocation of the same parentheses
+ * already has.
+ */
+ if (regstartp[no] == NULL)
+ regstartp[no] = save;
+ return(1);
+ } else
+ return(0);
+ }
+ /* NOTREACHED */
+ break;
+ case CLOSE+1:
+ case CLOSE+2:
+ case CLOSE+3:
+ case CLOSE+4:
+ case CLOSE+5:
+ case CLOSE+6:
+ case CLOSE+7:
+ case CLOSE+8:
+ case CLOSE+9: {
+ register int no;
+ register char *save;
+
+ no = OP(scan) - CLOSE;
+ save = reginput;
+
+ if (regmatch(next)) {
+ /*
+ * Don't set endp if some later
+ * invocation of the same parentheses
+ * already has.
+ */
+ if (regendp[no] == NULL)
+ regendp[no] = save;
+ return(1);
+ } else
+ return(0);
+ }
+ /* NOTREACHED */
+ break;
+ case BRANCH: {
+ register char *save;
+
+ if (OP(next) != BRANCH) /* No choice. */
+ next = OPERAND(scan); /* Avoid recursion. */
+ else {
+ do {
+ save = reginput;
+ if (regmatch(OPERAND(scan)))
+ return(1);
+ reginput = save;
+ scan = regnext(scan);
+ } while (scan != NULL && OP(scan) == BRANCH);
+ return(0);
+ /* NOTREACHED */
+ }
+ }
+ /* NOTREACHED */
+ break;
+ case STAR:
+ case PLUS: {
+ register char nextch;
+ register int no;
+ register char *save;
+ register int min;
+
+ /*
+ * Lookahead to avoid useless match attempts
+ * when we know what character comes next.
+ */
+ nextch = '\0';
+ if (OP(next) == EXACTLY)
+ nextch = *OPERAND(next);
+ min = (OP(scan) == STAR) ? 0 : 1;
+ save = reginput;
+ no = regrepeat(OPERAND(scan));
+ while (no >= min) {
+ /* If it could work, try it. */
+ if (nextch == '\0' || *reginput == nextch)
+ if (regmatch(next))
+ return(1);
+ /* Couldn't or didn't -- back up. */
+ no--;
+ reginput = save + no;
+ }
+ return(0);
+ }
+ /* NOTREACHED */
+ break;
+ case END:
+ return(1); /* Success! */
+ /* NOTREACHED */
+ break;
+ default:
+ regerror("memory corruption");
+ return(0);
+ /* NOTREACHED */
+ break;
+ }
+
+ scan = next;
+ }
+
+ /*
+ * We get here only if there's trouble -- normally "case END" is
+ * the terminating point.
+ */
+ regerror("corrupted pointers");
+ return(0);
+}
+
+/*
+ - regrepeat - repeatedly match something simple, report how many
+ */
+static int
+regrepeat(p)
+char *p;
+{
+ register int count = 0;
+ register char *scan;
+ register char *opnd;
+
+ scan = reginput;
+ opnd = OPERAND(p);
+ switch (OP(p)) {
+ case ANY:
+ count = strlen(scan);
+ scan += count;
+ break;
+ case EXACTLY:
+ while (*opnd == *scan) {
+ count++;
+ scan++;
+ }
+ break;
+ case ANYOF:
+ while (*scan != '\0' && strchr(opnd, *scan) != NULL) {
+ count++;
+ scan++;
+ }
+ break;
+ case ANYBUT:
+ while (*scan != '\0' && strchr(opnd, *scan) == NULL) {
+ count++;
+ scan++;
+ }
+ break;
+ default: /* Oh dear. Called inappropriately. */
+ regerror("internal foulup");
+ count = 0; /* Best compromise. */
+ break;
+ }
+ reginput = scan;
+
+ return(count);
+}
+
+/*
+ - regnext - dig the "next" pointer out of a node
+ */
+static char *
+regnext(p)
+register char *p;
+{
+ register int offset;
+
+ if (p == &regdummy)
+ return(NULL);
+
+ offset = NEXT(p);
+ if (offset == 0)
+ return(NULL);
+
+ if (OP(p) == BACK)
+ return(p-offset);
+ else
+ return(p+offset);
+}
+
+#ifdef DEBUG
+
+STATIC char *regprop();
+
+/*
+ - regdump - dump a regexp onto stdout in vaguely comprehensible form
+ */
+void
+regdump(r)
+regexp *r;
+{
+ register char *s;
+ register char op = EXACTLY; /* Arbitrary non-END op. */
+ register char *next;
+
+
+ s = r->program + 1;
+ while (op != END) { /* While that wasn't END last time... */
+ op = OP(s);
+ printf("%2d%s", s-r->program, regprop(s)); /* Where, what. */
+ next = regnext(s);
+ if (next == NULL) /* Next ptr. */
+ printf("(0)");
+ else
+ printf("(%d)", (s-r->program)+(next-s));
+ s += 3;
+ if (op == ANYOF || op == ANYBUT || op == EXACTLY) {
+ /* Literal string, where present. */
+ while (*s != '\0') {
+ putchar(*s);
+ s++;
+ }
+ s++;
+ }
+ putchar('\n');
+ }
+
+ /* Header fields of interest. */
+ if (r->regstart != '\0')
+ printf("start `%c' ", r->regstart);
+ if (r->reganch)
+ printf("anchored ");
+ if (r->regmust != NULL)
+ printf("must have \"%s\"", r->regmust);
+ printf("\n");
+}
+
+/*
+ - regprop - printable representation of opcode
+ */
+static char *
+regprop(op)
+char *op;
+{
+ register char *p;
+ static char buf[50];
+
+ (void) strcpy(buf, ":");
+
+ switch (OP(op)) {
+ case BOL:
+ p = "BOL";
+ break;
+ case EOL:
+ p = "EOL";
+ break;
+ case ANY:
+ p = "ANY";
+ break;
+ case ANYOF:
+ p = "ANYOF";
+ break;
+ case ANYBUT:
+ p = "ANYBUT";
+ break;
+ case BRANCH:
+ p = "BRANCH";
+ break;
+ case EXACTLY:
+ p = "EXACTLY";
+ break;
+ case NOTHING:
+ p = "NOTHING";
+ break;
+ case BACK:
+ p = "BACK";
+ break;
+ case END:
+ p = "END";
+ break;
+ case OPEN+1:
+ case OPEN+2:
+ case OPEN+3:
+ case OPEN+4:
+ case OPEN+5:
+ case OPEN+6:
+ case OPEN+7:
+ case OPEN+8:
+ case OPEN+9:
+ sprintf(buf+strlen(buf), "OPEN%d", OP(op)-OPEN);
+ p = NULL;
+ break;
+ case CLOSE+1:
+ case CLOSE+2:
+ case CLOSE+3:
+ case CLOSE+4:
+ case CLOSE+5:
+ case CLOSE+6:
+ case CLOSE+7:
+ case CLOSE+8:
+ case CLOSE+9:
+ sprintf(buf+strlen(buf), "CLOSE%d", OP(op)-CLOSE);
+ p = NULL;
+ break;
+ case STAR:
+ p = "STAR";
+ break;
+ case PLUS:
+ p = "PLUS";
+ break;
+ default:
+ regerror("corrupted opcode");
+ break;
+ }
+ if (p != NULL)
+ (void) strcat(buf, p);
+ return(buf);
+}
+#endif
+
+/*
+ * The following is provided for those people who do not have strcspn() in
+ * their C libraries. They should get off their butts and do something
+ * about it; at least one public-domain implementation of those (highly
+ * useful) string routines has been published on Usenet.
+ */
+#ifdef STRCSPN
+/*
+ * strcspn - find length of initial segment of s1 consisting entirely
+ * of characters not from s2
+ */
+
+static int
+strcspn(s1, s2)
+char *s1;
+char *s2;
+{
+ register char *scan1;
+ register char *scan2;
+ register int count;
+
+ count = 0;
+ for (scan1 = s1; *scan1 != '\0'; scan1++) {
+ for (scan2 = s2; *scan2 != '\0';) /* ++ moved down. */
+ if (*scan1 == *scan2++)
+ return(count);
+ count++;
+ }
+ return(count);
+}
+#endif
diff --git a/usr.bin/less/regexp.h b/usr.bin/less/regexp.h
new file mode 100644
index 00000000000..380a50769e5
--- /dev/null
+++ b/usr.bin/less/regexp.h
@@ -0,0 +1,33 @@
+/*
+ * Definitions etc. for regexp(3) routines.
+ *
+ * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof],
+ * not the System V one.
+ */
+
+#ifndef _REGEXP
+#define _REGEXP 1
+
+#define NSUBEXP 10
+typedef struct regexp {
+ char *startp[NSUBEXP];
+ char *endp[NSUBEXP];
+ char regstart; /* Internal use only. */
+ char reganch; /* Internal use only. */
+ char *regmust; /* Internal use only. */
+ int regmlen; /* Internal use only. */
+ char program[1]; /* Unwarranted chumminess with compiler. */
+} regexp;
+
+#if defined(__STDC__) || defined(__cplusplus)
+# define _ANSI_ARGS_(x) x
+#else
+# define _ANSI_ARGS_(x) ()
+#endif
+
+extern regexp *regcomp _ANSI_ARGS_((char *exp));
+extern int regexec _ANSI_ARGS_((regexp *prog, char *string));
+extern void regsub _ANSI_ARGS_((regexp *prog, char *source, char *dest));
+extern void regerror _ANSI_ARGS_((char *msg));
+
+#endif /* REGEXP */
diff --git a/usr.bin/less/screen.c b/usr.bin/less/screen.c
new file mode 100644
index 00000000000..95082ac6432
--- /dev/null
+++ b/usr.bin/less/screen.c
@@ -0,0 +1,1196 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * Routines which deal with the characteristics of the terminal.
+ * Uses termcap to be as terminal-independent as possible.
+ *
+ * {{ Maybe someday this should be rewritten to use curses or terminfo. }}
+ */
+
+#include "less.h"
+#include "cmd.h"
+
+#if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS
+#include <termios.h>
+#if HAVE_SYS_IOCTL_H && !defined(TIOCGWINSZ)
+#include <sys/ioctl.h>
+#endif
+#else
+#if HAVE_TERMIO_H
+#include <termio.h>
+#else
+#include <sgtty.h>
+#if HAVE_SYS_IOCTL_H && (defined(TIOCGWINSZ) || defined(TCGETA) || defined(TIOCGETP) || defined(WIOCGETD))
+#include <sys/ioctl.h>
+#endif
+#endif
+#endif
+#if HAVE_TERMCAP_H
+#include <termcap.h>
+#endif
+
+#ifndef TIOCGWINSZ
+/*
+ * For the Unix PC (ATT 7300 & 3B1):
+ * Since WIOCGETD is defined in sys/window.h, we can't use that to decide
+ * whether to include sys/window.h. Use SIGPHONE from sys/signal.h instead.
+ */
+#include <sys/signal.h>
+#ifdef SIGPHONE
+#include <sys/window.h>
+#endif
+#endif
+
+#if HAVE_SYS_STREAM_H
+#include <sys/stream.h>
+#endif
+#if HAVE_SYS_PTEM_H
+#include <sys/ptem.h>
+#endif
+
+#if OS2
+#define DEFAULT_TERM "ansi"
+#else
+#define DEFAULT_TERM "unknown"
+#endif
+
+/*
+ * Strings passed to tputs() to do various terminal functions.
+ */
+static char
+ *sc_pad, /* Pad string */
+ *sc_home, /* Cursor home */
+ *sc_addline, /* Add line, scroll down following lines */
+ *sc_lower_left, /* Cursor to last line, first column */
+ *sc_move, /* General cursor positioning */
+ *sc_clear, /* Clear screen */
+ *sc_eol_clear, /* Clear to end of line */
+ *sc_eos_clear, /* Clear to end of screen */
+ *sc_s_in, /* Enter standout (highlighted) mode */
+ *sc_s_out, /* Exit standout mode */
+ *sc_u_in, /* Enter underline mode */
+ *sc_u_out, /* Exit underline mode */
+ *sc_b_in, /* Enter bold mode */
+ *sc_b_out, /* Exit bold mode */
+ *sc_bl_in, /* Enter blink mode */
+ *sc_bl_out, /* Exit blink mode */
+ *sc_visual_bell, /* Visual bell (flash screen) sequence */
+ *sc_backspace, /* Backspace cursor */
+ *sc_s_keypad, /* Start keypad mode */
+ *sc_e_keypad, /* End keypad mode */
+ *sc_init, /* Startup terminal initialization */
+ *sc_deinit; /* Exit terminal de-initialization */
+
+static int init_done = 0;
+
+public int auto_wrap; /* Terminal does \r\n when write past margin */
+public int ignaw; /* Terminal ignores \n immediately after wrap */
+public int erase_char, kill_char; /* The user's erase and line-kill chars */
+public int werase_char; /* The user's word-erase char */
+public int sc_width, sc_height; /* Height & width of screen */
+public int bo_s_width, bo_e_width; /* Printing width of boldface seq */
+public int ul_s_width, ul_e_width; /* Printing width of underline seq */
+public int so_s_width, so_e_width; /* Printing width of standout seq */
+public int bl_s_width, bl_e_width; /* Printing width of blink seq */
+public int above_mem, below_mem; /* Memory retained above/below screen */
+public int can_goto_line; /* Can move cursor to any line */
+
+static char *cheaper();
+
+/*
+ * These two variables are sometimes defined in,
+ * and needed by, the termcap library.
+ */
+#if MUST_DEFINE_OSPEED
+extern short ospeed; /* Terminal output baud rate */
+extern char PC; /* Pad character */
+#endif
+
+extern int quiet; /* If VERY_QUIET, use visual bell for bell */
+extern int know_dumb; /* Don't complain about a dumb terminal */
+extern int back_scroll;
+extern int swindow;
+extern int no_init;
+#if HILITE_SEARCH
+extern int hilite_search;
+#endif
+
+extern char *tgetstr();
+extern char *tgoto();
+
+
+/*
+ * Change terminal to "raw mode", or restore to "normal" mode.
+ * "Raw mode" means
+ * 1. An outstanding read will complete on receipt of a single keystroke.
+ * 2. Input is not echoed.
+ * 3. On output, \n is mapped to \r\n.
+ * 4. \t is NOT expanded into spaces.
+ * 5. Signal-causing characters such as ctrl-C (interrupt),
+ * etc. are NOT disabled.
+ * It doesn't matter whether an input \n is mapped to \r, or vice versa.
+ */
+ public void
+raw_mode(on)
+ int on;
+{
+ static int curr_on = 0;
+
+ if (on == curr_on)
+ return;
+#if OS2
+ signal(SIGINT, SIG_IGN);
+ erase_char = '\b';
+ kill_char = '\033';
+#else
+#if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS
+ {
+ struct termios s;
+ static struct termios save_term;
+
+ if (on)
+ {
+ /*
+ * Get terminal modes.
+ */
+ tcgetattr(2, &s);
+
+ /*
+ * Save modes and set certain variables dependent on modes.
+ */
+ save_term = s;
+#if HAVE_OSPEED
+ switch (cfgetospeed(&s))
+ {
+#ifdef B0
+ case B0: ospeed = 0; break;
+#endif
+#ifdef B50
+ case B50: ospeed = 1; break;
+#endif
+#ifdef B75
+ case B75: ospeed = 2; break;
+#endif
+#ifdef B110
+ case B110: ospeed = 3; break;
+#endif
+#ifdef B134
+ case B134: ospeed = 4; break;
+#endif
+#ifdef B150
+ case B150: ospeed = 5; break;
+#endif
+#ifdef B200
+ case B200: ospeed = 6; break;
+#endif
+#ifdef B300
+ case B300: ospeed = 7; break;
+#endif
+#ifdef B600
+ case B600: ospeed = 8; break;
+#endif
+#ifdef B1200
+ case B1200: ospeed = 9; break;
+#endif
+#ifdef B1800
+ case B1800: ospeed = 10; break;
+#endif
+#ifdef B2400
+ case B2400: ospeed = 11; break;
+#endif
+#ifdef B4800
+ case B4800: ospeed = 12; break;
+#endif
+#ifdef B9600
+ case B9600: ospeed = 13; break;
+#endif
+#ifdef EXTA
+ case EXTA: ospeed = 14; break;
+#endif
+#ifdef EXTB
+ case EXTB: ospeed = 15; break;
+#endif
+#ifdef B57600
+ case B57600: ospeed = 16; break;
+#endif
+#ifdef B115200
+ case B115200: ospeed = 17; break;
+#endif
+ default: ;
+ }
+#endif
+ erase_char = s.c_cc[VERASE];
+ kill_char = s.c_cc[VKILL];
+#ifdef VWERASE
+ werase_char = s.c_cc[VWERASE];
+#else
+ werase_char = 0;
+#endif
+
+ /*
+ * Set the modes to the way we want them.
+ */
+ s.c_lflag &= ~(0
+#ifdef ICANON
+ | ICANON
+#endif
+#ifdef ECHO
+ | ECHO
+#endif
+#ifdef ECHOE
+ | ECHOE
+#endif
+#ifdef ECHOK
+ | ECHOK
+#endif
+#if ECHONL
+ | ECHONL
+#endif
+ );
+
+ s.c_oflag |= (0
+#ifdef XTABS
+ | XTABS
+#else
+#ifdef TAB3
+ | TAB3
+#else
+#ifdef OXTABS
+ | OXTABS
+#endif
+#endif
+#endif
+#ifdef OPOST
+ | OPOST
+#endif
+#ifdef ONLCR
+ | ONLCR
+#endif
+ );
+
+ s.c_oflag &= ~(0
+#ifdef ONOEOT
+ | ONOEOT
+#endif
+#ifdef OCRNL
+ | OCRNL
+#endif
+#ifdef ONOCR
+ | ONOCR
+#endif
+#ifdef ONLRET
+ | ONLRET
+#endif
+ );
+ s.c_cc[VMIN] = 1;
+ s.c_cc[VTIME] = 0;
+ } else
+ {
+ /*
+ * Restore saved modes.
+ */
+ s = save_term;
+ }
+ tcsetattr(2, TCSADRAIN, &s);
+ }
+#else
+#ifdef TCGETA
+ {
+ struct termio s;
+ static struct termio save_term;
+
+ if (on)
+ {
+ /*
+ * Get terminal modes.
+ */
+ ioctl(2, TCGETA, &s);
+
+ /*
+ * Save modes and set certain variables dependent on modes.
+ */
+ save_term = s;
+#if HAVE_OSPEED
+ ospeed = s.c_cflag & CBAUD;
+#endif
+ erase_char = s.c_cc[VERASE];
+ kill_char = s.c_cc[VKILL];
+#ifdef VWERASE
+ werase_char = s.c_cc[VWERASE];
+#else
+ werase_char = 0;
+#endif
+
+ /*
+ * Set the modes to the way we want them.
+ */
+ s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
+ s.c_oflag |= (OPOST|ONLCR|TAB3);
+ s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
+ s.c_cc[VMIN] = 1;
+ s.c_cc[VTIME] = 0;
+ } else
+ {
+ /*
+ * Restore saved modes.
+ */
+ s = save_term;
+ }
+ ioctl(2, TCSETAW, &s);
+ }
+#else
+ {
+ struct sgttyb s;
+ static struct sgttyb save_term;
+
+ if (on)
+ {
+ /*
+ * Get terminal modes.
+ */
+ ioctl(2, TIOCGETP, &s);
+
+ /*
+ * Save modes and set certain variables dependent on modes.
+ */
+ save_term = s;
+#if HAVE_OSPEED
+ ospeed = s.sg_ospeed;
+#endif
+ erase_char = s.sg_erase;
+ kill_char = s.sg_kill;
+ werase_char = 0;
+
+ /*
+ * Set the modes to the way we want them.
+ */
+ s.sg_flags |= CBREAK;
+ s.sg_flags &= ~(ECHO|XTABS);
+ } else
+ {
+ /*
+ * Restore saved modes.
+ */
+ s = save_term;
+ }
+ ioctl(2, TIOCSETN, &s);
+ }
+#endif
+#endif
+#endif
+ curr_on = on;
+}
+
+ static void
+cannot(s)
+ char *s;
+{
+ PARG parg;
+
+ if (know_dumb)
+ /*
+ * User knows this is a dumb terminal, so don't tell him.
+ */
+ return;
+
+ parg.p_string = s;
+ error("WARNING: terminal cannot %s", &parg);
+}
+
+/*
+ * Get size of the output screen.
+ */
+#if OS2
+ public void
+scrsize()
+{
+ int s[2];
+
+ _scrsize(s);
+ sc_width = s[0];
+ sc_height = s[1];
+}
+
+#else
+
+ public void
+scrsize()
+{
+ register char *s;
+#ifdef TIOCGWINSZ
+ struct winsize w;
+#else
+#ifdef WIOCGETD
+ struct uwdata w;
+#endif
+#endif
+
+#ifdef TIOCGWINSZ
+ if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_row > 0)
+ sc_height = w.ws_row;
+ else
+#else
+#ifdef WIOCGETD
+ if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_height > 0)
+ sc_height = w.uw_height/w.uw_vs;
+ else
+#endif
+#endif
+ if ((s = getenv("LINES")) != NULL)
+ sc_height = atoi(s);
+ else
+ sc_height = tgetnum("li");
+
+ if (sc_height <= 0)
+ sc_height = 24;
+
+#ifdef TIOCGWINSZ
+ if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_col > 0)
+ sc_width = w.ws_col;
+ else
+#ifdef WIOCGETD
+ if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_width > 0)
+ sc_width = w.uw_width/w.uw_hs;
+ else
+#endif
+#endif
+ if ((s = getenv("COLUMNS")) != NULL)
+ sc_width = atoi(s);
+ else
+ sc_width = tgetnum("co");
+
+ if (sc_width <= 0)
+ sc_width = 80;
+}
+#endif /* OS2 */
+
+/*
+ * Take care of the "variable" keys.
+ * Certain keys send escape sequences which differ on different terminals
+ * (such as the arrow keys, INSERT, DELETE, etc.)
+ * Construct the commands based on these keys.
+ */
+ public void
+get_editkeys()
+{
+ char *sp;
+ char *s;
+ char tbuf[40];
+
+ static char kfcmdtable[400];
+ int sz_kfcmdtable = 0;
+ static char kecmdtable[400];
+ int sz_kecmdtable = 0;
+
+#define put_cmd(str,action,tbl,sz) { \
+ strcpy(tbl+sz, str); \
+ sz += strlen(str) + 1; \
+ tbl[sz++] = action; }
+#define put_esc_cmd(str,action,tbl,sz) { \
+ tbl[sz++] = ESC; \
+ put_cmd(str,action,tbl,sz); }
+
+#define put_fcmd(str,action) put_cmd(str,action,kfcmdtable,sz_kfcmdtable)
+#define put_ecmd(str,action) put_cmd(str,action,kecmdtable,sz_kecmdtable)
+#define put_esc_fcmd(str,action) put_esc_cmd(str,action,kfcmdtable,sz_kfcmdtable)
+#define put_esc_ecmd(str,action) put_esc_cmd(str,action,kecmdtable,sz_kecmdtable)
+
+ /*
+ * Look at some interesting keys and see what strings they send.
+ * Create commands (both command keys and line-edit keys).
+ */
+
+ /* RIGHT ARROW */
+ sp = tbuf;
+ if ((s = tgetstr("kr", &sp)) != NULL)
+ {
+ put_ecmd(s, EC_RIGHT);
+ put_esc_ecmd(s, EC_W_RIGHT);
+ }
+
+ /* LEFT ARROW */
+ sp = tbuf;
+ if ((s = tgetstr("kl", &sp)) != NULL)
+ {
+ put_ecmd(s, EC_LEFT);
+ put_esc_ecmd(s, EC_W_LEFT);
+ }
+
+ /* UP ARROW */
+ sp = tbuf;
+ if ((s = tgetstr("ku", &sp)) != NULL)
+ {
+ put_ecmd(s, EC_UP);
+ put_fcmd(s, A_B_LINE);
+ }
+
+ /* DOWN ARROW */
+ sp = tbuf;
+ if ((s = tgetstr("kd", &sp)) != NULL)
+ {
+ put_ecmd(s, EC_DOWN);
+ put_fcmd(s, A_F_LINE);
+ }
+
+ /* PAGE UP */
+ sp = tbuf;
+ if ((s = tgetstr("kP", &sp)) != NULL)
+ {
+ put_fcmd(s, A_B_SCREEN);
+ }
+
+ /* PAGE DOWN */
+ sp = tbuf;
+ if ((s = tgetstr("kN", &sp)) != NULL)
+ {
+ put_fcmd(s, A_F_SCREEN);
+ }
+
+ /* HOME */
+ sp = tbuf;
+ if ((s = tgetstr("kh", &sp)) != NULL)
+ {
+ put_ecmd(s, EC_HOME);
+ }
+
+ /* END */
+ sp = tbuf;
+ if ((s = tgetstr("@7", &sp)) != NULL)
+ {
+ put_ecmd(s, EC_END);
+ }
+
+ /* DELETE */
+ sp = tbuf;
+ if ((s = tgetstr("kD", &sp)) == NULL)
+ {
+ /* Use DEL (\177) if no "kD" termcap. */
+ tbuf[1] = '\177';
+ tbuf[2] = '\0';
+ s = tbuf+1;
+ }
+ put_ecmd(s, EC_DELETE);
+ put_esc_ecmd(s, EC_W_DELETE);
+
+ /* BACKSPACE */
+ tbuf[0] = ESC;
+ tbuf[1] = erase_char;
+ tbuf[2] = '\0';
+ put_ecmd(tbuf, EC_W_BACKSPACE);
+
+ if (werase_char != 0)
+ {
+ tbuf[0] = werase_char;
+ tbuf[1] = '\0';
+ put_ecmd(tbuf, EC_W_BACKSPACE);
+ }
+
+ /*
+ * Register the two tables.
+ */
+ add_fcmd_table(kfcmdtable, sz_kfcmdtable);
+ add_ecmd_table(kecmdtable, sz_kecmdtable);
+}
+
+#if DEBUG
+ static void
+get_debug_term()
+{
+ auto_wrap = 1;
+ ignaw = 1;
+ so_s_width = so_e_width = 0;
+ bo_s_width = bo_e_width = 0;
+ ul_s_width = ul_e_width = 0;
+ bl_s_width = bl_e_width = 0;
+ sc_s_keypad = "(InitKey)";
+ sc_e_keypad = "(DeinitKey)";
+ sc_init = "(InitTerm)";
+ sc_deinit = "(DeinitTerm)";
+ sc_eol_clear = "(ClearEOL)";
+ sc_eos_clear = "(ClearEOS)";
+ sc_clear = "(ClearScreen)";
+ sc_move = "(Move<%d,%d>)";
+ sc_s_in = "(SO+)";
+ sc_s_out = "(SO-)";
+ sc_u_in = "(UL+)";
+ sc_u_out = "(UL-)";
+ sc_b_in = "(BO+)";
+ sc_b_out = "(BO-)";
+ sc_bl_in = "(BL+)";
+ sc_bl_out = "(BL-)";
+ sc_visual_bell ="(VBell)";
+ sc_backspace = "(BS)";
+ sc_home = "(Home)";
+ sc_lower_left = "(LL)";
+ sc_addline = "(AddLine)";
+}
+#endif
+
+/*
+ * Get terminal capabilities via termcap.
+ */
+ public void
+get_term()
+{
+ char *sp;
+ register char *t1, *t2;
+ register int hard;
+ char *term;
+ char termbuf[2048];
+
+ static char sbuf[1024];
+
+#ifdef OS2
+ /*
+ * Make sure the termcap database is available.
+ */
+ sp = getenv("TERMCAP");
+ if (sp == NULL || *sp == '\0')
+ {
+ char *termcap;
+ if ((sp = homefile("termcap.dat")) != NULL)
+ {
+ termcap = (char *) ecalloc(strlen(sp)+9, sizeof(char));
+ sprintf(termcap, "TERMCAP=%s", sp);
+ free(sp);
+ putenv(termcap);
+ }
+ }
+#endif
+ /*
+ * Find out what kind of terminal this is.
+ */
+ if ((term = getenv("TERM")) == NULL)
+ term = DEFAULT_TERM;
+ if (tgetent(termbuf, term) <= 0)
+ strcpy(termbuf, "dumb:hc:");
+
+ hard = tgetflag("hc");
+
+ /*
+ * Get size of the screen.
+ */
+ scrsize();
+ pos_init();
+
+#if DEBUG
+ if (strncmp(term,"LESSDEBUG",9) == 0)
+ {
+ get_debug_term();
+ return;
+ }
+#endif /* DEBUG */
+
+ auto_wrap = tgetflag("am");
+ ignaw = tgetflag("xn");
+ above_mem = tgetflag("da");
+ below_mem = tgetflag("db");
+
+ /*
+ * Assumes termcap variable "sg" is the printing width of:
+ * the standout sequence, the end standout sequence,
+ * the underline sequence, the end underline sequence,
+ * the boldface sequence, and the end boldface sequence.
+ */
+ if ((so_s_width = tgetnum("sg")) < 0)
+ so_s_width = 0;
+ so_e_width = so_s_width;
+
+ bo_s_width = bo_e_width = so_s_width;
+ ul_s_width = ul_e_width = so_s_width;
+ bl_s_width = bl_e_width = so_s_width;
+
+#if HILITE_SEARCH
+ if (so_s_width > 0 || so_e_width > 0)
+ /*
+ * Disable highlighting by default on magic cookie terminals.
+ * Turning on highlighting might change the displayed width
+ * of a line, causing the display to get messed up.
+ * The user can turn it back on with -g,
+ * but she won't like the results.
+ */
+ hilite_search = 0;
+#endif
+
+ /*
+ * Get various string-valued capabilities.
+ */
+ sp = sbuf;
+
+#if HAVE_OSPEED
+ sc_pad = tgetstr("pc", &sp);
+ if (sc_pad != NULL)
+ PC = *sc_pad;
+#endif
+
+ sc_s_keypad = tgetstr("ks", &sp);
+ if (sc_s_keypad == NULL)
+ sc_s_keypad = "";
+ sc_e_keypad = tgetstr("ke", &sp);
+ if (sc_e_keypad == NULL)
+ sc_e_keypad = "";
+
+ sc_init = tgetstr("ti", &sp);
+ if (sc_init == NULL)
+ sc_init = "";
+
+ sc_deinit= tgetstr("te", &sp);
+ if (sc_deinit == NULL)
+ sc_deinit = "";
+
+ sc_eol_clear = tgetstr("ce", &sp);
+ if (hard || sc_eol_clear == NULL || *sc_eol_clear == '\0')
+ {
+ cannot("clear to end of line");
+ sc_eol_clear = "";
+ }
+
+ sc_eos_clear = tgetstr("cd", &sp);
+ if (below_mem &&
+ (hard || sc_eos_clear == NULL || *sc_eos_clear == '\0'))
+ {
+ cannot("clear to end of screen");
+ sc_eol_clear = "";
+ }
+
+ sc_clear = tgetstr("cl", &sp);
+ if (hard || sc_clear == NULL || *sc_clear == '\0')
+ {
+ cannot("clear screen");
+ sc_clear = "\n\n";
+ }
+
+ sc_move = tgetstr("cm", &sp);
+ if (hard || sc_move == NULL || *sc_move == '\0')
+ {
+ /*
+ * This is not an error here, because we don't
+ * always need sc_move.
+ * We need it only if we don't have home or lower-left.
+ */
+ sc_move = "";
+ can_goto_line = 0;
+ } else
+ can_goto_line = 1;
+
+ sc_s_in = tgetstr("so", &sp);
+ if (hard || sc_s_in == NULL)
+ sc_s_in = "";
+
+ sc_s_out = tgetstr("se", &sp);
+ if (hard || sc_s_out == NULL)
+ sc_s_out = "";
+
+ sc_u_in = tgetstr("us", &sp);
+ if (hard || sc_u_in == NULL)
+ sc_u_in = sc_s_in;
+
+ sc_u_out = tgetstr("ue", &sp);
+ if (hard || sc_u_out == NULL)
+ sc_u_out = sc_s_out;
+
+ sc_b_in = tgetstr("md", &sp);
+ if (hard || sc_b_in == NULL)
+ {
+ sc_b_in = sc_s_in;
+ sc_b_out = sc_s_out;
+ } else
+ {
+ sc_b_out = tgetstr("me", &sp);
+ if (hard || sc_b_out == NULL)
+ sc_b_out = "";
+ }
+
+ sc_bl_in = tgetstr("mb", &sp);
+ if (hard || sc_bl_in == NULL)
+ {
+ sc_bl_in = sc_s_in;
+ sc_bl_out = sc_s_out;
+ } else
+ {
+ sc_bl_out = tgetstr("me", &sp);
+ if (hard || sc_bl_out == NULL)
+ sc_bl_out = "";
+ }
+
+ sc_visual_bell = tgetstr("vb", &sp);
+ if (hard || sc_visual_bell == NULL)
+ sc_visual_bell = "";
+
+ if (tgetflag("bs"))
+ sc_backspace = "\b";
+ else
+ {
+ sc_backspace = tgetstr("bc", &sp);
+ if (sc_backspace == NULL || *sc_backspace == '\0')
+ sc_backspace = "\b";
+ }
+
+ /*
+ * Choose between using "ho" and "cm" ("home" and "cursor move")
+ * to move the cursor to the upper left corner of the screen.
+ */
+ t1 = tgetstr("ho", &sp);
+ if (hard || t1 == NULL)
+ t1 = "";
+ if (*sc_move == '\0')
+ t2 = "";
+ else
+ {
+ strcpy(sp, tgoto(sc_move, 0, 0));
+ t2 = sp;
+ sp += strlen(sp) + 1;
+ }
+ sc_home = cheaper(t1, t2, "home cursor", "|\b^");
+
+ /*
+ * Choose between using "ll" and "cm" ("lower left" and "cursor move")
+ * to move the cursor to the lower left corner of the screen.
+ */
+ t1 = tgetstr("ll", &sp);
+ if (hard || t1 == NULL)
+ t1 = "";
+ if (*sc_move == '\0')
+ t2 = "";
+ else
+ {
+ strcpy(sp, tgoto(sc_move, 0, sc_height-1));
+ t2 = sp;
+ sp += strlen(sp) + 1;
+ }
+ sc_lower_left = cheaper(t1, t2,
+ "move cursor to lower left of screen", "\r");
+
+ /*
+ * Choose between using "al" or "sr" ("add line" or "scroll reverse")
+ * to add a line at the top of the screen.
+ */
+ t1 = tgetstr("al", &sp);
+ if (hard || t1 == NULL)
+ t1 = "";
+ t2 = tgetstr("sr", &sp);
+ if (hard || t2 == NULL)
+ t2 = "";
+#if OS2
+ if (*t1 == '\0' && *t2 == '\0')
+ sc_addline = "";
+ else
+#endif
+ if (above_mem)
+ sc_addline = t1;
+ else
+ sc_addline = cheaper(t1, t2, "scroll backwards", "");
+ if (*sc_addline == '\0')
+ {
+ /*
+ * Force repaint on any backward movement.
+ */
+ back_scroll = 0;
+ }
+}
+
+/*
+ * Return the cost of displaying a termcap string.
+ * We use the trick of calling tputs, but as a char printing function
+ * we give it inc_costcount, which just increments "costcount".
+ * This tells us how many chars would be printed by using this string.
+ * {{ Couldn't we just use strlen? }}
+ */
+static int costcount;
+
+/*ARGSUSED*/
+ static int
+inc_costcount(c)
+ int c;
+{
+ costcount++;
+ return (c);
+}
+
+ static int
+cost(t)
+ char *t;
+{
+ costcount = 0;
+ tputs(t, sc_height, inc_costcount);
+ return (costcount);
+}
+
+/*
+ * Return the "best" of the two given termcap strings.
+ * The best, if both exist, is the one with the lower
+ * cost (see cost() function).
+ */
+ static char *
+cheaper(t1, t2, doit, def)
+ char *t1, *t2;
+ char *doit;
+ char *def;
+{
+ if (*t1 == '\0' && *t2 == '\0')
+ {
+ cannot(doit);
+ return (def);
+ }
+ if (*t1 == '\0')
+ return (t2);
+ if (*t2 == '\0')
+ return (t1);
+ if (cost(t1) < cost(t2))
+ return (t1);
+ return (t2);
+}
+
+
+/*
+ * Below are the functions which perform all the
+ * terminal-specific screen manipulation.
+ */
+
+
+/*
+ * Initialize terminal
+ */
+ public void
+init()
+{
+ if (no_init)
+ return;
+ tputs(sc_init, sc_height, putchr);
+ tputs(sc_s_keypad, sc_height, putchr);
+ init_done = 1;
+}
+
+/*
+ * Deinitialize terminal
+ */
+ public void
+deinit()
+{
+ if (no_init)
+ return;
+ if (!init_done)
+ return;
+ tputs(sc_e_keypad, sc_height, putchr);
+ tputs(sc_deinit, sc_height, putchr);
+ init_done = 0;
+}
+
+/*
+ * Home cursor (move to upper left corner of screen).
+ */
+ public void
+home()
+{
+ tputs(sc_home, 1, putchr);
+}
+
+/*
+ * Add a blank line (called with cursor at home).
+ * Should scroll the display down.
+ */
+ public void
+add_line()
+{
+ tputs(sc_addline, sc_height, putchr);
+}
+
+/*
+ * Move cursor to lower left corner of screen.
+ */
+ public void
+lower_left()
+{
+ tputs(sc_lower_left, 1, putchr);
+}
+
+/*
+ * Goto a specific line on the screen.
+ */
+ public void
+goto_line(slinenum)
+ int slinenum;
+{
+ char *sc_goto;
+
+ sc_goto = tgoto(sc_move, 0, slinenum);
+ tputs(sc_goto, 1, putchr);
+}
+
+/*
+ * Ring the terminal bell.
+ */
+ public void
+bell()
+{
+ if (quiet == VERY_QUIET)
+ vbell();
+ else
+ putchr('\7');
+}
+
+/*
+ * Output the "visual bell", if there is one.
+ */
+ public void
+vbell()
+{
+ if (*sc_visual_bell == '\0')
+ return;
+ tputs(sc_visual_bell, sc_height, putchr);
+}
+
+/*
+ * Clear the screen.
+ */
+ public void
+clear()
+{
+ tputs(sc_clear, sc_height, putchr);
+}
+
+/*
+ * Clear from the cursor to the end of the cursor's line.
+ * {{ This must not move the cursor. }}
+ */
+ public void
+clear_eol()
+{
+ tputs(sc_eol_clear, 1, putchr);
+}
+
+/*
+ * Clear the bottom line of the display.
+ * Leave the cursor at the beginning of the bottom line.
+ */
+ public void
+clear_bot()
+{
+ lower_left();
+ if (below_mem)
+ tputs(sc_eos_clear, 1, putchr);
+ else
+ tputs(sc_eol_clear, 1, putchr);
+}
+
+/*
+ * Begin "standout" (bold, underline, or whatever).
+ */
+ public void
+so_enter()
+{
+ tputs(sc_s_in, 1, putchr);
+}
+
+/*
+ * End "standout".
+ */
+ public void
+so_exit()
+{
+ tputs(sc_s_out, 1, putchr);
+}
+
+/*
+ * Begin "underline" (hopefully real underlining,
+ * otherwise whatever the terminal provides).
+ */
+ public void
+ul_enter()
+{
+ tputs(sc_u_in, 1, putchr);
+}
+
+/*
+ * End "underline".
+ */
+ public void
+ul_exit()
+{
+ tputs(sc_u_out, 1, putchr);
+}
+
+/*
+ * Begin "bold"
+ */
+ public void
+bo_enter()
+{
+ tputs(sc_b_in, 1, putchr);
+}
+
+/*
+ * End "bold".
+ */
+ public void
+bo_exit()
+{
+ tputs(sc_b_out, 1, putchr);
+}
+
+/*
+ * Begin "blink"
+ */
+ public void
+bl_enter()
+{
+ tputs(sc_bl_in, 1, putchr);
+}
+
+/*
+ * End "blink".
+ */
+ public void
+bl_exit()
+{
+ tputs(sc_bl_out, 1, putchr);
+}
+
+/*
+ * Erase the character to the left of the cursor
+ * and move the cursor left.
+ */
+ public void
+backspace()
+{
+ /*
+ * Try to erase the previous character by overstriking with a space.
+ */
+ tputs(sc_backspace, 1, putchr);
+ putchr(' ');
+ tputs(sc_backspace, 1, putchr);
+}
+
+/*
+ * Output a plain backspace, without erasing the previous char.
+ */
+ public void
+putbs()
+{
+ tputs(sc_backspace, 1, putchr);
+}
diff --git a/usr.bin/less/search.c b/usr.bin/less/search.c
new file mode 100644
index 00000000000..73486878dd5
--- /dev/null
+++ b/usr.bin/less/search.c
@@ -0,0 +1,1197 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * Routines to search a file for a pattern.
+ */
+
+#include "less.h"
+#include "position.h"
+
+#define MINPOS(a,b) (((a) < (b)) ? (a) : (b))
+#define MAXPOS(a,b) (((a) > (b)) ? (a) : (b))
+
+#if HAVE_POSIX_REGCOMP
+#include <regex.h>
+#endif
+#if HAVE_RE_COMP
+char *re_comp();
+int re_exec();
+#endif
+#if HAVE_REGCMP
+char *regcmp();
+char *regex();
+extern char *__loc1;
+#endif
+#if HAVE_V8_REGCOMP
+#include "regexp.h"
+#endif
+#if NO_REGEX
+static int match();
+#endif
+
+extern int sigs;
+extern int how_search;
+extern int caseless;
+extern int linenums;
+extern int sc_height;
+extern int jump_sline;
+extern int bs_mode;
+#if HILITE_SEARCH
+extern int hilite_search;
+extern int screen_trashed;
+extern int size_linebuf;
+static int hide_hilite;
+static POSITION prep_startpos;
+static POSITION prep_endpos;
+
+struct hilite
+{
+ struct hilite *hl_next;
+ POSITION hl_startpos;
+ POSITION hl_endpos;
+};
+static struct hilite hilite_anchor = { NULL };
+#define hl_first hl_next
+#endif
+
+/*
+ * These are the static variables that represent the "remembered"
+ * search pattern.
+ */
+#if HAVE_POSIX_REGCOMP
+static regex_t *regpattern = NULL;
+#endif
+#if HAVE_RE_COMP
+int re_pattern = 0;
+#endif
+#if HAVE_REGCMP
+static char *cpattern = NULL;
+#endif
+#if HAVE_V8_REGCOMP
+static struct regexp *regpattern = NULL;
+#endif
+#if NO_REGEX
+static char *last_pattern = NULL;
+#endif
+
+static int is_caseless;
+static int is_ucase_pattern;
+
+/*
+ * Convert text. Perform one or more of these transformations:
+ */
+#define CVT_TO_LC 01 /* Convert upper-case to lower-case */
+#define CVT_BS 02 /* Do backspace processing */
+
+ static void
+cvt_text(odst, osrc, ops)
+ char *odst;
+ char *osrc;
+ int ops;
+{
+ register char *dst;
+ register char *src;
+
+ for (src = osrc, dst = odst; *src != '\0'; src++, dst++)
+ {
+ if ((ops & CVT_TO_LC) && isupper(*src))
+ /* Convert uppercase to lowercase. */
+ *dst = tolower(*src);
+ else if ((ops & CVT_BS) && *src == '\b' && dst > odst)
+ /* Delete BS and preceding char. */
+ dst -= 2;
+ else
+ /* Just copy. */
+ *dst = *src;
+ }
+ *dst = '\0';
+}
+
+/*
+ * Are there any uppercase letters in this string?
+ */
+ static int
+is_ucase(s)
+ char *s;
+{
+ register char *p;
+
+ for (p = s; *p != '\0'; p++)
+ if (isupper(*p))
+ return (1);
+ return (0);
+}
+
+/*
+ * Is there a previous (remembered) search pattern?
+ */
+ static int
+prev_pattern()
+{
+#if HAVE_POSIX_REGCOMP
+ return (regpattern != NULL);
+#endif
+#if HAVE_RE_COMP
+ return (re_pattern != 0);
+#endif
+#if HAVE_REGCMP
+ return (cpattern != NULL);
+#endif
+#if HAVE_V8_REGCOMP
+ return (regpattern != NULL);
+#endif
+#if NO_REGEX
+ return (last_pattern != NULL);
+#endif
+}
+
+#if HILITE_SEARCH
+/*
+ * Repaint the hilites currently displayed on the screen.
+ * Repaint each line which contains highlighted text.
+ * If on==0, force all hilites off.
+ */
+ public void
+repaint_hilite(on)
+ int on;
+{
+ int slinenum;
+ POSITION pos;
+ POSITION epos;
+ int save_hide_hilite;
+ extern int can_goto_line;
+
+ save_hide_hilite = hide_hilite;
+ if (!on)
+ {
+ if (hide_hilite)
+ return;
+ hide_hilite = 1;
+ }
+
+ if (!can_goto_line)
+ {
+ repaint();
+ hide_hilite = save_hide_hilite;
+ return;
+ }
+
+ for (slinenum = TOP; slinenum < TOP + sc_height-1; slinenum++)
+ {
+ pos = position(slinenum);
+ if (pos == NULL_POSITION)
+ continue;
+ epos = position(slinenum+1);
+ /*
+ * If any character in the line is highlighted,
+ * repaint the line.
+ */
+ if (is_hilited(pos, epos, 1))
+ {
+ (void) forw_line(pos);
+ goto_line(slinenum);
+ put_line();
+ }
+ }
+ hide_hilite = save_hide_hilite;
+}
+#endif
+
+/*
+ * Hide search string highlighting.
+ */
+ public void
+undo_search()
+{
+ if (!prev_pattern())
+ {
+ error("No previous regular expression", NULL_PARG);
+ return;
+ }
+#if HILITE_SEARCH
+ hide_hilite = !hide_hilite;
+ repaint_hilite(1);
+#endif
+}
+
+/*
+ * Compile a search pattern, for future use by match_pattern.
+ */
+ static int
+compile_pattern(pattern)
+ char *pattern;
+{
+#if HAVE_POSIX_REGCOMP
+ regex_t *s = (regex_t *) ecalloc(1, sizeof(regex_t));
+ if (regcomp(s, pattern, 0))
+ {
+ free(s);
+ error("Invalid pattern", NULL_PARG);
+ return (-1);
+ }
+ if (regpattern != NULL)
+ regfree(regpattern);
+ regpattern = s;
+#endif
+#if HAVE_RE_COMP
+ PARG parg;
+ if ((parg.p_string = re_comp(pattern)) != NULL)
+ {
+ error("%s", &parg);
+ return (-1);
+ }
+ re_pattern = 1;
+#endif
+#if HAVE_REGCMP
+ char *s;
+ if ((s = regcmp(pattern, 0)) == NULL)
+ {
+ error("Invalid pattern", NULL_PARG);
+ return (-1);
+ }
+ if (cpattern != NULL)
+ free(cpattern);
+ cpattern = s;
+#endif
+#if HAVE_V8_REGCOMP
+ struct regexp *s;
+ if ((s = regcomp(pattern)) == NULL)
+ {
+ /*
+ * regcomp has already printed error message via regerror().
+ */
+ return (-1);
+ }
+ if (regpattern != NULL)
+ free(regpattern);
+ regpattern = s;
+#endif
+#if NO_REGEX
+ static char lpbuf[100];
+ strcpy(lpbuf, pattern);
+ last_pattern = lpbuf;
+#endif
+ return (0);
+}
+
+/*
+ * Forget that we have a compiled pattern.
+ */
+ static void
+uncompile_pattern()
+{
+#if HAVE_POSIX_REGCOMP
+ if (regpattern != NULL)
+ regfree(regpattern);
+ regpattern = NULL;
+#endif
+#if HAVE_RE_COMP
+ re_pattern = 0;
+#endif
+#if HAVE_REGCMP
+ if (cpattern != NULL)
+ free(cpattern);
+ cpattern = NULL;
+#endif
+#if HAVE_V8_REGCOMP
+ if (regpattern != NULL)
+ free(regpattern);
+ regpattern = NULL;
+#endif
+#if NO_REGEX
+ last_pattern = NULL;
+#endif
+}
+
+/*
+ * Perform a pattern match with the previously compiled pattern.
+ * Set sp and ep to the start and end of the matched string.
+ */
+ static int
+match_pattern(line, sp, ep)
+ char *line;
+ char **sp;
+ char **ep;
+{
+ int matched;
+#if HAVE_POSIX_REGCOMP
+ regmatch_t rm;
+ matched = !regexec(regpattern, line, 1, &rm, 0);
+ if (!matched)
+ return (0);
+ *sp = line + rm.rm_so;
+ *ep = line + rm.rm_eo;
+#endif
+#if HAVE_RE_COMP
+ matched = (re_exec(line) == 1);
+ /*
+ * re_exec doesn't seem to provide a way to get the matched string.
+ */
+ *sp = *ep = NULL;
+#endif
+#if HAVE_REGCMP
+ *ep = regex(cpattern, line);
+ matched = (*ep != NULL);
+ if (!matched)
+ return (0);
+ *sp = __loc1;
+#endif
+#if HAVE_V8_REGCOMP
+ matched = regexec(regpattern, line);
+ if (!matched)
+ return (0);
+ *sp = regpattern->startp[0];
+ *ep = regpattern->endp[0];
+#endif
+#if NO_REGEX
+ matched = match(last_pattern, line, sp, ep);
+#endif
+ return (matched);
+}
+
+#if HILITE_SEARCH
+/*
+ * Clear the hilite list.
+ */
+ public void
+clr_hilite()
+{
+ struct hilite *hl;
+ struct hilite *nexthl;
+
+ for (hl = hilite_anchor.hl_first; hl != NULL; hl = nexthl)
+ {
+ nexthl = hl->hl_next;
+ free((void*)hl);
+ }
+ hilite_anchor.hl_first = NULL;
+ prep_startpos = prep_endpos = NULL_POSITION;
+}
+
+/*
+ * Should any characters in a specified range be highlighted?
+ * If nohide is nonzero, don't consider hide_hilite.
+ */
+ public int
+is_hilited(pos, epos, nohide)
+ POSITION pos;
+ POSITION epos;
+ int nohide;
+{
+ struct hilite *hl;
+
+ if (hilite_search == 0)
+ /*
+ * Not doing highlighting.
+ */
+ return (0);
+
+ if (!nohide && hide_hilite)
+ /*
+ * Highlighting is hidden.
+ */
+ return (0);
+
+ /*
+ * Look at each highlight and see if any part of it falls in the range.
+ */
+ for (hl = hilite_anchor.hl_first; hl != NULL; hl = hl->hl_next)
+ {
+ if (hl->hl_endpos > pos &&
+ (epos == NULL_POSITION || epos > hl->hl_startpos))
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Add a new hilite to a hilite list.
+ */
+ static void
+add_hilite(anchor, hl)
+ struct hilite *anchor;
+ struct hilite *hl;
+{
+ struct hilite *ihl;
+
+ /*
+ * Hilites are sorted in the list; find where new one belongs.
+ * Insert new one after ihl.
+ */
+ for (ihl = anchor; ihl->hl_next != NULL; ihl = ihl->hl_next)
+ {
+ if (ihl->hl_next->hl_startpos > hl->hl_startpos)
+ break;
+ }
+
+ /*
+ * Truncate hilite so it doesn't overlap any existing ones
+ * above and below it.
+ */
+ if (ihl != anchor)
+ hl->hl_startpos = MAXPOS(hl->hl_startpos, ihl->hl_endpos);
+ if (ihl->hl_next != NULL)
+ hl->hl_endpos = MINPOS(hl->hl_endpos, ihl->hl_next->hl_startpos);
+ if (hl->hl_startpos >= hl->hl_endpos)
+ {
+ /*
+ * Hilite was truncated out of existence.
+ */
+ free(hl);
+ return;
+ }
+ hl->hl_next = ihl->hl_next;
+ ihl->hl_next = hl;
+}
+
+/*
+ * Adjust hl_startpos & hl_endpos to account for backspace processing.
+ */
+ static void
+adj_hilite(anchor, linepos)
+ struct hilite *anchor;
+ POSITION linepos;
+{
+ char *line;
+ struct hilite *hl;
+ int checkstart;
+ POSITION opos;
+ POSITION npos;
+
+ /*
+ * The line was already scanned and hilites were added (in hilite_line).
+ * But it was assumed that each char position in the line
+ * correponds to one char position in the file.
+ * This may not be true if there are backspaces in the line.
+ * Get the raw line again. Look at each character.
+ */
+ (void) forw_raw_line(linepos, &line);
+ opos = npos = linepos;
+ hl = anchor->hl_first;
+ checkstart = TRUE;
+ while (hl != NULL)
+ {
+ /*
+ * See if we need to adjust the current hl_startpos or
+ * hl_endpos. After adjusting startpos[i], move to endpos[i].
+ * After adjusting endpos[i], move to startpos[i+1].
+ * The hilite list must be sorted thus:
+ * startpos[0] < endpos[0] <= startpos[1] < endpos[1] <= etc.
+ */
+ if (checkstart && hl->hl_startpos == opos)
+ {
+ hl->hl_startpos = npos;
+ checkstart = FALSE;
+ continue; /* {{ not really necessary }} */
+ } else if (!checkstart && hl->hl_endpos == opos)
+ {
+ hl->hl_endpos = npos;
+ checkstart = TRUE;
+ hl = hl->hl_next;
+ continue; /* {{ necessary }} */
+ }
+ if (*line == '\0')
+ break;
+ opos++;
+ npos++;
+ line++;
+ while (line[0] == '\b' && line[1] != '\0')
+ {
+ /*
+ * Found a backspace. The file position moves
+ * forward by 2 relative to the processed line
+ * which was searched in hilite_line.
+ */
+ npos += 2;
+ line += 2;
+ }
+ }
+}
+
+/*
+ * Make a hilite for each string in a physical line which matches
+ * the current pattern.
+ * sp,ep delimit the first match already found.
+ */
+ static void
+hilite_line(linepos, line, sp, ep)
+ POSITION linepos;
+ char *line;
+ char *sp;
+ char *ep;
+{
+ char *searchp;
+ struct hilite *hl;
+ struct hilite hilites;
+
+ if (sp == NULL || ep == NULL)
+ return;
+ /*
+ * sp and ep delimit the first match in the line.
+ * Mark the corresponding file positions, then
+ * look for further matches and mark them.
+ * {{ This technique, of calling match_pattern on subsequent
+ * substrings of the line, may mark more than is correct
+ * if, for example, the pattern starts with "^". }}
+ */
+ searchp = line;
+ /*
+ * Put the hilites into a temporary list until they're adjusted.
+ */
+ hilites.hl_first = NULL;
+ do {
+ if (ep > sp)
+ {
+ /*
+ * Assume that each char position in the "line"
+ * buffer corresponds to one char position in the file.
+ * This is not quite true; we need to adjust later.
+ */
+ hl = (struct hilite *) ecalloc(1, sizeof(struct hilite));
+ hl->hl_startpos = linepos + (sp-line);
+ hl->hl_endpos = linepos + (ep-line);
+ add_hilite(&hilites, hl);
+ }
+ /*
+ * If we matched more than zero characters,
+ * move to the first char after the string we matched.
+ * If we matched zero, just move to the next char.
+ */
+ if (ep > searchp)
+ searchp = ep;
+ else if (*searchp != '\0')
+ searchp++;
+ else /* end of line */
+ break;
+ } while (match_pattern(searchp, &sp, &ep));
+
+ if (bs_mode == BS_SPECIAL)
+ {
+ /*
+ * If there were backspaces in the original line, they
+ * were removed, and hl_startpos/hl_endpos are not correct.
+ * {{ This is very ugly. }}
+ */
+ adj_hilite(&hilites, linepos);
+ }
+ /*
+ * Now put the hilites into the real list.
+ */
+ while ((hl = hilites.hl_next) != NULL)
+ {
+ hilites.hl_next = hl->hl_next;
+ add_hilite(&hilite_anchor, hl);
+ }
+}
+#endif
+
+/*
+ * Change the caseless-ness of searches.
+ * Updates the internal search state to reflect a change in the -i flag.
+ */
+ public void
+chg_caseless()
+{
+ if (!is_ucase_pattern)
+ /*
+ * Pattern did not have uppercase.
+ * Just set the search caselessness to the global caselessness.
+ */
+ is_caseless = caseless;
+ else
+ /*
+ * Pattern did have uppercase.
+ * Discard the pattern; we can't change search caselessness now.
+ */
+ uncompile_pattern();
+}
+
+#if HILITE_SEARCH
+/*
+ * Find matching text which is currently on screen and highlight it.
+ */
+ static void
+hilite_screen()
+{
+ struct scrpos scrpos;
+
+ get_scrpos(&scrpos);
+ if (scrpos.pos == NULL_POSITION)
+ return;
+ prep_hilite(scrpos.pos, position(BOTTOM_PLUS_ONE));
+ repaint_hilite(1);
+}
+
+/*
+ * Change highlighting parameters.
+ */
+ public void
+chg_hilite()
+{
+ /*
+ * Erase any highlights currently on screen.
+ */
+ clr_hilite();
+ hide_hilite = 0;
+
+ if (hilite_search == OPT_ONPLUS)
+ /*
+ * Display highlights.
+ */
+ hilite_screen();
+}
+#endif
+
+/*
+ * Figure out where to start a search.
+ */
+ static POSITION
+search_pos(search_type)
+ int search_type;
+{
+ POSITION pos;
+ int linenum;
+
+ if (empty_screen())
+ {
+ /*
+ * Start at the beginning (or end) of the file.
+ * The empty_screen() case is mainly for
+ * command line initiated searches;
+ * for example, "+/xyz" on the command line.
+ * Also for multi-file (SRCH_PAST_EOF) searches.
+ */
+ if (search_type & SRCH_FORW)
+ {
+ return (ch_zero());
+ } else
+ {
+ pos = ch_length();
+ if (pos == NULL_POSITION)
+ {
+ (void) ch_end_seek();
+ pos = ch_length();
+ }
+ return (pos);
+ }
+ }
+ if (how_search)
+ {
+ /*
+ * Search does not include current screen.
+ */
+ if (search_type & SRCH_FORW)
+ linenum = BOTTOM_PLUS_ONE;
+ else
+ linenum = TOP;
+ pos = position(linenum);
+ } else
+ {
+ /*
+ * Search includes current screen.
+ * It starts at the jump target (if searching backwards),
+ * or at the jump target plus one (if forwards).
+ */
+ linenum = adjsline(jump_sline);
+ pos = position(linenum);
+ if (search_type & SRCH_FORW)
+ pos = forw_raw_line(pos, (char **)NULL);
+ }
+ return (pos);
+}
+
+/*
+ * Search a subset of the file, specified by start/end position.
+ */
+ static int
+search_range(pos, endpos, search_type, n, plinepos, pendpos)
+ POSITION pos;
+ POSITION endpos;
+ int search_type;
+ int n;
+ POSITION *plinepos;
+ POSITION *pendpos;
+{
+ char *line;
+ int linenum;
+ char *sp, *ep;
+ int line_match;
+ POSITION linepos, oldpos;
+
+ linenum = find_linenum(pos);
+ oldpos = pos;
+ for (;;)
+ {
+ /*
+ * Get lines until we find a matching one or until
+ * we hit end-of-file (or beginning-of-file if we're
+ * going backwards), or until we hit the end position.
+ */
+ if (ABORT_SIGS())
+ {
+ /*
+ * A signal aborts the search.
+ */
+ return (-1);
+ }
+
+ if (endpos != NULL_POSITION && pos >= endpos)
+ {
+ /*
+ * Reached end position without a match.
+ */
+ if (pendpos != NULL)
+ *pendpos = pos;
+ return (n);
+ }
+
+ if (search_type & SRCH_FORW)
+ {
+ /*
+ * Read the next line, and save the
+ * starting position of that line in linepos.
+ */
+ linepos = pos;
+ pos = forw_raw_line(pos, &line);
+ if (linenum != 0)
+ linenum++;
+ } else
+ {
+ /*
+ * Read the previous line and save the
+ * starting position of that line in linepos.
+ */
+ pos = back_raw_line(pos, &line);
+ linepos = pos;
+ if (linenum != 0)
+ linenum--;
+ }
+
+ if (pos == NULL_POSITION)
+ {
+ /*
+ * Reached EOF/BOF without a match.
+ */
+ if (pendpos != NULL)
+ *pendpos = NULL_POSITION;
+ return (n);
+ }
+
+ /*
+ * If we're using line numbers, we might as well
+ * remember the information we have now (the position
+ * and line number of the current line).
+ * Don't do it for every line because it slows down
+ * the search. Remember the line number only if
+ * we're "far" from the last place we remembered it.
+ */
+ if (linenums && abs((int)(pos - oldpos)) > 1024)
+ {
+ add_lnum(linenum, pos);
+ oldpos = pos;
+ }
+
+ /*
+ * If it's a caseless search, convert the line to lowercase.
+ * If we're doing backspace processing, delete backspaces.
+ */
+ if (is_caseless || bs_mode == BS_SPECIAL)
+ {
+ int ops = 0;
+ if (is_caseless)
+ ops |= CVT_TO_LC;
+ if (bs_mode == BS_SPECIAL)
+ ops |= CVT_BS;
+ cvt_text(line, line, ops);
+ }
+
+ /*
+ * Test the next line to see if we have a match.
+ * We are successful if we either want a match and got one,
+ * or if we want a non-match and got one.
+ */
+ line_match = match_pattern(line, &sp, &ep);
+ line_match = (!(search_type & SRCH_NOMATCH) && line_match) ||
+ ((search_type & SRCH_NOMATCH) && !line_match);
+ if (!line_match)
+ continue;
+ /*
+ * Got a match.
+ */
+ if (search_type & SRCH_FIND_ALL)
+ {
+#if HILITE_SEARCH
+ /*
+ * We are supposed to find all matches in the range.
+ * Just add the matches in this line to the
+ * hilite list and keep searching.
+ */
+ if (line_match)
+ hilite_line(linepos, line, sp, ep);
+#endif
+ } else if (--n <= 0)
+ {
+ /*
+ * Found the one match we're looking for.
+ * Return it.
+ */
+#if HILITE_SEARCH
+ if (hilite_search == 1)
+ {
+ /*
+ * Clear the hilite list and add only
+ * the matches in this one line.
+ */
+ clr_hilite();
+ if (line_match)
+ hilite_line(linepos, line, sp, ep);
+ }
+#endif
+ if (plinepos != NULL)
+ *plinepos = linepos;
+ return (0);
+ }
+ }
+}
+
+/*
+ * Search for the n-th occurrence of a specified pattern,
+ * either forward or backward.
+ * Return the number of matches not yet found in this file
+ * (that is, n minus the number of matches found).
+ * Return -1 if the search should be aborted.
+ * Caller may continue the search in another file
+ * if less than n matches are found in this file.
+ */
+ public int
+search(search_type, pattern, n)
+ int search_type;
+ char *pattern;
+ int n;
+{
+ POSITION pos;
+ int ucase;
+
+ if (pattern == NULL || *pattern == '\0')
+ {
+ /*
+ * A null pattern means use the previously compiled pattern.
+ */
+ if (!prev_pattern())
+ {
+ error("No previous regular expression", NULL_PARG);
+ return (-1);
+ }
+#if HILITE_SEARCH
+ if (hilite_search == OPT_ON)
+ {
+ /*
+ * Erase the highlights currently on screen.
+ * If the search fails, we'll redisplay them later.
+ */
+ repaint_hilite(0);
+ }
+ if (hilite_search == OPT_ONPLUS && hide_hilite)
+ {
+ /*
+ * Highlight any matches currently on screen,
+ * before we actually start the search.
+ */
+ hide_hilite = 0;
+ hilite_screen();
+ }
+ hide_hilite = 0;
+#endif
+ } else
+ {
+ /*
+ * Compile the pattern.
+ */
+ ucase = is_ucase(pattern);
+ if (caseless == OPT_ONPLUS)
+ cvt_text(pattern, pattern, CVT_TO_LC);
+ if (compile_pattern(pattern) < 0)
+ return (-1);
+ /*
+ * Ignore case if -I is set OR
+ * -i is set AND the pattern is all lowercase.
+ */
+ is_ucase_pattern = ucase;
+ if (is_ucase_pattern && caseless != OPT_ONPLUS)
+ is_caseless = 0;
+ else
+ is_caseless = caseless;
+#if HILITE_SEARCH
+ if (hilite_search)
+ {
+ /*
+ * Erase the highlights currently on screen.
+ * Also permanently delete them from the hilite list.
+ */
+ repaint_hilite(0);
+ hide_hilite = 0;
+ clr_hilite();
+ }
+ if (hilite_search == OPT_ONPLUS)
+ {
+ /*
+ * Highlight any matches currently on screen,
+ * before we actually start the search.
+ */
+ hilite_screen();
+ }
+#endif
+ }
+
+ /*
+ * Figure out where to start the search.
+ */
+ pos = search_pos(search_type);
+ if (pos == NULL_POSITION)
+ {
+ /*
+ * Can't find anyplace to start searching from.
+ */
+ if (search_type & SRCH_PAST_EOF)
+ return (n);
+ error("Nothing to search", NULL_PARG);
+ return (-1);
+ }
+
+ n = search_range(pos, NULL_POSITION, search_type, n,
+ &pos, (POSITION*)NULL);
+ if (n != 0)
+ {
+ /*
+ * Search was unsuccessful.
+ */
+#if HILITE_SEARCH
+ if (hilite_search == OPT_ON && n > 0)
+ /*
+ * Redisplay old hilites.
+ */
+ repaint_hilite(1);
+#endif
+ return (n);
+ }
+
+ /*
+ * Go to the matching line.
+ */
+ jump_loc(pos, jump_sline);
+
+#if HILITE_SEARCH
+ if (hilite_search == OPT_ON)
+ /*
+ * Display new hilites in the matching line.
+ */
+ repaint_hilite(1);
+#endif
+ return (0);
+}
+
+#if HILITE_SEARCH
+/*
+ * Prepare hilites in a given range of the file.
+ *
+ * The pair (prep_startpos,prep_endpos) delimits a contiguous region
+ * of the file that has been "prepared"; that is, scanned for matches for
+ * the current search pattern, and hilites have been created for such matches.
+ * If prep_startpos == NULL_POSITION, the prep region is empty.
+ * If prep_endpos == NULL_POSITION, the prep region extends to EOF.
+ * prep_hilite asks that the range (spos,epos) be covered by the prep region.
+ */
+ public void
+prep_hilite(spos, epos)
+ POSITION spos;
+ POSITION epos;
+{
+ POSITION nprep_startpos = prep_startpos;
+ POSITION nprep_endpos = prep_endpos;
+/*
+ * Search beyond where we're asked to search, so the prep region covers
+ * more than we need. Do one big search instead of a bunch of small ones.
+ */
+#define SEARCH_MORE (3*size_linebuf)
+
+ if (!prev_pattern())
+ return;
+ /*
+ * Find two ranges:
+ * The range that we need to search (spos,epos); and the range that
+ * the "prep" region will then cover (nprep_startpos,nprep_endpos).
+ */
+
+ if (prep_startpos == NULL_POSITION ||
+ (epos != NULL_POSITION && epos < prep_startpos) ||
+ (prep_endpos != NULL_POSITION && spos > prep_endpos))
+ {
+ /*
+ * New range is not contiguous with old prep region.
+ * Discard the old prep region and start a new one.
+ */
+ clr_hilite();
+ if (epos != NULL_POSITION)
+ epos += SEARCH_MORE;
+ nprep_startpos = spos;
+ nprep_endpos = epos;
+ } else
+ {
+ /*
+ * New range partially or completely overlaps old prep region.
+ */
+ if (epos == NULL_POSITION)
+ {
+ /*
+ * New range goes to end of file.
+ */
+ nprep_endpos = NULL_POSITION;
+ } else if (epos > prep_endpos)
+ {
+ /*
+ * New range ends after old prep region.
+ * Extend prep region to end at end of new range.
+ */
+ epos += SEARCH_MORE;
+ nprep_endpos = epos;
+ } else /* (epos <= prep_endpos) */
+ {
+ /*
+ * New range ends within old prep region.
+ * Truncate search to end at start of old prep region.
+ */
+ epos = prep_startpos;
+ }
+
+ if (spos < prep_startpos)
+ {
+ /*
+ * New range starts before old prep region.
+ * Extend old prep region backwards to start at
+ * start of new range.
+ */
+ if (spos < SEARCH_MORE)
+ spos = 0;
+ else
+ spos -= SEARCH_MORE;
+ nprep_startpos = spos;
+ } else /* (spos >= prep_startpos) */
+ {
+ /*
+ * New range starts within or after old prep region.
+ * Trim search to start near end of old prep region
+ * (actually, one linebuf before end of old range).
+ */
+ if (prep_endpos == NULL_POSITION)
+ return;
+ else if (prep_endpos < size_linebuf)
+ spos = 0;
+ else
+ spos = prep_endpos - size_linebuf;
+ }
+ }
+
+ if (epos == NULL_POSITION || epos > spos)
+ {
+ if (search_range(spos, epos, SRCH_FORW|SRCH_FIND_ALL, 0,
+ (POSITION*)NULL, &epos) >= 0)
+ {
+ if (epos == NULL_POSITION || epos > nprep_endpos)
+ nprep_endpos = epos;
+ }
+ }
+ prep_startpos = nprep_startpos;
+ prep_endpos = nprep_endpos;
+}
+#endif
+
+#if NO_REGEX
+/*
+ * We have no pattern matching function from the library.
+ * We use this function to do simple pattern matching.
+ * It supports no metacharacters like *, etc.
+ */
+ static int
+match(pattern, buf, pfound, pend)
+ char *pattern, *buf;
+ char **pfound, **pend;
+{
+ register char *pp, *lp;
+
+ for ( ; *buf != '\0'; buf++)
+ {
+ for (pp = pattern, lp = buf; *pp == *lp; pp++, lp++)
+ if (*pp == '\0' || *lp == '\0')
+ break;
+ if (*pp == '\0')
+ {
+ if (pfound != NULL)
+ *pfound = buf;
+ if (pend != NULL)
+ *pend = lp;
+ return (1);
+ }
+ }
+ return (0);
+}
+#endif
+
+#if HAVE_V8_REGCOMP
+/*
+ * This function is called by the V8 regcomp to report
+ * errors in regular expressions.
+ */
+ void
+regerror(s)
+ char *s;
+{
+ PARG parg;
+
+ parg.p_string = s;
+ error("%s", &parg);
+}
+#endif
+
+#if !HAVE_STRCHR
+/*
+ * strchr is used by regexp.c.
+ */
+ char *
+strchr(s, c)
+ char *s;
+ int c;
+{
+ for ( ; *s != '\0'; s++)
+ if (*s == c)
+ return (s);
+ if (c == '\0')
+ return (s);
+ return (NULL);
+}
+#endif
+
diff --git a/usr.bin/less/signal.c b/usr.bin/less/signal.c
new file mode 100644
index 00000000000..9cbafa064b6
--- /dev/null
+++ b/usr.bin/less/signal.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * Routines dealing with signals.
+ *
+ * A signal usually merely causes a bit to be set in the "signals" word.
+ * At some convenient time, the mainline code checks to see if any
+ * signals need processing by calling psignal().
+ * If we happen to be reading from a file [in iread()] at the time
+ * the signal is received, we call intread to interrupt the iread.
+ */
+
+#include "less.h"
+#include <signal.h>
+
+/*
+ * "sigs" contains bits indicating signals which need to be processed.
+ */
+public int sigs;
+
+extern int sc_width, sc_height;
+extern int screen_trashed;
+extern int lnloop;
+extern int linenums;
+extern int wscroll;
+extern int reading;
+
+/*
+ * Interrupt signal handler.
+ */
+ /* ARGSUSED*/
+ static RETSIGTYPE
+u_interrupt(type)
+ int type;
+{
+#if OS2
+ SIGNAL(SIGINT, SIG_ACK);
+#endif
+ SIGNAL(SIGINT, u_interrupt);
+ sigs |= S_INTERRUPT;
+ if (reading)
+ intread();
+}
+
+#ifdef SIGTSTP
+/*
+ * "Stop" (^Z) signal handler.
+ */
+ /* ARGSUSED*/
+ static RETSIGTYPE
+stop(type)
+ int type;
+{
+ SIGNAL(SIGTSTP, stop);
+ sigs |= S_STOP;
+ if (reading)
+ intread();
+}
+#endif
+
+#ifdef SIGWINCH
+/*
+ * "Window" change handler
+ */
+ /* ARGSUSED*/
+ public RETSIGTYPE
+winch(type)
+ int type;
+{
+ SIGNAL(SIGWINCH, winch);
+ sigs |= S_WINCH;
+ if (reading)
+ intread();
+}
+#else
+#ifdef SIGWIND
+/*
+ * "Window" change handler
+ */
+ /* ARGSUSED*/
+ public RETSIGTYPE
+winch(type)
+ int type;
+{
+ SIGNAL(SIGWIND, winch);
+ sigs |= S_WINCH;
+ if (reading)
+ intread();
+}
+#endif
+#endif
+
+/*
+ * Set up the signal handlers.
+ */
+ public void
+init_signals(on)
+ int on;
+{
+ if (on)
+ {
+ /*
+ * Set signal handlers.
+ */
+ (void) SIGNAL(SIGINT, u_interrupt);
+#ifdef SIGTSTP
+ (void) SIGNAL(SIGTSTP, stop);
+#endif
+#ifdef SIGWINCH
+ (void) SIGNAL(SIGWINCH, winch);
+#else
+#ifdef SIGWIND
+ (void) SIGNAL(SIGWIND, winch);
+#endif
+#endif
+ } else
+ {
+ /*
+ * Restore signals to defaults.
+ */
+ (void) SIGNAL(SIGINT, SIG_DFL);
+#ifdef SIGTSTP
+ (void) SIGNAL(SIGTSTP, SIG_DFL);
+#endif
+#ifdef SIGWINCH
+ (void) SIGNAL(SIGWINCH, SIG_IGN);
+#endif
+#ifdef SIGWIND
+ (void) SIGNAL(SIGWIND, SIG_IGN);
+#endif
+ }
+}
+
+/*
+ * Process any signals we have received.
+ * A received signal cause a bit to be set in "sigs".
+ */
+ public void
+psignals()
+{
+ register int tsignals;
+
+ if ((tsignals = sigs) == 0)
+ return;
+ sigs = 0;
+
+#ifdef SIGTSTP
+ if (tsignals & S_STOP)
+ {
+ /*
+ * Clean up the terminal.
+ */
+#ifdef SIGTTOU
+ SIGNAL(SIGTTOU, SIG_IGN);
+#endif
+ clear_bot();
+ deinit();
+ flush();
+ raw_mode(0);
+#ifdef SIGTTOU
+ SIGNAL(SIGTTOU, SIG_DFL);
+#endif
+ SIGNAL(SIGTSTP, SIG_DFL);
+ kill(getpid(), SIGTSTP);
+ /*
+ * ... Bye bye. ...
+ * Hopefully we'll be back later and resume here...
+ * Reset the terminal and arrange to repaint the
+ * screen when we get back to the main command loop.
+ */
+ SIGNAL(SIGTSTP, stop);
+ raw_mode(1);
+ init();
+ screen_trashed = 1;
+ tsignals |= S_WINCH;
+ }
+#endif
+#ifdef S_WINCH
+ if (tsignals & S_WINCH)
+ {
+ int old_width, old_height;
+ /*
+ * Re-execute scrsize() to read the new window size.
+ */
+ old_width = sc_width;
+ old_height = sc_height;
+ get_term();
+ if (sc_width != old_width || sc_height != old_height)
+ {
+ wscroll = (sc_height + 1) / 2;
+ screen_trashed = 1;
+ }
+ }
+#endif
+ if (tsignals & S_INTERRUPT)
+ {
+ bell();
+ /*
+ * {{ You may wish to replace the bell() with
+ * error("Interrupt", NULL_PARG); }}
+ */
+
+ /*
+ * If we were interrupted while in the "calculating
+ * line numbers" loop, turn off line numbers.
+ */
+ if (lnloop)
+ {
+ lnloop = 0;
+ if (linenums == 2)
+ screen_trashed = 1;
+ linenums = 0;
+ error("Line numbers turned off", NULL_PARG);
+ }
+
+ }
+}
diff --git a/usr.bin/less/tags.c b/usr.bin/less/tags.c
new file mode 100644
index 00000000000..d9318d8503b
--- /dev/null
+++ b/usr.bin/less/tags.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include "less.h"
+
+#define WHITESP(c) ((c)==' ' || (c)=='\t')
+
+#if TAGS
+
+public char *tagfile;
+public char *tags = "tags";
+
+static char *tagpattern;
+static int taglinenum;
+
+extern int linenums;
+extern int sigs;
+extern int jump_sline;
+
+/*
+ * Find a tag in the "tags" file.
+ * Sets "tagfile" to the name of the file containing the tag,
+ * and "tagpattern" to the search pattern which should be used
+ * to find the tag.
+ */
+ public void
+findtag(tag)
+ register char *tag;
+{
+ char *p;
+ char *q;
+ register FILE *f;
+ register int taglen;
+ int search_char;
+ int err;
+ static char tline[200];
+
+ if ((f = fopen(tags, "r")) == NULL)
+ {
+ error("No tags file", NULL_PARG);
+ tagfile = NULL;
+ return;
+ }
+
+ taglen = strlen(tag);
+
+ /*
+ * Search the tags file for the desired tag.
+ */
+ while (fgets(tline, sizeof(tline), f) != NULL)
+ {
+ if (strncmp(tag, tline, taglen) != 0 || !WHITESP(tline[taglen]))
+ continue;
+
+ /*
+ * Found it.
+ * The line contains the tag, the filename and the
+ * location in the file, separated by white space.
+ * The location is either a decimal line number,
+ * or a search pattern surrounded by a pair of delimiters.
+ * Parse the line and extract these parts.
+ */
+ tagfile = tagpattern = NULL;
+ taglinenum = 0;
+
+ /*
+ * Skip over the whitespace after the tag name.
+ */
+ p = skipsp(tline+taglen);
+ if (*p == '\0')
+ /* File name is missing! */
+ continue;
+
+ /*
+ * Save the file name.
+ * Skip over the whitespace after the file name.
+ */
+ tagfile = p;
+ while (!WHITESP(*p) && *p != '\0')
+ p++;
+ *p++ = '\0';
+ p = skipsp(p);
+ if (*p == '\0')
+ /* Pattern is missing! */
+ continue;
+
+ /*
+ * First see if it is a line number.
+ */
+ taglinenum = getnum(&p, 0, &err);
+ if (err)
+ {
+ /*
+ * No, it must be a pattern.
+ * Delete the initial "^" (if present) and
+ * the final "$" from the pattern.
+ * Delete any backslash in the pattern.
+ */
+ taglinenum = 0;
+ search_char = *p++;
+ if (*p == '^')
+ p++;
+ tagpattern = q = p;
+ while (*p != search_char && *p != '\0')
+ {
+ if (*p == '\\')
+ p++;
+ *q++ = *p++;
+ }
+ if (q[-1] == '$')
+ q--;
+ *q = '\0';
+ }
+
+ fclose(f);
+ return;
+ }
+ fclose(f);
+ error("No such tag in tags file", NULL_PARG);
+ tagfile = NULL;
+}
+
+/*
+ * Search for a tag.
+ * This is a stripped-down version of search().
+ * We don't use search() for several reasons:
+ * - We don't want to blow away any search string we may have saved.
+ * - The various regular-expression functions (from different systems:
+ * regcmp vs. re_comp) behave differently in the presence of
+ * parentheses (which are almost always found in a tag).
+ */
+ public POSITION
+tagsearch()
+{
+ POSITION pos, linepos;
+ int linenum;
+ char *line;
+
+ /*
+ * If we have the line number of the tag instead of the pattern,
+ * just use find_pos.
+ */
+ if (taglinenum)
+ return (find_pos(taglinenum));
+
+ pos = ch_zero();
+ linenum = find_linenum(pos);
+
+ for (;;)
+ {
+ /*
+ * Get lines until we find a matching one or
+ * until we hit end-of-file.
+ */
+ if (ABORT_SIGS())
+ return (NULL_POSITION);
+
+ /*
+ * Read the next line, and save the
+ * starting position of that line in linepos.
+ */
+ linepos = pos;
+ pos = forw_raw_line(pos, &line);
+ if (linenum != 0)
+ linenum++;
+
+ if (pos == NULL_POSITION)
+ {
+ /*
+ * We hit EOF without a match.
+ */
+ error("Tag not found", NULL_PARG);
+ return (NULL_POSITION);
+ }
+
+ /*
+ * If we're using line numbers, we might as well
+ * remember the information we have now (the position
+ * and line number of the current line).
+ */
+ if (linenums)
+ add_lnum(linenum, pos);
+
+ /*
+ * Test the line to see if we have a match.
+ * Use strncmp because the pattern may be
+ * truncated (in the tags file) if it is too long.
+ */
+ if (strncmp(tagpattern, line, strlen(tagpattern)) == 0)
+ break;
+ }
+
+ return (linepos);
+}
+
+#endif
diff --git a/usr.bin/less/ttyin.c b/usr.bin/less/ttyin.c
new file mode 100644
index 00000000000..ee1ed1f93e8
--- /dev/null
+++ b/usr.bin/less/ttyin.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * Routines dealing with getting input from the keyboard (i.e. from the user).
+ */
+
+#include "less.h"
+
+static int tty;
+
+/*
+ * Open keyboard for input.
+ */
+ public void
+open_getchr()
+{
+#if MSOFTC || OS2
+ extern int fd0;
+ /*
+ * Open a new handle to CON: in binary mode
+ * for unbuffered keyboard read.
+ */
+ fd0 = dup(0);
+ close(0);
+ tty = OPEN_TTYIN();
+#else
+ /*
+ * Try /dev/tty.
+ * If that doesn't work, use file descriptor 2,
+ * which in Unix is usually attached to the screen,
+ * but also usually lets you read from the keyboard.
+ */
+ tty = OPEN_TTYIN();
+ if (tty < 0)
+ tty = 2;
+#endif
+}
+
+/*
+ * Get a character from the keyboard.
+ */
+ public int
+getchr()
+{
+ char c;
+ int result;
+
+ do
+ {
+#if MSOFTC
+ /*
+ * In raw read, we don't see ^C so look here for it.
+ */
+ flush();
+ c = getch();
+ result = 1;
+ if (c == '\003')
+ return (READ_INTR);
+#else
+#if OS2
+ flush();
+ while (_read_kbd(0, 0, 0) != -1)
+ continue;
+ if ((c = _read_kbd(0, 1, 0)) == -1)
+ return (READ_INTR);
+ result = 1;
+#else
+ result = iread(tty, &c, sizeof(char));
+ if (result == READ_INTR)
+ return (READ_INTR);
+ if (result < 0)
+ {
+ /*
+ * Don't call error() here,
+ * because error calls getchr!
+ */
+ quit(QUIT_ERROR);
+ }
+#endif
+#endif
+ /*
+ * Various parts of the program cannot handle
+ * an input character of '\0'.
+ * If a '\0' was actually typed, convert it to '\340' here.
+ */
+ if (c == '\0')
+ c = '\340';
+ } while (result != 1);
+
+ return (c & 0377);
+}
diff --git a/usr.bin/less/version.c b/usr.bin/less/version.c
new file mode 100644
index 00000000000..c47f425cc3b
--- /dev/null
+++ b/usr.bin/less/version.c
@@ -0,0 +1,513 @@
+/*
+ * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+----------------------- CHANGE HISTORY --------------------------
+
+ 1/29/84 Allowed use on standard input
+ 2/1/84 Added E, N, P commands
+ 4/17/84 Added '=' command, 'stop' signal handling
+ 4/20/84 Added line folding
+v2 4/27/84 Fixed '=' command to use BOTTOM_PLUS_ONE,
+ instead of TOP, added 'p' & 'v' commands
+v3 5/3/84 Added -m and -t options, '-' command
+v4 5/3/84 Added LESS environment variable
+v5 5/3/84 New comments, fixed '-' command slightly
+v6 5/15/84 Added -Q, visual bell
+v7 5/24/84 Fixed jump_back(n) bug: n should count real
+ lines, not folded lines. Also allow number on G command.
+v8 5/30/84 Re-do -q and -Q commands
+v9 9/25/84 Added "+<cmd>" argument
+v10 10/10/84 Fixed bug in -b<n> argument processing
+v11 10/18/84 Made error() ring bell if \n not entered.
+-----------------------------------------------------------------
+v12 2/13/85 Reorganized signal handling and made portable to 4.2bsd.
+v13 2/16/85 Reword error message for '-' command.
+v14 2/22/85 Added -bf and -bp variants of -b.
+v15 2/25/85 Miscellaneous changes.
+v16 3/13/85 Added -u flag for backspace processing.
+v17 4/13/85 Added j and k commands, changed -t default.
+v18 4/20/85 Rewrote signal handling code.
+v19 5/2/85 Got rid of "verbose" eq_message().
+ Made search() scroll in some cases.
+v20 5/21/85 Fixed screen.c ioctls for System V.
+v21 5/23/85 Fixed some first_cmd bugs.
+v22 5/24/85 Added support for no RECOMP nor REGCMP.
+v23 5/25/85 Miscellanous changes and prettying up.
+ Posted to USENET.
+-----------------------------------------------------------------
+v24 6/3/85 Added ti,te terminal init & de-init.
+ (Thanks to Mike Kersenbrock)
+v25 6/8/85 Added -U flag, standout mode underlining.
+v26 6/9/85 Added -M flag.
+ Use underline termcap (us) if it exists.
+v27 6/15/85 Renamed some variables to make unique in
+ 6 chars. Minor fix to -m.
+v28 6/28/85 Fixed right margin bug.
+v29 6/28/85 Incorporated M.Rose's changes to signal.c
+v30 6/29/85 Fixed stupid bug in argument processing.
+v31 7/15/85 Added -p flag, changed repaint algorithm.
+ Added kludge for magic cookie terminals.
+v32 7/16/85 Added cat_file if output not a tty.
+v33 7/23/85 Added -e flag and EDITOR.
+v34 7/26/85 Added -s flag.
+v35 7/27/85 Rewrote option handling; added option.c.
+v36 7/29/85 Fixed -e flag to work if not last file.
+v37 8/10/85 Added -x flag.
+v38 8/19/85 Changed prompting; created prompt.c.
+v39 8/24/85 (Not -p) does not initially clear screen.
+v40 8/26/85 Added "skipping" indicator in forw().
+ Posted to USENET.
+-----------------------------------------------------------------
+v41 9/17/85 ONLY_RETURN, control char commands,
+ faster search, other minor fixes.
+v42 9/25/85 Added ++ command line syntax;
+ ch_fsize for pipes.
+v43 10/15/85 Added -h flag, changed prim.c algorithms.
+v44 10/16/85 Made END print in all cases of eof;
+ ignore SIGTTOU after receiv ing SIGTSTP.
+v45 10/16/85 Never print backspaces unless -u.
+v46 10/24/85 Backwards scroll in jump_loc.
+v47 10/30/85 Fixed bug in edit(): *first_cmd==0
+v48 11/16/85 Use TIOCSETN instead of TIOCSETP.
+ Added marks (m and ' commands).
+ Posted to USENET.
+-----------------------------------------------------------------
+v49 1/9/86 Fixed bug: signal didn't clear mcc.
+v50 1/15/86 Added ' (quote) to gomark.
+v51 1/16/86 Added + cmd, fixed problem if first_cmd
+ fails, made g cmd sort of "work" on pipes
+ ev en if bof is no longer buffered.
+v52 1/17/86 Made short files work better.
+v53 1/20/86 Added -P option.
+v54 1/20/86 Changed help to use HELPFILE.
+v55 1/23/86 Messages work better if not tty output.
+v56 1/24/86 Added -l option.
+v57 1/31/86 Fixed -l to get confirmation before
+ ov erwriting an existing file.
+v58 8/28/86 Added filename globbing.
+v59 9/15/86 Fixed some bugs with very long filenames.
+v60 9/26/86 Incorporated changes from Leith (Casey)
+ Leedom for boldface and -z option.
+v61 9/26/86 Got rid of annoying repaints after ! cmd.
+ Posted to USENET.
+-----------------------------------------------------------------
+v62 12/23/86 Added is_directory(); change -z default to
+ -1 instead of 24; cat-and-exit if -e and
+ file is less than a screenful.
+v63 1/8/87 Fixed bug in cat-and-exit if > 1 file.
+v64 1/12/87 Changed puts/putstr, putc/putchr,
+ getc/getchr to av oid name conflict with
+ stdio functions.
+v65 1/26/87 Allowed '-' command to change NUMBER
+ v alued options (thanks to Gary Puckering)
+v66 2/13/87 Fixed bug: prepaint should use force=1.
+v67 2/24/87 Added !! and % expansion to ! command.
+v68 2/25/87 Added SIGWINCH and TIOCGWINSZ support;
+ changed is_directory to bad_file.
+ (thanks to J. Robert Ward)
+v69 2/25/87 Added SIGWIND and WIOCGETD (for Unix PC).
+v70 3/13/87 Changed help cmd from 'h' to 'H'; better
+ error msgs in bad_file, errno_message.
+v71 5/11/87 Changed -p to -c, made triple -c/-C
+ for clear-eol like more's -c.
+v72 6/26/87 Added -E, -L, use $SHELL in lsystem().
+ (thanks to Stev e Spearman)
+v73 6/26/87 Allow Examine "#" for previous file.
+ Posted to USENET 8/25/87.
+-----------------------------------------------------------------
+v74 9/18/87 Fix conflict in EOF symbol with stdio.h,
+ Make os.c more portable to BSD.
+v75 9/23/87 Fix problems in get_term (thanks to
+ Paul Eggert); new backwards scrolling in
+ jump_loc (thanks to Marion Hakanson).
+v76 9/23/87 Added -i flag; allow single "!" to
+ inv oke a shell (thanks to Franco Barber).
+v77 9/24/87 Added -n flag and line number support.
+v78 9/25/87 Fixed problem with prompts longer than
+ the screen width.
+v79 9/29/87 Added the _ command.
+v80 10/6/87 Allow signal to break out of linenum scan.
+v81 10/6/87 Allow -b to be changed from within less.
+v82 10/7/87 Add cmd_decode to use a table for key
+ binding (thanks to Dav id Nason).
+v83 10/9/87 Allow .less file for user-defined keys.
+v84 10/11/87 Fix -e/-E problems (thanks to Felix Lee).
+v85 10/15/87 Search now keeps track of line numbers.
+v86 10/20/87 Added -B option and autobuf; fixed
+ "pipe error" bug.
+v87 3/1/88 Fix bug re BSD signals while reading file.
+v88 3/12/88 Use new format for -P option (thanks to
+ der Mouse), allow "+-c" without message,
+ fix bug re BSD hangup.
+v89 3/18/88 Turn off line numbers if linenum scan
+ is interrupted.
+v90 3/30/88 Allow -P from within less.
+v91 3/30/88 Added tags file support (new -t option)
+ (thanks to Brian Campbell).
+v92 4/4/88 Added -+option syntax.
+v93 4/11/88 Add support for slow input (thanks to
+ Joe Orost & apologies for taking almost
+ 3 years to get this in!)
+v94 4/11/88 Redo reading/signal stuff.
+v95 4/20/88 Repaint screen better after signal.
+v96 4/21/88 Add /! and ?! commands.
+v97 5/17/88 Allow -l/-L from within less.
+ Eliminate some static arrays (use calloc).
+ Posted to USENET.
+-----------------------------------------------------------------
+v98 10/14/88 Fix incorrect calloc call; uninitialized
+ var in exec_mca; core dump on unknown TERM.
+ Make v cmd work if past last line of file.
+ Fix some signal bugs.
+v99 10/29/88 Allow space between -X and string,
+ when X is a string-valued option.
+v100 1/5/89 Fix globbing bug when $SHELL not set;
+ allow spaces after -t command.
+v101 1/6/89 Fix problem with long (truncated) lines
+ in tags file (thanks to Neil Dixon).
+v102 1/6/89 Fix bug with E# when no prev file;
+ allow spaces after -l command.
+v103 3/14/89 Add -N, -f and -? options. Add z and w
+ commands. Add %L for prompt strings.
+v104 3/16/89 Added EDITPROTO.
+v105 3/20/89 Fix bug in find_linenum which cached
+ incorrectly on long lines.
+v106 3/31/89 Added -k option and multiple lesskey
+ files.
+v107 4/27/89 Add 8-bit char support and -g option.
+ Split option code into 3 files.
+v108 5/5/89 Allocate position table dynamically
+ (thanks to Paul Eggert); change % command
+ from "percent" to vi-style brace finder.
+v109 5/10/89 Added ESC-% command, split prim.c.
+v110 5/24/89 Fixed bug in + option; fixed repaint bug
+ under Sun windows (thanks to Paul Eggert).
+v111 5/25/89 Generalized # and % expansion; use
+ calloc for some error messages.
+v112 5/30/89 Get rid of ESC-%, add {}()[] commands.
+v113 5/31/89 Optimize lseeks (thanks to Paul Eggert).
+v114 7/25/89 Added ESC-/ and ESC-/! commands.
+v115 7/26/89 Added ESC-n command.
+v116 7/31/89 Added find_pos to optimize g command.
+v117 8/1/89 Change -f option to -r.
+v118 8/2/89 Save positions for all previous files,
+ not just the immediately previous one.
+v119 8/7/89 Save marks across file boundaries.
+ Add file handle stuff.
+v120 8/11/89 Add :ta command.
+v121 8/16/89 Add -f option.
+v122 8/30/89 Fix performance with many buffers.
+v123 8/31/89 Verbose prompts for string options.
+ Posted beta to USENET.
+-----------------------------------------------------------------
+v124 9/18/89 Reorganize search commands,
+ N = rev, ESC-n = span, add ESC-N.
+v125 9/18/89 Fix tab bug (thanks to Alex Liu).
+ Fix EOF bug when both -w and -c.
+v126 10/25/89 Add -j option.
+v127 10/27/89 Fix problems with blank lines before BOF.
+v128 10/27/89 Add %bj, etc. to prompt strings.
+v129 11/3/89 Add -+,-- commands; add set-option and
+ unset-option to lesskey.
+v130 11/6/89 Generalize A_EXTRA to string, remove
+ set-option, unset-option from lesskey.
+v131 11/7/89 Changed name of EDITPROTO to LESSEDIT.
+v132 11/8/89 Allow editing of command prefix.
+v133 11/16/89 Add -y option (thanks to Jeff Sullivan).
+v134 12/1/89 Glob filenames in the -l command.
+v135 12/5/89 Combined {}()[] commands into one, and
+ added ESC-^F and ESC-^B commands.
+v136 1/20/90 Added -S, -R flags. Added | command.
+ Added warning for binary files. (thanks
+ to Richard Brittain and J. Sullivan).
+v137 1/21/90 Rewrote horrible pappend code.
+ Added * notation for hi-bit chars.
+v138 1/24/90 Fix magic cookie terminal handling.
+ Get rid of "cleanup" loop in ch_get.
+v139 1/27/90 Added MSDOS support. (many thanks
+ to Richard Brittain).
+v140 2/7/90 Editing a new file adds it to the
+ command line list.
+v141 2/8/90 Add edit_list for editing >1 file.
+v142 2/10/90 Add :x command.
+v143 2/11/90 Add * and @ modifies to search cmds.
+ Change ESC-/ cmd from /@* to / *.
+v144 3/1/90 Messed around with ch_zero;
+ no real change.
+v145 3/2/90 Added -R and -v/-V for MSDOS;
+ renamed FILENAME to avoid conflict.
+v146 3/5/90 Pull cmdbuf functions out of command.c
+v147 3/7/90 Implement ?@; fix multi-file edit bugs.
+v148 3/29/90 Fixed bug in :e<file> then :e#.
+v149 4/3/90 Change error,ierror,query to use PARG.
+v150 4/6/90 Add LESS_CHARSET, LESS_CHARDEF.
+v151 4/13/90 Remove -g option; clean up ispipe.
+v152 4/14/90 lsystem() closes input file, for
+ editors which require exclusive open.
+v153 4/18/90 Fix bug if SHELL unset;
+ fix bug in overstrike control char.
+v154 4/25/90 Output to fd 2 via buffer.
+v155 4/30/90 Ignore -i if uppercase in pattern
+ (thanks to Michael Rendell.)
+v156 5/3/90 Remove scroll limits in forw() & back();
+ causes problems with -c.
+v157 5/4/90 Forward search starts at next real line
+ (not screen line) after jump target.
+v158 6/14/90 Added F command.
+v159 7/29/90 Fix bug in exiting: output not flushed.
+v160 7/29/90 Clear screen before initial output w/ -c.
+v161 7/29/90 Add -T flag.
+v162 8/14/90 Fix bug with +F on command line.
+v163 8/21/90 Added LESSBINFMT variable.
+v164 9/5/90 Added -p, LINES, COLUMNS and
+ unset mark ' == BOF, for 1003.2 D5.
+v165 9/6/90 At EOF with -c set, don't display empty
+ screen when try to page forward.
+v166 9/6/90 Fix G when final line in file wraps.
+v167 9/11/90 Translate CR/LF -> LF for 1003.2.
+v168 9/13/90 Return to curr file if "tag not found".
+v169 12/12/90 G goes to EOF even if file has grown.
+v170 1/17/91 Add optimization for BSD _setjmp;
+ fix #include ioctl.h TERMIO problem.
+ (thanks to Paul Eggert)
+ Posted to USENET.
+-----------------------------------------------------------------
+v171 3/6/91 Fix -? bug in get_filename.
+v172 3/15/91 Fix G bug in empty file.
+ Fix bug with ?\n and -i and uppercase
+ pattern at EOF!
+ (thanks to Paul Eggert)
+v173 3/17/91 Change N cmd to not permanently change
+ direction. (thanks to Brian Matthews)
+v174 3/18/91 Fix bug with namelogfile not getting
+ cleared when change files.
+v175 3/18/91 Fix bug with ++cmd on command line.
+ (thanks to Jim Meyering)
+v176 4/2/91 Change | to not force current screen,
+ include marked line, start/end from
+ top of screen. Improve search speed.
+ (thanks to Don Mears)
+v177 4/2/91 Add LESSHELP variable.
+ Fix bug with F command with -e.
+ Try /dev/tty for input before using fd 2.
+ Patches posted to USENET 4/2/91.
+-----------------------------------------------------------------
+v178 4/8/91 Fixed bug in globbing logfile name.
+ (thanks to Jim Meyering)
+v179 4/9/91 Allow negative -z for screen-relative.
+v180 4/9/91 Clear to eos rather than eol if "db";
+ don't use "sr" if "da".
+ (thanks to Tor Lillqvist)
+v181 4/18/91 Fixed bug with "negative" chars 80 - FF.
+ (thanks to Benny Sander Hofmann)
+v182 5/16/91 Fixed bug with attribute at EOL.
+ (thanks to Brian Matthews)
+v183 6/1/91 Rewrite linstall to do smart config.
+v184 7/11/91 Process \b in searches based on -u
+ rather than -i.
+v185 7/11/91 -Pxxx sets short prompt; assume SIGWINCH
+ after a SIGSTOP. (thanks to Ken Laprade)
+-----------------------------------------------------------------
+v186 4/20/92 Port to MS-DOS (Microsoft C).
+v187 4/23/92 Added -D option & TAB_COMPLETE_FILENAME.
+v188 4/28/92 Added command line editing features.
+v189 12/8/92 Fix mem overrun in anscreen.c:init;
+ fix edit_list to recover from bin file.
+v190 2/13/93 Make TAB enter one filename at a time;
+ create ^L with old TAB functionality.
+v191 3/10/93 Defer creating "flash" page for MS-DOS.
+v192 9/6/93 Add BACK-TAB.
+v193 9/17/93 Simplify binary_file handling.
+v194 1/4/94 Add rudiments of alt_filename handling.
+v195 1/11/94 Port back to Unix; support keypad.
+-----------------------------------------------------------------
+v196 6/7/94 Fix bug with bad filename; fix IFILE
+ type problem. (thanks to David MacKenzie)
+v197 6/7/94 Fix bug with .less tables inserted wrong.
+v198 6/23/94 Use autoconf installation technology.
+ (thanks to David MacKenzie)
+v199 6/29/94 Fix MS-DOS build (thanks to Tim Wiegman).
+v200 7/25/94 Clean up copyright, minor fixes.
+ Posted to prep.ai.mit.edu
+-----------------------------------------------------------------
+v201 7/27/94 Check for no memcpy; add casts to calloc;
+ look for regcmp in libgen.a.
+ (thanks to Kaveh Ghazi).
+v202 7/28/94 Fix bug in edit_next/edit_prev with
+ non-existant files.
+v203 8/2/94 Fix a variety of configuration bugs on
+ various systems. (thanks to Sakai
+ Kiyotaka, Harald Koenig, Bjorn Brox,
+ Teemu Rantanen, and Thorsten Lockert)
+v204 8/3/94 Use strerror if available.
+ (thanks to J.T. Conklin)
+v205 8/5/94 Fix bug in finding "me" termcap entry.
+ (thanks to Andreas Stolcke)
+8/10/94 v205+: Change BUFSIZ to LBUFSIZE to avoid name
+ conflict with stdio.h.
+ Posted to prep.ai.mit.edu
+-----------------------------------------------------------------
+v206 8/10/94 Use initial_scrpos for -t to avoid
+ displaying first page before init().
+ (thanks to Dominique Petitpierre)
+v207 8/12/94 Fix bug if stdout is not tty.
+v208 8/16/94 Fix bug in close_altfile if goto err1
+ in edit_ifile. (Thanks to M.J. Hewitt)
+v209 8/16/94 Change scroll to wscroll to avoid
+ conflict with library function.
+v210 8/16/94 Fix bug with bold on 8 bit chars.
+ (thanks to Vitor Duarte)
+v211 8/16/94 Don't quit on EOI in jump_loc / forw.
+v212 8/18/94 Use time_t if available.
+v213 8/20/94 Allow ospeed to be defined in termcap.h.
+v214 8/20/94 Added HILITE_SEARCH, -F, ESC-u cmd.
+ (thanks to Paul Lew and Bob Byrnes)
+v215 8/23/94 Fix -i toggle behavior.
+v216 8/23/94 Process BS in all searches, not only -u.
+v217 8/24/94 Added -X flag.
+v218 8/24/94 Reimplement undo_search.
+v219 8/24/94 Find tags marked with line number
+ instead of pattern.
+v220 8/24/94 Stay at same position after SIG_WINCH.
+v221 8/24/94 Fix bug in file percentage in big file.
+v222 8/25/94 Do better if can't reopen current file.
+v223 8/27/94 Support setlocale.
+ (thanks to Robert Joop)
+v224 8/29/94 Revert v216: process BS in search
+ only if -u.
+v225 9/6/94 Rewrite undo_search again: toggle.
+v226 9/15/94 Configuration fixes.
+ (thanks to David MacKenzie)
+v227 9/19/94 Fixed strerror config problem.
+ Posted to prep.ai.mit.edu
+-----------------------------------------------------------------
+v228 9/21/94 Fix bug in signals: repeated calls to
+ get_editkeys overflowed st_edittable.
+v229 9/21/94 Fix "Nothing to search" error if -a
+ and SRCH_PAST_EOF.
+v230 9/21/94 Don't print extra error msg in search
+ after regerror().
+v231 9/22/94 Fix hilite bug if search matches 0 chars.
+ (thanks to John Polstra)
+v232 9/23/94 Deal with weird systems that have
+ termios.h but not tcgetattr().
+ Posted to prep.ai.mit.edu
+-----------------------------------------------------------------
+v233 9/26/94 Use get_term() instead of pos_init() in
+ psignals to re-get lower_left termcap.
+ (Thanks to John Malecki)
+v234 9/26/94 Make MIDDLE closer to middle of screen.
+v235 9/27/94 Use local strchr if system doesn't have.
+v236 9/28/94 Don't use libucb; use libterm if
+ libtermcap & libcurses doesn't work.
+ (Fix for Solaris; thanks to Frank Kaefer)
+v237 9/30/94 Use system isupper() etc if provided.
+ Posted to prep.ai.mit.edu
+-----------------------------------------------------------------
+v238 10/6/94 Make binary non-blinking if LESSBINFMT
+ is set to a string without a *.
+v239 10/7/94 Don't let delimit_word run back past
+ beginning of cmdbuf.
+v240 10/10/94 Don't write into termcap buffer.
+ (Thanks to Benoit Speckel)
+v241 10/13/94 New lesskey file format.
+ Don't expand filenames in search command.
+v242 10/14/94 Allow lesskey specification of "literal".
+v243 10/14/94 Add #stop command to lesskey.
+v244 10/16/94 Add -f flag to lesskey.
+v245 10/25/94 Allow TAB_COMPLETE_FILENAME to be undefd.
+v246 10/27/94 Move help file to /usr/local/share.
+v247 10/27/94 Add -V option.
+v248 11/5/94 Add -V option to lesskey.
+v249 11/5/94 Remove -f flag from lesskey; default
+ input file is ~/.lesskey.in, not stdin.
+v250 11/7/94 Lesskey input file "-" means stdin.
+v251 11/9/94 Convert cfgetospeed result to ospeed.
+ (Thanks to Andrew Chernov)
+v252 11/16/94 Change default lesskey input file from
+ .lesskey.in to .lesskey.
+ Posted to prep.ai.mit.edu
+-----------------------------------------------------------------
+v253 11/21/94 Fix bug when tags file has a backslash.
+v254 12/6/94 Fix -k option.
+v255 12/8/94 Add #define EXAMINE to disable :e etc.
+v256 12/10/94 Change highlighting: only highlite search
+ results (but now it is reliable).
+v257 12/10/94 Add goto_line and repaint_highlight
+ to optimize highlight repaints.
+v258 12/12/94 Fixup in hilite_line if BS_SPECIAL.
+v259 12/12/94 Convert to autoconf 2.0.
+v260 12/13/94 Add SECURE define.
+v261 12/14/94 Use system WERASE char as EC_W_BACKSPACE.
+v262 12/16/94 Add -g/-G flag and screen_hilite.
+v263 12/20/94 Reimplement/optimize -G flag behavior.
+v264 12/23/94 Allow EXTRA string after line-edit cmd
+ in lesskey file.
+v265 12/24/94 Add LESSOPEN=|cmd syntax.
+v266 12/26/94 Add -I flag.
+v267 12/28/94 Formalize the four-byte header emitted
+ by a LESSOPEN pipe.
+v268 12/28/94 Get rid of four-byte header.
+v269 1/2/95 Close alt file before open new one.
+ Avoids multiple popen().
+v270 1/3/95 Use VISUAL; use S_ISDIR/S_ISREG; fix
+ config problem with Solaris POSIX regcomp.
+v271 1/4/95 Don't quit on read error.
+v272 1/5/95 Get rid of -L.
+v273 1/6/95 Fix ch_ungetchar bug; don't call
+ LESSOPEN on a pipe.
+v274 1/6/95 Ported to OS/2 (thanks to Kai Uwe Rommel)
+v275 1/18/95 Fix bug if toggle -G at EOF.
+v276 1/30/95 Fix OS/2 version.
+v277 1/31/95 Add "next" charset; don't display ^X
+ for X > 128.
+v278 2/14/95 Change default for -G.
+ Posted to prep.ai.mit.edu
+-----------------------------------------------------------------
+v279 2/22/95 Add GNU options --help, --version.
+ Minor config fixes.
+v280 2/24/95 Clean up calls to glob(); don't set #
+ if we can't open the new file.
+v281 2/24/95 Repeat search should turn on hilites.
+v282 3/2/95 Minor fixes.
+v283 3/2/95 Fix homefile; make OS2 look in $HOME.
+v284 3/2/95 Error if "v" on LESSOPENed file;
+ "%" figures out file size on pipe.
+v285 3/7/95 Don't set # in lsystem;
+ lesskey try $HOME first.
+v286 3/7/95 Reformat change history (too much free time?).
+v287 3/8/95 Fix hilite bug if overstrike multiple chars.
+v288 3/8/95 Allow lesskey to override get_editkey keys.
+v289 3/9/95 Fix adj_hilite bug when line gets processed by
+ hilite_line more than once.
+v290 3/9/95 Make configure automatically. Fix Sequent problem
+ with incompatible sigsetmask().
+
+*/
+
+char version[] = "290";