diff options
author | etheisen <etheisen@cvs.openbsd.org> | 1996-09-21 05:39:47 +0000 |
---|---|---|
committer | etheisen <etheisen@cvs.openbsd.org> | 1996-09-21 05:39:47 +0000 |
commit | 47d3895c0565680e9e7eba725be0896bfd06ce11 (patch) | |
tree | 921c411da39dc3da0c9f04e1732558983f372819 | |
parent | a2e06afb07d9a651364258a3d25e37f99d1dae46 (diff) |
Import of unmolested less-290.
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; ®dummy = 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 = ®dummy; + 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 == ®dummy) { + 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 != ®dummy) + *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 == ®dummy) { + 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 == ®dummy) + 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 == ®dummy || 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 == ®dummy) + 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"; |